Use the UTXO set to send coins

This commit is contained in:
Ivan Kuznetsov 2017-09-17 11:49:59 +07:00
parent 0b7d2ac63f
commit 3e491be4d7
4 changed files with 36 additions and 75 deletions

View File

@ -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)

View File

@ -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}

View File

@ -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
}

View File

@ -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
}