From 8efe9e2c0aa16288fb34a3c5465766ee4a3ca3c1 Mon Sep 17 00:00:00 2001 From: Nicholas Rodrigues Date: Thu, 23 May 2024 16:16:48 -0300 Subject: [PATCH] test: transactions integrated with blocks and blockchain --- internal/blockchain/blockchain.go | 57 +++++++++++++++++++++++++++ internal/transactions/transactions.go | 17 ++++++++ 2 files changed, 74 insertions(+) diff --git a/internal/blockchain/blockchain.go b/internal/blockchain/blockchain.go index ba7bd82..d0aa126 100644 --- a/internal/blockchain/blockchain.go +++ b/internal/blockchain/blockchain.go @@ -2,6 +2,7 @@ package blockchain import ( "bytes" + "encoding/hex" "github.com/NicholasRodrigues/go-chain/internal/transactions" ) @@ -52,3 +53,59 @@ func (bc *Blockchain) IsValid() bool { return true } + +// FindUnspentTransactions returns a list of transactions containing unspent outputs for an address +func (bc *Blockchain) FindUnspentTransactions(pubKeyHash []byte) []transactions.Transaction { + var unspentTxs []transactions.Transaction + spentTXOs := make(map[string][]int) + + for _, block := range bc.Blocks { + for _, tx := range block.Transactions { + txID := hex.EncodeToString(tx.ID) + + Outputs: + for outIdx, out := range tx.Vout { + // Check if the output was already spent + if spentTXOs[txID] != nil { + for _, spentOutIdx := range spentTXOs[txID] { + if spentOutIdx == outIdx { + continue Outputs + } + } + } + + if out.IsLockedWithKey(pubKeyHash) { + unspentTxs = append(unspentTxs, *tx) + } + } + + // Collect spent outputs + if !tx.IsCoinbase() { + for _, in := range tx.Vin { + if in.UsesKey(pubKeyHash) { + inTxID := hex.EncodeToString(in.Txid) + spentTXOs[inTxID] = append(spentTXOs[inTxID], in.Vout) + } + } + } + } + } + + return unspentTxs +} + +// FindUTXO returns unspent transaction outputs for a given address +func (bc *Blockchain) FindUTXO(pubKeyHash []byte) []transactions.TransactionOutput { + var UTXOs []transactions.TransactionOutput + unspentTransactions := bc.FindUnspentTransactions(pubKeyHash) + + for _, tx := range unspentTransactions { + for _, out := range tx.Vout { + if out.IsLockedWithKey(pubKeyHash) { + UTXOs = append(UTXOs, out) + } + } + } + + return UTXOs +} diff --git a/internal/transactions/transactions.go b/internal/transactions/transactions.go index 1d53628..730f205 100644 --- a/internal/transactions/transactions.go +++ b/internal/transactions/transactions.go @@ -166,3 +166,20 @@ func (tx *Transaction) String() string { return strings.Join(lines, "\n") } + +// IsLockedWithKey checks if the output can be used by the owner of the pubKeyHash +func (out *TransactionOutput) IsLockedWithKey(pubKeyHash []byte) bool { + return bytes.Compare([]byte(out.ScriptPubKey), pubKeyHash) == 0 +} + +// UsesKey checks if the input uses the pubKeyHash to unlock the output +func (in *TransactionInput) UsesKey(pubKeyHash []byte) bool { + lockingHash := HashPubKey(in.PubKey) + return bytes.Compare(lockingHash, pubKeyHash) == 0 +} + +// HashPubKey hashes public key +func HashPubKey(pubKey []byte) []byte { + publicSHA256 := sha256.Sum256(pubKey) + return publicSHA256[:] +}