Implemented BIP 0014 format for user agent

the new function AddUserAgent adds the user agent to the stack
and formats it as per BIP 0014
e.g: "/btcwire:0.1.4/myclient:1.2.3(optional; comments)/"

the validation on UserAgent has been moved to a new function
validateUserAgent
This commit is contained in:
Javed Khan 2014-04-19 14:27:53 +05:30
parent a9293bd32e
commit 620cbdeb8e
5 changed files with 75 additions and 23 deletions

View File

@ -105,7 +105,6 @@ from a remote peer is:
## TODO ## TODO
- Implement functions for [BIP 0014](https://en.bitcoin.it/wiki/BIP_0014)
- Implement alert message decoding/encoding - Implement alert message decoding/encoding
- Implement bloom filter messages (filterload, filteradd, filterclear, - Implement bloom filter messages (filterload, filteradd, filterclear,
merkleblock) as defined in [BIP 0037](https://en.bitcoin.it/wiki/BIP_0037) merkleblock) as defined in [BIP 0037](https://en.bitcoin.it/wiki/BIP_0037)

1
doc.go
View File

@ -150,6 +150,7 @@ Bitcoin Improvement Proposals
This package includes spec changes outlined by the following BIPs: 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) BIP0031 (https://en.bitcoin.it/wiki/BIP_0031)
BIP0035 (https://en.bitcoin.it/wiki/BIP_0035) BIP0035 (https://en.bitcoin.it/wiki/BIP_0035)

View File

@ -51,7 +51,7 @@ func TestMessage(t *testing.T) {
t.Errorf("NewNetAddress: %v", err) t.Errorf("NewNetAddress: %v", err)
} }
me.Timestamp = time.Time{} // Version message has zero value timestamp. me.Timestamp = time.Time{} // Version message has zero value timestamp.
msgVersion := btcwire.NewMsgVersion(me, you, 123123, "/test:0.0.1/", 0) msgVersion := btcwire.NewMsgVersion(me, you, 123123, 0)
msgVerack := btcwire.NewMsgVerAck() msgVerack := btcwire.NewMsgVerAck()
msgGetAddr := btcwire.NewMsgGetAddr() msgGetAddr := btcwire.NewMsgGetAddr()
@ -76,7 +76,7 @@ func TestMessage(t *testing.T) {
btcnet btcwire.BitcoinNet // Network to use for wire encoding btcnet btcwire.BitcoinNet // Network to use for wire encoding
bytes int // Expected num bytes read/written bytes int // Expected num bytes read/written
}{ }{
{msgVersion, msgVersion, pver, btcwire.MainNet, 122}, {msgVersion, msgVersion, pver, btcwire.MainNet, 125},
{msgVerack, msgVerack, pver, btcwire.MainNet, 24}, {msgVerack, msgVerack, pver, btcwire.MainNet, 24},
{msgGetAddr, msgGetAddr, pver, btcwire.MainNet, 24}, {msgGetAddr, msgGetAddr, pver, btcwire.MainNet, 24},
{msgAddr, msgAddr, pver, btcwire.MainNet, 25}, {msgAddr, msgAddr, pver, btcwire.MainNet, 25},

View File

@ -9,6 +9,7 @@ import (
"fmt" "fmt"
"io" "io"
"net" "net"
"strings"
"time" "time"
) )
@ -16,6 +17,9 @@ import (
// version message (MsgVersion). // version message (MsgVersion).
const MaxUserAgentLen = 2000 const MaxUserAgentLen = 2000
// DefaultUserAgent for btcwire in the stack
const DefaultUserAgent = "/btcwire:0.1.4/"
// MsgVersion implements the Message interface and represents a bitcoin version // 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 // message. It is used for a peer to advertise itself as soon as an outbound
// connection is made. The remote peer then uses this information along with // connection is made. The remote peer then uses this information along with
@ -115,10 +119,9 @@ func (msg *MsgVersion) BtcDecode(r io.Reader, pver uint32) error {
if err != nil { if err != nil {
return err return err
} }
if len(userAgent) > MaxUserAgentLen { err = validateUserAgent(userAgent)
str := fmt.Sprintf("user agent too long [len %v, max %v]", if err != nil {
len(userAgent), MaxUserAgentLen) return err
return messageError("MsgVersion.BtcDecode", str)
} }
msg.UserAgent = userAgent msg.UserAgent = userAgent
} }
@ -152,13 +155,12 @@ func (msg *MsgVersion) BtcDecode(r io.Reader, pver uint32) error {
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding. // BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
// This is part of the Message interface implementation. // This is part of the Message interface implementation.
func (msg *MsgVersion) BtcEncode(w io.Writer, pver uint32) error { func (msg *MsgVersion) BtcEncode(w io.Writer, pver uint32) error {
if len(msg.UserAgent) > MaxUserAgentLen { err := validateUserAgent(msg.UserAgent)
str := fmt.Sprintf("user agent too long [len %v, max %v]", if err != nil {
len(msg.UserAgent), MaxUserAgentLen) return err
return messageError("MsgVersion.BtcEncode", str)
} }
err := writeElements(w, msg.ProtocolVersion, msg.Services, err = writeElements(w, msg.ProtocolVersion, msg.Services,
msg.Timestamp.Unix()) msg.Timestamp.Unix())
if err != nil { if err != nil {
return err return err
@ -224,7 +226,7 @@ func (msg *MsgVersion) MaxPayloadLength(pver uint32) uint32 {
// Message interface using the passed parameters and defaults for the remaining // Message interface using the passed parameters and defaults for the remaining
// fields. // fields.
func NewMsgVersion(me *NetAddress, you *NetAddress, nonce uint64, func NewMsgVersion(me *NetAddress, you *NetAddress, nonce uint64,
userAgent string, lastBlock int32) *MsgVersion { lastBlock int32) *MsgVersion {
// Limit the timestamp to one second precision since the protocol // Limit the timestamp to one second precision since the protocol
// doesn't support better. // doesn't support better.
@ -235,7 +237,7 @@ func NewMsgVersion(me *NetAddress, you *NetAddress, nonce uint64,
AddrYou: *you, AddrYou: *you,
AddrMe: *me, AddrMe: *me,
Nonce: nonce, Nonce: nonce,
UserAgent: userAgent, UserAgent: DefaultUserAgent,
LastBlock: lastBlock, LastBlock: lastBlock,
DisableRelayTx: false, DisableRelayTx: false,
} }
@ -244,7 +246,7 @@ func NewMsgVersion(me *NetAddress, you *NetAddress, nonce uint64,
// NewMsgVersionFromConn is a convenience function that extracts the remote // NewMsgVersionFromConn is a convenience function that extracts the remote
// and local address from conn and returns a new bitcoin version message that // and local address from conn and returns a new bitcoin version message that
// conforms to the Message interface. See NewMsgVersion. // conforms to the Message interface. See NewMsgVersion.
func NewMsgVersionFromConn(conn net.Conn, nonce uint64, userAgent string, func NewMsgVersionFromConn(conn net.Conn, nonce uint64,
lastBlock int32) (*MsgVersion, error) { lastBlock int32) (*MsgVersion, error) {
// Don't assume any services until we know otherwise. // Don't assume any services until we know otherwise.
@ -259,5 +261,33 @@ func NewMsgVersionFromConn(conn net.Conn, nonce uint64, userAgent string,
return nil, err return nil, err
} }
return NewMsgVersion(lna, rna, nonce, userAgent, lastBlock), nil return NewMsgVersion(lna, rna, nonce, lastBlock), nil
}
// validateUserAgent checks userAgent length against MaxUserAgentLen
func validateUserAgent(userAgent string) error {
if len(userAgent) > MaxUserAgentLen {
str := fmt.Sprintf("user agent too long [len %v, max %v]",
len(userAgent), MaxUserAgentLen)
return messageError("MsgVersion", str)
}
return nil
}
// AddUserAgent adds a custom user agent
func (msg *MsgVersion) AddUserAgent(name string, version string,
comments ...string) error {
newUserAgent := fmt.Sprintf("%s:%s", name, version)
if len(comments) != 0 {
newUserAgent = fmt.Sprintf("%s(%s)", newUserAgent,
strings.Join(comments, "; "))
}
newUserAgent = fmt.Sprintf("%s%s/", msg.UserAgent, newUserAgent)
err := validateUserAgent(newUserAgent)
if err != nil {
return err
}
msg.UserAgent = newUserAgent
return nil
} }

View File

@ -21,7 +21,6 @@ func TestVersion(t *testing.T) {
pver := btcwire.ProtocolVersion pver := btcwire.ProtocolVersion
// Create version message data. // Create version message data.
userAgent := "/btcdtest:0.0.1/"
lastBlock := int32(234234) lastBlock := int32(234234)
tcpAddrMe := &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 8333} tcpAddrMe := &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 8333}
me, err := btcwire.NewNetAddress(tcpAddrMe, btcwire.SFNodeNetwork) me, err := btcwire.NewNetAddress(tcpAddrMe, btcwire.SFNodeNetwork)
@ -39,7 +38,7 @@ func TestVersion(t *testing.T) {
} }
// Ensure we get the correct data back out. // Ensure we get the correct data back out.
msg := btcwire.NewMsgVersion(me, you, nonce, userAgent, lastBlock) msg := btcwire.NewMsgVersion(me, you, nonce, lastBlock)
if msg.ProtocolVersion != int32(pver) { if msg.ProtocolVersion != int32(pver) {
t.Errorf("NewMsgVersion: wrong protocol version - got %v, want %v", t.Errorf("NewMsgVersion: wrong protocol version - got %v, want %v",
msg.ProtocolVersion, pver) msg.ProtocolVersion, pver)
@ -56,9 +55,9 @@ func TestVersion(t *testing.T) {
t.Errorf("NewMsgVersion: wrong nonce - got %v, want %v", t.Errorf("NewMsgVersion: wrong nonce - got %v, want %v",
msg.Nonce, nonce) msg.Nonce, nonce)
} }
if msg.UserAgent != userAgent { if msg.UserAgent != btcwire.DefaultUserAgent {
t.Errorf("NewMsgVersion: wrong user agent - got %v, want %v", t.Errorf("NewMsgVersion: wrong user agent - got %v, want %v",
msg.UserAgent, userAgent) msg.UserAgent, btcwire.DefaultUserAgent)
} }
if msg.LastBlock != lastBlock { if msg.LastBlock != lastBlock {
t.Errorf("NewMsgVersion: wrong last block - got %v, want %v", t.Errorf("NewMsgVersion: wrong last block - got %v, want %v",
@ -69,6 +68,29 @@ func TestVersion(t *testing.T) {
"default - got %v, want %v", msg.DisableRelayTx, false) "default - got %v, want %v", msg.DisableRelayTx, false)
} }
msg.AddUserAgent("myclient", "1.2.3", "optional", "comments")
customUserAgent := btcwire.DefaultUserAgent + "myclient:1.2.3(optional; comments)/"
if msg.UserAgent != customUserAgent {
t.Errorf("AddUserAgent: wrong user agent - got %s, want %s",
msg.UserAgent, customUserAgent)
}
msg.AddUserAgent("mygui", "3.4.5")
customUserAgent += "mygui:3.4.5/"
if msg.UserAgent != customUserAgent {
t.Errorf("AddUserAgent: wrong user agent - got %s, want %s",
msg.UserAgent, customUserAgent)
}
// accounting for ":", "/"
err = msg.AddUserAgent(strings.Repeat("t",
btcwire.MaxUserAgentLen-len(customUserAgent)-2+1), "")
if _, ok := err.(*btcwire.MessageError); !ok {
t.Errorf("AddUserAgent: expected error not received "+
"- got %v, want %T", err, btcwire.MessageError{})
}
// Version message should not have any services set by default. // Version message should not have any services set by default.
if msg.Services != 0 { if msg.Services != 0 {
t.Errorf("NewMsgVersion: wrong default services - got %v, want %v", t.Errorf("NewMsgVersion: wrong default services - got %v, want %v",
@ -111,7 +133,7 @@ func TestVersion(t *testing.T) {
// Use a fake connection. // Use a fake connection.
conn := &fakeConn{localAddr: tcpAddrMe, remoteAddr: tcpAddrYou} conn := &fakeConn{localAddr: tcpAddrMe, remoteAddr: tcpAddrYou}
msg, err = btcwire.NewMsgVersionFromConn(conn, nonce, userAgent, lastBlock) msg, err = btcwire.NewMsgVersionFromConn(conn, nonce, lastBlock)
if err != nil { if err != nil {
t.Errorf("NewMsgVersionFromConn: %v", err) t.Errorf("NewMsgVersionFromConn: %v", err)
} }
@ -131,7 +153,7 @@ func TestVersion(t *testing.T) {
localAddr: &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 8333}, localAddr: &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 8333},
remoteAddr: tcpAddrYou, remoteAddr: tcpAddrYou,
} }
msg, err = btcwire.NewMsgVersionFromConn(conn, nonce, userAgent, lastBlock) msg, err = btcwire.NewMsgVersionFromConn(conn, nonce, lastBlock)
if err != btcwire.ErrInvalidNetAddr { if err != btcwire.ErrInvalidNetAddr {
t.Errorf("NewMsgVersionFromConn: expected error not received "+ t.Errorf("NewMsgVersionFromConn: expected error not received "+
"- got %v, want %v", err, btcwire.ErrInvalidNetAddr) "- got %v, want %v", err, btcwire.ErrInvalidNetAddr)
@ -142,7 +164,7 @@ func TestVersion(t *testing.T) {
localAddr: tcpAddrMe, localAddr: tcpAddrMe,
remoteAddr: &net.UDPAddr{IP: net.ParseIP("192.168.0.1"), Port: 8333}, remoteAddr: &net.UDPAddr{IP: net.ParseIP("192.168.0.1"), Port: 8333},
} }
msg, err = btcwire.NewMsgVersionFromConn(conn, nonce, userAgent, lastBlock) msg, err = btcwire.NewMsgVersionFromConn(conn, nonce, lastBlock)
if err != btcwire.ErrInvalidNetAddr { if err != btcwire.ErrInvalidNetAddr {
t.Errorf("NewMsgVersionFromConn: expected error not received "+ t.Errorf("NewMsgVersionFromConn: expected error not received "+
"- got %v, want %v", err, btcwire.ErrInvalidNetAddr) "- got %v, want %v", err, btcwire.ErrInvalidNetAddr)