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:
parent
cf754d09bf
commit
f8ec476691
15 changed files with 737 additions and 277 deletions
|
@ -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
|
||||
|
|
18
common.go
18
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
|
||||
|
|
1
doc.go
1
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
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue