区块链项目 - 9 UTXO优化
9 UTXO優化
9.1 查找未花費輸出
之前我們要查詢 某個地址的余額需要遍歷整個數據庫,隨著數據量的增大,這個工作量會很大,我們現在來嘗試優化
/Users/xxx/go/src/publicChain/part63-UTXOSet/BLC/UTXO_Set.go
const utxoTableName = "utxoTableName"type UTXOSet struct {Blockchain *Blockchain }func (utxoSet *UTXOSet) ResetUTXOSet() {err := utxoSet.Blockchain.DB.Update(func(tx *bolt.Tx) error {b := tx.Bucket([]byte(utxoTableName))if b != nil {tx.DeleteBucket([]byte(utxoTableName))b, _ := tx.CreateBucket([]byte(utxoTableName))if b != nil {}}return nil})if err != nil {log.Panic(err)} }/Users/xxx/go/src/publicChain/part62-UTXOSet/BLC/Transaction_TXOutputs.go
type TXOutputs struct {TxOutputs []*TXOutput }/Users/xxx/go/src/publicChain/part62-UTXOSet/BLC/Blockchain.go
func (blockchain *Blockchain) FindUTXOMap() map[string]*TXOutputs {//define an Iterator variable to attain blockblockchainIterator := blockchain.Iterator()//store all UTXOs have been spentspentableUTXOsMap := make(map[string][]*TXInput)utxoMaps := make(map[string]*TXOutputs)for {//attain blocksblock := blockchainIterator.Next()//range all index of Txs slicefor i := len(block.Txs) - 1; i >= 0; i-- {//attain Txstx := block.Txs[i]//judging if tx is in coinbase and put all txInput into sliceif tx.IsCoinbaseTransaction() == false {for _, txInput := range tx.Vins {//convert []byte to stringtxHash := hex.EncodeToString(txInput.TxHash)spentableUTXOsMap[txHash] = append(spentableUTXOsMap[txHash], txInput)}}//define an instance of &TXOutputstxOutputs := &TXOutputs{[]*TXOutput{}}//convert []byte to stringtxHash := hex.EncodeToString(tx.TxHash)workOutLoop://range if a value has been spentfor index, out := range tx.Vouts {//convert []byte to stringtxHash := hex.EncodeToString(tx.TxHash)//attain []*TXInputtxInputs := spentableUTXOsMap[txHash]//judging and put out into sliceif len(txInputs) > 0 {isSpent := falsefor _, in := range txInputs {outPublicKey := out.Ripemd160HashinPublicKey := in.PublicKeyif bytes.Compare(outPublicKey, Ripemd160Hash(inPublicKey)) == 0 {if index == in.Vouts {isSpent = truecontinue workOutLoop}}}if isSpent == false {txOutputs.TxOutputs = append(txOutputs.TxOutputs, out)}} else {txOutputs.TxOutputs = append(txOutputs.TxOutputs, out)}}//set key-value pairsutxoMaps[txHash] = txOutputs}var hashInt big.InthashInt.SetBytes(block.PreBlockHash)if hashInt.Cmp(big.NewInt(0)) == 0 {break}}return utxoMaps }相應的命令行工具也改一下,編譯并運行,結果能夠正常輸出
./bc test map[8e578e4d1d1ef2f5101bdd72e5bd922cb8d85956333977e03c4260975db0223c:0xc00000ca50] ./bc send -from '["1Eubsorq2zfZ1vmuA8NRcNxPwptSG6c8BJ"]' -to '["1B5y2naPTzkXLztVk2ffLPd3wbpoQg6723"]' -amount '["2"]' ./bc getBalance -address "1Eubsorq2zfZ1vmuA8NRcNxPwptSG6c8BJ" 1Eubsorq2zfZ1vmuA8NRcNxPwptSG6c8BJ has 18 token ./bc send -from '["1Eubsorq2zfZ1vmuA8NRcNxPwptSG6c8BJ","1B5y2naPTzkXLztVk2ffLPd3wbpoQg6723"]' -to '["1B5y2naPTzkXLztVk2ffLPd3wbpoQg6723","1Eubsorq2zfZ1vmuA8NRcNxPwptSG6c8BJ"]' -amount '["3","4"]'嘗試進行多筆轉賬會出錯,我們下一節會詳細講解
9.2 多筆交易中的數字簽名
我們完善一下相關函數
func NewSimpleTransaction(from, to string, amount int, blockchain *Blockchain, txs []*Transaction) *Transaction {--//signatureblockchain.SignTransaction(tx, wallet.PrivateKey, txs)return tx } func (blockchain *Blockchain) SignTransaction(tx *Transaction, privateKey ecdsa.PrivateKey, txs []*Transaction) {--for _, vin := range tx.Vins {prevTX, err := blockchain.FindTransaction(vin.TxHash, txs)-- } func (blockchain *Blockchain) FindTransaction(ID []byte, txs []*Transaction) (Transaction, error) {for _, tx := range txs {if bytes.Compare(tx.TxHash, ID) == 0 {return *tx, nil}}bci := blockchain.Iterator()for {block := bci.Next()for _, tx := range block.Txs {if bytes.Compare(tx.TxHash, ID) == 0 {return *tx, nil}}var hashInt big.InthashInt.SetBytes(block.PreBlockHash)if big.NewInt(0).Cmp(&hashInt) == 0 {break}}return Transaction{}, nil }最后重新編譯運行
./bc send -from '["1Eubsorq2zfZ1vmuA8NRcNxPwptSG6c8BJ"]' -to '["1B5y2naPTzkXLztVk2ffLPd3wbpoQg6723"]' -amount '["2"]' ./bc getBalance -address "1Eubsorq2zfZ1vmuA8NRcNxPwptSG6c8BJ" 1Eubsorq2zfZ1vmuA8NRcNxPwptSG6c8BJ has 29 token ./bc getBalance -address "1B5y2naPTzkXLztVk2ffLPd3wbpoQg6723" 1B5y2naPTzkXLztVk2ffLPd3wbpoQg6723 has 1 token9.3 遍歷并存儲UXTO
/Users/xxx/go/src/publicChain/part63-UTXOSet/BLC/Transaction_TXOutputs.go
//SerializeBlockfunc (txOutput *TXOutputs) Serialize() []byte {var result bytes.Bufferencoder := gob.NewEncoder(&result)err := encoder.Encode(txOutput)if err != nil {log.Panic(err)}return result.Bytes() }//DeserializeBlockfunc DeserializeTXOutputs(txOutputsBytes []byte) *TXOutputs {var txOutputs TXOutputsdecoder := gob.NewDecoder(bytes.NewReader(txOutputsBytes))err := decoder.Decode(&txOutputs)if err != nil {log.Panic()}return &txOutputs }/Users/xxx/go/src/publicChain/part63-UTXOSet/BLC/UTXO_Set.go
const utxoTableName = "utxoTableName"type UTXOSet struct {Blockchain *Blockchain }func (utxoSet *UTXOSet) ResetUTXOSet() {err := utxoSet.Blockchain.DB.Update(func(tx *bolt.Tx) error {b := tx.Bucket([]byte(utxoTableName))if b != nil {err := tx.DeleteBucket([]byte(utxoTableName))if err != nil {log.Panic(err)}}b, _ = tx.CreateBucket([]byte(utxoTableName))if b != nil {txOutputMap := utxoSet.Blockchain.FindUTXOMap()for keyHash, outs := range txOutputMap {txHash, _ := hex.DecodeString(keyHash)b.Put(txHash, outs.Serialize())}}return nil})if err != nil {log.Panic(err)} }在終端中運行,程序能夠正常運行
9.3 FindUTXOMap方法優化
我們直接通過utxoSet來獲取余額
func (cli CLI) getBalance(address string) {fmt.Println("Address:" + address)blockchain := BlockChainObject()defer blockchain.DB.Close()utxoSet := &UTXOSet{blockchain}amount := utxoSet.GetBalance(address)fmt.Printf("%s has %d token\n", address, amount) }我們再來完善一下需要調用的方法
func (utxoSet *UTXOSet) FindUTXOForAddress(address string) []*UTXO {} func (utxoSet *UTXOSet) GetBalance(address string) int64 {// }我們再改為下列結構體,其他文件相應的也變更
type TXOutputs struct {UTXOS []*UTXO }/Users/xxx/go/src/publicChain/part63-UTXOSet/BLC/UTXO_Set.go
func (utxoSet *UTXOSet) GetBalance(address string) int64 {UTXOS := utxoSet.FindUTXOForAddress(address)var amount int64for _, utxo := range UTXOS {amount += utxo.Output.Value}return amount } func (utxoSet *UTXOSet) FindUTXOForAddress(address string) []*UTXO {var utxos []*UTXOutxoSet.Blockchain.DB.View(func(tx *bolt.Tx) error {//assume bucket exists and has keysb := tx.Bucket([]byte(utxoTableName))c := b.Cursor()for k,v := c.First();k != nil; k,v = c.Next() {fmt.Printf("key=%s, value=%s\n",k,v)txOutputs := DeserializeTXOutputs(v)for _,utxo := range txOutputs.UTXOS {if utxo.Output.UnlockScriptPublicKeyWithAddress(address) {utxos = append(utxos,utxo)}}}return nil})return utxos }在終端中的重新創建區塊并查詢余額,結果能夠正常輸出,現在我們每次查詢余額就不需要遍歷數據庫了
./bc creatBlockChain -address "1JfaJ6AtBCSop8npzv1UBhayhM9AxNnu9N" ./bc getBalance -address "1JfaJ6AtBCSop8npzv1UBhayhM9AxNnu9N" 1JfaJ6AtBCSop8npzv1UBhayhM9AxNnu9N has 10 token9.4 修改CoinbaseTransaction交易哈希導致的bug
我們修改一下如下代碼,調用ResetUTOXSet方法重置UTOX
func (cli *CLI) testMethod() {//attain blockchainblockchain := BlockChainObject()defer blockchain.DB.Close()//attain utxoSetutxoSet := &UTXOSet{blockchain}//call method utxoSet.ResetUTXOSet() }? 接著我們在終端中編譯并運行
./bc send -from '["1Dh46KP31Gfkd55wqdmnpqy2TQh1cvwpzY"]' -to '["17pGGPVzFrzvLLoHWJoJ926Eemwm1DJKRN"]' -amount '["2"]'我們發現在終端轉賬不能正常運行,我們修改一下部分代碼
func (tx *Transaction) HashTransaction() {var result bytes.Bufferencoder := gob.NewEncoder(&result)err := encoder.Encode(tx)if err != nil {log.Panic(err)}resultBytes := bytes.Join([][]byte{IntToHex(time.Now().Unix()), result.Bytes()}, []byte{})hash := sha256.Sum256(resultBytes)tx.TxHash = hash[:] }改好之后在次編譯重置即可查詢余額
./bc getBalance -address "17pGGPVzFrzvLLoHWJoJ926Eemwm1DJKRN" 17pGGPVzFrzvLLoHWJoJ926Eemwm1DJKRN has 2 token ./bc getBalance -address "1Dh46KP31Gfkd55wqdmnpqy2TQh1cvwpzY" 1Dh46KP31Gfkd55wqdmnpqy2TQh1cvwpzY has 18 token ./bc send -from '["1Dh46KP31Gfkd55wqdmnpqy2TQh1cvwpzY","17pGGPVzFrzvLLoHWJoJ926Eemwm1DJKRN"]' -to '["17pGGPVzFrzvLLoHWJoJ926Eemwm1DJKRN","1Dh46KP31Gfkd55wqdmnpqy2TQh1cvwpzY"]' -amount '["3","4"]' 17pGGPVzFrzvLLoHWJoJ926Eemwm1DJKRN has 1 token 1Dh46KP31Gfkd55wqdmnpqy2TQh1cvwpzY has 29 token一次性轉多筆也是能夠實現的
總結
以上是生活随笔為你收集整理的区块链项目 - 9 UTXO优化的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: uniapp 小程序分享功能
- 下一篇: 521表白神器(520后续)-教你用微信