123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695 |
- // Copyright (c) 2016 The btcsuite developers
- // Use of this source code is governed by an ISC
- // license that can be found in the LICENSE file.
- // This file is ignored during the regular tests due to the following build tag.
- // +build rpctest
- package integration
- import (
- "bytes"
- "runtime"
- "strings"
- "testing"
- "time"
- "github.com/pkt-cash/pktd/btcutil/er"
- "github.com/pkt-cash/pktd/txscript/opcode"
- "github.com/pkt-cash/pktd/txscript/params"
- "github.com/pkt-cash/pktd/txscript/scriptbuilder"
- "github.com/pkt-cash/pktd/wire/constants"
- "github.com/pkt-cash/pktd/blockchain"
- "github.com/pkt-cash/pktd/btcec"
- "github.com/pkt-cash/pktd/btcutil"
- "github.com/pkt-cash/pktd/chaincfg"
- "github.com/pkt-cash/pktd/chaincfg/chainhash"
- "github.com/pkt-cash/pktd/chaincfg/globalcfg"
- "github.com/pkt-cash/pktd/integration/rpctest"
- "github.com/pkt-cash/pktd/txscript"
- "github.com/pkt-cash/pktd/wire"
- )
- const (
- csvKey = "csv"
- )
- // makeTestOutput creates an on-chain output paying to a freshly generated
- // p2pkh output with the specified amount.
- func makeTestOutput(r *rpctest.Harness, t *testing.T,
- amt btcutil.Amount) (*btcec.PrivateKey, *wire.OutPoint, []byte, er.R) {
- // Create a fresh key, then send some coins to an address spendable by
- // that key.
- key, err := btcec.NewPrivateKey(btcec.S256())
- if err != nil {
- return nil, nil, nil, err
- }
- // Using the key created above, generate a pkScript which it's able to
- // spend.
- a, err := btcutil.NewAddressPubKey(key.PubKey().SerializeCompressed(), r.ActiveNet)
- if err != nil {
- return nil, nil, nil, err
- }
- selfAddrScript, err := txscript.PayToAddrScript(a.AddressPubKeyHash())
- if err != nil {
- return nil, nil, nil, err
- }
- output := &wire.TxOut{PkScript: selfAddrScript, Value: 1e8}
- // Next, create and broadcast a transaction paying to the output.
- fundTx, err := r.CreateTransaction([]*wire.TxOut{output}, 10, true)
- if err != nil {
- return nil, nil, nil, err
- }
- txHash, err := r.Node.SendRawTransaction(fundTx, true)
- if err != nil {
- return nil, nil, nil, err
- }
- // The transaction created above should be included within the next
- // generated block.
- blockHash, err := r.Node.Generate(1)
- if err != nil {
- return nil, nil, nil, err
- }
- assertTxInBlock(r, t, blockHash[0], txHash)
- // Locate the output index of the coins spendable by the key we
- // generated above, this is needed in order to create a proper utxo for
- // this output.
- var outputIndex uint32
- if bytes.Equal(fundTx.TxOut[0].PkScript, selfAddrScript) {
- outputIndex = 0
- } else {
- outputIndex = 1
- }
- utxo := &wire.OutPoint{
- Hash: fundTx.TxHash(),
- Index: outputIndex,
- }
- return key, utxo, selfAddrScript, nil
- }
- // TestBIP0113Activation tests for proper adherence of the BIP 113 rule
- // constraint which requires all transaction finality tests to use the MTP of
- // the last 11 blocks, rather than the timestamp of the block which includes
- // them.
- //
- // Overview:
- // - Pre soft-fork:
- // - Transactions with non-final lock-times from the PoV of MTP should be
- // rejected from the mempool.
- // - Transactions within non-final MTP based lock-times should be accepted
- // in valid blocks.
- //
- // - Post soft-fork:
- // - Transactions with non-final lock-times from the PoV of MTP should be
- // rejected from the mempool and when found within otherwise valid blocks.
- // - Transactions with final lock-times from the PoV of MTP should be
- // accepted to the mempool and mined in future block.
- func TestBIP0113Activation(t *testing.T) {
- pktdCfg := []string{"--rejectnonstd"}
- r, err := rpctest.New(&chaincfg.SimNetParams, nil, pktdCfg)
- if err != nil {
- t.Fatal("unable to create primary harness: ", err)
- }
- if err := r.SetUp(true, 1); err != nil {
- t.Fatalf("unable to setup test chain: %v", err)
- }
- defer r.TearDown()
- // Create a fresh output for usage within the test below.
- const outputValue = globalcfg.SatoshiPerBitcoin()
- outputKey, testOutput, testPkScript, err := makeTestOutput(r, t,
- outputValue)
- if err != nil {
- t.Fatalf("unable to create test output: %v", err)
- }
- // Fetch a fresh address from the harness, we'll use this address to
- // send funds back into the Harness.
- addr, err := r.NewAddress()
- if err != nil {
- t.Fatalf("unable to generate address: %v", err)
- }
- addrScript, err := txscript.PayToAddrScript(addr)
- if err != nil {
- t.Fatalf("unable to generate addr script: %v", err)
- }
- // Now create a transaction with a lock time which is "final" according
- // to the latest block, but not according to the current median time
- // past.
- tx := wire.NewMsgTx(1)
- tx.AddTxIn(&wire.TxIn{
- PreviousOutPoint: *testOutput,
- })
- tx.AddTxOut(&wire.TxOut{
- PkScript: addrScript,
- Value: outputValue - 1000,
- })
- // We set the lock-time of the transaction to just one minute after the
- // current MTP of the chain.
- chainInfo, err := r.Node.GetBlockChainInfo()
- if err != nil {
- t.Fatalf("unable to query for chain info: %v", err)
- }
- tx.LockTime = uint32(chainInfo.MedianTime) + 1
- sigScript, err := txscript.SignatureScript(tx, 0, testPkScript,
- params.SigHashAll, outputKey, true)
- if err != nil {
- t.Fatalf("unable to generate sig: %v", err)
- }
- tx.TxIn[0].SignatureScript = sigScript
- // This transaction should be rejected from the mempool as using MTP
- // for transactions finality is now a policy rule. Additionally, the
- // exact error should be the rejection of a non-final transaction.
- _, err = r.Node.SendRawTransaction(tx, true)
- if err == nil {
- t.Fatalf("transaction accepted, but should be non-final")
- } else if !strings.Contains(err.Error(), "not finalized") {
- t.Fatalf("transaction should be rejected due to being "+
- "non-final, instead: %v", err)
- }
- // However, since the block validation consensus rules haven't yet
- // activated, a block including the transaction should be accepted.
- txns := []*btcutil.Tx{btcutil.NewTx(tx)}
- block, err := r.GenerateAndSubmitBlock(txns, -1, time.Time{})
- if err != nil {
- t.Fatalf("unable to submit block: %v", err)
- }
- txid := tx.TxHash()
- assertTxInBlock(r, t, block.Hash(), &txid)
- // At this point, the block height should be 103: we mined 101 blocks
- // to create a single mature output, then an additional block to create
- // a new output, and then mined a single block above to include our
- // transaction.
- assertChainHeight(r, t, 103)
- // Next, mine enough blocks to ensure that the soft-fork becomes
- // activated. Assert that the block version of the second-to-last block
- // in the final range is active.
- // Next, mine ensure blocks to ensure that the soft-fork becomes
- // active. We're at height 103 and we need 200 blocks to be mined after
- // the genesis target period, so we mine 196 blocks. This'll put us at
- // height 299. The getblockchaininfo call checks the state for the
- // block AFTER the current height.
- numBlocks := (r.ActiveNet.MinerConfirmationWindow * 2) - 4
- if _, err := r.Node.Generate(numBlocks); err != nil {
- t.Fatalf("unable to generate blocks: %v", err)
- }
- assertChainHeight(r, t, 299)
- assertSoftForkStatus(r, t, csvKey, blockchain.ThresholdActive)
- // The timeLockDeltas slice represents a series of deviations from the
- // current MTP which will be used to test border conditions w.r.t
- // transaction finality. -1 indicates 1 second prior to the MTP, 0
- // indicates the current MTP, and 1 indicates 1 second after the
- // current MTP.
- //
- // This time, all transactions which are final according to the MTP
- // *should* be accepted to both the mempool and within a valid block.
- // While transactions with lock-times *after* the current MTP should be
- // rejected.
- timeLockDeltas := []int64{-1, 0, 1}
- for _, timeLockDelta := range timeLockDeltas {
- chainInfo, err = r.Node.GetBlockChainInfo()
- if err != nil {
- t.Fatalf("unable to query for chain info: %v", err)
- }
- medianTimePast := chainInfo.MedianTime
- // Create another test output to be spent shortly below.
- outputKey, testOutput, testPkScript, err = makeTestOutput(r, t,
- outputValue)
- if err != nil {
- t.Fatalf("unable to create test output: %v", err)
- }
- // Create a new transaction with a lock-time past the current known
- // MTP.
- tx = wire.NewMsgTx(1)
- tx.AddTxIn(&wire.TxIn{
- PreviousOutPoint: *testOutput,
- })
- tx.AddTxOut(&wire.TxOut{
- PkScript: addrScript,
- Value: outputValue - 1000,
- })
- tx.LockTime = uint32(medianTimePast + timeLockDelta)
- sigScript, err = txscript.SignatureScript(tx, 0, testPkScript,
- params.SigHashAll, outputKey, true)
- if err != nil {
- t.Fatalf("unable to generate sig: %v", err)
- }
- tx.TxIn[0].SignatureScript = sigScript
- // If the time-lock delta is greater than -1, then the
- // transaction should be rejected from the mempool and when
- // included within a block. A time-lock delta of -1 should be
- // accepted as it has a lock-time of one
- // second _before_ the current MTP.
- _, err = r.Node.SendRawTransaction(tx, true)
- if err == nil && timeLockDelta >= 0 {
- t.Fatal("transaction was accepted into the mempool " +
- "but should be rejected!")
- } else if err != nil && !strings.Contains(err.Error(), "not finalized") {
- t.Fatalf("transaction should be rejected from mempool "+
- "due to being non-final, instead: %v", err)
- }
- txns = []*btcutil.Tx{btcutil.NewTx(tx)}
- _, err := r.GenerateAndSubmitBlock(txns, -1, time.Time{})
- if err == nil && timeLockDelta >= 0 {
- t.Fatal("block should be rejected due to non-final " +
- "txn, but was accepted")
- } else if err != nil && !strings.Contains(err.Error(), "unfinalized") {
- t.Fatalf("block should be rejected due to non-final "+
- "tx, instead: %v", err)
- }
- }
- }
- // createCSVOutput creates an output paying to a trivially redeemable CSV
- // pkScript with the specified time-lock.
- func createCSVOutput(r *rpctest.Harness, t *testing.T,
- numSatoshis btcutil.Amount, timeLock int32,
- isSeconds bool) ([]byte, *wire.OutPoint, *wire.MsgTx, er.R) {
- // Convert the time-lock to the proper sequence lock based according to
- // if the lock is seconds or time based.
- sequenceLock := blockchain.LockTimeToSequence(isSeconds,
- uint32(timeLock))
- // Our CSV script is simply: <sequenceLock> OP_CSV OP_DROP
- b := scriptbuilder.NewScriptBuilder().
- AddInt64(int64(sequenceLock)).
- AddOp(opcode.OP_CHECKSEQUENCEVERIFY).
- AddOp(opcode.OP_DROP)
- csvScript, err := b.Script()
- if err != nil {
- return nil, nil, nil, err
- }
- // Using the script generated above, create a P2SH output which will be
- // accepted into the mempool.
- p2shAddr, err := btcutil.NewAddressScriptHash(csvScript, r.ActiveNet)
- if err != nil {
- return nil, nil, nil, err
- }
- p2shScript, err := txscript.PayToAddrScript(p2shAddr)
- if err != nil {
- return nil, nil, nil, err
- }
- output := &wire.TxOut{
- PkScript: p2shScript,
- Value: int64(numSatoshis),
- }
- // Finally create a valid transaction which creates the output crafted
- // above.
- tx, err := r.CreateTransaction([]*wire.TxOut{output}, 10, true)
- if err != nil {
- return nil, nil, nil, err
- }
- var outputIndex uint32
- if !bytes.Equal(tx.TxOut[0].PkScript, p2shScript) {
- outputIndex = 1
- }
- utxo := &wire.OutPoint{
- Hash: tx.TxHash(),
- Index: outputIndex,
- }
- return csvScript, utxo, tx, nil
- }
- // spendCSVOutput spends an output previously created by the createCSVOutput
- // function. The sigScript is a trivial push of OP_TRUE followed by the
- // redeemScript to pass P2SH evaluation.
- func spendCSVOutput(redeemScript []byte, csvUTXO *wire.OutPoint,
- sequence uint32, targetOutput *wire.TxOut,
- txVersion int32) (*wire.MsgTx, er.R) {
- tx := wire.NewMsgTx(txVersion)
- tx.AddTxIn(&wire.TxIn{
- PreviousOutPoint: *csvUTXO,
- Sequence: sequence,
- })
- tx.AddTxOut(targetOutput)
- b := scriptbuilder.NewScriptBuilder().
- AddOp(opcode.OP_TRUE).
- AddData(redeemScript)
- sigScript, err := b.Script()
- if err != nil {
- return nil, err
- }
- tx.TxIn[0].SignatureScript = sigScript
- return tx, nil
- }
- // assertTxInBlock asserts a transaction with the specified txid is found
- // within the block with the passed block hash.
- func assertTxInBlock(r *rpctest.Harness, t *testing.T, blockHash *chainhash.Hash,
- txid *chainhash.Hash) {
- block, err := r.Node.GetBlock(blockHash)
- if err != nil {
- t.Fatalf("unable to get block: %v", err)
- }
- if len(block.Transactions) < 2 {
- t.Fatal("target transaction was not mined")
- }
- for _, txn := range block.Transactions {
- txHash := txn.TxHash()
- if txn.TxHash() == txHash {
- return
- }
- }
- _, _, line, _ := runtime.Caller(1)
- t.Fatalf("assertion failed at line %v: txid %v was not found in "+
- "block %v", line, txid, blockHash)
- }
- // TestBIP0068AndBIP0112Activation tests for the proper adherence to the BIP
- // 112 and BIP 68 rule-set after the activation of the CSV-package soft-fork.
- //
- // Overview:
- // - Pre soft-fork:
- // - A transaction spending a CSV output validly should be rejected from the
- // mempool, but accepted in a valid generated block including the
- // transaction.
- // - Post soft-fork:
- // - See the cases exercised within the table driven tests towards the end
- // of this test.
- func TestBIP0068AndBIP0112Activation(t *testing.T) {
- // We'd like the test proper evaluation and validation of the BIP 68
- // (sequence locks) and BIP 112 rule-sets which add input-age based
- // relative lock times.
- pktdCfg := []string{"--rejectnonstd"}
- r, err := rpctest.New(&chaincfg.SimNetParams, nil, pktdCfg)
- if err != nil {
- t.Fatal("unable to create primary harness: ", err)
- }
- if err := r.SetUp(true, 1); err != nil {
- t.Fatalf("unable to setup test chain: %v", err)
- }
- defer r.TearDown()
- assertSoftForkStatus(r, t, csvKey, blockchain.ThresholdStarted)
- harnessAddr, err := r.NewAddress()
- if err != nil {
- t.Fatalf("unable to obtain harness address: %v", err)
- }
- harnessScript, err := txscript.PayToAddrScript(harnessAddr)
- if err != nil {
- t.Fatalf("unable to generate pkScript: %v", err)
- }
- const (
- outputAmt = globalcfg.SatoshiPerBitcoin()
- relativeBlockLock = 10
- )
- sweepOutput := &wire.TxOut{
- Value: outputAmt - 5000,
- PkScript: harnessScript,
- }
- // As the soft-fork hasn't yet activated _any_ transaction version
- // which uses the CSV opcode should be accepted. Since at this point,
- // CSV doesn't actually exist, it's just a NOP.
- for txVersion := int32(0); txVersion < 3; txVersion++ {
- // Create a trivially spendable output with a CSV lock-time of
- // 10 relative blocks.
- redeemScript, testUTXO, tx, err := createCSVOutput(r, t, outputAmt,
- relativeBlockLock, false)
- if err != nil {
- t.Fatalf("unable to create CSV encumbered output: %v", err)
- }
- // As the transaction is p2sh it should be accepted into the
- // mempool and found within the next generated block.
- if _, err := r.Node.SendRawTransaction(tx, true); err != nil {
- t.Fatalf("unable to broadcast tx: %v", err)
- }
- blocks, err := r.Node.Generate(1)
- if err != nil {
- t.Fatalf("unable to generate blocks: %v", err)
- }
- txid := tx.TxHash()
- assertTxInBlock(r, t, blocks[0], &txid)
- // Generate a custom transaction which spends the CSV output.
- sequenceNum := blockchain.LockTimeToSequence(false, 10)
- spendingTx, err := spendCSVOutput(redeemScript, testUTXO,
- sequenceNum, sweepOutput, txVersion)
- if err != nil {
- t.Fatalf("unable to spend csv output: %v", err)
- }
- // This transaction should be rejected from the mempool since
- // CSV validation is already mempool policy pre-fork.
- _, err = r.Node.SendRawTransaction(spendingTx, true)
- if err == nil {
- t.Fatalf("transaction should have been rejected, but was " +
- "instead accepted")
- }
- // However, this transaction should be accepted in a custom
- // generated block as CSV validation for scripts within blocks
- // shouldn't yet be active.
- txns := []*btcutil.Tx{btcutil.NewTx(spendingTx)}
- block, err := r.GenerateAndSubmitBlock(txns, -1, time.Time{})
- if err != nil {
- t.Fatalf("unable to submit block: %v", err)
- }
- txid = spendingTx.TxHash()
- assertTxInBlock(r, t, block.Hash(), &txid)
- }
- // At this point, the block height should be 107: we started at height
- // 101, then generated 2 blocks in each loop iteration above.
- assertChainHeight(r, t, 107)
- // With the height at 107 we need 200 blocks to be mined after the
- // genesis target period, so we mine 192 blocks. This'll put us at
- // height 299. The getblockchaininfo call checks the state for the
- // block AFTER the current height.
- numBlocks := (r.ActiveNet.MinerConfirmationWindow * 2) - 8
- if _, err := r.Node.Generate(numBlocks); err != nil {
- t.Fatalf("unable to generate blocks: %v", err)
- }
- assertChainHeight(r, t, 299)
- assertSoftForkStatus(r, t, csvKey, blockchain.ThresholdActive)
- // Knowing the number of outputs needed for the tests below, create a
- // fresh output for use within each of the test-cases below.
- const relativeTimeLock = 512
- const numTests = 8
- type csvOutput struct {
- RedeemScript []byte
- Utxo *wire.OutPoint
- Timelock int32
- }
- var spendableInputs [numTests]csvOutput
- // Create three outputs which have a block-based sequence locks, and
- // three outputs which use the above time based sequence lock.
- for i := 0; i < numTests; i++ {
- timeLock := relativeTimeLock
- isSeconds := true
- if i < 7 {
- timeLock = relativeBlockLock
- isSeconds = false
- }
- redeemScript, utxo, tx, err := createCSVOutput(r, t, outputAmt,
- int32(timeLock), isSeconds)
- if err != nil {
- t.Fatalf("unable to create CSV output: %v", err)
- }
- if _, err := r.Node.SendRawTransaction(tx, true); err != nil {
- t.Fatalf("unable to broadcast transaction: %v", err)
- }
- spendableInputs[i] = csvOutput{
- RedeemScript: redeemScript,
- Utxo: utxo,
- Timelock: int32(timeLock),
- }
- }
- // Mine a single block including all the transactions generated above.
- if _, err := r.Node.Generate(1); err != nil {
- t.Fatalf("unable to generate block: %v", err)
- }
- // Now mine 10 additional blocks giving the inputs generated above a
- // age of 11. Space out each block 10 minutes after the previous block.
- prevBlockHash, err := r.Node.GetBestBlockHash()
- if err != nil {
- t.Fatalf("unable to get prior block hash: %v", err)
- }
- prevBlock, err := r.Node.GetBlock(prevBlockHash)
- if err != nil {
- t.Fatalf("unable to get block: %v", err)
- }
- for i := 0; i < relativeBlockLock; i++ {
- timeStamp := prevBlock.Header.Timestamp.Add(time.Minute * 10)
- b, err := r.GenerateAndSubmitBlock(nil, -1, timeStamp)
- if err != nil {
- t.Fatalf("unable to generate block: %v", err)
- }
- prevBlock = b.MsgBlock()
- }
- // A helper function to create fully signed transactions in-line during
- // the array initialization below.
- var inputIndex uint32
- makeTxCase := func(sequenceNum uint32, txVersion int32) *wire.MsgTx {
- csvInput := spendableInputs[inputIndex]
- tx, err := spendCSVOutput(csvInput.RedeemScript, csvInput.Utxo,
- sequenceNum, sweepOutput, txVersion)
- if err != nil {
- t.Fatalf("unable to spend CSV output: %v", err)
- }
- inputIndex++
- return tx
- }
- tests := [numTests]struct {
- tx *wire.MsgTx
- accept bool
- }{
- // A valid transaction with a single input a sequence number
- // creating a 100 block relative time-lock. This transaction
- // should be rejected as its version number is 1, and only tx
- // of version > 2 will trigger the CSV behavior.
- {
- tx: makeTxCase(blockchain.LockTimeToSequence(false, 100), 1),
- accept: false,
- },
- // A transaction of version 2 spending a single input. The
- // input has a relative time-lock of 1 block, but the disable
- // bit it set. The transaction should be rejected as a result.
- {
- tx: makeTxCase(
- blockchain.LockTimeToSequence(false, 1)|constants.SequenceLockTimeDisabled,
- 2,
- ),
- accept: false,
- },
- // A v2 transaction with a single input having a 9 block
- // relative time lock. The referenced input is 11 blocks old,
- // but the CSV output requires a 10 block relative lock-time.
- // Therefore, the transaction should be rejected.
- {
- tx: makeTxCase(blockchain.LockTimeToSequence(false, 9), 2),
- accept: false,
- },
- // A v2 transaction with a single input having a 10 block
- // relative time lock. The referenced input is 11 blocks old so
- // the transaction should be accepted.
- {
- tx: makeTxCase(blockchain.LockTimeToSequence(false, 10), 2),
- accept: true,
- },
- // A v2 transaction with a single input having a 11 block
- // relative time lock. The input referenced has an input age of
- // 11 and the CSV op-code requires 10 blocks to have passed, so
- // this transaction should be accepted.
- {
- tx: makeTxCase(blockchain.LockTimeToSequence(false, 11), 2),
- accept: true,
- },
- // A v2 transaction whose input has a 1000 blck relative time
- // lock. This should be rejected as the input's age is only 11
- // blocks.
- {
- tx: makeTxCase(blockchain.LockTimeToSequence(false, 1000), 2),
- accept: false,
- },
- // A v2 transaction with a single input having a 512,000 second
- // relative time-lock. This transaction should be rejected as 6
- // days worth of blocks haven't yet been mined. The referenced
- // input doesn't have sufficient age.
- {
- tx: makeTxCase(blockchain.LockTimeToSequence(true, 512000), 2),
- accept: false,
- },
- // A v2 transaction whose single input has a 512 second
- // relative time-lock. This transaction should be accepted as
- // finalized.
- {
- tx: makeTxCase(blockchain.LockTimeToSequence(true, 512), 2),
- accept: true,
- },
- }
- for i, test := range tests {
- txid, err := r.Node.SendRawTransaction(test.tx, true)
- switch {
- // Test case passes, nothing further to report.
- case test.accept && err == nil:
- // Transaction should have been accepted but we have a non-nil
- // error.
- case test.accept && err != nil:
- t.Fatalf("test #%d, transaction should be accepted, "+
- "but was rejected: %v", i, err)
- // Transaction should have been rejected, but it was accepted.
- case !test.accept && err == nil:
- t.Fatalf("test #%d, transaction should be rejected, "+
- "but was accepted", i)
- // Transaction was rejected as wanted, nothing more to do.
- case !test.accept && err != nil:
- }
- // If the transaction should be rejected, manually mine a block
- // with the non-final transaction. It should be rejected.
- if !test.accept {
- txns := []*btcutil.Tx{btcutil.NewTx(test.tx)}
- _, err := r.GenerateAndSubmitBlock(txns, -1, time.Time{})
- if err == nil {
- t.Fatalf("test #%d, invalid block accepted", i)
- }
- continue
- }
- // Generate a block, the transaction should be included within
- // the newly mined block.
- blockHashes, err := r.Node.Generate(1)
- if err != nil {
- t.Fatalf("unable to mine block: %v", err)
- }
- assertTxInBlock(r, t, blockHashes[0], txid)
- }
- }
|