diff --git a/blockchain.go b/blockchain.go index b7115b7..ca6e9c2 100644 --- a/blockchain.go +++ b/blockchain.go @@ -58,6 +58,57 @@ func (bc *Blockchain) AddBlock(transactions []*Transaction) { }) } +// FindUTXOs finds and returns unspend transaction outputs for the address +func (bc *Blockchain) FindUTXOs(address string, amount int) (int, map[string][]int) { + var spentTXs map[string][]int + var unspentTXs map[string][]int + accumulated := 0 + bci := bc.Iterator() + + for { + block := bci.Next() + + Work: + for _, tx := range block.Transactions { + txid := string(tx.GetHash()) + + for outid, out := range tx.Vout { + // Was the output spent? + if spentTXs[txid] != nil { + for _, spentOut := range spentTXs[txid] { + if spentOut == outid { + continue + } + } + } + + if out.Unlock(address) && accumulated < amount { + accumulated += out.Value + unspentTXs[txid] = append(unspentTXs[txid], outid) + + if accumulated >= amount { + break Work + } + } + } + + if tx.IsCoinbase() == false { + for _, in := range tx.Vin { + if in.LockedBy(address) { + spentTXs[string(in.Txid)] = append(spentTXs[string(in.Txid)], in.Vout) + } + } + } + } + + if len(block.PrevBlockHash) == 0 { + break + } + } + + return accumulated, unspentTXs +} + // Iterator ... func (bc *Blockchain) Iterator() *BlockchainIterator { bci := &BlockchainIterator{bc.tip, bc.db} diff --git a/transaction.go b/transaction.go index 61fcf45..7bb21d3 100644 --- a/transaction.go +++ b/transaction.go @@ -4,6 +4,7 @@ import ( "bytes" "crypto/sha256" "encoding/gob" + "encoding/hex" "log" ) @@ -17,7 +18,7 @@ type Transaction struct { // IsCoinbase checks whether the transaction is coinbase func (tx Transaction) IsCoinbase() bool { - return len(tx.Vin) == 1 && tx.Vin[0].Txid == -1 && tx.Vin[0].Vout == -1 + return len(tx.Vin) == 1 && len(tx.Vin[0].Txid) == 0 && tx.Vin[0].Vout == -1 } // GetHash hashes the transaction and returns the hash @@ -37,7 +38,7 @@ func (tx Transaction) GetHash() []byte { // TXInput represents a transaction input type TXInput struct { - Txid int + Txid []byte Vout int ScriptSig string } @@ -48,6 +49,11 @@ type TXOutput struct { ScriptPubKey string } +// LockedBy checks whether the address initiated the transaction +func (in *TXInput) LockedBy(address string) bool { + return in.ScriptSig == address +} + // Unlock checks if the output can be unlocked with the provided data func (out *TXOutput) Unlock(unlockingData string) bool { return out.ScriptPubKey == unlockingData @@ -59,7 +65,7 @@ func NewCoinbaseTX(to, data string) *Transaction { data = "Coinbase" } - txin := TXInput{-1, -1, data} + txin := TXInput{[]byte{}, -1, data} txout := TXOutput{subsidy, to} tx := Transaction{[]TXInput{txin}, []TXOutput{txout}} @@ -67,11 +73,11 @@ func NewCoinbaseTX(to, data string) *Transaction { } // NewUTXOTransaction creates a new transaction -func NewUTXOTransaction(from, to string, value int) *Transaction { +func NewUTXOTransaction(from, to string, value int, bc *Blockchain) *Transaction { var inputs []TXInput var outputs []TXOutput - acc, validOutputs := s.findUnspentOutputs(from, value) + acc, validOutputs := bc.FindUTXOs(from, value) if acc < value { log.Panic("ERROR: Not enough funds") @@ -80,7 +86,12 @@ func NewUTXOTransaction(from, to string, value int) *Transaction { // Build a list of inputs for txid, outs := range validOutputs { for _, out := range outs { - input := TXInput{txid, out, from} + txidbytes, err := hex.DecodeString(txid) + if err != nil { + log.Panic(err) + } + + input := TXInput{txidbytes, out, from} inputs = append(inputs, input) } }