diff --git a/CHANGELOG.md b/CHANGELOG.md index f964ea33ea9..6fc8b93d57e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,9 +6,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Breaking +- [2648](https://github.com/FuelLabs/fuel-core/pull/2648): Add feature-flagged field to block header `fault_proving_header` that contains a commitment to all transaction ids. +- [2678](https://github.com/FuelLabs/fuel-core/pull/2678): Removed public accessors for `BlockHeader` fields and replaced with methods instead, moved `tx_id_commitment` to the application header of `BlockHeaderV2`. + ### Added - [2150](https://github.com/FuelLabs/fuel-core/pull/2150): Upgraded `libp2p` to `0.54.1` and introduced `ConnectionLimiter` to limit pending incoming/outgoing connections. -- [2648](https://github.com/FuelLabs/fuel-core/pull/2648): Add feature-flagged field to block header `fault_proving_header` that contains a commitment to all transaction ids. ### Changed diff --git a/Cargo.lock b/Cargo.lock index 3626821d0e3..c3aaf8e1c34 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4039,7 +4039,6 @@ dependencies = [ "bs58", "derivative", "derive_more 0.99.19", - "enum_dispatch", "fuel-vm 0.59.1", "k256", "rand", diff --git a/bin/fuel-core/src/cli/snapshot.rs b/bin/fuel-core/src/cli/snapshot.rs index 060dd9a87a2..34352a3b4d9 100644 --- a/bin/fuel-core/src/cli/snapshot.rs +++ b/bin/fuel-core/src/cli/snapshot.rs @@ -392,8 +392,9 @@ mod tests { .last_block_config() .cloned() .expect("Expects the last block config to be set"); - block.header_mut().application_mut().da_height = - last_block_config.da_block_height; + block + .header_mut() + .set_da_height(last_block_config.da_block_height); block .header_mut() .set_block_height(last_block_config.block_height); @@ -557,7 +558,7 @@ mod tests { fn given_block(&mut self) -> TableEntry { let mut block = CompressedBlock::default(); let height = self.rng.gen(); - block.header_mut().application_mut().da_height = self.rng.gen(); + block.header_mut().set_da_height(self.rng.gen()); block.header_mut().set_block_height(height); let _ = self .db diff --git a/crates/chain-config/src/config/state.rs b/crates/chain-config/src/config/state.rs index 06d4f605283..490a36b148f 100644 --- a/crates/chain-config/src/config/state.rs +++ b/crates/chain-config/src/config/state.rs @@ -105,13 +105,9 @@ impl LastBlockConfig { pub fn from_header(header: &BlockHeader, blocks_root: Bytes32) -> Self { Self { block_height: *header.height(), - da_block_height: header.application().da_height, - consensus_parameters_version: header - .application() - .consensus_parameters_version, - state_transition_version: header - .application() - .state_transition_bytecode_version, + da_block_height: header.da_height(), + consensus_parameters_version: header.consensus_parameters_version(), + state_transition_version: header.state_transition_bytecode_version(), blocks_root, } } diff --git a/crates/compression/src/compressed_block_payload/v1.rs b/crates/compression/src/compressed_block_payload/v1.rs index 51e0012a615..0b7d4ba0ff6 100644 --- a/crates/compression/src/compressed_block_payload/v1.rs +++ b/crates/compression/src/compressed_block_payload/v1.rs @@ -43,10 +43,10 @@ impl From<&BlockHeader> for CompressedBlockHeader { } = *header.consensus(); CompressedBlockHeader { application: ApplicationHeader { - da_height: header.da_height, - consensus_parameters_version: header.consensus_parameters_version, + da_height: header.da_height(), + consensus_parameters_version: header.consensus_parameters_version(), state_transition_bytecode_version: header - .state_transition_bytecode_version, + .state_transition_bytecode_version(), generated: Empty {}, }, consensus: ConsensusHeader { diff --git a/crates/fraud_proofs/global_merkle_root/storage/src/update.rs b/crates/fraud_proofs/global_merkle_root/storage/src/update.rs index d05eedc6b1a..74a7c680550 100644 --- a/crates/fraud_proofs/global_merkle_root/storage/src/update.rs +++ b/crates/fraud_proofs/global_merkle_root/storage/src/update.rs @@ -111,10 +111,10 @@ where storage: self, latest_state_transition_bytecode_version: block .header() - .state_transition_bytecode_version, + .state_transition_bytecode_version(), latest_consensus_parameters_version: block .header() - .consensus_parameters_version, + .consensus_parameters_version(), }; update_transaction.process_block(block)?; diff --git a/crates/fuel-core/src/executor.rs b/crates/fuel-core/src/executor.rs index 84c28cfc1b8..bbda81061b2 100644 --- a/crates/fuel-core/src/executor.rs +++ b/crates/fuel-core/src/executor.rs @@ -364,8 +364,8 @@ mod tests { assert!(skipped_transactions.is_empty()); assert_ne!( - start_block.header().transactions_root, - block.header().transactions_root + start_block.header().transactions_root(), + block.header().transactions_root() ); assert_eq!(block.transactions().len(), 11); assert!(block.transactions()[10].as_mint().is_some()); @@ -2713,7 +2713,7 @@ mod tests { .message_id() .to_bytes(), ); - assert_eq!(block.header().message_outbox_root.as_ref(), mt.root()); + assert_eq!(block.header().message_outbox_root().as_ref(), mt.root()); } #[test] @@ -2752,7 +2752,7 @@ mod tests { // Then let empty_root = empty_sum_sha256(); - assert_eq!(block.header().message_outbox_root.as_ref(), empty_root) + assert_eq!(block.header().message_outbox_root().as_ref(), empty_root) } #[test] @@ -3387,7 +3387,7 @@ mod tests { // then let expected = root_calculator.root().into(); - let actual = result.block.header().application().event_inbox_root; + let actual = result.block.header().event_inbox_root(); assert_eq!(actual, expected); } diff --git a/crates/fuel-core/src/query/message/test.rs b/crates/fuel-core/src/query/message/test.rs index 9aa7ee0799a..e718352d92d 100644 --- a/crates/fuel-core/src/query/message/test.rs +++ b/crates/fuel-core/src/query/message/test.rs @@ -202,8 +202,8 @@ async fn can_build_message_proof() { ) .unwrap(); assert_eq!( - proof.message_block_header.message_outbox_root, - message_block.header().message_outbox_root + proof.message_block_header.message_outbox_root(), + message_block.header().message_outbox_root() ); assert_eq!( proof.message_block_header.height(), diff --git a/crates/fuel-core/src/schema/block.rs b/crates/fuel-core/src/schema/block.rs index 98e35a7be6d..fc0688c2dca 100644 --- a/crates/fuel-core/src/schema/block.rs +++ b/crates/fuel-core/src/schema/block.rs @@ -188,42 +188,42 @@ impl Header { /// The layer 1 height of messages and events to include since the last layer 1 block number. async fn da_height(&self) -> U64 { - self.0.da_height.0.into() + self.0.da_height().0.into() } /// The version of the consensus parameters used to create this block. async fn consensus_parameters_version(&self) -> U32 { - self.0.consensus_parameters_version.into() + self.0.consensus_parameters_version().into() } /// The version of the state transition bytecode used to create this block. async fn state_transition_bytecode_version(&self) -> U32 { - self.0.state_transition_bytecode_version.into() + self.0.state_transition_bytecode_version().into() } /// Number of transactions in this block. async fn transactions_count(&self) -> U16 { - self.0.transactions_count.into() + self.0.transactions_count().into() } /// Number of message receipts in this block. async fn message_receipt_count(&self) -> U32 { - self.0.message_receipt_count.into() + self.0.message_receipt_count().into() } /// Merkle root of transactions. async fn transactions_root(&self) -> Bytes32 { - self.0.transactions_root.into() + self.0.transactions_root().into() } /// Merkle root of message receipts in this block. async fn message_outbox_root(&self) -> Bytes32 { - self.0.message_outbox_root.into() + self.0.message_outbox_root().into() } /// Merkle root of inbox events in this block. async fn event_inbox_root(&self) -> Bytes32 { - self.0.event_inbox_root.into() + self.0.event_inbox_root().into() } /// Fuel block height. diff --git a/crates/fuel-core/src/schema/dap.rs b/crates/fuel-core/src/schema/dap.rs index 808591fa0c3..0bbfb0be37b 100644 --- a/crates/fuel-core/src/schema/dap.rs +++ b/crates/fuel-core/src/schema/dap.rs @@ -220,10 +220,12 @@ impl ConcreteStorage { .get_current_block()? .ok_or(not_found!("Block for VMDatabase"))?; + let application_header = block.header().as_empty_application_header(); + let vm_database = VmStorage::new( view.into_transaction(), block.header().consensus(), - block.header().application(), + &application_header, // TODO: Use a real coinbase address Default::default(), ); diff --git a/crates/fuel-core/src/service/adapters/consensus_module.rs b/crates/fuel-core/src/service/adapters/consensus_module.rs index 484acaf4e8e..40d588d261e 100644 --- a/crates/fuel-core/src/service/adapters/consensus_module.rs +++ b/crates/fuel-core/src/service/adapters/consensus_module.rs @@ -40,7 +40,7 @@ impl VerifierAdapter { database: Database, ) -> Self { let block_height = *genesis_block.header().height(); - let da_block_height = genesis_block.header().da_height; + let da_block_height = genesis_block.header().da_height(); let config = VerifierConfig::new(consensus, block_height, da_block_height); Self { block_verifier: Arc::new(Verifier::new(config, database)), diff --git a/crates/fuel-core/src/service/adapters/consensus_parameters_provider.rs b/crates/fuel-core/src/service/adapters/consensus_parameters_provider.rs index 1553de47f64..db7c85f6bfe 100644 --- a/crates/fuel-core/src/service/adapters/consensus_parameters_provider.rs +++ b/crates/fuel-core/src/service/adapters/consensus_parameters_provider.rs @@ -124,8 +124,7 @@ impl RunnableTask for Task { .sealed_block .entity .header() - .application() - .consensus_parameters_version; + .consensus_parameters_version(); if new_version > *self.shared_state.latest_consensus_parameters_version.lock() { match self.shared_state.cache_consensus_parameters(new_version) { @@ -296,10 +295,7 @@ mod tests { version: ConsensusParametersVersion, ) -> SharedImportResult { let mut block = Block::default(); - block - .header_mut() - .application_mut() - .consensus_parameters_version = version; + block.header_mut().set_consensus_parameters_version(version); let sealed_block = SealedBlock { entity: block, consensus: Default::default(), diff --git a/crates/fuel-core/src/service/adapters/graphql_api/on_chain.rs b/crates/fuel-core/src/service/adapters/graphql_api/on_chain.rs index b3a6d860e76..9dc0c58a769 100644 --- a/crates/fuel-core/src/service/adapters/graphql_api/on_chain.rs +++ b/crates/fuel-core/src/service/adapters/graphql_api/on_chain.rs @@ -129,7 +129,7 @@ impl DatabaseContracts for OnChainIterableKeyValueView { impl DatabaseChain for OnChainIterableKeyValueView { fn da_height(&self) -> StorageResult { self.latest_compressed_block()? - .map(|block| block.header().da_height) + .map(|block| block.header().da_height()) .ok_or(not_found!("DaBlockHeight")) } } diff --git a/crates/fuel-core/src/service/genesis.rs b/crates/fuel-core/src/service/genesis.rs index e3d7bf3ff0a..0689bb4a57c 100644 --- a/crates/fuel-core/src/service/genesis.rs +++ b/crates/fuel-core/src/service/genesis.rs @@ -136,7 +136,7 @@ pub async fn execute_genesis_block( database_transaction_on_chain .storage_as_mut::() .insert( - &genesis_block.header().consensus_parameters_version, + &genesis_block.header().consensus_parameters_version(), &chain_config.consensus_parameters, )?; @@ -144,7 +144,7 @@ pub async fn execute_genesis_block( database_transaction_on_chain .storage_as_mut::() .insert( - &genesis_block.header().state_transition_bytecode_version, + &genesis_block.header().state_transition_bytecode_version(), &bytecode_root, )?; database_transaction_on_chain @@ -723,7 +723,7 @@ mod tests { .latest_block() .unwrap() .header() - .state_transition_bytecode_version; + .state_transition_bytecode_version(); last_block.blocks_root = view .block_header_merkle_root(&last_block.block_height) .unwrap(); diff --git a/crates/fuel-core/src/service/genesis/importer.rs b/crates/fuel-core/src/service/genesis/importer.rs index cb6206a976c..c7da7a9c747 100644 --- a/crates/fuel-core/src/service/genesis/importer.rs +++ b/crates/fuel-core/src/service/genesis/importer.rs @@ -179,7 +179,7 @@ impl SnapshotImporter { } let block_height = *self.genesis_block.header().height(); - let da_block_height = self.genesis_block.header().da_height; + let da_block_height = self.genesis_block.header().da_height(); let db = self.db.on_chain().clone(); let migration_name = migration_name::(); @@ -233,7 +233,7 @@ impl SnapshotImporter { } let block_height = *self.genesis_block.header().height(); - let da_block_height = self.genesis_block.header().da_height; + let da_block_height = self.genesis_block.header().da_height(); let db = self.db.off_chain().clone(); diff --git a/crates/services/consensus_module/Cargo.toml b/crates/services/consensus_module/Cargo.toml index bced45bbcf2..b8f1fd2c90c 100644 --- a/crates/services/consensus_module/Cargo.toml +++ b/crates/services/consensus_module/Cargo.toml @@ -19,3 +19,11 @@ fuel-core-types = { workspace = true, features = ["std"] } [dev-dependencies] fuel-core-types = { path = "../../types", features = ["test-helpers"] } test-case = { workspace = true } + +[features] +fault-proving = [ + "fuel-core-types/fault-proving", + "fuel-core-storage/fault-proving", + "fuel-core-poa/fault-proving", + "fuel-core-chain-config/fault-proving", +] diff --git a/crates/services/consensus_module/poa/src/verifier.rs b/crates/services/consensus_module/poa/src/verifier.rs index 3f25e72ca71..6e34f606719 100644 --- a/crates/services/consensus_module/poa/src/verifier.rs +++ b/crates/services/consensus_module/poa/src/verifier.rs @@ -61,7 +61,7 @@ pub fn verify_block_fields( let prev_header = database.block_header(&prev_height)?; ensure!( - header.da_height >= prev_header.da_height, + header.da_height() >= prev_header.da_height(), "The `da_height` of the next block can't be lower" ); @@ -70,8 +70,14 @@ pub fn verify_block_fields( "The `time` of the next block can't be lower" ); + let application_header_hash = match header { + BlockHeader::V1(header) => &header.application().hash(), + #[cfg(feature = "fault-proving")] + BlockHeader::V2(header) => &header.application().hash(), + }; + ensure!( - header.application_hash() == &header.application().hash(), + header.application_hash() == application_header_hash, "The application hash mismatch." ); diff --git a/crates/services/consensus_module/poa/src/verifier/tests.rs b/crates/services/consensus_module/poa/src/verifier/tests.rs index f8450bc9f93..80d0e2936eb 100644 --- a/crates/services/consensus_module/poa/src/verifier/tests.rs +++ b/crates/services/consensus_module/poa/src/verifier/tests.rs @@ -5,8 +5,6 @@ use fuel_core_types::{ blockchain::header::{ ApplicationHeader, ConsensusHeader, - GeneratedApplicationFields, - GeneratedConsensusFields, PartialBlockHeader, }, fuel_tx::Transaction, @@ -18,8 +16,7 @@ struct Input { block_header_merkle_root: [u8; 32], prev_header_time: Tai64, prev_header_da_height: u64, - ch: ConsensusHeader, - ah: ApplicationHeader, + bh: BlockHeader, txs: Vec, } @@ -51,8 +48,7 @@ fn correct() -> Input { block_header_merkle_root: [2u8; 32], prev_header_time: Tai64(2), prev_header_da_height: 2, - ch: *block_header.consensus(), - ah: *block_header.application(), + bh: block_header, txs, } } @@ -61,35 +57,35 @@ fn correct() -> Input { #[test_case( { let mut i = correct(); - i.ch.height = 0u32.into(); + i.bh.set_block_height(0u32.into()); i } => matches Err(_) ; "Height 0" )] #[test_case( { let mut i = correct(); - i.ch.prev_root = [3u8; 32].into(); + i.bh.set_previous_root([3u8; 32].into()); i } => matches Err(_) ; "genesis verify prev root mismatch should error" )] #[test_case( { let mut i = correct(); - i.ah.da_height = 1u64.into(); + i.bh.set_da_height(1u64.into()); i } => matches Err(_) ; "genesis verify da height lower then prev header should error" )] #[test_case( { let mut i = correct(); - i.ch.generated.application_hash = [0u8; 32].into(); + i.bh.set_application_hash([0u8; 32].into()); i } => matches Err(_) ; "genesis verify application hash mismatch should error" )] #[test_case( { let mut i = correct(); - i.ch.time = Tai64(1); + i.bh.set_time(Tai64(1)); i } => matches Err(_) ; "genesis verify time before prev header should error" )] @@ -105,8 +101,7 @@ fn test_verify_genesis_block_fields(input: Input) -> anyhow::Result<()> { block_header_merkle_root, prev_header_time, prev_header_da_height, - ch, - ah, + bh, txs, } = input; let mut d = MockDatabase::default(); @@ -119,8 +114,18 @@ fn test_verify_genesis_block_fields(input: Input) -> anyhow::Result<()> { Ok(h) }); let mut b = Block::default(); - b.header_mut().set_consensus_header(ch); - b.header_mut().set_application_header(ah); + b.header_mut().set_consensus_header(*bh.consensus()); + match (bh, b.header_mut()) { + (BlockHeader::V1(bh), BlockHeader::V1(ref mut h)) => { + h.set_application_header(*bh.application()) + } + #[cfg(feature = "fault-proving")] + (BlockHeader::V2(bh), BlockHeader::V2(ref mut h)) => { + h.set_application_header(*bh.application()) + } + #[cfg_attr(not(feature = "fault-proving"), allow(unreachable_patterns))] + _ => unreachable!(), + } *b.transactions_mut() = txs; verify_block_fields(&d, &b) } diff --git a/crates/services/consensus_module/src/block_verifier.rs b/crates/services/consensus_module/src/block_verifier.rs index bd47ec5d3b2..662ab9934aa 100644 --- a/crates/services/consensus_module/src/block_verifier.rs +++ b/crates/services/consensus_module/src/block_verifier.rs @@ -106,7 +106,7 @@ fn verify_genesis_block_fields( "The genesis time should be unix epoch time" ); ensure!( - header.da_height == expected_genesis_da_height, + header.da_height() == expected_genesis_da_height, "The genesis `da_height` is not as expected" ); ensure!( diff --git a/crates/services/consensus_module/src/block_verifier/tests.rs b/crates/services/consensus_module/src/block_verifier/tests.rs index 935f3514ffd..3f31c2e9005 100644 --- a/crates/services/consensus_module/src/block_verifier/tests.rs +++ b/crates/services/consensus_module/src/block_verifier/tests.rs @@ -26,7 +26,7 @@ use test_case::test_case; let mut h = BlockHeader::default(); h.set_previous_root(Bytes32::zeroed()); h.set_time(Tai64::UNIX_EPOCH); - h.application_mut().da_height = 1234u64.into(); + h.set_da_height(1234u64.into()); h }, 0, 1234 => matches Ok(_) ; "Correct header at `1234` da height" @@ -37,7 +37,7 @@ use test_case::test_case; h.set_previous_root(Bytes32::zeroed()); h.set_time(Tai64::UNIX_EPOCH); h.set_block_height(113u32.into()); - h.application_mut().da_height = 1234u64.into(); + h.set_da_height(1234u64.into()); h }, 113, 1234 => matches Ok(_) ; "Correct header at `113` height and at `1234` da height" @@ -67,7 +67,7 @@ use test_case::test_case; let mut h = BlockHeader::default(); h.set_previous_root(Bytes32::zeroed()); h.set_time(Tai64::UNIX_EPOCH); - h.application_mut().da_height = 1234u64.into(); + h.set_da_height(1234u64.into()); h }, 0, 0 => matches Err(_) ; "wrong header da height" diff --git a/crates/services/executor/src/executor.rs b/crates/services/executor/src/executor.rs index d4ce6102b17..22d03c51468 100644 --- a/crates/services/executor/src/executor.rs +++ b/crates/services/executor/src/executor.rs @@ -343,7 +343,7 @@ where self, block: &Block, ) -> ExecutorResult> { - let consensus_params_version = block.header().consensus_parameters_version; + let consensus_params_version = block.header().consensus_parameters_version(); let (block_executor, storage_tx) = self.into_executor(consensus_params_version)?; @@ -916,7 +916,7 @@ where .storage::() .get(&prev_block_height)? .ok_or(ExecutorError::PreviousBlockIsNotFound)?; - let previous_da_height = prev_block_header.header().da_height; + let previous_da_height = prev_block_header.header().da_height(); let Some(next_unprocessed_da_height) = previous_da_height.0.checked_add(1) else { return Err(ExecutorError::DaHeightExceededItsLimit) }; diff --git a/crates/services/gas_price_service/src/common/l2_block_source.rs b/crates/services/gas_price_service/src/common/l2_block_source.rs index d53a489bcbc..40490fb8382 100644 --- a/crates/services/gas_price_service/src/common/l2_block_source.rs +++ b/crates/services/gas_price_service/src/common/l2_block_source.rs @@ -69,7 +69,7 @@ where }), std::cmp::Ordering::Equal => Ok(BlockInfo::GenesisBlock), std::cmp::Ordering::Greater => { - let param_version = block.header().consensus_parameters_version; + let param_version = block.header().consensus_parameters_version(); let GasPriceSettings { gas_price_factor, diff --git a/crates/services/gas_price_service/src/v0/uninitialized_task.rs b/crates/services/gas_price_service/src/v0/uninitialized_task.rs index a32ea99c0e3..d0cb57c28ef 100644 --- a/crates/services/gas_price_service/src/v0/uninitialized_task.rs +++ b/crates/services/gas_price_service/src/v0/uninitialized_task.rs @@ -296,7 +296,7 @@ where let block = view .get_block(&height.into())? .ok_or(not_found!("FullBlock"))?; - let param_version = block.header().consensus_parameters_version; + let param_version = block.header().consensus_parameters_version(); let GasPriceSettings { gas_price_factor, diff --git a/crates/services/gas_price_service/src/v1/uninitialized_task.rs b/crates/services/gas_price_service/src/v1/uninitialized_task.rs index 35a1efdba53..85a0151093e 100644 --- a/crates/services/gas_price_service/src/v1/uninitialized_task.rs +++ b/crates/services/gas_price_service/src/v1/uninitialized_task.rs @@ -337,7 +337,7 @@ where let block = view .get_block(&height.into())? .ok_or(not_found!("FullBlock"))?; - let param_version = block.header().consensus_parameters_version; + let param_version = block.header().consensus_parameters_version(); let GasPriceSettings { gas_price_factor, diff --git a/crates/services/p2p/Cargo.toml b/crates/services/p2p/Cargo.toml index a4b2aed62b7..598b3d03985 100644 --- a/crates/services/p2p/Cargo.toml +++ b/crates/services/p2p/Cargo.toml @@ -72,3 +72,8 @@ version = "0.4.0" [features] test-helpers = ["fuel-core-types/test-helpers"] +fault-proving = [ + "fuel-core-types/fault-proving", + "fuel-core-storage/fault-proving", + "fuel-core-chain-config/fault-proving", +] diff --git a/crates/services/p2p/src/p2p_service.rs b/crates/services/p2p/src/p2p_service.rs index 671d2bbebda..db87eb95151 100644 --- a/crates/services/p2p/src/p2p_service.rs +++ b/crates/services/p2p/src/p2p_service.rs @@ -1779,8 +1779,18 @@ mod tests { // Metadata gets skipped during serialization, so this is the fuzzy way to compare blocks fn eq_except_metadata(a: &SealedBlockHeader, b: &SealedBlockHeader) -> bool { - a.entity.application() == b.entity.application() - && a.entity.consensus() == b.entity.consensus() + let app_eq = match (&a.entity, &b.entity) { + (BlockHeader::V1(a), BlockHeader::V1(b)) => { + a.application() == b.application() + } + #[cfg(feature = "fault-proving")] + (BlockHeader::V2(a), BlockHeader::V2(b)) => { + a.application() == b.application() + } + #[cfg_attr(not(feature = "fault-proving"), allow(unreachable_patterns))] + _ => false, + }; + app_eq && a.entity.consensus() == b.entity.consensus() } async fn request_response_works_with( diff --git a/crates/services/producer/src/block_producer.rs b/crates/services/producer/src/block_producer.rs index 71d8d5c9fce..a841ba3fb87 100644 --- a/crates/services/producer/src/block_producer.rs +++ b/crates/services/producer/src/block_producer.rs @@ -125,7 +125,7 @@ where let block_time = predefined_block.header().consensus().time; - let da_height = predefined_block.header().application().da_height; + let da_height = predefined_block.header().da_height(); let view = self.view_provider.latest_view()?; @@ -522,7 +522,7 @@ where Ok(PreviousBlockInfo { prev_root, - da_height: previous_block.header().da_height, + da_height: previous_block.header().da_height(), }) } } diff --git a/crates/services/producer/src/block_producer/tests.rs b/crates/services/producer/src/block_producer/tests.rs index f6bd0427cf3..d65432a62c1 100644 --- a/crates/services/producer/src/block_producer/tests.rs +++ b/crates/services/producer/src/block_producer/tests.rs @@ -221,7 +221,7 @@ mod produce_and_execute_block_txpool { // Then let header = result.block.header(); assert_eq!( - header.consensus_parameters_version, + header.consensus_parameters_version(), consensus_parameters_version ); } @@ -279,7 +279,7 @@ mod produce_and_execute_block_txpool { // Then let header = result.block.header(); assert_eq!( - header.state_transition_bytecode_version, + header.state_transition_bytecode_version(), state_transition_bytecode_version ); } @@ -399,13 +399,7 @@ mod produce_and_execute_block_txpool { // then let expected = prev_da_height + 3; - let actual: u64 = res - .into_result() - .block - .header() - .application() - .da_height - .into(); + let actual: u64 = res.into_result().block.header().da_height().into(); assert_eq!(expected, actual); } @@ -445,13 +439,7 @@ mod produce_and_execute_block_txpool { // then let expected = prev_da_height + 3; - let actual: u64 = res - .into_result() - .block - .header() - .application() - .da_height - .into(); + let actual: u64 = res.into_result().block.header().da_height().into(); assert_eq!(expected, actual); } @@ -495,13 +483,7 @@ mod produce_and_execute_block_txpool { // then let expected = prev_da_height + i; - let actual: u64 = res - .into_result() - .block - .header() - .application() - .da_height - .into(); + let actual: u64 = res.into_result().block.header().da_height().into(); assert_eq!(expected, actual); } } @@ -754,7 +736,7 @@ fn ctx_for_block( executor: MockExecutorWithCapture, ) -> TestContext { let prev_height = block.header().height().pred().unwrap(); - let prev_da_height = block.header().da_height.as_u64() - 1; + let prev_da_height = block.header().da_height().as_u64() - 1; TestContextBuilder::new() .with_prev_height(prev_height) .with_prev_da_height(prev_da_height.into()) @@ -833,7 +815,7 @@ proptest! { let _ = rt.block_on(ctx.producer().produce_and_execute_predefined(&block)).unwrap(); // then - let expected_da_height = block.header().application().da_height; + let expected_da_height = block.header().da_height(); let captured = executor.captured.lock().unwrap(); let actual = captured.as_ref().unwrap().header_to_produce.application.da_height; assert_eq!(expected_da_height, actual); diff --git a/crates/services/sync/src/import.rs b/crates/services/sync/src/import.rs index f3d93359183..c535a78bee1 100644 --- a/crates/services/sync/src/import.rs +++ b/crates/services/sync/src/import.rs @@ -526,7 +526,7 @@ async fn await_da_height( consensus: &Arc, ) { let _ = consensus - .await_da_height(&header.entity.da_height) + .await_da_height(&header.entity.da_height()) .await .trace_err("Failed to wait for DA layer to sync"); } diff --git a/crates/services/upgradable-executor/src/executor.rs b/crates/services/upgradable-executor/src/executor.rs index 76d07ba3ab6..cc6ef6c1c0d 100644 --- a/crates/services/upgradable-executor/src/executor.rs +++ b/crates/services/upgradable-executor/src/executor.rs @@ -466,7 +466,7 @@ where block: &Block, options: ExecutionOptions, ) -> ExecutorResult> { - let block_version = block.header().state_transition_bytecode_version; + let block_version = block.header().state_transition_bytecode_version(); let native_executor_version = self.native_executor_version(); if block_version == native_executor_version { match &self.execution_strategy { @@ -502,7 +502,7 @@ where block: &Block, options: ExecutionOptions, ) -> ExecutorResult> { - let block_version = block.header().state_transition_bytecode_version; + let block_version = block.header().state_transition_bytecode_version(); let native_executor_version = self.native_executor_version(); if block_version == native_executor_version { self.native_validate_inner(block, options) @@ -596,7 +596,7 @@ where options: ExecutionOptions, ) -> ExecutorResult> { self.trace_block_version_warning( - block.header().state_transition_bytecode_version, + block.header().state_transition_bytecode_version(), ); let previous_block_height = block.header().height().pred(); diff --git a/crates/types/Cargo.toml b/crates/types/Cargo.toml index cbf9cd7753c..e7377efd0d2 100644 --- a/crates/types/Cargo.toml +++ b/crates/types/Cargo.toml @@ -24,7 +24,6 @@ derivative = { version = "2", default-features = false, optional = true, feature "use_core", ] } derive_more = { version = "0.99" } -enum_dispatch = { workspace = true } fuel-vm-private = { workspace = true, default-features = false, features = [ "alloc", ] } diff --git a/crates/types/src/blockchain/block.rs b/crates/types/src/blockchain/block.rs index 2d045d1e996..bcb7ad56f17 100644 --- a/crates/types/src/blockchain/block.rs +++ b/crates/types/src/blockchain/block.rs @@ -178,8 +178,18 @@ impl Block { // identifier on the fly. // // This assertion is a double-checks that this behavior is not changed. - debug_assert_eq!(self.header().id(), self.header().hash()); - self.header().id() + let header = self.header(); + match header { + BlockHeader::V1(header_v1) => { + debug_assert_eq!(header_v1.id(), header_v1.hash()); + header_v1.id() + } + #[cfg(feature = "fault-proving")] + BlockHeader::V2(header_v2) => { + debug_assert_eq!(header_v2.id(), header_v2.hash()); + header_v2.id() + } + } } /// Get the executed transactions. diff --git a/crates/types/src/blockchain/header.rs b/crates/types/src/blockchain/header.rs index 60a739dc9bb..b5dcf3d848e 100644 --- a/crates/types/src/blockchain/header.rs +++ b/crates/types/src/blockchain/header.rs @@ -24,24 +24,18 @@ use crate::{ MessageId, }, }; -use enum_dispatch::enum_dispatch; use tai64::Tai64; pub use v1::BlockHeaderV1; +use v1::GeneratedApplicationFieldsV1; #[cfg(feature = "fault-proving")] -pub use v2::{ - BlockHeaderV2, - FaultProvingHeader, -}; +pub use v2::BlockHeaderV2; +#[cfg(feature = "fault-proving")] +use v2::GeneratedApplicationFieldsV2; /// Version-able block header type #[derive(Clone, Debug, derivative::Derivative)] #[derivative(PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[enum_dispatch(GetBlockHeaderFields)] -#[cfg_attr( - any(test, feature = "test-helpers"), - enum_dispatch(BlockHeaderDataTestHelpers) -)] pub enum BlockHeader { /// V1 BlockHeader V1(BlockHeaderV1), @@ -50,159 +44,255 @@ pub enum BlockHeader { V2(BlockHeaderV2), } -/// Helpful methods for all variants of the block header. -#[enum_dispatch] -pub(crate) trait GetBlockHeaderFields { - /// Get the consensus portion of the header. - fn consensus(&self) -> &ConsensusHeader; - /// Get the application portion of the header. - fn application(&self) -> &ApplicationHeader; - /// Get the metadata of the header. - fn metadata(&self) -> &Option; - /// Re-generate the header metadata. - fn recalculate_metadata(&mut self); - /// Get the hash of the fuel header. - fn hash(&self) -> BlockId; - /// Get the transaction ID Commitment - fn tx_id_commitment(&self) -> Option; -} - -// reimplement GetBlockHeaderFields but with plain impl -// since both v1 and v2 support the fields, each function can just do -// GetBlockHeaderFields::fn(&self) impl BlockHeader { + /// Get the da height + pub fn da_height(&self) -> DaBlockHeight { + match self { + BlockHeader::V1(header) => header.application().da_height, + #[cfg(feature = "fault-proving")] + BlockHeader::V2(header) => header.application().da_height, + } + } + + /// Get the consensus parameters version + pub fn consensus_parameters_version(&self) -> ConsensusParametersVersion { + match self { + BlockHeader::V1(header) => header.application().consensus_parameters_version, + #[cfg(feature = "fault-proving")] + BlockHeader::V2(header) => header.application().consensus_parameters_version, + } + } + + /// Get the state transition bytecode version + pub fn state_transition_bytecode_version(&self) -> StateTransitionBytecodeVersion { + match self { + BlockHeader::V1(header) => { + header.application().state_transition_bytecode_version + } + #[cfg(feature = "fault-proving")] + BlockHeader::V2(header) => { + header.application().state_transition_bytecode_version + } + } + } + /// Get the consensus portion of the header. pub fn consensus(&self) -> &ConsensusHeader { - GetBlockHeaderFields::consensus(self) + match self { + BlockHeader::V1(header) => header.consensus(), + #[cfg(feature = "fault-proving")] + BlockHeader::V2(header) => header.consensus(), + } } - /// Get the application portion of the header. - pub fn application(&self) -> &ApplicationHeader { - GetBlockHeaderFields::application(self) + /// Get the Block ID + pub fn id(&self) -> BlockId { + match self { + BlockHeader::V1(header) => header.id(), + #[cfg(feature = "fault-proving")] + BlockHeader::V2(header) => header.id(), + } } - /// Get the metadata of the header. - pub fn metadata(&self) -> &Option { - GetBlockHeaderFields::metadata(self) + /// Validate the transactions + pub fn validate_transactions(&self, transactions: &[Transaction]) -> bool { + match self { + BlockHeader::V1(header) => header.validate_transactions(transactions), + #[cfg(feature = "fault-proving")] + BlockHeader::V2(header) => header.validate_transactions(transactions), + } } - /// Re-generate the header metadata. + /// Recalculate the metadata pub fn recalculate_metadata(&mut self) { - GetBlockHeaderFields::recalculate_metadata(self) + match self { + BlockHeader::V1(header) => header.recalculate_metadata(), + #[cfg(feature = "fault-proving")] + BlockHeader::V2(header) => header.recalculate_metadata(), + } } - /// Get the hash of the fuel header. - pub fn hash(&self) -> BlockId { - GetBlockHeaderFields::hash(self) + /// Getter for the transactions root + pub fn transactions_root(&self) -> Bytes32 { + match self { + BlockHeader::V1(header) => header.application().transactions_root, + #[cfg(feature = "fault-proving")] + BlockHeader::V2(header) => header.application().transactions_root, + } } - /// Get the cached fuel header hash. - pub fn id(&self) -> BlockId { - if let Some(ref metadata) = self.metadata() { - metadata.id - } else { - self.hash() + /// Getter for the transactions count + pub fn transactions_count(&self) -> u16 { + match self { + BlockHeader::V1(header) => header.application().transactions_count, + #[cfg(feature = "fault-proving")] + BlockHeader::V2(header) => header.application().transactions_count, } } - /// Validate the transactions match the header. - pub fn validate_transactions(&self, transactions: &[Transaction]) -> bool { - let transactions_root = generate_txns_root(transactions); + /// Getter for the message receipt count + pub fn message_receipt_count(&self) -> u32 { + match self { + BlockHeader::V1(header) => header.application().message_receipt_count, + #[cfg(feature = "fault-proving")] + BlockHeader::V2(header) => header.application().message_receipt_count, + } + } + + /// Getter for the message outbox root + pub fn message_outbox_root(&self) -> Bytes32 { + match self { + BlockHeader::V1(header) => header.application().message_outbox_root, + #[cfg(feature = "fault-proving")] + BlockHeader::V2(header) => header.application().message_outbox_root, + } + } - transactions_root == self.application().transactions_root - && transactions.len() == self.application().transactions_count as usize + /// Getter for the event inbox root + pub fn event_inbox_root(&self) -> Bytes32 { + match self { + BlockHeader::V1(header) => header.application().event_inbox_root, + #[cfg(feature = "fault-proving")] + BlockHeader::V2(header) => header.application().event_inbox_root, + } } - /// Getter for the tx id commitment + /// Getter for the transaction ID commitment pub fn tx_id_commitment(&self) -> Option { - GetBlockHeaderFields::tx_id_commitment(self) + match self { + BlockHeader::V1(header) => header.tx_id_commitment(), + #[cfg(feature = "fault-proving")] + BlockHeader::V2(header) => header.tx_id_commitment(), + } } -} -/// Helpful test methods for all variants of the block header. -#[cfg(any(test, feature = "test-helpers"))] -#[enum_dispatch] -pub(crate) trait BlockHeaderDataTestHelpers { - /// Mutable getter for consensus portion of header - fn consensus_mut(&mut self) -> &mut ConsensusHeader; - /// Set the entire consensus header - fn set_consensus_header( - &mut self, - consensus: ConsensusHeader, - ); - /// Mutable getter for application portion of header - fn application_mut(&mut self) -> &mut ApplicationHeader; - /// Set the entire application header - fn set_application_header( - &mut self, - application: ApplicationHeader, - ); - /// Set the block height for the header - fn set_block_height(&mut self, height: BlockHeight); - /// Set the previous root for the header - fn set_previous_root(&mut self, root: Bytes32); - /// Set the time for the header - fn set_time(&mut self, time: Tai64); - /// Set the transaction root for the header - fn set_transaction_root(&mut self, root: Bytes32); - /// Set the DA height for the header - fn set_da_height(&mut self, da_height: DaBlockHeight); + /// Alias the application header into an empty one. + pub fn as_empty_application_header(&self) -> ApplicationHeader { + match self { + BlockHeader::V1(header) => ApplicationHeader { + da_height: header.application().da_height, + consensus_parameters_version: header + .application() + .consensus_parameters_version, + state_transition_bytecode_version: header + .application() + .state_transition_bytecode_version, + generated: Empty {}, + }, + #[cfg(feature = "fault-proving")] + BlockHeader::V2(header) => ApplicationHeader { + da_height: header.application().da_height, + consensus_parameters_version: header + .application() + .consensus_parameters_version, + state_transition_bytecode_version: header + .application() + .state_transition_bytecode_version, + generated: Empty {}, + }, + } + } } -/// Reimplement helpers so that its easy to import #[cfg(any(test, feature = "test-helpers"))] impl BlockHeader { - /// Mutable getter for consensus portion of header - pub fn consensus_mut(&mut self) -> &mut ConsensusHeader { - BlockHeaderDataTestHelpers::consensus_mut(self) + /// Set the time for the header + pub fn set_time(&mut self, time: Tai64) { + match self { + BlockHeader::V1(header) => header.set_time(time), + #[cfg(feature = "fault-proving")] + BlockHeader::V2(header) => header.set_time(time), + } } - /// Set the entire consensus header + /// Set the da height for the header + pub fn set_da_height(&mut self, da_height: DaBlockHeight) { + match self { + BlockHeader::V1(header) => header.set_da_height(da_height), + #[cfg(feature = "fault-proving")] + BlockHeader::V2(header) => header.set_da_height(da_height), + } + } + + /// Set the consensus header pub fn set_consensus_header( &mut self, consensus: ConsensusHeader, ) { - BlockHeaderDataTestHelpers::set_consensus_header(self, consensus) - } - - /// Mutable getter for application portion of header - pub fn application_mut( - &mut self, - ) -> &mut ApplicationHeader { - BlockHeaderDataTestHelpers::application_mut(self) + match self { + BlockHeader::V1(header) => header.set_consensus_header(consensus), + #[cfg(feature = "fault-proving")] + BlockHeader::V2(header) => header.set_consensus_header(consensus), + } } - /// Set the entire application header - pub fn set_application_header( - &mut self, - application: ApplicationHeader, - ) { - BlockHeaderDataTestHelpers::set_application_header(self, application) + /// Set the previous root for the header + pub fn set_previous_root(&mut self, root: Bytes32) { + match self { + BlockHeader::V1(header) => header.set_previous_root(root), + #[cfg(feature = "fault-proving")] + BlockHeader::V2(header) => header.set_previous_root(root), + } } /// Set the block height for the header pub fn set_block_height(&mut self, height: BlockHeight) { - BlockHeaderDataTestHelpers::set_block_height(self, height) - } - - /// Set the previous root for the header - pub fn set_previous_root(&mut self, root: Bytes32) { - BlockHeaderDataTestHelpers::set_previous_root(self, root) + match self { + BlockHeader::V1(header) => header.set_block_height(height), + #[cfg(feature = "fault-proving")] + BlockHeader::V2(header) => header.set_block_height(height), + } } - /// Set the time for the header - pub fn set_time(&mut self, time: Tai64) { - BlockHeaderDataTestHelpers::set_time(self, time) + /// Mutable getter for consensus portion of header + pub fn consensus_mut(&mut self) -> &mut ConsensusHeader { + match self { + BlockHeader::V1(header) => header.consensus_mut(), + #[cfg(feature = "fault-proving")] + BlockHeader::V2(header) => header.consensus_mut(), + } } /// Set the transaction root for the header pub fn set_transaction_root(&mut self, root: Bytes32) { - BlockHeaderDataTestHelpers::set_transaction_root(self, root) + match self { + BlockHeader::V1(header) => { + header.set_transaction_root(root); + } + #[cfg(feature = "fault-proving")] + BlockHeader::V2(header) => { + header.set_transaction_root(root); + } + } } - /// Set the DA height for the header - pub fn set_da_height(&mut self, da_height: DaBlockHeight) { - BlockHeaderDataTestHelpers::set_da_height(self, da_height) + /// Set the consensus parameters version + pub fn set_consensus_parameters_version( + &mut self, + version: ConsensusParametersVersion, + ) { + match self { + BlockHeader::V1(header) => { + header.set_consensus_parameters_version(version); + } + #[cfg(feature = "fault-proving")] + BlockHeader::V2(header) => { + header.set_consensus_parameters_version(version); + } + } + } + + /// Set the application hash + pub fn set_application_hash(&mut self, hash: Bytes32) { + match self { + BlockHeader::V1(header) => { + header.set_application_hash(hash); + } + #[cfg(feature = "fault-proving")] + BlockHeader::V2(header) => { + header.set_application_hash(hash); + } + } } } @@ -260,22 +350,24 @@ where } } -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(any(test, feature = "test-helpers"), derive(Default))] -/// Concrete generated application header fields. -/// These are generated once the full block has been run. -pub struct GeneratedApplicationFields { - /// Number of transactions in this block. - pub transactions_count: u16, - /// Number of message receipts in this block. - pub message_receipt_count: u32, - /// Merkle root of transactions. - pub transactions_root: Bytes32, - /// Merkle root of message receipts in this block. - pub message_outbox_root: Bytes32, - /// Root hash of all imported events from L1 - pub event_inbox_root: Bytes32, +#[cfg(feature = "fault-proving")] +impl From<&ApplicationHeader> + for ApplicationHeader +{ + fn from(value: &ApplicationHeader) -> Self { + Self { + da_height: value.da_height, + consensus_parameters_version: value.consensus_parameters_version, + state_transition_bytecode_version: value.state_transition_bytecode_version, + generated: GeneratedApplicationFieldsV1 { + transactions_count: value.transactions_count, + message_receipt_count: value.message_receipt_count, + transactions_root: value.transactions_root, + message_outbox_root: value.message_outbox_root, + event_inbox_root: value.event_inbox_root, + }, + } + } } #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -315,9 +407,22 @@ pub struct BlockHeaderMetadata { #[cfg(any(test, feature = "test-helpers"))] impl Default for BlockHeader { fn default() -> Self { - let mut default: BlockHeader = BlockHeaderV1::default().into(); - default.recalculate_metadata(); - default + match () { + #[cfg(feature = "fault-proving")] + () => { + let mut default = BlockHeaderV2::default(); + default.recalculate_metadata(); + + BlockHeader::V2(default) + } + #[cfg(not(feature = "fault-proving"))] + () => { + let mut default = BlockHeaderV1::default(); + default.recalculate_metadata(); + + BlockHeader::V1(default) + } + } } } @@ -327,9 +432,19 @@ impl BlockHeader { /// The method should be used only for tests. pub fn new_block(height: BlockHeight, time: Tai64) -> Self { let mut default = Self::default(); - default.consensus_mut().height = height; - default.consensus_mut().time = time; - default.recalculate_metadata(); + match default { + BlockHeader::V1(ref mut header) => { + header.consensus_mut().height = height; + header.consensus_mut().time = time; + header.recalculate_metadata(); + } + #[cfg(feature = "fault-proving")] + BlockHeader::V2(ref mut header) => { + header.consensus_mut().height = height; + header.consensus_mut().time = time; + header.recalculate_metadata(); + } + } default } } @@ -338,19 +453,35 @@ impl BlockHeader { impl BlockHeader { /// Merkle root of all previous block header hashes. pub fn prev_root(&self) -> &Bytes32 { - &self.as_ref().prev_root + match self { + BlockHeader::V1(header) => &header.consensus().prev_root, + #[cfg(feature = "fault-proving")] + BlockHeader::V2(header) => &header.consensus().prev_root, + } } /// Fuel block height. pub fn height(&self) -> &BlockHeight { - &self.as_ref().height + match self { + BlockHeader::V1(header) => &header.consensus().height, + #[cfg(feature = "fault-proving")] + BlockHeader::V2(header) => &header.consensus().height, + } } /// The block producer time. pub fn time(&self) -> Tai64 { - self.as_ref().time + match self { + BlockHeader::V1(header) => header.consensus().time, + #[cfg(feature = "fault-proving")] + BlockHeader::V2(header) => header.consensus().time, + } } /// The hash of the application header. pub fn application_hash(&self) -> &Bytes32 { - &self.as_ref().application_hash + match self { + BlockHeader::V1(header) => &header.consensus().application_hash, + #[cfg(feature = "fault-proving")] + BlockHeader::V2(header) => &header.consensus().application_hash, + } } /// The type of consensus this header is using. @@ -386,12 +517,17 @@ impl From<&BlockHeader> for PartialBlockHeader { time, .. } = *header.consensus(); + + let da_height = header.da_height(); + let consensus_parameters_version = header.consensus_parameters_version(); + let state_transition_bytecode_version = + header.state_transition_bytecode_version(); + PartialBlockHeader { application: ApplicationHeader { - da_height: header.da_height, - consensus_parameters_version: header.consensus_parameters_version, - state_transition_bytecode_version: header - .state_transition_bytecode_version, + da_height, + consensus_parameters_version, + state_transition_bytecode_version, generated: Empty {}, }, consensus: ConsensusHeader { @@ -448,26 +584,26 @@ impl PartialBlockHeader { .root() .into(); - let application = ApplicationHeader { - da_height: self.application.da_height, - consensus_parameters_version: self.application.consensus_parameters_version, - state_transition_bytecode_version: self - .application - .state_transition_bytecode_version, - generated: GeneratedApplicationFields { - transactions_count: u16::try_from(transactions.len()) - .map_err(|_| BlockHeaderError::TooManyTransactions)?, - message_receipt_count: u32::try_from(outbox_message_ids.len()) - .map_err(|_| BlockHeaderError::TooManyMessages)?, - transactions_root, - message_outbox_root, - event_inbox_root, - }, - }; - #[cfg(not(feature = "fault-proving"))] - let mut header: BlockHeader = BlockHeaderV1 { - application, + let mut header: BlockHeader = BlockHeader::V1(BlockHeaderV1 { + application: ApplicationHeader { + da_height: self.application.da_height, + consensus_parameters_version: self + .application + .consensus_parameters_version, + state_transition_bytecode_version: self + .application + .state_transition_bytecode_version, + generated: GeneratedApplicationFieldsV1 { + transactions_count: u16::try_from(transactions.len()) + .map_err(|_| BlockHeaderError::TooManyTransactions)?, + message_receipt_count: u32::try_from(outbox_message_ids.len()) + .map_err(|_| BlockHeaderError::TooManyMessages)?, + transactions_root, + message_outbox_root, + event_inbox_root, + }, + }, consensus: ConsensusHeader { prev_root: self.consensus.prev_root, height: self.consensus.height, @@ -478,12 +614,32 @@ impl PartialBlockHeader { }, }, metadata: None, - } - .into(); + }); #[cfg(feature = "fault-proving")] - let mut header: BlockHeader = BlockHeaderV2 { - application, + let mut header: BlockHeader = BlockHeader::V2(BlockHeaderV2 { + application: ApplicationHeader { + da_height: self.application.da_height, + consensus_parameters_version: self + .application + .consensus_parameters_version, + state_transition_bytecode_version: self + .application + .state_transition_bytecode_version, + generated: GeneratedApplicationFieldsV2 { + transactions_count: u16::try_from(transactions.len()) + .map_err(|_| BlockHeaderError::TooManyTransactions)?, + message_receipt_count: u32::try_from(outbox_message_ids.len()) + .map_err(|_| BlockHeaderError::TooManyMessages)?, + transactions_root, + message_outbox_root, + event_inbox_root, + tx_id_commitment: v2::generate_tx_id_commitment( + transactions, + chain_id, + ), + }, + }, consensus: ConsensusHeader { prev_root: self.consensus.prev_root, height: self.consensus.height, @@ -494,11 +650,7 @@ impl PartialBlockHeader { }, }, metadata: None, - fault_proving: FaultProvingHeader { - tx_id_commitment: v2::generate_tx_id_commitment(transactions, chain_id), - }, - } - .into(); + }); // Cache the hash. header.recalculate_metadata(); @@ -517,39 +669,6 @@ fn generate_txns_root(transactions: &[Transaction]) -> Bytes32 { transaction_tree.root().into() } -impl ApplicationHeader { - /// Hash the application header. - pub fn hash(&self) -> Bytes32 { - // Order matters and is the same as the spec. - let mut hasher = crate::fuel_crypto::Hasher::default(); - let Self { - da_height, - consensus_parameters_version, - state_transition_bytecode_version, - generated: - GeneratedApplicationFields { - transactions_count, - message_receipt_count, - transactions_root, - message_outbox_root, - event_inbox_root, - }, - } = self; - - hasher.input(da_height.to_be_bytes()); - hasher.input(consensus_parameters_version.to_be_bytes()); - hasher.input(state_transition_bytecode_version.to_be_bytes()); - - hasher.input(transactions_count.to_be_bytes()); - hasher.input(message_receipt_count.to_be_bytes()); - hasher.input(transactions_root.as_ref()); - hasher.input(message_outbox_root.as_ref()); - hasher.input(event_inbox_root.as_ref()); - - hasher.digest() - } -} - impl ConsensusHeader { /// Hash the consensus header. pub fn hash(&self) -> BlockId { @@ -586,14 +705,6 @@ where } } -impl core::ops::Deref for BlockHeader { - type Target = ApplicationHeader; - - fn deref(&self) -> &Self::Target { - self.application() - } -} - impl core::ops::Deref for PartialBlockHeader { type Target = ApplicationHeader; @@ -602,14 +713,6 @@ impl core::ops::Deref for PartialBlockHeader { } } -impl core::ops::Deref for ApplicationHeader { - type Target = GeneratedApplicationFields; - - fn deref(&self) -> &Self::Target { - &self.generated - } -} - impl core::ops::Deref for ConsensusHeader { type Target = GeneratedConsensusFields; @@ -618,12 +721,6 @@ impl core::ops::Deref for ConsensusHeader { } } -impl core::convert::AsRef> for BlockHeader { - fn as_ref(&self) -> &ConsensusHeader { - self.consensus() - } -} - impl core::convert::AsRef> for PartialBlockHeader { fn as_ref(&self) -> &ConsensusHeader { &self.consensus diff --git a/crates/types/src/blockchain/header/v1.rs b/crates/types/src/blockchain/header/v1.rs index cfa4d5fe999..5f3f62b01c7 100644 --- a/crates/types/src/blockchain/header/v1.rs +++ b/crates/types/src/blockchain/header/v1.rs @@ -1,16 +1,18 @@ use crate::{ blockchain::{ header::{ + generate_txns_root, ApplicationHeader, BlockHeaderMetadata, ConsensusHeader, - GeneratedApplicationFields, GeneratedConsensusFields, - GetBlockHeaderFields, }, primitives::BlockId, }, - fuel_tx::Bytes32, + fuel_tx::{ + Bytes32, + Transaction, + }, }; /// A fuel block header that has all the fields generated because it @@ -21,9 +23,9 @@ use crate::{ #[cfg_attr(any(test, feature = "test-helpers"), derive(Default))] pub struct BlockHeaderV1 { /// The application header. - pub application: ApplicationHeader, + pub(crate) application: ApplicationHeader, /// The consensus header. - pub consensus: ConsensusHeader, + pub(crate) consensus: ConsensusHeader, /// The header metadata calculated during creation. /// The field is pub(crate) to enforce the use of the [`PartialBlockHeader::generate`] method. #[cfg_attr(feature = "serde", serde(skip))] @@ -31,83 +33,179 @@ pub struct BlockHeaderV1 { pub(crate) metadata: Option, } -impl GetBlockHeaderFields for BlockHeaderV1 { - fn consensus(&self) -> &ConsensusHeader { +impl BlockHeaderV1 { + pub(crate) fn consensus(&self) -> &ConsensusHeader { &self.consensus } - fn application(&self) -> &ApplicationHeader { + /// Returns a reference to the application header. + pub fn application(&self) -> &ApplicationHeader { &self.application } - fn metadata(&self) -> &Option { + pub(crate) fn metadata(&self) -> &Option { &self.metadata } - fn recalculate_metadata(&mut self) { + pub(crate) fn recalculate_metadata(&mut self) { let application_hash = self.application().hash(); self.consensus.generated.application_hash = application_hash; let id = self.hash(); self.metadata = Some(BlockHeaderMetadata { id }); } - fn hash(&self) -> BlockId { + pub(crate) fn hash(&self) -> BlockId { debug_assert_eq!(&self.consensus.application_hash, &self.application().hash()); // This internally hashes the hash of the application header. self.consensus().hash() } - fn tx_id_commitment(&self) -> Option { + pub(crate) fn tx_id_commitment(&self) -> Option { None } + + pub(crate) fn id(&self) -> BlockId { + if let Some(ref metadata) = self.metadata() { + metadata.id + } else { + self.hash() + } + } + + pub(crate) fn validate_transactions(&self, transactions: &[Transaction]) -> bool { + let transactions_root = generate_txns_root(transactions); + + transactions_root == self.application().transactions_root + && transactions.len() == self.application().transactions_count as usize + } } #[cfg(any(test, feature = "test-helpers"))] -impl crate::blockchain::header::BlockHeaderDataTestHelpers for BlockHeaderV1 { - fn consensus_mut(&mut self) -> &mut ConsensusHeader { +impl BlockHeaderV1 { + pub(crate) fn consensus_mut( + &mut self, + ) -> &mut ConsensusHeader { &mut self.consensus } - fn set_consensus_header( + pub(crate) fn set_consensus_header( &mut self, consensus: ConsensusHeader, ) { self.consensus = consensus; } - fn application_mut(&mut self) -> &mut ApplicationHeader { + /// Returns a mutable reference to the application header. + pub fn application_mut( + &mut self, + ) -> &mut ApplicationHeader { &mut self.application } - fn set_application_header( + /// Sets the application header for the block. + pub fn set_application_header( &mut self, - application: ApplicationHeader, + application: ApplicationHeader, ) { self.application = application; } - fn set_block_height(&mut self, height: crate::fuel_types::BlockHeight) { + pub(crate) fn set_block_height(&mut self, height: crate::fuel_types::BlockHeight) { self.consensus_mut().height = height; self.recalculate_metadata(); } - fn set_previous_root(&mut self, root: crate::fuel_tx::Bytes32) { + pub(crate) fn set_previous_root(&mut self, root: crate::fuel_tx::Bytes32) { self.consensus_mut().prev_root = root; self.recalculate_metadata(); } - fn set_time(&mut self, time: tai64::Tai64) { + pub(crate) fn set_time(&mut self, time: tai64::Tai64) { self.consensus_mut().time = time; self.recalculate_metadata(); } - fn set_transaction_root(&mut self, root: crate::fuel_tx::Bytes32) { + pub(crate) fn set_transaction_root(&mut self, root: crate::fuel_tx::Bytes32) { self.application_mut().generated.transactions_root = root; self.recalculate_metadata(); } - fn set_da_height(&mut self, da_height: crate::blockchain::primitives::DaBlockHeight) { + pub(crate) fn set_da_height( + &mut self, + da_height: crate::blockchain::primitives::DaBlockHeight, + ) { self.application_mut().da_height = da_height; self.recalculate_metadata(); } + + pub(crate) fn set_consensus_parameters_version( + &mut self, + version: super::ConsensusParametersVersion, + ) { + self.application_mut().consensus_parameters_version = version; + self.recalculate_metadata(); + } + + pub(crate) fn set_application_hash(&mut self, hash: Bytes32) { + self.consensus_mut().generated.application_hash = hash; + } +} + +/// Concrete generated application header fields. +/// These are generated once the full block has been run. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(any(test, feature = "test-helpers"), derive(Default))] +pub struct GeneratedApplicationFieldsV1 { + /// Number of transactions in this block. + pub transactions_count: u16, + /// Number of message receipts in this block. + pub message_receipt_count: u32, + /// Merkle root of transactions. + pub transactions_root: Bytes32, + /// Merkle root of message receipts in this block. + pub message_outbox_root: Bytes32, + /// Root hash of all imported events from L1 + pub event_inbox_root: Bytes32, +} + +impl ApplicationHeader { + /// Hash the application header. + pub fn hash(&self) -> Bytes32 { + // Order matters and is the same as the spec. + let mut hasher = crate::fuel_crypto::Hasher::default(); + let Self { + da_height, + consensus_parameters_version, + state_transition_bytecode_version, + generated: + GeneratedApplicationFieldsV1 { + transactions_count, + message_receipt_count, + transactions_root, + message_outbox_root, + event_inbox_root, + }, + } = self; + + hasher.input(da_height.to_be_bytes()); + hasher.input(consensus_parameters_version.to_be_bytes()); + hasher.input(state_transition_bytecode_version.to_be_bytes()); + + hasher.input(transactions_count.to_be_bytes()); + hasher.input(message_receipt_count.to_be_bytes()); + hasher.input(transactions_root.as_ref()); + hasher.input(message_outbox_root.as_ref()); + hasher.input(event_inbox_root.as_ref()); + + hasher.digest() + } +} + +impl core::ops::Deref for ApplicationHeader { + type Target = GeneratedApplicationFieldsV1; + + fn deref(&self) -> &Self::Target { + &self.generated + } } diff --git a/crates/types/src/blockchain/header/v2.rs b/crates/types/src/blockchain/header/v2.rs index 30d82bafa40..fc5ab775b0a 100644 --- a/crates/types/src/blockchain/header/v2.rs +++ b/crates/types/src/blockchain/header/v2.rs @@ -1,12 +1,11 @@ use crate::{ blockchain::{ header::{ + generate_txns_root, ApplicationHeader, BlockHeaderMetadata, ConsensusHeader, - GeneratedApplicationFields, GeneratedConsensusFields, - GetBlockHeaderFields, }, primitives::BlockId, }, @@ -37,15 +36,6 @@ pub(crate) fn generate_tx_id_commitment( hasher.digest() } -/// The fault proving header -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[cfg_attr(any(test, feature = "test-helpers"), derive(Default))] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct FaultProvingHeader { - /// The transaction id commitment - pub tx_id_commitment: Bytes32, -} - /// A fuel block header that has all the fields generated because it /// has been executed. /// differences from V1: @@ -56,99 +46,196 @@ pub struct FaultProvingHeader { #[cfg_attr(any(test, feature = "test-helpers"), derive(Default))] pub struct BlockHeaderV2 { /// The application header. - pub application: ApplicationHeader, + pub(crate) application: ApplicationHeader, /// The consensus header. - pub consensus: ConsensusHeader, + pub(crate) consensus: ConsensusHeader, /// The header metadata calculated during creation. /// The field is private to enforce the use of the [`PartialBlockHeader::generate`] method. #[cfg_attr(feature = "serde", serde(skip))] #[derivative(PartialEq = "ignore")] pub(crate) metadata: Option, - /// fault proving relevant data - pub fault_proving: FaultProvingHeader, } -impl GetBlockHeaderFields for BlockHeaderV2 { - fn consensus(&self) -> &ConsensusHeader { +impl BlockHeaderV2 { + pub(crate) fn consensus(&self) -> &ConsensusHeader { &self.consensus } - fn application(&self) -> &ApplicationHeader { + /// Returns a reference to the application header. + pub fn application(&self) -> &ApplicationHeader { &self.application } - fn metadata(&self) -> &Option { + pub(crate) fn metadata(&self) -> &Option { &self.metadata } - fn recalculate_metadata(&mut self) { + pub(crate) fn recalculate_metadata(&mut self) { let application_hash = self.application().hash(); self.consensus.generated.application_hash = application_hash; let id = self.hash(); self.metadata = Some(BlockHeaderMetadata { id }); } - fn hash(&self) -> BlockId { + pub(crate) fn hash(&self) -> BlockId { debug_assert_eq!(&self.consensus.application_hash, &self.application().hash()); // This internally hashes the hash of the application header. - let consensus_header_hash = self.consensus().hash(); - let mut hasher = fuel_crypto::Hasher::default(); - hasher.input::<&Bytes32>(consensus_header_hash.as_ref()); - hasher.input(self.fault_proving.tx_id_commitment.as_ref()); - hasher.digest().into() + self.consensus().hash() + } + + pub(crate) fn tx_id_commitment(&self) -> Option { + Some(self.application().tx_id_commitment) + } + + pub(crate) fn id(&self) -> BlockId { + if let Some(ref metadata) = self.metadata() { + metadata.id + } else { + self.hash() + } } - fn tx_id_commitment(&self) -> Option { - Some(self.fault_proving.tx_id_commitment) + pub(crate) fn validate_transactions(&self, transactions: &[Transaction]) -> bool { + let transactions_root = generate_txns_root(transactions); + + transactions_root == self.application().transactions_root + && transactions.len() == self.application().transactions_count as usize } } #[cfg(any(test, feature = "test-helpers"))] -impl crate::blockchain::header::BlockHeaderDataTestHelpers for BlockHeaderV2 { - fn consensus_mut(&mut self) -> &mut ConsensusHeader { +impl BlockHeaderV2 { + pub(crate) fn consensus_mut( + &mut self, + ) -> &mut ConsensusHeader { &mut self.consensus } - fn set_consensus_header( + pub(crate) fn set_consensus_header( &mut self, consensus: ConsensusHeader, ) { self.consensus = consensus; } - fn application_mut(&mut self) -> &mut ApplicationHeader { + /// Returns a mutable reference to the application header. + pub fn application_mut( + &mut self, + ) -> &mut ApplicationHeader { &mut self.application } - fn set_application_header( + /// Sets the application header. + pub fn set_application_header( &mut self, - application: ApplicationHeader, + application: ApplicationHeader, ) { self.application = application; } - fn set_block_height(&mut self, height: crate::fuel_types::BlockHeight) { + pub(crate) fn set_block_height(&mut self, height: crate::fuel_types::BlockHeight) { self.consensus_mut().height = height; self.recalculate_metadata(); } - fn set_previous_root(&mut self, root: crate::fuel_tx::Bytes32) { + pub(crate) fn set_previous_root(&mut self, root: crate::fuel_tx::Bytes32) { self.consensus_mut().prev_root = root; self.recalculate_metadata(); } - fn set_time(&mut self, time: tai64::Tai64) { + pub(crate) fn set_time(&mut self, time: tai64::Tai64) { self.consensus_mut().time = time; self.recalculate_metadata(); } - fn set_transaction_root(&mut self, root: crate::fuel_tx::Bytes32) { + pub(crate) fn set_transaction_root(&mut self, root: crate::fuel_tx::Bytes32) { self.application_mut().generated.transactions_root = root; self.recalculate_metadata(); } - fn set_da_height(&mut self, da_height: crate::blockchain::primitives::DaBlockHeight) { + pub(crate) fn set_da_height( + &mut self, + da_height: crate::blockchain::primitives::DaBlockHeight, + ) { self.application_mut().da_height = da_height; self.recalculate_metadata(); } + + pub(crate) fn set_consensus_parameters_version( + &mut self, + version: super::ConsensusParametersVersion, + ) { + self.application_mut().consensus_parameters_version = version; + self.recalculate_metadata(); + } + + pub(crate) fn set_application_hash(&mut self, hash: Bytes32) { + self.consensus_mut().generated.application_hash = hash; + } +} + +/// Concrete generated application header fields. +/// These are generated once the full block has been run. +/// contains the tx_id_commitment field +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(any(test, feature = "test-helpers"), derive(Default))] +pub struct GeneratedApplicationFieldsV2 { + /// Number of transactions in this block. + pub transactions_count: u16, + /// Number of message receipts in this block. + pub message_receipt_count: u32, + /// Merkle root of transactions. + pub transactions_root: Bytes32, + /// Merkle root of message receipts in this block. + pub message_outbox_root: Bytes32, + /// Root hash of all imported events from L1 + pub event_inbox_root: Bytes32, + /// TxID commitment + pub tx_id_commitment: Bytes32, +} + +impl ApplicationHeader { + /// Hash the application header. + pub fn hash(&self) -> Bytes32 { + // Order matters and is the same as the spec. + let mut hasher = crate::fuel_crypto::Hasher::default(); + let Self { + da_height, + consensus_parameters_version, + state_transition_bytecode_version, + generated: + GeneratedApplicationFieldsV2 { + transactions_count, + message_receipt_count, + transactions_root, + message_outbox_root, + event_inbox_root, + tx_id_commitment, + }, + } = self; + + hasher.input(da_height.to_be_bytes()); + hasher.input(consensus_parameters_version.to_be_bytes()); + hasher.input(state_transition_bytecode_version.to_be_bytes()); + + hasher.input(transactions_count.to_be_bytes()); + hasher.input(message_receipt_count.to_be_bytes()); + hasher.input(transactions_root.as_ref()); + hasher.input(message_outbox_root.as_ref()); + hasher.input(event_inbox_root.as_ref()); + + // this is the only difference between the two versions + hasher.input(tx_id_commitment.as_ref()); + + hasher.digest() + } +} + +impl core::ops::Deref for ApplicationHeader { + type Target = GeneratedApplicationFieldsV2; + + fn deref(&self) -> &Self::Target { + &self.generated + } }