|
|
|
@ -1,5 +1,9 @@ |
|
|
|
|
package main |
|
|
|
|
|
|
|
|
|
import ( |
|
|
|
|
"encoding/hex" |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
// Blockchain keeps a sequence of Blocks
|
|
|
|
|
type Blockchain struct { |
|
|
|
|
blocks []*Block |
|
|
|
@ -18,3 +22,76 @@ func (bc *Blockchain) AddBlock(data string) { |
|
|
|
|
func NewBlockchain() *Blockchain { |
|
|
|
|
return &Blockchain{[]*Block{NewGenesisBlock()}} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// FindUnspentTransactions returns a list of transactions containing unspent outputs
|
|
|
|
|
func (bc *Blockchain) FindUnspentTransactions(address string) []Transaction { |
|
|
|
|
var unspentTXs []Transaction |
|
|
|
|
spentTXOs := make(map[string][]int) |
|
|
|
|
|
|
|
|
|
for index := len(bc.blocks); index >= 0; index-- { |
|
|
|
|
block := bc.blocks[index]; |
|
|
|
|
|
|
|
|
|
BreakTransaction: |
|
|
|
|
for _, tx := range block.Transactions { |
|
|
|
|
txId := hex.EncodeToString(tx.Id) |
|
|
|
|
|
|
|
|
|
idx := -1 |
|
|
|
|
if spentTXOs[txId] != nil { |
|
|
|
|
idx = 0 |
|
|
|
|
} |
|
|
|
|
for outIdx, txOutput := range tx.txOutput { |
|
|
|
|
if idx >= 0 spentTXOs[txId][idx] == outIdx { |
|
|
|
|
continue |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if txOutput.address == address { |
|
|
|
|
unspentTXs = append(unspentTXs, *tx) |
|
|
|
|
continue BreakTransaction |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return unspentTXs |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// FindUTXO finds and returns all unspent transaction outputs
|
|
|
|
|
func (bc *Blockchain) FindUTXO(address string) []TXOutput { |
|
|
|
|
var UTXOs []TXOutput |
|
|
|
|
unspentTXs := bc.FindUnspentTransactions(address) |
|
|
|
|
|
|
|
|
|
for _, tx := range unspentTXs { |
|
|
|
|
for _, txOutput := range tx.txOutput { |
|
|
|
|
if txOutput.address == address { |
|
|
|
|
UTXOs = append(UTXOs, txOutput) |
|
|
|
|
break |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return UTXOs |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// FindSpendableOutputs finds and returns unspent outputs to reference in inputs
|
|
|
|
|
func (bc *Blockchain) FindSpendableOutputs(address string, amount int) (int, map[string][]int) { |
|
|
|
|
unspentOutputs := make(map[string][]int) |
|
|
|
|
unspentTXs := bc.FindUnspentTransactions(address) |
|
|
|
|
accumulated := 0 |
|
|
|
|
|
|
|
|
|
Work: |
|
|
|
|
for _, tx := range unspentTXs { |
|
|
|
|
txID := hex.EncodeToString(tx.ID) |
|
|
|
|
|
|
|
|
|
for outIdx, txOutput := range tx.txOutput { |
|
|
|
|
if txOutput.address == address && accumulated < amount { |
|
|
|
|
accumulated += txOutput.value |
|
|
|
|
unspentOutputs[txID] = append(unspentOutputs[txID], outIdx) |
|
|
|
|
|
|
|
|
|
if accumulated >= amount { |
|
|
|
|
break Work |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return accumulated, unspentOutputs |
|
|
|
|
} |
|
|
|
|