rpctest: Add GenerateAndSubmitBlockWithCustomCoinbaseOutputs

This commit is contained in:
Alex 2017-05-08 13:38:57 -06:00 committed by Olaoluwa Osuntokun
parent 9b9ef42f8a
commit ec228f9ff9
3 changed files with 113 additions and 9 deletions

View file

@ -88,7 +88,8 @@ func standardCoinbaseScript(nextBlockHeight int32, extraNonce uint64) ([]byte, e
// createCoinbaseTx returns a coinbase transaction paying an appropriate // createCoinbaseTx returns a coinbase transaction paying an appropriate
// subsidy based on the passed block height to the provided address. // subsidy based on the passed block height to the provided address.
func createCoinbaseTx(coinbaseScript []byte, nextBlockHeight int32, 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. // Create the script to pay to the provided payment address.
pkScript, err := txscript.PayToAddrScript(addr) pkScript, err := txscript.PayToAddrScript(addr)
@ -105,10 +106,16 @@ func createCoinbaseTx(coinbaseScript []byte, nextBlockHeight int32,
SignatureScript: coinbaseScript, SignatureScript: coinbaseScript,
Sequence: wire.MaxTxInSequenceNum, Sequence: wire.MaxTxInSequenceNum,
}) })
if len(mineTo) == 0 {
tx.AddTxOut(&wire.TxOut{ tx.AddTxOut(&wire.TxOut{
Value: blockchain.CalcBlockSubsidy(nextBlockHeight, net), Value: blockchain.CalcBlockSubsidy(nextBlockHeight, net),
PkScript: pkScript, PkScript: pkScript,
}) })
} else {
for i := range mineTo {
tx.AddTxOut(&mineTo[i])
}
}
return btcutil.NewTx(tx), nil 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 // second is used. Passing nil for the previous block results in a block that
// builds off of the genesis block for the specified chain. // builds off of the genesis block for the specified chain.
func CreateBlock(prevBlock *btcutil.Block, inclusionTxs []*btcutil.Tx, func CreateBlock(prevBlock *btcutil.Block, inclusionTxs []*btcutil.Tx,
blockVersion int32, blockTime time.Time, blockVersion int32, blockTime time.Time, miningAddr btcutil.Address,
miningAddr btcutil.Address, net *chaincfg.Params) (*btcutil.Block, error) { mineTo []wire.TxOut, net *chaincfg.Params) (*btcutil.Block, error) {
var ( var (
prevHash *chainhash.Hash prevHash *chainhash.Hash
@ -156,7 +163,7 @@ func CreateBlock(prevBlock *btcutil.Block, inclusionTxs []*btcutil.Tx,
return nil, err return nil, err
} }
coinbaseTx, err := createCoinbaseTx(coinbaseScript, blockHeight, coinbaseTx, err := createCoinbaseTx(coinbaseScript, blockHeight,
miningAddr, net) miningAddr, mineTo, net)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -406,6 +406,27 @@ func (h *Harness) P2PAddress() string {
// This function is safe for concurrent access. // This function is safe for concurrent access.
func (h *Harness) GenerateAndSubmitBlock(txns []*btcutil.Tx, blockVersion int32, func (h *Harness) GenerateAndSubmitBlock(txns []*btcutil.Tx, blockVersion int32,
blockTime time.Time) (*btcutil.Block, error) { 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() h.Lock()
defer h.Unlock() defer h.Unlock()
@ -427,7 +448,7 @@ func (h *Harness) GenerateAndSubmitBlock(txns []*btcutil.Tx, blockVersion int32,
// Create a new block including the specified transactions // Create a new block including the specified transactions
newBlock, err := CreateBlock(prevBlock, txns, blockVersion, newBlock, err := CreateBlock(prevBlock, txns, blockVersion,
blockTime, h.wallet.coinbaseAddr, h.ActiveNet) blockTime, h.wallet.coinbaseAddr, mineTo, h.ActiveNet)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -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) { func testMemWalletReorg(r *Harness, t *testing.T) {
// Create a fresh harness, we'll be using the main harness to force a // Create a fresh harness, we'll be using the main harness to force a
// re-org on this local harness. // re-org on this local harness.
@ -478,6 +553,7 @@ var harnessTestCases = []HarnessTestCase{
testJoinBlocks, testJoinBlocks,
testJoinMempools, // Depends on results of testJoinBlocks testJoinMempools, // Depends on results of testJoinBlocks
testGenerateAndSubmitBlock, testGenerateAndSubmitBlock,
testGenerateAndSubmitBlockWithCustomCoinbaseOutputs,
testMemWalletReorg, testMemWalletReorg,
testMemWalletLockedOutputs, testMemWalletLockedOutputs,
} }