From ea39fe090dddf45e3a91aaecd0dd6323ed5e5dc2 Mon Sep 17 00:00:00 2001 From: Calvin Kim Date: Tue, 2 Apr 2024 17:52:34 +0900 Subject: [PATCH] blockchain: add block generating functions in test code The block generating functions here allow for a test to create mock blocks. This is useful for testing invalidateblock and reconsiderblock methods on blockchain that will be added in later commits. --- blockchain/common_test.go | 94 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/blockchain/common_test.go b/blockchain/common_test.go index 5037c182..12badd3e 100644 --- a/blockchain/common_test.go +++ b/blockchain/common_test.go @@ -14,6 +14,7 @@ import ( "strings" "time" + "github.com/btcsuite/btcd/blockchain/internal/testhelper" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg/chainhash" @@ -396,3 +397,96 @@ func newFakeNode(parent *blockNode, blockVersion int32, bits uint32, timestamp t } return newBlockNode(header, parent) } + +// addBlock adds a block to the blockchain that succeeds the previous block. +// The blocks spends all the provided spendable outputs. The new block and +// the new spendable outputs created in the block are returned. +func addBlock(chain *BlockChain, prev *btcutil.Block, spends []*testhelper.SpendableOut) ( + *btcutil.Block, []*testhelper.SpendableOut, error) { + + block, outs, err := newBlock(chain, prev, spends) + if err != nil { + return nil, nil, err + } + + _, _, err = chain.ProcessBlock(block, BFNone) + if err != nil { + return nil, nil, err + } + + return block, outs, nil +} + +// calcMerkleRoot creates a merkle tree from the slice of transactions and +// returns the root of the tree. +func calcMerkleRoot(txns []*wire.MsgTx) chainhash.Hash { + if len(txns) == 0 { + return chainhash.Hash{} + } + + utilTxns := make([]*btcutil.Tx, 0, len(txns)) + for _, tx := range txns { + utilTxns = append(utilTxns, btcutil.NewTx(tx)) + } + return CalcMerkleRoot(utilTxns, false) +} + +// newBlock creates a block to the blockchain that succeeds the previous block. +// The blocks spends all the provided spendable outputs. The new block and the +// newly spendable outputs created in the block are returned. +func newBlock(chain *BlockChain, prev *btcutil.Block, + spends []*testhelper.SpendableOut) (*btcutil.Block, []*testhelper.SpendableOut, error) { + + blockHeight := prev.Height() + 1 + txns := make([]*wire.MsgTx, 0, 1+len(spends)) + + // Create and add coinbase tx. + cb := testhelper.CreateCoinbaseTx(blockHeight, CalcBlockSubsidy(blockHeight, chain.chainParams)) + txns = append(txns, cb) + + // Spend all txs to be spent. + for _, spend := range spends { + cb.TxOut[0].Value += int64(testhelper.LowFee) + + spendTx := testhelper.CreateSpendTx(spend, testhelper.LowFee) + txns = append(txns, spendTx) + } + + // Use a timestamp that is one second after the previous block unless + // this is the first block in which case the current time is used. + var ts time.Time + if blockHeight == 1 { + ts = time.Unix(time.Now().Unix(), 0) + } else { + ts = prev.MsgBlock().Header.Timestamp.Add(time.Second) + } + + // Create the block. The nonce will be solved in the below code in + // SolveBlock. + block := btcutil.NewBlock(&wire.MsgBlock{ + Header: wire.BlockHeader{ + Version: 1, + PrevBlock: *prev.Hash(), + MerkleRoot: calcMerkleRoot(txns), + Bits: chain.chainParams.PowLimitBits, + Timestamp: ts, + Nonce: 0, // To be solved. + }, + Transactions: txns, + }) + block.SetHeight(blockHeight) + + // Solve the block. + if !testhelper.SolveBlock(&block.MsgBlock().Header) { + return nil, nil, fmt.Errorf("Unable to solve block at height %d", blockHeight) + } + + // Create spendable outs to return. + outs := make([]*testhelper.SpendableOut, len(txns)) + for i, tx := range txns { + out := testhelper.MakeSpendableOutForTx(tx, 0) + outs[i] = &out + } + + return block, outs, nil +}