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

Feat: Shadow block recovery mechanism #5296

Merged
merged 104 commits into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
104 commits
Select commit Hold shift + click to select a range
4f2c74f
fix/5274: add test coverage for microforks
jcnelson Oct 7, 2024
62d2629
chore: separate load_tenure_info() into inner_load_tenure_info(), so …
jcnelson Oct 10, 2024
e274770
feat: enable shadow blocks -- blocks that are added to the chainstate…
jcnelson Oct 10, 2024
a81f6f4
chore: update staging DB to add code to insert and query shadow block…
jcnelson Oct 10, 2024
a01b36c
chore: accomodate shadow blocks when validating a tenure against a so…
jcnelson Oct 10, 2024
f6e2b8d
chore: static transaction validation test coverage for data in shadow…
jcnelson Oct 10, 2024
9f9ff79
chore: add functionality to mine shadow block tenures and build atop …
jcnelson Oct 10, 2024
f07aca5
chore: don't divide by zero when processing a shadow tenure coinbase …
jcnelson Oct 10, 2024
de26a08
chore: try to load blocks from chainstate if we have them, so we don'…
jcnelson Oct 10, 2024
345f63a
chore: when getting tenure start/end blocks, see if we have them loca…
jcnelson Oct 10, 2024
136fd0f
chore: drive downloaders from chainstate data
jcnelson Oct 10, 2024
1cc53b7
chore: load tenure data from chainstate if we have it
jcnelson Oct 10, 2024
2c954dc
chore: log the neighbor address of the remote peer when we get back a…
jcnelson Oct 10, 2024
13d7212
chore: test that shadow tenures are reported as 1's in a nakamoto inv…
jcnelson Oct 10, 2024
5cf2946
Merge branch 'develop' into fix/5285
jcnelson Oct 11, 2024
6531b2c
Merge branch 'fix/5274' into fix/5285
jcnelson Oct 11, 2024
5154771
feat: epoch 3.0 supports shadow blocks
jcnelson Oct 18, 2024
e700ffa
feat: mock coinbaes in test burnchain blocks, since block-commits tha…
jcnelson Oct 18, 2024
81699d6
feat: a block-commit mined in epoch 3.x whose parent_vtxindex is 0 is…
jcnelson Oct 18, 2024
8b44fe2
feat: count shadow tenures as prepare-phase sortitions even if they'r…
jcnelson Oct 18, 2024
9d7f894
chore: make some tenure-mining code fallible so we can test resuming …
jcnelson Oct 18, 2024
5fe06ad
chore: more debug
jcnelson Oct 18, 2024
81b17ec
feat: fix process_next_nakamoto_block() to treat shadow blocks as una…
jcnelson Oct 18, 2024
fdbec99
feat: getters for shadow blocks and shadow tenures, and also, require…
jcnelson Oct 18, 2024
4f30e51
chore: make some mining code fallible so we can handle null-miner win…
jcnelson Oct 18, 2024
18d11c9
chore: API sync
jcnelson Oct 18, 2024
0f96ead
fix: pass the tenure IDs for the tenure start/end blocks to tenure do…
jcnelson Oct 18, 2024
aba7ca9
chore: with_dbs() helper
jcnelson Oct 18, 2024
4ff1a39
chore: drop "relayed" shadow blocks, since they get passed from the d…
jcnelson Oct 18, 2024
0b0f712
chore: test coverage for downloading tenures that start/stop with a s…
jcnelson Oct 18, 2024
4511362
chore: test that shadow blocks are part of the block inventory
jcnelson Oct 18, 2024
c077cf7
chore: some minnig functions are fallible now
jcnelson Oct 18, 2024
3e575e0
chore: always use affirmation maps from now on
jcnelson Oct 18, 2024
6101b9f
Merge branch 'develop' into fix/5285
jcnelson Oct 18, 2024
1035b89
chore: fix compiler warnings
jcnelson Oct 18, 2024
acd8732
chore: cargo fmt
jcnelson Oct 18, 2024
3a46282
fix: always use affirmation maps now that mainline testnet uses bitco…
jcnelson Oct 18, 2024
bdd5ea3
chore: address @kantai's PR feedback by refactoring the shadow block …
jcnelson Oct 21, 2024
47761f2
Merge branch 'develop' into fix/5285
jcnelson Oct 21, 2024
9ad17f1
chore: print out assert failure
jcnelson Oct 21, 2024
c478e29
chore: move staging blocks' shadow code to shadow.rs
jcnelson Oct 21, 2024
b7d5d4f
chore: cargo fmt
jcnelson Oct 21, 2024
fb54e4c
chore: add unit test coverage for shadow DB methods and validation logic
jcnelson Oct 22, 2024
4dd6630
Merge branch 'develop' into fix/5285
jcnelson Oct 22, 2024
8a5e6bd
Merge branch 'develop' into fix/5285
jcnelson Oct 22, 2024
81920f1
chore: fix unit test due to vtxindex=0 not being allowed by default i…
jcnelson Oct 22, 2024
8939fd3
Merge branch 'fix/5285' of https://github.com/stacks-network/stacks-b…
jcnelson Oct 22, 2024
19044ef
Merge branch 'develop' into fix/5285
jcnelson Oct 23, 2024
1597209
chore: add nakamoto testnet pox settings
jcnelson Oct 23, 2024
fbec882
chore: expose NakamotoBlockBuilder::get_account()
jcnelson Oct 23, 2024
7e26c01
feat: add `get-account`, `get-nakamoto-tip`, `make-shadow-block`, and…
jcnelson Oct 23, 2024
dcab2b6
chore: typo
jcnelson Oct 24, 2024
8e8981e
chore: typo
jcnelson Oct 24, 2024
0865787
Merge branch 'develop' into fix/5285
jcnelson Oct 24, 2024
9bcee64
fix: use correct pox params
jcnelson Oct 24, 2024
850d00b
chore: move shadow block creation and replay into separate functions …
jcnelson Oct 24, 2024
fb391fc
chore: make function public
jcnelson Oct 24, 2024
0f29c68
chore: shadow-chainstate-repair and shadow-chainstate-patch
jcnelson Oct 24, 2024
e8da151
fix: the parent can be a shadow block and thus not have a commit
jcnelson Oct 24, 2024
4851c3c
chore: integration test
jcnelson Oct 24, 2024
8fff6bd
chore: run integration test in CI
jcnelson Oct 24, 2024
e72a266
fix: use correct pox params
jcnelson Oct 24, 2024
05ac928
Merge branch 'fix/5285' into feat/shadow-block-tooling
jcnelson Oct 24, 2024
05a99fa
chore: typo
jcnelson Oct 24, 2024
bfbfbf7
chore: cargo fmt
jcnelson Oct 24, 2024
f7a06d4
Merge branch 'develop' into fix/5285
jcnelson Oct 25, 2024
8743b0b
chore: fix compile issue
jcnelson Oct 25, 2024
84c982c
Adding stacks-signer binary to image
wileyj Oct 25, 2024
c75ed39
chore: read/write
jcnelson Oct 25, 2024
b98239d
fix: load blocks json from file
jcnelson Oct 25, 2024
4153cec
Merge branch 'feat/shadow-block-tooling' of https://github.com/stacks…
jcnelson Oct 25, 2024
ca17ede
chore: don't use docstring
jcnelson Oct 25, 2024
439abdb
fix: accomodate shadow blocks which have no sortition but do have a s…
jcnelson Oct 25, 2024
69eb7ac
fix: use a real signer to verify that we can resume mining atop shado…
jcnelson Oct 25, 2024
3430a81
fix: advance cursor before depth check
jcnelson Oct 28, 2024
1578014
chore: handle shadow block case
jcnelson Oct 28, 2024
0ace927
chore: log HTTP RPC error values at debug level
jcnelson Oct 28, 2024
7bd1ae8
chore: info when post-shadow tenures happen
jcnelson Oct 28, 2024
7341a4b
chore: cargo fmt
jcnelson Oct 28, 2024
b30c0b4
chore: halt block-processing while we process shadow blocks
jcnelson Oct 28, 2024
ab605d9
Merge branch 'develop' into fix/5285
jcnelson Nov 8, 2024
5970a57
Merge branch 'fix/5285' into feat/shadow-block-tooling
jcnelson Nov 8, 2024
5f83847
chore: search depth only increases with sortitions, not shadow blocks
jcnelson Nov 9, 2024
4a105e4
Merge branch 'develop' into fix/5285
jcnelson Nov 11, 2024
6eaf1ed
Merge branch 'develop' into fix/5285
jcnelson Nov 11, 2024
ad01a76
Merge branch 'fix/5285' into feat/shadow-block-tooling
jcnelson Nov 11, 2024
62f5130
Merge branch 'develop' into fix/5285
jcnelson Nov 12, 2024
28e1be4
chore: fix failing integration test and unstage accidentally-added file
jcnelson Nov 12, 2024
f43ede5
Merge branch 'develop' into fix/5285
jcnelson Nov 15, 2024
08365d6
chore: fix slow unit test
jcnelson Nov 15, 2024
75e409d
chore: fix test
jcnelson Nov 16, 2024
c60b569
Merge branch 'fix/5285' of https://github.com/stacks-network/stacks-b…
jcnelson Nov 16, 2024
34d658a
Merge branch 'develop' into fix/5285
jcnelson Nov 19, 2024
a99a6d1
Merge branch 'fix/5285' into feat/shadow-block-tooling
jcnelson Nov 19, 2024
eebf10d
move net::api::tests::postblock_proposal::test_try_make_response to p…
wileyj Nov 19, 2024
a7e89bc
Merge branch 'develop' into fix/5285
jcnelson Nov 19, 2024
a3cdab6
Merge branch 'fix/5285' into feat/shadow-block-tooling
jcnelson Nov 19, 2024
82c36d7
move net::api::tests::postblock_proposal::test_try_make_response to p…
wileyj Nov 19, 2024
d038752
Merge pull request #5362 from stacks-network/feat/shadow-block-tooling
wileyj Nov 20, 2024
edc8b67
Merge branch 'develop' into fix/5285
jcnelson Nov 20, 2024
212914b
Merge branch 'develop' into fix/5285
jcnelson Nov 21, 2024
6ddfd6c
Merge branch 'develop' into fix/5285
jcnelson Nov 21, 2024
a3640b4
chore: PR feedback
jcnelson Nov 21, 2024
ca5ab06
chore: fix compile error
jcnelson Nov 22, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/actions/dockerfiles/Dockerfile.debian-source
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,5 @@ RUN --mount=type=tmpfs,target=${BUILD_DIR} cp -R /src/. ${BUILD_DIR}/ \
&& cp -R ${BUILD_DIR}/target/${TARGET}/release/. /out

