diff --git a/LICENSE b/LICENSE index 46dcd395..8005b865 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ ISC License -Copyright (c) 2013-2023 The btcsuite developers +Copyright (c) 2013-2024 The btcsuite developers Copyright (c) 2015-2016 The Decred developers Permission to use, copy, modify, and distribute this software for any diff --git a/peer/peer.go b/peer/peer.go index 195fc0b4..5767cbbf 100644 --- a/peer/peer.go +++ b/peer/peer.go @@ -18,18 +18,19 @@ import ( "sync/atomic" "time" + "github.com/btcsuite/go-socks/socks" + "github.com/davecgh/go-spew/spew" + "github.com/decred/dcrd/lru" + "github.com/btcsuite/btcd/blockchain" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/go-socks/socks" - "github.com/davecgh/go-spew/spew" - "github.com/decred/dcrd/lru" ) const ( // MaxProtocolVersion is the max protocol version the peer supports. - MaxProtocolVersion = wire.AddrV2Version + MaxProtocolVersion = wire.SendAddrV2Version // DefaultTrickleInterval is the min time between attempts to send an // inv message to a peer. @@ -877,8 +878,8 @@ func (p *Peer) PushAddrMsg(addresses []*wire.NetAddress) ([]*wire.NetAddress, er // // This function is safe for concurrent access. func (p *Peer) PushAddrV2Msg(addrs []*wire.NetAddressV2) ( - []*wire.NetAddressV2, error) { - + []*wire.NetAddressV2, error, +) { count := len(addrs) // Nothing to send. @@ -1900,8 +1901,8 @@ func (p *Peer) QueueMessage(msg wire.Message, doneChan chan<- struct{}) { // // This function is safe for concurrent access. func (p *Peer) QueueMessageWithEncoding(msg wire.Message, doneChan chan<- struct{}, - encoding wire.MessageEncoding) { - + encoding wire.MessageEncoding, +) { // Avoid risk of deadlock if goroutine already exited. The goroutine // we will be sending to hangs around until it knows for a fact that // it is marked as disconnected and *then* it drains the channels. @@ -2152,7 +2153,7 @@ func (p *Peer) writeLocalVersionMsg() error { // writeSendAddrV2Msg writes our sendaddrv2 message to the remote peer if the // peer supports protocol version 70016 and above. func (p *Peer) writeSendAddrV2Msg(pver uint32) error { - if pver < wire.AddrV2Version { + if pver < wire.SendAddrV2Version { return nil } @@ -2180,7 +2181,7 @@ func (p *Peer) waitToFinishNegotiation(pver uint32) error { switch m := remoteMsg.(type) { case *wire.MsgSendAddrV2: - if pver >= wire.AddrV2Version { + if pver >= wire.SendAddrV2Version { p.flagsMtx.Lock() p.sendAddrV2 = true p.flagsMtx.Unlock() diff --git a/wire/message.go b/wire/message.go index 1f412fa6..a90fb627 100644 --- a/wire/message.go +++ b/wire/message.go @@ -1,4 +1,4 @@ -// Copyright (c) 2013-2016 The btcsuite developers +// Copyright (c) 2013-2024 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -59,6 +59,7 @@ const ( CmdCFHeaders = "cfheaders" CmdCFCheckpt = "cfcheckpt" CmdSendAddrV2 = "sendaddrv2" + CmdWTxIdRelay = "wtxidrelay" ) // MessageEncoding represents the wire message encoding format to be used. @@ -111,6 +112,9 @@ func makeEmptyMessage(command string) (Message, error) { case CmdSendAddrV2: msg = &MsgSendAddrV2{} + case CmdWTxIdRelay: + msg = &MsgWTxIdRelay{} + case CmdGetAddr: msg = &MsgGetAddr{} @@ -276,8 +280,8 @@ func WriteMessage(w io.Writer, msg Message, pver uint32, btcnet BitcoinNet) erro // to specify the message encoding format to be used when serializing wire // messages. func WriteMessageWithEncodingN(w io.Writer, msg Message, pver uint32, - btcnet BitcoinNet, encoding MessageEncoding) (int, error) { - + btcnet BitcoinNet, encoding MessageEncoding, +) (int, error) { totalBytes := 0 // Enforce max command size. @@ -353,8 +357,8 @@ func WriteMessageWithEncodingN(w io.Writer, msg Message, pver uint32, // allows the caller to specify which message encoding is to to consult when // decoding wire messages. func ReadMessageWithEncodingN(r io.Reader, pver uint32, btcnet BitcoinNet, - enc MessageEncoding) (int, Message, []byte, error) { - + enc MessageEncoding, +) (int, Message, []byte, error) { totalBytes := 0 n, hdr, err := readMessageHeader(r) totalBytes += n diff --git a/wire/msgsendaddrv2.go b/wire/msgsendaddrv2.go index d6d19efb..8348b815 100644 --- a/wire/msgsendaddrv2.go +++ b/wire/msgsendaddrv2.go @@ -1,6 +1,7 @@ package wire import ( + "fmt" "io" ) @@ -14,12 +15,24 @@ type MsgSendAddrV2 struct{} // BtcDecode decodes r using the bitcoin protocol encoding into the receiver. // This is part of the Message interface implementation. func (msg *MsgSendAddrV2) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error { + if pver < SendAddrV2Version { + str := fmt.Sprintf("sendaddrv2 message invalid for protocol "+ + "version %d", pver) + return messageError("MsgSendAddrV2.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 *MsgSendAddrV2) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error { + if pver < SendAddrV2Version { + str := fmt.Sprintf("sendaddrv2 message invalid for protocol "+ + "version %d", pver) + return messageError("MsgSendAddrV2.BtcEncode", str) + } + return nil } diff --git a/wire/msgsendaddrv2_test.go b/wire/msgsendaddrv2_test.go new file mode 100644 index 00000000..db6be1d2 --- /dev/null +++ b/wire/msgsendaddrv2_test.go @@ -0,0 +1,193 @@ +// Copyright (c) 2024 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 ( + "bytes" + "reflect" + "testing" + + "github.com/davecgh/go-spew/spew" +) + +// TestSendAddrV2 tests the MsgSendAddrV2 API against the latest protocol +// version. +func TestSendAddrV2(t *testing.T) { + pver := ProtocolVersion + enc := BaseEncoding + + // Ensure the command is expected value. + wantCmd := "sendaddrv2" + msg := NewMsgSendAddrV2() + if cmd := msg.Command(); cmd != wantCmd { + t.Errorf("NewMsgSendAddrV2: 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, enc) + if err != nil { + t.Errorf("encode of MsgSendAddrV2 failed %v err <%v>", msg, + err) + } + + // Older protocol versions should fail encode since message didn't + // exist yet. + oldPver := SendAddrV2Version - 1 + err = msg.BtcEncode(&buf, oldPver, enc) + if err == nil { + s := "encode of MsgSendAddrV2 passed for old protocol " + + "version %v err <%v>" + t.Errorf(s, msg, err) + } + + // Test decode with latest protocol version. + readmsg := NewMsgSendAddrV2() + err = readmsg.BtcDecode(&buf, pver, enc) + if err != nil { + t.Errorf("decode of MsgSendAddrV2 failed [%v] err <%v>", buf, + err) + } + + // Older protocol versions should fail decode since message didn't + // exist yet. + err = readmsg.BtcDecode(&buf, oldPver, enc) + if err == nil { + s := "decode of MsgSendAddrV2 passed for old protocol " + + "version %v err <%v>" + t.Errorf(s, msg, err) + } +} + +// TestSendAddrV2BIP0130 tests the MsgSendAddrV2 API against the protocol +// prior to version SendAddrV2Version. +func TestSendAddrV2BIP0130(t *testing.T) { + // Use the protocol version just prior to SendAddrV2Version changes. + pver := SendAddrV2Version - 1 + enc := BaseEncoding + + msg := NewMsgSendAddrV2() + + // Test encode with old protocol version. + var buf bytes.Buffer + err := msg.BtcEncode(&buf, pver, enc) + if err == nil { + t.Errorf("encode of MsgSendAddrV2 succeeded when it should " + + "have failed") + } + + // Test decode with old protocol version. + readmsg := NewMsgSendAddrV2() + err = readmsg.BtcDecode(&buf, pver, enc) + if err == nil { + t.Errorf("decode of MsgSendAddrV2 succeeded when it should " + + "have failed") + } +} + +// TestSendAddrV2CrossProtocol tests the MsgSendAddrV2 API when encoding with +// the latest protocol version and decoding with SendAddrV2Version. +func TestSendAddrV2CrossProtocol(t *testing.T) { + enc := BaseEncoding + msg := NewMsgSendAddrV2() + + // Encode with latest protocol version. + var buf bytes.Buffer + err := msg.BtcEncode(&buf, ProtocolVersion, enc) + if err != nil { + t.Errorf("encode of MsgSendAddrV2 failed %v err <%v>", msg, + err) + } + + // Decode with old protocol version. + readmsg := NewMsgSendAddrV2() + err = readmsg.BtcDecode(&buf, SendAddrV2Version, enc) + if err != nil { + t.Errorf("decode of MsgSendAddrV2 failed [%v] err <%v>", buf, + err) + } +} + +// TestSendAddrV2Wire tests the MsgSendAddrV2 wire encode and decode for +// various protocol versions. +func TestSendAddrV2Wire(t *testing.T) { + msgSendAddrV2 := NewMsgSendAddrV2() + msgSendAddrV2Encoded := []byte{} + + tests := []struct { + in *MsgSendAddrV2 // Message to encode + out *MsgSendAddrV2 // Expected decoded message + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + enc MessageEncoding // Message encoding format + }{ + // Latest protocol version. + { + msgSendAddrV2, + msgSendAddrV2, + msgSendAddrV2Encoded, + ProtocolVersion, + BaseEncoding, + }, + + // Protocol version SendAddrV2Version+1 + { + msgSendAddrV2, + msgSendAddrV2, + msgSendAddrV2Encoded, + SendAddrV2Version + 1, + BaseEncoding, + }, + + // Protocol version SendAddrV2Version + { + msgSendAddrV2, + msgSendAddrV2, + msgSendAddrV2Encoded, + SendAddrV2Version, + BaseEncoding, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode the message to wire format. + var buf bytes.Buffer + err := test.in.BtcEncode(&buf, test.pver, test.enc) + if err != nil { + t.Errorf("BtcEncode #%d error %v", i, err) + continue + } + if !bytes.Equal(buf.Bytes(), test.buf) { + t.Errorf("BtcEncode #%d\n got: %s want: %s", i, + spew.Sdump(buf.Bytes()), spew.Sdump(test.buf)) + continue + } + + // Decode the message from wire format. + var msg MsgSendAddrV2 + rbuf := bytes.NewReader(test.buf) + err = msg.BtcDecode(rbuf, test.pver, test.enc) + if err != nil { + t.Errorf("BtcDecode #%d error %v", i, err) + continue + } + if !reflect.DeepEqual(&msg, test.out) { + t.Errorf("BtcDecode #%d\n got: %s want: %s", i, + spew.Sdump(msg), spew.Sdump(test.out)) + continue + } + } +} diff --git a/wire/msgwtxidrelay.go b/wire/msgwtxidrelay.go new file mode 100644 index 00000000..480c711d --- /dev/null +++ b/wire/msgwtxidrelay.go @@ -0,0 +1,59 @@ +// Copyright (c) 2024 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" +) + +// MsgWTxIdRelay defines a bitcoin wtxidrelay message which is used for a peer +// to signal support for relaying witness transaction id (BIP141). It +// implements the Message interface. +// +// This message has no payload. +type MsgWTxIdRelay struct{} + +// BtcDecode decodes r using the bitcoin protocol encoding into the receiver. +// This is part of the Message interface implementation. +func (msg *MsgWTxIdRelay) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error { + if pver < WTxIdRelayVersion { + str := fmt.Sprintf("wtxidrelay message invalid for protocol "+ + "version %d", pver) + return messageError("MsgWTxIdRelay.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 *MsgWTxIdRelay) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error { + if pver < WTxIdRelayVersion { + str := fmt.Sprintf("wtxidrelay message invalid for protocol "+ + "version %d", pver) + return messageError("MsgWTxIdRelay.BtcEncode", str) + } + + return nil +} + +// Command returns the protocol command string for the message. This is part +// of the Message interface implementation. +func (msg *MsgWTxIdRelay) Command() string { + return CmdWTxIdRelay +} + +// MaxPayloadLength returns the maximum length the payload can be for the +// receiver. This is part of the Message interface implementation. +func (msg *MsgWTxIdRelay) MaxPayloadLength(pver uint32) uint32 { + return 0 +} + +// NewMsgWTxIdRelay returns a new bitcoin wtxidrelay message that conforms +// to the Message interface. +func NewMsgWTxIdRelay() *MsgWTxIdRelay { + return &MsgWTxIdRelay{} +} diff --git a/wire/msgwtxidrelay_test.go b/wire/msgwtxidrelay_test.go new file mode 100644 index 00000000..e7cde73a --- /dev/null +++ b/wire/msgwtxidrelay_test.go @@ -0,0 +1,193 @@ +// Copyright (c) 2024 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 ( + "bytes" + "reflect" + "testing" + + "github.com/davecgh/go-spew/spew" +) + +// TestWTxIdRelay tests the MsgWTxIdRelay API against the latest protocol +// version. +func TestWTxIdRelay(t *testing.T) { + pver := ProtocolVersion + enc := BaseEncoding + + // Ensure the command is expected value. + wantCmd := "wtxidrelay" + msg := NewMsgWTxIdRelay() + if cmd := msg.Command(); cmd != wantCmd { + t.Errorf("NewMsgWTxIdRelay: 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, enc) + if err != nil { + t.Errorf("encode of MsgWTxIdRelay failed %v err <%v>", msg, + err) + } + + // Older protocol versions should fail encode since message didn't + // exist yet. + oldPver := WTxIdRelayVersion - 1 + err = msg.BtcEncode(&buf, oldPver, enc) + if err == nil { + s := "encode of MsgWTxIdRelay passed for old protocol " + + "version %v err <%v>" + t.Errorf(s, msg, err) + } + + // Test decode with latest protocol version. + readmsg := NewMsgWTxIdRelay() + err = readmsg.BtcDecode(&buf, pver, enc) + if err != nil { + t.Errorf("decode of MsgWTxIdRelay failed [%v] err <%v>", buf, + err) + } + + // Older protocol versions should fail decode since message didn't + // exist yet. + err = readmsg.BtcDecode(&buf, oldPver, enc) + if err == nil { + s := "decode of MsgWTxIdRelay passed for old protocol " + + "version %v err <%v>" + t.Errorf(s, msg, err) + } +} + +// TestWTxIdRelayBIP0130 tests the MsgWTxIdRelay API against the protocol +// prior to version WTxIdRelayVersion. +func TestWTxIdRelayBIP0130(t *testing.T) { + // Use the protocol version just prior to WTxIdRelayVersion changes. + pver := WTxIdRelayVersion - 1 + enc := BaseEncoding + + msg := NewMsgWTxIdRelay() + + // Test encode with old protocol version. + var buf bytes.Buffer + err := msg.BtcEncode(&buf, pver, enc) + if err == nil { + t.Errorf("encode of MsgWTxIdRelay succeeded when it should " + + "have failed") + } + + // Test decode with old protocol version. + readmsg := NewMsgWTxIdRelay() + err = readmsg.BtcDecode(&buf, pver, enc) + if err == nil { + t.Errorf("decode of MsgWTxIdRelay succeeded when it should " + + "have failed") + } +} + +// TestWTxIdRelayCrossProtocol tests the MsgWTxIdRelay API when encoding with +// the latest protocol version and decoding with WTxIdRelayVersion. +func TestWTxIdRelayCrossProtocol(t *testing.T) { + enc := BaseEncoding + msg := NewMsgWTxIdRelay() + + // Encode with latest protocol version. + var buf bytes.Buffer + err := msg.BtcEncode(&buf, ProtocolVersion, enc) + if err != nil { + t.Errorf("encode of MsgWTxIdRelay failed %v err <%v>", msg, + err) + } + + // Decode with old protocol version. + readmsg := NewMsgWTxIdRelay() + err = readmsg.BtcDecode(&buf, WTxIdRelayVersion, enc) + if err != nil { + t.Errorf("decode of MsgWTxIdRelay failed [%v] err <%v>", buf, + err) + } +} + +// TestWTxIdRelayWire tests the MsgWTxIdRelay wire encode and decode for +// various protocol versions. +func TestWTxIdRelayWire(t *testing.T) { + msgWTxIdRelay := NewMsgWTxIdRelay() + msgWTxIdRelayEncoded := []byte{} + + tests := []struct { + in *MsgWTxIdRelay // Message to encode + out *MsgWTxIdRelay // Expected decoded message + buf []byte // Wire encoding + pver uint32 // Protocol version for wire encoding + enc MessageEncoding // Message encoding format + }{ + // Latest protocol version. + { + msgWTxIdRelay, + msgWTxIdRelay, + msgWTxIdRelayEncoded, + ProtocolVersion, + BaseEncoding, + }, + + // Protocol version WTxIdRelayVersion+1 + { + msgWTxIdRelay, + msgWTxIdRelay, + msgWTxIdRelayEncoded, + WTxIdRelayVersion + 1, + BaseEncoding, + }, + + // Protocol version WTxIdRelayVersion + { + msgWTxIdRelay, + msgWTxIdRelay, + msgWTxIdRelayEncoded, + WTxIdRelayVersion, + BaseEncoding, + }, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + // Encode the message to wire format. + var buf bytes.Buffer + err := test.in.BtcEncode(&buf, test.pver, test.enc) + if err != nil { + t.Errorf("BtcEncode #%d error %v", i, err) + continue + } + if !bytes.Equal(buf.Bytes(), test.buf) { + t.Errorf("BtcEncode #%d\n got: %s want: %s", i, + spew.Sdump(buf.Bytes()), spew.Sdump(test.buf)) + continue + } + + // Decode the message from wire format. + var msg MsgWTxIdRelay + rbuf := bytes.NewReader(test.buf) + err = msg.BtcDecode(rbuf, test.pver, test.enc) + if err != nil { + t.Errorf("BtcDecode #%d error %v", i, err) + continue + } + if !reflect.DeepEqual(&msg, test.out) { + t.Errorf("BtcDecode #%d\n got: %s want: %s", i, + spew.Sdump(msg), spew.Sdump(test.out)) + continue + } + } +} diff --git a/wire/protocol.go b/wire/protocol.go index baeec053..e1344f3a 100644 --- a/wire/protocol.go +++ b/wire/protocol.go @@ -1,4 +1,4 @@ -// Copyright (c) 2013-2016 The btcsuite developers +// Copyright (c) 2013-2024 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -52,12 +52,16 @@ const ( // feefilter message. FeeFilterVersion uint32 = 70013 - // AddrV2Version is the protocol version which added two new messages. - // sendaddrv2 is sent during the version-verack handshake and signals - // support for sending and receiving the addrv2 message. In the future, - // new messages that occur during the version-verack handshake will not - // come with a protocol version bump. - AddrV2Version uint32 = 70016 + // SendAddrV2Version is the protocol version which added two new + // messages. sendaddrv2 is sent during the version-verack handshake + // and signals support for sending and receiving the addrv2 message. In + // the future, new messages that occur during the version-verack + // handshake will not come with a protocol version bump. + // In addition, wtxidrelay was also added as an optional message in the + // same protocol version. + SendAddrV2Version uint32 = 70016 + WTxIdRelayVersion uint32 = SendAddrV2Version + AddrV2Version uint32 = SendAddrV2Version // Keep for upstream compatibility ) const (