Skip to content

Commit 22af7d9

Browse files
authored
Merge pull request #6365 from jcnelson/feat/shadow-marf
Ephemeral MARF
2 parents 87c4373 + 19c4b0e commit 22af7d9

File tree

18 files changed

+2506
-185
lines changed

18 files changed

+2506
-185
lines changed

stackslib/src/chainstate/nakamoto/miner.rs

Lines changed: 63 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ pub struct MinerTenureInfo<'a> {
112112
pub cause: Option<TenureChangeCause>,
113113
pub active_reward_set: boot::RewardSet,
114114
pub tenure_block_commit_opt: Option<LeaderBlockCommitOp>,
115+
pub ephemeral: bool,
115116
}
116117

117118
/// Structure returned from `NakamotoBlockBuilder::build_nakamoto_block` with
@@ -223,14 +224,27 @@ impl NakamotoBlockBuilder {
223224
/// This function should be called before `tenure_begin`.
224225
/// It creates a MinerTenureInfo struct which owns connections to the chainstate and sortition
225226
/// DBs, so that block-processing is guaranteed to terminate before the lives of these handles
226-
/// expire.
227+
/// expire. This is used for normal blocks.
227228
pub fn load_tenure_info<'a>(
228229
&self,
229230
chainstate: &'a mut StacksChainState,
230231
burn_dbconn: &'a SortitionHandleConn,
231232
cause: Option<TenureChangeCause>,
232233
) -> Result<MinerTenureInfo<'a>, Error> {
233-
self.inner_load_tenure_info(chainstate, burn_dbconn, cause, false)
234+
self.inner_load_tenure_info(chainstate, burn_dbconn, cause, false, false)
235+
}
236+
237+
/// This function should be called before `tenure_begin`.
238+
/// It creates a MinerTenureInfo struct which owns connections to the chainstate and sortition
239+
/// DBs, so that block-processing is guaranteed to terminate before the lives of these handles
240+
/// expire. This is used for ephemeral blocks
241+
pub fn load_ephemeral_tenure_info<'a>(
242+
&self,
243+
chainstate: &'a mut StacksChainState,
244+
burn_dbconn: &'a SortitionHandleConn,
245+
cause: Option<TenureChangeCause>,
246+
) -> Result<MinerTenureInfo<'a>, Error> {
247+
self.inner_load_tenure_info(chainstate, burn_dbconn, cause, false, true)
234248
}
235249

236250
/// This function should be called before `tenure_begin`.
@@ -243,8 +257,9 @@ impl NakamotoBlockBuilder {
243257
burn_dbconn: &'a SortitionHandleConn,
244258
cause: Option<TenureChangeCause>,
245259
shadow_block: bool,
260+
ephemeral: bool,
246261
) -> Result<MinerTenureInfo<'a>, Error> {
247-
debug!("Nakamoto miner tenure begin"; "shadow" => shadow_block, "tenure_change" => ?cause);
262+
debug!("Nakamoto miner tenure begin"; "shadow" => shadow_block, "tenure_change" => ?cause, "ephemeral" => ephemeral);
248263

249264
let Some(tenure_election_sn) =
250265
SortitionDB::get_block_snapshot_consensus(burn_dbconn, &self.header.consensus_hash)?
@@ -379,6 +394,7 @@ impl NakamotoBlockBuilder {
379394
coinbase_height,
380395
active_reward_set,
381396
tenure_block_commit_opt,
397+
ephemeral,
382398
})
383399
}
384400

