Skip to content

Commit 09c4b06

Browse files
authored
Merge pull request #5060 from jbencin/feat/neon-miner-replay
feat: Neon mock miner replay
2 parents 6937df5 + aae44ab commit 09c4b06

File tree

14 files changed

+960
-387
lines changed

14 files changed

+960
-387
lines changed

.github/workflows/bitcoin-tests.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ jobs:
7272
- tests::neon_integrations::confirm_unparsed_ongoing_ops
7373
- tests::neon_integrations::min_txs
7474
- tests::neon_integrations::vote_for_aggregate_key_burn_op_test
75+
- tests::neon_integrations::mock_miner_replay
7576
- tests::epoch_25::microblocks_disabled
7677
- tests::should_succeed_handling_malformed_and_valid_txs
7778
- tests::nakamoto_integrations::simple_neon_integration

stacks-common/src/types/chainstate.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,11 @@ impl_byte_array_serde!(TrieHash);
3030

3131
pub const TRIEHASH_ENCODED_SIZE: usize = 32;
3232

33-
#[derive(Serialize, Deserialize)]
3433
pub struct BurnchainHeaderHash(pub [u8; 32]);
3534
impl_array_newtype!(BurnchainHeaderHash, u8, 32);
3635
impl_array_hexstring_fmt!(BurnchainHeaderHash);
3736
impl_byte_array_newtype!(BurnchainHeaderHash, u8, 32);
37+
impl_byte_array_serde!(BurnchainHeaderHash);
3838

3939
pub struct BlockHeaderHash(pub [u8; 32]);
4040
impl_array_newtype!(BlockHeaderHash, u8, 32);

stacks-common/src/util/macros.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,28 @@ macro_rules! impl_byte_array_serde {
617617
};
618618
}
619619

620+
#[allow(unused_macros)]
621+
#[macro_export]
622+
macro_rules! impl_file_io_serde_json {
623+
($thing:ident) => {
624+
impl $thing {
625+
pub fn serialize_to_file<P>(&self, path: P) -> Result<(), std::io::Error>
626+
where
627+
P: AsRef<std::path::Path>,
628+
{
629+
$crate::util::serialize_json_to_file(self, path)
630+
}
631+
632+
pub fn deserialize_from_file<P>(path: P) -> Result<Self, std::io::Error>
633+
where
634+
P: AsRef<std::path::Path>,
635+
{
636+
$crate::util::deserialize_json_from_file(path)
637+
}
638+
}
639+
};
640+
}
641+
620642
// print debug statements while testing
621643
#[allow(unused_macros)]
622644
#[macro_export]

stacks-common/src/util/mod.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ pub mod uint;
2828
pub mod vrf;
2929

3030
use std::collections::HashMap;
31+
use std::fs::File;
32+
use std::io::{BufReader, BufWriter, Write};
33+
use std::path::Path;
3134
use std::time::{SystemTime, UNIX_EPOCH};
3235
use std::{error, fmt, thread, time};
3336

@@ -120,3 +123,26 @@ pub mod db_common {
120123
true
121124
}
122125
}
126+
127+
/// Write any `serde_json` object directly to a file
128+
pub fn serialize_json_to_file<J, P>(json: &J, path: P) -> Result<(), std::io::Error>
129+
where
130+
J: ?Sized + serde::Serialize,
131+
P: AsRef<Path>,
132+
{
133+
let file = File::create(path)?;
134+
let mut writer = BufWriter::new(file);
135+
serde_json::to_writer(&mut writer, json)?;
136+
writer.flush()
137+
}
138+
139+
/// Read any `serde_json` object directly from a file
140+
pub fn deserialize_json_from_file<J, P>(path: P) -> Result<J, std::io::Error>
141+
where
142+
J: serde::de::DeserializeOwned,
143+
P: AsRef<Path>,
144+
{
145+
let file = File::open(path)?;
146+
let reader = BufReader::new(file);
147+
serde_json::from_reader::<_, J>(reader).map_err(std::io::Error::from)
148+
}

stackslib/src/chainstate/stacks/block.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,14 @@ impl StacksBlock {
651651
pub fn has_microblock_parent(&self) -> bool {
652652
self.header.has_microblock_parent()
653653
}
654+
655+
/// Returns size in bytes of `StacksMessageCodec` representation
656+
/// Note that this will serialize the block, so don't call if there is a better way to get block size
657+
pub fn block_size(&self) -> Result<usize, codec_error> {
658+
let mut buf = vec![];
659+
self.consensus_serialize(&mut buf)?;
660+
Ok(buf.len())
661+
}
654662
}
655663

