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