// TestTx tests the API for Tx. func TestTx(t *testing.T) { testTx := Block100000.Transactions[0] tx := btcutil.NewTx(testTx) // Ensure we get the same data back out. if msgTx := tx.MsgTx(); !reflect.DeepEqual(msgTx, testTx) { t.Errorf("MsgTx: mismatched MsgTx - got %v, want %v", spew.Sdump(msgTx), spew.Sdump(testTx)) } // Ensure transaction index set and get work properly. wantIndex := 0 tx.SetIndex(0) if gotIndex := tx.Index(); gotIndex != wantIndex { t.Errorf("Index: mismatched index - got %v, want %v", gotIndex, wantIndex) } // Hash for block 100,000 transaction 0. wantShaStr := "8c14f0db3df150123e6f3dbbf30f8b955a8249b62ac1d1ff16284aefa3d06d87" wantSha, err := wire.NewShaHashFromStr(wantShaStr) if err != nil { t.Errorf("NewShaHashFromStr: %v", err) } // Request the sha multiple times to test generation and caching. for i := 0; i < 2; i++ { sha := tx.Sha() if !sha.IsEqual(wantSha) { t.Errorf("Sha #%d mismatched sha - got %v, want %v", i, sha, wantSha) } } }
// fetchTxStoreMain fetches transaction data about the provided set of // transactions from the point of view of the end of the main chain. It takes // a flag which specifies whether or not fully spent transaction should be // included in the results. func fetchTxStoreMain(db database.Db, txSet map[wire.ShaHash]struct{}, includeSpent bool) TxStore { // Just return an empty store now if there are no requested hashes. txStore := make(TxStore) if len(txSet) == 0 { return txStore } // The transaction store map needs to have an entry for every requested // transaction. By default, all the transactions are marked as missing. // Each entry will be filled in with the appropriate data below. txList := make([]*wire.ShaHash, 0, len(txSet)) for hash := range txSet { hashCopy := hash txStore[hash] = &TxData{Hash: &hashCopy, Err: database.ErrTxShaMissing} txList = append(txList, &hashCopy) } // Ask the database (main chain) for the list of transactions. This // will return the information from the point of view of the end of the // main chain. Choose whether or not to include fully spent // transactions depending on the passed flag. var txReplyList []*database.TxListReply if includeSpent { txReplyList = db.FetchTxByShaList(txList) } else { txReplyList = db.FetchUnSpentTxByShaList(txList) } for _, txReply := range txReplyList { // Lookup the existing results entry to modify. Skip // this reply if there is no corresponding entry in // the transaction store map which really should not happen, but // be safe. txD, ok := txStore[*txReply.Sha] if !ok { continue } // Fill in the transaction details. A copy is used here since // there is no guarantee the returned data isn't cached and // this code modifies the data. A bug caused by modifying the // cached data would likely be difficult to track down and could // cause subtle errors, so avoid the potential altogether. txD.Err = txReply.Err if txReply.Err == nil { txD.Tx = btcutil.NewTx(txReply.Tx) txD.BlockHeight = txReply.Height txD.Spent = make([]bool, len(txReply.TxSpent)) copy(txD.Spent, txReply.TxSpent) } } return txStore }
// 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 int32, 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 := wire.NewMsgTx() tx.AddTxIn(&wire.TxIn{ // Coinbase transactions have no inputs, so previous outpoint is // zero hash and max index. PreviousOutPoint: *wire.NewOutPoint(&wire.ShaHash{}, wire.MaxPrevOutIndex), SignatureScript: coinbaseScript, Sequence: wire.MaxTxInSequenceNum, }) tx.AddTxOut(&wire.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 := wire.NewOutPoint(&wire.ShaHash{}, math.MaxUint32) coinbaseTx := wire.NewMsgTx() coinbaseTx.Version = 2 coinbaseTx.AddTxIn(wire.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 int32 // 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 } } } }