inv_test.go raw
1 package btcwire
2
3 import (
4 "bytes"
5 "errors"
6 "io"
7 "reflect"
8 "testing"
9
10 "git.smesh.lol/nostrino/pkg/crypto/chainhash"
11 )
12
13 func TestInvVectTypes(t *testing.T) {
14 if InvVectTypeError != 0 || InvVectTypeTx != 1 || InvVectTypeBlock != 2 ||
15 InvVectTypeFilteredBlock != 3 || InvVectTypeWitnessTx != 0x40000001 {
16 t.Fatalf("inventory type constants changed: %d %d %d %d %#x",
17 InvVectTypeError, InvVectTypeTx, InvVectTypeBlock,
18 InvVectTypeFilteredBlock, InvVectTypeWitnessTx)
19 }
20 }
21
22 func TestInvListRoundTrip(t *testing.T) {
23 types := []uint32{
24 InvVectTypeError,
25 InvVectTypeTx,
26 InvVectTypeBlock,
27 InvVectTypeFilteredBlock,
28 InvVectTypeWitnessTx,
29 }
30 var list []InvVect
31 for i, typ := range types {
32 list = append(list, InvVect{Type: typ, Hash: testHash(byte(0x60 + i))})
33 }
34 tests := []struct {
35 name string
36 value Payload
37 zero func() Payload
38 }{
39 {"inv", &Inv{InvList: list}, func() Payload { return &Inv{} }},
40 {"getdata", &GetData{InvList: list}, func() Payload { return &GetData{} }},
41 {"notfound", &NotFound{InvList: list}, func() Payload { return &NotFound{} }},
42 }
43 for _, tc := range tests {
44 t.Run(tc.name, func(t *testing.T) {
45 payload, err := tc.value.EncodePayload()
46 if err != nil {
47 t.Fatalf("EncodePayload: %v", err)
48 }
49 if got, wantLen := len(payload), 1+len(list)*invVectSize; got != wantLen {
50 t.Fatalf("payload length = %d, want %d", got, wantLen)
51 }
52 // First vector: type little endian, then the hash in wire order.
53 if got, want := payload[1:5], []byte{0x00, 0x00, 0x00, 0x00}; !bytes.Equal(got, want) {
54 t.Fatalf("first vector type bytes = %x, want %x", got, want)
55 }
56 if got := payload[5 : 5+chainhash.HashSize]; !bytes.Equal(got, list[0].Hash[:]) {
57 t.Fatalf("first vector hash = %x, want %x", got, list[0].Hash)
58 }
59 decoded := tc.zero()
60 if err := decoded.DecodePayload(payload); err != nil {
61 t.Fatalf("DecodePayload: %v", err)
62 }
63 if !reflect.DeepEqual(decoded, tc.value) {
64 t.Fatalf("decoded = %+v, want %+v", decoded, tc.value)
65 }
66 })
67 }
68 }
69
70 func TestInvListEmpty(t *testing.T) {
71 payload, err := (&Inv{InvList: []InvVect{}}).EncodePayload()
72 if err != nil {
73 t.Fatalf("EncodePayload: %v", err)
74 }
75 if !bytes.Equal(payload, []byte{0x00}) {
76 t.Fatalf("payload = %x, want 00", payload)
77 }
78 var got Inv
79 if err := got.DecodePayload(payload); err != nil {
80 t.Fatalf("DecodePayload: %v", err)
81 }
82 if len(got.InvList) != 0 {
83 t.Fatalf("decoded %d vectors, want 0", len(got.InvList))
84 }
85 }
86
87 func TestInvListLimits(t *testing.T) {
88 tooMany := make([]InvVect, MaxInvPerMsg+1)
89 if _, err := (&GetData{InvList: tooMany}).EncodePayload(); !errors.Is(err, ErrTooManyItems) {
90 t.Fatalf("EncodePayload error = %v, want ErrTooManyItems", err)
91 }
92 // 50001 encoded as 0xfd 0x51 0xc3.
93 payload := []byte{0xfd, byte((MaxInvPerMsg + 1) & 0xff), byte((MaxInvPerMsg + 1) >> 8)}
94 var got NotFound
95 if err := got.DecodePayload(payload); !errors.Is(err, ErrTooManyItems) {
96 t.Fatalf("DecodePayload error = %v, want ErrTooManyItems", err)
97 }
98 }
99
100 func TestInvListTruncated(t *testing.T) {
101 want := &Inv{InvList: []InvVect{
102 {Type: InvVectTypeTx, Hash: testHash(0x70)},
103 {Type: InvVectTypeBlock, Hash: testHash(0x71)},
104 }}
105 payload, err := want.EncodePayload()
106 if err != nil {
107 t.Fatalf("EncodePayload: %v", err)
108 }
109 for n := 0; n < len(payload); n++ {
110 var got Inv
111 if err := got.DecodePayload(payload[:n]); err == nil {
112 t.Fatalf("decoding %d of %d bytes succeeded", n, len(payload))
113 }
114 }
115 // A count that claims more data than the payload holds is truncated
116 // before any allocation is attempted.
117 var got Inv
118 if err := got.DecodePayload([]byte{0x02, 0x01}); !errors.Is(err, io.ErrUnexpectedEOF) {
119 t.Fatalf("error = %v, want io.ErrUnexpectedEOF", err)
120 }
121 }
122