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// TestInv tests the MsgInv API.
18func TestInv(t *testing.T) {
19	pver := ProtocolVersion
20
21	// Ensure the command is expected value.
22	wantCmd := "inv"
23	msg := NewMsgInv()
24	if cmd := msg.Command(); cmd != wantCmd {
25		t.Errorf("NewMsgInv: wrong command - got %v want %v",
26			cmd, wantCmd)
27	}
28
29	// Ensure max payload is expected value for latest protocol version.
30	// Num inventory vectors (varInt) + max allowed inventory vectors.
31	wantPayload := uint32(1800009)
32	maxPayload := msg.MaxPayloadLength(pver)
33	if maxPayload != wantPayload {
34		t.Errorf("MaxPayloadLength: wrong max payload length for "+
35			"protocol version %d - got %v, want %v", pver,
36			maxPayload, wantPayload)
37	}
38
39	// Ensure inventory vectors are added properly.
40	hash := chainhash.Hash{}
41	iv := NewInvVect(InvTypeBlock, &hash)
42	err := msg.AddInvVect(iv)
43	if err != nil {
44		t.Errorf("AddInvVect: %v", err)
45	}
46	if msg.InvList[0] != iv {
47		t.Errorf("AddInvVect: wrong invvect added - got %v, want %v",
48			spew.Sprint(msg.InvList[0]), spew.Sprint(iv))
49	}
50
51	// Ensure adding more than the max allowed inventory vectors per
52	// message returns an error.
53	for i := 0; i < MaxInvPerMsg; i++ {
54		err = msg.AddInvVect(iv)
55	}
56	if err == nil {
57		t.Errorf("AddInvVect: expected error on too many inventory " +
58			"vectors not received")
59	}
60
61	// Ensure creating the message with a size hint larger than the max
62	// works as expected.
63	msg = NewMsgInvSizeHint(MaxInvPerMsg + 1)
64	wantCap := MaxInvPerMsg
65	if cap(msg.InvList) != wantCap {
66		t.Errorf("NewMsgInvSizeHint: wrong cap for size hint - "+
67			"got %v, want %v", cap(msg.InvList), wantCap)
68	}
69}
70
71// TestInvWire tests the MsgInv wire encode and decode for various numbers
72// of inventory vectors and protocol versions.
73func TestInvWire(t *testing.T) {
74	// Block 203707 hash.
75	hashStr := "3264bc2ac36a60840790ba1d475d01367e7c723da941069e9dc"
76	blockHash, err := chainhash.NewHashFromStr(hashStr)
77	if err != nil {
78		t.Errorf("NewHashFromStr: %v", err)
79	}
80
81	// Transaction 1 of Block 203707 hash.
82	hashStr = "d28a3dc7392bf00a9855ee93dd9a81eff82a2c4fe57fbd42cfe71b487accfaf0"
83	txHash, err := chainhash.NewHashFromStr(hashStr)
84	if err != nil {
85		t.Errorf("NewHashFromStr: %v", err)
86	}
87
88	iv := NewInvVect(InvTypeBlock, blockHash)
89	iv2 := NewInvVect(InvTypeTx, txHash)
90
91	// Empty inv message.
92	NoInv := NewMsgInv()
93	NoInvEncoded := []byte{
94		0x00, // Varint for number of inventory vectors
95	}
96
97	// Inv message with multiple inventory vectors.
98	MultiInv := NewMsgInv()
99	MultiInv.AddInvVect(iv)
100	MultiInv.AddInvVect(iv2)
101	MultiInvEncoded := []byte{
102		0x02,                   // Varint for number of inv vectors
103		0x02, 0x00, 0x00, 0x00, // InvTypeBlock
104		0xdc, 0xe9, 0x69, 0x10, 0x94, 0xda, 0x23, 0xc7,
105		0xe7, 0x67, 0x13, 0xd0, 0x75, 0xd4, 0xa1, 0x0b,
106		0x79, 0x40, 0x08, 0xa6, 0x36, 0xac, 0xc2, 0x4b,
107		0x26, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 203707 hash
108		0x01, 0x00, 0x00, 0x00, // InvTypeTx
109		0xf0, 0xfa, 0xcc, 0x7a, 0x48, 0x1b, 0xe7, 0xcf,
110		0x42, 0xbd, 0x7f, 0xe5, 0x4f, 0x2c, 0x2a, 0xf8,
111		0xef, 0x81, 0x9a, 0xdd, 0x93, 0xee, 0x55, 0x98,
112		0x0a, 0xf0, 0x2b, 0x39, 0xc7, 0x3d, 0x8a, 0xd2, // Tx 1 of block 203707 hash
113	}
114
115	tests := []struct {
116		in   *MsgInv         // Message to encode
117		out  *MsgInv         // Expected decoded message
118		buf  []byte          // Wire encoding pver uint32
119		pver uint32          // Protocol version for wire encoding
120		enc  MessageEncoding // Message encodinf format
121	}{
122		// Latest protocol version with no inv vectors.
123		{
124			NoInv,
125			NoInv,
126			NoInvEncoded,
127			ProtocolVersion,
128			BaseEncoding,
129		},
130
131		// Latest protocol version with multiple inv vectors.
132		{
133			MultiInv,
134			MultiInv,
135			MultiInvEncoded,
136			ProtocolVersion,
137			BaseEncoding,
138		},
139
140		// Protocol version BIP0035Version no inv vectors.
141		{
142			NoInv,
143			NoInv,
144			NoInvEncoded,
145			BIP0035Version,
146			BaseEncoding,
147		},
148
149		// Protocol version BIP0035Version with multiple inv vectors.
150		{
151			MultiInv,
152			MultiInv,
153			MultiInvEncoded,
154			BIP0035Version,
155			BaseEncoding,
156		},
157
158		// Protocol version BIP0031Version no inv vectors.
159		{
160			NoInv,
161			NoInv,
162			NoInvEncoded,
163			BIP0031Version,
164			BaseEncoding,
165		},
166
167		// Protocol version BIP0031Version with multiple inv vectors.
168		{
169			MultiInv,
170			MultiInv,
171			MultiInvEncoded,
172			BIP0031Version,
173			BaseEncoding,
174		},
175
176		// Protocol version NetAddressTimeVersion no inv vectors.
177		{
178			NoInv,
179			NoInv,
180			NoInvEncoded,
181			NetAddressTimeVersion,
182			BaseEncoding,
183		},
184
185		// Protocol version NetAddressTimeVersion with multiple inv vectors.
186		{
187			MultiInv,
188			MultiInv,
189			MultiInvEncoded,
190			NetAddressTimeVersion,
191			BaseEncoding,
192		},
193
194		// Protocol version MultipleAddressVersion no inv vectors.
195		{
196			NoInv,
197			NoInv,
198			NoInvEncoded,
199			MultipleAddressVersion,
200			BaseEncoding,
201		},
202
203		// Protocol version MultipleAddressVersion with multiple inv vectors.
204		{
205			MultiInv,
206			MultiInv,
207			MultiInvEncoded,
208			MultipleAddressVersion,
209			BaseEncoding,
210		},
211	}
212
213	t.Logf("Running %d tests", len(tests))
214	for i, test := range tests {
215		// Encode the message to wire format.
216		var buf bytes.Buffer
217		err := test.in.BtcEncode(&buf, test.pver, test.enc)
218		if err != nil {
219			t.Errorf("BtcEncode #%d error %v", i, err)
220			continue
221		}
222		if !bytes.Equal(buf.Bytes(), test.buf) {
223			t.Errorf("BtcEncode #%d\n got: %s want: %s", i,
224				spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
225			continue
226		}
227
228		// Decode the message from wire format.
229		var msg MsgInv
230		rbuf := bytes.NewReader(test.buf)
231		err = msg.BtcDecode(rbuf, test.pver, test.enc)
232		if err != nil {
233			t.Errorf("BtcDecode #%d error %v", i, err)
234			continue
235		}
236		if !reflect.DeepEqual(&msg, test.out) {
237			t.Errorf("BtcDecode #%d\n got: %s want: %s", i,
238				spew.Sdump(msg), spew.Sdump(test.out))
239			continue
240		}
241	}
242}
243
244// TestInvWireErrors performs negative tests against wire encode and decode
245// of MsgInv to confirm error paths work correctly.
246func TestInvWireErrors(t *testing.T) {
247	pver := ProtocolVersion
248	wireErr := &MessageError{}
249
250	// Block 203707 hash.
251	hashStr := "3264bc2ac36a60840790ba1d475d01367e7c723da941069e9dc"
252	blockHash, err := chainhash.NewHashFromStr(hashStr)
253	if err != nil {
254		t.Errorf("NewHashFromStr: %v", err)
255	}
256
257	iv := NewInvVect(InvTypeBlock, blockHash)
258
259	// Base inv message used to induce errors.
260	baseInv := NewMsgInv()
261	baseInv.AddInvVect(iv)
262	baseInvEncoded := []byte{
263		0x02,                   // Varint for number of inv vectors
264		0x02, 0x00, 0x00, 0x00, // InvTypeBlock
265		0xdc, 0xe9, 0x69, 0x10, 0x94, 0xda, 0x23, 0xc7,
266		0xe7, 0x67, 0x13, 0xd0, 0x75, 0xd4, 0xa1, 0x0b,
267		0x79, 0x40, 0x08, 0xa6, 0x36, 0xac, 0xc2, 0x4b,
268		0x26, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Block 203707 hash
269	}
270
271	// Inv message that forces an error by having more than the max allowed
272	// inv vectors.
273	maxInv := NewMsgInv()
274	for i := 0; i < MaxInvPerMsg; i++ {
275		maxInv.AddInvVect(iv)
276	}
277	maxInv.InvList = append(maxInv.InvList, iv)
278	maxInvEncoded := []byte{
279		0xfd, 0x51, 0xc3, // Varint for number of inv vectors (50001)
280	}
281
282	tests := []struct {
283		in       *MsgInv         // Value to encode
284		buf      []byte          // Wire encoding
285		pver     uint32          // Protocol version for wire encoding
286		enc      MessageEncoding // Message encoding format
287		max      int             // Max size of fixed buffer to induce errors
288		writeErr error           // Expected write error
289		readErr  error           // Expected read error
290	}{
291		// Latest protocol version with intentional read/write errors.
292		// Force error in inventory vector count
293		{baseInv, baseInvEncoded, pver, BaseEncoding, 0, io.ErrShortWrite, io.EOF},
294		// Force error in inventory list.
295		{baseInv, baseInvEncoded, pver, BaseEncoding, 1, io.ErrShortWrite, io.EOF},
296		// Force error with greater than max inventory vectors.
297		{maxInv, maxInvEncoded, pver, BaseEncoding, 3, wireErr, wireErr},
298	}
299
300	t.Logf("Running %d tests", len(tests))
301	for i, test := range tests {
302		// Encode to wire format.
303		w := newFixedWriter(test.max)
304		err := test.in.BtcEncode(w, test.pver, test.enc)
305		if reflect.TypeOf(err) != reflect.TypeOf(test.writeErr) {
306			t.Errorf("BtcEncode #%d wrong error got: %v, want: %v",
307				i, err, test.writeErr)
308			continue
309		}
310
311		// For errors which are not of type MessageError, check them for
312		// equality.
313		if _, ok := err.(*MessageError); !ok {
314			if err != test.writeErr {
315				t.Errorf("BtcEncode #%d wrong error got: %v, "+
316					"want: %v", i, err, test.writeErr)
317				continue
318			}
319		}
320
321		// Decode from wire format.
322		var msg MsgInv
323		r := newFixedReader(test.max, test.buf)
324		err = msg.BtcDecode(r, test.pver, test.enc)
325		if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) {
326			t.Errorf("BtcDecode #%d wrong error got: %v, want: %v",
327				i, err, test.readErr)
328			continue
329		}
330
331		// For errors which are not of type MessageError, check them for
332		// equality.
333		if _, ok := err.(*MessageError); !ok {
334			if err != test.readErr {
335				t.Errorf("BtcDecode #%d wrong error got: %v, "+
336					"want: %v", i, err, test.readErr)
337				continue
338			}
339		}
340
341	}
342}
343