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