From 3e491be4d7bfd102e8c7f60c7e21767c194592d3 Mon Sep 17 00:00:00 2001 From: Ivan Kuznetsov Date: Sun, 17 Sep 2017 11:49:59 +0700 Subject: [PATCH] Use the UTXO set to send coins --- blockchain.go | 71 -------------------------------------------------- cli_send.go | 3 ++- transaction.go | 6 ++--- utxo_set.go | 31 ++++++++++++++++++++++ 4 files changed, 36 insertions(+), 75 deletions(-) diff --git a/blockchain.go b/blockchain.go index 2faa7c6..fa68abc 100644 --- a/blockchain.go +++ b/blockchain.go @@ -95,31 +95,6 @@ func NewBlockchain() *Blockchain { return &bc } -// FindSpendableOutputs finds and returns unspent outputs to reference in inputs -func (bc *Blockchain) FindSpendableOutputs(pubKeyHash []byte, amount int) (int, map[string][]int) { - unspentOutputs := make(map[string][]int) - unspentTXs := bc.FindUnspentTransactions(pubKeyHash) - accumulated := 0 - -Work: - for _, tx := range unspentTXs { - txID := hex.EncodeToString(tx.ID) - - for outIdx, out := range tx.Vout { - if out.IsLockedWithKey(pubKeyHash) && accumulated < amount { - accumulated += out.Value - unspentOutputs[txID] = append(unspentOutputs[txID], outIdx) - - if accumulated >= amount { - break Work - } - } - } - } - - return accumulated, unspentOutputs -} - // FindTransaction finds a transaction by its ID func (bc *Blockchain) FindTransaction(ID []byte) (Transaction, error) { bci := bc.Iterator() @@ -141,52 +116,6 @@ func (bc *Blockchain) FindTransaction(ID []byte) (Transaction, error) { return Transaction{}, errors.New("Transaction is not found") } -// FindUnspentTransactions returns a list of transactions containing unspent outputs -func (bc *Blockchain) FindUnspentTransactions(pubKeyHash []byte) []Transaction { - var unspentTXs []Transaction - spentTXOs := make(map[string][]int) - bci := bc.Iterator() - - for { - block := bci.Next() - - for _, tx := range block.Transactions { - txID := hex.EncodeToString(tx.ID) - - Outputs: - for outIdx, out := range tx.Vout { - // Was the output spent? - if spentTXOs[txID] != nil { - for _, spentOutIdx := range spentTXOs[txID] { - if spentOutIdx == outIdx { - continue Outputs - } - } - } - - if out.IsLockedWithKey(pubKeyHash) { - unspentTXs = append(unspentTXs, *tx) - } - } - - if tx.IsCoinbase() == false { - for _, in := range tx.Vin { - if in.UsesKey(pubKeyHash) { - inTxID := hex.EncodeToString(in.Txid) - spentTXOs[inTxID] = append(spentTXOs[inTxID], in.Vout) - } - } - } - } - - if len(block.PrevBlockHash) == 0 { - break - } - } - - return unspentTXs -} - // FindAllUTXO finds all unspent transaction outputs and returns transactions with spent outputs removed func (bc *Blockchain) FindAllUTXO() map[string]TXOutputs { UTXO := make(map[string]TXOutputs) diff --git a/cli_send.go b/cli_send.go index f9c45d6..3528611 100644 --- a/cli_send.go +++ b/cli_send.go @@ -14,9 +14,10 @@ func (cli *CLI) send(from, to string, amount int) { } bc := NewBlockchain() + UTXOSet := UTXOSet{bc} defer bc.db.Close() - tx := NewUTXOTransaction(from, to, amount, bc) + tx := NewUTXOTransaction(from, to, amount, &UTXOSet) cbTx := NewCoinbaseTX(from, "") txs := []*Transaction{cbTx, tx} diff --git a/transaction.go b/transaction.go index 90950cc..e2fac83 100644 --- a/transaction.go +++ b/transaction.go @@ -185,7 +185,7 @@ func NewCoinbaseTX(to, data string) *Transaction { } // NewUTXOTransaction creates a new transaction -func NewUTXOTransaction(from, to string, amount int, bc *Blockchain) *Transaction { +func NewUTXOTransaction(from, to string, amount int, UTXOSet *UTXOSet) *Transaction { var inputs []TXInput var outputs []TXOutput @@ -195,7 +195,7 @@ func NewUTXOTransaction(from, to string, amount int, bc *Blockchain) *Transactio } wallet := wallets.GetWallet(from) pubKeyHash := HashPubKey(wallet.PublicKey) - acc, validOutputs := bc.FindSpendableOutputs(pubKeyHash, amount) + acc, validOutputs := UTXOSet.FindSpendableOutputs(pubKeyHash, amount) if acc < amount { log.Panic("ERROR: Not enough funds") @@ -222,7 +222,7 @@ func NewUTXOTransaction(from, to string, amount int, bc *Blockchain) *Transactio tx := Transaction{nil, inputs, outputs} tx.ID = tx.Hash() - bc.SignTransaction(&tx, wallet.PrivateKey) + UTXOSet.Blockchain.SignTransaction(&tx, wallet.PrivateKey) return &tx } diff --git a/utxo_set.go b/utxo_set.go index 0ee851d..74e7fec 100644 --- a/utxo_set.go +++ b/utxo_set.go @@ -110,3 +110,34 @@ func (u UTXOSet) FindUTXO(pubKeyHash []byte) []TXOutput { return UTXOs } + +// FindSpendableOutputs finds and returns unspent outputs to reference in inputs +func (u UTXOSet) FindSpendableOutputs(pubkeyHash []byte, amount int) (int, map[string][]int) { + unspentOutputs := make(map[string][]int) + accumulated := 0 + db := u.Blockchain.db + + err := db.View(func(tx *bolt.Tx) error { + b := tx.Bucket([]byte(utxoBucket)) + c := b.Cursor() + + for k, v := c.First(); k != nil; k, v = c.Next() { + txID := hex.EncodeToString(k) + outs := DeserializeOutputs(v) + + for outIdx, out := range outs.Outputs { + if out.IsLockedWithKey(pubkeyHash) && accumulated < amount { + accumulated += out.Value + unspentOutputs[txID] = append(unspentOutputs[txID], outIdx) + } + } + } + + return nil + }) + if err != nil { + log.Panic(err) + } + + return accumulated, unspentOutputs +}