func TestBIP0068AndBIP0112Activation(t *testing.T) {
	t.Parallel()
	soterdCfg := []string{"--rejectnonstd"}
	r, err := rpctest.New(&chaincfg.SimNetParams, nil, soterdCfg, false)
	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, blockdag.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         = soterutil.NanoSoterPerSoter
		relativeBlockLock = 10
	)
	sweepOutput := &wire.TxOut{
		Value:    outputAmt - 5000,
		PkScript: harnessScript,
	}
	for txVersion := int32(0); txVersion < 3; txVersion++ {
		redeemScript, testUTXO, tx, err := createCSVOutput(r, t, outputAmt,
			relativeBlockLock, false)
		if err != nil {
			t.Fatalf("unable to create CSV encumbered output: %v", err)
		}
		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)
		sequenceNum := blockdag.LockTimeToSequence(false, 10)
		spendingTx, err := spendCSVOutput(redeemScript, testUTXO,
			sequenceNum, sweepOutput, txVersion)
		if err != nil {
			t.Fatalf("unable to spend csv output: %v", err)
		}
		_, err = r.Node.SendRawTransaction(spendingTx, true)
		if err == nil {
			t.Fatalf("transaction should have been rejected, but was " +
				"instead accepted")
		}
		txns := []*soterutil.Tx{soterutil.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)
	}
	assertChainHeight(r, t, 107)
	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, blockdag.ThresholdActive)
	const relativeTimeLock = 512
	const numTests = 8
	type csvOutput struct {
		RedeemScript []byte
		Utxo         *wire.OutPoint
		Timelock     int32
	}
	var spendableInputs [numTests]csvOutput
	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),
		}
	}
	if _, err := r.Node.Generate(1); err != nil {
		t.Fatalf("unable to generate block: %v", err)
	}
	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()
	}
	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
	}{
		{
			tx:     makeTxCase(blockdag.LockTimeToSequence(false, 100), 1),
			accept: false,
		},
		{
			tx: makeTxCase(
				blockdag.LockTimeToSequence(false, 1)|wire.SequenceLockTimeDisabled,
				2,
			),
			accept: false,
		},
		{
			tx:     makeTxCase(blockdag.LockTimeToSequence(false, 9), 2),
			accept: false,
		},
		{
			tx:     makeTxCase(blockdag.LockTimeToSequence(false, 10), 2),
			accept: true,
		},
		{
			tx:     makeTxCase(blockdag.LockTimeToSequence(false, 11), 2),
			accept: true,
		},
		{
			tx:     makeTxCase(blockdag.LockTimeToSequence(false, 1000), 2),
			accept: false,
		},
		{
			tx:     makeTxCase(blockdag.LockTimeToSequence(true, 512000), 2),
			accept: false,
		},
		{
			tx:     makeTxCase(blockdag.LockTimeToSequence(true, 512), 2),
			accept: true,
		},
	}
	for i, test := range tests {
		txid, err := r.Node.SendRawTransaction(test.tx, true)
		switch {
		case test.accept && err == nil:
		case test.accept && err != nil:
			t.Fatalf("test #%d, transaction should be accepted, "+
				"but was rejected: %v", i, err)
		case !test.accept && err == nil:
			t.Fatalf("test #%d, transaction should be rejected, "+
				"but was accepted", i)
		case !test.accept && err != nil:
		}
		if !test.accept {
			txns := []*soterutil.Tx{soterutil.NewTx(test.tx)}
			_, err := r.GenerateAndSubmitBlock(txns, -1, time.Time{})
			if err == nil {
				t.Fatalf("test #%d, invalid block accepted", i)
			}
			continue
		}
		blockHashes, err := r.Node.Generate(1)
		if err != nil {
			t.Fatalf("unable to mine block: %v", err)
		}
		assertTxInBlock(r, t, blockHashes[0], txid)
	}
}
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.
package integration
import (
	"bytes"
	"runtime"
	"strings"
	"testing"
	"time"
	"github.com/soteria-dag/soterd/blockdag"
	"github.com/soteria-dag/soterd/chaincfg"
	"github.com/soteria-dag/soterd/chaincfg/chainhash"
	"github.com/soteria-dag/soterd/integration/rpctest"
	"github.com/soteria-dag/soterd/soterec"
	"github.com/soteria-dag/soterd/soterutil"
	"github.com/soteria-dag/soterd/txscript"
	"github.com/soteria-dag/soterd/wire"
)
const (
	csvKey = "csv"
)
func makeTestOutput(r *rpctest.Harness, t *testing.T,
	amt soterutil.Amount) (*soterec.PrivateKey, *wire.OutPoint, []byte, error) {
	key, err := soterec.NewPrivateKey(soterec.S256())
	if err != nil {
		return nil, nil, nil, err
	}
	a, err := soterutil.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: soterutil.NanoSoterPerSoter}
	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
	}
	blockHash, err := r.Node.Generate(1)
	if err != nil {
		return nil, nil, nil, err
	}
	assertTxInBlock(r, t, blockHash[0], txHash)
	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
}
func TestBIP0113Activation(t *testing.T) {
	t.Parallel()
	soterdCfg := []string{"--rejectnonstd"}
	r, err := rpctest.New(&chaincfg.SimNetParams, nil, soterdCfg, false)
	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()
	const outputValue = soterutil.NanoSoterPerSoter
	outputKey, testOutput, testPkScript, err := makeTestOutput(r, t,
		outputValue)
	if err != nil {
		t.Fatalf("unable to create test output: %v", err)
	}
	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)
	}
	tx := wire.NewMsgTx(1)
	tx.AddTxIn(&wire.TxIn{
		PreviousOutPoint: *testOutput,
	})
	tx.AddTxOut(&wire.TxOut{
		PkScript: addrScript,
		Value:    outputValue - 1000,
	})
	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,
		txscript.SigHashAll, outputKey, true)
	if err != nil {
		t.Fatalf("unable to generate sig: %v", err)
	}
	tx.TxIn[0].SignatureScript = sigScript
	_, 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)
	}
	txns := []*soterutil.Tx{soterutil.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)
	assertChainHeight(r, t, 103)
	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, blockdag.ThresholdActive)
	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
		outputKey, testOutput, testPkScript, err = makeTestOutput(r, t,
			outputValue)
		if err != nil {
			t.Fatalf("unable to create test output: %v", err)
		}
		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,
			txscript.SigHashAll, outputKey, true)
		if err != nil {
			t.Fatalf("unable to generate sig: %v", err)
		}
		tx.TxIn[0].SignatureScript = sigScript
		_, 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 = []*soterutil.Tx{soterutil.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)
		}
	}
}
func createCSVOutput(r *rpctest.Harness, t *testing.T,
	numNanoSoters soterutil.Amount, timeLock int32,
	isSeconds bool) ([]byte, *wire.OutPoint, *wire.MsgTx, error) {
	sequenceLock := blockdag.LockTimeToSequence(isSeconds,
		uint32(timeLock))
	b := txscript.NewScriptBuilder().
		AddInt64(int64(sequenceLock)).
		AddOp(txscript.OP_CHECKSEQUENCEVERIFY).
		AddOp(txscript.OP_DROP)
	csvScript, err := b.Script()
	if err != nil {
		return nil, nil, nil, err
	}
	p2shAddr, err := soterutil.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(numNanoSoters),
	}
	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
}
func spendCSVOutput(redeemScript []byte, csvUTXO *wire.OutPoint,
	sequence uint32, targetOutput *wire.TxOut,
	txVersion int32) (*wire.MsgTx, error) {
	tx := wire.NewMsgTx(txVersion)
	tx.AddTxIn(&wire.TxIn{
		PreviousOutPoint: *csvUTXO,
		Sequence:         sequence,
	})
	tx.AddTxOut(targetOutput)
	b := txscript.NewScriptBuilder().
		AddOp(txscript.OP_TRUE).
		AddData(redeemScript)
	sigScript, err := b.Script()
	if err != nil {
		return nil, err
	}
	tx.TxIn[0].SignatureScript = sigScript
	return tx, nil
}
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)
}