FROM --platform=${TARGETPLATFORM} debian:bookworm
COPY --from=build /out/stacks-node /out/stacks-signer /bin/
COPY --from=build /out/stacks-node /out/stacks-signer /out/stacks-inspect /bin/
CMD ["stacks-node", "mainnet"]
1 change: 1 addition & 0 deletions .github/workflows/bitcoin-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ jobs:
- tests::nakamoto_integrations::utxo_check_on_startup_panic
- tests::nakamoto_integrations::utxo_check_on_startup_recover
- tests::nakamoto_integrations::v3_signer_api_endpoint
- tests::nakamoto_integrations::test_shadow_recovery
- tests::nakamoto_integrations::signer_chainstate
- tests::nakamoto_integrations::clarity_cost_spend_down
- tests::nakamoto_integrations::v3_blockbyheight_api_endpoint
Expand Down
15 changes: 15 additions & 0 deletions stacks-common/src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,21 @@ impl StacksEpochId {
}
}

/// Whether or not this epoch supports shadow blocks
pub fn supports_shadow_blocks(&self) -> bool {
match self {
StacksEpochId::Epoch10
| StacksEpochId::Epoch20
| StacksEpochId::Epoch2_05
| StacksEpochId::Epoch21
| StacksEpochId::Epoch22
| StacksEpochId::Epoch23
| StacksEpochId::Epoch24
| StacksEpochId::Epoch25 => false,
StacksEpochId::Epoch30 => true,
}
}

