Skip to content

Commit 994ef3b

Browse files
committed
test: add long tx test
1 parent 2003a72 commit 994ef3b

File tree

2 files changed

+200
-2
lines changed

2 files changed

+200
-2
lines changed

testnet/stacks-node/src/tests/nakamoto_integrations.rs

Lines changed: 199 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ use stacks_signer::signerdb::{BlockInfo, BlockState, ExtraBlockInfo, SignerDb};
9494
use super::bitcoin_regtest::BitcoinCoreController;
9595
use crate::config::{EventKeyType, EventObserverConfig, InitialBalance};
9696
use crate::nakamoto_node::miner::{
97-
TEST_BLOCK_ANNOUNCE_STALL, TEST_BROADCAST_STALL, TEST_MINE_STALL,
97+
TEST_BLOCK_ANNOUNCE_STALL, TEST_BROADCAST_STALL, TEST_MINE_STALL, TEST_SKIP_P2P_BROADCAST,
9898
};
9999
use crate::neon::{Counters, RunLoopCounter};
100100
use crate::operations::BurnchainOpSigner;
@@ -8926,3 +8926,201 @@ fn v3_signer_api_endpoint() {
89268926

89278927
run_loop_thread.join().unwrap();
89288928
}
8929+
8930+
#[test]
8931+
#[ignore]
8932+
/// This test spins up a nakamoto-neon node.
8933+
/// It starts in Epoch 2.0, mines with `neon_node` to Epoch 3.0, and then switches
8934+
/// to Nakamoto operation (activating pox-4 by submitting a stack-stx tx). The BootLoop
8935+
/// struct handles the epoch-2/3 tear-down and spin-up.
8936+
/// This test asserts that a long running transaction doesn't get mined,
8937+
/// but that the stacks-node continues to make progress
8938+
fn skip_mining_long_tx() {
8939+
if env::var("BITCOIND_TEST") != Ok("1".into()) {
8940+
return;
8941+
}
8942+
8943+
let (mut naka_conf, _miner_account) = naka_neon_integration_conf(None);
8944+
let prom_bind = format!("{}:{}", "127.0.0.1", 6000);
8945+
naka_conf.node.prometheus_bind = Some(prom_bind.clone());
8946+
naka_conf.miner.wait_on_interim_blocks = Duration::from_secs(1);
8947+
naka_conf.miner.nakamoto_attempt_time_ms = 5_000;
8948+
let sender_1_sk = Secp256k1PrivateKey::from_seed(&[30]);
8949+
let sender_2_sk = Secp256k1PrivateKey::from_seed(&[31]);
8950+
// setup sender + recipient for a test stx transfer
8951+
let sender_1_addr = tests::to_addr(&sender_1_sk);
8952+
let sender_2_addr = tests::to_addr(&sender_2_sk);
8953+
let send_amt = 1000;
8954+
let send_fee = 180;
8955+
naka_conf.add_initial_balance(
8956+
PrincipalData::from(sender_1_addr.clone()).to_string(),
8957+
send_amt * 15 + send_fee * 15,
8958+
);
8959+
naka_conf.add_initial_balance(
8960+
PrincipalData::from(sender_2_addr.clone()).to_string(),
8961+
10000,
8962+
);
8963+
let sender_signer_sk = Secp256k1PrivateKey::new();
8964+
let sender_signer_addr = tests::to_addr(&sender_signer_sk);
8965+
let mut signers = TestSigners::new(vec![sender_signer_sk.clone()]);
8966+
naka_conf.add_initial_balance(
8967+
PrincipalData::from(sender_signer_addr.clone()).to_string(),
8968+
100000,
8969+
);
8970+
let recipient = PrincipalData::from(StacksAddress::burn_address(false));
8971+
let stacker_sk = setup_stacker(&mut naka_conf);
8972+
let http_origin = format!("http://{}", &naka_conf.node.rpc_bind);
8973+
8974+
test_observer::spawn();
8975+
let observer_port = test_observer::EVENT_OBSERVER_PORT;
8976+
naka_conf.events_observers.insert(EventObserverConfig {
8977+
endpoint: format!("localhost:{observer_port}"),
8978+
events_keys: vec![EventKeyType::AnyEvent],
8979+
});
8980+
8981+
let mut btcd_controller = BitcoinCoreController::new(naka_conf.clone());
8982+
btcd_controller
8983+
.start_bitcoind()
8984+
.expect("Failed starting bitcoind");
8985+
let mut btc_regtest_controller = BitcoinRegtestController::new(naka_conf.clone(), None);
8986+
btc_regtest_controller.bootstrap_chain(201);
8987+
8988+
let mut run_loop = boot_nakamoto::BootRunLoop::new(naka_conf.clone()).unwrap();
8989+
let run_loop_stopper = run_loop.get_termination_switch();
8990+
let Counters {
8991+
blocks_processed,
8992+
naka_submitted_commits: commits_submitted,
8993+
naka_proposed_blocks: proposals_submitted,
8994+
naka_mined_blocks: mined_naka_blocks,
8995+
..
8996+
} = run_loop.counters();
8997+
8998+
let coord_channel = run_loop.coordinator_channels();
8999+
9000+
let run_loop_thread = thread::spawn(move || run_loop.start(None, 0));
9001+
wait_for_runloop(&blocks_processed);
9002+
boot_to_epoch_3(
9003+
&naka_conf,
9004+
&blocks_processed,
9005+
&[stacker_sk],
9006+
&[sender_signer_sk],
9007+
&mut Some(&mut signers),
9008+
&mut btc_regtest_controller,
9009+
);
9010+
9011+
info!("Bootstrapped to Epoch-3.0 boundary, starting nakamoto miner");
9012+
9013+
let burnchain = naka_conf.get_burnchain();
9014+
let sortdb = burnchain.open_sortition_db(true).unwrap();
9015+
let (chainstate, _) = StacksChainState::open(
9016+
naka_conf.is_mainnet(),
9017+
naka_conf.burnchain.chain_id,
9018+
&naka_conf.get_chainstate_path_str(),
9019+
None,
9020+
)
9021+
.unwrap();
9022+
9023+
info!("Nakamoto miner started...");
9024+
blind_signer(&naka_conf, &signers, proposals_submitted);
9025+
9026+
wait_for_first_naka_block_commit(60, &commits_submitted);
9027+
9028+
// submit a long running TX and the transfer TX
9029+
let input_list: Vec<_> = (1..100u64).into_iter().map(|x| x.to_string()).collect();
9030+
let input_list = input_list.join(" ");
9031+
9032+
// Mine a few nakamoto tenures with some interim blocks in them
9033+
for i in 0..5 {
9034+
let mined_before = mined_naka_blocks.load(Ordering::SeqCst);
9035+
next_block_and_mine_commit(
9036+
&mut btc_regtest_controller,
9037+
60,
9038+
&coord_channel,
9039+
&commits_submitted,
9040+
)
9041+
.unwrap();
9042+
9043+
if i == 0 {
9044+
// we trigger the nakamoto miner to evaluate the long running transaction,
9045+
// but we disable the block broadcast, so the tx doesn't end up included in a
9046+
// confirmed block, even though its been evaluated.
9047+
// once we've seen the miner increment the mined counter, we allow it to start
9048+
// broadcasting (because at this point, any future blocks produced will skip the long
9049+
// running tx because they have an estimate).
9050+
wait_for(30, || {
9051+
Ok(mined_naka_blocks.load(Ordering::SeqCst) > mined_before)
9052+
})
9053+
.unwrap();
9054+
9055+
TEST_SKIP_P2P_BROADCAST.lock().unwrap().replace(true);
9056+
let tx = make_contract_publish(
9057+
&sender_2_sk,
9058+
0,
9059+
9_000,
9060+
"large_contract",
9061+
&format!(
9062+
"(define-constant INP_LIST (list {input_list}))
9063+
(define-private (mapping-fn (input int))
9064+
(begin (sha256 (sha256 (sha256 (sha256 (sha256 (sha256 (sha256 (sha256 (sha256 input)))))))))
9065+
0))
9066+
9067+
(define-private (mapping-fn-2 (input int))
9068+
(begin (map mapping-fn INP_LIST) (map mapping-fn INP_LIST) (map mapping-fn INP_LIST) (map mapping-fn INP_LIST) 0))
9069+
9070+
(begin
9071+
(map mapping-fn-2 INP_LIST))"
9072+
),
9073+
);
9074+
submit_tx(&http_origin, &tx);
9075+
9076+
wait_for(90, || {
9077+
Ok(mined_naka_blocks.load(Ordering::SeqCst) > mined_before + 1)
9078+
})
9079+
.unwrap();
9080+
9081+
TEST_SKIP_P2P_BROADCAST.lock().unwrap().replace(false);
9082+
} else {
9083+
let transfer_tx =
9084+
make_stacks_transfer(&sender_1_sk, i - 1, send_fee, &recipient, send_amt);
9085+
submit_tx(&http_origin, &transfer_tx);
9086+
9087+
wait_for(30, || {
9088+
let cur_sender_nonce = get_account(&http_origin, &sender_1_addr).nonce;
9089+
Ok(cur_sender_nonce >= i)
9090+
})
9091+
.unwrap();
9092+
}
9093+
}
9094+
9095+
let sender_1_nonce = get_account(&http_origin, &sender_1_addr).nonce;
9096+
let sender_2_nonce = get_account(&http_origin, &sender_2_addr).nonce;
9097+
9098+
// load the chain tip, and assert that it is a nakamoto block and at least 30 blocks have advanced in epoch 3
9099+
let tip = NakamotoChainState::get_canonical_block_header(chainstate.db(), &sortdb)
9100+
.unwrap()
9101+
.unwrap();
9102+
info!(
9103+
"Latest tip";
9104+
"height" => tip.stacks_block_height,
9105+
"is_nakamoto" => tip.anchored_header.as_stacks_nakamoto().is_some(),
9106+
"sender_1_nonce" => sender_1_nonce,
9107+
"sender_2_nonce" => sender_2_nonce,
9108+
);
9109+
9110+
assert_eq!(sender_2_nonce, 0);
9111+
assert_eq!(sender_1_nonce, 4);
9112+
9113+
// Check that we aren't missing burn blocks
9114+
let bhh = u64::from(tip.burn_header_height);
9115+
test_observer::contains_burn_block_range(220..=bhh).unwrap();
9116+
9117+
check_nakamoto_empty_block_heuristics();
9118+
9119+
coord_channel
9120+
.lock()
9121+
.expect("Mutex poisoned")
9122+
.stop_chains_coordinator();
9123+
run_loop_stopper.store(false, Ordering::SeqCst);
9124+
9125+
run_loop_thread.join().unwrap();
9126+
}

testnet/stacks-node/src/tests/neon_integrations.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -591,7 +591,7 @@ pub mod test_observer {
591591
}
592592
}
593593
let tx_hex = tx_json.get("raw_tx").unwrap().as_str().unwrap();
594-
let tx_bytes = hex_bytes(tx_hex).unwrap();
594+
let tx_bytes = hex_bytes(&tx_hex[2..]).unwrap();
595595
let tx =
596596
StacksTransaction::consensus_deserialize(&mut tx_bytes.as_slice()).unwrap();
597597
Some(tx)

0 commit comments

Comments
 (0)