656664
impl StacksMessageCodec for StacksMicroblockHeader {

stackslib/src/chainstate/stacks/db/blocks.rs

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3716,30 +3716,44 @@ impl StacksChainState {
37163716
blocks_conn: &DBConn,
37173717
staging_block: &StagingBlock,
37183718
) -> Result<Option<Vec<StacksMicroblock>>, Error> {
3719-
if staging_block.parent_microblock_hash == EMPTY_MICROBLOCK_PARENT_HASH
3720-
&& staging_block.parent_microblock_seq == 0
3721-
{
3719+
Self::inner_find_parent_microblock_stream(
3720+
blocks_conn,
3721+
&staging_block.anchored_block_hash,
3722+
&staging_block.parent_anchored_block_hash,
3723+
&staging_block.parent_consensus_hash,
3724+
&staging_block.parent_microblock_hash,
3725+
staging_block.parent_microblock_seq,
3726+
)
3727+
}
3728+
3729+
/// Allow `find_parent_microblock_stream()` to be called without `StagingBlock`
3730+
pub fn inner_find_parent_microblock_stream(
3731+
blocks_conn: &DBConn,
3732+
anchored_block_hash: &BlockHeaderHash,
3733+
parent_anchored_block_hash: &BlockHeaderHash,
3734+
parent_consensus_hash: &ConsensusHash,
3735+
parent_microblock_hash: &BlockHeaderHash,
3736+
parent_microblock_seq: u16,
3737+
) -> Result<Option<Vec<StacksMicroblock>>, Error> {
3738+
if *parent_microblock_hash == EMPTY_MICROBLOCK_PARENT_HASH && parent_microblock_seq == 0 {
37223739
// no parent microblocks, ever
37233740
return Ok(Some(vec![]));
37243741
}
37253742

37263743
// find the microblock stream fork that this block confirms
37273744
match StacksChainState::load_microblock_stream_fork(
37283745
blocks_conn,
3729-
&staging_block.parent_consensus_hash,
3730-
&staging_block.parent_anchored_block_hash,
3731-
&staging_block.parent_microblock_hash,
3746+
parent_consensus_hash,
3747+
parent_anchored_block_hash,
3748+
parent_microblock_hash,
37323749
)? {
37333750
Some(microblocks) => {
37343751
return Ok(Some(microblocks));
37353752
}
37363753
None => {
37373754
// parent microblocks haven't arrived yet, or there are none
37383755
debug!(
3739-
"No parent microblock stream for {}: expected a stream with tail {},{}",
3740-
staging_block.anchored_block_hash,
3741-
staging_block.parent_microblock_hash,
3742-
staging_block.parent_microblock_seq
3756+
"No parent microblock stream for {anchored_block_hash}: expected a stream with tail {parent_microblock_hash},{parent_microblock_seq}",
37433757
);
37443758
return Ok(None);
37453759
}
@@ -5997,29 +6011,26 @@ impl StacksChainState {
59976011
/// the given block.
59986012
pub fn extract_connecting_microblocks(
59996013
parent_block_header_info: &StacksHeaderInfo,
6000-
next_staging_block: &StagingBlock,
6014+
next_block_consensus_hash: &ConsensusHash,
6015+
next_block_hash: &BlockHeaderHash,
60016016
block: &StacksBlock,
60026017
mut next_microblocks: Vec<StacksMicroblock>,
60036018
) -> Result<Vec<StacksMicroblock>, Error> {
60046019
// NOTE: since we got the microblocks from staging, where their signatures were already
60056020
// validated, we don't need to validate them again.
6006-
let microblock_terminus = match StacksChainState::validate_parent_microblock_stream(
6021+
let Some((microblock_terminus, _)) = StacksChainState::validate_parent_microblock_stream(
60076022
parent_block_header_info
60086023
.anchored_header
60096024
.as_stacks_epoch2()
60106025
.ok_or_else(|| Error::InvalidChildOfNakomotoBlock)?,
60116026
&block.header,
60126027
&next_microblocks,
60136028
false,
6014-
) {
6015-
Some((terminus, _)) => terminus,
6016-
None => {
6017-
debug!(
6018-
"Stopping at block {}/{} -- discontiguous header stream",
6019-
next_staging_block.consensus_hash, next_staging_block.anchored_block_hash,
6020-
);
6021-
return Ok(vec![]);
6022-
}
6029+
) else {
6030+
debug!(
6031+
"Stopping at block {next_block_consensus_hash}/{next_block_hash} -- discontiguous header stream"
6032+
);
6033+
return Ok(vec![]);
60236034
};
60246035

60256036
// do not consider trailing microblocks that this anchored block does _not_ confirm
@@ -6214,7 +6225,8 @@ impl StacksChainState {
62146225
// block's parent to this block.
62156226
let next_microblocks = StacksChainState::extract_connecting_microblocks(
62166227
&parent_header_info,
6217-
&next_staging_block,
6228+
&parent_header_info.consensus_hash,
6229+
&next_staging_block.anchored_block_hash,
62186230
&block,
62196231
next_microblocks,
62206232
)?;

stackslib/src/chainstate/stacks/miner.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,30 @@ use crate::monitoring::{
6666
use crate::net::relay::Relayer;
6767
use crate::net::Error as net_error;
6868

69+
/// Fully-assembled Stacks anchored, block as well as some extra metadata pertaining to how it was
70+
/// linked to the burnchain and what view(s) the miner had of the burnchain before and after
71+
/// completing the block.
72+
#[derive(Debug, Clone, Serialize, Deserialize)]
73+
pub struct AssembledAnchorBlock {
74+
/// Consensus hash of the parent Stacks block
75+
pub parent_consensus_hash: ConsensusHash,
76+
/// Consensus hash this Stacks block
77+
pub consensus_hash: ConsensusHash,
78+
/// Burnchain tip's block hash when we finished mining
79+
pub burn_hash: BurnchainHeaderHash,
80+
/// Burnchain tip's block height when we finished mining
81+
pub burn_block_height: u64,
82+
/// Burnchain tip's block hash when we started mining (could be different)
83+
pub orig_burn_hash: BurnchainHeaderHash,
84+
/// The block we produced
85+
pub anchored_block: StacksBlock,
86+
/// The attempt count of this block (multiple blocks will be attempted per burnchain block)
87+
pub attempt: u64,
88+
/// Epoch timestamp in milliseconds when we started producing the block.
89+
pub tenure_begin: u128,
90+
}
91+
impl_file_io_serde_json!(AssembledAnchorBlock);
92+
6993
/// System status for mining.
7094
/// The miner can be Ready, in which case a miner is allowed to run
7195
/// The miner can be Blocked, in which case the miner *should not start* and/or *should terminate*

0 commit comments

Comments
 (0)