@@ -402,25 +418,47 @@ impl NakamotoBlockBuilder {
402418
clarity_tx,
403419
matured_miner_rewards_opt,
404420
..
405-
} = NakamotoChainState::setup_block(
406-
&mut info.chainstate_tx,
407-
info.clarity_instance,
408-
burn_dbconn,
409-
burn_dbconn.context.first_block_height,
410-
&burn_dbconn.context.pox_constants,
411-
&info.parent_consensus_hash,
412-
&info.parent_header_hash,
413-
info.parent_burn_block_height,
414-
&info.burn_tip,
415-
info.burn_tip_height,
416-
info.cause == Some(TenureChangeCause::BlockFound),
417-
info.coinbase_height,
418-
info.cause == Some(TenureChangeCause::Extended),
419-
&self.header.pox_treatment,
420-
block_commit,
421-
&info.active_reward_set,
422-
Some(self.header.timestamp),
423-
)?;
421+
} = if info.ephemeral {
422+
NakamotoChainState::setup_ephemeral_block(
423+
&mut info.chainstate_tx,
424+
info.clarity_instance,
425+
burn_dbconn,
426+
burn_dbconn.context.first_block_height,
427+
&burn_dbconn.context.pox_constants,
428+
&info.parent_consensus_hash,
429+
&info.parent_header_hash,
430+
info.parent_burn_block_height,
431+
&info.burn_tip,
432+
info.burn_tip_height,
433+
info.cause == Some(TenureChangeCause::BlockFound),
434+
info.coinbase_height,
435+
info.cause == Some(TenureChangeCause::Extended),
436+
&self.header.pox_treatment,
437+
block_commit,
438+
&info.active_reward_set,
439+
Some(self.header.timestamp),
440+
)
441+
} else {
442+
NakamotoChainState::setup_block(
443+
&mut info.chainstate_tx,
444+
info.clarity_instance,
445+
burn_dbconn,
446+
burn_dbconn.context.first_block_height,
447+
&burn_dbconn.context.pox_constants,
448+
&info.parent_consensus_hash,
449+
&info.parent_header_hash,
450+
info.parent_burn_block_height,
451+
&info.burn_tip,
452+
info.burn_tip_height,
453+
info.cause == Some(TenureChangeCause::BlockFound),
454+
info.coinbase_height,
455+
info.cause == Some(TenureChangeCause::Extended),
456+
&self.header.pox_treatment,
457+
block_commit,
458+
&info.active_reward_set,
459+
Some(self.header.timestamp),
460+
)
461+
}?;
424462
self.matured_miner_rewards_opt = matured_miner_rewards_opt;
425463
Ok(clarity_tx)
426464
}
@@ -466,10 +504,11 @@ impl NakamotoBlockBuilder {
466504
};
467505

468506
test_debug!(
469-
"\n\nMined Nakamoto block {}, {} transactions, state root is {}\n",
507+
"\n\nMined Nakamoto block {}, {} transactions, state root is {}\nBlock: {:?}",
470508
block.header.block_hash(),
471509
block.txs.len(),
472-
state_root_hash
510+
state_root_hash,
511+
&block
473512
);
474513

475514
debug!(

stackslib/src/chainstate/nakamoto/mod.rs

Lines changed: 68 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3900,7 +3900,8 @@ impl NakamotoChainState {
39003900
}
39013901

39023902
/// Begin block-processing for a normal block and return all of the pre-processed state within a
3903-
/// `SetupBlockResult`. Used by the Nakamoto miner, and called by Self::setup_normal_block()
3903+
/// `SetupBlockResult`. Used by the Nakamoto miner, and called by
3904+
/// Self::setup_normal_block_processing()
39043905
pub fn setup_block<'a, 'b>(
39053906
chainstate_tx: &'b mut ChainstateTx,
39063907
clarity_instance: &'a mut ClarityInstance,
@@ -3937,6 +3938,49 @@ impl NakamotoChainState {
39373938
coinbase_height,
39383939
tenure_extend,
39393940
timestamp,
3941+
false,
3942+
)
3943+
}
3944+
3945+
/// Begin block-processing for a replay of a normal block and return all of the pre-processed state within a
3946+
/// `SetupBlockResult`. Used by the block replay logic, and called by Self::setup_normal_block_processing()
3947+
pub fn setup_ephemeral_block<'a, 'b>(
3948+
chainstate_tx: &'b mut ChainstateTx,
3949+
clarity_instance: &'a mut ClarityInstance,
3950+
sortition_dbconn: &'b dyn SortitionDBRef,
3951+
first_block_height: u64,
3952+
pox_constants: &PoxConstants,
3953+
parent_consensus_hash: &ConsensusHash,
3954+
parent_header_hash: &BlockHeaderHash,
3955+
parent_burn_height: u32,
3956+
burn_header_hash: &BurnchainHeaderHash,
3957+
burn_header_height: u32,
3958+
new_tenure: bool,
3959+
coinbase_height: u64,
3960+
tenure_extend: bool,
3961+
block_bitvec: &BitVec<4000>,
3962+
tenure_block_commit: &LeaderBlockCommitOp,
3963+
active_reward_set: &RewardSet,
3964+
timestamp: Option<u64>,
3965+
) -> Result<SetupBlockResult<'a, 'b>, ChainstateError> {
3966+
// this block's bitvec header must match the miner's block commit punishments
3967+
Self::check_pox_bitvector(block_bitvec, tenure_block_commit, active_reward_set)?;
3968+
Self::inner_setup_block(
3969+
chainstate_tx,
3970+
clarity_instance,
3971+
sortition_dbconn,
3972+
first_block_height,
3973+
pox_constants,
3974+
parent_consensus_hash,
3975+
parent_header_hash,
3976+
parent_burn_height,
3977+
burn_header_hash,
3978+
burn_header_height,
3979+
new_tenure,
3980+
coinbase_height,
3981+
tenure_extend,
3982+
timestamp,
3983+
true,
39403984
)
39413985
}
39423986

@@ -4063,6 +4107,7 @@ impl NakamotoChainState {
40634107
/// * coinbase_height: the number of tenures that this block confirms (including epoch2 blocks)
40644108
/// (this is equivalent to the number of coinbases)
40654109
/// * tenure_extend: whether or not to reset the tenure's ongoing execution cost
4110+
/// * ephemeral: whether or not to begin an ephemeral block (i.e. which won't hit disk)
40664111
///
40674112
/// Returns clarity_tx, list of receipts, microblock execution cost,
40684113
/// microblock fees, microblock burns, list of microblock tx receipts,
@@ -4083,6 +4128,7 @@ impl NakamotoChainState {
40834128
coinbase_height: u64,
40844129
tenure_extend: bool,
40854130
timestamp: Option<u64>,
4131+
ephemeral: bool,
40864132
) -> Result<SetupBlockResult<'a, 'b>, ChainstateError> {
40874133
let parent_index_hash = StacksBlockId::new(parent_consensus_hash, parent_header_hash);
40884134
let parent_sortition_id = sortition_dbconn
@@ -4132,15 +4178,27 @@ impl NakamotoChainState {
41324178
parent_cost_total
41334179
};
41344180

4135-
let mut clarity_tx = StacksChainState::chainstate_block_begin(
4136-
chainstate_tx,
4137-
clarity_instance,
4138-
sortition_dbconn.as_burn_state_db(),
4139-
parent_consensus_hash,
4140-
parent_header_hash,
4141-
&MINER_BLOCK_CONSENSUS_HASH,
4142-
&MINER_BLOCK_HEADER_HASH,
4143-
);
4181+
let mut clarity_tx = if ephemeral {
4182+
StacksChainState::chainstate_ephemeral_block_begin(
4183+
chainstate_tx,
4184+
clarity_instance,
4185+
sortition_dbconn.as_burn_state_db(),
4186+
&parent_consensus_hash,
4187+
&parent_header_hash,
4188+
&MINER_BLOCK_CONSENSUS_HASH,
4189+
&MINER_BLOCK_HEADER_HASH,
4190+
)
4191+
} else {
4192+
StacksChainState::chainstate_block_begin(
4193+
chainstate_tx,
4194+
clarity_instance,
4195+
sortition_dbconn.as_burn_state_db(),
4196+
&parent_consensus_hash,
4197+
&parent_header_hash,
4198+
&MINER_BLOCK_CONSENSUS_HASH,
4199+
&MINER_BLOCK_HEADER_HASH,
4200+
)
4201+
};
41444202

41454203
// now that we have access to the ClarityVM, we can account for reward deductions from
41464204
// PoisonMicroblocks if we have new rewards scheduled

stackslib/src/chainstate/nakamoto/shadow.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,7 @@ impl NakamotoChainState {
415415
coinbase_height,
416416
tenure_extend,
417417
None,
418+
false,
418419
)
419420
}
420421
}
@@ -432,7 +433,7 @@ impl NakamotoBlockBuilder {
432433
burn_dbconn: &'a SortitionHandleConn,
433434
cause: Option<TenureChangeCause>,
434435
) -> Result<MinerTenureInfo<'a>, Error> {
435-
self.inner_load_tenure_info(chainstate, burn_dbconn, cause, true)
436+
self.inner_load_tenure_info(chainstate, burn_dbconn, cause, true, false)
436437
}
437438

438439
/// Begin/resume mining a shadow tenure's transactions.

stackslib/src/chainstate/stacks/boot/contract_tests.rs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,11 @@ use crate::chainstate::stacks::boot::{
3131
};
3232
use crate::chainstate::stacks::index::ClarityMarfTrieId;
3333
use crate::chainstate::stacks::{C32_ADDRESS_VERSION_TESTNET_SINGLESIG, *};
34-
use crate::clarity_vm::clarity::{ClarityBlockConnection, Error as ClarityError};
35-
use crate::clarity_vm::database::marf::{MarfedKV, WritableMarfStore};
34+
use crate::clarity_vm::clarity::{
35+
ClarityBlockConnection, ClarityMarfStore, ClarityMarfStoreTransaction, Error as ClarityError,
36+
WritableMarfStore,
37+
};
38+
use crate::clarity_vm::database::marf::MarfedKV;
3639
use crate::core::{
3740
StacksEpoch, StacksEpochId, BITCOIN_REGTEST_FIRST_BLOCK_HASH,
3841
BITCOIN_REGTEST_FIRST_BLOCK_HEIGHT, BITCOIN_REGTEST_FIRST_BLOCK_TIMESTAMP,
@@ -143,15 +146,15 @@ impl ClarityTestSim {
143146
&mut self,
144147
new_tenure: bool,
145148
) -> (
146-
WritableMarfStore,
149+
Box<dyn WritableMarfStore + '_>,
147150
TestSimHeadersDB,
148151
TestSimBurnStateDB,
149152
StacksEpochId,
150153
) {
151-
let mut store = self.marf.begin(
154+
let mut store: Box<dyn WritableMarfStore> = Box::new(self.marf.begin(
152155
&StacksBlockId(test_sim_height_to_hash(self.block_height, self.fork)),
153156
&StacksBlockId(test_sim_height_to_hash(self.block_height + 1, self.fork)),
154-
);
157+
));
155158

156159
self.block_height += 1;
157160
if new_tenure {
@@ -235,8 +238,8 @@ impl ClarityTestSim {
235238
self.execute_next_block_with_tenure(true, f)
236239
}
237240

238-
fn check_and_bump_epoch(
239-
store: &mut WritableMarfStore,
241+
fn check_and_bump_epoch<'a>(
242+
store: &mut Box<dyn WritableMarfStore + 'a>,
240243
headers_db: &TestSimHeadersDB,
241244
burn_db: &dyn BurnStateDB,
242245
) -> StacksEpochId {
@@ -263,10 +266,10 @@ impl ClarityTestSim {
263266
where
264267
F: FnOnce(&mut OwnedEnvironment) -> R,
265268
{
266-
let mut store = self.marf.begin(
269+
let mut store: Box<dyn WritableMarfStore> = Box::new(self.marf.begin(
267270
&StacksBlockId(test_sim_height_to_hash(parent_height, self.fork)),
268271
&StacksBlockId(test_sim_height_to_hash(parent_height + 1, self.fork + 1)),
269-
);
272+
));
270273

271274
let r = {
272275
let headers_db = TestSimHeadersDB {

0 commit comments

Comments
 (0)