Complete BIP0037 support started by dhill.

- Correct MsgFilterLoad max payload
- Enforce max flag bytes per merkle block
- Improve and finish tests to include testing all error paths
- Add fast paths for BloomUpdateType
- Convert all byte fields to use read/writeVarBytes
- Style and consistency updates
- README.md and doc.go updates

Closes #12.
This commit is contained in:
Dave Collins 2014-05-05 02:15:18 -05:00
parent cf754d09bf
commit f8ec476691
15 changed files with 737 additions and 277 deletions

View file

@ -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 ## GPG Verification Key
All official release tags are signed by Conformal so users can ensure the code All official release tags are signed by Conformal so users can ensure the code

View file

@ -130,6 +130,15 @@ func readElement(r io.Reader, element interface{}) error {
} }
*e = BitcoinNet(binary.LittleEndian.Uint32(b)) *e = BitcoinNet(binary.LittleEndian.Uint32(b))
return nil 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 // 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 err
} }
return nil 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 // Fall back to the slower binary.Write if a fast path was not available

1
doc.go
View file

@ -153,6 +153,7 @@ This package includes spec changes outlined by the following BIPs:
BIP0014 (https://en.bitcoin.it/wiki/BIP_0014) BIP0014 (https://en.bitcoin.it/wiki/BIP_0014)
BIP0031 (https://en.bitcoin.it/wiki/BIP_0031) BIP0031 (https://en.bitcoin.it/wiki/BIP_0031)
BIP0035 (https://en.bitcoin.it/wiki/BIP_0035) BIP0035 (https://en.bitcoin.it/wiki/BIP_0035)
BIP0037 (https://en.bitcoin.it/wiki/BIP_0037)
Other important information Other important information

View file

@ -15,21 +15,31 @@ import (
"io" "io"
) )
// MaxMessagePayload makes the internal maxMessagePayload constant available to const (
// the test package. // MaxMessagePayload makes the internal maxMessagePayload constant
const MaxMessagePayload uint32 = maxMessagePayload // available to the test package.
MaxMessagePayload uint32 = maxMessagePayload
// MaxCountSetCancel makes the internal maxCountSetCancel constant available to // MaxTxPerBlock makes the internal maxTxPerBlock constant available to
// the test package. // the test package.
const MaxCountSetCancel uint32 = maxCountSetCancel MaxTxPerBlock = maxTxPerBlock
// MaxCountSetSubVer makes the internal maxCountSetSubVer constant available to // MaxFlagsPerMerkleBlock makes the internal maxFlagsPerMerkleBlock
// the test package. // constant available to the test package.
const MaxCountSetSubVer uint32 = maxCountSetSubVer MaxFlagsPerMerkleBlock = maxFlagsPerMerkleBlock
// CommandSize makes the internal commandSize constant available to the test // MaxCountSetCancel makes the internal maxCountSetCancel constant
// package. // available to the test package.
const CommandSize = commandSize 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 // TstRandomUint64 makes the internal randomUint64 function available to the
// test package. // test package.

View file

@ -44,8 +44,6 @@ type MsgBlock struct {
// AddTransaction adds a transaction to the message. // AddTransaction adds a transaction to the message.
func (msg *MsgBlock) AddTransaction(tx *MsgTx) error { 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) msg.Transactions = append(msg.Transactions, tx)
return nil return nil

View file

@ -16,8 +16,9 @@ const (
MaxFilterAddDataSize = 520 MaxFilterAddDataSize = 520
) )
// MsgFilterAdd implements the Message interface and represents a bitcoin filteradd // MsgFilterAdd implements the Message interface and represents a bitcoin
// message which is used to add a data element to an existing Bloom filter. // filteradd message. It is used to add a data element to an existing Bloom
// filter.
// //
// This message was not added until protocol version BIP0037Version. // This message was not added until protocol version BIP0037Version.
type MsgFilterAdd struct { type MsgFilterAdd struct {
@ -33,19 +34,9 @@ func (msg *MsgFilterAdd) BtcDecode(r io.Reader, pver uint32) error {
return messageError("MsgFilterAdd.BtcDecode", str) return messageError("MsgFilterAdd.BtcDecode", str)
} }
size, err := readVarInt(r, pver) var err error
if err != nil { msg.Data, err = readVarBytes(r, pver, MaxFilterAddDataSize,
return err "filteradd data")
}
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)
if err != nil { if err != nil {
return err return err
} }
@ -69,12 +60,7 @@ func (msg *MsgFilterAdd) BtcEncode(w io.Writer, pver uint32) error {
return messageError("MsgFilterAdd.BtcEncode", str) return messageError("MsgFilterAdd.BtcEncode", str)
} }
err := writeVarInt(w, pver, uint64(size)) err := writeVarBytes(w, pver, msg.Data)
if err != nil {
return err
}
err = writeElement(w, msg.Data)
if err != nil { if err != nil {
return err return err
} }
@ -91,11 +77,12 @@ func (msg *MsgFilterAdd) Command() string {
// MaxPayloadLength returns the maximum length the payload can be for the // MaxPayloadLength returns the maximum length the payload can be for the
// receiver. This is part of the Message interface implementation. // receiver. This is part of the Message interface implementation.
func (msg *MsgFilterAdd) MaxPayloadLength(pver uint32) uint32 { 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 // NewMsgFilterAdd returns a new bitcoin filteradd message that conforms to the
// interface. See MsgFilterAdd for details. // Message interface. See MsgFilterAdd for details.
func NewMsgFilterAdd(data []byte) *MsgFilterAdd { func NewMsgFilterAdd(data []byte) *MsgFilterAdd {
return &MsgFilterAdd{ return &MsgFilterAdd{
Data: data, Data: data,

View file

@ -8,10 +8,12 @@ import (
"bytes" "bytes"
"github.com/conformal/btcwire" "github.com/conformal/btcwire"
"io" "io"
"reflect"
"testing" "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) { func TestFilterAddLatest(t *testing.T) {
pver := btcwire.ProtocolVersion pver := btcwire.ProtocolVersion
@ -26,7 +28,7 @@ func TestFilterAddLatest(t *testing.T) {
} }
// Ensure max payload is expected value for latest protocol version. // Ensure max payload is expected value for latest protocol version.
wantPayload := uint32(529) wantPayload := uint32(523)
maxPayload := msg.MaxPayloadLength(pver) maxPayload := msg.MaxPayloadLength(pver)
if maxPayload != wantPayload { if maxPayload != wantPayload {
t.Errorf("MaxPayloadLength: wrong max payload length for "+ t.Errorf("MaxPayloadLength: wrong max payload length for "+
@ -42,7 +44,7 @@ func TestFilterAddLatest(t *testing.T) {
} }
// Test decode with latest protocol version. // Test decode with latest protocol version.
readmsg := btcwire.MsgFilterAdd{} var readmsg btcwire.MsgFilterAdd
err = readmsg.BtcDecode(&buf, pver) err = readmsg.BtcDecode(&buf, pver)
if err != nil { if err != nil {
t.Errorf("decode of MsgFilterAdd failed [%v] err <%v>", buf, err) t.Errorf("decode of MsgFilterAdd failed [%v] err <%v>", buf, err)
@ -51,27 +53,36 @@ func TestFilterAddLatest(t *testing.T) {
return return
} }
// TestFilterAddCrossProtocol tests the MsgFilterAdd API when encoding with the latest // TestFilterAddCrossProtocol tests the MsgFilterAdd API when encoding with the
// protocol version and decoded with BIP0031Version. // latest protocol version and decoding with BIP0031Version.
func TestFilterAddCrossProtocol(t *testing.T) { func TestFilterAddCrossProtocol(t *testing.T) {
data := []byte{0x01, 0x02} data := []byte{0x01, 0x02}
msg := btcwire.NewMsgFilterAdd(data) 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 var buf bytes.Buffer
err := msg.BtcEncode(&buf, btcwire.BIP0037Version-1) err := msg.BtcEncode(&buf, btcwire.ProtocolVersion)
if err == nil { if err != nil {
t.Errorf("encode of MsgFilterAdd succeeded when it shouldn't have %v", t.Errorf("encode of MsgFilterAdd failed %v err <%v>", msg, err)
msg)
} }
// Decode with old protocol version. // Decode with old protocol version.
readmsg := btcwire.MsgFilterAdd{} var readmsg btcwire.MsgFilterAdd
err = readmsg.BtcDecode(&buf, btcwire.BIP0031Version) err = readmsg.BtcDecode(&buf, btcwire.BIP0031Version)
if err == nil { if err == nil {
t.Errorf("decode of MsgFilterAdd succeeded when it shouldn't have %v", t.Errorf("decode of MsgFilterAdd succeeded when it shouldn't "+
msg) "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. // TestFilterAddMaxDataSize tests the MsgFilterAdd API maximum data size.
@ -83,16 +94,16 @@ func TestFilterAddMaxDataSize(t *testing.T) {
var buf bytes.Buffer var buf bytes.Buffer
err := msg.BtcEncode(&buf, btcwire.ProtocolVersion) err := msg.BtcEncode(&buf, btcwire.ProtocolVersion)
if err == nil { if err == nil {
t.Errorf("encode of MsgFilterAdd succeeded when it shouldn't have %v", t.Errorf("encode of MsgFilterAdd succeeded when it shouldn't "+
msg) "have %v", msg)
} }
// Decode with latest protocol version. // Decode with latest protocol version.
readbuf := bytes.NewReader(data) readbuf := bytes.NewReader(data)
err = msg.BtcDecode(readbuf, btcwire.ProtocolVersion) err = msg.BtcDecode(readbuf, btcwire.ProtocolVersion)
if err == nil { if err == nil {
t.Errorf("decode of MsgFilterAdd succeeded when it shouldn't have %v", t.Errorf("decode of MsgFilterAdd succeeded when it shouldn't "+
msg) "have %v", msg)
} }
} }
@ -100,6 +111,12 @@ func TestFilterAddMaxDataSize(t *testing.T) {
// of MsgFilterAdd to confirm error paths work correctly. // of MsgFilterAdd to confirm error paths work correctly.
func TestFilterAddWireErrors(t *testing.T) { func TestFilterAddWireErrors(t *testing.T) {
pver := btcwire.ProtocolVersion 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 { tests := []struct {
in *btcwire.MsgFilterAdd // Value to encode in *btcwire.MsgFilterAdd // Value to encode
@ -110,38 +127,20 @@ func TestFilterAddWireErrors(t *testing.T) {
readErr error // Expected read error readErr error // Expected read error
}{ }{
// Latest protocol version with intentional read/write errors. // Latest protocol version with intentional read/write errors.
// Force error in data size.
{ {
&btcwire.MsgFilterAdd{Data: []byte{0x01, 0x02, 0x03, 0x04}}, baseFilterAdd, baseFilterAddEncoded, pver, 0,
[]byte{ io.ErrShortWrite, io.EOF,
0x05, // Varint for size of data
0x02, 0x03, 0x04, // Data
},
pver,
2,
io.ErrShortWrite,
io.ErrUnexpectedEOF,
}, },
// Force error in data.
{ {
&btcwire.MsgFilterAdd{Data: []byte{0x01, 0x02, 0x03, 0x04}}, baseFilterAdd, baseFilterAddEncoded, pver, 1,
[]byte{ io.ErrShortWrite, io.EOF,
0x05, // Varint for size of data
0x02, 0x03, 0x04, // Data
},
pver,
0,
io.ErrShortWrite,
io.EOF,
}, },
// Force error due to unsupported protocol version.
{ {
&btcwire.MsgFilterAdd{Data: []byte{0x01, 0x02, 0x03, 0x04}}, baseFilterAdd, baseFilterAddEncoded, pverNoFilterAdd, 5,
[]byte{ btcwireErr, btcwireErr,
0x05, // Varint for size of data
0x02, 0x03, 0x04, // Data
},
pver,
1,
io.ErrShortWrite,
io.EOF,
}, },
} }
@ -150,20 +149,40 @@ func TestFilterAddWireErrors(t *testing.T) {
// Encode to wire format. // Encode to wire format.
w := newFixedWriter(test.max) w := newFixedWriter(test.max)
err := test.in.BtcEncode(w, test.pver) 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", t.Errorf("BtcEncode #%d wrong error got: %v, want: %v",
i, err, test.writeErr) i, err, test.writeErr)
continue 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. // Decode from wire format.
var msg btcwire.MsgFilterAdd var msg btcwire.MsgFilterAdd
r := newFixedReader(test.max, test.buf) r := newFixedReader(test.max, test.buf)
err = msg.BtcDecode(r, test.pver) 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", t.Errorf("BtcDecode #%d wrong error got: %v, want: %v",
i, err, test.readErr) i, err, test.readErr)
continue 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
}
}
} }
} }

View file

@ -9,10 +9,11 @@ import (
"io" "io"
) )
// MsgFilterClear implements the Message interface and represents a bitcoin filterclear // MsgFilterClear implements the Message interface and represents a bitcoin
// message which is used to reset a Bloom filter. // 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{} type MsgFilterClear struct{}
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver. // BtcDecode decodes r using the bitcoin protocol encoding into the receiver.

View file

@ -7,10 +7,13 @@ package btcwire_test
import ( import (
"bytes" "bytes"
"github.com/conformal/btcwire" "github.com/conformal/btcwire"
"github.com/davecgh/go-spew/spew"
"reflect"
"testing" "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) { func TestFilterClearLatest(t *testing.T) {
pver := btcwire.ProtocolVersion pver := btcwire.ProtocolVersion
@ -32,41 +35,162 @@ func TestFilterClearLatest(t *testing.T) {
maxPayload, wantPayload) 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 var buf bytes.Buffer
err := msg.BtcEncode(&buf, pver) err := msg.BtcEncode(&buf, btcwire.ProtocolVersion)
if err != nil { if err != nil {
t.Errorf("encode of MsgFilterClear failed %v err <%v>", msg, err) 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. // Decode with old protocol version.
readmsg := btcwire.NewMsgFilterClear() var readmsg btcwire.MsgFilterClear
err = readmsg.BtcDecode(&buf, btcwire.BIP0031Version) err = readmsg.BtcDecode(&buf, btcwire.BIP0031Version)
if err == nil { if err == nil {
t.Errorf("decode of MsgFilterClear succeeded when it shouldn't have %v", t.Errorf("decode of MsgFilterClear succeeded when it "+
msg) "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
}
}
} }
} }

View file

@ -9,33 +9,37 @@ import (
"io" "io"
) )
// BloomUpdateType specifies how the filter is updated when a match is found
type BloomUpdateType uint8 type BloomUpdateType uint8
const ( 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 BloomUpdateNone BloomUpdateType = 0
// BloomUpdateAll indicates if the filter matches any data element in a // 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 BloomUpdateAll BloomUpdateType = 1
// BloomUpdateP2PubkeyOnly indicates if the filter matches a data element in // BloomUpdateP2PubkeyOnly indicates if the filter matches a data
// a scriptPubkey and the script is of the standard payToPybKey or payToMultiSig, // element in a public key script and the script is of the standard
// the outpoint is inserted into the filter. // pay-to-pubkey or multisig, the outpoint is serialized and inserted
// into the filter.
BloomUpdateP2PubkeyOnly BloomUpdateType = 2 BloomUpdateP2PubkeyOnly BloomUpdateType = 2
) )
const ( const (
// MaxFilterLoadHashFuncs is the maximum number of hash functions to load // MaxFilterLoadHashFuncs is the maximum number of hash functions to
// into the Bloom filter. // load into the Bloom filter.
MaxFilterLoadHashFuncs = 50 MaxFilterLoadHashFuncs = 50
// MaxFilterLoadFilterSize is the maximum size in bytes a filter may be. // MaxFilterLoadFilterSize is the maximum size in bytes a filter may be.
MaxFilterLoadFilterSize = 36000 MaxFilterLoadFilterSize = 36000
) )
// MsgFilterLoad implements the Message interface and represents a bitcoin filterload // MsgFilterLoad implements the Message interface and represents a bitcoin
// message which is used to reset a Bloom filter. // filterload 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.
type MsgFilterLoad struct { type MsgFilterLoad struct {
@ -54,19 +58,9 @@ func (msg *MsgFilterLoad) BtcDecode(r io.Reader, pver uint32) error {
return messageError("MsgFilterLoad.BtcDecode", str) return messageError("MsgFilterLoad.BtcDecode", str)
} }
// Read num filter and limit to max. var err error
size, err := readVarInt(r, pver) msg.Filter, err = readVarBytes(r, pver, MaxFilterLoadFilterSize,
if err != nil { "filterload filter size")
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)
if err != nil { if err != nil {
return err return err
} }
@ -107,11 +101,12 @@ func (msg *MsgFilterLoad) BtcEncode(w io.Writer, pver uint32) error {
return messageError("MsgFilterLoad.BtcEncode", str) return messageError("MsgFilterLoad.BtcEncode", str)
} }
err := writeVarInt(w, pver, uint64(size)) err := writeVarBytes(w, pver, msg.Filter)
if err != nil { if err != nil {
return err 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 { if err != nil {
return err return err
} }
@ -119,23 +114,6 @@ func (msg *MsgFilterLoad) BtcEncode(w io.Writer, pver uint32) error {
return nil 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 // Command returns the protocol command string for the message. This is part
// of the Message interface implementation. // of the Message interface implementation.
func (msg *MsgFilterLoad) Command() string { 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 // MaxPayloadLength returns the maximum length the payload can be for the
// receiver. This is part of the Message interface implementation. // receiver. This is part of the Message interface implementation.
func (msg *MsgFilterLoad) MaxPayloadLength(pver uint32) uint32 { 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 // NewMsgFilterLoad returns a new bitcoin filterload message that conforms to
// interface. See MsgFilterLoad for details. // the Message interface. See MsgFilterLoad for details.
func NewMsgFilterLoad(filter []byte, hashFuncs uint32, tweak uint32, flags BloomUpdateType) *MsgFilterLoad { func NewMsgFilterLoad(filter []byte, hashFuncs uint32, tweak uint32, flags BloomUpdateType) *MsgFilterLoad {
return &MsgFilterLoad{ return &MsgFilterLoad{
Filter: filter, Filter: filter,

View file

@ -8,10 +8,12 @@ import (
"bytes" "bytes"
"github.com/conformal/btcwire" "github.com/conformal/btcwire"
"io" "io"
"reflect"
"testing" "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) { func TestFilterLoadLatest(t *testing.T) {
pver := btcwire.ProtocolVersion pver := btcwire.ProtocolVersion
@ -25,8 +27,8 @@ func TestFilterLoadLatest(t *testing.T) {
cmd, wantCmd) cmd, wantCmd)
} }
// Ensure max payadd is expected value for latest protocol version. // Ensure max payload is expected value for latest protocol version.
wantPayload := uint32(36018) wantPayload := uint32(36012)
maxPayload := msg.MaxPayloadLength(pver) maxPayload := msg.MaxPayloadLength(pver)
if maxPayload != wantPayload { if maxPayload != wantPayload {
t.Errorf("MaxPayLoadLength: wrong max payload length for "+ t.Errorf("MaxPayLoadLength: wrong max payload length for "+
@ -51,22 +53,22 @@ func TestFilterLoadLatest(t *testing.T) {
return return
} }
// TestFilterLoadCrossProtocol tests the MsgFilterLoad API when encoding with the latest // TestFilterLoadCrossProtocol tests the MsgFilterLoad API when encoding with
// protocol version and decoded with BIP0031Version. // the latest protocol version and decoding with BIP0031Version.
func TestFilterLoadCrossProtocol(t *testing.T) { func TestFilterLoadCrossProtocol(t *testing.T) {
data := []byte{0x01, 0x02} data := []byte{0x01, 0x02}
msg := btcwire.NewMsgFilterLoad(data, 10, 0, 0) msg := btcwire.NewMsgFilterLoad(data, 10, 0, 0)
// Encode with old protocol version. // Encode with latest protocol version.
var buf bytes.Buffer var buf bytes.Buffer
err := msg.BtcEncode(&buf, btcwire.BIP0037Version-1) err := msg.BtcEncode(&buf, btcwire.ProtocolVersion)
if err == nil { if err != nil {
t.Errorf("encode of MsgFilterLoad succeeded when it shouldn't have %v", t.Errorf("encode of NewMsgFilterLoad failed %v err <%v>", msg,
msg) err)
} }
// Decode with old protocol version. // Decode with old protocol version.
readmsg := btcwire.MsgFilterLoad{} var readmsg btcwire.MsgFilterLoad
err = readmsg.BtcDecode(&buf, btcwire.BIP0031Version) err = readmsg.BtcDecode(&buf, btcwire.BIP0031Version)
if err == nil { if err == nil {
t.Errorf("decode of MsgFilterLoad succeeded when it shouldn't have %v", 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 var buf bytes.Buffer
err := msg.BtcEncode(&buf, btcwire.ProtocolVersion) err := msg.BtcEncode(&buf, btcwire.ProtocolVersion)
if err == nil { if err == nil {
t.Errorf("encode of MsgFilterLoad succeeded when it shouldn't have %v", t.Errorf("encode of MsgFilterLoad succeeded when it shouldn't "+
msg) "have %v", msg)
} }
// Decode with latest protocol version. // Decode with latest protocol version.
readbuf := bytes.NewReader(data) readbuf := bytes.NewReader(data)
err = msg.BtcDecode(readbuf, btcwire.ProtocolVersion) err = msg.BtcDecode(readbuf, btcwire.ProtocolVersion)
if err == nil { if err == nil {
t.Errorf("decode of MsgFilterLoad succeeded when it shouldn't have %v", t.Errorf("decode of MsgFilterLoad succeeded when it shouldn't "+
msg) "have %v", msg)
} }
} }
@ -130,6 +132,17 @@ func TestFilterLoadMaxHashFuncsSize(t *testing.T) {
// of MsgFilterLoad to confirm error paths work correctly. // of MsgFilterLoad to confirm error paths work correctly.
func TestFilterLoadWireErrors(t *testing.T) { func TestFilterLoadWireErrors(t *testing.T) {
pver := btcwire.ProtocolVersion 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 { tests := []struct {
in *btcwire.MsgFilterLoad // Value to encode in *btcwire.MsgFilterLoad // Value to encode
@ -140,62 +153,35 @@ func TestFilterLoadWireErrors(t *testing.T) {
readErr error // Expected read error readErr error // Expected read error
}{ }{
// Latest protocol version with intentional read/write errors. // Latest protocol version with intentional read/write errors.
// Force error in filter size.
{ {
&btcwire.MsgFilterLoad{ baseFilterLoad, baseFilterLoadEncoded, pver, 0,
Filter: []byte{0x01, 0x02, 0x03, 0x04}, io.ErrShortWrite, io.EOF,
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,
}, },
// Force error in filter.
{ {
&btcwire.MsgFilterLoad{ baseFilterLoad, baseFilterLoadEncoded, pver, 1,
Filter: []byte{0x01, 0x02, 0x03, 0x04}, io.ErrShortWrite, io.EOF,
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,
}, },
// Force error in hash funcs.
{ {
&btcwire.MsgFilterLoad{ baseFilterLoad, baseFilterLoadEncoded, pver, 5,
Filter: []byte{0x01, 0x02, 0x03, 0x04}, io.ErrShortWrite, io.EOF,
HashFuncs: 10, },
Tweak: 0, // Force error in tweak.
Flags: btcwire.BloomUpdateNone, {
}, baseFilterLoad, baseFilterLoadEncoded, pver, 9,
[]byte{ io.ErrShortWrite, io.EOF,
0x04, // Varint for size of Filter },
0x01, 0x02, 0x03, 0x04, // Filter // Force error in flags.
0x00, 0x0a, // HashFuncs {
0x00, 0x00, // Tweak baseFilterLoad, baseFilterLoadEncoded, pver, 13,
0x00, // Flags io.ErrShortWrite, io.EOF,
}, },
pver, // Force error due to unsupported protocol version.
10, {
io.ErrShortWrite, baseFilterLoad, baseFilterLoadEncoded, pverNoFilterLoad,
io.ErrUnexpectedEOF, 10, btcwireErr, btcwireErr,
}, },
} }
@ -204,20 +190,41 @@ func TestFilterLoadWireErrors(t *testing.T) {
// Encode to wire format. // Encode to wire format.
w := newFixedWriter(test.max) w := newFixedWriter(test.max)
err := test.in.BtcEncode(w, test.pver) 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", t.Errorf("BtcEncode #%d wrong error got: %v, want: %v",
i, err, test.writeErr) i, err, test.writeErr)
continue 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. // Decode from wire format.
var msg btcwire.MsgFilterLoad var msg btcwire.MsgFilterLoad
r := newFixedReader(test.max, test.buf) r := newFixedReader(test.max, test.buf)
err = msg.BtcDecode(r, test.pver) 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", t.Errorf("BtcDecode #%d wrong error got: %v, want: %v",
i, err, test.readErr) i, err, test.readErr)
continue 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
}
}
} }
} }

View file

@ -9,8 +9,14 @@ import (
"io" "io"
) )
// MsgMerkleBlock implements the Message interface and represents a bitcoin merkleblock // maxFlagsPerMerkleBlock is the maximum number of flag bytes that could
// message which is used to reset a Bloom filter. // 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. // This message was not added until protocol version BIP0037Version.
type MsgMerkleBlock struct { type MsgMerkleBlock struct {
@ -64,7 +70,7 @@ func (msg *MsgMerkleBlock) BtcDecode(r io.Reader, pver uint32) error {
msg.Hashes = make([]*ShaHash, 0, count) msg.Hashes = make([]*ShaHash, 0, count)
for i := uint64(0); i < count; i++ { for i := uint64(0); i < count; i++ {
sha := ShaHash{} var sha ShaHash
err := readElement(r, &sha) err := readElement(r, &sha)
if err != nil { if err != nil {
return err return err
@ -72,13 +78,8 @@ func (msg *MsgMerkleBlock) BtcDecode(r io.Reader, pver uint32) error {
msg.AddTxHash(&sha) msg.AddTxHash(&sha)
} }
count, err = readVarInt(r, pver) msg.Flags, err = readVarBytes(r, pver, maxFlagsPerMerkleBlock,
if err != nil { "merkle block flags size")
return err
}
msg.Flags = make([]byte, 0, count)
err = readElement(r, &msg.Flags)
if err != nil { if err != nil {
return err return err
} }
@ -96,10 +97,16 @@ func (msg *MsgMerkleBlock) BtcEncode(w io.Writer, pver uint32) error {
} }
// Read num transaction hashes and limit to max. // Read num transaction hashes and limit to max.
count := len(msg.Hashes) numHashes := len(msg.Hashes)
if count > maxTxPerBlock { if numHashes > maxTxPerBlock {
str := fmt.Sprintf("too many transaction hashes for message "+ 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) return messageError("MsgMerkleBlock.BtcDecode", str)
} }
@ -113,11 +120,10 @@ func (msg *MsgMerkleBlock) BtcEncode(w io.Writer, pver uint32) error {
return err return err
} }
err = writeVarInt(w, pver, uint64(count)) err = writeVarInt(w, pver, uint64(numHashes))
if err != nil { if err != nil {
return err return err
} }
for _, hash := range msg.Hashes { for _, hash := range msg.Hashes {
err = writeElement(w, hash) err = writeElement(w, hash)
if err != nil { 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(numFlagBytes))
err = writeVarInt(w, pver, uint64(count))
if err != nil { if err != nil {
return err return err
} }
err = writeElement(w, msg.Flags) err = writeElement(w, msg.Flags)
if err != nil { if err != nil {
return err return err
@ -151,8 +155,8 @@ func (msg *MsgMerkleBlock) MaxPayloadLength(pver uint32) uint32 {
return MaxBlockPayload return MaxBlockPayload
} }
// NewMsgMerkleBlock returns a new bitcoin merkleblock message that conforms to the Message // NewMsgMerkleBlock returns a new bitcoin merkleblock message that conforms to
// interface. See MsgMerkleBlock for details. // the Message interface. See MsgMerkleBlock for details.
func NewMsgMerkleBlock(bh *BlockHeader) *MsgMerkleBlock { func NewMsgMerkleBlock(bh *BlockHeader) *MsgMerkleBlock {
return &MsgMerkleBlock{ return &MsgMerkleBlock{
Header: *bh, Header: *bh,

View file

@ -8,13 +8,16 @@ import (
"bytes" "bytes"
"crypto/rand" "crypto/rand"
"github.com/conformal/btcwire" "github.com/conformal/btcwire"
"github.com/davecgh/go-spew/spew"
"io"
"reflect"
"testing" "testing"
"time"
) )
// TestMerkleBlock tests the MsgMerkleBlock API. // TestMerkleBlock tests the MsgMerkleBlock API.
func TestMerkleBlock(t *testing.T) { func TestMerkleBlock(t *testing.T) {
pver := btcwire.ProtocolVersion pver := btcwire.ProtocolVersion
pverOld := btcwire.BIP0037Version - 1
// Block 1 header. // Block 1 header.
prevHash := &blockOne.Header.PrevBlock prevHash := &blockOne.Header.PrevBlock
@ -43,7 +46,7 @@ func TestMerkleBlock(t *testing.T) {
// Load maxTxPerBlock hashes // Load maxTxPerBlock hashes
data := make([]byte, 32) data := make([]byte, 32)
for i := 0; i < (btcwire.MaxBlockPayload/10)+1; i++ { for i := 0; i < btcwire.MaxTxPerBlock; i++ {
rand.Read(data) rand.Read(data)
hash, err := btcwire.NewShaHash(data) hash, err := btcwire.NewShaHash(data)
if err != nil { 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) rand.Read(data)
hash, err := btcwire.NewShaHash(data) hash, err := btcwire.NewShaHash(data)
if err != nil { if err != nil {
@ -77,13 +80,6 @@ func TestMerkleBlock(t *testing.T) {
t.Errorf("encode of MsgMerkleBlock failed %v err <%v>", msg, err) 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. // Test decode with latest protocol version.
readmsg := btcwire.MsgMerkleBlock{} readmsg := btcwire.MsgMerkleBlock{}
err = readmsg.BtcDecode(&buf, pver) 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) t.Errorf("decode of MsgMerkleBlock failed [%v] err <%v>", buf, err)
} }
// Test decode with old protocol version. // Force extra hash to test maxTxPerBlock.
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
msg.Hashes = append(msg.Hashes, hash) msg.Hashes = append(msg.Hashes, hash)
err = msg.BtcEncode(&buf, pver) err = msg.BtcEncode(&buf, pver)
if err == nil { if err == nil {
t.Errorf("encode of MsgMerkleBlock succeeded with too many tx hashes " + t.Errorf("encode of MsgMerkleBlock succeeded with too many " +
"when it should have failed") "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 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
}

View file

@ -96,7 +96,7 @@ func TestPingBIP0031(t *testing.T) {
} }
// TestPingCrossProtocol tests the MsgPing API when encoding with the latest // 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) { func TestPingCrossProtocol(t *testing.T) {
nonce, err := btcwire.RandomUint64() nonce, err := btcwire.RandomUint64()
if err != nil { if err != nil {

View file

@ -113,7 +113,7 @@ func TestPongBIP0031(t *testing.T) {
} }
// TestPongCrossProtocol tests the MsgPong API when encoding with the latest // 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) { func TestPongCrossProtocol(t *testing.T) {
nonce, err := btcwire.RandomUint64() nonce, err := btcwire.RandomUint64()
if err != nil { if err != nil {
@ -140,7 +140,7 @@ func TestPongCrossProtocol(t *testing.T) {
} }
// Since one of the protocol versions doesn't support the pong message, // 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 { if msg.Nonce == readmsg.Nonce {
t.Error("Should not get same nonce for cross protocol") t.Error("Should not get same nonce for cross protocol")
} }