Use the UTXO set to send coins
This commit is contained in:
parent
0b7d2ac63f
commit
3e491be4d7
|
@ -95,31 +95,6 @@ func NewBlockchain() *Blockchain {
|
||||||
return &bc
|
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
|
// FindTransaction finds a transaction by its ID
|
||||||
func (bc *Blockchain) FindTransaction(ID []byte) (Transaction, error) {
|
func (bc *Blockchain) FindTransaction(ID []byte) (Transaction, error) {
|
||||||
bci := bc.Iterator()
|
bci := bc.Iterator()
|
||||||
|
@ -141,52 +116,6 @@ func (bc *Blockchain) FindTransaction(ID []byte) (Transaction, error) {
|
||||||
return Transaction{}, errors.New("Transaction is not found")
|
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
|
// FindAllUTXO finds all unspent transaction outputs and returns transactions with spent outputs removed
|
||||||
func (bc *Blockchain) FindAllUTXO() map[string]TXOutputs {
|
func (bc *Blockchain) FindAllUTXO() map[string]TXOutputs {
|
||||||
UTXO := make(map[string]TXOutputs)
|
UTXO := make(map[string]TXOutputs)
|
||||||
|
|
|
@ -14,9 +14,10 @@ func (cli *CLI) send(from, to string, amount int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bc := NewBlockchain()
|
bc := NewBlockchain()
|
||||||
|
UTXOSet := UTXOSet{bc}
|
||||||
defer bc.db.Close()
|
defer bc.db.Close()
|
||||||
|
|
||||||
tx := NewUTXOTransaction(from, to, amount, bc)
|
tx := NewUTXOTransaction(from, to, amount, &UTXOSet)
|
||||||
cbTx := NewCoinbaseTX(from, "")
|
cbTx := NewCoinbaseTX(from, "")
|
||||||
txs := []*Transaction{cbTx, tx}
|
txs := []*Transaction{cbTx, tx}
|
||||||
|
|
||||||
|
|
|
@ -185,7 +185,7 @@ func NewCoinbaseTX(to, data string) *Transaction {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewUTXOTransaction creates a new 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 inputs []TXInput
|
||||||
var outputs []TXOutput
|
var outputs []TXOutput
|
||||||
|
|
||||||
|
@ -195,7 +195,7 @@ func NewUTXOTransaction(from, to string, amount int, bc *Blockchain) *Transactio
|
||||||
}
|
}
|
||||||
wallet := wallets.GetWallet(from)
|
wallet := wallets.GetWallet(from)
|
||||||
pubKeyHash := HashPubKey(wallet.PublicKey)
|
pubKeyHash := HashPubKey(wallet.PublicKey)
|
||||||
acc, validOutputs := bc.FindSpendableOutputs(pubKeyHash, amount)
|
acc, validOutputs := UTXOSet.FindSpendableOutputs(pubKeyHash, amount)
|
||||||
|
|
||||||
if acc < amount {
|
if acc < amount {
|
||||||
log.Panic("ERROR: Not enough funds")
|
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 := Transaction{nil, inputs, outputs}
|
||||||
tx.ID = tx.Hash()
|
tx.ID = tx.Hash()
|
||||||
bc.SignTransaction(&tx, wallet.PrivateKey)
|
UTXOSet.Blockchain.SignTransaction(&tx, wallet.PrivateKey)
|
||||||
|
|
||||||
return &tx
|
return &tx
|
||||||
}
|
}
|
||||||
|
|
31
utxo_set.go
31
utxo_set.go
|
@ -110,3 +110,34 @@ func (u UTXOSet) FindUTXO(pubKeyHash []byte) []TXOutput {
|
||||||
|
|
||||||
return UTXOs
|
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
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue