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