headers_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 TestGetHeadersRoundTrip(t *testing.T) {
14 want := &GetHeaders{
15 ProtocolVersion: ProtocolVersion,
16 LocatorHashes: []chainhash.Hash{testHash(1), testHash(2), testHash(3)},
17 StopHash: testHash(4),
18 }
19 payload, err := want.EncodePayload()
20 if err != nil {
21 t.Fatalf("EncodePayload: %v", err)
22 }
23 // version (4) + count (1) + 3 hashes + stop hash.
24 if got, wantLen := len(payload), 4+1+4*chainhash.HashSize; got != wantLen {
25 t.Fatalf("payload length = %d, want %d", got, wantLen)
26 }
27 var got GetHeaders
28 if err := got.DecodePayload(payload); err != nil {
29 t.Fatalf("DecodePayload: %v", err)
30 }
31 if !reflect.DeepEqual(&got, want) {
32 t.Fatalf("decoded = %+v, want %+v", got, *want)
33 }
34 }
35
36 func TestGetHeadersEmptyLocator(t *testing.T) {
37 want := &GetHeaders{ProtocolVersion: ProtocolVersion}
38 payload, err := want.EncodePayload()
39 if err != nil {
40 t.Fatalf("EncodePayload: %v", err)
41 }
42 var got GetHeaders
43 if err := got.DecodePayload(payload); err != nil {
44 t.Fatalf("DecodePayload: %v", err)
45 }
46 if got.ProtocolVersion != want.ProtocolVersion {
47 t.Fatalf("ProtocolVersion = %d, want %d", got.ProtocolVersion, want.ProtocolVersion)
48 }
49 if len(got.LocatorHashes) != 0 {
50 t.Fatalf("LocatorHashes = %v, want empty", got.LocatorHashes)
51 }
52 }
53
54 func TestGetHeadersLimits(t *testing.T) {
55 tooMany := make([]chainhash.Hash, MaxLocatorHashesPerMsg+1)
56 if _, err := (&GetHeaders{LocatorHashes: tooMany}).EncodePayload(); !errors.Is(err, ErrTooManyItems) {
57 t.Fatalf("EncodePayload error = %v, want ErrTooManyItems", err)
58 }
59 // A decoded count above the limit is rejected before any hashes are read.
60 payload := []byte{0x00, 0x00, 0x00, 0x00, MaxLocatorHashesPerMsg + 1}
61 var got GetHeaders
62 if err := got.DecodePayload(payload); !errors.Is(err, ErrTooManyItems) {
63 t.Fatalf("DecodePayload error = %v, want ErrTooManyItems", err)
64 }
65 }
66
67 func TestGetHeadersTruncated(t *testing.T) {
68 want := &GetHeaders{
69 ProtocolVersion: ProtocolVersion,
70 LocatorHashes: []chainhash.Hash{testHash(1), testHash(2)},
71 StopHash: testHash(3),
72 }
73 payload, err := want.EncodePayload()
74 if err != nil {
75 t.Fatalf("EncodePayload: %v", err)
76 }
77 for n := 0; n < len(payload); n++ {
78 var got GetHeaders
79 if err := got.DecodePayload(payload[:n]); err == nil {
80 t.Fatalf("decoding %d of %d bytes succeeded", n, len(payload))
81 }
82 }
83 }
84
85 func TestHeadersRoundTrip(t *testing.T) {
86 want := &Headers{Headers: []BlockHeader{
87 mustGenesisHeader(t),
88 {
89 Version: 2,
90 PrevBlock: testHash(0x30),
91 MerkleRoot: testHash(0x40),
92 Timestamp: 1700000000,
93 Bits: 0x1d00ffff,
94 Nonce: 12345,
95 },
96 }}
97 payload, err := want.EncodePayload()
98 if err != nil {
99 t.Fatalf("EncodePayload: %v", err)
100 }
101 // count (1) + one 80-byte header and a zero tx count per entry.
102 if got, wantLen := len(payload), 1+len(want.Headers)*(BlockHeaderSize+1); got != wantLen {
103 t.Fatalf("payload length = %d, want %d", got, wantLen)
104 }
105 if payload[1+BlockHeaderSize] != 0x00 {
106 t.Fatalf("transaction count byte = %x, want 0", payload[1+BlockHeaderSize])
107 }
108 var got Headers
109 if err := got.DecodePayload(payload); err != nil {
110 t.Fatalf("DecodePayload: %v", err)
111 }
112 if len(got.Headers) != len(want.Headers) {
113 t.Fatalf("decoded %d headers, want %d", len(got.Headers), len(want.Headers))
114 }
115 for i := range want.Headers {
116 if got.Headers[i] != want.Headers[i] {
117 t.Fatalf("header %d = %+v, want %+v", i, got.Headers[i], want.Headers[i])
118 }
119 }
120 }
121
122 func TestHeadersEmpty(t *testing.T) {
123 want := &Headers{}
124 payload, err := want.EncodePayload()
125 if err != nil {
126 t.Fatalf("EncodePayload: %v", err)
127 }
128 if !bytes.Equal(payload, []byte{0x00}) {
129 t.Fatalf("payload = %x, want 00", payload)
130 }
131 var got Headers
132 if err := got.DecodePayload(payload); err != nil {
133 t.Fatalf("DecodePayload: %v", err)
134 }
135 if len(got.Headers) != 0 {
136 t.Fatalf("decoded %d headers, want 0", len(got.Headers))
137 }
138 }
139
140 func TestHeadersNonZeroTxCount(t *testing.T) {
141 genesis := mustGenesisHeader(t)
142 raw := genesis.Serialize()
143 payload := append([]byte{0x01}, raw[:]...)
144 payload = append(payload, 0x01)
145 var got Headers
146 err := got.DecodePayload(payload)
147 if !errors.Is(err, ErrInvalidPayload) {
148 t.Fatalf("DecodePayload error = %v, want ErrInvalidPayload", err)
149 }
150 }
151
152 func TestHeadersLimits(t *testing.T) {
153 t.Run("encode too many", func(t *testing.T) {
154 h := &Headers{Headers: make([]BlockHeader, MaxBlockHeadersPerMsg+1)}
155 if _, err := h.EncodePayload(); !errors.Is(err, ErrTooManyItems) {
156 t.Fatalf("error = %v, want ErrTooManyItems", err)
157 }
158 })
159 t.Run("decode too many", func(t *testing.T) {
160 payload := []byte{0xfd}
161 payload = append(payload, byte((MaxBlockHeadersPerMsg+1)&0xff), byte((MaxBlockHeadersPerMsg+1)>>8))
162 var got Headers
163 if err := got.DecodePayload(payload); !errors.Is(err, ErrTooManyItems) {
164 t.Fatalf("error = %v, want ErrTooManyItems", err)
165 }
166 })
167 t.Run("decode exactly the maximum", func(t *testing.T) {
168 var e Encoder
169 e.CompactSize(MaxBlockHeadersPerMsg)
170 genesis := mustGenesisHeader(t)
171 raw := genesis.Serialize()
172 for i := 0; i < MaxBlockHeadersPerMsg; i++ {
173 e.Write(raw[:])
174 e.CompactSize(0)
175 }
176 var got Headers
177 if err := got.DecodePayload(e.Bytes()); err != nil {
178 t.Fatalf("DecodePayload: %v", err)
179 }
180 if len(got.Headers) != MaxBlockHeadersPerMsg {
181 t.Fatalf("decoded %d headers, want %d", len(got.Headers), MaxBlockHeadersPerMsg)
182 }
183 })
184 }
185
186 func TestHeadersTruncated(t *testing.T) {
187 want := &Headers{Headers: []BlockHeader{mustGenesisHeader(t)}}
188 payload, err := want.EncodePayload()
189 if err != nil {
190 t.Fatalf("EncodePayload: %v", err)
191 }
192 for n := 0; n < len(payload); n++ {
193 var got Headers
194 if err := got.DecodePayload(payload[:n]); err == nil {
195 t.Fatalf("decoding %d of %d bytes succeeded", n, len(payload))
196 }
197 }
198 // A count of one with a header but no transaction count is truncated.
199 var got Headers
200 if err := got.DecodePayload(payload[:len(payload)-1]); !errors.Is(err, io.ErrUnexpectedEOF) {
201 t.Fatalf("missing tx count error = %v, want io.ErrUnexpectedEOF", err)
202 }
203 }
204