Skip to content

Commit

Permalink
Merge pull request #5544 from stacks-network/fix/5502
Browse files Browse the repository at this point in the history
Fix/5502
  • Loading branch information
jcnelson authored Dec 18, 2024
2 parents ca77e95 + 7d4499e commit c900752
Show file tree
Hide file tree
Showing 7 changed files with 87 additions and 556 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ and this project adheres to the versioning scheme outlined in the [README.md](RE

### Changed

- Nodes will assume that all PoX anchor blocks exist by default, and stall initial block download indefinitely to await their arrival (#5502)

## [3.1.0.0.1]

### Added
Expand Down
30 changes: 28 additions & 2 deletions stackslib/src/chainstate/coordinator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,9 @@ pub trait BlockEventDispatcher {
}

pub struct ChainsCoordinatorConfig {
/// true: assume all anchor blocks are present, and block chain sync until they arrive
/// false: process sortitions in reward cycles without anchor blocks
pub assume_present_anchor_blocks: bool,
/// true: use affirmation maps before 2.1
/// false: only use affirmation maps in 2.1 or later
pub always_use_affirmation_maps: bool,
Expand All @@ -209,8 +212,17 @@ pub struct ChainsCoordinatorConfig {
impl ChainsCoordinatorConfig {
pub fn new() -> ChainsCoordinatorConfig {
ChainsCoordinatorConfig {
always_use_affirmation_maps: false,
always_use_affirmation_maps: true,
require_affirmed_anchor_blocks: true,
assume_present_anchor_blocks: true,
}
}

pub fn test_new() -> ChainsCoordinatorConfig {
ChainsCoordinatorConfig {
always_use_affirmation_maps: false,
require_affirmed_anchor_blocks: false,
assume_present_anchor_blocks: false,
}
}
}
Expand Down Expand Up @@ -700,7 +712,7 @@ impl<'a, T: BlockEventDispatcher, U: RewardSetProvider, B: BurnchainHeaderReader
notifier: (),
atlas_config,
atlas_db: Some(atlas_db),
config: ChainsCoordinatorConfig::new(),
config: ChainsCoordinatorConfig::test_new(),
burnchain_indexer,
refresh_stacker_db: Arc::new(AtomicBool::new(false)),
in_nakamoto_epoch: false,
Expand Down Expand Up @@ -2336,6 +2348,20 @@ impl<
panic!("BUG: no epoch defined at height {}", header.block_height)
});

if self.config.assume_present_anchor_blocks {
// anchor blocks are always assumed to be present in the chain history,
// so report its absence if we don't have it.
if let PoxAnchorBlockStatus::SelectedAndUnknown(missing_anchor_block, _) =
&rc_info.anchor_status
{
info!(
"Currently missing PoX anchor block {}, which is assumed to be present",
&missing_anchor_block
);
return Ok(Some(missing_anchor_block.clone()));
}
}

if cur_epoch.epoch_id >= StacksEpochId::Epoch21 || self.config.always_use_affirmation_maps {
// potentially have an anchor block, but only process the next reward cycle (and
// subsequent reward cycles) with it if the prepare-phase block-commits affirm its
Expand Down
7 changes: 7 additions & 0 deletions stackslib/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1656,6 +1656,7 @@ pub struct NodeConfig {
pub use_test_genesis_chainstate: Option<bool>,
pub always_use_affirmation_maps: bool,
pub require_affirmed_anchor_blocks: bool,
pub assume_present_anchor_blocks: bool,
/// Fault injection for failing to push blocks
pub fault_injection_block_push_fail_probability: Option<u8>,
// fault injection for hiding blocks.
Expand Down Expand Up @@ -1939,6 +1940,7 @@ impl Default for NodeConfig {
use_test_genesis_chainstate: None,
always_use_affirmation_maps: true,
require_affirmed_anchor_blocks: true,
assume_present_anchor_blocks: true,
fault_injection_block_push_fail_probability: None,
fault_injection_hide_blocks: false,
chain_liveness_poll_time_secs: 300,
Expand Down Expand Up @@ -2428,6 +2430,7 @@ pub struct NodeConfigFile {
pub use_test_genesis_chainstate: Option<bool>,
pub always_use_affirmation_maps: Option<bool>,
pub require_affirmed_anchor_blocks: Option<bool>,
pub assume_present_anchor_blocks: Option<bool>,
/// At most, how often should the chain-liveness thread
/// wake up the chains-coordinator. Defaults to 300s (5 min).
pub chain_liveness_poll_time_secs: Option<u64>,
Expand Down Expand Up @@ -2509,6 +2512,10 @@ impl NodeConfigFile {
// miners should always try to mine, even if they don't have the anchored
// blocks in the canonical affirmation map. Followers, however, can stall.
require_affirmed_anchor_blocks: self.require_affirmed_anchor_blocks.unwrap_or(!miner),
// as of epoch 3.0, all prepare phases have anchor blocks.
// at the start of epoch 3.0, the chain stalls without anchor blocks.
// only set this to false if you're doing some very extreme testing.
assume_present_anchor_blocks: true,
// chainstate fault_injection activation for hide_blocks.
// you can't set this in the config file.
fault_injection_hide_blocks: false,
Expand Down
1 change: 1 addition & 0 deletions testnet/stacks-node/src/run_loop/nakamoto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ impl RunLoop {
let mut fee_estimator = moved_config.make_fee_estimator();

let coord_config = ChainsCoordinatorConfig {
assume_present_anchor_blocks: moved_config.node.assume_present_anchor_blocks,
always_use_affirmation_maps: moved_config.node.always_use_affirmation_maps,
require_affirmed_anchor_blocks: moved_config
.node
Expand Down
49 changes: 5 additions & 44 deletions testnet/stacks-node/src/run_loop/neon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,7 @@ impl RunLoop {
let mut fee_estimator = moved_config.make_fee_estimator();

let coord_config = ChainsCoordinatorConfig {
assume_present_anchor_blocks: moved_config.node.assume_present_anchor_blocks,
always_use_affirmation_maps: moved_config.node.always_use_affirmation_maps,
require_affirmed_anchor_blocks: moved_config
.node
Expand Down Expand Up @@ -1146,19 +1147,8 @@ impl RunLoop {

let mut sortition_db_height = rc_aligned_height;
let mut burnchain_height = sortition_db_height;
let mut num_sortitions_in_last_cycle = 1;

// prepare to fetch the first reward cycle!
let mut target_burnchain_block_height = cmp::min(
burnchain_config.reward_cycle_to_block_height(
burnchain_config
.block_height_to_reward_cycle(burnchain_height)
.expect("BUG: block height is not in a reward cycle")
+ 1,
),
burnchain.get_headers_height() - 1,
);

debug!("Runloop: Begin main runloop starting a burnchain block {sortition_db_height}");

let mut last_tenure_sortition_height = 0;
Expand Down Expand Up @@ -1186,17 +1176,13 @@ impl RunLoop {

let remote_chain_height = burnchain.get_headers_height() - 1;

// wait for the p2p state-machine to do at least one pass
debug!("Runloop: Wait until Stacks block downloads reach a quiescent state before processing more burnchain blocks"; "remote_chain_height" => remote_chain_height, "local_chain_height" => burnchain_height);

// wait until it's okay to process the next reward cycle's sortitions
let ibd = match self.get_pox_watchdog().pox_sync_wait(
// wait until it's okay to process the next reward cycle's sortitions.
let (ibd, target_burnchain_block_height) = match self.get_pox_watchdog().pox_sync_wait(
&burnchain_config,
&burnchain_tip,
remote_chain_height,
num_sortitions_in_last_cycle,
) {
Ok(ibd) => ibd,
Ok(x) => x,
Err(e) => {
debug!("Runloop: PoX sync wait routine aborted: {e:?}");
continue;
Expand All @@ -1210,9 +1196,6 @@ impl RunLoop {
0.0
};

// will recalculate this in the following loop
num_sortitions_in_last_cycle = 0;

// Download each burnchain block and process their sortitions. This, in turn, will
// cause the node's p2p and relayer threads to go fetch and download Stacks blocks and
// process them. This loop runs for one reward cycle, so that the next pass of the
Expand Down Expand Up @@ -1260,8 +1243,6 @@ impl RunLoop {
"Runloop: New burnchain block height {next_sortition_height} > {sortition_db_height}"
);

let mut sort_count = 0;

debug!("Runloop: block mining until we process all sortitions");
signal_mining_blocked(globals.get_miner_status());

Expand All @@ -1279,9 +1260,6 @@ impl RunLoop {
"Failed to find block in fork processed by burnchain indexer",
)
};
if block.sortition {
sort_count += 1;
}

let sortition_id = &block.sortition_id;

Expand Down Expand Up @@ -1328,9 +1306,8 @@ impl RunLoop {
debug!("Runloop: enable miner after processing sortitions");
signal_mining_ready(globals.get_miner_status());

num_sortitions_in_last_cycle = sort_count;
debug!(
"Runloop: Synchronized sortitions up to block height {next_sortition_height} from {sortition_db_height} (chain tip height is {burnchain_height}); {num_sortitions_in_last_cycle} sortitions"
"Runloop: Synchronized sortitions up to block height {next_sortition_height} from {sortition_db_height} (chain tip height is {burnchain_height})"
);

sortition_db_height = next_sortition_height;
Expand All @@ -1349,22 +1326,6 @@ impl RunLoop {
}
}

// advance one reward cycle at a time.
// If we're still downloading, then this is simply target_burnchain_block_height + reward_cycle_len.
// Otherwise, this is burnchain_tip + reward_cycle_len
let next_target_burnchain_block_height = cmp::min(
burnchain_config.reward_cycle_to_block_height(
burnchain_config
.block_height_to_reward_cycle(target_burnchain_block_height)
.expect("FATAL: burnchain height before system start")
+ 1,
),
remote_chain_height,
);

debug!("Runloop: Advance target burnchain block height from {target_burnchain_block_height} to {next_target_burnchain_block_height} (sortition height {sortition_db_height})");
target_burnchain_block_height = next_target_burnchain_block_height;

if sortition_db_height >= burnchain_height && !ibd {
let canonical_stacks_tip_height =
SortitionDB::get_canonical_burn_chain_tip(burnchain.sortdb_ref().conn())
Expand Down
Loading

0 comments on commit c900752

Please sign in to comment.