Skip to content

test: transactions integrated with blocks and blockchain #8

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions internal/blockchain/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package blockchain

import (
"bytes"
"encoding/hex"
"github.com/NicholasRodrigues/go-chain/internal/transactions"
)

Expand Down Expand Up @@ -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
}
17 changes: 17 additions & 0 deletions internal/transactions/transactions.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,3 +166,20 @@

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

Check failure on line 172 in internal/transactions/transactions.go

View workflow job for this annotation

GitHub Actions / Lint

S1004: should use bytes.Equal([]byte(out.ScriptPubKey), pubKeyHash) instead (gosimple)

Check failure on line 172 in internal/transactions/transactions.go

View workflow job for this annotation

GitHub Actions / Style

should use bytes.Equal([]byte(out.ScriptPubKey), pubKeyHash) instead (S1004)
}

// 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

Check failure on line 178 in internal/transactions/transactions.go

View workflow job for this annotation

GitHub Actions / Lint

S1004: should use bytes.Equal(lockingHash, pubKeyHash) instead (gosimple)

Check failure on line 178 in internal/transactions/transactions.go

View workflow job for this annotation

GitHub Actions / Style

should use bytes.Equal(lockingHash, pubKeyHash) instead (S1004)
}

// HashPubKey hashes public key
func HashPubKey(pubKey []byte) []byte {
publicSHA256 := sha256.Sum256(pubKey)
return publicSHA256[:]
}
Loading