From 6f8691de792146b113dfb7630431a36848660502 Mon Sep 17 00:00:00 2001 From: cryptoAtwill Date: Mon, 4 Dec 2023 23:47:05 +0800 Subject: [PATCH 1/6] fall back in proposal checking --- fendermint/vm/interpreter/src/chain.rs | 2 +- fendermint/vm/topdown/src/finality/fetch.rs | 50 ++++++++++++++++++--- fendermint/vm/topdown/src/finality/mod.rs | 30 ------------- fendermint/vm/topdown/src/finality/null.rs | 31 ++++++------- fendermint/vm/topdown/src/lib.rs | 3 +- fendermint/vm/topdown/src/toggle.rs | 10 ++++- 6 files changed, 70 insertions(+), 56 deletions(-) diff --git a/fendermint/vm/interpreter/src/chain.rs b/fendermint/vm/interpreter/src/chain.rs index 5d7dfd57..9fe48fcd 100644 --- a/fendermint/vm/interpreter/src/chain.rs +++ b/fendermint/vm/interpreter/src/chain.rs @@ -155,7 +155,7 @@ where height: height as u64, block_hash, }; - let is_final = atomically(|| finality_provider.check_proposal(&prop)).await; + let is_final = finality_provider.check_proposal(&prop).await?; if !is_final { return Ok(false); } diff --git a/fendermint/vm/topdown/src/finality/fetch.rs b/fendermint/vm/topdown/src/finality/fetch.rs index 714e7003..9009313c 100644 --- a/fendermint/vm/topdown/src/finality/fetch.rs +++ b/fendermint/vm/topdown/src/finality/fetch.rs @@ -5,10 +5,11 @@ use crate::finality::null::FinalityWithNull; use crate::finality::ParentViewPayload; use crate::proxy::ParentQueryProxy; use crate::{ - handle_null_round, BlockHash, BlockHeight, Config, Error, IPCParentFinality, - ParentFinalityProvider, ParentViewProvider, + handle_null_round, is_null_round_error, BlockHash, BlockHeight, Config, Error, + IPCParentFinality, ParentFinalityProvider, ParentViewProvider, }; -use async_stm::{Stm, StmResult}; +use async_stm::{atomically, Stm, StmResult}; +use async_trait::async_trait; use ipc_sdk::cross::CrossMsg; use ipc_sdk::staking::StakingChangeRequest; use std::sync::Arc; @@ -108,6 +109,7 @@ impl ParentViewProvider for CachedF } } +#[async_trait] impl ParentFinalityProvider for CachedFinalityProvider { @@ -115,8 +117,46 @@ impl ParentFinalityProvider self.inner.next_proposal() } - fn check_proposal(&self, proposal: &IPCParentFinality) -> Stm { - self.inner.check_proposal(proposal) + async fn check_proposal(&self, proposal: &IPCParentFinality) -> anyhow::Result { + if let Some(check) = atomically(|| { + if let Some(valid_height) = self.inner.check_height(proposal)? { + if !valid_height { + return Ok(Some(false)); + } + if let Some(valid_hash) = self.inner.check_block_hash(proposal)? { + return Ok(Some(valid_hash)); + } + + tracing::warn!("height {} found in cache but not hash", proposal.height); + } + Ok(None) + }).await { + return Ok(check); + } + + + match retry!( + self.config.exponential_back_off, + self.config.exponential_retry_limit, + self.parent_client.get_block_hash(proposal.height).await + ) { + Ok(r) => Ok(r.block_hash == *proposal.block_hash), + Err(e) => { + if is_null_round_error(&e) { + tracing::warn!( + proposal = proposal.to_string(), + "some validator proposed null block, reject" + ); + } else { + tracing::error!( + proposal = proposal.to_string(), + error = e.to_string(), + "encountered error checking proposal" + ) + } + Ok(false) + } + } } fn set_new_finality( diff --git a/fendermint/vm/topdown/src/finality/mod.rs b/fendermint/vm/topdown/src/finality/mod.rs index 2e8c40f9..8d58f0eb 100644 --- a/fendermint/vm/topdown/src/finality/mod.rs +++ b/fendermint/vm/topdown/src/finality/mod.rs @@ -145,34 +145,4 @@ mod tests { .await .unwrap(); } - - #[tokio::test] - async fn test_check_proposal_works() { - let provider = new_provider(); - - atomically_or_err(|| { - let target_block = 100; - - // inject data - provider.new_parent_view(target_block, Some((vec![1u8; 32], vec![], vec![])))?; - provider.set_new_finality( - IPCParentFinality { - height: target_block - 1, - block_hash: vec![1u8; 32], - }, - Some(genesis_finality()), - )?; - - let finality = IPCParentFinality { - height: target_block, - block_hash: vec![1u8; 32], - }; - - assert!(provider.check_proposal(&finality).is_ok()); - - Ok(()) - }) - .await - .unwrap(); - } } diff --git a/fendermint/vm/topdown/src/finality/null.rs b/fendermint/vm/topdown/src/finality/null.rs index 5540fc3a..9867a61a 100644 --- a/fendermint/vm/topdown/src/finality/null.rs +++ b/fendermint/vm/topdown/src/finality/null.rs @@ -93,13 +93,6 @@ impl FinalityWithNull { Ok(Some(proposal)) } - pub fn check_proposal(&self, proposal: &IPCParentFinality) -> Stm { - if !self.check_height(proposal)? { - return Ok(false); - } - self.check_block_hash(proposal) - } - pub fn set_new_finality( &self, finality: IPCParentFinality, @@ -305,13 +298,14 @@ impl FinalityWithNull { Ok(()) } - fn check_height(&self, proposal: &IPCParentFinality) -> Stm { + pub(crate) fn check_height(&self, proposal: &IPCParentFinality) -> Stm> { let binding = self.last_committed_finality.read()?; // last committed finality is not ready yet, we don't vote, just reject let last_committed_finality = if let Some(f) = binding.as_ref() { f } else { - return Ok(false); + tracing::debug!("cannot check height in cache as last committed finality not ready"); + return Ok(None); }; // the incoming proposal has height already committed, reject @@ -321,31 +315,34 @@ impl FinalityWithNull { proposed = proposal.height, "proposed height already committed", ); - return Ok(false); + return Ok(Some(false)); } if let Some(latest_height) = self.latest_height_in_cache()? { let r = latest_height >= proposal.height; tracing::debug!(is_true = r, "incoming proposal height seen?"); // requires the incoming height cannot be more advanced than our trusted parent node - Ok(r) + Ok(Some(r)) } else { // latest height is not found, meaning we dont have any prefetched cache, we just be // strict and vote no simply because we don't know. - tracing::debug!("reject proposal, no data in cache"); - Ok(false) + tracing::debug!("cannot check height, no data in cache"); + Ok(None) } } - fn check_block_hash(&self, proposal: &IPCParentFinality) -> Stm { + pub(crate) fn check_block_hash(&self, proposal: &IPCParentFinality) -> Stm> { Ok( if let Some(block_hash) = self.block_hash_at_height(proposal.height)? { let r = block_hash == proposal.block_hash; tracing::debug!(proposal = proposal.to_string(), is_same = r, "same hash?"); - r + Some(r) } else { - tracing::debug!(proposal = proposal.to_string(), "reject, hash not found"); - false + tracing::debug!( + proposal = proposal.to_string(), + "cannot check proposal, not found in cache" + ); + None }, ) } diff --git a/fendermint/vm/topdown/src/lib.rs b/fendermint/vm/topdown/src/lib.rs index 3edc0751..a366e1d2 100644 --- a/fendermint/vm/topdown/src/lib.rs +++ b/fendermint/vm/topdown/src/lib.rs @@ -146,11 +146,12 @@ pub trait ParentViewProvider { ) -> anyhow::Result>; } +#[async_trait] pub trait ParentFinalityProvider: ParentViewProvider { /// Latest proposal for parent finality fn next_proposal(&self) -> Stm>; /// Check if the target proposal is valid - fn check_proposal(&self, proposal: &IPCParentFinality) -> Stm; + async fn check_proposal(&self, proposal: &IPCParentFinality) -> anyhow::Result; /// Called when finality is committed fn set_new_finality( &self, diff --git a/fendermint/vm/topdown/src/toggle.rs b/fendermint/vm/topdown/src/toggle.rs index 68a70bbf..089809a5 100644 --- a/fendermint/vm/topdown/src/toggle.rs +++ b/fendermint/vm/topdown/src/toggle.rs @@ -8,6 +8,7 @@ use crate::{ }; use anyhow::anyhow; use async_stm::{Stm, StmResult}; +use async_trait::async_trait; use ipc_sdk::cross::CrossMsg; use ipc_sdk::staking::StakingChangeRequest; @@ -73,13 +74,18 @@ impl ParentViewProvider for Toggl } } +#[async_trait] impl ParentFinalityProvider for Toggle

