From e667b82476cccfe88ee98f1ad720536bc03719f9 Mon Sep 17 00:00:00 2001 From: Jude Nelson Date: Wed, 22 Jan 2025 16:36:07 -0500 Subject: [PATCH 1/5] fix: only shut off the epoch2x state machines once the Stacks tip is a Nakamoto tip --- stackslib/src/net/p2p.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/stackslib/src/net/p2p.rs b/stackslib/src/net/p2p.rs index 78c8982106..57908ca6af 100644 --- a/stackslib/src/net/p2p.rs +++ b/stackslib/src/net/p2p.rs @@ -3606,12 +3606,7 @@ impl PeerNetwork { // in Nakamoto epoch, but we might still be doing epoch 2.x things since Nakamoto does // not begin on a reward cycle boundary. - if cur_epoch.epoch_id == StacksEpochId::Epoch30 - && (self.burnchain_tip.block_height - <= cur_epoch.start_height - + u64::from(self.burnchain.pox_constants.reward_cycle_length) - || self.connection_opts.force_nakamoto_epoch_transition) - { + if cur_epoch.epoch_id >= StacksEpochId::Epoch30 && !self.stacks_tip.is_nakamoto { debug!( "{:?}: run Epoch 2.x work loop in Nakamoto epoch", self.get_local_peer() From f035175149f74ebd071823e0d2a1e1b318e4cf02 Mon Sep 17 00:00:00 2001 From: Jude Nelson Date: Thu, 23 Jan 2025 17:16:13 -0500 Subject: [PATCH 2/5] chore: make the epoch2x state machine check testable, and extend TestPeer to check it --- stackslib/src/net/mod.rs | 34 ++++++++++++++++++++++++++++++++++ stackslib/src/net/p2p.rs | 23 ++++++++++++++++++++++- 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/stackslib/src/net/mod.rs b/stackslib/src/net/mod.rs index cfefa2c5fe..ecbca3301f 100644 --- a/stackslib/src/net/mod.rs +++ b/stackslib/src/net/mod.rs @@ -3314,6 +3314,10 @@ pub mod test { let old_tip = self.network.stacks_tip.clone(); + // make sure the right state machines run + let epoch2_passes = self.network.epoch2_state_machine_passes; + let nakamoto_passes = self.network.nakamoto_state_machine_passes; + let ret = self.network.run( &indexer, &sortdb, @@ -3326,6 +3330,19 @@ pub mod test { &RPCHandlerArgs::default(), ); + if self.network.get_current_epoch().epoch_id >= StacksEpochId::Epoch30 { + assert_eq!( + self.network.nakamoto_state_machine_passes, + nakamoto_passes + 1 + ); + } + if self + .network + .need_epoch2_state_machines(self.network.get_current_epoch().epoch_id) + { + assert_eq!(self.network.epoch2_state_machine_passes, epoch2_passes + 1); + } + self.sortdb = Some(sortdb); self.stacks_node = Some(stacks_node); self.mempool = Some(mempool); @@ -3394,6 +3411,10 @@ pub mod test { let old_tip = self.network.stacks_tip.clone(); + // make sure the right state machines run + let epoch2_passes = self.network.epoch2_state_machine_passes; + let nakamoto_passes = self.network.nakamoto_state_machine_passes; + let ret = self.network.run( &indexer, &sortdb, @@ -3406,6 +3427,19 @@ pub mod test { &RPCHandlerArgs::default(), ); + if self.network.get_current_epoch().epoch_id >= StacksEpochId::Epoch30 { + assert_eq!( + self.network.nakamoto_state_machine_passes, + nakamoto_passes + 1 + ); + } + if self + .network + .need_epoch2_state_machines(self.network.get_current_epoch().epoch_id) + { + assert_eq!(self.network.epoch2_state_machine_passes, epoch2_passes + 1); + } + self.sortdb = Some(sortdb); self.stacks_node = Some(stacks_node); self.mempool = Some(mempool); diff --git a/stackslib/src/net/p2p.rs b/stackslib/src/net/p2p.rs index 57908ca6af..c5b1afa33e 100644 --- a/stackslib/src/net/p2p.rs +++ b/stackslib/src/net/p2p.rs @@ -423,6 +423,12 @@ pub struct PeerNetwork { // how many downloader passes have we done? pub num_downloader_passes: u64, + // number of epoch2 state machine passes + pub(crate) epoch2_state_machine_passes: u128, + + // number of nakamoto state machine passes + pub(crate) nakamoto_state_machine_passes: u128, + // to whom did we send a block or microblock stream as part of our anti-entropy protocol, and // when did we send it? antientropy_blocks: HashMap>, @@ -593,6 +599,8 @@ impl PeerNetwork { num_state_machine_passes: 0, num_inv_sync_passes: 0, num_downloader_passes: 0, + epoch2_state_machine_passes: 0, + nakamoto_state_machine_passes: 0, antientropy_blocks: HashMap::new(), antientropy_microblocks: HashMap::new(), @@ -3578,6 +3586,15 @@ impl PeerNetwork { } } + /// Check to see if we need to run the epoch 2.x state machines. + /// This will be true if we're either in epoch 2.5 or lower, OR, if we're in epoch 3.0 or + /// higher AND the Stacks tip is not yet a Nakamoto block. This latter condition indicates + /// that the epoch 2.x state machines are still needed to download the final epoch 2.x blocks. + pub(crate) fn need_epoch2_state_machines(&self, epoch_id: StacksEpochId) -> bool { + epoch_id < StacksEpochId::Epoch30 + || (epoch_id >= StacksEpochId::Epoch30 && !self.stacks_tip.is_nakamoto) + } + /// Do the actual work in the state machine. /// Return true if we need to prune connections. /// This will call the epoch-appropriate network worker @@ -3606,7 +3623,7 @@ impl PeerNetwork { // in Nakamoto epoch, but we might still be doing epoch 2.x things since Nakamoto does // not begin on a reward cycle boundary. - if cur_epoch.epoch_id >= StacksEpochId::Epoch30 && !self.stacks_tip.is_nakamoto { + if self.need_epoch2_state_machines(cur_epoch.epoch_id) { debug!( "{:?}: run Epoch 2.x work loop in Nakamoto epoch", self.get_local_peer() @@ -3655,6 +3672,8 @@ impl PeerNetwork { ibd: bool, network_result: &mut NetworkResult, ) { + self.nakamoto_state_machine_passes += 1; + // always do an inv sync let learned = self.do_network_inv_sync_nakamoto(sortdb, ibd); debug!( @@ -3704,6 +3723,8 @@ impl PeerNetwork { ibd: bool, network_result: &mut NetworkResult, ) -> bool { + self.epoch2_state_machine_passes += 1; + // do some Actual Work(tm) let mut do_prune = false; let mut did_cycle = false; From 56e0581ed770ab666366c6968a759180126a37ec Mon Sep 17 00:00:00 2001 From: Jude Nelson Date: Fri, 24 Jan 2025 00:47:14 -0500 Subject: [PATCH 3/5] chore: address PR feedback --- stackslib/src/net/mod.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/stackslib/src/net/mod.rs b/stackslib/src/net/mod.rs index 2c37b5285a..27054ed329 100644 --- a/stackslib/src/net/mod.rs +++ b/stackslib/src/net/mod.rs @@ -3334,6 +3334,15 @@ pub mod test { self.network.nakamoto_state_machine_passes, nakamoto_passes + 1 ); + let epoch2_expected_passes = if self.network.stacks_tip.is_nakamoto { + epoch2_passes + } else { + epoch2_passes + 1 + }; + assert_eq!( + self.network.epoch2_state_machine_passes, + epoch2_expected_passes + ); } if self .network @@ -3431,6 +3440,15 @@ pub mod test { self.network.nakamoto_state_machine_passes, nakamoto_passes + 1 ); + let epoch2_expected_passes = if self.network.stacks_tip.is_nakamoto { + epoch2_passes + } else { + epoch2_passes + 1 + }; + assert_eq!( + self.network.epoch2_state_machine_passes, + epoch2_expected_passes + ); } if self .network From 3420de365d1f5171e2f4da68f248afcfce49b622 Mon Sep 17 00:00:00 2001 From: Jude Nelson Date: Tue, 4 Feb 2025 13:59:34 -0500 Subject: [PATCH 4/5] chore: honor config option to force nakamoto state machine transition --- stackslib/src/net/p2p.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/stackslib/src/net/p2p.rs b/stackslib/src/net/p2p.rs index 26d5fd0fb4..4c39bf32bd 100644 --- a/stackslib/src/net/p2p.rs +++ b/stackslib/src/net/p2p.rs @@ -3623,7 +3623,9 @@ impl PeerNetwork { // in Nakamoto epoch, but we might still be doing epoch 2.x things since Nakamoto does // not begin on a reward cycle boundary. - if self.need_epoch2_state_machines(cur_epoch.epoch_id) { + if self.need_epoch2_state_machines(cur_epoch.epoch_id) + || self.connection_opts.force_nakamoto_epoch_transition + { debug!( "{:?}: run Epoch 2.x work loop in Nakamoto epoch", self.get_local_peer() From 0915796360ff162e9aa1173d115b814f0a34dc4d Mon Sep 17 00:00:00 2001 From: Jude Nelson Date: Tue, 4 Feb 2025 20:45:43 -0500 Subject: [PATCH 5/5] chore: fix failing unit test --- stackslib/src/net/mod.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/stackslib/src/net/mod.rs b/stackslib/src/net/mod.rs index 2a8cf7f523..986352c42c 100644 --- a/stackslib/src/net/mod.rs +++ b/stackslib/src/net/mod.rs @@ -3325,7 +3325,9 @@ pub mod test { self.network.nakamoto_state_machine_passes, nakamoto_passes + 1 ); - let epoch2_expected_passes = if self.network.stacks_tip.is_nakamoto { + let epoch2_expected_passes = if self.network.stacks_tip.is_nakamoto + && !self.network.connection_opts.force_nakamoto_epoch_transition + { epoch2_passes } else { epoch2_passes + 1 @@ -3431,7 +3433,9 @@ pub mod test { self.network.nakamoto_state_machine_passes, nakamoto_passes + 1 ); - let epoch2_expected_passes = if self.network.stacks_tip.is_nakamoto { + let epoch2_expected_passes = if self.network.stacks_tip.is_nakamoto + && !self.network.connection_opts.force_nakamoto_epoch_transition + { epoch2_passes } else { epoch2_passes + 1