diff --git a/node/src/components/block_executor.rs b/node/src/components/block_executor.rs index 58a00b740d..edba793fbd 100644 --- a/node/src/components/block_executor.rs +++ b/node/src/components/block_executor.rs @@ -85,8 +85,8 @@ impl Display for Event { deploys, } => write!( f, - "fetch deploys for finalized block {} has {} deploys", - state.finalized_block.proto_block().hash(), + "fetch deploys for finalized block with height {} has {} deploys", + state.finalized_block.height(), deploys.len() ), Event::DeployExecutionResult { @@ -94,8 +94,8 @@ impl Display for Event { result: Ok(_), } => write!( f, - "deploys execution result for finalized block {} with pre-state hash {}: success", - state.finalized_block.proto_block().hash(), + "deploys execution result for finalized block with height {} with pre-state hash {}: success", + state.finalized_block.height(), state.pre_state_hash ), Event::DeployExecutionResult { @@ -103,21 +103,21 @@ impl Display for Event { result: Err(_), } => write!( f, - "deploys execution result for finalized block {} with pre-state hash {}: root not found", - state.finalized_block.proto_block().hash(), + "deploys execution result for finalized block with height {} with pre-state hash {}: root not found", + state.finalized_block.height(), state.pre_state_hash ), Event::CommitExecutionEffects { state, commit_result: Ok(CommitResult::Success { state_root, ..})} => write!( f, - "commit execution effects for finalized block {} with pre-state hash {}: success with post-state hash {}", - state.finalized_block.proto_block().hash(), + "commit execution effects for finalized block with height {} with pre-state hash {}: success with post-state hash {}", + state.finalized_block.height(), state.pre_state_hash, state_root, ), Event::CommitExecutionEffects { state, commit_result } => write!( f, - "commit execution effects for finalized block {} with pre-state hash {}: failed {:?}", - state.finalized_block.proto_block().hash(), + "commit execution effects for finalized block with height {} with pre-state hash {}: failed {:?}", + state.finalized_block.height(), state.pre_state_hash, commit_result, ), @@ -148,7 +148,7 @@ type BlockHeight = u64; /// The Block executor component. #[derive(Debug, Default)] pub(crate) struct BlockExecutor { - genesis_post_state_hash: Option, + genesis_post_state_hash: Digest, /// A mapping from proto block to executed block's ID and post-state hash, to allow /// identification of a parent block's details once a finalized block has been executed. /// @@ -160,7 +160,7 @@ pub(crate) struct BlockExecutor { impl BlockExecutor { pub(crate) fn new(genesis_post_state_hash: Digest) -> Self { BlockExecutor { - genesis_post_state_hash: Some(genesis_post_state_hash), + genesis_post_state_hash, parent_map: HashMap::new(), } } @@ -291,8 +291,8 @@ impl BlockExecutor { } fn pre_state_hash(&mut self, finalized_block: &FinalizedBlock) -> Digest { - if finalized_block.is_genesis_child() && self.genesis_post_state_hash.is_some() { - self.genesis_post_state_hash.take().unwrap() + if finalized_block.is_genesis_child() { + self.genesis_post_state_hash } else { // Try to get the parent's post-state-hash from the `parent_map`. // We're subtracting 1 from the height as we want to get _parent's_ post-state hash. diff --git a/node/src/components/consensus/consensus_protocol.rs b/node/src/components/consensus/consensus_protocol.rs index 1375d5643c..eb0d6b8313 100644 --- a/node/src/components/consensus/consensus_protocol.rs +++ b/node/src/components/consensus/consensus_protocol.rs @@ -46,7 +46,6 @@ pub(crate) enum ConsensusProtocolResult { /// TODO: Add more details that are necessary for block creation. CreateNewBlock { block_context: BlockContext, - opt_parent: Option, }, /// A block was finalized. The timestamp is from when the block was proposed. FinalizedBlock { diff --git a/node/src/components/consensus/era_supervisor.rs b/node/src/components/consensus/era_supervisor.rs index 43930ec4e3..449acf07fa 100644 --- a/node/src/components/consensus/era_supervisor.rs +++ b/node/src/components/consensus/era_supervisor.rs @@ -64,8 +64,6 @@ pub(crate) struct Era { consensus: Box>, /// The height of this era's first block. start_height: u64, - /// The previous era's switch block. `None` for era 0. - prev_switch_block: Option, } pub(crate) struct EraSupervisor { @@ -119,7 +117,6 @@ where validator_stakes, highway_config.genesis_era_start_timestamp, 0, - None, rng, ); let effects = era_supervisor @@ -152,7 +149,6 @@ where validator_stakes: Vec<(PublicKey, Motes)>, start_time: Timestamp, start_height: u64, - prev_switch_block: Option, rng: &mut R, ) -> Vec> { if self.active_eras.contains_key(&era_id) { @@ -207,7 +203,6 @@ where let era = Era { consensus: Box::new(highway), start_height, - prev_switch_block, }; let _ = self.active_eras.insert(era_id, era); @@ -393,25 +388,14 @@ where .set_timeout(timediff.into()) .event(move |_| Event::Timer { era_id, timestamp }) } - ConsensusProtocolResult::CreateNewBlock { - block_context, - opt_parent, - } => { - let opt_parent = opt_parent.or_else(|| { - self.era_supervisor - .active_eras - .get(&era_id)? - .prev_switch_block - .clone() - }); - self.effect_builder - .request_proto_block(block_context, opt_parent, self.rng.gen()) - .event(move |(proto_block, block_context)| Event::NewProtoBlock { - era_id, - proto_block, - block_context, - }) - } + ConsensusProtocolResult::CreateNewBlock { block_context } => self + .effect_builder + .request_proto_block(block_context, self.rng.gen()) + .event(move |(proto_block, block_context)| Event::NewProtoBlock { + era_id, + proto_block, + block_context, + }), ConsensusProtocolResult::FinalizedBlock { value: proto_block, new_equivocators, @@ -458,7 +442,6 @@ where validator_stakes, fb.timestamp(), fb.height() + 1, - Some(fb.proto_block().clone()), self.rng, ); let new_era_id = era_id.successor(); diff --git a/node/src/components/consensus/highway_core/active_validator.rs b/node/src/components/consensus/highway_core/active_validator.rs index 10b88faefc..7034a2c03c 100644 --- a/node/src/components/consensus/highway_core/active_validator.rs +++ b/node/src/components/consensus/highway_core/active_validator.rs @@ -25,7 +25,7 @@ pub(crate) enum Effect { ScheduleTimer(Timestamp), /// `propose` needs to be called with a value for a new block with the specified block context /// and parent value. - RequestNewBlock(BlockContext, Option), + RequestNewBlock(BlockContext), } /// A validator that actively participates in consensus by creating new vertices. @@ -172,10 +172,9 @@ impl ActiveValidator { } let opt_parent = opt_parent_hash.map(|bh| state.block(bh)); let height = opt_parent.map_or(0, |block| block.height); - let opt_value = opt_parent.map(|block| block.value.clone()); self.next_proposal = Some((timestamp, panorama)); let bctx = BlockContext::new(timestamp, height); - Some(Effect::RequestNewBlock(bctx, opt_value)) + Some(Effect::RequestNewBlock(bctx)) } /// Proposes a new block with the given consensus value. @@ -407,7 +406,7 @@ mod tests { // Alice wants to propose a block, and also make her witness vote at 426. let bctx = match &*alice_av.handle_timer(416.into(), &state, &mut rng) { - [Eff::ScheduleTimer(timestamp), Eff::RequestNewBlock(bctx, None)] + [Eff::ScheduleTimer(timestamp), Eff::RequestNewBlock(bctx)] if *timestamp == 426.into() => { bctx.clone() diff --git a/node/src/components/consensus/highway_core/highway_testing.rs b/node/src/components/consensus/highway_core/highway_testing.rs index 97b8f28cec..1249979ff4 100644 --- a/node/src/components/consensus/highway_core/highway_testing.rs +++ b/node/src/components/consensus/highway_core/highway_testing.rs @@ -96,9 +96,7 @@ impl From> for HighwayMessage { // validators so for them it's just `Vertex` that needs to be validated. Effect::NewVertex(ValidVertex(v)) => HighwayMessage::NewVertex(v), Effect::ScheduleTimer(t) => HighwayMessage::Timer(t), - Effect::RequestNewBlock(block_context, _opt_parent) => { - HighwayMessage::RequestBlock(block_context) - } + Effect::RequestNewBlock(block_context) => HighwayMessage::RequestBlock(block_context), } } } diff --git a/node/src/components/consensus/protocols/highway.rs b/node/src/components/consensus/protocols/highway.rs index 7e6230537e..e902b2b9ca 100644 --- a/node/src/components/consensus/protocols/highway.rs +++ b/node/src/components/consensus/protocols/highway.rs @@ -109,11 +109,8 @@ impl HighwayProtocol { AvEffect::ScheduleTimer(timestamp) => { vec![ConsensusProtocolResult::ScheduleTimer(timestamp)] } - AvEffect::RequestNewBlock(block_context, opt_parent) => { - vec![ConsensusProtocolResult::CreateNewBlock { - block_context, - opt_parent, - }] + AvEffect::RequestNewBlock(block_context) => { + vec![ConsensusProtocolResult::CreateNewBlock { block_context }] } } } diff --git a/node/src/components/deploy_buffer.rs b/node/src/components/deploy_buffer.rs index 2f2fce05cf..95c0f7c78d 100644 --- a/node/src/components/deploy_buffer.rs +++ b/node/src/components/deploy_buffer.rs @@ -210,7 +210,7 @@ impl DeployBuffer { self.collected_deploys .retain(|deploy_hash, _| !deploys.contains_key(deploy_hash)); self.finalized.insert(block, deploys); - } else { + } else if !block.is_empty() { // TODO: Events are not guaranteed to be handled in order, so this could happen! error!("finalized block that hasn't been processed!"); } @@ -255,7 +255,7 @@ where } Event::Buffer { hash, header } => self.add_deploy(hash, *header), Event::ProposedProtoBlock(block) => { - let (hash, _, deploys, _) = block.destructure(); + let (hash, deploys, _) = block.destructure(); self.added_block(hash, deploys) } Event::FinalizedProtoBlock(block) => self.finalized_block(*block.hash()), diff --git a/node/src/effect.rs b/node/src/effect.rs index baba7f9cb8..4aa4ebd76a 100644 --- a/node/src/effect.rs +++ b/node/src/effect.rs @@ -630,7 +630,6 @@ impl EffectBuilder { pub(crate) async fn request_proto_block( self, block_context: BlockContext, - maybe_parent: Option, random_bit: bool, ) -> (ProtoBlock, BlockContext) where @@ -648,16 +647,7 @@ impl EffectBuilder { .await .into_iter() .collect(); - // The only circumstance where `maybe_parent` is `None` is for the very first proto block, - // i.e. the one immediately after the genesis block. In this case, just use - // `Digest::default`, and the block executor (which knows the post-state hash of the genesis - // block) will apply the executed block's parent hash correctly. - let parent_hash = maybe_parent - .as_ref() - .map(ProtoBlock::hash) - .copied() - .unwrap_or_default(); - let proto_block = ProtoBlock::new(parent_hash, deploys, random_bit); + let proto_block = ProtoBlock::new(deploys, random_bit); (proto_block, block_context) } diff --git a/node/src/types/block.rs b/node/src/types/block.rs index 1646c068eb..c70591ade6 100644 --- a/node/src/types/block.rs +++ b/node/src/types/block.rs @@ -42,6 +42,11 @@ impl ProtoBlockHash { pub fn inner(&self) -> &Digest { &self.0 } + + /// Returns `true` is `self` is a hash of empty `ProtoBlock`. + pub(crate) fn is_empty(self) -> bool { + self == ProtoBlock::empty_random_bit_false() || self == ProtoBlock::empty_random_bit_true() + } } impl Display for ProtoBlockHash { @@ -62,23 +67,17 @@ impl Display for ProtoBlockHash { #[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct ProtoBlock { hash: ProtoBlockHash, - parent_hash: ProtoBlockHash, deploys: Vec, random_bit: bool, } impl ProtoBlock { - pub(crate) fn new( - parent_hash: ProtoBlockHash, - deploys: Vec, - random_bit: bool, - ) -> Self { + pub(crate) fn new(deploys: Vec, random_bit: bool) -> Self { let hash = ProtoBlockHash::new(hash::hash( - &rmp_serde::to_vec(&(parent_hash, &deploys, random_bit)).expect("serialize ProtoBlock"), + &rmp_serde::to_vec(&(&deploys, random_bit)).expect("serialize ProtoBlock"), )); ProtoBlock { - parent_hash, hash, deploys, random_bit, @@ -89,11 +88,6 @@ impl ProtoBlock { &self.hash } - #[allow(unused)] - pub(crate) fn parent_hash(&self) -> &ProtoBlockHash { - &self.parent_hash - } - /// The list of deploy hashes included in the block. pub(crate) fn deploys(&self) -> &Vec { &self.deploys @@ -104,8 +98,20 @@ impl ProtoBlock { self.random_bit } - pub(crate) fn destructure(self) -> (ProtoBlockHash, ProtoBlockHash, Vec, bool) { - (self.hash, self.parent_hash, self.deploys, self.random_bit) + pub(crate) fn destructure(self) -> (ProtoBlockHash, Vec, bool) { + (self.hash, self.deploys, self.random_bit) + } + + /// Returns hash of empty ProtoBlock (no deploys) with a random bit set to false. + /// Added here so that it's always aligned with how hash is calculated. + pub(crate) fn empty_random_bit_false() -> ProtoBlockHash { + *ProtoBlock::new(vec![], false).hash() + } + + /// Returns hash of empty ProtoBlock (no deploys) with a random bit set to true. + /// Added here so that it's always aligned with how hash is calculated. + pub(crate) fn empty_random_bit_true() -> ProtoBlockHash { + *ProtoBlock::new(vec![], true).hash() } } @@ -113,9 +119,8 @@ impl Display for ProtoBlock { fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result { write!( formatter, - "proto block {}, parent hash {}, deploys [{}], random bit {}", + "proto block {}, deploys [{}], random bit {}", self.hash.inner(), - self.parent_hash.inner(), DisplayIter::new(self.deploys.iter()), self.random_bit(), ) @@ -235,6 +240,28 @@ impl FinalizedBlock { } } +impl From for FinalizedBlock { + fn from(b: Block) -> Self { + let proto_block = + ProtoBlock::new(b.header().deploy_hashes().clone(), b.header().random_bit); + + let timestamp = *b.header().timestamp(); + let switch_block = b.header().switch_block; + let era_id = b.header().era_id; + let height = b.header().height; + let system_transactions = b.take_header().system_transactions; + + FinalizedBlock { + proto_block, + timestamp, + system_transactions, + switch_block, + era_id, + height, + } + } +} + impl Display for FinalizedBlock { fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result { write!( @@ -283,8 +310,11 @@ pub struct BlockHeader { body_hash: Digest, deploy_hashes: Vec, random_bit: bool, + switch_block: bool, timestamp: Timestamp, system_transactions: Vec, + era_id: EraId, + height: u64, } impl BlockHeader { @@ -330,12 +360,13 @@ impl Display for BlockHeader { write!( formatter, "block header parent hash {}, post-state hash {}, body hash {}, deploys [{}], \ - random bit {}, timestamp {}, system_transactions [{}]", + random bit {}, switch block {}, timestamp {}, system_transactions [{}]", self.parent_hash.inner(), self.post_state_hash, self.body_hash, DisplayIter::new(self.deploy_hashes.iter()), self.random_bit, + self.switch_block, self.timestamp, DisplayIter::new(self.system_transactions.iter()), ) @@ -363,14 +394,20 @@ impl Block { .unwrap_or_else(|error| panic!("should serialize block body: {}", error)); let body_hash = hash::hash(&serialized_body); + let era_id = finalized_block.era_id(); + let height = finalized_block.height(); + let header = BlockHeader { parent_hash, post_state_hash, body_hash, deploy_hashes: finalized_block.proto_block.deploys, random_bit: finalized_block.proto_block.random_bit, + switch_block: finalized_block.switch_block, timestamp: finalized_block.timestamp, system_transactions: finalized_block.system_transactions, + era_id, + height, }; let serialized_header = Self::serialize_header(&header) .unwrap_or_else(|error| panic!("should serialize block header: {}", error)); @@ -405,13 +442,12 @@ impl Block { /// Generates a random instance using a `TestRng`. #[cfg(test)] pub fn random(rng: &mut TestRng) -> Self { - let proto_parent_hash = ProtoBlockHash(Digest::random(rng)); let deploy_count = rng.gen_range(0, 11); let deploy_hashes = iter::repeat_with(|| DeployHash::new(Digest::random(rng))) .take(deploy_count) .collect(); let random_bit = rng.gen(); - let proto_block = ProtoBlock::new(proto_parent_hash, deploy_hashes, random_bit); + let proto_block = ProtoBlock::new(deploy_hashes, random_bit); // TODO - make Timestamp deterministic. let timestamp = Timestamp::now(); @@ -451,7 +487,7 @@ impl Display for Block { write!( formatter, "executed block {}, parent hash {}, post-state hash {}, body hash {}, deploys [{}], \ - random bit {}, timestamp {}, system_transactions [{}], proofs count {}", + random bit {}, timestamp {}, era_id {}, height {}, system_transactions [{}], proofs count {}", self.hash.inner(), self.header.parent_hash.inner(), self.header.post_state_hash, @@ -459,6 +495,8 @@ impl Display for Block { DisplayIter::new(self.header.deploy_hashes.iter()), self.header.random_bit, self.header.timestamp, + self.header.era_id.0, + self.header.height, DisplayIter::new(self.header.system_transactions.iter()), self.proofs.len() )