package blockchain import ( "encoding/hex" ) // 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 // array in that transaction to that balance. utxo map[string]map[string]int } // VerifyTransactions verifies if a list of transactions valid. func (utxopool *UTXOPool) VerifyTransactions(transactions []*Transaction) bool { spentTXOs := make(map[string]map[string]bool) if utxopool != nil { for _, tx := range transactions { inTotal := 0 // Calculate the sum of TxInput for _, in := range tx.TxInput { inTxID := hex.EncodeToString(in.TxID) // Check if the transaction with the addres is spent or not. if val, ok := spentTXOs[in.Address][inTxID]; ok { if val { return false } } // Mark the transactions with the addres spent. if _, ok := spentTXOs[in.Address]; ok { spentTXOs[in.Address][inTxID] = true } else { spentTXOs[in.Address] = make(map[string]bool) spentTXOs[in.Address][inTxID] = true } // Sum the balance up to the inTotal. if val, ok := utxopool.utxo[in.Address][inTxID]; ok { inTotal += val } else { return false } } outTotal := 0 // Calculate the sum of TxOutput for _, out := range tx.TxOutput { outTotal += out.Value } if inTotal != outTotal { return false } } } return true } // VerifyAndUpdate verifies a list of transactions and update utxoPool. func (utxopool *UTXOPool) VerifyAndUpdate(transactions []*Transaction) bool { if utxopool.VerifyTransactions(transactions) { utxopool.Update(transactions) return true } return false } // Update utxo balances with a list of new transactions. func (utxopool *UTXOPool) Update(transactions []*Transaction) { if utxopool != nil { for _, tx := range transactions { curTxID := hex.EncodeToString(tx.ID) // Remove for _, in := range tx.TxInput { inTxID := hex.EncodeToString(in.TxID) delete(utxopool.utxo[in.Address], inTxID) } // Update for _, out := range tx.TxOutput { if _, ok := utxopool.utxo[out.Address]; ok { utxopool.utxo[out.Address][curTxID] = out.Value } else { utxopool.utxo[out.Address] = make(map[string]int) utxopool.utxo[out.Address][curTxID] = out.Value } } } } } // CreateUTXOPoolFromTransaction a utxo pool from a genesis transaction. func CreateUTXOPoolFromTransaction(tx *Transaction) *UTXOPool { var utxoPool UTXOPool txID := hex.EncodeToString(tx.ID) utxoPool.utxo = make(map[string]map[string]int) for _, out := range tx.TxOutput { utxoPool.utxo[out.Address] = make(map[string]int) utxoPool.utxo[out.Address][txID] = DefaultCoinbaseValue } return &utxoPool } // CreateUTXOPoolFromGenesisBlockChain a utxo pool from a genesis blockchain. func CreateUTXOPoolFromGenesisBlockChain(bc *Blockchain) *UTXOPool { tx := bc.blocks[0].Transactions[0] var utxoPool UTXOPool txID := hex.EncodeToString(tx.ID) utxoPool.utxo = make(map[string]map[string]int) for _, out := range tx.TxOutput { utxoPool.utxo[out.Address] = make(map[string]int) utxoPool.utxo[out.Address][txID] = DefaultCoinbaseValue } return &utxoPool }