Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/5502 #5544

Merged
merged 13 commits into from
Dec 18, 2024
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
Loading