{ fn next_proposal(&self) -> Stm> { self.perform_or_else(|p| p.next_proposal(), None) } - fn check_proposal(&self, proposal: &IPCParentFinality) -> Stm { - self.perform_or_else(|p| p.check_proposal(proposal), false) + async fn check_proposal(&self, proposal: &IPCParentFinality) -> anyhow::Result { + if let Some(p) = &self.inner { + p.check_proposal(proposal).await + } else { + Ok(false) + } } fn set_new_finality( From cce853b955d34283bdc1385f6acb6eb1c017e356 Mon Sep 17 00:00:00 2001 From: Akosh Farkash Date: Tue, 5 Dec 2023 11:27:47 +0000 Subject: [PATCH 2/6] FM-356: Snapshot config (#445) * FM-356: Rename TopDownConfig to TopDownSettings * FM-356: Snapshot settings * FM-356: Disable snapshots for now --- fendermint/app/config/default.toml | 20 ++++++++++++++ fendermint/app/settings/src/lib.rs | 36 ++++++++++++++++++++++--- fendermint/app/src/cmd/run.rs | 38 ++++++++++++++++++++++----- fendermint/vm/snapshot/src/manager.rs | 38 +++++++++++++-------------- 4 files changed, 103 insertions(+), 29 deletions(-) diff --git a/fendermint/app/config/default.toml b/fendermint/app/config/default.toml index 013411df..d49a077b 100644 --- a/fendermint/app/config/default.toml +++ b/fendermint/app/config/default.toml @@ -5,6 +5,8 @@ # Database files. data_dir = "data" +# State snapshots. +snapshots_dir = "snapshots" # Solidity contracts. contracts_dir = "contracts" # Builtin actor bundle CAR file. @@ -37,6 +39,24 @@ port = 26658 # Keep unlimited history by default. state_hist_size = 0 +[snapshots] +# Enable the export and import of snapshots. +enabled = false +# How often to attempt to export snapshots in terms of block height. +# With 1-2s block time, we can aim for 8h snapshots and cover the last 24h keeping the last 3. +# The Filecoin Calibration net does not allow longer lookups than 16h, so we should have a snapshot within that time. +block_interval = 30000 +# Number of snapshots to keep before purging old ones. +# Keep the last 2-3 snapshots around as recommended by CometBFT docs. +hist_size = 3 +# Target chunk size, in bytes. +# It has to be less than 16MB and the FVM has max 1MB blocks, so 10MB as recommended by CometBFT docs is a good start. +chunk_size_bytes = 10485760 +# How long to keep a snapshot from being purged after it has been requested by a peer. +last_access_hold = 300 +# Ask CometBFT every now and then whether it's syncing; snapshot production is skipped +sync_poll_interval = 60 + [broadcast] # Maximum number of times to retry broadcasting a transaction after failure. max_retries = 5 diff --git a/fendermint/app/settings/src/lib.rs b/fendermint/app/settings/src/lib.rs index c95d17e7..767b9354 100644 --- a/fendermint/app/settings/src/lib.rs +++ b/fendermint/app/settings/src/lib.rs @@ -106,7 +106,7 @@ pub struct BroadcastSettings { #[serde_as] #[derive(Debug, Deserialize, Clone)] -pub struct TopDownConfig { +pub struct TopDownSettings { /// The number of blocks to delay before reporting a height as final on the parent chain. /// To propose a certain number of epochs delayed from the latest height, we see to be /// conservative and avoid other from rejecting the proposal because they don't see the @@ -143,7 +143,7 @@ pub struct IpcSettings { pub subnet_id: SubnetID, /// The config for top down checkpoint. It's None if subnet id is root or not activating /// any top down checkpoint related operations - pub topdown: Option, + pub topdown: Option, } impl IpcSettings { @@ -151,23 +151,45 @@ impl IpcSettings { !self.subnet_id.is_root() && self.topdown.is_some() } - pub fn topdown_config(&self) -> anyhow::Result<&TopDownConfig> { + pub fn topdown_config(&self) -> anyhow::Result<&TopDownSettings> { self.topdown .as_ref() .ok_or_else(|| anyhow!("top down config missing")) } } +#[serde_as] +#[derive(Debug, Deserialize, Clone)] +pub struct SnapshotSettings { + /// Enable the export and import of snapshots. + pub enabled: bool, + /// How often to attempt to export snapshots in terms of block height. + pub block_interval: BlockHeight, + /// Number of snapshots to keep before purging old ones. + pub hist_size: usize, + /// Target chunk size, in bytes. + pub chunk_size_bytes: usize, + /// How long to keep a snapshot from being purged after it has been requested by a peer. + #[serde_as(as = "DurationSeconds")] + pub last_access_hold: Duration, + /// How often to poll CometBFT to see whether it has caught up with the chain. + #[serde_as(as = "DurationSeconds")] + pub sync_poll_interval: Duration, +} + #[derive(Debug, Deserialize, Clone)] pub struct Settings { /// Home directory configured on the CLI, to which all paths in settings can be set relative. home_dir: PathBuf, /// Database files. data_dir: PathBuf, + /// State snapshots. + snapshots_dir: PathBuf, /// Solidity contracts. contracts_dir: PathBuf, /// Builtin-actors CAR file. builtin_actors_bundle: PathBuf, + /// Where to reach CometBFT for queries or broadcasting transactions. tendermint_rpc_url: Url, @@ -176,6 +198,7 @@ pub struct Settings { pub abci: AbciSettings, pub db: DbSettings, + pub snapshots: SnapshotSettings, pub eth: EthSettings, pub fvm: FvmSettings, pub resolver: ResolverSettings, @@ -207,7 +230,12 @@ macro_rules! home_relative { } impl Settings { - home_relative!(data_dir, contracts_dir, builtin_actors_bundle); + home_relative!( + data_dir, + snapshots_dir, + contracts_dir, + builtin_actors_bundle + ); /// Load the default configuration from a directory, /// then potential overrides specific to the run mode, diff --git a/fendermint/app/src/cmd/run.rs b/fendermint/app/src/cmd/run.rs index 54574ee8..8638ce42 100644 --- a/fendermint/app/src/cmd/run.rs +++ b/fendermint/app/src/cmd/run.rs @@ -15,6 +15,7 @@ use fendermint_vm_interpreter::{ signed::SignedMessageInterpreter, }; use fendermint_vm_resolver::ipld::IpldResolver; +use fendermint_vm_snapshot::SnapshotManager; use fendermint_vm_topdown::proxy::IPCProviderProxy; use fendermint_vm_topdown::sync::launch_polling_syncer; use fendermint_vm_topdown::{CachedFinalityProvider, Toggle}; @@ -67,8 +68,9 @@ async fn run(settings: Settings) -> anyhow::Result<()> { let tendermint_rpc_url = settings.tendermint_rpc_url()?; tracing::info!("Connecting to Tendermint at {tendermint_rpc_url}"); - let client: tendermint_rpc::HttpClient = tendermint_rpc::HttpClient::new(tendermint_rpc_url) - .context("failed to create Tendermint client")?; + let tendermint_client: tendermint_rpc::HttpClient = + tendermint_rpc::HttpClient::new(tendermint_rpc_url) + .context("failed to create Tendermint client")?; let validator = match settings.validator_key { Some(ref key) => { @@ -92,7 +94,7 @@ async fn run(settings: Settings) -> anyhow::Result<()> { // For now we are using the validator key for submitting transactions. // This allows us to identify transactions coming from bonded validators, to give priority to protocol related transactions. let broadcaster = Broadcaster::new( - client.clone(), + tendermint_client.clone(), addr, sk.clone(), settings.fvm.gas_fee_cap.clone(), @@ -106,7 +108,7 @@ async fn run(settings: Settings) -> anyhow::Result<()> { }); let interpreter = FvmMessageInterpreter::::new( - client.clone(), + tendermint_client.clone(), validator_ctx, settings.contracts_dir(), settings.fvm.gas_overestimation_rate, @@ -127,6 +129,7 @@ async fn run(settings: Settings) -> anyhow::Result<()> { let resolve_pool = CheckpointPool::new(); + // If enabled, start a resolver that communicates with the application through the resolve pool. if settings.resolver.enabled() { let service = make_resolver_service(&settings, db.clone(), state_store.clone(), ns.bit_store)?; @@ -180,6 +183,29 @@ async fn run(settings: Settings) -> anyhow::Result<()> { (Arc::new(Toggle::disabled()), None) }; + // Start a snapshot manager in the background. + let snapshots = if settings.snapshots.enabled { + let (manager, client) = SnapshotManager::new( + state_store.clone(), + settings.snapshots_dir(), + settings.snapshots.block_interval, + settings.snapshots.chunk_size_bytes, + settings.snapshots.hist_size, + settings.snapshots.last_access_hold, + settings.snapshots.sync_poll_interval, + ) + .context("failed to create snapshot manager")?; + + tracing::info!("starting the SnapshotManager..."); + let tendermint_client = tendermint_client.clone(); + tokio::spawn(async move { manager.run(tendermint_client).await }); + + Some(client) + } else { + info!("snapshots disabled"); + None + }; + let app: App<_, _, AppStore, _> = App::new( AppConfig { app_namespace: ns.app, @@ -192,7 +218,7 @@ async fn run(settings: Settings) -> anyhow::Result<()> { interpreter, resolve_pool, parent_finality_provider.clone(), - None, + snapshots, )?; if let Some((agent_proxy, config)) = ipc_tuple { @@ -203,7 +229,7 @@ async fn run(settings: Settings) -> anyhow::Result<()> { config, parent_finality_provider, agent_proxy, - client, + tendermint_client, ) .await { diff --git a/fendermint/vm/snapshot/src/manager.rs b/fendermint/vm/snapshot/src/manager.rs index bb102922..3957c0e4 100644 --- a/fendermint/vm/snapshot/src/manager.rs +++ b/fendermint/vm/snapshot/src/manager.rs @@ -19,13 +19,13 @@ pub struct SnapshotManager { /// Blockstore store: BS, /// Location to store completed snapshots. - snapshot_dir: PathBuf, + snapshots_dir: PathBuf, /// Target size in bytes for snapshot chunks. chunk_size: usize, /// Number of snapshots to keep. /// /// 0 means unlimited. - history_size: usize, + hist_size: usize, /// Time to hold on from purging a snapshot after a remote client /// asked for a chunk from it. last_access_hold: Duration, @@ -45,22 +45,22 @@ where /// Create a new manager. pub fn new( store: BS, - snapshot_interval: BlockHeight, - snapshot_dir: PathBuf, + snapshots_dir: PathBuf, + block_interval: BlockHeight, chunk_size: usize, - history_size: usize, + hist_size: usize, last_access_hold: Duration, sync_poll_interval: Duration, ) -> anyhow::Result<(Self, SnapshotClient)> { - let snapshot_items = list_manifests(&snapshot_dir).context("failed to list manifests")?; + let snapshot_items = list_manifests(&snapshots_dir).context("failed to list manifests")?; let state = SnapshotState::new(snapshot_items); let manager = Self { store, - snapshot_dir, + snapshots_dir, chunk_size, - history_size, + hist_size, last_access_hold, sync_poll_interval, state: state.clone(), @@ -68,7 +68,7 @@ where is_syncing: TVar::new(true), }; - let client = SnapshotClient::new(snapshot_interval, state); + let client = SnapshotClient::new(block_interval, state); Ok((manager, client)) } @@ -145,14 +145,14 @@ where /// Remove snapshot directories if we have more than the desired history size. async fn prune_history(&self) { - if self.history_size == 0 { + if self.hist_size == 0 { return; } let removables = atomically(|| { self.state.snapshots.modify_mut(|snapshots| { let mut removables = Vec::new(); - while snapshots.len() > self.history_size { + while snapshots.len() > self.hist_size { // Stop at the first snapshot that was accessed recently. if let Some(last_access) = snapshots.head().and_then(|s| s.last_access.elapsed().ok()) @@ -174,7 +174,7 @@ where for r in removables { if let Err(e) = std::fs::remove_dir_all(&r.snapshot_dir) { - tracing::error!(error =? e, snapshot_dir = r.snapshot_dir.to_string_lossy().to_string(), "failed to remove snapsot"); + tracing::error!(error =? e, snapshots_dir = r.snapshot_dir.to_string_lossy().to_string(), "failed to remove snapsot"); } } } @@ -246,14 +246,14 @@ where let _ = write_manifest(temp_dir.path(), &manifest).context("failed to export manifest")?; // Move snapshot to final location - doing it in one step so there's less room for error. - let snapshot_dir = self.snapshot_dir.join(&snapshot_name); - std::fs::rename(temp_dir.path(), &snapshot_dir).context("failed to move snapshot")?; + let snapshots_dir = self.snapshots_dir.join(&snapshot_name); + std::fs::rename(temp_dir.path(), &snapshots_dir).context("failed to move snapshot")?; // Delete the big CAR file - keep the parts only. - std::fs::remove_file(snapshot_dir.join(SNAPSHOT_FILE_NAME)) + std::fs::remove_file(snapshots_dir.join(SNAPSHOT_FILE_NAME)) .context("failed to remove CAR file")?; - Ok(SnapshotItem::new(snapshot_dir, manifest)) + Ok(SnapshotItem::new(snapshots_dir, manifest)) } } @@ -307,7 +307,7 @@ mod tests { // Initialise genesis and export it directly to see if it works. #[tokio::test] - async fn create_snapshot_directly() { + async fn create_snapshots_directly() { let (state_params, store) = init_genesis().await; let snapshot = Snapshot::new(store, state_params, 0).expect("failed to create snapshot"); let tmp_path = tempfile::NamedTempFile::new().unwrap().into_temp_path(); @@ -332,8 +332,8 @@ mod tests { let (snapshot_manager, snapshot_client) = SnapshotManager::new( store.clone(), - 1, temp_dir.path().into(), + 1, 10000, 1, Duration::ZERO, @@ -397,8 +397,8 @@ mod tests { // Create a new manager instance let (_, new_client) = SnapshotManager::new( store, - 1, temp_dir.path().into(), + 1, 10000, 1, Duration::ZERO, From bc7de499e05f1d483890abdae21cb7a4e9873f02 Mon Sep 17 00:00:00 2001 From: adlrocha <6717133+adlrocha@users.noreply.github.com> Date: Tue, 5 Dec 2023 17:52:52 +0100 Subject: [PATCH 3/6] IPC-304: upgrade contracts (#454) * IPC-304: upgrade contract * upgrade contract dependencies --- Cargo.lock | 8 ++++---- fendermint/testing/contract-test/tests/staking/machine.rs | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 234d9589..3d017b41 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4679,7 +4679,7 @@ checksum = "59ce5ef949d49ee85593fc4d3f3f95ad61657076395cbbce23e2121fc5542074" [[package]] name = "ipc-identity" version = "0.1.0" -source = "git+https://github.com/consensus-shipyard/ipc.git?branch=dev#28840fb77c5dc7c9869470962ac1d677754d3fe8" +source = "git+https://github.com/consensus-shipyard/ipc.git?branch=dev#68d5c10e5f801dd3e89f96e39dff5144489861f0" dependencies = [ "ahash 0.8.6", "anyhow", @@ -4706,7 +4706,7 @@ dependencies = [ [[package]] name = "ipc-provider" version = "0.1.0" -source = "git+https://github.com/consensus-shipyard/ipc.git?branch=dev#28840fb77c5dc7c9869470962ac1d677754d3fe8" +source = "git+https://github.com/consensus-shipyard/ipc.git?branch=dev#68d5c10e5f801dd3e89f96e39dff5144489861f0" dependencies = [ "anyhow", "async-channel", @@ -4746,7 +4746,7 @@ dependencies = [ [[package]] name = "ipc-sdk" version = "0.1.0" -source = "git+https://github.com/consensus-shipyard/ipc.git?branch=dev#28840fb77c5dc7c9869470962ac1d677754d3fe8" +source = "git+https://github.com/consensus-shipyard/ipc.git?branch=dev#68d5c10e5f801dd3e89f96e39dff5144489861f0" dependencies = [ "anyhow", "cid", @@ -4770,7 +4770,7 @@ dependencies = [ [[package]] name = "ipc_actors_abis" version = "0.1.0" -source = "git+https://github.com/consensus-shipyard/ipc-solidity-actors.git?branch=dev#6f812b55a717a8b148c1b1b2b7db12aa330d5d84" +source = "git+https://github.com/consensus-shipyard/ipc-solidity-actors.git?branch=dev#ae9edfbf9ca41fbf31f28b8428229f091ada0917" dependencies = [ "anyhow", "ethers", diff --git a/fendermint/testing/contract-test/tests/staking/machine.rs b/fendermint/testing/contract-test/tests/staking/machine.rs index 9bc6d63c..554782dc 100644 --- a/fendermint/testing/contract-test/tests/staking/machine.rs +++ b/fendermint/testing/contract-test/tests/staking/machine.rs @@ -116,6 +116,7 @@ impl StateMachine for StakingMachine { .unwrap(), min_validators: 1, min_cross_msg_fee: et::U256::zero(), + permissioned: false, }; eprintln!("\n> PARENT IPC: {parent_ipc:?}"); From 101e8722dec444476b47ea35d921bdcdbb738c5c Mon Sep 17 00:00:00 2001 From: Akosh Farkash Date: Wed, 6 Dec 2023 10:05:33 +0000 Subject: [PATCH 4/6] FM-363: Break up smoke-test Makefile.toml into reusable parts (#453) * FM-363: Break up smoke-test Makefile.toml into reusable parts * FM-363: Reuse the infra scripts for testing * FM-363: Move cometbft related command to cometbft.toml --- fendermint/testing/Makefile/ci.env | 3 + fendermint/testing/Makefile/cometbft.toml | 3 + fendermint/testing/Makefile/common.env | 22 ++ fendermint/testing/Makefile/common.toml | 82 +++++++ fendermint/testing/Makefile/docker.toml | 3 + fendermint/testing/Makefile/ethapi.toml | 3 + fendermint/testing/Makefile/fendermint.toml | 7 + fendermint/testing/smoke-test/Makefile.toml | 230 +----------------- fendermint/testing/smoke-test/scripts/init.sh | 8 +- fendermint/testing/smoke-test/smoke.env | 2 + infra/Makefile.toml | 66 +++-- infra/scripts/cometbft.toml | 6 + infra/scripts/docker.toml | 5 - infra/scripts/ethapi.toml | 4 +- infra/scripts/fendermint.toml | 9 +- 15 files changed, 191 insertions(+), 262 deletions(-) create mode 100644 fendermint/testing/Makefile/ci.env create mode 100644 fendermint/testing/Makefile/cometbft.toml create mode 100644 fendermint/testing/Makefile/common.env create mode 100644 fendermint/testing/Makefile/common.toml create mode 100644 fendermint/testing/Makefile/docker.toml create mode 100644 fendermint/testing/Makefile/ethapi.toml create mode 100644 fendermint/testing/Makefile/fendermint.toml create mode 100644 fendermint/testing/smoke-test/smoke.env diff --git a/fendermint/testing/Makefile/ci.env b/fendermint/testing/Makefile/ci.env new file mode 100644 index 00000000..8288bb87 --- /dev/null +++ b/fendermint/testing/Makefile/ci.env @@ -0,0 +1,3 @@ +CMT_WAIT_MILLIS = 20000 +# Help debug any issues with simplecoin by logging requests and responses. +VERBOSITY = "--verbose" diff --git a/fendermint/testing/Makefile/cometbft.toml b/fendermint/testing/Makefile/cometbft.toml new file mode 100644 index 00000000..ebd2d7a6 --- /dev/null +++ b/fendermint/testing/Makefile/cometbft.toml @@ -0,0 +1,3 @@ +extend = [ + { path = "../../../infra/scripts/cometbft.toml" }, +] diff --git a/fendermint/testing/Makefile/common.env b/fendermint/testing/Makefile/common.env new file mode 100644 index 00000000..dc16a946 --- /dev/null +++ b/fendermint/testing/Makefile/common.env @@ -0,0 +1,22 @@ +CMT_DOCKER_IMAGE="cometbft/cometbft:v0.37.x" +FM_DOCKER_IMAGE="fendermint:latest" +CMT_CONTAINER_NAME="${NETWORK_NAME}-cometbft" +FM_CONTAINER_NAME="${NETWORK_NAME}-fendermint" +ETHAPI_CONTAINER_NAME="${NETWORK_NAME}-ethapi" +CMT_P2P_HOST_PORT=26656 +CMT_RPC_HOST_PORT=26657 +ETHAPI_HOST_PORT=8545 +FM_NETWORK=main +FM_LOG_LEVEL=info +ETHAPI_LOG_LEVEL=debug +# If this wasn't present, any wait task is skipped. +CARGO_MAKE_WAIT_MILLISECONDS=5000 +# This wait time seems to work locally. +CMT_WAIT_MILLIS=10000 +# Keep example logs to a minimum. +VERBOSITY="" +TEST_SCRIPTS_DIR="${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/fendermint/testing/${TEST_DIR}/scripts" +TEST_DATA_DIR="${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/fendermint/testing/${TEST_DIR}/test-data" +NODE_NAME=node-0 +CMT_DIR=${TEST_DATA_DIR}/${NODE_NAME}/cometbft +BASE_DIR=${TEST_DATA_DIR} diff --git a/fendermint/testing/Makefile/common.toml b/fendermint/testing/Makefile/common.toml new file mode 100644 index 00000000..4f0f3d21 --- /dev/null +++ b/fendermint/testing/Makefile/common.toml @@ -0,0 +1,82 @@ +# smoke-test infrastructure: +# cargo install cargo-make +# +# cd fendermint/testing/smoke-test +# - then - +# cargo make --profile ci +# - or - +# cargo make setup +# cargo make test +# docker logs smoke-ethapi +# cargo make teardown + +extend = [ + { path = "./docker.toml" }, + { path = "./cometbft.toml" }, + { path = "./fendermint.toml" }, + { path = "./ethapi.toml" }, +] + + +# Define the following in test specific `Makefile.toml` files, +# where `env.env` defines `NETWORK_NAME` and `TEST_DIR`, expected by `common.env`: +# env_files = [ +# { path = "./env.env" }, +# { path = "../Makefile/common.env" }, +# { path = "../Makefile/ci.env", profile = "ci" }, +# ] + + +[tasks.default] +clear = true +run_task = { name = [ + "setup", + "test", +], fork = true, cleanup_task = "teardown" } + +# Waiting before starting the Eth API for the CometBFT websocket to start listening. +[tasks.setup] +dependencies = [ + "test-data-dir", + "docker-network-create", + "cometbft-init", + "fendermint-init", + "fendermint-start", + "cometbft-start", + "ethapi-start", + "cometbft-wait", + "fendermint-logs", + "cometbft-logs", + "ethapi-logs", +] + +[tasks.test] +clear = true +dependencies = [] + +[tasks.teardown] +# `dependencies` doesn't seem to work with `cleanup_task`. +run_task = { name = [ + "cometbft-stop", + "cometbft-rm", + "fendermint-stop", + "fendermint-rm", + "ethapi-stop", + "ethapi-rm", + "docker-network-rm", + "test-data-dir-rm", +] } + + +[tasks.test-data-dir] +script = """ +mkdir -p ${TEST_DATA_DIR}/keys; +mkdir -p ${TEST_DATA_DIR}/${NODE_NAME}/fendermint/data/logs; +mkdir -p ${TEST_DATA_DIR}/${NODE_NAME}/cometbft; +cp -r ${TEST_SCRIPTS_DIR} ${TEST_DATA_DIR}/scripts +""" + +[tasks.test-data-dir-rm] +script = """ +rm -rf ${TEST_DATA_DIR} +""" diff --git a/fendermint/testing/Makefile/docker.toml b/fendermint/testing/Makefile/docker.toml new file mode 100644 index 00000000..e739e7f3 --- /dev/null +++ b/fendermint/testing/Makefile/docker.toml @@ -0,0 +1,3 @@ +extend = [ + { path = "../../../infra/scripts/docker.toml" }, +] diff --git a/fendermint/testing/Makefile/ethapi.toml b/fendermint/testing/Makefile/ethapi.toml new file mode 100644 index 00000000..0d61c9d8 --- /dev/null +++ b/fendermint/testing/Makefile/ethapi.toml @@ -0,0 +1,3 @@ +extend = [ + { path = "../../../infra/scripts/ethapi.toml" }, +] diff --git a/fendermint/testing/Makefile/fendermint.toml b/fendermint/testing/Makefile/fendermint.toml new file mode 100644 index 00000000..7e56c1ce --- /dev/null +++ b/fendermint/testing/Makefile/fendermint.toml @@ -0,0 +1,7 @@ +extend = [ + { path = "../../../infra/scripts/fendermint.toml" }, +] + +[tasks.fendermint-init] +extend = "fendermint-run" +env = { "ENTRY" = "/data/scripts/init.sh", "FLAGS" = "-a STDOUT -a STDERR --rm" } diff --git a/fendermint/testing/smoke-test/Makefile.toml b/fendermint/testing/smoke-test/Makefile.toml index cd78d471..dcb348c7 100644 --- a/fendermint/testing/smoke-test/Makefile.toml +++ b/fendermint/testing/smoke-test/Makefile.toml @@ -1,27 +1,3 @@ -[env] -NETWORK_NAME = "smoke" -CMT_CONTAINER_NAME = "smoke-cometbft" -FM_CONTAINER_NAME = "smoke-fendermint" -ETHAPI_CONTAINER_NAME = "smoke-ethapi" -CMT_DOCKER_IMAGE = "cometbft/cometbft:v0.37.x" -FM_DOCKER_IMAGE = "fendermint:latest" -TEST_DATA_DIR = "${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/fendermint/testing/smoke-test/test-data" -TEST_SCRIPTS_DIR = "${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/fendermint/testing/smoke-test/scripts" -ACTORS_BUNDLE = "${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/../builtin-actors/output/bundle.car" -CMT_HOST_PORT = 26657 -ETHAPI_HOST_PORT = 8545 -# If this wasn't present, any wait task is skipped. -CARGO_MAKE_WAIT_MILLISECONDS = 5000 -# This wait time seems to work locally. -CMT_WAIT_MILLIS = 10000 -# Keep example logs to a minimum. -VERBOSITY = "" - -[env.ci] -CMT_WAIT_MILLIS = 20000 -# Help debug any issues with simplecoin by logging requests and responses. -VERBOSITY = "--verbose" - # smoke-test infrastructure: # cargo install cargo-make # @@ -34,53 +10,27 @@ VERBOSITY = "--verbose" # docker logs smoke-ethapi # cargo make teardown -[tasks.default] -clear = true -run_task = { name = [ - "setup", - "test", -], fork = true, cleanup_task = "teardown" } +extend = [ + { path = "../Makefile/common.toml" }, +] -# Waiting before starting the Eth API for the CometBFT websocket to start listening. -[tasks.setup] -dependencies = [ - "test-data-dir", - "network-create", - "cometbft-init", - "fendermint-init", - "fendermint-start", - "cometbft-start", - "ethapi-start", - "cometbft-wait", - "fendermint-logs", - "cometbft-logs", - "ethapi-logs", +env_files = [ + { path = "./smoke.env" }, + { path = "../Makefile/common.env" }, + { path = "../Makefile/ci.env", profile = "ci" }, ] [tasks.test] clear = true dependencies = ["simplecoin-example", "ethapi-example"] -[tasks.teardown] -# `dependencies` doesn't seem to work with `cleanup_task`. -run_task = { name = [ - "cometbft-stop", - "cometbft-rm", - "fendermint-stop", - "fendermint-rm", - "ethapi-stop", - "ethapi-rm", - "network-rm", - "test-data-dir-rm", -] } - [tasks.simplecoin-example] # Using --release in the hope that it can reuse artifacts compiled earlier for the docker build. script = """ cd ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY} cargo run -p fendermint_rpc --release --example simplecoin -- \ - --secret-key fendermint/testing/smoke-test/test-data/fendermint/keys/alice.sk \ + --secret-key fendermint/testing/smoke-test/test-data/keys/alice.sk \ ${VERBOSITY} """ @@ -89,166 +39,6 @@ cargo run -p fendermint_rpc --release --example simplecoin -- \ script = """ cd ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY} cargo run -p fendermint_eth_api --release --example ethers -- \ - --secret-key-from fendermint/testing/smoke-test/test-data/fendermint/keys/emily.sk \ - --secret-key-to fendermint/testing/smoke-test/test-data/fendermint/keys/eric.sk -""" - - -[tasks.test-data-dir] -script = """ -mkdir -p ${TEST_DATA_DIR}/fendermint; -mkdir -p ${TEST_DATA_DIR}/cometbft; -""" - -[tasks.test-data-dir-rm] -script = """ -rm -rf ${TEST_DATA_DIR} + --secret-key-from fendermint/testing/smoke-test/test-data/keys/emily.sk \ + --secret-key-to fendermint/testing/smoke-test/test-data/keys/eric.sk """ - - -[tasks.cometbft-pull] -command = "docker" -args = ["pull", "${CMT_DOCKER_IMAGE}"] - -[tasks.cometbft-init] -extend = "cometbft-run" -env = { "CMD" = "init", "FLAGS" = "-a STDOUT -a STDERR --rm" } - -[tasks.cometbft-start] -extend = "cometbft-run" -env = { "CMD" = "start", "FLAGS" = "-d" } - -[tasks.cometbft-wait] -extend = "wait" -env = { "CARGO_MAKE_WAIT_MILLISECONDS" = "${CMT_WAIT_MILLIS}" } - -[tasks.cometbft-run] -script = """ -docker run \ - ${FLAGS} \ - --name ${CMT_CONTAINER_NAME} \ - --user $(id -u) \ - --network ${NETWORK_NAME} \ - --publish ${CMT_HOST_PORT}:26657 \ - --volume ${TEST_DATA_DIR}/cometbft:/cometbft \ - --env CMT_PROXY_APP=tcp://${FM_CONTAINER_NAME}:26658 \ - --env CMT_PEX=false \ - --env CMT_MAX_SUBSCRIPTION_CLIENTS=10 \ - --env CMT_MAX_SUBSCRIPTIONS_PER_CLIENT=1000 \ - ${CMT_DOCKER_IMAGE} \ - ${CMD} -""" -dependencies = ["cometbft-pull", "test-data-dir", "network-create"] - - -[tasks.fendermint-init] -extend = "fendermint-run" -env = { "ENTRY" = "/scripts/init.sh", "FLAGS" = "-a STDOUT -a STDERR --rm" } - -[tasks.fendermint-start] -extend = "fendermint-run" -env = { "ENTRY" = "fendermint", "CMD" = "run", "FLAGS" = "-d" } - -[tasks.fendermint-run] -script = """ -docker run \ - ${FLAGS} \ - --name ${FM_CONTAINER_NAME} \ - --init \ - --user $(id -u) \ - --network ${NETWORK_NAME} \ - --volume ${TEST_DATA_DIR}:/data \ - --volume ${TEST_SCRIPTS_DIR}:/scripts \ - --env RUST_BACKTRACE=1 \ - --env FM_DATA_DIR=/data/fendermint/data \ - --env FM_CHAIN_NAME=${NETWORK_NAME} \ - --env TENDERMINT_RPC_URL=http://${CMT_CONTAINER_NAME}:26657 \ - --env LOG_LEVEL=info \ - --entrypoint ${ENTRY} \ - ${FM_DOCKER_IMAGE} \ - ${CMD} -""" -dependencies = ["test-data-dir", "network-create"] - -[tasks.ethapi-start] -extend = "ethapi-run" -env = { "CMD" = "eth run", "FLAGS" = "-d" } - - -[tasks.ethapi-run] -script = """ -docker run \ - ${FLAGS} \ - --name ${ETHAPI_CONTAINER_NAME} \ - --init \ - --user $(id -u) \ - --network ${NETWORK_NAME} \ - --publish ${ETHAPI_HOST_PORT}:8545 \ - --env TENDERMINT_WS_URL=ws://${CMT_CONTAINER_NAME}:26657/websocket \ - --env LOG_LEVEL=debug \ - --env RUST_BACKTRACE=1 \ - ${FM_DOCKER_IMAGE} \ - ${CMD} -""" -dependencies = ["network-create"] - -[tasks.network-create] -command = "docker" -args = ["network", "create", "${NETWORK_NAME}"] -ignore_errors = true - -[tasks.network-rm] -command = "docker" -args = ["network", "rm", "${NETWORK_NAME}"] -ignore_errors = true - -[tasks.cometbft-rm] -extend = "docker-rm" -env = { "CONTAINER_NAME" = "${CMT_CONTAINER_NAME}" } - -[tasks.cometbft-stop] -extend = "docker-stop" -env = { "CONTAINER_NAME" = "${CMT_CONTAINER_NAME}" } - -[tasks.fendermint-rm] -extend = "docker-rm" -env = { "CONTAINER_NAME" = "${FM_CONTAINER_NAME}" } - -[tasks.fendermint-stop] -extend = "docker-stop" -env = { "CONTAINER_NAME" = "${FM_CONTAINER_NAME}" } - -[tasks.ethapi-rm] -extend = "docker-rm" -env = { "CONTAINER_NAME" = "${ETHAPI_CONTAINER_NAME}" } - -[tasks.ethapi-stop] -extend = "docker-stop" -env = { "CONTAINER_NAME" = "${ETHAPI_CONTAINER_NAME}" } - -[tasks.ethapi-logs] -extend = "docker-logs" -env = { "CONTAINER_NAME" = "${ETHAPI_CONTAINER_NAME}" } - -[tasks.fendermint-logs] -extend = "docker-logs" -env = { "CONTAINER_NAME" = "${FM_CONTAINER_NAME}" } - -[tasks.cometbft-logs] -extend = "docker-logs" -env = { "CONTAINER_NAME" = "${CMT_CONTAINER_NAME}" } - -[tasks.docker-stop] -command = "docker" -args = ["stop", "${CONTAINER_NAME}"] -ignore_errors = true - -[tasks.docker-rm] -command = "docker" -args = ["rm", "--force", "${CONTAINER_NAME}"] -ignore_errors = true - -[tasks.docker-logs] -command = "docker" -args = ["logs", "${CONTAINER_NAME}"] -ignore_errors = true diff --git a/fendermint/testing/smoke-test/scripts/init.sh b/fendermint/testing/smoke-test/scripts/init.sh index 68ce7d48..e7641fb1 100755 --- a/fendermint/testing/smoke-test/scripts/init.sh +++ b/fendermint/testing/smoke-test/scripts/init.sh @@ -4,11 +4,9 @@ set -e # Create test artifacts, which is basically the Tendermint genesis file. -CMT_DIR=/data/cometbft -FM_DIR=/data/fendermint - -KEYS_DIR=$FM_DIR/keys -GENESIS_FILE=$FM_DIR/genesis.json +KEYS_DIR=/data/keys +CMT_DIR=/data/${NODE_NAME}/cometbft +GENESIS_FILE=/data/genesis.json # Create a genesis file fendermint \ diff --git a/fendermint/testing/smoke-test/smoke.env b/fendermint/testing/smoke-test/smoke.env new file mode 100644 index 00000000..c75423ad --- /dev/null +++ b/fendermint/testing/smoke-test/smoke.env @@ -0,0 +1,2 @@ +NETWORK_NAME="smoke" +TEST_DIR="smoke-test" diff --git a/infra/Makefile.toml b/infra/Makefile.toml index 4f8ab609..f5fc9243 100644 --- a/infra/Makefile.toml +++ b/infra/Makefile.toml @@ -3,7 +3,7 @@ extend = [ { path = "scripts/cometbft.toml" }, { path = "scripts/fendermint.toml" }, { path = "scripts/ethapi.toml" }, - { path = "scripts/genesis.toml"}, + { path = "scripts/genesis.toml" }, { path = "scripts/node.toml" }, { path = "scripts/testnet.toml" }, { path = "scripts/testnode.toml" }, @@ -19,40 +19,50 @@ SUBNET_ID = { value = "/r0", condition = { env_not_set = ["SUBNET_ID"] } } # The network name is derived from the SUBNET_ID, replacing slashes with dashes, and dropping the first dash if any. NETWORK_NAME = { script = ["echo $SUBNET_ID | sed -e 's|/|-|g' -e 's|^-||1'"] } # External P2P address advertised by CometBFT to other peers. -CMT_EXTERNAL_ADDR = { value = "", condition = { env_not_set = ["CMT_EXTERNAL_ADDR"] }} +CMT_EXTERNAL_ADDR = { value = "", condition = { env_not_set = ["CMT_EXTERNAL_ADDR"] } } -BALANCE = { value = "1000", condition = { env_not_set = ["BALANCE"] }} -BASE_FEE = { value = "1000", condition = { env_not_set = ["BASE_FEE"] }} -TIMESTAMP = { value = "1680101412", condition = { env_not_set = ["TIMESTAMP"] }} -POWER_SCALE = { value = "3", condition = { env_not_set = ["POWER_SCALE"] }} -CMT_P2P_HOST_PORT = { value = "26656", condition = { env_not_set = ["CMT_P2P_HOST_PORT"] }} -CMT_RPC_HOST_PORT = { value = "26657", condition = { env_not_set = ["CMT_RPC_HOST_PORT"] }} -ETHAPI_HOST_PORT = { value = "8545", condition = { env_not_set = ["ETHAPI_HOST_PORT"] }} +BALANCE = { value = "1000", condition = { env_not_set = ["BALANCE"] } } +BASE_FEE = { value = "1000", condition = { env_not_set = ["BASE_FEE"] } } +TIMESTAMP = { value = "1680101412", condition = { env_not_set = ["TIMESTAMP"] } } +POWER_SCALE = { value = "3", condition = { env_not_set = ["POWER_SCALE"] } } +CMT_P2P_HOST_PORT = { value = "26656", condition = { env_not_set = ["CMT_P2P_HOST_PORT"] } } +CMT_RPC_HOST_PORT = { value = "26657", condition = { env_not_set = ["CMT_RPC_HOST_PORT"] } } +ETHAPI_HOST_PORT = { value = "8545", condition = { env_not_set = ["ETHAPI_HOST_PORT"] } } # IPC subnet related parameters # Use calibration as default value NODE_NAME = { value = "ipc-node", condition = { env_not_set = ["NODE_NAME"] } } -PARENT_ENDPOINT = { value = "https://api.calibration.node.glif.io/rpc/v1", condition = { env_not_set = ["PARENT_ENDPOINT"] } } -PARENT_GATEWAY = { value = "0x56948d2CFaa2EF355B8C08Ac925202db212146D1", condition = { env_not_set = ["PARENT_GATEWAY"] } } -PARENT_REGISTRY = { value = "0x6A4884D2B6A597792dC68014D4B7C117cca5668e", condition = { env_not_set = ["PARENT_REGISTRY"] } } +PARENT_ENDPOINT = { value = "https://api.calibration.node.glif.io/rpc/v1", condition = { env_not_set = [ + "PARENT_ENDPOINT", +] } } +PARENT_GATEWAY = { value = "0x56948d2CFaa2EF355B8C08Ac925202db212146D1", condition = { env_not_set = [ + "PARENT_GATEWAY", +] } } +PARENT_REGISTRY = { value = "0x6A4884D2B6A597792dC68014D4B7C117cca5668e", condition = { env_not_set = [ + "PARENT_REGISTRY", +] } } FM_NETWORK = { value = "test", condition = { env_not_set = ["FM_NETWORK"] } } -TOPDOWN_CHAIN_HEAD_DELAY = { value = "10", condition = { env_not_set = ["TOPDOWN_CHAIN_HEAD_DELAY"] } } +TOPDOWN_CHAIN_HEAD_DELAY = { value = "10", condition = { env_not_set = [ + "TOPDOWN_CHAIN_HEAD_DELAY", +] } } TOPDOWN_PROPOSAL_DELAY = { value = "2", condition = { env_not_set = ["TOPDOWN_PROPOSAL_DELAY"] } } -TOPDOWN_MAX_PROPOSAL_RANGE = { value = "100", condition = { env_not_set = ["TOPDOWN_MAX_PROPOSAL_RANGE"] } } +TOPDOWN_MAX_PROPOSAL_RANGE = { value = "100", condition = { env_not_set = [ + "TOPDOWN_MAX_PROPOSAL_RANGE", +] } } # Comma-separated list of bootstrap nodes to be used by the CometBFT node. BOOTSTRAPS = { value = "", condition = { env_not_set = ["BOOTSTRAPS"] } } PRIVATE_KEY_PATH = { value = "", condition = { env_not_set = ["PRIVATE_KEY_PATH"] } } # Deployment-related -BASE_DIR="${HOME}/.ipc/${NETWORK_NAME}/${NODE_NAME}" -FM_DIR="${BASE_DIR}/${NODE_NAME}/fendermint" -CMT_DIR="${BASE_DIR}/${NODE_NAME}/cometbft" +BASE_DIR = "${HOME}/.ipc/${NETWORK_NAME}/${NODE_NAME}" +FM_DIR = "${BASE_DIR}/${NODE_NAME}/fendermint" +CMT_DIR = "${BASE_DIR}/${NODE_NAME}/cometbft" -GENESIS_FILE="${BASE_DIR}/genesis.json" -KEYS_SUBDIR="keys" -KEY_NAME="validator_key" -PUB_KEY_PATH="${KEYS_SUBDIR}/${KEY_NAME}.pk" -PRIV_KEY_PATH="${KEYS_SUBDIR}/${KEY_NAME}.sk" +GENESIS_FILE = "${BASE_DIR}/genesis.json" +KEYS_SUBDIR = "keys" +KEY_NAME = "validator_key" +PUB_KEY_PATH = "${KEYS_SUBDIR}/${KEY_NAME}.pk" +PRIV_KEY_PATH = "${KEYS_SUBDIR}/${KEY_NAME}.sk" COMETBFT_SUBDIR = "cometbft" @@ -74,10 +84,12 @@ CMT_WAIT_MILLIS = 20000 # Keep example logs to a minimum. VERBOSITY = "" # supports info, error, debug, etc. -LOG_LEVEL= "info" +LOG_LEVEL = "info" +ETHAPI_LOG_LEVEL = { value = "${LOG_LEVEL}", condition = { env_not_set = ["ETHAPI_LOG_LEVEL"] } } +FM_LOG_LEVEL = { value = "${LOG_LEVEL}", condition = { env_not_set = ["FM_LOG_LEVEL"] } } [tasks.info] -script=""" +script = """ echo echo Chain info: echo - Chain: ${SUBNET_ID} @@ -107,7 +119,8 @@ echo [tasks.default] clear = true script_runner = "@duckscript" -script = [''' +script = [ + ''' echo echo Main tasks: echo - testnet: run 4-nodes testnet @@ -124,4 +137,5 @@ script = [''' echo echo Run 'cargo make --list-all-steps' for a complete list of available tasks. echo -'''] +''', +] diff --git a/infra/scripts/cometbft.toml b/infra/scripts/cometbft.toml index 482c6b7d..e7174b35 100644 --- a/infra/scripts/cometbft.toml +++ b/infra/scripts/cometbft.toml @@ -212,3 +212,9 @@ dependencies = [ "set-external-addr", "duplicate-ip-enable", ] + + +[tasks.node-id] +command = "docker" +args = ["exec", "${CONTAINER_NAME}", "cometbft show-node-id"] +ignore_errors = true diff --git a/infra/scripts/docker.toml b/infra/scripts/docker.toml index d763bf5d..7c8cc75c 100644 --- a/infra/scripts/docker.toml +++ b/infra/scripts/docker.toml @@ -22,8 +22,3 @@ ignore_errors = true command = "docker" args = ["logs", "${CONTAINER_NAME}"] ignore_errors = true - -[tasks.docker-id] -command = "docker" -args = ["exec", "${CONTAINER_NAME}", "cometbft show-node-id"] -ignore_errors = true diff --git a/infra/scripts/ethapi.toml b/infra/scripts/ethapi.toml index 2a9f4dd7..2abdb36d 100644 --- a/infra/scripts/ethapi.toml +++ b/infra/scripts/ethapi.toml @@ -8,7 +8,7 @@ docker run \ --network ${NETWORK_NAME} \ --publish ${ETHAPI_HOST_PORT}:8545 \ --env TENDERMINT_WS_URL=ws://${CMT_CONTAINER_NAME}:26657/websocket \ - --env LOG_LEVEL=${LOG_LEVEL} \ + --env LOG_LEVEL=${ETHAPI_LOG_LEVEL} \ --env RUST_BACKTRACE=1 \ ${FM_DOCKER_IMAGE} \ ${CMD} @@ -29,4 +29,4 @@ env = { "CONTAINER_NAME" = "${ETHAPI_CONTAINER_NAME}" } [tasks.ethapi-logs] extend = "docker-logs" -env = { "CONTAINER_NAME" = "${ETHAPI_CONTAINER_NAME}" } \ No newline at end of file +env = { "CONTAINER_NAME" = "${ETHAPI_CONTAINER_NAME}" } diff --git a/infra/scripts/fendermint.toml b/infra/scripts/fendermint.toml index 0910c5f0..a1617423 100644 --- a/infra/scripts/fendermint.toml +++ b/infra/scripts/fendermint.toml @@ -3,7 +3,9 @@ extend = "fendermint-run" env = { "ENTRY" = "fendermint", "CMD" = "run", "FLAGS" = "-d" } [tasks.fendermint-pull] -condition = { env_not_set = ["FM_PULL_SKIP"], fail_message = "Skipped pulling fendermint Docker image." } +condition = { env_not_set = [ + "FM_PULL_SKIP", +], fail_message = "Skipped pulling fendermint Docker image." } script = """ docker pull ghcr.io/consensus-shipyard/fendermint:${FM_DOCKER_TAG} docker tag ghcr.io/consensus-shipyard/fendermint:${FM_DOCKER_TAG} fendermint:${FM_DOCKER_TAG} @@ -18,13 +20,12 @@ docker run \ --user $(id -u) \ --network ${NETWORK_NAME} \ --volume ${BASE_DIR}:/data \ + --env NODE_NAME=${NODE_NAME} \ --env FM_DATA_DIR=/data/${NODE_NAME}/fendermint/data \ --env FM_LOG_DIR=/data/${NODE_NAME}/fendermint/data/logs \ - --env FM_ABCI__LISTEN__HOST=0.0.0.0 \ - --env FM_ETH__LISTEN__HOST=0.0.0.0 \ --env FM_CHAIN_NAME=${NETWORK_NAME} \ --env TENDERMINT_RPC_URL=http://${CMT_CONTAINER_NAME}:26657 \ - --env LOG_LEVEL=${LOG_LEVEL} \ + --env LOG_LEVEL=${FM_LOG_LEVEL} \ --env RUST_BACKTRACE=1 \ --entrypoint ${ENTRY} \ ${FM_DOCKER_IMAGE} \ From ea59cfc5d3035c16e83114b4b098b769f3894d17 Mon Sep 17 00:00:00 2001 From: Akosh Farkash Date: Wed, 6 Dec 2023 12:00:44 +0000 Subject: [PATCH 5/6] FIX: Do not fail if status doesn't respond in the interpreter (#455) --- fendermint/vm/interpreter/src/fvm/exec.rs | 2 +- fendermint/vm/interpreter/src/fvm/mod.rs | 19 ++++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/fendermint/vm/interpreter/src/fvm/exec.rs b/fendermint/vm/interpreter/src/fvm/exec.rs index a79ae218..8b6244ff 100644 --- a/fendermint/vm/interpreter/src/fvm/exec.rs +++ b/fendermint/vm/interpreter/src/fvm/exec.rs @@ -141,7 +141,7 @@ where // Asynchronously broadcast signature, if validating. if let Some(ref ctx) = self.validator_ctx { // Do not resend past signatures. - if !self.syncing().await? { + if !self.syncing().await { // Fetch any incomplete checkpoints synchronously because the state can't be shared across threads. let incomplete_checkpoints = checkpoint::unsigned_checkpoints(&self.gateway, &mut state, ctx.public_key) diff --git a/fendermint/vm/interpreter/src/fvm/mod.rs b/fendermint/vm/interpreter/src/fvm/mod.rs index 21f866ab..dd9a5b32 100644 --- a/fendermint/vm/interpreter/src/fvm/mod.rs +++ b/fendermint/vm/interpreter/src/fvm/mod.rs @@ -16,7 +16,6 @@ pub mod store; pub mod bundle; pub(crate) mod topdown; -use anyhow::Context; pub use check::FvmCheckRet; pub use checkpoint::PowerUpdates; pub use exec::FvmApplyRet; @@ -101,13 +100,15 @@ where C: Client + Sync, { /// Indicate that the node is syncing with the rest of the network and hasn't caught up with the tip yet. - async fn syncing(&self) -> anyhow::Result { - let status: tendermint_rpc::endpoint::status::Response = self - .client - .status() - .await - .context("failed to get Tendermint status")?; - - Ok(status.sync_info.catching_up) + async fn syncing(&self) -> bool { + match self.client.status().await { + Ok(status) => status.sync_info.catching_up, + Err(e) => { + // CometBFT often takes a long time to boot, e.g. while it's replaying blocks it won't + // respond to JSON-RPC calls. Let's treat this as an indication that we are syncing. + tracing::warn!(error =? e, "failed to get CometBFT sync status"); + true + } + } } } From f30cf0dc2678c77a93233ecc3f55bc9a17fbc1df Mon Sep 17 00:00:00 2001 From: cryptoAtwill Date: Thu, 7 Dec 2023 13:20:40 +0800 Subject: [PATCH 6/6] more unit tests --- fendermint/vm/topdown/src/finality/fetch.rs | 84 +++++++++++++++++++-- 1 file changed, 79 insertions(+), 5 deletions(-) diff --git a/fendermint/vm/topdown/src/finality/fetch.rs b/fendermint/vm/topdown/src/finality/fetch.rs index 9009313c..2bdfd4f5 100644 --- a/fendermint/vm/topdown/src/finality/fetch.rs +++ b/fendermint/vm/topdown/src/finality/fetch.rs @@ -127,14 +127,15 @@ impl ParentFinalityProvider return Ok(Some(valid_hash)); } - tracing::warn!("height {} found in cache but not hash", proposal.height); + tracing::info!("height {} found in cache but not hash", proposal.height); } Ok(None) - }).await { + }) + .await + { return Ok(check); } - match retry!( self.config.exponential_back_off, self.config.exponential_retry_limit, @@ -284,10 +285,11 @@ mod tests { use crate::finality::ParentViewPayload; use crate::proxy::ParentQueryProxy; use crate::{ - BlockHeight, CachedFinalityProvider, Config, IPCParentFinality, ParentViewProvider, - SequentialKeyCache, NULL_ROUND_ERR_MSG, + BlockHeight, CachedFinalityProvider, Config, IPCParentFinality, ParentFinalityProvider, + ParentViewProvider, SequentialKeyCache, NULL_ROUND_ERR_MSG, }; use anyhow::anyhow; + use async_stm::atomically_or_err; use async_trait::async_trait; use fvm_shared::address::Address; use fvm_shared::econ::TokenAmount; @@ -491,4 +493,76 @@ mod tests { assert_eq!(messages.len(), 4) } + + #[tokio::test] + async fn test_check_proposal() { + let parent_blocks = new_parent_blocks!( + 100 => Some((vec![0; 32], vec![new_validator_changes(0)], vec![])), // genesis block + 101 => Some((vec![1; 32], vec![new_validator_changes(1)], vec![])), + 102 => Some((vec![2; 32], vec![], vec![])), + 103 => Some((vec![3; 32], vec![new_validator_changes(3)], vec![])), + 104 => None, + 105 => None, + 106 => Some((vec![6; 32], vec![new_validator_changes(6)], vec![])) + ); + let provider = new_provider(parent_blocks); + let proposal = IPCParentFinality { + height: 101, + block_hash: vec![1; 32], + }; + let is_ok = provider.check_proposal(&proposal).await.unwrap(); + assert!(is_ok); + + // miss cache and invalid block hash + let is_ok = provider + .check_proposal(&IPCParentFinality { + height: 101, + block_hash: vec![2; 32], + }) + .await + .unwrap(); + assert!(!is_ok); + + // hit last committed finality but invalid height + let is_ok = provider + .check_proposal(&IPCParentFinality { + height: 100, + block_hash: vec![2; 32], + }) + .await + .unwrap(); + assert!(!is_ok); + + // hit last committed finality but invalid height + let is_ok = provider + .check_proposal(&IPCParentFinality { + height: 99, + block_hash: vec![2; 32], + }) + .await + .unwrap(); + assert!(!is_ok); + + // insert cache + atomically_or_err(|| { + provider.inner.new_parent_view( + 101, + Some((vec![1; 32], vec![new_validator_changes(1)], vec![])), + )?; + provider.inner.new_parent_view( + 102, + Some((vec![2; 32], vec![new_validator_changes(1)], vec![])), + ) + }) + .await + .unwrap(); + let is_ok = provider + .check_proposal(&IPCParentFinality { + height: 102, + block_hash: vec![2; 32], + }) + .await + .unwrap(); + assert!(is_ok); + } }