From c56e6adfd2ac7bbd3ba7ecb1ac1eca54fb04e83d Mon Sep 17 00:00:00 2001 From: Aaron Blankstein Date: Fri, 29 Jul 2022 09:51:15 -0500 Subject: [PATCH] test: add integration test for #3185 --- .github/workflows/bitcoin-tests.yml | 1 + .../src/tests/neon_integrations.rs | 91 +++++++++++++++++++ 2 files changed, 92 insertions(+) diff --git a/.github/workflows/bitcoin-tests.yml b/.github/workflows/bitcoin-tests.yml index 3eea180f0..0937a6b82 100644 --- a/.github/workflows/bitcoin-tests.yml +++ b/.github/workflows/bitcoin-tests.yml @@ -38,6 +38,7 @@ jobs: fail-fast: false matrix: test-name: + - tests::neon_integrations::miner_submit_twice - tests::neon_integrations::microblock_integration_test - tests::neon_integrations::size_check_integration_test - tests::neon_integrations::cost_voting_integration diff --git a/testnet/stacks-node/src/tests/neon_integrations.rs b/testnet/stacks-node/src/tests/neon_integrations.rs index 7159b4173..f20582aaf 100644 --- a/testnet/stacks-node/src/tests/neon_integrations.rs +++ b/testnet/stacks-node/src/tests/neon_integrations.rs @@ -2265,6 +2265,97 @@ fn filter_long_runtime_tx_integration_test() { channel.stop_chains_coordinator(); } +#[test] +#[ignore] +fn miner_submit_twice() { + if env::var("BITCOIND_TEST") != Ok("1".into()) { + return; + } + + let spender_sk = StacksPrivateKey::new(); + let spender_addr: PrincipalData = to_addr(&spender_sk).into(); + let contract_content = " + (define-public (foo (a int)) + (ok (* 2 (+ a 1)))) + (define-private (bar) + (foo 56)) + "; + let tx_1 = make_contract_publish(&spender_sk, 0, 50_000, "first-contract", contract_content); + let tx_2 = make_contract_publish(&spender_sk, 1, 50_000, "second-contract", contract_content); + + let (mut conf, _) = neon_integration_test_conf(); + conf.initial_balances.push(InitialBalance { + address: spender_addr.clone(), + amount: 1049230, + }); + + // all transactions have high-enough fees... + conf.miner.min_tx_fee = 1; + conf.node.mine_microblocks = false; + // one should be mined in first attempt, and two should be in second attempt + conf.miner.first_attempt_time_ms = 20; + conf.miner.subsequent_attempt_time_ms = 30_000; + + // note: this test depends on timing of how long it takes to assemble a block, + // but it won't flake if the miner behaves correctly: a correct miner should + // always be able to mine both transactions by the end of this test. an incorrect + // miner may sometimes pass this test though, if they can successfully mine a + // 2-transaction block in 20 ms *OR* if they are slow enough that they mine a + // 0-transaction block in that time (because this would trigger a re-attempt, which + // is exactly what this test is measuring). + // + // The "fixed" behavior is the corner case where a miner did a "first attempt", which + // included 1 or more transaction, but they could have made a second attempt with + // more transactions. + + let mut btcd_controller = BitcoinCoreController::new(conf.clone()); + btcd_controller + .start_bitcoind() + .map_err(|_e| ()) + .expect("Failed starting bitcoind"); + + let mut btc_regtest_controller = BitcoinRegtestController::new(conf.clone(), None); + let http_origin = format!("http://{}", &conf.node.rpc_bind); + + btc_regtest_controller.bootstrap_chain(201); + + eprintln!("Chain bootstrapped..."); + + let mut run_loop = neon::RunLoop::new(conf); + let blocks_processed = run_loop.get_blocks_processed_arc(); + + let channel = run_loop.get_coordinator_channel().unwrap(); + + thread::spawn(move || run_loop.start(None, 0)); + + // give the run loop some time to start up! + wait_for_runloop(&blocks_processed); + + // first block wakes up the run loop + next_block_and_wait(&mut btc_regtest_controller, &blocks_processed); + + // first block will hold our VRF registration + next_block_and_wait(&mut btc_regtest_controller, &blocks_processed); + + // second block will be the first mined Stacks block + next_block_and_wait(&mut btc_regtest_controller, &blocks_processed); + + submit_tx(&http_origin, &tx_1); + submit_tx(&http_origin, &tx_2); + + // mine a couple more blocks + // waiting enough time between them that a second attempt could be made. + next_block_and_wait(&mut btc_regtest_controller, &blocks_processed); + thread::sleep(Duration::from_secs(15)); + next_block_and_wait(&mut btc_regtest_controller, &blocks_processed); + + // 1 transaction mined + let account = get_account(&http_origin, &spender_addr); + assert_eq!(account.nonce, 2); + + channel.stop_chains_coordinator(); +} + #[test] #[ignore] fn mining_transactions_is_fair() {