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 mempool
6
7import (
8	"bytes"
9	"testing"
10	"time"
11
12	"github.com/btcsuite/btcd/btcec"
13	"github.com/btcsuite/btcd/chaincfg"
14	"github.com/btcsuite/btcd/chaincfg/chainhash"
15	"github.com/btcsuite/btcd/txscript"
16	"github.com/btcsuite/btcd/wire"
17	"github.com/btcsuite/btcutil"
18)
19
20// TestCalcMinRequiredTxRelayFee tests the calcMinRequiredTxRelayFee API.
21func TestCalcMinRequiredTxRelayFee(t *testing.T) {
22	tests := []struct {
23		name     string         // test description.
24		size     int64          // Transaction size in bytes.
25		relayFee btcutil.Amount // minimum relay transaction fee.
26		want     int64          // Expected fee.
27	}{
28		{
29			// Ensure combination of size and fee that are less than 1000
30			// produce a non-zero fee.
31			"250 bytes with relay fee of 3",
32			250,
33			3,
34			3,
35		},
36		{
37			"100 bytes with default minimum relay fee",
38			100,
39			DefaultMinRelayTxFee,
40			100,
41		},
42		{
43			"max standard tx size with default minimum relay fee",
44			maxStandardTxWeight / 4,
45			DefaultMinRelayTxFee,
46			100000,
47		},
48		{
49			"max standard tx size with max satoshi relay fee",
50			maxStandardTxWeight / 4,
51			btcutil.MaxSatoshi,
52			btcutil.MaxSatoshi,
53		},
54		{
55			"1500 bytes with 5000 relay fee",
56			1500,
57			5000,
58			7500,
59		},
60		{
61			"1500 bytes with 3000 relay fee",
62			1500,
63			3000,
64			4500,
65		},
66		{
67			"782 bytes with 5000 relay fee",
68			782,
69			5000,
70			3910,
71		},
72		{
73			"782 bytes with 3000 relay fee",
74			782,
75			3000,
76			2346,
77		},
78		{
79			"782 bytes with 2550 relay fee",
80			782,
81			2550,
82			1994,
83		},
84	}
85
86	for _, test := range tests {
87		got := calcMinRequiredTxRelayFee(test.size, test.relayFee)
88		if got != test.want {
89			t.Errorf("TestCalcMinRequiredTxRelayFee test '%s' "+
90				"failed: got %v want %v", test.name, got,
91				test.want)
92			continue
93		}
94	}
95}
96
97// TestCheckPkScriptStandard tests the checkPkScriptStandard API.
98func TestCheckPkScriptStandard(t *testing.T) {
99	var pubKeys [][]byte
100	for i := 0; i < 4; i++ {
101		pk, err := btcec.NewPrivateKey(btcec.S256())
102		if err != nil {
103			t.Fatalf("TestCheckPkScriptStandard NewPrivateKey failed: %v",
104				err)
105			return
106		}
107		pubKeys = append(pubKeys, pk.PubKey().SerializeCompressed())
108	}
109
110	tests := []struct {
111		name       string // test description.
112		script     *txscript.ScriptBuilder
113		isStandard bool
114	}{
115		{
116			"key1 and key2",
117			txscript.NewScriptBuilder().AddOp(txscript.OP_2).
118				AddData(pubKeys[0]).AddData(pubKeys[1]).
119				AddOp(txscript.OP_2).AddOp(txscript.OP_CHECKMULTISIG),
120			true,
121		},
122		{
123			"key1 or key2",
124			txscript.NewScriptBuilder().AddOp(txscript.OP_1).
125				AddData(pubKeys[0]).AddData(pubKeys[1]).
126				AddOp(txscript.OP_2).AddOp(txscript.OP_CHECKMULTISIG),
127			true,
128		},
129		{
130			"escrow",
131			txscript.NewScriptBuilder().AddOp(txscript.OP_2).
132				AddData(pubKeys[0]).AddData(pubKeys[1]).
133				AddData(pubKeys[2]).
134				AddOp(txscript.OP_3).AddOp(txscript.OP_CHECKMULTISIG),
135			true,
136		},
137		{
138			"one of four",
139			txscript.NewScriptBuilder().AddOp(txscript.OP_1).
140				AddData(pubKeys[0]).AddData(pubKeys[1]).
141				AddData(pubKeys[2]).AddData(pubKeys[3]).
142				AddOp(txscript.OP_4).AddOp(txscript.OP_CHECKMULTISIG),
143			false,
144		},
145		{
146			"malformed1",
147			txscript.NewScriptBuilder().AddOp(txscript.OP_3).
148				AddData(pubKeys[0]).AddData(pubKeys[1]).
149				AddOp(txscript.OP_2).AddOp(txscript.OP_CHECKMULTISIG),
150			false,
151		},
152		{
153			"malformed2",
154			txscript.NewScriptBuilder().AddOp(txscript.OP_2).
155				AddData(pubKeys[0]).AddData(pubKeys[1]).
156				AddOp(txscript.OP_3).AddOp(txscript.OP_CHECKMULTISIG),
157			false,
158		},
159		{
160			"malformed3",
161			txscript.NewScriptBuilder().AddOp(txscript.OP_0).
162				AddData(pubKeys[0]).AddData(pubKeys[1]).
163				AddOp(txscript.OP_2).AddOp(txscript.OP_CHECKMULTISIG),
164			false,
165		},
166		{
167			"malformed4",
168			txscript.NewScriptBuilder().AddOp(txscript.OP_1).
169				AddData(pubKeys[0]).AddData(pubKeys[1]).
170				AddOp(txscript.OP_0).AddOp(txscript.OP_CHECKMULTISIG),
171			false,
172		},
173		{
174			"malformed5",
175			txscript.NewScriptBuilder().AddOp(txscript.OP_1).
176				AddData(pubKeys[0]).AddData(pubKeys[1]).
177				AddOp(txscript.OP_CHECKMULTISIG),
178			false,
179		},
180		{
181			"malformed6",
182			txscript.NewScriptBuilder().AddOp(txscript.OP_1).
183				AddData(pubKeys[0]).AddData(pubKeys[1]),
184			false,
185		},
186	}
187
188	for _, test := range tests {
189		script, err := test.script.Script()
190		if err != nil {
191			t.Fatalf("TestCheckPkScriptStandard test '%s' "+
192				"failed: %v", test.name, err)
193			continue
194		}
195		scriptClass := txscript.GetScriptClass(script)
196		got := checkPkScriptStandard(script, scriptClass)
197		if (test.isStandard && got != nil) ||
198			(!test.isStandard && got == nil) {
199
200			t.Fatalf("TestCheckPkScriptStandard test '%s' failed",
201				test.name)
202			return
203		}
204	}
205}
206
207// TestDust tests the isDust API.
208func TestDust(t *testing.T) {
209	pkScript := []byte{0x76, 0xa9, 0x21, 0x03, 0x2f, 0x7e, 0x43,
210		0x0a, 0xa4, 0xc9, 0xd1, 0x59, 0x43, 0x7e, 0x84, 0xb9,
211		0x75, 0xdc, 0x76, 0xd9, 0x00, 0x3b, 0xf0, 0x92, 0x2c,
212		0xf3, 0xaa, 0x45, 0x28, 0x46, 0x4b, 0xab, 0x78, 0x0d,
213		0xba, 0x5e, 0x88, 0xac}
214
215	tests := []struct {
216		name     string // test description
217		txOut    wire.TxOut
218		relayFee btcutil.Amount // minimum relay transaction fee.
219		isDust   bool
220	}{
221		{
222			// Any value is allowed with a zero relay fee.
223			"zero value with zero relay fee",
224			wire.TxOut{Value: 0, PkScript: pkScript},
225			0,
226			false,
227		},
228		{
229			// Zero value is dust with any relay fee"
230			"zero value with very small tx fee",
231			wire.TxOut{Value: 0, PkScript: pkScript},
232			1,
233			true,
234		},
235		{
236			"38 byte public key script with value 584",
237			wire.TxOut{Value: 584, PkScript: pkScript},
238			1000,
239			true,
240		},
241		{
242			"38 byte public key script with value 585",
243			wire.TxOut{Value: 585, PkScript: pkScript},
244			1000,
245			false,
246		},
247		{
248			// Maximum allowed value is never dust.
249			"max satoshi amount is never dust",
250			wire.TxOut{Value: btcutil.MaxSatoshi, PkScript: pkScript},
251			btcutil.MaxSatoshi,
252			false,
253		},
254		{
255			// Maximum int64 value causes overflow.
256			"maximum int64 value",
257			wire.TxOut{Value: 1<<63 - 1, PkScript: pkScript},
258			1<<63 - 1,
259			true,
260		},
261		{
262			// Unspendable pkScript due to an invalid public key
263			// script.
264			"unspendable pkScript",
265			wire.TxOut{Value: 5000, PkScript: []byte{0x01}},
266			0, // no relay fee
267			true,
268		},
269	}
270	for _, test := range tests {
271		res := isDust(&test.txOut, test.relayFee)
272		if res != test.isDust {
273			t.Fatalf("Dust test '%s' failed: want %v got %v",
274				test.name, test.isDust, res)
275			continue
276		}
277	}
278}
279
280// TestCheckTransactionStandard tests the checkTransactionStandard API.
281func TestCheckTransactionStandard(t *testing.T) {
282	// Create some dummy, but otherwise standard, data for transactions.
283	prevOutHash, err := chainhash.NewHashFromStr("01")
284	if err != nil {
285		t.Fatalf("NewShaHashFromStr: unexpected error: %v", err)
286	}
287	dummyPrevOut := wire.OutPoint{Hash: *prevOutHash, Index: 1}
288	dummySigScript := bytes.Repeat([]byte{0x00}, 65)
289	dummyTxIn := wire.TxIn{
290		PreviousOutPoint: dummyPrevOut,
291		SignatureScript:  dummySigScript,
292		Sequence:         wire.MaxTxInSequenceNum,
293	}
294	addrHash := [20]byte{0x01}
295	addr, err := btcutil.NewAddressPubKeyHash(addrHash[:],
296		&chaincfg.TestNet3Params)
297	if err != nil {
298		t.Fatalf("NewAddressPubKeyHash: unexpected error: %v", err)
299	}
300	dummyPkScript, err := txscript.PayToAddrScript(addr)
301	if err != nil {
302		t.Fatalf("PayToAddrScript: unexpected error: %v", err)
303	}
304	dummyTxOut := wire.TxOut{
305		Value:    100000000, // 1 BTC
306		PkScript: dummyPkScript,
307	}
308
309	tests := []struct {
310		name       string
311		tx         wire.MsgTx
312		height     int32
313		isStandard bool
314		code       wire.RejectCode
315	}{
316		{
317			name: "Typical pay-to-pubkey-hash transaction",
318			tx: wire.MsgTx{
319				Version:  1,
320				TxIn:     []*wire.TxIn{&dummyTxIn},
321				TxOut:    []*wire.TxOut{&dummyTxOut},
322				LockTime: 0,
323			},
324			height:     300000,
325			isStandard: true,
326		},
327		{
328			name: "Transaction version too high",
329			tx: wire.MsgTx{
330				Version:  wire.TxVersion + 1,
331				TxIn:     []*wire.TxIn{&dummyTxIn},
332				TxOut:    []*wire.TxOut{&dummyTxOut},
333				LockTime: 0,
334			},
335			height:     300000,
336			isStandard: false,
337			code:       wire.RejectNonstandard,
338		},
339		{
340			name: "Transaction is not finalized",
341			tx: wire.MsgTx{
342				Version: 1,
343				TxIn: []*wire.TxIn{{
344					PreviousOutPoint: dummyPrevOut,
345					SignatureScript:  dummySigScript,
346					Sequence:         0,
347				}},
348				TxOut:    []*wire.TxOut{&dummyTxOut},
349				LockTime: 300001,
350			},
351			height:     300000,
352			isStandard: false,
353			code:       wire.RejectNonstandard,
354		},
355		{
356			name: "Transaction size is too large",
357			tx: wire.MsgTx{
358				Version: 1,
359				TxIn:    []*wire.TxIn{&dummyTxIn},
360				TxOut: []*wire.TxOut{{
361					Value: 0,
362					PkScript: bytes.Repeat([]byte{0x00},
363						(maxStandardTxWeight/4)+1),
364				}},
365				LockTime: 0,
366			},
367			height:     300000,
368			isStandard: false,
369			code:       wire.RejectNonstandard,
370		},
371		{
372			name: "Signature script size is too large",
373			tx: wire.MsgTx{
374				Version: 1,
375				TxIn: []*wire.TxIn{{
376					PreviousOutPoint: dummyPrevOut,
377					SignatureScript: bytes.Repeat([]byte{0x00},
378						maxStandardSigScriptSize+1),
379					Sequence: wire.MaxTxInSequenceNum,
380				}},
381				TxOut:    []*wire.TxOut{&dummyTxOut},
382				LockTime: 0,
383			},
384			height:     300000,
385			isStandard: false,
386			code:       wire.RejectNonstandard,
387		},
388		{
389			name: "Signature script that does more than push data",
390			tx: wire.MsgTx{
391				Version: 1,
392				TxIn: []*wire.TxIn{{
393					PreviousOutPoint: dummyPrevOut,
394					SignatureScript: []byte{
395						txscript.OP_CHECKSIGVERIFY},
396					Sequence: wire.MaxTxInSequenceNum,
397				}},
398				TxOut:    []*wire.TxOut{&dummyTxOut},
399				LockTime: 0,
400			},
401			height:     300000,
402			isStandard: false,
403			code:       wire.RejectNonstandard,
404		},
405		{
406			name: "Valid but non standard public key script",
407			tx: wire.MsgTx{
408				Version: 1,
409				TxIn:    []*wire.TxIn{&dummyTxIn},
410				TxOut: []*wire.TxOut{{
411					Value:    100000000,
412					PkScript: []byte{txscript.OP_TRUE},
413				}},
414				LockTime: 0,
415			},
416			height:     300000,
417			isStandard: false,
418			code:       wire.RejectNonstandard,
419		},
420		{
421			name: "More than one nulldata output",
422			tx: wire.MsgTx{
423				Version: 1,
424				TxIn:    []*wire.TxIn{&dummyTxIn},
425				TxOut: []*wire.TxOut{{
426					Value:    0,
427					PkScript: []byte{txscript.OP_RETURN},
428				}, {
429					Value:    0,
430					PkScript: []byte{txscript.OP_RETURN},
431				}},
432				LockTime: 0,
433			},
434			height:     300000,
435			isStandard: false,
436			code:       wire.RejectNonstandard,
437		},
438		{
439			name: "Dust output",
440			tx: wire.MsgTx{
441				Version: 1,
442				TxIn:    []*wire.TxIn{&dummyTxIn},
443				TxOut: []*wire.TxOut{{
444					Value:    0,
445					PkScript: dummyPkScript,
446				}},
447				LockTime: 0,
448			},
449			height:     300000,
450			isStandard: false,
451			code:       wire.RejectDust,
452		},
453		{
454			name: "One nulldata output with 0 amount (standard)",
455			tx: wire.MsgTx{
456				Version: 1,
457				TxIn:    []*wire.TxIn{&dummyTxIn},
458				TxOut: []*wire.TxOut{{
459					Value:    0,
460					PkScript: []byte{txscript.OP_RETURN},
461				}},
462				LockTime: 0,
463			},
464			height:     300000,
465			isStandard: true,
466		},
467	}
468
469	pastMedianTime := time.Now()
470	for _, test := range tests {
471		// Ensure standardness is as expected.
472		err := checkTransactionStandard(btcutil.NewTx(&test.tx),
473			test.height, pastMedianTime, DefaultMinRelayTxFee, 1)
474		if err == nil && test.isStandard {
475			// Test passes since function returned standard for a
476			// transaction which is intended to be standard.
477			continue
478		}
479		if err == nil && !test.isStandard {
480			t.Errorf("checkTransactionStandard (%s): standard when "+
481				"it should not be", test.name)
482			continue
483		}
484		if err != nil && test.isStandard {
485			t.Errorf("checkTransactionStandard (%s): nonstandard "+
486				"when it should not be: %v", test.name, err)
487			continue
488		}
489
490		// Ensure error type is a TxRuleError inside of a RuleError.
491		rerr, ok := err.(RuleError)
492		if !ok {
493			t.Errorf("checkTransactionStandard (%s): unexpected "+
494				"error type - got %T", test.name, err)
495			continue
496		}
497		txrerr, ok := rerr.Err.(TxRuleError)
498		if !ok {
499			t.Errorf("checkTransactionStandard (%s): unexpected "+
500				"error type - got %T", test.name, rerr.Err)
501			continue
502		}
503
504		// Ensure the reject code is the expected one.
505		if txrerr.RejectCode != test.code {
506			t.Errorf("checkTransactionStandard (%s): unexpected "+
507				"error code - got %v, want %v", test.name,
508				txrerr.RejectCode, test.code)
509			continue
510		}
511	}
512}
513