From ec228f9ff946828373234feef81d2492b6125c23 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 8 May 2017 13:38:57 -0600 Subject: [PATCH] rpctest: Add GenerateAndSubmitBlockWithCustomCoinbaseOutputs --- integration/rpctest/blockgen.go | 23 +++++--- integration/rpctest/rpc_harness.go | 23 +++++++- integration/rpctest/rpc_harness_test.go | 76 +++++++++++++++++++++++++ 3 files changed, 113 insertions(+), 9 deletions(-) diff --git a/integration/rpctest/blockgen.go b/integration/rpctest/blockgen.go index bf092f61..6c7218f7 100644 --- a/integration/rpctest/blockgen.go +++ b/integration/rpctest/blockgen.go @@ -88,7 +88,8 @@ func standardCoinbaseScript(nextBlockHeight int32, extraNonce uint64) ([]byte, e // createCoinbaseTx returns a coinbase transaction paying an appropriate // subsidy based on the passed block height to the provided address. func createCoinbaseTx(coinbaseScript []byte, nextBlockHeight int32, - addr btcutil.Address, net *chaincfg.Params) (*btcutil.Tx, error) { + addr btcutil.Address, mineTo []wire.TxOut, + net *chaincfg.Params) (*btcutil.Tx, error) { // Create the script to pay to the provided payment address. pkScript, err := txscript.PayToAddrScript(addr) @@ -105,10 +106,16 @@ func createCoinbaseTx(coinbaseScript []byte, nextBlockHeight int32, SignatureScript: coinbaseScript, Sequence: wire.MaxTxInSequenceNum, }) - tx.AddTxOut(&wire.TxOut{ - Value: blockchain.CalcBlockSubsidy(nextBlockHeight, net), - PkScript: pkScript, - }) + if len(mineTo) == 0 { + tx.AddTxOut(&wire.TxOut{ + Value: blockchain.CalcBlockSubsidy(nextBlockHeight, net), + PkScript: pkScript, + }) + } else { + for i := range mineTo { + tx.AddTxOut(&mineTo[i]) + } + } return btcutil.NewTx(tx), nil } @@ -118,8 +125,8 @@ func createCoinbaseTx(coinbaseScript []byte, nextBlockHeight int32, // second is used. Passing nil for the previous block results in a block that // builds off of the genesis block for the specified chain. func CreateBlock(prevBlock *btcutil.Block, inclusionTxs []*btcutil.Tx, - blockVersion int32, blockTime time.Time, - miningAddr btcutil.Address, net *chaincfg.Params) (*btcutil.Block, error) { + blockVersion int32, blockTime time.Time, miningAddr btcutil.Address, + mineTo []wire.TxOut, net *chaincfg.Params) (*btcutil.Block, error) { var ( prevHash *chainhash.Hash @@ -156,7 +163,7 @@ func CreateBlock(prevBlock *btcutil.Block, inclusionTxs []*btcutil.Tx, return nil, err } coinbaseTx, err := createCoinbaseTx(coinbaseScript, blockHeight, - miningAddr, net) + miningAddr, mineTo, net) if err != nil { return nil, err } diff --git a/integration/rpctest/rpc_harness.go b/integration/rpctest/rpc_harness.go index 653a3782..ea8868ce 100644 --- a/integration/rpctest/rpc_harness.go +++ b/integration/rpctest/rpc_harness.go @@ -406,6 +406,27 @@ func (h *Harness) P2PAddress() string { // This function is safe for concurrent access. func (h *Harness) GenerateAndSubmitBlock(txns []*btcutil.Tx, blockVersion int32, blockTime time.Time) (*btcutil.Block, error) { + return h.GenerateAndSubmitBlockWithCustomCoinbaseOutputs(txns, + blockVersion, blockTime, []wire.TxOut{}) +} + +// GenerateAndSubmitBlockWithCustomCoinbaseOutputs creates a block whose +// contents include the passed coinbase outputs and transactions and submits +// it to the running simnet node. For generating blocks with only a coinbase tx, +// callers can simply pass nil instead of transactions to be mined. +// Additionally, a custom block version can be set by the caller. A blockVersion +// of -1 indicates that the current default block version should be used. An +// uninitialized time.Time should be used for the blockTime parameter if one +// doesn't wish to set a custom time. The mineTo list of outputs will be added +// to the coinbase; this is not checked for correctness until the block is +// submitted; thus, it is the caller's responsibility to ensure that the outputs +// are correct. If the list is empty, the coinbase reward goes to the wallet +// managed by the Harness. +// +// This function is safe for concurrent access. +func (h *Harness) GenerateAndSubmitBlockWithCustomCoinbaseOutputs( + txns []*btcutil.Tx, blockVersion int32, blockTime time.Time, + mineTo []wire.TxOut) (*btcutil.Block, error) { h.Lock() defer h.Unlock() @@ -427,7 +448,7 @@ func (h *Harness) GenerateAndSubmitBlock(txns []*btcutil.Tx, blockVersion int32, // Create a new block including the specified transactions newBlock, err := CreateBlock(prevBlock, txns, blockVersion, - blockTime, h.wallet.coinbaseAddr, h.ActiveNet) + blockTime, h.wallet.coinbaseAddr, mineTo, h.ActiveNet) if err != nil { return nil, err } diff --git a/integration/rpctest/rpc_harness_test.go b/integration/rpctest/rpc_harness_test.go index c3fc18b4..20797d38 100644 --- a/integration/rpctest/rpc_harness_test.go +++ b/integration/rpctest/rpc_harness_test.go @@ -391,6 +391,81 @@ func testGenerateAndSubmitBlock(r *Harness, t *testing.T) { } } +func testGenerateAndSubmitBlockWithCustomCoinbaseOutputs(r *Harness, + t *testing.T) { + // Generate a few test spend transactions. + addr, err := r.NewAddress() + if err != nil { + t.Fatalf("unable to generate new address: %v", err) + } + pkScript, err := txscript.PayToAddrScript(addr) + if err != nil { + t.Fatalf("unable to create script: %v", err) + } + output := wire.NewTxOut(btcutil.SatoshiPerBitcoin, pkScript) + + const numTxns = 5 + txns := make([]*btcutil.Tx, 0, numTxns) + for i := 0; i < numTxns; i++ { + tx, err := r.CreateTransaction([]*wire.TxOut{output}, 10) + if err != nil { + t.Fatalf("unable to create tx: %v", err) + } + + txns = append(txns, btcutil.NewTx(tx)) + } + + // Now generate a block with the default block version, a zero'd out + // time, and a burn output. + block, err := r.GenerateAndSubmitBlockWithCustomCoinbaseOutputs(txns, + -1, time.Time{}, []wire.TxOut{{ + Value: 0, + PkScript: []byte{}, + }}) + if err != nil { + t.Fatalf("unable to generate block: %v", err) + } + + // Ensure that all created transactions were included, and that the + // block version was properly set to the default. + numBlocksTxns := len(block.Transactions()) + if numBlocksTxns != numTxns+1 { + t.Fatalf("block did not include all transactions: "+ + "expected %v, got %v", numTxns+1, numBlocksTxns) + } + blockVersion := block.MsgBlock().Header.Version + if blockVersion != BlockVersion { + t.Fatalf("block version is not default: expected %v, got %v", + BlockVersion, blockVersion) + } + + // Next generate a block with a "non-standard" block version along with + // time stamp a minute after the previous block's timestamp. + timestamp := block.MsgBlock().Header.Timestamp.Add(time.Minute) + targetBlockVersion := int32(1337) + block, err = r.GenerateAndSubmitBlockWithCustomCoinbaseOutputs(nil, + targetBlockVersion, timestamp, []wire.TxOut{{ + Value: 0, + PkScript: []byte{}, + }}) + if err != nil { + t.Fatalf("unable to generate block: %v", err) + } + + // Finally ensure that the desired block version and timestamp were set + // properly. + header := block.MsgBlock().Header + blockVersion = header.Version + if blockVersion != targetBlockVersion { + t.Fatalf("block version mismatch: expected %v, got %v", + targetBlockVersion, blockVersion) + } + if !timestamp.Equal(header.Timestamp) { + t.Fatalf("header time stamp mismatch: expected %v, got %v", + timestamp, header.Timestamp) + } +} + func testMemWalletReorg(r *Harness, t *testing.T) { // Create a fresh harness, we'll be using the main harness to force a // re-org on this local harness. @@ -478,6 +553,7 @@ var harnessTestCases = []HarnessTestCase{ testJoinBlocks, testJoinMempools, // Depends on results of testJoinBlocks testGenerateAndSubmitBlock, + testGenerateAndSubmitBlockWithCustomCoinbaseOutputs, testMemWalletReorg, testMemWalletLockedOutputs, }