Skip to content

Commit d4d3917

Browse files
committed
fix: revert logic for setting expected_burn_height
1 parent 8f790dc commit d4d3917

File tree

2 files changed

+158
-3
lines changed

2 files changed

+158
-3
lines changed

stacks-signer/src/v0/signer_state.rs

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -564,7 +564,15 @@ impl LocalStateMachine {
564564
// to be changed.
565565
match update {
566566
StateMachineUpdate::BurnBlock(pending_burn_block) => {
567-
expected_burn_block = Some(pending_burn_block);
567+
match expected_burn_block {
568+
None => expected_burn_block = Some(pending_burn_block),
569+
Some(ref expected) => {
570+
if pending_burn_block.burn_block_height > expected.burn_block_height
571+
{
572+
expected_burn_block = Some(pending_burn_block);
573+
}
574+
}
575+
}
568576
}
569577
}
570578

@@ -989,8 +997,41 @@ impl LocalStateMachine {
989997
replay_state: &ReplayState,
990998
) -> Result<Option<ReplayState>, SignerChainstateError> {
991999
if expected_burn_block.burn_block_height > prior_state_machine.burn_block_height {
992-
// no bitcoin fork, because we're advancing the burn block height
993-
return Ok(None);
1000+
// prevent too large of a loop
1001+
if expected_burn_block
1002+
.burn_block_height
1003+
.saturating_sub(prior_state_machine.burn_block_height)
1004+
> 10
1005+
{
1006+
return Ok(None);
1007+
}
1008+
// are we building on top of this prior tip?
1009+
let mut parent_burn_block_info =
1010+
db.get_burn_block_by_ch(&expected_burn_block.consensus_hash)?;
1011+
1012+
while parent_burn_block_info.block_height > prior_state_machine.burn_block_height {
1013+
let Ok(parent_info) =
1014+
db.get_burn_block_by_hash(&parent_burn_block_info.parent_burn_block_hash)
1015+
else {
1016+
warn!(
1017+
"Failed to get parent burn block info for {}",
1018+
parent_burn_block_info.parent_burn_block_hash
1019+
);
1020+
return Ok(None);
1021+
};
1022+
parent_burn_block_info = parent_info;
1023+
}
1024+
if parent_burn_block_info.consensus_hash == prior_state_machine.burn_block {
1025+
// no bitcoin fork, because we're building on the parent
1026+
return Ok(None);
1027+
} else {
1028+
info!("Detected bitcoin fork - prior tip is not parent of new tip.";
1029+
"new_tip.burn_block_height" => expected_burn_block.burn_block_height,
1030+
"new_tip.consensus_hash" => %expected_burn_block.consensus_hash,
1031+
"prior_tip.burn_block_height" => prior_state_machine.burn_block_height,
1032+
"prior_tip.consensus_hash" => %prior_state_machine.burn_block,
1033+
);
1034+
}
9941035
}
9951036
if expected_burn_block.consensus_hash == prior_state_machine.burn_block {
9961037
// no bitcoin fork, because we're at the same burn block hash as before

testnet/stacks-node/src/tests/signer/v0.rs

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3943,6 +3943,118 @@ fn tx_replay_failsafe() {
39433943
signer_test.shutdown();
39443944
}
39453945

3946+
/// Simple/fast test scenario for transaction replay.
3947+
///
3948+
/// We fork one tenure, which has a STX transfer. The test
3949+
/// verifies that the replay set is updated correctly, and then
3950+
/// exits.
3951+
#[ignore]
3952+
#[test]
3953+
fn tx_replay_simple() {
3954+
if env::var("BITCOIND_TEST") != Ok("1".into()) {
3955+
return;
3956+
}
3957+
3958+
let num_signers = 5;
3959+
let sender_sk = Secp256k1PrivateKey::from_seed("sender_1".as_bytes());
3960+
let sender_addr = tests::to_addr(&sender_sk);
3961+
let send_amt = 100;
3962+
let send_fee = 180;
3963+
let signer_test: SignerTest<SpawnedSigner> =
3964+
SignerTest::new_with_config_modifications_and_snapshot(
3965+
num_signers,
3966+
vec![(sender_addr, (send_amt + send_fee) * 10)],
3967+
|c| {
3968+
c.validate_with_replay_tx = true;
3969+
},
3970+
|node_config| {
3971+
node_config.miner.block_commit_delay = Duration::from_secs(1);
3972+
node_config.miner.replay_transactions = true;
3973+
node_config.miner.activated_vrf_key_path =
3974+
Some(format!("{}/vrf_key", node_config.node.working_dir));
3975+
},
3976+
None,
3977+
None,
3978+
Some(function_name!()),
3979+
);
3980+
3981+
let conf = &signer_test.running_nodes.conf;
3982+
let _http_origin = format!("http://{}", &conf.node.rpc_bind);
3983+
let btc_controller = &signer_test.running_nodes.btc_regtest_controller;
3984+
3985+
let miner_pk = btc_controller
3986+
.get_mining_pubkey()
3987+
.as_deref()
3988+
.map(Secp256k1PublicKey::from_hex)
3989+
.unwrap()
3990+
.unwrap();
3991+
3992+
if signer_test.bootstrap_snapshot() {
3993+
signer_test.shutdown_and_snapshot();
3994+
return;
3995+
}
3996+
3997+
info!("------------------------- Beginning test -------------------------");
3998+
3999+
let tip = signer_test.get_peer_info();
4000+
4001+
info!("---- Tip ----";
4002+
"tip.stacks_tip_height" => tip.stacks_tip_height,
4003+
"tip.burn_block_height" => tip.burn_block_height,
4004+
);
4005+
4006+
let pre_fork_tenures = 1;
4007+
for i in 0..pre_fork_tenures {
4008+
info!("Mining pre-fork tenure {} of {pre_fork_tenures}", i + 1);
4009+
signer_test.mine_nakamoto_block(Duration::from_secs(30), true);
4010+
}
4011+
4012+
info!("---- Submitting STX transfer ----");
4013+
4014+
// let tip = get_chain_info(&conf);
4015+
// Make a transfer tx (this will get forked)
4016+
let (txid, nonce) = signer_test
4017+
.submit_transfer_tx(&sender_sk, send_fee, send_amt)
4018+
.unwrap();
4019+
4020+
// Ensure we got a new block with this tx
4021+
signer_test
4022+
.wait_for_nonce_increase(&sender_addr, nonce)
4023+
.expect("Timed out waiting for transfer tx to be mined");
4024+
4025+
let tip_before = get_chain_info(&conf);
4026+
4027+
info!("---- Triggering Bitcoin fork ----";
4028+
"tip.stacks_tip_height" => tip_before.stacks_tip_height,
4029+
"tip.burn_block_height" => tip_before.burn_block_height,
4030+
"tip.consensus_hash" => %tip_before.pox_consensus,
4031+
);
4032+
4033+
let burn_header_hash_to_fork = btc_controller.get_block_hash(tip_before.burn_block_height);
4034+
btc_controller.invalidate_block(&burn_header_hash_to_fork);
4035+
TEST_MINE_STALL.set(true);
4036+
btc_controller.build_next_block(2);
4037+
4038+
wait_for(30, || {
4039+
let tip = get_chain_info(&conf);
4040+
Ok(tip.stacks_tip_height < tip_before.stacks_tip_height)
4041+
})
4042+
.expect("Timed out waiting for next block to be mined");
4043+
4044+
let tip = get_chain_info(&conf);
4045+
4046+
info!("---- Tip after fork ----";
4047+
"tip.stacks_tip_height" => tip.stacks_tip_height,
4048+
"tip.burn_block_height" => tip.burn_block_height,
4049+
);
4050+
4051+
info!("---- Wait for tx replay set to be updated ----");
4052+
4053+
signer_test.wait_for_replay_set_eq(5, vec![txid.clone()]);
4054+
4055+
signer_test.shutdown();
4056+
}
4057+
39464058
/// Test scenario where two signers disagree on the tx replay set,
39474059
/// which means there is no consensus on the tx replay set.
39484060
#[test]
@@ -4488,6 +4600,7 @@ fn tx_replay_with_fork_after_empty_tenures_before_starting_replaying_txs() {
44884600
vec![(sender1_addr, (send_amt + send_fee) * num_txs)],
44894601
|c| {
44904602
c.validate_with_replay_tx = true;
4603+
c.reset_replay_set_after_fork_blocks = 5;
44914604
},
44924605
|node_config| {
44934606
node_config.miner.block_commit_delay = Duration::from_secs(1);
@@ -4859,6 +4972,7 @@ fn tx_replay_with_fork_middle_replay_while_tenure_extending() {
48594972
|c| {
48604973
c.validate_with_replay_tx = true;
48614974
c.tenure_idle_timeout = Duration::from_secs(10);
4975+
c.reset_replay_set_after_fork_blocks = 5;
48624976
},
48634977
|node_config| {
48644978
node_config.miner.block_commit_delay = Duration::from_secs(1);

0 commit comments

Comments
 (0)