1// Copyright (c) 2013-2016 The btcsuite developers
2// Use of this source code is governed by an ISC
3// license that can be found in the LICENSE file.
4
5package wire
6
7import (
8	"bytes"
9	"io"
10	"reflect"
11	"testing"
12
13	"github.com/btcsuite/btcd/chaincfg/chainhash"
14	"github.com/davecgh/go-spew/spew"
15)
16
17// TestGetBlocks tests the MsgGetBlocks API.
18func TestGetBlocks(t *testing.T) {
19	pver := ProtocolVersion
20
21	// Block 99500 hash.
22	hashStr := "000000000002e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0"
23	locatorHash, err := chainhash.NewHashFromStr(hashStr)
24	if err != nil {
25		t.Errorf("NewHashFromStr: %v", err)
26	}
27
28	// Block 100000 hash.
29	hashStr = "3ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506"
30	hashStop, err := chainhash.NewHashFromStr(hashStr)
31	if err != nil {
32		t.Errorf("NewHashFromStr: %v", err)
33	}
34
35	// Ensure we get the same data back out.
36	msg := NewMsgGetBlocks(hashStop)
37	if !msg.HashStop.IsEqual(hashStop) {
38		t.Errorf("NewMsgGetBlocks: wrong stop hash - got %v, want %v",
39			msg.HashStop, hashStop)
40	}
41
42	// Ensure the command is expected value.
43	wantCmd := "getblocks"
44	if cmd := msg.Command(); cmd != wantCmd {
45		t.Errorf("NewMsgGetBlocks: wrong command - got %v want %v",
46			cmd, wantCmd)
47	}
48
49	// Ensure max payload is expected value for latest protocol version.
50	// Protocol version 4 bytes + num hashes (varInt) + max block locator
51	// hashes + hash stop.
52	wantPayload := uint32(16045)
53	maxPayload := msg.MaxPayloadLength(pver)
54	if maxPayload != wantPayload {
55		t.Errorf("MaxPayloadLength: wrong max payload length for "+
56			"protocol version %d - got %v, want %v", pver,
57			maxPayload, wantPayload)
58	}
59
60	// Ensure block locator hashes are added properly.
61	err = msg.AddBlockLocatorHash(locatorHash)
62	if err != nil {
63		t.Errorf("AddBlockLocatorHash: %v", err)
64	}
65	if msg.BlockLocatorHashes[0] != locatorHash {
66		t.Errorf("AddBlockLocatorHash: wrong block locator added - "+
67			"got %v, want %v",
68			spew.Sprint(msg.BlockLocatorHashes[0]),
69			spew.Sprint(locatorHash))
70	}
71
72	// Ensure adding more than the max allowed block locator hashes per
73	// message returns an error.
74	for i := 0; i < MaxBlockLocatorsPerMsg; i++ {
75		err = msg.AddBlockLocatorHash(locatorHash)
76	}
77	if err == nil {
78		t.Errorf("AddBlockLocatorHash: expected error on too many " +
79			"block locator hashes not received")
80	}
81}
82
83// TestGetBlocksWire tests the MsgGetBlocks wire encode and decode for various
84// numbers of block locator hashes and protocol versions.
85func TestGetBlocksWire(t *testing.T) {
86	// Set protocol inside getblocks message.
87	pver := uint32(60002)
88
89	// Block 99499 hash.
90	hashStr := "2710f40c87ec93d010a6fd95f42c59a2cbacc60b18cf6b7957535"
91	hashLocator, err := chainhash.NewHashFromStr(hashStr)
92	if err != nil {
93		t.Errorf("NewHashFromStr: %v", err)
94	}
95
96	// Block 99500 hash.
97	hashStr = "2e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0"
98	hashLocator2, err := chainhash.NewHashFromStr(hashStr)
99	if err != nil {
100		t.Errorf("NewHashFromStr: %v", err)
101	}
102
103	// Block 100000 hash.
104	hashStr = "3ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506"
105	hashStop, err := chainhash.NewHashFromStr(hashStr)
106	if err != nil {
107		t.Errorf("NewHashFromStr: %v", err)
108	}
109
110	// MsgGetBlocks message with no block locators or stop hash.
111	noLocators := NewMsgGetBlocks(&chainhash.Hash{})
112	noLocators.ProtocolVersion = pver
113	noLocatorsEncoded := []byte{
114		0x62, 0xea, 0x00, 0x00, // Protocol version 60002
115		0x00, // Varint for number of block locator hashes
116		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
117		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
118		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
119		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Hash stop
120	}
121
122	// MsgGetBlocks message with multiple block locators and a stop hash.
123	multiLocators := NewMsgGetBlocks(hashStop)
124	multiLocators.AddBlockLocatorHash(hashLocator2)
125	multiLocators.AddBlockLocatorHash(hashLocator)
126	multiLocators.ProtocolVersion = pver
127	multiLocatorsEncoded := []byte{
128		0x62, 0xea, 0x00, 0x00, // Protocol version 60002
129		0x02, // Varint for number of block locator hashes
130		0xe0, 0xde, 0x06, 0x44, 0x68, 0x13, 0x2c, 0x63,
131		0xd2, 0x20, 0xcc, 0x69, 0x12, 0x83, 0xcb, 0x65,
132		0xbc, 0xaa, 0xe4, 0x79, 0x94, 0xef, 0x9e, 0x7b,
133		0xad, 0xe7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 99500 hash
134		0x35, 0x75, 0x95, 0xb7, 0xf6, 0x8c, 0xb1, 0x60,
135		0xcc, 0xba, 0x2c, 0x9a, 0xc5, 0x42, 0x5f, 0xd9,
136		0x6f, 0x0a, 0x01, 0x3d, 0xc9, 0x7e, 0xc8, 0x40,
137		0x0f, 0x71, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 99499 hash
138		0x06, 0xe5, 0x33, 0xfd, 0x1a, 0xda, 0x86, 0x39,
139		0x1f, 0x3f, 0x6c, 0x34, 0x32, 0x04, 0xb0, 0xd2,
140		0x78, 0xd4, 0xaa, 0xec, 0x1c, 0x0b, 0x20, 0xaa,
141		0x27, 0xba, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // Hash stop
142	}
143
144	tests := []struct {
145		in   *MsgGetBlocks   // Message to encode
146		out  *MsgGetBlocks   // Expected decoded message
147		buf  []byte          // Wire encoding
148		pver uint32          // Protocol version for wire encoding
149		enc  MessageEncoding // Message encoding format
150	}{
151		// Latest protocol version with no block locators.
152		{
153			noLocators,
154			noLocators,
155			noLocatorsEncoded,
156			ProtocolVersion,
157			BaseEncoding,
158		},
159
160		// Latest protocol version with multiple block locators.
161		{
162			multiLocators,
163			multiLocators,
164			multiLocatorsEncoded,
165			ProtocolVersion,
166			BaseEncoding,
167		},
168
169		// Protocol version BIP0035Version with no block locators.
170		{
171			noLocators,
172			noLocators,
173			noLocatorsEncoded,
174			BIP0035Version,
175			BaseEncoding,
176		},
177
178		// Protocol version BIP0035Version with multiple block locators.
179		{
180			multiLocators,
181			multiLocators,
182			multiLocatorsEncoded,
183			BIP0035Version,
184			BaseEncoding,
185		},
186
187		// Protocol version BIP0031Version with no block locators.
188		{
189			noLocators,
190			noLocators,
191			noLocatorsEncoded,
192			BIP0031Version,
193			BaseEncoding,
194		},
195
196		// Protocol version BIP0031Versionwith multiple block locators.
197		{
198			multiLocators,
199			multiLocators,
200			multiLocatorsEncoded,
201			BIP0031Version,
202			BaseEncoding,
203		},
204
205		// Protocol version NetAddressTimeVersion with no block locators.
206		{
207			noLocators,
208			noLocators,
209			noLocatorsEncoded,
210			NetAddressTimeVersion,
211			BaseEncoding,
212		},
213
214		// Protocol version NetAddressTimeVersion multiple block locators.
215		{
216			multiLocators,
217			multiLocators,
218			multiLocatorsEncoded,
219			NetAddressTimeVersion,
220			BaseEncoding,
221		},
222
223		// Protocol version MultipleAddressVersion with no block locators.
224		{
225			noLocators,
226			noLocators,
227			noLocatorsEncoded,
228			MultipleAddressVersion,
229			BaseEncoding,
230		},
231
232		// Protocol version MultipleAddressVersion multiple block locators.
233		{
234			multiLocators,
235			multiLocators,
236			multiLocatorsEncoded,
237			MultipleAddressVersion,
238			BaseEncoding,
239		},
240	}
241
242	t.Logf("Running %d tests", len(tests))
243	for i, test := range tests {
244		// Encode the message to wire format.
245		var buf bytes.Buffer
246		err := test.in.BtcEncode(&buf, test.pver, test.enc)
247		if err != nil {
248			t.Errorf("BtcEncode #%d error %v", i, err)
249			continue
250		}
251		if !bytes.Equal(buf.Bytes(), test.buf) {
252			t.Errorf("BtcEncode #%d\n got: %s want: %s", i,
253				spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
254			continue
255		}
256
257		// Decode the message from wire format.
258		var msg MsgGetBlocks
259		rbuf := bytes.NewReader(test.buf)
260		err = msg.BtcDecode(rbuf, test.pver, test.enc)
261		if err != nil {
262			t.Errorf("BtcDecode #%d error %v", i, err)
263			continue
264		}
265		if !reflect.DeepEqual(&msg, test.out) {
266			t.Errorf("BtcDecode #%d\n got: %s want: %s", i,
267				spew.Sdump(&msg), spew.Sdump(test.out))
268			continue
269		}
270	}
271}
272
273// TestGetBlocksWireErrors performs negative tests against wire encode and
274// decode of MsgGetBlocks to confirm error paths work correctly.
275func TestGetBlocksWireErrors(t *testing.T) {
276	// Set protocol inside getheaders message.  Use protocol version 60002
277	// specifically here instead of the latest because the test data is
278	// using bytes encoded with that protocol version.
279	pver := uint32(60002)
280	wireErr := &MessageError{}
281
282	// Block 99499 hash.
283	hashStr := "2710f40c87ec93d010a6fd95f42c59a2cbacc60b18cf6b7957535"
284	hashLocator, err := chainhash.NewHashFromStr(hashStr)
285	if err != nil {
286		t.Errorf("NewHashFromStr: %v", err)
287	}
288
289	// Block 99500 hash.
290	hashStr = "2e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0"
291	hashLocator2, err := chainhash.NewHashFromStr(hashStr)
292	if err != nil {
293		t.Errorf("NewHashFromStr: %v", err)
294	}
295
296	// Block 100000 hash.
297	hashStr = "3ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506"
298	hashStop, err := chainhash.NewHashFromStr(hashStr)
299	if err != nil {
300		t.Errorf("NewHashFromStr: %v", err)
301	}
302
303	// MsgGetBlocks message with multiple block locators and a stop hash.
304	baseGetBlocks := NewMsgGetBlocks(hashStop)
305	baseGetBlocks.ProtocolVersion = pver
306	baseGetBlocks.AddBlockLocatorHash(hashLocator2)
307	baseGetBlocks.AddBlockLocatorHash(hashLocator)
308	baseGetBlocksEncoded := []byte{
309		0x62, 0xea, 0x00, 0x00, // Protocol version 60002
310		0x02, // Varint for number of block locator hashes
311		0xe0, 0xde, 0x06, 0x44, 0x68, 0x13, 0x2c, 0x63,
312		0xd2, 0x20, 0xcc, 0x69, 0x12, 0x83, 0xcb, 0x65,
313		0xbc, 0xaa, 0xe4, 0x79, 0x94, 0xef, 0x9e, 0x7b,
314		0xad, 0xe7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 99500 hash
315		0x35, 0x75, 0x95, 0xb7, 0xf6, 0x8c, 0xb1, 0x60,
316		0xcc, 0xba, 0x2c, 0x9a, 0xc5, 0x42, 0x5f, 0xd9,
317		0x6f, 0x0a, 0x01, 0x3d, 0xc9, 0x7e, 0xc8, 0x40,
318		0x0f, 0x71, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 99499 hash
319		0x06, 0xe5, 0x33, 0xfd, 0x1a, 0xda, 0x86, 0x39,
320		0x1f, 0x3f, 0x6c, 0x34, 0x32, 0x04, 0xb0, 0xd2,
321		0x78, 0xd4, 0xaa, 0xec, 0x1c, 0x0b, 0x20, 0xaa,
322		0x27, 0xba, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // Hash stop
323	}
324
325	// Message that forces an error by having more than the max allowed
326	// block locator hashes.
327	maxGetBlocks := NewMsgGetBlocks(hashStop)
328	for i := 0; i < MaxBlockLocatorsPerMsg; i++ {
329		maxGetBlocks.AddBlockLocatorHash(&mainNetGenesisHash)
330	}
331	maxGetBlocks.BlockLocatorHashes = append(maxGetBlocks.BlockLocatorHashes,
332		&mainNetGenesisHash)
333	maxGetBlocksEncoded := []byte{
334		0x62, 0xea, 0x00, 0x00, // Protocol version 60002
335		0xfd, 0xf5, 0x01, // Varint for number of block loc hashes (501)
336	}
337
338	tests := []struct {
339		in       *MsgGetBlocks   // Value to encode
340		buf      []byte          // Wire encoding
341		pver     uint32          // Protocol version for wire encoding
342		enc      MessageEncoding // Message encoding format
343		max      int             // Max size of fixed buffer to induce errors
344		writeErr error           // Expected write error
345		readErr  error           // Expected read error
346	}{
347		// Force error in protocol version.
348		{baseGetBlocks, baseGetBlocksEncoded, pver, BaseEncoding, 0, io.ErrShortWrite, io.EOF},
349		// Force error in block locator hash count.
350		{baseGetBlocks, baseGetBlocksEncoded, pver, BaseEncoding, 4, io.ErrShortWrite, io.EOF},
351		// Force error in block locator hashes.
352		{baseGetBlocks, baseGetBlocksEncoded, pver, BaseEncoding, 5, io.ErrShortWrite, io.EOF},
353		// Force error in stop hash.
354		{baseGetBlocks, baseGetBlocksEncoded, pver, BaseEncoding, 69, io.ErrShortWrite, io.EOF},
355		// Force error with greater than max block locator hashes.
356		{maxGetBlocks, maxGetBlocksEncoded, pver, BaseEncoding, 7, wireErr, wireErr},
357	}
358
359	t.Logf("Running %d tests", len(tests))
360	for i, test := range tests {
361		// Encode to wire format.
362		w := newFixedWriter(test.max)
363		err := test.in.BtcEncode(w, test.pver, test.enc)
364		if reflect.TypeOf(err) != reflect.TypeOf(test.writeErr) {
365			t.Errorf("BtcEncode #%d wrong error got: %v, want: %v",
366				i, err, test.writeErr)
367			continue
368		}
369
370		// For errors which are not of type MessageError, check them for
371		// equality.
372		if _, ok := err.(*MessageError); !ok {
373			if err != test.writeErr {
374				t.Errorf("BtcEncode #%d wrong error got: %v, "+
375					"want: %v", i, err, test.writeErr)
376				continue
377			}
378		}
379
380		// Decode from wire format.
381		var msg MsgGetBlocks
382		r := newFixedReader(test.max, test.buf)
383		err = msg.BtcDecode(r, test.pver, test.enc)
384		if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) {
385			t.Errorf("BtcDecode #%d wrong error got: %v, want: %v",
386				i, err, test.readErr)
387			continue
388		}
389
390		// For errors which are not of type MessageError, check them for
391		// equality.
392		if _, ok := err.(*MessageError); !ok {
393			if err != test.readErr {
394				t.Errorf("BtcDecode #%d wrong error got: %v, "+
395					"want: %v", i, err, test.readErr)
396				continue
397			}
398		}
399	}
400}
401