github.com/btcsuite/btcd@v0.24.0/integration/csv_fork_test.go (about) 1 // Copyright (c) 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 5 // This file is ignored during the regular tests due to the following build tag. 6 //go:build rpctest 7 // +build rpctest 8 9 package integration 10 11 import ( 12 "bytes" 13 "runtime" 14 "strings" 15 "testing" 16 "time" 17 18 "github.com/btcsuite/btcd/blockchain" 19 "github.com/btcsuite/btcd/btcec/v2" 20 "github.com/btcsuite/btcd/btcutil" 21 "github.com/btcsuite/btcd/chaincfg" 22 "github.com/btcsuite/btcd/chaincfg/chainhash" 23 "github.com/btcsuite/btcd/integration/rpctest" 24 "github.com/btcsuite/btcd/txscript" 25 "github.com/btcsuite/btcd/wire" 26 ) 27 28 const ( 29 csvKey = "csv" 30 ) 31 32 // makeTestOutput creates an on-chain output paying to a freshly generated 33 // p2pkh output with the specified amount. 34 func makeTestOutput(r *rpctest.Harness, t *testing.T, 35 amt btcutil.Amount) (*btcec.PrivateKey, *wire.OutPoint, []byte, error) { 36 37 // Create a fresh key, then send some coins to an address spendable by 38 // that key. 39 key, err := btcec.NewPrivateKey() 40 if err != nil { 41 return nil, nil, nil, err 42 } 43 44 // Using the key created above, generate a pkScript which it's able to 45 // spend. 46 a, err := btcutil.NewAddressPubKey(key.PubKey().SerializeCompressed(), r.ActiveNet) 47 if err != nil { 48 return nil, nil, nil, err 49 } 50 selfAddrScript, err := txscript.PayToAddrScript(a.AddressPubKeyHash()) 51 if err != nil { 52 return nil, nil, nil, err 53 } 54 output := &wire.TxOut{PkScript: selfAddrScript, Value: 1e8} 55 56 // Next, create and broadcast a transaction paying to the output. 57 fundTx, err := r.CreateTransaction([]*wire.TxOut{output}, 10, true) 58 if err != nil { 59 return nil, nil, nil, err 60 } 61 txHash, err := r.Client.SendRawTransaction(fundTx, true) 62 if err != nil { 63 return nil, nil, nil, err 64 } 65 66 // The transaction created above should be included within the next 67 // generated block. 68 blockHash, err := r.Client.Generate(1) 69 if err != nil { 70 return nil, nil, nil, err 71 } 72 assertTxInBlock(r, t, blockHash[0], txHash) 73 74 // Locate the output index of the coins spendable by the key we 75 // generated above, this is needed in order to create a proper utxo for 76 // this output. 77 var outputIndex uint32 78 if bytes.Equal(fundTx.TxOut[0].PkScript, selfAddrScript) { 79 outputIndex = 0 80 } else { 81 outputIndex = 1 82 } 83 84 utxo := &wire.OutPoint{ 85 Hash: fundTx.TxHash(), 86 Index: outputIndex, 87 } 88 89 return key, utxo, selfAddrScript, nil 90 } 91 92 // TestBIP0113Activation tests for proper adherence of the BIP 113 rule 93 // constraint which requires all transaction finality tests to use the MTP of 94 // the last 11 blocks, rather than the timestamp of the block which includes 95 // them. 96 // 97 // Overview: 98 // 99 // - Pre soft-fork: 100 // 1. Transactions with non-final lock-times from the PoV of MTP should be 101 // rejected from the mempool. 102 // 2. Transactions within non-final MTP based lock-times should be accepted 103 // in valid blocks. 104 // 105 // - Post soft-fork: 106 // 1. Transactions with non-final lock-times from the PoV of MTP should be 107 // rejected from the mempool and when found within otherwise valid blocks. 108 // 2. Transactions with final lock-times from the PoV of MTP should be 109 // accepted to the mempool and mined in future block. 110 func TestBIP0113Activation(t *testing.T) { 111 t.Parallel() 112 113 btcdCfg := []string{"--rejectnonstd"} 114 r, err := rpctest.New(&chaincfg.SimNetParams, nil, btcdCfg, "") 115 if err != nil { 116 t.Fatal("unable to create primary harness: ", err) 117 } 118 if err := r.SetUp(true, 1); err != nil { 119 t.Fatalf("unable to setup test chain: %v", err) 120 } 121 defer r.TearDown() 122 123 // Create a fresh output for usage within the test below. 124 const outputValue = btcutil.SatoshiPerBitcoin 125 outputKey, testOutput, testPkScript, err := makeTestOutput(r, t, 126 outputValue) 127 if err != nil { 128 t.Fatalf("unable to create test output: %v", err) 129 } 130 131 // Fetch a fresh address from the harness, we'll use this address to 132 // send funds back into the Harness. 133 addr, err := r.NewAddress() 134 if err != nil { 135 t.Fatalf("unable to generate address: %v", err) 136 } 137 addrScript, err := txscript.PayToAddrScript(addr) 138 if err != nil { 139 t.Fatalf("unable to generate addr script: %v", err) 140 } 141 142 // Now create a transaction with a lock time which is "final" according 143 // to the latest block, but not according to the current median time 144 // past. 145 tx := wire.NewMsgTx(1) 146 tx.AddTxIn(&wire.TxIn{ 147 PreviousOutPoint: *testOutput, 148 }) 149 tx.AddTxOut(&wire.TxOut{ 150 PkScript: addrScript, 151 Value: outputValue - 1000, 152 }) 153 154 // We set the lock-time of the transaction to just one minute after the 155 // current MTP of the chain. 156 chainInfo, err := r.Client.GetBlockChainInfo() 157 if err != nil { 158 t.Fatalf("unable to query for chain info: %v", err) 159 } 160 tx.LockTime = uint32(chainInfo.MedianTime) + 1 161 162 sigScript, err := txscript.SignatureScript(tx, 0, testPkScript, 163 txscript.SigHashAll, outputKey, true) 164 if err != nil { 165 t.Fatalf("unable to generate sig: %v", err) 166 } 167 tx.TxIn[0].SignatureScript = sigScript 168 169 // This transaction should be rejected from the mempool as using MTP 170 // for transactions finality is now a policy rule. Additionally, the 171 // exact error should be the rejection of a non-final transaction. 172 _, err = r.Client.SendRawTransaction(tx, true) 173 if err == nil { 174 t.Fatalf("transaction accepted, but should be non-final") 175 } else if !strings.Contains(err.Error(), "not finalized") { 176 t.Fatalf("transaction should be rejected due to being "+ 177 "non-final, instead: %v", err) 178 } 179 180 // However, since the block validation consensus rules haven't yet 181 // activated, a block including the transaction should be accepted. 182 txns := []*btcutil.Tx{btcutil.NewTx(tx)} 183 block, err := r.GenerateAndSubmitBlock(txns, -1, time.Time{}) 184 if err != nil { 185 t.Fatalf("unable to submit block: %v", err) 186 } 187 txid := tx.TxHash() 188 assertTxInBlock(r, t, block.Hash(), &txid) 189 190 // At this point, the block height should be 103: we mined 101 blocks 191 // to create a single mature output, then an additional block to create 192 // a new output, and then mined a single block above to include our 193 // transaction. 194 assertChainHeight(r, t, 103) 195 196 // Next, mine enough blocks to ensure that the soft-fork becomes 197 // activated. Assert that the block version of the second-to-last block 198 // in the final range is active. 199 200 // Next, mine ensure blocks to ensure that the soft-fork becomes 201 // active. We're at height 103 and we need 200 blocks to be mined after 202 // the genesis target period, so we mine 196 blocks. This'll put us at 203 // height 299. The getblockchaininfo call checks the state for the 204 // block AFTER the current height. 205 numBlocks := (r.ActiveNet.MinerConfirmationWindow * 2) - 4 206 if _, err := r.Client.Generate(numBlocks); err != nil { 207 t.Fatalf("unable to generate blocks: %v", err) 208 } 209 210 assertChainHeight(r, t, 299) 211 assertSoftForkStatus(r, t, csvKey, blockchain.ThresholdActive) 212 213 // The timeLockDeltas slice represents a series of deviations from the 214 // current MTP which will be used to test border conditions w.r.t 215 // transaction finality. -1 indicates 1 second prior to the MTP, 0 216 // indicates the current MTP, and 1 indicates 1 second after the 217 // current MTP. 218 // 219 // This time, all transactions which are final according to the MTP 220 // *should* be accepted to both the mempool and within a valid block. 221 // While transactions with lock-times *after* the current MTP should be 222 // rejected. 223 timeLockDeltas := []int64{-1, 0, 1} 224 for _, timeLockDelta := range timeLockDeltas { 225 chainInfo, err = r.Client.GetBlockChainInfo() 226 if err != nil { 227 t.Fatalf("unable to query for chain info: %v", err) 228 } 229 medianTimePast := chainInfo.MedianTime 230 231 // Create another test output to be spent shortly below. 232 outputKey, testOutput, testPkScript, err = makeTestOutput(r, t, 233 outputValue) 234 if err != nil { 235 t.Fatalf("unable to create test output: %v", err) 236 } 237 238 // Create a new transaction with a lock-time past the current known 239 // MTP. 240 tx = wire.NewMsgTx(1) 241 tx.AddTxIn(&wire.TxIn{ 242 PreviousOutPoint: *testOutput, 243 }) 244 tx.AddTxOut(&wire.TxOut{ 245 PkScript: addrScript, 246 Value: outputValue - 1000, 247 }) 248 tx.LockTime = uint32(medianTimePast + timeLockDelta) 249 sigScript, err = txscript.SignatureScript(tx, 0, testPkScript, 250 txscript.SigHashAll, outputKey, true) 251 if err != nil { 252 t.Fatalf("unable to generate sig: %v", err) 253 } 254 tx.TxIn[0].SignatureScript = sigScript 255 256 // If the time-lock delta is greater than -1, then the 257 // transaction should be rejected from the mempool and when 258 // included within a block. A time-lock delta of -1 should be 259 // accepted as it has a lock-time of one 260 // second _before_ the current MTP. 261 262 _, err = r.Client.SendRawTransaction(tx, true) 263 if err == nil && timeLockDelta >= 0 { 264 t.Fatal("transaction was accepted into the mempool " + 265 "but should be rejected!") 266 } else if err != nil && !strings.Contains(err.Error(), "not finalized") { 267 t.Fatalf("transaction should be rejected from mempool "+ 268 "due to being non-final, instead: %v", err) 269 } 270 271 txns = []*btcutil.Tx{btcutil.NewTx(tx)} 272 _, err := r.GenerateAndSubmitBlock(txns, -1, time.Time{}) 273 if err == nil && timeLockDelta >= 0 { 274 t.Fatal("block should be rejected due to non-final " + 275 "txn, but was accepted") 276 } else if err != nil && !strings.Contains(err.Error(), "unfinalized") { 277 t.Fatalf("block should be rejected due to non-final "+ 278 "tx, instead: %v", err) 279 } 280 } 281 } 282 283 // createCSVOutput creates an output paying to a trivially redeemable CSV 284 // pkScript with the specified time-lock. 285 func createCSVOutput(r *rpctest.Harness, t *testing.T, 286 numSatoshis btcutil.Amount, timeLock int32, 287 isSeconds bool) ([]byte, *wire.OutPoint, *wire.MsgTx, error) { 288 289 // Convert the time-lock to the proper sequence lock based according to 290 // if the lock is seconds or time based. 291 sequenceLock := blockchain.LockTimeToSequence(isSeconds, 292 uint32(timeLock)) 293 294 // Our CSV script is simply: <sequenceLock> OP_CSV OP_DROP 295 b := txscript.NewScriptBuilder(). 296 AddInt64(int64(sequenceLock)). 297 AddOp(txscript.OP_CHECKSEQUENCEVERIFY). 298 AddOp(txscript.OP_DROP) 299 csvScript, err := b.Script() 300 if err != nil { 301 return nil, nil, nil, err 302 } 303 304 // Using the script generated above, create a P2SH output which will be 305 // accepted into the mempool. 306 p2shAddr, err := btcutil.NewAddressScriptHash(csvScript, r.ActiveNet) 307 if err != nil { 308 return nil, nil, nil, err 309 } 310 p2shScript, err := txscript.PayToAddrScript(p2shAddr) 311 if err != nil { 312 return nil, nil, nil, err 313 } 314 output := &wire.TxOut{ 315 PkScript: p2shScript, 316 Value: int64(numSatoshis), 317 } 318 319 // Finally create a valid transaction which creates the output crafted 320 // above. 321 tx, err := r.CreateTransaction([]*wire.TxOut{output}, 10, true) 322 if err != nil { 323 return nil, nil, nil, err 324 } 325 326 var outputIndex uint32 327 if !bytes.Equal(tx.TxOut[0].PkScript, p2shScript) { 328 outputIndex = 1 329 } 330 331 utxo := &wire.OutPoint{ 332 Hash: tx.TxHash(), 333 Index: outputIndex, 334 } 335 336 return csvScript, utxo, tx, nil 337 } 338 339 // spendCSVOutput spends an output previously created by the createCSVOutput 340 // function. The sigScript is a trivial push of OP_TRUE followed by the 341 // redeemScript to pass P2SH evaluation. 342 func spendCSVOutput(redeemScript []byte, csvUTXO *wire.OutPoint, 343 sequence uint32, targetOutput *wire.TxOut, 344 txVersion int32) (*wire.MsgTx, error) { 345 346 tx := wire.NewMsgTx(txVersion) 347 tx.AddTxIn(&wire.TxIn{ 348 PreviousOutPoint: *csvUTXO, 349 Sequence: sequence, 350 }) 351 tx.AddTxOut(targetOutput) 352 353 b := txscript.NewScriptBuilder(). 354 AddOp(txscript.OP_TRUE). 355 AddData(redeemScript) 356 357 sigScript, err := b.Script() 358 if err != nil { 359 return nil, err 360 } 361 tx.TxIn[0].SignatureScript = sigScript 362 363 return tx, nil 364 } 365 366 // assertTxInBlock asserts a transaction with the specified txid is found 367 // within the block with the passed block hash. 368 func assertTxInBlock(r *rpctest.Harness, t *testing.T, blockHash *chainhash.Hash, 369 txid *chainhash.Hash) { 370 371 block, err := r.Client.GetBlock(blockHash) 372 if err != nil { 373 t.Fatalf("unable to get block: %v", err) 374 } 375 if len(block.Transactions) < 2 { 376 t.Fatal("target transaction was not mined") 377 } 378 379 for _, txn := range block.Transactions { 380 txHash := txn.TxHash() 381 if txn.TxHash() == txHash { 382 return 383 } 384 } 385 386 _, _, line, _ := runtime.Caller(1) 387 t.Fatalf("assertion failed at line %v: txid %v was not found in "+ 388 "block %v", line, txid, blockHash) 389 } 390 391 // TestBIP0068AndBIP0112Activation tests for the proper adherence to the BIP 392 // 112 and BIP 68 rule-set after the activation of the CSV-package soft-fork. 393 // 394 // Overview: 395 // - Pre soft-fork: 396 // 1. A transaction spending a CSV output validly should be rejected from the 397 // mempool, but accepted in a valid generated block including the 398 // transaction. 399 // 400 // - Post soft-fork: 401 // 1. See the cases exercised within the table driven tests towards the end 402 // of this test. 403 func TestBIP0068AndBIP0112Activation(t *testing.T) { 404 t.Parallel() 405 406 // We'd like the test proper evaluation and validation of the BIP 68 407 // (sequence locks) and BIP 112 rule-sets which add input-age based 408 // relative lock times. 409 410 btcdCfg := []string{"--rejectnonstd"} 411 r, err := rpctest.New(&chaincfg.SimNetParams, nil, btcdCfg, "") 412 if err != nil { 413 t.Fatal("unable to create primary harness: ", err) 414 } 415 if err := r.SetUp(true, 1); err != nil { 416 t.Fatalf("unable to setup test chain: %v", err) 417 } 418 defer r.TearDown() 419 420 assertSoftForkStatus(r, t, csvKey, blockchain.ThresholdStarted) 421 422 harnessAddr, err := r.NewAddress() 423 if err != nil { 424 t.Fatalf("unable to obtain harness address: %v", err) 425 } 426 harnessScript, err := txscript.PayToAddrScript(harnessAddr) 427 if err != nil { 428 t.Fatalf("unable to generate pkScript: %v", err) 429 } 430 431 const ( 432 outputAmt = btcutil.SatoshiPerBitcoin 433 relativeBlockLock = 10 434 ) 435 436 sweepOutput := &wire.TxOut{ 437 Value: outputAmt - 5000, 438 PkScript: harnessScript, 439 } 440 441 // As the soft-fork hasn't yet activated _any_ transaction version 442 // which uses the CSV opcode should be accepted. Since at this point, 443 // CSV doesn't actually exist, it's just a NOP. 444 for txVersion := int32(0); txVersion < 3; txVersion++ { 445 // Create a trivially spendable output with a CSV lock-time of 446 // 10 relative blocks. 447 redeemScript, testUTXO, tx, err := createCSVOutput(r, t, outputAmt, 448 relativeBlockLock, false) 449 if err != nil { 450 t.Fatalf("unable to create CSV encumbered output: %v", err) 451 } 452 453 // As the transaction is p2sh it should be accepted into the 454 // mempool and found within the next generated block. 455 if _, err := r.Client.SendRawTransaction(tx, true); err != nil { 456 t.Fatalf("unable to broadcast tx: %v", err) 457 } 458 blocks, err := r.Client.Generate(1) 459 if err != nil { 460 t.Fatalf("unable to generate blocks: %v", err) 461 } 462 txid := tx.TxHash() 463 assertTxInBlock(r, t, blocks[0], &txid) 464 465 // Generate a custom transaction which spends the CSV output. 466 sequenceNum := blockchain.LockTimeToSequence(false, 10) 467 spendingTx, err := spendCSVOutput(redeemScript, testUTXO, 468 sequenceNum, sweepOutput, txVersion) 469 if err != nil { 470 t.Fatalf("unable to spend csv output: %v", err) 471 } 472 473 // This transaction should be rejected from the mempool since 474 // CSV validation is already mempool policy pre-fork. 475 _, err = r.Client.SendRawTransaction(spendingTx, true) 476 if err == nil { 477 t.Fatalf("transaction should have been rejected, but was " + 478 "instead accepted") 479 } 480 481 // However, this transaction should be accepted in a custom 482 // generated block as CSV validation for scripts within blocks 483 // shouldn't yet be active. 484 txns := []*btcutil.Tx{btcutil.NewTx(spendingTx)} 485 block, err := r.GenerateAndSubmitBlock(txns, -1, time.Time{}) 486 if err != nil { 487 t.Fatalf("unable to submit block: %v", err) 488 } 489 txid = spendingTx.TxHash() 490 assertTxInBlock(r, t, block.Hash(), &txid) 491 } 492 493 // At this point, the block height should be 107: we started at height 494 // 101, then generated 2 blocks in each loop iteration above. 495 assertChainHeight(r, t, 107) 496 497 // With the height at 107 we need 200 blocks to be mined after the 498 // genesis target period, so we mine 192 blocks. This'll put us at 499 // height 299. The getblockchaininfo call checks the state for the 500 // block AFTER the current height. 501 numBlocks := (r.ActiveNet.MinerConfirmationWindow * 2) - 8 502 if _, err := r.Client.Generate(numBlocks); err != nil { 503 t.Fatalf("unable to generate blocks: %v", err) 504 } 505 506 assertChainHeight(r, t, 299) 507 assertSoftForkStatus(r, t, csvKey, blockchain.ThresholdActive) 508 509 // Knowing the number of outputs needed for the tests below, create a 510 // fresh output for use within each of the test-cases below. 511 const relativeTimeLock = 512 512 const numTests = 8 513 type csvOutput struct { 514 RedeemScript []byte 515 Utxo *wire.OutPoint 516 Timelock int32 517 } 518 var spendableInputs [numTests]csvOutput 519 520 // Create three outputs which have a block-based sequence locks, and 521 // three outputs which use the above time based sequence lock. 522 for i := 0; i < numTests; i++ { 523 timeLock := relativeTimeLock 524 isSeconds := true 525 if i < 7 { 526 timeLock = relativeBlockLock 527 isSeconds = false 528 } 529 530 redeemScript, utxo, tx, err := createCSVOutput(r, t, outputAmt, 531 int32(timeLock), isSeconds) 532 if err != nil { 533 t.Fatalf("unable to create CSV output: %v", err) 534 } 535 536 if _, err := r.Client.SendRawTransaction(tx, true); err != nil { 537 t.Fatalf("unable to broadcast transaction: %v", err) 538 } 539 540 spendableInputs[i] = csvOutput{ 541 RedeemScript: redeemScript, 542 Utxo: utxo, 543 Timelock: int32(timeLock), 544 } 545 } 546 547 // Mine a single block including all the transactions generated above. 548 if _, err := r.Client.Generate(1); err != nil { 549 t.Fatalf("unable to generate block: %v", err) 550 } 551 552 // Now mine 10 additional blocks giving the inputs generated above a 553 // age of 11. Space out each block 10 minutes after the previous block. 554 prevBlockHash, err := r.Client.GetBestBlockHash() 555 if err != nil { 556 t.Fatalf("unable to get prior block hash: %v", err) 557 } 558 prevBlock, err := r.Client.GetBlock(prevBlockHash) 559 if err != nil { 560 t.Fatalf("unable to get block: %v", err) 561 } 562 for i := 0; i < relativeBlockLock; i++ { 563 timeStamp := prevBlock.Header.Timestamp.Add(time.Minute * 10) 564 b, err := r.GenerateAndSubmitBlock(nil, -1, timeStamp) 565 if err != nil { 566 t.Fatalf("unable to generate block: %v", err) 567 } 568 569 prevBlock = b.MsgBlock() 570 } 571 572 // A helper function to create fully signed transactions in-line during 573 // the array initialization below. 574 var inputIndex uint32 575 makeTxCase := func(sequenceNum uint32, txVersion int32) *wire.MsgTx { 576 csvInput := spendableInputs[inputIndex] 577 578 tx, err := spendCSVOutput(csvInput.RedeemScript, csvInput.Utxo, 579 sequenceNum, sweepOutput, txVersion) 580 if err != nil { 581 t.Fatalf("unable to spend CSV output: %v", err) 582 } 583 584 inputIndex++ 585 return tx 586 } 587 588 tests := [numTests]struct { 589 tx *wire.MsgTx 590 accept bool 591 }{ 592 // A valid transaction with a single input a sequence number 593 // creating a 100 block relative time-lock. This transaction 594 // should be rejected as its version number is 1, and only tx 595 // of version > 2 will trigger the CSV behavior. 596 { 597 tx: makeTxCase(blockchain.LockTimeToSequence(false, 100), 1), 598 accept: false, 599 }, 600 // A transaction of version 2 spending a single input. The 601 // input has a relative time-lock of 1 block, but the disable 602 // bit it set. The transaction should be rejected as a result. 603 { 604 tx: makeTxCase( 605 blockchain.LockTimeToSequence(false, 1)|wire.SequenceLockTimeDisabled, 606 2, 607 ), 608 accept: false, 609 }, 610 // A v2 transaction with a single input having a 9 block 611 // relative time lock. The referenced input is 11 blocks old, 612 // but the CSV output requires a 10 block relative lock-time. 613 // Therefore, the transaction should be rejected. 614 { 615 tx: makeTxCase(blockchain.LockTimeToSequence(false, 9), 2), 616 accept: false, 617 }, 618 // A v2 transaction with a single input having a 10 block 619 // relative time lock. The referenced input is 11 blocks old so 620 // the transaction should be accepted. 621 { 622 tx: makeTxCase(blockchain.LockTimeToSequence(false, 10), 2), 623 accept: true, 624 }, 625 // A v2 transaction with a single input having a 11 block 626 // relative time lock. The input referenced has an input age of 627 // 11 and the CSV op-code requires 10 blocks to have passed, so 628 // this transaction should be accepted. 629 { 630 tx: makeTxCase(blockchain.LockTimeToSequence(false, 11), 2), 631 accept: true, 632 }, 633 // A v2 transaction whose input has a 1000 blck relative time 634 // lock. This should be rejected as the input's age is only 11 635 // blocks. 636 { 637 tx: makeTxCase(blockchain.LockTimeToSequence(false, 1000), 2), 638 accept: false, 639 }, 640 // A v2 transaction with a single input having a 512,000 second 641 // relative time-lock. This transaction should be rejected as 6 642 // days worth of blocks haven't yet been mined. The referenced 643 // input doesn't have sufficient age. 644 { 645 tx: makeTxCase(blockchain.LockTimeToSequence(true, 512000), 2), 646 accept: false, 647 }, 648 // A v2 transaction whose single input has a 512 second 649 // relative time-lock. This transaction should be accepted as 650 // finalized. 651 { 652 tx: makeTxCase(blockchain.LockTimeToSequence(true, 512), 2), 653 accept: true, 654 }, 655 } 656 657 for i, test := range tests { 658 txid, err := r.Client.SendRawTransaction(test.tx, true) 659 switch { 660 // Test case passes, nothing further to report. 661 case test.accept && err == nil: 662 663 // Transaction should have been accepted but we have a non-nil 664 // error. 665 case test.accept && err != nil: 666 t.Fatalf("test #%d, transaction should be accepted, "+ 667 "but was rejected: %v", i, err) 668 669 // Transaction should have been rejected, but it was accepted. 670 case !test.accept && err == nil: 671 t.Fatalf("test #%d, transaction should be rejected, "+ 672 "but was accepted", i) 673 674 // Transaction was rejected as wanted, nothing more to do. 675 case !test.accept && err != nil: 676 } 677 678 // If the transaction should be rejected, manually mine a block 679 // with the non-final transaction. It should be rejected. 680 if !test.accept { 681 txns := []*btcutil.Tx{btcutil.NewTx(test.tx)} 682 _, err := r.GenerateAndSubmitBlock(txns, -1, time.Time{}) 683 if err == nil { 684 t.Fatalf("test #%d, invalid block accepted", i) 685 } 686 687 continue 688 } 689 690 // Generate a block, the transaction should be included within 691 // the newly mined block. 692 blockHashes, err := r.Client.Generate(1) 693 if err != nil { 694 t.Fatalf("unable to mine block: %v", err) 695 } 696 assertTxInBlock(r, t, blockHashes[0], txid) 697 } 698 }