diff --git a/CHANGELOG.md b/CHANGELOG.md index c1622952d9..3758f0f64a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Fixed +- [#1866](https://github.com/FuelLabs/fuel-core/pull/1866): Fixed a runtime panic that occurred when restarting a node. The panic happens when the relayer database is already populated, and the relayer attempts an empty commit during start up. This invalid commit is removed in this PR. - [#1871](https://github.com/FuelLabs/fuel-core/pull/1871): Fixed `block` endpoint to return fetch the blocks from both databases after regenesis. - [#1856](https://github.com/FuelLabs/fuel-core/pull/1856): Replaced instances of `Union` with `Enum` for GraphQL definitions of `ConsensusParametersVersion` and related types. This is needed because `Union` does not support multiple `Version`s inside discriminants or empty variants. diff --git a/crates/fuel-core/src/combined_database.rs b/crates/fuel-core/src/combined_database.rs index 6023c580a4..3e3a18c886 100644 --- a/crates/fuel-core/src/combined_database.rs +++ b/crates/fuel-core/src/combined_database.rs @@ -147,6 +147,11 @@ impl CombinedDatabase { &self.relayer } + #[cfg(any(feature = "test-helpers", test))] + pub fn relayer_mut(&mut self) -> &mut Database { + &mut self.relayer + } + #[cfg(feature = "test-helpers")] pub fn read_state_config(&self) -> StorageResult { use fuel_core_chain_config::AddTable; diff --git a/crates/fuel-core/src/database.rs b/crates/fuel-core/src/database.rs index de14aac4f2..de4efe7a90 100644 --- a/crates/fuel-core/src/database.rs +++ b/crates/fuel-core/src/database.rs @@ -622,7 +622,7 @@ mod tests { .unwrap(); // Then - assert_eq!(database.latest_height().unwrap(), None); + assert_eq!(AtomicView::latest_height(&database), None); } #[test] @@ -782,7 +782,7 @@ mod tests { .unwrap(); // Then - assert_eq!(database.latest_height().unwrap(), None); + assert_eq!(AtomicView::latest_height(&database), None); } #[test] @@ -903,10 +903,7 @@ mod tests { database_description::relayer::Relayer, DatabaseHeight, }; - use fuel_core_relayer::storage::{ - DaHeightTable, - EventsHistory, - }; + use fuel_core_relayer::storage::EventsHistory; use fuel_core_storage::transactional::WriteTransaction; #[test] @@ -939,12 +936,18 @@ mod tests { // When database - .storage_as_mut::() - .insert(&(), &DaBlockHeight::default()) + .storage_as_mut::>() + .insert( + &(), + &DatabaseMetadata::::V1 { + version: Default::default(), + height: Default::default(), + }, + ) .unwrap(); // Then - assert_eq!(database.latest_height().unwrap(), None); + assert_eq!(AtomicView::latest_height(&database), None); } #[test] @@ -1008,9 +1011,13 @@ mod tests { .unwrap(); // When - let result = database - .storage_as_mut::() - .insert(&(), &DaBlockHeight::default()); + let result = database.storage_as_mut::>().insert( + &(), + &DatabaseMetadata::::V1 { + version: Default::default(), + height: Default::default(), + }, + ); // Then assert!(result.is_err()); diff --git a/crates/fuel-core/src/service/adapters/consensus_module.rs b/crates/fuel-core/src/service/adapters/consensus_module.rs index b4e86c0a58..3a6d7fc147 100644 --- a/crates/fuel-core/src/service/adapters/consensus_module.rs +++ b/crates/fuel-core/src/service/adapters/consensus_module.rs @@ -65,7 +65,7 @@ impl RelayerPort for MaybeRelayerAdapter { #[cfg(feature = "relayer")] { if let Some(sync) = self.relayer_synced.as_ref() { - let current_height = sync.get_finalized_da_height()?; + let current_height = sync.get_finalized_da_height(); anyhow::ensure!( da_height.saturating_sub(*current_height) <= **_max_da_lag, "Relayer is too far out of sync" diff --git a/crates/fuel-core/src/service/adapters/producer.rs b/crates/fuel-core/src/service/adapters/producer.rs index cdccedd254..6cb543169c 100644 --- a/crates/fuel-core/src/service/adapters/producer.rs +++ b/crates/fuel-core/src/service/adapters/producer.rs @@ -136,7 +136,7 @@ impl fuel_core_producer::ports::Relayer for MaybeRelayerAdapter { { if let Some(sync) = self.relayer_synced.as_ref() { sync.await_at_least_synced(height).await?; - let highest = sync.get_finalized_da_height()?; + let highest = sync.get_finalized_da_height(); Ok(highest) } else { Ok(*height) diff --git a/crates/fuel-core/src/service/adapters/relayer.rs b/crates/fuel-core/src/service/adapters/relayer.rs index 83623436d5..c776146ec0 100644 --- a/crates/fuel-core/src/service/adapters/relayer.rs +++ b/crates/fuel-core/src/service/adapters/relayer.rs @@ -4,9 +4,11 @@ use crate::database::{ }; use fuel_core_relayer::ports::Transactional; use fuel_core_storage::transactional::{ + AtomicView, IntoTransaction, StorageTransaction, }; +use fuel_core_types::blockchain::primitives::DaBlockHeight; impl Transactional for Database { type Transaction<'a> = StorageTransaction<&'a mut Self> where Self: 'a; @@ -14,4 +16,8 @@ impl Transactional for Database { fn transaction(&mut self) -> Self::Transaction<'_> { self.into_transaction() } + + fn latest_da_height(&self) -> Option { + AtomicView::latest_height(self) + } } diff --git a/crates/fuel-core/src/service/sub_services.rs b/crates/fuel-core/src/service/sub_services.rs index 23698c249b..0cba647ccf 100644 --- a/crates/fuel-core/src/service/sub_services.rs +++ b/crates/fuel-core/src/service/sub_services.rs @@ -11,6 +11,7 @@ use crate::{ schema::build_schema, service::{ adapters::{ + consensus_parameters_provider, BlockImporterAdapter, BlockProducerAdapter, ConsensusParametersProvider, @@ -32,7 +33,6 @@ use tokio::sync::Mutex; #[cfg(feature = "relayer")] use crate::relayer::Config as RelayerConfig; -use crate::service::adapters::consensus_parameters_provider; #[cfg(feature = "relayer")] use fuel_core_types::blockchain::primitives::DaBlockHeight; diff --git a/crates/services/relayer/src/mock_db.rs b/crates/services/relayer/src/mock_db.rs index 1c8c512c4d..c9905b8ba7 100644 --- a/crates/services/relayer/src/mock_db.rs +++ b/crates/services/relayer/src/mock_db.rs @@ -1,10 +1,7 @@ #![allow(missing_docs)] use crate::ports::RelayerDb; -use fuel_core_storage::{ - not_found, - Result as StorageResult, -}; +use fuel_core_storage::Result as StorageResult; use fuel_core_types::{ blockchain::primitives::DaBlockHeight, entities::{ @@ -92,6 +89,17 @@ impl MockDb { .map(|map| map.iter().map(|(_, tx)| tx).cloned().collect()) .unwrap_or_default() } + + #[cfg(any(test, feature = "test-helpers"))] + pub fn set_finalized_da_height_to_at_least( + &mut self, + height: &DaBlockHeight, + ) -> StorageResult<()> { + let mut lock = self.data.lock().unwrap(); + let max = lock.finalized_da_height.get_or_insert(0u64.into()); + *max = (*max).max(*height); + Ok(()) + } } impl RelayerDb for MockDb { @@ -122,21 +130,7 @@ impl RelayerDb for MockDb { Ok(()) } - fn set_finalized_da_height_to_at_least( - &mut self, - height: &DaBlockHeight, - ) -> StorageResult<()> { - let mut lock = self.data.lock().unwrap(); - let max = lock.finalized_da_height.get_or_insert(0u64.into()); - *max = (*max).max(*height); - Ok(()) - } - - fn get_finalized_da_height(&self) -> StorageResult { - self.data - .lock() - .unwrap() - .finalized_da_height - .ok_or(not_found!("FinalizedDaHeight for test")) + fn get_finalized_da_height(&self) -> Option { + self.data.lock().unwrap().finalized_da_height } } diff --git a/crates/services/relayer/src/ports.rs b/crates/services/relayer/src/ports.rs index 77447023b3..1d84a875bd 100644 --- a/crates/services/relayer/src/ports.rs +++ b/crates/services/relayer/src/ports.rs @@ -21,16 +21,9 @@ pub trait RelayerDb: Send + Sync { events: &[Event], ) -> StorageResult<()>; - /// Set finalized da height that represent last block from da layer that got finalized. - /// This will only set the value if it is greater than the current. - fn set_finalized_da_height_to_at_least( - &mut self, - block: &DaBlockHeight, - ) -> StorageResult<()>; - /// Get finalized da height that represent last block from da layer that got finalized. /// Panics if height is not set as of initialization of database. - fn get_finalized_da_height(&self) -> StorageResult; + fn get_finalized_da_height(&self) -> Option; } /// The trait that should be implemented by the database transaction returned by the database. @@ -49,4 +42,7 @@ pub trait Transactional { /// Returns the storage transaction. fn transaction(&mut self) -> Self::Transaction<'_>; + + /// Returns the latest da block height. + fn latest_da_height(&self) -> Option; } diff --git a/crates/services/relayer/src/ports/tests.rs b/crates/services/relayer/src/ports/tests.rs index e0cba3bb02..6257e39774 100644 --- a/crates/services/relayer/src/ports/tests.rs +++ b/crates/services/relayer/src/ports/tests.rs @@ -5,24 +5,21 @@ use crate::{ RelayerDb, Transactional, }, - storage::{ - DaHeightTable, - EventsHistory, - }, + storage::EventsHistory, + Config, }; use fuel_core_storage::test_helpers::{ MockBasic, MockStorage, }; use fuel_core_types::{ + blockchain::primitives::DaBlockHeight, entities::{ Message, RelayedTransaction, }, services::relayer::Event, }; -use std::borrow::Cow; -use test_case::test_case; type DBTx = MockStorage; type ReturnDB = Box DBTx + Send + Sync>; @@ -41,27 +38,23 @@ impl Transactional for MockDatabase { fn transaction(&mut self) -> Self::Transaction<'_> { (self.data)() } + + fn latest_da_height(&self) -> Option { + Some(Config::DEFAULT_DA_DEPLOY_HEIGHT.into()) + } } #[test] fn test_insert_events() { - let same_height = 12; + // Given + let same_height = 12u64; let return_db_tx = move || { let mut db = DBTx::default(); db.storage .expect_insert::() .times(1) .returning(|_, _| Ok(None)); - db.storage - .expect_insert::() - .times(1) - .withf(move |_, v| **v == same_height) - .returning(|_, _| Ok(None)); db.data.expect_commit().returning(|| Ok(())); - db.storage - .expect_get::() - .once() - .returning(|_| Ok(Some(std::borrow::Cow::Owned(9u64.into())))); db }; @@ -73,12 +66,18 @@ fn test_insert_events() { let mut m = Message::default(); m.set_amount(10); m.set_da_height(same_height.into()); + let mut m2 = m.clone(); m2.set_nonce(1.into()); assert_ne!(m.id(), m2.id()); + let messages = [m.into(), m2.into()]; - db.insert_events(&same_height.into(), &messages[..]) - .unwrap(); + + // When + let result = db.insert_events(&same_height.into(), &messages[..]); + + // Then + assert!(result.is_ok()); } #[test] @@ -100,24 +99,16 @@ fn insert_always_raises_da_height_monotonically() { db.storage .expect_insert::() .returning(|_, _| Ok(None)); - db.storage - .expect_insert::() - .once() - .withf(move |_, v| *v == same_height) - .returning(|_, _| Ok(None)); db.data.expect_commit().returning(|| Ok(())); - db.storage - .expect_get::() - .once() - .returning(|_| Ok(None)); db }; - // When let mut db = MockDatabase { data: Box::new(return_db_tx), storage: Default::default(), }; + + // When let result = db.insert_events(&same_height, &events); // Then @@ -175,6 +166,7 @@ fn insert_fails_for_events_same_height_but_on_different_height() { data: Box::new(DBTx::default), storage: Default::default(), }; + let next_height = last_height + 1; let result = db.insert_events(&next_height.into(), &events); @@ -207,43 +199,3 @@ fn insert_fails_for_events_same_height_but_on_different_height() { last_height, ); } - -#[test_case(None, 0, 0; "can set DA height to 0 when there is none available")] -#[test_case(None, 10, 10; "can set DA height to 10 when there is none available")] -#[test_case(0, 10, 10; "can set DA height to 10 when it is 0")] -#[test_case(0, None, 0; "inserts are bypassed when height goes from 0 to 0")] -#[test_case(10, 11, 11; "can set DA height to 11 when it is 10")] -#[test_case(11, None, 11; "inserts are bypassed when height goes from 11 to 11")] -#[test_case(11, None, 10; "inserts are bypassed when height reverted from 11 to 10")] -fn set_raises_da_height_monotonically( - get: impl Into>, - inserts: impl Into>, - new_height: u64, -) { - let inserts = inserts.into(); - let get = get.into(); - let return_db_tx = move || { - let mut db = DBTx::default(); - if let Some(h) = inserts { - db.storage - .expect_insert::() - .once() - .withf(move |_, v| **v == h) - .returning(|_, _| Ok(None)); - } - let get = get.map(|g| Cow::Owned(g.into())); - db.storage - .expect_get::() - .once() - .returning(move |_| Ok(get.clone())); - db.data.expect_commit().returning(|| Ok(())); - db - }; - - let mut db = MockDatabase { - data: Box::new(return_db_tx), - storage: Default::default(), - }; - db.set_finalized_da_height_to_at_least(&new_height.into()) - .unwrap(); -} diff --git a/crates/services/relayer/src/service.rs b/crates/services/relayer/src/service.rs index b4b750bdf8..bd45595a5c 100644 --- a/crates/services/relayer/src/service.rs +++ b/crates/services/relayer/src/service.rs @@ -63,6 +63,7 @@ type CustomizableService = ServiceRunner>; pub struct SharedState { /// Receives signals when the relayer reaches consistency with the DA layer. synced: Synced, + start_da_block_height: DaBlockHeight, database: D, } @@ -111,17 +112,6 @@ impl NotInitializedTask { } } -impl Task -where - D: RelayerDb + 'static, -{ - fn set_deploy_height(&mut self) { - self.database - .set_finalized_da_height_to_at_least(&self.config.da_deploy_height) - .expect("Should be able to set the finalized da height"); - } -} - #[async_trait] impl RelayerData for Task where @@ -189,6 +179,7 @@ where SharedState { synced, + start_da_block_height: self.config.da_deploy_height, database: self.database.clone(), } } @@ -206,7 +197,7 @@ where config, retry_on_error, } = self; - let mut task = Task { + let task = Task { synced, eth_node, database, @@ -214,7 +205,6 @@ where shutdown, retry_on_error, }; - task.set_deploy_height(); Ok(task) } @@ -298,11 +288,13 @@ impl SharedState { /// Get finalized da height that represents last block from da layer that got finalized. /// Panics if height is not set as of initialization of the relayer. - pub fn get_finalized_da_height(&self) -> anyhow::Result + pub fn get_finalized_da_height(&self) -> DaBlockHeight where D: RelayerDb + 'static, { - self.database.get_finalized_da_height().map_err(Into::into) + self.database + .get_finalized_da_height() + .unwrap_or(self.start_da_block_height) } /// Getter for database field @@ -342,7 +334,12 @@ where D: RelayerDb + 'static, { fn observed(&self) -> Option { - self.database.get_finalized_da_height().map(|h| *h).ok() + Some( + self.database + .get_finalized_da_height() + .map(|h| h.into()) + .unwrap_or(self.config.da_deploy_height.0), + ) } } diff --git a/crates/services/relayer/src/service/get_logs/test.rs b/crates/services/relayer/src/service/get_logs/test.rs index c22cc771e8..938780670d 100644 --- a/crates/services/relayer/src/service/get_logs/test.rs +++ b/crates/services/relayer/src/service/get_logs/test.rs @@ -172,9 +172,6 @@ async fn test_da_height_updates( stream: Vec), ProviderError>>, ) -> u64 { let mut mock_db = crate::mock_db::MockDb::default(); - mock_db - .set_finalized_da_height_to_at_least(&0u64.into()) - .unwrap(); let logs = futures::stream::iter(stream); diff --git a/crates/services/relayer/src/service/test.rs b/crates/services/relayer/src/service/test.rs index eedd6eaa7f..03870f01d1 100644 --- a/crates/services/relayer/src/service/test.rs +++ b/crates/services/relayer/src/service/test.rs @@ -1,5 +1,6 @@ #![allow(non_snake_case)] use crate::test_helpers::middleware::MockMiddleware; + use futures::TryStreamExt; use test_case::test_case; @@ -61,23 +62,6 @@ async fn deploy_height_does_not_override() { assert_eq!(*mock_db.get_finalized_da_height().unwrap(), 50); } -#[tokio::test] -async fn deploy_height_does_override() { - let mut mock_db = crate::mock_db::MockDb::default(); - mock_db - .set_finalized_da_height_to_at_least(&50u64.into()) - .unwrap(); - let config = Config { - da_deploy_height: 52u64.into(), - ..Default::default() - }; - let eth_node = MockMiddleware::default(); - let relayer = NotInitializedTask::new(eth_node, mock_db.clone(), config, false); - let _ = relayer.into_task(&Default::default(), ()).await; - - assert_eq!(*mock_db.get_finalized_da_height().unwrap(), 52); -} - #[test_case(6, Some(6), Some(6); "if local is up to date with remote, then update sync")] #[test_case(6, Some(100), Some(100); "if local is somehow ahead of remote, then update sync")] #[test_case(6, Some(5), None; "if local is behind remote, then nothing sent to sync")] diff --git a/crates/services/relayer/src/storage.rs b/crates/services/relayer/src/storage.rs index 7b02cfd874..1d8ddc0b4a 100644 --- a/crates/services/relayer/src/storage.rs +++ b/crates/services/relayer/src/storage.rs @@ -21,7 +21,6 @@ use fuel_core_storage::{ Mappable, Result as StorageResult, StorageAsMut, - StorageInspect, StorageMutate, }; use fuel_core_types::{ @@ -47,8 +46,6 @@ pub enum Column { Metadata = 0, /// The column of the table that stores history of the relayer. History = 1, - /// The column that tracks the da height of the relayer. - RelayerHeight = 2, } impl Column { @@ -71,31 +68,6 @@ impl StorageColumn for Column { } } -/// Teh table to track the relayer's da height. -pub struct DaHeightTable; - -impl Mappable for DaHeightTable { - type Key = Self::OwnedKey; - type OwnedKey = (); - type Value = Self::OwnedValue; - type OwnedValue = DaBlockHeight; -} - -/// Key for da height. -/// If the relayer metadata ever contains more than one key, this should be -/// changed from a unit value. -const METADATA_KEY: () = (); - -// TODO: Remove `DaHeightTable` and logic associated with it, since the height tracking is controlled by the database -impl TableWithBlueprint for DaHeightTable { - type Blueprint = Plain>; - type Column = Column; - - fn column() -> Column { - Column::RelayerHeight - } -} - /// The table contains history of events on the DA. pub struct EventsHistory; @@ -121,9 +93,7 @@ impl RelayerDb for T where T: Send + Sync, T: Transactional, - T: StorageInspect, - for<'a> T::Transaction<'a>: StorageMutate - + StorageMutate, + for<'a> T::Transaction<'a>: StorageMutate, { fn insert_events( &mut self, @@ -134,6 +104,10 @@ where // set atomically with the insertion based on the current // height. Also so that the messages are inserted atomically // with the height. + + // Get the current DA block height from the database. + let before = self.latest_da_height().unwrap_or_default(); + let mut db_tx = self.transaction(); for event in events { @@ -143,9 +117,21 @@ where } db_tx.storage::().insert(da_height, events)?; - - grow_monotonically(&mut db_tx, da_height)?; db_tx.commit()?; + + // Compare the new DA block height with previous the block height. Block + // height must always be monotonically increasing. If the new block + // height is less than the previous block height, the service is in + // an error state and must be shut down. + let after = self + .latest_da_height() + .expect("DA height must be set at this point"); + if after < before { + StorageResult::Err( + anyhow::anyhow!("Block height must be monotonically increasing").into(), + )? + } + // TODO: Think later about how to clean up the history of the relayer. // Since we don't have too much information on the relayer and it can be useful // at any time, maybe we want to consider keeping it all the time instead of creating snapshots. @@ -153,25 +139,8 @@ where Ok(()) } - fn set_finalized_da_height_to_at_least( - &mut self, - height: &DaBlockHeight, - ) -> StorageResult<()> { - // A transaction is required to ensure that the height is - // set atomically with the insertion based on the current - // height. - let mut db_tx = self.transaction(); - grow_monotonically(&mut db_tx, height)?; - db_tx.commit()?; - Ok(()) - } - - fn get_finalized_da_height(&self) -> StorageResult { - use fuel_core_storage::StorageAsRef; - Ok(*self - .storage::() - .get(&METADATA_KEY)? - .unwrap_or_default()) + fn get_finalized_da_height(&self) -> Option { + self.latest_da_height() } } @@ -185,40 +154,10 @@ where } } -fn grow_monotonically( - s: &mut Storage, - height: &DaBlockHeight, -) -> StorageResult<()> -where - Storage: StorageMutate, -{ - let current = (&s) - .storage::() - .get(&METADATA_KEY)? - .map(|cow| cow.as_u64()); - match current { - Some(current) => { - if **height > current { - s.storage::().insert(&METADATA_KEY, height)?; - } - } - None => { - s.storage::().insert(&METADATA_KEY, height)?; - } - } - Ok(()) -} - #[cfg(test)] mod tests { use super::*; - fuel_core_storage::basic_storage_tests!( - DaHeightTable, - ::Key::default(), - ::Value::default() - ); - fuel_core_storage::basic_storage_tests!( EventsHistory, ::Key::default(), diff --git a/crates/services/relayer/tests/integration.rs b/crates/services/relayer/tests/integration.rs index 6fbab22b85..26ff2b07a3 100644 --- a/crates/services/relayer/tests/integration.rs +++ b/crates/services/relayer/tests/integration.rs @@ -14,10 +14,7 @@ use fuel_core_relayer::{ new_service_test, ports::RelayerDb, test_helpers::{ - middleware::{ - MockMiddleware, - TriggerType, - }, + middleware::MockMiddleware, EvtToLog, LogTestHelper, }, @@ -47,7 +44,10 @@ async fn stop_service_at_the_begin() { // The test verifies that if the service is stopped at the beginning, it will sync nothing. // It is possible to test because we simulate delay from the Ethereum node by the // `tokio::task::yield_now().await` in each method of the `MockMiddleware`. - let mock_db = MockDb::default(); + let mut mock_db = MockDb::default(); + mock_db + .set_finalized_da_height_to_at_least(&0u64.into()) + .unwrap(); let eth_node = MockMiddleware::default(); // Setup the eth node with a block high enough that there // will be some finalized blocks. @@ -66,7 +66,10 @@ async fn stop_service_at_the_middle() { // it will stop immediately. // It is possible to test because we simulate delay from the Ethereum node by the // `tokio::task::yield_now().await` in each method of the `MockMiddleware`. - let mock_db = MockDb::default(); + let mut mock_db = MockDb::default(); + mock_db + .set_finalized_da_height_to_at_least(&0u64.into()) + .unwrap(); let eth_node = MockMiddleware::default(); eth_node.update_data(|data| data.best_block.number = Some(100.into())); let config = Config { @@ -265,44 +268,6 @@ fn transaction(max_gas: u64, block_number: u64, block_index: u64) -> Log { log } -#[tokio::test(start_paused = true)] -async fn deploy_height_is_set() { - let mock_db = MockDb::default(); - let config = Config { - da_deploy_height: 50u64.into(), - ..Default::default() - }; - let eth_node = MockMiddleware::default(); - eth_node.update_data(|data| data.best_block.number = Some(54.into())); - let (tx, rx) = tokio::sync::oneshot::channel(); - let mut tx = Some(tx); - eth_node.set_before_event(move |_, evt| { - if let TriggerType::GetLogs(evt) = evt { - assert!( - matches!( - evt, - ethers_core::types::Filter { - block_option: ethers_core::types::FilterBlockOption::Range { - from_block: Some(ethers_core::types::BlockNumber::Number(f)), - to_block: Some(ethers_core::types::BlockNumber::Number(t)) - }, - .. - } if f.as_u64() == 51 && t.as_u64() == 54 - ), - "{evt:?}" - ); - if let Some(tx) = tx.take() { - tx.send(()).unwrap(); - } - } - }); - let relayer = new_service_test(eth_node, mock_db.clone(), config); - relayer.start_and_await().await.unwrap(); - relayer.shared.await_synced().await.unwrap(); - rx.await.unwrap(); - assert_eq!(*mock_db.get_finalized_da_height().unwrap(), 54); -} - struct TestContext { mock_db: MockDb, eth_node: MockMiddleware, diff --git a/tests/tests/relayer.rs b/tests/tests/relayer.rs index 8a5434dfc4..79d7c37fd4 100644 --- a/tests/tests/relayer.rs +++ b/tests/tests/relayer.rs @@ -294,6 +294,81 @@ async fn can_find_failed_relayed_tx() { assert_eq!(expected, actual); } +#[tokio::test(flavor = "multi_thread")] +async fn can_restart_node_with_relayer_data() { + let mut rng = StdRng::seed_from_u64(1234); + let mut config = Config::local_node(); + config.relayer = Some(relayer::Config::default()); + let relayer_config = config.relayer.as_mut().expect("Expected relayer config"); + let eth_node = MockMiddleware::default(); + let contract_address = relayer_config.eth_v2_listening_contracts[0]; + + // setup a real spendable message + let secret_key: SecretKey = SecretKey::random(&mut rng); + let pk = secret_key.public_key(); + let recipient = Input::owner(&pk); + let sender = Address::zeroed(); + let amount = 100; + let nonce = Nonce::from(2u64); + let logs = vec![make_message_event( + nonce, + 5, + contract_address, + Some(sender.into()), + Some(recipient.into()), + Some(amount), + None, + 0, + )]; + eth_node.update_data(|data| data.logs_batch = vec![logs.clone()]); + // Setup the eth node with a block high enough that there + // will be some finalized blocks. + eth_node.update_data(|data| data.best_block.number = Some(200.into())); + let eth_node = Arc::new(eth_node); + let eth_node_handle = spawn_eth_node(eth_node).await; + + relayer_config.relayer = Some( + format!("http://{}", eth_node_handle.address) + .as_str() + .try_into() + .unwrap(), + ); + + let capacity = 1024 * 1024; + let tmp_dir = tempfile::TempDir::new().unwrap(); + + { + // Given + let database = CombinedDatabase::open(tmp_dir.path(), capacity).unwrap(); + + let service = FuelService::from_combined_database(database, config.clone()) + .await + .unwrap(); + let client = FuelClient::from(service.bound_address); + client.health().await.unwrap(); + + for _ in 0..5 { + let tx = Transaction::default_test_tx(); + client.submit_and_await_commit(&tx).await.unwrap(); + } + + service.stop_and_await().await.unwrap(); + } + + { + // When + let database = CombinedDatabase::open(tmp_dir.path(), capacity).unwrap(); + let service = FuelService::from_combined_database(database, config) + .await + .unwrap(); + let client = FuelClient::from(service.bound_address); + + // Then + client.health().await.unwrap(); + service.stop_and_await().await.unwrap(); + } +} + #[allow(clippy::too_many_arguments)] fn make_message_event( nonce: Nonce,