/// Does this epoch support unlocking PoX contributors that miss a slot?
///
/// Epoch 2.0 - 2.05 didn't support this feature, but they weren't epoch-guarded on it. Instead,
Expand Down
5 changes: 5 additions & 0 deletions stackslib/src/burnchains/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,7 @@ impl PoxConstants {
)
}

// NOTE: this is the *old* pre-Nakamoto testnet
pub fn testnet_default() -> PoxConstants {
PoxConstants::new(
POX_REWARD_CYCLE_LENGTH / 2, // 1050
Expand All @@ -468,6 +469,10 @@ impl PoxConstants {
) // total liquid supply is 40000000000000000 µSTX
}

pub fn nakamoto_testnet_default() -> PoxConstants {
PoxConstants::new(900, 100, 51, 100, 0, u64::MAX, u64::MAX, 242, 243, 246, 244)
}
hstove marked this conversation as resolved.
Show resolved Hide resolved

// TODO: add tests from mutation testing results #4838
#[cfg_attr(test, mutants::skip)]
pub fn regtest_default() -> PoxConstants {
Expand Down
80 changes: 68 additions & 12 deletions stackslib/src/burnchains/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -351,10 +351,30 @@ impl TestMinerFactory {

impl TestBurnchainBlock {
pub fn new(parent_snapshot: &BlockSnapshot, fork_id: u64) -> TestBurnchainBlock {
let burn_header_hash = BurnchainHeaderHash::from_test_data(
parent_snapshot.block_height + 1,
&parent_snapshot.index_root,
fork_id,
);
TestBurnchainBlock {
parent_snapshot: parent_snapshot.clone(),
block_height: parent_snapshot.block_height + 1,
txs: vec![],
txs: vec![
// make sure that no block-commit gets vtxindex == 0 unless explicitly structured.
// This prestx mocks a burnchain coinbase
BlockstackOperationType::PreStx(PreStxOp {
output: StacksAddress::burn_address(false),
txid: Txid::from_test_data(
parent_snapshot.block_height + 1,
0,
&burn_header_hash,
128,
),
vtxindex: 0,
block_height: parent_snapshot.block_height + 1,
burn_header_hash,
}),
],
fork_id: fork_id,
timestamp: get_epoch_time_secs(),
}
Expand Down Expand Up @@ -397,6 +417,7 @@ impl TestBurnchainBlock {
parent_block_snapshot: Option<&BlockSnapshot>,
new_seed: Option<VRFSeed>,
epoch_marker: u8,
parent_is_shadow: bool,
) -> LeaderBlockCommitOp {
let pubks = miner
.privks
Expand Down Expand Up @@ -435,6 +456,13 @@ impl TestBurnchainBlock {
)
.expect("FATAL: failed to read block commit");

if parent_is_shadow {
assert!(
get_commit_res.is_none(),
"FATAL: shadow parent should not have a block-commit"
);
}

let input = SortitionDB::get_last_block_commit_by_sender(ic.conn(), &apparent_sender)
.unwrap()
.map(|commit| (commit.txid.clone(), 1 + (commit.commit_outs.len() as u32)))
Expand All @@ -454,7 +482,8 @@ impl TestBurnchainBlock {
block_hash,
self.block_height,
&new_seed,
&parent,
parent.block_height as u32,
parent.vtxindex as u16,
leader_key.block_height as u32,
leader_key.vtxindex as u16,
burn_fee,
Expand All @@ -464,16 +493,42 @@ impl TestBurnchainBlock {
txop
}
None => {
// initial
let txop = LeaderBlockCommitOp::initial(
block_hash,
self.block_height,
&new_seed,
leader_key,
burn_fee,
&input,
&apparent_sender,
);
let txop = if parent_is_shadow {
test_debug!(
"Block-commit for {} (burn height {}) builds on shadow sortition",
block_hash,
self.block_height
);

LeaderBlockCommitOp::new(
block_hash,
self.block_height,
&new_seed,
last_snapshot_with_sortition.block_height as u32,
0,
leader_key.block_height as u32,
leader_key.vtxindex as u16,
burn_fee,
&input,
&apparent_sender,
)
} else {
// initial
test_debug!(
"Block-commit for {} (burn height {}) builds on genesis",
block_hash,
self.block_height,
);
LeaderBlockCommitOp::initial(
block_hash,
self.block_height,
&new_seed,
leader_key,
burn_fee,
&input,
&apparent_sender,
)
};
txop
}
};
Expand Down Expand Up @@ -517,6 +572,7 @@ impl TestBurnchainBlock {
parent_block_snapshot,
None,
STACKS_EPOCH_2_4_MARKER,
false,
)
}

Expand Down
26 changes: 20 additions & 6 deletions stackslib/src/chainstate/burn/operations/leader_block_commit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,8 @@ impl LeaderBlockCommitOp {
block_header_hash: &BlockHeaderHash,
block_height: u64,
new_seed: &VRFSeed,
parent: &LeaderBlockCommitOp,
parent_block_height: u32,
parent_vtxindex: u16,
key_block_ptr: u32,
key_vtxindex: u16,
burn_fee: u64,
Expand All @@ -148,8 +149,8 @@ impl LeaderBlockCommitOp {
new_seed: new_seed.clone(),
key_block_ptr: key_block_ptr,
key_vtxindex: key_vtxindex,
parent_block_ptr: parent.block_height as u32,
parent_vtxindex: parent.vtxindex as u16,
parent_block_ptr: parent_block_height,
parent_vtxindex: parent_vtxindex,
memo: vec![],
burn_fee: burn_fee,
input: input.clone(),
Expand Down Expand Up @@ -696,8 +697,19 @@ impl LeaderBlockCommitOp {
// is descendant
let directly_descended_from_anchor = epoch_id.block_commits_to_parent()
&& self.block_header_hash == reward_set_info.anchor_block;
let descended_from_anchor = directly_descended_from_anchor || tx
.descended_from(parent_block_height, &reward_set_info.anchor_block)

// second, if we're in a nakamoto epoch, and the parent block has vtxindex 0 (i.e. the
// coinbase of the burnchain block), then assume that this block descends from the anchor
// block for the purposes of validating its PoX payouts. The block validation logic will
// check that the parent block is indeed a shadow block, and that `self.parent_block_ptr`
// points to the shadow block's tenure's burnchain block.
let maybe_shadow_parent = epoch_id.supports_shadow_blocks()
&& self.parent_block_ptr != 0
&& self.parent_vtxindex == 0;

let descended_from_anchor = directly_descended_from_anchor
|| maybe_shadow_parent
|| tx.descended_from(parent_block_height, &reward_set_info.anchor_block)
.map_err(|e| {
error!("Failed to check whether parent (height={}) is descendent of anchor block={}: {}",
parent_block_height, &reward_set_info.anchor_block, e);
Expand Down Expand Up @@ -1031,10 +1043,12 @@ impl LeaderBlockCommitOp {
return Err(op_error::BlockCommitNoParent);
} else if self.parent_block_ptr != 0 || self.parent_vtxindex != 0 {
// not building off of genesis, so the parent block must exist
// unless the parent is a shadow block
let has_parent = tx
.get_block_commit_parent(parent_block_height, self.parent_vtxindex.into(), &tx_tip)?
.is_some();
if !has_parent {
let maybe_shadow_block = self.parent_vtxindex == 0 && epoch_id.supports_shadow_blocks();
hstove marked this conversation as resolved.
Show resolved Hide resolved
if !has_parent && !maybe_shadow_block {
warn!("Invalid block commit: no parent block in this fork";
"apparent_sender" => %apparent_sender_repr
);
Expand Down
29 changes: 28 additions & 1 deletion stackslib/src/chainstate/nakamoto/coordinator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ use crate::monitoring::increment_stx_blocks_processed_counter;
use crate::net::Error as NetError;
use crate::util_lib::db::Error as DBError;

#[cfg(any(test, feature = "testing"))]
pub static TEST_COORDINATOR_STALL: std::sync::Mutex<Option<bool>> = std::sync::Mutex::new(None);

#[cfg(test)]
pub mod tests;

Expand Down Expand Up @@ -484,7 +487,14 @@ pub fn load_nakamoto_reward_set<U: RewardSetProvider>(
let Some(anchor_block_header) = prepare_phase_sortitions
.into_iter()
.find_map(|sn| {
if !sn.sortition {
let shadow_tenure = match chain_state.nakamoto_blocks_db().is_shadow_tenure(&sn.consensus_hash) {
Ok(x) => x,
Err(e) => {
return Some(Err(e));
}
};

if !sn.sortition && !shadow_tenure {
return None
}

Expand Down Expand Up @@ -757,6 +767,21 @@ impl<
true
}

#[cfg(any(test, feature = "testing"))]
fn fault_injection_pause_nakamoto_block_processing() {
if *TEST_COORDINATOR_STALL.lock().unwrap() == Some(true) {
// Do an extra check just so we don't log EVERY time.
warn!("Coordinator is stalled due to testing directive");
while *TEST_COORDINATOR_STALL.lock().unwrap() == Some(true) {
std::thread::sleep(std::time::Duration::from_millis(10));
}
warn!("Coordinator is no longer stalled due to testing directive. Continuing...");
}
}

#[cfg(not(any(test, feature = "testing")))]
fn fault_injection_pause_nakamoto_block_processing() {}

/// Handle one or more new Nakamoto Stacks blocks.
/// If we process a PoX anchor block, then return its block hash. This unblocks processing the
/// next reward cycle's burnchain blocks. Subsequent calls to this function will terminate
Expand All @@ -769,6 +794,8 @@ impl<
);

loop {
Self::fault_injection_pause_nakamoto_block_processing();

// process at most one block per loop pass
let mut processed_block_receipt = match NakamotoChainState::process_next_nakamoto_block(
&mut self.chain_state_db,
Expand Down
Loading