// BenchmarkDecodeMerkleBlock performs a benchmark on how long it takes to // decode a reasonably sized merkleblock message. func BenchmarkDecodeMerkleBlock(b *testing.B) { // Create a message with random data. pver := ProtocolVersion var m MsgMerkleBlock hash, err := chainhash.NewHashFromStr(fmt.Sprintf("%x", 10000)) if err != nil { b.Fatalf("NewHashFromStr: unexpected error: %v", err) } m.Header = *NewBlockHeader(1, hash, hash, 0, uint32(10000)) for i := 0; i < 105; i++ { hash, err := chainhash.NewHashFromStr(fmt.Sprintf("%x", i)) if err != nil { b.Fatalf("NewHashFromStr: unexpected error: %v", err) } m.AddTxHash(hash) if i%8 == 0 { m.Flags = append(m.Flags, uint8(i)) } } // Serialize it so the bytes are available to test the decode below. var bb bytes.Buffer if err := m.BtcEncode(&bb, pver, LatestEncoding); err != nil { b.Fatalf("MsgMerkleBlock.BtcEncode: unexpected error: %v", err) } buf := bb.Bytes() r := bytes.NewReader(buf) var msg MsgMerkleBlock b.ResetTimer() for i := 0; i < b.N; i++ { r.Seek(0, 0) msg.BtcDecode(r, pver, LatestEncoding) } }
// newHashFromStr converts the passed big-endian hex string into a // chainhash.Hash. It only differs from the one available in chainhash in that // it panics on an error since it will only (and must only) be called with // hard-coded, and therefore known good, hashes. func newHashFromStr(hexStr string) *chainhash.Hash { hash, err := chainhash.NewHashFromStr(hexStr) if err != nil { panic("invalid hash in source file: " + hexStr) } return hash }
// Execute is the main entry point for the command. It's invoked by the parser. func (cmd *fetchBlockCmd) Execute(args []string) error { // Setup the global config options and ensure they are valid. if err := setupGlobalConfig(); err != nil { return err } if len(args) < 1 { return errors.New("required block hash parameter not specified") } blockHash, err := chainhash.NewHashFromStr(args[0]) if err != nil { return err } // Load the block database. db, err := loadBlockDB() if err != nil { return err } defer db.Close() return db.View(func(tx database.Tx) error { log.Infof("Fetching block %s", blockHash) startTime := time.Now() blockBytes, err := tx.FetchBlock(blockHash) if err != nil { return err } log.Infof("Loaded block in %v", time.Since(startTime)) log.Infof("Block Hex: %s", hex.EncodeToString(blockBytes)) return nil }) }
// newHashFromStr converts the passed big-endian hex string into a // wire.Hash. It only differs from the one available in chainhash in that // it panics on an error since it will only (and must only) be called with // hard-coded, and therefore known good, hashes. func newHashFromStr(hexStr string) *chainhash.Hash { hash, err := chainhash.NewHashFromStr(hexStr) if err != nil { panic(err) } return hash }
// BenchmarkDecodeNotFound performs a benchmark on how long it takes to decode // a notfound message with the maximum number of entries. func BenchmarkDecodeNotFound(b *testing.B) { // Create a message with the maximum number of entries. pver := ProtocolVersion var m MsgNotFound for i := 0; i < MaxInvPerMsg; i++ { hash, err := chainhash.NewHashFromStr(fmt.Sprintf("%x", i)) if err != nil { b.Fatalf("NewHashFromStr: unexpected error: %v", err) } m.AddInvVect(NewInvVect(InvTypeBlock, hash)) } // Serialize it so the bytes are available to test the decode below. var bb bytes.Buffer if err := m.BtcEncode(&bb, pver, LatestEncoding); err != nil { b.Fatalf("MsgNotFound.BtcEncode: unexpected error: %v", err) } buf := bb.Bytes() r := bytes.NewReader(buf) var msg MsgNotFound b.ResetTimer() for i := 0; i < b.N; i++ { r.Seek(0, 0) msg.BtcDecode(r, pver, LatestEncoding) } }
// Execute is the main entry point for the command. It's invoked by the parser. func (cmd *blockRegionCmd) Execute(args []string) error { // Setup the global config options and ensure they are valid. if err := setupGlobalConfig(); err != nil { return err } // Ensure expected arguments. if len(args) < 1 { return errors.New("required block hash parameter not specified") } if len(args) < 2 { return errors.New("required start offset parameter not " + "specified") } if len(args) < 3 { return errors.New("required region length parameter not " + "specified") } // Parse arguments. blockHash, err := chainhash.NewHashFromStr(args[0]) if err != nil { return err } startOffset, err := strconv.ParseUint(args[1], 10, 32) if err != nil { return err } regionLen, err := strconv.ParseUint(args[2], 10, 32) if err != nil { return err } // Load the block database. db, err := loadBlockDB() if err != nil { return err } defer db.Close() return db.View(func(tx database.Tx) error { log.Infof("Fetching block region %s<%d:%d>", blockHash, startOffset, startOffset+regionLen-1) region := database.BlockRegion{ Hash: blockHash, Offset: uint32(startOffset), Len: uint32(regionLen), } startTime := time.Now() regionBytes, err := tx.FetchBlockRegion(®ion) if err != nil { return err } log.Infof("Loaded block region in %v", time.Since(startTime)) log.Infof("Double Hash: %s", chainhash.DoubleHashH(regionBytes)) log.Infof("Region Hex: %s", hex.EncodeToString(regionBytes)) return nil }) }
// TestGetHeaders tests the MsgGetHeader API. func TestGetHeaders(t *testing.T) { pver := ProtocolVersion // Block 99500 hash. hashStr := "000000000002e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0" locatorHash, err := chainhash.NewHashFromStr(hashStr) if err != nil { t.Errorf("NewHashFromStr: %v", err) } // Ensure the command is expected value. wantCmd := "getheaders" msg := NewMsgGetHeaders() if cmd := msg.Command(); cmd != wantCmd { t.Errorf("NewMsgGetHeaders: wrong command - got %v want %v", cmd, wantCmd) } // Ensure max payload is expected value for latest protocol version. // Protocol version 4 bytes + num hashes (varInt) + max block locator // hashes + hash stop. wantPayload := uint32(16045) maxPayload := msg.MaxPayloadLength(pver) if maxPayload != wantPayload { t.Errorf("MaxPayloadLength: wrong max payload length for "+ "protocol version %d - got %v, want %v", pver, maxPayload, wantPayload) } // Ensure block locator hashes are added properly. err = msg.AddBlockLocatorHash(locatorHash) if err != nil { t.Errorf("AddBlockLocatorHash: %v", err) } if msg.BlockLocatorHashes[0] != locatorHash { t.Errorf("AddBlockLocatorHash: wrong block locator added - "+ "got %v, want %v", spew.Sprint(msg.BlockLocatorHashes[0]), spew.Sprint(locatorHash)) } // Ensure adding more than the max allowed block locator hashes per // message returns an error. for i := 0; i < MaxBlockLocatorsPerMsg; i++ { err = msg.AddBlockLocatorHash(locatorHash) } if err == nil { t.Errorf("AddBlockLocatorHash: expected error on too many " + "block locator hashes not received") } return }
// TestCalcSignatureHash runs the Bitcoin Core signature hash calculation tests // in sighash.json. // https://github.com/bitcoin/bitcoin/blob/master/src/test/data/sighash.json func TestCalcSignatureHash(t *testing.T) { file, err := ioutil.ReadFile("data/sighash.json") if err != nil { t.Errorf("TestCalcSignatureHash: %v\n", err) return } var tests [][]interface{} err = json.Unmarshal(file, &tests) if err != nil { t.Errorf("TestCalcSignatureHash couldn't Unmarshal: %v\n", err) return } for i, test := range tests { if i == 0 { // Skip first line -- contains comments only. continue } if len(test) != 5 { t.Fatalf("TestCalcSignatureHash: Test #%d has "+ "wrong length.", i) } var tx wire.MsgTx rawTx, _ := hex.DecodeString(test[0].(string)) err := tx.Deserialize(bytes.NewReader(rawTx)) if err != nil { t.Errorf("TestCalcSignatureHash failed test #%d: "+ "Failed to parse transaction: %v", i, err) continue } subScript, _ := hex.DecodeString(test[1].(string)) parsedScript, err := parseScript(subScript) if err != nil { t.Errorf("TestCalcSignatureHash failed test #%d: "+ "Failed to parse sub-script: %v", i, err) continue } hashType := SigHashType(testVecF64ToUint32(test[3].(float64))) hash := calcSignatureHash(parsedScript, hashType, &tx, int(test[2].(float64))) expectedHash, _ := chainhash.NewHashFromStr(test[4].(string)) if !bytes.Equal(hash, expectedHash[:]) { t.Errorf("TestCalcSignatureHash failed test #%d: "+ "Signature hash mismatch.", i) } } }
// newHashFromStr converts the passed big-endian hex string into a // chainhash.Hash. It only differs from the one available in chainhash in that // it panics on an error since it will only (and must only) be called with // hard-coded, and therefore known good, hashes. func newHashFromStr(hexStr string) *chainhash.Hash { hash, err := chainhash.NewHashFromStr(hexStr) if err != nil { // Ordinarily I don't like panics in library code since it // can take applications down without them having a chance to // recover which is extremely annoying, however an exception is // being made in this case because the only way this can panic // is if there is an error in the hard-coded hashes. Thus it // will only ever potentially panic on init and therefore is // 100% predictable. panic(err) } return hash }
// TestTxHash tests the ability to generate the hash of a transaction accurately. func TestTxHash(t *testing.T) { // Hash of first transaction from block 113875. hashStr := "f051e59b5e2503ac626d03aaeac8ab7be2d72ba4b7e97119c5852d70d52dcb86" wantHash, err := chainhash.NewHashFromStr(hashStr) if err != nil { t.Errorf("NewHashFromStr: %v", err) return } // First transaction from block 113875. msgTx := NewMsgTx(1) txIn := TxIn{ PreviousOutPoint: OutPoint{ Hash: chainhash.Hash{}, Index: 0xffffffff, }, SignatureScript: []byte{0x04, 0x31, 0xdc, 0x00, 0x1b, 0x01, 0x62}, Sequence: 0xffffffff, } txOut := TxOut{ Value: 5000000000, PkScript: []byte{ 0x41, // OP_DATA_65 0x04, 0xd6, 0x4b, 0xdf, 0xd0, 0x9e, 0xb1, 0xc5, 0xfe, 0x29, 0x5a, 0xbd, 0xeb, 0x1d, 0xca, 0x42, 0x81, 0xbe, 0x98, 0x8e, 0x2d, 0xa0, 0xb6, 0xc1, 0xc6, 0xa5, 0x9d, 0xc2, 0x26, 0xc2, 0x86, 0x24, 0xe1, 0x81, 0x75, 0xe8, 0x51, 0xc9, 0x6b, 0x97, 0x3d, 0x81, 0xb0, 0x1c, 0xc3, 0x1f, 0x04, 0x78, 0x34, 0xbc, 0x06, 0xd6, 0xd6, 0xed, 0xf6, 0x20, 0xd1, 0x84, 0x24, 0x1a, 0x6a, 0xed, 0x8b, 0x63, 0xa6, // 65-byte signature 0xac, // OP_CHECKSIG }, } msgTx.AddTxIn(&txIn) msgTx.AddTxOut(&txOut) msgTx.LockTime = 0 // Ensure the hash produced is expected. txHash := msgTx.TxHash() if !txHash.IsEqual(wantHash) { t.Errorf("TxHash: wrong hash - got %v, want %v", spew.Sprint(txHash), spew.Sprint(wantHash)) } }
// TestTx tests the MsgTx API. func TestTx(t *testing.T) { pver := ProtocolVersion // Block 100000 hash. hashStr := "3ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506" hash, err := chainhash.NewHashFromStr(hashStr) if err != nil { t.Errorf("NewHashFromStr: %v", err) } // Ensure the command is expected value. wantCmd := "tx" msg := NewMsgTx(1) if cmd := msg.Command(); cmd != wantCmd { t.Errorf("NewMsgAddr: wrong command - got %v want %v", cmd, wantCmd) } // Ensure max payload is expected value for latest protocol version. // Num addresses (varInt) + max allowed addresses. wantPayload := uint32(1000 * 4000) maxPayload := msg.MaxPayloadLength(pver) if maxPayload != wantPayload { t.Errorf("MaxPayloadLength: wrong max payload length for "+ "protocol version %d - got %v, want %v", pver, maxPayload, wantPayload) } // Ensure we get the same transaction output point data back out. // NOTE: This is a block hash and made up index, but we're only // testing package functionality. prevOutIndex := uint32(1) prevOut := NewOutPoint(hash, prevOutIndex) if !prevOut.Hash.IsEqual(hash) { t.Errorf("NewOutPoint: wrong hash - got %v, want %v", spew.Sprint(&prevOut.Hash), spew.Sprint(hash)) } if prevOut.Index != prevOutIndex { t.Errorf("NewOutPoint: wrong index - got %v, want %v", prevOut.Index, prevOutIndex) } prevOutStr := fmt.Sprintf("%s:%d", hash.String(), prevOutIndex) if s := prevOut.String(); s != prevOutStr { t.Errorf("OutPoint.String: unexpected result - got %v, "+ "want %v", s, prevOutStr) } // Ensure we get the same transaction input back out. sigScript := []byte{0x04, 0x31, 0xdc, 0x00, 0x1b, 0x01, 0x62} witnessData := [][]byte{ []byte{0x04, 0x31}, []byte{0x01, 0x43}, } txIn := NewTxIn(prevOut, sigScript, witnessData) if !reflect.DeepEqual(&txIn.PreviousOutPoint, prevOut) { t.Errorf("NewTxIn: wrong prev outpoint - got %v, want %v", spew.Sprint(&txIn.PreviousOutPoint), spew.Sprint(prevOut)) } if !bytes.Equal(txIn.SignatureScript, sigScript) { t.Errorf("NewTxIn: wrong signature script - got %v, want %v", spew.Sdump(txIn.SignatureScript), spew.Sdump(sigScript)) } if !reflect.DeepEqual(txIn.Witness, TxWitness(witnessData)) { t.Errorf("NewTxIn: wrong witness data - got %v, want %v", spew.Sdump(txIn.Witness), spew.Sdump(witnessData)) } // Ensure we get the same transaction output back out. txValue := int64(5000000000) pkScript := []byte{ 0x41, // OP_DATA_65 0x04, 0xd6, 0x4b, 0xdf, 0xd0, 0x9e, 0xb1, 0xc5, 0xfe, 0x29, 0x5a, 0xbd, 0xeb, 0x1d, 0xca, 0x42, 0x81, 0xbe, 0x98, 0x8e, 0x2d, 0xa0, 0xb6, 0xc1, 0xc6, 0xa5, 0x9d, 0xc2, 0x26, 0xc2, 0x86, 0x24, 0xe1, 0x81, 0x75, 0xe8, 0x51, 0xc9, 0x6b, 0x97, 0x3d, 0x81, 0xb0, 0x1c, 0xc3, 0x1f, 0x04, 0x78, 0x34, 0xbc, 0x06, 0xd6, 0xd6, 0xed, 0xf6, 0x20, 0xd1, 0x84, 0x24, 0x1a, 0x6a, 0xed, 0x8b, 0x63, 0xa6, // 65-byte signature 0xac, // OP_CHECKSIG } txOut := NewTxOut(txValue, pkScript) if txOut.Value != txValue { t.Errorf("NewTxOut: wrong pk script - got %v, want %v", txOut.Value, txValue) } if !bytes.Equal(txOut.PkScript, pkScript) { t.Errorf("NewTxOut: wrong pk script - got %v, want %v", spew.Sdump(txOut.PkScript), spew.Sdump(pkScript)) } // Ensure transaction inputs are added properly. msg.AddTxIn(txIn) if !reflect.DeepEqual(msg.TxIn[0], txIn) { t.Errorf("AddTxIn: wrong transaction input added - got %v, want %v", spew.Sprint(msg.TxIn[0]), spew.Sprint(txIn)) } // Ensure transaction outputs are added properly. msg.AddTxOut(txOut) if !reflect.DeepEqual(msg.TxOut[0], txOut) { t.Errorf("AddTxIn: wrong transaction output added - got %v, want %v", spew.Sprint(msg.TxOut[0]), spew.Sprint(txOut)) } // Ensure the copy produced an identical transaction message. newMsg := msg.Copy() if !reflect.DeepEqual(newMsg, msg) { t.Errorf("Copy: mismatched tx messages - got %v, want %v", spew.Sdump(newMsg), spew.Sdump(msg)) } return }
// TestGetHeadersWire tests the MsgGetHeaders wire encode and decode for various // numbers of block locator hashes and protocol versions. func TestGetHeadersWire(t *testing.T) { // Set protocol inside getheaders message. Use protocol version 60002 // specifically here instead of the latest because the test data is // using bytes encoded with that protocol version. pver := uint32(60002) // Block 99499 hash. hashStr := "2710f40c87ec93d010a6fd95f42c59a2cbacc60b18cf6b7957535" hashLocator, err := chainhash.NewHashFromStr(hashStr) if err != nil { t.Errorf("NewHashFromStr: %v", err) } // Block 99500 hash. hashStr = "2e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0" hashLocator2, err := chainhash.NewHashFromStr(hashStr) if err != nil { t.Errorf("NewHashFromStr: %v", err) } // Block 100000 hash. hashStr = "3ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506" hashStop, err := chainhash.NewHashFromStr(hashStr) if err != nil { t.Errorf("NewHashFromStr: %v", err) } // MsgGetHeaders message with no block locators or stop hash. noLocators := NewMsgGetHeaders() noLocators.ProtocolVersion = pver noLocatorsEncoded := []byte{ 0x62, 0xea, 0x00, 0x00, // Protocol version 60002 0x00, // Varint for number of block locator hashes 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Hash stop } // MsgGetHeaders message with multiple block locators and a stop hash. multiLocators := NewMsgGetHeaders() multiLocators.ProtocolVersion = pver multiLocators.HashStop = *hashStop multiLocators.AddBlockLocatorHash(hashLocator2) multiLocators.AddBlockLocatorHash(hashLocator) multiLocatorsEncoded := []byte{ 0x62, 0xea, 0x00, 0x00, // Protocol version 60002 0x02, // Varint for number of block locator hashes 0xe0, 0xde, 0x06, 0x44, 0x68, 0x13, 0x2c, 0x63, 0xd2, 0x20, 0xcc, 0x69, 0x12, 0x83, 0xcb, 0x65, 0xbc, 0xaa, 0xe4, 0x79, 0x94, 0xef, 0x9e, 0x7b, 0xad, 0xe7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 99500 hash 0x35, 0x75, 0x95, 0xb7, 0xf6, 0x8c, 0xb1, 0x60, 0xcc, 0xba, 0x2c, 0x9a, 0xc5, 0x42, 0x5f, 0xd9, 0x6f, 0x0a, 0x01, 0x3d, 0xc9, 0x7e, 0xc8, 0x40, 0x0f, 0x71, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 99499 hash 0x06, 0xe5, 0x33, 0xfd, 0x1a, 0xda, 0x86, 0x39, 0x1f, 0x3f, 0x6c, 0x34, 0x32, 0x04, 0xb0, 0xd2, 0x78, 0xd4, 0xaa, 0xec, 0x1c, 0x0b, 0x20, 0xaa, 0x27, 0xba, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // Hash stop } tests := []struct { in *MsgGetHeaders // Message to encode out *MsgGetHeaders // Expected decoded message buf []byte // Wire encoding pver uint32 // Protocol version for wire encoding enc MessageEncoding // Message encoding format }{ // Latest protocol version with no block locators. { noLocators, noLocators, noLocatorsEncoded, ProtocolVersion, BaseEncoding, }, // Latest protocol version with multiple block locators. { multiLocators, multiLocators, multiLocatorsEncoded, ProtocolVersion, BaseEncoding, }, // Protocol version BIP0035Version with no block locators. { noLocators, noLocators, noLocatorsEncoded, BIP0035Version, BaseEncoding, }, // Protocol version BIP0035Version with multiple block locators. { multiLocators, multiLocators, multiLocatorsEncoded, BIP0035Version, BaseEncoding, }, // Protocol version BIP0031Version with no block locators. { noLocators, noLocators, noLocatorsEncoded, BIP0031Version, BaseEncoding, }, // Protocol version BIP0031Versionwith multiple block locators. { multiLocators, multiLocators, multiLocatorsEncoded, BIP0031Version, BaseEncoding, }, // Protocol version NetAddressTimeVersion with no block locators. { noLocators, noLocators, noLocatorsEncoded, NetAddressTimeVersion, BaseEncoding, }, // Protocol version NetAddressTimeVersion multiple block locators. { multiLocators, multiLocators, multiLocatorsEncoded, NetAddressTimeVersion, BaseEncoding, }, // Protocol version MultipleAddressVersion with no block locators. { noLocators, noLocators, noLocatorsEncoded, MultipleAddressVersion, BaseEncoding, }, // Protocol version MultipleAddressVersion multiple block locators. { multiLocators, multiLocators, multiLocatorsEncoded, MultipleAddressVersion, BaseEncoding, }, } t.Logf("Running %d tests", len(tests)) for i, test := range tests { // Encode the message to wire format. var buf bytes.Buffer err := test.in.BtcEncode(&buf, test.pver, test.enc) if err != nil { t.Errorf("BtcEncode #%d error %v", i, err) continue } if !bytes.Equal(buf.Bytes(), test.buf) { t.Errorf("BtcEncode #%d\n got: %s want: %s", i, spew.Sdump(buf.Bytes()), spew.Sdump(test.buf)) continue } // Decode the message from wire format. var msg MsgGetHeaders rbuf := bytes.NewReader(test.buf) err = msg.BtcDecode(rbuf, test.pver, test.enc) if err != nil { t.Errorf("BtcDecode #%d error %v", i, err) continue } if !reflect.DeepEqual(&msg, test.out) { t.Errorf("BtcDecode #%d\n got: %s want: %s", i, spew.Sdump(&msg), spew.Sdump(test.out)) continue } } }
// TestHaveBlock tests the HaveBlock API to ensure proper functionality. func TestHaveBlock(t *testing.T) { // Load up blocks such that there is a side chain. // (genesis block) -> 1 -> 2 -> 3 -> 4 // \-> 3a testFiles := []string{ "blk_0_to_4.dat.bz2", "blk_3A.dat.bz2", } var blocks []*btcutil.Block for _, file := range testFiles { blockTmp, err := loadBlocks(file) if err != nil { t.Errorf("Error loading file: %v\n", err) return } blocks = append(blocks, blockTmp...) } // Create a new database and chain instance to run tests against. chain, teardownFunc, err := chainSetup("haveblock", &chaincfg.MainNetParams) if err != nil { t.Errorf("Failed to setup chain instance: %v", err) return } defer teardownFunc() // Since we're not dealing with the real block chain, disable // checkpoints and set the coinbase maturity to 1. chain.DisableCheckpoints(true) chain.TstSetCoinbaseMaturity(1) for i := 1; i < len(blocks); i++ { _, isOrphan, err := chain.ProcessBlock(blocks[i], blockchain.BFNone) if err != nil { t.Errorf("ProcessBlock fail on block %v: %v\n", i, err) return } if isOrphan { t.Errorf("ProcessBlock incorrectly returned block %v "+ "is an orphan\n", i) return } } // Insert an orphan block. _, isOrphan, err := chain.ProcessBlock(btcutil.NewBlock(&Block100000), blockchain.BFNone) if err != nil { t.Errorf("Unable to process block: %v", err) return } if !isOrphan { t.Errorf("ProcessBlock indicated block is an not orphan when " + "it should be\n") return } tests := []struct { hash string want bool }{ // Genesis block should be present (in the main chain). {hash: chaincfg.MainNetParams.GenesisHash.String(), want: true}, // Block 3a should be present (on a side chain). {hash: "00000000474284d20067a4d33f6a02284e6ef70764a3a26d6a5b9df52ef663dd", want: true}, // Block 100000 should be present (as an orphan). {hash: "000000000003ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506", want: true}, // Random hashes should not be available. {hash: "123", want: false}, } for i, test := range tests { hash, err := chainhash.NewHashFromStr(test.hash) if err != nil { t.Errorf("NewHashFromStr: %v", err) continue } result, err := chain.HaveBlock(hash) if err != nil { t.Errorf("HaveBlock #%d unexpected error: %v", i, err) return } if result != test.want { t.Errorf("HaveBlock #%d got %v want %v", i, result, test.want) continue } } }
// TestOutboundPeer tests that the outbound peer works as expected. func TestOutboundPeer(t *testing.T) { peerCfg := &peer.Config{ NewestBlock: func() (*chainhash.Hash, int32, error) { return nil, 0, errors.New("newest block not found") }, UserAgentName: "peer", UserAgentVersion: "1.0", ChainParams: &chaincfg.MainNetParams, Services: 0, } r, w := io.Pipe() c := &conn{raddr: "10.0.0.1:8333", Writer: w, Reader: r} p, err := peer.NewOutboundPeer(peerCfg, "10.0.0.1:8333") if err != nil { t.Errorf("NewOutboundPeer: unexpected err - %v\n", err) return } // Test trying to connect twice. p.AssociateConnection(c) p.AssociateConnection(c) disconnected := make(chan struct{}) go func() { p.WaitForDisconnect() disconnected <- struct{}{} }() select { case <-disconnected: close(disconnected) case <-time.After(time.Second): t.Fatal("Peer did not automatically disconnect.") } if p.Connected() { t.Fatalf("Should not be connected as NewestBlock produces error.") } // Test Queue Inv fakeBlockHash := &chainhash.Hash{0: 0x00, 1: 0x01} fakeInv := wire.NewInvVect(wire.InvTypeBlock, fakeBlockHash) // Should be noops as the peer could not connect. p.QueueInventory(fakeInv) p.AddKnownInventory(fakeInv) p.QueueInventory(fakeInv) fakeMsg := wire.NewMsgVerAck() p.QueueMessage(fakeMsg, nil) done := make(chan struct{}) p.QueueMessage(fakeMsg, done) <-done p.Disconnect() // Test NewestBlock var newestBlock = func() (*chainhash.Hash, int32, error) { hashStr := "14a0810ac680a3eb3f82edc878cea25ec41d6b790744e5daeef" hash, err := chainhash.NewHashFromStr(hashStr) if err != nil { return nil, 0, err } return hash, 234439, nil } peerCfg.NewestBlock = newestBlock r1, w1 := io.Pipe() c1 := &conn{raddr: "10.0.0.1:8333", Writer: w1, Reader: r1} p1, err := peer.NewOutboundPeer(peerCfg, "10.0.0.1:8333") if err != nil { t.Errorf("NewOutboundPeer: unexpected err - %v\n", err) return } p1.AssociateConnection(c1) // Test update latest block latestBlockHash, err := chainhash.NewHashFromStr("1a63f9cdff1752e6375c8c76e543a71d239e1a2e5c6db1aa679") if err != nil { t.Errorf("NewHashFromStr: unexpected err %v\n", err) return } p1.UpdateLastAnnouncedBlock(latestBlockHash) p1.UpdateLastBlockHeight(234440) if p1.LastAnnouncedBlock() != latestBlockHash { t.Errorf("LastAnnouncedBlock: wrong block - got %v, want %v", p1.LastAnnouncedBlock(), latestBlockHash) return } // Test Queue Inv after connection p1.QueueInventory(fakeInv) p1.Disconnect() // Test regression peerCfg.ChainParams = &chaincfg.RegressionNetParams peerCfg.Services = wire.SFNodeBloom r2, w2 := io.Pipe() c2 := &conn{raddr: "10.0.0.1:8333", Writer: w2, Reader: r2} p2, err := peer.NewOutboundPeer(peerCfg, "10.0.0.1:8333") if err != nil { t.Errorf("NewOutboundPeer: unexpected err - %v\n", err) return } p2.AssociateConnection(c2) // Test PushXXX var addrs []*wire.NetAddress for i := 0; i < 5; i++ { na := wire.NetAddress{} addrs = append(addrs, &na) } if _, err := p2.PushAddrMsg(addrs); err != nil { t.Errorf("PushAddrMsg: unexpected err %v\n", err) return } if err := p2.PushGetBlocksMsg(nil, &chainhash.Hash{}); err != nil { t.Errorf("PushGetBlocksMsg: unexpected err %v\n", err) return } if err := p2.PushGetHeadersMsg(nil, &chainhash.Hash{}); err != nil { t.Errorf("PushGetHeadersMsg: unexpected err %v\n", err) return } p2.PushRejectMsg("block", wire.RejectMalformed, "malformed", nil, false) p2.PushRejectMsg("block", wire.RejectInvalid, "invalid", nil, false) // Test Queue Messages p2.QueueMessage(wire.NewMsgGetAddr(), nil) p2.QueueMessage(wire.NewMsgPing(1), nil) p2.QueueMessage(wire.NewMsgMemPool(), nil) p2.QueueMessage(wire.NewMsgGetData(), nil) p2.QueueMessage(wire.NewMsgGetHeaders(), nil) p2.QueueMessage(wire.NewMsgFeeFilter(20000), nil) p2.Disconnect() }
// TestGetHeadersWireErrors performs negative tests against wire encode and // decode of MsgGetHeaders to confirm error paths work correctly. func TestGetHeadersWireErrors(t *testing.T) { // Set protocol inside getheaders message. Use protocol version 60002 // specifically here instead of the latest because the test data is // using bytes encoded with that protocol version. pver := uint32(60002) wireErr := &MessageError{} // Block 99499 hash. hashStr := "2710f40c87ec93d010a6fd95f42c59a2cbacc60b18cf6b7957535" hashLocator, err := chainhash.NewHashFromStr(hashStr) if err != nil { t.Errorf("NewHashFromStr: %v", err) } // Block 99500 hash. hashStr = "2e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0" hashLocator2, err := chainhash.NewHashFromStr(hashStr) if err != nil { t.Errorf("NewHashFromStr: %v", err) } // Block 100000 hash. hashStr = "3ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506" hashStop, err := chainhash.NewHashFromStr(hashStr) if err != nil { t.Errorf("NewHashFromStr: %v", err) } // MsgGetHeaders message with multiple block locators and a stop hash. baseGetHeaders := NewMsgGetHeaders() baseGetHeaders.ProtocolVersion = pver baseGetHeaders.HashStop = *hashStop baseGetHeaders.AddBlockLocatorHash(hashLocator2) baseGetHeaders.AddBlockLocatorHash(hashLocator) baseGetHeadersEncoded := []byte{ 0x62, 0xea, 0x00, 0x00, // Protocol version 60002 0x02, // Varint for number of block locator hashes 0xe0, 0xde, 0x06, 0x44, 0x68, 0x13, 0x2c, 0x63, 0xd2, 0x20, 0xcc, 0x69, 0x12, 0x83, 0xcb, 0x65, 0xbc, 0xaa, 0xe4, 0x79, 0x94, 0xef, 0x9e, 0x7b, 0xad, 0xe7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 99500 hash 0x35, 0x75, 0x95, 0xb7, 0xf6, 0x8c, 0xb1, 0x60, 0xcc, 0xba, 0x2c, 0x9a, 0xc5, 0x42, 0x5f, 0xd9, 0x6f, 0x0a, 0x01, 0x3d, 0xc9, 0x7e, 0xc8, 0x40, 0x0f, 0x71, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 99499 hash 0x06, 0xe5, 0x33, 0xfd, 0x1a, 0xda, 0x86, 0x39, 0x1f, 0x3f, 0x6c, 0x34, 0x32, 0x04, 0xb0, 0xd2, 0x78, 0xd4, 0xaa, 0xec, 0x1c, 0x0b, 0x20, 0xaa, 0x27, 0xba, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // Hash stop } // Message that forces an error by having more than the max allowed // block locator hashes. maxGetHeaders := NewMsgGetHeaders() for i := 0; i < MaxBlockLocatorsPerMsg; i++ { maxGetHeaders.AddBlockLocatorHash(&mainNetGenesisHash) } maxGetHeaders.BlockLocatorHashes = append(maxGetHeaders.BlockLocatorHashes, &mainNetGenesisHash) maxGetHeadersEncoded := []byte{ 0x62, 0xea, 0x00, 0x00, // Protocol version 60002 0xfd, 0xf5, 0x01, // Varint for number of block loc hashes (501) } tests := []struct { in *MsgGetHeaders // Value to encode buf []byte // Wire encoding pver uint32 // Protocol version for wire encoding enc MessageEncoding // Message encoding format max int // Max size of fixed buffer to induce errors writeErr error // Expected write error readErr error // Expected read error }{ // Force error in protocol version. {baseGetHeaders, baseGetHeadersEncoded, pver, BaseEncoding, 0, io.ErrShortWrite, io.EOF}, // Force error in block locator hash count. {baseGetHeaders, baseGetHeadersEncoded, pver, BaseEncoding, 4, io.ErrShortWrite, io.EOF}, // Force error in block locator hashes. {baseGetHeaders, baseGetHeadersEncoded, pver, BaseEncoding, 5, io.ErrShortWrite, io.EOF}, // Force error in stop hash. {baseGetHeaders, baseGetHeadersEncoded, pver, BaseEncoding, 69, io.ErrShortWrite, io.EOF}, // Force error with greater than max block locator hashes. {maxGetHeaders, maxGetHeadersEncoded, pver, BaseEncoding, 7, wireErr, wireErr}, } t.Logf("Running %d tests", len(tests)) for i, test := range tests { // Encode to wire format. w := newFixedWriter(test.max) err := test.in.BtcEncode(w, test.pver, test.enc) if reflect.TypeOf(err) != reflect.TypeOf(test.writeErr) { t.Errorf("BtcEncode #%d wrong error got: %v, want: %v", i, err, test.writeErr) continue } // For errors which are not of type MessageError, check them for // equality. if _, ok := err.(*MessageError); !ok { if err != test.writeErr { t.Errorf("BtcEncode #%d wrong error got: %v, "+ "want: %v", i, err, test.writeErr) continue } } // Decode from wire format. var msg MsgGetHeaders r := newFixedReader(test.max, test.buf) err = msg.BtcDecode(r, test.pver, test.enc) if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) { t.Errorf("BtcDecode #%d wrong error got: %v, want: %v", i, err, test.readErr) continue } // For errors which are not of type MessageError, check them for // equality. if _, ok := err.(*MessageError); !ok { if err != test.readErr { t.Errorf("BtcDecode #%d wrong error got: %v, "+ "want: %v", i, err, test.readErr) continue } } } }
// newHashFromStr converts the passed big-endian hex string into a // chainhash.Hash. It only differs from the one available in chainhash in that // it ignores the error since it will only (and must only) be called with // hard-coded, and therefore known good, hashes. func newHashFromStr(hexStr string) *chainhash.Hash { hash, _ := chainhash.NewHashFromStr(hexStr) return hash }
// TestTxValidTests ensures all of the tests in tx_valid.json pass as expected. func TestTxValidTests(t *testing.T) { file, err := ioutil.ReadFile("data/tx_valid.json") if err != nil { t.Errorf("TestTxValidTests: %v\n", err) return } var tests [][]interface{} err = json.Unmarshal(file, &tests) if err != nil { t.Errorf("TestTxValidTests couldn't Unmarshal: %v\n", err) return } // form is either: // ["this is a comment "] // or: // [[[previous hash, previous index, previous scriptPubKey, input value]...,] // serializedTransaction, verifyFlags] testloop: for i, test := range tests { inputs, ok := test[0].([]interface{}) if !ok { continue } if len(test) != 3 { t.Errorf("bad test (bad length) %d: %v", i, test) continue } serializedhex, ok := test[1].(string) if !ok { t.Errorf("bad test (arg 2 not string) %d: %v", i, test) continue } serializedTx, err := hex.DecodeString(serializedhex) if err != nil { t.Errorf("bad test (arg 2 not hex %v) %d: %v", err, i, test) continue } tx, err := btcutil.NewTxFromBytes(serializedTx) if err != nil { t.Errorf("bad test (arg 2 not msgtx %v) %d: %v", err, i, test) continue } verifyFlags, ok := test[2].(string) if !ok { t.Errorf("bad test (arg 3 not string) %d: %v", i, test) continue } flags, err := parseScriptFlags(verifyFlags) if err != nil { t.Errorf("bad test %d: %v", i, err) continue } prevOuts := make(map[wire.OutPoint]scriptWithInputVal) for j, iinput := range inputs { input, ok := iinput.([]interface{}) if !ok { t.Errorf("bad test (%dth input not array)"+ "%d: %v", j, i, test) continue } if len(input) < 3 || len(input) > 4 { t.Errorf("bad test (%dth input wrong length)"+ "%d: %v", j, i, test) continue } previoustx, ok := input[0].(string) if !ok { t.Errorf("bad test (%dth input hash not string)"+ "%d: %v", j, i, test) continue } prevhash, err := chainhash.NewHashFromStr(previoustx) if err != nil { t.Errorf("bad test (%dth input hash not hash %v)"+ "%d: %v", j, err, i, test) continue } idxf, ok := input[1].(float64) if !ok { t.Errorf("bad test (%dth input idx not number)"+ "%d: %v", j, i, test) continue } idx := testVecF64ToUint32(idxf) oscript, ok := input[2].(string) if !ok { t.Errorf("bad test (%dth input script not "+ "string) %d: %v", j, i, test) continue } script, err := parseShortForm(oscript) if err != nil { t.Errorf("bad test (%dth input script doesn't "+ "parse %v) %d: %v", j, err, i, test) continue } var inputValue float64 if len(input) == 4 { inputValue, ok = input[3].(float64) if !ok { t.Errorf("bad test (%dth input value not int) "+ "%d: %v", j, i, test) continue } } v := scriptWithInputVal{ inputVal: int64(inputValue), pkScript: script, } prevOuts[*wire.NewOutPoint(prevhash, idx)] = v } for k, txin := range tx.MsgTx().TxIn { prevOut, ok := prevOuts[txin.PreviousOutPoint] if !ok { t.Errorf("bad test (missing %dth input) %d:%v", k, i, test) continue testloop } vm, err := NewEngine(prevOut.pkScript, tx.MsgTx(), k, flags, nil, nil, prevOut.inputVal) if err != nil { t.Errorf("test (%d:%v:%d) failed to create "+ "script: %v", i, test, k, err) continue } err = vm.Execute() if err != nil { t.Errorf("test (%d:%v:%d) failed to execute: "+ "%v", i, test, k, err) continue } } } }
// TestTxSha tests the ability to generate the wtxid, and txid of a transaction // with witness inputs accurately. func TestWTxSha(t *testing.T) { hashStrTxid := "0f167d1385a84d1518cfee208b653fc9163b605ccf1b75347e2850b3e2eb19f3" wantHashTxid, err := chainhash.NewHashFromStr(hashStrTxid) if err != nil { t.Errorf("NewShaHashFromStr: %v", err) return } hashStrWTxid := "0858eab78e77b6b033da30f46699996396cf48fcf625a783c85a51403e175e74" wantHashWTxid, err := chainhash.NewHashFromStr(hashStrWTxid) if err != nil { t.Errorf("NewShaHashFromStr: %v", err) return } // From block 23157 in a past version of segnet. msgTx := NewMsgTx() txIn := TxIn{ PreviousOutPoint: OutPoint{ Hash: chainhash.Hash{ 0xa5, 0x33, 0x52, 0xd5, 0x13, 0x57, 0x66, 0xf0, 0x30, 0x76, 0x59, 0x74, 0x18, 0x26, 0x3d, 0xa2, 0xd9, 0xc9, 0x58, 0x31, 0x59, 0x68, 0xfe, 0xa8, 0x23, 0x52, 0x94, 0x67, 0x48, 0x1f, 0xf9, 0xcd, }, Index: 19, }, Witness: [][]byte{ []byte{ // 70-byte signature 0x30, 0x43, 0x02, 0x1f, 0x4d, 0x23, 0x81, 0xdc, 0x97, 0xf1, 0x82, 0xab, 0xd8, 0x18, 0x5f, 0x51, 0x75, 0x30, 0x18, 0x52, 0x32, 0x12, 0xf5, 0xdd, 0xc0, 0x7c, 0xc4, 0xe6, 0x3a, 0x8d, 0xc0, 0x36, 0x58, 0xda, 0x19, 0x02, 0x20, 0x60, 0x8b, 0x5c, 0x4d, 0x92, 0xb8, 0x6b, 0x6d, 0xe7, 0xd7, 0x8e, 0xf2, 0x3a, 0x2f, 0xa7, 0x35, 0xbc, 0xb5, 0x9b, 0x91, 0x4a, 0x48, 0xb0, 0xe1, 0x87, 0xc5, 0xe7, 0x56, 0x9a, 0x18, 0x19, 0x70, 0x01, }, []byte{ // 33-byte serialize pub key 0x03, 0x07, 0xea, 0xd0, 0x84, 0x80, 0x7e, 0xb7, 0x63, 0x46, 0xdf, 0x69, 0x77, 0x00, 0x0c, 0x89, 0x39, 0x2f, 0x45, 0xc7, 0x64, 0x25, 0xb2, 0x61, 0x81, 0xf5, 0x21, 0xd7, 0xf3, 0x70, 0x06, 0x6a, 0x8f, }, }, Sequence: 0xffffffff, } txOut := TxOut{ Value: 395019, PkScript: []byte{ 0x00, // Version 0 witness program 0x14, // OP_DATA_20 0x9d, 0xda, 0xc6, 0xf3, 0x9d, 0x51, 0xe0, 0x39, 0x8e, 0x53, 0x2a, 0x22, 0xc4, 0x1b, 0xa1, 0x89, 0x40, 0x6a, 0x85, 0x23, // 20-byte pub key hash }, } msgTx.AddTxIn(&txIn) msgTx.AddTxOut(&txOut) msgTx.LockTime = 0 // Ensure the correct txid, and wtxid is produced as expected. txid := msgTx.TxHash() if !txid.IsEqual(wantHashTxid) { t.Errorf("TxSha: wrong hash - got %v, want %v", spew.Sprint(txid), spew.Sprint(wantHashTxid)) } wtxid := msgTx.WitnessHash() if !wtxid.IsEqual(wantHashWTxid) { t.Errorf("WTxSha: wrong hash - got %v, want %v", spew.Sprint(wtxid), spew.Sprint(wantHashWTxid)) } }
// TestCheckTransactionStandard tests the checkTransactionStandard API. func TestCheckTransactionStandard(t *testing.T) { // Create some dummy, but otherwise standard, data for transactions. prevOutHash, err := chainhash.NewHashFromStr("01") if err != nil { t.Fatalf("NewShaHashFromStr: unexpected error: %v", err) } dummyPrevOut := wire.OutPoint{Hash: *prevOutHash, Index: 1} dummySigScript := bytes.Repeat([]byte{0x00}, 65) dummyTxIn := wire.TxIn{ PreviousOutPoint: dummyPrevOut, SignatureScript: dummySigScript, Sequence: wire.MaxTxInSequenceNum, } addrHash := [20]byte{0x01} addr, err := btcutil.NewAddressPubKeyHash(addrHash[:], &chaincfg.TestNet3Params) if err != nil { t.Fatalf("NewAddressPubKeyHash: unexpected error: %v", err) } dummyPkScript, err := txscript.PayToAddrScript(addr) if err != nil { t.Fatalf("PayToAddrScript: unexpected error: %v", err) } dummyTxOut := wire.TxOut{ Value: 100000000, // 1 BTC PkScript: dummyPkScript, } tests := []struct { name string tx wire.MsgTx height int32 isStandard bool code wire.RejectCode }{ { name: "Typical pay-to-pubkey-hash transaction", tx: wire.MsgTx{ Version: 1, TxIn: []*wire.TxIn{&dummyTxIn}, TxOut: []*wire.TxOut{&dummyTxOut}, LockTime: 0, }, height: 300000, isStandard: true, }, { name: "Transaction version too high", tx: wire.MsgTx{ Version: wire.TxVersion + 1, TxIn: []*wire.TxIn{&dummyTxIn}, TxOut: []*wire.TxOut{&dummyTxOut}, LockTime: 0, }, height: 300000, isStandard: false, code: wire.RejectNonstandard, }, { name: "Transaction is not finalized", tx: wire.MsgTx{ Version: 1, TxIn: []*wire.TxIn{{ PreviousOutPoint: dummyPrevOut, SignatureScript: dummySigScript, Sequence: 0, }}, TxOut: []*wire.TxOut{&dummyTxOut}, LockTime: 300001, }, height: 300000, isStandard: false, code: wire.RejectNonstandard, }, { name: "Transaction size is too large", tx: wire.MsgTx{ Version: 1, TxIn: []*wire.TxIn{&dummyTxIn}, TxOut: []*wire.TxOut{{ Value: 0, PkScript: bytes.Repeat([]byte{0x00}, MaxStandardTxSize+1), }}, LockTime: 0, }, height: 300000, isStandard: false, code: wire.RejectNonstandard, }, { name: "Signature script size is too large", tx: wire.MsgTx{ Version: 1, TxIn: []*wire.TxIn{{ PreviousOutPoint: dummyPrevOut, SignatureScript: bytes.Repeat([]byte{0x00}, maxStandardSigScriptSize+1), Sequence: wire.MaxTxInSequenceNum, }}, TxOut: []*wire.TxOut{&dummyTxOut}, LockTime: 0, }, height: 300000, isStandard: false, code: wire.RejectNonstandard, }, { name: "Signature script that does more than push data", tx: wire.MsgTx{ Version: 1, TxIn: []*wire.TxIn{{ PreviousOutPoint: dummyPrevOut, SignatureScript: []byte{ txscript.OP_CHECKSIGVERIFY}, Sequence: wire.MaxTxInSequenceNum, }}, TxOut: []*wire.TxOut{&dummyTxOut}, LockTime: 0, }, height: 300000, isStandard: false, code: wire.RejectNonstandard, }, { name: "Valid but non standard public key script", tx: wire.MsgTx{ Version: 1, TxIn: []*wire.TxIn{&dummyTxIn}, TxOut: []*wire.TxOut{{ Value: 100000000, PkScript: []byte{txscript.OP_TRUE}, }}, LockTime: 0, }, height: 300000, isStandard: false, code: wire.RejectNonstandard, }, { name: "More than one nulldata output", tx: wire.MsgTx{ Version: 1, TxIn: []*wire.TxIn{&dummyTxIn}, TxOut: []*wire.TxOut{{ Value: 0, PkScript: []byte{txscript.OP_RETURN}, }, { Value: 0, PkScript: []byte{txscript.OP_RETURN}, }}, LockTime: 0, }, height: 300000, isStandard: false, code: wire.RejectNonstandard, }, { name: "Dust output", tx: wire.MsgTx{ Version: 1, TxIn: []*wire.TxIn{&dummyTxIn}, TxOut: []*wire.TxOut{{ Value: 0, PkScript: dummyPkScript, }}, LockTime: 0, }, height: 300000, isStandard: false, code: wire.RejectDust, }, { name: "One nulldata output with 0 amount (standard)", tx: wire.MsgTx{ Version: 1, TxIn: []*wire.TxIn{&dummyTxIn}, TxOut: []*wire.TxOut{{ Value: 0, PkScript: []byte{txscript.OP_RETURN}, }}, LockTime: 0, }, height: 300000, isStandard: true, }, } pastMedianTime := time.Now() for _, test := range tests { // Ensure standardness is as expected. err := checkTransactionStandard(btcutil.NewTx(&test.tx), test.height, pastMedianTime, DefaultMinRelayTxFee, 1) if err == nil && test.isStandard { // Test passes since function returned standard for a // transaction which is intended to be standard. continue } if err == nil && !test.isStandard { t.Errorf("checkTransactionStandard (%s): standard when "+ "it should not be", test.name) continue } if err != nil && test.isStandard { t.Errorf("checkTransactionStandard (%s): nonstandard "+ "when it should not be: %v", test.name, err) continue } // Ensure error type is a TxRuleError inside of a RuleError. rerr, ok := err.(RuleError) if !ok { t.Errorf("checkTransactionStandard (%s): unexpected "+ "error type - got %T", test.name, err) continue } txrerr, ok := rerr.Err.(TxRuleError) if !ok { t.Errorf("checkTransactionStandard (%s): unexpected "+ "error type - got %T", test.name, rerr.Err) continue } // Ensure the reject code is the expected one. if txrerr.RejectCode != test.code { t.Errorf("checkTransactionStandard (%s): unexpected "+ "error code - got %v, want %v", test.name, txrerr.RejectCode, test.code) continue } } }
// TestGetDataWireErrors performs negative tests against wire encode and decode // of MsgGetData to confirm error paths work correctly. func TestGetDataWireErrors(t *testing.T) { pver := ProtocolVersion wireErr := &MessageError{} // Block 203707 hash. hashStr := "3264bc2ac36a60840790ba1d475d01367e7c723da941069e9dc" blockHash, err := chainhash.NewHashFromStr(hashStr) if err != nil { t.Errorf("NewHashFromStr: %v", err) } iv := NewInvVect(InvTypeBlock, blockHash) // Base message used to induce errors. baseGetData := NewMsgGetData() baseGetData.AddInvVect(iv) baseGetDataEncoded := []byte{ 0x02, // Varint for number of inv vectors 0x02, 0x00, 0x00, 0x00, // InvTypeBlock 0xdc, 0xe9, 0x69, 0x10, 0x94, 0xda, 0x23, 0xc7, 0xe7, 0x67, 0x13, 0xd0, 0x75, 0xd4, 0xa1, 0x0b, 0x79, 0x40, 0x08, 0xa6, 0x36, 0xac, 0xc2, 0x4b, 0x26, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 203707 hash } // Message that forces an error by having more than the max allowed inv // vectors. maxGetData := NewMsgGetData() for i := 0; i < MaxInvPerMsg; i++ { maxGetData.AddInvVect(iv) } maxGetData.InvList = append(maxGetData.InvList, iv) maxGetDataEncoded := []byte{ 0xfd, 0x51, 0xc3, // Varint for number of inv vectors (50001) } tests := []struct { in *MsgGetData // Value to encode buf []byte // Wire encoding pver uint32 // Protocol version for wire encoding enc MessageEncoding // Message encoding format max int // Max size of fixed buffer to induce errors writeErr error // Expected write error readErr error // Expected read error }{ // Latest protocol version with intentional read/write errors. // Force error in inventory vector count {baseGetData, baseGetDataEncoded, pver, BaseEncoding, 0, io.ErrShortWrite, io.EOF}, // Force error in inventory list. {baseGetData, baseGetDataEncoded, pver, BaseEncoding, 1, io.ErrShortWrite, io.EOF}, // Force error with greater than max inventory vectors. {maxGetData, maxGetDataEncoded, pver, BaseEncoding, 3, wireErr, wireErr}, } t.Logf("Running %d tests", len(tests)) for i, test := range tests { // Encode to wire format. w := newFixedWriter(test.max) err := test.in.BtcEncode(w, test.pver, test.enc) if reflect.TypeOf(err) != reflect.TypeOf(test.writeErr) { t.Errorf("BtcEncode #%d wrong error got: %v, want: %v", i, err, test.writeErr) continue } // For errors which are not of type MessageError, check them for // equality. if _, ok := err.(*MessageError); !ok { if err != test.writeErr { t.Errorf("BtcEncode #%d wrong error got: %v, "+ "want: %v", i, err, test.writeErr) continue } } // Decode from wire format. var msg MsgGetData r := newFixedReader(test.max, test.buf) err = msg.BtcDecode(r, test.pver, test.enc) if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) { t.Errorf("BtcDecode #%d wrong error got: %v, want: %v", i, err, test.readErr) continue } // For errors which are not of type MessageError, check them for // equality. if _, ok := err.(*MessageError); !ok { if err != test.readErr { t.Errorf("BtcDecode #%d wrong error got: %v, "+ "want: %v", i, err, test.readErr) continue } } } }
// TestGetDataWire tests the MsgGetData wire encode and decode for various // numbers of inventory vectors and protocol versions. func TestGetDataWire(t *testing.T) { // Block 203707 hash. hashStr := "3264bc2ac36a60840790ba1d475d01367e7c723da941069e9dc" blockHash, err := chainhash.NewHashFromStr(hashStr) if err != nil { t.Errorf("NewHashFromStr: %v", err) } // Transation 1 of Block 203707 hash. hashStr = "d28a3dc7392bf00a9855ee93dd9a81eff82a2c4fe57fbd42cfe71b487accfaf0" txHash, err := chainhash.NewHashFromStr(hashStr) if err != nil { t.Errorf("NewHashFromStr: %v", err) } iv := NewInvVect(InvTypeBlock, blockHash) iv2 := NewInvVect(InvTypeTx, txHash) // Empty MsgGetData message. NoInv := NewMsgGetData() NoInvEncoded := []byte{ 0x00, // Varint for number of inventory vectors } // MsgGetData message with multiple inventory vectors. MultiInv := NewMsgGetData() MultiInv.AddInvVect(iv) MultiInv.AddInvVect(iv2) MultiInvEncoded := []byte{ 0x02, // Varint for number of inv vectors 0x02, 0x00, 0x00, 0x00, // InvTypeBlock 0xdc, 0xe9, 0x69, 0x10, 0x94, 0xda, 0x23, 0xc7, 0xe7, 0x67, 0x13, 0xd0, 0x75, 0xd4, 0xa1, 0x0b, 0x79, 0x40, 0x08, 0xa6, 0x36, 0xac, 0xc2, 0x4b, 0x26, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 203707 hash 0x01, 0x00, 0x00, 0x00, // InvTypeTx 0xf0, 0xfa, 0xcc, 0x7a, 0x48, 0x1b, 0xe7, 0xcf, 0x42, 0xbd, 0x7f, 0xe5, 0x4f, 0x2c, 0x2a, 0xf8, 0xef, 0x81, 0x9a, 0xdd, 0x93, 0xee, 0x55, 0x98, 0x0a, 0xf0, 0x2b, 0x39, 0xc7, 0x3d, 0x8a, 0xd2, // Tx 1 of block 203707 hash } tests := []struct { in *MsgGetData // Message to encode out *MsgGetData // Expected decoded message buf []byte // Wire encoding pver uint32 // Protocol version for wire encoding enc MessageEncoding // Message encoding format }{ // Latest protocol version with no inv vectors. { NoInv, NoInv, NoInvEncoded, ProtocolVersion, BaseEncoding, }, // Latest protocol version with multiple inv vectors. { MultiInv, MultiInv, MultiInvEncoded, ProtocolVersion, BaseEncoding, }, // Protocol version BIP0035Version no inv vectors. { NoInv, NoInv, NoInvEncoded, BIP0035Version, BaseEncoding, }, // Protocol version BIP0035Version with multiple inv vectors. { MultiInv, MultiInv, MultiInvEncoded, BIP0035Version, BaseEncoding, }, // Protocol version BIP0031Version no inv vectors. { NoInv, NoInv, NoInvEncoded, BIP0031Version, BaseEncoding, }, // Protocol version BIP0031Version with multiple inv vectors. { MultiInv, MultiInv, MultiInvEncoded, BIP0031Version, BaseEncoding, }, // Protocol version NetAddressTimeVersion no inv vectors. { NoInv, NoInv, NoInvEncoded, NetAddressTimeVersion, BaseEncoding, }, // Protocol version NetAddressTimeVersion with multiple inv vectors. { MultiInv, MultiInv, MultiInvEncoded, NetAddressTimeVersion, BaseEncoding, }, // Protocol version MultipleAddressVersion no inv vectors. { NoInv, NoInv, NoInvEncoded, MultipleAddressVersion, BaseEncoding, }, // Protocol version MultipleAddressVersion with multiple inv vectors. { MultiInv, MultiInv, MultiInvEncoded, MultipleAddressVersion, BaseEncoding, }, } t.Logf("Running %d tests", len(tests)) for i, test := range tests { // Encode the message to wire format. var buf bytes.Buffer err := test.in.BtcEncode(&buf, test.pver, test.enc) if err != nil { t.Errorf("BtcEncode #%d error %v", i, err) continue } if !bytes.Equal(buf.Bytes(), test.buf) { t.Errorf("BtcEncode #%d\n got: %s want: %s", i, spew.Sdump(buf.Bytes()), spew.Sdump(test.buf)) continue } // Decode the message from wire format. var msg MsgGetData rbuf := bytes.NewReader(test.buf) err = msg.BtcDecode(rbuf, test.pver, test.enc) if err != nil { t.Errorf("BtcDecode #%d error %v", i, err) continue } if !reflect.DeepEqual(&msg, test.out) { t.Errorf("BtcDecode #%d\n got: %s want: %s", i, spew.Sdump(msg), spew.Sdump(test.out)) continue } } }
// TestInvVectWire tests the InvVect wire encode and decode for various // protocol versions and supported inventory vector types. func TestInvVectWire(t *testing.T) { // Block 203707 hash. hashStr := "3264bc2ac36a60840790ba1d475d01367e7c723da941069e9dc" baseHash, err := chainhash.NewHashFromStr(hashStr) if err != nil { t.Errorf("NewHashFromStr: %v", err) } // errInvVect is an inventory vector with an error. errInvVect := InvVect{ Type: InvTypeError, Hash: chainhash.Hash{}, } // errInvVectEncoded is the wire encoded bytes of errInvVect. errInvVectEncoded := []byte{ 0x00, 0x00, 0x00, 0x00, // InvTypeError 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // No hash } // txInvVect is an inventory vector representing a transaction. txInvVect := InvVect{ Type: InvTypeTx, Hash: *baseHash, } // txInvVectEncoded is the wire encoded bytes of txInvVect. txInvVectEncoded := []byte{ 0x01, 0x00, 0x00, 0x00, // InvTypeTx 0xdc, 0xe9, 0x69, 0x10, 0x94, 0xda, 0x23, 0xc7, 0xe7, 0x67, 0x13, 0xd0, 0x75, 0xd4, 0xa1, 0x0b, 0x79, 0x40, 0x08, 0xa6, 0x36, 0xac, 0xc2, 0x4b, 0x26, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 203707 hash } // blockInvVect is an inventory vector representing a block. blockInvVect := InvVect{ Type: InvTypeBlock, Hash: *baseHash, } // blockInvVectEncoded is the wire encoded bytes of blockInvVect. blockInvVectEncoded := []byte{ 0x02, 0x00, 0x00, 0x00, // InvTypeBlock 0xdc, 0xe9, 0x69, 0x10, 0x94, 0xda, 0x23, 0xc7, 0xe7, 0x67, 0x13, 0xd0, 0x75, 0xd4, 0xa1, 0x0b, 0x79, 0x40, 0x08, 0xa6, 0x36, 0xac, 0xc2, 0x4b, 0x26, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 203707 hash } tests := []struct { in InvVect // NetAddress to encode out InvVect // Expected decoded NetAddress buf []byte // Wire encoding pver uint32 // Protocol version for wire encoding }{ // Latest protocol version error inventory vector. { errInvVect, errInvVect, errInvVectEncoded, ProtocolVersion, }, // Latest protocol version tx inventory vector. { txInvVect, txInvVect, txInvVectEncoded, ProtocolVersion, }, // Latest protocol version block inventory vector. { blockInvVect, blockInvVect, blockInvVectEncoded, ProtocolVersion, }, // Protocol version BIP0035Version error inventory vector. { errInvVect, errInvVect, errInvVectEncoded, BIP0035Version, }, // Protocol version BIP0035Version tx inventory vector. { txInvVect, txInvVect, txInvVectEncoded, BIP0035Version, }, // Protocol version BIP0035Version block inventory vector. { blockInvVect, blockInvVect, blockInvVectEncoded, BIP0035Version, }, // Protocol version BIP0031Version error inventory vector. { errInvVect, errInvVect, errInvVectEncoded, BIP0031Version, }, // Protocol version BIP0031Version tx inventory vector. { txInvVect, txInvVect, txInvVectEncoded, BIP0031Version, }, // Protocol version BIP0031Version block inventory vector. { blockInvVect, blockInvVect, blockInvVectEncoded, BIP0031Version, }, // Protocol version NetAddressTimeVersion error inventory vector. { errInvVect, errInvVect, errInvVectEncoded, NetAddressTimeVersion, }, // Protocol version NetAddressTimeVersion tx inventory vector. { txInvVect, txInvVect, txInvVectEncoded, NetAddressTimeVersion, }, // Protocol version NetAddressTimeVersion block inventory vector. { blockInvVect, blockInvVect, blockInvVectEncoded, NetAddressTimeVersion, }, // Protocol version MultipleAddressVersion error inventory vector. { errInvVect, errInvVect, errInvVectEncoded, MultipleAddressVersion, }, // Protocol version MultipleAddressVersion tx inventory vector. { txInvVect, txInvVect, txInvVectEncoded, MultipleAddressVersion, }, // Protocol version MultipleAddressVersion block inventory vector. { blockInvVect, blockInvVect, blockInvVectEncoded, MultipleAddressVersion, }, } t.Logf("Running %d tests", len(tests)) for i, test := range tests { // Encode to wire format. var buf bytes.Buffer err := writeInvVect(&buf, test.pver, &test.in) if err != nil { t.Errorf("writeInvVect #%d error %v", i, err) continue } if !bytes.Equal(buf.Bytes(), test.buf) { t.Errorf("writeInvVect #%d\n got: %s want: %s", i, spew.Sdump(buf.Bytes()), spew.Sdump(test.buf)) continue } // Decode the message from wire format. var iv InvVect rbuf := bytes.NewReader(test.buf) err = readInvVect(rbuf, test.pver, &iv) if err != nil { t.Errorf("readInvVect #%d error %v", i, err) continue } if !reflect.DeepEqual(iv, test.out) { t.Errorf("readInvVect #%d\n got: %s want: %s", i, spew.Sdump(iv), spew.Sdump(test.out)) continue } } }