From 09286bb82a6e307d8333ab94284de16888cebb6b Mon Sep 17 00:00:00 2001 From: Richard Liu Date: Sat, 4 Aug 2018 23:46:23 -0700 Subject: [PATCH 1/5] add outpoint --- blockchain/blockchain.go | 18 +++++++------- blockchain/transaction.go | 51 ++++++++++++++++++++++++++++++--------- blockchain/utxopool.go | 40 +++++++++++++++--------------- client/btctxgen/main.go | 4 +-- client/txgen/main.go | 10 ++++---- 5 files changed, 76 insertions(+), 47 deletions(-) diff --git a/blockchain/blockchain.go b/blockchain/blockchain.go index 2f2e8b91f..b9f4d01cf 100644 --- a/blockchain/blockchain.go +++ b/blockchain/blockchain.go @@ -51,8 +51,8 @@ func (bc *Blockchain) FindUnspentTransactions(address string) []Transaction { for _, txInput := range tx.TxInput { if address == txInput.Address { - ID := hex.EncodeToString(txInput.TxID[:]) - spentTXOs[ID] = append(spentTXOs[ID], txInput.TxOutputIndex) + ID := hex.EncodeToString(txInput.PreviousOutPoint.Hash[:]) + spentTXOs[ID] = append(spentTXOs[ID], txInput.PreviousOutPoint.Index) } } } @@ -103,7 +103,7 @@ Work: } // NewUTXOTransaction creates a new transaction -func (bc *Blockchain) NewUTXOTransaction(from, to string, amount int, shardId uint32) *Transaction { +func (bc *Blockchain) NewUTXOTransaction(from, to string, amount int, shardID uint32) *Transaction { var inputs []TXInput var outputs []TXOutput @@ -120,18 +120,18 @@ func (bc *Blockchain) NewUTXOTransaction(from, to string, amount int, shardId ui return nil } - txId := [32]byte{} - copy(txId[:], id[:]) + txID := Hash{} + copy(txID[:], id[:]) for _, out := range outs { - input := TXInput{txId, out, from, shardId} - inputs = append(inputs, input) + input := NewTXInput(NewOutPoint(&txID, out), from, shardID) + inputs = append(inputs, *input) } } // Build a list of outputs - outputs = append(outputs, TXOutput{amount, to, shardId}) + outputs = append(outputs, TXOutput{amount, to, shardID}) if acc > amount { - outputs = append(outputs, TXOutput{acc - amount, from, shardId}) // a change + outputs = append(outputs, TXOutput{acc - amount, from, shardID}) // a change } tx := Transaction{[32]byte{}, inputs, outputs, nil} diff --git a/blockchain/transaction.go b/blockchain/transaction.go index 23e888032..a1840e9e0 100644 --- a/blockchain/transaction.go +++ b/blockchain/transaction.go @@ -25,12 +25,41 @@ type TXOutput struct { ShardId uint32 // The Id of the shard where this UTXO belongs } +type Hash = [32]byte + +// Output defines a data type that is used to track previous +// transaction outputs. +// Hash is the transaction id +// Index is the index of the transaction ouput in the previous transaction +type OutPoint struct { + Hash Hash + Index int +} + +// NewOutPoint returns a new transaction outpoint point with the +// provided hash and index. +func NewOutPoint(hash *Hash, index int) *OutPoint { + return &OutPoint{ + Hash: *hash, + Index: index, + } +} + // TXInput is the struct of transaction input (a UTXO) in a transaction. type TXInput struct { - TxID [32]byte - TxOutputIndex int - Address string - ShardId uint32 // The Id of the shard where this UTXO belongs + PreviousOutPoint OutPoint + Address string + ShardID uint32 // The Id of the shard where this UTXO belongs +} + +// NewTXInput returns a new transaction input with the provided +// previous outpoint point, output address and shardID +func NewTXInput(prevOut *OutPoint, address string, shardID uint32) *TXInput { + return &TXInput{ + PreviousOutPoint: *prevOut, + Address: address, + ShardID: shardID, + } } // The proof of accept or reject in the cross shard transaction locking phase. @@ -68,24 +97,24 @@ func (tx *Transaction) SetID() { } // NewCoinbaseTX creates a new coinbase transaction -func NewCoinbaseTX(to, data string, shardId uint32) *Transaction { +func NewCoinbaseTX(to, data string, shardID uint32) *Transaction { if data == "" { data = fmt.Sprintf("Reward to '%s'", to) } - txin := TXInput{[32]byte{}, -1, data, shardId} - txout := TXOutput{DefaultCoinbaseValue, to, shardId} - tx := Transaction{[32]byte{}, []TXInput{txin}, []TXOutput{txout}, nil} + txin := NewTXInput(nil, to, shardID) + txout := TXOutput{DefaultCoinbaseValue, to, shardID} + tx := Transaction{[32]byte{}, []TXInput{*txin}, []TXOutput{txout}, nil} tx.SetID() return &tx } // Used for debuging. func (txInput *TXInput) String() string { - res := fmt.Sprintf("TxID: %v, ", hex.EncodeToString(txInput.TxID[:])) - res += fmt.Sprintf("TxOutputIndex: %v, ", txInput.TxOutputIndex) + res := fmt.Sprintf("TxID: %v, ", hex.EncodeToString(txInput.PreviousOutPoint.Hash[:])) + res += fmt.Sprintf("TxOutputIndex: %v, ", txInput.PreviousOutPoint.Index) res += fmt.Sprintf("Address: %v, ", txInput.Address) - res += fmt.Sprintf("Shard Id: %v", txInput.ShardId) + res += fmt.Sprintf("Shard Id: %v", txInput.ShardID) return res } diff --git a/blockchain/utxopool.go b/blockchain/utxopool.go index 0adcebf9b..0be9bd0cf 100644 --- a/blockchain/utxopool.go +++ b/blockchain/utxopool.go @@ -57,13 +57,13 @@ func (utxoPool *UTXOPool) VerifyOneTransaction(tx *Transaction, spentTXOs *map[s // Calculate the sum of TxInput for _, in := range tx.TxInput { // Only check the input for my own shard. - if in.ShardId != utxoPool.ShardId { + if in.ShardID != utxoPool.ShardId { crossShard = true continue } - inTxID := hex.EncodeToString(in.TxID[:]) - index := in.TxOutputIndex + inTxID := hex.EncodeToString(in.PreviousOutPoint.Hash[:]) + index := in.PreviousOutPoint.Index // Check if the transaction with the addres is spent or not. if val, ok := (*spentTXOs)[in.Address][inTxID][index]; ok { if val { @@ -150,7 +150,7 @@ func (utxoPool *UTXOPool) UpdateOneTransaction(tx *Transaction) { isCrossShard := false // check whether it's a cross shard tx. for _, in := range tx.TxInput { - if in.ShardId != utxoPool.ShardId { + if in.ShardID != utxoPool.ShardId { isCrossShard = true break } @@ -161,11 +161,11 @@ func (utxoPool *UTXOPool) UpdateOneTransaction(tx *Transaction) { // Check whether for this shard this cross transaction is valid or not. for _, in := range tx.TxInput { // Only check the input for my own shard. - if in.ShardId != utxoPool.ShardId { + if in.ShardID != utxoPool.ShardId { continue } - inTxID := hex.EncodeToString(in.TxID[:]) - if _, ok := utxoPool.UtxoMap[in.Address][inTxID][in.TxOutputIndex]; !ok { + inTxID := hex.EncodeToString(in.PreviousOutPoint.Hash[:]) + if _, ok := utxoPool.UtxoMap[in.Address][inTxID][in.PreviousOutPoint.Index]; !ok { isValidCrossShard = false } } @@ -181,17 +181,17 @@ func (utxoPool *UTXOPool) UpdateOneTransaction(tx *Transaction) { if isValidCrossShard { for _, in := range tx.TxInput { // Only check the input for my own shard. - if in.ShardId != utxoPool.ShardId { + if in.ShardID != utxoPool.ShardId { continue } // NOTE: for the locking phase of cross tx, the utxo is simply removed from the pool. - inTxID := hex.EncodeToString(in.TxID[:]) - value := utxoPool.UtxoMap[in.Address][inTxID][in.TxOutputIndex] - utxoPool.DeleteOneUtxo(in.Address, inTxID, in.TxOutputIndex) + inTxID := hex.EncodeToString(in.PreviousOutPoint.Hash[:]) + value := utxoPool.UtxoMap[in.Address][inTxID][in.PreviousOutPoint.Index] + utxoPool.DeleteOneUtxo(in.Address, inTxID, in.PreviousOutPoint.Index) if isCrossShard { // put the delete (locked) utxo into a separate locked utxo pool - inTxID := hex.EncodeToString(in.TxID[:]) + inTxID := hex.EncodeToString(in.PreviousOutPoint.Hash[:]) if _, ok := utxoPool.LockedUtxoMap[in.Address]; !ok { utxoPool.LockedUtxoMap[in.Address] = make(map[string]map[int]int) utxoPool.LockedUtxoMap[in.Address][inTxID] = make(map[int]int) @@ -199,7 +199,7 @@ func (utxoPool *UTXOPool) UpdateOneTransaction(tx *Transaction) { if _, ok := utxoPool.LockedUtxoMap[in.Address][inTxID]; !ok { utxoPool.LockedUtxoMap[in.Address][inTxID] = make(map[int]int) } - utxoPool.LockedUtxoMap[in.Address][inTxID][in.TxOutputIndex] = value + utxoPool.LockedUtxoMap[in.Address][inTxID][in.PreviousOutPoint.Index] = value } } } @@ -212,12 +212,12 @@ func (utxoPool *UTXOPool) UpdateOneTransaction(tx *Transaction) { // unlock-to-abort, bring back (unlock) the utxo input for _, in := range tx.TxInput { // Only unlock the input for my own shard. - if in.ShardId != utxoPool.ShardId { + if in.ShardID != utxoPool.ShardId { continue } // Simply bring back the locked (removed) utxo - inTxID := hex.EncodeToString(in.TxID[:]) + inTxID := hex.EncodeToString(in.PreviousOutPoint.Hash[:]) if _, ok := utxoPool.UtxoMap[in.Address]; !ok { utxoPool.UtxoMap[in.Address] = make(map[string]map[int]int) utxoPool.UtxoMap[in.Address][inTxID] = make(map[int]int) @@ -225,10 +225,10 @@ func (utxoPool *UTXOPool) UpdateOneTransaction(tx *Transaction) { if _, ok := utxoPool.UtxoMap[in.Address][inTxID]; !ok { utxoPool.UtxoMap[in.Address][inTxID] = make(map[int]int) } - value := utxoPool.LockedUtxoMap[in.Address][inTxID][in.TxOutputIndex] - utxoPool.UtxoMap[in.Address][inTxID][in.TxOutputIndex] = value + value := utxoPool.LockedUtxoMap[in.Address][inTxID][in.PreviousOutPoint.Index] + utxoPool.UtxoMap[in.Address][inTxID][in.PreviousOutPoint.Index] = value - utxoPool.DeleteOneLockedUtxo(in.Address, inTxID, in.TxOutputIndex) + utxoPool.DeleteOneLockedUtxo(in.Address, inTxID, in.PreviousOutPoint.Index) } } } else { @@ -319,10 +319,10 @@ func (utxoPool *UTXOPool) SelectTransactionsForNewBlock(transactions []*Transact return selected, unselected, crossShardTxs } -func getShardTxInput(transaction *Transaction, shardId uint32) []TXInput { +func getShardTxInput(transaction *Transaction, shardID uint32) []TXInput { result := []TXInput{} for _, txInput := range transaction.TxInput { - if txInput.ShardId == shardId { + if txInput.ShardID == shardID { result = append(result, txInput) } } diff --git a/client/btctxgen/main.go b/client/btctxgen/main.go index d7495e6f6..b89c9f221 100644 --- a/client/btctxgen/main.go +++ b/client/btctxgen/main.go @@ -78,7 +78,7 @@ LOOP: isCrossShardTx := false if btcTx.IsCoinBase() { // TODO: merge txID with txIndex in TxInput - tx.TxInput = []blockchain.TXInput{blockchain.TXInput{[32]byte{}, -1, "", nodeShardID}} + tx.TxInput = []blockchain.TXInput{*blockchain.NewTXInput(nil, "", nodeShardID)} } else { for _, btcTXI := range btcTx.TxIn { btcTXIDStr := btc.NewUint256(btcTXI.Input.Hash[:]).String() @@ -86,7 +86,7 @@ LOOP: if txRef.shardID != nodeShardID { isCrossShardTx = true } - tx.TxInput = append(tx.TxInput, blockchain.TXInput{txRef.txID, int(btcTXI.Input.Vout), "", txRef.shardID}) + tx.TxInput = append(tx.TxInput, *blockchain.NewTXInput(blockchain.NewOutPoint(&txRef.txID, int(btcTXI.Input.Vout)), "", txRef.shardID)) } } diff --git a/client/txgen/main.go b/client/txgen/main.go index 0a7423bd6..584a8d969 100644 --- a/client/txgen/main.go +++ b/client/txgen/main.go @@ -145,7 +145,7 @@ func generateCrossShardTx(txInfo *TxInfo) { for crossShardIndex, crossShardValue := range crossShardUtxos { crossUtxoValue = crossShardValue - crossTxin = &blockchain.TXInput{crossTxId, crossShardIndex, txInfo.address, uint32(crossShardId)} + crossTxin = blockchain.NewTXInput(blockchain.NewOutPoint(&crossTxId, crossShardIndex), txInfo.address, uint32(crossShardId)) break } if crossTxin != nil { @@ -154,8 +154,8 @@ func generateCrossShardTx(txInfo *TxInfo) { } // Add the utxo from current shard - txin := blockchain.TXInput{txInfo.id, txInfo.index, txInfo.address, nodeShardID} - txInputs := []blockchain.TXInput{txin} + txIn := blockchain.NewTXInput(blockchain.NewOutPoint(&txInfo.id, txInfo.index), txInfo.address, nodeShardID) + txInputs := []blockchain.TXInput{*txIn} // Add the utxo from the other shard, if any if crossTxin != nil { // This means the ratio of cross shard tx could be lower than 1/3 @@ -183,11 +183,11 @@ func generateCrossShardTx(txInfo *TxInfo) { func generateSingleShardTx(txInfo *TxInfo) { nodeShardID := txInfo.dataNodes[txInfo.shardID].Consensus.ShardID // Add the utxo as new tx input - txin := blockchain.TXInput{txInfo.id, txInfo.index, txInfo.address, nodeShardID} + txin := blockchain.NewTXInput(blockchain.NewOutPoint(&txInfo.id, txInfo.index), txInfo.address, nodeShardID) // Spend the utxo to a random address in [0 - N) txout := blockchain.TXOutput{txInfo.value, strconv.Itoa(rand.Intn(setting.numOfAddress)), nodeShardID} - tx := blockchain.Transaction{[32]byte{}, []blockchain.TXInput{txin}, []blockchain.TXOutput{txout}, nil} + tx := blockchain.Transaction{[32]byte{}, []blockchain.TXInput{*txin}, []blockchain.TXOutput{txout}, nil} tx.SetID() txInfo.txs = append(txInfo.txs, &tx) From 5ad778781ef9cf498808c2d37c9fcaa186c90d20 Mon Sep 17 00:00:00 2001 From: Richard Liu Date: Sun, 5 Aug 2018 01:12:31 -0700 Subject: [PATCH 2/5] fixed null outpoint issue; make outpoint.index uint32. --- blockchain/blockchain.go | 10 ++++---- blockchain/transaction.go | 15 ++++++++---- blockchain/utxopool.go | 50 +++++++++++++++++++-------------------- client/btctxgen/main.go | 6 ++--- client/txgen/main.go | 2 +- 5 files changed, 45 insertions(+), 38 deletions(-) diff --git a/blockchain/blockchain.go b/blockchain/blockchain.go index b9f4d01cf..cb71620e2 100644 --- a/blockchain/blockchain.go +++ b/blockchain/blockchain.go @@ -23,7 +23,7 @@ func (bc *Blockchain) GetLatestBlock() *Block { // FindUnspentTransactions returns a list of transactions containing unspent outputs func (bc *Blockchain) FindUnspentTransactions(address string) []Transaction { var unspentTXs []Transaction - spentTXOs := make(map[string][]int) + spentTXOs := make(map[string][]uint32) for index := len(bc.Blocks) - 1; index >= 0; index-- { block := bc.Blocks[index] @@ -37,7 +37,7 @@ func (bc *Blockchain) FindUnspentTransactions(address string) []Transaction { idx = 0 } for outIdx, txOutput := range tx.TxOutput { - if idx >= 0 && spentTXOs[txID][idx] == outIdx { + if idx >= 0 && spentTXOs[txID][idx] == uint32(outIdx) { idx++ continue } @@ -78,8 +78,8 @@ func (bc *Blockchain) FindUTXO(address string) []TXOutput { } // FindSpendableOutputs finds and returns unspent outputs to reference in inputs -func (bc *Blockchain) FindSpendableOutputs(address string, amount int) (int, map[string][]int) { - unspentOutputs := make(map[string][]int) +func (bc *Blockchain) FindSpendableOutputs(address string, amount int) (int, map[string][]uint32) { + unspentOutputs := make(map[string][]uint32) unspentTXs := bc.FindUnspentTransactions(address) accumulated := 0 @@ -90,7 +90,7 @@ Work: for outIdx, txOutput := range tx.TxOutput { if txOutput.Address == address && accumulated < amount { accumulated += txOutput.Value - unspentOutputs[txID] = append(unspentOutputs[txID], outIdx) + unspentOutputs[txID] = append(unspentOutputs[txID], uint32(outIdx)) if accumulated >= amount { break Work diff --git a/blockchain/transaction.go b/blockchain/transaction.go index a1840e9e0..494e75500 100644 --- a/blockchain/transaction.go +++ b/blockchain/transaction.go @@ -7,9 +7,16 @@ import ( "encoding/hex" "fmt" "log" + "math" +) + +var ( + // zeroHash is the zero value for a Hash and is defined as + // a package level variable to avoid the need to create a new instance + // every time a check is needed. + zeroHash Hash ) -// Transaction represents a Bitcoin transaction type Transaction struct { ID [32]byte // 32 byte hash TxInput []TXInput @@ -33,12 +40,12 @@ type Hash = [32]byte // Index is the index of the transaction ouput in the previous transaction type OutPoint struct { Hash Hash - Index int + Index uint32 } // NewOutPoint returns a new transaction outpoint point with the // provided hash and index. -func NewOutPoint(hash *Hash, index int) *OutPoint { +func NewOutPoint(hash *Hash, index uint32) *OutPoint { return &OutPoint{ Hash: *hash, Index: index, @@ -102,7 +109,7 @@ func NewCoinbaseTX(to, data string, shardID uint32) *Transaction { data = fmt.Sprintf("Reward to '%s'", to) } - txin := NewTXInput(nil, to, shardID) + txin := NewTXInput(NewOutPoint(&Hash{}, math.MaxUint32), to, shardID) txout := TXOutput{DefaultCoinbaseValue, to, shardID} tx := Transaction{[32]byte{}, []TXInput{*txin}, []TXOutput{txout}, nil} tx.SetID() diff --git a/blockchain/utxopool.go b/blockchain/utxopool.go index 0be9bd0cf..b91dc0a86 100644 --- a/blockchain/utxopool.go +++ b/blockchain/utxopool.go @@ -25,15 +25,15 @@ type UTXOPool struct { ] ] */ - UtxoMap map[string]map[string]map[int]int - LockedUtxoMap map[string]map[string]map[int]int + UtxoMap map[string]map[string]map[uint32]int + LockedUtxoMap map[string]map[string]map[uint32]int ShardId uint32 mutex sync.Mutex } // VerifyTransactions verifies if a list of transactions valid for this shard. func (utxoPool *UTXOPool) VerifyTransactions(transactions []*Transaction) bool { - spentTXOs := make(map[string]map[string]map[int]bool) + spentTXOs := make(map[string]map[string]map[uint32]bool) if utxoPool != nil { for _, tx := range transactions { if valid, crossShard := utxoPool.VerifyOneTransaction(tx, &spentTXOs); !crossShard && !valid { @@ -45,13 +45,13 @@ func (utxoPool *UTXOPool) VerifyTransactions(transactions []*Transaction) bool { } // VerifyOneTransaction verifies if a list of transactions valid. -func (utxoPool *UTXOPool) VerifyOneTransaction(tx *Transaction, spentTXOs *map[string]map[string]map[int]bool) (valid, crossShard bool) { +func (utxoPool *UTXOPool) VerifyOneTransaction(tx *Transaction, spentTXOs *map[string]map[string]map[uint32]bool) (valid, crossShard bool) { if len(tx.Proofs) != 0 { return utxoPool.VerifyUnlockTransaction(tx) } if spentTXOs == nil { - spentTXOs = &map[string]map[string]map[int]bool{} + spentTXOs = &map[string]map[string]map[uint32]bool{} } inTotal := 0 // Calculate the sum of TxInput @@ -72,10 +72,10 @@ func (utxoPool *UTXOPool) VerifyOneTransaction(tx *Transaction, spentTXOs *map[s } // Mark the transactions with the address and index spent. if _, ok := (*spentTXOs)[in.Address]; !ok { - (*spentTXOs)[in.Address] = make(map[string]map[int]bool) + (*spentTXOs)[in.Address] = make(map[string]map[uint32]bool) } if _, ok := (*spentTXOs)[in.Address][inTxID]; !ok { - (*spentTXOs)[in.Address][inTxID] = make(map[int]bool) + (*spentTXOs)[in.Address][inTxID] = make(map[uint32]bool) } (*spentTXOs)[in.Address][inTxID][index] = true @@ -193,11 +193,11 @@ func (utxoPool *UTXOPool) UpdateOneTransaction(tx *Transaction) { // put the delete (locked) utxo into a separate locked utxo pool inTxID := hex.EncodeToString(in.PreviousOutPoint.Hash[:]) if _, ok := utxoPool.LockedUtxoMap[in.Address]; !ok { - utxoPool.LockedUtxoMap[in.Address] = make(map[string]map[int]int) - utxoPool.LockedUtxoMap[in.Address][inTxID] = make(map[int]int) + utxoPool.LockedUtxoMap[in.Address] = make(map[string]map[uint32]int) + utxoPool.LockedUtxoMap[in.Address][inTxID] = make(map[uint32]int) } if _, ok := utxoPool.LockedUtxoMap[in.Address][inTxID]; !ok { - utxoPool.LockedUtxoMap[in.Address][inTxID] = make(map[int]int) + utxoPool.LockedUtxoMap[in.Address][inTxID] = make(map[uint32]int) } utxoPool.LockedUtxoMap[in.Address][inTxID][in.PreviousOutPoint.Index] = value } @@ -219,11 +219,11 @@ func (utxoPool *UTXOPool) UpdateOneTransaction(tx *Transaction) { // Simply bring back the locked (removed) utxo inTxID := hex.EncodeToString(in.PreviousOutPoint.Hash[:]) if _, ok := utxoPool.UtxoMap[in.Address]; !ok { - utxoPool.UtxoMap[in.Address] = make(map[string]map[int]int) - utxoPool.UtxoMap[in.Address][inTxID] = make(map[int]int) + utxoPool.UtxoMap[in.Address] = make(map[string]map[uint32]int) + utxoPool.UtxoMap[in.Address][inTxID] = make(map[uint32]int) } if _, ok := utxoPool.UtxoMap[in.Address][inTxID]; !ok { - utxoPool.UtxoMap[in.Address][inTxID] = make(map[int]int) + utxoPool.UtxoMap[in.Address][inTxID] = make(map[uint32]int) } value := utxoPool.LockedUtxoMap[in.Address][inTxID][in.PreviousOutPoint.Index] utxoPool.UtxoMap[in.Address][inTxID][in.PreviousOutPoint.Index] = value @@ -239,13 +239,13 @@ func (utxoPool *UTXOPool) UpdateOneTransaction(tx *Transaction) { continue } if _, ok := utxoPool.UtxoMap[out.Address]; !ok { - utxoPool.UtxoMap[out.Address] = make(map[string]map[int]int) - utxoPool.UtxoMap[out.Address][txID] = make(map[int]int) + utxoPool.UtxoMap[out.Address] = make(map[string]map[uint32]int) + utxoPool.UtxoMap[out.Address][txID] = make(map[uint32]int) } if _, ok := utxoPool.UtxoMap[out.Address][txID]; !ok { - utxoPool.UtxoMap[out.Address][txID] = make(map[int]int) + utxoPool.UtxoMap[out.Address][txID] = make(map[uint32]int) } - utxoPool.UtxoMap[out.Address][txID][index] = out.Value + utxoPool.UtxoMap[out.Address][txID][uint32(index)] = out.Value } } } // If it's a cross shard locking Tx, then don't update so the input UTXOs are locked (removed), and the money is not spendable until unlock-to-commit or unlock-to-abort @@ -275,12 +275,12 @@ func (utxoPool *UTXOPool) VerifyAndUpdate(transactions []*Transaction) bool { func CreateUTXOPoolFromTransaction(tx *Transaction, shardId uint32) *UTXOPool { var utxoPool UTXOPool txID := hex.EncodeToString(tx.ID[:]) - utxoPool.UtxoMap = make(map[string]map[string]map[int]int) - utxoPool.LockedUtxoMap = make(map[string]map[string]map[int]int) + utxoPool.UtxoMap = make(map[string]map[string]map[uint32]int) + utxoPool.LockedUtxoMap = make(map[string]map[string]map[uint32]int) for index, out := range tx.TxOutput { - utxoPool.UtxoMap[out.Address] = make(map[string]map[int]int) - utxoPool.UtxoMap[out.Address][txID] = make(map[int]int) - utxoPool.UtxoMap[out.Address][txID][index] = out.Value + utxoPool.UtxoMap[out.Address] = make(map[string]map[uint32]int) + utxoPool.UtxoMap[out.Address][txID] = make(map[uint32]int) + utxoPool.UtxoMap[out.Address][txID][uint32(index)] = out.Value } utxoPool.ShardId = shardId return &utxoPool @@ -296,7 +296,7 @@ func CreateUTXOPoolFromGenesisBlockChain(bc *Blockchain) *UTXOPool { // SelectTransactionsForNewBlock returns a list of index of valid transactions for the new block. func (utxoPool *UTXOPool) SelectTransactionsForNewBlock(transactions []*Transaction, maxNumTxs int) ([]*Transaction, []*Transaction, []*CrossShardTxAndProof) { selected, unselected, crossShardTxs := []*Transaction{}, []*Transaction{}, []*CrossShardTxAndProof{} - spentTXOs := make(map[string]map[string]map[int]bool) + spentTXOs := make(map[string]map[string]map[uint32]bool) for _, tx := range transactions { valid, crossShard := utxoPool.VerifyOneTransaction(tx, &spentTXOs) @@ -330,7 +330,7 @@ func getShardTxInput(transaction *Transaction, shardID uint32) []TXInput { } // DeleteOneBalanceItem deletes one balance item of UTXOPool and clean up if possible. -func (utxoPool *UTXOPool) DeleteOneUtxo(address, txID string, index int) { +func (utxoPool *UTXOPool) DeleteOneUtxo(address, txID string, index uint32) { delete(utxoPool.UtxoMap[address][txID], index) if len(utxoPool.UtxoMap[address][txID]) == 0 { delete(utxoPool.UtxoMap[address], txID) @@ -341,7 +341,7 @@ func (utxoPool *UTXOPool) DeleteOneUtxo(address, txID string, index int) { } // DeleteOneBalanceItem deletes one balance item of UTXOPool and clean up if possible. -func (utxoPool *UTXOPool) DeleteOneLockedUtxo(address, txID string, index int) { +func (utxoPool *UTXOPool) DeleteOneLockedUtxo(address, txID string, index uint32) { delete(utxoPool.LockedUtxoMap[address][txID], index) if len(utxoPool.LockedUtxoMap[address][txID]) == 0 { delete(utxoPool.LockedUtxoMap[address], txID) diff --git a/client/btctxgen/main.go b/client/btctxgen/main.go index b89c9f221..c0fb40be6 100644 --- a/client/btctxgen/main.go +++ b/client/btctxgen/main.go @@ -12,6 +12,7 @@ import ( "harmony-benchmark/node" "harmony-benchmark/p2p" proto_node "harmony-benchmark/proto/node" + "math" "sync" "time" @@ -77,8 +78,7 @@ LOOP: tx := blockchain.Transaction{} isCrossShardTx := false if btcTx.IsCoinBase() { - // TODO: merge txID with txIndex in TxInput - tx.TxInput = []blockchain.TXInput{*blockchain.NewTXInput(nil, "", nodeShardID)} + tx.TxInput = []blockchain.TXInput{*blockchain.NewTXInput(blockchain.NewOutPoint(&blockchain.Hash{}, math.MaxUint32), "", nodeShardID)} } else { for _, btcTXI := range btcTx.TxIn { btcTXIDStr := btc.NewUint256(btcTXI.Input.Hash[:]).String() @@ -86,7 +86,7 @@ LOOP: if txRef.shardID != nodeShardID { isCrossShardTx = true } - tx.TxInput = append(tx.TxInput, *blockchain.NewTXInput(blockchain.NewOutPoint(&txRef.txID, int(btcTXI.Input.Vout)), "", txRef.shardID)) + tx.TxInput = append(tx.TxInput, *blockchain.NewTXInput(blockchain.NewOutPoint(&txRef.txID, btcTXI.Input.Vout), "", txRef.shardID)) } } diff --git a/client/txgen/main.go b/client/txgen/main.go index 584a8d969..1d59a19ac 100644 --- a/client/txgen/main.go +++ b/client/txgen/main.go @@ -35,7 +35,7 @@ type TxInfo struct { dataNodes []*node.Node // Temp Input id [32]byte - index int + index uint32 value int address string // Output From 2c297dedc6e1ec4a684c15f6a69c1d0c7d4f3869 Mon Sep 17 00:00:00 2001 From: Richard Liu Date: Sun, 5 Aug 2018 01:23:34 -0700 Subject: [PATCH 3/5] fix build issue. --- blockchain/utxopool_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/blockchain/utxopool_test.go b/blockchain/utxopool_test.go index 44cfa9a7d..9fa924041 100644 --- a/blockchain/utxopool_test.go +++ b/blockchain/utxopool_test.go @@ -54,9 +54,9 @@ func TestDeleteOneBalanceItem(t *testing.T) { func TestCleanUp(t *testing.T) { var utxoPool UTXOPool - utxoPool.UtxoMap = make(map[string]map[string]map[int]int) - utxoPool.UtxoMap["minh"] = make(map[string]map[int]int) - utxoPool.UtxoMap["rj"] = map[string]map[int]int{ + utxoPool.UtxoMap = make(map[string]map[string]map[uint32]int) + utxoPool.UtxoMap["minh"] = make(map[string]map[uint32]int) + utxoPool.UtxoMap["rj"] = map[string]map[uint32]int{ "abcd": { 0: 1, }, From 6c5c207d4db77a32d63bd8a35af9ac5c49267d43 Mon Sep 17 00:00:00 2001 From: Richard Liu Date: Sun, 5 Aug 2018 01:46:29 -0700 Subject: [PATCH 4/5] update utxo types. --- blockchain/utxopool.go | 52 ++++++++++++++++++++----------------- blockchain/utxopool_test.go | 6 ++--- 2 files changed, 31 insertions(+), 27 deletions(-) diff --git a/blockchain/utxopool.go b/blockchain/utxopool.go index b91dc0a86..75ae3dcb7 100644 --- a/blockchain/utxopool.go +++ b/blockchain/utxopool.go @@ -8,6 +8,10 @@ import ( "sync" ) +type Vout2AmountMap = map[uint32]int +type TXHash2Vout2AmountMap = map[string]Vout2AmountMap +type UtxoMap = map[string]TXHash2Vout2AmountMap + // UTXOPool stores transactions and balance associated with each address. type UTXOPool struct { // Mapping from address to a map of transaction id to a map of the index of output @@ -25,9 +29,9 @@ type UTXOPool struct { ] ] */ - UtxoMap map[string]map[string]map[uint32]int - LockedUtxoMap map[string]map[string]map[uint32]int - ShardId uint32 + UtxoMap UtxoMap + LockedUtxoMap UtxoMap + ShardID uint32 mutex sync.Mutex } @@ -57,7 +61,7 @@ func (utxoPool *UTXOPool) VerifyOneTransaction(tx *Transaction, spentTXOs *map[s // Calculate the sum of TxInput for _, in := range tx.TxInput { // Only check the input for my own shard. - if in.ShardID != utxoPool.ShardId { + if in.ShardID != utxoPool.ShardID { crossShard = true continue } @@ -150,7 +154,7 @@ func (utxoPool *UTXOPool) UpdateOneTransaction(tx *Transaction) { isCrossShard := false // check whether it's a cross shard tx. for _, in := range tx.TxInput { - if in.ShardID != utxoPool.ShardId { + if in.ShardID != utxoPool.ShardID { isCrossShard = true break } @@ -161,7 +165,7 @@ func (utxoPool *UTXOPool) UpdateOneTransaction(tx *Transaction) { // Check whether for this shard this cross transaction is valid or not. for _, in := range tx.TxInput { // Only check the input for my own shard. - if in.ShardID != utxoPool.ShardId { + if in.ShardID != utxoPool.ShardID { continue } inTxID := hex.EncodeToString(in.PreviousOutPoint.Hash[:]) @@ -181,7 +185,7 @@ func (utxoPool *UTXOPool) UpdateOneTransaction(tx *Transaction) { if isValidCrossShard { for _, in := range tx.TxInput { // Only check the input for my own shard. - if in.ShardID != utxoPool.ShardId { + if in.ShardID != utxoPool.ShardID { continue } @@ -193,11 +197,11 @@ func (utxoPool *UTXOPool) UpdateOneTransaction(tx *Transaction) { // put the delete (locked) utxo into a separate locked utxo pool inTxID := hex.EncodeToString(in.PreviousOutPoint.Hash[:]) if _, ok := utxoPool.LockedUtxoMap[in.Address]; !ok { - utxoPool.LockedUtxoMap[in.Address] = make(map[string]map[uint32]int) - utxoPool.LockedUtxoMap[in.Address][inTxID] = make(map[uint32]int) + utxoPool.LockedUtxoMap[in.Address] = make(TXHash2Vout2AmountMap) + utxoPool.LockedUtxoMap[in.Address][inTxID] = make(Vout2AmountMap) } if _, ok := utxoPool.LockedUtxoMap[in.Address][inTxID]; !ok { - utxoPool.LockedUtxoMap[in.Address][inTxID] = make(map[uint32]int) + utxoPool.LockedUtxoMap[in.Address][inTxID] = make(Vout2AmountMap) } utxoPool.LockedUtxoMap[in.Address][inTxID][in.PreviousOutPoint.Index] = value } @@ -212,18 +216,18 @@ func (utxoPool *UTXOPool) UpdateOneTransaction(tx *Transaction) { // unlock-to-abort, bring back (unlock) the utxo input for _, in := range tx.TxInput { // Only unlock the input for my own shard. - if in.ShardID != utxoPool.ShardId { + if in.ShardID != utxoPool.ShardID { continue } // Simply bring back the locked (removed) utxo inTxID := hex.EncodeToString(in.PreviousOutPoint.Hash[:]) if _, ok := utxoPool.UtxoMap[in.Address]; !ok { - utxoPool.UtxoMap[in.Address] = make(map[string]map[uint32]int) - utxoPool.UtxoMap[in.Address][inTxID] = make(map[uint32]int) + utxoPool.UtxoMap[in.Address] = make(TXHash2Vout2AmountMap) + utxoPool.UtxoMap[in.Address][inTxID] = make(Vout2AmountMap) } if _, ok := utxoPool.UtxoMap[in.Address][inTxID]; !ok { - utxoPool.UtxoMap[in.Address][inTxID] = make(map[uint32]int) + utxoPool.UtxoMap[in.Address][inTxID] = make(Vout2AmountMap) } value := utxoPool.LockedUtxoMap[in.Address][inTxID][in.PreviousOutPoint.Index] utxoPool.UtxoMap[in.Address][inTxID][in.PreviousOutPoint.Index] = value @@ -235,15 +239,15 @@ func (utxoPool *UTXOPool) UpdateOneTransaction(tx *Transaction) { // normal utxo output update for index, out := range tx.TxOutput { // Only check the input for my own shard. - if out.ShardId != utxoPool.ShardId { + if out.ShardId != utxoPool.ShardID { continue } if _, ok := utxoPool.UtxoMap[out.Address]; !ok { - utxoPool.UtxoMap[out.Address] = make(map[string]map[uint32]int) - utxoPool.UtxoMap[out.Address][txID] = make(map[uint32]int) + utxoPool.UtxoMap[out.Address] = make(TXHash2Vout2AmountMap) + utxoPool.UtxoMap[out.Address][txID] = make(Vout2AmountMap) } if _, ok := utxoPool.UtxoMap[out.Address][txID]; !ok { - utxoPool.UtxoMap[out.Address][txID] = make(map[uint32]int) + utxoPool.UtxoMap[out.Address][txID] = make(Vout2AmountMap) } utxoPool.UtxoMap[out.Address][txID][uint32(index)] = out.Value } @@ -275,14 +279,14 @@ func (utxoPool *UTXOPool) VerifyAndUpdate(transactions []*Transaction) bool { func CreateUTXOPoolFromTransaction(tx *Transaction, shardId uint32) *UTXOPool { var utxoPool UTXOPool txID := hex.EncodeToString(tx.ID[:]) - utxoPool.UtxoMap = make(map[string]map[string]map[uint32]int) - utxoPool.LockedUtxoMap = make(map[string]map[string]map[uint32]int) + utxoPool.UtxoMap = make(UtxoMap) + utxoPool.LockedUtxoMap = make(UtxoMap) for index, out := range tx.TxOutput { - utxoPool.UtxoMap[out.Address] = make(map[string]map[uint32]int) - utxoPool.UtxoMap[out.Address][txID] = make(map[uint32]int) + utxoPool.UtxoMap[out.Address] = make(TXHash2Vout2AmountMap) + utxoPool.UtxoMap[out.Address][txID] = make(Vout2AmountMap) utxoPool.UtxoMap[out.Address][txID][uint32(index)] = out.Value } - utxoPool.ShardId = shardId + utxoPool.ShardID = shardId return &utxoPool } @@ -304,7 +308,7 @@ func (utxoPool *UTXOPool) SelectTransactionsForNewBlock(transactions []*Transact if valid || crossShard { selected = append(selected, tx) if crossShard { - proof := CrossShardTxProof{Accept: valid, TxID: tx.ID, TxInput: getShardTxInput(tx, utxoPool.ShardId)} + proof := CrossShardTxProof{Accept: valid, TxID: tx.ID, TxInput: getShardTxInput(tx, utxoPool.ShardID)} txAndProof := CrossShardTxAndProof{tx, &proof} crossShardTxs = append(crossShardTxs, &txAndProof) } diff --git a/blockchain/utxopool_test.go b/blockchain/utxopool_test.go index 9fa924041..4f253468a 100644 --- a/blockchain/utxopool_test.go +++ b/blockchain/utxopool_test.go @@ -54,9 +54,9 @@ func TestDeleteOneBalanceItem(t *testing.T) { func TestCleanUp(t *testing.T) { var utxoPool UTXOPool - utxoPool.UtxoMap = make(map[string]map[string]map[uint32]int) - utxoPool.UtxoMap["minh"] = make(map[string]map[uint32]int) - utxoPool.UtxoMap["rj"] = map[string]map[uint32]int{ + utxoPool.UtxoMap = make(UtxoMap) + utxoPool.UtxoMap["minh"] = make(TXHash2Vout2AmountMap) + utxoPool.UtxoMap["rj"] = TXHash2Vout2AmountMap{ "abcd": { 0: 1, }, From 932852b674e9e920fa3e4fde3e69890303d8e20f Mon Sep 17 00:00:00 2001 From: Richard Liu Date: Sun, 5 Aug 2018 02:22:59 -0700 Subject: [PATCH 5/5] rename --- blockchain/blockchain.go | 4 ++-- blockchain/transaction.go | 20 ++++++++++---------- blockchain/utxopool.go | 12 ++++++------ client/btctxgen/main.go | 2 +- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/blockchain/blockchain.go b/blockchain/blockchain.go index cb71620e2..7f2617fda 100644 --- a/blockchain/blockchain.go +++ b/blockchain/blockchain.go @@ -51,7 +51,7 @@ func (bc *Blockchain) FindUnspentTransactions(address string) []Transaction { for _, txInput := range tx.TxInput { if address == txInput.Address { - ID := hex.EncodeToString(txInput.PreviousOutPoint.Hash[:]) + ID := hex.EncodeToString(txInput.PreviousOutPoint.TxID[:]) spentTXOs[ID] = append(spentTXOs[ID], txInput.PreviousOutPoint.Index) } } @@ -120,7 +120,7 @@ func (bc *Blockchain) NewUTXOTransaction(from, to string, amount int, shardID ui return nil } - txID := Hash{} + txID := TxID{} copy(txID[:], id[:]) for _, out := range outs { input := NewTXInput(NewOutPoint(&txID, out), from, shardID) diff --git a/blockchain/transaction.go b/blockchain/transaction.go index 494e75500..fd51e2b82 100644 --- a/blockchain/transaction.go +++ b/blockchain/transaction.go @@ -14,7 +14,7 @@ var ( // zeroHash is the zero value for a Hash and is defined as // a package level variable to avoid the need to create a new instance // every time a check is needed. - zeroHash Hash + zeroHash TxID ) type Transaction struct { @@ -29,25 +29,25 @@ type Transaction struct { type TXOutput struct { Value int Address string - ShardId uint32 // The Id of the shard where this UTXO belongs + ShardID uint32 // The Id of the shard where this UTXO belongs } -type Hash = [32]byte +type TxID = [32]byte // Output defines a data type that is used to track previous // transaction outputs. -// Hash is the transaction id +// TxID is the transaction id // Index is the index of the transaction ouput in the previous transaction type OutPoint struct { - Hash Hash + TxID TxID Index uint32 } // NewOutPoint returns a new transaction outpoint point with the -// provided hash and index. -func NewOutPoint(hash *Hash, index uint32) *OutPoint { +// provided txID and index. +func NewOutPoint(txID *TxID, index uint32) *OutPoint { return &OutPoint{ - Hash: *hash, + TxID: *txID, Index: index, } } @@ -109,7 +109,7 @@ func NewCoinbaseTX(to, data string, shardID uint32) *Transaction { data = fmt.Sprintf("Reward to '%s'", to) } - txin := NewTXInput(NewOutPoint(&Hash{}, math.MaxUint32), to, shardID) + txin := NewTXInput(NewOutPoint(&TxID{}, math.MaxUint32), to, shardID) txout := TXOutput{DefaultCoinbaseValue, to, shardID} tx := Transaction{[32]byte{}, []TXInput{*txin}, []TXOutput{txout}, nil} tx.SetID() @@ -118,7 +118,7 @@ func NewCoinbaseTX(to, data string, shardID uint32) *Transaction { // Used for debuging. func (txInput *TXInput) String() string { - res := fmt.Sprintf("TxID: %v, ", hex.EncodeToString(txInput.PreviousOutPoint.Hash[:])) + res := fmt.Sprintf("TxID: %v, ", hex.EncodeToString(txInput.PreviousOutPoint.TxID[:])) res += fmt.Sprintf("TxOutputIndex: %v, ", txInput.PreviousOutPoint.Index) res += fmt.Sprintf("Address: %v, ", txInput.Address) res += fmt.Sprintf("Shard Id: %v", txInput.ShardID) diff --git a/blockchain/utxopool.go b/blockchain/utxopool.go index 75ae3dcb7..62ecb22c3 100644 --- a/blockchain/utxopool.go +++ b/blockchain/utxopool.go @@ -66,7 +66,7 @@ func (utxoPool *UTXOPool) VerifyOneTransaction(tx *Transaction, spentTXOs *map[s continue } - inTxID := hex.EncodeToString(in.PreviousOutPoint.Hash[:]) + inTxID := hex.EncodeToString(in.PreviousOutPoint.TxID[:]) index := in.PreviousOutPoint.Index // Check if the transaction with the addres is spent or not. if val, ok := (*spentTXOs)[in.Address][inTxID][index]; ok { @@ -168,7 +168,7 @@ func (utxoPool *UTXOPool) UpdateOneTransaction(tx *Transaction) { if in.ShardID != utxoPool.ShardID { continue } - inTxID := hex.EncodeToString(in.PreviousOutPoint.Hash[:]) + inTxID := hex.EncodeToString(in.PreviousOutPoint.TxID[:]) if _, ok := utxoPool.UtxoMap[in.Address][inTxID][in.PreviousOutPoint.Index]; !ok { isValidCrossShard = false } @@ -190,12 +190,12 @@ func (utxoPool *UTXOPool) UpdateOneTransaction(tx *Transaction) { } // NOTE: for the locking phase of cross tx, the utxo is simply removed from the pool. - inTxID := hex.EncodeToString(in.PreviousOutPoint.Hash[:]) + inTxID := hex.EncodeToString(in.PreviousOutPoint.TxID[:]) value := utxoPool.UtxoMap[in.Address][inTxID][in.PreviousOutPoint.Index] utxoPool.DeleteOneUtxo(in.Address, inTxID, in.PreviousOutPoint.Index) if isCrossShard { // put the delete (locked) utxo into a separate locked utxo pool - inTxID := hex.EncodeToString(in.PreviousOutPoint.Hash[:]) + inTxID := hex.EncodeToString(in.PreviousOutPoint.TxID[:]) if _, ok := utxoPool.LockedUtxoMap[in.Address]; !ok { utxoPool.LockedUtxoMap[in.Address] = make(TXHash2Vout2AmountMap) utxoPool.LockedUtxoMap[in.Address][inTxID] = make(Vout2AmountMap) @@ -221,7 +221,7 @@ func (utxoPool *UTXOPool) UpdateOneTransaction(tx *Transaction) { } // Simply bring back the locked (removed) utxo - inTxID := hex.EncodeToString(in.PreviousOutPoint.Hash[:]) + inTxID := hex.EncodeToString(in.PreviousOutPoint.TxID[:]) if _, ok := utxoPool.UtxoMap[in.Address]; !ok { utxoPool.UtxoMap[in.Address] = make(TXHash2Vout2AmountMap) utxoPool.UtxoMap[in.Address][inTxID] = make(Vout2AmountMap) @@ -239,7 +239,7 @@ func (utxoPool *UTXOPool) UpdateOneTransaction(tx *Transaction) { // normal utxo output update for index, out := range tx.TxOutput { // Only check the input for my own shard. - if out.ShardId != utxoPool.ShardID { + if out.ShardID != utxoPool.ShardID { continue } if _, ok := utxoPool.UtxoMap[out.Address]; !ok { diff --git a/client/btctxgen/main.go b/client/btctxgen/main.go index c0fb40be6..e259cdef9 100644 --- a/client/btctxgen/main.go +++ b/client/btctxgen/main.go @@ -78,7 +78,7 @@ LOOP: tx := blockchain.Transaction{} isCrossShardTx := false if btcTx.IsCoinBase() { - tx.TxInput = []blockchain.TXInput{*blockchain.NewTXInput(blockchain.NewOutPoint(&blockchain.Hash{}, math.MaxUint32), "", nodeShardID)} + tx.TxInput = []blockchain.TXInput{*blockchain.NewTXInput(blockchain.NewOutPoint(&blockchain.TxID{}, math.MaxUint32), "", nodeShardID)} } else { for _, btcTXI := range btcTx.TxIn { btcTXIDStr := btc.NewUint256(btcTXI.Input.Hash[:]).String()