// Copyright (c) 2013-2015 Conformal Systems LLC. // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. package wire_test import ( "bytes" "io" "net" "reflect" "testing" "time" "github.com/btcsuite/btcd/wire" "github.com/davecgh/go-spew/spew" ) // TestNetAddress tests the NetAddress API. func TestNetAddress(t *testing.T) { ip := net.ParseIP("127.0.0.1") port := 8333 // Test NewNetAddress. tcpAddr := &net.TCPAddr{ IP: ip, Port: port, } na, err := wire.NewNetAddress(tcpAddr, 0) if err != nil { t.Errorf("NewNetAddress: %v", err) } // Ensure we get the same ip, port, and services back out. if !na.IP.Equal(ip) { t.Errorf("NetNetAddress: wrong ip - got %v, want %v", na.IP, ip) } if na.Port != uint16(port) { t.Errorf("NetNetAddress: wrong port - got %v, want %v", na.Port, port) } if na.Services != 0 { t.Errorf("NetNetAddress: wrong services - got %v, want %v", na.Services, 0) } if na.HasService(wire.SFNodeNetwork) { t.Errorf("HasService: SFNodeNetwork service is set") } // Ensure adding the full service node flag works. na.AddService(wire.SFNodeNetwork) if na.Services != wire.SFNodeNetwork { t.Errorf("AddService: wrong services - got %v, want %v", na.Services, wire.SFNodeNetwork) } if !na.HasService(wire.SFNodeNetwork) { t.Errorf("HasService: SFNodeNetwork service not set") } // Ensure max payload is expected value for latest protocol version. pver := wire.ProtocolVersion wantPayload := uint32(30) maxPayload := wire.TstMaxNetAddressPayload(wire.ProtocolVersion) if maxPayload != wantPayload { t.Errorf("maxNetAddressPayload: wrong max payload length for "+ "protocol version %d - got %v, want %v", pver, maxPayload, wantPayload) } // Protocol version before NetAddressTimeVersion when timestamp was // added. Ensure max payload is expected value for it. pver = wire.NetAddressTimeVersion - 1 wantPayload = 26 maxPayload = wire.TstMaxNetAddressPayload(pver) if maxPayload != wantPayload { t.Errorf("maxNetAddressPayload: wrong max payload length for "+ "protocol version %d - got %v, want %v", pver, maxPayload, wantPayload) } // Check for expected failure on wrong address type. udpAddr := &net.UDPAddr{} _, err = wire.NewNetAddress(udpAddr, 0) if err != wire.ErrInvalidNetAddr { t.Errorf("NewNetAddress: expected error not received - "+ "got %v, want %v", err, wire.ErrInvalidNetAddr) } } // TestNetAddressWire tests the NetAddress wire encode and decode for various // protocol versions and timestamp flag combinations. func TestNetAddressWire(t *testing.T) { // baseNetAddr is used in the various tests as a baseline NetAddress. baseNetAddr := wire.NetAddress{ Timestamp: time.Unix(0x495fab29, 0), // 2009-01-03 12:15:05 -0600 CST Services: wire.SFNodeNetwork, IP: net.ParseIP("127.0.0.1"), Port: 8333, } // baseNetAddrNoTS is baseNetAddr with a zero value for the timestamp. baseNetAddrNoTS := baseNetAddr baseNetAddrNoTS.Timestamp = time.Time{} // baseNetAddrEncoded is the wire encoded bytes of baseNetAddr. baseNetAddrEncoded := []byte{ 0x29, 0xab, 0x5f, 0x49, // Timestamp 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // SFNodeNetwork 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x01, // IP 127.0.0.1 0x20, 0x8d, // Port 8333 in big-endian } // baseNetAddrNoTSEncoded is the wire encoded bytes of baseNetAddrNoTS. baseNetAddrNoTSEncoded := []byte{ // No timestamp 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // SFNodeNetwork 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x01, // IP 127.0.0.1 0x20, 0x8d, // Port 8333 in big-endian } tests := []struct { in wire.NetAddress // NetAddress to encode out wire.NetAddress // Expected decoded NetAddress ts bool // Include timestamp? buf []byte // Wire encoding pver uint32 // Protocol version for wire encoding }{ // Latest protocol version without ts flag. { baseNetAddr, baseNetAddrNoTS, false, baseNetAddrNoTSEncoded, wire.ProtocolVersion, }, // Latest protocol version with ts flag. { baseNetAddr, baseNetAddr, true, baseNetAddrEncoded, wire.ProtocolVersion, }, // Protocol version NetAddressTimeVersion without ts flag. { baseNetAddr, baseNetAddrNoTS, false, baseNetAddrNoTSEncoded, wire.NetAddressTimeVersion, }, // Protocol version NetAddressTimeVersion with ts flag. { baseNetAddr, baseNetAddr, true, baseNetAddrEncoded, wire.NetAddressTimeVersion, }, // Protocol version NetAddressTimeVersion-1 without ts flag. { baseNetAddr, baseNetAddrNoTS, false, baseNetAddrNoTSEncoded, wire.NetAddressTimeVersion - 1, }, // Protocol version NetAddressTimeVersion-1 with timestamp. // Even though the timestamp flag is set, this shouldn't have a // timestamp since it is a protocol version before it was // added. { baseNetAddr, baseNetAddrNoTS, true, baseNetAddrNoTSEncoded, wire.NetAddressTimeVersion - 1, }, } t.Logf("Running %d tests", len(tests)) for i, test := range tests { // Encode to wire format. var buf bytes.Buffer err := wire.TstWriteNetAddress(&buf, test.pver, &test.in, test.ts) if err != nil { t.Errorf("writeNetAddress #%d error %v", i, err) continue } if !bytes.Equal(buf.Bytes(), test.buf) { t.Errorf("writeNetAddress #%d\n got: %s want: %s", i, spew.Sdump(buf.Bytes()), spew.Sdump(test.buf)) continue } // Decode the message from wire format. var na wire.NetAddress rbuf := bytes.NewReader(test.buf) err = wire.TstReadNetAddress(rbuf, test.pver, &na, test.ts) if err != nil { t.Errorf("readNetAddress #%d error %v", i, err) continue } if !reflect.DeepEqual(na, test.out) { t.Errorf("readNetAddress #%d\n got: %s want: %s", i, spew.Sdump(na), spew.Sdump(test.out)) continue } } } // TestNetAddressWireErrors performs negative tests against wire encode and // decode NetAddress to confirm error paths work correctly. func TestNetAddressWireErrors(t *testing.T) { pver := wire.ProtocolVersion pverNAT := wire.NetAddressTimeVersion - 1 // baseNetAddr is used in the various tests as a baseline NetAddress. baseNetAddr := wire.NetAddress{ Timestamp: time.Unix(0x495fab29, 0), // 2009-01-03 12:15:05 -0600 CST Services: wire.SFNodeNetwork, IP: net.ParseIP("127.0.0.1"), Port: 8333, } tests := []struct { in *wire.NetAddress // Value to encode buf []byte // Wire encoding pver uint32 // Protocol version for wire encoding ts bool // Include timestamp flag max int // Max size of fixed buffer to induce errors writeErr error // Expected write error readErr error // Expected read error }{ // Latest protocol version with timestamp and intentional // read/write errors. // Force errors on timestamp. {&baseNetAddr, []byte{}, pver, true, 0, io.ErrShortWrite, io.EOF}, // Force errors on services. {&baseNetAddr, []byte{}, pver, true, 4, io.ErrShortWrite, io.EOF}, // Force errors on ip. {&baseNetAddr, []byte{}, pver, true, 12, io.ErrShortWrite, io.EOF}, // Force errors on port. {&baseNetAddr, []byte{}, pver, true, 28, io.ErrShortWrite, io.EOF}, // Latest protocol version with no timestamp and intentional // read/write errors. // Force errors on services. {&baseNetAddr, []byte{}, pver, false, 0, io.ErrShortWrite, io.EOF}, // Force errors on ip. {&baseNetAddr, []byte{}, pver, false, 8, io.ErrShortWrite, io.EOF}, // Force errors on port. {&baseNetAddr, []byte{}, pver, false, 24, io.ErrShortWrite, io.EOF}, // Protocol version before NetAddressTimeVersion with timestamp // flag set (should not have timestamp due to old protocol // version) and intentional read/write errors. // Force errors on services. {&baseNetAddr, []byte{}, pverNAT, true, 0, io.ErrShortWrite, io.EOF}, // Force errors on ip. {&baseNetAddr, []byte{}, pverNAT, true, 8, io.ErrShortWrite, io.EOF}, // Force errors on port. {&baseNetAddr, []byte{}, pverNAT, true, 24, io.ErrShortWrite, io.EOF}, } t.Logf("Running %d tests", len(tests)) for i, test := range tests { // Encode to wire format. w := newFixedWriter(test.max) err := wire.TstWriteNetAddress(w, test.pver, test.in, test.ts) if err != test.writeErr { t.Errorf("writeNetAddress #%d wrong error got: %v, want: %v", i, err, test.writeErr) continue } // Decode from wire format. var na wire.NetAddress r := newFixedReader(test.max, test.buf) err = wire.TstReadNetAddress(r, test.pver, &na, test.ts) if err != test.readErr { t.Errorf("readNetAddress #%d wrong error got: %v, want: %v", i, err, test.readErr) continue } } }