Example #1
0
// TestLightningNetworkDaemon performs a series of integration tests amongst a
// programmatically driven network of lnd nodes.
func TestLightningNetworkDaemon(t *testing.T) {
	ht := newHarnessTest(t)

	// First create the network harness to gain access to its
	// 'OnTxAccepted' call back.
	lndHarness, err := newNetworkHarness()
	if err != nil {
		ht.Fatalf("unable to create lightning network harness: %v", err)
	}
	defer lndHarness.TearDownAll()

	handlers := &btcrpcclient.NotificationHandlers{
		OnTxAccepted: lndHarness.OnTxAccepted,
	}

	// First create an instance of the btcd's rpctest.Harness. This will be
	// used to fund the wallets of the nodes within the test network and to
	// drive blockchain related events within the network.
	btcdHarness, err := rpctest.New(harnessNetParams, handlers, nil)
	if err != nil {
		ht.Fatalf("unable to create mining node: %v", err)
	}
	defer btcdHarness.TearDown()
	if err := btcdHarness.SetUp(true, 50); err != nil {
		ht.Fatalf("unable to set up mining node: %v", err)
	}
	if err := btcdHarness.Node.NotifyNewTransactions(false); err != nil {
		ht.Fatalf("unable to request transaction notifications: %v", err)
	}

	// With the btcd harness created, we can now complete the
	// initialization of the network. args - list of lnd arguments,
	// example: "--debuglevel=debug"
	// TODO(roasbeef): create master balanced channel with all the monies?
	if err := lndHarness.InitializeSeedNodes(btcdHarness, nil); err != nil {
		ht.Fatalf("unable to initialize seed nodes: %v", err)
	}
	if err = lndHarness.SetUp(); err != nil {
		ht.Fatalf("unable to set up test lightning network: %v", err)
	}

	// Spawn a new goroutine to watch for any fatal errors that any of the
	// running lnd processes encounter. If an error occurs, then the test
	// fails immediately with a fatal error, as far as fatal is happening
	// inside goroutine main goroutine would not be finished at the same
	// time as we receive fatal error from lnd process.
	go func() {
		err := <-lndHarness.ProcessErrors()
		ht.Fatalf("lnd finished with error (stderr): "+
			"\n%v", err)
	}()

	t.Logf("Running %v integration tests", len(testsCases))
	for _, testCase := range testsCases {
		ht.RunTestCase(testCase, lndHarness)
	}
}
Example #2
0
// TestInterfaces tests all registered interfaces with a unified set of tests
// which excersie each of the required methods found within the ChainNotifier
// interface.
//
// NOTE: In the future, when additional implementations of the ChainNotifier
// interface have been implemented, in order to ensure the new concrete
// implementation is automatically tested, two steps must be undertaken. First,
// one needs add a "non-captured" (_) import from the new sub-package. This
// import should trigger an init() method within the package which registeres
// the interface. Second, an additional case in the switch within the main loop
// below needs to be added which properly initializes the interface.
func TestInterfaces(t *testing.T) {
	// Initialize the harness around a btcd node which will serve as our
	// dedicated miner to generate blocks, cause re-orgs, etc. We'll set up
	// this node with a chain length of 125, so we have plentyyy of BTC to
	// play around with.
	miner, err := rpctest.New(netParams, nil, nil)
	if err != nil {
		t.Fatalf("unable to create mining node: %v", err)
	}
	defer miner.TearDown()
	if err := miner.SetUp(true, 25); err != nil {
		t.Fatalf("unable to set up mining node: %v", err)
	}

	rpcConfig := miner.RPCConfig()

	log.Printf("Running %v ChainNotifier interface tests\n", len(ntfnTests))
	var notifier chainntnfs.ChainNotifier
	for _, notifierDriver := range chainntnfs.RegisteredNotifiers() {
		notifierType := notifierDriver.NotifierType

		switch notifierType {
		case "btcd":
			notifier, err = notifierDriver.New(&rpcConfig)
			if err != nil {
				t.Fatalf("unable to create %v notifier: %v",
					notifierType, err)
			}
		}

		if err := notifier.Start(); err != nil {
			t.Fatalf("unable to start notifier %v: %v",
				notifierType, err)
		}

		for _, ntfnTest := range ntfnTests {
			ntfnTest(miner, notifier, t)
		}

		notifier.Stop()
	}
}
Example #3
0
func TestLightningWallet(t *testing.T) {
	// TODO(roasbeef): switch to testnetL later
	netParams := &chaincfg.SimNetParams

	// Initialize the harness around a btcd node which will serve as our
	// dedicated miner to generate blocks, cause re-orgs, etc. We'll set
	// up this node with a chain length of 125, so we have plentyyy of BTC
	// to play around with.
	miningNode, err := rpctest.New(netParams, nil, nil)
	defer miningNode.TearDown()
	if err != nil {
		t.Fatalf("unable to create mining node: %v", err)
	}
	if err := miningNode.SetUp(true, 25); err != nil {
		t.Fatalf("unable to set up mining node: %v", err)
	}

	// Funding via 5 outputs with 4BTC each.
	testDir, lnwallet, err := createTestWallet(miningNode, netParams)
	if err != nil {
		t.Fatalf("unable to create test ln wallet: %v", err)
	}
	defer os.RemoveAll(testDir)
	defer lnwallet.Shutdown()

	// The wallet should now have 20BTC available for spending.
	assertProperBalance(t, lnwallet, 1, 20)

	// Execute every test, clearing possibly mutated wallet state after
	// each step.
	for _, walletTest := range walletTests {
		walletTest(miningNode, lnwallet, t)

		if err := clearWalletState(lnwallet); err != nil && err != walletdb.ErrBucketNotFound {
			t.Fatalf("unable to clear wallet state: %v", err)
		}

		// TODO(roasbeef): possible reset mining node's chainstate to
		// initial level
	}
}
Example #4
0
func TestMain(m *testing.M) {
	var err error

	// In order to properly test scenarios on as if we were on mainnet,
	// ensure that non-standard transactions aren't accepted into the
	// mempool or relayed.
	btcdCfg := []string{"--rejectnonstd"}
	primaryHarness, err = rpctest.New(&chaincfg.SimNetParams, nil, btcdCfg)
	if err != nil {
		fmt.Println("unable to create primary harness: ", err)
		os.Exit(1)
	}

	// Initialize the primary mining node with a chain of length 125,
	// providing 25 mature coinbases to allow spending from for testing
	// purposes.
	if err := primaryHarness.SetUp(true, 25); err != nil {
		fmt.Println("unable to setup test chain: ", err)

		// Even though the harness was not fully setup, it still needs
		// to be torn down to ensure all resources such as temp
		// directories are cleaned up.  The error is intentionally
		// ignored since this is already an error path and nothing else
		// could be done about it anyways.
		_ = primaryHarness.TearDown()
		os.Exit(1)
	}

	exitCode := m.Run()

	// Clean up any active harnesses that are still currently running.This
	// includes removing all temporary directories, and shutting down any
	// created processes.
	if err := rpctest.TearDownAll(); err != nil {
		fmt.Println("unable to tear down all harnesses: ", err)
		os.Exit(1)
	}

	os.Exit(exitCode)
}
Example #5
0
// TestInterfaces tests all registered interfaces with a unified set of tests
// which excersie each of the required methods found within the WalletController
// interface.
//
// NOTE: In the future, when additional implementations of the WalletController
// interface have been implemented, in order to ensure the new concrete
// implementation is automatically tested, two steps must be undertaken. First,
// one needs add a "non-captured" (_) import from the new sub-package. This
// import should trigger an init() method within the package which registeres
// the interface. Second, an additional case in the switch within the main loop
// below needs to be added which properly initializes the interface.
//
// TODO(roasbeef): purge bobNode in favor of dual lnwallet's
func TestLightningWallet(t *testing.T) {
	netParams := &chaincfg.SimNetParams

	// Initialize the harness around a btcd node which will serve as our
	// dedicated miner to generate blocks, cause re-orgs, etc. We'll set
	// up this node with a chain length of 125, so we have plentyyy of BTC
	// to play around with.
	miningNode, err := rpctest.New(netParams, nil, nil)
	defer miningNode.TearDown()
	if err != nil {
		t.Fatalf("unable to create mining node: %v", err)
	}
	if err := miningNode.SetUp(true, 25); err != nil {
		t.Fatalf("unable to set up mining node: %v", err)
	}

	rpcConfig := miningNode.RPCConfig()

	chainNotifier, err := btcdnotify.New(&rpcConfig)
	if err != nil {
		t.Fatalf("unable to create notifier: %v", err)
	}
	if err := chainNotifier.Start(); err != nil {
		t.Fatalf("unable to start notifier: %v", err)
	}

	var bio lnwallet.BlockChainIO
	var signer lnwallet.Signer
	var wc lnwallet.WalletController
	for _, walletDriver := range lnwallet.RegisteredWallets() {
		tempTestDir, err := ioutil.TempDir("", "lnwallet")
		if err != nil {
			t.Fatalf("unable to create temp directory: %v", err)
		}
		defer os.RemoveAll(tempTestDir)

		walletType := walletDriver.WalletType
		switch walletType {
		case "btcwallet":
			btcwalletConfig := &btcwallet.Config{
				PrivatePass: privPass,
				HdSeed:      testHdSeed[:],
				DataDir:     tempTestDir,
				NetParams:   netParams,
				RpcHost:     rpcConfig.Host,
				RpcUser:     rpcConfig.User,
				RpcPass:     rpcConfig.Pass,
				CACert:      rpcConfig.Certificates,
			}
			wc, err = walletDriver.New(btcwalletConfig)
			if err != nil {
				t.Fatalf("unable to create btcwallet: %v", err)
			}
			signer = wc.(*btcwallet.BtcWallet)
			bio = wc.(*btcwallet.BtcWallet)
		default:
			t.Fatalf("unknown wallet driver: %v", walletType)
		}

		// Funding via 20 outputs with 4BTC each.
		lnwallet, err := createTestWallet(tempTestDir, miningNode, netParams,
			chainNotifier, wc, signer, bio)
		if err != nil {
			t.Fatalf("unable to create test ln wallet: %v", err)
		}

		// The wallet should now have 80BTC available for spending.
		assertProperBalance(t, lnwallet, 1, 80)

		// Execute every test, clearing possibly mutated wallet state after
		// each step.
		for _, walletTest := range walletTests {
			walletTest(miningNode, lnwallet, t)

			// TODO(roasbeef): possible reset mining node's chainstate to
			// initial level, cleanly wipe buckets
			if err := clearWalletState(lnwallet); err != nil &&
				err != bolt.ErrBucketNotFound {
				t.Fatalf("unable to wipe wallet state: %v", err)
			}
		}

		lnwallet.Shutdown()
	}
}
Example #6
0
// TestBIP0009Mining ensures blocks built via btcd's CPU miner follow the rules
// set forth by BIP0009 by using the test dummy deployment.
//
// Overview:
// - Generate block 1
//   - Assert bit is NOT set (ThresholdDefined)
// - Generate enough blocks to reach first state transition
//   - Assert bit is NOT set for block prior to state transition
//   - Assert bit is set for block at state transition (ThresholdStarted)
// - Generate enough blocks to reach second state transition
//   - Assert bit is set for block at state transition (ThresholdLockedIn)
// - Generate enough blocks to reach third state transition
//   - Assert bit is set for block prior to state transition (ThresholdLockedIn)
//   - Assert bit is NOT set for block at state transition (ThresholdActive)
func TestBIP0009Mining(t *testing.T) {
	t.Parallel()

	// Initialize the primary mining node with only the genesis block.
	r, err := rpctest.New(&chaincfg.SimNetParams, nil, nil)
	if err != nil {
		t.Fatalf("unable to create primary harness: %v", err)
	}
	if err := r.SetUp(true, 0); err != nil {
		t.Fatalf("unable to setup test chain: %v", err)
	}
	defer r.TearDown()

	// Assert the chain only consists of the gensis block.
	assertChainHeight(r, t, 0)

	// *** ThresholdDefined ***
	//
	// Generate a block that extends the genesis block.  It should not have
	// the test dummy bit set in the version since the first window is
	// in the defined threshold state.
	deployment := &r.ActiveNet.Deployments[chaincfg.DeploymentTestDummy]
	testDummyBitNum := deployment.BitNumber
	hashes, err := r.Node.Generate(1)
	if err != nil {
		t.Fatalf("unable to generate blocks: %v", err)
	}
	assertChainHeight(r, t, 1)
	assertVersionBit(r, t, hashes[0], testDummyBitNum, false)

	// *** ThresholdStarted ***
	//
	// Generate enough blocks to reach the first state transition.
	//
	// The second to last generated block should not have the test bit set
	// in the version.
	//
	// The last generated block should now have the test bit set in the
	// version since the btcd mining code will have recognized the test
	// dummy deployment as started.
	confirmationWindow := r.ActiveNet.MinerConfirmationWindow
	numNeeded := confirmationWindow - 1
	hashes, err = r.Node.Generate(numNeeded)
	if err != nil {
		t.Fatalf("failed to generated %d blocks: %v", numNeeded, err)
	}
	assertChainHeight(r, t, confirmationWindow)
	assertVersionBit(r, t, hashes[len(hashes)-2], testDummyBitNum, false)
	assertVersionBit(r, t, hashes[len(hashes)-1], testDummyBitNum, true)

	// *** ThresholdLockedIn ***
	//
	// Generate enough blocks to reach the next state transition.
	//
	// The last generated block should still have the test bit set in the
	// version since the btcd mining code will have recognized the test
	// dummy deployment as locked in.
	hashes, err = r.Node.Generate(confirmationWindow)
	if err != nil {
		t.Fatalf("failed to generated %d blocks: %v", confirmationWindow,
			err)
	}
	assertChainHeight(r, t, confirmationWindow*2)
	assertVersionBit(r, t, hashes[len(hashes)-1], testDummyBitNum, true)

	// *** ThresholdActivated ***
	//
	// Generate enough blocks to reach the next state transition.
	//
	// The second to last generated block should still have the test bit set
	// in the version since it is still locked in.
	//
	// The last generated block should NOT have the test bit set in the
	// version since the btcd mining code will have recognized the test
	// dummy deployment as activated and thus there is no longer any need
	// to set the bit.
	hashes, err = r.Node.Generate(confirmationWindow)
	if err != nil {
		t.Fatalf("failed to generated %d blocks: %v", confirmationWindow,
			err)
	}
	assertChainHeight(r, t, confirmationWindow*3)
	assertVersionBit(r, t, hashes[len(hashes)-2], testDummyBitNum, true)
	assertVersionBit(r, t, hashes[len(hashes)-1], testDummyBitNum, false)
}
Example #7
0
// testBIP0009 ensures the BIP0009 soft fork mechanism follows the state
// transition rules set forth by the BIP for the provided soft fork key.  It
// uses the regression test network to signal support and advance through the
// various threshold states including failure to achieve locked in status.
//
// See TestBIP0009 for an overview of what is tested.
//
// NOTE: This only differs from the exported version in that it accepts the
// specific soft fork deployment to test.
func testBIP0009(t *testing.T, forkKey string, deploymentID uint32) {
	// Initialize the primary mining node with only the genesis block.
	r, err := rpctest.New(&chaincfg.RegressionNetParams, nil, nil)
	if err != nil {
		t.Fatalf("unable to create primary harness: %v", err)
	}
	if err := r.SetUp(false, 0); err != nil {
		t.Fatalf("unable to setup test chain: %v", err)
	}
	defer r.TearDown()

	// *** ThresholdDefined ***
	//
	// Assert the chain height is the expected value and the soft fork
	// status starts out as defined.
	assertChainHeight(r, t, 0)
	assertSoftForkStatus(r, t, forkKey, blockchain.ThresholdDefined)

	// *** ThresholdDefined part 2 - 1 block prior to ThresholdStarted ***
	//
	// Generate enough blocks to reach the height just before the first
	// state transition without signalling support since the state should
	// move to started once the start time has been reached regardless of
	// support signalling.
	//
	// NOTE: This is two blocks before the confirmation window because the
	// getblockchaininfo RPC reports the status for the block AFTER the
	// current one.  All of the heights below are thus offset by one to
	// compensate.
	//
	// Assert the chain height is the expected value and soft fork status is
	// still defined and did NOT move to started.
	confirmationWindow := r.ActiveNet.MinerConfirmationWindow
	for i := uint32(0); i < confirmationWindow-2; i++ {
		_, err := r.GenerateAndSubmitBlock(nil, vbLegacyBlockVersion,
			time.Time{})
		if err != nil {
			t.Fatalf("failed to generated block %d: %v", i, err)
		}
	}
	assertChainHeight(r, t, confirmationWindow-2)
	assertSoftForkStatus(r, t, forkKey, blockchain.ThresholdDefined)

	// *** ThresholdStarted ***
	//
	// Generate another block to reach the next window.
	//
	// Assert the chain height is the expected value and the soft fork
	// status is started.
	_, err = r.GenerateAndSubmitBlock(nil, vbLegacyBlockVersion, time.Time{})
	if err != nil {
		t.Fatalf("failed to generated block: %v", err)
	}
	assertChainHeight(r, t, confirmationWindow-1)
	assertSoftForkStatus(r, t, forkKey, blockchain.ThresholdStarted)

	// *** ThresholdStarted part 2 - Fail to achieve ThresholdLockedIn ***
	//
	// Generate enough blocks to reach the next window in such a way that
	// the number blocks with the version bit set to signal support is 1
	// less than required to achieve locked in status.
	//
	// Assert the chain height is the expected value and the soft fork
	// status is still started and did NOT move to locked in.
	if deploymentID > uint32(len(r.ActiveNet.Deployments)) {
		t.Fatalf("deployment ID %d does not exist", deploymentID)
	}
	deployment := &r.ActiveNet.Deployments[deploymentID]
	activationThreshold := r.ActiveNet.RuleChangeActivationThreshold
	signalForkVersion := int32(1<<deployment.BitNumber) | vbTopBits
	for i := uint32(0); i < activationThreshold-1; i++ {
		_, err := r.GenerateAndSubmitBlock(nil, signalForkVersion,
			time.Time{})
		if err != nil {
			t.Fatalf("failed to generated block %d: %v", i, err)
		}
	}
	for i := uint32(0); i < confirmationWindow-(activationThreshold-1); i++ {
		_, err := r.GenerateAndSubmitBlock(nil, vbLegacyBlockVersion,
			time.Time{})
		if err != nil {
			t.Fatalf("failed to generated block %d: %v", i, err)
		}
	}
	assertChainHeight(r, t, (confirmationWindow*2)-1)
	assertSoftForkStatus(r, t, forkKey, blockchain.ThresholdStarted)

	// *** ThresholdLockedIn ***
	//
	// Generate enough blocks to reach the next window in such a way that
	// the number blocks with the version bit set to signal support is
	// exactly the number required to achieve locked in status.
	//
	// Assert the chain height is the expected value and the soft fork
	// status moved to locked in.
	for i := uint32(0); i < activationThreshold; i++ {
		_, err := r.GenerateAndSubmitBlock(nil, signalForkVersion,
			time.Time{})
		if err != nil {
			t.Fatalf("failed to generated block %d: %v", i, err)
		}
	}
	for i := uint32(0); i < confirmationWindow-activationThreshold; i++ {
		_, err := r.GenerateAndSubmitBlock(nil, vbLegacyBlockVersion,
			time.Time{})
		if err != nil {
			t.Fatalf("failed to generated block %d: %v", i, err)
		}
	}
	assertChainHeight(r, t, (confirmationWindow*3)-1)
	assertSoftForkStatus(r, t, forkKey, blockchain.ThresholdLockedIn)

	// *** ThresholdLockedIn part 2 -- 1 block prior to ThresholdActive ***
	//
	// Generate enough blocks to reach the height just before the next
	// window without continuing to signal support since it is already
	// locked in.
	//
	// Assert the chain height is the expected value and the soft fork
	// status is still locked in and did NOT move to active.
	for i := uint32(0); i < confirmationWindow-1; i++ {
		_, err := r.GenerateAndSubmitBlock(nil, vbLegacyBlockVersion,
			time.Time{})
		if err != nil {
			t.Fatalf("failed to generated block %d: %v", i, err)
		}
	}
	assertChainHeight(r, t, (confirmationWindow*4)-2)
	assertSoftForkStatus(r, t, forkKey, blockchain.ThresholdLockedIn)

	// *** ThresholdActive ***
	//
	// Generate another block to reach the next window without continuing to
	// signal support since it is already locked in.
	//
	// Assert the chain height is the expected value and the soft fork
	// status moved to active.
	_, err = r.GenerateAndSubmitBlock(nil, vbLegacyBlockVersion, time.Time{})
	if err != nil {
		t.Fatalf("failed to generated block: %v", err)
	}
	assertChainHeight(r, t, (confirmationWindow*4)-1)
	assertSoftForkStatus(r, t, forkKey, blockchain.ThresholdActive)
}
Example #8
0
// 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) {
	t.Parallel()

	// 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.

	btcdCfg := []string{"--rejectnonstd"}
	r, err := rpctest.New(&chaincfg.SimNetParams, nil, btcdCfg)
	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         = btcutil.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)|wire.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)
	}
}
Example #9
0
// TestBIP0113Activation tests for proper adherance 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) {
	t.Parallel()

	btcdCfg := []string{"--rejectnonstd"}
	r, err := rpctest.New(&chaincfg.SimNetParams, nil, btcdCfg)
	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 = btcutil.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,
		txscript.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
	// transation.
	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,
			txscript.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)
		}
	}
}