diff --git a/README.md b/README.md index 2ce3404d..74c7dfe9 100644 --- a/README.md +++ b/README.md @@ -103,12 +103,6 @@ from a remote peer is: } ``` -## TODO - -- Implement alert message decoding/encoding -- Implement bloom filter messages (filterload, filteradd, filterclear, - merkleblock) as defined in [BIP 0037](https://en.bitcoin.it/wiki/BIP_0037) - ## GPG Verification Key All official release tags are signed by Conformal so users can ensure the code diff --git a/common.go b/common.go index b84afa32..f9582a94 100644 --- a/common.go +++ b/common.go @@ -130,6 +130,15 @@ func readElement(r io.Reader, element interface{}) error { } *e = BitcoinNet(binary.LittleEndian.Uint32(b)) return nil + + case *BloomUpdateType: + b := scratch[0:1] + _, err := io.ReadFull(r, b) + if err != nil { + return err + } + *e = BloomUpdateType(b[0]) + return nil } // Fall back to the slower binary.Read if a fast path was not available @@ -262,6 +271,15 @@ func writeElement(w io.Writer, element interface{}) error { return err } return nil + + case BloomUpdateType: + b := scratch[0:1] + b[0] = uint8(e) + _, err := w.Write(b) + if err != nil { + return err + } + return nil } // Fall back to the slower binary.Write if a fast path was not available diff --git a/doc.go b/doc.go index ac5e4b45..f43328f5 100644 --- a/doc.go +++ b/doc.go @@ -153,6 +153,7 @@ This package includes spec changes outlined by the following BIPs: BIP0014 (https://en.bitcoin.it/wiki/BIP_0014) BIP0031 (https://en.bitcoin.it/wiki/BIP_0031) BIP0035 (https://en.bitcoin.it/wiki/BIP_0035) + BIP0037 (https://en.bitcoin.it/wiki/BIP_0037) Other important information diff --git a/internal_test.go b/internal_test.go index 031d3068..7b59992c 100644 --- a/internal_test.go +++ b/internal_test.go @@ -15,21 +15,31 @@ import ( "io" ) -// MaxMessagePayload makes the internal maxMessagePayload constant available to -// the test package. -const MaxMessagePayload uint32 = maxMessagePayload +const ( + // MaxMessagePayload makes the internal maxMessagePayload constant + // available to the test package. + MaxMessagePayload uint32 = maxMessagePayload -// MaxCountSetCancel makes the internal maxCountSetCancel constant available to -// the test package. -const MaxCountSetCancel uint32 = maxCountSetCancel + // MaxTxPerBlock makes the internal maxTxPerBlock constant available to + // the test package. + MaxTxPerBlock = maxTxPerBlock -// MaxCountSetSubVer makes the internal maxCountSetSubVer constant available to -// the test package. -const MaxCountSetSubVer uint32 = maxCountSetSubVer + // MaxFlagsPerMerkleBlock makes the internal maxFlagsPerMerkleBlock + // constant available to the test package. + MaxFlagsPerMerkleBlock = maxFlagsPerMerkleBlock -// CommandSize makes the internal commandSize constant available to the test -// package. -const CommandSize = commandSize + // MaxCountSetCancel makes the internal maxCountSetCancel constant + // available to the test package. + MaxCountSetCancel = maxCountSetCancel + + // MaxCountSetSubVer makes the internal maxCountSetSubVer constant + // available to the test package. + MaxCountSetSubVer = maxCountSetSubVer + + // CommandSize makes the internal commandSize constant available to the + // test package. + CommandSize = commandSize +) // TstRandomUint64 makes the internal randomUint64 function available to the // test package. diff --git a/msgblock.go b/msgblock.go index 33186d8f..d03dc539 100644 --- a/msgblock.go +++ b/msgblock.go @@ -44,8 +44,6 @@ type MsgBlock struct { // AddTransaction adds a transaction to the message. func (msg *MsgBlock) AddTransaction(tx *MsgTx) error { - // TODO: Return error if adding the transaction would make the message - // too large. msg.Transactions = append(msg.Transactions, tx) return nil diff --git a/msgfilteradd.go b/msgfilteradd.go index 5e05177b..4b19a62e 100644 --- a/msgfilteradd.go +++ b/msgfilteradd.go @@ -16,8 +16,9 @@ const ( MaxFilterAddDataSize = 520 ) -// MsgFilterAdd implements the Message interface and represents a bitcoin filteradd -// message which is used to add a data element to an existing Bloom filter. +// MsgFilterAdd implements the Message interface and represents a bitcoin +// filteradd message. It is used to add a data element to an existing Bloom +// filter. // // This message was not added until protocol version BIP0037Version. type MsgFilterAdd struct { @@ -33,19 +34,9 @@ func (msg *MsgFilterAdd) BtcDecode(r io.Reader, pver uint32) error { return messageError("MsgFilterAdd.BtcDecode", str) } - size, err := readVarInt(r, pver) - if err != nil { - return err - } - - if size > MaxFilterAddDataSize { - str := fmt.Sprintf("filteradd size too large for message "+ - "[size %v, max %v]", size, MaxFilterAddDataSize) - return messageError("MsgFilterAdd.BtcDecode", str) - } - - msg.Data = make([]byte, size) - _, err = io.ReadFull(r, msg.Data) + var err error + msg.Data, err = readVarBytes(r, pver, MaxFilterAddDataSize, + "filteradd data") if err != nil { return err } @@ -69,12 +60,7 @@ func (msg *MsgFilterAdd) BtcEncode(w io.Writer, pver uint32) error { return messageError("MsgFilterAdd.BtcEncode", str) } - err := writeVarInt(w, pver, uint64(size)) - if err != nil { - return err - } - - err = writeElement(w, msg.Data) + err := writeVarBytes(w, pver, msg.Data) if err != nil { return err } @@ -91,11 +77,12 @@ func (msg *MsgFilterAdd) Command() string { // MaxPayloadLength returns the maximum length the payload can be for the // receiver. This is part of the Message interface implementation. func (msg *MsgFilterAdd) MaxPayloadLength(pver uint32) uint32 { - return MaxVarIntPayload + MaxFilterAddDataSize + return uint32(VarIntSerializeSize(MaxFilterAddDataSize)) + + MaxFilterAddDataSize } -// NewMsgFilterAdd returns a new bitcoin filteradd message that conforms to the Message -// interface. See MsgFilterAdd for details. +// NewMsgFilterAdd returns a new bitcoin filteradd message that conforms to the +// Message interface. See MsgFilterAdd for details. func NewMsgFilterAdd(data []byte) *MsgFilterAdd { return &MsgFilterAdd{ Data: data, diff --git a/msgfilteradd_test.go b/msgfilteradd_test.go index 9c111fc3..86429f7c 100644 --- a/msgfilteradd_test.go +++ b/msgfilteradd_test.go @@ -8,10 +8,12 @@ import ( "bytes" "github.com/conformal/btcwire" "io" + "reflect" "testing" ) -// TestFilterCLearLatest tests the MsgFilterAdd API against the latest protocol version. +// TestFilterAddLatest tests the MsgFilterAdd API against the latest protocol +// version. func TestFilterAddLatest(t *testing.T) { pver := btcwire.ProtocolVersion @@ -26,7 +28,7 @@ func TestFilterAddLatest(t *testing.T) { } // Ensure max payload is expected value for latest protocol version. - wantPayload := uint32(529) + wantPayload := uint32(523) maxPayload := msg.MaxPayloadLength(pver) if maxPayload != wantPayload { t.Errorf("MaxPayloadLength: wrong max payload length for "+ @@ -42,7 +44,7 @@ func TestFilterAddLatest(t *testing.T) { } // Test decode with latest protocol version. - readmsg := btcwire.MsgFilterAdd{} + var readmsg btcwire.MsgFilterAdd err = readmsg.BtcDecode(&buf, pver) if err != nil { t.Errorf("decode of MsgFilterAdd failed [%v] err <%v>", buf, err) @@ -51,27 +53,36 @@ func TestFilterAddLatest(t *testing.T) { return } -// TestFilterAddCrossProtocol tests the MsgFilterAdd API when encoding with the latest -// protocol version and decoded with BIP0031Version. +// TestFilterAddCrossProtocol tests the MsgFilterAdd API when encoding with the +// latest protocol version and decoding with BIP0031Version. func TestFilterAddCrossProtocol(t *testing.T) { data := []byte{0x01, 0x02} msg := btcwire.NewMsgFilterAdd(data) + if !bytes.Equal(msg.Data, data) { + t.Errorf("should get same data back out") + } - // Encode with old protocol version. + // Encode with latest protocol version. var buf bytes.Buffer - err := msg.BtcEncode(&buf, btcwire.BIP0037Version-1) - if err == nil { - t.Errorf("encode of MsgFilterAdd succeeded when it shouldn't have %v", - msg) + err := msg.BtcEncode(&buf, btcwire.ProtocolVersion) + if err != nil { + t.Errorf("encode of MsgFilterAdd failed %v err <%v>", msg, err) } // Decode with old protocol version. - readmsg := btcwire.MsgFilterAdd{} + var readmsg btcwire.MsgFilterAdd err = readmsg.BtcDecode(&buf, btcwire.BIP0031Version) if err == nil { - t.Errorf("decode of MsgFilterAdd succeeded when it shouldn't have %v", - msg) + t.Errorf("decode of MsgFilterAdd succeeded when it shouldn't "+ + "have %v", msg) } + + // Since one of the protocol versions doesn't support the filteradd + // message, make sure the data didn't get encoded and decoded back out. + if bytes.Equal(msg.Data, readmsg.Data) { + t.Error("should not get same data for cross protocol") + } + } // TestFilterAddMaxDataSize tests the MsgFilterAdd API maximum data size. @@ -83,16 +94,16 @@ func TestFilterAddMaxDataSize(t *testing.T) { var buf bytes.Buffer err := msg.BtcEncode(&buf, btcwire.ProtocolVersion) if err == nil { - t.Errorf("encode of MsgFilterAdd succeeded when it shouldn't have %v", - msg) + t.Errorf("encode of MsgFilterAdd succeeded when it shouldn't "+ + "have %v", msg) } // Decode with latest protocol version. readbuf := bytes.NewReader(data) err = msg.BtcDecode(readbuf, btcwire.ProtocolVersion) if err == nil { - t.Errorf("decode of MsgFilterAdd succeeded when it shouldn't have %v", - msg) + t.Errorf("decode of MsgFilterAdd succeeded when it shouldn't "+ + "have %v", msg) } } @@ -100,6 +111,12 @@ func TestFilterAddMaxDataSize(t *testing.T) { // of MsgFilterAdd to confirm error paths work correctly. func TestFilterAddWireErrors(t *testing.T) { pver := btcwire.ProtocolVersion + pverNoFilterAdd := btcwire.BIP0037Version - 1 + btcwireErr := &btcwire.MessageError{} + + baseData := []byte{0x01, 0x02, 0x03, 0x04} + baseFilterAdd := btcwire.NewMsgFilterAdd(baseData) + baseFilterAddEncoded := append([]byte{0x04}, baseData...) tests := []struct { in *btcwire.MsgFilterAdd // Value to encode @@ -110,38 +127,20 @@ func TestFilterAddWireErrors(t *testing.T) { readErr error // Expected read error }{ // Latest protocol version with intentional read/write errors. + // Force error in data size. { - &btcwire.MsgFilterAdd{Data: []byte{0x01, 0x02, 0x03, 0x04}}, - []byte{ - 0x05, // Varint for size of data - 0x02, 0x03, 0x04, // Data - }, - pver, - 2, - io.ErrShortWrite, - io.ErrUnexpectedEOF, + baseFilterAdd, baseFilterAddEncoded, pver, 0, + io.ErrShortWrite, io.EOF, }, + // Force error in data. { - &btcwire.MsgFilterAdd{Data: []byte{0x01, 0x02, 0x03, 0x04}}, - []byte{ - 0x05, // Varint for size of data - 0x02, 0x03, 0x04, // Data - }, - pver, - 0, - io.ErrShortWrite, - io.EOF, + baseFilterAdd, baseFilterAddEncoded, pver, 1, + io.ErrShortWrite, io.EOF, }, + // Force error due to unsupported protocol version. { - &btcwire.MsgFilterAdd{Data: []byte{0x01, 0x02, 0x03, 0x04}}, - []byte{ - 0x05, // Varint for size of data - 0x02, 0x03, 0x04, // Data - }, - pver, - 1, - io.ErrShortWrite, - io.EOF, + baseFilterAdd, baseFilterAddEncoded, pverNoFilterAdd, 5, + btcwireErr, btcwireErr, }, } @@ -150,20 +149,40 @@ func TestFilterAddWireErrors(t *testing.T) { // Encode to wire format. w := newFixedWriter(test.max) err := test.in.BtcEncode(w, test.pver) - if err != test.writeErr { + 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 btcwire.MessageError, check + // them for equality. + if _, ok := err.(*btcwire.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 btcwire.MsgFilterAdd r := newFixedReader(test.max, test.buf) err = msg.BtcDecode(r, test.pver) - if err != test.readErr { + 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 btcwire.MessageError, check + // them for equality. + if _, ok := err.(*btcwire.MessageError); !ok { + if err != test.readErr { + t.Errorf("BtcDecode #%d wrong error got: %v, "+ + "want: %v", i, err, test.readErr) + continue + } + } } } diff --git a/msgfilterclear.go b/msgfilterclear.go index ea7cfcac..5d736b6e 100644 --- a/msgfilterclear.go +++ b/msgfilterclear.go @@ -9,10 +9,11 @@ import ( "io" ) -// MsgFilterClear implements the Message interface and represents a bitcoin filterclear -// message which is used to reset a Bloom filter. +// MsgFilterClear implements the Message interface and represents a bitcoin +// filterclear message which is used to reset a Bloom filter. // -// This message was not added until protocol version BIP0037Version. +// This message was not added until protocol version BIP0037Version and has +// no payload. type MsgFilterClear struct{} // BtcDecode decodes r using the bitcoin protocol encoding into the receiver. diff --git a/msgfilterclear_test.go b/msgfilterclear_test.go index d8cf3396..8579342c 100644 --- a/msgfilterclear_test.go +++ b/msgfilterclear_test.go @@ -7,10 +7,13 @@ package btcwire_test import ( "bytes" "github.com/conformal/btcwire" + "github.com/davecgh/go-spew/spew" + "reflect" "testing" ) -// TestFilterCLearLatest tests the MsgFilterClear API against the latest protocol version. +// TestFilterCLearLatest tests the MsgFilterClear API against the latest +// protocol version. func TestFilterClearLatest(t *testing.T) { pver := btcwire.ProtocolVersion @@ -32,41 +35,162 @@ func TestFilterClearLatest(t *testing.T) { maxPayload, wantPayload) } - // Test encode with latest protocol version. + return +} + +// TestFilterClearCrossProtocol tests the MsgFilterClear API when encoding with +// the latest protocol version and decoding with BIP0031Version. +func TestFilterClearCrossProtocol(t *testing.T) { + msg := btcwire.NewMsgFilterClear() + + // Encode with latest protocol version. var buf bytes.Buffer - err := msg.BtcEncode(&buf, pver) + err := msg.BtcEncode(&buf, btcwire.ProtocolVersion) if err != nil { t.Errorf("encode of MsgFilterClear failed %v err <%v>", msg, err) } - // Test decode with latest protocol version. - readmsg := btcwire.NewMsgFilterClear() - err = readmsg.BtcDecode(&buf, pver) - if err != nil { - t.Errorf("decode of MsgFilterClear failed [%v] err <%v>", buf, err) - } - - return -} - -// TestFilterClearCrossProtocol tests the MsgFilterClear API when encoding with the latest -// protocol version and decoded with BIP0031Version. -func TestFilterClearCrossProtocol(t *testing.T) { - msg := btcwire.NewMsgFilterClear() - - // Encode with old protocol version. - var buf bytes.Buffer - err := msg.BtcEncode(&buf, btcwire.BIP0037Version-1) - if err == nil { - t.Errorf("encode of MsgFilterClear succeeded when it shouldn't have %v", - msg) - } - // Decode with old protocol version. - readmsg := btcwire.NewMsgFilterClear() + var readmsg btcwire.MsgFilterClear err = readmsg.BtcDecode(&buf, btcwire.BIP0031Version) if err == nil { - t.Errorf("decode of MsgFilterClear succeeded when it shouldn't have %v", - msg) + t.Errorf("decode of MsgFilterClear succeeded when it "+ + "shouldn't have %v", msg) + } +} + +// TestFilterClearWire tests the MsgFilterClear wire encode and decode for +// various protocol versions. +func TestFilterClearWire(t *testing.T) { + msgFilterClear := btcwire.NewMsgFilterClear() + msgFilterClearEncoded := []byte{} + + tests := []struct { + in *btcwire.MsgFilterClear // Message to encode + out *btcwire.MsgFilterClear // Expected decoded message + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + }{ + // Latest protocol version. + { + msgFilterClear, + msgFilterClear, + msgFilterClearEncoded, + btcwire.ProtocolVersion, + }, + + // Protocol version BIP0037Version + 1. + { + msgFilterClear, + msgFilterClear, + msgFilterClearEncoded, + btcwire.BIP0037Version + 1, + }, + + // Protocol version BIP0037Version. + { + msgFilterClear, + msgFilterClear, + msgFilterClearEncoded, + btcwire.BIP0037Version, + }, + } + + 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) + 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 btcwire.MsgFilterClear + rbuf := bytes.NewBuffer(test.buf) + err = msg.BtcDecode(rbuf, test.pver) + 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 + } + } +} + +// TestFilterClearWireErrors performs negative tests against wire encode and +// decode of MsgFilterClear to confirm error paths work correctly. +func TestFilterClearWireErrors(t *testing.T) { + pverNoFilterClear := btcwire.BIP0037Version - 1 + btcwireErr := &btcwire.MessageError{} + + baseFilterClear := btcwire.NewMsgFilterClear() + baseFilterClearEncoded := []byte{} + + tests := []struct { + in *btcwire.MsgFilterClear // Value to encode + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + max int // Max size of fixed buffer to induce errors + writeErr error // Expected write error + readErr error // Expected read error + }{ + // Force error due to unsupported protocol version. + { + baseFilterClear, baseFilterClearEncoded, + pverNoFilterClear, 4, btcwireErr, btcwireErr, + }, + } + + 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) + 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 btcwire.MessageError, check + // them for equality. + if _, ok := err.(*btcwire.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 btcwire.MsgFilterClear + r := newFixedReader(test.max, test.buf) + err = msg.BtcDecode(r, test.pver) + 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 btcwire.MessageError, check + // them for equality. + if _, ok := err.(*btcwire.MessageError); !ok { + if err != test.readErr { + t.Errorf("BtcDecode #%d wrong error got: %v, "+ + "want: %v", i, err, test.readErr) + continue + } + } + } } diff --git a/msgfilterload.go b/msgfilterload.go index 412476b4..35725f19 100644 --- a/msgfilterload.go +++ b/msgfilterload.go @@ -9,33 +9,37 @@ import ( "io" ) +// BloomUpdateType specifies how the filter is updated when a match is found type BloomUpdateType uint8 const ( - // BloomUpdateNone indicates the filter is not adjusted when a match is found. + // BloomUpdateNone indicates the filter is not adjusted when a match is + // found. BloomUpdateNone BloomUpdateType = 0 // BloomUpdateAll indicates if the filter matches any data element in a - // scriptPubKey, the outpoint is serialized and inserted into the filter. + // public key script, the outpoint is serialized and inserted into the + // filter. BloomUpdateAll BloomUpdateType = 1 - // BloomUpdateP2PubkeyOnly indicates if the filter matches a data element in - // a scriptPubkey and the script is of the standard payToPybKey or payToMultiSig, - // the outpoint is inserted into the filter. + // BloomUpdateP2PubkeyOnly indicates if the filter matches a data + // element in a public key script and the script is of the standard + // pay-to-pubkey or multisig, the outpoint is serialized and inserted + // into the filter. BloomUpdateP2PubkeyOnly BloomUpdateType = 2 ) const ( - // MaxFilterLoadHashFuncs is the maximum number of hash functions to load - // into the Bloom filter. + // MaxFilterLoadHashFuncs is the maximum number of hash functions to + // load into the Bloom filter. MaxFilterLoadHashFuncs = 50 // MaxFilterLoadFilterSize is the maximum size in bytes a filter may be. MaxFilterLoadFilterSize = 36000 ) -// MsgFilterLoad implements the Message interface and represents a bitcoin filterload -// message which is used to reset a Bloom filter. +// MsgFilterLoad implements the Message interface and represents a bitcoin +// filterload message which is used to reset a Bloom filter. // // This message was not added until protocol version BIP0037Version. type MsgFilterLoad struct { @@ -54,19 +58,9 @@ func (msg *MsgFilterLoad) BtcDecode(r io.Reader, pver uint32) error { return messageError("MsgFilterLoad.BtcDecode", str) } - // Read num filter and limit to max. - size, err := readVarInt(r, pver) - if err != nil { - return err - } - if size > MaxFilterLoadFilterSize { - str := fmt.Sprintf("filterload filter size too large for message "+ - "[size %v, max %v]", size, MaxFilterLoadFilterSize) - return messageError("MsgFilterLoad.BtcDecode", str) - } - - msg.Filter = make([]byte, size) - _, err = io.ReadFull(r, msg.Filter) + var err error + msg.Filter, err = readVarBytes(r, pver, MaxFilterLoadFilterSize, + "filterload filter size") if err != nil { return err } @@ -107,11 +101,12 @@ func (msg *MsgFilterLoad) BtcEncode(w io.Writer, pver uint32) error { return messageError("MsgFilterLoad.BtcEncode", str) } - err := writeVarInt(w, pver, uint64(size)) + err := writeVarBytes(w, pver, msg.Filter) if err != nil { return err } - err = writeElements(w, msg.Filter, msg.HashFuncs, msg.Tweak, msg.Flags) + + err = writeElements(w, msg.HashFuncs, msg.Tweak, msg.Flags) if err != nil { return err } @@ -119,23 +114,6 @@ func (msg *MsgFilterLoad) BtcEncode(w io.Writer, pver uint32) error { return nil } -// Serialize encodes the transaction to w using a format that suitable for -// long-term storage such as a database while respecting the Version field in -// the transaction. This function differs from BtcEncode in that BtcEncode -// encodes the transaction to the bitcoin wire protocol in order to be sent -// across the network. The wire encoding can technically differ depending on -// the protocol version and doesn't even really need to match the format of a -// stored transaction at all. As of the time this comment was written, the -// encoded transaction is the same in both instances, but there is a distinct -// difference and separating the two allows the API to be flexible enough to -// deal with changes. -func (msg *MsgFilterLoad) Serialize(w io.Writer) error { - // At the current time, there is no difference between the wire encoding - // at protocol version 0 and the stable long-term storage format. As - // a result, make use of BtcEncode. - return msg.BtcEncode(w, BIP0037Version) -} - // Command returns the protocol command string for the message. This is part // of the Message interface implementation. func (msg *MsgFilterLoad) Command() string { @@ -145,11 +123,14 @@ func (msg *MsgFilterLoad) Command() string { // MaxPayloadLength returns the maximum length the payload can be for the // receiver. This is part of the Message interface implementation. func (msg *MsgFilterLoad) MaxPayloadLength(pver uint32) uint32 { - return MaxVarIntPayload + MaxFilterLoadFilterSize + 4 + 4 + 1 + // Num filter bytes (varInt) + filter + 4 bytes hash funcs + + // 4 bytes tweak + 1 byte flags. + return uint32(VarIntSerializeSize(MaxFilterLoadFilterSize)) + + MaxFilterLoadFilterSize + 9 } -// NewMsgFilterLoad returns a new bitcoin filterload message that conforms to the Message -// interface. See MsgFilterLoad for details. +// NewMsgFilterLoad returns a new bitcoin filterload message that conforms to +// the Message interface. See MsgFilterLoad for details. func NewMsgFilterLoad(filter []byte, hashFuncs uint32, tweak uint32, flags BloomUpdateType) *MsgFilterLoad { return &MsgFilterLoad{ Filter: filter, diff --git a/msgfilterload_test.go b/msgfilterload_test.go index 2bf387fe..4328b7b1 100644 --- a/msgfilterload_test.go +++ b/msgfilterload_test.go @@ -8,10 +8,12 @@ import ( "bytes" "github.com/conformal/btcwire" "io" + "reflect" "testing" ) -// TestFilterCLearLatest tests the MsgFilterLoad API against the latest protocol version. +// TestFilterCLearLatest tests the MsgFilterLoad API against the latest protocol +// version. func TestFilterLoadLatest(t *testing.T) { pver := btcwire.ProtocolVersion @@ -25,8 +27,8 @@ func TestFilterLoadLatest(t *testing.T) { cmd, wantCmd) } - // Ensure max payadd is expected value for latest protocol version. - wantPayload := uint32(36018) + // Ensure max payload is expected value for latest protocol version. + wantPayload := uint32(36012) maxPayload := msg.MaxPayloadLength(pver) if maxPayload != wantPayload { t.Errorf("MaxPayLoadLength: wrong max payload length for "+ @@ -51,22 +53,22 @@ func TestFilterLoadLatest(t *testing.T) { return } -// TestFilterLoadCrossProtocol tests the MsgFilterLoad API when encoding with the latest -// protocol version and decoded with BIP0031Version. +// TestFilterLoadCrossProtocol tests the MsgFilterLoad API when encoding with +// the latest protocol version and decoding with BIP0031Version. func TestFilterLoadCrossProtocol(t *testing.T) { data := []byte{0x01, 0x02} msg := btcwire.NewMsgFilterLoad(data, 10, 0, 0) - // Encode with old protocol version. + // Encode with latest protocol version. var buf bytes.Buffer - err := msg.BtcEncode(&buf, btcwire.BIP0037Version-1) - if err == nil { - t.Errorf("encode of MsgFilterLoad succeeded when it shouldn't have %v", - msg) + err := msg.BtcEncode(&buf, btcwire.ProtocolVersion) + if err != nil { + t.Errorf("encode of NewMsgFilterLoad failed %v err <%v>", msg, + err) } // Decode with old protocol version. - readmsg := btcwire.MsgFilterLoad{} + var readmsg btcwire.MsgFilterLoad err = readmsg.BtcDecode(&buf, btcwire.BIP0031Version) if err == nil { t.Errorf("decode of MsgFilterLoad succeeded when it shouldn't have %v", @@ -83,16 +85,16 @@ func TestFilterLoadMaxFilterSize(t *testing.T) { var buf bytes.Buffer err := msg.BtcEncode(&buf, btcwire.ProtocolVersion) if err == nil { - t.Errorf("encode of MsgFilterLoad succeeded when it shouldn't have %v", - msg) + t.Errorf("encode of MsgFilterLoad succeeded when it shouldn't "+ + "have %v", msg) } // Decode with latest protocol version. readbuf := bytes.NewReader(data) err = msg.BtcDecode(readbuf, btcwire.ProtocolVersion) if err == nil { - t.Errorf("decode of MsgFilterLoad succeeded when it shouldn't have %v", - msg) + t.Errorf("decode of MsgFilterLoad succeeded when it shouldn't "+ + "have %v", msg) } } @@ -130,6 +132,17 @@ func TestFilterLoadMaxHashFuncsSize(t *testing.T) { // of MsgFilterLoad to confirm error paths work correctly. func TestFilterLoadWireErrors(t *testing.T) { pver := btcwire.ProtocolVersion + pverNoFilterLoad := btcwire.BIP0037Version - 1 + btcwireErr := &btcwire.MessageError{} + + baseFilter := []byte{0x01, 0x02, 0x03, 0x04} + baseFilterLoad := btcwire.NewMsgFilterLoad(baseFilter, 10, 0, + btcwire.BloomUpdateNone) + baseFilterLoadEncoded := append([]byte{0x04}, baseFilter...) + baseFilterLoadEncoded = append(baseFilterLoadEncoded, + 0x00, 0x00, 0x00, 0x0a, // HashFuncs + 0x00, 0x00, 0x00, 0x00, // Tweak + 0x00) // Flags tests := []struct { in *btcwire.MsgFilterLoad // Value to encode @@ -140,62 +153,35 @@ func TestFilterLoadWireErrors(t *testing.T) { readErr error // Expected read error }{ // Latest protocol version with intentional read/write errors. + // Force error in filter size. { - &btcwire.MsgFilterLoad{ - Filter: []byte{0x01, 0x02, 0x03, 0x04}, - HashFuncs: 10, - Tweak: 0, - Flags: btcwire.BloomUpdateNone, - }, - []byte{ - 0x04, // Varint for size of Filter - 0x01, 0x02, 0x03, 0x04, // Filter - 0x00, 0x0a, // HashFuncs - 0x00, 0x00, // Tweak - 0x00, // Flags - }, - pver, - 2, - io.ErrShortWrite, - io.ErrUnexpectedEOF, + baseFilterLoad, baseFilterLoadEncoded, pver, 0, + io.ErrShortWrite, io.EOF, }, + // Force error in filter. { - &btcwire.MsgFilterLoad{ - Filter: []byte{0x01, 0x02, 0x03, 0x04}, - HashFuncs: 10, - Tweak: 0, - Flags: btcwire.BloomUpdateNone, - }, - []byte{ - 0x04, // Varint for size of Filter - 0x01, 0x02, 0x03, 0x04, // Filter - 0x00, 0x0a, // HashFuncs - 0x00, 0x00, // Tweak - 0x00, // Flags - }, - pver, - 0, - io.ErrShortWrite, - io.EOF, + baseFilterLoad, baseFilterLoadEncoded, pver, 1, + io.ErrShortWrite, io.EOF, }, + // Force error in hash funcs. { - &btcwire.MsgFilterLoad{ - Filter: []byte{0x01, 0x02, 0x03, 0x04}, - HashFuncs: 10, - Tweak: 0, - Flags: btcwire.BloomUpdateNone, - }, - []byte{ - 0x04, // Varint for size of Filter - 0x01, 0x02, 0x03, 0x04, // Filter - 0x00, 0x0a, // HashFuncs - 0x00, 0x00, // Tweak - 0x00, // Flags - }, - pver, - 10, - io.ErrShortWrite, - io.ErrUnexpectedEOF, + baseFilterLoad, baseFilterLoadEncoded, pver, 5, + io.ErrShortWrite, io.EOF, + }, + // Force error in tweak. + { + baseFilterLoad, baseFilterLoadEncoded, pver, 9, + io.ErrShortWrite, io.EOF, + }, + // Force error in flags. + { + baseFilterLoad, baseFilterLoadEncoded, pver, 13, + io.ErrShortWrite, io.EOF, + }, + // Force error due to unsupported protocol version. + { + baseFilterLoad, baseFilterLoadEncoded, pverNoFilterLoad, + 10, btcwireErr, btcwireErr, }, } @@ -204,20 +190,41 @@ func TestFilterLoadWireErrors(t *testing.T) { // Encode to wire format. w := newFixedWriter(test.max) err := test.in.BtcEncode(w, test.pver) - if err != test.writeErr { + 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 btcwire.MessageError, check + // them for equality. + if _, ok := err.(*btcwire.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 btcwire.MsgFilterLoad r := newFixedReader(test.max, test.buf) err = msg.BtcDecode(r, test.pver) - if err != test.readErr { + 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 btcwire.MessageError, check + // them for equality. + if _, ok := err.(*btcwire.MessageError); !ok { + if err != test.readErr { + t.Errorf("BtcDecode #%d wrong error got: %v, "+ + "want: %v", i, err, test.readErr) + continue + } + } + } } diff --git a/msgmerkleblock.go b/msgmerkleblock.go index a4cc8376..18b579fb 100644 --- a/msgmerkleblock.go +++ b/msgmerkleblock.go @@ -9,8 +9,14 @@ import ( "io" ) -// MsgMerkleBlock implements the Message interface and represents a bitcoin merkleblock -// message which is used to reset a Bloom filter. +// maxFlagsPerMerkleBlock is the maximum number of flag bytes that could +// possibly fit into a merkle block. Since each transaction is represented by +// a single bit, this is the max number of transactions per block divided by +// 8 bits per byte. Then an extra one to cover partials. +const maxFlagsPerMerkleBlock = maxTxPerBlock / 8 + +// MsgMerkleBlock implements the Message interface and represents a bitcoin +// merkleblock message which is used to reset a Bloom filter. // // This message was not added until protocol version BIP0037Version. type MsgMerkleBlock struct { @@ -64,7 +70,7 @@ func (msg *MsgMerkleBlock) BtcDecode(r io.Reader, pver uint32) error { msg.Hashes = make([]*ShaHash, 0, count) for i := uint64(0); i < count; i++ { - sha := ShaHash{} + var sha ShaHash err := readElement(r, &sha) if err != nil { return err @@ -72,13 +78,8 @@ func (msg *MsgMerkleBlock) BtcDecode(r io.Reader, pver uint32) error { msg.AddTxHash(&sha) } - count, err = readVarInt(r, pver) - if err != nil { - return err - } - - msg.Flags = make([]byte, 0, count) - err = readElement(r, &msg.Flags) + msg.Flags, err = readVarBytes(r, pver, maxFlagsPerMerkleBlock, + "merkle block flags size") if err != nil { return err } @@ -96,10 +97,16 @@ func (msg *MsgMerkleBlock) BtcEncode(w io.Writer, pver uint32) error { } // Read num transaction hashes and limit to max. - count := len(msg.Hashes) - if count > maxTxPerBlock { + numHashes := len(msg.Hashes) + if numHashes > maxTxPerBlock { str := fmt.Sprintf("too many transaction hashes for message "+ - "[count %v, max %v]", count, maxTxPerBlock) + "[count %v, max %v]", numHashes, maxTxPerBlock) + return messageError("MsgMerkleBlock.BtcDecode", str) + } + numFlagBytes := len(msg.Flags) + if numFlagBytes > maxFlagsPerMerkleBlock { + str := fmt.Sprintf("too many flag bytes for message [count %v, "+ + "max %v]", numFlagBytes, maxFlagsPerMerkleBlock) return messageError("MsgMerkleBlock.BtcDecode", str) } @@ -113,11 +120,10 @@ func (msg *MsgMerkleBlock) BtcEncode(w io.Writer, pver uint32) error { return err } - err = writeVarInt(w, pver, uint64(count)) + err = writeVarInt(w, pver, uint64(numHashes)) if err != nil { return err } - for _, hash := range msg.Hashes { err = writeElement(w, hash) if err != nil { @@ -125,12 +131,10 @@ func (msg *MsgMerkleBlock) BtcEncode(w io.Writer, pver uint32) error { } } - count = len(msg.Flags) - err = writeVarInt(w, pver, uint64(count)) + err = writeVarInt(w, pver, uint64(numFlagBytes)) if err != nil { return err } - err = writeElement(w, msg.Flags) if err != nil { return err @@ -151,8 +155,8 @@ func (msg *MsgMerkleBlock) MaxPayloadLength(pver uint32) uint32 { return MaxBlockPayload } -// NewMsgMerkleBlock returns a new bitcoin merkleblock message that conforms to the Message -// interface. See MsgMerkleBlock for details. +// NewMsgMerkleBlock returns a new bitcoin merkleblock message that conforms to +// the Message interface. See MsgMerkleBlock for details. func NewMsgMerkleBlock(bh *BlockHeader) *MsgMerkleBlock { return &MsgMerkleBlock{ Header: *bh, diff --git a/msgmerkleblock_test.go b/msgmerkleblock_test.go index 79144ce5..e4040d05 100644 --- a/msgmerkleblock_test.go +++ b/msgmerkleblock_test.go @@ -8,13 +8,16 @@ import ( "bytes" "crypto/rand" "github.com/conformal/btcwire" + "github.com/davecgh/go-spew/spew" + "io" + "reflect" "testing" + "time" ) // TestMerkleBlock tests the MsgMerkleBlock API. func TestMerkleBlock(t *testing.T) { pver := btcwire.ProtocolVersion - pverOld := btcwire.BIP0037Version - 1 // Block 1 header. prevHash := &blockOne.Header.PrevBlock @@ -43,7 +46,7 @@ func TestMerkleBlock(t *testing.T) { // Load maxTxPerBlock hashes data := make([]byte, 32) - for i := 0; i < (btcwire.MaxBlockPayload/10)+1; i++ { + for i := 0; i < btcwire.MaxTxPerBlock; i++ { rand.Read(data) hash, err := btcwire.NewShaHash(data) if err != nil { @@ -57,7 +60,7 @@ func TestMerkleBlock(t *testing.T) { } } - // Add one more Tx to test failure + // Add one more Tx to test failure. rand.Read(data) hash, err := btcwire.NewShaHash(data) if err != nil { @@ -77,13 +80,6 @@ func TestMerkleBlock(t *testing.T) { t.Errorf("encode of MsgMerkleBlock failed %v err <%v>", msg, err) } - // Test encode with old protocol version. - if err = msg.BtcEncode(&buf, pverOld); err == nil { - t.Errorf("encode of MsgMerkleBlock succeeded with old protocol " + - "version when it should have failed") - return - } - // Test decode with latest protocol version. readmsg := btcwire.MsgMerkleBlock{} err = readmsg.BtcDecode(&buf, pver) @@ -91,19 +87,339 @@ func TestMerkleBlock(t *testing.T) { t.Errorf("decode of MsgMerkleBlock failed [%v] err <%v>", buf, err) } - // Test decode with old protocol version. - if err = readmsg.BtcDecode(&buf, pverOld); err == nil { - t.Errorf("decode of MsgMerkleBlock successed with old protocol " + - "version when it should have failed") - return - } - - // Force extra hash to test maxTxPerBlock + // Force extra hash to test maxTxPerBlock. msg.Hashes = append(msg.Hashes, hash) err = msg.BtcEncode(&buf, pver) if err == nil { - t.Errorf("encode of MsgMerkleBlock succeeded with too many tx hashes " + - "when it should have failed") + t.Errorf("encode of MsgMerkleBlock succeeded with too many " + + "tx hashes when it should have failed") + return + } + + // Force too many flag bytes to test maxFlagsPerMerkleBlock. + // Reset the number of hashes back to a valid value. + msg.Hashes = msg.Hashes[len(msg.Hashes)-1:] + msg.Flags = make([]byte, btcwire.MaxFlagsPerMerkleBlock+1) + err = msg.BtcEncode(&buf, pver) + if err == nil { + t.Errorf("encode of MsgMerkleBlock succeeded with too many " + + "flag bytes when it should have failed") return } } + +// TestMerkleBlockCrossProtocol tests the MsgMerkleBlock API when encoding with +// the latest protocol version and decoding with BIP0031Version. +func TestMerkleBlockCrossProtocol(t *testing.T) { + // Block 1 header. + prevHash := &blockOne.Header.PrevBlock + merkleHash := &blockOne.Header.MerkleRoot + bits := blockOne.Header.Bits + nonce := blockOne.Header.Nonce + bh := btcwire.NewBlockHeader(prevHash, merkleHash, bits, nonce) + + msg := btcwire.NewMsgMerkleBlock(bh) + + // Encode with latest protocol version. + var buf bytes.Buffer + err := msg.BtcEncode(&buf, btcwire.ProtocolVersion) + if err != nil { + t.Errorf("encode of NewMsgFilterLoad failed %v err <%v>", msg, + err) + } + + // Decode with old protocol version. + var readmsg btcwire.MsgFilterLoad + err = readmsg.BtcDecode(&buf, btcwire.BIP0031Version) + if err == nil { + t.Errorf("decode of MsgFilterLoad succeeded when it shouldn't have %v", + msg) + } +} + +// TestMerkleBlockWire tests the MsgMerkleBlock wire encode and decode for +// various numbers of transaction hashes and protocol versions. +func TestMerkleBlockWire(t *testing.T) { + tests := []struct { + in *btcwire.MsgMerkleBlock // Message to encode + out *btcwire.MsgMerkleBlock // Expected decoded message + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + }{ + // Latest protocol version. + { + &merkleBlockOne, &merkleBlockOne, merkleBlockOneBytes, + btcwire.ProtocolVersion, + }, + + // Protocol version BIP0037Version. + { + &merkleBlockOne, &merkleBlockOne, merkleBlockOneBytes, + btcwire.BIP0037Version, + }, + } + + 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) + 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 btcwire.MsgMerkleBlock + rbuf := bytes.NewBuffer(test.buf) + err = msg.BtcDecode(rbuf, test.pver) + 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 + } + } +} + +// TestMerkleBlockWireErrors performs negative tests against wire encode and +// decode of MsgBlock to confirm error paths work correctly. +func TestMerkleBlockWireErrors(t *testing.T) { + // Use protocol version 70001 specifically here instead of the latest + // because the test data is using bytes encoded with that protocol + // version. + pver := uint32(70001) + pverNoMerkleBlock := btcwire.BIP0037Version - 1 + btcwireErr := &btcwire.MessageError{} + + tests := []struct { + in *btcwire.MsgMerkleBlock // Value to encode + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + max int // Max size of fixed buffer to induce errors + writeErr error // Expected write error + readErr error // Expected read error + }{ + // Force error in version. + { + &merkleBlockOne, merkleBlockOneBytes, pver, 0, + io.ErrShortWrite, io.EOF, + }, + // Force error in prev block hash. + { + &merkleBlockOne, merkleBlockOneBytes, pver, 4, + io.ErrShortWrite, io.EOF, + }, + // Force error in merkle root. + { + &merkleBlockOne, merkleBlockOneBytes, pver, 36, + io.ErrShortWrite, io.EOF, + }, + // Force error in timestamp. + { + &merkleBlockOne, merkleBlockOneBytes, pver, 68, + io.ErrShortWrite, io.EOF, + }, + // Force error in difficulty bits. + { + &merkleBlockOne, merkleBlockOneBytes, pver, 72, + io.ErrShortWrite, io.EOF, + }, + // Force error in header nonce. + { + &merkleBlockOne, merkleBlockOneBytes, pver, 76, + io.ErrShortWrite, io.EOF, + }, + // Force error in transaction count. + { + &merkleBlockOne, merkleBlockOneBytes, pver, 80, + io.ErrShortWrite, io.EOF, + }, + // Force error in num hashes. + { + &merkleBlockOne, merkleBlockOneBytes, pver, 84, + io.ErrShortWrite, io.EOF, + }, + // Force error in hashes. + { + &merkleBlockOne, merkleBlockOneBytes, pver, 85, + io.ErrShortWrite, io.EOF, + }, + // Force error in num flag bytes. + { + &merkleBlockOne, merkleBlockOneBytes, pver, 117, + io.ErrShortWrite, io.EOF, + }, + // Force error in flag bytes. + { + &merkleBlockOne, merkleBlockOneBytes, pver, 118, + io.ErrShortWrite, io.EOF, + }, + // Force error due to unsupported protocol version. + { + &merkleBlockOne, merkleBlockOneBytes, pverNoMerkleBlock, + 119, btcwireErr, btcwireErr, + }, + } + + 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) + 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 btcwire.MessageError, check + // them for equality. + if _, ok := err.(*btcwire.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 btcwire.MsgMerkleBlock + r := newFixedReader(test.max, test.buf) + err = msg.BtcDecode(r, test.pver) + 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 btcwire.MessageError, check + // them for equality. + if _, ok := err.(*btcwire.MessageError); !ok { + if err != test.readErr { + t.Errorf("BtcDecode #%d wrong error got: %v, "+ + "want: %v", i, err, test.readErr) + continue + } + } + } +} + +// TestMerkleBlockOverflowErrors performs tests to ensure encoding and decoding +// merkle blocks that are intentionally crafted to use large values for the +// number of hashes and flags are handled properly. This could otherwise +// potentially be used as an attack vector. +func TestMerkleBlockOverflowErrors(t *testing.T) { + // Use protocol version 70001 specifically here instead of the latest + // protocol version because the test data is using bytes encoded with + // that version. + pver := uint32(70001) + + // Create bytes for a merkle block that claims to have more than the max + // allowed tx hashes. + var buf bytes.Buffer + btcwire.TstWriteVarInt(&buf, pver, btcwire.MaxTxPerBlock+1) + numHashesOffset := 84 + exceedMaxHashes := make([]byte, numHashesOffset) + copy(exceedMaxHashes, merkleBlockOneBytes[:numHashesOffset]) + exceedMaxHashes = append(exceedMaxHashes, buf.Bytes()...) + + // Create bytes for a merkle block that claims to have more than the max + // allowed flag bytes. + buf.Reset() + btcwire.TstWriteVarInt(&buf, pver, btcwire.MaxFlagsPerMerkleBlock+1) + numFlagBytesOffset := 117 + exceedMaxFlagBytes := make([]byte, numFlagBytesOffset) + copy(exceedMaxFlagBytes, merkleBlockOneBytes[:numFlagBytesOffset]) + exceedMaxFlagBytes = append(exceedMaxFlagBytes, buf.Bytes()...) + + tests := []struct { + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + err error // Expected error + }{ + // Block that claims to have more than max allowed hashes. + {exceedMaxHashes, pver, &btcwire.MessageError{}}, + // Block that claims to have more than max allowed flag bytes. + {exceedMaxFlagBytes, pver, &btcwire.MessageError{}}, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Decode from wire format. + var msg btcwire.MsgMerkleBlock + r := bytes.NewReader(test.buf) + err := msg.BtcDecode(r, test.pver) + if reflect.TypeOf(err) != reflect.TypeOf(test.err) { + t.Errorf("BtcDecode #%d wrong error got: %v, want: %v", + i, err, reflect.TypeOf(test.err)) + continue + } + } +} + +// merkleBlockOne is a merkle block created from block one of the block chain +// where the first transaction matches. +var merkleBlockOne = btcwire.MsgMerkleBlock{ + Header: btcwire.BlockHeader{ + Version: 1, + PrevBlock: btcwire.ShaHash([btcwire.HashSize]byte{ // Make go vet happy. + 0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72, + 0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f, + 0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c, + 0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, + }), + MerkleRoot: btcwire.ShaHash([btcwire.HashSize]byte{ // Make go vet happy. + 0x98, 0x20, 0x51, 0xfd, 0x1e, 0x4b, 0xa7, 0x44, + 0xbb, 0xbe, 0x68, 0x0e, 0x1f, 0xee, 0x14, 0x67, + 0x7b, 0xa1, 0xa3, 0xc3, 0x54, 0x0b, 0xf7, 0xb1, + 0xcd, 0xb6, 0x06, 0xe8, 0x57, 0x23, 0x3e, 0x0e, + }), + Timestamp: time.Unix(0x4966bc61, 0), // 2009-01-08 20:54:25 -0600 CST + Bits: 0x1d00ffff, // 486604799 + Nonce: 0x9962e301, // 2573394689 + }, + Transactions: 1, + Hashes: []*btcwire.ShaHash{ + (*btcwire.ShaHash)(&[btcwire.HashSize]byte{ // Make go vet happy. + 0x98, 0x20, 0x51, 0xfd, 0x1e, 0x4b, 0xa7, 0x44, + 0xbb, 0xbe, 0x68, 0x0e, 0x1f, 0xee, 0x14, 0x67, + 0x7b, 0xa1, 0xa3, 0xc3, 0x54, 0x0b, 0xf7, 0xb1, + 0xcd, 0xb6, 0x06, 0xe8, 0x57, 0x23, 0x3e, 0x0e, + }), + }, + Flags: []byte{0x80}, +} + +// merkleBlockOneBytes is the serialized bytes for a merkle block created from +// block one of the block chain where the first transation matches. +var merkleBlockOneBytes = []byte{ + 0x01, 0x00, 0x00, 0x00, // Version 1 + 0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72, + 0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f, + 0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c, + 0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, // PrevBlock + 0x98, 0x20, 0x51, 0xfd, 0x1e, 0x4b, 0xa7, 0x44, + 0xbb, 0xbe, 0x68, 0x0e, 0x1f, 0xee, 0x14, 0x67, + 0x7b, 0xa1, 0xa3, 0xc3, 0x54, 0x0b, 0xf7, 0xb1, + 0xcd, 0xb6, 0x06, 0xe8, 0x57, 0x23, 0x3e, 0x0e, // MerkleRoot + 0x61, 0xbc, 0x66, 0x49, // Timestamp + 0xff, 0xff, 0x00, 0x1d, // Bits + 0x01, 0xe3, 0x62, 0x99, // Nonce + 0x01, 0x00, 0x00, 0x00, // TxnCount + 0x01, // Num hashes + 0x98, 0x20, 0x51, 0xfd, 0x1e, 0x4b, 0xa7, 0x44, + 0xbb, 0xbe, 0x68, 0x0e, 0x1f, 0xee, 0x14, 0x67, + 0x7b, 0xa1, 0xa3, 0xc3, 0x54, 0x0b, 0xf7, 0xb1, + 0xcd, 0xb6, 0x06, 0xe8, 0x57, 0x23, 0x3e, 0x0e, // Hash + 0x01, // Num flag bytes + 0x80, // Flags +} diff --git a/msgping_test.go b/msgping_test.go index f84bae4e..c4958af5 100644 --- a/msgping_test.go +++ b/msgping_test.go @@ -96,7 +96,7 @@ func TestPingBIP0031(t *testing.T) { } // TestPingCrossProtocol tests the MsgPing API when encoding with the latest -// protocol version and decoded with BIP0031Version. +// protocol version and decoding with BIP0031Version. func TestPingCrossProtocol(t *testing.T) { nonce, err := btcwire.RandomUint64() if err != nil { diff --git a/msgpong_test.go b/msgpong_test.go index d588c6fb..f1dc4b1d 100644 --- a/msgpong_test.go +++ b/msgpong_test.go @@ -113,7 +113,7 @@ func TestPongBIP0031(t *testing.T) { } // TestPongCrossProtocol tests the MsgPong API when encoding with the latest -// protocol version and decoded with BIP0031Version. +// protocol version and decoding with BIP0031Version. func TestPongCrossProtocol(t *testing.T) { nonce, err := btcwire.RandomUint64() if err != nil { @@ -140,7 +140,7 @@ func TestPongCrossProtocol(t *testing.T) { } // Since one of the protocol versions doesn't support the pong message, - // make sure the nonce didn't get encoded and decoded back out. + // make sure the nonce didn't get encoded and decoded back out. if msg.Nonce == readmsg.Nonce { t.Error("Should not get same nonce for cross protocol") }