// createCoinbaseTx returns a coinbase transaction paying an appropriate subsidy // based on the passed block height to the provided address. When the address // is nil, the coinbase transaction will instead be redeemable by anyone. // // See the comment for NewBlockTemplate for more information about why the nil // address handling is useful. func createCoinbaseTx(coinbaseScript []byte, nextBlockHeight int64, addr btcutil.Address) (*btcutil.Tx, error) { // Create the script to pay to the provided payment address if one was // specified. Otherwise create a script that allows the coinbase to be // redeemable by anyone. var pkScript []byte if addr != nil { var err error pkScript, err = txscript.PayToAddrScript(addr) if err != nil { return nil, err } } else { var err error scriptBuilder := txscript.NewScriptBuilder() pkScript, err = scriptBuilder.AddOp(txscript.OP_TRUE).Script() if err != nil { return nil, err } } tx := btcwire.NewMsgTx() tx.AddTxIn(&btcwire.TxIn{ // Coinbase transactions have no inputs, so previous outpoint is // zero hash and max index. PreviousOutPoint: *btcwire.NewOutPoint(&btcwire.ShaHash{}, btcwire.MaxPrevOutIndex), SignatureScript: coinbaseScript, Sequence: btcwire.MaxTxInSequenceNum, }) tx.AddTxOut(&btcwire.TxOut{ Value: blockchain.CalcBlockSubsidy(nextBlockHeight, activeNetParams.Params), PkScript: pkScript, }) return btcutil.NewTx(tx), nil }
// TestCheckSerializedHeight tests the checkSerializedHeight function with // various serialized heights and also does negative tests to ensure errors // and handled properly. func TestCheckSerializedHeight(t *testing.T) { // Create an empty coinbase template to be used in the tests below. coinbaseOutpoint := btcwire.NewOutPoint(&btcwire.ShaHash{}, math.MaxUint32) coinbaseTx := btcwire.NewMsgTx() coinbaseTx.Version = 2 coinbaseTx.AddTxIn(btcwire.NewTxIn(coinbaseOutpoint, nil)) // Expected rule errors. missingHeightError := blockchain.RuleError{ ErrorCode: blockchain.ErrMissingCoinbaseHeight, } badHeightError := blockchain.RuleError{ ErrorCode: blockchain.ErrBadCoinbaseHeight, } tests := []struct { sigScript []byte // Serialized data wantHeight int64 // Expected height err error // Expected error type }{ // No serialized height length. {[]byte{}, 0, missingHeightError}, // Serialized height length with no height bytes. {[]byte{0x02}, 0, missingHeightError}, // Serialized height length with too few height bytes. {[]byte{0x02, 0x4a}, 0, missingHeightError}, // Serialized height that needs 2 bytes to encode. {[]byte{0x02, 0x4a, 0x52}, 21066, nil}, // Serialized height that needs 2 bytes to encode, but backwards // endianness. {[]byte{0x02, 0x4a, 0x52}, 19026, badHeightError}, // Serialized height that needs 3 bytes to encode. {[]byte{0x03, 0x40, 0x0d, 0x03}, 200000, nil}, // Serialized height that needs 3 bytes to encode, but backwards // endianness. {[]byte{0x03, 0x40, 0x0d, 0x03}, 1074594560, badHeightError}, } t.Logf("Running %d tests", len(tests)) for i, test := range tests { msgTx := coinbaseTx.Copy() msgTx.TxIn[0].SignatureScript = test.sigScript tx := btcutil.NewTx(msgTx) err := blockchain.TstCheckSerializedHeight(tx, test.wantHeight) if reflect.TypeOf(err) != reflect.TypeOf(test.err) { t.Errorf("checkSerializedHeight #%d wrong error type "+ "got: %v <%T>, want: %T", i, err, err, test.err) continue } if rerr, ok := err.(blockchain.RuleError); ok { trerr := test.err.(blockchain.RuleError) if rerr.ErrorCode != trerr.ErrorCode { t.Errorf("checkSerializedHeight #%d wrong "+ "error code got: %v, want: %v", i, rerr.ErrorCode, trerr.ErrorCode) continue } } } }