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
- Implement functions for [BIP 0014](https://en.bitcoin.it/wiki/BIP_0014)
- 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)

1
doc.go
View file

@ -150,6 +150,7 @@ Bitcoin Improvement Proposals
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)

View file

@ -51,7 +51,7 @@ func TestMessage(t *testing.T) {
t.Errorf("NewNetAddress: %v", err)
}
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()
msgGetAddr := btcwire.NewMsgGetAddr()
@ -76,7 +76,7 @@ func TestMessage(t *testing.T) {
btcnet btcwire.BitcoinNet // Network to use for wire encoding
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},
{msgGetAddr, msgGetAddr, pver, btcwire.MainNet, 24},
{msgAddr, msgAddr, pver, btcwire.MainNet, 25},

View file

@ -9,6 +9,7 @@ import (
"fmt"
"io"
"net"
"strings"
"time"
)
@ -16,6 +17,9 @@ import (
// version message (MsgVersion).
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
// 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
@ -115,10 +119,9 @@ func (msg *MsgVersion) BtcDecode(r io.Reader, pver uint32) error {
if err != nil {
return err
}
if len(userAgent) > MaxUserAgentLen {
str := fmt.Sprintf("user agent too long [len %v, max %v]",
len(userAgent), MaxUserAgentLen)
return messageError("MsgVersion.BtcDecode", str)
err = validateUserAgent(userAgent)
if err != nil {
return err
}
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.
// This is part of the Message interface implementation.
func (msg *MsgVersion) BtcEncode(w io.Writer, pver uint32) error {
if len(msg.UserAgent) > MaxUserAgentLen {
str := fmt.Sprintf("user agent too long [len %v, max %v]",
len(msg.UserAgent), MaxUserAgentLen)
return messageError("MsgVersion.BtcEncode", str)
err := validateUserAgent(msg.UserAgent)
if err != nil {
return err
}
err := writeElements(w, msg.ProtocolVersion, msg.Services,
err = writeElements(w, msg.ProtocolVersion, msg.Services,
msg.Timestamp.Unix())
if err != nil {
return err
@ -224,7 +226,7 @@ func (msg *MsgVersion) MaxPayloadLength(pver uint32) uint32 {
// Message interface using the passed parameters and defaults for the remaining
// fields.
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
// doesn't support better.
@ -235,7 +237,7 @@ func NewMsgVersion(me *NetAddress, you *NetAddress, nonce uint64,
AddrYou: *you,
AddrMe: *me,
Nonce: nonce,
UserAgent: userAgent,
UserAgent: DefaultUserAgent,
LastBlock: lastBlock,
DisableRelayTx: false,
}
@ -244,7 +246,7 @@ func NewMsgVersion(me *NetAddress, you *NetAddress, nonce uint64,
// NewMsgVersionFromConn is a convenience function that extracts the remote
// and local address from conn and returns a new bitcoin version message that
// 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) {
// 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 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
// Create version message data.
userAgent := "/btcdtest:0.0.1/"
lastBlock := int32(234234)
tcpAddrMe := &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 8333}
me, err := btcwire.NewNetAddress(tcpAddrMe, btcwire.SFNodeNetwork)
@ -39,7 +38,7 @@ func TestVersion(t *testing.T) {
}
// 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) {
t.Errorf("NewMsgVersion: wrong protocol version - got %v, want %v",
msg.ProtocolVersion, pver)
@ -56,9 +55,9 @@ func TestVersion(t *testing.T) {
t.Errorf("NewMsgVersion: wrong nonce - got %v, want %v",
msg.Nonce, nonce)
}
if msg.UserAgent != userAgent {
if msg.UserAgent != btcwire.DefaultUserAgent {
t.Errorf("NewMsgVersion: wrong user agent - got %v, want %v",
msg.UserAgent, userAgent)
msg.UserAgent, btcwire.DefaultUserAgent)
}
if msg.LastBlock != lastBlock {
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)
}
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.
if msg.Services != 0 {
t.Errorf("NewMsgVersion: wrong default services - got %v, want %v",
@ -111,7 +133,7 @@ func TestVersion(t *testing.T) {
// Use a fake connection.
conn := &fakeConn{localAddr: tcpAddrMe, remoteAddr: tcpAddrYou}
msg, err = btcwire.NewMsgVersionFromConn(conn, nonce, userAgent, lastBlock)
msg, err = btcwire.NewMsgVersionFromConn(conn, nonce, lastBlock)
if err != nil {
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},
remoteAddr: tcpAddrYou,
}
msg, err = btcwire.NewMsgVersionFromConn(conn, nonce, userAgent, lastBlock)
msg, err = btcwire.NewMsgVersionFromConn(conn, nonce, lastBlock)
if err != btcwire.ErrInvalidNetAddr {
t.Errorf("NewMsgVersionFromConn: expected error not received "+
"- got %v, want %v", err, btcwire.ErrInvalidNetAddr)
@ -142,7 +164,7 @@ func TestVersion(t *testing.T) {
localAddr: tcpAddrMe,
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 {
t.Errorf("NewMsgVersionFromConn: expected error not received "+
"- got %v, want %v", err, btcwire.ErrInvalidNetAddr)