version_test.go raw
1 package btcwire
2
3 import (
4 "bytes"
5 "errors"
6 "math"
7 "reflect"
8 "testing"
9 )
10
11 func TestVersionRoundTrip(t *testing.T) {
12 tests := []struct {
13 name string
14 recv string
15 from string
16 ua string
17 relay bool
18 }{
19 {"ipv4", "127.0.0.1", "10.0.0.1", "Satoshi:27.0.0", true},
20 {"ipv6", "2001:db8::1", "::1", "nostrino:0.1.0", false},
21 {"mixed", "::ffff:192.168.1.1", "192.168.1.2", "Satoshi:25.0.0/", true},
22 {"empty user agent", "0.0.0.0", "0.0.0.0", "", true},
23 {"long user agent", "127.0.0.1", "127.0.0.1", string(bytes.Repeat([]byte("x"), MaxUserAgentSize)), false},
24 }
25 for _, tc := range tests {
26 t.Run(tc.name, func(t *testing.T) {
27 want := Version{
28 ProtocolVersion: ProtocolVersion,
29 Services: ServiceNodeNetwork | ServiceNodeWitness | ServiceNodeCompactFilters,
30 Timestamp: 1700000000,
31 AddrRecv: mustAddress(t, tc.recv, 8333),
32 AddrFrom: mustAddress(t, tc.from, 18333),
33 Nonce: 0x0102030405060708,
34 UserAgent: tc.ua,
35 LastBlock: 800000,
36 Relay: tc.relay,
37 }
38 payload, err := want.EncodePayload()
39 if err != nil {
40 t.Fatalf("EncodePayload: %v", err)
41 }
42 var got Version
43 if err := got.DecodePayload(payload); err != nil {
44 t.Fatalf("DecodePayload: %v", err)
45 }
46 if !reflect.DeepEqual(got, want) {
47 t.Fatalf("decoded = %+v, want %+v", got, want)
48 }
49 })
50 }
51 }
52
53 func TestVersionWireLayout(t *testing.T) {
54 v := Version{
55 ProtocolVersion: ProtocolVersion,
56 Services: ServiceNodeNetwork,
57 Timestamp: 1700000000,
58 AddrRecv: mustAddress(t, "127.0.0.1", 8333),
59 AddrFrom: mustAddress(t, "1.2.3.4", 18333),
60 Nonce: 0x0102030405060708,
61 UserAgent: "abc",
62 LastBlock: 100,
63 Relay: true,
64 }
65 payload, err := v.EncodePayload()
66 if err != nil {
67 t.Fatalf("EncodePayload: %v", err)
68 }
69 // 4 + 8 + 8 + 26 + 26 + 8 + 1 + 3 + 4 + 1.
70 if got, want := len(payload), 89; got != want {
71 t.Fatalf("payload length = %d, want %d", got, want)
72 }
73 checks := []struct {
74 name string
75 got []byte
76 want []byte
77 }{
78 {"protocol version", payload[0:4], []byte{0x80, 0x11, 0x01, 0x00}},
79 {"services", payload[4:12], []byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
80 {"timestamp", payload[12:20], []byte{0x00, 0xf1, 0x53, 0x65, 0x00, 0x00, 0x00, 0x00}},
81 {"addr recv port", payload[44:46], []byte{0x20, 0x8d}},
82 {"addr from port", payload[70:72], []byte{0x47, 0x9d}},
83 {"nonce", payload[72:80], []byte{0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01}},
84 {"user agent length", payload[80:81], []byte{0x03}},
85 {"user agent", payload[81:84], []byte("abc")},
86 {"last block", payload[84:88], []byte{0x64, 0x00, 0x00, 0x00}},
87 {"relay", payload[88:89], []byte{0x01}},
88 }
89 for _, c := range checks {
90 if !bytes.Equal(c.got, c.want) {
91 t.Fatalf("%s bytes = %x, want %x", c.name, c.got, c.want)
92 }
93 }
94 // 8333 = 0x208d and 18333 = 0x479d, big endian on the wire.
95 if got := payload[28:44]; !bytes.Equal(got, []byte{
96 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
97 0x00, 0x00, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x01,
98 }) {
99 t.Fatalf("addr recv IP = %x", got)
100 }
101 }
102
103 func TestVersionDecodeTruncated(t *testing.T) {
104 v := Version{
105 ProtocolVersion: ProtocolVersion,
106 Services: ServiceNodeNetwork,
107 Timestamp: 1700000000,
108 AddrRecv: mustAddress(t, "127.0.0.1", 8333),
109 AddrFrom: mustAddress(t, "127.0.0.1", 8333),
110 Nonce: 7,
111 UserAgent: "abc",
112 LastBlock: 42,
113 Relay: true,
114 }
115 payload, err := v.EncodePayload()
116 if err != nil {
117 t.Fatalf("EncodePayload: %v", err)
118 }
119 // Everything before the optional relay byte is required.
120 required := len(payload) - 1
121 for n := 0; n < required; n++ {
122 var got Version
123 if err := got.DecodePayload(payload[:n]); err == nil {
124 t.Fatalf("decoding %d of %d bytes succeeded", n, len(payload))
125 }
126 }
127 // The relay byte is optional: without it the message is still valid and
128 // relay defaults to true.
129 var got Version
130 if err := got.DecodePayload(payload[:required]); err != nil {
131 t.Fatalf("decoding without relay byte: %v", err)
132 }
133 if !got.Relay {
134 t.Fatal("missing relay byte did not default to true")
135 }
136 if got.UserAgent != "abc" || got.LastBlock != 42 || got.Nonce != 7 {
137 t.Fatalf("decoded fields = %+v", got)
138 }
139 }
140
141 func TestVersionRelayDecoding(t *testing.T) {
142 v := Version{
143 ProtocolVersion: ProtocolVersion,
144 UserAgent: "ua",
145 LastBlock: 1,
146 }
147 payload, err := v.EncodePayload()
148 if err != nil {
149 t.Fatalf("EncodePayload: %v", err)
150 }
151 payload[len(payload)-1] = 0
152 var got Version
153 if err := got.DecodePayload(payload); err != nil {
154 t.Fatalf("DecodePayload: %v", err)
155 }
156 if got.Relay {
157 t.Fatal("relay byte 0 decoded as true")
158 }
159 // Any non-zero byte is treated as true, matching Bitcoin Core.
160 payload[len(payload)-1] = 2
161 if err := got.DecodePayload(payload); err != nil {
162 t.Fatalf("DecodePayload: %v", err)
163 }
164 if !got.Relay {
165 t.Fatal("relay byte 2 decoded as false")
166 }
167 }
168
169 func TestVersionUserAgentLimit(t *testing.T) {
170 t.Run("encode rejects oversize", func(t *testing.T) {
171 v := &Version{UserAgent: string(bytes.Repeat([]byte("x"), MaxUserAgentSize+1))}
172 if _, err := v.EncodePayload(); !errors.Is(err, ErrInvalidPayload) {
173 t.Fatalf("error = %v, want ErrInvalidPayload", err)
174 }
175 })
176 t.Run("decode rejects oversize", func(t *testing.T) {
177 // Fixed fields, then a 257-byte user agent, then the last block and
178 // relay byte.
179 payload := make([]byte, 80)
180 payload = append(payload, 0xfd, 0x01, 0x01)
181 payload = append(payload, bytes.Repeat([]byte("x"), 257)...)
182 payload = append(payload, 0, 0, 0, 0, 0)
183 var got Version
184 if err := got.DecodePayload(payload); !errors.Is(err, ErrVarBytesTooLarge) {
185 t.Fatalf("error = %v, want ErrVarBytesTooLarge", err)
186 }
187 })
188 }
189
190 func TestEmptyPayloadMessages(t *testing.T) {
191 tests := []struct {
192 name string
193 value Payload
194 }{
195 {"verack", &VerAck{}},
196 {"sendheaders", &SendHeaders{}},
197 {"wtxidrelay", &WtxidRelay{}},
198 {"sendaddrv2", &SendAddrV2{}},
199 }
200 for _, tc := range tests {
201 t.Run(tc.name, func(t *testing.T) {
202 payload, err := tc.value.EncodePayload()
203 if err != nil {
204 t.Fatalf("EncodePayload: %v", err)
205 }
206 if len(payload) != 0 {
207 t.Fatalf("payload = %x, want empty", payload)
208 }
209 if err := tc.value.DecodePayload(nil); err != nil {
210 t.Fatalf("DecodePayload(nil): %v", err)
211 }
212 if err := tc.value.DecodePayload([]byte{0x00}); !errors.Is(err, ErrInvalidPayload) {
213 t.Fatalf("DecodePayload([0]) error = %v, want ErrInvalidPayload", err)
214 }
215 })
216 }
217 }
218
219 func TestPingPong(t *testing.T) {
220 nonces := []uint64{0, 1, 0xffffffffffffffff}
221 for _, nonce := range nonces {
222 t.Run("ping", func(t *testing.T) {
223 want := &Ping{Nonce: nonce}
224 payload, err := want.EncodePayload()
225 if err != nil {
226 t.Fatalf("EncodePayload: %v", err)
227 }
228 if len(payload) != 8 {
229 t.Fatalf("payload length = %d, want 8", len(payload))
230 }
231 var got Ping
232 if err := got.DecodePayload(payload); err != nil {
233 t.Fatalf("DecodePayload: %v", err)
234 }
235 if got.Nonce != nonce {
236 t.Fatalf("Nonce = %d, want %d", got.Nonce, nonce)
237 }
238 })
239 t.Run("pong", func(t *testing.T) {
240 want := &Pong{Nonce: nonce}
241 payload, err := want.EncodePayload()
242 if err != nil {
243 t.Fatalf("EncodePayload: %v", err)
244 }
245 var got Pong
246 if err := got.DecodePayload(payload); err != nil {
247 t.Fatalf("DecodePayload: %v", err)
248 }
249 if got.Nonce != nonce {
250 t.Fatalf("Nonce = %d, want %d", got.Nonce, nonce)
251 }
252 })
253 }
254 for _, n := range []int{0, 7, 9} {
255 var p Ping
256 if err := p.DecodePayload(make([]byte, n)); !errors.Is(err, ErrInvalidPayload) {
257 t.Fatalf("ping payload of %d bytes error = %v, want ErrInvalidPayload", n, err)
258 }
259 var q Pong
260 if err := q.DecodePayload(make([]byte, n)); !errors.Is(err, ErrInvalidPayload) {
261 t.Fatalf("pong payload of %d bytes error = %v, want ErrInvalidPayload", n, err)
262 }
263 }
264 }
265
266 func TestFeeFilter(t *testing.T) {
267 rates := []int64{0, 1, 1000, -1, math.MaxInt64, math.MinInt64}
268 for _, rate := range rates {
269 want := &FeeFilter{FeeRate: rate}
270 payload, err := want.EncodePayload()
271 if err != nil {
272 t.Fatalf("EncodePayload: %v", err)
273 }
274 if len(payload) != 8 {
275 t.Fatalf("payload length = %d, want 8", len(payload))
276 }
277 var got FeeFilter
278 if err := got.DecodePayload(payload); err != nil {
279 t.Fatalf("DecodePayload: %v", err)
280 }
281 if got.FeeRate != rate {
282 t.Fatalf("FeeRate = %d, want %d", got.FeeRate, rate)
283 }
284 }
285 var f FeeFilter
286 if err := f.DecodePayload(make([]byte, 4)); !errors.Is(err, ErrInvalidPayload) {
287 t.Fatalf("short payload error = %v, want ErrInvalidPayload", err)
288 }
289 if err := f.DecodePayload(make([]byte, 9)); !errors.Is(err, ErrInvalidPayload) {
290 t.Fatalf("long payload error = %v, want ErrInvalidPayload", err)
291 }
292 }
293
294 func TestProtocolConstants(t *testing.T) {
295 if ProtocolVersion != 70016 {
296 t.Fatalf("ProtocolVersion = %d, want 70016", ProtocolVersion)
297 }
298 if ServiceNodeNetwork != 1 {
299 t.Fatalf("ServiceNodeNetwork = %d, want 1", ServiceNodeNetwork)
300 }
301 if ServiceNodeWitness != 1<<3 {
302 t.Fatalf("ServiceNodeWitness = %d, want %d", ServiceNodeWitness, 1<<3)
303 }
304 if ServiceNodeCompactFilters != 1<<6 {
305 t.Fatalf("ServiceNodeCompactFilters = %d, want %d", ServiceNodeCompactFilters, 1<<6)
306 }
307 }
308
309 func TestVersionFrameRoundTrip(t *testing.T) {
310 v := &Version{
311 ProtocolVersion: ProtocolVersion,
312 Services: ServiceNodeCompactFilters,
313 Timestamp: 1,
314 Nonce: 2,
315 UserAgent: "Satoshi:27.0.0",
316 LastBlock: 3,
317 Relay: true,
318 }
319 frame, err := EncodeMessagePayload(MainNet, CmdVersion, v)
320 if err != nil {
321 t.Fatalf("EncodeMessagePayload: %v", err)
322 }
323 msg, err := DecodeMessage(MainNet, bytes.NewReader(frame))
324 if err != nil {
325 t.Fatalf("DecodeMessage: %v", err)
326 }
327 var got Version
328 if err := msg.Decode(&got); err != nil {
329 t.Fatalf("Decode: %v", err)
330 }
331 if !reflect.DeepEqual(got, *v) {
332 t.Fatalf("decoded = %+v, want %+v", got, *v)
333 }
334 }
335