use crate::ports::{ BlockProducer, BlockProducerDatabase, Relayer, TxPool, }; use fuel_core_storage::{ not_found, transactional::{ AtomicView, Changes, }, Result as StorageResult, }; use fuel_core_types::{ blockchain::{ block::{ Block, CompressedBlock, }, header::{ ConsensusParametersVersion, StateTransitionBytecodeVersion, }, primitives::DaBlockHeight, }, fuel_types::{ Address, BlockHeight, Bytes32, ChainId, }, services::{ block_producer::Components, executor::{ Error as ExecutorError, ExecutionResult, Result as ExecutorResult, UncommittedResult, }, txpool::ArcPoolTx, }, }; use std::{ borrow::Cow, collections::HashMap, ops::Deref, sync::{ Arc, Mutex, }, }; // TODO: Replace mocks with `mockall`. #[derive(Default, Clone)] pub struct MockRelayer { pub block_production_key: Address, pub latest_block_height: DaBlockHeight, pub latest_da_blocks_with_costs: HashMap, } #[async_trait::async_trait] impl Relayer for MockRelayer { async fn wait_for_at_least_height( &self, _height: &DaBlockHeight, ) -> anyhow::Result { let heighest = self.latest_block_height; Ok(heighest) } async fn get_cost_for_block(&self, height: &DaBlockHeight) -> anyhow::Result { let cost = self .latest_da_blocks_with_costs .get(height) .cloned() .unwrap_or_default(); Ok(cost) } } #[derive(Default)] pub struct MockTxPool(pub Vec); #[async_trait::async_trait] impl TxPool for MockTxPool { type TxSource = Vec; fn get_source(&self, _: BlockHeight) -> Self::TxSource { self.0.clone() } } #[derive(Default)] pub struct MockExecutor(pub MockDb); #[derive(Debug)] struct DatabaseTransaction { database: MockDb, } impl AsMut for DatabaseTransaction { fn as_mut(&mut self) -> &mut MockDb { &mut self.database } } impl AsRef for DatabaseTransaction { fn as_ref(&self) -> &MockDb { &self.database } } impl AsMut for MockDb { fn as_mut(&mut self) -> &mut MockDb { self } } impl AsRef for MockDb { fn as_ref(&self) -> &MockDb { self } } fn to_block(component: &Components>) -> Block { let transactions = component .transactions_source .clone() .into_iter() .map(|tx| tx.as_ref().into()) .collect(); Block::new( component.header_to_produce, transactions, &[], Default::default(), ) .unwrap() } impl BlockProducer> for MockExecutor { fn produce_without_commit( &self, component: Components>, ) -> ExecutorResult> { let block = to_block(&component); // simulate executor inserting a block let mut block_db = self.0.blocks.lock().unwrap(); block_db.insert( *block.header().height(), block.compress(&ChainId::default()), ); Ok(UncommittedResult::new( ExecutionResult { block, skipped_transactions: vec![], tx_status: vec![], events: vec![], }, Default::default(), )) } } pub struct FailingMockExecutor(pub Mutex>); impl BlockProducer> for FailingMockExecutor { fn produce_without_commit( &self, component: Components>, ) -> ExecutorResult> { // simulate an execution failure let mut err = self.0.lock().unwrap(); if let Some(err) = err.take() { Err(err) } else { let block = to_block(&component); Ok(UncommittedResult::new( ExecutionResult { block, skipped_transactions: vec![], tx_status: vec![], events: vec![], }, Default::default(), )) } } } #[derive(Clone)] pub struct MockExecutorWithCapture { pub captured: Arc>>>>, } impl BlockProducer> for MockExecutorWithCapture { fn produce_without_commit( &self, component: Components>, ) -> ExecutorResult> { let block = to_block(&component); *self.captured.lock().unwrap() = Some(component); Ok(UncommittedResult::new( ExecutionResult { block, skipped_transactions: vec![], tx_status: vec![], events: vec![], }, Default::default(), )) } } impl Default for MockExecutorWithCapture { fn default() -> Self { Self { captured: Arc::new(Mutex::new(None)), } } } #[derive(Clone, Default, Debug)] pub struct MockDb { pub blocks: Arc>>, pub consensus_parameters_version: ConsensusParametersVersion, pub state_transition_bytecode_version: StateTransitionBytecodeVersion, } impl AtomicView for MockDb { type View = Self; type Height = BlockHeight; fn latest_height(&self) -> Option { let blocks = self.blocks.lock().unwrap(); blocks.keys().max().cloned() } fn view_at(&self, _: &BlockHeight) -> StorageResult { Ok(self.latest_view()) } fn latest_view(&self) -> Self::View { self.clone() } } impl BlockProducerDatabase for MockDb { fn get_block(&self, height: &BlockHeight) -> StorageResult> { let blocks = self.blocks.lock().unwrap(); blocks .get(height) .cloned() .map(Cow::Owned) .ok_or(not_found!("Didn't find block for test")) } fn block_header_merkle_root(&self, height: &BlockHeight) -> StorageResult { Ok(Bytes32::new( [u8::try_from(*height.deref()).expect("Test use small values"); 32], )) } fn latest_consensus_parameters_version( &self, ) -> StorageResult { Ok(self.consensus_parameters_version) } fn latest_state_transition_bytecode_version( &self, ) -> StorageResult { Ok(self.state_transition_bytecode_version) } }