diff --git a/wire/doc.go b/wire/doc.go index 1d83a263a..c10373ee1 100644 --- a/wire/doc.go +++ b/wire/doc.go @@ -1,4 +1,4 @@ -// Copyright (c) 2013-2015 The btcsuite developers +// Copyright (c) 2013-2016 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -156,5 +156,6 @@ This package includes spec changes outlined by the following BIPs: BIP0035 (https://github.com/bitcoin/bips/blob/master/bip-0035.mediawiki) BIP0037 (https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki) BIP0111 (https://github.com/bitcoin/bips/blob/master/bip-0111.mediawiki) + BIP0130 (https://github.com/bitcoin/bips/blob/master/bip-0130.mediawiki) */ package wire diff --git a/wire/message.go b/wire/message.go index fe9fa48ce..b5a114bf7 100644 --- a/wire/message.go +++ b/wire/message.go @@ -47,6 +47,7 @@ const ( CmdFilterLoad = "filterload" CmdMerkleBlock = "merkleblock" CmdReject = "reject" + CmdSendHeaders = "sendheaders" ) // Message is an interface that describes a bitcoin message. A type that @@ -128,6 +129,9 @@ func makeEmptyMessage(command string) (Message, error) { case CmdReject: msg = &MsgReject{} + case CmdSendHeaders: + msg = &MsgSendHeaders{} + default: return nil, fmt.Errorf("unhandled command [%s]", command) } diff --git a/wire/msgsendheaders.go b/wire/msgsendheaders.go new file mode 100644 index 000000000..532e4aa1d --- /dev/null +++ b/wire/msgsendheaders.go @@ -0,0 +1,60 @@ +// Copyright (c) 2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire + +import ( + "fmt" + "io" +) + +// MsgSendHeaders implements the Message interface and represents a bitcoin +// sendheaders message. It is used to request the peer send block headers +// rather than inventory vectors. +// +// This message has no payload and was not added until protocol versions +// starting with SendHeadersVersion. +type MsgSendHeaders struct{} + +// BtcDecode decodes r using the bitcoin protocol encoding into the receiver. +// This is part of the Message interface implementation. +func (msg *MsgSendHeaders) BtcDecode(r io.Reader, pver uint32) error { + if pver < SendHeadersVersion { + str := fmt.Sprintf("sendheaders message invalid for protocol "+ + "version %d", pver) + return messageError("MsgSendHeaders.BtcDecode", str) + } + + return nil +} + +// BtcEncode encodes the receiver to w using the bitcoin protocol encoding. +// This is part of the Message interface implementation. +func (msg *MsgSendHeaders) BtcEncode(w io.Writer, pver uint32) error { + if pver < SendHeadersVersion { + str := fmt.Sprintf("sendheaders message invalid for protocol "+ + "version %d", pver) + return messageError("MsgSendHeaders.BtcEncode", str) + } + + return nil +} + +// Command returns the protocol command string for the message. This is part +// of the Message interface implementation. +func (msg *MsgSendHeaders) Command() string { + return CmdSendHeaders +} + +// MaxPayloadLength returns the maximum length the payload can be for the +// receiver. This is part of the Message interface implementation. +func (msg *MsgSendHeaders) MaxPayloadLength(pver uint32) uint32 { + return 0 +} + +// NewMsgSendHeaders returns a new bitcoin sendheaders message that conforms to +// the Message interface. See MsgSendHeaders for details. +func NewMsgSendHeaders() *MsgSendHeaders { + return &MsgSendHeaders{} +} diff --git a/wire/msgsendheaders_test.go b/wire/msgsendheaders_test.go new file mode 100644 index 000000000..e09db6ad1 --- /dev/null +++ b/wire/msgsendheaders_test.go @@ -0,0 +1,191 @@ +// Copyright (c) 2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wire_test + +import ( + "bytes" + "reflect" + "testing" + + "github.com/btcsuite/btcd/wire" + "github.com/davecgh/go-spew/spew" +) + +// TestSendHeaders tests the MsgSendHeaders API against the latest protocol +// version. +func TestSendHeaders(t *testing.T) { + pver := wire.ProtocolVersion + + // Ensure the command is expected value. + wantCmd := "sendheaders" + msg := wire.NewMsgSendHeaders() + if cmd := msg.Command(); cmd != wantCmd { + t.Errorf("NewMsgSendHeaders: wrong command - got %v want %v", + cmd, wantCmd) + } + + // Ensure max payload is expected value. + wantPayload := uint32(0) + maxPayload := msg.MaxPayloadLength(pver) + if maxPayload != wantPayload { + t.Errorf("MaxPayloadLength: wrong max payload length for "+ + "protocol version %d - got %v, want %v", pver, + maxPayload, wantPayload) + } + + // Test encode with latest protocol version. + var buf bytes.Buffer + err := msg.BtcEncode(&buf, pver) + if err != nil { + t.Errorf("encode of MsgSendHeaders failed %v err <%v>", msg, + err) + } + + // Older protocol versions should fail encode since message didn't + // exist yet. + oldPver := wire.SendHeadersVersion - 1 + err = msg.BtcEncode(&buf, oldPver) + if err == nil { + s := "encode of MsgSendHeaders passed for old protocol " + + "version %v err <%v>" + t.Errorf(s, msg, err) + } + + // Test decode with latest protocol version. + readmsg := wire.NewMsgSendHeaders() + err = readmsg.BtcDecode(&buf, pver) + if err != nil { + t.Errorf("decode of MsgSendHeaders failed [%v] err <%v>", buf, + err) + } + + // Older protocol versions should fail decode since message didn't + // exist yet. + err = readmsg.BtcDecode(&buf, oldPver) + if err == nil { + s := "decode of MsgSendHeaders passed for old protocol " + + "version %v err <%v>" + t.Errorf(s, msg, err) + } + + return +} + +// TestSendHeadersBIP0130 tests the MsgSendHeaders API against the protocol +// prior to version SendHeadersVersion. +func TestSendHeadersBIP0130(t *testing.T) { + // Use the protocol version just prior to SendHeadersVersion changes. + pver := wire.SendHeadersVersion - 1 + + msg := wire.NewMsgSendHeaders() + + // Test encode with old protocol version. + var buf bytes.Buffer + err := msg.BtcEncode(&buf, pver) + if err == nil { + t.Errorf("encode of MsgSendHeaders succeeded when it should " + + "have failed") + } + + // Test decode with old protocol version. + readmsg := wire.NewMsgSendHeaders() + err = readmsg.BtcDecode(&buf, pver) + if err == nil { + t.Errorf("decode of MsgSendHeaders succeeded when it should " + + "have failed") + } + + return +} + +// TestSendHeadersCrossProtocol tests the MsgSendHeaders API when encoding with +// the latest protocol version and decoding with SendHeadersVersion. +func TestSendHeadersCrossProtocol(t *testing.T) { + msg := wire.NewMsgSendHeaders() + + // Encode with latest protocol version. + var buf bytes.Buffer + err := msg.BtcEncode(&buf, wire.ProtocolVersion) + if err != nil { + t.Errorf("encode of MsgSendHeaders failed %v err <%v>", msg, + err) + } + + // Decode with old protocol version. + readmsg := wire.NewMsgSendHeaders() + err = readmsg.BtcDecode(&buf, wire.SendHeadersVersion) + if err != nil { + t.Errorf("decode of MsgSendHeaders failed [%v] err <%v>", buf, + err) + } +} + +// TestSendHeadersWire tests the MsgSendHeaders wire encode and decode for +// various protocol versions. +func TestSendHeadersWire(t *testing.T) { + msgSendHeaders := wire.NewMsgSendHeaders() + msgSendHeadersEncoded := []byte{} + + tests := []struct { + in *wire.MsgSendHeaders // Message to encode + out *wire.MsgSendHeaders // Expected decoded message + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + }{ + // Latest protocol version. + { + msgSendHeaders, + msgSendHeaders, + msgSendHeadersEncoded, + wire.ProtocolVersion, + }, + + // Protocol version SendHeadersVersion+1 + { + msgSendHeaders, + msgSendHeaders, + msgSendHeadersEncoded, + wire.SendHeadersVersion + 1, + }, + + // Protocol version SendHeadersVersion + { + msgSendHeaders, + msgSendHeaders, + msgSendHeadersEncoded, + wire.SendHeadersVersion, + }, + } + + 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 wire.MsgSendHeaders + rbuf := bytes.NewReader(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 + } + } +} diff --git a/wire/msgversion.go b/wire/msgversion.go index 4466f0f00..6fd3fc6b0 100644 --- a/wire/msgversion.go +++ b/wire/msgversion.go @@ -18,7 +18,7 @@ import ( const MaxUserAgentLen = 2000 // DefaultUserAgent for wire in the stack -const DefaultUserAgent = "/btcwire:0.3.0/" +const DefaultUserAgent = "/btcwire:0.4.0/" // MsgVersion implements the Message interface and represents a bitcoin version // message. It is used for a peer to advertise itself as soon as an outbound diff --git a/wire/protocol.go b/wire/protocol.go index 13826083e..ba2c209fb 100644 --- a/wire/protocol.go +++ b/wire/protocol.go @@ -1,4 +1,4 @@ -// Copyright (c) 2013-2015 The btcsuite developers +// Copyright (c) 2013-2016 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -12,7 +12,7 @@ import ( const ( // ProtocolVersion is the latest protocol version this package supports. - ProtocolVersion uint32 = 70011 + ProtocolVersion uint32 = 70012 // MultipleAddressVersion is the protocol version which added multiple // addresses per message (pver >= MultipleAddressVersion). @@ -39,6 +39,10 @@ const ( // service flag. BIP0111Version uint32 = 70011 + // SendHeadersVersion is the protocol version which added a new + // sendheaders message. + SendHeadersVersion uint32 = 70012 + // RejectVersion is the protocol version which added a new reject // message. RejectVersion uint32 = 70002