From 19f93f569a7ff0b4c0649e5e9d446d9dd4401632 Mon Sep 17 00:00:00 2001 From: cryptoAtwill Date: Fri, 31 May 2024 00:14:34 +0800 Subject: [PATCH 01/69] complete flow --- fendermint/app/options/src/genesis.rs | 22 + fendermint/app/src/app.rs | 48 +- fendermint/app/src/cmd/genesis.rs | 16 + fendermint/app/src/lib.rs | 2 + fendermint/vm/genesis/src/lib.rs | 1 + .../vm/interpreter/src/fvm/state/genesis.rs | 11 + .../vm/interpreter/src/fvm/state/snapshot.rs | 4 +- fendermint/vm/interpreter/src/genesis.rs | 785 ++++++++++++++++++ fendermint/vm/interpreter/src/lib.rs | 1 + 9 files changed, 846 insertions(+), 44 deletions(-) create mode 100644 fendermint/vm/interpreter/src/genesis.rs diff --git a/fendermint/app/options/src/genesis.rs b/fendermint/app/options/src/genesis.rs index 3da1794bc0..2b8e682a2e 100644 --- a/fendermint/app/options/src/genesis.rs +++ b/fendermint/app/options/src/genesis.rs @@ -147,6 +147,28 @@ pub enum GenesisIpcCommands { Gateway(GenesisIpcGatewayArgs), /// Fetch the genesis parameters of a subnet from the parent. FromParent(Box), + /// Seal the genesis state from the genesis parameter file + SealState(SealGenesisArgs), +} + +#[derive(Args, Debug, Clone)] +pub struct SealGenesisArgs { + /// The built in actors bundle path + #[arg(long, short)] + pub builtin_actors_path: PathBuf, + + /// The custom actors bundle path + #[arg(long, short)] + pub custom_actors_path: PathBuf, + + /// The ipc artifacts output path. If you are using ipc-monorepo, it should be the `out` folder + /// of `make build` + #[arg(long, short)] + pub ipc_artifacts_path: PathBuf, + + /// The sealed, i.e. finalized genesis state CAR file dump path + #[arg(long, short)] + pub sealed_genesis_path: PathBuf, } #[derive(Args, Debug, Clone)] diff --git a/fendermint/app/src/app.rs b/fendermint/app/src/app.rs index 81d67073f8..f4a734a4f4 100644 --- a/fendermint/app/src/app.rs +++ b/fendermint/app/src/app.rs @@ -42,6 +42,7 @@ use serde::{Deserialize, Serialize}; use tendermint::abci::request::CheckTxKind; use tendermint::abci::{request, response}; use tracing::instrument; +use fendermint_vm_interpreter::genesis::read_genesis_car; use crate::events::{NewBlock, ProposalProcessed}; use crate::AppExitCode; @@ -136,8 +137,10 @@ where /// Path to the Wasm bundle. /// /// Only loaded once during genesis; later comes from the [`StateTree`]. + #[deprecated] builtin_actors_bundle: PathBuf, /// Path to the custom actor WASM bundle. + #[deprecated] custom_actors_bundle: PathBuf, /// Block height where we should gracefully stop the node halt_height: i64, @@ -447,29 +450,6 @@ where /// Called once upon genesis. async fn init_chain(&self, request: request::InitChain) -> AbciResult { - let bundle = &self.builtin_actors_bundle; - let bundle = std::fs::read(bundle) - .map_err(|e| anyhow!("failed to load builtin bundle CAR from {bundle:?}: {e}"))?; - - let custom_actors_bundle = &self.custom_actors_bundle; - let custom_actors_bundle = std::fs::read(custom_actors_bundle).map_err(|e| { - anyhow!("failed to load custom actor bundle CAR from {custom_actors_bundle:?}: {e}") - })?; - - let state = FvmGenesisState::new( - self.state_store_clone(), - self.multi_engine.clone(), - &bundle, - &custom_actors_bundle, - ) - .await - .context("failed to create genesis state")?; - - tracing::info!( - manifest_root = format!("{}", state.manifest_data_cid), - "pre-genesis state created" - ); - let genesis_bytes = request.app_state_bytes.to_vec(); let genesis_hash = fendermint_vm_message::cid(&genesis_bytes).context("failed to compute genesis CID")?; @@ -477,15 +457,8 @@ where // Make it easy to spot any discrepancies between nodes. tracing::info!(genesis_hash = genesis_hash.to_string(), "genesis"); - let (state, out) = self - .interpreter - .init(state, genesis_bytes) - .await - .context("failed to init from genesis")?; - - let state_root = state.commit().context("failed to commit genesis state")?; - let validators = - to_validator_updates(out.validators).context("failed to convert validators")?; + let (validators, state_params) = read_genesis_car(&genesis_bytes, &self.state_store).await?; + let validators = to_validator_updates(validators).context("failed to convert validators")?; // Let's pretend that the genesis state is that of a fictive block at height 0. // The record will be stored under height 1, and the record after the application @@ -502,16 +475,7 @@ where let app_state = AppState { block_height: height, oldest_state_height: height, - state_params: FvmStateParams { - state_root, - timestamp: out.timestamp, - network_version: out.network_version, - base_fee: out.base_fee, - circ_supply: out.circ_supply, - chain_id: out.chain_id.into(), - power_scale: out.power_scale, - app_version: 0, - }, + state_params, }; let response = response::InitChain { diff --git a/fendermint/app/src/cmd/genesis.rs b/fendermint/app/src/cmd/genesis.rs index 183a89cb7c..d188ba6f55 100644 --- a/fendermint/app/src/cmd/genesis.rs +++ b/fendermint/app/src/cmd/genesis.rs @@ -14,6 +14,7 @@ use fendermint_vm_genesis::{ ipc, Account, Actor, ActorMeta, Collateral, Genesis, Multisig, PermissionMode, SignerAddr, Validator, ValidatorKey, }; +use fendermint_vm_interpreter::genesis::GenesisCreator; use crate::cmd; use crate::options::genesis::*; @@ -93,6 +94,8 @@ cmd! { set_ipc_gateway(&genesis_file, args), GenesisIpcCommands::FromParent(args) => new_genesis_from_parent(&genesis_file, args).await, + GenesisIpcCommands::SealState(args) => + seal_state(&genesis_file, args).await, } } } @@ -279,6 +282,19 @@ fn set_ipc_gateway(genesis_file: &PathBuf, args: &GenesisIpcGatewayArgs) -> anyh }) } +async fn seal_state(genesis_file: &PathBuf, args: &SealGenesisArgs) -> anyhow::Result<()> { + let genesis = read_genesis(genesis_file)?; + + let genesis_creator = GenesisCreator::new( + args.builtin_actors_path.clone(), + args.custom_actors_path.clone(), + args.ipc_artifacts_path.clone(), + args.sealed_genesis_path.clone(), + ); + + genesis_creator.create(genesis).await +} + async fn new_genesis_from_parent( genesis_file: &PathBuf, args: &GenesisFromParentArgs, diff --git a/fendermint/app/src/lib.rs b/fendermint/app/src/lib.rs index 29c83a384a..d0972423d9 100644 --- a/fendermint/app/src/lib.rs +++ b/fendermint/app/src/lib.rs @@ -1,3 +1,5 @@ +extern crate core; + // Copyright 2022-2024 Protocol Labs // SPDX-License-Identifier: Apache-2.0, MIT mod app; diff --git a/fendermint/vm/genesis/src/lib.rs b/fendermint/vm/genesis/src/lib.rs index ae87b4a1db..ab2c1d1361 100644 --- a/fendermint/vm/genesis/src/lib.rs +++ b/fendermint/vm/genesis/src/lib.rs @@ -7,6 +7,7 @@ use anyhow::anyhow; use fvm_shared::bigint::{BigInt, Integer}; use serde::{Deserialize, Serialize}; use serde_with::serde_as; +use std::path::PathBuf; use fendermint_actor_eam::PermissionModeParams; use fvm_shared::version::NetworkVersion; diff --git a/fendermint/vm/interpreter/src/fvm/state/genesis.rs b/fendermint/vm/interpreter/src/fvm/state/genesis.rs index f43b6c4fde..da90cbe0a7 100644 --- a/fendermint/vm/interpreter/src/fvm/state/genesis.rs +++ b/fendermint/vm/interpreter/src/fvm/state/genesis.rs @@ -177,6 +177,17 @@ where } } + /// Flush the data to the block store. Returns the state root cid and the underlying state store. + pub fn finalize(self) -> anyhow::Result<(Cid, DB)> { + match self.stage { + Stage::Tree(_) => Err(anyhow!("invalid finalize state")), + Stage::Exec(exec_state) => match exec_state.commit()? { + (_, _, true) => bail!("FVM parameters are not expected to be updated in genesis"), + (cid, _, _) => Ok((cid, self.store)), + }, + } + } + /// Replaces the built in actor with custom actor. This assumes the system actor is already /// created, else it would throw an error. pub fn replace_builtin_actor( diff --git a/fendermint/vm/interpreter/src/fvm/state/snapshot.rs b/fendermint/vm/interpreter/src/fvm/state/snapshot.rs index 0801ade9e3..7f70c5cbc4 100644 --- a/fendermint/vm/interpreter/src/fvm/state/snapshot.rs +++ b/fendermint/vm/interpreter/src/fvm/state/snapshot.rs @@ -213,7 +213,7 @@ where } #[pin_project::pin_project] -struct StateTreeStreamer { +pub(crate) struct StateTreeStreamer { /// The list of cids to pull from the blockstore #[pin] dfs: VecDeque, @@ -286,7 +286,7 @@ fn walk_ipld_cids(ipld: Ipld, dfs: &mut VecDeque) { } } -fn derive_cid(t: &T) -> anyhow::Result<(Cid, Vec)> { +pub(crate) fn derive_cid(t: &T) -> anyhow::Result<(Cid, Vec)> { let bytes = fvm_ipld_encoding::to_vec(&t)?; let cid = Cid::new_v1(DAG_CBOR, Code::Blake2b256.digest(&bytes)); Ok((cid, bytes)) diff --git a/fendermint/vm/interpreter/src/genesis.rs b/fendermint/vm/interpreter/src/genesis.rs new file mode 100644 index 0000000000..5b804639d0 --- /dev/null +++ b/fendermint/vm/interpreter/src/genesis.rs @@ -0,0 +1,785 @@ +// Copyright 2022-2024 Protocol Labs +// SPDX-License-Identifier: Apache-2.0, MIT + +use std::collections::{BTreeSet, HashMap}; +use std::marker::PhantomData; +use std::path::{Path, PathBuf}; +use std::pin::Pin; +use std::sync::Arc; + +use anyhow::{anyhow, Context}; +use cid::Cid; +use ethers::abi::Tokenize; +use ethers::core::types as et; +use fendermint_actor_eam::PermissionModeParams; +use fendermint_eth_hardhat::{Hardhat, FQN}; +use fendermint_vm_actor_interface::diamond::{EthContract, EthContractMap}; +use fendermint_vm_actor_interface::eam::EthAddress; +use fendermint_vm_actor_interface::ipc::IPC_CONTRACTS; +use fendermint_vm_actor_interface::{ + account, burntfunds, chainmetadata, cron, eam, init, ipc, reward, system, EMPTY_ARR, +}; +use fendermint_vm_core::{chainid, Timestamp}; +use fendermint_vm_genesis::{ActorMeta, Genesis, Power, PowerScale, Validator}; +use fvm::engine::MultiEngine; +use fvm_ipld_blockstore::Blockstore; +use fvm_ipld_car::{CarHeader, CarReader}; +use fvm_ipld_encoding::CborStore; +use fvm_shared::chainid::ChainID; +use fvm_shared::econ::TokenAmount; +use fvm_shared::version::NetworkVersion; +use ipc_actors_abis::i_diamond::FacetCut; +use num_traits::Zero; + +use crate::fvm::state::snapshot::{derive_cid, StateTreeStreamer}; +use crate::fvm::state::{FvmGenesisState, FvmStateParams}; +use crate::fvm::store::memory::MemoryBlockstore; +use serde::{Deserialize, Serialize}; +use serde_with::serde_as; +use tokio_stream::StreamExt; +use tokio_util::compat::TokioAsyncWriteCompatExt; + +/// The sealed genesis state metadata +#[serde_as] +#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] +pub struct GenesisMetadata { + pub state_params: FvmStateParams, + pub validators: Vec>, +} + +impl GenesisMetadata { + fn new(state_root: Cid, out: FvmGenesisOutput) -> GenesisMetadata { + let state_params = FvmStateParams { + state_root, + timestamp: out.timestamp, + network_version: out.network_version, + base_fee: out.base_fee, + circ_supply: out.circ_supply, + chain_id: out.chain_id.into(), + power_scale: out.power_scale, + app_version: 0, + }; + + GenesisMetadata { + state_params, + validators: out.validators, + } + } +} + +pub async fn read_genesis_car(bytes: &[u8], store: &DB) -> anyhow::Result<(Vec>, FvmStateParams)> { + let car_reader = CarReader::new(bytes).await?; + + let roots = car_reader.read_into(store).await?; + + if roots.len() != 1 { + return Err(anyhow!("invalid genesis car, should have 1 root cid")); + } + + let metadata_cid = roots[0]; + let metadata = if let Some(metadata) = store.get_cbor::(&metadata_cid)? { + metadata + } else { + return Err(anyhow!("invalid genesis car, metadata not found")); + }; + + Ok((metadata.validators, metadata.state_params)) +} + +/// The output of genesis creation +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct FvmGenesisOutput { + pub chain_id: ChainID, + pub timestamp: Timestamp, + pub network_version: NetworkVersion, + pub base_fee: TokenAmount, + pub power_scale: PowerScale, + pub circ_supply: TokenAmount, + pub validators: Vec>, +} + +pub struct GenesisCreator { + /// Hardhat like util to deploy ipc contracts + hardhat: Hardhat, + /// The built in actors bundle path + builtin_actors_path: PathBuf, + /// The custom actors bundle path + custom_actors_path: PathBuf, + /// The CAR path to flush the sealed genesis state + sealed_out_path: PathBuf, +} + +impl GenesisCreator { + pub fn new( + builtin_actors_path: PathBuf, + custom_actors_path: PathBuf, + ipc_artifacts_path: PathBuf, + sealed_out_path: PathBuf, + ) -> Self { + Self { + hardhat: Hardhat::new(ipc_artifacts_path), + builtin_actors_path, + custom_actors_path, + sealed_out_path, + } + } + /// Initialize actor states from the Genesis parameters + pub async fn create(&self, genesis: Genesis) -> anyhow::Result<()> { + let mut state = self.init_state().await?; + let out = self.populate_state(&mut state, genesis)?; + let (state_root, store) = state.finalize()?; + self.write_car(state_root, out, store).await + } + + async fn write_car( + &self, + state_root: Cid, + out: FvmGenesisOutput, + store: MemoryBlockstore, + ) -> anyhow::Result<()> { + let file = tokio::fs::File::create(&self.sealed_out_path).await?; + + let metadata = GenesisMetadata::new(state_root, out); + + let streamer = StateTreeStreamer::new(state_root, store); + let (metadata_cid, metadata_bytes) = derive_cid(&metadata)?; + + // create the target car header with the metadata cid as the only root + let car = CarHeader::new(vec![metadata_cid], 1); + + // create the stream to stream all the data into the car file + let mut streamer = tokio_stream::iter(vec![(metadata_cid, metadata_bytes)]).merge(streamer); + + let write_task = tokio::spawn(async move { + let mut write = file.compat_write(); + car.write_stream_async(&mut Pin::new(&mut write), &mut streamer) + .await + }); + + write_task.await??; + + Ok(()) + } + + async fn init_state(&self) -> anyhow::Result> { + let bundle = std::fs::read(&self.builtin_actors_path).with_context(|| { + format!( + "failed to read bundle: {}", + self.builtin_actors_path.to_string_lossy() + ) + })?; + + let custom_actors_bundle = std::fs::read(&self.custom_actors_path).with_context(|| { + format!( + "failed to read custom actors_bundle: {}", + self.custom_actors_path.to_string_lossy() + ) + })?; + + let store = MemoryBlockstore::new(); + + FvmGenesisState::new( + store, + Arc::new(MultiEngine::new(1)), + &bundle, + &custom_actors_bundle, + ) + .await + .context("failed to create genesis state") + } + + fn populate_state( + &self, + state: &mut FvmGenesisState, + genesis: Genesis, + ) -> anyhow::Result { + // NOTE: We could consider adding the chain ID to the interpreter + // and rejecting genesis if it doesn't match the expectation, + // but the Tendermint genesis file also has this field, and + // presumably Tendermint checks that its peers have the same. + let chain_id = chainid::from_str_hashed(&genesis.chain_name)?; + + // Convert validators to CometBFT power scale. + let validators = genesis + .validators + .iter() + .cloned() + .map(|vc| vc.map_power(|c| c.into_power(genesis.power_scale))) + .collect(); + + // Currently we just pass them back as they are, but later we should + // store them in the IPC actors; or in case of a snapshot restore them + // from the state. + let out = FvmGenesisOutput { + chain_id, + timestamp: genesis.timestamp, + network_version: genesis.network_version, + circ_supply: circ_supply(&genesis), + base_fee: genesis.base_fee, + power_scale: genesis.power_scale, + validators, + }; + + // STAGE 0: Declare the built-in EVM contracts we'll have to deploy. + + // Pre-defined IDs for top-level Ethereum contracts. + let mut eth_builtin_ids = BTreeSet::new(); + let mut eth_root_contracts = Vec::new(); + let mut eth_contracts = EthContractMap::default(); + + // Only allocate IDs if the contracts are deployed. + if genesis.ipc.is_some() { + eth_contracts.extend(IPC_CONTRACTS.clone()); + } + + eth_builtin_ids.extend(eth_contracts.values().map(|c| c.actor_id)); + eth_root_contracts.extend(eth_contracts.keys()); + eth_root_contracts.extend( + eth_contracts + .values() + .flat_map(|c| c.facets.iter().map(|f| f.name)), + ); + // Collect dependencies of the main IPC actors. + let mut eth_libs = self + .hardhat + .dependencies( + ð_root_contracts + .iter() + .map(|n| (contract_src(n), *n)) + .collect::>(), + ) + .context("failed to collect EVM contract dependencies")?; + + // Only keep library dependencies, not contracts with constructors. + eth_libs.retain(|(_, d)| !eth_contracts.contains_key(d.as_str())); + + // STAGE 1: First we initialize native built-in actors. + + // System actor + state + .create_builtin_actor( + system::SYSTEM_ACTOR_CODE_ID, + system::SYSTEM_ACTOR_ID, + &system::State { + builtin_actors: state.manifest_data_cid, + }, + TokenAmount::zero(), + None, + ) + .context("failed to create system actor")?; + + // Init actor + let (init_state, addr_to_id) = init::State::new( + state.store(), + genesis.chain_name.clone(), + &genesis.accounts, + ð_builtin_ids, + eth_libs.len() as u64, + ) + .context("failed to create init state")?; + + state + .create_builtin_actor( + init::INIT_ACTOR_CODE_ID, + init::INIT_ACTOR_ID, + &init_state, + TokenAmount::zero(), + None, + ) + .context("failed to create init actor")?; + + // Cron actor + state + .create_builtin_actor( + cron::CRON_ACTOR_CODE_ID, + cron::CRON_ACTOR_ID, + &cron::State { + entries: vec![], // TODO: Maybe with the IPC. + }, + TokenAmount::zero(), + None, + ) + .context("failed to create cron actor")?; + + // Ethereum Account Manager (EAM) actor + state + .create_builtin_actor( + eam::EAM_ACTOR_CODE_ID, + eam::EAM_ACTOR_ID, + &EMPTY_ARR, + TokenAmount::zero(), + None, + ) + .context("failed to create EAM actor")?; + + // Burnt funds actor (it's just an account). + state + .create_builtin_actor( + account::ACCOUNT_ACTOR_CODE_ID, + burntfunds::BURNT_FUNDS_ACTOR_ID, + &account::State { + address: burntfunds::BURNT_FUNDS_ACTOR_ADDR, + }, + TokenAmount::zero(), + None, + ) + .context("failed to create burnt funds actor")?; + + // A placeholder for the reward actor, beause I don't think + // using the one in the builtin actors library would be appropriate. + // This effectively burns the miner rewards. Better than panicking. + state + .create_builtin_actor( + account::ACCOUNT_ACTOR_CODE_ID, + reward::REWARD_ACTOR_ID, + &account::State { + address: reward::REWARD_ACTOR_ADDR, + }, + TokenAmount::zero(), + None, + ) + .context("failed to create reward actor")?; + + // STAGE 1b: Then we initialize the in-repo custom actors. + + // Initialize the chain metadata actor which handles saving metadata about the chain + // (e.g. block hashes) which we can query. + let chainmetadata_state = fendermint_actor_chainmetadata::State::new( + &state.store(), + fendermint_actor_chainmetadata::DEFAULT_LOOKBACK_LEN, + )?; + state + .create_custom_actor( + fendermint_actor_chainmetadata::CHAINMETADATA_ACTOR_NAME, + chainmetadata::CHAINMETADATA_ACTOR_ID, + &chainmetadata_state, + TokenAmount::zero(), + None, + ) + .context("failed to create chainmetadata actor")?; + + let eam_state = fendermint_actor_eam::State::new( + state.store(), + PermissionModeParams::from(genesis.eam_permission_mode), + )?; + state + .replace_builtin_actor( + eam::EAM_ACTOR_NAME, + eam::EAM_ACTOR_ID, + fendermint_actor_eam::IPC_EAM_ACTOR_NAME, + &eam_state, + TokenAmount::zero(), + None, + ) + .context("failed to replace built in eam actor")?; + + // STAGE 2: Create non-builtin accounts which do not have a fixed ID. + + // The next ID is going to be _after_ the accounts, which have already been assigned an ID by the `Init` actor. + // The reason we aren't using the `init_state.next_id` is because that already accounted for the multisig accounts. + let mut next_id = init::FIRST_NON_SINGLETON_ADDR + addr_to_id.len() as u64; + + for a in genesis.accounts { + let balance = a.balance; + match a.meta { + ActorMeta::Account(acct) => { + state + .create_account_actor(acct, balance, &addr_to_id) + .context("failed to create account actor")?; + } + ActorMeta::Multisig(ms) => { + state + .create_multisig_actor(ms, balance, &addr_to_id, next_id) + .context("failed to create multisig actor")?; + next_id += 1; + } + } + } + + // STAGE 3: Initialize the FVM and create built-in FEVM actors. + + state + .init_exec_state( + out.timestamp, + out.network_version, + out.base_fee.clone(), + out.circ_supply.clone(), + out.chain_id.into(), + out.power_scale, + ) + .context("failed to init exec state")?; + + let mut deployer = ContractDeployer::::new(&self.hardhat, ð_contracts); + + // Deploy Ethereum libraries. + for (lib_src, lib_name) in eth_libs { + deployer.deploy_library(state, &mut next_id, lib_src, &lib_name)?; + } + + if let Some(ipc_params) = genesis.ipc { + // IPC Gateway actor. + let gateway_addr = { + use ipc::gateway::ConstructorParameters; + + let params = ConstructorParameters::new(ipc_params.gateway, genesis.validators) + .context("failed to create gateway constructor")?; + + let facets = deployer + .facets(ipc::gateway::CONTRACT_NAME) + .context("failed to collect gateway facets")?; + + deployer.deploy_contract(state, ipc::gateway::CONTRACT_NAME, (facets, params))? + }; + + // IPC SubnetRegistry actor. + { + use ipc::registry::ConstructorParameters; + + let mut facets = deployer + .facets(ipc::registry::CONTRACT_NAME) + .context("failed to collect registry facets")?; + + let getter_facet = facets.remove(0); + let manager_facet = facets.remove(0); + let rewarder_facet = facets.remove(0); + let checkpointer_facet = facets.remove(0); + let pauser_facet = facets.remove(0); + let diamond_loupe_facet = facets.remove(0); + let diamond_cut_facet = facets.remove(0); + let ownership_facet = facets.remove(0); + + debug_assert_eq!(facets.len(), 2, "SubnetRegistry has 2 facets of its own"); + + let params = ConstructorParameters { + gateway: gateway_addr, + getter_facet: getter_facet.facet_address, + manager_facet: manager_facet.facet_address, + rewarder_facet: rewarder_facet.facet_address, + pauser_facet: pauser_facet.facet_address, + checkpointer_facet: checkpointer_facet.facet_address, + diamond_cut_facet: diamond_cut_facet.facet_address, + diamond_loupe_facet: diamond_loupe_facet.facet_address, + ownership_facet: ownership_facet.facet_address, + subnet_getter_selectors: getter_facet.function_selectors, + subnet_manager_selectors: manager_facet.function_selectors, + subnet_rewarder_selectors: rewarder_facet.function_selectors, + subnet_checkpointer_selectors: checkpointer_facet.function_selectors, + subnet_pauser_selectors: pauser_facet.function_selectors, + subnet_actor_diamond_cut_selectors: diamond_cut_facet.function_selectors, + subnet_actor_diamond_loupe_selectors: diamond_loupe_facet.function_selectors, + subnet_actor_ownership_selectors: ownership_facet.function_selectors, + creation_privileges: 0, + }; + + deployer.deploy_contract(state, ipc::registry::CONTRACT_NAME, (facets, params))?; + }; + } + + Ok(out) + } +} + +fn contract_src(name: &str) -> PathBuf { + PathBuf::from(format!("{name}.sol")) +} + +struct ContractDeployer<'a, DB> { + hardhat: &'a Hardhat, + top_contracts: &'a EthContractMap, + // Assign dynamic ID addresses to libraries, but use fixed addresses for the top level contracts. + lib_addrs: HashMap, + phantom_db: PhantomData, +} + +impl<'a, DB> ContractDeployer<'a, DB> +where + DB: Blockstore + 'static + Clone, +{ + pub fn new(hardhat: &'a Hardhat, top_contracts: &'a EthContractMap) -> Self { + Self { + hardhat, + top_contracts, + lib_addrs: Default::default(), + phantom_db: PhantomData, + } + } + + /// Deploy a library contract with a dynamic ID and no constructor. + pub fn deploy_library( + &mut self, + state: &mut FvmGenesisState, + next_id: &mut u64, + lib_src: impl AsRef, + lib_name: &str, + ) -> anyhow::Result<()> { + let fqn = self.hardhat.fqn(lib_src.as_ref(), lib_name); + + let bytecode = self + .hardhat + .bytecode(&lib_src, lib_name, &self.lib_addrs) + .with_context(|| format!("failed to load library bytecode {fqn}"))?; + + let eth_addr = state + .create_evm_actor(*next_id, bytecode) + .with_context(|| format!("failed to create library actor {fqn}"))?; + + let id_addr = et::Address::from(EthAddress::from_id(*next_id).0); + let eth_addr = et::Address::from(eth_addr.0); + + tracing::info!( + actor_id = next_id, + ?eth_addr, + ?id_addr, + fqn, + "deployed Ethereum library" + ); + + // We can use the masked ID here or the delegated address. + // Maybe the masked ID is quicker because it doesn't need to be resolved. + self.lib_addrs.insert(fqn, id_addr); + + *next_id += 1; + + Ok(()) + } + + /// Construct the bytecode of a top-level contract and deploy it with some constructor parameters. + pub fn deploy_contract( + &self, + state: &mut FvmGenesisState, + contract_name: &str, + constructor_params: T, + ) -> anyhow::Result + where + T: Tokenize, + { + let contract = self.top_contract(contract_name)?; + let contract_id = contract.actor_id; + let contract_src = contract_src(contract_name); + + let bytecode = self + .hardhat + .bytecode(contract_src, contract_name, &self.lib_addrs) + .with_context(|| format!("failed to load {contract_name} bytecode"))?; + + let eth_addr = state + .create_evm_actor_with_cons(contract_id, &contract.abi, bytecode, constructor_params) + .with_context(|| format!("failed to create {contract_name} actor"))?; + + let id_addr = et::Address::from(EthAddress::from_id(contract_id).0); + let eth_addr = et::Address::from(eth_addr.0); + + tracing::info!( + actor_id = contract_id, + ?eth_addr, + ?id_addr, + contract_name, + "deployed Ethereum contract" + ); + + // The Ethereum address is more usable inside the EVM than the ID address. + Ok(eth_addr) + } + + /// Collect Facet Cuts for the diamond pattern, where the facet address comes from already deployed library facets. + pub fn facets(&self, contract_name: &str) -> anyhow::Result> { + let contract = self.top_contract(contract_name)?; + let mut facet_cuts = Vec::new(); + + for facet in contract.facets.iter() { + let facet_name = facet.name; + let facet_src = contract_src(facet_name); + let facet_fqn = self.hardhat.fqn(&facet_src, facet_name); + + let facet_addr = self + .lib_addrs + .get(&facet_fqn) + .ok_or_else(|| anyhow!("facet {facet_name} has not been deployed"))?; + + let method_sigs = facet + .abi + .functions() + .filter(|f| f.signature() != "init(bytes)") + .map(|f| f.short_signature()) + .collect(); + + let facet_cut = FacetCut { + facet_address: *facet_addr, + action: 0, // Add + function_selectors: method_sigs, + }; + + facet_cuts.push(facet_cut); + } + + Ok(facet_cuts) + } + + fn top_contract(&self, contract_name: &str) -> anyhow::Result<&EthContract> { + self.top_contracts + .get(contract_name) + .ok_or_else(|| anyhow!("unknown top contract name: {contract_name}")) + } +} + +/// Sum of balances in the genesis accounts. +fn circ_supply(g: &Genesis) -> TokenAmount { + g.accounts + .iter() + .fold(TokenAmount::zero(), |s, a| s + a.balance.clone()) +} +// +// #[cfg(test)] +// mod tests { +// use std::{str::FromStr, sync::Arc}; +// +// use cid::Cid; +// use fendermint_vm_genesis::{ipc::IpcParams, Genesis}; +// use fvm::engine::MultiEngine; +// use quickcheck::Arbitrary; +// use tendermint_rpc::{MockClient, MockRequestMethodMatcher}; +// +// use crate::{ +// fvm::{ +// bundle::{bundle_path, contracts_path, custom_actors_bundle_path}, +// state::ipc::GatewayCaller, +// store::memory::MemoryBlockstore, +// upgrades::UpgradeScheduler, +// FvmMessageInterpreter, +// }, +// GenesisInterpreter, +// }; +// +// use super::FvmGenesisState; +// +// #[tokio::test] +// async fn load_genesis() { +// let genesis = make_genesis(); +// let bundle = read_bundle(); +// let custom_actors_bundle = read_custom_actors_bundle(); +// let interpreter = make_interpreter(); +// +// let multi_engine = Arc::new(MultiEngine::default()); +// let store = MemoryBlockstore::new(); +// +// let state = FvmGenesisState::new(store, multi_engine, &bundle, &custom_actors_bundle) +// .await +// .expect("failed to create state"); +// +// let (mut state, out) = interpreter +// .init(state, genesis.clone()) +// .await +// .expect("failed to create actors"); +// +// assert_eq!(out.validators.len(), genesis.validators.len()); +// +// // Try calling a method on the IPC Gateway. +// let exec_state = state.exec_state().expect("should be in exec stage"); +// let caller = GatewayCaller::default(); +// +// let period = caller +// .bottom_up_check_period(exec_state) +// .expect("error calling the gateway"); +// +// assert_eq!(period, genesis.ipc.unwrap().gateway.bottom_up_check_period); +// +// let _state_root = state.commit().expect("failed to commit"); +// } +// +// #[tokio::test] +// async fn load_genesis_deterministic() { +// let genesis = make_genesis(); +// let bundle = read_bundle(); +// let custom_actors_bundle = read_custom_actors_bundle(); +// let interpreter = make_interpreter(); +// let multi_engine = Arc::new(MultiEngine::default()); +// +// // Create a couple of states and load the same thing. +// let mut outputs = Vec::new(); +// for _ in 0..3 { +// let store = MemoryBlockstore::new(); +// let state = +// FvmGenesisState::new(store, multi_engine.clone(), &bundle, &custom_actors_bundle) +// .await +// .expect("failed to create state"); +// +// let (state, out) = interpreter +// .init(state, genesis.clone()) +// .await +// .expect("failed to create actors"); +// +// let state_root_hash = state.commit().expect("failed to commit"); +// outputs.push((state_root_hash, out)); +// } +// +// for out in &outputs[1..] { +// assert_eq!(out.0, outputs[0].0, "state root hash is different"); +// } +// } +// +// // This is a sort of canary test, if it fails means something changed in the way we do genesis, +// // which is probably fine, but it's better to know about it, and if anybody doesn't get the same +// // then we might have some non-determinism. +// #[ignore] // I see a different value on CI than locally. +// #[tokio::test] +// async fn load_genesis_known() { +// let genesis_json = "{\"chain_name\":\"/r314159/f410fnfmitm2ww7oehhtbokf6wulhrr62sgq3sgqmenq\",\"timestamp\":1073250,\"network_version\":18,\"base_fee\":\"1000\",\"power_scale\":3,\"validators\":[{\"public_key\":\"BLX9ojqB+8Z26aMmKoCRb3Te6AnSU6zY8hPcf1X5Q69XCNaHVcRxzYO2xx7o/2vgdS7nkDTMRRbkDGzy+FYdAFc=\",\"power\":\"1000000000000000000\"},{\"public_key\":\"BFcOveVieknZiscWsfXa06aGbBkKeucBycd/w0N1QHlaZfa/5dJcH7D0hvcdfv3B2Rv1OPuxo1PkgsEbWegWKcA=\",\"power\":\"1000000000000000000\"},{\"public_key\":\"BEP30ykovfrQp3zo+JVRvDVL2emC+Ju1Kpox3zMVYZyFKvYt64qyN/HOVjridDrkEsnQU8BVen4Aegja4fBZ+LU=\",\"power\":\"1000000000000000000\"}],\"accounts\":[{\"meta\":{\"Account\":{\"owner\":\"f410fggjevhgketpz6gw6ordusynlgcd5piyug4aomuq\"}},\"balance\":\"1000000000000000000\"},{\"meta\":{\"Account\":{\"owner\":\"f410frbdnwklaitcjsqe7swjwp5naple6vthq4woyfry\"}},\"balance\":\"2000000000000000000\"},{\"meta\":{\"Account\":{\"owner\":\"f410fxo4lih4n2acr3oadalidwqjgoqkzhp5dw3zwkvy\"}},\"balance\":\"1000000000000000000\"}],\"ipc\":{\"gateway\":{\"subnet_id\":\"/r314159/f410fnfmitm2ww7oehhtbokf6wulhrr62sgq3sgqmenq\",\"bottom_up_check_period\":30,\"msg_fee\":\"1000000000000\",\"majority_percentage\":60,\"active_validators_limit\":100}}}"; +// +// let genesis: Genesis = serde_json::from_str(genesis_json).expect("failed to parse genesis"); +// +// let bundle = read_bundle(); +// let custom_actors_bundle = read_custom_actors_bundle(); +// let interpreter = make_interpreter(); +// let multi_engine = Arc::new(MultiEngine::default()); +// +// let store = MemoryBlockstore::new(); +// let state = +// FvmGenesisState::new(store, multi_engine.clone(), &bundle, &custom_actors_bundle) +// .await +// .expect("failed to create state"); +// +// let (state, _) = interpreter +// .init(state, genesis.clone()) +// .await +// .expect("failed to create actors"); +// +// let state_root_hash = state.commit().expect("failed to commit"); +// +// let expected_root_hash = +// Cid::from_str("bafy2bzacedebgy4j7qnh2v2x4kkr2jqfkryql5ookbjrwge6dbrr24ytlqnj4") +// .unwrap(); +// +// assert_eq!(state_root_hash, expected_root_hash); +// } +// +// fn make_genesis() -> Genesis { +// let mut g = quickcheck::Gen::new(5); +// let mut genesis = Genesis::arbitrary(&mut g); +// +// // Make sure we have IPC enabled. +// genesis.ipc = Some(IpcParams::arbitrary(&mut g)); +// genesis +// } +// +// fn make_interpreter( +// ) -> FvmMessageInterpreter> { +// let (client, _) = MockClient::new(MockRequestMethodMatcher::default()); +// FvmMessageInterpreter::new( +// client, +// None, +// contracts_path(), +// 1.05, +// 1.05, +// false, +// UpgradeScheduler::new(), +// ) +// } +// +// fn read_bundle() -> Vec { +// std::fs::read(bundle_path()).expect("failed to read bundle") +// } +// +// fn read_custom_actors_bundle() -> Vec { +// std::fs::read(custom_actors_bundle_path()).expect("failed to read custom actor bundle") +// } +// } diff --git a/fendermint/vm/interpreter/src/lib.rs b/fendermint/vm/interpreter/src/lib.rs index 42dab76b8c..afa1b43db5 100644 --- a/fendermint/vm/interpreter/src/lib.rs +++ b/fendermint/vm/interpreter/src/lib.rs @@ -5,6 +5,7 @@ use async_trait::async_trait; pub mod bytes; pub mod chain; pub mod fvm; +pub mod genesis; pub mod signed; #[cfg(feature = "arb")] From fcdc70fef4fe638d614c2da4d6d4dd2fad162d22 Mon Sep 17 00:00:00 2001 From: cryptoAtwill Date: Mon, 3 Jun 2024 11:02:01 +0800 Subject: [PATCH 02/69] convert fendermint to cometbft genesis --- fendermint/app/options/src/genesis.rs | 3 +++ fendermint/app/src/app.rs | 8 +++++--- fendermint/app/src/cmd/genesis.rs | 4 ++-- fendermint/vm/genesis/src/lib.rs | 1 - fendermint/vm/interpreter/src/genesis.rs | 20 +++++++++++++------- 5 files changed, 23 insertions(+), 13 deletions(-) diff --git a/fendermint/app/options/src/genesis.rs b/fendermint/app/options/src/genesis.rs index 2b8e682a2e..b374824a62 100644 --- a/fendermint/app/options/src/genesis.rs +++ b/fendermint/app/options/src/genesis.rs @@ -133,6 +133,9 @@ pub struct GenesisAddValidatorArgs { #[derive(Args, Debug)] pub struct GenesisIntoTendermintArgs { + /// Sealed genesis file that is the initial app bytes for cometbft + #[arg(long, short)] + pub sealed: PathBuf, /// Output file name for the Tendermint genesis JSON file. #[arg(long, short)] pub out: PathBuf, diff --git a/fendermint/app/src/app.rs b/fendermint/app/src/app.rs index f4a734a4f4..bb8455aef9 100644 --- a/fendermint/app/src/app.rs +++ b/fendermint/app/src/app.rs @@ -25,6 +25,7 @@ use fendermint_vm_interpreter::fvm::state::{ }; use fendermint_vm_interpreter::fvm::store::ReadOnlyBlockstore; use fendermint_vm_interpreter::fvm::{FvmApplyRet, FvmGenesisOutput, PowerUpdates}; +use fendermint_vm_interpreter::genesis::read_genesis_car; use fendermint_vm_interpreter::signed::InvalidSignature; use fendermint_vm_interpreter::{ CheckInterpreter, ExecInterpreter, GenesisInterpreter, ProposalInterpreter, QueryInterpreter, @@ -42,7 +43,6 @@ use serde::{Deserialize, Serialize}; use tendermint::abci::request::CheckTxKind; use tendermint::abci::{request, response}; use tracing::instrument; -use fendermint_vm_interpreter::genesis::read_genesis_car; use crate::events::{NewBlock, ProposalProcessed}; use crate::AppExitCode; @@ -457,8 +457,10 @@ where // Make it easy to spot any discrepancies between nodes. tracing::info!(genesis_hash = genesis_hash.to_string(), "genesis"); - let (validators, state_params) = read_genesis_car(&genesis_bytes, &self.state_store).await?; - let validators = to_validator_updates(validators).context("failed to convert validators")?; + let (validators, state_params) = + read_genesis_car(&genesis_bytes, &self.state_store).await?; + let validators = + to_validator_updates(validators).context("failed to convert validators")?; // Let's pretend that the genesis state is that of a fictive block at height 0. // The record will be stored under height 1, and the record after the application diff --git a/fendermint/app/src/cmd/genesis.rs b/fendermint/app/src/cmd/genesis.rs index d188ba6f55..c6e7053704 100644 --- a/fendermint/app/src/cmd/genesis.rs +++ b/fendermint/app/src/cmd/genesis.rs @@ -215,7 +215,7 @@ fn set_eam_permissions( fn into_tendermint(genesis_file: &PathBuf, args: &GenesisIntoTendermintArgs) -> anyhow::Result<()> { let genesis = read_genesis(genesis_file)?; - let genesis_json = serde_json::to_value(&genesis)?; + let app_state = hex::encode(std::fs::read(&args.sealed)?); let chain_id: u64 = chainid::from_str_hashed(&genesis.chain_name)?.into(); let chain_id = chain_id.to_string(); @@ -250,7 +250,7 @@ fn into_tendermint(genesis_file: &PathBuf, args: &GenesisIntoTendermintArgs) -> // Hopefully leaving this empty will skip validation, // otherwise we have to run the genesis in memory here and now. app_hash: tendermint::AppHash::default(), - app_state: genesis_json, + app_state: serde_json::Value::String(app_state), }; let tmg_json = serde_json::to_string_pretty(&tmg)?; std::fs::write(&args.out, tmg_json)?; diff --git a/fendermint/vm/genesis/src/lib.rs b/fendermint/vm/genesis/src/lib.rs index ab2c1d1361..ae87b4a1db 100644 --- a/fendermint/vm/genesis/src/lib.rs +++ b/fendermint/vm/genesis/src/lib.rs @@ -7,7 +7,6 @@ use anyhow::anyhow; use fvm_shared::bigint::{BigInt, Integer}; use serde::{Deserialize, Serialize}; use serde_with::serde_as; -use std::path::PathBuf; use fendermint_actor_eam::PermissionModeParams; use fvm_shared::version::NetworkVersion; diff --git a/fendermint/vm/interpreter/src/genesis.rs b/fendermint/vm/interpreter/src/genesis.rs index 5b804639d0..0e8cc9ad2a 100644 --- a/fendermint/vm/interpreter/src/genesis.rs +++ b/fendermint/vm/interpreter/src/genesis.rs @@ -42,13 +42,13 @@ use tokio_util::compat::TokioAsyncWriteCompatExt; /// The sealed genesis state metadata #[serde_as] #[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] -pub struct GenesisMetadata { +struct GenesisMetadata { pub state_params: FvmStateParams, pub validators: Vec>, } impl GenesisMetadata { - fn new(state_root: Cid, out: FvmGenesisOutput) -> GenesisMetadata { + fn new(state_root: Cid, out: GenesisOutput) -> GenesisMetadata { let state_params = FvmStateParams { state_root, timestamp: out.timestamp, @@ -67,7 +67,10 @@ impl GenesisMetadata { } } -pub async fn read_genesis_car(bytes: &[u8], store: &DB) -> anyhow::Result<(Vec>, FvmStateParams)> { +pub async fn read_genesis_car( + bytes: &[u8], + store: &DB, +) -> anyhow::Result<(Vec>, FvmStateParams)> { let car_reader = CarReader::new(bytes).await?; let roots = car_reader.read_into(store).await?; @@ -88,7 +91,7 @@ pub async fn read_genesis_car(bytes: &[u /// The output of genesis creation #[derive(Debug, Clone, PartialEq, Eq)] -pub struct FvmGenesisOutput { +pub struct GenesisOutput { pub chain_id: ChainID, pub timestamp: Timestamp, pub network_version: NetworkVersion, @@ -134,7 +137,7 @@ impl GenesisCreator { async fn write_car( &self, state_root: Cid, - out: FvmGenesisOutput, + out: GenesisOutput, store: MemoryBlockstore, ) -> anyhow::Result<()> { let file = tokio::fs::File::create(&self.sealed_out_path).await?; @@ -143,6 +146,7 @@ impl GenesisCreator { let streamer = StateTreeStreamer::new(state_root, store); let (metadata_cid, metadata_bytes) = derive_cid(&metadata)?; + tracing::info!("generated genesis metadata header cid: {}", metadata_cid); // create the target car header with the metadata cid as the only root let car = CarHeader::new(vec![metadata_cid], 1); @@ -158,6 +162,8 @@ impl GenesisCreator { write_task.await??; + tracing::info!("written sealed genesis state to file"); + Ok(()) } @@ -192,7 +198,7 @@ impl GenesisCreator { &self, state: &mut FvmGenesisState, genesis: Genesis, - ) -> anyhow::Result { + ) -> anyhow::Result { // NOTE: We could consider adding the chain ID to the interpreter // and rejecting genesis if it doesn't match the expectation, // but the Tendermint genesis file also has this field, and @@ -210,7 +216,7 @@ impl GenesisCreator { // Currently we just pass them back as they are, but later we should // store them in the IPC actors; or in case of a snapshot restore them // from the state. - let out = FvmGenesisOutput { + let out = GenesisOutput { chain_id, timestamp: genesis.timestamp, network_version: genesis.network_version, From 3d8f00f29c5bb15d4030ac213e16cc53a2818788 Mon Sep 17 00:00:00 2001 From: cryptoAtwill Date: Mon, 3 Jun 2024 11:15:40 +0800 Subject: [PATCH 03/69] remove deprecated fields --- fendermint/app/settings/src/lib.rs | 12 +----------- fendermint/app/src/app.rs | 17 ----------------- fendermint/app/src/cmd/run.rs | 2 -- 3 files changed, 1 insertion(+), 30 deletions(-) diff --git a/fendermint/app/settings/src/lib.rs b/fendermint/app/settings/src/lib.rs index e83f53c05c..e05a1866e4 100644 --- a/fendermint/app/settings/src/lib.rs +++ b/fendermint/app/settings/src/lib.rs @@ -262,10 +262,6 @@ pub struct Settings { snapshots_dir: PathBuf, /// Solidity contracts. contracts_dir: PathBuf, - /// Builtin-actors CAR file. - builtin_actors_bundle: PathBuf, - /// Custom actors CAR file. - custom_actors_bundle: PathBuf, /// Where to reach CometBFT for queries or broadcasting transactions. tendermint_rpc_url: Url, @@ -289,13 +285,7 @@ pub struct Settings { } impl Settings { - home_relative!( - data_dir, - snapshots_dir, - contracts_dir, - builtin_actors_bundle, - custom_actors_bundle - ); + home_relative!(data_dir, snapshots_dir, contracts_dir); /// Load the default configuration from a directory, /// then potential overrides specific to the run mode, diff --git a/fendermint/app/src/app.rs b/fendermint/app/src/app.rs index bb8455aef9..c45f402713 100644 --- a/fendermint/app/src/app.rs +++ b/fendermint/app/src/app.rs @@ -1,7 +1,6 @@ // Copyright 2022-2024 Protocol Labs // SPDX-License-Identifier: Apache-2.0, MIT use std::future::Future; -use std::path::PathBuf; use std::sync::Arc; use anyhow::{anyhow, Context, Result}; @@ -107,12 +106,6 @@ pub struct AppConfig { pub state_hist_namespace: S::Namespace, /// Size of state history to keep; 0 means unlimited. pub state_hist_size: u64, - /// Path to the Wasm bundle. - /// - /// Only loaded once during genesis; later comes from the [`StateTree`]. - pub builtin_actors_bundle: PathBuf, - /// Path to the custom actor WASM bundle. - pub custom_actors_bundle: PathBuf, /// Block height where we should gracefully stop the node pub halt_height: i64, } @@ -134,14 +127,6 @@ where state_store: Arc, /// Wasm engine cache. multi_engine: Arc, - /// Path to the Wasm bundle. - /// - /// Only loaded once during genesis; later comes from the [`StateTree`]. - #[deprecated] - builtin_actors_bundle: PathBuf, - /// Path to the custom actor WASM bundle. - #[deprecated] - custom_actors_bundle: PathBuf, /// Block height where we should gracefully stop the node halt_height: i64, /// Namespace to store app state. @@ -195,8 +180,6 @@ where db: Arc::new(db), state_store: Arc::new(state_store), multi_engine: Arc::new(MultiEngine::new(1)), - builtin_actors_bundle: config.builtin_actors_bundle, - custom_actors_bundle: config.custom_actors_bundle, halt_height: config.halt_height, namespace: config.app_namespace, state_hist: KVCollection::new(config.state_hist_namespace), diff --git a/fendermint/app/src/cmd/run.rs b/fendermint/app/src/cmd/run.rs index 28f8b9a973..1fcc1d1ab1 100644 --- a/fendermint/app/src/cmd/run.rs +++ b/fendermint/app/src/cmd/run.rs @@ -290,8 +290,6 @@ async fn run(settings: Settings) -> anyhow::Result<()> { app_namespace: ns.app, state_hist_namespace: ns.state_hist, state_hist_size: settings.db.state_hist_size, - builtin_actors_bundle: settings.builtin_actors_bundle(), - custom_actors_bundle: settings.custom_actors_bundle(), halt_height: settings.halt_height, }, db, From 8a8efd6023f1f0a6b428221905b18af37818b4ee Mon Sep 17 00:00:00 2001 From: cryptoAtwill Date: Mon, 3 Jun 2024 14:56:00 +0800 Subject: [PATCH 04/69] fix cursor --- fendermint/app/src/app.rs | 2 +- fendermint/vm/interpreter/src/genesis.rs | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/fendermint/app/src/app.rs b/fendermint/app/src/app.rs index c45f402713..4c07781e1b 100644 --- a/fendermint/app/src/app.rs +++ b/fendermint/app/src/app.rs @@ -441,7 +441,7 @@ where tracing::info!(genesis_hash = genesis_hash.to_string(), "genesis"); let (validators, state_params) = - read_genesis_car(&genesis_bytes, &self.state_store).await?; + read_genesis_car(genesis_bytes, &self.state_store).await?; let validators = to_validator_updates(validators).context("failed to convert validators")?; diff --git a/fendermint/vm/interpreter/src/genesis.rs b/fendermint/vm/interpreter/src/genesis.rs index 0e8cc9ad2a..b0c5a54b52 100644 --- a/fendermint/vm/interpreter/src/genesis.rs +++ b/fendermint/vm/interpreter/src/genesis.rs @@ -11,6 +11,7 @@ use anyhow::{anyhow, Context}; use cid::Cid; use ethers::abi::Tokenize; use ethers::core::types as et; +use futures_util::io::Cursor; use fendermint_actor_eam::PermissionModeParams; use fendermint_eth_hardhat::{Hardhat, FQN}; use fendermint_vm_actor_interface::diamond::{EthContract, EthContractMap}; @@ -23,7 +24,7 @@ use fendermint_vm_core::{chainid, Timestamp}; use fendermint_vm_genesis::{ActorMeta, Genesis, Power, PowerScale, Validator}; use fvm::engine::MultiEngine; use fvm_ipld_blockstore::Blockstore; -use fvm_ipld_car::{CarHeader, CarReader}; +use fvm_ipld_car::{CarHeader, load_car}; use fvm_ipld_encoding::CborStore; use fvm_shared::chainid::ChainID; use fvm_shared::econ::TokenAmount; @@ -68,12 +69,10 @@ impl GenesisMetadata { } pub async fn read_genesis_car( - bytes: &[u8], + bytes: Vec, store: &DB, ) -> anyhow::Result<(Vec>, FvmStateParams)> { - let car_reader = CarReader::new(bytes).await?; - - let roots = car_reader.read_into(store).await?; + let roots = load_car(store, Cursor::new(&bytes)).await?; if roots.len() != 1 { return Err(anyhow!("invalid genesis car, should have 1 root cid")); From 3890de409ad7f35b73f3ac8c5812392aca317f2f Mon Sep 17 00:00:00 2001 From: cryptoAtwill Date: Mon, 3 Jun 2024 15:47:42 +0800 Subject: [PATCH 05/69] fix genesis app bytes --- fendermint/app/src/app.rs | 12 +++++++++--- fendermint/vm/interpreter/src/genesis.rs | 4 ++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/fendermint/app/src/app.rs b/fendermint/app/src/app.rs index 4c07781e1b..a89cedb8cc 100644 --- a/fendermint/app/src/app.rs +++ b/fendermint/app/src/app.rs @@ -373,6 +373,13 @@ where // It's really the empty state tree that would be the best indicator. !(height == 0 && params.timestamp.0 == 0 && params.network_version == NetworkVersion::V0) } + + fn parse_genesis_app_bytes(bytes: &[u8]) -> Result> { + match serde_json::from_slice(bytes)? { + serde_json::Value::String(s) => Ok(hex::decode(s)?), + _ => Err(anyhow!("invalid app state json")), + } + } } // NOTE: The `Application` interface doesn't allow failures at the moment. The protobuf @@ -433,15 +440,14 @@ where /// Called once upon genesis. async fn init_chain(&self, request: request::InitChain) -> AbciResult { - let genesis_bytes = request.app_state_bytes.to_vec(); + let genesis_bytes = Self::parse_genesis_app_bytes(&request.app_state_bytes)?; let genesis_hash = fendermint_vm_message::cid(&genesis_bytes).context("failed to compute genesis CID")?; // Make it easy to spot any discrepancies between nodes. tracing::info!(genesis_hash = genesis_hash.to_string(), "genesis"); - let (validators, state_params) = - read_genesis_car(genesis_bytes, &self.state_store).await?; + let (validators, state_params) = read_genesis_car(genesis_bytes, &self.state_store).await?; let validators = to_validator_updates(validators).context("failed to convert validators")?; diff --git a/fendermint/vm/interpreter/src/genesis.rs b/fendermint/vm/interpreter/src/genesis.rs index b0c5a54b52..7ee3edd4fd 100644 --- a/fendermint/vm/interpreter/src/genesis.rs +++ b/fendermint/vm/interpreter/src/genesis.rs @@ -11,7 +11,6 @@ use anyhow::{anyhow, Context}; use cid::Cid; use ethers::abi::Tokenize; use ethers::core::types as et; -use futures_util::io::Cursor; use fendermint_actor_eam::PermissionModeParams; use fendermint_eth_hardhat::{Hardhat, FQN}; use fendermint_vm_actor_interface::diamond::{EthContract, EthContractMap}; @@ -22,9 +21,10 @@ use fendermint_vm_actor_interface::{ }; use fendermint_vm_core::{chainid, Timestamp}; use fendermint_vm_genesis::{ActorMeta, Genesis, Power, PowerScale, Validator}; +use futures_util::io::Cursor; use fvm::engine::MultiEngine; use fvm_ipld_blockstore::Blockstore; -use fvm_ipld_car::{CarHeader, load_car}; +use fvm_ipld_car::{load_car, CarHeader}; use fvm_ipld_encoding::CborStore; use fvm_shared::chainid::ChainID; use fvm_shared::econ::TokenAmount; From 14e6deaaeb90e00d0ddd63523d7202430adc47e5 Mon Sep 17 00:00:00 2001 From: cryptoAtwill Date: Mon, 3 Jun 2024 22:04:08 +0800 Subject: [PATCH 06/69] fmt --- fendermint/app/options/src/genesis.rs | 4 ++-- fendermint/app/src/cmd/genesis.rs | 2 +- fendermint/vm/interpreter/src/genesis.rs | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/fendermint/app/options/src/genesis.rs b/fendermint/app/options/src/genesis.rs index b374824a62..8f4fc0e1f8 100644 --- a/fendermint/app/options/src/genesis.rs +++ b/fendermint/app/options/src/genesis.rs @@ -169,9 +169,9 @@ pub struct SealGenesisArgs { #[arg(long, short)] pub ipc_artifacts_path: PathBuf, - /// The sealed, i.e. finalized genesis state CAR file dump path + /// The sealed genesis state output path, i.e. finalized genesis state CAR file dump path #[arg(long, short)] - pub sealed_genesis_path: PathBuf, + pub output_path: PathBuf, } #[derive(Args, Debug, Clone)] diff --git a/fendermint/app/src/cmd/genesis.rs b/fendermint/app/src/cmd/genesis.rs index c6e7053704..ee3fcf1502 100644 --- a/fendermint/app/src/cmd/genesis.rs +++ b/fendermint/app/src/cmd/genesis.rs @@ -289,7 +289,7 @@ async fn seal_state(genesis_file: &PathBuf, args: &SealGenesisArgs) -> anyhow::R args.builtin_actors_path.clone(), args.custom_actors_path.clone(), args.ipc_artifacts_path.clone(), - args.sealed_genesis_path.clone(), + args.output_path.clone(), ); genesis_creator.create(genesis).await diff --git a/fendermint/vm/interpreter/src/genesis.rs b/fendermint/vm/interpreter/src/genesis.rs index 7ee3edd4fd..a3bc1c54c3 100644 --- a/fendermint/vm/interpreter/src/genesis.rs +++ b/fendermint/vm/interpreter/src/genesis.rs @@ -125,6 +125,7 @@ impl GenesisCreator { sealed_out_path, } } + /// Initialize actor states from the Genesis parameters pub async fn create(&self, genesis: Genesis) -> anyhow::Result<()> { let mut state = self.init_state().await?; From 4dc0482b04e3d73201c85fa48425d877de4eac61 Mon Sep 17 00:00:00 2001 From: cryptoAtwill Date: Tue, 4 Jun 2024 16:43:46 +0800 Subject: [PATCH 07/69] minor changes --- fendermint/app/options/src/genesis.rs | 4 ++-- fendermint/app/src/cmd/genesis.rs | 2 +- fendermint/vm/interpreter/src/genesis.rs | 30 +++++++++++++----------- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/fendermint/app/options/src/genesis.rs b/fendermint/app/options/src/genesis.rs index 8f4fc0e1f8..7a115c1976 100644 --- a/fendermint/app/options/src/genesis.rs +++ b/fendermint/app/options/src/genesis.rs @@ -164,10 +164,10 @@ pub struct SealGenesisArgs { #[arg(long, short)] pub custom_actors_path: PathBuf, - /// The ipc artifacts output path. If you are using ipc-monorepo, it should be the `out` folder + /// The solidity artifacts output path. If you are using ipc-monorepo, it should be the `out` folder /// of `make build` #[arg(long, short)] - pub ipc_artifacts_path: PathBuf, + pub artifacts_path: PathBuf, /// The sealed genesis state output path, i.e. finalized genesis state CAR file dump path #[arg(long, short)] diff --git a/fendermint/app/src/cmd/genesis.rs b/fendermint/app/src/cmd/genesis.rs index ee3fcf1502..a512cedb33 100644 --- a/fendermint/app/src/cmd/genesis.rs +++ b/fendermint/app/src/cmd/genesis.rs @@ -288,7 +288,7 @@ async fn seal_state(genesis_file: &PathBuf, args: &SealGenesisArgs) -> anyhow::R let genesis_creator = GenesisCreator::new( args.builtin_actors_path.clone(), args.custom_actors_path.clone(), - args.ipc_artifacts_path.clone(), + args.artifacts_path.clone(), args.output_path.clone(), ); diff --git a/fendermint/vm/interpreter/src/genesis.rs b/fendermint/vm/interpreter/src/genesis.rs index a3bc1c54c3..49b1773418 100644 --- a/fendermint/vm/interpreter/src/genesis.rs +++ b/fendermint/vm/interpreter/src/genesis.rs @@ -115,11 +115,11 @@ impl GenesisCreator { pub fn new( builtin_actors_path: PathBuf, custom_actors_path: PathBuf, - ipc_artifacts_path: PathBuf, + artifacts_path: PathBuf, sealed_out_path: PathBuf, ) -> Self { Self { - hardhat: Hardhat::new(ipc_artifacts_path), + hardhat: Hardhat::new(artifacts_path), builtin_actors_path, custom_actors_path, sealed_out_path, @@ -229,19 +229,17 @@ impl GenesisCreator { // STAGE 0: Declare the built-in EVM contracts we'll have to deploy. // Pre-defined IDs for top-level Ethereum contracts. - let mut eth_builtin_ids = BTreeSet::new(); - let mut eth_root_contracts = Vec::new(); - let mut eth_contracts = EthContractMap::default(); + let mut all_contracts = Vec::new(); + let mut top_level_contracts = EthContractMap::default(); // Only allocate IDs if the contracts are deployed. if genesis.ipc.is_some() { - eth_contracts.extend(IPC_CONTRACTS.clone()); + top_level_contracts.extend(IPC_CONTRACTS.clone()); } - eth_builtin_ids.extend(eth_contracts.values().map(|c| c.actor_id)); - eth_root_contracts.extend(eth_contracts.keys()); - eth_root_contracts.extend( - eth_contracts + all_contracts.extend(top_level_contracts.keys()); + all_contracts.extend( + top_level_contracts .values() .flat_map(|c| c.facets.iter().map(|f| f.name)), ); @@ -249,7 +247,7 @@ impl GenesisCreator { let mut eth_libs = self .hardhat .dependencies( - ð_root_contracts + &all_contracts .iter() .map(|n| (contract_src(n), *n)) .collect::>(), @@ -257,7 +255,7 @@ impl GenesisCreator { .context("failed to collect EVM contract dependencies")?; // Only keep library dependencies, not contracts with constructors. - eth_libs.retain(|(_, d)| !eth_contracts.contains_key(d.as_str())); + eth_libs.retain(|(_, d)| !top_level_contracts.contains_key(d.as_str())); // STAGE 1: First we initialize native built-in actors. @@ -279,7 +277,10 @@ impl GenesisCreator { state.store(), genesis.chain_name.clone(), &genesis.accounts, - ð_builtin_ids, + &top_level_contracts + .values() + .map(|c| c.actor_id) + .collect::>(), eth_libs.len() as u64, ) .context("failed to create init state")?; @@ -415,7 +416,8 @@ impl GenesisCreator { ) .context("failed to init exec state")?; - let mut deployer = ContractDeployer::::new(&self.hardhat, ð_contracts); + let mut deployer = + ContractDeployer::::new(&self.hardhat, &top_level_contracts); // Deploy Ethereum libraries. for (lib_src, lib_name) in eth_libs { From 02b183f0e8d286169593e574e1bf1adbc8cfdb6c Mon Sep 17 00:00:00 2001 From: cryptoAtwill <108330426+cryptoAtwill@users.noreply.github.com> Date: Wed, 5 Jun 2024 15:20:24 +0800 Subject: [PATCH 08/69] Fix cicd (#1023) --- fendermint/testing/smoke-test/scripts/init.sh | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/fendermint/testing/smoke-test/scripts/init.sh b/fendermint/testing/smoke-test/scripts/init.sh index 1ccbcc77dc..c3fa8d63a5 100755 --- a/fendermint/testing/smoke-test/scripts/init.sh +++ b/fendermint/testing/smoke-test/scripts/init.sh @@ -7,6 +7,7 @@ set -e KEYS_DIR=/data/keys CMT_DIR=/data/${NODE_NAME}/cometbft GENESIS_FILE=/data/genesis.json +SEALED_GENESIS_FILE=/data/sealed.car # Create a genesis file fendermint \ @@ -63,10 +64,20 @@ fendermint \ --msg-fee 10 \ --majority-percentage 66 +# Seal the genesis state +fendermint \ + genesis --genesis-file $GENESIS_FILE \ + ipc \ + seal-state \ + --builtin-actors-path /fendermint/bundle.car \ + --custom-actors-path /fendermint/custom_actors_bundle.car \ + --artifacts-path /fendermint/contracts \ + --output-path "${SEALED_GENESIS_FILE}" + # Convert FM genesis to CMT fendermint \ genesis --genesis-file $GENESIS_FILE \ - into-tendermint --out $CMT_DIR/config/genesis.json + into-tendermint --out $CMT_DIR/config/genesis.json --sealed "${SEALED_GENESIS_FILE}" # Convert FM validator key to CMT fendermint \ From d88face4499a23054c8757f9126d5b8dced061b1 Mon Sep 17 00:00:00 2001 From: cryptoAtwill <108330426+cryptoAtwill@users.noreply.github.com> Date: Thu, 6 Jun 2024 21:20:11 +0800 Subject: [PATCH 09/69] Fix cicd (#1025) --- fendermint/testing/graph-test/scripts/init.sh | 13 ++++++++++++- fendermint/testing/snapshot-test/scripts/init.sh | 13 ++++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/fendermint/testing/graph-test/scripts/init.sh b/fendermint/testing/graph-test/scripts/init.sh index 461ccfa3e1..8fc376fb1d 100755 --- a/fendermint/testing/graph-test/scripts/init.sh +++ b/fendermint/testing/graph-test/scripts/init.sh @@ -7,6 +7,7 @@ set -e KEYS_DIR=/data/keys CMT_DIR=/data/${NODE_NAME}/cometbft GENESIS_FILE=/data/genesis.json +SEALED_GENESIS_FILE=/data/sealed.car # Create a genesis file fendermint \ @@ -47,10 +48,20 @@ fendermint \ genesis --genesis-file $GENESIS_FILE \ add-validator --public-key $KEYS_DIR/$VALIDATOR_NAME.pk --power 1 +# Seal the genesis state +fendermint \ + genesis --genesis-file $GENESIS_FILE \ + ipc \ + seal-state \ + --builtin-actors-path /fendermint/bundle.car \ + --custom-actors-path /fendermint/custom_actors_bundle.car \ + --artifacts-path /fendermint/contracts \ + --output-path "${SEALED_GENESIS_FILE}" + # Convert FM genesis to CMT fendermint \ genesis --genesis-file $GENESIS_FILE \ - into-tendermint --out $CMT_DIR/config/genesis.json + into-tendermint --out $CMT_DIR/config/genesis.json --sealed "${SEALED_GENESIS_FILE}" # Copy the default validator key cp $KEYS_DIR/$VALIDATOR_NAME.priv_validator_key.json \ diff --git a/fendermint/testing/snapshot-test/scripts/init.sh b/fendermint/testing/snapshot-test/scripts/init.sh index 011bbfa75d..9e75058d99 100755 --- a/fendermint/testing/snapshot-test/scripts/init.sh +++ b/fendermint/testing/snapshot-test/scripts/init.sh @@ -7,6 +7,7 @@ set -e KEYS_DIR=/data/keys CMT_DIR=/data/${NODE_NAME}/cometbft GENESIS_FILE=/data/genesis.json +SEALED_GENESIS_FILE=/data/sealed.car # Create a genesis file fendermint \ @@ -42,10 +43,20 @@ fendermint \ genesis --genesis-file $GENESIS_FILE \ add-validator --public-key $KEYS_DIR/$VALIDATOR_NAME.pk --power 1 +# Seal the genesis state +fendermint \ + genesis --genesis-file $GENESIS_FILE \ + ipc \ + seal-state \ + --builtin-actors-path /fendermint/bundle.car \ + --custom-actors-path /fendermint/custom_actors_bundle.car \ + --artifacts-path /fendermint/contracts \ + --output-path "${SEALED_GENESIS_FILE}" + # Convert FM genesis to CMT fendermint \ genesis --genesis-file $GENESIS_FILE \ - into-tendermint --out $CMT_DIR/config/genesis.json + into-tendermint --out $CMT_DIR/config/genesis.json --sealed "${SEALED_GENESIS_FILE}" # Copy the default validator key cp $KEYS_DIR/$VALIDATOR_NAME.priv_validator_key.json \ From 2a4d428eec495dee3a6a8f00cdff32a58c3e68d6 Mon Sep 17 00:00:00 2001 From: cryptoAtwill <108330426+cryptoAtwill@users.noreply.github.com> Date: Mon, 10 Jun 2024 16:38:56 +0800 Subject: [PATCH 10/69] Fix cicd (#1027) Co-authored-by: Eric Tu <6364934+ec2@users.noreply.github.com> Co-authored-by: Mikers Co-authored-by: raulk Co-authored-by: Shashank Trivedi <100513286+lordshashank@users.noreply.github.com> --- .github/workflows/contracts-prettier.yaml | 4 +- .github/workflows/contracts-test.yaml | 6 +- Cargo.lock | 2 + contracts/.gitignore | 1 + contracts/package-lock.json | 61 ++++++++++++++++--- contracts/package.json | 29 ++++++--- contracts/remappings.json | 6 +- contracts/remappings.txt | 6 +- contracts/scripts/npm-postpack.js | 11 ++++ contracts/scripts/npm-prepack.js | 16 +++++ contracts/sdk/IpcContractUpgradeable.sol | 2 +- contracts/src/gateway/GatewayManagerFacet.sol | 2 +- .../src/gateway/GatewayMessengerFacet.sol | 2 +- .../gateway/router/TopDownFinalityFacet.sol | 2 +- .../src/gateway/router/XnetMessagingFacet.sol | 2 +- contracts/src/lib/AccountHelper.sol | 2 +- contracts/src/lib/CrossMsgHelper.sol | 2 +- contracts/src/lib/LibGateway.sol | 2 +- contracts/src/lib/LibGatewayActorStorage.sol | 2 +- contracts/test/IntegrationTestBase.sol | 2 +- .../test/integration/GatewayDiamond.t.sol | 2 +- .../integration/GatewayDiamondToken.t.sol | 2 +- .../test/integration/L2GatewayDiamond.t.sol | 2 +- contracts/test/integration/MultiSubnet.t.sol | 2 +- .../test/integration/SubnetActorDiamond.t.sol | 2 +- contracts/test/sdk/IpcContract.t.sol | 2 +- contracts/test/unit/AccountHelper.t.sol | 2 +- fendermint/app/src/app.rs | 2 + fendermint/testing/graph-test/Makefile.toml | 2 +- .../subgraph/graph-node/docker-compose.yaml | 2 + fendermint/testing/materializer/Cargo.toml | 1 + .../testing/materializer/src/docker/node.rs | 18 +++++- .../testing/materializer/tests/docker.rs | 2 +- .../tests/docker_tests/standalone.rs | 4 ++ .../vm/interpreter/src/fvm/store/memory.rs | 10 +-- fendermint/vm/interpreter/src/genesis.rs | 2 + ipc/cli/Cargo.toml | 1 + ipc/cli/src/main.rs | 8 ++- specs/supply-sources.md | 45 ++++++++++++++ 39 files changed, 220 insertions(+), 53 deletions(-) create mode 100644 contracts/scripts/npm-postpack.js create mode 100644 contracts/scripts/npm-prepack.js create mode 100644 specs/supply-sources.md diff --git a/.github/workflows/contracts-prettier.yaml b/.github/workflows/contracts-prettier.yaml index 1dd4e9bb58..b105c791c7 100644 --- a/.github/workflows/contracts-prettier.yaml +++ b/.github/workflows/contracts-prettier.yaml @@ -15,11 +15,11 @@ jobs: steps: - uses: actions/checkout@v4 with: - ref: ${{ github.head_ref }} + ref: ${{ github.ref }} - name: Set up node.js uses: actions/setup-node@v4 with: - node-version: '21' + node-version: "21" - name: Run formatter run: cd contracts && make fmt - name: Check diff clean diff --git a/.github/workflows/contracts-test.yaml b/.github/workflows/contracts-test.yaml index 6d52bc31a4..4602b13c40 100644 --- a/.github/workflows/contracts-test.yaml +++ b/.github/workflows/contracts-test.yaml @@ -12,19 +12,19 @@ jobs: - name: Checkout Repository uses: actions/checkout@v3 with: - ref: ${{ github.event.pull_request.head.ref }} + ref: ${{ github.ref }} submodules: recursive - name: Install python uses: actions/setup-python@v4 with: - python-version: '3.10' + python-version: "3.10" - name: Install abi run: pip install eth_abi - name: Install lcov and genhtml - run: sudo apt-get update && sudo apt-get -y install lcov + run: sudo apt-get update && sudo apt-get -y install lcov - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 diff --git a/Cargo.lock b/Cargo.lock index e372d6f959..540f6c41b4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3088,6 +3088,7 @@ dependencies = [ "fendermint_vm_core", "fendermint_vm_encoding", "fendermint_vm_genesis", + "fendermint_vm_interpreter", "fendermint_vm_message", "futures", "fvm_shared", @@ -5039,6 +5040,7 @@ dependencies = [ "tokio", "tokio-tungstenite 0.18.0", "toml 0.7.8", + "tracing-subscriber", "url", "zeroize", ] diff --git a/contracts/.gitignore b/contracts/.gitignore index 84ef71d4eb..80a64e5f16 100644 --- a/contracts/.gitignore +++ b/contracts/.gitignore @@ -7,6 +7,7 @@ crytic-export/ broadcast/ out/ binding/src +contracts/ node_modules diff --git a/contracts/package-lock.json b/contracts/package-lock.json index 54ab8eb06c..4bd21db3e2 100644 --- a/contracts/package-lock.json +++ b/contracts/package-lock.json @@ -1,12 +1,12 @@ { - "name": "ipc-solidity-actors", - "version": "0.0.1", + "name": "@consensus-shipyard/ipc-contracts", + "version": "1.0.0-alpha.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "ipc-solidity-actors", - "version": "0.0.1", + "name": "@consensus-shipyard/ipc-contracts", + "version": "1.0.0-alpha.0", "license": "MIT OR Apache-2.0", "dependencies": { "@solidity-parser/parser": "^0.16.2", @@ -18,10 +18,14 @@ "devDependencies": { "@nomicfoundation/hardhat-foundry": "^1.0.1", "@nomiclabs/hardhat-ethers": "^2.2.3", + "@openzeppelin/contracts": "5.0.1", "@typechain/ethers-v5": "^11.1.2", "@typechain/hardhat": "^7.0.0", "dotenv": "^16.0.1", + "elliptic-curve-solidity": "github:witnet/elliptic-curve-solidity#3475478", "ethers": "^5.7.0", + "fevmate": "github:wadealexc/fevmate#6a80e98", + "fs-extra": "^11.2.0", "hardhat-contract-sizer": "^2.6.1", "hardhat-deploy": "^0.11.14", "hardhat-deploy-ethers": "^0.3.0-beta.13", @@ -1452,6 +1456,12 @@ "hardhat": "^2.0.0" } }, + "node_modules/@openzeppelin/contracts": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-5.0.1.tgz", + "integrity": "sha512-yQJaT5HDp9hYOOp4jTYxMsR02gdFZFXhewX5HW9Jo4fsqSVqqyIO/xTHdWDaKX5a3pv1txmf076Lziz+sO7L1w==", + "dev": true + }, "node_modules/@scure/base": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.3.tgz", @@ -1652,6 +1662,21 @@ "typechain": "^8.2.0" } }, + "node_modules/@typechain/hardhat/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@types/bn.js": { "version": "5.1.5", "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.5.tgz", @@ -2681,6 +2706,11 @@ "minimalistic-crypto-utils": "^1.0.1" } }, + "node_modules/elliptic-curve-solidity": { + "version": "0.2.5", + "resolved": "git+ssh://git@github.com/witnet/elliptic-curve-solidity.git#347547890840fd501809dfe0b855206407136ec0", + "dev": true + }, "node_modules/elliptic/node_modules/bn.js": { "version": "4.12.0", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", @@ -2932,6 +2962,20 @@ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true }, + "node_modules/fevmate": { + "version": "1.0.3", + "resolved": "git+ssh://git@github.com/wadealexc/fevmate.git#6a80e989847fd563df21360176cbfd5d08aaabbb", + "dev": true, + "dependencies": { + "@openzeppelin/contracts": "^4.8.1" + } + }, + "node_modules/fevmate/node_modules/@openzeppelin/contracts": { + "version": "4.9.6", + "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.9.6.tgz", + "integrity": "sha512-xSmezSupL+y9VkHZJGDoCBpmnB2ogM13ccaYDWqJTfS3dbuHkgjuwDFUmaFauBCboQMGB/S5UqUl2y54X99BmA==", + "dev": true + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -3022,18 +3066,17 @@ "integrity": "sha512-H5KQDspykdHuztLTg+ajGN0Z2qUjcEf3Ybxc6hLt0k7/zPkn29XnKnxlBPyW2XIddWrGaJBzBl4VLYOtk39yZg==" }, "node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", "dev": true, "dependencies": { - "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" }, "engines": { - "node": ">=10" + "node": ">=14.14" } }, "node_modules/fs.realpath": { diff --git a/contracts/package.json b/contracts/package.json index ee45a946f9..4b2b0c4971 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -1,32 +1,47 @@ { - "name": "ipc-solidity-actors", - "version": "0.0.1", + "name": "@consensus-shipyard/ipc-contracts", + "version": "1.0.0-alpha.0", "description": "This repository includes the reference implementation of all the actors (i.e. smart contracts) responsible for the operation of the IPC (i.e. Inter-Planetary Consensus) protocol. These actors are written in Solidity and target FileCoin’s FEVM.", - "main": "index.js", + "author": "ConsensusLab, Protocol Labs, Filecoin Core Devs, Limechain", "directories": { "lib": "lib", "test": "test" }, + "files": [ + "contracts", + "sdk", + "README.md", + "LICENSE-APACHE", + "LICENSE-MIT" + ], "scripts": { + "prepack": "node scripts/npm-prepack.js", + "postpack": "node scripts/npm-postpack.js", "test": "echo \"Error: no test specified\" && exit 1" }, "repository": { "type": "git", - "url": "git+https://github.com/consensus-shipyard/ipc-solidity-actors.git" + "url": "git+https://github.com/consensus-shipyard/ipc.git" + }, + "publishConfig": { + "registry": "https://npm.pkg.github.com" }, - "author": "Limechain team, ConsensusLab, Protocol Labs, Filecoin Core Devs", "license": "MIT OR Apache-2.0", "bugs": { - "url": "https://github.com/consensus-shipyard/ipc-solidity-actors/issues" + "url": "https://github.com/consensus-shipyard/ipc/issues" }, - "homepage": "https://github.com/consensus-shipyard/ipc-solidity-actors/", + "homepage": "https://github.com/consensus-shipyard/ipc/", "devDependencies": { "@nomicfoundation/hardhat-foundry": "^1.0.1", "@nomiclabs/hardhat-ethers": "^2.2.3", + "@openzeppelin/contracts": "5.0.1", "@typechain/ethers-v5": "^11.1.2", "@typechain/hardhat": "^7.0.0", "dotenv": "^16.0.1", + "elliptic-curve-solidity": "github:witnet/elliptic-curve-solidity#3475478", "ethers": "^5.7.0", + "fevmate": "github:wadealexc/fevmate#6a80e98", + "fs-extra": "^11.2.0", "hardhat-contract-sizer": "^2.6.1", "hardhat-deploy": "^0.11.14", "hardhat-deploy-ethers": "^0.3.0-beta.13", diff --git a/contracts/remappings.json b/contracts/remappings.json index dcad5f3e6e..00b36a5a81 100644 --- a/contracts/remappings.json +++ b/contracts/remappings.json @@ -3,8 +3,8 @@ "ds-test/=lib/forge-std/lib/ds-test/src/", "forge-std/=lib/forge-std/src/", "openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/", - "fevmate=lib/fevmate/contracts", - "murky/=lib/murky/src", - "elliptic-curve-solidity/=lib/elliptic-curve-solidity" + "fevmate/=lib/fevmate/", + "murky/=lib/murky/src/", + "elliptic-curve-solidity/=lib/elliptic-curve-solidity/" ] } diff --git a/contracts/remappings.txt b/contracts/remappings.txt index c3e0743841..1725e0403e 100644 --- a/contracts/remappings.txt +++ b/contracts/remappings.txt @@ -1,6 +1,6 @@ ds-test/=lib/forge-std/lib/ds-test/src/ forge-std/=lib/forge-std/src/ openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/ -fevmate=lib/fevmate/contracts -murky/=lib/murky/src -elliptic-curve-solidity/=lib/elliptic-curve-solidity \ No newline at end of file +fevmate/=lib/fevmate/ +murky/=lib/murky/src/ +elliptic-curve-solidity/=lib/elliptic-curve-solidity/ diff --git a/contracts/scripts/npm-postpack.js b/contracts/scripts/npm-postpack.js new file mode 100644 index 0000000000..4388051b67 --- /dev/null +++ b/contracts/scripts/npm-postpack.js @@ -0,0 +1,11 @@ +// A cleanup script after npm pack is done. +// This script is plugged into the npm build through package.json scripts. +const fs = require('fs-extra') +const path = require('path') + +const contractsDir = path.join(__dirname, '../contracts') + +// Make sure the destination directory is empty and exists +fs.remove(contractsDir) + +console.log('Cleanup after pack is done.') diff --git a/contracts/scripts/npm-prepack.js b/contracts/scripts/npm-prepack.js new file mode 100644 index 0000000000..1de1318a50 --- /dev/null +++ b/contracts/scripts/npm-prepack.js @@ -0,0 +1,16 @@ +// A script to place contracts under the conventional contracts/ directory before publishing. +// This script is plugged into the npm build through package.json scripts. +const fs = require('fs-extra') +const path = require('path') + +// Define source and destination directories +const srcDir = path.join(__dirname, '../src') +const contractsDir = path.join(__dirname, '../contracts') + +// Make sure the destination directory is empty and exists +fs.emptyDirSync(contractsDir) + +// Copy from src to contracts +fs.copySync(srcDir, contractsDir, { overwrite: true }) + +console.log('Preparation for pack is done.') diff --git a/contracts/sdk/IpcContractUpgradeable.sol b/contracts/sdk/IpcContractUpgradeable.sol index bbf30434d5..4b58f07ddb 100644 --- a/contracts/sdk/IpcContractUpgradeable.sol +++ b/contracts/sdk/IpcContractUpgradeable.sol @@ -29,7 +29,7 @@ abstract contract IpcExchangeUpgradeable is Initializable, IIpcHandler, OwnableU function __IpcExchangeUpgradeable_init(address gatewayAddr_) public onlyInitializing { gatewayAddr = gatewayAddr_; - __Ownable_init(); + __Ownable_init(msg.sender); __ReentrancyGuard_init(); } diff --git a/contracts/src/gateway/GatewayManagerFacet.sol b/contracts/src/gateway/GatewayManagerFacet.sol index bab0a72baf..e8a434830c 100644 --- a/contracts/src/gateway/GatewayManagerFacet.sol +++ b/contracts/src/gateway/GatewayManagerFacet.sol @@ -12,7 +12,7 @@ import {AlreadyRegisteredSubnet, CannotReleaseZero, MethodNotAllowed, NotEnoughF import {LibGateway} from "../lib/LibGateway.sol"; import {SubnetIDHelper} from "../lib/SubnetIDHelper.sol"; import {CrossMsgHelper} from "../lib/CrossMsgHelper.sol"; -import {FilAddress} from "fevmate/utils/FilAddress.sol"; +import {FilAddress} from "fevmate/contracts/utils/FilAddress.sol"; import {ReentrancyGuard} from "../lib/LibReentrancyGuard.sol"; import {IERC20} from "openzeppelin-contracts/token/ERC20/IERC20.sol"; import {Address} from "openzeppelin-contracts/utils/Address.sol"; diff --git a/contracts/src/gateway/GatewayMessengerFacet.sol b/contracts/src/gateway/GatewayMessengerFacet.sol index 7f410f28e1..4739b4eb74 100644 --- a/contracts/src/gateway/GatewayMessengerFacet.sol +++ b/contracts/src/gateway/GatewayMessengerFacet.sol @@ -8,7 +8,7 @@ import {SubnetID, SupplyKind, IPCAddress} from "../structs/Subnet.sol"; import {InvalidXnetMessage, InvalidXnetMessageReason, CannotSendCrossMsgToItself, MethodNotAllowed} from "../errors/IPCErrors.sol"; import {SubnetIDHelper} from "../lib/SubnetIDHelper.sol"; import {LibGateway} from "../lib/LibGateway.sol"; -import {FilAddress} from "fevmate/utils/FilAddress.sol"; +import {FilAddress} from "fevmate/contracts/utils/FilAddress.sol"; import {SupplySourceHelper} from "../lib/SupplySourceHelper.sol"; import {CrossMsgHelper} from "../lib/CrossMsgHelper.sol"; import {FvmAddressHelper} from "../lib/FvmAddressHelper.sol"; diff --git a/contracts/src/gateway/router/TopDownFinalityFacet.sol b/contracts/src/gateway/router/TopDownFinalityFacet.sol index 3e10887935..b29f7e2036 100644 --- a/contracts/src/gateway/router/TopDownFinalityFacet.sol +++ b/contracts/src/gateway/router/TopDownFinalityFacet.sol @@ -6,7 +6,7 @@ import {ParentFinality} from "../../structs/CrossNet.sol"; import {PermissionMode, Validator, ValidatorInfo, StakingChangeRequest, Membership} from "../../structs/Subnet.sol"; import {LibGateway} from "../../lib/LibGateway.sol"; -import {FilAddress} from "fevmate/utils/FilAddress.sol"; +import {FilAddress} from "fevmate/contracts/utils/FilAddress.sol"; import {ParentValidatorsTracker, ValidatorSet} from "../../structs/Subnet.sol"; import {LibValidatorTracking, LibValidatorSet} from "../../lib/LibStaking.sol"; diff --git a/contracts/src/gateway/router/XnetMessagingFacet.sol b/contracts/src/gateway/router/XnetMessagingFacet.sol index 6f34242879..9504def4fd 100644 --- a/contracts/src/gateway/router/XnetMessagingFacet.sol +++ b/contracts/src/gateway/router/XnetMessagingFacet.sol @@ -8,7 +8,7 @@ import {IPCMsgType} from "../../enums/IPCMsgType.sol"; import {SubnetActorGetterFacet} from "../../subnet/SubnetActorGetterFacet.sol"; import {Subnet} from "../../structs/Subnet.sol"; -import {FilAddress} from "fevmate/utils/FilAddress.sol"; +import {FilAddress} from "fevmate/contracts/utils/FilAddress.sol"; import {SubnetIDHelper} from "../../lib/SubnetIDHelper.sol"; import {CrossMsgHelper} from "../../lib/CrossMsgHelper.sol"; import {SupplySourceHelper} from "../../lib/SupplySourceHelper.sol"; diff --git a/contracts/src/lib/AccountHelper.sol b/contracts/src/lib/AccountHelper.sol index 8757c860e5..12212b381e 100644 --- a/contracts/src/lib/AccountHelper.sol +++ b/contracts/src/lib/AccountHelper.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.23; -import {FilAddress} from "fevmate/utils/FilAddress.sol"; +import {FilAddress} from "fevmate/contracts/utils/FilAddress.sol"; /// @title Helper library for checking account type /// @author LimeChain team diff --git a/contracts/src/lib/CrossMsgHelper.sol b/contracts/src/lib/CrossMsgHelper.sol index d9b12b97ca..05a9a47826 100644 --- a/contracts/src/lib/CrossMsgHelper.sol +++ b/contracts/src/lib/CrossMsgHelper.sol @@ -8,7 +8,7 @@ import {SubnetID, IPCAddress} from "../structs/Subnet.sol"; import {SubnetIDHelper} from "../lib/SubnetIDHelper.sol"; import {FvmAddressHelper} from "../lib/FvmAddressHelper.sol"; import {FvmAddress} from "../structs/FvmAddress.sol"; -import {FilAddress} from "fevmate/utils/FilAddress.sol"; +import {FilAddress} from "fevmate/contracts/utils/FilAddress.sol"; import {Address} from "openzeppelin-contracts/utils/Address.sol"; import {SupplySource} from "../structs/Subnet.sol"; import {SupplySourceHelper} from "./SupplySourceHelper.sol"; diff --git a/contracts/src/lib/LibGateway.sol b/contracts/src/lib/LibGateway.sol index 81a0d9a171..3713658a21 100644 --- a/contracts/src/lib/LibGateway.sol +++ b/contracts/src/lib/LibGateway.sol @@ -10,7 +10,7 @@ import {CallMsg, IpcMsgKind, IpcEnvelope, OutcomeType, BottomUpMsgBatch, BottomU import {Membership} from "../structs/Subnet.sol"; import {CannotSendCrossMsgToItself, MethodNotAllowed, MaxMsgsPerBatchExceeded, InvalidXnetMessage ,OldConfigurationNumber, NotRegisteredSubnet, InvalidActorAddress, ParentFinalityAlreadyCommitted, InvalidXnetMessageReason} from "../errors/IPCErrors.sol"; import {CrossMsgHelper} from "../lib/CrossMsgHelper.sol"; -import {FilAddress} from "fevmate/utils/FilAddress.sol"; +import {FilAddress} from "fevmate/contracts/utils/FilAddress.sol"; import {SubnetIDHelper} from "../lib/SubnetIDHelper.sol"; import {SupplySourceHelper} from "../lib/SupplySourceHelper.sol"; diff --git a/contracts/src/lib/LibGatewayActorStorage.sol b/contracts/src/lib/LibGatewayActorStorage.sol index 64108acf26..a1888cc004 100644 --- a/contracts/src/lib/LibGatewayActorStorage.sol +++ b/contracts/src/lib/LibGatewayActorStorage.sol @@ -7,7 +7,7 @@ import {BottomUpCheckpoint, BottomUpMsgBatch, IpcEnvelope, ParentFinality} from import {SubnetID, Subnet, ParentValidatorsTracker} from "../structs/Subnet.sol"; import {Membership} from "../structs/Subnet.sol"; import {AccountHelper} from "../lib/AccountHelper.sol"; -import {FilAddress} from "fevmate/utils/FilAddress.sol"; +import {FilAddress} from "fevmate/contracts/utils/FilAddress.sol"; import {EnumerableSet} from "openzeppelin-contracts/utils/structs/EnumerableSet.sol"; struct GatewayActorStorage { diff --git a/contracts/test/IntegrationTestBase.sol b/contracts/test/IntegrationTestBase.sol index 6fb050addb..093b5c6509 100644 --- a/contracts/test/IntegrationTestBase.sol +++ b/contracts/test/IntegrationTestBase.sol @@ -13,7 +13,7 @@ import {SubnetID, SupplyKind, PermissionMode, PermissionMode, Subnet, SupplySour import {SubnetIDHelper} from "../src/lib/SubnetIDHelper.sol"; import {FvmAddressHelper} from "../src/lib/FvmAddressHelper.sol"; import {CrossMsgHelper} from "../src/lib/CrossMsgHelper.sol"; -import {FilAddress} from "fevmate/utils/FilAddress.sol"; +import {FilAddress} from "fevmate/contracts/utils/FilAddress.sol"; import {GatewayDiamond} from "../src/GatewayDiamond.sol"; import {SubnetActorDiamond} from "../src/SubnetActorDiamond.sol"; import {GatewayGetterFacet} from "../src/gateway/GatewayGetterFacet.sol"; diff --git a/contracts/test/integration/GatewayDiamond.t.sol b/contracts/test/integration/GatewayDiamond.t.sol index aa33a321f8..04f6da90c8 100644 --- a/contracts/test/integration/GatewayDiamond.t.sol +++ b/contracts/test/integration/GatewayDiamond.t.sol @@ -18,7 +18,7 @@ import {SubnetID, Subnet, IPCAddress, Validator, StakingChange, StakingChangeReq import {SubnetIDHelper} from "../../src/lib/SubnetIDHelper.sol"; import {FvmAddressHelper} from "../../src/lib/FvmAddressHelper.sol"; import {CrossMsgHelper} from "../../src/lib/CrossMsgHelper.sol"; -import {FilAddress} from "fevmate/utils/FilAddress.sol"; +import {FilAddress} from "fevmate/contracts/utils/FilAddress.sol"; import {GatewayDiamond, FunctionNotFound} from "../../src/GatewayDiamond.sol"; import {GatewayGetterFacet} from "../../src/gateway/GatewayGetterFacet.sol"; import {GatewayManagerFacet} from "../../src/gateway/GatewayManagerFacet.sol"; diff --git a/contracts/test/integration/GatewayDiamondToken.t.sol b/contracts/test/integration/GatewayDiamondToken.t.sol index 17ae01c5f2..f119fedc75 100644 --- a/contracts/test/integration/GatewayDiamondToken.t.sol +++ b/contracts/test/integration/GatewayDiamondToken.t.sol @@ -13,7 +13,7 @@ import {FvmAddressHelper} from "../../src/lib/FvmAddressHelper.sol"; import {CrossMsgHelper} from "../../src/lib/CrossMsgHelper.sol"; import {IIpcHandler} from "../../sdk/interfaces/IIpcHandler.sol"; import {SupplySourceHelper} from "../../src/lib/SupplySourceHelper.sol"; -import {FilAddress} from "fevmate/utils/FilAddress.sol"; +import {FilAddress} from "fevmate/contracts/utils/FilAddress.sol"; import {GatewayDiamond} from "../../src/GatewayDiamond.sol"; import {LibGateway} from "../../src/lib/LibGateway.sol"; import {MockIpcContract, TestUtils} from "../helpers/TestUtils.sol"; diff --git a/contracts/test/integration/L2GatewayDiamond.t.sol b/contracts/test/integration/L2GatewayDiamond.t.sol index f230d87252..4008b9245f 100644 --- a/contracts/test/integration/L2GatewayDiamond.t.sol +++ b/contracts/test/integration/L2GatewayDiamond.t.sol @@ -21,7 +21,7 @@ import {DiamondCutFacet} from "../../src/diamond/DiamondCutFacet.sol"; import {IntegrationTestBase} from "../IntegrationTestBase.sol"; import {L2GatewayActorDiamond} from "../IntegrationTestPresets.sol"; import {TestUtils} from "../helpers/TestUtils.sol"; -import {FilAddress} from "fevmate/utils/FilAddress.sol"; +import {FilAddress} from "fevmate/contracts/utils/FilAddress.sol"; import {GatewayFacetsHelper} from "../helpers/GatewayFacetsHelper.sol"; diff --git a/contracts/test/integration/MultiSubnet.t.sol b/contracts/test/integration/MultiSubnet.t.sol index 670785ab98..764f383e09 100644 --- a/contracts/test/integration/MultiSubnet.t.sol +++ b/contracts/test/integration/MultiSubnet.t.sol @@ -29,7 +29,7 @@ import {DiamondCutFacet} from "../../src/diamond/DiamondCutFacet.sol"; import {IntegrationTestBase, RootSubnetDefinition, TestSubnetDefinition} from "../IntegrationTestBase.sol"; import {L2GatewayActorDiamond, L1GatewayActorDiamond} from "../IntegrationTestPresets.sol"; import {TestUtils, MockIpcContract, MockIpcContractPayable, MockIpcContractRevert, MockIpcContractFallback} from "../helpers/TestUtils.sol"; -import {FilAddress} from "fevmate/utils/FilAddress.sol"; +import {FilAddress} from "fevmate/contracts/utils/FilAddress.sol"; import {MerkleTreeHelper} from "../helpers/MerkleTreeHelper.sol"; import {IERC20} from "openzeppelin-contracts/token/ERC20/IERC20.sol"; diff --git a/contracts/test/integration/SubnetActorDiamond.t.sol b/contracts/test/integration/SubnetActorDiamond.t.sol index 5b64ee7c8a..73442ba875 100644 --- a/contracts/test/integration/SubnetActorDiamond.t.sol +++ b/contracts/test/integration/SubnetActorDiamond.t.sol @@ -30,7 +30,7 @@ import {SubnetActorPauseFacet} from "../../src/subnet/SubnetActorPauseFacet.sol" import {SubnetActorCheckpointingFacet} from "../../src/subnet/SubnetActorCheckpointingFacet.sol"; import {SubnetActorRewardFacet} from "../../src/subnet/SubnetActorRewardFacet.sol"; import {DiamondCutFacet} from "../../src/diamond/DiamondCutFacet.sol"; -import {FilAddress} from "fevmate/utils/FilAddress.sol"; +import {FilAddress} from "fevmate/contracts/utils/FilAddress.sol"; import {LibStaking} from "../../src/lib/LibStaking.sol"; import {LibDiamond} from "../../src/lib/LibDiamond.sol"; import {Pausable} from "../../src/lib/LibPausable.sol"; diff --git a/contracts/test/sdk/IpcContract.t.sol b/contracts/test/sdk/IpcContract.t.sol index c5d791d010..18f31c727f 100644 --- a/contracts/test/sdk/IpcContract.t.sol +++ b/contracts/test/sdk/IpcContract.t.sol @@ -11,7 +11,7 @@ import {SubnetID, Subnet, IPCAddress, Validator} from "../../src/structs/Subnet. import {SubnetIDHelper} from "../../src/lib/SubnetIDHelper.sol"; import {FvmAddressHelper} from "../../src/lib/FvmAddressHelper.sol"; import {CrossMsgHelper} from "../../src/lib/CrossMsgHelper.sol"; -import {FilAddress} from "fevmate/utils/FilAddress.sol"; +import {FilAddress} from "fevmate/contracts/utils/FilAddress.sol"; import {IpcExchange} from "../../sdk/IpcContract.sol"; import {IIpcHandler} from "../../sdk/interfaces/IIpcHandler.sol"; import {IGateway} from "../../src/interfaces/IGateway.sol"; diff --git a/contracts/test/unit/AccountHelper.t.sol b/contracts/test/unit/AccountHelper.t.sol index 017c97bdf9..f0e8ffc3ea 100644 --- a/contracts/test/unit/AccountHelper.t.sol +++ b/contracts/test/unit/AccountHelper.t.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.23; import "forge-std/Test.sol"; -import "fevmate/utils/FilAddress.sol"; +import "fevmate/contracts/utils/FilAddress.sol"; import "../../src/lib/AccountHelper.sol"; contract AccountHelperTest is Test { diff --git a/fendermint/app/src/app.rs b/fendermint/app/src/app.rs index a89cedb8cc..df6a3e524a 100644 --- a/fendermint/app/src/app.rs +++ b/fendermint/app/src/app.rs @@ -451,6 +451,8 @@ where let validators = to_validator_updates(validators).context("failed to convert validators")?; + tracing::info!(state_params = serde_json::to_string(&state_params)?); + // Let's pretend that the genesis state is that of a fictive block at height 0. // The record will be stored under height 1, and the record after the application // of the actual block 1 will be at height 2, so they are distinct records. diff --git a/fendermint/testing/graph-test/Makefile.toml b/fendermint/testing/graph-test/Makefile.toml index f47b7c4bbc..3fe2c024eb 100644 --- a/fendermint/testing/graph-test/Makefile.toml +++ b/fendermint/testing/graph-test/Makefile.toml @@ -57,7 +57,7 @@ npm run graph-node echo "Waiting for Graph node to start..." # Notice that subgraph node does takes a while to spin up -sleep 30s +sleep 30 npm run create-local npm run deploy-local diff --git a/fendermint/testing/graph-test/subgraph/graph-node/docker-compose.yaml b/fendermint/testing/graph-test/subgraph/graph-node/docker-compose.yaml index 4888cd5d4c..8b0d49e4a2 100644 --- a/fendermint/testing/graph-test/subgraph/graph-node/docker-compose.yaml +++ b/fendermint/testing/graph-test/subgraph/graph-node/docker-compose.yaml @@ -2,6 +2,7 @@ version: '3' services: graph-node: image: graphprotocol/graph-node:v0.27.0 + platform: linux/amd64 ports: - '8000:8000' - '8001:8001' @@ -24,6 +25,7 @@ services: GRAPH_ETHEREUM_GENESIS_BLOCK_NUMBER: 1 ipfs: image: ipfs/go-ipfs:v0.10.0 + platform: linux/amd64 ports: - '5001:5001' postgres: diff --git a/fendermint/testing/materializer/Cargo.toml b/fendermint/testing/materializer/Cargo.toml index b43b097026..fabce48d2e 100644 --- a/fendermint/testing/materializer/Cargo.toml +++ b/fendermint/testing/materializer/Cargo.toml @@ -44,6 +44,7 @@ fendermint_vm_core = { path = "../../vm/core" } fendermint_vm_genesis = { path = "../../vm/genesis" } fendermint_vm_encoding = { path = "../../vm/encoding" } fendermint_vm_message = { path = "../../vm/message" } +fendermint_vm_interpreter = { path = "../../vm/interpreter" } fendermint_testing = { path = "..", optional = true } diff --git a/fendermint/testing/materializer/src/docker/node.rs b/fendermint/testing/materializer/src/docker/node.rs index f64035a720..cfa9c79b91 100644 --- a/fendermint/testing/materializer/src/docker/node.rs +++ b/fendermint/testing/materializer/src/docker/node.rs @@ -205,6 +205,21 @@ impl DockerNode { export_file(keys_dir.join(COMETBFT_NODE_ID), cometbft_node_id)?; + fendermint_runner + .run_cmd( + "genesis \ + --genesis-file /fendermint/genesis.json \ + ipc \ + seal-state \ + --builtin-actors-path /fendermint/bundle.car \ + --custom-actors-path /fendermint/custom_actors_bundle.car \ + --artifacts-path /fendermint/contracts \ + --output-path /cometbft/config/sealed.json \ + ", + ) + .await + .context("failed to seal genesis state")?; + // Convert fendermint genesis to cometbft. fendermint_runner .run_cmd( @@ -212,6 +227,7 @@ impl DockerNode { --genesis-file /fendermint/genesis.json \ into-tendermint \ --out /cometbft/config/genesis.json \ + --sealed /cometbft/config/sealed.json \ ", ) .await @@ -511,7 +527,7 @@ impl DockerNode { } if let Some(client) = self.ethapi_http_provider()? { - if let Err(e) = client.get_chainid().await { + if let Err(e) = client.get_block(1).await { continue; } } diff --git a/fendermint/testing/materializer/tests/docker.rs b/fendermint/testing/materializer/tests/docker.rs index c6910f8ccd..04038a1833 100644 --- a/fendermint/testing/materializer/tests/docker.rs +++ b/fendermint/testing/materializer/tests/docker.rs @@ -166,7 +166,7 @@ async fn wait_for_startup(testnet: &DockerTestnet) -> anyhow::Result { } if let Some(client) = dnode.ethapi_http_provider()? { - if let Err(e) = client.get_chainid().await { + if let Err(e) = client.get_block(1).await { eprintln!("EthAPI on {name} still fails: {e}"); continue 'startup; } diff --git a/fendermint/testing/materializer/tests/docker_tests/standalone.rs b/fendermint/testing/materializer/tests/docker_tests/standalone.rs index 38b1fd05d0..8059438898 100644 --- a/fendermint/testing/materializer/tests/docker_tests/standalone.rs +++ b/fendermint/testing/materializer/tests/docker_tests/standalone.rs @@ -66,6 +66,8 @@ async fn test_sent_tx_found_in_mempool() { .await .context("failed to set up middleware")?; + eprintln!("middleware ready, pending tests"); + // Create the simplest transaction possible: send tokens between accounts. let to: H160 = charlie.eth_addr().into(); let transfer = Eip1559TransactionRequest::new().to(to).value(1); @@ -77,6 +79,8 @@ async fn test_sent_tx_found_in_mempool() { let tx_hash = pending.tx_hash(); + eprintln!("sent pending txn {:?}", tx_hash); + // We expect that the transaction is pending, however it should not return an error. match middleware.get_transaction(tx_hash).await { Ok(Some(_)) => {} diff --git a/fendermint/vm/interpreter/src/fvm/store/memory.rs b/fendermint/vm/interpreter/src/fvm/store/memory.rs index d49b9eb3b9..9ad8a4d86f 100644 --- a/fendermint/vm/interpreter/src/fvm/store/memory.rs +++ b/fendermint/vm/interpreter/src/fvm/store/memory.rs @@ -24,11 +24,6 @@ impl MemoryBlockstore { } impl Blockstore for MemoryBlockstore { - fn has(&self, k: &Cid) -> Result { - let guard = self.blocks.read().unwrap(); - Ok(guard.contains_key(k)) - } - fn get(&self, k: &Cid) -> Result>> { let guard = self.blocks.read().unwrap(); Ok(guard.get(k).cloned()) @@ -39,4 +34,9 @@ impl Blockstore for MemoryBlockstore { guard.insert(*k, block.into()); Ok(()) } + + fn has(&self, k: &Cid) -> Result { + let guard = self.blocks.read().unwrap(); + Ok(guard.contains_key(k)) + } } diff --git a/fendermint/vm/interpreter/src/genesis.rs b/fendermint/vm/interpreter/src/genesis.rs index 49b1773418..951bec82aa 100644 --- a/fendermint/vm/interpreter/src/genesis.rs +++ b/fendermint/vm/interpreter/src/genesis.rs @@ -142,6 +142,8 @@ impl GenesisCreator { ) -> anyhow::Result<()> { let file = tokio::fs::File::create(&self.sealed_out_path).await?; + tracing::info!(state_root = state_root.to_string(), "state root"); + let metadata = GenesisMetadata::new(state_root, out); let streamer = StateTreeStreamer::new(state_root, store); diff --git a/ipc/cli/Cargo.toml b/ipc/cli/Cargo.toml index 9aae53cf54..6dc4e71d53 100644 --- a/ipc/cli/Cargo.toml +++ b/ipc/cli/Cargo.toml @@ -46,3 +46,4 @@ ipc-wallet = { workspace = true } ipc-provider = { workspace = true } ipc-api = { workspace = true } ipc-types = { workspace = true } +tracing-subscriber.workspace = true diff --git a/ipc/cli/src/main.rs b/ipc/cli/src/main.rs index 8d183e364b..fc6a154ba8 100644 --- a/ipc/cli/src/main.rs +++ b/ipc/cli/src/main.rs @@ -1,9 +1,15 @@ // Copyright 2022-2024 Protocol Labs // SPDX-License-Identifier: MIT +use tracing_subscriber::prelude::*; +use tracing_subscriber::{fmt, EnvFilter}; + #[tokio::main] async fn main() { - env_logger::init_from_env(env_logger::Env::new().default_filter_or("info")); + tracing_subscriber::registry() + .with(fmt::layer()) + .with(EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"))) + .init(); if let Err(e) = ipc_cli::cli().await { log::error!("main process failed: {e:#}"); diff --git a/specs/supply-sources.md b/specs/supply-sources.md new file mode 100644 index 0000000000..418d901fea --- /dev/null +++ b/specs/supply-sources.md @@ -0,0 +1,45 @@ +# Supply Sources + +## Subnet native coin + +Every subnet has a native coin. This is the economic unit that’s passed when sending value in transactions, and it’s used to settle gas payments (both burns and premiums). This coin is externally defined by configuring the “supply source” at the parent, and the subnet itself has no internal knowledge of what the supply is, nor does it need to (thanks to IPC’s decoupled architecture). + +## Supply sources + +The `SupplySource` of a subnet is configured at subnet creation time. It determines which currency in the parent chain will turn into the native circulating supply of the subnet. The `SupplySource` can’t be modified at the later stage, so make sure you choose it wisely. + +It can be one of two kinds (as per the `SupplyKind` enum): + +- `Native` (default) +- `ERC20` + +The `Native` supply source indicates that the subnet adopts the native coin of the parent as its own native circulating supply currency. `Native` has transitive properties. + +- if an L2 subnet is anchored to *Filecoin* as its root chain, the subnet’s currency will be *FIL*. +- if an L3 subnet is anchored to an L2 whose circulating supply is *FIL*, the L3 will adopt *FIL* too (transitive property). + +Conversely, the `ERC20` supply source indicates that the subnet adopts an arbitrary ERC20-compliant token residing at the parent as its own native circulating supply currency. Consequently, the total supply of the subnet is controlled by the ERC20 token at the parent. This choice is highly desirable when the developer wants to kickstart a custom cryptoeconomy within their subnet. + +When specifying the `ERC20` supply kind, the `SupplySource` must specify `tokenAddress` - the address of the smart contract implementing the ERC20 interface. This token must exist at subnet creation time. Counterfactual token deployments are disallowed. + +## Implementation notes + +### **Creating subnet with ERC20 `SupplySource`** + +In order to create a subnet with non-default `SupplySource` , in the IPC CLI `subnet create` command the subnet owner needs to specify: + +- `--supply-source-kind erc20` +- `--supply_source_address
` + +In order to make sure the specified contract exists `[SupplySourceHelper#validate](https://github.com/consensus-shipyard/ipc/blob/main/contracts/src/lib/SupplySourceHelper.sol#L29)` function is called which executed basic sanity check using `[IERC20#balanceOf](https://github.com/consensus-shipyard/ipc/blob/main/contracts/src/lib/SupplySourceHelper.sol#L38)` which reverts if the contract doesn’t exist or doesn’t implement `balanceOf`. + +### Sending funds to the subnet depending on the `SupplySource` + +Funds can be sent to the subnet’s receiver address using + +- `GatewayManagerFacet#fund(subnetID, to)` if the `Native` supply source is used +- `GatewayManagerFacet#fundWithToken(subnetID, to, amount)` if the `ERC20` supply source is used + +`GatewayManagerFacet#fundWithToken` locks a specified amount into custody using `IERC20#safeTransferFrom`. + +Both `fund` and `fundWithToken` functions commit a top-down message to be processed by the subnet in order to increase the recipient’s balance. From 01c8bf1224ee2b06a598812b44544dd65bad551b Mon Sep 17 00:00:00 2001 From: cryptoAtwill Date: Tue, 6 Aug 2024 00:04:41 +0800 Subject: [PATCH 11/69] add block gas limit --- Cargo.lock | 20 ++++ Cargo.toml | 1 + fendermint/actors/gas/Cargo.toml | 34 +++++++ fendermint/actors/gas/src/lib.rs | 157 +++++++++++++++++++++++++++++++ fendermint/vm/core/src/gas.rs | 16 ++++ fendermint/vm/core/src/lib.rs | 1 + 6 files changed, 229 insertions(+) create mode 100644 fendermint/actors/gas/Cargo.toml create mode 100644 fendermint/actors/gas/src/lib.rs create mode 100644 fendermint/vm/core/src/gas.rs diff --git a/Cargo.lock b/Cargo.lock index ecebaa242e..8c289c5cf0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2835,6 +2835,26 @@ dependencies = [ "serde", ] +[[package]] +name = "fendermint_actor_gas" +version = "0.1.0" +dependencies = [ + "anyhow", + "cid", + "fil_actors_evm_shared", + "fil_actors_runtime", + "frc42_dispatch", + "fvm_ipld_blockstore", + "fvm_ipld_encoding", + "fvm_shared", + "hex-literal 0.4.1", + "log", + "multihash 0.18.1", + "num-derive 0.3.3", + "num-traits", + "serde", +] + [[package]] name = "fendermint_actors" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index bb28ffb436..b01e0d35ac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,7 @@ members = [ "fendermint/vm/*", "fendermint/actors", "fendermint/actors/chainmetadata", + "fendermint/actors/gas", ] [workspace.package] diff --git a/fendermint/actors/gas/Cargo.toml b/fendermint/actors/gas/Cargo.toml new file mode 100644 index 0000000000..b6d551181f --- /dev/null +++ b/fendermint/actors/gas/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "fendermint_actor_gas" +description = "Builtin transaction gas tracking actor for IPC" +license.workspace = true +edition.workspace = true +authors.workspace = true +version = "0.1.0" + +[lib] +## lib is necessary for integration tests +## cdylib is necessary for Wasm build +crate-type = ["cdylib", "lib"] + +[dependencies] +anyhow = { workspace = true } +cid = { workspace = true } +fil_actors_runtime = { workspace = true } +fvm_ipld_blockstore = { workspace = true } +fvm_ipld_encoding = { workspace = true } +fvm_shared = { workspace = true } +log = { workspace = true } +multihash = { workspace = true } +num-derive = { workspace = true } +num-traits = { workspace = true } +serde = { workspace = true } +hex-literal = { workspace = true } +frc42_dispatch = { workspace = true } + +[dev-dependencies] +fil_actors_evm_shared = { workspace = true } +fil_actors_runtime = { workspace = true, features = ["test_utils"] } + +[features] +fil-actor = ["fil_actors_runtime/fil-actor"] diff --git a/fendermint/actors/gas/src/lib.rs b/fendermint/actors/gas/src/lib.rs new file mode 100644 index 0000000000..f88dd89c4a --- /dev/null +++ b/fendermint/actors/gas/src/lib.rs @@ -0,0 +1,157 @@ +// Copyright 2021-2023 Protocol Labs +// SPDX-License-Identifier: Apache-2.0, MIT + +use fil_actors_runtime::runtime::{ActorCode, Runtime}; +use fil_actors_runtime::ActorError; +use fil_actors_runtime::SYSTEM_ACTOR_ADDR; +use fvm_ipld_blockstore::Blockstore; +use fvm_ipld_encoding::ipld_block::IpldBlock; +use fvm_ipld_encoding::tuple::*; +use fvm_shared::{MethodNum, METHOD_CONSTRUCTOR}; +use num_derive::FromPrimitive; + +#[cfg(feature = "fil-actor")] +fil_actors_runtime::wasm_trampoline!(IPCEamActor); + +pub const IPC_GAS_ACTOR_NAME: &str = "gas"; + +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone)] +pub struct State { + block_gas_limit: Gas, +} + +pub struct IPCGasActor; + +#[derive(FromPrimitive)] +#[repr(u64)] +pub enum Method { + Constructor = METHOD_CONSTRUCTOR, + SetBlockGasLimit = frc42_dispatch::method_hash!("SetBlockGasLimit"), + BlockGasLimit = frc42_dispatch::method_hash!("BlockGasLimit"), +} + +type Gas = u64; + +impl IPCGasActor { + /// Creates the actor + pub fn constructor(rt: &impl Runtime, args: ConstructorParams) -> Result<(), ActorError> { + let st = State { + block_gas_limit: args.block_gas_limit, + }; + rt.create(&st)?; + + Ok(()) + } + + fn set_block_gas_limit(rt: &impl Runtime, limit: Gas) -> Result<(), ActorError> { + if rt.message().caller() != SYSTEM_ACTOR_ADDR { + return Err(ActorError::forbidden("not system actor".into())); + } + + rt.transaction(|st: &mut State, _rt| { + st.block_gas_limit = limit; + Ok(()) + })?; + + Ok(()) + } + + fn block_gas_limit(rt: &impl Runtime) -> Result { + Ok(rt.state::()?.block_gas_limit) + } +} + +impl ActorCode for IPCGasActor { + type Methods = Method; + + fn name() -> &'static str { + IPC_GAS_ACTOR_NAME + } + + fn invoke_method( + rt: &RT, + method: MethodNum, + params: Option, + ) -> Result, ActorError> + where + RT: Runtime, + RT::Blockstore: Blockstore + Clone, + { + if method == Method::Constructor as u64 { + fil_actors_runtime::dispatch(rt, method, Self::constructor, params) + } else if method == Method::SetBlockGasLimit as u64 { + fil_actors_runtime::dispatch(rt, method, Self::set_block_gas_limit, params) + } else if method == Method::BlockGasLimit as u64 { + fil_actors_runtime::dispatch(rt, method, Self::block_gas_limit, params) + } else { + Err(ActorError::not_found("method not found".into())) + } + } +} + +#[derive(Debug, Serialize_tuple, Deserialize_tuple)] +pub struct ConstructorParams { + block_gas_limit: Gas, +} + +#[cfg(test)] +mod tests { + use crate::{ConstructorParams, IPCGasActor, Method, State}; + use fil_actors_runtime::test_utils::{expect_empty, MockRuntime, SYSTEM_ACTOR_CODE_ID}; + use fil_actors_runtime::SYSTEM_ACTOR_ADDR; + use fvm_ipld_encoding::ipld_block::IpldBlock; + use fvm_shared::address::Address; + use fvm_shared::error::ExitCode; + + pub fn construct_and_verify() -> MockRuntime { + let rt = MockRuntime { + receiver: Address::new_id(10), + ..Default::default() + }; + + let result = rt + .call::( + Method::Constructor as u64, + IpldBlock::serialize_cbor(&ConstructorParams { + block_gas_limit: 100, + }) + .unwrap(), + ) + .unwrap(); + expect_empty(result); + rt.verify(); + rt.reset(); + + rt + } + + #[test] + fn test_set_ok() { + let rt = construct_and_verify(); + rt.set_caller(*SYSTEM_ACTOR_CODE_ID, SYSTEM_ACTOR_ADDR); + + let r = rt.call::( + Method::SetBlockGasLimit as u64, + IpldBlock::serialize_cbor(&20).unwrap(), + ); + assert!(r.is_ok()); + + let s = rt.get_state::(); + assert_eq!(s.block_gas_limit, 20); + } + + #[test] + fn test_not_allowed() { + let rt = construct_and_verify(); + rt.set_caller(*SYSTEM_ACTOR_CODE_ID, Address::new_id(1000)); + + let code = rt + .call::( + Method::SetBlockGasLimit as u64, + IpldBlock::serialize_cbor(&20).unwrap(), + ) + .unwrap_err() + .exit_code(); + assert_eq!(code, ExitCode::USR_FORBIDDEN) + } +} diff --git a/fendermint/vm/core/src/gas.rs b/fendermint/vm/core/src/gas.rs new file mode 100644 index 0000000000..ad791417a2 --- /dev/null +++ b/fendermint/vm/core/src/gas.rs @@ -0,0 +1,16 @@ +// Copyright 2022-2024 Protocol Labs +// SPDX-License-Identifier: Apache-2.0, MIT + +pub type Gas = u64; + +/// Handles the gas modeling in the current blockchain +pub trait GasLayer { + /// The state of the blockchain + type State; + + /// Update the block gas limit + fn set_block_gas_limit(&self, state: &mut Self::State); + + /// Obtain the current block gas limit + fn block_gas_limit(&self, state: &Self::State) -> Gas; +} \ No newline at end of file diff --git a/fendermint/vm/core/src/lib.rs b/fendermint/vm/core/src/lib.rs index b1ca80e3f0..4ef87e8cfb 100644 --- a/fendermint/vm/core/src/lib.rs +++ b/fendermint/vm/core/src/lib.rs @@ -2,5 +2,6 @@ // SPDX-License-Identifier: Apache-2.0, MIT pub mod chainid; mod timestamp; +pub mod gas; pub use timestamp::Timestamp; From dd2832d20a338b13ad6c0252941d492fa916b25a Mon Sep 17 00:00:00 2001 From: cryptoAtwill Date: Tue, 6 Aug 2024 16:48:40 +0800 Subject: [PATCH 12/69] check gas limit in deliver --- Cargo.lock | 1 + fendermint/actors/gas/src/lib.rs | 20 ++-- fendermint/vm/actor_interface/src/gas.rs | 4 + fendermint/vm/actor_interface/src/lib.rs | 1 + fendermint/vm/core/src/gas.rs | 16 --- fendermint/vm/core/src/lib.rs | 1 - fendermint/vm/interpreter/Cargo.toml | 1 + fendermint/vm/interpreter/src/fvm/exec.rs | 7 +- .../vm/interpreter/src/fvm/gas/default.rs | 108 ++++++++++++++++++ fendermint/vm/interpreter/src/fvm/gas/mod.rs | 19 +++ fendermint/vm/interpreter/src/fvm/mod.rs | 4 + .../vm/interpreter/src/fvm/state/exec.rs | 20 +++- .../vm/interpreter/src/fvm/state/mod.rs | 1 + 13 files changed, 173 insertions(+), 30 deletions(-) create mode 100644 fendermint/vm/actor_interface/src/gas.rs delete mode 100644 fendermint/vm/core/src/gas.rs create mode 100644 fendermint/vm/interpreter/src/fvm/gas/default.rs create mode 100644 fendermint/vm/interpreter/src/fvm/gas/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 8c289c5cf0..31c05f03fb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3343,6 +3343,7 @@ dependencies = [ "ethers", "fendermint_actor_chainmetadata", "fendermint_actor_eam", + "fendermint_actor_gas", "fendermint_actors", "fendermint_crypto", "fendermint_eth_hardhat", diff --git a/fendermint/actors/gas/src/lib.rs b/fendermint/actors/gas/src/lib.rs index f88dd89c4a..f65a710ff2 100644 --- a/fendermint/actors/gas/src/lib.rs +++ b/fendermint/actors/gas/src/lib.rs @@ -14,24 +14,22 @@ use num_derive::FromPrimitive; fil_actors_runtime::wasm_trampoline!(IPCEamActor); pub const IPC_GAS_ACTOR_NAME: &str = "gas"; +pub type Gas = u64; + +pub struct IPCGasActor; #[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone)] pub struct State { block_gas_limit: Gas, } -pub struct IPCGasActor; - #[derive(FromPrimitive)] #[repr(u64)] pub enum Method { Constructor = METHOD_CONSTRUCTOR, SetBlockGasLimit = frc42_dispatch::method_hash!("SetBlockGasLimit"), - BlockGasLimit = frc42_dispatch::method_hash!("BlockGasLimit"), } -type Gas = u64; - impl IPCGasActor { /// Creates the actor pub fn constructor(rt: &impl Runtime, args: ConstructorParams) -> Result<(), ActorError> { @@ -55,10 +53,6 @@ impl IPCGasActor { Ok(()) } - - fn block_gas_limit(rt: &impl Runtime) -> Result { - Ok(rt.state::()?.block_gas_limit) - } } impl ActorCode for IPCGasActor { @@ -81,8 +75,6 @@ impl ActorCode for IPCGasActor { fil_actors_runtime::dispatch(rt, method, Self::constructor, params) } else if method == Method::SetBlockGasLimit as u64 { fil_actors_runtime::dispatch(rt, method, Self::set_block_gas_limit, params) - } else if method == Method::BlockGasLimit as u64 { - fil_actors_runtime::dispatch(rt, method, Self::block_gas_limit, params) } else { Err(ActorError::not_found("method not found".into())) } @@ -94,6 +86,12 @@ pub struct ConstructorParams { block_gas_limit: Gas, } +impl State { + pub fn block_gas_limit(&self) -> Gas { + self.block_gas_limit + } +} + #[cfg(test)] mod tests { use crate::{ConstructorParams, IPCGasActor, Method, State}; diff --git a/fendermint/vm/actor_interface/src/gas.rs b/fendermint/vm/actor_interface/src/gas.rs new file mode 100644 index 0000000000..75b4301242 --- /dev/null +++ b/fendermint/vm/actor_interface/src/gas.rs @@ -0,0 +1,4 @@ +// Copyright 2022-2024 Protocol Labs +// SPDX-License-Identifier: Apache-2.0, MIT + +define_id!(GAS { id: 66 }); diff --git a/fendermint/vm/actor_interface/src/lib.rs b/fendermint/vm/actor_interface/src/lib.rs index 2e25c18853..eb9d0569d8 100644 --- a/fendermint/vm/actor_interface/src/lib.rs +++ b/fendermint/vm/actor_interface/src/lib.rs @@ -50,6 +50,7 @@ pub mod diamond; pub mod eam; pub mod ethaccount; pub mod evm; +pub mod gas; pub mod init; pub mod ipc; pub mod multisig; diff --git a/fendermint/vm/core/src/gas.rs b/fendermint/vm/core/src/gas.rs deleted file mode 100644 index ad791417a2..0000000000 --- a/fendermint/vm/core/src/gas.rs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2022-2024 Protocol Labs -// SPDX-License-Identifier: Apache-2.0, MIT - -pub type Gas = u64; - -/// Handles the gas modeling in the current blockchain -pub trait GasLayer { - /// The state of the blockchain - type State; - - /// Update the block gas limit - fn set_block_gas_limit(&self, state: &mut Self::State); - - /// Obtain the current block gas limit - fn block_gas_limit(&self, state: &Self::State) -> Gas; -} \ No newline at end of file diff --git a/fendermint/vm/core/src/lib.rs b/fendermint/vm/core/src/lib.rs index 4ef87e8cfb..b1ca80e3f0 100644 --- a/fendermint/vm/core/src/lib.rs +++ b/fendermint/vm/core/src/lib.rs @@ -2,6 +2,5 @@ // SPDX-License-Identifier: Apache-2.0, MIT pub mod chainid; mod timestamp; -pub mod gas; pub use timestamp::Timestamp; diff --git a/fendermint/vm/interpreter/Cargo.toml b/fendermint/vm/interpreter/Cargo.toml index 63e591da80..73269b0f86 100644 --- a/fendermint/vm/interpreter/Cargo.toml +++ b/fendermint/vm/interpreter/Cargo.toml @@ -23,6 +23,7 @@ fendermint_rpc = { path = "../../rpc" } fendermint_tracing = { path = "../../tracing" } fendermint_actors = { path = "../../actors" } fendermint_actor_chainmetadata = { path = "../../actors/chainmetadata" } +fendermint_actor_gas = { path = "../../actors/gas" } fendermint_actor_eam = { workspace = true } fendermint_testing = { path = "../../testing", optional = true } ipc_actors_abis = { workspace = true } diff --git a/fendermint/vm/interpreter/src/fvm/exec.rs b/fendermint/vm/interpreter/src/fvm/exec.rs index f53e630a9e..e677df5875 100644 --- a/fendermint/vm/interpreter/src/fvm/exec.rs +++ b/fendermint/vm/interpreter/src/fvm/exec.rs @@ -57,6 +57,8 @@ where // Block height (FVM epoch) as sequence is intentional let height = state.block_height(); + self.gas.reset_block_gas_quota(&mut state)?; + // check for upgrades in the upgrade_scheduler let chain_id = state.chain_id(); let block_height: u64 = state.block_height().try_into().unwrap(); @@ -149,7 +151,7 @@ where async fn deliver( &self, mut state: Self::State, - msg: Self::Message, + mut msg: Self::Message, ) -> anyhow::Result<(Self::State, Self::DeliverOutput)> { let (apply_ret, emitters, latency) = if msg.from == system::SYSTEM_ACTOR_ADDR { let (execution_result, latency) = measure_time(|| state.execute_implicit(msg.clone())); @@ -157,9 +159,12 @@ where (apply_ret, emitters, latency) } else { + msg.gas_limit = msg.gas_limit.min(self.gas.available_block_gas()); + let (execution_result, latency) = measure_time(|| state.execute_explicit(msg.clone())); let (apply_ret, emitters) = execution_result?; + self.gas.deduct_block_gas_quota(apply_ret.msg_receipt.gas_used)?; (apply_ret, emitters, latency) }; diff --git a/fendermint/vm/interpreter/src/fvm/gas/default.rs b/fendermint/vm/interpreter/src/fvm/gas/default.rs new file mode 100644 index 0000000000..1964c64ea9 --- /dev/null +++ b/fendermint/vm/interpreter/src/fvm/gas/default.rs @@ -0,0 +1,108 @@ +// Copyright 2022-2024 Protocol Labs +// SPDX-License-Identifier: Apache-2.0, MIT + +use crate::fvm::gas::GasLayer; +use crate::fvm::state::{read_actor_state, FvmExecState}; +use crate::fvm::FvmMessage; +use anyhow::anyhow; +use fendermint_actor_gas::{Gas, State}; +use fendermint_vm_actor_interface::gas::GAS_ACTOR_ADDR; +use fendermint_vm_actor_interface::system; +use fvm_ipld_blockstore::Blockstore; +use std::marker::PhantomData; +use std::sync::atomic::{AtomicU64, Ordering}; + +type AtomicGas = AtomicU64; + +pub struct DefaultGas { + /// The total gas available to be used by transactions in the current block + /// The executor requires Send + Sync, using an atomic variable instead of u64. + block_gas_quota: AtomicGas, + p: PhantomData, +} + +impl DefaultGas { + pub fn new() -> Self { + Self { + block_gas_quota: AtomicGas::new(0), + p: Default::default(), + } + } + + pub fn reset_block_gas_quota(&self, state: &mut ::State) -> anyhow::Result<()> { + let old_limit = self.block_gas_quota.load(Ordering::SeqCst); + self.atomic_set_block_gas_quota(old_limit, self.block_gas_limit(state)?)?; + Ok(()) + } + + pub fn available_block_gas(&self) -> Gas { + self.block_gas_quota.load(Ordering::SeqCst) + } + + pub fn deduct_block_gas_quota(&self, gas: Gas) -> anyhow::Result<()> { + let quota = self.block_gas_quota.load(Ordering::SeqCst); + match quota.checked_sub(gas) { + Some(v) => { + self.atomic_set_block_gas_quota(quota, v)?; + Ok(()) + } + None => Err(anyhow!("out of block gas")), + } + } + + fn atomic_set_block_gas_quota(&self, old: Gas, new: Gas) -> anyhow::Result<()> { + self.block_gas_quota.compare_exchange( + old, + new, + Ordering::SeqCst, + Ordering::SeqCst + ) + .map_err(|_| anyhow!("concurrent update to block gas available, should not happen"))?; + Ok(()) + } +} + +impl GasLayer for DefaultGas +where + DB: Blockstore + Clone + 'static, +{ + type State = FvmExecState; + + fn set_block_gas_limit(&self, state: &mut Self::State, limit: Gas) -> anyhow::Result<()> { + let params = fvm_ipld_encoding::RawBytes::serialize(limit)?; + + let msg = FvmMessage { + from: system::SYSTEM_ACTOR_ADDR, + to: GAS_ACTOR_ADDR, + sequence: state.block_height() as u64, + // exclude this from gas restriction + gas_limit: u64::MAX, + method_num: fendermint_actor_gas::Method::SetBlockGasLimit as u64, + params, + value: Default::default(), + version: Default::default(), + gas_fee_cap: Default::default(), + gas_premium: Default::default(), + }; + + let (apply_ret, _) = state.execute_implicit(msg)?; + + if let Some(err) = apply_ret.failure_info { + anyhow::bail!("failed to update block gas limit: {}", err) + } else { + Ok(()) + } + } + + fn block_gas_limit(&self, state: &Self::State) -> anyhow::Result { + let s = + read_actor_state::(state, fendermint_vm_actor_interface::gas::GAS_ACTOR_ID)?; + Ok(s.block_gas_limit()) + } +} + +impl Clone for DefaultGas { + fn clone(&self) -> Self { + Self { block_gas_quota: AtomicU64::new(self.block_gas_quota.load(Ordering::SeqCst)), p: Default::default() } + } +} \ No newline at end of file diff --git a/fendermint/vm/interpreter/src/fvm/gas/mod.rs b/fendermint/vm/interpreter/src/fvm/gas/mod.rs new file mode 100644 index 0000000000..8902178259 --- /dev/null +++ b/fendermint/vm/interpreter/src/fvm/gas/mod.rs @@ -0,0 +1,19 @@ +// Copyright 2022-2024 Protocol Labs +// SPDX-License-Identifier: Apache-2.0, MIT + +use fendermint_actor_gas::Gas; + +pub(crate) mod default; + +/// Handles the gas modeling in the current blockchain +pub trait GasLayer { + /// The state of the blockchain + type State; + + /// Update the block gas limit. This will only take effect in the next block. + #[allow(dead_code)] + fn set_block_gas_limit(&self, state: &mut Self::State, limit: Gas) -> anyhow::Result<()>; + + /// Obtain the current block gas limit + fn block_gas_limit(&self, state: &Self::State) -> anyhow::Result; +} diff --git a/fendermint/vm/interpreter/src/fvm/mod.rs b/fendermint/vm/interpreter/src/fvm/mod.rs index 0aefb4b2ee..cd4eb57c31 100644 --- a/fendermint/vm/interpreter/src/fvm/mod.rs +++ b/fendermint/vm/interpreter/src/fvm/mod.rs @@ -16,8 +16,10 @@ pub mod upgrades; #[cfg(any(test, feature = "bundle"))] pub mod bundle; +pub(crate) mod gas; pub(crate) mod topdown; +use crate::fvm::gas::default::DefaultGas; pub use check::FvmCheckRet; pub use checkpoint::PowerUpdates; pub use exec::FvmApplyRet; @@ -82,6 +84,7 @@ where gateway: GatewayCaller, /// Upgrade scheduler stores all the upgrades to be executed at given heights. upgrade_scheduler: UpgradeScheduler, + gas: DefaultGas, } impl FvmMessageInterpreter @@ -107,6 +110,7 @@ where push_chain_meta: true, gateway: GatewayCaller::default(), upgrade_scheduler, + gas: DefaultGas::new(), } } diff --git a/fendermint/vm/interpreter/src/fvm/state/exec.rs b/fendermint/vm/interpreter/src/fvm/state/exec.rs index 7d5f10dda4..03794e2216 100644 --- a/fendermint/vm/interpreter/src/fvm/state/exec.rs +++ b/fendermint/vm/interpreter/src/fvm/state/exec.rs @@ -15,11 +15,12 @@ use fvm::{ DefaultKernel, }; use fvm_ipld_blockstore::Blockstore; -use fvm_ipld_encoding::RawBytes; +use fvm_ipld_encoding::{CborStore, RawBytes}; use fvm_shared::{ address::Address, chainid::ChainID, clock::ChainEpoch, econ::TokenAmount, error::ExitCode, message::Message, receipt::Receipt, version::NetworkVersion, ActorID, }; +use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use serde_with::serde_as; @@ -350,3 +351,20 @@ fn check_error(e: anyhow::Error) -> (ApplyRet, ActorAddressMap) { }; (ret, Default::default()) } + +pub(crate) fn read_actor_state( + state: &FvmExecState, + actor_id: ActorID, +) -> anyhow::Result { + let state_tree = state.state_tree(); + + let state_cid = state_tree + .get_actor(actor_id)? + .ok_or_else(|| anyhow::anyhow!("actor state not found: {}", actor_id))? + .state; + + Ok(state_tree + .store() + .get_cbor::(&state_cid)? + .ok_or_else(|| anyhow::anyhow!("actor state should not be null"))?) +} diff --git a/fendermint/vm/interpreter/src/fvm/state/mod.rs b/fendermint/vm/interpreter/src/fvm/state/mod.rs index 22a1504e4c..43098aa243 100644 --- a/fendermint/vm/interpreter/src/fvm/state/mod.rs +++ b/fendermint/vm/interpreter/src/fvm/state/mod.rs @@ -12,6 +12,7 @@ pub mod snapshot; use std::sync::Arc; pub use check::FvmCheckState; +pub(crate) use exec::read_actor_state; pub use exec::{BlockHash, FvmExecState, FvmStateParams, FvmUpdatableParams}; pub use genesis::{empty_state_tree, FvmGenesisState}; pub use query::FvmQueryState; From 2ea920379315e54a3fbcb9a79ef8d6b183340541 Mon Sep 17 00:00:00 2001 From: cryptoAtwill Date: Wed, 7 Aug 2024 11:51:10 +0800 Subject: [PATCH 13/69] review feedback --- Cargo.lock | 4 +- Cargo.toml | 2 +- fendermint/actors/gas/src/lib.rs | 155 ------------------ .../actors/{gas => gas_market}/Cargo.toml | 2 +- fendermint/actors/gas_market/src/lib.rs | 140 ++++++++++++++++ fendermint/vm/actor_interface/src/gas.rs | 2 +- fendermint/vm/interpreter/Cargo.toml | 2 +- fendermint/vm/interpreter/src/fvm/exec.rs | 6 +- .../vm/interpreter/src/fvm/gas/default.rs | 108 ------------ fendermint/vm/interpreter/src/fvm/gas/mod.rs | 116 +++++++++++-- fendermint/vm/interpreter/src/fvm/mod.rs | 6 +- 11 files changed, 259 insertions(+), 284 deletions(-) delete mode 100644 fendermint/actors/gas/src/lib.rs rename fendermint/actors/{gas => gas_market}/Cargo.toml (96%) create mode 100644 fendermint/actors/gas_market/src/lib.rs delete mode 100644 fendermint/vm/interpreter/src/fvm/gas/default.rs diff --git a/Cargo.lock b/Cargo.lock index 31c05f03fb..7579620d8c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2836,7 +2836,7 @@ dependencies = [ ] [[package]] -name = "fendermint_actor_gas" +name = "fendermint_actor_gas_market" version = "0.1.0" dependencies = [ "anyhow", @@ -3343,7 +3343,7 @@ dependencies = [ "ethers", "fendermint_actor_chainmetadata", "fendermint_actor_eam", - "fendermint_actor_gas", + "fendermint_actor_gas_market", "fendermint_actors", "fendermint_crypto", "fendermint_eth_hardhat", diff --git a/Cargo.toml b/Cargo.toml index b01e0d35ac..8de32ca161 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,7 @@ members = [ "fendermint/vm/*", "fendermint/actors", "fendermint/actors/chainmetadata", - "fendermint/actors/gas", + "fendermint/actors/gas_market", ] [workspace.package] diff --git a/fendermint/actors/gas/src/lib.rs b/fendermint/actors/gas/src/lib.rs deleted file mode 100644 index f65a710ff2..0000000000 --- a/fendermint/actors/gas/src/lib.rs +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright 2021-2023 Protocol Labs -// SPDX-License-Identifier: Apache-2.0, MIT - -use fil_actors_runtime::runtime::{ActorCode, Runtime}; -use fil_actors_runtime::ActorError; -use fil_actors_runtime::SYSTEM_ACTOR_ADDR; -use fvm_ipld_blockstore::Blockstore; -use fvm_ipld_encoding::ipld_block::IpldBlock; -use fvm_ipld_encoding::tuple::*; -use fvm_shared::{MethodNum, METHOD_CONSTRUCTOR}; -use num_derive::FromPrimitive; - -#[cfg(feature = "fil-actor")] -fil_actors_runtime::wasm_trampoline!(IPCEamActor); - -pub const IPC_GAS_ACTOR_NAME: &str = "gas"; -pub type Gas = u64; - -pub struct IPCGasActor; - -#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone)] -pub struct State { - block_gas_limit: Gas, -} - -#[derive(FromPrimitive)] -#[repr(u64)] -pub enum Method { - Constructor = METHOD_CONSTRUCTOR, - SetBlockGasLimit = frc42_dispatch::method_hash!("SetBlockGasLimit"), -} - -impl IPCGasActor { - /// Creates the actor - pub fn constructor(rt: &impl Runtime, args: ConstructorParams) -> Result<(), ActorError> { - let st = State { - block_gas_limit: args.block_gas_limit, - }; - rt.create(&st)?; - - Ok(()) - } - - fn set_block_gas_limit(rt: &impl Runtime, limit: Gas) -> Result<(), ActorError> { - if rt.message().caller() != SYSTEM_ACTOR_ADDR { - return Err(ActorError::forbidden("not system actor".into())); - } - - rt.transaction(|st: &mut State, _rt| { - st.block_gas_limit = limit; - Ok(()) - })?; - - Ok(()) - } -} - -impl ActorCode for IPCGasActor { - type Methods = Method; - - fn name() -> &'static str { - IPC_GAS_ACTOR_NAME - } - - fn invoke_method( - rt: &RT, - method: MethodNum, - params: Option, - ) -> Result, ActorError> - where - RT: Runtime, - RT::Blockstore: Blockstore + Clone, - { - if method == Method::Constructor as u64 { - fil_actors_runtime::dispatch(rt, method, Self::constructor, params) - } else if method == Method::SetBlockGasLimit as u64 { - fil_actors_runtime::dispatch(rt, method, Self::set_block_gas_limit, params) - } else { - Err(ActorError::not_found("method not found".into())) - } - } -} - -#[derive(Debug, Serialize_tuple, Deserialize_tuple)] -pub struct ConstructorParams { - block_gas_limit: Gas, -} - -impl State { - pub fn block_gas_limit(&self) -> Gas { - self.block_gas_limit - } -} - -#[cfg(test)] -mod tests { - use crate::{ConstructorParams, IPCGasActor, Method, State}; - use fil_actors_runtime::test_utils::{expect_empty, MockRuntime, SYSTEM_ACTOR_CODE_ID}; - use fil_actors_runtime::SYSTEM_ACTOR_ADDR; - use fvm_ipld_encoding::ipld_block::IpldBlock; - use fvm_shared::address::Address; - use fvm_shared::error::ExitCode; - - pub fn construct_and_verify() -> MockRuntime { - let rt = MockRuntime { - receiver: Address::new_id(10), - ..Default::default() - }; - - let result = rt - .call::( - Method::Constructor as u64, - IpldBlock::serialize_cbor(&ConstructorParams { - block_gas_limit: 100, - }) - .unwrap(), - ) - .unwrap(); - expect_empty(result); - rt.verify(); - rt.reset(); - - rt - } - - #[test] - fn test_set_ok() { - let rt = construct_and_verify(); - rt.set_caller(*SYSTEM_ACTOR_CODE_ID, SYSTEM_ACTOR_ADDR); - - let r = rt.call::( - Method::SetBlockGasLimit as u64, - IpldBlock::serialize_cbor(&20).unwrap(), - ); - assert!(r.is_ok()); - - let s = rt.get_state::(); - assert_eq!(s.block_gas_limit, 20); - } - - #[test] - fn test_not_allowed() { - let rt = construct_and_verify(); - rt.set_caller(*SYSTEM_ACTOR_CODE_ID, Address::new_id(1000)); - - let code = rt - .call::( - Method::SetBlockGasLimit as u64, - IpldBlock::serialize_cbor(&20).unwrap(), - ) - .unwrap_err() - .exit_code(); - assert_eq!(code, ExitCode::USR_FORBIDDEN) - } -} diff --git a/fendermint/actors/gas/Cargo.toml b/fendermint/actors/gas_market/Cargo.toml similarity index 96% rename from fendermint/actors/gas/Cargo.toml rename to fendermint/actors/gas_market/Cargo.toml index b6d551181f..a2ff9f0eb8 100644 --- a/fendermint/actors/gas/Cargo.toml +++ b/fendermint/actors/gas_market/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "fendermint_actor_gas" +name = "fendermint_actor_gas_market" description = "Builtin transaction gas tracking actor for IPC" license.workspace = true edition.workspace = true diff --git a/fendermint/actors/gas_market/src/lib.rs b/fendermint/actors/gas_market/src/lib.rs new file mode 100644 index 0000000000..8b0e251186 --- /dev/null +++ b/fendermint/actors/gas_market/src/lib.rs @@ -0,0 +1,140 @@ +// Copyright 2021-2023 Protocol Labs +// SPDX-License-Identifier: Apache-2.0, MIT + +use fil_actors_runtime::actor_error; +use fil_actors_runtime::runtime::{ActorCode, Runtime}; +use fil_actors_runtime::SYSTEM_ACTOR_ADDR; +use fil_actors_runtime::{actor_dispatch, ActorError}; +use fvm_ipld_encoding::tuple::*; +use fvm_shared::METHOD_CONSTRUCTOR; +use num_derive::FromPrimitive; + +#[cfg(feature = "fil-actor")] +fil_actors_runtime::wasm_trampoline!(EIP1559GasMarketActor); + +pub const IPC_GAS_ACTOR_NAME: &str = "gas"; + +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone)] +pub struct EIP1559GasState { + pub block_gas_limit: u64, +} + +pub struct EIP1559GasMarketActor {} + +#[derive(FromPrimitive)] +#[repr(u64)] +pub enum Method { + Constructor = METHOD_CONSTRUCTOR, + UpdateGasMarketState = frc42_dispatch::method_hash!("UpdateGasMarketState"), +} + +impl EIP1559GasMarketActor { + /// Creates the actor + pub fn constructor(rt: &impl Runtime, st: EIP1559GasState) -> Result<(), ActorError> { + rt.validate_immediate_caller_is(std::iter::once(&SYSTEM_ACTOR_ADDR))?; + rt.create(&st)?; + + Ok(()) + } + + fn update_gas_market_state( + rt: &impl Runtime, + new_state: EIP1559GasState, + ) -> Result<(), ActorError> { + rt.validate_immediate_caller_is(std::iter::once(&SYSTEM_ACTOR_ADDR))?; + + rt.transaction(|st: &mut EIP1559GasState, _rt| { + *st = new_state; + Ok(()) + })?; + + Ok(()) + } +} + +impl ActorCode for EIP1559GasMarketActor { + type Methods = Method; + + fn name() -> &'static str { + IPC_GAS_ACTOR_NAME + } + + actor_dispatch! { + Constructor => constructor, + UpdateGasMarketState => update_gas_market_state, + } +} + +#[cfg(test)] +mod tests { + use crate::{EIP1559GasMarketActor, EIP1559GasState, Method}; + use fil_actors_runtime::test_utils::{expect_empty, MockRuntime, SYSTEM_ACTOR_CODE_ID}; + use fil_actors_runtime::SYSTEM_ACTOR_ADDR; + use fvm_ipld_encoding::ipld_block::IpldBlock; + use fvm_shared::address::Address; + use fvm_shared::error::ExitCode; + + pub fn construct_and_verify() -> MockRuntime { + let rt = MockRuntime { + receiver: Address::new_id(10), + ..Default::default() + }; + + rt.set_caller(*SYSTEM_ACTOR_CODE_ID, SYSTEM_ACTOR_ADDR); + rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); + + let result = rt + .call::( + Method::Constructor as u64, + IpldBlock::serialize_cbor(&EIP1559GasState { + block_gas_limit: 100, + }) + .unwrap(), + ) + .unwrap(); + expect_empty(result); + rt.verify(); + rt.reset(); + + rt + } + + #[test] + fn test_set_ok() { + let rt = construct_and_verify(); + + rt.set_caller(*SYSTEM_ACTOR_CODE_ID, SYSTEM_ACTOR_ADDR); + rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); + + let r = rt.call::( + Method::UpdateGasMarketState as u64, + IpldBlock::serialize_cbor(&EIP1559GasState { + block_gas_limit: 20, + }) + .unwrap(), + ); + assert!(r.is_ok()); + + let s = rt.get_state::(); + assert_eq!(s.block_gas_limit, 20); + } + + #[test] + fn test_not_allowed() { + let rt = construct_and_verify(); + rt.set_caller(*SYSTEM_ACTOR_CODE_ID, Address::new_id(1000)); + rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); + + let code = rt + .call::( + Method::UpdateGasMarketState as u64, + IpldBlock::serialize_cbor(&EIP1559GasState { + block_gas_limit: 20, + }) + .unwrap(), + ) + .unwrap_err() + .exit_code(); + assert_eq!(code, ExitCode::USR_FORBIDDEN) + } +} diff --git a/fendermint/vm/actor_interface/src/gas.rs b/fendermint/vm/actor_interface/src/gas.rs index 75b4301242..3fae569a77 100644 --- a/fendermint/vm/actor_interface/src/gas.rs +++ b/fendermint/vm/actor_interface/src/gas.rs @@ -1,4 +1,4 @@ // Copyright 2022-2024 Protocol Labs // SPDX-License-Identifier: Apache-2.0, MIT -define_id!(GAS { id: 66 }); +define_id!(GAS { id: 98 }); diff --git a/fendermint/vm/interpreter/Cargo.toml b/fendermint/vm/interpreter/Cargo.toml index 73269b0f86..401d8141c7 100644 --- a/fendermint/vm/interpreter/Cargo.toml +++ b/fendermint/vm/interpreter/Cargo.toml @@ -23,7 +23,7 @@ fendermint_rpc = { path = "../../rpc" } fendermint_tracing = { path = "../../tracing" } fendermint_actors = { path = "../../actors" } fendermint_actor_chainmetadata = { path = "../../actors/chainmetadata" } -fendermint_actor_gas = { path = "../../actors/gas" } +fendermint_actor_gas_market = { path = "../../actors/gas_market" } fendermint_actor_eam = { workspace = true } fendermint_testing = { path = "../../testing", optional = true } ipc_actors_abis = { workspace = true } diff --git a/fendermint/vm/interpreter/src/fvm/exec.rs b/fendermint/vm/interpreter/src/fvm/exec.rs index e677df5875..249bceed84 100644 --- a/fendermint/vm/interpreter/src/fvm/exec.rs +++ b/fendermint/vm/interpreter/src/fvm/exec.rs @@ -57,7 +57,7 @@ where // Block height (FVM epoch) as sequence is intentional let height = state.block_height(); - self.gas.reset_block_gas_quota(&mut state)?; + self.gas.reset_from_chain_state(&state)?; // check for upgrades in the upgrade_scheduler let chain_id = state.chain_id(); @@ -159,12 +159,14 @@ where (apply_ret, emitters, latency) } else { + // TODO: maybe compare the gas limits are better? msg.gas_limit = msg.gas_limit.min(self.gas.available_block_gas()); let (execution_result, latency) = measure_time(|| state.execute_explicit(msg.clone())); let (apply_ret, emitters) = execution_result?; - self.gas.deduct_block_gas_quota(apply_ret.msg_receipt.gas_used)?; + self.gas + .deduct_available_block_gas(apply_ret.msg_receipt.gas_used)?; (apply_ret, emitters, latency) }; diff --git a/fendermint/vm/interpreter/src/fvm/gas/default.rs b/fendermint/vm/interpreter/src/fvm/gas/default.rs deleted file mode 100644 index 1964c64ea9..0000000000 --- a/fendermint/vm/interpreter/src/fvm/gas/default.rs +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright 2022-2024 Protocol Labs -// SPDX-License-Identifier: Apache-2.0, MIT - -use crate::fvm::gas::GasLayer; -use crate::fvm::state::{read_actor_state, FvmExecState}; -use crate::fvm::FvmMessage; -use anyhow::anyhow; -use fendermint_actor_gas::{Gas, State}; -use fendermint_vm_actor_interface::gas::GAS_ACTOR_ADDR; -use fendermint_vm_actor_interface::system; -use fvm_ipld_blockstore::Blockstore; -use std::marker::PhantomData; -use std::sync::atomic::{AtomicU64, Ordering}; - -type AtomicGas = AtomicU64; - -pub struct DefaultGas { - /// The total gas available to be used by transactions in the current block - /// The executor requires Send + Sync, using an atomic variable instead of u64. - block_gas_quota: AtomicGas, - p: PhantomData, -} - -impl DefaultGas { - pub fn new() -> Self { - Self { - block_gas_quota: AtomicGas::new(0), - p: Default::default(), - } - } - - pub fn reset_block_gas_quota(&self, state: &mut ::State) -> anyhow::Result<()> { - let old_limit = self.block_gas_quota.load(Ordering::SeqCst); - self.atomic_set_block_gas_quota(old_limit, self.block_gas_limit(state)?)?; - Ok(()) - } - - pub fn available_block_gas(&self) -> Gas { - self.block_gas_quota.load(Ordering::SeqCst) - } - - pub fn deduct_block_gas_quota(&self, gas: Gas) -> anyhow::Result<()> { - let quota = self.block_gas_quota.load(Ordering::SeqCst); - match quota.checked_sub(gas) { - Some(v) => { - self.atomic_set_block_gas_quota(quota, v)?; - Ok(()) - } - None => Err(anyhow!("out of block gas")), - } - } - - fn atomic_set_block_gas_quota(&self, old: Gas, new: Gas) -> anyhow::Result<()> { - self.block_gas_quota.compare_exchange( - old, - new, - Ordering::SeqCst, - Ordering::SeqCst - ) - .map_err(|_| anyhow!("concurrent update to block gas available, should not happen"))?; - Ok(()) - } -} - -impl GasLayer for DefaultGas -where - DB: Blockstore + Clone + 'static, -{ - type State = FvmExecState; - - fn set_block_gas_limit(&self, state: &mut Self::State, limit: Gas) -> anyhow::Result<()> { - let params = fvm_ipld_encoding::RawBytes::serialize(limit)?; - - let msg = FvmMessage { - from: system::SYSTEM_ACTOR_ADDR, - to: GAS_ACTOR_ADDR, - sequence: state.block_height() as u64, - // exclude this from gas restriction - gas_limit: u64::MAX, - method_num: fendermint_actor_gas::Method::SetBlockGasLimit as u64, - params, - value: Default::default(), - version: Default::default(), - gas_fee_cap: Default::default(), - gas_premium: Default::default(), - }; - - let (apply_ret, _) = state.execute_implicit(msg)?; - - if let Some(err) = apply_ret.failure_info { - anyhow::bail!("failed to update block gas limit: {}", err) - } else { - Ok(()) - } - } - - fn block_gas_limit(&self, state: &Self::State) -> anyhow::Result { - let s = - read_actor_state::(state, fendermint_vm_actor_interface::gas::GAS_ACTOR_ID)?; - Ok(s.block_gas_limit()) - } -} - -impl Clone for DefaultGas { - fn clone(&self) -> Self { - Self { block_gas_quota: AtomicU64::new(self.block_gas_quota.load(Ordering::SeqCst)), p: Default::default() } - } -} \ No newline at end of file diff --git a/fendermint/vm/interpreter/src/fvm/gas/mod.rs b/fendermint/vm/interpreter/src/fvm/gas/mod.rs index 8902178259..6b7244a4c1 100644 --- a/fendermint/vm/interpreter/src/fvm/gas/mod.rs +++ b/fendermint/vm/interpreter/src/fvm/gas/mod.rs @@ -1,19 +1,115 @@ // Copyright 2022-2024 Protocol Labs // SPDX-License-Identifier: Apache-2.0, MIT -use fendermint_actor_gas::Gas; +use crate::fvm::state::{read_actor_state, FvmExecState}; +use crate::fvm::FvmMessage; +use anyhow::anyhow; +use fendermint_actor_gas_market::EIP1559GasState; +use fendermint_vm_actor_interface::gas::GAS_ACTOR_ADDR; +use fendermint_vm_actor_interface::system; +use fvm_ipld_blockstore::Blockstore; +use std::marker::PhantomData; +use std::sync::atomic::{AtomicU64, Ordering}; -pub(crate) mod default; +type AtomicGas = AtomicU64; +pub type Gas = u64; -/// Handles the gas modeling in the current blockchain -pub trait GasLayer { - /// The state of the blockchain - type State; +pub struct EIP1559GasMarket { + /// The total gas available to be used by transactions in the current block + /// The executor requires Send + Sync, using an atomic variable instead of u64. + available_block_gas: AtomicGas, + _p: PhantomData, +} + +impl EIP1559GasMarket { + pub fn new() -> Self { + Self { + available_block_gas: AtomicGas::new(0), + _p: Default::default(), + } + } + + pub fn reset_from_chain_state(&self, chain_state: &FvmExecState) -> anyhow::Result<()> { + let current = self.available_block_gas.load(Ordering::SeqCst); + let gas_state = get_gas_state(chain_state)?; + self.atomic_set_block_gas_quota(current, gas_state.block_gas_limit)?; + Ok(()) + } + + pub fn available_block_gas(&self) -> Gas { + self.available_block_gas.load(Ordering::SeqCst) + } + + pub fn deduct_available_block_gas(&self, gas: Gas) -> anyhow::Result<()> { + let available = self.available_block_gas.load(Ordering::SeqCst); + match available.checked_sub(gas) { + Some(v) => { + self.atomic_set_block_gas_quota(available, v)?; + Ok(()) + } + None => Err(anyhow!("out of block gas")), + } + } + + fn atomic_set_block_gas_quota(&self, old: Gas, new: Gas) -> anyhow::Result<()> { + self.available_block_gas + .compare_exchange(old, new, Ordering::SeqCst, Ordering::SeqCst) + .map_err(|_| anyhow!("concurrent update to block gas available, should not happen"))?; + Ok(()) + } +} - /// Update the block gas limit. This will only take effect in the next block. +impl EIP1559GasMarket +where + DB: Blockstore + Clone + 'static, +{ #[allow(dead_code)] - fn set_block_gas_limit(&self, state: &mut Self::State, limit: Gas) -> anyhow::Result<()>; + fn update_state( + &self, + blockchain_state: &mut FvmExecState, + gas_state: EIP1559GasState, + ) -> anyhow::Result<()> { + let params = fvm_ipld_encoding::RawBytes::serialize(gas_state)?; + + let msg = FvmMessage { + from: system::SYSTEM_ACTOR_ADDR, + to: GAS_ACTOR_ADDR, + sequence: blockchain_state.block_height() as u64, + // exclude this from gas restriction + gas_limit: u64::MAX, + method_num: fendermint_actor_gas_market::Method::UpdateGasMarketState as u64, + params, + value: Default::default(), + version: Default::default(), + gas_fee_cap: Default::default(), + gas_premium: Default::default(), + }; + + let (apply_ret, _) = blockchain_state.execute_implicit(msg)?; + + if let Some(err) = apply_ret.failure_info { + anyhow::bail!("failed to update EIP1559 gas state: {}", err) + } else { + Ok(()) + } + } +} + +fn get_gas_state( + state: &FvmExecState, +) -> anyhow::Result { + let s = read_actor_state::( + state, + fendermint_vm_actor_interface::gas::GAS_ACTOR_ID, + )?; + Ok(s) +} - /// Obtain the current block gas limit - fn block_gas_limit(&self, state: &Self::State) -> anyhow::Result; +impl Clone for EIP1559GasMarket { + fn clone(&self) -> Self { + Self { + available_block_gas: AtomicU64::new(self.available_block_gas.load(Ordering::SeqCst)), + _p: Default::default(), + } + } } diff --git a/fendermint/vm/interpreter/src/fvm/mod.rs b/fendermint/vm/interpreter/src/fvm/mod.rs index cd4eb57c31..3c5cee6745 100644 --- a/fendermint/vm/interpreter/src/fvm/mod.rs +++ b/fendermint/vm/interpreter/src/fvm/mod.rs @@ -19,7 +19,7 @@ pub mod bundle; pub(crate) mod gas; pub(crate) mod topdown; -use crate::fvm::gas::default::DefaultGas; +use crate::fvm::gas::EIP1559GasMarket; pub use check::FvmCheckRet; pub use checkpoint::PowerUpdates; pub use exec::FvmApplyRet; @@ -84,7 +84,7 @@ where gateway: GatewayCaller, /// Upgrade scheduler stores all the upgrades to be executed at given heights. upgrade_scheduler: UpgradeScheduler, - gas: DefaultGas, + gas: EIP1559GasMarket, } impl FvmMessageInterpreter @@ -110,7 +110,7 @@ where push_chain_meta: true, gateway: GatewayCaller::default(), upgrade_scheduler, - gas: DefaultGas::new(), + gas: EIP1559GasMarket::new(), } } From b686131a054ea6d1793551a24d36886fb686c225 Mon Sep 17 00:00:00 2001 From: cryptoAtwill Date: Wed, 7 Aug 2024 11:52:03 +0800 Subject: [PATCH 14/69] demote mod to file --- fendermint/vm/interpreter/src/fvm/{gas/mod.rs => gas.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename fendermint/vm/interpreter/src/fvm/{gas/mod.rs => gas.rs} (100%) diff --git a/fendermint/vm/interpreter/src/fvm/gas/mod.rs b/fendermint/vm/interpreter/src/fvm/gas.rs similarity index 100% rename from fendermint/vm/interpreter/src/fvm/gas/mod.rs rename to fendermint/vm/interpreter/src/fvm/gas.rs From 22219f1a60a340baa16cf078a56715835b555323 Mon Sep 17 00:00:00 2001 From: cryptoAtwill <108330426+cryptoAtwill@users.noreply.github.com> Date: Wed, 7 Aug 2024 19:05:55 +0800 Subject: [PATCH 15/69] Update fendermint/vm/interpreter/src/genesis.rs Co-authored-by: Karel Moravec --- fendermint/vm/interpreter/src/genesis.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fendermint/vm/interpreter/src/genesis.rs b/fendermint/vm/interpreter/src/genesis.rs index 951bec82aa..6bca3cb4c6 100644 --- a/fendermint/vm/interpreter/src/genesis.rs +++ b/fendermint/vm/interpreter/src/genesis.rs @@ -172,7 +172,7 @@ impl GenesisCreator { async fn init_state(&self) -> anyhow::Result> { let bundle = std::fs::read(&self.builtin_actors_path).with_context(|| { format!( - "failed to read bundle: {}", + "failed to read builtin actors bundle: {}", self.builtin_actors_path.to_string_lossy() ) })?; From 6a1b4dda9a9c12b6b35347c445c7065cd18e86b7 Mon Sep 17 00:00:00 2001 From: cryptoAtwill <108330426+cryptoAtwill@users.noreply.github.com> Date: Wed, 7 Aug 2024 19:06:06 +0800 Subject: [PATCH 16/69] Update fendermint/vm/interpreter/src/genesis.rs Co-authored-by: Karel Moravec --- fendermint/vm/interpreter/src/genesis.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fendermint/vm/interpreter/src/genesis.rs b/fendermint/vm/interpreter/src/genesis.rs index 6bca3cb4c6..8729c7ac23 100644 --- a/fendermint/vm/interpreter/src/genesis.rs +++ b/fendermint/vm/interpreter/src/genesis.rs @@ -179,7 +179,7 @@ impl GenesisCreator { let custom_actors_bundle = std::fs::read(&self.custom_actors_path).with_context(|| { format!( - "failed to read custom actors_bundle: {}", + "failed to read custom actors bundle: {}", self.custom_actors_path.to_string_lossy() ) })?; From 7c6276b97cb33515a26506b3691bc1335dd11412 Mon Sep 17 00:00:00 2001 From: cryptoAtwill Date: Wed, 7 Aug 2024 22:35:24 +0800 Subject: [PATCH 17/69] review feedbacks --- Cargo.lock | 8 + Cargo.toml | 1 + fendermint/app/options/src/genesis.rs | 6 +- fendermint/app/src/app.rs | 5 +- fendermint/app/src/cmd/genesis.rs | 14 +- fendermint/vm/interpreter/Cargo.toml | 2 + fendermint/vm/interpreter/src/genesis.rs | 210 +++++------------------ 7 files changed, 68 insertions(+), 178 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ecebaa242e..21a0c7b4f1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3319,6 +3319,7 @@ dependencies = [ "arbitrary", "async-stm", "async-trait", + "base64 0.21.7", "cid", "ethers", "fendermint_actor_chainmetadata", @@ -3360,6 +3361,7 @@ dependencies = [ "serde", "serde_json", "serde_with 2.3.3", + "snap", "strum 0.26.1", "tempfile", "tendermint 0.31.1", @@ -8741,6 +8743,12 @@ version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" +[[package]] +name = "snap" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b" + [[package]] name = "snow" version = "0.9.6" diff --git a/Cargo.toml b/Cargo.toml index bb28ffb436..c40794e203 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -143,6 +143,7 @@ serde_yaml = { version = "0.9" } serde_tuple = "0.5" serde_with = "2.3" serial_test = "3.0" +snap = "1.1.0" strum = { version = "0.26.1", features = ["derive"] } tempfile = "3.7" thiserror = "1" diff --git a/fendermint/app/options/src/genesis.rs b/fendermint/app/options/src/genesis.rs index 7a115c1976..364cc8c71e 100644 --- a/fendermint/app/options/src/genesis.rs +++ b/fendermint/app/options/src/genesis.rs @@ -133,9 +133,9 @@ pub struct GenesisAddValidatorArgs { #[derive(Args, Debug)] pub struct GenesisIntoTendermintArgs { - /// Sealed genesis file that is the initial app bytes for cometbft + /// The initial app bytes path for cometbft #[arg(long, short)] - pub sealed: PathBuf, + pub app_state: Option, /// Output file name for the Tendermint genesis JSON file. #[arg(long, short)] pub out: PathBuf, @@ -151,7 +151,7 @@ pub enum GenesisIpcCommands { /// Fetch the genesis parameters of a subnet from the parent. FromParent(Box), /// Seal the genesis state from the genesis parameter file - SealState(SealGenesisArgs), + SealGenesis(SealGenesisArgs), } #[derive(Args, Debug, Clone)] diff --git a/fendermint/app/src/app.rs b/fendermint/app/src/app.rs index b2f237de5a..78997c241f 100644 --- a/fendermint/app/src/app.rs +++ b/fendermint/app/src/app.rs @@ -23,7 +23,7 @@ use fendermint_vm_interpreter::fvm::state::{ }; use fendermint_vm_interpreter::fvm::store::ReadOnlyBlockstore; use fendermint_vm_interpreter::fvm::{FvmApplyRet, FvmGenesisOutput, PowerUpdates}; -use fendermint_vm_interpreter::genesis::read_genesis_car; +use fendermint_vm_interpreter::genesis::{decode_and_decompress, read_genesis_car}; use fendermint_vm_interpreter::signed::InvalidSignature; use fendermint_vm_interpreter::{ CheckInterpreter, ExecInterpreter, GenesisInterpreter, ProposalInterpreter, QueryInterpreter, @@ -378,8 +378,9 @@ where } fn parse_genesis_app_bytes(bytes: &[u8]) -> Result> { + // cometbft serves data in json format, convert from json string match serde_json::from_slice(bytes)? { - serde_json::Value::String(s) => Ok(hex::decode(s)?), + serde_json::Value::String(s) => Ok(decode_and_decompress(&s)?), _ => Err(anyhow!("invalid app state json")), } } diff --git a/fendermint/app/src/cmd/genesis.rs b/fendermint/app/src/cmd/genesis.rs index c5c55f0141..ebd18f973e 100644 --- a/fendermint/app/src/cmd/genesis.rs +++ b/fendermint/app/src/cmd/genesis.rs @@ -14,7 +14,7 @@ use fendermint_vm_genesis::{ ipc, Account, Actor, ActorMeta, Collateral, Genesis, Multisig, PermissionMode, SignerAddr, Validator, ValidatorKey, }; -use fendermint_vm_interpreter::genesis::GenesisCreator; +use fendermint_vm_interpreter::genesis::{compress_and_encode, GenesisCreator}; use crate::cmd; use crate::options::genesis::*; @@ -95,8 +95,8 @@ cmd! { set_ipc_gateway(&genesis_file, args), GenesisIpcCommands::FromParent(args) => new_genesis_from_parent(&genesis_file, args).await, - GenesisIpcCommands::SealState(args) => - seal_state(&genesis_file, args).await, + GenesisIpcCommands::SealGenesis(args) => + seal_genesis(&genesis_file, args).await, } } } @@ -216,7 +216,10 @@ fn set_eam_permissions( fn into_tendermint(genesis_file: &PathBuf, args: &GenesisIntoTendermintArgs) -> anyhow::Result<()> { let genesis = read_genesis(genesis_file)?; - let app_state = hex::encode(std::fs::read(&args.sealed)?); + let app_state: String = match args.app_state { + None => String::from(""), + Some(ref path) => compress_and_encode(&std::fs::read(path)?)?, + }; let chain_id: u64 = chainid::from_str_hashed(&genesis.chain_name)?.into(); let chain_id = chain_id.to_string(); @@ -251,6 +254,7 @@ fn into_tendermint(genesis_file: &PathBuf, args: &GenesisIntoTendermintArgs) -> // Hopefully leaving this empty will skip validation, // otherwise we have to run the genesis in memory here and now. app_hash: tendermint::AppHash::default(), + // cometbft serves data in json format, convert to string to be specific app_state: serde_json::Value::String(app_state), }; let tmg_json = serde_json::to_string_pretty(&tmg)?; @@ -283,7 +287,7 @@ fn set_ipc_gateway(genesis_file: &PathBuf, args: &GenesisIpcGatewayArgs) -> anyh }) } -async fn seal_state(genesis_file: &PathBuf, args: &SealGenesisArgs) -> anyhow::Result<()> { +async fn seal_genesis(genesis_file: &PathBuf, args: &SealGenesisArgs) -> anyhow::Result<()> { let genesis = read_genesis(genesis_file)?; let genesis_creator = GenesisCreator::new( diff --git a/fendermint/vm/interpreter/Cargo.toml b/fendermint/vm/interpreter/Cargo.toml index 63e591da80..20ca5b0a29 100644 --- a/fendermint/vm/interpreter/Cargo.toml +++ b/fendermint/vm/interpreter/Cargo.toml @@ -33,6 +33,7 @@ ipc-observability = { workspace = true } async-trait = { workspace = true } async-stm = { workspace = true } anyhow = { workspace = true } +base64 = { workspace = true } ethers = { workspace = true } hex = { workspace = true } num-traits = { workspace = true } @@ -58,6 +59,7 @@ futures-util = { workspace = true } libipld = { workspace = true } tokio = { workspace = true } pin-project = { workspace = true } +snap = { workspace = true } tokio-stream = { workspace = true } tokio-util = { workspace = true } diff --git a/fendermint/vm/interpreter/src/genesis.rs b/fendermint/vm/interpreter/src/genesis.rs index 951bec82aa..8375cec73b 100644 --- a/fendermint/vm/interpreter/src/genesis.rs +++ b/fendermint/vm/interpreter/src/genesis.rs @@ -2,12 +2,14 @@ // SPDX-License-Identifier: Apache-2.0, MIT use std::collections::{BTreeSet, HashMap}; +use std::io::{Read, Write}; use std::marker::PhantomData; use std::path::{Path, PathBuf}; use std::pin::Pin; use std::sync::Arc; use anyhow::{anyhow, Context}; +use base64::Engine; use cid::Cid; use ethers::abi::Tokenize; use ethers::core::types as et; @@ -68,6 +70,22 @@ impl GenesisMetadata { } } +pub fn compress_and_encode(bytes: &[u8]) -> anyhow::Result { + let mut wtr = snap::write::FrameEncoder::new(vec![]); + wtr.write_all(bytes)?; + let compressed = wtr.into_inner()?; + Ok(base64::engine::general_purpose::STANDARD.encode(compressed)) +} + +pub fn decode_and_decompress(raw: &str) -> anyhow::Result> { + let bytes = base64::engine::general_purpose::STANDARD.decode(raw)?; + + let mut buf = vec![]; + snap::read::FrameDecoder::new(bytes.as_slice()).read_to_end(&mut buf)?; + + Ok(buf) +} + pub async fn read_genesis_car( bytes: Vec, store: &DB, @@ -108,7 +126,7 @@ pub struct GenesisCreator { /// The custom actors bundle path custom_actors_path: PathBuf, /// The CAR path to flush the sealed genesis state - sealed_out_path: PathBuf, + out_path: PathBuf, } impl GenesisCreator { @@ -122,7 +140,7 @@ impl GenesisCreator { hardhat: Hardhat::new(artifacts_path), builtin_actors_path, custom_actors_path, - sealed_out_path, + out_path: sealed_out_path, } } @@ -140,7 +158,7 @@ impl GenesisCreator { out: GenesisOutput, store: MemoryBlockstore, ) -> anyhow::Result<()> { - let file = tokio::fs::File::create(&self.sealed_out_path).await?; + let file = tokio::fs::File::create(&self.out_path).await?; tracing::info!(state_root = state_root.to_string(), "state root"); @@ -156,13 +174,9 @@ impl GenesisCreator { // create the stream to stream all the data into the car file let mut streamer = tokio_stream::iter(vec![(metadata_cid, metadata_bytes)]).merge(streamer); - let write_task = tokio::spawn(async move { - let mut write = file.compat_write(); - car.write_stream_async(&mut Pin::new(&mut write), &mut streamer) - .await - }); - - write_task.await??; + let mut write = file.compat_write(); + car.write_stream_async(&mut Pin::new(&mut write), &mut streamer) + .await?; tracing::info!("written sealed genesis state to file"); @@ -515,7 +529,7 @@ where } /// Deploy a library contract with a dynamic ID and no constructor. - pub fn deploy_library( + fn deploy_library( &mut self, state: &mut FvmGenesisState, next_id: &mut u64, @@ -554,7 +568,7 @@ where } /// Construct the bytecode of a top-level contract and deploy it with some constructor parameters. - pub fn deploy_contract( + fn deploy_contract( &self, state: &mut FvmGenesisState, contract_name: &str, @@ -592,7 +606,7 @@ where } /// Collect Facet Cuts for the diamond pattern, where the facet address comes from already deployed library facets. - pub fn facets(&self, contract_name: &str) -> anyhow::Result> { + fn facets(&self, contract_name: &str) -> anyhow::Result> { let contract = self.top_contract(contract_name)?; let mut facet_cuts = Vec::new(); @@ -638,158 +652,18 @@ fn circ_supply(g: &Genesis) -> TokenAmount { .iter() .fold(TokenAmount::zero(), |s, a| s + a.balance.clone()) } -// -// #[cfg(test)] -// mod tests { -// use std::{str::FromStr, sync::Arc}; -// -// use cid::Cid; -// use fendermint_vm_genesis::{ipc::IpcParams, Genesis}; -// use fvm::engine::MultiEngine; -// use quickcheck::Arbitrary; -// use tendermint_rpc::{MockClient, MockRequestMethodMatcher}; -// -// use crate::{ -// fvm::{ -// bundle::{bundle_path, contracts_path, custom_actors_bundle_path}, -// state::ipc::GatewayCaller, -// store::memory::MemoryBlockstore, -// upgrades::UpgradeScheduler, -// FvmMessageInterpreter, -// }, -// GenesisInterpreter, -// }; -// -// use super::FvmGenesisState; -// -// #[tokio::test] -// async fn load_genesis() { -// let genesis = make_genesis(); -// let bundle = read_bundle(); -// let custom_actors_bundle = read_custom_actors_bundle(); -// let interpreter = make_interpreter(); -// -// let multi_engine = Arc::new(MultiEngine::default()); -// let store = MemoryBlockstore::new(); -// -// let state = FvmGenesisState::new(store, multi_engine, &bundle, &custom_actors_bundle) -// .await -// .expect("failed to create state"); -// -// let (mut state, out) = interpreter -// .init(state, genesis.clone()) -// .await -// .expect("failed to create actors"); -// -// assert_eq!(out.validators.len(), genesis.validators.len()); -// -// // Try calling a method on the IPC Gateway. -// let exec_state = state.exec_state().expect("should be in exec stage"); -// let caller = GatewayCaller::default(); -// -// let period = caller -// .bottom_up_check_period(exec_state) -// .expect("error calling the gateway"); -// -// assert_eq!(period, genesis.ipc.unwrap().gateway.bottom_up_check_period); -// -// let _state_root = state.commit().expect("failed to commit"); -// } -// -// #[tokio::test] -// async fn load_genesis_deterministic() { -// let genesis = make_genesis(); -// let bundle = read_bundle(); -// let custom_actors_bundle = read_custom_actors_bundle(); -// let interpreter = make_interpreter(); -// let multi_engine = Arc::new(MultiEngine::default()); -// -// // Create a couple of states and load the same thing. -// let mut outputs = Vec::new(); -// for _ in 0..3 { -// let store = MemoryBlockstore::new(); -// let state = -// FvmGenesisState::new(store, multi_engine.clone(), &bundle, &custom_actors_bundle) -// .await -// .expect("failed to create state"); -// -// let (state, out) = interpreter -// .init(state, genesis.clone()) -// .await -// .expect("failed to create actors"); -// -// let state_root_hash = state.commit().expect("failed to commit"); -// outputs.push((state_root_hash, out)); -// } -// -// for out in &outputs[1..] { -// assert_eq!(out.0, outputs[0].0, "state root hash is different"); -// } -// } -// -// // This is a sort of canary test, if it fails means something changed in the way we do genesis, -// // which is probably fine, but it's better to know about it, and if anybody doesn't get the same -// // then we might have some non-determinism. -// #[ignore] // I see a different value on CI than locally. -// #[tokio::test] -// async fn load_genesis_known() { -// let genesis_json = "{\"chain_name\":\"/r314159/f410fnfmitm2ww7oehhtbokf6wulhrr62sgq3sgqmenq\",\"timestamp\":1073250,\"network_version\":18,\"base_fee\":\"1000\",\"power_scale\":3,\"validators\":[{\"public_key\":\"BLX9ojqB+8Z26aMmKoCRb3Te6AnSU6zY8hPcf1X5Q69XCNaHVcRxzYO2xx7o/2vgdS7nkDTMRRbkDGzy+FYdAFc=\",\"power\":\"1000000000000000000\"},{\"public_key\":\"BFcOveVieknZiscWsfXa06aGbBkKeucBycd/w0N1QHlaZfa/5dJcH7D0hvcdfv3B2Rv1OPuxo1PkgsEbWegWKcA=\",\"power\":\"1000000000000000000\"},{\"public_key\":\"BEP30ykovfrQp3zo+JVRvDVL2emC+Ju1Kpox3zMVYZyFKvYt64qyN/HOVjridDrkEsnQU8BVen4Aegja4fBZ+LU=\",\"power\":\"1000000000000000000\"}],\"accounts\":[{\"meta\":{\"Account\":{\"owner\":\"f410fggjevhgketpz6gw6ordusynlgcd5piyug4aomuq\"}},\"balance\":\"1000000000000000000\"},{\"meta\":{\"Account\":{\"owner\":\"f410frbdnwklaitcjsqe7swjwp5naple6vthq4woyfry\"}},\"balance\":\"2000000000000000000\"},{\"meta\":{\"Account\":{\"owner\":\"f410fxo4lih4n2acr3oadalidwqjgoqkzhp5dw3zwkvy\"}},\"balance\":\"1000000000000000000\"}],\"ipc\":{\"gateway\":{\"subnet_id\":\"/r314159/f410fnfmitm2ww7oehhtbokf6wulhrr62sgq3sgqmenq\",\"bottom_up_check_period\":30,\"msg_fee\":\"1000000000000\",\"majority_percentage\":60,\"active_validators_limit\":100}}}"; -// -// let genesis: Genesis = serde_json::from_str(genesis_json).expect("failed to parse genesis"); -// -// let bundle = read_bundle(); -// let custom_actors_bundle = read_custom_actors_bundle(); -// let interpreter = make_interpreter(); -// let multi_engine = Arc::new(MultiEngine::default()); -// -// let store = MemoryBlockstore::new(); -// let state = -// FvmGenesisState::new(store, multi_engine.clone(), &bundle, &custom_actors_bundle) -// .await -// .expect("failed to create state"); -// -// let (state, _) = interpreter -// .init(state, genesis.clone()) -// .await -// .expect("failed to create actors"); -// -// let state_root_hash = state.commit().expect("failed to commit"); -// -// let expected_root_hash = -// Cid::from_str("bafy2bzacedebgy4j7qnh2v2x4kkr2jqfkryql5ookbjrwge6dbrr24ytlqnj4") -// .unwrap(); -// -// assert_eq!(state_root_hash, expected_root_hash); -// } -// -// fn make_genesis() -> Genesis { -// let mut g = quickcheck::Gen::new(5); -// let mut genesis = Genesis::arbitrary(&mut g); -// -// // Make sure we have IPC enabled. -// genesis.ipc = Some(IpcParams::arbitrary(&mut g)); -// genesis -// } -// -// fn make_interpreter( -// ) -> FvmMessageInterpreter> { -// let (client, _) = MockClient::new(MockRequestMethodMatcher::default()); -// FvmMessageInterpreter::new( -// client, -// None, -// contracts_path(), -// 1.05, -// 1.05, -// false, -// UpgradeScheduler::new(), -// ) -// } -// -// fn read_bundle() -> Vec { -// std::fs::read(bundle_path()).expect("failed to read bundle") -// } -// -// fn read_custom_actors_bundle() -> Vec { -// std::fs::read(custom_actors_bundle_path()).expect("failed to read custom actor bundle") -// } -// } + +#[cfg(test)] +mod tests { + use crate::genesis::{compress_and_encode, decode_and_decompress}; + + #[test] + fn test_compression() { + let bytes = (0..10000).map(|_| rand::random::()).collect::>(); + + let s = compress_and_encode(&bytes).unwrap(); + let recovered = decode_and_decompress(&s).unwrap(); + + assert_eq!(recovered, bytes); + } +} \ No newline at end of file From fa0f3308a7cfefe747f6c3f05d05eeca909eb188 Mon Sep 17 00:00:00 2001 From: cryptoAtwill Date: Thu, 8 Aug 2024 00:26:21 +0800 Subject: [PATCH 18/69] add genesis app state schema --- fendermint/app/src/app.rs | 6 ++- fendermint/app/src/cmd/genesis.rs | 4 +- fendermint/vm/interpreter/src/genesis.rs | 68 ++++++++++++++++++------ 3 files changed, 59 insertions(+), 19 deletions(-) diff --git a/fendermint/app/src/app.rs b/fendermint/app/src/app.rs index 78997c241f..877616b170 100644 --- a/fendermint/app/src/app.rs +++ b/fendermint/app/src/app.rs @@ -23,7 +23,9 @@ use fendermint_vm_interpreter::fvm::state::{ }; use fendermint_vm_interpreter::fvm::store::ReadOnlyBlockstore; use fendermint_vm_interpreter::fvm::{FvmApplyRet, FvmGenesisOutput, PowerUpdates}; -use fendermint_vm_interpreter::genesis::{decode_and_decompress, read_genesis_car}; +use fendermint_vm_interpreter::genesis::{ + decode_and_decompress, read_genesis_car, GenesisAppState, +}; use fendermint_vm_interpreter::signed::InvalidSignature; use fendermint_vm_interpreter::{ CheckInterpreter, ExecInterpreter, GenesisInterpreter, ProposalInterpreter, QueryInterpreter, @@ -380,7 +382,7 @@ where fn parse_genesis_app_bytes(bytes: &[u8]) -> Result> { // cometbft serves data in json format, convert from json string match serde_json::from_slice(bytes)? { - serde_json::Value::String(s) => Ok(decode_and_decompress(&s)?), + serde_json::Value::String(s) => Ok(GenesisAppState::decode_and_decompress(&s)?), _ => Err(anyhow!("invalid app state json")), } } diff --git a/fendermint/app/src/cmd/genesis.rs b/fendermint/app/src/cmd/genesis.rs index ebd18f973e..d525fc496a 100644 --- a/fendermint/app/src/cmd/genesis.rs +++ b/fendermint/app/src/cmd/genesis.rs @@ -14,7 +14,7 @@ use fendermint_vm_genesis::{ ipc, Account, Actor, ActorMeta, Collateral, Genesis, Multisig, PermissionMode, SignerAddr, Validator, ValidatorKey, }; -use fendermint_vm_interpreter::genesis::{compress_and_encode, GenesisCreator}; +use fendermint_vm_interpreter::genesis::{compress_and_encode, GenesisAppState, GenesisCreator}; use crate::cmd; use crate::options::genesis::*; @@ -218,7 +218,7 @@ fn into_tendermint(genesis_file: &PathBuf, args: &GenesisIntoTendermintArgs) -> let genesis = read_genesis(genesis_file)?; let app_state: String = match args.app_state { None => String::from(""), - Some(ref path) => compress_and_encode(&std::fs::read(path)?)?, + Some(ref path) => GenesisAppState::v1(std::fs::read(path)?).compress_and_encode()?, }; let chain_id: u64 = chainid::from_str_hashed(&genesis.chain_name)?.into(); diff --git a/fendermint/vm/interpreter/src/genesis.rs b/fendermint/vm/interpreter/src/genesis.rs index 2f8e0ab955..494721c332 100644 --- a/fendermint/vm/interpreter/src/genesis.rs +++ b/fendermint/vm/interpreter/src/genesis.rs @@ -70,20 +70,54 @@ impl GenesisMetadata { } } -pub fn compress_and_encode(bytes: &[u8]) -> anyhow::Result { - let mut wtr = snap::write::FrameEncoder::new(vec![]); - wtr.write_all(bytes)?; - let compressed = wtr.into_inner()?; - Ok(base64::engine::general_purpose::STANDARD.encode(compressed)) +/// Genesis app state wrapper for cometbft +pub enum GenesisAppState { + V1(Vec), } -pub fn decode_and_decompress(raw: &str) -> anyhow::Result> { - let bytes = base64::engine::general_purpose::STANDARD.decode(raw)?; +impl GenesisAppState { + pub fn v1(bytes: Vec) -> Self { + Self::V1(bytes) + } + + pub fn into_bytes(self) -> Vec { + match self { + Self::V1(b) => b, + } + } + + pub fn compress_and_encode(&self) -> anyhow::Result { + let bytes = match self { + GenesisAppState::V1(ref bytes) => { + let mut wtr = snap::write::FrameEncoder::new(vec![]); + wtr.write_all(bytes)?; + let compressed = wtr.into_inner()?; + + let mut res = vec![0]; + res.extend(compressed); + + res + } + }; + + Ok(base64::engine::general_purpose::STANDARD.encode(bytes)) + } - let mut buf = vec![]; - snap::read::FrameDecoder::new(bytes.as_slice()).read_to_end(&mut buf)?; + pub fn decode_and_decompress(raw: &str) -> anyhow::Result> { + let bytes = base64::engine::general_purpose::STANDARD.decode(raw)?; + if bytes.is_empty() { + return Err(anyhow!("empty bytes for genesis app state")); + } - Ok(buf) + match bytes[0] { + 0 => { + let mut buf = vec![]; + snap::read::FrameDecoder::new(&bytes.as_slice()[1..]).read_to_end(&mut buf)?; + Ok(buf) + } + _ => Err(anyhow!("not supported schema versioin")), + } + } } pub async fn read_genesis_car( @@ -655,15 +689,19 @@ fn circ_supply(g: &Genesis) -> TokenAmount { #[cfg(test)] mod tests { - use crate::genesis::{compress_and_encode, decode_and_decompress}; + use crate::genesis::GenesisAppState; #[test] fn test_compression() { - let bytes = (0..10000).map(|_| rand::random::()).collect::>(); + let bytes = (0..10000) + .map(|_| rand::random::()) + .collect::>(); - let s = compress_and_encode(&bytes).unwrap(); - let recovered = decode_and_decompress(&s).unwrap(); + let s = GenesisAppState::v1(bytes.clone()) + .compress_and_encode() + .unwrap(); + let recovered = GenesisAppState::decode_and_decompress(&s).unwrap(); assert_eq!(recovered, bytes); } -} \ No newline at end of file +} From b0038cc5ed8ccab0b16148a4652bc6fbddc336b2 Mon Sep 17 00:00:00 2001 From: cryptoAtwill Date: Thu, 8 Aug 2024 12:01:52 +0800 Subject: [PATCH 19/69] update infra and simplify genesis --- fendermint/app/options/src/genesis.rs | 2 +- fendermint/app/src/app.rs | 4 +- fendermint/app/src/cmd/genesis.rs | 12 +- fendermint/testing/graph-test/scripts/init.sh | 4 +- .../testing/materializer/src/docker/node.rs | 4 +- fendermint/testing/smoke-test/scripts/init.sh | 4 +- .../testing/snapshot-test/scripts/init.sh | 4 +- fendermint/vm/interpreter/src/genesis.rs | 257 ++++++++++-------- infra/fendermint/Makefile.toml | 3 + infra/fendermint/scripts/genesis.toml | 6 +- infra/fendermint/scripts/subnet.toml | 1 + 11 files changed, 172 insertions(+), 129 deletions(-) diff --git a/fendermint/app/options/src/genesis.rs b/fendermint/app/options/src/genesis.rs index 364cc8c71e..740384758c 100644 --- a/fendermint/app/options/src/genesis.rs +++ b/fendermint/app/options/src/genesis.rs @@ -167,7 +167,7 @@ pub struct SealGenesisArgs { /// The solidity artifacts output path. If you are using ipc-monorepo, it should be the `out` folder /// of `make build` #[arg(long, short)] - pub artifacts_path: PathBuf, + pub artifacts_path: Option, /// The sealed genesis state output path, i.e. finalized genesis state CAR file dump path #[arg(long, short)] diff --git a/fendermint/app/src/app.rs b/fendermint/app/src/app.rs index 877616b170..af4e8da72b 100644 --- a/fendermint/app/src/app.rs +++ b/fendermint/app/src/app.rs @@ -23,9 +23,7 @@ use fendermint_vm_interpreter::fvm::state::{ }; use fendermint_vm_interpreter::fvm::store::ReadOnlyBlockstore; use fendermint_vm_interpreter::fvm::{FvmApplyRet, FvmGenesisOutput, PowerUpdates}; -use fendermint_vm_interpreter::genesis::{ - decode_and_decompress, read_genesis_car, GenesisAppState, -}; +use fendermint_vm_interpreter::genesis::{read_genesis_car, GenesisAppState}; use fendermint_vm_interpreter::signed::InvalidSignature; use fendermint_vm_interpreter::{ CheckInterpreter, ExecInterpreter, GenesisInterpreter, ProposalInterpreter, QueryInterpreter, diff --git a/fendermint/app/src/cmd/genesis.rs b/fendermint/app/src/cmd/genesis.rs index d525fc496a..598c4a3c79 100644 --- a/fendermint/app/src/cmd/genesis.rs +++ b/fendermint/app/src/cmd/genesis.rs @@ -14,7 +14,7 @@ use fendermint_vm_genesis::{ ipc, Account, Actor, ActorMeta, Collateral, Genesis, Multisig, PermissionMode, SignerAddr, Validator, ValidatorKey, }; -use fendermint_vm_interpreter::genesis::{compress_and_encode, GenesisAppState, GenesisCreator}; +use fendermint_vm_interpreter::genesis::{GenesisAppState, GenesisCreator}; use crate::cmd; use crate::options::genesis::*; @@ -216,9 +216,11 @@ fn set_eam_permissions( fn into_tendermint(genesis_file: &PathBuf, args: &GenesisIntoTendermintArgs) -> anyhow::Result<()> { let genesis = read_genesis(genesis_file)?; - let app_state: String = match args.app_state { - None => String::from(""), - Some(ref path) => GenesisAppState::v1(std::fs::read(path)?).compress_and_encode()?, + let app_state: Option = match args.app_state { + Some(ref path) if path.exists() => { + Some(GenesisAppState::v1(std::fs::read(path)?).compress_and_encode()?) + } + _ => None, }; let chain_id: u64 = chainid::from_str_hashed(&genesis.chain_name)?.into(); @@ -255,7 +257,7 @@ fn into_tendermint(genesis_file: &PathBuf, args: &GenesisIntoTendermintArgs) -> // otherwise we have to run the genesis in memory here and now. app_hash: tendermint::AppHash::default(), // cometbft serves data in json format, convert to string to be specific - app_state: serde_json::Value::String(app_state), + app_state, }; let tmg_json = serde_json::to_string_pretty(&tmg)?; std::fs::write(&args.out, tmg_json)?; diff --git a/fendermint/testing/graph-test/scripts/init.sh b/fendermint/testing/graph-test/scripts/init.sh index 8fc376fb1d..2e76741eee 100755 --- a/fendermint/testing/graph-test/scripts/init.sh +++ b/fendermint/testing/graph-test/scripts/init.sh @@ -52,7 +52,7 @@ fendermint \ fendermint \ genesis --genesis-file $GENESIS_FILE \ ipc \ - seal-state \ + seal-genesis \ --builtin-actors-path /fendermint/bundle.car \ --custom-actors-path /fendermint/custom_actors_bundle.car \ --artifacts-path /fendermint/contracts \ @@ -61,7 +61,7 @@ fendermint \ # Convert FM genesis to CMT fendermint \ genesis --genesis-file $GENESIS_FILE \ - into-tendermint --out $CMT_DIR/config/genesis.json --sealed "${SEALED_GENESIS_FILE}" + into-tendermint --out $CMT_DIR/config/genesis.json --app-state "${SEALED_GENESIS_FILE}" # Copy the default validator key cp $KEYS_DIR/$VALIDATOR_NAME.priv_validator_key.json \ diff --git a/fendermint/testing/materializer/src/docker/node.rs b/fendermint/testing/materializer/src/docker/node.rs index cfa9c79b91..128ab78fff 100644 --- a/fendermint/testing/materializer/src/docker/node.rs +++ b/fendermint/testing/materializer/src/docker/node.rs @@ -210,7 +210,7 @@ impl DockerNode { "genesis \ --genesis-file /fendermint/genesis.json \ ipc \ - seal-state \ + seal-genesis \ --builtin-actors-path /fendermint/bundle.car \ --custom-actors-path /fendermint/custom_actors_bundle.car \ --artifacts-path /fendermint/contracts \ @@ -227,7 +227,7 @@ impl DockerNode { --genesis-file /fendermint/genesis.json \ into-tendermint \ --out /cometbft/config/genesis.json \ - --sealed /cometbft/config/sealed.json \ + --app-state /cometbft/config/sealed.json \ ", ) .await diff --git a/fendermint/testing/smoke-test/scripts/init.sh b/fendermint/testing/smoke-test/scripts/init.sh index c3fa8d63a5..0728e6eabe 100755 --- a/fendermint/testing/smoke-test/scripts/init.sh +++ b/fendermint/testing/smoke-test/scripts/init.sh @@ -68,7 +68,7 @@ fendermint \ fendermint \ genesis --genesis-file $GENESIS_FILE \ ipc \ - seal-state \ + seal-genesis \ --builtin-actors-path /fendermint/bundle.car \ --custom-actors-path /fendermint/custom_actors_bundle.car \ --artifacts-path /fendermint/contracts \ @@ -77,7 +77,7 @@ fendermint \ # Convert FM genesis to CMT fendermint \ genesis --genesis-file $GENESIS_FILE \ - into-tendermint --out $CMT_DIR/config/genesis.json --sealed "${SEALED_GENESIS_FILE}" + into-tendermint --out $CMT_DIR/config/genesis.json --app-state "${SEALED_GENESIS_FILE}" # Convert FM validator key to CMT fendermint \ diff --git a/fendermint/testing/snapshot-test/scripts/init.sh b/fendermint/testing/snapshot-test/scripts/init.sh index 9e75058d99..1639e36f78 100755 --- a/fendermint/testing/snapshot-test/scripts/init.sh +++ b/fendermint/testing/snapshot-test/scripts/init.sh @@ -47,7 +47,7 @@ fendermint \ fendermint \ genesis --genesis-file $GENESIS_FILE \ ipc \ - seal-state \ + seal-genesis \ --builtin-actors-path /fendermint/bundle.car \ --custom-actors-path /fendermint/custom_actors_bundle.car \ --artifacts-path /fendermint/contracts \ @@ -56,7 +56,7 @@ fendermint \ # Convert FM genesis to CMT fendermint \ genesis --genesis-file $GENESIS_FILE \ - into-tendermint --out $CMT_DIR/config/genesis.json --sealed "${SEALED_GENESIS_FILE}" + into-tendermint --out $CMT_DIR/config/genesis.json --app-state "${SEALED_GENESIS_FILE}" # Copy the default validator key cp $KEYS_DIR/$VALIDATOR_NAME.priv_validator_key.json \ diff --git a/fendermint/vm/interpreter/src/genesis.rs b/fendermint/vm/interpreter/src/genesis.rs index 494721c332..11df5c68f4 100644 --- a/fendermint/vm/interpreter/src/genesis.rs +++ b/fendermint/vm/interpreter/src/genesis.rs @@ -14,7 +14,7 @@ use cid::Cid; use ethers::abi::Tokenize; use ethers::core::types as et; use fendermint_actor_eam::PermissionModeParams; -use fendermint_eth_hardhat::{Hardhat, FQN}; +use fendermint_eth_hardhat::{ContractSourceAndName, Hardhat, FQN}; use fendermint_vm_actor_interface::diamond::{EthContract, EthContractMap}; use fendermint_vm_actor_interface::eam::EthAddress; use fendermint_vm_actor_interface::ipc::IPC_CONTRACTS; @@ -22,7 +22,7 @@ use fendermint_vm_actor_interface::{ account, burntfunds, chainmetadata, cron, eam, init, ipc, reward, system, EMPTY_ARR, }; use fendermint_vm_core::{chainid, Timestamp}; -use fendermint_vm_genesis::{ActorMeta, Genesis, Power, PowerScale, Validator}; +use fendermint_vm_genesis::{ActorMeta, Collateral, Genesis, Power, PowerScale, Validator}; use futures_util::io::Cursor; use fvm::engine::MultiEngine; use fvm_ipld_blockstore::Blockstore; @@ -37,6 +37,7 @@ use num_traits::Zero; use crate::fvm::state::snapshot::{derive_cid, StateTreeStreamer}; use crate::fvm::state::{FvmGenesisState, FvmStateParams}; use crate::fvm::store::memory::MemoryBlockstore; +use fendermint_vm_genesis::ipc::IpcParams; use serde::{Deserialize, Serialize}; use serde_with::serde_as; use tokio_stream::StreamExt; @@ -80,23 +81,12 @@ impl GenesisAppState { Self::V1(bytes) } - pub fn into_bytes(self) -> Vec { - match self { - Self::V1(b) => b, - } - } - pub fn compress_and_encode(&self) -> anyhow::Result { let bytes = match self { GenesisAppState::V1(ref bytes) => { - let mut wtr = snap::write::FrameEncoder::new(vec![]); + let mut wtr = snap::write::FrameEncoder::new(vec![0]); wtr.write_all(bytes)?; - let compressed = wtr.into_inner()?; - - let mut res = vec![0]; - res.extend(compressed); - - res + wtr.into_inner()? } }; @@ -154,7 +144,7 @@ pub struct GenesisOutput { pub struct GenesisCreator { /// Hardhat like util to deploy ipc contracts - hardhat: Hardhat, + hardhat: Option, /// The built in actors bundle path builtin_actors_path: PathBuf, /// The custom actors bundle path @@ -167,11 +157,11 @@ impl GenesisCreator { pub fn new( builtin_actors_path: PathBuf, custom_actors_path: PathBuf, - artifacts_path: PathBuf, + maybe_artifacts_path: Option, sealed_out_path: PathBuf, ) -> Self { Self { - hardhat: Hardhat::new(artifacts_path), + hardhat: maybe_artifacts_path.map(Hardhat::new), builtin_actors_path, custom_actors_path, out_path: sealed_out_path, @@ -244,6 +234,19 @@ impl GenesisCreator { .context("failed to create genesis state") } + fn handle_ipc<'a, T, F: Fn(&'a Hardhat, &'a IpcParams) -> T>( + &'a self, + maybe_ipc: Option<&'a IpcParams>, + f: F, + ) -> anyhow::Result> { + // Only allocate IDs if the contracts are deployed. + match (maybe_ipc, &self.hardhat) { + (Some(ipc_params), Some(ref hardhat)) => Ok(Some(f(hardhat, ipc_params))), + (Some(_), None) => Err(anyhow!("ipc enabled but artifacts path not provided")), + _ => Ok(None), + } + } + fn populate_state( &self, state: &mut FvmGenesisState, @@ -277,38 +280,14 @@ impl GenesisCreator { }; // STAGE 0: Declare the built-in EVM contracts we'll have to deploy. - - // Pre-defined IDs for top-level Ethereum contracts. - let mut all_contracts = Vec::new(); - let mut top_level_contracts = EthContractMap::default(); - - // Only allocate IDs if the contracts are deployed. - if genesis.ipc.is_some() { - top_level_contracts.extend(IPC_CONTRACTS.clone()); - } - - all_contracts.extend(top_level_contracts.keys()); - all_contracts.extend( - top_level_contracts - .values() - .flat_map(|c| c.facets.iter().map(|f| f.name)), - ); - // Collect dependencies of the main IPC actors. - let mut eth_libs = self - .hardhat - .dependencies( - &all_contracts - .iter() - .map(|n| (contract_src(n), *n)) - .collect::>(), - ) - .context("failed to collect EVM contract dependencies")?; - - // Only keep library dependencies, not contracts with constructors. - eth_libs.retain(|(_, d)| !top_level_contracts.contains_key(d.as_str())); + // ipc_entrypoints contains the external user facing contracts + // all_ipc_contracts contains ipc_entrypoints + util contracts + let (all_ipc_contracts, ipc_entrypoints) = self + .handle_ipc(genesis.ipc.as_ref(), |h, _| collect_contracts(h))? + .transpose()? + .unwrap_or((Vec::new(), EthContractMap::new())); // STAGE 1: First we initialize native built-in actors. - // System actor state .create_builtin_actor( @@ -327,11 +306,11 @@ impl GenesisCreator { state.store(), genesis.chain_name.clone(), &genesis.accounts, - &top_level_contracts + &ipc_entrypoints .values() .map(|c| c.actor_id) .collect::>(), - eth_libs.len() as u64, + all_ipc_contracts.len() as u64, ) .context("failed to create init state")?; @@ -466,75 +445,131 @@ impl GenesisCreator { ) .context("failed to init exec state")?; - let mut deployer = - ContractDeployer::::new(&self.hardhat, &top_level_contracts); - - // Deploy Ethereum libraries. - for (lib_src, lib_name) in eth_libs { - deployer.deploy_library(state, &mut next_id, lib_src, &lib_name)?; + let validators = genesis.validators; + { + let (hardhat, ipc_params) = self + .handle_ipc(genesis.ipc.as_ref(), |hardhat, ipc_params| { + (hardhat, ipc_params) + })? + .unwrap(); + deploy_contracts( + all_ipc_contracts, + &ipc_entrypoints, + validators, + next_id, + state, + ipc_params, + hardhat, + )?; } - if let Some(ipc_params) = genesis.ipc { - // IPC Gateway actor. - let gateway_addr = { - use ipc::gateway::ConstructorParameters; + Ok(out) + } +} - let params = ConstructorParameters::new(ipc_params.gateway, genesis.validators) - .context("failed to create gateway constructor")?; +fn collect_contracts( + hardhat: &Hardhat, +) -> anyhow::Result<(Vec, EthContractMap)> { + let mut all_contracts = Vec::new(); + let mut top_level_contracts = EthContractMap::default(); + + top_level_contracts.extend(IPC_CONTRACTS.clone()); + + all_contracts.extend(top_level_contracts.keys()); + all_contracts.extend( + top_level_contracts + .values() + .flat_map(|c| c.facets.iter().map(|f| f.name)), + ); + // Collect dependencies of the main IPC actors. + let mut eth_libs = hardhat + .dependencies( + &all_contracts + .iter() + .map(|n| (contract_src(n), *n)) + .collect::>(), + ) + .context("failed to collect EVM contract dependencies")?; - let facets = deployer - .facets(ipc::gateway::CONTRACT_NAME) - .context("failed to collect gateway facets")?; + // Only keep library dependencies, not contracts with constructors. + eth_libs.retain(|(_, d)| !top_level_contracts.contains_key(d.as_str())); + Ok((eth_libs, top_level_contracts)) +} - deployer.deploy_contract(state, ipc::gateway::CONTRACT_NAME, (facets, params))? - }; +fn deploy_contracts( + ipc_contracts: Vec, + top_level_contracts: &EthContractMap, + validators: Vec>, + mut next_id: u64, + state: &mut FvmGenesisState, + ipc_params: &IpcParams, + hardhat: &Hardhat, +) -> anyhow::Result<()> { + let mut deployer = ContractDeployer::::new(hardhat, top_level_contracts); + + // Deploy Ethereum libraries. + for (lib_src, lib_name) in ipc_contracts { + deployer.deploy_library(state, &mut next_id, lib_src, &lib_name)?; + } - // IPC SubnetRegistry actor. - { - use ipc::registry::ConstructorParameters; - - let mut facets = deployer - .facets(ipc::registry::CONTRACT_NAME) - .context("failed to collect registry facets")?; - - let getter_facet = facets.remove(0); - let manager_facet = facets.remove(0); - let rewarder_facet = facets.remove(0); - let checkpointer_facet = facets.remove(0); - let pauser_facet = facets.remove(0); - let diamond_loupe_facet = facets.remove(0); - let diamond_cut_facet = facets.remove(0); - let ownership_facet = facets.remove(0); - - debug_assert_eq!(facets.len(), 2, "SubnetRegistry has 2 facets of its own"); - - let params = ConstructorParameters { - gateway: gateway_addr, - getter_facet: getter_facet.facet_address, - manager_facet: manager_facet.facet_address, - rewarder_facet: rewarder_facet.facet_address, - pauser_facet: pauser_facet.facet_address, - checkpointer_facet: checkpointer_facet.facet_address, - diamond_cut_facet: diamond_cut_facet.facet_address, - diamond_loupe_facet: diamond_loupe_facet.facet_address, - ownership_facet: ownership_facet.facet_address, - subnet_getter_selectors: getter_facet.function_selectors, - subnet_manager_selectors: manager_facet.function_selectors, - subnet_rewarder_selectors: rewarder_facet.function_selectors, - subnet_checkpointer_selectors: checkpointer_facet.function_selectors, - subnet_pauser_selectors: pauser_facet.function_selectors, - subnet_actor_diamond_cut_selectors: diamond_cut_facet.function_selectors, - subnet_actor_diamond_loupe_selectors: diamond_loupe_facet.function_selectors, - subnet_actor_ownership_selectors: ownership_facet.function_selectors, - creation_privileges: 0, - }; - - deployer.deploy_contract(state, ipc::registry::CONTRACT_NAME, (facets, params))?; - }; - } + // IPC Gateway actor. + let gateway_addr = { + use ipc::gateway::ConstructorParameters; - Ok(out) + let params = ConstructorParameters::new(ipc_params.gateway.clone(), validators) + .context("failed to create gateway constructor")?; + + let facets = deployer + .facets(ipc::gateway::CONTRACT_NAME) + .context("failed to collect gateway facets")?; + + deployer.deploy_contract(state, ipc::gateway::CONTRACT_NAME, (facets, params))? + }; + + // IPC SubnetRegistry actor. + { + use ipc::registry::ConstructorParameters; + + let mut facets = deployer + .facets(ipc::registry::CONTRACT_NAME) + .context("failed to collect registry facets")?; + + let getter_facet = facets.remove(0); + let manager_facet = facets.remove(0); + let rewarder_facet = facets.remove(0); + let checkpointer_facet = facets.remove(0); + let pauser_facet = facets.remove(0); + let diamond_loupe_facet = facets.remove(0); + let diamond_cut_facet = facets.remove(0); + let ownership_facet = facets.remove(0); + + debug_assert_eq!(facets.len(), 2, "SubnetRegistry has 2 facets of its own"); + + let params = ConstructorParameters { + gateway: gateway_addr, + getter_facet: getter_facet.facet_address, + manager_facet: manager_facet.facet_address, + rewarder_facet: rewarder_facet.facet_address, + pauser_facet: pauser_facet.facet_address, + checkpointer_facet: checkpointer_facet.facet_address, + diamond_cut_facet: diamond_cut_facet.facet_address, + diamond_loupe_facet: diamond_loupe_facet.facet_address, + ownership_facet: ownership_facet.facet_address, + subnet_getter_selectors: getter_facet.function_selectors, + subnet_manager_selectors: manager_facet.function_selectors, + subnet_rewarder_selectors: rewarder_facet.function_selectors, + subnet_checkpointer_selectors: checkpointer_facet.function_selectors, + subnet_pauser_selectors: pauser_facet.function_selectors, + subnet_actor_diamond_cut_selectors: diamond_cut_facet.function_selectors, + subnet_actor_diamond_loupe_selectors: diamond_loupe_facet.function_selectors, + subnet_actor_ownership_selectors: ownership_facet.function_selectors, + creation_privileges: 0, + }; + + deployer.deploy_contract(state, ipc::registry::CONTRACT_NAME, (facets, params))?; } + + Ok(()) } fn contract_src(name: &str) -> PathBuf { diff --git a/infra/fendermint/Makefile.toml b/infra/fendermint/Makefile.toml index 8e7549a862..799a6b7a4c 100644 --- a/infra/fendermint/Makefile.toml +++ b/infra/fendermint/Makefile.toml @@ -73,6 +73,7 @@ CMT_DIR = "${BASE_DIR}/${NODE_NAME}/cometbft" ENV_FILE = "${BASE_DIR}/.env" GENESIS_FILE = "${BASE_DIR}/genesis.json" +SEALED_GENESIS = "${BASE_DIR}/sealed_genesis.car" KEYS_SUBDIR = "keys" VALIDATOR_KEY_NAME = "validator_key" @@ -122,6 +123,7 @@ echo - CometBFT directory: ${CMT_DIR} echo - Fendermint directory: ${FM_DIR} echo - Keys directory: ${KEYS_DIR} echo - Genesis file: ${GENESIS_FILE} +echo - Sealed Genesis: ${SEALED_GENESIS} echo - Validator Private key: ${VALIDATOR_PRIV_KEY_PATH} echo - Network: ${NETWORK_NAME} echo - CometBFT container: ${CMT_CONTAINER_NAME} @@ -131,6 +133,7 @@ echo echo 4 nodes testnet layout: echo - IPC directory: ${BASE_DIR} echo - Genesis file: ${GENESIS_FILE} +echo - Sealed Genesis: ${SEALED_GENESIS} echo - Network: ${NETWORK_NAME} echo """ diff --git a/infra/fendermint/scripts/genesis.toml b/infra/fendermint/scripts/genesis.toml index 28a424a2d7..d7b23676a3 100644 --- a/infra/fendermint/scripts/genesis.toml +++ b/infra/fendermint/scripts/genesis.toml @@ -35,7 +35,11 @@ env = { "CMD" = """genesis --genesis-file /data/genesis.json ipc gateway --subne --majority-percentage 67 \ """ } +[tasks.genesis-seal] +extend = "fendermint-tool" +env = { "CMD" = "genesis --genesis-file /data/genesis.json ipc seal-genesis --builtin-actors-path /fendermint/bundle.car --custom-actors-path /fendermint/custom_actors_bundle.car --artifacts-path /fendermint/contracts --output-path {SEALED_GENESIS}" } + [tasks.genesis-write] extend = "fendermint-tool" -env = { "CMD" = "genesis --genesis-file /data/genesis.json into-tendermint --out /data/genesis.committed.json" } +env = { "CMD" = "genesis --genesis-file /data/genesis.json into-tendermint --app-state {SEALED_GENESIS} --out /data/genesis.committed.json" } script.post = "cp ${BASE_DIR}/genesis.committed.json ${CMT_DIR}/config/genesis.json" diff --git a/infra/fendermint/scripts/subnet.toml b/infra/fendermint/scripts/subnet.toml index 9a30db4940..583718ab0a 100644 --- a/infra/fendermint/scripts/subnet.toml +++ b/infra/fendermint/scripts/subnet.toml @@ -103,6 +103,7 @@ dependencies = [ dependencies = [ "subnet-fetch-genesis", "subnet-genesis-set-eam-permissions", + "genesis-seal", "genesis-write", "fendermint-new-network-key", ] From 22e59581010842aa20e4dbf6f87467983a8bbee5 Mon Sep 17 00:00:00 2001 From: cryptoAtwill Date: Thu, 8 Aug 2024 12:05:27 +0800 Subject: [PATCH 20/69] fmt --- fendermint/vm/interpreter/src/genesis.rs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/fendermint/vm/interpreter/src/genesis.rs b/fendermint/vm/interpreter/src/genesis.rs index 11df5c68f4..de9a258b45 100644 --- a/fendermint/vm/interpreter/src/genesis.rs +++ b/fendermint/vm/interpreter/src/genesis.rs @@ -445,17 +445,14 @@ impl GenesisCreator { ) .context("failed to init exec state")?; - let validators = genesis.validators; - { - let (hardhat, ipc_params) = self - .handle_ipc(genesis.ipc.as_ref(), |hardhat, ipc_params| { - (hardhat, ipc_params) - })? - .unwrap(); + let maybe_ipc = self.handle_ipc(genesis.ipc.as_ref(), |hardhat, ipc_params| { + (hardhat, ipc_params) + })?; + if let Some((hardhat, ipc_params)) = maybe_ipc { deploy_contracts( all_ipc_contracts, &ipc_entrypoints, - validators, + genesis.validators, next_id, state, ipc_params, From 03191418748742c2f29be184aa636bf724716f14 Mon Sep 17 00:00:00 2001 From: cryptoAtwill Date: Thu, 8 Aug 2024 13:43:00 +0800 Subject: [PATCH 21/69] fix infra --- infra/fendermint/Makefile.toml | 2 +- infra/fendermint/scripts/genesis.toml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/infra/fendermint/Makefile.toml b/infra/fendermint/Makefile.toml index 799a6b7a4c..42962658e3 100644 --- a/infra/fendermint/Makefile.toml +++ b/infra/fendermint/Makefile.toml @@ -73,7 +73,7 @@ CMT_DIR = "${BASE_DIR}/${NODE_NAME}/cometbft" ENV_FILE = "${BASE_DIR}/.env" GENESIS_FILE = "${BASE_DIR}/genesis.json" -SEALED_GENESIS = "${BASE_DIR}/sealed_genesis.car" +SEALED_GENESIS = "/data/sealed_genesis.car" KEYS_SUBDIR = "keys" VALIDATOR_KEY_NAME = "validator_key" diff --git a/infra/fendermint/scripts/genesis.toml b/infra/fendermint/scripts/genesis.toml index d7b23676a3..a182836be7 100644 --- a/infra/fendermint/scripts/genesis.toml +++ b/infra/fendermint/scripts/genesis.toml @@ -37,9 +37,9 @@ env = { "CMD" = """genesis --genesis-file /data/genesis.json ipc gateway --subne [tasks.genesis-seal] extend = "fendermint-tool" -env = { "CMD" = "genesis --genesis-file /data/genesis.json ipc seal-genesis --builtin-actors-path /fendermint/bundle.car --custom-actors-path /fendermint/custom_actors_bundle.car --artifacts-path /fendermint/contracts --output-path {SEALED_GENESIS}" } +env = { "CMD" = "genesis --genesis-file /data/genesis.json ipc seal-genesis --builtin-actors-path /fendermint/bundle.car --custom-actors-path /fendermint/custom_actors_bundle.car --artifacts-path /fendermint/contracts --output-path ${SEALED_GENESIS}" } [tasks.genesis-write] extend = "fendermint-tool" -env = { "CMD" = "genesis --genesis-file /data/genesis.json into-tendermint --app-state {SEALED_GENESIS} --out /data/genesis.committed.json" } +env = { "CMD" = "genesis --genesis-file /data/genesis.json into-tendermint --app-state ${SEALED_GENESIS} --out /data/genesis.committed.json" } script.post = "cp ${BASE_DIR}/genesis.committed.json ${CMT_DIR}/config/genesis.json" From 672ed236b0e6496179c73d947c8a53a9f08f6c83 Mon Sep 17 00:00:00 2001 From: cryptoAtwill Date: Fri, 9 Aug 2024 13:18:22 +0800 Subject: [PATCH 22/69] update gas usage --- fendermint/actors/gas_market/src/lib.rs | 88 +++++++++++--- fendermint/vm/interpreter/src/fvm/exec.rs | 11 +- fendermint/vm/interpreter/src/fvm/gas.rs | 115 ------------------ .../vm/interpreter/src/fvm/gas/eip1559.rs | 108 ++++++++++++++++ fendermint/vm/interpreter/src/fvm/gas/mod.rs | 33 +++++ fendermint/vm/interpreter/src/fvm/mod.rs | 6 +- 6 files changed, 219 insertions(+), 142 deletions(-) delete mode 100644 fendermint/vm/interpreter/src/fvm/gas.rs create mode 100644 fendermint/vm/interpreter/src/fvm/gas/eip1559.rs create mode 100644 fendermint/vm/interpreter/src/fvm/gas/mod.rs diff --git a/fendermint/actors/gas_market/src/lib.rs b/fendermint/actors/gas_market/src/lib.rs index 8b0e251186..a0161b9b37 100644 --- a/fendermint/actors/gas_market/src/lib.rs +++ b/fendermint/actors/gas_market/src/lib.rs @@ -6,17 +6,28 @@ use fil_actors_runtime::runtime::{ActorCode, Runtime}; use fil_actors_runtime::SYSTEM_ACTOR_ADDR; use fil_actors_runtime::{actor_dispatch, ActorError}; use fvm_ipld_encoding::tuple::*; +use fvm_shared::econ::TokenAmount; use fvm_shared::METHOD_CONSTRUCTOR; use num_derive::FromPrimitive; +use num_traits::Zero; +use std::ops::Mul; #[cfg(feature = "fil-actor")] fil_actors_runtime::wasm_trampoline!(EIP1559GasMarketActor); -pub const IPC_GAS_ACTOR_NAME: &str = "gas"; +/// Base fee max change denominator as defined in [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) +const EIP1559_BASE_FEE_MAX_CHANGE_DENOMINATOR: u64 = 8; +/// Elasticity multiplier as defined in [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) +const EIP1559_ELASTICITY_MULTIPLIER: u64 = 2; +/// Initial base fee as defined in [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) +pub const EIP1559_INITIAL_BASE_FEE: u64 = 1_000_000_000; +pub const IPC_GAS_MARKET_ACTOR_NAME: &str = "gas_market"; +pub type Gas = u64; #[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone)] pub struct EIP1559GasState { - pub block_gas_limit: u64, + pub block_gas_limit: Gas, + pub base_fee: TokenAmount, } pub struct EIP1559GasMarketActor {} @@ -25,7 +36,8 @@ pub struct EIP1559GasMarketActor {} #[repr(u64)] pub enum Method { Constructor = METHOD_CONSTRUCTOR, - UpdateGasMarketState = frc42_dispatch::method_hash!("UpdateGasMarketState"), + SetBlockGasLimit = frc42_dispatch::method_hash!("SetBlockGasLimit"), + UpdateBlockGasConsumption = frc42_dispatch::method_hash!("UpdateBlockGasConsumption"), } impl EIP1559GasMarketActor { @@ -37,31 +49,72 @@ impl EIP1559GasMarketActor { Ok(()) } - fn update_gas_market_state( - rt: &impl Runtime, - new_state: EIP1559GasState, - ) -> Result<(), ActorError> { + fn set_block_gas_limit(rt: &impl Runtime, block_gas_limit: Gas) -> Result<(), ActorError> { rt.validate_immediate_caller_is(std::iter::once(&SYSTEM_ACTOR_ADDR))?; rt.transaction(|st: &mut EIP1559GasState, _rt| { - *st = new_state; + st.block_gas_limit = block_gas_limit; Ok(()) })?; Ok(()) } + + fn update_block_gas_consumption( + rt: &impl Runtime, + block_gas_used: Gas, + ) -> Result<(), ActorError> { + rt.validate_immediate_caller_is(std::iter::once(&SYSTEM_ACTOR_ADDR))?; + + rt.transaction(|st: &mut EIP1559GasState, _rt| { + st.base_fee = update_base_fee(st.block_gas_limit, block_gas_used, st.base_fee.clone()); + Ok(()) + }) + } +} + +fn update_base_fee(gas_limit: Gas, gas_used: Gas, base_fee: TokenAmount) -> TokenAmount { + let gas_target = gas_limit / EIP1559_ELASTICITY_MULTIPLIER; + + if gas_used == gas_target { + return base_fee; + } + + if gas_used > gas_target { + let gas_used_delta = gas_used - gas_target; + let base_fee_delta = base_fee + .clone() + .mul(gas_used_delta) + .div_floor(gas_target) + .div_floor(EIP1559_BASE_FEE_MAX_CHANGE_DENOMINATOR) + .max(TokenAmount::from_atto(1)); + base_fee + base_fee_delta + } else { + let gas_used_delta = gas_target - gas_used; + let base_fee_per_gas_delta = base_fee + .clone() + .mul(gas_used_delta) + .div_floor(gas_target) + .div_floor(EIP1559_BASE_FEE_MAX_CHANGE_DENOMINATOR); + if base_fee_per_gas_delta > base_fee { + TokenAmount::zero() + } else { + base_fee - base_fee_per_gas_delta + } + } } impl ActorCode for EIP1559GasMarketActor { type Methods = Method; fn name() -> &'static str { - IPC_GAS_ACTOR_NAME + IPC_GAS_MARKET_ACTOR_NAME } actor_dispatch! { Constructor => constructor, - UpdateGasMarketState => update_gas_market_state, + SetBlockGasLimit => set_block_gas_limit, + UpdateBlockGasConsumption => update_block_gas_consumption, } } @@ -88,6 +141,7 @@ mod tests { Method::Constructor as u64, IpldBlock::serialize_cbor(&EIP1559GasState { block_gas_limit: 100, + base_fee: Default::default(), }) .unwrap(), ) @@ -107,11 +161,8 @@ mod tests { rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); let r = rt.call::( - Method::UpdateGasMarketState as u64, - IpldBlock::serialize_cbor(&EIP1559GasState { - block_gas_limit: 20, - }) - .unwrap(), + Method::SetBlockGasLimit as u64, + IpldBlock::serialize_cbor(&20).unwrap(), ); assert!(r.is_ok()); @@ -127,11 +178,8 @@ mod tests { let code = rt .call::( - Method::UpdateGasMarketState as u64, - IpldBlock::serialize_cbor(&EIP1559GasState { - block_gas_limit: 20, - }) - .unwrap(), + Method::SetBlockGasLimit as u64, + IpldBlock::serialize_cbor(&20).unwrap(), ) .unwrap_err() .exit_code(); diff --git a/fendermint/vm/interpreter/src/fvm/exec.rs b/fendermint/vm/interpreter/src/fvm/exec.rs index 249bceed84..96cae811b7 100644 --- a/fendermint/vm/interpreter/src/fvm/exec.rs +++ b/fendermint/vm/interpreter/src/fvm/exec.rs @@ -12,6 +12,7 @@ use fvm_shared::{address::Address, ActorID, MethodNum, BLOCK_GAS_LIMIT}; use ipc_observability::{emit, measure_time, observe::TracingError, Traceable}; use tendermint_rpc::Client; +use crate::fvm::gas::GasMarket; use crate::ExecInterpreter; use super::{ @@ -57,7 +58,7 @@ where // Block height (FVM epoch) as sequence is intentional let height = state.block_height(); - self.gas.reset_from_chain_state(&state)?; + self.gas.reload_from_chain(&state)?; // check for upgrades in the upgrade_scheduler let chain_id = state.chain_id(); @@ -159,14 +160,14 @@ where (apply_ret, emitters, latency) } else { - // TODO: maybe compare the gas limits are better? + // TODO: maybe compare the gas limits is better? msg.gas_limit = msg.gas_limit.min(self.gas.available_block_gas()); let (execution_result, latency) = measure_time(|| state.execute_explicit(msg.clone())); let (apply_ret, emitters) = execution_result?; - self.gas - .deduct_available_block_gas(apply_ret.msg_receipt.gas_used)?; + self.gas.consume_gas(apply_ret.msg_receipt.gas_used)?; + (apply_ret, emitters, latency) }; @@ -193,6 +194,8 @@ where } async fn end(&self, mut state: Self::State) -> anyhow::Result<(Self::State, Self::EndOutput)> { + self.gas.update_params(&mut state)?; + // TODO: Consider doing this async, since it's purely informational and not consensus-critical. let _ = checkpoint::emit_trace_if_check_checkpoint_finalized(&self.gateway, &mut state) .inspect_err(|e| { diff --git a/fendermint/vm/interpreter/src/fvm/gas.rs b/fendermint/vm/interpreter/src/fvm/gas.rs deleted file mode 100644 index 6b7244a4c1..0000000000 --- a/fendermint/vm/interpreter/src/fvm/gas.rs +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2022-2024 Protocol Labs -// SPDX-License-Identifier: Apache-2.0, MIT - -use crate::fvm::state::{read_actor_state, FvmExecState}; -use crate::fvm::FvmMessage; -use anyhow::anyhow; -use fendermint_actor_gas_market::EIP1559GasState; -use fendermint_vm_actor_interface::gas::GAS_ACTOR_ADDR; -use fendermint_vm_actor_interface::system; -use fvm_ipld_blockstore::Blockstore; -use std::marker::PhantomData; -use std::sync::atomic::{AtomicU64, Ordering}; - -type AtomicGas = AtomicU64; -pub type Gas = u64; - -pub struct EIP1559GasMarket { - /// The total gas available to be used by transactions in the current block - /// The executor requires Send + Sync, using an atomic variable instead of u64. - available_block_gas: AtomicGas, - _p: PhantomData, -} - -impl EIP1559GasMarket { - pub fn new() -> Self { - Self { - available_block_gas: AtomicGas::new(0), - _p: Default::default(), - } - } - - pub fn reset_from_chain_state(&self, chain_state: &FvmExecState) -> anyhow::Result<()> { - let current = self.available_block_gas.load(Ordering::SeqCst); - let gas_state = get_gas_state(chain_state)?; - self.atomic_set_block_gas_quota(current, gas_state.block_gas_limit)?; - Ok(()) - } - - pub fn available_block_gas(&self) -> Gas { - self.available_block_gas.load(Ordering::SeqCst) - } - - pub fn deduct_available_block_gas(&self, gas: Gas) -> anyhow::Result<()> { - let available = self.available_block_gas.load(Ordering::SeqCst); - match available.checked_sub(gas) { - Some(v) => { - self.atomic_set_block_gas_quota(available, v)?; - Ok(()) - } - None => Err(anyhow!("out of block gas")), - } - } - - fn atomic_set_block_gas_quota(&self, old: Gas, new: Gas) -> anyhow::Result<()> { - self.available_block_gas - .compare_exchange(old, new, Ordering::SeqCst, Ordering::SeqCst) - .map_err(|_| anyhow!("concurrent update to block gas available, should not happen"))?; - Ok(()) - } -} - -impl EIP1559GasMarket -where - DB: Blockstore + Clone + 'static, -{ - #[allow(dead_code)] - fn update_state( - &self, - blockchain_state: &mut FvmExecState, - gas_state: EIP1559GasState, - ) -> anyhow::Result<()> { - let params = fvm_ipld_encoding::RawBytes::serialize(gas_state)?; - - let msg = FvmMessage { - from: system::SYSTEM_ACTOR_ADDR, - to: GAS_ACTOR_ADDR, - sequence: blockchain_state.block_height() as u64, - // exclude this from gas restriction - gas_limit: u64::MAX, - method_num: fendermint_actor_gas_market::Method::UpdateGasMarketState as u64, - params, - value: Default::default(), - version: Default::default(), - gas_fee_cap: Default::default(), - gas_premium: Default::default(), - }; - - let (apply_ret, _) = blockchain_state.execute_implicit(msg)?; - - if let Some(err) = apply_ret.failure_info { - anyhow::bail!("failed to update EIP1559 gas state: {}", err) - } else { - Ok(()) - } - } -} - -fn get_gas_state( - state: &FvmExecState, -) -> anyhow::Result { - let s = read_actor_state::( - state, - fendermint_vm_actor_interface::gas::GAS_ACTOR_ID, - )?; - Ok(s) -} - -impl Clone for EIP1559GasMarket { - fn clone(&self) -> Self { - Self { - available_block_gas: AtomicU64::new(self.available_block_gas.load(Ordering::SeqCst)), - _p: Default::default(), - } - } -} diff --git a/fendermint/vm/interpreter/src/fvm/gas/eip1559.rs b/fendermint/vm/interpreter/src/fvm/gas/eip1559.rs new file mode 100644 index 0000000000..2ff0e5b9e7 --- /dev/null +++ b/fendermint/vm/interpreter/src/fvm/gas/eip1559.rs @@ -0,0 +1,108 @@ +use crate::fvm::gas::{Gas, GasMarket}; +use crate::fvm::state::{read_actor_state, FvmExecState}; +use crate::fvm::FvmMessage; +use anyhow::anyhow; +use fendermint_actor_gas_market::EIP1559GasState; +use fendermint_vm_actor_interface::gas::GAS_ACTOR_ADDR; +use fendermint_vm_actor_interface::system; +use fvm_ipld_blockstore::Blockstore; +use std::sync::atomic::{AtomicU64, Ordering}; + +type AtomicGas = AtomicU64; + +/// The gas market based on EIP1155 +#[derive(Default)] +pub struct EIP1559GasMarket { + /// The block gas limit + block_gas_limit: AtomicGas, + /// The accumulated gas usage so far + block_gas_used: AtomicGas, +} + +impl GasMarket for EIP1559GasMarket { + type State = EIP1559GasState; + + fn reload_from_chain( + &self, + chain_state: &FvmExecState, + ) -> anyhow::Result<()> { + let state = get_state(chain_state)?; + self.block_gas_used.store(0, Ordering::SeqCst); + self.block_gas_limit + .store(state.block_gas_limit, Ordering::SeqCst); + Ok(()) + } + + fn available_block_gas(&self) -> Gas { + self.block_gas_limit.load(Ordering::SeqCst) - self.block_gas_used.load(Ordering::SeqCst) + } + + fn consume_gas(&self, gas: Gas) -> anyhow::Result<()> { + let block_gas_used = self.block_gas_used.load(Ordering::SeqCst); + + if block_gas_used + gas >= self.block_gas_limit.load(Ordering::SeqCst) { + return Err(anyhow!("out of block gas")); + } + + let new_gas_used = block_gas_used + gas; + self.update_block_gas_used(block_gas_used, new_gas_used) + } + + fn update_params( + &self, + chain_state: &mut FvmExecState, + ) -> anyhow::Result<()> { + let block_gas_used = self.block_gas_used.load(Ordering::SeqCst); + let params = fvm_ipld_encoding::RawBytes::serialize(block_gas_used)?; + + let msg = FvmMessage { + from: system::SYSTEM_ACTOR_ADDR, + to: GAS_ACTOR_ADDR, + sequence: chain_state.block_height() as u64, + // exclude this from gas restriction + gas_limit: u64::MAX, + method_num: fendermint_actor_gas_market::Method::UpdateBlockGasConsumption as u64, + params, + value: Default::default(), + version: Default::default(), + gas_fee_cap: Default::default(), + gas_premium: Default::default(), + }; + + let (apply_ret, _) = chain_state.execute_implicit(msg)?; + + if let Some(err) = apply_ret.failure_info { + anyhow::bail!("failed to update EIP1559 gas state: {}", err) + } else { + Ok(()) + } + } +} + +impl EIP1559GasMarket { + fn update_block_gas_used(&self, old_used: Gas, new_used: Gas) -> anyhow::Result<()> { + self.block_gas_used + .compare_exchange(old_used, new_used, Ordering::SeqCst, Ordering::SeqCst) + .map_err(|_| anyhow!("concurrent update in block gas used, should not happen"))?; + Ok(()) + } +} + +impl Clone for EIP1559GasMarket { + fn clone(&self) -> Self { + Self { + block_gas_limit: AtomicGas::new(self.block_gas_limit.load(Ordering::SeqCst)), + block_gas_used: AtomicGas::new(self.block_gas_used.load(Ordering::SeqCst)), + } + } +} + +#[inline] +fn get_state( + chain_state: &FvmExecState, +) -> anyhow::Result { + read_actor_state::( + chain_state, + fendermint_vm_actor_interface::gas::GAS_ACTOR_ID, + ) +} diff --git a/fendermint/vm/interpreter/src/fvm/gas/mod.rs b/fendermint/vm/interpreter/src/fvm/gas/mod.rs new file mode 100644 index 0000000000..00710ce216 --- /dev/null +++ b/fendermint/vm/interpreter/src/fvm/gas/mod.rs @@ -0,0 +1,33 @@ +// Copyright 2022-2024 Protocol Labs +// SPDX-License-Identifier: Apache-2.0, MIT + +use crate::fvm::state::FvmExecState; +use fvm_ipld_blockstore::Blockstore; + +pub mod eip1559; + +pub type Gas = u64; + +/// The gas market for fendermint. This should be backed by an fvm actor. +pub trait GasMarket { + /// The gas market state + type State; + + /// Reset the gas market based on the current block chain state + fn reload_from_chain( + &self, + chain_state: &FvmExecState, + ) -> anyhow::Result<()>; + + /// Obtain the current block gas available for execution + fn available_block_gas(&self) -> Gas; + + /// Tracks the amount of gas consumed by a transaction + fn consume_gas(&self, gas: Gas) -> anyhow::Result<()>; + + /// Update the gas market params to blockchain state. This usually happens at the end of the block + fn update_params( + &self, + chain_state: &mut FvmExecState, + ) -> anyhow::Result<()>; +} diff --git a/fendermint/vm/interpreter/src/fvm/mod.rs b/fendermint/vm/interpreter/src/fvm/mod.rs index 3c5cee6745..35449396d7 100644 --- a/fendermint/vm/interpreter/src/fvm/mod.rs +++ b/fendermint/vm/interpreter/src/fvm/mod.rs @@ -19,7 +19,7 @@ pub mod bundle; pub(crate) mod gas; pub(crate) mod topdown; -use crate::fvm::gas::EIP1559GasMarket; +use crate::fvm::gas::eip1559::EIP1559GasMarket; pub use check::FvmCheckRet; pub use checkpoint::PowerUpdates; pub use exec::FvmApplyRet; @@ -84,7 +84,7 @@ where gateway: GatewayCaller, /// Upgrade scheduler stores all the upgrades to be executed at given heights. upgrade_scheduler: UpgradeScheduler, - gas: EIP1559GasMarket, + gas: EIP1559GasMarket, } impl FvmMessageInterpreter @@ -110,7 +110,7 @@ where push_chain_meta: true, gateway: GatewayCaller::default(), upgrade_scheduler, - gas: EIP1559GasMarket::new(), + gas: EIP1559GasMarket::default(), } } From 05d79e1428600a9fa8683e39fe02d48d2e8e2848 Mon Sep 17 00:00:00 2001 From: cryptoAtwill Date: Mon, 12 Aug 2024 12:18:19 +0800 Subject: [PATCH 23/69] review feedback --- fendermint/actors/gas_market/src/lib.rs | 12 +++ fendermint/vm/interpreter/src/fvm/exec.rs | 2 +- .../src/fvm/gas/{eip1559.rs => actor.rs} | 89 +++++++++++-------- fendermint/vm/interpreter/src/fvm/gas/mod.rs | 11 +-- fendermint/vm/interpreter/src/fvm/mod.rs | 6 +- .../vm/interpreter/src/fvm/state/exec.rs | 20 +---- .../vm/interpreter/src/fvm/state/mod.rs | 1 - 7 files changed, 70 insertions(+), 71 deletions(-) rename fendermint/vm/interpreter/src/fvm/gas/{eip1559.rs => actor.rs} (56%) diff --git a/fendermint/actors/gas_market/src/lib.rs b/fendermint/actors/gas_market/src/lib.rs index a0161b9b37..93c6af1232 100644 --- a/fendermint/actors/gas_market/src/lib.rs +++ b/fendermint/actors/gas_market/src/lib.rs @@ -36,6 +36,7 @@ pub struct EIP1559GasMarketActor {} #[repr(u64)] pub enum Method { Constructor = METHOD_CONSTRUCTOR, + GetState = frc42_dispatch::method_hash!("GetState"), SetBlockGasLimit = frc42_dispatch::method_hash!("SetBlockGasLimit"), UpdateBlockGasConsumption = frc42_dispatch::method_hash!("UpdateBlockGasConsumption"), } @@ -60,6 +61,10 @@ impl EIP1559GasMarketActor { Ok(()) } + fn get_state(rt: &impl Runtime) -> Result { + rt.state() + } + fn update_block_gas_consumption( rt: &impl Runtime, block_gas_used: Gas, @@ -114,6 +119,7 @@ impl ActorCode for EIP1559GasMarketActor { actor_dispatch! { Constructor => constructor, SetBlockGasLimit => set_block_gas_limit, + GetState => get_state, UpdateBlockGasConsumption => update_block_gas_consumption, } } @@ -166,6 +172,12 @@ mod tests { ); assert!(r.is_ok()); + let r = rt + .call::( + Method::GetState as u64, + IpldBlock::serialize_cbor(&()).unwrap(), + ) + .unwrap(); let s = rt.get_state::(); assert_eq!(s.block_gas_limit, 20); } diff --git a/fendermint/vm/interpreter/src/fvm/exec.rs b/fendermint/vm/interpreter/src/fvm/exec.rs index 96cae811b7..88a92de719 100644 --- a/fendermint/vm/interpreter/src/fvm/exec.rs +++ b/fendermint/vm/interpreter/src/fvm/exec.rs @@ -58,7 +58,7 @@ where // Block height (FVM epoch) as sequence is intentional let height = state.block_height(); - self.gas.reload_from_chain(&state)?; + self.gas.load(&mut state)?; // check for upgrades in the upgrade_scheduler let chain_id = state.chain_id(); diff --git a/fendermint/vm/interpreter/src/fvm/gas/eip1559.rs b/fendermint/vm/interpreter/src/fvm/gas/actor.rs similarity index 56% rename from fendermint/vm/interpreter/src/fvm/gas/eip1559.rs rename to fendermint/vm/interpreter/src/fvm/gas/actor.rs index 2ff0e5b9e7..b00bfcbfff 100644 --- a/fendermint/vm/interpreter/src/fvm/gas/eip1559.rs +++ b/fendermint/vm/interpreter/src/fvm/gas/actor.rs @@ -1,38 +1,31 @@ use crate::fvm::gas::{Gas, GasMarket}; -use crate::fvm::state::{read_actor_state, FvmExecState}; +use crate::fvm::state::FvmExecState; use crate::fvm::FvmMessage; -use anyhow::anyhow; -use fendermint_actor_gas_market::EIP1559GasState; +use anyhow::Context; + use fendermint_vm_actor_interface::gas::GAS_ACTOR_ADDR; use fendermint_vm_actor_interface::system; use fvm_ipld_blockstore::Blockstore; +use fvm_ipld_encoding::BytesDe; use std::sync::atomic::{AtomicU64, Ordering}; type AtomicGas = AtomicU64; +type GasMarketState = fendermint_actor_gas_market::EIP1559GasState; /// The gas market based on EIP1155 +/// Due to the reference trait bound limit (`&self` instead of `&mut self`) in Interpreter, `Atmomic` +/// is used. However, the calling pattern should be single threaded, so direct `store` could be used. +/// The usage of `Atomic` is purely to bypass the compilation issue without using unsafe. +/// TODO: remove this overhead when trait bound is updated. #[derive(Default)] -pub struct EIP1559GasMarket { +pub struct ActorGasMarket { /// The block gas limit block_gas_limit: AtomicGas, /// The accumulated gas usage so far block_gas_used: AtomicGas, } -impl GasMarket for EIP1559GasMarket { - type State = EIP1559GasState; - - fn reload_from_chain( - &self, - chain_state: &FvmExecState, - ) -> anyhow::Result<()> { - let state = get_state(chain_state)?; - self.block_gas_used.store(0, Ordering::SeqCst); - self.block_gas_limit - .store(state.block_gas_limit, Ordering::SeqCst); - Ok(()) - } - +impl GasMarket for ActorGasMarket { fn available_block_gas(&self) -> Gas { self.block_gas_limit.load(Ordering::SeqCst) - self.block_gas_used.load(Ordering::SeqCst) } @@ -41,11 +34,12 @@ impl GasMarket for EIP1559GasMarket { let block_gas_used = self.block_gas_used.load(Ordering::SeqCst); if block_gas_used + gas >= self.block_gas_limit.load(Ordering::SeqCst) { - return Err(anyhow!("out of block gas")); + anyhow::bail!("out of block gas") } + self.block_gas_used + .store(block_gas_used + gas, Ordering::SeqCst); - let new_gas_used = block_gas_used + gas; - self.update_block_gas_used(block_gas_used, new_gas_used) + Ok(()) } fn update_params( @@ -79,16 +73,47 @@ impl GasMarket for EIP1559GasMarket { } } -impl EIP1559GasMarket { - fn update_block_gas_used(&self, old_used: Gas, new_used: Gas) -> anyhow::Result<()> { - self.block_gas_used - .compare_exchange(old_used, new_used, Ordering::SeqCst, Ordering::SeqCst) - .map_err(|_| anyhow!("concurrent update in block gas used, should not happen"))?; +impl ActorGasMarket { + pub fn load( + &self, + chain_state: &mut FvmExecState, + ) -> anyhow::Result<()> { + let msg = FvmMessage { + from: system::SYSTEM_ACTOR_ADDR, + to: GAS_ACTOR_ADDR, + sequence: chain_state.block_height() as u64, + // exclude this from gas restriction + gas_limit: u64::MAX, + method_num: fendermint_actor_gas_market::Method::GetState as u64, + params: fvm_ipld_encoding::RawBytes::serialize(())?, + value: Default::default(), + version: Default::default(), + gas_fee_cap: Default::default(), + gas_premium: Default::default(), + }; + + let (apply_ret, _) = chain_state.execute_implicit(msg)?; + + if let Some(err) = apply_ret.failure_info { + anyhow::bail!("failed to read gas market state: {}", err); + } + + let output = apply_ret + .msg_receipt + .return_data + .deserialize::() + .map(|bz| bz.0) + .context("failed to deserialize error data")?; + let state = fvm_ipld_encoding::from_slice::(&output)?; + + self.block_gas_used.store(0, Ordering::SeqCst); + self.block_gas_limit + .store(state.block_gas_limit, Ordering::SeqCst); Ok(()) } } -impl Clone for EIP1559GasMarket { +impl Clone for ActorGasMarket { fn clone(&self) -> Self { Self { block_gas_limit: AtomicGas::new(self.block_gas_limit.load(Ordering::SeqCst)), @@ -96,13 +121,3 @@ impl Clone for EIP1559GasMarket { } } } - -#[inline] -fn get_state( - chain_state: &FvmExecState, -) -> anyhow::Result { - read_actor_state::( - chain_state, - fendermint_vm_actor_interface::gas::GAS_ACTOR_ID, - ) -} diff --git a/fendermint/vm/interpreter/src/fvm/gas/mod.rs b/fendermint/vm/interpreter/src/fvm/gas/mod.rs index 00710ce216..f29b6cd5c0 100644 --- a/fendermint/vm/interpreter/src/fvm/gas/mod.rs +++ b/fendermint/vm/interpreter/src/fvm/gas/mod.rs @@ -4,21 +4,12 @@ use crate::fvm::state::FvmExecState; use fvm_ipld_blockstore::Blockstore; -pub mod eip1559; +pub mod actor; pub type Gas = u64; /// The gas market for fendermint. This should be backed by an fvm actor. pub trait GasMarket { - /// The gas market state - type State; - - /// Reset the gas market based on the current block chain state - fn reload_from_chain( - &self, - chain_state: &FvmExecState, - ) -> anyhow::Result<()>; - /// Obtain the current block gas available for execution fn available_block_gas(&self) -> Gas; diff --git a/fendermint/vm/interpreter/src/fvm/mod.rs b/fendermint/vm/interpreter/src/fvm/mod.rs index 35449396d7..fa62398c6c 100644 --- a/fendermint/vm/interpreter/src/fvm/mod.rs +++ b/fendermint/vm/interpreter/src/fvm/mod.rs @@ -19,7 +19,7 @@ pub mod bundle; pub(crate) mod gas; pub(crate) mod topdown; -use crate::fvm::gas::eip1559::EIP1559GasMarket; +use crate::fvm::gas::actor::ActorGasMarket; pub use check::FvmCheckRet; pub use checkpoint::PowerUpdates; pub use exec::FvmApplyRet; @@ -84,7 +84,7 @@ where gateway: GatewayCaller, /// Upgrade scheduler stores all the upgrades to be executed at given heights. upgrade_scheduler: UpgradeScheduler, - gas: EIP1559GasMarket, + gas: ActorGasMarket, } impl FvmMessageInterpreter @@ -110,7 +110,7 @@ where push_chain_meta: true, gateway: GatewayCaller::default(), upgrade_scheduler, - gas: EIP1559GasMarket::default(), + gas: ActorGasMarket::default(), } } diff --git a/fendermint/vm/interpreter/src/fvm/state/exec.rs b/fendermint/vm/interpreter/src/fvm/state/exec.rs index 03794e2216..7d5f10dda4 100644 --- a/fendermint/vm/interpreter/src/fvm/state/exec.rs +++ b/fendermint/vm/interpreter/src/fvm/state/exec.rs @@ -15,12 +15,11 @@ use fvm::{ DefaultKernel, }; use fvm_ipld_blockstore::Blockstore; -use fvm_ipld_encoding::{CborStore, RawBytes}; +use fvm_ipld_encoding::RawBytes; use fvm_shared::{ address::Address, chainid::ChainID, clock::ChainEpoch, econ::TokenAmount, error::ExitCode, message::Message, receipt::Receipt, version::NetworkVersion, ActorID, }; -use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use serde_with::serde_as; @@ -351,20 +350,3 @@ fn check_error(e: anyhow::Error) -> (ApplyRet, ActorAddressMap) { }; (ret, Default::default()) } - -pub(crate) fn read_actor_state( - state: &FvmExecState, - actor_id: ActorID, -) -> anyhow::Result { - let state_tree = state.state_tree(); - - let state_cid = state_tree - .get_actor(actor_id)? - .ok_or_else(|| anyhow::anyhow!("actor state not found: {}", actor_id))? - .state; - - Ok(state_tree - .store() - .get_cbor::(&state_cid)? - .ok_or_else(|| anyhow::anyhow!("actor state should not be null"))?) -} diff --git a/fendermint/vm/interpreter/src/fvm/state/mod.rs b/fendermint/vm/interpreter/src/fvm/state/mod.rs index 43098aa243..22a1504e4c 100644 --- a/fendermint/vm/interpreter/src/fvm/state/mod.rs +++ b/fendermint/vm/interpreter/src/fvm/state/mod.rs @@ -12,7 +12,6 @@ pub mod snapshot; use std::sync::Arc; pub use check::FvmCheckState; -pub(crate) use exec::read_actor_state; pub use exec::{BlockHash, FvmExecState, FvmStateParams, FvmUpdatableParams}; pub use genesis::{empty_state_tree, FvmGenesisState}; pub use query::FvmQueryState; From 54381478f4e87c426a8ccaaee4a17227a07bfefb Mon Sep 17 00:00:00 2001 From: cryptoAtwill <108330426+cryptoAtwill@users.noreply.github.com> Date: Mon, 12 Aug 2024 20:52:29 +0800 Subject: [PATCH 24/69] Update fendermint/vm/interpreter/src/fvm/gas/actor.rs Co-authored-by: raulk --- fendermint/vm/interpreter/src/fvm/gas/actor.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fendermint/vm/interpreter/src/fvm/gas/actor.rs b/fendermint/vm/interpreter/src/fvm/gas/actor.rs index b00bfcbfff..56730ae210 100644 --- a/fendermint/vm/interpreter/src/fvm/gas/actor.rs +++ b/fendermint/vm/interpreter/src/fvm/gas/actor.rs @@ -13,7 +13,7 @@ type AtomicGas = AtomicU64; type GasMarketState = fendermint_actor_gas_market::EIP1559GasState; /// The gas market based on EIP1155 -/// Due to the reference trait bound limit (`&self` instead of `&mut self`) in Interpreter, `Atmomic` +/// Due to the reference trait bound limit (`&self` instead of `&mut self`) in Interpreter, `Atomic` /// is used. However, the calling pattern should be single threaded, so direct `store` could be used. /// The usage of `Atomic` is purely to bypass the compilation issue without using unsafe. /// TODO: remove this overhead when trait bound is updated. From 99926b3e812466bb53ab6a59d518bef118e01edf Mon Sep 17 00:00:00 2001 From: cryptoAtwill Date: Tue, 13 Aug 2024 19:14:06 +0800 Subject: [PATCH 25/69] review feedbacks --- fendermint/actors/gas_market/src/lib.rs | 21 ++-- fendermint/vm/interpreter/src/fvm/exec.rs | 21 ++-- .../vm/interpreter/src/fvm/gas/actor.rs | 110 ++++++++---------- fendermint/vm/interpreter/src/fvm/gas/mod.rs | 11 +- fendermint/vm/interpreter/src/fvm/mod.rs | 3 - .../vm/interpreter/src/fvm/state/exec.rs | 24 ++-- 6 files changed, 89 insertions(+), 101 deletions(-) diff --git a/fendermint/actors/gas_market/src/lib.rs b/fendermint/actors/gas_market/src/lib.rs index 93c6af1232..bb6b03b2fd 100644 --- a/fendermint/actors/gas_market/src/lib.rs +++ b/fendermint/actors/gas_market/src/lib.rs @@ -16,13 +16,14 @@ use std::ops::Mul; fil_actors_runtime::wasm_trampoline!(EIP1559GasMarketActor); /// Base fee max change denominator as defined in [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) -const EIP1559_BASE_FEE_MAX_CHANGE_DENOMINATOR: u64 = 8; +const BASE_FEE_MAX_CHANGE_DENOMINATOR: u64 = 8; /// Elasticity multiplier as defined in [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) -const EIP1559_ELASTICITY_MULTIPLIER: u64 = 2; +const ELASTICITY_MULTIPLIER: u64 = 2; /// Initial base fee as defined in [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) -pub const EIP1559_INITIAL_BASE_FEE: u64 = 1_000_000_000; +pub const INITIAL_BASE_FEE: u64 = 1_000_000_000; pub const IPC_GAS_MARKET_ACTOR_NAME: &str = "gas_market"; pub type Gas = u64; +pub type GasMarketReading = EIP1559GasState; #[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone)] pub struct EIP1559GasState { @@ -36,7 +37,7 @@ pub struct EIP1559GasMarketActor {} #[repr(u64)] pub enum Method { Constructor = METHOD_CONSTRUCTOR, - GetState = frc42_dispatch::method_hash!("GetState"), + CurrentGasReading = frc42_dispatch::method_hash!("CurrentGasReading"), SetBlockGasLimit = frc42_dispatch::method_hash!("SetBlockGasLimit"), UpdateBlockGasConsumption = frc42_dispatch::method_hash!("UpdateBlockGasConsumption"), } @@ -61,7 +62,7 @@ impl EIP1559GasMarketActor { Ok(()) } - fn get_state(rt: &impl Runtime) -> Result { + fn current_gas_reading(rt: &impl Runtime) -> Result { rt.state() } @@ -79,7 +80,7 @@ impl EIP1559GasMarketActor { } fn update_base_fee(gas_limit: Gas, gas_used: Gas, base_fee: TokenAmount) -> TokenAmount { - let gas_target = gas_limit / EIP1559_ELASTICITY_MULTIPLIER; + let gas_target = gas_limit / ELASTICITY_MULTIPLIER; if gas_used == gas_target { return base_fee; @@ -91,7 +92,7 @@ fn update_base_fee(gas_limit: Gas, gas_used: Gas, base_fee: TokenAmount) -> Toke .clone() .mul(gas_used_delta) .div_floor(gas_target) - .div_floor(EIP1559_BASE_FEE_MAX_CHANGE_DENOMINATOR) + .div_floor(BASE_FEE_MAX_CHANGE_DENOMINATOR) .max(TokenAmount::from_atto(1)); base_fee + base_fee_delta } else { @@ -100,7 +101,7 @@ fn update_base_fee(gas_limit: Gas, gas_used: Gas, base_fee: TokenAmount) -> Toke .clone() .mul(gas_used_delta) .div_floor(gas_target) - .div_floor(EIP1559_BASE_FEE_MAX_CHANGE_DENOMINATOR); + .div_floor(BASE_FEE_MAX_CHANGE_DENOMINATOR); if base_fee_per_gas_delta > base_fee { TokenAmount::zero() } else { @@ -119,7 +120,7 @@ impl ActorCode for EIP1559GasMarketActor { actor_dispatch! { Constructor => constructor, SetBlockGasLimit => set_block_gas_limit, - GetState => get_state, + CurrentGasReading => get_state, UpdateBlockGasConsumption => update_block_gas_consumption, } } @@ -174,7 +175,7 @@ mod tests { let r = rt .call::( - Method::GetState as u64, + Method::CurrentGasReading as u64, IpldBlock::serialize_cbor(&()).unwrap(), ) .unwrap(); diff --git a/fendermint/vm/interpreter/src/fvm/exec.rs b/fendermint/vm/interpreter/src/fvm/exec.rs index 88a92de719..bdcee5a600 100644 --- a/fendermint/vm/interpreter/src/fvm/exec.rs +++ b/fendermint/vm/interpreter/src/fvm/exec.rs @@ -1,7 +1,7 @@ // Copyright 2022-2024 Protocol Labs // SPDX-License-Identifier: Apache-2.0, MIT -use anyhow::Context; +use anyhow::{bail, Context}; use async_trait::async_trait; use std::collections::HashMap; @@ -58,8 +58,6 @@ where // Block height (FVM epoch) as sequence is intentional let height = state.block_height(); - self.gas.load(&mut state)?; - // check for upgrades in the upgrade_scheduler let chain_id = state.chain_id(); let block_height: u64 = state.block_height().try_into().unwrap(); @@ -152,7 +150,7 @@ where async fn deliver( &self, mut state: Self::State, - mut msg: Self::Message, + msg: Self::Message, ) -> anyhow::Result<(Self::State, Self::DeliverOutput)> { let (apply_ret, emitters, latency) = if msg.from == system::SYSTEM_ACTOR_ADDR { let (execution_result, latency) = measure_time(|| state.execute_implicit(msg.clone())); @@ -160,13 +158,20 @@ where (apply_ret, emitters, latency) } else { - // TODO: maybe compare the gas limits is better? - msg.gas_limit = msg.gas_limit.min(self.gas.available_block_gas()); + if msg.gas_limit > state.gas_market().available_block_gas() { + bail!("gas limit exceed available block gas limit") + } let (execution_result, latency) = measure_time(|| state.execute_explicit(msg.clone())); let (apply_ret, emitters) = execution_result?; - self.gas.consume_gas(apply_ret.msg_receipt.gas_used)?; + if state + .gas_market_mut() + .record_gas_used(apply_ret.msg_receipt.gas_used) + .is_err() + { + tracing::warn!("should not have exceeded block gas limit"); + } (apply_ret, emitters, latency) }; @@ -194,7 +199,7 @@ where } async fn end(&self, mut state: Self::State) -> anyhow::Result<(Self::State, Self::EndOutput)> { - self.gas.update_params(&mut state)?; + state.update_gas_market()?; // TODO: Consider doing this async, since it's purely informational and not consensus-critical. let _ = checkpoint::emit_trace_if_check_checkpoint_finalized(&self.gateway, &mut state) diff --git a/fendermint/vm/interpreter/src/fvm/gas/actor.rs b/fendermint/vm/interpreter/src/fvm/gas/actor.rs index 56730ae210..00ae0f32b9 100644 --- a/fendermint/vm/interpreter/src/fvm/gas/actor.rs +++ b/fendermint/vm/interpreter/src/fvm/gas/actor.rs @@ -1,123 +1,107 @@ use crate::fvm::gas::{Gas, GasMarket}; -use crate::fvm::state::FvmExecState; use crate::fvm::FvmMessage; use anyhow::Context; use fendermint_vm_actor_interface::gas::GAS_ACTOR_ADDR; use fendermint_vm_actor_interface::system; -use fvm_ipld_blockstore::Blockstore; +use fvm::executor::{ApplyKind, Executor}; use fvm_ipld_encoding::BytesDe; -use std::sync::atomic::{AtomicU64, Ordering}; +use fvm_shared::clock::ChainEpoch; -type AtomicGas = AtomicU64; type GasMarketState = fendermint_actor_gas_market::EIP1559GasState; -/// The gas market based on EIP1155 -/// Due to the reference trait bound limit (`&self` instead of `&mut self`) in Interpreter, `Atomic` -/// is used. However, the calling pattern should be single threaded, so direct `store` could be used. -/// The usage of `Atomic` is purely to bypass the compilation issue without using unsafe. -/// TODO: remove this overhead when trait bound is updated. #[derive(Default)] pub struct ActorGasMarket { /// The block gas limit - block_gas_limit: AtomicGas, + block_gas_limit: Gas, /// The accumulated gas usage so far - block_gas_used: AtomicGas, + block_gas_used: Gas, } impl GasMarket for ActorGasMarket { fn available_block_gas(&self) -> Gas { - self.block_gas_limit.load(Ordering::SeqCst) - self.block_gas_used.load(Ordering::SeqCst) + self.block_gas_limit - self.block_gas_used } - fn consume_gas(&self, gas: Gas) -> anyhow::Result<()> { - let block_gas_used = self.block_gas_used.load(Ordering::SeqCst); - - if block_gas_used + gas >= self.block_gas_limit.load(Ordering::SeqCst) { + fn record_gas_used(&mut self, gas: Gas) -> anyhow::Result<()> { + if self.block_gas_used + gas >= self.block_gas_limit { anyhow::bail!("out of block gas") } - self.block_gas_used - .store(block_gas_used + gas, Ordering::SeqCst); + self.block_gas_used += gas; Ok(()) } +} - fn update_params( - &self, - chain_state: &mut FvmExecState, - ) -> anyhow::Result<()> { - let block_gas_used = self.block_gas_used.load(Ordering::SeqCst); - let params = fvm_ipld_encoding::RawBytes::serialize(block_gas_used)?; - +impl ActorGasMarket { + pub fn new( + executor: &mut E, + block_height: ChainEpoch, + ) -> anyhow::Result { let msg = FvmMessage { from: system::SYSTEM_ACTOR_ADDR, to: GAS_ACTOR_ADDR, - sequence: chain_state.block_height() as u64, + sequence: block_height as u64, // exclude this from gas restriction gas_limit: u64::MAX, - method_num: fendermint_actor_gas_market::Method::UpdateBlockGasConsumption as u64, - params, + method_num: fendermint_actor_gas_market::Method::GetState as u64, + params: fvm_ipld_encoding::RawBytes::serialize(())?, value: Default::default(), version: Default::default(), gas_fee_cap: Default::default(), gas_premium: Default::default(), }; - let (apply_ret, _) = chain_state.execute_implicit(msg)?; + let raw_length = fvm_ipld_encoding::to_vec(&msg).map(|bz| bz.len())?; + let apply_ret = executor.execute_message(msg, ApplyKind::Implicit, raw_length)?; if let Some(err) = apply_ret.failure_info { - anyhow::bail!("failed to update EIP1559 gas state: {}", err) - } else { - Ok(()) + anyhow::bail!("failed to read gas market state: {}", err); } + + let output = apply_ret + .msg_receipt + .return_data + .deserialize::() + .map(|bz| bz.0) + .context("failed to deserialize error data")?; + let state = fvm_ipld_encoding::from_slice::(&output)?; + + Ok(Self { + block_gas_limit: state.block_gas_limit, + block_gas_used: 0, + }) } -} -impl ActorGasMarket { - pub fn load( + pub fn commit( &self, - chain_state: &mut FvmExecState, + executor: &mut E, + block_height: ChainEpoch, ) -> anyhow::Result<()> { + let block_gas_used = self.block_gas_used; + let params = fvm_ipld_encoding::RawBytes::serialize(block_gas_used)?; + let msg = FvmMessage { from: system::SYSTEM_ACTOR_ADDR, to: GAS_ACTOR_ADDR, - sequence: chain_state.block_height() as u64, + sequence: block_height as u64, // exclude this from gas restriction gas_limit: u64::MAX, - method_num: fendermint_actor_gas_market::Method::GetState as u64, - params: fvm_ipld_encoding::RawBytes::serialize(())?, + method_num: fendermint_actor_gas_market::Method::UpdateBlockGasConsumption as u64, + params, value: Default::default(), version: Default::default(), gas_fee_cap: Default::default(), gas_premium: Default::default(), }; - let (apply_ret, _) = chain_state.execute_implicit(msg)?; + let raw_length = fvm_ipld_encoding::to_vec(&msg).map(|bz| bz.len())?; + let apply_ret = executor.execute_message(msg, ApplyKind::Implicit, raw_length)?; if let Some(err) = apply_ret.failure_info { - anyhow::bail!("failed to read gas market state: {}", err); - } - - let output = apply_ret - .msg_receipt - .return_data - .deserialize::() - .map(|bz| bz.0) - .context("failed to deserialize error data")?; - let state = fvm_ipld_encoding::from_slice::(&output)?; - - self.block_gas_used.store(0, Ordering::SeqCst); - self.block_gas_limit - .store(state.block_gas_limit, Ordering::SeqCst); - Ok(()) - } -} - -impl Clone for ActorGasMarket { - fn clone(&self) -> Self { - Self { - block_gas_limit: AtomicGas::new(self.block_gas_limit.load(Ordering::SeqCst)), - block_gas_used: AtomicGas::new(self.block_gas_used.load(Ordering::SeqCst)), + anyhow::bail!("failed to update EIP1559 gas state: {}", err) + } else { + Ok(()) } } } diff --git a/fendermint/vm/interpreter/src/fvm/gas/mod.rs b/fendermint/vm/interpreter/src/fvm/gas/mod.rs index f29b6cd5c0..8c9dfb91ba 100644 --- a/fendermint/vm/interpreter/src/fvm/gas/mod.rs +++ b/fendermint/vm/interpreter/src/fvm/gas/mod.rs @@ -1,9 +1,6 @@ // Copyright 2022-2024 Protocol Labs // SPDX-License-Identifier: Apache-2.0, MIT -use crate::fvm::state::FvmExecState; -use fvm_ipld_blockstore::Blockstore; - pub mod actor; pub type Gas = u64; @@ -14,11 +11,5 @@ pub trait GasMarket { fn available_block_gas(&self) -> Gas; /// Tracks the amount of gas consumed by a transaction - fn consume_gas(&self, gas: Gas) -> anyhow::Result<()>; - - /// Update the gas market params to blockchain state. This usually happens at the end of the block - fn update_params( - &self, - chain_state: &mut FvmExecState, - ) -> anyhow::Result<()>; + fn record_gas_used(&mut self, gas: Gas) -> anyhow::Result<()>; } diff --git a/fendermint/vm/interpreter/src/fvm/mod.rs b/fendermint/vm/interpreter/src/fvm/mod.rs index fa62398c6c..3a301a43c1 100644 --- a/fendermint/vm/interpreter/src/fvm/mod.rs +++ b/fendermint/vm/interpreter/src/fvm/mod.rs @@ -19,7 +19,6 @@ pub mod bundle; pub(crate) mod gas; pub(crate) mod topdown; -use crate::fvm::gas::actor::ActorGasMarket; pub use check::FvmCheckRet; pub use checkpoint::PowerUpdates; pub use exec::FvmApplyRet; @@ -84,7 +83,6 @@ where gateway: GatewayCaller, /// Upgrade scheduler stores all the upgrades to be executed at given heights. upgrade_scheduler: UpgradeScheduler, - gas: ActorGasMarket, } impl FvmMessageInterpreter @@ -110,7 +108,6 @@ where push_chain_meta: true, gateway: GatewayCaller::default(), upgrade_scheduler, - gas: ActorGasMarket::default(), } } diff --git a/fendermint/vm/interpreter/src/fvm/state/exec.rs b/fendermint/vm/interpreter/src/fvm/state/exec.rs index 7d5f10dda4..cbd6f6bc46 100644 --- a/fendermint/vm/interpreter/src/fvm/state/exec.rs +++ b/fendermint/vm/interpreter/src/fvm/state/exec.rs @@ -24,6 +24,7 @@ use serde::{Deserialize, Serialize}; use serde_with::serde_as; use crate::fvm::externs::FendermintExterns; +use crate::fvm::gas::actor::ActorGasMarket; use fendermint_vm_core::{chainid::HasChainID, Timestamp}; use fendermint_vm_encoding::IsHumanReadable; @@ -113,6 +114,8 @@ where /// Indicate whether the parameters have been updated. params_dirty: bool, + + gas_market: ActorGasMarket, } impl FvmExecState @@ -146,7 +149,8 @@ where let engine = multi_engine.get(&nc)?; let externs = FendermintExterns::new(blockstore.clone(), params.state_root); let machine = DefaultMachine::new(&mc, blockstore, externs)?; - let executor = DefaultExecutor::new(engine, machine)?; + let mut executor = DefaultExecutor::new(engine, machine)?; + let gas_market = ActorGasMarket::new(&mut executor, block_height)?; Ok(Self { executor, @@ -159,6 +163,7 @@ where power_scale: params.power_scale, }, params_dirty: false, + gas_market, }) } @@ -174,6 +179,14 @@ where self } + pub fn gas_market_mut(&mut self) -> &mut ActorGasMarket { + &mut self.gas_market + } + + pub fn gas_market(&self) -> &ActorGasMarket { + &self.gas_market + } + /// Execute message implicitly. pub fn execute_implicit(&mut self, msg: Message) -> ExecResult { self.execute_message(msg, ApplyKind::Implicit) @@ -286,12 +299,9 @@ where self.update_params(|p| f(&mut p.app_version)) } - /// Update the application version. - pub fn update_base_fee(&mut self, f: F) - where - F: FnOnce(&mut TokenAmount), - { - self.update_params(|p| f(&mut p.base_fee)) + pub fn update_gas_market(&mut self) -> anyhow::Result<()> { + let height = self.block_height(); + self.gas_market.commit(&mut self.executor, height) } /// Update the circulating supply, effective from the next block. From 79d1264b59d37a4d6113f04e9f16c053d555636a Mon Sep 17 00:00:00 2001 From: cryptoAtwill Date: Tue, 13 Aug 2024 19:31:33 +0800 Subject: [PATCH 26/69] merge with main --- Cargo.lock | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.lock b/Cargo.lock index b61ddd29d5..f50cd93960 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3342,6 +3342,7 @@ dependencies = [ "serde", "serde_json", "serde_with 2.3.3", + "snap", "strum", "tempfile", "tendermint 0.31.1", From 6d15ea6d2db018687c79cd029476a9219cf62cf8 Mon Sep 17 00:00:00 2001 From: cryptoAtwill Date: Tue, 13 Aug 2024 21:42:06 +0800 Subject: [PATCH 27/69] update based on review --- Cargo.lock | 1 + fendermint/actors/gas_market/Cargo.toml | 1 + fendermint/actors/gas_market/src/lib.rs | 78 +++++++++++-------- fendermint/vm/actor_interface/src/gas.rs | 2 +- .../vm/interpreter/src/fvm/gas/actor.rs | 14 ++-- 5 files changed, 57 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4949a73137..b60c2bc1d2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2848,6 +2848,7 @@ dependencies = [ "fvm_ipld_encoding", "fvm_shared", "hex-literal 0.4.1", + "lazy_static", "log", "multihash 0.18.1", "num-derive 0.3.3", diff --git a/fendermint/actors/gas_market/Cargo.toml b/fendermint/actors/gas_market/Cargo.toml index a2ff9f0eb8..a4cc318da4 100644 --- a/fendermint/actors/gas_market/Cargo.toml +++ b/fendermint/actors/gas_market/Cargo.toml @@ -22,6 +22,7 @@ log = { workspace = true } multihash = { workspace = true } num-derive = { workspace = true } num-traits = { workspace = true } +lazy_static = { workspace = true } serde = { workspace = true } hex-literal = { workspace = true } frc42_dispatch = { workspace = true } diff --git a/fendermint/actors/gas_market/src/lib.rs b/fendermint/actors/gas_market/src/lib.rs index bb6b03b2fd..8bc2b561e4 100644 --- a/fendermint/actors/gas_market/src/lib.rs +++ b/fendermint/actors/gas_market/src/lib.rs @@ -8,8 +8,8 @@ use fil_actors_runtime::{actor_dispatch, ActorError}; use fvm_ipld_encoding::tuple::*; use fvm_shared::econ::TokenAmount; use fvm_shared::METHOD_CONSTRUCTOR; +use lazy_static::lazy_static; use num_derive::FromPrimitive; -use num_traits::Zero; use std::ops::Mul; #[cfg(feature = "fil-actor")] @@ -19,8 +19,11 @@ fil_actors_runtime::wasm_trampoline!(EIP1559GasMarketActor); const BASE_FEE_MAX_CHANGE_DENOMINATOR: u64 = 8; /// Elasticity multiplier as defined in [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) const ELASTICITY_MULTIPLIER: u64 = 2; -/// Initial base fee as defined in [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) -pub const INITIAL_BASE_FEE: u64 = 1_000_000_000; +lazy_static! { + /// Initial base fee as defined in [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) + static ref INITIAL_BASE_FEE: TokenAmount = TokenAmount::from_atto(1_000_000_000); + static ref MIN_BASE_FEE: TokenAmount = TokenAmount::from_atto(100); +} pub const IPC_GAS_MARKET_ACTOR_NAME: &str = "gas_market"; pub type Gas = u64; pub type GasMarketReading = EIP1559GasState; @@ -31,6 +34,16 @@ pub struct EIP1559GasState { pub base_fee: TokenAmount, } +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone)] +pub struct BlockGasUtilization { + pub block_gas_used: Gas, +} + +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone)] +pub struct SetConstants { + pub block_gas_limit: Gas, +} + pub struct EIP1559GasMarketActor {} #[derive(FromPrimitive)] @@ -38,24 +51,22 @@ pub struct EIP1559GasMarketActor {} pub enum Method { Constructor = METHOD_CONSTRUCTOR, CurrentGasReading = frc42_dispatch::method_hash!("CurrentGasReading"), - SetBlockGasLimit = frc42_dispatch::method_hash!("SetBlockGasLimit"), - UpdateBlockGasConsumption = frc42_dispatch::method_hash!("UpdateBlockGasConsumption"), + SetConstants = frc42_dispatch::method_hash!("SetConstants"), + UpdateUtilization = frc42_dispatch::method_hash!("UpdateUtilization"), } impl EIP1559GasMarketActor { /// Creates the actor pub fn constructor(rt: &impl Runtime, st: EIP1559GasState) -> Result<(), ActorError> { rt.validate_immediate_caller_is(std::iter::once(&SYSTEM_ACTOR_ADDR))?; - rt.create(&st)?; - - Ok(()) + rt.create(&st) } - fn set_block_gas_limit(rt: &impl Runtime, block_gas_limit: Gas) -> Result<(), ActorError> { + fn set_constants(rt: &impl Runtime, constants: SetConstants) -> Result<(), ActorError> { rt.validate_immediate_caller_is(std::iter::once(&SYSTEM_ACTOR_ADDR))?; rt.transaction(|st: &mut EIP1559GasState, _rt| { - st.block_gas_limit = block_gas_limit; + st.block_gas_limit = constants.block_gas_limit; Ok(()) })?; @@ -66,36 +77,39 @@ impl EIP1559GasMarketActor { rt.state() } - fn update_block_gas_consumption( + fn update_utilization( rt: &impl Runtime, - block_gas_used: Gas, + utilization: BlockGasUtilization, ) -> Result<(), ActorError> { rt.validate_immediate_caller_is(std::iter::once(&SYSTEM_ACTOR_ADDR))?; rt.transaction(|st: &mut EIP1559GasState, _rt| { - st.base_fee = update_base_fee(st.block_gas_limit, block_gas_used, st.base_fee.clone()); + st.base_fee = st.next_base_fee(utilization.block_gas_used); Ok(()) }) } } -fn update_base_fee(gas_limit: Gas, gas_used: Gas, base_fee: TokenAmount) -> TokenAmount { - let gas_target = gas_limit / ELASTICITY_MULTIPLIER; +impl EIP1559GasState { + fn next_base_fee(&self, gas_used: Gas) -> TokenAmount { + let base_fee = self.base_fee.clone(); + let gas_target = self.block_gas_limit / ELASTICITY_MULTIPLIER; - if gas_used == gas_target { - return base_fee; - } + if gas_used == gas_target { + return base_fee; + } + + if gas_used > gas_target { + let gas_used_delta = gas_used - gas_target; + let base_fee_delta = base_fee + .clone() + .mul(gas_used_delta) + .div_floor(gas_target) + .div_floor(BASE_FEE_MAX_CHANGE_DENOMINATOR) + .max(TokenAmount::from_atto(1)); + return base_fee + base_fee_delta; + } - if gas_used > gas_target { - let gas_used_delta = gas_used - gas_target; - let base_fee_delta = base_fee - .clone() - .mul(gas_used_delta) - .div_floor(gas_target) - .div_floor(BASE_FEE_MAX_CHANGE_DENOMINATOR) - .max(TokenAmount::from_atto(1)); - base_fee + base_fee_delta - } else { let gas_used_delta = gas_target - gas_used; let base_fee_per_gas_delta = base_fee .clone() @@ -103,7 +117,7 @@ fn update_base_fee(gas_limit: Gas, gas_used: Gas, base_fee: TokenAmount) -> Toke .div_floor(gas_target) .div_floor(BASE_FEE_MAX_CHANGE_DENOMINATOR); if base_fee_per_gas_delta > base_fee { - TokenAmount::zero() + MIN_BASE_FEE.clone() } else { base_fee - base_fee_per_gas_delta } @@ -119,9 +133,9 @@ impl ActorCode for EIP1559GasMarketActor { actor_dispatch! { Constructor => constructor, - SetBlockGasLimit => set_block_gas_limit, - CurrentGasReading => get_state, - UpdateBlockGasConsumption => update_block_gas_consumption, + SetConstants => set_constants, + CurrentGasReading => current_gas_reading, + UpdateUtilization => update_utilization, } } diff --git a/fendermint/vm/actor_interface/src/gas.rs b/fendermint/vm/actor_interface/src/gas.rs index 3fae569a77..ccc6c1f18a 100644 --- a/fendermint/vm/actor_interface/src/gas.rs +++ b/fendermint/vm/actor_interface/src/gas.rs @@ -1,4 +1,4 @@ // Copyright 2022-2024 Protocol Labs // SPDX-License-Identifier: Apache-2.0, MIT -define_id!(GAS { id: 98 }); +define_id!(GAS_MARKET { id: 98 }); diff --git a/fendermint/vm/interpreter/src/fvm/gas/actor.rs b/fendermint/vm/interpreter/src/fvm/gas/actor.rs index 00ae0f32b9..5ec0762f4f 100644 --- a/fendermint/vm/interpreter/src/fvm/gas/actor.rs +++ b/fendermint/vm/interpreter/src/fvm/gas/actor.rs @@ -2,7 +2,7 @@ use crate::fvm::gas::{Gas, GasMarket}; use crate::fvm::FvmMessage; use anyhow::Context; -use fendermint_vm_actor_interface::gas::GAS_ACTOR_ADDR; +use fendermint_vm_actor_interface::gas::GAS_MARKET_ACTOR_ADDR; use fendermint_vm_actor_interface::system; use fvm::executor::{ApplyKind, Executor}; use fvm_ipld_encoding::BytesDe; @@ -40,11 +40,11 @@ impl ActorGasMarket { ) -> anyhow::Result { let msg = FvmMessage { from: system::SYSTEM_ACTOR_ADDR, - to: GAS_ACTOR_ADDR, + to: GAS_MARKET_ACTOR_ADDR, sequence: block_height as u64, // exclude this from gas restriction gas_limit: u64::MAX, - method_num: fendermint_actor_gas_market::Method::GetState as u64, + method_num: fendermint_actor_gas_market::Method::CurrentGasReading as u64, params: fvm_ipld_encoding::RawBytes::serialize(())?, value: Default::default(), version: Default::default(), @@ -79,15 +79,17 @@ impl ActorGasMarket { block_height: ChainEpoch, ) -> anyhow::Result<()> { let block_gas_used = self.block_gas_used; - let params = fvm_ipld_encoding::RawBytes::serialize(block_gas_used)?; + let params = fvm_ipld_encoding::RawBytes::serialize( + fendermint_actor_gas_market::BlockGasUtilization { block_gas_used }, + )?; let msg = FvmMessage { from: system::SYSTEM_ACTOR_ADDR, - to: GAS_ACTOR_ADDR, + to: GAS_MARKET_ACTOR_ADDR, sequence: block_height as u64, // exclude this from gas restriction gas_limit: u64::MAX, - method_num: fendermint_actor_gas_market::Method::UpdateBlockGasConsumption as u64, + method_num: fendermint_actor_gas_market::Method::UpdateUtilization as u64, params, value: Default::default(), version: Default::default(), From e7b3e0654d67f0c615f81f63b6ba0ed978d6cd08 Mon Sep 17 00:00:00 2001 From: cryptoAtwill Date: Tue, 13 Aug 2024 22:29:32 +0800 Subject: [PATCH 28/69] update base fee logic --- fendermint/actors/gas_market/src/lib.rs | 48 +++++++++++-------------- 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/fendermint/actors/gas_market/src/lib.rs b/fendermint/actors/gas_market/src/lib.rs index 8bc2b561e4..a90c3fa811 100644 --- a/fendermint/actors/gas_market/src/lib.rs +++ b/fendermint/actors/gas_market/src/lib.rs @@ -10,7 +10,7 @@ use fvm_shared::econ::TokenAmount; use fvm_shared::METHOD_CONSTRUCTOR; use lazy_static::lazy_static; use num_derive::FromPrimitive; -use std::ops::Mul; +use std::cmp::Ordering; #[cfg(feature = "fil-actor")] fil_actors_runtime::wasm_trampoline!(EIP1559GasMarketActor); @@ -22,7 +22,7 @@ const ELASTICITY_MULTIPLIER: u64 = 2; lazy_static! { /// Initial base fee as defined in [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) static ref INITIAL_BASE_FEE: TokenAmount = TokenAmount::from_atto(1_000_000_000); - static ref MIN_BASE_FEE: TokenAmount = TokenAmount::from_atto(100); + static ref MINIMAL_BASE_FEE: TokenAmount = TokenAmount::from_atto(100); } pub const IPC_GAS_MARKET_ACTOR_NAME: &str = "gas_market"; pub type Gas = u64; @@ -95,31 +95,25 @@ impl EIP1559GasState { let base_fee = self.base_fee.clone(); let gas_target = self.block_gas_limit / ELASTICITY_MULTIPLIER; - if gas_used == gas_target { - return base_fee; - } - - if gas_used > gas_target { - let gas_used_delta = gas_used - gas_target; - let base_fee_delta = base_fee - .clone() - .mul(gas_used_delta) - .div_floor(gas_target) - .div_floor(BASE_FEE_MAX_CHANGE_DENOMINATOR) - .max(TokenAmount::from_atto(1)); - return base_fee + base_fee_delta; - } - - let gas_used_delta = gas_target - gas_used; - let base_fee_per_gas_delta = base_fee - .clone() - .mul(gas_used_delta) - .div_floor(gas_target) - .div_floor(BASE_FEE_MAX_CHANGE_DENOMINATOR); - if base_fee_per_gas_delta > base_fee { - MIN_BASE_FEE.clone() - } else { - base_fee - base_fee_per_gas_delta + match gas_used.cmp(&gas_target) { + Ordering::Equal => base_fee, + Ordering::Less => { + let base_fee_delta = base_fee.atto() * (gas_target - gas_used) + / gas_target + / BASE_FEE_MAX_CHANGE_DENOMINATOR; + let base_fee_delta = TokenAmount::from_atto(base_fee_delta); + if base_fee_delta >= base_fee { + MINIMAL_BASE_FEE.clone() + } else { + base_fee - base_fee_delta + } + } + Ordering::Greater => { + let gas_used_delta = gas_used - gas_target; + let delta = + base_fee.atto() * gas_used_delta / gas_target / BASE_FEE_MAX_CHANGE_DENOMINATOR; + base_fee + TokenAmount::from_atto(delta).max(TokenAmount::from_atto(1)) + } } } } From 0ae95c8eb681b0a2f85f01043a0419dcd0594428 Mon Sep 17 00:00:00 2001 From: cryptoAtwill <108330426+cryptoAtwill@users.noreply.github.com> Date: Wed, 14 Aug 2024 15:06:43 +0800 Subject: [PATCH 29/69] Update fevm-contract-tests.yaml --- .github/workflows/fevm-contract-tests.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/fevm-contract-tests.yaml b/.github/workflows/fevm-contract-tests.yaml index f4b59d5e43..fcf8f6e885 100644 --- a/.github/workflows/fevm-contract-tests.yaml +++ b/.github/workflows/fevm-contract-tests.yaml @@ -71,8 +71,7 @@ jobs: id: docker-build working-directory: ipc/fendermint run: | - export PATH="$PATH:/home/runner/.config/.foundry/bin" - docker pull ghcr.io/consensus-shipyard/fendermint:latest + make docker-build - name: Run a testnode id: testnode working-directory: ipc/ From 169bc367c06f90b394cb578d143bd3a3c8e28638 Mon Sep 17 00:00:00 2001 From: cryptoAtwill Date: Wed, 14 Aug 2024 15:12:23 +0800 Subject: [PATCH 30/69] fix tests --- fendermint/actors/gas_market/src/lib.rs | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/fendermint/actors/gas_market/src/lib.rs b/fendermint/actors/gas_market/src/lib.rs index a90c3fa811..d3b5c18c77 100644 --- a/fendermint/actors/gas_market/src/lib.rs +++ b/fendermint/actors/gas_market/src/lib.rs @@ -135,7 +135,7 @@ impl ActorCode for EIP1559GasMarketActor { #[cfg(test)] mod tests { - use crate::{EIP1559GasMarketActor, EIP1559GasState, Method}; + use crate::{EIP1559GasMarketActor, EIP1559GasState, Method, SetConstants}; use fil_actors_runtime::test_utils::{expect_empty, MockRuntime, SYSTEM_ACTOR_CODE_ID}; use fil_actors_runtime::SYSTEM_ACTOR_ADDR; use fvm_ipld_encoding::ipld_block::IpldBlock; @@ -176,17 +176,11 @@ mod tests { rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); let r = rt.call::( - Method::SetBlockGasLimit as u64, - IpldBlock::serialize_cbor(&20).unwrap(), + Method::SetConstants as u64, + IpldBlock::serialize_cbor(&SetConstants { block_gas_limit: 20 }).unwrap(), ); assert!(r.is_ok()); - let r = rt - .call::( - Method::CurrentGasReading as u64, - IpldBlock::serialize_cbor(&()).unwrap(), - ) - .unwrap(); let s = rt.get_state::(); assert_eq!(s.block_gas_limit, 20); } @@ -199,8 +193,8 @@ mod tests { let code = rt .call::( - Method::SetBlockGasLimit as u64, - IpldBlock::serialize_cbor(&20).unwrap(), + Method::SetConstants as u64, + IpldBlock::serialize_cbor(&SetConstants { block_gas_limit: 20}).unwrap(), ) .unwrap_err() .exit_code(); From fa372d69ee8ab395c33eacd6f000267e5f043499 Mon Sep 17 00:00:00 2001 From: cryptoAtwill <108330426+cryptoAtwill@users.noreply.github.com> Date: Wed, 14 Aug 2024 15:20:12 +0800 Subject: [PATCH 31/69] Update fevm-contract-tests.yaml --- .github/workflows/fevm-contract-tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/fevm-contract-tests.yaml b/.github/workflows/fevm-contract-tests.yaml index fcf8f6e885..b276b9e759 100644 --- a/.github/workflows/fevm-contract-tests.yaml +++ b/.github/workflows/fevm-contract-tests.yaml @@ -71,7 +71,7 @@ jobs: id: docker-build working-directory: ipc/fendermint run: | - make docker-build + export PATH="$PATH:/home/runner/.config/.foundry/bin" && make docker-build - name: Run a testnode id: testnode working-directory: ipc/ From 5332a31b9f49ad8a83b2a6424bf2929191d201d0 Mon Sep 17 00:00:00 2001 From: cryptoAtwill <108330426+cryptoAtwill@users.noreply.github.com> Date: Wed, 14 Aug 2024 16:29:24 +0800 Subject: [PATCH 32/69] Update testnode.toml --- infra/fendermint/scripts/testnode.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/infra/fendermint/scripts/testnode.toml b/infra/fendermint/scripts/testnode.toml index 3477c8039f..7bf1de9bf9 100644 --- a/infra/fendermint/scripts/testnode.toml +++ b/infra/fendermint/scripts/testnode.toml @@ -6,7 +6,6 @@ workspace = false dependencies = [ "create-log-volume", "testnode-down", - "fendermint-pull", "testnode-init", "docker-network-create", "cometbft-init", From ed66780cd6efc25b53367f4cdca177a7f4ea1d01 Mon Sep 17 00:00:00 2001 From: cryptoAtwill Date: Wed, 14 Aug 2024 17:02:45 +0800 Subject: [PATCH 33/69] skip fendermint build --- infra/fendermint/scripts/testnode.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/infra/fendermint/scripts/testnode.toml b/infra/fendermint/scripts/testnode.toml index 7bf1de9bf9..0fe82fc5d6 100644 --- a/infra/fendermint/scripts/testnode.toml +++ b/infra/fendermint/scripts/testnode.toml @@ -6,6 +6,7 @@ workspace = false dependencies = [ "create-log-volume", "testnode-down", + "fendermint-pull", "testnode-init", "docker-network-create", "cometbft-init", @@ -18,6 +19,7 @@ dependencies = [ "ethapi-start", "testnode-report", ] +env = { FM_PULL_SKIP = true } [tasks.testnode-init] dependencies = [ @@ -70,6 +72,7 @@ dependencies = [ "genesis-new-accounts", "genesis-add-validator", "genesis-new-gateway", + "genesis-seal", "genesis-write", "testnode-export-keys", ] From 272c29c1133fde9cb57b55b33b259374abf7af0f Mon Sep 17 00:00:00 2001 From: cryptoAtwill Date: Wed, 14 Aug 2024 17:33:41 +0800 Subject: [PATCH 34/69] remove pull --- infra/fendermint/scripts/testnode.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/infra/fendermint/scripts/testnode.toml b/infra/fendermint/scripts/testnode.toml index 0fe82fc5d6..503f116c60 100644 --- a/infra/fendermint/scripts/testnode.toml +++ b/infra/fendermint/scripts/testnode.toml @@ -6,7 +6,6 @@ workspace = false dependencies = [ "create-log-volume", "testnode-down", - "fendermint-pull", "testnode-init", "docker-network-create", "cometbft-init", @@ -19,7 +18,6 @@ dependencies = [ "ethapi-start", "testnode-report", ] -env = { FM_PULL_SKIP = true } [tasks.testnode-init] dependencies = [ From 29cda12c3144c5830cd4a70bd24a26538a099c6f Mon Sep 17 00:00:00 2001 From: cryptoAtwill Date: Thu, 15 Aug 2024 20:26:29 +0800 Subject: [PATCH 35/69] prepare and process --- fendermint/actors/gas_market/src/lib.rs | 10 ++- fendermint/app/src/app.rs | 28 +++++++- fendermint/vm/interpreter/src/chain.rs | 91 ++++++++++++++++++++++--- 3 files changed, 113 insertions(+), 16 deletions(-) diff --git a/fendermint/actors/gas_market/src/lib.rs b/fendermint/actors/gas_market/src/lib.rs index d3b5c18c77..37b1e4da12 100644 --- a/fendermint/actors/gas_market/src/lib.rs +++ b/fendermint/actors/gas_market/src/lib.rs @@ -177,7 +177,10 @@ mod tests { let r = rt.call::( Method::SetConstants as u64, - IpldBlock::serialize_cbor(&SetConstants { block_gas_limit: 20 }).unwrap(), + IpldBlock::serialize_cbor(&SetConstants { + block_gas_limit: 20, + }) + .unwrap(), ); assert!(r.is_ok()); @@ -194,7 +197,10 @@ mod tests { let code = rt .call::( Method::SetConstants as u64, - IpldBlock::serialize_cbor(&SetConstants { block_gas_limit: 20}).unwrap(), + IpldBlock::serialize_cbor(&SetConstants { + block_gas_limit: 20, + }) + .unwrap(), ) .unwrap_err() .exit_code(); diff --git a/fendermint/app/src/app.rs b/fendermint/app/src/app.rs index af4e8da72b..09fefffb3b 100644 --- a/fendermint/app/src/app.rs +++ b/fendermint/app/src/app.rs @@ -407,7 +407,7 @@ where Genesis = Vec, Output = FvmGenesisOutput, >, - I: ProposalInterpreter>, + I: ProposalInterpreter), Message = Vec>, I: ExecInterpreter< State = (ChainEnv, FvmExecState), Message = Vec, @@ -615,13 +615,25 @@ where ); let txs = request.txs.into_iter().map(|tx| tx.to_vec()).collect(); + let (state_params, block_height) = + self.state_params_at_height(request.height.value().into())?; + let state = FvmExecState::new( + self.state_store_clone(), + self.multi_engine.as_ref(), + block_height as ChainEpoch, + state_params, + ) + .context("error creating new state")?; + let txs = self .interpreter - .prepare(self.chain_env.clone(), txs) + .prepare((self.chain_env.clone(), state), txs) .await .context("failed to prepare proposal")?; let txs = txs.into_iter().map(bytes::Bytes::from).collect(); + // TODO: This seems leaky placed here, should be done in `interpreter`, that's where it's ipc + // TODO: aware, here might have filtered more important messages. let (txs, size) = take_until_max_size(txs, request.max_tx_bytes.try_into().unwrap()); emit(BlockProposalSent { @@ -648,9 +660,19 @@ where let size_txs = txs.iter().map(|tx| tx.len()).sum::(); let num_txs = txs.len(); + let (state_params, block_height) = + self.state_params_at_height(request.height.value().into())?; + let state = FvmExecState::new( + self.state_store_clone(), + self.multi_engine.as_ref(), + block_height as ChainEpoch, + state_params, + ) + .context("error creating new state")?; + let accept = self .interpreter - .process(self.chain_env.clone(), txs) + .process((self.chain_env.clone(), state), txs) .await .context("failed to process proposal")?; diff --git a/fendermint/vm/interpreter/src/chain.rs b/fendermint/vm/interpreter/src/chain.rs index 79136138f8..3d7513b3b0 100644 --- a/fendermint/vm/interpreter/src/chain.rs +++ b/fendermint/vm/interpreter/src/chain.rs @@ -1,5 +1,6 @@ // Copyright 2022-2024 Protocol Labs // SPDX-License-Identifier: Apache-2.0, MIT +use crate::fvm::gas::{Gas, GasMarket}; use crate::fvm::state::ipc::GatewayCaller; use crate::fvm::{topdown, FvmApplyRet, PowerUpdates}; use crate::{ @@ -8,7 +9,7 @@ use crate::{ signed::{SignedMessageApplyRes, SignedMessageCheckRes, SyntheticMessage, VerifiableMessage}, CheckInterpreter, ExecInterpreter, GenesisInterpreter, ProposalInterpreter, QueryInterpreter, }; -use anyhow::{bail, Context}; +use anyhow::{anyhow, bail, Context}; use async_stm::atomically; use async_trait::async_trait; use fendermint_tracing::emit; @@ -94,6 +95,56 @@ impl ChainMessageInterpreter { gateway_caller: GatewayCaller::default(), } } + + fn signed_msgs_wtih_gas_limit( + &self, + msgs: Vec, + ) -> anyhow::Result> { + msgs.into_iter() + .map(|msg| match msg { + ChainMessage::Signed(inner) => { + let gas_limit = inner.message.gas_limit; + Ok((ChainMessage::Signed(inner), gas_limit)) + } + ChainMessage::Ipc(_) => { + Err(anyhow!("should not have ipc messages in user proposals")) + } + }) + .collect::>>() + } + + /// Performs message selection: + /// - Order by gas limit in descending order + /// - Make sure total gas limit does not exceed the `total_gas_limit` parameter + fn messages_selection( + &self, + msgs: Vec, + total_gas_limit: Gas, + ) -> anyhow::Result> { + let mut msgs_with_gas_limit = self.signed_msgs_wtih_gas_limit(msgs)?; + + // sort by gas limit descending + msgs_with_gas_limit.sort_by(|a, b| b.1.cmp(&a.1)); + + let mut total_gas_limit_consumed = 0; + let mut msgs = vec![]; + for (msg, gas_limit) in msgs_with_gas_limit { + if total_gas_limit_consumed + gas_limit <= total_gas_limit { + msgs.push(msg); + total_gas_limit_consumed += gas_limit; + } else { + break; + } + } + + tracing::info!( + num_msgs = msgs.len(), + total_gas_limit, + "selected message under total gas limit" + ); + + Ok(msgs) + } } #[async_trait] @@ -102,7 +153,7 @@ where DB: Blockstore + Clone + 'static + Send + Sync, I: Sync + Send, { - type State = ChainEnv; + type State = (ChainEnv, FvmExecState); type Message = ChainMessage; /// Check whether there are any "ready" messages in the IPLD resolution mempool which can be appended to the proposal. @@ -111,11 +162,13 @@ where /// account the transactions which are part of top-down or bottom-up checkpoints, to stay within gas limits. async fn prepare( &self, - state: Self::State, + (chain_env, state): Self::State, mut msgs: Vec, ) -> anyhow::Result> { + msgs = self.messages_selection(msgs, state.gas_market().available_block_gas())?; + // Collect resolved CIDs ready to be proposed from the pool. - let ckpts = atomically(|| state.checkpoint_pool.collect_resolved()).await; + let ckpts = atomically(|| chain_env.checkpoint_pool.collect_resolved()).await; // Create transactions ready to be included on the chain. let ckpts = ckpts.into_iter().map(|ckpt| match ckpt { @@ -124,14 +177,19 @@ where // Prepare top down proposals. // Before we try to find a quorum, pause incoming votes. This is optional but if there are lots of votes coming in it might hold up proposals. - atomically(|| state.parent_finality_votes.pause_votes_until_find_quorum()).await; + atomically(|| { + chain_env + .parent_finality_votes + .pause_votes_until_find_quorum() + }) + .await; // The pre-requisite for proposal is that there is a quorum of gossiped votes at that height. // The final proposal can be at most as high as the quorum, but can be less if we have already, // hit some limits such as how many blocks we can propose in a single step. let finalities = atomically(|| { - let parent = state.parent_finality_provider.next_proposal()?; - let quorum = state + let parent = chain_env.parent_finality_provider.next_proposal()?; + let quorum = chain_env .parent_finality_votes .find_quorum()? .map(|(height, block_hash)| IPCParentFinality { height, block_hash }); @@ -175,7 +233,13 @@ where } /// Perform finality checks on top-down transactions and availability checks on bottom-up transactions. - async fn process(&self, env: Self::State, msgs: Vec) -> anyhow::Result { + async fn process( + &self, + (chain_env, state): Self::State, + msgs: Vec, + ) -> anyhow::Result { + let mut block_gas_usage = 0; + for msg in msgs { match msg { ChainMessage::Ipc(IpcMessage::BottomUpExec(msg)) => { @@ -187,7 +251,7 @@ where // 1) we validated it when it was relayed, and // 2) if a validator proposes something invalid, we can make them pay during execution. let is_resolved = - atomically(|| match env.checkpoint_pool.get_status(&item)? { + atomically(|| match chain_env.checkpoint_pool.get_status(&item)? { None => Ok(false), Some(status) => status.is_resolved(), }) @@ -206,15 +270,20 @@ where block_hash, }; let is_final = - atomically(|| env.parent_finality_provider.check_proposal(&prop)).await; + atomically(|| chain_env.parent_finality_provider.check_proposal(&prop)) + .await; if !is_final { return Ok(false); } } + ChainMessage::Signed(signed) => { + block_gas_usage += signed.message.gas_limit; + } _ => {} }; } - Ok(true) + + Ok(block_gas_usage <= state.gas_market().available_block_gas()) } } From ef2740cf1cbb4e0a6854de2ebed637b61b5e758e Mon Sep 17 00:00:00 2001 From: cryptoAtwill Date: Mon, 19 Aug 2024 13:23:31 +0800 Subject: [PATCH 36/69] rename to genesis builder --- fendermint/app/src/cmd/genesis.rs | 15 ++++++---- fendermint/vm/interpreter/src/genesis.rs | 38 ++++++++++++++---------- 2 files changed, 32 insertions(+), 21 deletions(-) diff --git a/fendermint/app/src/cmd/genesis.rs b/fendermint/app/src/cmd/genesis.rs index 598c4a3c79..134c9ad4df 100644 --- a/fendermint/app/src/cmd/genesis.rs +++ b/fendermint/app/src/cmd/genesis.rs @@ -14,7 +14,7 @@ use fendermint_vm_genesis::{ ipc, Account, Actor, ActorMeta, Collateral, Genesis, Multisig, PermissionMode, SignerAddr, Validator, ValidatorKey, }; -use fendermint_vm_interpreter::genesis::{GenesisAppState, GenesisCreator}; +use fendermint_vm_interpreter::genesis::{GenesisAppState, GenesisBuilder}; use crate::cmd; use crate::options::genesis::*; @@ -290,16 +290,19 @@ fn set_ipc_gateway(genesis_file: &PathBuf, args: &GenesisIpcGatewayArgs) -> anyh } async fn seal_genesis(genesis_file: &PathBuf, args: &SealGenesisArgs) -> anyhow::Result<()> { - let genesis = read_genesis(genesis_file)?; + let genesis_params = read_genesis(genesis_file)?; - let genesis_creator = GenesisCreator::new( + let mut builder = GenesisBuilder::new( args.builtin_actors_path.clone(), args.custom_actors_path.clone(), - args.artifacts_path.clone(), - args.output_path.clone(), + genesis_params, ); - genesis_creator.create(genesis).await + if let Some(ref ipc_system_artifacts) = args.artifacts_path { + builder = builder.with_ipc_system_contracts(ipc_system_artifacts.clone()); + } + + builder.write_to(args.output_path.clone()).await } async fn new_genesis_from_parent( diff --git a/fendermint/vm/interpreter/src/genesis.rs b/fendermint/vm/interpreter/src/genesis.rs index de9a258b45..aefd7d2425 100644 --- a/fendermint/vm/interpreter/src/genesis.rs +++ b/fendermint/vm/interpreter/src/genesis.rs @@ -142,51 +142,59 @@ pub struct GenesisOutput { pub validators: Vec>, } -pub struct GenesisCreator { +pub struct GenesisBuilder { /// Hardhat like util to deploy ipc contracts hardhat: Option, /// The built in actors bundle path builtin_actors_path: PathBuf, /// The custom actors bundle path custom_actors_path: PathBuf, - /// The CAR path to flush the sealed genesis state - out_path: PathBuf, + + /// Genesis params + genesis_params: Genesis, } -impl GenesisCreator { +impl GenesisBuilder { pub fn new( builtin_actors_path: PathBuf, custom_actors_path: PathBuf, - maybe_artifacts_path: Option, - sealed_out_path: PathBuf, + genesis_params: Genesis, ) -> Self { Self { - hardhat: maybe_artifacts_path.map(Hardhat::new), + hardhat: None, builtin_actors_path, custom_actors_path, - out_path: sealed_out_path, + genesis_params, } } - /// Initialize actor states from the Genesis parameters - pub async fn create(&self, genesis: Genesis) -> anyhow::Result<()> { + pub fn with_ipc_system_contracts(mut self, path: PathBuf) -> Self { + self.hardhat = Some(Hardhat::new(path)); + self + } + + /// Initialize actor states from the Genesis parameters and write the sealed genesis state to + /// a CAR file specified by `out_path` + pub async fn write_to(&self, out_path: PathBuf) -> anyhow::Result<()> { let mut state = self.init_state().await?; - let out = self.populate_state(&mut state, genesis)?; + let genesis_state = self.populate_state(&mut state, self.genesis_params.clone())?; let (state_root, store) = state.finalize()?; - self.write_car(state_root, out, store).await + self.write_car(state_root, genesis_state, out_path, store) + .await } async fn write_car( &self, state_root: Cid, - out: GenesisOutput, + genesis_state: GenesisOutput, + out_path: PathBuf, store: MemoryBlockstore, ) -> anyhow::Result<()> { - let file = tokio::fs::File::create(&self.out_path).await?; + let file = tokio::fs::File::create(&out_path).await?; tracing::info!(state_root = state_root.to_string(), "state root"); - let metadata = GenesisMetadata::new(state_root, out); + let metadata = GenesisMetadata::new(state_root, genesis_state); let streamer = StateTreeStreamer::new(state_root, store); let (metadata_cid, metadata_bytes) = derive_cid(&metadata)?; From b07453024b695e228f600aa667b4739061372029 Mon Sep 17 00:00:00 2001 From: cryptoAtwill <108330426+cryptoAtwill@users.noreply.github.com> Date: Tue, 20 Aug 2024 16:17:47 +0800 Subject: [PATCH 37/69] Update fendermint/vm/interpreter/src/fvm/state/exec.rs Co-authored-by: raulk --- fendermint/vm/interpreter/src/fvm/state/exec.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fendermint/vm/interpreter/src/fvm/state/exec.rs b/fendermint/vm/interpreter/src/fvm/state/exec.rs index cbd6f6bc46..b93201afbd 100644 --- a/fendermint/vm/interpreter/src/fvm/state/exec.rs +++ b/fendermint/vm/interpreter/src/fvm/state/exec.rs @@ -114,7 +114,8 @@ where /// Indicate whether the parameters have been updated. params_dirty: bool, - +/// Keeps track of block gas usage during execution, and takes care of updating +/// the chosen gas market strategy (by default an on-chain actor delivering EIP-1559 behaviour). gas_market: ActorGasMarket, } From 5b4d061988bea91f68702d111a3781cf28051a4a Mon Sep 17 00:00:00 2001 From: cryptoAtwill Date: Tue, 20 Aug 2024 16:57:38 +0800 Subject: [PATCH 38/69] update bail to logging --- fendermint/actors/gas_market/src/lib.rs | 10 ++++++++-- fendermint/vm/interpreter/src/fvm/exec.rs | 4 ++-- fendermint/vm/interpreter/src/fvm/gas/actor.rs | 4 ++-- fendermint/vm/interpreter/src/fvm/state/exec.rs | 4 ++-- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/fendermint/actors/gas_market/src/lib.rs b/fendermint/actors/gas_market/src/lib.rs index d3b5c18c77..37b1e4da12 100644 --- a/fendermint/actors/gas_market/src/lib.rs +++ b/fendermint/actors/gas_market/src/lib.rs @@ -177,7 +177,10 @@ mod tests { let r = rt.call::( Method::SetConstants as u64, - IpldBlock::serialize_cbor(&SetConstants { block_gas_limit: 20 }).unwrap(), + IpldBlock::serialize_cbor(&SetConstants { + block_gas_limit: 20, + }) + .unwrap(), ); assert!(r.is_ok()); @@ -194,7 +197,10 @@ mod tests { let code = rt .call::( Method::SetConstants as u64, - IpldBlock::serialize_cbor(&SetConstants { block_gas_limit: 20}).unwrap(), + IpldBlock::serialize_cbor(&SetConstants { + block_gas_limit: 20, + }) + .unwrap(), ) .unwrap_err() .exit_code(); diff --git a/fendermint/vm/interpreter/src/fvm/exec.rs b/fendermint/vm/interpreter/src/fvm/exec.rs index bdcee5a600..95a7e974dd 100644 --- a/fendermint/vm/interpreter/src/fvm/exec.rs +++ b/fendermint/vm/interpreter/src/fvm/exec.rs @@ -1,7 +1,7 @@ // Copyright 2022-2024 Protocol Labs // SPDX-License-Identifier: Apache-2.0, MIT -use anyhow::{bail, Context}; +use anyhow::Context; use async_trait::async_trait; use std::collections::HashMap; @@ -159,7 +159,7 @@ where (apply_ret, emitters, latency) } else { if msg.gas_limit > state.gas_market().available_block_gas() { - bail!("gas limit exceed available block gas limit") + tracing::warn!("gas limit exceed available block gas limit"); } let (execution_result, latency) = measure_time(|| state.execute_explicit(msg.clone())); diff --git a/fendermint/vm/interpreter/src/fvm/gas/actor.rs b/fendermint/vm/interpreter/src/fvm/gas/actor.rs index 5ec0762f4f..196adb5e80 100644 --- a/fendermint/vm/interpreter/src/fvm/gas/actor.rs +++ b/fendermint/vm/interpreter/src/fvm/gas/actor.rs @@ -25,9 +25,9 @@ impl GasMarket for ActorGasMarket { fn record_gas_used(&mut self, gas: Gas) -> anyhow::Result<()> { if self.block_gas_used + gas >= self.block_gas_limit { - anyhow::bail!("out of block gas") + tracing::warn!("out of block gas, should not have happened") } - self.block_gas_used += gas; + self.block_gas_used = self.block_gas_used.saturating_add(gas); Ok(()) } diff --git a/fendermint/vm/interpreter/src/fvm/state/exec.rs b/fendermint/vm/interpreter/src/fvm/state/exec.rs index b93201afbd..782d3c3fe7 100644 --- a/fendermint/vm/interpreter/src/fvm/state/exec.rs +++ b/fendermint/vm/interpreter/src/fvm/state/exec.rs @@ -114,8 +114,8 @@ where /// Indicate whether the parameters have been updated. params_dirty: bool, -/// Keeps track of block gas usage during execution, and takes care of updating -/// the chosen gas market strategy (by default an on-chain actor delivering EIP-1559 behaviour). + /// Keeps track of block gas usage during execution, and takes care of updating + /// the chosen gas market strategy (by default an on-chain actor delivering EIP-1559 behaviour). gas_market: ActorGasMarket, } From ca9ba85fcc97d5f65d0e0fa798d0109e74aa06c8 Mon Sep 17 00:00:00 2001 From: cryptoAtwill Date: Tue, 20 Aug 2024 16:58:50 +0800 Subject: [PATCH 39/69] add header --- fendermint/vm/interpreter/src/fvm/gas/actor.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fendermint/vm/interpreter/src/fvm/gas/actor.rs b/fendermint/vm/interpreter/src/fvm/gas/actor.rs index 196adb5e80..fd2d79ca6f 100644 --- a/fendermint/vm/interpreter/src/fvm/gas/actor.rs +++ b/fendermint/vm/interpreter/src/fvm/gas/actor.rs @@ -1,3 +1,5 @@ +// Copyright 2022-2024 Protocol Labs +// SPDX-License-Identifier: Apache-2.0, MIT use crate::fvm::gas::{Gas, GasMarket}; use crate::fvm::FvmMessage; use anyhow::Context; From f04e987b8b22a183414e9c81da90bf0fc6308a72 Mon Sep 17 00:00:00 2001 From: cryptoAtwill Date: Tue, 20 Aug 2024 17:28:06 +0800 Subject: [PATCH 40/69] init gas market in genesis --- fendermint/actors/gas_market/src/lib.rs | 4 ++++ fendermint/vm/interpreter/src/genesis.rs | 16 +++++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/fendermint/actors/gas_market/src/lib.rs b/fendermint/actors/gas_market/src/lib.rs index 37b1e4da12..138f89847e 100644 --- a/fendermint/actors/gas_market/src/lib.rs +++ b/fendermint/actors/gas_market/src/lib.rs @@ -91,6 +91,10 @@ impl EIP1559GasMarketActor { } impl EIP1559GasState { + pub fn new(block_gas_limit: Gas) -> Self { + Self { block_gas_limit, base_fee: INITIAL_BASE_FEE.clone() } + } + fn next_base_fee(&self, gas_used: Gas) -> TokenAmount { let base_fee = self.base_fee.clone(); let gas_target = self.block_gas_limit / ELASTICITY_MULTIPLIER; diff --git a/fendermint/vm/interpreter/src/genesis.rs b/fendermint/vm/interpreter/src/genesis.rs index aefd7d2425..bc4a906ec5 100644 --- a/fendermint/vm/interpreter/src/genesis.rs +++ b/fendermint/vm/interpreter/src/genesis.rs @@ -19,7 +19,7 @@ use fendermint_vm_actor_interface::diamond::{EthContract, EthContractMap}; use fendermint_vm_actor_interface::eam::EthAddress; use fendermint_vm_actor_interface::ipc::IPC_CONTRACTS; use fendermint_vm_actor_interface::{ - account, burntfunds, chainmetadata, cron, eam, init, ipc, reward, system, EMPTY_ARR, + account, burntfunds, chainmetadata, gas, cron, eam, init, ipc, reward, system, EMPTY_ARR, }; use fendermint_vm_core::{chainid, Timestamp}; use fendermint_vm_genesis::{ActorMeta, Collateral, Genesis, Power, PowerScale, Validator}; @@ -28,6 +28,7 @@ use fvm::engine::MultiEngine; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_car::{load_car, CarHeader}; use fvm_ipld_encoding::CborStore; +use fvm_shared::BLOCK_GAS_LIMIT; use fvm_shared::chainid::ChainID; use fvm_shared::econ::TokenAmount; use fvm_shared::version::NetworkVersion; @@ -417,6 +418,19 @@ impl GenesisBuilder { ) .context("failed to replace built in eam actor")?; + let gas_market_state = fendermint_actor_gas_market::EIP1559GasState::new( + BLOCK_GAS_LIMIT + ); + state + .create_custom_actor( + fendermint_actor_gas_market::IPC_GAS_MARKET_ACTOR_NAME, + gas::GAS_MARKET_ACTOR_ID, + &gas_market_state, + TokenAmount::zero(), + None, + ) + .context("failed to create gas market actor")?; + // STAGE 2: Create non-builtin accounts which do not have a fixed ID. // The next ID is going to be _after_ the accounts, which have already been assigned an ID by the `Init` actor. From a33c4b924516eaf969f74b38091c8ec95ada57e9 Mon Sep 17 00:00:00 2001 From: cryptoAtwill Date: Tue, 20 Aug 2024 17:35:21 +0800 Subject: [PATCH 41/69] fmt --- fendermint/actors/gas_market/src/lib.rs | 5 ++++- fendermint/vm/interpreter/src/genesis.rs | 8 +++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/fendermint/actors/gas_market/src/lib.rs b/fendermint/actors/gas_market/src/lib.rs index 138f89847e..045fb2cba7 100644 --- a/fendermint/actors/gas_market/src/lib.rs +++ b/fendermint/actors/gas_market/src/lib.rs @@ -92,7 +92,10 @@ impl EIP1559GasMarketActor { impl EIP1559GasState { pub fn new(block_gas_limit: Gas) -> Self { - Self { block_gas_limit, base_fee: INITIAL_BASE_FEE.clone() } + Self { + block_gas_limit, + base_fee: INITIAL_BASE_FEE.clone(), + } } fn next_base_fee(&self, gas_used: Gas) -> TokenAmount { diff --git a/fendermint/vm/interpreter/src/genesis.rs b/fendermint/vm/interpreter/src/genesis.rs index bc4a906ec5..9ac78235ff 100644 --- a/fendermint/vm/interpreter/src/genesis.rs +++ b/fendermint/vm/interpreter/src/genesis.rs @@ -19,7 +19,7 @@ use fendermint_vm_actor_interface::diamond::{EthContract, EthContractMap}; use fendermint_vm_actor_interface::eam::EthAddress; use fendermint_vm_actor_interface::ipc::IPC_CONTRACTS; use fendermint_vm_actor_interface::{ - account, burntfunds, chainmetadata, gas, cron, eam, init, ipc, reward, system, EMPTY_ARR, + account, burntfunds, chainmetadata, cron, eam, gas, init, ipc, reward, system, EMPTY_ARR, }; use fendermint_vm_core::{chainid, Timestamp}; use fendermint_vm_genesis::{ActorMeta, Collateral, Genesis, Power, PowerScale, Validator}; @@ -28,10 +28,10 @@ use fvm::engine::MultiEngine; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_car::{load_car, CarHeader}; use fvm_ipld_encoding::CborStore; -use fvm_shared::BLOCK_GAS_LIMIT; use fvm_shared::chainid::ChainID; use fvm_shared::econ::TokenAmount; use fvm_shared::version::NetworkVersion; +use fvm_shared::BLOCK_GAS_LIMIT; use ipc_actors_abis::i_diamond::FacetCut; use num_traits::Zero; @@ -418,9 +418,7 @@ impl GenesisBuilder { ) .context("failed to replace built in eam actor")?; - let gas_market_state = fendermint_actor_gas_market::EIP1559GasState::new( - BLOCK_GAS_LIMIT - ); + let gas_market_state = fendermint_actor_gas_market::EIP1559GasState::new(BLOCK_GAS_LIMIT); state .create_custom_actor( fendermint_actor_gas_market::IPC_GAS_MARKET_ACTOR_NAME, From 9f968961bb8ea23e2e4fe77037087b2c89f822f3 Mon Sep 17 00:00:00 2001 From: cryptoAtwill Date: Tue, 20 Aug 2024 18:09:08 +0800 Subject: [PATCH 42/69] add gas_market to custom actor build --- fendermint/actors/build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fendermint/actors/build.rs b/fendermint/actors/build.rs index 887950ad0a..fc1a180e9a 100644 --- a/fendermint/actors/build.rs +++ b/fendermint/actors/build.rs @@ -8,7 +8,7 @@ use std::path::Path; use std::process::{Command, Stdio}; use std::thread; -const ACTORS: &[&str] = &["chainmetadata", "eam"]; +const ACTORS: &[&str] = &["chainmetadata", "eam", "gas_market"]; const FILES_TO_WATCH: &[&str] = &["Cargo.toml", "src"]; From 5b72dc5b96a527dc8ed44508212b382116df06cc Mon Sep 17 00:00:00 2001 From: cryptoAtwill Date: Tue, 20 Aug 2024 18:37:00 +0800 Subject: [PATCH 43/69] format code --- fendermint/actors/gas_market/src/lib.rs | 1 + .../vm/interpreter/src/fvm/gas/actor.rs | 22 +++++++------------ 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/fendermint/actors/gas_market/src/lib.rs b/fendermint/actors/gas_market/src/lib.rs index 045fb2cba7..967c0ea7ce 100644 --- a/fendermint/actors/gas_market/src/lib.rs +++ b/fendermint/actors/gas_market/src/lib.rs @@ -74,6 +74,7 @@ impl EIP1559GasMarketActor { } fn current_gas_reading(rt: &impl Runtime) -> Result { + rt.validate_immediate_caller_accept_any()?; rt.state() } diff --git a/fendermint/vm/interpreter/src/fvm/gas/actor.rs b/fendermint/vm/interpreter/src/fvm/gas/actor.rs index fd2d79ca6f..4b9b0d0f72 100644 --- a/fendermint/vm/interpreter/src/fvm/gas/actor.rs +++ b/fendermint/vm/interpreter/src/fvm/gas/actor.rs @@ -4,14 +4,12 @@ use crate::fvm::gas::{Gas, GasMarket}; use crate::fvm::FvmMessage; use anyhow::Context; +use fendermint_actor_gas_market::GasMarketReading; use fendermint_vm_actor_interface::gas::GAS_MARKET_ACTOR_ADDR; use fendermint_vm_actor_interface::system; use fvm::executor::{ApplyKind, Executor}; -use fvm_ipld_encoding::BytesDe; use fvm_shared::clock::ChainEpoch; -type GasMarketState = fendermint_actor_gas_market::EIP1559GasState; - #[derive(Default)] pub struct ActorGasMarket { /// The block gas limit @@ -45,9 +43,9 @@ impl ActorGasMarket { to: GAS_MARKET_ACTOR_ADDR, sequence: block_height as u64, // exclude this from gas restriction - gas_limit: u64::MAX, + gas_limit: fvm_shared::BLOCK_GAS_LIMIT, method_num: fendermint_actor_gas_market::Method::CurrentGasReading as u64, - params: fvm_ipld_encoding::RawBytes::serialize(())?, + params: fvm_ipld_encoding::RawBytes::default(), value: Default::default(), version: Default::default(), gas_fee_cap: Default::default(), @@ -61,16 +59,12 @@ impl ActorGasMarket { anyhow::bail!("failed to read gas market state: {}", err); } - let output = apply_ret - .msg_receipt - .return_data - .deserialize::() - .map(|bz| bz.0) - .context("failed to deserialize error data")?; - let state = fvm_ipld_encoding::from_slice::(&output)?; + let reading = + fvm_ipld_encoding::from_slice::(&apply_ret.msg_receipt.return_data) + .context("failed to parse gas market readying")?; Ok(Self { - block_gas_limit: state.block_gas_limit, + block_gas_limit: reading.block_gas_limit, block_gas_used: 0, }) } @@ -90,7 +84,7 @@ impl ActorGasMarket { to: GAS_MARKET_ACTOR_ADDR, sequence: block_height as u64, // exclude this from gas restriction - gas_limit: u64::MAX, + gas_limit: fvm_shared::BLOCK_GAS_LIMIT, method_num: fendermint_actor_gas_market::Method::UpdateUtilization as u64, params, value: Default::default(), From 4de6eac171f46d719f918b57143990b95c85a4a8 Mon Sep 17 00:00:00 2001 From: cryptoAtwill <108330426+cryptoAtwill@users.noreply.github.com> Date: Tue, 20 Aug 2024 20:05:42 +0800 Subject: [PATCH 44/69] feat(node): block gas limit PR review changes (#1107) Co-authored-by: cryptoAtwill Co-authored-by: raulk --- Cargo.lock | 1 + fendermint/actors/gas_market/Cargo.toml | 1 + fendermint/actors/gas_market/src/lib.rs | 127 ++++++++++-------- fendermint/vm/actor_interface/src/gas.rs | 2 +- fendermint/vm/interpreter/src/fvm/exec.rs | 19 ++- .../vm/interpreter/src/fvm/gas/actor.rs | 122 ++++++++--------- fendermint/vm/interpreter/src/fvm/gas/mod.rs | 11 +- fendermint/vm/interpreter/src/fvm/mod.rs | 3 - .../vm/interpreter/src/fvm/state/exec.rs | 25 +++- 9 files changed, 157 insertions(+), 154 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c6203003e3..792df29d4c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2829,6 +2829,7 @@ dependencies = [ "fvm_ipld_encoding", "fvm_shared", "hex-literal 0.4.1", + "lazy_static", "log", "multihash 0.18.1", "num-derive 0.3.3", diff --git a/fendermint/actors/gas_market/Cargo.toml b/fendermint/actors/gas_market/Cargo.toml index a2ff9f0eb8..a4cc318da4 100644 --- a/fendermint/actors/gas_market/Cargo.toml +++ b/fendermint/actors/gas_market/Cargo.toml @@ -22,6 +22,7 @@ log = { workspace = true } multihash = { workspace = true } num-derive = { workspace = true } num-traits = { workspace = true } +lazy_static = { workspace = true } serde = { workspace = true } hex-literal = { workspace = true } frc42_dispatch = { workspace = true } diff --git a/fendermint/actors/gas_market/src/lib.rs b/fendermint/actors/gas_market/src/lib.rs index 93c6af1232..37b1e4da12 100644 --- a/fendermint/actors/gas_market/src/lib.rs +++ b/fendermint/actors/gas_market/src/lib.rs @@ -8,21 +8,25 @@ use fil_actors_runtime::{actor_dispatch, ActorError}; use fvm_ipld_encoding::tuple::*; use fvm_shared::econ::TokenAmount; use fvm_shared::METHOD_CONSTRUCTOR; +use lazy_static::lazy_static; use num_derive::FromPrimitive; -use num_traits::Zero; -use std::ops::Mul; +use std::cmp::Ordering; #[cfg(feature = "fil-actor")] fil_actors_runtime::wasm_trampoline!(EIP1559GasMarketActor); /// Base fee max change denominator as defined in [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) -const EIP1559_BASE_FEE_MAX_CHANGE_DENOMINATOR: u64 = 8; +const BASE_FEE_MAX_CHANGE_DENOMINATOR: u64 = 8; /// Elasticity multiplier as defined in [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) -const EIP1559_ELASTICITY_MULTIPLIER: u64 = 2; -/// Initial base fee as defined in [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) -pub const EIP1559_INITIAL_BASE_FEE: u64 = 1_000_000_000; +const ELASTICITY_MULTIPLIER: u64 = 2; +lazy_static! { + /// Initial base fee as defined in [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) + static ref INITIAL_BASE_FEE: TokenAmount = TokenAmount::from_atto(1_000_000_000); + static ref MINIMAL_BASE_FEE: TokenAmount = TokenAmount::from_atto(100); +} pub const IPC_GAS_MARKET_ACTOR_NAME: &str = "gas_market"; pub type Gas = u64; +pub type GasMarketReading = EIP1559GasState; #[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone)] pub struct EIP1559GasState { @@ -30,81 +34,86 @@ pub struct EIP1559GasState { pub base_fee: TokenAmount, } +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone)] +pub struct BlockGasUtilization { + pub block_gas_used: Gas, +} + +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone)] +pub struct SetConstants { + pub block_gas_limit: Gas, +} + pub struct EIP1559GasMarketActor {} #[derive(FromPrimitive)] #[repr(u64)] pub enum Method { Constructor = METHOD_CONSTRUCTOR, - GetState = frc42_dispatch::method_hash!("GetState"), - SetBlockGasLimit = frc42_dispatch::method_hash!("SetBlockGasLimit"), - UpdateBlockGasConsumption = frc42_dispatch::method_hash!("UpdateBlockGasConsumption"), + CurrentGasReading = frc42_dispatch::method_hash!("CurrentGasReading"), + SetConstants = frc42_dispatch::method_hash!("SetConstants"), + UpdateUtilization = frc42_dispatch::method_hash!("UpdateUtilization"), } impl EIP1559GasMarketActor { /// Creates the actor pub fn constructor(rt: &impl Runtime, st: EIP1559GasState) -> Result<(), ActorError> { rt.validate_immediate_caller_is(std::iter::once(&SYSTEM_ACTOR_ADDR))?; - rt.create(&st)?; - - Ok(()) + rt.create(&st) } - fn set_block_gas_limit(rt: &impl Runtime, block_gas_limit: Gas) -> Result<(), ActorError> { + fn set_constants(rt: &impl Runtime, constants: SetConstants) -> Result<(), ActorError> { rt.validate_immediate_caller_is(std::iter::once(&SYSTEM_ACTOR_ADDR))?; rt.transaction(|st: &mut EIP1559GasState, _rt| { - st.block_gas_limit = block_gas_limit; + st.block_gas_limit = constants.block_gas_limit; Ok(()) })?; Ok(()) } - fn get_state(rt: &impl Runtime) -> Result { + fn current_gas_reading(rt: &impl Runtime) -> Result { rt.state() } - fn update_block_gas_consumption( + fn update_utilization( rt: &impl Runtime, - block_gas_used: Gas, + utilization: BlockGasUtilization, ) -> Result<(), ActorError> { rt.validate_immediate_caller_is(std::iter::once(&SYSTEM_ACTOR_ADDR))?; rt.transaction(|st: &mut EIP1559GasState, _rt| { - st.base_fee = update_base_fee(st.block_gas_limit, block_gas_used, st.base_fee.clone()); + st.base_fee = st.next_base_fee(utilization.block_gas_used); Ok(()) }) } } -fn update_base_fee(gas_limit: Gas, gas_used: Gas, base_fee: TokenAmount) -> TokenAmount { - let gas_target = gas_limit / EIP1559_ELASTICITY_MULTIPLIER; - - if gas_used == gas_target { - return base_fee; - } - - if gas_used > gas_target { - let gas_used_delta = gas_used - gas_target; - let base_fee_delta = base_fee - .clone() - .mul(gas_used_delta) - .div_floor(gas_target) - .div_floor(EIP1559_BASE_FEE_MAX_CHANGE_DENOMINATOR) - .max(TokenAmount::from_atto(1)); - base_fee + base_fee_delta - } else { - let gas_used_delta = gas_target - gas_used; - let base_fee_per_gas_delta = base_fee - .clone() - .mul(gas_used_delta) - .div_floor(gas_target) - .div_floor(EIP1559_BASE_FEE_MAX_CHANGE_DENOMINATOR); - if base_fee_per_gas_delta > base_fee { - TokenAmount::zero() - } else { - base_fee - base_fee_per_gas_delta +impl EIP1559GasState { + fn next_base_fee(&self, gas_used: Gas) -> TokenAmount { + let base_fee = self.base_fee.clone(); + let gas_target = self.block_gas_limit / ELASTICITY_MULTIPLIER; + + match gas_used.cmp(&gas_target) { + Ordering::Equal => base_fee, + Ordering::Less => { + let base_fee_delta = base_fee.atto() * (gas_target - gas_used) + / gas_target + / BASE_FEE_MAX_CHANGE_DENOMINATOR; + let base_fee_delta = TokenAmount::from_atto(base_fee_delta); + if base_fee_delta >= base_fee { + MINIMAL_BASE_FEE.clone() + } else { + base_fee - base_fee_delta + } + } + Ordering::Greater => { + let gas_used_delta = gas_used - gas_target; + let delta = + base_fee.atto() * gas_used_delta / gas_target / BASE_FEE_MAX_CHANGE_DENOMINATOR; + base_fee + TokenAmount::from_atto(delta).max(TokenAmount::from_atto(1)) + } } } } @@ -118,15 +127,15 @@ impl ActorCode for EIP1559GasMarketActor { actor_dispatch! { Constructor => constructor, - SetBlockGasLimit => set_block_gas_limit, - GetState => get_state, - UpdateBlockGasConsumption => update_block_gas_consumption, + SetConstants => set_constants, + CurrentGasReading => current_gas_reading, + UpdateUtilization => update_utilization, } } #[cfg(test)] mod tests { - use crate::{EIP1559GasMarketActor, EIP1559GasState, Method}; + use crate::{EIP1559GasMarketActor, EIP1559GasState, Method, SetConstants}; use fil_actors_runtime::test_utils::{expect_empty, MockRuntime, SYSTEM_ACTOR_CODE_ID}; use fil_actors_runtime::SYSTEM_ACTOR_ADDR; use fvm_ipld_encoding::ipld_block::IpldBlock; @@ -167,17 +176,14 @@ mod tests { rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); let r = rt.call::( - Method::SetBlockGasLimit as u64, - IpldBlock::serialize_cbor(&20).unwrap(), + Method::SetConstants as u64, + IpldBlock::serialize_cbor(&SetConstants { + block_gas_limit: 20, + }) + .unwrap(), ); assert!(r.is_ok()); - let r = rt - .call::( - Method::GetState as u64, - IpldBlock::serialize_cbor(&()).unwrap(), - ) - .unwrap(); let s = rt.get_state::(); assert_eq!(s.block_gas_limit, 20); } @@ -190,8 +196,11 @@ mod tests { let code = rt .call::( - Method::SetBlockGasLimit as u64, - IpldBlock::serialize_cbor(&20).unwrap(), + Method::SetConstants as u64, + IpldBlock::serialize_cbor(&SetConstants { + block_gas_limit: 20, + }) + .unwrap(), ) .unwrap_err() .exit_code(); diff --git a/fendermint/vm/actor_interface/src/gas.rs b/fendermint/vm/actor_interface/src/gas.rs index 3fae569a77..ccc6c1f18a 100644 --- a/fendermint/vm/actor_interface/src/gas.rs +++ b/fendermint/vm/actor_interface/src/gas.rs @@ -1,4 +1,4 @@ // Copyright 2022-2024 Protocol Labs // SPDX-License-Identifier: Apache-2.0, MIT -define_id!(GAS { id: 98 }); +define_id!(GAS_MARKET { id: 98 }); diff --git a/fendermint/vm/interpreter/src/fvm/exec.rs b/fendermint/vm/interpreter/src/fvm/exec.rs index 88a92de719..95a7e974dd 100644 --- a/fendermint/vm/interpreter/src/fvm/exec.rs +++ b/fendermint/vm/interpreter/src/fvm/exec.rs @@ -58,8 +58,6 @@ where // Block height (FVM epoch) as sequence is intentional let height = state.block_height(); - self.gas.load(&mut state)?; - // check for upgrades in the upgrade_scheduler let chain_id = state.chain_id(); let block_height: u64 = state.block_height().try_into().unwrap(); @@ -152,7 +150,7 @@ where async fn deliver( &self, mut state: Self::State, - mut msg: Self::Message, + msg: Self::Message, ) -> anyhow::Result<(Self::State, Self::DeliverOutput)> { let (apply_ret, emitters, latency) = if msg.from == system::SYSTEM_ACTOR_ADDR { let (execution_result, latency) = measure_time(|| state.execute_implicit(msg.clone())); @@ -160,13 +158,20 @@ where (apply_ret, emitters, latency) } else { - // TODO: maybe compare the gas limits is better? - msg.gas_limit = msg.gas_limit.min(self.gas.available_block_gas()); + if msg.gas_limit > state.gas_market().available_block_gas() { + tracing::warn!("gas limit exceed available block gas limit"); + } let (execution_result, latency) = measure_time(|| state.execute_explicit(msg.clone())); let (apply_ret, emitters) = execution_result?; - self.gas.consume_gas(apply_ret.msg_receipt.gas_used)?; + if state + .gas_market_mut() + .record_gas_used(apply_ret.msg_receipt.gas_used) + .is_err() + { + tracing::warn!("should not have exceeded block gas limit"); + } (apply_ret, emitters, latency) }; @@ -194,7 +199,7 @@ where } async fn end(&self, mut state: Self::State) -> anyhow::Result<(Self::State, Self::EndOutput)> { - self.gas.update_params(&mut state)?; + state.update_gas_market()?; // TODO: Consider doing this async, since it's purely informational and not consensus-critical. let _ = checkpoint::emit_trace_if_check_checkpoint_finalized(&self.gateway, &mut state) diff --git a/fendermint/vm/interpreter/src/fvm/gas/actor.rs b/fendermint/vm/interpreter/src/fvm/gas/actor.rs index 56730ae210..fd2d79ca6f 100644 --- a/fendermint/vm/interpreter/src/fvm/gas/actor.rs +++ b/fendermint/vm/interpreter/src/fvm/gas/actor.rs @@ -1,123 +1,111 @@ +// Copyright 2022-2024 Protocol Labs +// SPDX-License-Identifier: Apache-2.0, MIT use crate::fvm::gas::{Gas, GasMarket}; -use crate::fvm::state::FvmExecState; use crate::fvm::FvmMessage; use anyhow::Context; -use fendermint_vm_actor_interface::gas::GAS_ACTOR_ADDR; +use fendermint_vm_actor_interface::gas::GAS_MARKET_ACTOR_ADDR; use fendermint_vm_actor_interface::system; -use fvm_ipld_blockstore::Blockstore; +use fvm::executor::{ApplyKind, Executor}; use fvm_ipld_encoding::BytesDe; -use std::sync::atomic::{AtomicU64, Ordering}; +use fvm_shared::clock::ChainEpoch; -type AtomicGas = AtomicU64; type GasMarketState = fendermint_actor_gas_market::EIP1559GasState; -/// The gas market based on EIP1155 -/// Due to the reference trait bound limit (`&self` instead of `&mut self`) in Interpreter, `Atomic` -/// is used. However, the calling pattern should be single threaded, so direct `store` could be used. -/// The usage of `Atomic` is purely to bypass the compilation issue without using unsafe. -/// TODO: remove this overhead when trait bound is updated. #[derive(Default)] pub struct ActorGasMarket { /// The block gas limit - block_gas_limit: AtomicGas, + block_gas_limit: Gas, /// The accumulated gas usage so far - block_gas_used: AtomicGas, + block_gas_used: Gas, } impl GasMarket for ActorGasMarket { fn available_block_gas(&self) -> Gas { - self.block_gas_limit.load(Ordering::SeqCst) - self.block_gas_used.load(Ordering::SeqCst) + self.block_gas_limit - self.block_gas_used } - fn consume_gas(&self, gas: Gas) -> anyhow::Result<()> { - let block_gas_used = self.block_gas_used.load(Ordering::SeqCst); - - if block_gas_used + gas >= self.block_gas_limit.load(Ordering::SeqCst) { - anyhow::bail!("out of block gas") + fn record_gas_used(&mut self, gas: Gas) -> anyhow::Result<()> { + if self.block_gas_used + gas >= self.block_gas_limit { + tracing::warn!("out of block gas, should not have happened") } - self.block_gas_used - .store(block_gas_used + gas, Ordering::SeqCst); + self.block_gas_used = self.block_gas_used.saturating_add(gas); Ok(()) } +} - fn update_params( - &self, - chain_state: &mut FvmExecState, - ) -> anyhow::Result<()> { - let block_gas_used = self.block_gas_used.load(Ordering::SeqCst); - let params = fvm_ipld_encoding::RawBytes::serialize(block_gas_used)?; - +impl ActorGasMarket { + pub fn new( + executor: &mut E, + block_height: ChainEpoch, + ) -> anyhow::Result { let msg = FvmMessage { from: system::SYSTEM_ACTOR_ADDR, - to: GAS_ACTOR_ADDR, - sequence: chain_state.block_height() as u64, + to: GAS_MARKET_ACTOR_ADDR, + sequence: block_height as u64, // exclude this from gas restriction gas_limit: u64::MAX, - method_num: fendermint_actor_gas_market::Method::UpdateBlockGasConsumption as u64, - params, + method_num: fendermint_actor_gas_market::Method::CurrentGasReading as u64, + params: fvm_ipld_encoding::RawBytes::serialize(())?, value: Default::default(), version: Default::default(), gas_fee_cap: Default::default(), gas_premium: Default::default(), }; - let (apply_ret, _) = chain_state.execute_implicit(msg)?; + let raw_length = fvm_ipld_encoding::to_vec(&msg).map(|bz| bz.len())?; + let apply_ret = executor.execute_message(msg, ApplyKind::Implicit, raw_length)?; if let Some(err) = apply_ret.failure_info { - anyhow::bail!("failed to update EIP1559 gas state: {}", err) - } else { - Ok(()) + anyhow::bail!("failed to read gas market state: {}", err); } + + let output = apply_ret + .msg_receipt + .return_data + .deserialize::() + .map(|bz| bz.0) + .context("failed to deserialize error data")?; + let state = fvm_ipld_encoding::from_slice::(&output)?; + + Ok(Self { + block_gas_limit: state.block_gas_limit, + block_gas_used: 0, + }) } -} -impl ActorGasMarket { - pub fn load( + pub fn commit( &self, - chain_state: &mut FvmExecState, + executor: &mut E, + block_height: ChainEpoch, ) -> anyhow::Result<()> { + let block_gas_used = self.block_gas_used; + let params = fvm_ipld_encoding::RawBytes::serialize( + fendermint_actor_gas_market::BlockGasUtilization { block_gas_used }, + )?; + let msg = FvmMessage { from: system::SYSTEM_ACTOR_ADDR, - to: GAS_ACTOR_ADDR, - sequence: chain_state.block_height() as u64, + to: GAS_MARKET_ACTOR_ADDR, + sequence: block_height as u64, // exclude this from gas restriction gas_limit: u64::MAX, - method_num: fendermint_actor_gas_market::Method::GetState as u64, - params: fvm_ipld_encoding::RawBytes::serialize(())?, + method_num: fendermint_actor_gas_market::Method::UpdateUtilization as u64, + params, value: Default::default(), version: Default::default(), gas_fee_cap: Default::default(), gas_premium: Default::default(), }; - let (apply_ret, _) = chain_state.execute_implicit(msg)?; + let raw_length = fvm_ipld_encoding::to_vec(&msg).map(|bz| bz.len())?; + let apply_ret = executor.execute_message(msg, ApplyKind::Implicit, raw_length)?; if let Some(err) = apply_ret.failure_info { - anyhow::bail!("failed to read gas market state: {}", err); - } - - let output = apply_ret - .msg_receipt - .return_data - .deserialize::() - .map(|bz| bz.0) - .context("failed to deserialize error data")?; - let state = fvm_ipld_encoding::from_slice::(&output)?; - - self.block_gas_used.store(0, Ordering::SeqCst); - self.block_gas_limit - .store(state.block_gas_limit, Ordering::SeqCst); - Ok(()) - } -} - -impl Clone for ActorGasMarket { - fn clone(&self) -> Self { - Self { - block_gas_limit: AtomicGas::new(self.block_gas_limit.load(Ordering::SeqCst)), - block_gas_used: AtomicGas::new(self.block_gas_used.load(Ordering::SeqCst)), + anyhow::bail!("failed to update EIP1559 gas state: {}", err) + } else { + Ok(()) } } } diff --git a/fendermint/vm/interpreter/src/fvm/gas/mod.rs b/fendermint/vm/interpreter/src/fvm/gas/mod.rs index f29b6cd5c0..8c9dfb91ba 100644 --- a/fendermint/vm/interpreter/src/fvm/gas/mod.rs +++ b/fendermint/vm/interpreter/src/fvm/gas/mod.rs @@ -1,9 +1,6 @@ // Copyright 2022-2024 Protocol Labs // SPDX-License-Identifier: Apache-2.0, MIT -use crate::fvm::state::FvmExecState; -use fvm_ipld_blockstore::Blockstore; - pub mod actor; pub type Gas = u64; @@ -14,11 +11,5 @@ pub trait GasMarket { fn available_block_gas(&self) -> Gas; /// Tracks the amount of gas consumed by a transaction - fn consume_gas(&self, gas: Gas) -> anyhow::Result<()>; - - /// Update the gas market params to blockchain state. This usually happens at the end of the block - fn update_params( - &self, - chain_state: &mut FvmExecState, - ) -> anyhow::Result<()>; + fn record_gas_used(&mut self, gas: Gas) -> anyhow::Result<()>; } diff --git a/fendermint/vm/interpreter/src/fvm/mod.rs b/fendermint/vm/interpreter/src/fvm/mod.rs index fa62398c6c..3a301a43c1 100644 --- a/fendermint/vm/interpreter/src/fvm/mod.rs +++ b/fendermint/vm/interpreter/src/fvm/mod.rs @@ -19,7 +19,6 @@ pub mod bundle; pub(crate) mod gas; pub(crate) mod topdown; -use crate::fvm::gas::actor::ActorGasMarket; pub use check::FvmCheckRet; pub use checkpoint::PowerUpdates; pub use exec::FvmApplyRet; @@ -84,7 +83,6 @@ where gateway: GatewayCaller, /// Upgrade scheduler stores all the upgrades to be executed at given heights. upgrade_scheduler: UpgradeScheduler, - gas: ActorGasMarket, } impl FvmMessageInterpreter @@ -110,7 +108,6 @@ where push_chain_meta: true, gateway: GatewayCaller::default(), upgrade_scheduler, - gas: ActorGasMarket::default(), } } diff --git a/fendermint/vm/interpreter/src/fvm/state/exec.rs b/fendermint/vm/interpreter/src/fvm/state/exec.rs index 7d5f10dda4..782d3c3fe7 100644 --- a/fendermint/vm/interpreter/src/fvm/state/exec.rs +++ b/fendermint/vm/interpreter/src/fvm/state/exec.rs @@ -24,6 +24,7 @@ use serde::{Deserialize, Serialize}; use serde_with::serde_as; use crate::fvm::externs::FendermintExterns; +use crate::fvm::gas::actor::ActorGasMarket; use fendermint_vm_core::{chainid::HasChainID, Timestamp}; use fendermint_vm_encoding::IsHumanReadable; @@ -113,6 +114,9 @@ where /// Indicate whether the parameters have been updated. params_dirty: bool, + /// Keeps track of block gas usage during execution, and takes care of updating + /// the chosen gas market strategy (by default an on-chain actor delivering EIP-1559 behaviour). + gas_market: ActorGasMarket, } impl FvmExecState @@ -146,7 +150,8 @@ where let engine = multi_engine.get(&nc)?; let externs = FendermintExterns::new(blockstore.clone(), params.state_root); let machine = DefaultMachine::new(&mc, blockstore, externs)?; - let executor = DefaultExecutor::new(engine, machine)?; + let mut executor = DefaultExecutor::new(engine, machine)?; + let gas_market = ActorGasMarket::new(&mut executor, block_height)?; Ok(Self { executor, @@ -159,6 +164,7 @@ where power_scale: params.power_scale, }, params_dirty: false, + gas_market, }) } @@ -174,6 +180,14 @@ where self } + pub fn gas_market_mut(&mut self) -> &mut ActorGasMarket { + &mut self.gas_market + } + + pub fn gas_market(&self) -> &ActorGasMarket { + &self.gas_market + } + /// Execute message implicitly. pub fn execute_implicit(&mut self, msg: Message) -> ExecResult { self.execute_message(msg, ApplyKind::Implicit) @@ -286,12 +300,9 @@ where self.update_params(|p| f(&mut p.app_version)) } - /// Update the application version. - pub fn update_base_fee(&mut self, f: F) - where - F: FnOnce(&mut TokenAmount), - { - self.update_params(|p| f(&mut p.base_fee)) + pub fn update_gas_market(&mut self) -> anyhow::Result<()> { + let height = self.block_height(); + self.gas_market.commit(&mut self.executor, height) } /// Update the circulating supply, effective from the next block. From a73287220f415f9bcd00859877d3e94ee0a3a96b Mon Sep 17 00:00:00 2001 From: cryptoAtwill <108330426+cryptoAtwill@users.noreply.github.com> Date: Tue, 20 Aug 2024 20:43:20 +0800 Subject: [PATCH 45/69] Update fendermint/vm/interpreter/src/fvm/exec.rs Co-authored-by: raulk --- fendermint/vm/interpreter/src/fvm/exec.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fendermint/vm/interpreter/src/fvm/exec.rs b/fendermint/vm/interpreter/src/fvm/exec.rs index 95a7e974dd..2d46cb37f8 100644 --- a/fendermint/vm/interpreter/src/fvm/exec.rs +++ b/fendermint/vm/interpreter/src/fvm/exec.rs @@ -170,7 +170,7 @@ where .record_gas_used(apply_ret.msg_receipt.gas_used) .is_err() { - tracing::warn!("should not have exceeded block gas limit"); + tracing::warn!("[ASSERTION FAILED] gas market failed while recording utilization: {err}"); } (apply_ret, emitters, latency) From 7e6e2ca6b32c5b145359aa2c1383c2db1dbb4615 Mon Sep 17 00:00:00 2001 From: cryptoAtwill <108330426+cryptoAtwill@users.noreply.github.com> Date: Tue, 20 Aug 2024 20:43:32 +0800 Subject: [PATCH 46/69] Update fendermint/vm/interpreter/src/fvm/exec.rs Co-authored-by: raulk --- fendermint/vm/interpreter/src/fvm/exec.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fendermint/vm/interpreter/src/fvm/exec.rs b/fendermint/vm/interpreter/src/fvm/exec.rs index 2d46cb37f8..7f1b1cc4ce 100644 --- a/fendermint/vm/interpreter/src/fvm/exec.rs +++ b/fendermint/vm/interpreter/src/fvm/exec.rs @@ -159,7 +159,8 @@ where (apply_ret, emitters, latency) } else { if msg.gas_limit > state.gas_market().available_block_gas() { - tracing::warn!("gas limit exceed available block gas limit"); + // This is panic-worthy, but we suppress it to avoid liveness issues. + tracing::warn!("[ASSERTION FAILED] message gas limit exceed available block gas limit; consensus engine is misbehaving"); } let (execution_result, latency) = measure_time(|| state.execute_explicit(msg.clone())); From cf0228a2bb8acb1951363d1db850a79b8c4bacd9 Mon Sep 17 00:00:00 2001 From: cryptoAtwill <108330426+cryptoAtwill@users.noreply.github.com> Date: Tue, 20 Aug 2024 20:53:36 +0800 Subject: [PATCH 47/69] Update fendermint/actors/gas_market/src/lib.rs Co-authored-by: raulk --- fendermint/actors/gas_market/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fendermint/actors/gas_market/src/lib.rs b/fendermint/actors/gas_market/src/lib.rs index 967c0ea7ce..793f820d93 100644 --- a/fendermint/actors/gas_market/src/lib.rs +++ b/fendermint/actors/gas_market/src/lib.rs @@ -136,7 +136,7 @@ impl ActorCode for EIP1559GasMarketActor { actor_dispatch! { Constructor => constructor, SetConstants => set_constants, - CurrentGasReading => current_gas_reading, + CurrentReading => current_reading, UpdateUtilization => update_utilization, } } From b00bf392d60448f1a8b894fbe28d473f2dd92c14 Mon Sep 17 00:00:00 2001 From: cryptoAtwill <108330426+cryptoAtwill@users.noreply.github.com> Date: Tue, 20 Aug 2024 20:53:43 +0800 Subject: [PATCH 48/69] Update fendermint/vm/interpreter/src/fvm/gas/actor.rs Co-authored-by: raulk --- fendermint/vm/interpreter/src/fvm/gas/actor.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fendermint/vm/interpreter/src/fvm/gas/actor.rs b/fendermint/vm/interpreter/src/fvm/gas/actor.rs index 4b9b0d0f72..48d753bcff 100644 --- a/fendermint/vm/interpreter/src/fvm/gas/actor.rs +++ b/fendermint/vm/interpreter/src/fvm/gas/actor.rs @@ -23,7 +23,7 @@ impl GasMarket for ActorGasMarket { self.block_gas_limit - self.block_gas_used } - fn record_gas_used(&mut self, gas: Gas) -> anyhow::Result<()> { + fn record_utilization(&mut self, gas: Gas) -> anyhow::Result<()> { if self.block_gas_used + gas >= self.block_gas_limit { tracing::warn!("out of block gas, should not have happened") } From 88090ac8802e0811650b775b38002ebe3a6e6f55 Mon Sep 17 00:00:00 2001 From: cryptoAtwill <108330426+cryptoAtwill@users.noreply.github.com> Date: Tue, 20 Aug 2024 20:53:57 +0800 Subject: [PATCH 49/69] Update fendermint/vm/interpreter/src/fvm/gas/actor.rs Co-authored-by: raulk --- fendermint/vm/interpreter/src/fvm/gas/actor.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fendermint/vm/interpreter/src/fvm/gas/actor.rs b/fendermint/vm/interpreter/src/fvm/gas/actor.rs index 48d753bcff..41e424e3ac 100644 --- a/fendermint/vm/interpreter/src/fvm/gas/actor.rs +++ b/fendermint/vm/interpreter/src/fvm/gas/actor.rs @@ -19,7 +19,11 @@ pub struct ActorGasMarket { } impl GasMarket for ActorGasMarket { - fn available_block_gas(&self) -> Gas { + struct Available { + block_gas: Gas + } + + fn available(&self) -> Available { self.block_gas_limit - self.block_gas_used } From ca9b0af8050de981f9429ff0cab3711c7cd63a4f Mon Sep 17 00:00:00 2001 From: cryptoAtwill <108330426+cryptoAtwill@users.noreply.github.com> Date: Tue, 20 Aug 2024 20:54:07 +0800 Subject: [PATCH 50/69] Update fendermint/vm/interpreter/src/fvm/gas/actor.rs Co-authored-by: raulk --- fendermint/vm/interpreter/src/fvm/gas/actor.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fendermint/vm/interpreter/src/fvm/gas/actor.rs b/fendermint/vm/interpreter/src/fvm/gas/actor.rs index 41e424e3ac..d7267b988a 100644 --- a/fendermint/vm/interpreter/src/fvm/gas/actor.rs +++ b/fendermint/vm/interpreter/src/fvm/gas/actor.rs @@ -38,7 +38,7 @@ impl GasMarket for ActorGasMarket { } impl ActorGasMarket { - pub fn new( + pub fn create( executor: &mut E, block_height: ChainEpoch, ) -> anyhow::Result { From 7ffaa0c02505a5df50712c9936b60000f77f6ca5 Mon Sep 17 00:00:00 2001 From: cryptoAtwill Date: Tue, 20 Aug 2024 22:25:21 +0800 Subject: [PATCH 51/69] feedbacks --- fendermint/actors/gas_market/src/lib.rs | 39 ++++++++++++------- fendermint/vm/interpreter/src/fvm/exec.rs | 20 +++++----- .../vm/interpreter/src/fvm/gas/actor.rs | 22 +++++------ fendermint/vm/interpreter/src/fvm/gas/mod.rs | 8 +++- .../vm/interpreter/src/fvm/state/exec.rs | 2 +- fendermint/vm/interpreter/src/genesis.rs | 14 ++++++- 6 files changed, 67 insertions(+), 38 deletions(-) diff --git a/fendermint/actors/gas_market/src/lib.rs b/fendermint/actors/gas_market/src/lib.rs index 793f820d93..d127ec7f7f 100644 --- a/fendermint/actors/gas_market/src/lib.rs +++ b/fendermint/actors/gas_market/src/lib.rs @@ -8,7 +8,6 @@ use fil_actors_runtime::{actor_dispatch, ActorError}; use fvm_ipld_encoding::tuple::*; use fvm_shared::econ::TokenAmount; use fvm_shared::METHOD_CONSTRUCTOR; -use lazy_static::lazy_static; use num_derive::FromPrimitive; use std::cmp::Ordering; @@ -19,11 +18,7 @@ fil_actors_runtime::wasm_trampoline!(EIP1559GasMarketActor); const BASE_FEE_MAX_CHANGE_DENOMINATOR: u64 = 8; /// Elasticity multiplier as defined in [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) const ELASTICITY_MULTIPLIER: u64 = 2; -lazy_static! { - /// Initial base fee as defined in [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) - static ref INITIAL_BASE_FEE: TokenAmount = TokenAmount::from_atto(1_000_000_000); - static ref MINIMAL_BASE_FEE: TokenAmount = TokenAmount::from_atto(100); -} + pub const IPC_GAS_MARKET_ACTOR_NAME: &str = "gas_market"; pub type Gas = u64; pub type GasMarketReading = EIP1559GasState; @@ -32,6 +27,7 @@ pub type GasMarketReading = EIP1559GasState; pub struct EIP1559GasState { pub block_gas_limit: Gas, pub base_fee: TokenAmount, + pub minimal_base_fee: TokenAmount, } #[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone)] @@ -41,7 +37,9 @@ pub struct BlockGasUtilization { #[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone)] pub struct SetConstants { - pub block_gas_limit: Gas, + pub block_gas_limit: Option, + pub base_fee: Option, + pub minimal_base_fee: Option, } pub struct EIP1559GasMarketActor {} @@ -50,7 +48,7 @@ pub struct EIP1559GasMarketActor {} #[repr(u64)] pub enum Method { Constructor = METHOD_CONSTRUCTOR, - CurrentGasReading = frc42_dispatch::method_hash!("CurrentGasReading"), + CurrentReading = frc42_dispatch::method_hash!("CurrentReading"), SetConstants = frc42_dispatch::method_hash!("SetConstants"), UpdateUtilization = frc42_dispatch::method_hash!("UpdateUtilization"), } @@ -66,14 +64,14 @@ impl EIP1559GasMarketActor { rt.validate_immediate_caller_is(std::iter::once(&SYSTEM_ACTOR_ADDR))?; rt.transaction(|st: &mut EIP1559GasState, _rt| { - st.block_gas_limit = constants.block_gas_limit; + st.set_constants(constants); Ok(()) })?; Ok(()) } - fn current_gas_reading(rt: &impl Runtime) -> Result { + fn current_reading(rt: &impl Runtime) -> Result { rt.validate_immediate_caller_accept_any()?; rt.state() } @@ -92,13 +90,27 @@ impl EIP1559GasMarketActor { } impl EIP1559GasState { - pub fn new(block_gas_limit: Gas) -> Self { + pub fn new(block_gas_limit: Gas, base_fee: TokenAmount, minimal_base_fee: TokenAmount) -> Self { Self { block_gas_limit, - base_fee: INITIAL_BASE_FEE.clone(), + base_fee, + minimal_base_fee, + } + } + + #[inline] + fn update_if_some(maybe_some: Option, to_change: &mut T) { + if let Some(v) = maybe_some { + *to_change = v; } } + fn set_constants(&mut self, constants: SetConstants) { + Self::update_if_some(constants.minimal_base_fee, &mut self.minimal_base_fee); + Self::update_if_some(constants.base_fee, &mut self.base_fee); + Self::update_if_some(constants.block_gas_limit, &mut self.block_gas_limit); + } + fn next_base_fee(&self, gas_used: Gas) -> TokenAmount { let base_fee = self.base_fee.clone(); let gas_target = self.block_gas_limit / ELASTICITY_MULTIPLIER; @@ -111,7 +123,7 @@ impl EIP1559GasState { / BASE_FEE_MAX_CHANGE_DENOMINATOR; let base_fee_delta = TokenAmount::from_atto(base_fee_delta); if base_fee_delta >= base_fee { - MINIMAL_BASE_FEE.clone() + self.minimal_base_fee.clone() } else { base_fee - base_fee_delta } @@ -165,6 +177,7 @@ mod tests { IpldBlock::serialize_cbor(&EIP1559GasState { block_gas_limit: 100, base_fee: Default::default(), + minimal_base_fee: Default::default(), }) .unwrap(), ) diff --git a/fendermint/vm/interpreter/src/fvm/exec.rs b/fendermint/vm/interpreter/src/fvm/exec.rs index 7f1b1cc4ce..1f8912edff 100644 --- a/fendermint/vm/interpreter/src/fvm/exec.rs +++ b/fendermint/vm/interpreter/src/fvm/exec.rs @@ -158,21 +158,23 @@ where (apply_ret, emitters, latency) } else { - if msg.gas_limit > state.gas_market().available_block_gas() { - // This is panic-worthy, but we suppress it to avoid liveness issues. - tracing::warn!("[ASSERTION FAILED] message gas limit exceed available block gas limit; consensus engine is misbehaving"); + let available_gas = state.gas_market().available().block_gas; + if msg.gas_limit > available_gas { + // This is panic-worthy, but we suppress it to avoid liveness issues. + // Consider maybe record as evidence for the validator slashing? + tracing::warn!( + txn_gas_limit = msg.gas_limit, + block_gas_available = available_gas, + "[ASSERTION FAILED] message gas limit exceed available block gas limit; consensus engine is misbehaving" + ); } let (execution_result, latency) = measure_time(|| state.execute_explicit(msg.clone())); let (apply_ret, emitters) = execution_result?; - if state + state .gas_market_mut() - .record_gas_used(apply_ret.msg_receipt.gas_used) - .is_err() - { - tracing::warn!("[ASSERTION FAILED] gas market failed while recording utilization: {err}"); - } + .record_utilization(apply_ret.msg_receipt.gas_used); (apply_ret, emitters, latency) }; diff --git a/fendermint/vm/interpreter/src/fvm/gas/actor.rs b/fendermint/vm/interpreter/src/fvm/gas/actor.rs index d7267b988a..e7c188d5e4 100644 --- a/fendermint/vm/interpreter/src/fvm/gas/actor.rs +++ b/fendermint/vm/interpreter/src/fvm/gas/actor.rs @@ -1,6 +1,7 @@ // Copyright 2022-2024 Protocol Labs // SPDX-License-Identifier: Apache-2.0, MIT -use crate::fvm::gas::{Gas, GasMarket}; + +use crate::fvm::gas::{Available, Gas, GasMarket}; use crate::fvm::FvmMessage; use anyhow::Context; @@ -19,21 +20,18 @@ pub struct ActorGasMarket { } impl GasMarket for ActorGasMarket { - struct Available { - block_gas: Gas - } - fn available(&self) -> Available { - self.block_gas_limit - self.block_gas_used + Available { + block_gas: self.block_gas_limit - self.block_gas_used, + } } - fn record_utilization(&mut self, gas: Gas) -> anyhow::Result<()> { + fn record_utilization(&mut self, gas: Gas) { + // sanity check if self.block_gas_used + gas >= self.block_gas_limit { - tracing::warn!("out of block gas, should not have happened") + tracing::warn!("out of block gas, should not have happened, vm execution more than available gas limit"); } - self.block_gas_used = self.block_gas_used.saturating_add(gas); - - Ok(()) + self.block_gas_used += gas; } } @@ -48,7 +46,7 @@ impl ActorGasMarket { sequence: block_height as u64, // exclude this from gas restriction gas_limit: fvm_shared::BLOCK_GAS_LIMIT, - method_num: fendermint_actor_gas_market::Method::CurrentGasReading as u64, + method_num: fendermint_actor_gas_market::Method::CurrentReading as u64, params: fvm_ipld_encoding::RawBytes::default(), value: Default::default(), version: Default::default(), diff --git a/fendermint/vm/interpreter/src/fvm/gas/mod.rs b/fendermint/vm/interpreter/src/fvm/gas/mod.rs index 8c9dfb91ba..768c47bb6d 100644 --- a/fendermint/vm/interpreter/src/fvm/gas/mod.rs +++ b/fendermint/vm/interpreter/src/fvm/gas/mod.rs @@ -5,11 +5,15 @@ pub mod actor; pub type Gas = u64; +pub struct Available { + pub block_gas: Gas, +} + /// The gas market for fendermint. This should be backed by an fvm actor. pub trait GasMarket { /// Obtain the current block gas available for execution - fn available_block_gas(&self) -> Gas; + fn available(&self) -> Available; /// Tracks the amount of gas consumed by a transaction - fn record_gas_used(&mut self, gas: Gas) -> anyhow::Result<()>; + fn record_utilization(&mut self, gas: Gas); } diff --git a/fendermint/vm/interpreter/src/fvm/state/exec.rs b/fendermint/vm/interpreter/src/fvm/state/exec.rs index 782d3c3fe7..577317dc12 100644 --- a/fendermint/vm/interpreter/src/fvm/state/exec.rs +++ b/fendermint/vm/interpreter/src/fvm/state/exec.rs @@ -151,7 +151,7 @@ where let externs = FendermintExterns::new(blockstore.clone(), params.state_root); let machine = DefaultMachine::new(&mc, blockstore, externs)?; let mut executor = DefaultExecutor::new(engine, machine)?; - let gas_market = ActorGasMarket::new(&mut executor, block_height)?; + let gas_market = ActorGasMarket::create(&mut executor, block_height)?; Ok(Self { executor, diff --git a/fendermint/vm/interpreter/src/genesis.rs b/fendermint/vm/interpreter/src/genesis.rs index 9ac78235ff..7562bc26e7 100644 --- a/fendermint/vm/interpreter/src/genesis.rs +++ b/fendermint/vm/interpreter/src/genesis.rs @@ -418,7 +418,19 @@ impl GenesisBuilder { ) .context("failed to replace built in eam actor")?; - let gas_market_state = fendermint_actor_gas_market::EIP1559GasState::new(BLOCK_GAS_LIMIT); + // currently hard code them for now, once genesis V2 is implemented, should be taken + // from genesis. + // initial base fee as defined in [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) + // minimal base fee taken from filecoin. + let (initial_base_fee, minimal_base_fee) = ( + TokenAmount::from_atto(1_000_000_000), + TokenAmount::from_atto(100), + ); + let gas_market_state = fendermint_actor_gas_market::EIP1559GasState::new( + BLOCK_GAS_LIMIT, + initial_base_fee, + minimal_base_fee, + ); state .create_custom_actor( fendermint_actor_gas_market::IPC_GAS_MARKET_ACTOR_NAME, From 1a6fb2102b7f2902620dbdcc4ad2f83df54deb7d Mon Sep 17 00:00:00 2001 From: cryptoAtwill Date: Tue, 20 Aug 2024 22:59:57 +0800 Subject: [PATCH 52/69] more customization params --- fendermint/actors/gas_market/src/lib.rs | 113 ++++++++++++++---- .../vm/interpreter/src/fvm/gas/actor.rs | 11 +- fendermint/vm/interpreter/src/genesis.rs | 15 +-- 3 files changed, 101 insertions(+), 38 deletions(-) diff --git a/fendermint/actors/gas_market/src/lib.rs b/fendermint/actors/gas_market/src/lib.rs index d127ec7f7f..260f4d5419 100644 --- a/fendermint/actors/gas_market/src/lib.rs +++ b/fendermint/actors/gas_market/src/lib.rs @@ -14,20 +14,38 @@ use std::cmp::Ordering; #[cfg(feature = "fil-actor")] fil_actors_runtime::wasm_trampoline!(EIP1559GasMarketActor); -/// Base fee max change denominator as defined in [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) -const BASE_FEE_MAX_CHANGE_DENOMINATOR: u64 = 8; -/// Elasticity multiplier as defined in [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) -const ELASTICITY_MULTIPLIER: u64 = 2; - pub const IPC_GAS_MARKET_ACTOR_NAME: &str = "gas_market"; pub type Gas = u64; -pub type GasMarketReading = EIP1559GasState; + +/// Constant params used by EIP1559 +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone)] +pub struct EIP1559Constants { + /// The minimal base fee when gas utilization is low + minimal_base_fee: TokenAmount, + /// Elasticity multiplier as defined in [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) + elasticity_multiplier: u64, + /// Base fee max change denominator as defined in [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) + base_fee_max_change_denominator: u64, +} #[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone)] pub struct EIP1559GasState { + block_gas_limit: Gas, + base_fee: TokenAmount, + constants: EIP1559Constants, +} + +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone)] +pub struct GasActorConstructorParams { + block_gas_limit: Gas, + base_fee: TokenAmount, + constants: Option, +} + +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone)] +pub struct GasMarketReading { pub block_gas_limit: Gas, pub base_fee: TokenAmount, - pub minimal_base_fee: TokenAmount, } #[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone)] @@ -39,7 +57,7 @@ pub struct BlockGasUtilization { pub struct SetConstants { pub block_gas_limit: Option, pub base_fee: Option, - pub minimal_base_fee: Option, + pub constants: Option, } pub struct EIP1559GasMarketActor {} @@ -55,8 +73,13 @@ pub enum Method { impl EIP1559GasMarketActor { /// Creates the actor - pub fn constructor(rt: &impl Runtime, st: EIP1559GasState) -> Result<(), ActorError> { + pub fn constructor( + rt: &impl Runtime, + params: GasActorConstructorParams, + ) -> Result<(), ActorError> { rt.validate_immediate_caller_is(std::iter::once(&SYSTEM_ACTOR_ADDR))?; + + let st = EIP1559GasState::from(params); rt.create(&st) } @@ -73,7 +96,12 @@ impl EIP1559GasMarketActor { fn current_reading(rt: &impl Runtime) -> Result { rt.validate_immediate_caller_accept_any()?; - rt.state() + + let st = rt.state::()?; + Ok(GasMarketReading { + block_gas_limit: st.block_gas_limit, + base_fee: st.base_fee, + }) } fn update_utilization( @@ -89,15 +117,45 @@ impl EIP1559GasMarketActor { } } -impl EIP1559GasState { - pub fn new(block_gas_limit: Gas, base_fee: TokenAmount, minimal_base_fee: TokenAmount) -> Self { +impl Default for EIP1559Constants { + fn default() -> Self { + Self { + // Take from filecoin setting + minimal_base_fee: TokenAmount::from_atto(100), + // Elasticity multiplier as defined in [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) + elasticity_multiplier: 2, + // Base fee max change denominator as defined in [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) + base_fee_max_change_denominator: 8, + } + } +} + +impl From for EIP1559GasState { + fn from(params: GasActorConstructorParams) -> Self { + Self { + block_gas_limit: params.block_gas_limit, + base_fee: params.base_fee, + constants: params.constants.unwrap_or_default(), + } + } +} + +impl GasActorConstructorParams { + pub fn new(block_gas_limit: Gas, base_fee: TokenAmount) -> Self { Self { block_gas_limit, base_fee, - minimal_base_fee, + constants: None, } } + pub fn with_constants(mut self, constants: EIP1559Constants) -> Self { + self.constants = Some(constants); + self + } +} + +impl EIP1559GasState { #[inline] fn update_if_some(maybe_some: Option, to_change: &mut T) { if let Some(v) = maybe_some { @@ -106,32 +164,33 @@ impl EIP1559GasState { } fn set_constants(&mut self, constants: SetConstants) { - Self::update_if_some(constants.minimal_base_fee, &mut self.minimal_base_fee); + Self::update_if_some(constants.constants, &mut self.constants); Self::update_if_some(constants.base_fee, &mut self.base_fee); Self::update_if_some(constants.block_gas_limit, &mut self.block_gas_limit); } fn next_base_fee(&self, gas_used: Gas) -> TokenAmount { let base_fee = self.base_fee.clone(); - let gas_target = self.block_gas_limit / ELASTICITY_MULTIPLIER; + let gas_target = self.block_gas_limit / self.constants.elasticity_multiplier; match gas_used.cmp(&gas_target) { Ordering::Equal => base_fee, Ordering::Less => { let base_fee_delta = base_fee.atto() * (gas_target - gas_used) / gas_target - / BASE_FEE_MAX_CHANGE_DENOMINATOR; + / self.constants.base_fee_max_change_denominator; let base_fee_delta = TokenAmount::from_atto(base_fee_delta); if base_fee_delta >= base_fee { - self.minimal_base_fee.clone() + self.constants.minimal_base_fee.clone() } else { base_fee - base_fee_delta } } Ordering::Greater => { let gas_used_delta = gas_used - gas_target; - let delta = - base_fee.atto() * gas_used_delta / gas_target / BASE_FEE_MAX_CHANGE_DENOMINATOR; + let delta = base_fee.atto() * gas_used_delta + / gas_target + / self.constants.base_fee_max_change_denominator; base_fee + TokenAmount::from_atto(delta).max(TokenAmount::from_atto(1)) } } @@ -155,7 +214,9 @@ impl ActorCode for EIP1559GasMarketActor { #[cfg(test)] mod tests { - use crate::{EIP1559GasMarketActor, EIP1559GasState, Method, SetConstants}; + use crate::{ + EIP1559GasMarketActor, EIP1559GasState, GasActorConstructorParams, Method, SetConstants, + }; use fil_actors_runtime::test_utils::{expect_empty, MockRuntime, SYSTEM_ACTOR_CODE_ID}; use fil_actors_runtime::SYSTEM_ACTOR_ADDR; use fvm_ipld_encoding::ipld_block::IpldBlock; @@ -174,10 +235,10 @@ mod tests { let result = rt .call::( Method::Constructor as u64, - IpldBlock::serialize_cbor(&EIP1559GasState { + IpldBlock::serialize_cbor(&GasActorConstructorParams { block_gas_limit: 100, base_fee: Default::default(), - minimal_base_fee: Default::default(), + constants: None, }) .unwrap(), ) @@ -199,7 +260,9 @@ mod tests { let r = rt.call::( Method::SetConstants as u64, IpldBlock::serialize_cbor(&SetConstants { - block_gas_limit: 20, + block_gas_limit: Some(20), + base_fee: None, + constants: None, }) .unwrap(), ); @@ -219,7 +282,9 @@ mod tests { .call::( Method::SetConstants as u64, IpldBlock::serialize_cbor(&SetConstants { - block_gas_limit: 20, + block_gas_limit: Some(20), + base_fee: None, + constants: None, }) .unwrap(), ) diff --git a/fendermint/vm/interpreter/src/fvm/gas/actor.rs b/fendermint/vm/interpreter/src/fvm/gas/actor.rs index e7c188d5e4..ed69dcc9d1 100644 --- a/fendermint/vm/interpreter/src/fvm/gas/actor.rs +++ b/fendermint/vm/interpreter/src/fvm/gas/actor.rs @@ -22,16 +22,17 @@ pub struct ActorGasMarket { impl GasMarket for ActorGasMarket { fn available(&self) -> Available { Available { - block_gas: self.block_gas_limit - self.block_gas_used, + block_gas: self.block_gas_limit - self.block_gas_used.min(self.block_gas_limit), } } fn record_utilization(&mut self, gas: Gas) { + self.block_gas_used += gas; + // sanity check - if self.block_gas_used + gas >= self.block_gas_limit { - tracing::warn!("out of block gas, should not have happened, vm execution more than available gas limit"); + if self.block_gas_used >= self.block_gas_limit { + tracing::warn!("out of block gas, vm execution more than available gas limit"); } - self.block_gas_used += gas; } } @@ -76,7 +77,7 @@ impl ActorGasMarket { executor: &mut E, block_height: ChainEpoch, ) -> anyhow::Result<()> { - let block_gas_used = self.block_gas_used; + let block_gas_used = self.block_gas_used.min(self.block_gas_limit); let params = fvm_ipld_encoding::RawBytes::serialize( fendermint_actor_gas_market::BlockGasUtilization { block_gas_used }, )?; diff --git a/fendermint/vm/interpreter/src/genesis.rs b/fendermint/vm/interpreter/src/genesis.rs index 7562bc26e7..9b0cfb949a 100644 --- a/fendermint/vm/interpreter/src/genesis.rs +++ b/fendermint/vm/interpreter/src/genesis.rs @@ -421,15 +421,12 @@ impl GenesisBuilder { // currently hard code them for now, once genesis V2 is implemented, should be taken // from genesis. // initial base fee as defined in [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) - // minimal base fee taken from filecoin. - let (initial_base_fee, minimal_base_fee) = ( - TokenAmount::from_atto(1_000_000_000), - TokenAmount::from_atto(100), - ); - let gas_market_state = fendermint_actor_gas_market::EIP1559GasState::new( - BLOCK_GAS_LIMIT, - initial_base_fee, - minimal_base_fee, + let initial_base_fee = TokenAmount::from_atto(1_000_000_000); + let gas_market_state = fendermint_actor_gas_market::EIP1559GasState::from( + fendermint_actor_gas_market::GasActorConstructorParams::new( + BLOCK_GAS_LIMIT, + initial_base_fee, + ), ); state .create_custom_actor( From cd90e98c5f5d73ea9d1541f90fc17b97e16b8471 Mon Sep 17 00:00:00 2001 From: cryptoAtwill Date: Wed, 21 Aug 2024 10:30:00 +0800 Subject: [PATCH 53/69] up gas limit --- fendermint/vm/interpreter/src/fvm/gas/actor.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fendermint/vm/interpreter/src/fvm/gas/actor.rs b/fendermint/vm/interpreter/src/fvm/gas/actor.rs index ed69dcc9d1..a3e28033b9 100644 --- a/fendermint/vm/interpreter/src/fvm/gas/actor.rs +++ b/fendermint/vm/interpreter/src/fvm/gas/actor.rs @@ -46,7 +46,7 @@ impl ActorGasMarket { to: GAS_MARKET_ACTOR_ADDR, sequence: block_height as u64, // exclude this from gas restriction - gas_limit: fvm_shared::BLOCK_GAS_LIMIT, + gas_limit: i64::MAX as u64, method_num: fendermint_actor_gas_market::Method::CurrentReading as u64, params: fvm_ipld_encoding::RawBytes::default(), value: Default::default(), @@ -87,7 +87,7 @@ impl ActorGasMarket { to: GAS_MARKET_ACTOR_ADDR, sequence: block_height as u64, // exclude this from gas restriction - gas_limit: fvm_shared::BLOCK_GAS_LIMIT, + gas_limit: i64::MAX as u64, method_num: fendermint_actor_gas_market::Method::UpdateUtilization as u64, params, value: Default::default(), From 47556e93be8fed7cfaea6e6a0b24874751fc0e74 Mon Sep 17 00:00:00 2001 From: cryptoAtwill <108330426+cryptoAtwill@users.noreply.github.com> Date: Thu, 22 Aug 2024 02:28:51 +0800 Subject: [PATCH 54/69] fix(node): remove genesis interpreter (#1118) Co-authored-by: cryptoAtwill --- fendermint/app/src/app.rs | 11 +- fendermint/app/src/cmd/run.rs | 1 - fendermint/testing/contract-test/Cargo.toml | 2 +- fendermint/testing/contract-test/src/lib.rs | 149 ++-- .../contract-test/tests/run_upgrades.rs | 17 +- .../contract-test/tests/staking/machine.rs | 12 +- fendermint/vm/interpreter/Cargo.toml | 1 + fendermint/vm/interpreter/src/bytes.rs | 44 +- fendermint/vm/interpreter/src/chain.rs | 21 +- fendermint/vm/interpreter/src/fvm/genesis.rs | 682 ------------------ fendermint/vm/interpreter/src/fvm/mod.rs | 7 - .../vm/interpreter/src/fvm/state/genesis.rs | 11 - fendermint/vm/interpreter/src/genesis.rs | 17 + fendermint/vm/interpreter/src/lib.rs | 17 - fendermint/vm/interpreter/src/signed.rs | 20 +- fendermint/vm/snapshot/Cargo.toml | 2 +- fendermint/vm/snapshot/src/manager.rs | 60 +- 17 files changed, 98 insertions(+), 976 deletions(-) delete mode 100644 fendermint/vm/interpreter/src/fvm/genesis.rs diff --git a/fendermint/app/src/app.rs b/fendermint/app/src/app.rs index af4e8da72b..c44b9addd6 100644 --- a/fendermint/app/src/app.rs +++ b/fendermint/app/src/app.rs @@ -18,15 +18,15 @@ use fendermint_vm_interpreter::bytes::{ }; use fendermint_vm_interpreter::chain::{ChainEnv, ChainMessageApplyRet, IllegalMessage}; use fendermint_vm_interpreter::fvm::state::{ - empty_state_tree, CheckStateRef, FvmExecState, FvmGenesisState, FvmQueryState, FvmStateParams, + empty_state_tree, CheckStateRef, FvmExecState, FvmQueryState, FvmStateParams, FvmUpdatableParams, }; use fendermint_vm_interpreter::fvm::store::ReadOnlyBlockstore; -use fendermint_vm_interpreter::fvm::{FvmApplyRet, FvmGenesisOutput, PowerUpdates}; +use fendermint_vm_interpreter::fvm::{FvmApplyRet, PowerUpdates}; use fendermint_vm_interpreter::genesis::{read_genesis_car, GenesisAppState}; use fendermint_vm_interpreter::signed::InvalidSignature; use fendermint_vm_interpreter::{ - CheckInterpreter, ExecInterpreter, GenesisInterpreter, ProposalInterpreter, QueryInterpreter, + CheckInterpreter, ExecInterpreter, ProposalInterpreter, QueryInterpreter, }; use fendermint_vm_message::query::FvmQueryHeight; use fendermint_vm_snapshot::{SnapshotClient, SnapshotError}; @@ -402,11 +402,6 @@ where S::Namespace: Sync + Send, DB: KVWritable + KVReadable + Clone + Send + Sync + 'static, SS: Blockstore + Clone + Send + Sync + 'static, - I: GenesisInterpreter< - State = FvmGenesisState, - Genesis = Vec, - Output = FvmGenesisOutput, - >, I: ProposalInterpreter>, I: ExecInterpreter< State = (ChainEnv, FvmExecState), diff --git a/fendermint/app/src/cmd/run.rs b/fendermint/app/src/cmd/run.rs index 50556135c2..dca099b1f0 100644 --- a/fendermint/app/src/cmd/run.rs +++ b/fendermint/app/src/cmd/run.rs @@ -137,7 +137,6 @@ async fn run(settings: Settings) -> anyhow::Result<()> { let interpreter = FvmMessageInterpreter::::new( tendermint_client.clone(), validator_ctx, - settings.contracts_dir(), settings.fvm.gas_overestimation_rate, settings.fvm.gas_search_step, settings.fvm.exec_in_check, diff --git a/fendermint/testing/contract-test/Cargo.toml b/fendermint/testing/contract-test/Cargo.toml index 0c186f8b69..109e5fae18 100644 --- a/fendermint/testing/contract-test/Cargo.toml +++ b/fendermint/testing/contract-test/Cargo.toml @@ -31,7 +31,7 @@ fendermint_vm_core = { path = "../../vm/core" } fendermint_vm_genesis = { path = "../../vm/genesis" } fendermint_vm_message = { path = "../../vm/message" } fendermint_vm_interpreter = { path = "../../vm/interpreter", features = [ - "bundle", + "bundle", "test-util" ] } [dev-dependencies] diff --git a/fendermint/testing/contract-test/src/lib.rs b/fendermint/testing/contract-test/src/lib.rs index a6290d6659..65a4ce8ad3 100644 --- a/fendermint/testing/contract-test/src/lib.rs +++ b/fendermint/testing/contract-test/src/lib.rs @@ -3,72 +3,52 @@ use anyhow::{anyhow, Context, Result}; use byteorder::{BigEndian, WriteBytesExt}; -use cid::Cid; use fendermint_vm_core::Timestamp; use fendermint_vm_interpreter::fvm::PowerUpdates; -use fvm_shared::{bigint::Zero, clock::ChainEpoch, econ::TokenAmount, version::NetworkVersion}; +use fvm_shared::clock::ChainEpoch; use std::{future::Future, sync::Arc}; use fendermint_vm_genesis::Genesis; +use fendermint_vm_interpreter::genesis::{create_test_genesis_state, GenesisOutput}; use fendermint_vm_interpreter::{ fvm::{ bundle::{bundle_path, contracts_path, custom_actors_bundle_path}, - state::{FvmExecState, FvmGenesisState, FvmStateParams, FvmUpdatableParams}, + state::{FvmExecState, FvmStateParams, FvmUpdatableParams}, store::memory::MemoryBlockstore, - upgrades::UpgradeScheduler, - FvmApplyRet, FvmGenesisOutput, FvmMessage, FvmMessageInterpreter, + FvmApplyRet, FvmMessage, }, - ExecInterpreter, GenesisInterpreter, + ExecInterpreter, }; use fvm::engine::MultiEngine; pub mod ipc; -pub async fn init_exec_state( - multi_engine: Arc, +pub async fn create_test_exec_state( genesis: Genesis, -) -> anyhow::Result<(FvmExecState, FvmGenesisOutput)> { +) -> Result<( + FvmExecState, + GenesisOutput, + MemoryBlockstore, +)> { let bundle_path = bundle_path(); - let bundle = std::fs::read(&bundle_path) - .with_context(|| format!("failed to read bundle: {}", bundle_path.to_string_lossy()))?; - let custom_actors_bundle_path = custom_actors_bundle_path(); - let custom_actors_bundle = std::fs::read(&custom_actors_bundle_path).with_context(|| { - format!( - "failed to read custom actors_bundle: {}", - custom_actors_bundle_path.to_string_lossy() - ) - })?; - - let store = MemoryBlockstore::new(); - - let state = FvmGenesisState::new(store, multi_engine, &bundle, &custom_actors_bundle) - .await - .context("failed to create state")?; - - let (client, _) = - tendermint_rpc::MockClient::new(tendermint_rpc::MockRequestMethodMatcher::default()); - - let interpreter = FvmMessageInterpreter::new( - client, - None, - contracts_path(), - 1.05, - 1.05, - false, - UpgradeScheduler::new(), - ); - - let (state, out) = interpreter - .init(state, genesis) - .await - .context("failed to create actors")?; - - let state = state - .into_exec_state() - .map_err(|_| anyhow!("should be in exec stage"))?; - - Ok((state, out)) + let maybe_contract_path = genesis.ipc.as_ref().map(|_| contracts_path()); + + let (state, out) = create_test_genesis_state( + bundle_path, + custom_actors_bundle_path, + genesis, + maybe_contract_path, + ) + .await?; + let store = state.store().clone(); + Ok(( + state + .into_exec_state() + .map_err(|_| anyhow!("cannot parse state"))?, + out, + store, + )) } pub struct Tester { @@ -81,11 +61,6 @@ pub struct Tester { impl Tester where - I: GenesisInterpreter< - State = FvmGenesisState, - Genesis = Genesis, - Output = FvmGenesisOutput, - >, I: ExecInterpreter< State = FvmExecState, Message = FvmMessage, @@ -94,61 +69,13 @@ where EndOutput = PowerUpdates, >, { - fn state_store_clone(&self) -> MemoryBlockstore { - self.state_store.as_ref().clone() - } + pub async fn new(interpreter: I, genesis: Genesis) -> anyhow::Result { + let (exec_state, out, store) = create_test_exec_state(genesis).await?; + let (state_root, _, _) = exec_state + .commit() + .context("failed to commit genesis state")?; - pub fn new(interpreter: I, state_store: MemoryBlockstore) -> Self { - Self { - interpreter: Arc::new(interpreter), - state_store: Arc::new(state_store), - multi_engine: Arc::new(MultiEngine::new(1)), - exec_state: Arc::new(tokio::sync::Mutex::new(None)), - state_params: FvmStateParams { - timestamp: Timestamp(0), - state_root: Cid::default(), - network_version: NetworkVersion::V21, - base_fee: TokenAmount::zero(), - circ_supply: TokenAmount::zero(), - chain_id: 0, - power_scale: 0, - app_version: 0, - }, - } - } - - pub async fn init(&mut self, genesis: Genesis) -> anyhow::Result<()> { - let bundle_path = bundle_path(); - let bundle = std::fs::read(&bundle_path) - .with_context(|| format!("failed to read bundle: {}", bundle_path.to_string_lossy()))?; - - let custom_actors_bundle_path = custom_actors_bundle_path(); - let custom_actors_bundle = - std::fs::read(&custom_actors_bundle_path).with_context(|| { - format!( - "failed to read custom actors_bundle: {}", - custom_actors_bundle_path.to_string_lossy() - ) - })?; - - let state = FvmGenesisState::new( - self.state_store_clone(), - self.multi_engine.clone(), - &bundle, - &custom_actors_bundle, - ) - .await - .context("failed to create genesis state")?; - - let (state, out) = self - .interpreter - .init(state, genesis) - .await - .context("failed to init from genesis")?; - - let state_root = state.commit().context("failed to commit genesis state")?; - - self.state_params = FvmStateParams { + let state_params = FvmStateParams { state_root, timestamp: out.timestamp, network_version: out.network_version, @@ -159,7 +86,13 @@ where app_version: 0, }; - Ok(()) + Ok(Self { + interpreter: Arc::new(interpreter), + state_store: Arc::new(store), + multi_engine: Arc::new(MultiEngine::new(1)), + exec_state: Arc::new(tokio::sync::Mutex::new(None)), + state_params, + }) } /// Take the execution state, update it, put it back, return the output. diff --git a/fendermint/testing/contract-test/tests/run_upgrades.rs b/fendermint/testing/contract-test/tests/run_upgrades.rs index 00b3f4a4d2..532c2f71f9 100644 --- a/fendermint/testing/contract-test/tests/run_upgrades.rs +++ b/fendermint/testing/contract-test/tests/run_upgrades.rs @@ -26,7 +26,7 @@ use fendermint_vm_core::Timestamp; use fendermint_vm_genesis::{Account, Actor, ActorMeta, Genesis, PermissionMode, SignerAddr}; use fendermint_vm_interpreter::fvm::store::memory::MemoryBlockstore; use fendermint_vm_interpreter::fvm::upgrades::{Upgrade, UpgradeScheduler}; -use fendermint_vm_interpreter::fvm::{bundle::contracts_path, FvmMessageInterpreter}; +use fendermint_vm_interpreter::fvm::FvmMessageInterpreter; // returns a seeded secret key which is guaranteed to be the same every time fn my_secret_key() -> SecretKey { @@ -194,17 +194,8 @@ async fn test_applying_upgrades() { ) .unwrap(); - let interpreter: FvmMessageInterpreter = FvmMessageInterpreter::new( - NeverCallClient, - None, - contracts_path(), - 1.05, - 1.05, - false, - upgrade_scheduler, - ); - - let mut tester = Tester::new(interpreter, MemoryBlockstore::new()); + let interpreter: FvmMessageInterpreter = + FvmMessageInterpreter::new(NeverCallClient, None, 1.05, 1.05, false, upgrade_scheduler); let genesis = Genesis { chain_name: CHAIN_NAME.to_string(), @@ -223,7 +214,7 @@ async fn test_applying_upgrades() { ipc: None, }; - tester.init(genesis).await.unwrap(); + let mut tester = Tester::new(interpreter, genesis).await.unwrap(); // check that the app version is 0 assert_eq!(tester.state_params().app_version, 0); diff --git a/fendermint/testing/contract-test/tests/staking/machine.rs b/fendermint/testing/contract-test/tests/staking/machine.rs index 76adc405fa..99ecb95fa4 100644 --- a/fendermint/testing/contract-test/tests/staking/machine.rs +++ b/fendermint/testing/contract-test/tests/staking/machine.rs @@ -1,6 +1,6 @@ // Copyright 2022-2024 Protocol Labs // SPDX-License-Identifier: Apache-2.0, MIT -use std::{cell::RefCell, collections::HashSet, sync::Arc}; +use std::{cell::RefCell, collections::HashSet}; use arbitrary::{Arbitrary, Unstructured}; use fendermint_contract_test::ipc::{registry::RegistryCaller, subnet::SubnetCaller}; @@ -19,7 +19,6 @@ use fendermint_vm_message::{ conv::from_fvm::{self, to_eth_tokens}, signed::sign_secp256k1, }; -use fvm::engine::MultiEngine; use fvm_ipld_blockstore::Blockstore; use fvm_shared::bigint::Integer; use fvm_shared::econ::TokenAmount; @@ -65,9 +64,7 @@ pub enum StakingCommand { } #[derive(Default)] -pub struct StakingMachine { - multi_engine: Arc, -} +pub struct StakingMachine {} impl StateMachine for StakingMachine { type System = StakingSystem; @@ -86,9 +83,8 @@ impl StateMachine for StakingMachine { fn new_system(&self, state: &Self::State) -> Self::System { let rt = tokio::runtime::Runtime::new().expect("create tokio runtime for init"); - let (mut exec_state, _) = rt - .block_on(fendermint_contract_test::init_exec_state( - self.multi_engine.clone(), + let (mut exec_state, _, _) = rt + .block_on(fendermint_contract_test::create_test_exec_state( state.parent_genesis.clone(), )) .expect("failed to init parent"); diff --git a/fendermint/vm/interpreter/Cargo.toml b/fendermint/vm/interpreter/Cargo.toml index 20ca5b0a29..8e40ffed5a 100644 --- a/fendermint/vm/interpreter/Cargo.toml +++ b/fendermint/vm/interpreter/Cargo.toml @@ -88,3 +88,4 @@ arb = [ "fendermint_testing/arb", "rand", ] +test-util = [] diff --git a/fendermint/vm/interpreter/src/bytes.rs b/fendermint/vm/interpreter/src/bytes.rs index 1af84dd853..ca5b54909e 100644 --- a/fendermint/vm/interpreter/src/bytes.rs +++ b/fendermint/vm/interpreter/src/bytes.rs @@ -1,16 +1,15 @@ // Copyright 2022-2024 Protocol Labs // SPDX-License-Identifier: Apache-2.0, MIT -use anyhow::{anyhow, Context}; +use anyhow::Context; use async_trait::async_trait; use cid::Cid; -use fendermint_vm_genesis::Genesis; use fendermint_vm_message::chain::ChainMessage; use fvm_ipld_encoding::Error as IpldError; use crate::{ chain::{ChainMessageApplyRet, ChainMessageCheckRes}, fvm::{FvmQuery, FvmQueryRet}, - CheckInterpreter, ExecInterpreter, GenesisInterpreter, ProposalInterpreter, QueryInterpreter, + CheckInterpreter, ExecInterpreter, ProposalInterpreter, QueryInterpreter, }; pub type BytesMessageApplyRes = Result; @@ -276,42 +275,3 @@ where Ok((state, Ok(ret))) } } - -#[async_trait] -impl GenesisInterpreter for BytesMessageInterpreter -where - I: GenesisInterpreter, -{ - type State = I::State; - type Genesis = Vec; - type Output = I::Output; - - async fn init( - &self, - state: Self::State, - genesis: Self::Genesis, - ) -> anyhow::Result<(Self::State, Self::Output)> { - // TODO (IPC-44): Handle the serialized application state as well as `Genesis`. - let genesis: Genesis = parse_genesis(&genesis)?; - self.inner.init(state, genesis).await - } -} - -/// Parse the initial genesis either as JSON or CBOR. -fn parse_genesis(bytes: &[u8]) -> anyhow::Result { - try_parse_genesis_json(bytes).or_else(|e1| { - try_parse_genesis_cbor(bytes) - .map_err(|e2| anyhow!("failed to deserialize genesis as JSON or CBOR: {e1}; {e2}")) - }) -} - -fn try_parse_genesis_json(bytes: &[u8]) -> anyhow::Result { - let json = String::from_utf8(bytes.to_vec())?; - let genesis = serde_json::from_str(&json)?; - Ok(genesis) -} - -fn try_parse_genesis_cbor(bytes: &[u8]) -> anyhow::Result { - let genesis = fvm_ipld_encoding::from_slice(bytes)?; - Ok(genesis) -} diff --git a/fendermint/vm/interpreter/src/chain.rs b/fendermint/vm/interpreter/src/chain.rs index 79136138f8..ae4d6af24a 100644 --- a/fendermint/vm/interpreter/src/chain.rs +++ b/fendermint/vm/interpreter/src/chain.rs @@ -6,7 +6,7 @@ use crate::{ fvm::state::FvmExecState, fvm::FvmMessage, signed::{SignedMessageApplyRes, SignedMessageCheckRes, SyntheticMessage, VerifiableMessage}, - CheckInterpreter, ExecInterpreter, GenesisInterpreter, ProposalInterpreter, QueryInterpreter, + CheckInterpreter, ExecInterpreter, ProposalInterpreter, QueryInterpreter, }; use anyhow::{bail, Context}; use async_stm::atomically; @@ -497,25 +497,6 @@ where } } -#[async_trait] -impl GenesisInterpreter for ChainMessageInterpreter -where - DB: Blockstore + Clone + 'static + Send + Sync, - I: GenesisInterpreter, -{ - type State = I::State; - type Genesis = I::Genesis; - type Output = I::Output; - - async fn init( - &self, - state: Self::State, - genesis: Self::Genesis, - ) -> anyhow::Result<(Self::State, Self::Output)> { - self.inner.init(state, genesis).await - } -} - /// Convert a signed relayed bottom-up checkpoint to a syntetic message we can send to the FVM. /// /// By mapping to an FVM message we invoke the right contract to validate the checkpoint, diff --git a/fendermint/vm/interpreter/src/fvm/genesis.rs b/fendermint/vm/interpreter/src/fvm/genesis.rs deleted file mode 100644 index 23de68361c..0000000000 --- a/fendermint/vm/interpreter/src/fvm/genesis.rs +++ /dev/null @@ -1,682 +0,0 @@ -// Copyright 2022-2024 Protocol Labs -// SPDX-License-Identifier: Apache-2.0, MIT - -use std::collections::{BTreeSet, HashMap}; -use std::marker::PhantomData; -use std::path::{Path, PathBuf}; - -use anyhow::{anyhow, Context}; -use async_trait::async_trait; -use ethers::abi::Tokenize; -use ethers::core::types as et; -use fendermint_actor_eam::PermissionModeParams; -use fendermint_eth_hardhat::{Hardhat, FQN}; -use fendermint_vm_actor_interface::diamond::{EthContract, EthContractMap}; -use fendermint_vm_actor_interface::eam::EthAddress; -use fendermint_vm_actor_interface::ipc::IPC_CONTRACTS; -use fendermint_vm_actor_interface::{ - account, burntfunds, chainmetadata, cron, eam, init, ipc, reward, system, EMPTY_ARR, -}; -use fendermint_vm_core::{chainid, Timestamp}; -use fendermint_vm_genesis::{ActorMeta, Genesis, Power, PowerScale, Validator}; -use fvm_ipld_blockstore::Blockstore; -use fvm_shared::chainid::ChainID; -use fvm_shared::econ::TokenAmount; -use fvm_shared::version::NetworkVersion; -use ipc_actors_abis::i_diamond::FacetCut; -use num_traits::Zero; - -use crate::GenesisInterpreter; - -use super::state::FvmGenesisState; -use super::FvmMessageInterpreter; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct FvmGenesisOutput { - pub chain_id: ChainID, - pub timestamp: Timestamp, - pub network_version: NetworkVersion, - pub base_fee: TokenAmount, - pub power_scale: PowerScale, - pub circ_supply: TokenAmount, - pub validators: Vec>, -} - -#[async_trait] -impl GenesisInterpreter for FvmMessageInterpreter -where - DB: Blockstore + 'static + Send + Sync + Clone, - TC: Send + Sync + 'static, -{ - type State = FvmGenesisState; - type Genesis = Genesis; - type Output = FvmGenesisOutput; - - /// Initialize actor states from the Genesis spec. - /// - /// This method doesn't create all builtin Filecoin actors, - /// it leaves out the ones specific to file storage. - /// - /// The ones included are: - /// * system - /// * init - /// * cron - /// * EAM - /// * burnt funds - /// * rewards (placeholder) - /// * accounts - /// * IPC - /// - /// TODO: - /// * faucet? - /// - /// See genesis initialization in: - /// * [Lotus](https://github.com/filecoin-project/lotus/blob/v1.20.4/chain/gen/genesis/genesis.go) - /// * [ref-fvm tester](https://github.com/filecoin-project/ref-fvm/blob/fvm%40v3.1.0/testing/integration/src/tester.rs#L99-L103) - /// * [fvm-workbench](https://github.com/anorth/fvm-workbench/blob/67219b3fd0b5654d54f722ab5acea6ec0abb2edc/builtin/src/genesis.rs) - async fn init( - &self, - mut state: Self::State, - genesis: Self::Genesis, - ) -> anyhow::Result<(Self::State, Self::Output)> { - // Log the genesis in JSON format, hopefully it's not enormous. - tracing::debug!(genesis = serde_json::to_string(&genesis)?, "init"); - - // NOTE: We could consider adding the chain ID to the interpreter - // and rejecting genesis if it doesn't match the expectation, - // but the Tendermint genesis file also has this field, and - // presumably Tendermint checks that its peers have the same. - let chain_id = chainid::from_str_hashed(&genesis.chain_name)?; - - // Convert validators to CometBFT power scale. - let validators = genesis - .validators - .iter() - .cloned() - .map(|vc| vc.map_power(|c| c.into_power(genesis.power_scale))) - .collect(); - - // Currently we just pass them back as they are, but later we should - // store them in the IPC actors; or in case of a snapshot restore them - // from the state. - let out = FvmGenesisOutput { - chain_id, - timestamp: genesis.timestamp, - network_version: genesis.network_version, - circ_supply: circ_supply(&genesis), - base_fee: genesis.base_fee, - power_scale: genesis.power_scale, - validators, - }; - - // STAGE 0: Declare the built-in EVM contracts we'll have to deploy. - - // Pre-defined IDs for top-level Ethereum contracts. - let mut eth_builtin_ids = BTreeSet::new(); - let mut eth_root_contracts = Vec::new(); - let mut eth_contracts = EthContractMap::default(); - - // Only allocate IDs if the contracts are deployed. - if genesis.ipc.is_some() { - eth_contracts.extend(IPC_CONTRACTS.clone()); - } - - eth_builtin_ids.extend(eth_contracts.values().map(|c| c.actor_id)); - eth_root_contracts.extend(eth_contracts.keys()); - eth_root_contracts.extend( - eth_contracts - .values() - .flat_map(|c| c.facets.iter().map(|f| f.name)), - ); - // Collect dependencies of the main IPC actors. - let mut eth_libs = self - .contracts - .dependencies( - ð_root_contracts - .iter() - .map(|n| (contract_src(n), *n)) - .collect::>(), - ) - .context("failed to collect EVM contract dependencies")?; - - // Only keep library dependencies, not contracts with constructors. - eth_libs.retain(|(_, d)| !eth_contracts.contains_key(d.as_str())); - - // STAGE 1: First we initialize native built-in actors. - - // System actor - state - .create_builtin_actor( - system::SYSTEM_ACTOR_CODE_ID, - system::SYSTEM_ACTOR_ID, - &system::State { - builtin_actors: state.manifest_data_cid, - }, - TokenAmount::zero(), - None, - ) - .context("failed to create system actor")?; - - // Init actor - let (init_state, addr_to_id) = init::State::new( - state.store(), - genesis.chain_name.clone(), - &genesis.accounts, - ð_builtin_ids, - eth_libs.len() as u64, - ) - .context("failed to create init state")?; - - state - .create_builtin_actor( - init::INIT_ACTOR_CODE_ID, - init::INIT_ACTOR_ID, - &init_state, - TokenAmount::zero(), - None, - ) - .context("failed to create init actor")?; - - // Cron actor - state - .create_builtin_actor( - cron::CRON_ACTOR_CODE_ID, - cron::CRON_ACTOR_ID, - &cron::State { - entries: vec![], // TODO: Maybe with the IPC. - }, - TokenAmount::zero(), - None, - ) - .context("failed to create cron actor")?; - - // Ethereum Account Manager (EAM) actor - state - .create_builtin_actor( - eam::EAM_ACTOR_CODE_ID, - eam::EAM_ACTOR_ID, - &EMPTY_ARR, - TokenAmount::zero(), - None, - ) - .context("failed to create EAM actor")?; - - // Burnt funds actor (it's just an account). - state - .create_builtin_actor( - account::ACCOUNT_ACTOR_CODE_ID, - burntfunds::BURNT_FUNDS_ACTOR_ID, - &account::State { - address: burntfunds::BURNT_FUNDS_ACTOR_ADDR, - }, - TokenAmount::zero(), - None, - ) - .context("failed to create burnt funds actor")?; - - // A placeholder for the reward actor, beause I don't think - // using the one in the builtin actors library would be appropriate. - // This effectively burns the miner rewards. Better than panicking. - state - .create_builtin_actor( - account::ACCOUNT_ACTOR_CODE_ID, - reward::REWARD_ACTOR_ID, - &account::State { - address: reward::REWARD_ACTOR_ADDR, - }, - TokenAmount::zero(), - None, - ) - .context("failed to create reward actor")?; - - // STAGE 1b: Then we initialize the in-repo custom actors. - - // Initialize the chain metadata actor which handles saving metadata about the chain - // (e.g. block hashes) which we can query. - let chainmetadata_state = fendermint_actor_chainmetadata::State::new( - &state.store(), - fendermint_actor_chainmetadata::DEFAULT_LOOKBACK_LEN, - )?; - state - .create_custom_actor( - fendermint_actor_chainmetadata::CHAINMETADATA_ACTOR_NAME, - chainmetadata::CHAINMETADATA_ACTOR_ID, - &chainmetadata_state, - TokenAmount::zero(), - None, - ) - .context("failed to create chainmetadata actor")?; - - let eam_state = fendermint_actor_eam::State::new( - state.store(), - PermissionModeParams::from(genesis.eam_permission_mode), - )?; - state - .replace_builtin_actor( - eam::EAM_ACTOR_NAME, - eam::EAM_ACTOR_ID, - fendermint_actor_eam::IPC_EAM_ACTOR_NAME, - &eam_state, - TokenAmount::zero(), - None, - ) - .context("failed to replace built in eam actor")?; - - // STAGE 2: Create non-builtin accounts which do not have a fixed ID. - - // The next ID is going to be _after_ the accounts, which have already been assigned an ID by the `Init` actor. - // The reason we aren't using the `init_state.next_id` is because that already accounted for the multisig accounts. - let mut next_id = init::FIRST_NON_SINGLETON_ADDR + addr_to_id.len() as u64; - - for a in genesis.accounts { - let balance = a.balance; - match a.meta { - ActorMeta::Account(acct) => { - state - .create_account_actor(acct, balance, &addr_to_id) - .context("failed to create account actor")?; - } - ActorMeta::Multisig(ms) => { - state - .create_multisig_actor(ms, balance, &addr_to_id, next_id) - .context("failed to create multisig actor")?; - next_id += 1; - } - } - } - - // STAGE 3: Initialize the FVM and create built-in FEVM actors. - - state - .init_exec_state( - out.timestamp, - out.network_version, - out.base_fee.clone(), - out.circ_supply.clone(), - out.chain_id.into(), - out.power_scale, - ) - .context("failed to init exec state")?; - - let mut deployer = ContractDeployer::::new(&self.contracts, ð_contracts); - - // Deploy Ethereum libraries. - for (lib_src, lib_name) in eth_libs { - deployer.deploy_library(&mut state, &mut next_id, lib_src, &lib_name)?; - } - - if let Some(ipc_params) = genesis.ipc { - // IPC Gateway actor. - let gateway_addr = { - use ipc::gateway::ConstructorParameters; - - let params = ConstructorParameters::new(ipc_params.gateway, genesis.validators) - .context("failed to create gateway constructor")?; - - let facets = deployer - .facets(ipc::gateway::CONTRACT_NAME) - .context("failed to collect gateway facets")?; - - deployer.deploy_contract( - &mut state, - ipc::gateway::CONTRACT_NAME, - (facets, params), - )? - }; - - // IPC SubnetRegistry actor. - { - use ipc::registry::ConstructorParameters; - - let mut facets = deployer - .facets(ipc::registry::CONTRACT_NAME) - .context("failed to collect registry facets")?; - - let getter_facet = facets.remove(0); - let manager_facet = facets.remove(0); - let rewarder_facet = facets.remove(0); - let checkpointer_facet = facets.remove(0); - let pauser_facet = facets.remove(0); - let diamond_loupe_facet = facets.remove(0); - let diamond_cut_facet = facets.remove(0); - let ownership_facet = facets.remove(0); - - debug_assert_eq!(facets.len(), 2, "SubnetRegistry has 2 facets of its own"); - - let params = ConstructorParameters { - gateway: gateway_addr, - getter_facet: getter_facet.facet_address, - manager_facet: manager_facet.facet_address, - rewarder_facet: rewarder_facet.facet_address, - pauser_facet: pauser_facet.facet_address, - checkpointer_facet: checkpointer_facet.facet_address, - diamond_cut_facet: diamond_cut_facet.facet_address, - diamond_loupe_facet: diamond_loupe_facet.facet_address, - ownership_facet: ownership_facet.facet_address, - subnet_getter_selectors: getter_facet.function_selectors, - subnet_manager_selectors: manager_facet.function_selectors, - subnet_rewarder_selectors: rewarder_facet.function_selectors, - subnet_checkpointer_selectors: checkpointer_facet.function_selectors, - subnet_pauser_selectors: pauser_facet.function_selectors, - subnet_actor_diamond_cut_selectors: diamond_cut_facet.function_selectors, - subnet_actor_diamond_loupe_selectors: diamond_loupe_facet.function_selectors, - subnet_actor_ownership_selectors: ownership_facet.function_selectors, - creation_privileges: 0, - }; - - deployer.deploy_contract( - &mut state, - ipc::registry::CONTRACT_NAME, - (facets, params), - )?; - }; - } - - Ok((state, out)) - } -} - -fn contract_src(name: &str) -> PathBuf { - PathBuf::from(format!("{name}.sol")) -} - -struct ContractDeployer<'a, DB> { - hardhat: &'a Hardhat, - top_contracts: &'a EthContractMap, - // Assign dynamic ID addresses to libraries, but use fixed addresses for the top level contracts. - lib_addrs: HashMap, - phantom_db: PhantomData, -} - -impl<'a, DB> ContractDeployer<'a, DB> -where - DB: Blockstore + 'static + Send + Sync + Clone, -{ - pub fn new(hardhat: &'a Hardhat, top_contracts: &'a EthContractMap) -> Self { - Self { - hardhat, - top_contracts, - lib_addrs: Default::default(), - phantom_db: PhantomData, - } - } - - /// Deploy a library contract with a dynamic ID and no constructor. - pub fn deploy_library( - &mut self, - state: &mut FvmGenesisState, - next_id: &mut u64, - lib_src: impl AsRef, - lib_name: &str, - ) -> anyhow::Result<()> { - let fqn = self.hardhat.fqn(lib_src.as_ref(), lib_name); - - let bytecode = self - .hardhat - .bytecode(&lib_src, lib_name, &self.lib_addrs) - .with_context(|| format!("failed to load library bytecode {fqn}"))?; - - let eth_addr = state - .create_evm_actor(*next_id, bytecode) - .with_context(|| format!("failed to create library actor {fqn}"))?; - - let id_addr = et::Address::from(EthAddress::from_id(*next_id).0); - let eth_addr = et::Address::from(eth_addr.0); - - tracing::info!( - actor_id = next_id, - ?eth_addr, - ?id_addr, - fqn, - "deployed Ethereum library" - ); - - // We can use the masked ID here or the delegated address. - // Maybe the masked ID is quicker because it doesn't need to be resolved. - self.lib_addrs.insert(fqn, id_addr); - - *next_id += 1; - - Ok(()) - } - - /// Construct the bytecode of a top-level contract and deploy it with some constructor parameters. - pub fn deploy_contract( - &self, - state: &mut FvmGenesisState, - contract_name: &str, - constructor_params: T, - ) -> anyhow::Result - where - T: Tokenize, - { - let contract = self.top_contract(contract_name)?; - let contract_id = contract.actor_id; - let contract_src = contract_src(contract_name); - - let bytecode = self - .hardhat - .bytecode(contract_src, contract_name, &self.lib_addrs) - .with_context(|| format!("failed to load {contract_name} bytecode"))?; - - let eth_addr = state - .create_evm_actor_with_cons(contract_id, &contract.abi, bytecode, constructor_params) - .with_context(|| format!("failed to create {contract_name} actor"))?; - - let id_addr = et::Address::from(EthAddress::from_id(contract_id).0); - let eth_addr = et::Address::from(eth_addr.0); - - tracing::info!( - actor_id = contract_id, - ?eth_addr, - ?id_addr, - contract_name, - "deployed Ethereum contract" - ); - - // The Ethereum address is more usable inside the EVM than the ID address. - Ok(eth_addr) - } - - /// Collect Facet Cuts for the diamond pattern, where the facet address comes from already deployed library facets. - pub fn facets(&self, contract_name: &str) -> anyhow::Result> { - let contract = self.top_contract(contract_name)?; - let mut facet_cuts = Vec::new(); - - for facet in contract.facets.iter() { - let facet_name = facet.name; - let facet_src = contract_src(facet_name); - let facet_fqn = self.hardhat.fqn(&facet_src, facet_name); - - let facet_addr = self - .lib_addrs - .get(&facet_fqn) - .ok_or_else(|| anyhow!("facet {facet_name} has not been deployed"))?; - - let method_sigs = facet - .abi - .functions() - .filter(|f| f.signature() != "init(bytes)") - .map(|f| f.short_signature()) - .collect(); - - let facet_cut = FacetCut { - facet_address: *facet_addr, - action: 0, // Add - function_selectors: method_sigs, - }; - - facet_cuts.push(facet_cut); - } - - Ok(facet_cuts) - } - - fn top_contract(&self, contract_name: &str) -> anyhow::Result<&EthContract> { - self.top_contracts - .get(contract_name) - .ok_or_else(|| anyhow!("unknown top contract name: {contract_name}")) - } -} - -/// Sum of balances in the genesis accounts. -fn circ_supply(g: &Genesis) -> TokenAmount { - g.accounts - .iter() - .fold(TokenAmount::zero(), |s, a| s + a.balance.clone()) -} - -#[cfg(test)] -mod tests { - use std::{str::FromStr, sync::Arc}; - - use cid::Cid; - use fendermint_vm_genesis::{ipc::IpcParams, Genesis}; - use fvm::engine::MultiEngine; - use quickcheck::Arbitrary; - use tendermint_rpc::{MockClient, MockRequestMethodMatcher}; - - use crate::{ - fvm::{ - bundle::{bundle_path, contracts_path, custom_actors_bundle_path}, - state::ipc::GatewayCaller, - store::memory::MemoryBlockstore, - upgrades::UpgradeScheduler, - FvmMessageInterpreter, - }, - GenesisInterpreter, - }; - - use super::FvmGenesisState; - - #[tokio::test] - async fn load_genesis() { - let genesis = make_genesis(); - let bundle = read_bundle(); - let custom_actors_bundle = read_custom_actors_bundle(); - let interpreter = make_interpreter(); - - let multi_engine = Arc::new(MultiEngine::default()); - let store = MemoryBlockstore::new(); - - let state = FvmGenesisState::new(store, multi_engine, &bundle, &custom_actors_bundle) - .await - .expect("failed to create state"); - - let (mut state, out) = interpreter - .init(state, genesis.clone()) - .await - .expect("failed to create actors"); - - assert_eq!(out.validators.len(), genesis.validators.len()); - - // Try calling a method on the IPC Gateway. - let exec_state = state.exec_state().expect("should be in exec stage"); - let caller = GatewayCaller::default(); - - let period = caller - .bottom_up_check_period(exec_state) - .expect("error calling the gateway"); - - assert_eq!(period, genesis.ipc.unwrap().gateway.bottom_up_check_period); - - let _state_root = state.commit().expect("failed to commit"); - } - - #[tokio::test] - async fn load_genesis_deterministic() { - let genesis = make_genesis(); - let bundle = read_bundle(); - let custom_actors_bundle = read_custom_actors_bundle(); - let interpreter = make_interpreter(); - let multi_engine = Arc::new(MultiEngine::default()); - - // Create a couple of states and load the same thing. - let mut outputs = Vec::new(); - for _ in 0..3 { - let store = MemoryBlockstore::new(); - let state = - FvmGenesisState::new(store, multi_engine.clone(), &bundle, &custom_actors_bundle) - .await - .expect("failed to create state"); - - let (state, out) = interpreter - .init(state, genesis.clone()) - .await - .expect("failed to create actors"); - - let state_root_hash = state.commit().expect("failed to commit"); - outputs.push((state_root_hash, out)); - } - - for out in &outputs[1..] { - assert_eq!(out.0, outputs[0].0, "state root hash is different"); - } - } - - // This is a sort of canary test, if it fails means something changed in the way we do genesis, - // which is probably fine, but it's better to know about it, and if anybody doesn't get the same - // then we might have some non-determinism. - #[ignore] // I see a different value on CI than locally. - #[tokio::test] - async fn load_genesis_known() { - let genesis_json = "{\"chain_name\":\"/r314159/f410fnfmitm2ww7oehhtbokf6wulhrr62sgq3sgqmenq\",\"timestamp\":1073250,\"network_version\":18,\"base_fee\":\"1000\",\"power_scale\":3,\"validators\":[{\"public_key\":\"BLX9ojqB+8Z26aMmKoCRb3Te6AnSU6zY8hPcf1X5Q69XCNaHVcRxzYO2xx7o/2vgdS7nkDTMRRbkDGzy+FYdAFc=\",\"power\":\"1000000000000000000\"},{\"public_key\":\"BFcOveVieknZiscWsfXa06aGbBkKeucBycd/w0N1QHlaZfa/5dJcH7D0hvcdfv3B2Rv1OPuxo1PkgsEbWegWKcA=\",\"power\":\"1000000000000000000\"},{\"public_key\":\"BEP30ykovfrQp3zo+JVRvDVL2emC+Ju1Kpox3zMVYZyFKvYt64qyN/HOVjridDrkEsnQU8BVen4Aegja4fBZ+LU=\",\"power\":\"1000000000000000000\"}],\"accounts\":[{\"meta\":{\"Account\":{\"owner\":\"f410fggjevhgketpz6gw6ordusynlgcd5piyug4aomuq\"}},\"balance\":\"1000000000000000000\"},{\"meta\":{\"Account\":{\"owner\":\"f410frbdnwklaitcjsqe7swjwp5naple6vthq4woyfry\"}},\"balance\":\"2000000000000000000\"},{\"meta\":{\"Account\":{\"owner\":\"f410fxo4lih4n2acr3oadalidwqjgoqkzhp5dw3zwkvy\"}},\"balance\":\"1000000000000000000\"}],\"ipc\":{\"gateway\":{\"subnet_id\":\"/r314159/f410fnfmitm2ww7oehhtbokf6wulhrr62sgq3sgqmenq\",\"bottom_up_check_period\":30,\"msg_fee\":\"1000000000000\",\"majority_percentage\":60,\"active_validators_limit\":100}}}"; - - let genesis: Genesis = serde_json::from_str(genesis_json).expect("failed to parse genesis"); - - let bundle = read_bundle(); - let custom_actors_bundle = read_custom_actors_bundle(); - let interpreter = make_interpreter(); - let multi_engine = Arc::new(MultiEngine::default()); - - let store = MemoryBlockstore::new(); - let state = - FvmGenesisState::new(store, multi_engine.clone(), &bundle, &custom_actors_bundle) - .await - .expect("failed to create state"); - - let (state, _) = interpreter - .init(state, genesis.clone()) - .await - .expect("failed to create actors"); - - let state_root_hash = state.commit().expect("failed to commit"); - - let expected_root_hash = - Cid::from_str("bafy2bzacedebgy4j7qnh2v2x4kkr2jqfkryql5ookbjrwge6dbrr24ytlqnj4") - .unwrap(); - - assert_eq!(state_root_hash, expected_root_hash); - } - - fn make_genesis() -> Genesis { - let mut g = quickcheck::Gen::new(5); - let mut genesis = Genesis::arbitrary(&mut g); - - // Make sure we have IPC enabled. - genesis.ipc = Some(IpcParams::arbitrary(&mut g)); - genesis - } - - fn make_interpreter( - ) -> FvmMessageInterpreter> { - let (client, _) = MockClient::new(MockRequestMethodMatcher::default()); - FvmMessageInterpreter::new( - client, - None, - contracts_path(), - 1.05, - 1.05, - false, - UpgradeScheduler::new(), - ) - } - - fn read_bundle() -> Vec { - std::fs::read(bundle_path()).expect("failed to read bundle") - } - - fn read_custom_actors_bundle() -> Vec { - std::fs::read(custom_actors_bundle_path()).expect("failed to read custom actor bundle") - } -} diff --git a/fendermint/vm/interpreter/src/fvm/mod.rs b/fendermint/vm/interpreter/src/fvm/mod.rs index 0aefb4b2ee..3e098765b2 100644 --- a/fendermint/vm/interpreter/src/fvm/mod.rs +++ b/fendermint/vm/interpreter/src/fvm/mod.rs @@ -1,13 +1,11 @@ // Copyright 2022-2024 Protocol Labs // SPDX-License-Identifier: Apache-2.0, MIT -use std::path::PathBuf; mod broadcast; mod check; mod checkpoint; mod exec; mod externs; -mod genesis; pub mod observe; mod query; pub mod state; @@ -22,10 +20,8 @@ pub use check::FvmCheckRet; pub use checkpoint::PowerUpdates; pub use exec::FvmApplyRet; use fendermint_crypto::{PublicKey, SecretKey}; -use fendermint_eth_hardhat::Hardhat; pub use fendermint_vm_message::query::FvmQuery; use fvm_ipld_blockstore::Blockstore; -pub use genesis::FvmGenesisOutput; pub use query::FvmQueryRet; use tendermint_rpc::Client; @@ -63,7 +59,6 @@ pub struct FvmMessageInterpreter where DB: Blockstore + 'static + Clone, { - contracts: Hardhat, /// Tendermint client for querying the RPC. client: C, /// If this is a validator node, this should be the key we can use to sign transactions. @@ -91,7 +86,6 @@ where pub fn new( client: C, validator_ctx: Option>, - contracts_dir: PathBuf, gas_overestimation_rate: f64, gas_search_step: f64, exec_in_check: bool, @@ -100,7 +94,6 @@ where Self { client, validator_ctx, - contracts: Hardhat::new(contracts_dir), gas_overestimation_rate, gas_search_step, exec_in_check, diff --git a/fendermint/vm/interpreter/src/fvm/state/genesis.rs b/fendermint/vm/interpreter/src/fvm/state/genesis.rs index da90cbe0a7..bdbed99473 100644 --- a/fendermint/vm/interpreter/src/fvm/state/genesis.rs +++ b/fendermint/vm/interpreter/src/fvm/state/genesis.rs @@ -166,17 +166,6 @@ where Ok(()) } - /// Flush the data to the block store. - pub fn commit(self) -> anyhow::Result { - match self.stage { - Stage::Tree(mut state_tree) => Ok(state_tree.flush()?), - Stage::Exec(exec_state) => match exec_state.commit()? { - (_, _, true) => bail!("FVM parameters are not expected to be updated in genesis"), - (cid, _, _) => Ok(cid), - }, - } - } - /// Flush the data to the block store. Returns the state root cid and the underlying state store. pub fn finalize(self) -> anyhow::Result<(Cid, DB)> { match self.stage { diff --git a/fendermint/vm/interpreter/src/genesis.rs b/fendermint/vm/interpreter/src/genesis.rs index aefd7d2425..988482a05f 100644 --- a/fendermint/vm/interpreter/src/genesis.rs +++ b/fendermint/vm/interpreter/src/genesis.rs @@ -727,6 +727,23 @@ fn circ_supply(g: &Genesis) -> TokenAmount { .fold(TokenAmount::zero(), |s, a| s + a.balance.clone()) } +#[cfg(any(feature = "test-util", test))] +pub async fn create_test_genesis_state( + bundle_path: PathBuf, + custom_actors_bundle_path: PathBuf, + genesis_params: Genesis, + maybe_ipc_path: Option, +) -> anyhow::Result<(FvmGenesisState, GenesisOutput)> { + let mut builder = GenesisBuilder::new(bundle_path, custom_actors_bundle_path, genesis_params); + if let Some(p) = maybe_ipc_path { + builder = builder.with_ipc_system_contracts(p); + } + + let mut state = builder.init_state().await?; + let out = builder.populate_state(&mut state, builder.genesis_params.clone())?; + Ok((state, out)) +} + #[cfg(test)] mod tests { use crate::genesis::GenesisAppState; diff --git a/fendermint/vm/interpreter/src/lib.rs b/fendermint/vm/interpreter/src/lib.rs index afa1b43db5..40868b6cdf 100644 --- a/fendermint/vm/interpreter/src/lib.rs +++ b/fendermint/vm/interpreter/src/lib.rs @@ -11,23 +11,6 @@ pub mod signed; #[cfg(feature = "arb")] mod arb; -/// Initialize the chain state. -/// -/// This could be from the original genesis file, or perhaps a checkpointed snapshot. -#[async_trait] -pub trait GenesisInterpreter: Sync + Send { - type State: Send; - type Genesis: Send; - type Output; - - /// Initialize the chain. - async fn init( - &self, - state: Self::State, - genesis: Self::Genesis, - ) -> anyhow::Result<(Self::State, Self::Output)>; -} - /// Prepare and process transaction proposals. #[async_trait] pub trait ProposalInterpreter: Sync + Send { diff --git a/fendermint/vm/interpreter/src/signed.rs b/fendermint/vm/interpreter/src/signed.rs index 847d790a57..0998fb27e6 100644 --- a/fendermint/vm/interpreter/src/signed.rs +++ b/fendermint/vm/interpreter/src/signed.rs @@ -14,7 +14,7 @@ use serde::Serialize; use crate::{ fvm::{FvmApplyRet, FvmCheckRet, FvmMessage}, - CheckInterpreter, ExecInterpreter, GenesisInterpreter, QueryInterpreter, + CheckInterpreter, ExecInterpreter, QueryInterpreter, }; /// Message validation failed due to an invalid signature. @@ -230,21 +230,3 @@ where self.inner.query(state, qry).await } } - -#[async_trait] -impl GenesisInterpreter for SignedMessageInterpreter -where - I: GenesisInterpreter, -{ - type State = I::State; - type Genesis = I::Genesis; - type Output = I::Output; - - async fn init( - &self, - state: Self::State, - genesis: Self::Genesis, - ) -> anyhow::Result<(Self::State, Self::Output)> { - self.inner.init(state, genesis).await - } -} diff --git a/fendermint/vm/snapshot/Cargo.toml b/fendermint/vm/snapshot/Cargo.toml index 4183249fd1..25b5c1e1b5 100644 --- a/fendermint/vm/snapshot/Cargo.toml +++ b/fendermint/vm/snapshot/Cargo.toml @@ -43,7 +43,7 @@ fendermint_testing = { path = "../../testing", features = ["arb"], optional = tr [dev-dependencies] fvm = { workspace = true } fendermint_testing = { path = "../../testing", features = ["golden"] } -fendermint_vm_interpreter = { path = "../interpreter", features = ["bundle"] } +fendermint_vm_interpreter = { path = "../interpreter", features = ["bundle", "test-util"] } fendermint_vm_genesis = { path = "../genesis", features = ["arb"] } fendermint_vm_snapshot = { path = ".", features = ["arb"] } diff --git a/fendermint/vm/snapshot/src/manager.rs b/fendermint/vm/snapshot/src/manager.rs index 495219067e..4a6f490f88 100644 --- a/fendermint/vm/snapshot/src/manager.rs +++ b/fendermint/vm/snapshot/src/manager.rs @@ -309,21 +309,16 @@ fn move_or_copy(from: &Path, to: &Path) -> anyhow::Result<()> { #[cfg(test)] mod tests { - use std::{sync::Arc, time::Duration}; + use std::time::Duration; use async_stm::{atomically, retry}; use fendermint_vm_genesis::Genesis; - use fendermint_vm_interpreter::{ - fvm::{ - bundle::{bundle_path, contracts_path, custom_actors_bundle_path}, - state::{snapshot::Snapshot, FvmGenesisState, FvmStateParams}, - store::memory::MemoryBlockstore, - upgrades::UpgradeScheduler, - FvmMessageInterpreter, - }, - GenesisInterpreter, + use fendermint_vm_interpreter::fvm::{ + bundle::{bundle_path, contracts_path, custom_actors_bundle_path}, + state::{snapshot::Snapshot, FvmStateParams}, + store::memory::MemoryBlockstore, }; - use fvm::engine::MultiEngine; + use fendermint_vm_interpreter::genesis::create_test_genesis_state; use quickcheck::Arbitrary; use crate::{manager::SnapshotParams, manifest, PARTS_DIR_NAME}; @@ -446,33 +441,22 @@ mod tests { let mut g = quickcheck::Gen::new(5); let genesis = Genesis::arbitrary(&mut g); - let bundle = std::fs::read(bundle_path()).expect("failed to read bundle"); - let custom_actors_bundle = std::fs::read(custom_actors_bundle_path()) - .expect("failed to read custom actors bundle"); - let multi_engine = Arc::new(MultiEngine::default()); - - let store = MemoryBlockstore::new(); - let state = - FvmGenesisState::new(store.clone(), multi_engine, &bundle, &custom_actors_bundle) - .await - .expect("failed to create state"); - - let interpreter = FvmMessageInterpreter::new( - mock_client(), - None, - contracts_path(), - 1.05, - 1.05, - false, - UpgradeScheduler::new(), - ); - - let (state, out) = interpreter - .init(state, genesis) - .await - .expect("failed to init genesis"); - - let state_root = state.commit().expect("failed to commit"); + let maybe_contract_path = genesis.ipc.as_ref().map(|_| contracts_path()); + let (state, out) = create_test_genesis_state( + bundle_path(), + custom_actors_bundle_path(), + genesis, + maybe_contract_path, + ) + .await + .expect("cannot create genesis state"); + let store = state.store().clone(); + // unwrap_or_else + panic is used because the return type is not Result, also the exec state + // does not implement debug, which expect cannot be used + let state = state + .into_exec_state() + .unwrap_or_else(|_| panic!("cannot create exec state")); + let (state_root, _, _) = state.commit().expect("failed to commit"); let state_params = FvmStateParams { state_root, From e5fb277deec96d7ac332224b66f2a789e6125a99 Mon Sep 17 00:00:00 2001 From: cryptoAtwill Date: Thu, 22 Aug 2024 11:59:39 +0800 Subject: [PATCH 55/69] update gas market actor --- fendermint/actors/gas_market/src/lib.rs | 151 +++++++++++++----- .../vm/interpreter/src/fvm/state/genesis.rs | 17 ++ fendermint/vm/interpreter/src/genesis.rs | 6 +- 3 files changed, 128 insertions(+), 46 deletions(-) diff --git a/fendermint/actors/gas_market/src/lib.rs b/fendermint/actors/gas_market/src/lib.rs index 260f4d5419..998370e774 100644 --- a/fendermint/actors/gas_market/src/lib.rs +++ b/fendermint/actors/gas_market/src/lib.rs @@ -16,10 +16,12 @@ fil_actors_runtime::wasm_trampoline!(EIP1559GasMarketActor); pub const IPC_GAS_MARKET_ACTOR_NAME: &str = "gas_market"; pub type Gas = u64; +pub type SetConstants = EIP1559Constants; /// Constant params used by EIP1559 #[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone)] pub struct EIP1559Constants { + block_gas_limit: Gas, /// The minimal base fee when gas utilization is low minimal_base_fee: TokenAmount, /// Elasticity multiplier as defined in [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) @@ -30,14 +32,12 @@ pub struct EIP1559Constants { #[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone)] pub struct EIP1559GasState { - block_gas_limit: Gas, base_fee: TokenAmount, constants: EIP1559Constants, } #[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone)] pub struct GasActorConstructorParams { - block_gas_limit: Gas, base_fee: TokenAmount, constants: Option, } @@ -53,13 +53,6 @@ pub struct BlockGasUtilization { pub block_gas_used: Gas, } -#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone)] -pub struct SetConstants { - pub block_gas_limit: Option, - pub base_fee: Option, - pub constants: Option, -} - pub struct EIP1559GasMarketActor {} #[derive(FromPrimitive)] @@ -67,6 +60,7 @@ pub struct EIP1559GasMarketActor {} pub enum Method { Constructor = METHOD_CONSTRUCTOR, CurrentReading = frc42_dispatch::method_hash!("CurrentReading"), + GetConstants = frc42_dispatch::method_hash!("GetConstants"), SetConstants = frc42_dispatch::method_hash!("SetConstants"), UpdateUtilization = frc42_dispatch::method_hash!("UpdateUtilization"), } @@ -87,7 +81,7 @@ impl EIP1559GasMarketActor { rt.validate_immediate_caller_is(std::iter::once(&SYSTEM_ACTOR_ADDR))?; rt.transaction(|st: &mut EIP1559GasState, _rt| { - st.set_constants(constants); + st.constants = constants; Ok(()) })?; @@ -99,11 +93,18 @@ impl EIP1559GasMarketActor { let st = rt.state::()?; Ok(GasMarketReading { - block_gas_limit: st.block_gas_limit, + block_gas_limit: st.constants.block_gas_limit, base_fee: st.base_fee, }) } + fn get_constants(rt: &impl Runtime) -> Result { + rt.validate_immediate_caller_accept_any()?; + + let st = rt.state::()?; + Ok(st.constants) + } + fn update_utilization( rt: &impl Runtime, utilization: BlockGasUtilization, @@ -120,7 +121,8 @@ impl EIP1559GasMarketActor { impl Default for EIP1559Constants { fn default() -> Self { Self { - // Take from filecoin setting + // Take from filecoin setting, fvm_shared::BLOCK_GAS_LIMIT + block_gas_limit: 10_000_000_000, minimal_base_fee: TokenAmount::from_atto(100), // Elasticity multiplier as defined in [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) elasticity_multiplier: 2, @@ -133,7 +135,6 @@ impl Default for EIP1559Constants { impl From for EIP1559GasState { fn from(params: GasActorConstructorParams) -> Self { Self { - block_gas_limit: params.block_gas_limit, base_fee: params.base_fee, constants: params.constants.unwrap_or_default(), } @@ -141,9 +142,8 @@ impl From for EIP1559GasState { } impl GasActorConstructorParams { - pub fn new(block_gas_limit: Gas, base_fee: TokenAmount) -> Self { + pub fn new(base_fee: TokenAmount) -> Self { Self { - block_gas_limit, base_fee, constants: None, } @@ -156,22 +156,9 @@ impl GasActorConstructorParams { } impl EIP1559GasState { - #[inline] - fn update_if_some(maybe_some: Option, to_change: &mut T) { - if let Some(v) = maybe_some { - *to_change = v; - } - } - - fn set_constants(&mut self, constants: SetConstants) { - Self::update_if_some(constants.constants, &mut self.constants); - Self::update_if_some(constants.base_fee, &mut self.base_fee); - Self::update_if_some(constants.block_gas_limit, &mut self.block_gas_limit); - } - fn next_base_fee(&self, gas_used: Gas) -> TokenAmount { let base_fee = self.base_fee.clone(); - let gas_target = self.block_gas_limit / self.constants.elasticity_multiplier; + let gas_target = self.constants.block_gas_limit / self.constants.elasticity_multiplier; match gas_used.cmp(&gas_target) { Ordering::Equal => base_fee, @@ -208,6 +195,7 @@ impl ActorCode for EIP1559GasMarketActor { Constructor => constructor, SetConstants => set_constants, CurrentReading => current_reading, + GetConstants => get_constants, UpdateUtilization => update_utilization, } } @@ -215,12 +203,14 @@ impl ActorCode for EIP1559GasMarketActor { #[cfg(test)] mod tests { use crate::{ - EIP1559GasMarketActor, EIP1559GasState, GasActorConstructorParams, Method, SetConstants, + BlockGasUtilization, EIP1559Constants, EIP1559GasMarketActor, EIP1559GasState, + GasActorConstructorParams, GasMarketReading, Method, }; use fil_actors_runtime::test_utils::{expect_empty, MockRuntime, SYSTEM_ACTOR_CODE_ID}; use fil_actors_runtime::SYSTEM_ACTOR_ADDR; use fvm_ipld_encoding::ipld_block::IpldBlock; use fvm_shared::address::Address; + use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; pub fn construct_and_verify() -> MockRuntime { @@ -236,8 +226,7 @@ mod tests { .call::( Method::Constructor as u64, IpldBlock::serialize_cbor(&GasActorConstructorParams { - block_gas_limit: 100, - base_fee: Default::default(), + base_fee: TokenAmount::from_atto(100), constants: None, }) .unwrap(), @@ -259,17 +248,96 @@ mod tests { let r = rt.call::( Method::SetConstants as u64, - IpldBlock::serialize_cbor(&SetConstants { - block_gas_limit: Some(20), - base_fee: None, - constants: None, + IpldBlock::serialize_cbor(&EIP1559Constants { + minimal_base_fee: Default::default(), + elasticity_multiplier: 0, + base_fee_max_change_denominator: 0, + block_gas_limit: 20, }) .unwrap(), ); assert!(r.is_ok()); let s = rt.get_state::(); - assert_eq!(s.block_gas_limit, 20); + assert_eq!(s.constants.block_gas_limit, 20); + } + + #[test] + fn test_update_utilization_full_usage() { + let rt = construct_and_verify(); + + rt.set_caller(*SYSTEM_ACTOR_CODE_ID, SYSTEM_ACTOR_ADDR); + rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); + + let r = rt.call::( + Method::UpdateUtilization as u64, + IpldBlock::serialize_cbor(&BlockGasUtilization { + // full block usage + block_gas_used: 10_000_000_000, + }) + .unwrap(), + ); + assert!(r.is_ok()); + + rt.expect_validate_caller_any(); + let r = rt + .call::(Method::CurrentReading as u64, None) + .unwrap() + .unwrap(); + let reading = r.deserialize::().unwrap(); + assert_eq!(reading.base_fee, TokenAmount::from_atto(112)); + } + + #[test] + fn test_update_utilization_equal_usage() { + let rt = construct_and_verify(); + + rt.set_caller(*SYSTEM_ACTOR_CODE_ID, SYSTEM_ACTOR_ADDR); + rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); + + let r = rt.call::( + Method::UpdateUtilization as u64, + IpldBlock::serialize_cbor(&BlockGasUtilization { + // full block usage + block_gas_used: 5_000_000_000, + }) + .unwrap(), + ); + assert!(r.is_ok()); + + rt.expect_validate_caller_any(); + let r = rt + .call::(Method::CurrentReading as u64, None) + .unwrap() + .unwrap(); + let reading = r.deserialize::().unwrap(); + assert_eq!(reading.base_fee, TokenAmount::from_atto(100)); + } + + #[test] + fn test_update_utilization_under_usage() { + let rt = construct_and_verify(); + + rt.set_caller(*SYSTEM_ACTOR_CODE_ID, SYSTEM_ACTOR_ADDR); + rt.expect_validate_caller_addr(vec![SYSTEM_ACTOR_ADDR]); + + let r = rt.call::( + Method::UpdateUtilization as u64, + IpldBlock::serialize_cbor(&BlockGasUtilization { + // full block usage + block_gas_used: 100_000_000, + }) + .unwrap(), + ); + assert!(r.is_ok()); + + rt.expect_validate_caller_any(); + let r = rt + .call::(Method::CurrentReading as u64, None) + .unwrap() + .unwrap(); + let reading = r.deserialize::().unwrap(); + assert_eq!(reading.base_fee, TokenAmount::from_atto(88)); } #[test] @@ -281,10 +349,11 @@ mod tests { let code = rt .call::( Method::SetConstants as u64, - IpldBlock::serialize_cbor(&SetConstants { - block_gas_limit: Some(20), - base_fee: None, - constants: None, + IpldBlock::serialize_cbor(&EIP1559Constants { + minimal_base_fee: TokenAmount::from_atto(10000), + elasticity_multiplier: 0, + base_fee_max_change_denominator: 0, + block_gas_limit: 20, }) .unwrap(), ) diff --git a/fendermint/vm/interpreter/src/fvm/state/genesis.rs b/fendermint/vm/interpreter/src/fvm/state/genesis.rs index da90cbe0a7..f6a25784a1 100644 --- a/fendermint/vm/interpreter/src/fvm/state/genesis.rs +++ b/fendermint/vm/interpreter/src/fvm/state/genesis.rs @@ -290,6 +290,23 @@ where self.create_actor_internal(code_cid, id, state, balance, delegated_address) } + pub fn construct_custom_actor( + &mut self, + name: &str, + id: ActorID, + state: &impl Serialize, + balance: TokenAmount, + delegated_address: Option
, + ) -> anyhow::Result<()> { + // Retrieve the CID of the actor code by the numeric ID. + let code_cid = *self + .custom_actor_manifest + .code_by_name(name) + .ok_or_else(|| anyhow!("can't find actor: {name} in the custom actor manifest"))?; + + self.create_actor_internal(code_cid, id, state, balance, delegated_address) + } + /// Creates an actor using code specified in the manifest. fn create_actor_internal( &mut self, diff --git a/fendermint/vm/interpreter/src/genesis.rs b/fendermint/vm/interpreter/src/genesis.rs index 9b0cfb949a..e252057d9e 100644 --- a/fendermint/vm/interpreter/src/genesis.rs +++ b/fendermint/vm/interpreter/src/genesis.rs @@ -31,7 +31,6 @@ use fvm_ipld_encoding::CborStore; use fvm_shared::chainid::ChainID; use fvm_shared::econ::TokenAmount; use fvm_shared::version::NetworkVersion; -use fvm_shared::BLOCK_GAS_LIMIT; use ipc_actors_abis::i_diamond::FacetCut; use num_traits::Zero; @@ -423,10 +422,7 @@ impl GenesisBuilder { // initial base fee as defined in [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) let initial_base_fee = TokenAmount::from_atto(1_000_000_000); let gas_market_state = fendermint_actor_gas_market::EIP1559GasState::from( - fendermint_actor_gas_market::GasActorConstructorParams::new( - BLOCK_GAS_LIMIT, - initial_base_fee, - ), + fendermint_actor_gas_market::GasActorConstructorParams::new(initial_base_fee), ); state .create_custom_actor( From d3bbb3566eba03bc9741618978075370a62058a8 Mon Sep 17 00:00:00 2001 From: cryptoAtwill Date: Thu, 22 Aug 2024 19:59:26 +0800 Subject: [PATCH 56/69] end block --- fendermint/actors/gas_market/src/lib.rs | 8 +- fendermint/app/src/app.rs | 9 +- fendermint/app/src/tmconv.rs | 29 +---- fendermint/testing/contract-test/src/lib.rs | 4 +- fendermint/vm/interpreter/src/chain.rs | 8 +- fendermint/vm/interpreter/src/fvm/cometbft.rs | 115 ++++++++++++++++++ fendermint/vm/interpreter/src/fvm/exec.rs | 10 +- .../vm/interpreter/src/fvm/gas/actor.rs | 67 +++++++++- fendermint/vm/interpreter/src/fvm/gas/mod.rs | 11 ++ fendermint/vm/interpreter/src/fvm/mod.rs | 1 + 10 files changed, 215 insertions(+), 47 deletions(-) create mode 100644 fendermint/vm/interpreter/src/fvm/cometbft.rs diff --git a/fendermint/actors/gas_market/src/lib.rs b/fendermint/actors/gas_market/src/lib.rs index 998370e774..cbabdc202a 100644 --- a/fendermint/actors/gas_market/src/lib.rs +++ b/fendermint/actors/gas_market/src/lib.rs @@ -21,13 +21,13 @@ pub type SetConstants = EIP1559Constants; /// Constant params used by EIP1559 #[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone)] pub struct EIP1559Constants { - block_gas_limit: Gas, + pub block_gas_limit: Gas, /// The minimal base fee when gas utilization is low - minimal_base_fee: TokenAmount, + pub minimal_base_fee: TokenAmount, /// Elasticity multiplier as defined in [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) - elasticity_multiplier: u64, + pub elasticity_multiplier: u64, /// Base fee max change denominator as defined in [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) - base_fee_max_change_denominator: u64, + pub base_fee_max_change_denominator: u64, } #[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone)] diff --git a/fendermint/app/src/app.rs b/fendermint/app/src/app.rs index 1ddd694863..e34f790f44 100644 --- a/fendermint/app/src/app.rs +++ b/fendermint/app/src/app.rs @@ -17,12 +17,13 @@ use fendermint_vm_interpreter::bytes::{ BytesMessageApplyRes, BytesMessageCheckRes, BytesMessageQuery, BytesMessageQueryRes, }; use fendermint_vm_interpreter::chain::{ChainEnv, ChainMessageApplyRet, IllegalMessage}; +use fendermint_vm_interpreter::fvm::cometbft::{to_validator_updates, EndBlockUpdate}; use fendermint_vm_interpreter::fvm::state::{ empty_state_tree, CheckStateRef, FvmExecState, FvmQueryState, FvmStateParams, FvmUpdatableParams, }; use fendermint_vm_interpreter::fvm::store::ReadOnlyBlockstore; -use fendermint_vm_interpreter::fvm::{FvmApplyRet, PowerUpdates}; +use fendermint_vm_interpreter::fvm::FvmApplyRet; use fendermint_vm_interpreter::genesis::{read_genesis_car, GenesisAppState}; use fendermint_vm_interpreter::signed::InvalidSignature; use fendermint_vm_interpreter::{ @@ -408,7 +409,7 @@ where Message = Vec, BeginOutput = FvmApplyRet, DeliverOutput = BytesMessageApplyRes, - EndOutput = PowerUpdates, + EndOutput = EndBlockUpdate, >, I: CheckInterpreter< State = FvmExecState>, @@ -789,9 +790,7 @@ where .await .context("end failed")?; - let r = to_end_block(ret)?; - - Ok(r) + Ok(response::EndBlock::try_from(ret)?) } /// Commit the current state at the current height. diff --git a/fendermint/app/src/tmconv.rs b/fendermint/app/src/tmconv.rs index 984abc4fac..21746c02bb 100644 --- a/fendermint/app/src/tmconv.rs +++ b/fendermint/app/src/tmconv.rs @@ -3,10 +3,9 @@ //! Conversions to Tendermint data types. use anyhow::{anyhow, bail, Context}; use fendermint_vm_core::Timestamp; -use fendermint_vm_genesis::{Power, Validator}; use fendermint_vm_interpreter::fvm::{ state::{BlockHash, FvmStateParams}, - FvmApplyRet, FvmCheckRet, FvmQueryRet, PowerUpdates, + FvmApplyRet, FvmCheckRet, FvmQueryRet, }; use fendermint_vm_message::signed::DomainHash; use fendermint_vm_snapshot::{SnapshotItem, SnapshotManifest}; @@ -148,18 +147,6 @@ pub fn to_check_tx(ret: FvmCheckRet) -> response::CheckTx { } } -/// Map the return values from epoch boundary operations to validator updates. -pub fn to_end_block(power_table: PowerUpdates) -> anyhow::Result { - let validator_updates = - to_validator_updates(power_table.0).context("failed to convert validator updates")?; - - Ok(response::EndBlock { - validator_updates, - consensus_param_updates: None, - events: Vec::new(), // TODO: Events from epoch transitions? - }) -} - /// Map the return values from cron operations. pub fn to_begin_block(ret: FvmApplyRet) -> response::BeginBlock { let events = to_events("event", ret.apply_ret.events, ret.emitters); @@ -319,20 +306,6 @@ pub fn to_query(ret: FvmQueryRet, block_height: BlockHeight) -> anyhow::Result>, -) -> anyhow::Result> { - let mut updates = vec![]; - for v in validators { - updates.push(tendermint::validator::Update { - pub_key: tendermint::PublicKey::try_from(v.public_key)?, - power: tendermint::vote::Power::try_from(v.power.0)?, - }); - } - Ok(updates) -} - pub fn to_timestamp(time: tendermint::time::Time) -> Timestamp { Timestamp( time.unix_timestamp() diff --git a/fendermint/testing/contract-test/src/lib.rs b/fendermint/testing/contract-test/src/lib.rs index 65a4ce8ad3..060c532af6 100644 --- a/fendermint/testing/contract-test/src/lib.rs +++ b/fendermint/testing/contract-test/src/lib.rs @@ -4,11 +4,11 @@ use anyhow::{anyhow, Context, Result}; use byteorder::{BigEndian, WriteBytesExt}; use fendermint_vm_core::Timestamp; -use fendermint_vm_interpreter::fvm::PowerUpdates; use fvm_shared::clock::ChainEpoch; use std::{future::Future, sync::Arc}; use fendermint_vm_genesis::Genesis; +use fendermint_vm_interpreter::fvm::cometbft::EndBlockUpdate; use fendermint_vm_interpreter::genesis::{create_test_genesis_state, GenesisOutput}; use fendermint_vm_interpreter::{ fvm::{ @@ -66,7 +66,7 @@ where Message = FvmMessage, BeginOutput = FvmApplyRet, DeliverOutput = FvmApplyRet, - EndOutput = PowerUpdates, + EndOutput = EndBlockUpdate, >, { pub async fn new(interpreter: I, genesis: Genesis) -> anyhow::Result { diff --git a/fendermint/vm/interpreter/src/chain.rs b/fendermint/vm/interpreter/src/chain.rs index 1eb7b079a1..a54bb57e7f 100644 --- a/fendermint/vm/interpreter/src/chain.rs +++ b/fendermint/vm/interpreter/src/chain.rs @@ -1,8 +1,9 @@ // Copyright 2022-2024 Protocol Labs // SPDX-License-Identifier: Apache-2.0, MIT +use crate::fvm::cometbft::EndBlockUpdate; use crate::fvm::gas::{Gas, GasMarket}; use crate::fvm::state::ipc::GatewayCaller; -use crate::fvm::{topdown, FvmApplyRet, PowerUpdates}; +use crate::fvm::{topdown, FvmApplyRet}; use crate::{ fvm::state::FvmExecState, fvm::FvmMessage, @@ -245,7 +246,7 @@ where Message = VerifiableMessage, DeliverOutput = SignedMessageApplyRes, State = FvmExecState, - EndOutput = PowerUpdates, + EndOutput = EndBlockUpdate, >, { // The state consists of the resolver pool, which this interpreter needs, and the rest of the @@ -426,8 +427,9 @@ where let (state, out) = self.inner.end(state).await?; // Update any component that needs to know about changes in the power table. - if !out.0.is_empty() { + if !out.validators.0.is_empty() { let power_updates = out + .validators .0 .iter() .map(|v| { diff --git a/fendermint/vm/interpreter/src/fvm/cometbft.rs b/fendermint/vm/interpreter/src/fvm/cometbft.rs new file mode 100644 index 0000000000..daa7333072 --- /dev/null +++ b/fendermint/vm/interpreter/src/fvm/cometbft.rs @@ -0,0 +1,115 @@ +// Copyright 2022-2024 Protocol Labs +// SPDX-License-Identifier: Apache-2.0, MIT + +use crate::fvm::state::FvmExecState; +use crate::fvm::{FvmMessageInterpreter, PowerUpdates}; +use anyhow::Context; +use fendermint_vm_genesis::{Power, Validator}; +use fvm_ipld_blockstore::Blockstore; +use tendermint_rpc::Client; + +/// The end block update for cometbft +pub struct EndBlockUpdate { + pub consensus: Option, + pub validators: PowerUpdates, +} + +/// Potential updates to cometbft consensus parameters. Currently only block `max_gas` needs to be +/// updated, but in the future, more parameter could be updated. +pub struct ConsensusBlockUpdate { + max_gas: Option, +} + +/// Convert validator power to tendermint validator update. +/// TODO: the import is quite strange, `Validator` and `Power` are imported from `genesis` crate, +/// TODO: which should be from a `type` or `validator` crate. +pub fn to_validator_updates( + validators: Vec>, +) -> anyhow::Result> { + let mut updates = vec![]; + for v in validators { + updates.push(tendermint::validator::Update { + pub_key: tendermint::PublicKey::try_from(v.public_key)?, + power: tendermint::vote::Power::try_from(v.power.0)?, + }); + } + Ok(updates) +} + +impl TryFrom for tendermint::abci::response::EndBlock { + type Error = anyhow::Error; + + fn try_from(value: EndBlockUpdate) -> Result { + let validator_updates = to_validator_updates(value.validators.0) + .context("failed to convert validator updates")?; + + Ok(tendermint::abci::response::EndBlock { + validator_updates, + consensus_param_updates: value.consensus, + events: Vec::new(), // TODO: Events from epoch transitions? + }) + } +} + +impl FvmMessageInterpreter +where + DB: Blockstore + Clone + 'static + Send + Sync, + TC: Client + Clone + Send + Sync + 'static, +{ + pub(crate) async fn update_cometbft_consensus_params( + &self, + state: &mut FvmExecState, + end_block: &mut EndBlockUpdate, + ) -> anyhow::Result<()> { + let mut updates = ConsensusBlockUpdate::empty(); + + state.gas_market().process_consensus_update(&mut updates); + + if !updates.is_some() { + return Ok(()); + } + + let params = self + .client + .consensus_params(tendermint::block::Height::try_from(state.block_height())?) + .await? + .consensus_params; + end_block.with_consensus(updates.apply(params)); + + Ok(()) + } +} + +impl ConsensusBlockUpdate { + pub fn empty() -> Self { + Self { max_gas: None } + } + + pub fn is_some(&self) -> bool { + self.max_gas.is_some() + } + + pub fn apply(self, mut params: tendermint::consensus::Params) -> tendermint::consensus::Params { + if let Some(ref max_gas) = self.max_gas { + params.block.max_gas = *max_gas as i64; + } + params + } + + pub fn process_block_size(&mut self, block_gas_limit: u64) { + self.max_gas = Some(block_gas_limit); + } +} + +impl EndBlockUpdate { + pub fn new(power: PowerUpdates) -> Self { + Self { + validators: power, + consensus: None, + } + } + + pub fn with_consensus(&mut self, params: tendermint::consensus::Params) { + self.consensus = Some(params) + } +} diff --git a/fendermint/vm/interpreter/src/fvm/exec.rs b/fendermint/vm/interpreter/src/fvm/exec.rs index 1f8912edff..9fcd1a15e0 100644 --- a/fendermint/vm/interpreter/src/fvm/exec.rs +++ b/fendermint/vm/interpreter/src/fvm/exec.rs @@ -12,6 +12,7 @@ use fvm_shared::{address::Address, ActorID, MethodNum, BLOCK_GAS_LIMIT}; use ipc_observability::{emit, measure_time, observe::TracingError, Traceable}; use tendermint_rpc::Client; +use crate::fvm::cometbft::EndBlockUpdate; use crate::fvm::gas::GasMarket; use crate::ExecInterpreter; @@ -49,7 +50,7 @@ where /// Return validator power updates. /// Currently ignoring events as there aren't any emitted by the smart contract, /// but keep in mind that if there were, those would have to be propagated. - type EndOutput = PowerUpdates; + type EndOutput = EndBlockUpdate; async fn begin( &self, @@ -262,6 +263,11 @@ where PowerUpdates::default() }; - Ok((state, updates)) + // process cometbft end block updates + let mut end_block = EndBlockUpdate::new(updates); + self.update_cometbft_consensus_params(&mut state, &mut end_block) + .await?; + + Ok((state, end_block)) } } diff --git a/fendermint/vm/interpreter/src/fvm/gas/actor.rs b/fendermint/vm/interpreter/src/fvm/gas/actor.rs index a3e28033b9..b832f34376 100644 --- a/fendermint/vm/interpreter/src/fvm/gas/actor.rs +++ b/fendermint/vm/interpreter/src/fvm/gas/actor.rs @@ -5,10 +5,11 @@ use crate::fvm::gas::{Available, Gas, GasMarket}; use crate::fvm::FvmMessage; use anyhow::Context; -use fendermint_actor_gas_market::GasMarketReading; +use crate::fvm::cometbft::ConsensusBlockUpdate; +use fendermint_actor_gas_market::{GasMarketReading, SetConstants}; use fendermint_vm_actor_interface::gas::GAS_MARKET_ACTOR_ADDR; use fendermint_vm_actor_interface::system; -use fvm::executor::{ApplyKind, Executor}; +use fvm::executor::{ApplyKind, ApplyRet, Executor}; use fvm_shared::clock::ChainEpoch; #[derive(Default)] @@ -17,9 +18,21 @@ pub struct ActorGasMarket { block_gas_limit: Gas, /// The accumulated gas usage so far block_gas_used: Gas, + /// Pending update to the underlying gas actor + constant_update: Option, } impl GasMarket for ActorGasMarket { + type Constant = SetConstants; + + fn get_constants(&self) -> anyhow::Result { + todo!() + } + + fn set_constants(&mut self, constants: Self::Constant) { + self.constant_update = Some(constants); + } + fn available(&self) -> Available { Available { block_gas: self.block_gas_limit - self.block_gas_used.min(self.block_gas_limit), @@ -69,13 +82,56 @@ impl ActorGasMarket { Ok(Self { block_gas_limit: reading.block_gas_limit, block_gas_used: 0, + constant_update: None, }) } + pub fn process_consensus_update(&self, update: &mut ConsensusBlockUpdate) { + if let Some(ref set_constant) = self.constant_update { + update.process_block_size(set_constant.block_gas_limit); + } + } + pub fn commit( &self, executor: &mut E, block_height: ChainEpoch, + ) -> anyhow::Result<()> { + self.commit_constants(executor, block_height)?; + self.commit_utilization(executor, block_height) + } + + fn commit_constants( + &self, + executor: &mut E, + block_height: ChainEpoch, + ) -> anyhow::Result<()> { + let Some(ref constants) = self.constant_update else { + return Ok(()); + }; + + let msg = FvmMessage { + from: system::SYSTEM_ACTOR_ADDR, + to: GAS_MARKET_ACTOR_ADDR, + sequence: block_height as u64, + // exclude this from gas restriction + gas_limit: i64::MAX as u64, + method_num: fendermint_actor_gas_market::Method::SetConstants as u64, + params: fvm_ipld_encoding::RawBytes::serialize(constants)?, + value: Default::default(), + version: Default::default(), + gas_fee_cap: Default::default(), + gas_premium: Default::default(), + }; + self.call_fvm(msg, executor)?; + + Ok(()) + } + + fn commit_utilization( + &self, + executor: &mut E, + block_height: ChainEpoch, ) -> anyhow::Result<()> { let block_gas_used = self.block_gas_used.min(self.block_gas_limit); let params = fvm_ipld_encoding::RawBytes::serialize( @@ -96,13 +152,18 @@ impl ActorGasMarket { gas_premium: Default::default(), }; + self.call_fvm(msg, executor)?; + Ok(()) + } + + fn call_fvm(&self, msg: FvmMessage, executor: &mut E) -> anyhow::Result { let raw_length = fvm_ipld_encoding::to_vec(&msg).map(|bz| bz.len())?; let apply_ret = executor.execute_message(msg, ApplyKind::Implicit, raw_length)?; if let Some(err) = apply_ret.failure_info { anyhow::bail!("failed to update EIP1559 gas state: {}", err) } else { - Ok(()) + Ok(apply_ret) } } } diff --git a/fendermint/vm/interpreter/src/fvm/gas/mod.rs b/fendermint/vm/interpreter/src/fvm/gas/mod.rs index 768c47bb6d..f6e26ef93e 100644 --- a/fendermint/vm/interpreter/src/fvm/gas/mod.rs +++ b/fendermint/vm/interpreter/src/fvm/gas/mod.rs @@ -11,6 +11,17 @@ pub struct Available { /// The gas market for fendermint. This should be backed by an fvm actor. pub trait GasMarket { + /// The constant parameters that determines the readings of gas market, such as block gas limit. + type Constant; + + #[allow(dead_code)] + fn get_constants(&self) -> anyhow::Result; + + /// Update the constants of the gas market. If the gas market is actor based, then it's recommended + /// to flush at EndBlock. + #[allow(dead_code)] + fn set_constants(&mut self, constants: Self::Constant); + /// Obtain the current block gas available for execution fn available(&self) -> Available; diff --git a/fendermint/vm/interpreter/src/fvm/mod.rs b/fendermint/vm/interpreter/src/fvm/mod.rs index 73296813ea..1c14ab2ebd 100644 --- a/fendermint/vm/interpreter/src/fvm/mod.rs +++ b/fendermint/vm/interpreter/src/fvm/mod.rs @@ -14,6 +14,7 @@ pub mod upgrades; #[cfg(any(test, feature = "bundle"))] pub mod bundle; +pub mod cometbft; pub(crate) mod gas; pub(crate) mod topdown; From 65a6802d37039241fa5181f39d5c09a788786c1e Mon Sep 17 00:00:00 2001 From: cryptoAtwill Date: Mon, 26 Aug 2024 21:53:58 +0800 Subject: [PATCH 57/69] add validator tracking and gas premium distribution --- Cargo.lock | 2 + fendermint/app/src/app.rs | 24 +++++-- fendermint/app/src/cmd/run.rs | 3 +- fendermint/app/src/ipc.rs | 14 ++-- fendermint/app/src/lib.rs | 1 + fendermint/app/src/validators.rs | 69 +++++++++++++++++++ fendermint/vm/interpreter/src/chain.rs | 4 +- fendermint/vm/interpreter/src/fvm/exec.rs | 4 +- .../vm/interpreter/src/fvm/gas/actor.rs | 63 ++++++++++++++--- fendermint/vm/interpreter/src/fvm/gas/mod.rs | 19 ++++- .../vm/interpreter/src/fvm/state/exec.rs | 21 +++--- 11 files changed, 190 insertions(+), 34 deletions(-) create mode 100644 fendermint/app/src/validators.rs diff --git a/Cargo.lock b/Cargo.lock index c6203003e3..bf262d1471 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2828,7 +2828,9 @@ dependencies = [ "fvm_ipld_blockstore", "fvm_ipld_encoding", "fvm_shared", + "hex", "hex-literal 0.4.1", + "k256 0.13.3", "log", "multihash 0.18.1", "num-derive 0.3.3", diff --git a/fendermint/app/src/app.rs b/fendermint/app/src/app.rs index e34f790f44..f9b6259a10 100644 --- a/fendermint/app/src/app.rs +++ b/fendermint/app/src/app.rs @@ -42,12 +42,14 @@ use num_traits::Zero; use serde::{Deserialize, Serialize}; use tendermint::abci::request::CheckTxKind; use tendermint::abci::{request, response}; +use tendermint_rpc::Client; use tracing::instrument; use crate::observe::{ BlockCommitted, BlockProposalEvaluated, BlockProposalReceived, BlockProposalSent, Message, MpoolReceived, }; +use crate::validators::ValidatorTracker; use crate::AppExitCode; use crate::BlockHeight; use crate::{tmconv::*, VERSION}; @@ -116,10 +118,11 @@ pub struct AppConfig { /// Handle ABCI requests. #[derive(Clone)] -pub struct App +pub struct App where SS: Blockstore + Clone + 'static, S: KVStore, + C: Client, { /// Database backing all key-value operations. db: Arc, @@ -160,9 +163,11 @@ where /// /// Zero means unlimited. state_hist_size: u64, + /// Tracks the validator + validators: ValidatorTracker, } -impl App +impl App where S: KVStore + Codec @@ -171,6 +176,7 @@ where + Codec, DB: KVWritable + KVReadable + Clone + 'static, SS: Blockstore + Clone + 'static, + C: Client, { pub fn new( config: AppConfig, @@ -179,6 +185,7 @@ where interpreter: I, chain_env: ChainEnv, snapshots: Option, + client: C, ) -> Result { let app = Self { db: Arc::new(db), @@ -193,13 +200,14 @@ where snapshots, exec_state: Arc::new(tokio::sync::Mutex::new(None)), check_state: Arc::new(tokio::sync::Mutex::new(None)), + validators: ValidatorTracker::new(client), }; app.init_committed_state()?; Ok(app) } } -impl App +impl App where S: KVStore + Codec @@ -208,6 +216,7 @@ where + Codec, DB: KVWritable + KVReadable + 'static + Clone, SS: Blockstore + 'static + Clone, + C: Client, { /// Get an owned clone of the state store. fn state_store_clone(&self) -> SS { @@ -393,7 +402,7 @@ where // the `tower-abci` library would throw an exception when it tried to convert a // `Response::Exception` into a `ConsensusResponse` for example. #[async_trait] -impl Application for App +impl Application for App where S: KVStore + Codec @@ -421,6 +430,7 @@ where Query = BytesMessageQuery, Output = BytesMessageQueryRes, >, + C: Client + Sync, { /// Provide information about the ABCI application. async fn info(&self, _request: request::Info) -> AbciResult { @@ -727,10 +737,14 @@ where state_params.timestamp = to_timestamp(request.header.time); + let validator = self + .validators + .get_validator(&request.header.proposer_address, block_height) + .await?; let state = FvmExecState::new(db, self.multi_engine.as_ref(), block_height, state_params) .context("error creating new state")? .with_block_hash(block_hash) - .with_validator_id(request.header.proposer_address); + .with_validator(validator); tracing::debug!("initialized exec state"); diff --git a/fendermint/app/src/cmd/run.rs b/fendermint/app/src/cmd/run.rs index dca099b1f0..9d8e685169 100644 --- a/fendermint/app/src/cmd/run.rs +++ b/fendermint/app/src/cmd/run.rs @@ -294,7 +294,7 @@ async fn run(settings: Settings) -> anyhow::Result<()> { None }; - let app: App<_, _, AppStore, _> = App::new( + let app: App<_, _, AppStore, _, _> = App::new( AppConfig { app_namespace: ns.app, state_hist_namespace: ns.state_hist, @@ -310,6 +310,7 @@ async fn run(settings: Settings) -> anyhow::Result<()> { parent_finality_votes: parent_finality_votes.clone(), }, snapshots, + tendermint_client.clone(), )?; if let Some((agent_proxy, config)) = ipc_tuple { diff --git a/fendermint/app/src/ipc.rs b/fendermint/app/src/ipc.rs index 6a03bc4b25..5415a0efe7 100644 --- a/fendermint/app/src/ipc.rs +++ b/fendermint/app/src/ipc.rs @@ -15,6 +15,7 @@ use fvm_ipld_blockstore::Blockstore; use std::sync::Arc; use serde::{Deserialize, Serialize}; +use tendermint_rpc::Client; /// All the things that can be voted on in a subnet. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] @@ -24,17 +25,18 @@ pub enum AppVote { } /// Queries the LATEST COMMITTED parent finality from the storage -pub struct AppParentFinalityQuery +pub struct AppParentFinalityQuery where SS: Blockstore + Clone + 'static, S: KVStore, + C: Client, { /// The app to get state - app: App, + app: App, gateway_caller: GatewayCaller>>, } -impl AppParentFinalityQuery +impl AppParentFinalityQuery where S: KVStore + Codec @@ -43,8 +45,9 @@ where + Codec, DB: KVWritable + KVReadable + 'static + Clone, SS: Blockstore + 'static + Clone, + C: Client, { - pub fn new(app: App) -> Self { + pub fn new(app: App) -> Self { Self { app, gateway_caller: GatewayCaller::default(), @@ -62,7 +65,7 @@ where } } -impl ParentFinalityStateQuery for AppParentFinalityQuery +impl ParentFinalityStateQuery for AppParentFinalityQuery where S: KVStore + Codec @@ -71,6 +74,7 @@ where + Codec, DB: KVWritable + KVReadable + 'static + Clone, SS: Blockstore + 'static + Clone, + C: Client, { fn get_latest_committed_finality(&self) -> anyhow::Result> { self.with_exec_state(|mut exec_state| { diff --git a/fendermint/app/src/lib.rs b/fendermint/app/src/lib.rs index f9a45b9a5a..a43f8d93fc 100644 --- a/fendermint/app/src/lib.rs +++ b/fendermint/app/src/lib.rs @@ -8,6 +8,7 @@ pub mod metrics; pub mod observe; mod store; mod tmconv; +mod validators; pub use app::{App, AppConfig}; pub use store::{AppStore, BitswapBlockstore}; diff --git a/fendermint/app/src/validators.rs b/fendermint/app/src/validators.rs new file mode 100644 index 0000000000..5870b71606 --- /dev/null +++ b/fendermint/app/src/validators.rs @@ -0,0 +1,69 @@ +// Copyright 2022-2024 Protocol Labs +// SPDX-License-Identifier: Apache-2.0, MIT + +//! Tracks the validator id from tendermint to their corresponding public key. + +use anyhow::anyhow; +use fendermint_crypto::PublicKey; +use fvm_shared::clock::ChainEpoch; +use std::collections::HashMap; +use std::sync::{Arc, RwLock}; +use tendermint::block::Height; +use tendermint_rpc::{Client, Paging}; + +#[derive(Clone)] +pub(crate) struct ValidatorTracker { + client: C, + public_keys: Arc>>, +} + +impl ValidatorTracker { + pub fn new(client: C) -> Self { + Self { + client, + public_keys: Arc::new(RwLock::new(HashMap::new())), + } + } +} + +impl ValidatorTracker { + /// Get the public key of the validator by id. Note that the id is expected to be a validator. + pub async fn get_validator( + &self, + id: &tendermint::account::Id, + height: ChainEpoch, + ) -> anyhow::Result { + if let Some(key) = self.get_from_cache(id) { + return Ok(key); + } + + // this means validators have changed, re-pull all validators + let height = Height::try_from(height)?; + let response = self.client.validators(height, Paging::All).await?; + + let mut new_validators = HashMap::new(); + let mut pubkey = None; + for validator in response.validators { + let p = validator.pub_key.secp256k1().unwrap(); + let compressed = p.to_encoded_point(true); + let b = compressed.as_bytes(); + let key = PublicKey::parse_slice(b, None)?; + + if *id == validator.address { + pubkey = Some(key); + } + + new_validators.insert(validator.address, key); + } + + *self.public_keys.write().unwrap() = new_validators; + + // cannot find the validator, this should not have happened usually + pubkey.ok_or_else(|| anyhow!("{} not validator", id)) + } + + fn get_from_cache(&self, id: &tendermint::account::Id) -> Option { + let keys = self.public_keys.read().unwrap(); + keys.get(id).copied() + } +} diff --git a/fendermint/vm/interpreter/src/chain.rs b/fendermint/vm/interpreter/src/chain.rs index a54bb57e7f..dc9a9b5534 100644 --- a/fendermint/vm/interpreter/src/chain.rs +++ b/fendermint/vm/interpreter/src/chain.rs @@ -383,7 +383,9 @@ where tracing::debug!("chain interpreter applied topdown msgs"); let local_block_height = state.block_height() as u64; - let proposer = state.validator_id().map(|id| id.to_string()); + let proposer = state + .validator_pubkey() + .map(|id| hex::encode(id.serialize_compressed())); let proposer_ref = proposer.as_deref(); atomically(|| { diff --git a/fendermint/vm/interpreter/src/fvm/exec.rs b/fendermint/vm/interpreter/src/fvm/exec.rs index 9fcd1a15e0..619b278aa1 100644 --- a/fendermint/vm/interpreter/src/fvm/exec.rs +++ b/fendermint/vm/interpreter/src/fvm/exec.rs @@ -13,7 +13,7 @@ use ipc_observability::{emit, measure_time, observe::TracingError, Traceable}; use tendermint_rpc::Client; use crate::fvm::cometbft::EndBlockUpdate; -use crate::fvm::gas::GasMarket; +use crate::fvm::gas::{GasMarket, GasUtilization}; use crate::ExecInterpreter; use super::{ @@ -175,7 +175,7 @@ where state .gas_market_mut() - .record_utilization(apply_ret.msg_receipt.gas_used); + .record_utilization(GasUtilization::from(&apply_ret)); (apply_ret, emitters, latency) }; diff --git a/fendermint/vm/interpreter/src/fvm/gas/actor.rs b/fendermint/vm/interpreter/src/fvm/gas/actor.rs index b832f34376..2fdd78d365 100644 --- a/fendermint/vm/interpreter/src/fvm/gas/actor.rs +++ b/fendermint/vm/interpreter/src/fvm/gas/actor.rs @@ -1,19 +1,26 @@ // Copyright 2022-2024 Protocol Labs // SPDX-License-Identifier: Apache-2.0, MIT -use crate::fvm::gas::{Available, Gas, GasMarket}; +use crate::fvm::gas::{Available, Gas, GasMarket, GasUtilization}; use crate::fvm::FvmMessage; use anyhow::Context; use crate::fvm::cometbft::ConsensusBlockUpdate; use fendermint_actor_gas_market::{GasMarketReading, SetConstants}; +use fendermint_crypto::PublicKey; +use fendermint_vm_actor_interface::eam::EthAddress; use fendermint_vm_actor_interface::gas::GAS_MARKET_ACTOR_ADDR; -use fendermint_vm_actor_interface::system; +use fendermint_vm_actor_interface::{reward, system}; use fvm::executor::{ApplyKind, ApplyRet, Executor}; +use fvm_shared::address::Address; use fvm_shared::clock::ChainEpoch; +use fvm_shared::econ::TokenAmount; +use fvm_shared::METHOD_SEND; #[derive(Default)] pub struct ActorGasMarket { + /// The total gas premium for the miner + gas_premium: TokenAmount, /// The block gas limit block_gas_limit: Gas, /// The accumulated gas usage so far @@ -39,8 +46,9 @@ impl GasMarket for ActorGasMarket { } } - fn record_utilization(&mut self, gas: Gas) { - self.block_gas_used += gas; + fn record_utilization(&mut self, utilization: GasUtilization) { + self.gas_premium += utilization.gas_premium; + self.block_gas_used += utilization.gas_used; // sanity check if self.block_gas_used >= self.block_gas_limit { @@ -80,6 +88,7 @@ impl ActorGasMarket { .context("failed to parse gas market readying")?; Ok(Self { + gas_premium: TokenAmount::from_atto(0), block_gas_limit: reading.block_gas_limit, block_gas_used: 0, constant_update: None, @@ -96,9 +105,43 @@ impl ActorGasMarket { &self, executor: &mut E, block_height: ChainEpoch, + validator: Option, ) -> anyhow::Result<()> { self.commit_constants(executor, block_height)?; - self.commit_utilization(executor, block_height) + self.commit_utilization(executor, block_height)?; + self.distribute_reward(executor, block_height, validator) + } + + fn distribute_reward( + &self, + executor: &mut E, + block_height: ChainEpoch, + validator: Option, + ) -> anyhow::Result<()> { + if validator.is_none() || self.gas_premium.is_zero() { + return Ok(()); + } + + let validator = validator.unwrap(); + let validator = Address::from(EthAddress::new_secp256k1(&validator.serialize())?); + + let msg = FvmMessage { + from: reward::REWARD_ACTOR_ADDR, + to: validator, + sequence: block_height as u64, + // exclude this from gas restriction + gas_limit: i64::MAX as u64, + method_num: METHOD_SEND, + params: fvm_ipld_encoding::RawBytes::default(), + value: self.gas_premium.clone(), + + version: Default::default(), + gas_fee_cap: Default::default(), + gas_premium: Default::default(), + }; + self.exec_msg_implicitly(msg, executor)?; + + Ok(()) } fn commit_constants( @@ -123,7 +166,7 @@ impl ActorGasMarket { gas_fee_cap: Default::default(), gas_premium: Default::default(), }; - self.call_fvm(msg, executor)?; + self.exec_msg_implicitly(msg, executor)?; Ok(()) } @@ -152,11 +195,15 @@ impl ActorGasMarket { gas_premium: Default::default(), }; - self.call_fvm(msg, executor)?; + self.exec_msg_implicitly(msg, executor)?; Ok(()) } - fn call_fvm(&self, msg: FvmMessage, executor: &mut E) -> anyhow::Result { + fn exec_msg_implicitly( + &self, + msg: FvmMessage, + executor: &mut E, + ) -> anyhow::Result { let raw_length = fvm_ipld_encoding::to_vec(&msg).map(|bz| bz.len())?; let apply_ret = executor.execute_message(msg, ApplyKind::Implicit, raw_length)?; diff --git a/fendermint/vm/interpreter/src/fvm/gas/mod.rs b/fendermint/vm/interpreter/src/fvm/gas/mod.rs index f6e26ef93e..346f57c947 100644 --- a/fendermint/vm/interpreter/src/fvm/gas/mod.rs +++ b/fendermint/vm/interpreter/src/fvm/gas/mod.rs @@ -1,6 +1,9 @@ // Copyright 2022-2024 Protocol Labs // SPDX-License-Identifier: Apache-2.0, MIT +use fvm::executor::ApplyRet; +use fvm_shared::econ::TokenAmount; + pub mod actor; pub type Gas = u64; @@ -9,6 +12,11 @@ pub struct Available { pub block_gas: Gas, } +pub struct GasUtilization { + gas_used: Gas, + gas_premium: TokenAmount, +} + /// The gas market for fendermint. This should be backed by an fvm actor. pub trait GasMarket { /// The constant parameters that determines the readings of gas market, such as block gas limit. @@ -26,5 +34,14 @@ pub trait GasMarket { fn available(&self) -> Available; /// Tracks the amount of gas consumed by a transaction - fn record_utilization(&mut self, gas: Gas); + fn record_utilization(&mut self, gas: GasUtilization); +} + +impl From<&ApplyRet> for GasUtilization { + fn from(ret: &ApplyRet) -> Self { + Self { + gas_used: ret.msg_receipt.gas_used, + gas_premium: ret.miner_tip.clone(), + } + } } diff --git a/fendermint/vm/interpreter/src/fvm/state/exec.rs b/fendermint/vm/interpreter/src/fvm/state/exec.rs index 577317dc12..10497ce209 100644 --- a/fendermint/vm/interpreter/src/fvm/state/exec.rs +++ b/fendermint/vm/interpreter/src/fvm/state/exec.rs @@ -5,6 +5,7 @@ use std::collections::{HashMap, HashSet}; use anyhow::Ok; use cid::Cid; +use fendermint_crypto::PublicKey; use fendermint_vm_genesis::PowerScale; use fvm::{ call_manager::DefaultCallManager, @@ -30,9 +31,6 @@ use fendermint_vm_encoding::IsHumanReadable; pub type BlockHash = [u8; 32]; -/// First 20 bytes of SHA256(PublicKey) -pub type ValidatorId = tendermint::account::Id; - pub type ActorAddressMap = HashMap; /// The result of the message application bundled with any delegated addresses of event emitters. @@ -107,8 +105,8 @@ where /// execution interpreter without having to add yet another piece to track at the app level. block_hash: Option, - /// ID of the validator who created this block. For queries and checks this is empty. - validator_id: Option, + /// Public key of the validator who created this block. For queries and checks this is empty. + validator_pubkey: Option, /// State of parameters that are outside the control of the FVM but can change and need to be persisted. params: FvmUpdatableParams, @@ -156,7 +154,7 @@ where Ok(Self { executor, block_hash: None, - validator_id: None, + validator_pubkey: None, params: FvmUpdatableParams { app_version: params.app_version, base_fee: params.base_fee, @@ -175,8 +173,8 @@ where } /// Set the validator during execution. - pub fn with_validator_id(mut self, validator_id: ValidatorId) -> Self { - self.validator_id = Some(validator_id); + pub fn with_validator(mut self, key: PublicKey) -> Self { + self.validator_pubkey = Some(key); self } @@ -233,8 +231,8 @@ where } /// Identity of the block creator, if we are indeed executing any blocks. - pub fn validator_id(&self) -> Option { - self.validator_id + pub fn validator_pubkey(&self) -> Option { + self.validator_pubkey } /// The timestamp of the currently executing block. @@ -302,7 +300,8 @@ where pub fn update_gas_market(&mut self) -> anyhow::Result<()> { let height = self.block_height(); - self.gas_market.commit(&mut self.executor, height) + self.gas_market + .commit(&mut self.executor, height, self.validator_pubkey) } /// Update the circulating supply, effective from the next block. From 05ec86b8837d67a79a8b7d16266af9a47111e4e3 Mon Sep 17 00:00:00 2001 From: cryptoAtwill Date: Tue, 27 Aug 2024 17:27:34 +0800 Subject: [PATCH 58/69] distribute validator reward --- Cargo.lock | 3 +- fendermint/app/Cargo.toml | 1 + fendermint/app/src/app.rs | 27 +++- fendermint/app/src/tmconv.rs | 66 +++++++- fendermint/testing/contract-test/src/lib.rs | 4 +- fendermint/vm/interpreter/src/chain.rs | 151 +++--------------- fendermint/vm/interpreter/src/fvm/cometbft.rs | 115 ------------- fendermint/vm/interpreter/src/fvm/exec.rs | 10 +- .../vm/interpreter/src/fvm/gas/actor.rs | 11 +- fendermint/vm/interpreter/src/fvm/gas/mod.rs | 3 - fendermint/vm/interpreter/src/fvm/mod.rs | 1 - fendermint/vm/interpreter/src/lib.rs | 1 + fendermint/vm/interpreter/src/selector.rs | 46 ++++++ 13 files changed, 158 insertions(+), 281 deletions(-) delete mode 100644 fendermint/vm/interpreter/src/fvm/cometbft.rs create mode 100644 fendermint/vm/interpreter/src/selector.rs diff --git a/Cargo.lock b/Cargo.lock index bf262d1471..16aab6317b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2828,9 +2828,7 @@ dependencies = [ "fvm_ipld_blockstore", "fvm_ipld_encoding", "fvm_shared", - "hex", "hex-literal 0.4.1", - "k256 0.13.3", "log", "multihash 0.18.1", "num-derive 0.3.3", @@ -2863,6 +2861,7 @@ dependencies = [ "bytes", "cid", "fendermint_abci", + "fendermint_actor_gas_market", "fendermint_app_options", "fendermint_app_settings", "fendermint_crypto", diff --git a/fendermint/app/Cargo.toml b/fendermint/app/Cargo.toml index 8e884e69f7..d2b275e1b1 100644 --- a/fendermint/app/Cargo.toml +++ b/fendermint/app/Cargo.toml @@ -51,6 +51,7 @@ fendermint_rocksdb = { path = "../rocksdb" } fendermint_rpc = { path = "../rpc" } fendermint_storage = { path = "../storage" } fendermint_tracing = { path = "../tracing" } +fendermint_actor_gas_market = { path = "../actors/gas_market" } fendermint_vm_actor_interface = { path = "../vm/actor_interface" } fendermint_vm_core = { path = "../vm/core" } fendermint_vm_encoding = { path = "../vm/encoding" } diff --git a/fendermint/app/src/app.rs b/fendermint/app/src/app.rs index f9b6259a10..c1a193ebd4 100644 --- a/fendermint/app/src/app.rs +++ b/fendermint/app/src/app.rs @@ -17,13 +17,12 @@ use fendermint_vm_interpreter::bytes::{ BytesMessageApplyRes, BytesMessageCheckRes, BytesMessageQuery, BytesMessageQueryRes, }; use fendermint_vm_interpreter::chain::{ChainEnv, ChainMessageApplyRet, IllegalMessage}; -use fendermint_vm_interpreter::fvm::cometbft::{to_validator_updates, EndBlockUpdate}; use fendermint_vm_interpreter::fvm::state::{ empty_state_tree, CheckStateRef, FvmExecState, FvmQueryState, FvmStateParams, FvmUpdatableParams, }; use fendermint_vm_interpreter::fvm::store::ReadOnlyBlockstore; -use fendermint_vm_interpreter::fvm::FvmApplyRet; +use fendermint_vm_interpreter::fvm::{FvmApplyRet, PowerUpdates}; use fendermint_vm_interpreter::genesis::{read_genesis_car, GenesisAppState}; use fendermint_vm_interpreter::signed::InvalidSignature; use fendermint_vm_interpreter::{ @@ -165,6 +164,8 @@ where state_hist_size: u64, /// Tracks the validator validators: ValidatorTracker, + /// The cometbft client + client: C, } impl App @@ -176,7 +177,7 @@ where + Codec, DB: KVWritable + KVReadable + Clone + 'static, SS: Blockstore + Clone + 'static, - C: Client, + C: Client + Clone, { pub fn new( config: AppConfig, @@ -200,7 +201,8 @@ where snapshots, exec_state: Arc::new(tokio::sync::Mutex::new(None)), check_state: Arc::new(tokio::sync::Mutex::new(None)), - validators: ValidatorTracker::new(client), + validators: ValidatorTracker::new(client.clone()), + client, }; app.init_committed_state()?; Ok(app) @@ -418,7 +420,7 @@ where Message = Vec, BeginOutput = FvmApplyRet, DeliverOutput = BytesMessageApplyRes, - EndOutput = EndBlockUpdate, + EndOutput = PowerUpdates, >, I: CheckInterpreter< State = FvmExecState>, @@ -430,7 +432,7 @@ where Query = BytesMessageQuery, Output = BytesMessageQueryRes, >, - C: Client + Sync, + C: Client + Sync + Clone, { /// Provide information about the ABCI application. async fn info(&self, _request: request::Info) -> AbciResult { @@ -800,11 +802,20 @@ where // TODO: Return events from epoch transitions. let ret = self - .modify_exec_state(|s| self.interpreter.end(s)) + .modify_exec_state(|s| async { + let ((chain_env, mut state), update) = self.interpreter.end(s).await?; + + let mut end_block = EndBlockUpdate::new(update); + if let Some(gas) = state.gas_market_mut().take_constant_update() { + end_block.update_gas(gas) + } + + Ok(((chain_env, state), end_block)) + }) .await .context("end failed")?; - Ok(response::EndBlock::try_from(ret)?) + Ok(to_end_block(&self.client, request.height, ret).await?) } /// Commit the current state at the current height. diff --git a/fendermint/app/src/tmconv.rs b/fendermint/app/src/tmconv.rs index 21746c02bb..6c031072ec 100644 --- a/fendermint/app/src/tmconv.rs +++ b/fendermint/app/src/tmconv.rs @@ -2,18 +2,22 @@ // SPDX-License-Identifier: Apache-2.0, MIT //! Conversions to Tendermint data types. use anyhow::{anyhow, bail, Context}; +use fendermint_actor_gas_market::SetConstants; use fendermint_vm_core::Timestamp; +use fendermint_vm_genesis::{Power, Validator}; use fendermint_vm_interpreter::fvm::{ state::{BlockHash, FvmStateParams}, - FvmApplyRet, FvmCheckRet, FvmQueryRet, + FvmApplyRet, FvmCheckRet, FvmQueryRet, PowerUpdates, }; use fendermint_vm_message::signed::DomainHash; use fendermint_vm_snapshot::{SnapshotItem, SnapshotManifest}; +use fvm_shared::clock::ChainEpoch; use fvm_shared::{address::Address, error::ExitCode, event::StampedEvent, ActorID}; use prost::Message; use serde::{Deserialize, Serialize}; use std::{collections::HashMap, num::NonZeroU32}; use tendermint::abci::{response, Code, Event, EventAttribute}; +use tendermint_rpc::Client; use crate::{app::AppError, BlockHeight}; @@ -23,6 +27,25 @@ struct SnapshotMetadata { state_params: FvmStateParams, } +/// The end block update for cometbft +pub struct EndBlockUpdate { + pub max_gas: Option, + pub validators: PowerUpdates, +} + +impl EndBlockUpdate { + pub fn new(power: PowerUpdates) -> Self { + Self { + max_gas: None, + validators: power, + } + } + + pub fn update_gas(&mut self, constants: SetConstants) { + self.max_gas = Some(constants.block_gas_limit); + } +} + /// IPLD encoding of data types we know we must be able to encode. macro_rules! ipld_encode { ($var:ident) => { @@ -31,6 +54,31 @@ macro_rules! ipld_encode { }; } +pub(crate) async fn to_end_block( + client: &C, + height: ChainEpoch, + value: EndBlockUpdate, +) -> anyhow::Result { + let validator_updates = + to_validator_updates(value.validators.0).context("failed to convert validator updates")?; + + let mut consensus_param_updates = None; + if let Some(max_gas) = value.max_gas { + let mut consensus_params = client + .consensus_params(tendermint::block::Height::try_from(height)?) + .await? + .consensus_params; + consensus_params.block.max_gas = max_gas as i64; + consensus_param_updates = Some(consensus_params); + } + + Ok(response::EndBlock { + validator_updates, + consensus_param_updates, + events: Vec::new(), // TODO: Events from epoch transitions? + }) +} + /// Response to delivery where the input was blatantly invalid. /// This indicates that the validator who made the block was Byzantine. pub fn invalid_deliver_tx(err: AppError, description: String) -> response::DeliverTx { @@ -63,6 +111,22 @@ pub fn invalid_query(err: AppError, description: String) -> response::Query { } } +/// Convert validator power to tendermint validator update. +/// TODO: the import is quite strange, `Validator` and `Power` are imported from `genesis` crate, +/// TODO: which should be from a `type` or `validator` crate. +pub fn to_validator_updates( + validators: Vec>, +) -> anyhow::Result> { + let mut updates = vec![]; + for v in validators { + updates.push(tendermint::validator::Update { + pub_key: tendermint::PublicKey::try_from(v.public_key)?, + power: tendermint::vote::Power::try_from(v.power.0)?, + }); + } + Ok(updates) +} + pub fn to_deliver_tx( ret: FvmApplyRet, domain_hash: Option, diff --git a/fendermint/testing/contract-test/src/lib.rs b/fendermint/testing/contract-test/src/lib.rs index 060c532af6..4ed529025b 100644 --- a/fendermint/testing/contract-test/src/lib.rs +++ b/fendermint/testing/contract-test/src/lib.rs @@ -8,7 +8,7 @@ use fvm_shared::clock::ChainEpoch; use std::{future::Future, sync::Arc}; use fendermint_vm_genesis::Genesis; -use fendermint_vm_interpreter::fvm::cometbft::EndBlockUpdate; +use fendermint_vm_interpreter::fvm::PowerUpdates; use fendermint_vm_interpreter::genesis::{create_test_genesis_state, GenesisOutput}; use fendermint_vm_interpreter::{ fvm::{ @@ -66,7 +66,7 @@ where Message = FvmMessage, BeginOutput = FvmApplyRet, DeliverOutput = FvmApplyRet, - EndOutput = EndBlockUpdate, + EndOutput = PowerUpdates, >, { pub async fn new(interpreter: I, genesis: Genesis) -> anyhow::Result { diff --git a/fendermint/vm/interpreter/src/chain.rs b/fendermint/vm/interpreter/src/chain.rs index dc9a9b5534..67b84bb765 100644 --- a/fendermint/vm/interpreter/src/chain.rs +++ b/fendermint/vm/interpreter/src/chain.rs @@ -1,9 +1,9 @@ // Copyright 2022-2024 Protocol Labs // SPDX-License-Identifier: Apache-2.0, MIT -use crate::fvm::cometbft::EndBlockUpdate; -use crate::fvm::gas::{Gas, GasMarket}; +use crate::fvm::gas::GasMarket; use crate::fvm::state::ipc::GatewayCaller; -use crate::fvm::{topdown, FvmApplyRet}; +use crate::fvm::{topdown, FvmApplyRet, PowerUpdates}; +use crate::selector::{GasLimitSelector, MessageSelector}; use crate::{ fvm::state::FvmExecState, fvm::FvmMessage, @@ -116,7 +116,7 @@ where (chain_env, state): Self::State, mut msgs: Vec, ) -> anyhow::Result> { - msgs = messages_selection(msgs, state.gas_market().available().block_gas)?; + msgs = messages_selection(msgs, &state)?; // Collect resolved CIDs ready to be proposed from the pool. let ckpts = atomically(|| chain_env.checkpoint_pool.collect_resolved()).await; @@ -246,7 +246,7 @@ where Message = VerifiableMessage, DeliverOutput = SignedMessageApplyRes, State = FvmExecState, - EndOutput = EndBlockUpdate, + EndOutput = PowerUpdates, >, { // The state consists of the resolver pool, which this interpreter needs, and the rest of the @@ -429,9 +429,8 @@ where let (state, out) = self.inner.end(state).await?; // Update any component that needs to know about changes in the power table. - if !out.validators.0.is_empty() { + if !out.0.is_empty() { let power_updates = out - .validators .0 .iter() .map(|v| { @@ -550,135 +549,23 @@ fn relayed_bottom_up_ckpt_to_fvm( Ok(msg) } -fn signed_msgs_with_gas_limit(msgs: Vec) -> anyhow::Result> { - msgs.into_iter() +fn messages_selection( + msgs: Vec, + state: &FvmExecState, +) -> anyhow::Result> { + let mut signed = msgs + .into_iter() .map(|msg| match msg { - ChainMessage::Signed(inner) => { - let gas_limit = inner.message.gas_limit; - Ok((ChainMessage::Signed(inner), gas_limit)) - } + ChainMessage::Signed(inner) => Ok(inner), ChainMessage::Ipc(_) => Err(anyhow!("should not have ipc messages in user proposals")), }) - .collect::>>() -} - -/// Performs message selection: -/// - Order by gas limit in descending order -/// - Make sure total gas limit does not exceed the `total_gas_limit` parameter -fn messages_selection( - msgs: Vec, - total_gas_limit: Gas, -) -> anyhow::Result> { - let mut msgs_with_gas_limit = signed_msgs_with_gas_limit(msgs)?; - - // sort by gas limit descending - msgs_with_gas_limit.sort_by(|a, b| b.1.cmp(&a.1)); - - let mut total_gas_limit_consumed = 0; - let mut msgs = vec![]; - for (msg, gas_limit) in msgs_with_gas_limit { - if total_gas_limit_consumed + gas_limit <= total_gas_limit { - msgs.push(msg); - total_gas_limit_consumed += gas_limit; - } else { - break; - } - } - - tracing::info!( - num_msgs = msgs.len(), - total_gas_limit, - "selected message under total gas limit" - ); - - Ok(msgs) -} - -#[cfg(test)] -mod tests { - use crate::chain::messages_selection; - use fendermint_vm_message::chain::ChainMessage; - use fendermint_vm_message::signed::SignedMessage; - use quickcheck::Arbitrary; - use rand::random; - - #[test] - fn test_message_selection_partial_selected() { - let mut gen = quickcheck::Gen::new(100); - - let mut total = 0; - let msgs_len = 100; - - let messages = (0..msgs_len) - .map(|_| { - let mut msg = SignedMessage::arbitrary(&mut gen); - - msg.message.gas_limit = random::() % fvm_shared::BLOCK_GAS_LIMIT; - total += msg.message.gas_limit; + .collect::>>()?; - ChainMessage::Signed(msg) - }) - .collect::>(); - - let selected = messages_selection(messages, total / 2).unwrap(); - let selected_len = selected.len(); - let mut selected_total = 0; - let mut max = u64::MAX; - - for s in selected { - if let ChainMessage::Signed(signed) = s { - selected_total += signed.message.gas_limit; - assert!( - max >= signed.message.gas_limit, - "gas limit should be sorted descending" - ); - max = signed.message.gas_limit; - } else { - unreachable!("should be all signed messages") - } - } - - assert!(selected_total <= total / 2, "should not exceed gas limit"); - assert!(selected_len <= msgs_len, "not full selection"); + // currently only one selector, we can potentially extend to more selectors + let selectors = vec![GasLimitSelector {}]; + for s in selectors { + signed = s.select_messages(state, signed) } - #[test] - fn test_message_selection_all_selected() { - let mut gen = quickcheck::Gen::new(100); - - let mut total = 0; - let msgs_len = 100; - - let messages = (0..msgs_len) - .map(|_| { - let mut msg = SignedMessage::arbitrary(&mut gen); - - msg.message.gas_limit = random::() % fvm_shared::BLOCK_GAS_LIMIT; - total += msg.message.gas_limit; - - ChainMessage::Signed(msg) - }) - .collect::>(); - - let selected = messages_selection(messages, total).unwrap(); - let selected_len = selected.len(); - let mut selected_total = 0; - let mut max = u64::MAX; - - for s in selected { - if let ChainMessage::Signed(signed) = s { - selected_total += signed.message.gas_limit; - assert!( - max >= signed.message.gas_limit, - "gas limit should be sorted descending" - ); - max = signed.message.gas_limit; - } else { - unreachable!("should be all signed messages") - } - } - - assert_eq!(selected_total, total, "should not exceed gas limit"); - assert_eq!(selected_len, msgs_len, "not full selection"); - } + Ok(signed.into_iter().map(ChainMessage::Signed).collect()) } diff --git a/fendermint/vm/interpreter/src/fvm/cometbft.rs b/fendermint/vm/interpreter/src/fvm/cometbft.rs deleted file mode 100644 index daa7333072..0000000000 --- a/fendermint/vm/interpreter/src/fvm/cometbft.rs +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2022-2024 Protocol Labs -// SPDX-License-Identifier: Apache-2.0, MIT - -use crate::fvm::state::FvmExecState; -use crate::fvm::{FvmMessageInterpreter, PowerUpdates}; -use anyhow::Context; -use fendermint_vm_genesis::{Power, Validator}; -use fvm_ipld_blockstore::Blockstore; -use tendermint_rpc::Client; - -/// The end block update for cometbft -pub struct EndBlockUpdate { - pub consensus: Option, - pub validators: PowerUpdates, -} - -/// Potential updates to cometbft consensus parameters. Currently only block `max_gas` needs to be -/// updated, but in the future, more parameter could be updated. -pub struct ConsensusBlockUpdate { - max_gas: Option, -} - -/// Convert validator power to tendermint validator update. -/// TODO: the import is quite strange, `Validator` and `Power` are imported from `genesis` crate, -/// TODO: which should be from a `type` or `validator` crate. -pub fn to_validator_updates( - validators: Vec>, -) -> anyhow::Result> { - let mut updates = vec![]; - for v in validators { - updates.push(tendermint::validator::Update { - pub_key: tendermint::PublicKey::try_from(v.public_key)?, - power: tendermint::vote::Power::try_from(v.power.0)?, - }); - } - Ok(updates) -} - -impl TryFrom for tendermint::abci::response::EndBlock { - type Error = anyhow::Error; - - fn try_from(value: EndBlockUpdate) -> Result { - let validator_updates = to_validator_updates(value.validators.0) - .context("failed to convert validator updates")?; - - Ok(tendermint::abci::response::EndBlock { - validator_updates, - consensus_param_updates: value.consensus, - events: Vec::new(), // TODO: Events from epoch transitions? - }) - } -} - -impl FvmMessageInterpreter -where - DB: Blockstore + Clone + 'static + Send + Sync, - TC: Client + Clone + Send + Sync + 'static, -{ - pub(crate) async fn update_cometbft_consensus_params( - &self, - state: &mut FvmExecState, - end_block: &mut EndBlockUpdate, - ) -> anyhow::Result<()> { - let mut updates = ConsensusBlockUpdate::empty(); - - state.gas_market().process_consensus_update(&mut updates); - - if !updates.is_some() { - return Ok(()); - } - - let params = self - .client - .consensus_params(tendermint::block::Height::try_from(state.block_height())?) - .await? - .consensus_params; - end_block.with_consensus(updates.apply(params)); - - Ok(()) - } -} - -impl ConsensusBlockUpdate { - pub fn empty() -> Self { - Self { max_gas: None } - } - - pub fn is_some(&self) -> bool { - self.max_gas.is_some() - } - - pub fn apply(self, mut params: tendermint::consensus::Params) -> tendermint::consensus::Params { - if let Some(ref max_gas) = self.max_gas { - params.block.max_gas = *max_gas as i64; - } - params - } - - pub fn process_block_size(&mut self, block_gas_limit: u64) { - self.max_gas = Some(block_gas_limit); - } -} - -impl EndBlockUpdate { - pub fn new(power: PowerUpdates) -> Self { - Self { - validators: power, - consensus: None, - } - } - - pub fn with_consensus(&mut self, params: tendermint::consensus::Params) { - self.consensus = Some(params) - } -} diff --git a/fendermint/vm/interpreter/src/fvm/exec.rs b/fendermint/vm/interpreter/src/fvm/exec.rs index 619b278aa1..cce99378ca 100644 --- a/fendermint/vm/interpreter/src/fvm/exec.rs +++ b/fendermint/vm/interpreter/src/fvm/exec.rs @@ -12,7 +12,6 @@ use fvm_shared::{address::Address, ActorID, MethodNum, BLOCK_GAS_LIMIT}; use ipc_observability::{emit, measure_time, observe::TracingError, Traceable}; use tendermint_rpc::Client; -use crate::fvm::cometbft::EndBlockUpdate; use crate::fvm::gas::{GasMarket, GasUtilization}; use crate::ExecInterpreter; @@ -50,7 +49,7 @@ where /// Return validator power updates. /// Currently ignoring events as there aren't any emitted by the smart contract, /// but keep in mind that if there were, those would have to be propagated. - type EndOutput = EndBlockUpdate; + type EndOutput = PowerUpdates; async fn begin( &self, @@ -263,11 +262,6 @@ where PowerUpdates::default() }; - // process cometbft end block updates - let mut end_block = EndBlockUpdate::new(updates); - self.update_cometbft_consensus_params(&mut state, &mut end_block) - .await?; - - Ok((state, end_block)) + Ok((state, updates)) } } diff --git a/fendermint/vm/interpreter/src/fvm/gas/actor.rs b/fendermint/vm/interpreter/src/fvm/gas/actor.rs index 2fdd78d365..4e576ba366 100644 --- a/fendermint/vm/interpreter/src/fvm/gas/actor.rs +++ b/fendermint/vm/interpreter/src/fvm/gas/actor.rs @@ -5,7 +5,6 @@ use crate::fvm::gas::{Available, Gas, GasMarket, GasUtilization}; use crate::fvm::FvmMessage; use anyhow::Context; -use crate::fvm::cometbft::ConsensusBlockUpdate; use fendermint_actor_gas_market::{GasMarketReading, SetConstants}; use fendermint_crypto::PublicKey; use fendermint_vm_actor_interface::eam::EthAddress; @@ -32,10 +31,6 @@ pub struct ActorGasMarket { impl GasMarket for ActorGasMarket { type Constant = SetConstants; - fn get_constants(&self) -> anyhow::Result { - todo!() - } - fn set_constants(&mut self, constants: Self::Constant) { self.constant_update = Some(constants); } @@ -95,10 +90,8 @@ impl ActorGasMarket { }) } - pub fn process_consensus_update(&self, update: &mut ConsensusBlockUpdate) { - if let Some(ref set_constant) = self.constant_update { - update.process_block_size(set_constant.block_gas_limit); - } + pub fn take_constant_update(&mut self) -> Option { + self.constant_update.take() } pub fn commit( diff --git a/fendermint/vm/interpreter/src/fvm/gas/mod.rs b/fendermint/vm/interpreter/src/fvm/gas/mod.rs index 346f57c947..e372deb395 100644 --- a/fendermint/vm/interpreter/src/fvm/gas/mod.rs +++ b/fendermint/vm/interpreter/src/fvm/gas/mod.rs @@ -22,9 +22,6 @@ pub trait GasMarket { /// The constant parameters that determines the readings of gas market, such as block gas limit. type Constant; - #[allow(dead_code)] - fn get_constants(&self) -> anyhow::Result; - /// Update the constants of the gas market. If the gas market is actor based, then it's recommended /// to flush at EndBlock. #[allow(dead_code)] diff --git a/fendermint/vm/interpreter/src/fvm/mod.rs b/fendermint/vm/interpreter/src/fvm/mod.rs index 1c14ab2ebd..73296813ea 100644 --- a/fendermint/vm/interpreter/src/fvm/mod.rs +++ b/fendermint/vm/interpreter/src/fvm/mod.rs @@ -14,7 +14,6 @@ pub mod upgrades; #[cfg(any(test, feature = "bundle"))] pub mod bundle; -pub mod cometbft; pub(crate) mod gas; pub(crate) mod topdown; diff --git a/fendermint/vm/interpreter/src/lib.rs b/fendermint/vm/interpreter/src/lib.rs index 40868b6cdf..34b3e61b05 100644 --- a/fendermint/vm/interpreter/src/lib.rs +++ b/fendermint/vm/interpreter/src/lib.rs @@ -10,6 +10,7 @@ pub mod signed; #[cfg(feature = "arb")] mod arb; +mod selector; /// Prepare and process transaction proposals. #[async_trait] diff --git a/fendermint/vm/interpreter/src/selector.rs b/fendermint/vm/interpreter/src/selector.rs new file mode 100644 index 0000000000..e3ab7197cb --- /dev/null +++ b/fendermint/vm/interpreter/src/selector.rs @@ -0,0 +1,46 @@ +// Copyright 2022-2024 Protocol Labs +// SPDX-License-Identifier: Apache-2.0, MIT + +//! Gas related message selection + +use crate::fvm::gas::GasMarket; +use crate::fvm::state::FvmExecState; +use fendermint_vm_message::signed::SignedMessage; +use fvm_ipld_blockstore::Blockstore; + +/// Implement this trait to perform message selection +pub trait MessageSelector { + fn select_messages( + &self, + state: &FvmExecState, + msgs: Vec, + ) -> Vec; +} + +pub(crate) struct GasLimitSelector; + +impl MessageSelector for GasLimitSelector { + fn select_messages( + &self, + state: &FvmExecState, + mut msgs: Vec, + ) -> Vec { + let total_gas_limit = state.gas_market().available().block_gas; + + // sort by gas limit descending + msgs.sort_by(|a, b| b.message.gas_limit.cmp(&a.message.gas_limit)); + + let mut total_gas_limit_consumed = 0; + let mut selected = vec![]; + for msg in msgs { + if total_gas_limit_consumed + msg.message.gas_limit <= total_gas_limit { + total_gas_limit_consumed += msg.message.gas_limit; + selected.push(msg); + } else { + break; + } + } + + selected + } +} From 7fc7df0366e758a2ec89462f7b96a8c31279dc1f Mon Sep 17 00:00:00 2001 From: cryptoAtwill Date: Tue, 27 Aug 2024 17:48:02 +0800 Subject: [PATCH 59/69] rename methods --- fendermint/vm/interpreter/src/fvm/gas/actor.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fendermint/vm/interpreter/src/fvm/gas/actor.rs b/fendermint/vm/interpreter/src/fvm/gas/actor.rs index 4e576ba366..cc5a988c7b 100644 --- a/fendermint/vm/interpreter/src/fvm/gas/actor.rs +++ b/fendermint/vm/interpreter/src/fvm/gas/actor.rs @@ -132,7 +132,7 @@ impl ActorGasMarket { gas_fee_cap: Default::default(), gas_premium: Default::default(), }; - self.exec_msg_implicitly(msg, executor)?; + self.apply_implicit_message(msg, executor)?; Ok(()) } @@ -159,7 +159,7 @@ impl ActorGasMarket { gas_fee_cap: Default::default(), gas_premium: Default::default(), }; - self.exec_msg_implicitly(msg, executor)?; + self.apply_implicit_message(msg, executor)?; Ok(()) } @@ -188,11 +188,11 @@ impl ActorGasMarket { gas_premium: Default::default(), }; - self.exec_msg_implicitly(msg, executor)?; + self.apply_implicit_message(msg, executor)?; Ok(()) } - fn exec_msg_implicitly( + fn apply_implicit_message( &self, msg: FvmMessage, executor: &mut E, From ae6277b4efa05af6b5670de424a41b398ddb0bcf Mon Sep 17 00:00:00 2001 From: cryptoAtwill Date: Tue, 27 Aug 2024 20:47:05 +0800 Subject: [PATCH 60/69] use read only block store --- fendermint/app/src/app.rs | 29 +++++++++----------------- fendermint/vm/interpreter/src/chain.rs | 3 ++- 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/fendermint/app/src/app.rs b/fendermint/app/src/app.rs index c1a193ebd4..6d2095267f 100644 --- a/fendermint/app/src/app.rs +++ b/fendermint/app/src/app.rs @@ -414,7 +414,10 @@ where S::Namespace: Sync + Send, DB: KVWritable + KVReadable + Clone + Send + Sync + 'static, SS: Blockstore + Clone + Send + Sync + 'static, - I: ProposalInterpreter), Message = Vec>, + I: ProposalInterpreter< + State = (ChainEnv, FvmExecState>>), + Message = Vec, + >, I: ExecInterpreter< State = (ChainEnv, FvmExecState), Message = Vec, @@ -623,15 +626,9 @@ where ); let txs = request.txs.into_iter().map(|tx| tx.to_vec()).collect(); - let (state_params, block_height) = - self.state_params_at_height(request.height.value().into())?; - let state = FvmExecState::new( - self.state_store_clone(), - self.multi_engine.as_ref(), - block_height as ChainEpoch, - state_params, - ) - .context("error creating new state")?; + let state = self + .new_read_only_exec_state()? + .ok_or_else(|| anyhow!("exec state should be present"))?; let txs = self .interpreter @@ -668,15 +665,9 @@ where let size_txs = txs.iter().map(|tx| tx.len()).sum::(); let num_txs = txs.len(); - let (state_params, block_height) = - self.state_params_at_height(request.height.value().into())?; - let state = FvmExecState::new( - self.state_store_clone(), - self.multi_engine.as_ref(), - block_height as ChainEpoch, - state_params, - ) - .context("error creating new state")?; + let state = self + .new_read_only_exec_state()? + .ok_or_else(|| anyhow!("exec state should be present"))?; let accept = self .interpreter diff --git a/fendermint/vm/interpreter/src/chain.rs b/fendermint/vm/interpreter/src/chain.rs index 67b84bb765..1d63bef784 100644 --- a/fendermint/vm/interpreter/src/chain.rs +++ b/fendermint/vm/interpreter/src/chain.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0, MIT use crate::fvm::gas::GasMarket; use crate::fvm::state::ipc::GatewayCaller; +use crate::fvm::store::ReadOnlyBlockstore; use crate::fvm::{topdown, FvmApplyRet, PowerUpdates}; use crate::selector::{GasLimitSelector, MessageSelector}; use crate::{ @@ -104,7 +105,7 @@ where DB: Blockstore + Clone + 'static + Send + Sync, I: Sync + Send, { - type State = (ChainEnv, FvmExecState); + type State = (ChainEnv, FvmExecState>>); type Message = ChainMessage; /// Check whether there are any "ready" messages in the IPLD resolution mempool which can be appended to the proposal. From 46f8e2b640a5d299286370990753ec8ac9250743 Mon Sep 17 00:00:00 2001 From: cryptoAtwill Date: Wed, 11 Sep 2024 17:46:00 +0800 Subject: [PATCH 61/69] add more tests --- Cargo.lock | 1 + fendermint/testing/contract-test/Cargo.toml | 2 + fendermint/testing/contract-test/src/lib.rs | 32 ++- .../testing/contract-test/tests/gas_market.rs | 258 ++++++++++++++++++ .../vm/interpreter/src/fvm/gas/actor.rs | 13 +- fendermint/vm/interpreter/src/fvm/mod.rs | 2 +- 6 files changed, 302 insertions(+), 6 deletions(-) create mode 100644 fendermint/testing/contract-test/tests/gas_market.rs diff --git a/Cargo.lock b/Cargo.lock index 16aab6317b..1388824c7e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2986,6 +2986,7 @@ dependencies = [ "bytes", "cid", "ethers", + "fendermint_actor_gas_market", "fendermint_crypto", "fendermint_rpc", "fendermint_testing", diff --git a/fendermint/testing/contract-test/Cargo.toml b/fendermint/testing/contract-test/Cargo.toml index 109e5fae18..643bc26534 100644 --- a/fendermint/testing/contract-test/Cargo.toml +++ b/fendermint/testing/contract-test/Cargo.toml @@ -44,3 +44,5 @@ lazy_static = { workspace = true } bytes = { workspace = true } fvm_ipld_encoding = { workspace = true } multihash = { workspace = true } +fvm = { workspace = true, features = ["testing"] } +fendermint_actor_gas_market = { path = "../../actors/gas_market" } \ No newline at end of file diff --git a/fendermint/testing/contract-test/src/lib.rs b/fendermint/testing/contract-test/src/lib.rs index 4ed529025b..e5a0eeac2c 100644 --- a/fendermint/testing/contract-test/src/lib.rs +++ b/fendermint/testing/contract-test/src/lib.rs @@ -20,6 +20,7 @@ use fendermint_vm_interpreter::{ ExecInterpreter, }; use fvm::engine::MultiEngine; +use fendermint_crypto::PublicKey; pub mod ipc; @@ -96,7 +97,7 @@ where } /// Take the execution state, update it, put it back, return the output. - async fn modify_exec_state(&self, f: F) -> anyhow::Result + pub async fn modify_exec_state(&self, f: F) -> anyhow::Result where F: FnOnce(FvmExecState) -> R, R: Future, T)>>, @@ -125,6 +126,10 @@ where } pub async fn begin_block(&self, block_height: ChainEpoch) -> Result<()> { + self.begin_block_with_validator(block_height, None).await + } + + pub async fn begin_block_with_validator(&self, block_height: ChainEpoch, maybe_validator: Option) -> Result<()> { let mut block_hash: [u8; 32] = [0; 32]; let _ = block_hash.as_mut().write_i64::(block_height); @@ -132,9 +137,12 @@ where let mut state_params = self.state_params.clone(); state_params.timestamp = Timestamp(block_height as u64); - let state = FvmExecState::new(db, self.multi_engine.as_ref(), block_height, state_params) + let mut state = FvmExecState::new(db, self.multi_engine.as_ref(), block_height, state_params) .context("error creating new state")? .with_block_hash(block_hash); + if let Some(validator) = maybe_validator { + state = state.with_validator(validator); + } self.put_exec_state(state).await; @@ -146,6 +154,26 @@ where Ok(()) } + + pub async fn execute_msgs(&self, msgs: Vec) -> Result<()> { + let _ret = self + .modify_exec_state(|mut s| async { + for msg in msgs { + let (a, out) = self.interpreter.deliver(s, msg).await?; + if let Some(e) = out.apply_ret.failure_info { + println!("failed: {}", e); + return Err(anyhow!("err in msg deliver")); + } + s = a; + } + Ok((s, ())) + }) + .await + .context("execute msgs failed")?; + + Ok(()) + } + pub async fn end_block(&self, _block_height: ChainEpoch) -> Result<()> { let _ret = self .modify_exec_state(|s| self.interpreter.end(s)) diff --git a/fendermint/testing/contract-test/tests/gas_market.rs b/fendermint/testing/contract-test/tests/gas_market.rs new file mode 100644 index 0000000000..0e0be5260c --- /dev/null +++ b/fendermint/testing/contract-test/tests/gas_market.rs @@ -0,0 +1,258 @@ +// Copyright 2022-2024 Protocol Labs +// SPDX-License-Identifier: Apache-2.0, MIT + +mod staking; + +use anyhow::Context; +use async_trait::async_trait; +use fendermint_actor_gas_market::{GasMarketReading, SetConstants}; +use fendermint_contract_test::Tester; +use fendermint_crypto::{PublicKey, SecretKey}; +use fendermint_vm_actor_interface::gas::GAS_MARKET_ACTOR_ADDR; +use fendermint_vm_actor_interface::system; +use fendermint_vm_core::Timestamp; +use fendermint_vm_genesis::{Account, Actor, ActorMeta, Genesis, PermissionMode, SignerAddr}; +use fendermint_vm_interpreter::fvm::gas::GasMarket; +use fendermint_vm_interpreter::fvm::state::FvmExecState; +use fendermint_vm_interpreter::fvm::store::memory::MemoryBlockstore; +use fendermint_vm_interpreter::fvm::upgrades::UpgradeScheduler; +use fendermint_vm_interpreter::fvm::FvmMessageInterpreter; +use fvm_shared::address::Address; +use fvm_shared::bigint::Zero; +use fvm_shared::clock::ChainEpoch; +use fvm_shared::econ::TokenAmount; +use fvm_shared::message::Message; +use fvm_shared::version::NetworkVersion; +use lazy_static::lazy_static; +use rand::rngs::StdRng; +use rand::SeedableRng; +use tendermint_rpc::Client; +use fendermint_vm_actor_interface::eam::EthAddress; + +lazy_static! { + static ref ADDR: Address = + Address::new_secp256k1(&my_secret_key().public_key().serialize()).unwrap(); + static ref ADDR2: Address = + Address::new_secp256k1(&my_secret_key().public_key().serialize()).unwrap(); +} +const CHAIN_NAME: &str = "mychain"; +type I = FvmMessageInterpreter; + +// returns a seeded secret key which is guaranteed to be the same every time +fn my_secret_key() -> SecretKey { + SecretKey::random(&mut StdRng::seed_from_u64(123)) +} + +/// Creates a default tester with validator public key +async fn default_tester() -> (Tester, PublicKey) { + let validator = my_secret_key().public_key(); + let upgrade_scheduler = UpgradeScheduler::new(); + + let interpreter: FvmMessageInterpreter = + FvmMessageInterpreter::new(NeverCallClient, None, 1.05, 1.05, false, upgrade_scheduler); + + let genesis = Genesis { + chain_name: CHAIN_NAME.to_string(), + timestamp: Timestamp(0), + network_version: NetworkVersion::V21, + base_fee: TokenAmount::zero(), + power_scale: 0, + validators: Vec::new(), + accounts: vec![ + Actor { + meta: ActorMeta::Account(Account { + owner: SignerAddr(*ADDR), + }), + balance: TokenAmount::from_whole(100), + }, + Actor { + meta: ActorMeta::Account(Account { + owner: SignerAddr(*ADDR2), + }), + balance: TokenAmount::from_whole(10), + }, + ], + eam_permission_mode: PermissionMode::Unrestricted, + ipc: None, + }; + (Tester::new(interpreter, genesis).await.unwrap(), validator) +} + +#[tokio::test] +async fn test_gas_market_base_fee_oscillation() { + let (mut tester, _) = default_tester().await; + + let num_msgs = 10; + let total_gas_limit = 6178630; + let base_gas_limit = total_gas_limit / num_msgs; + + let mut gas_constants = SetConstants::default(); + gas_constants.block_gas_limit = total_gas_limit; + + let messages = (0..num_msgs) + .map(|i| Message { + version: 0, + from: ADDR.clone(), + to: Address::new_id(10), + sequence: i, + value: TokenAmount::from_atto(1), + method_num: 0, + params: Default::default(), + gas_limit: base_gas_limit, + gas_fee_cap: Default::default(), + gas_premium: TokenAmount::from_atto(1), + }) + .collect::>(); + + // iterate over all the upgrades + let height = 1; + tester.begin_block(height).await.unwrap(); + tester + .modify_exec_state(|mut state| async { + state.gas_market_mut().set_constants(gas_constants); + Ok((state, ())) + }) + .await + .unwrap(); + tester.end_block(height).await.unwrap(); + tester.commit().await.unwrap(); + + let height = 2; + tester.begin_block(height).await.unwrap(); + let before_reading = tester + .modify_exec_state(|mut state| async { + let reading = current_reading(&mut state, height)?; + Ok((state, reading)) + }) + .await + .unwrap(); + tester.execute_msgs(messages).await.unwrap(); + tester.end_block(height).await.unwrap(); + tester.commit().await.unwrap(); + + let height = 3; + tester.begin_block(height).await.unwrap(); + let post_full_block_reading = tester + .modify_exec_state(|mut state| async { + let reading = current_reading(&mut state, height)?; + Ok((state, reading)) + }) + .await + .unwrap(); + tester.end_block(height).await.unwrap(); + tester.commit().await.unwrap(); + assert!( + before_reading.base_fee < post_full_block_reading.base_fee, + "base fee should have increased" + ); + + let height = 4; + tester.begin_block(height).await.unwrap(); + let post_empty_block_reading = tester + .modify_exec_state(|mut state| async { + let reading = current_reading(&mut state, height)?; + Ok((state, reading)) + }) + .await + .unwrap(); + tester.end_block(height).await.unwrap(); + tester.commit().await.unwrap(); + assert!( + post_empty_block_reading.base_fee < post_full_block_reading.base_fee, + "base fee should have decreased" + ); +} + +#[tokio::test] +async fn test_gas_market_premium_distribution() { + let (mut tester, validator) = default_tester().await; + let evm_address = Address::from(EthAddress::new_secp256k1(&validator.serialize()).unwrap()); + + let num_msgs = 10; + let total_gas_limit = 62306300; + let premium = 1; + let base_gas_limit = total_gas_limit / num_msgs; + + let messages = (0..num_msgs) + .map(|i| Message { + version: 0, + from: ADDR.clone(), + to: ADDR2.clone(), + sequence: i, + value: TokenAmount::from_atto(1), + method_num: 0, + params: Default::default(), + gas_limit: base_gas_limit, + gas_fee_cap: TokenAmount::from_atto(base_gas_limit), + gas_premium: TokenAmount::from_atto(premium), + }) + .collect::>(); + + // iterate over all the upgrades + let height = 1; + tester.begin_block_with_validator(height, Some(validator)).await.unwrap(); + let initial_balance = tester + .modify_exec_state(|state| async { + let tree = state.state_tree(); + let balance = tree.get_actor_by_address(&evm_address)?.map(|v| v.balance).unwrap_or(TokenAmount::zero()); + Ok((state, balance)) + }) + .await + .unwrap(); + assert_eq!(initial_balance, TokenAmount::zero()); + + tester.execute_msgs(messages).await.unwrap(); + tester.end_block(height).await.unwrap(); + let final_balance = tester + .modify_exec_state(|state| async { + let tree = state.state_tree(); + let balance = tree.get_actor_by_address(&evm_address)?.map(|v| v.balance).unwrap_or(TokenAmount::zero()); + Ok((state, balance)) + }) + .await + .unwrap(); + tester.commit().await.unwrap(); + + assert!(final_balance > initial_balance, "validator balance should have increased") +} + +pub fn current_reading( + state: &mut FvmExecState, + block_height: ChainEpoch, +) -> anyhow::Result { + let msg = Message { + from: system::SYSTEM_ACTOR_ADDR, + to: GAS_MARKET_ACTOR_ADDR, + sequence: block_height as u64, + // exclude this from gas restriction + gas_limit: i64::MAX as u64, + method_num: fendermint_actor_gas_market::Method::CurrentReading as u64, + params: fvm_ipld_encoding::RawBytes::default(), + value: Default::default(), + version: Default::default(), + gas_fee_cap: Default::default(), + gas_premium: Default::default(), + }; + let (apply_ret, _) = state.execute_implicit(msg)?; + + if let Some(err) = apply_ret.failure_info { + anyhow::bail!("failed to read gas market state: {}", err); + } + + let r = fvm_ipld_encoding::from_slice::(&apply_ret.msg_receipt.return_data) + .context("failed to parse gas market readying")?; + Ok(r) +} + +#[derive(Clone)] +struct NeverCallClient; + +#[async_trait] +impl Client for NeverCallClient { + async fn perform(&self, _request: R) -> Result + where + R: tendermint_rpc::SimpleRequest, + { + todo!() + } +} diff --git a/fendermint/vm/interpreter/src/fvm/gas/actor.rs b/fendermint/vm/interpreter/src/fvm/gas/actor.rs index cc5a988c7b..e0da5b38c2 100644 --- a/fendermint/vm/interpreter/src/fvm/gas/actor.rs +++ b/fendermint/vm/interpreter/src/fvm/gas/actor.rs @@ -53,10 +53,10 @@ impl GasMarket for ActorGasMarket { } impl ActorGasMarket { - pub fn create( + pub fn current_reading( executor: &mut E, block_height: ChainEpoch, - ) -> anyhow::Result { + ) -> anyhow::Result { let msg = FvmMessage { from: system::SYSTEM_ACTOR_ADDR, to: GAS_MARKET_ACTOR_ADDR, @@ -78,10 +78,17 @@ impl ActorGasMarket { anyhow::bail!("failed to read gas market state: {}", err); } - let reading = + let r = fvm_ipld_encoding::from_slice::(&apply_ret.msg_receipt.return_data) .context("failed to parse gas market readying")?; + Ok(r) + } + pub fn create( + executor: &mut E, + block_height: ChainEpoch, + ) -> anyhow::Result { + let reading = Self::current_reading(executor, block_height)?; Ok(Self { gas_premium: TokenAmount::from_atto(0), block_gas_limit: reading.block_gas_limit, diff --git a/fendermint/vm/interpreter/src/fvm/mod.rs b/fendermint/vm/interpreter/src/fvm/mod.rs index 73296813ea..c45d8039ce 100644 --- a/fendermint/vm/interpreter/src/fvm/mod.rs +++ b/fendermint/vm/interpreter/src/fvm/mod.rs @@ -14,7 +14,7 @@ pub mod upgrades; #[cfg(any(test, feature = "bundle"))] pub mod bundle; -pub(crate) mod gas; +pub mod gas; pub(crate) mod topdown; pub use check::FvmCheckRet; From f681494e589e799324cbbf8dca1c09023ff9ab07 Mon Sep 17 00:00:00 2001 From: cryptoAtwill Date: Wed, 11 Sep 2024 18:06:18 +0800 Subject: [PATCH 62/69] fmt --- fendermint/testing/contract-test/src/lib.rs | 22 ++++++++++--------- .../testing/contract-test/tests/gas_market.rs | 22 ++++++++++++++----- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/fendermint/testing/contract-test/src/lib.rs b/fendermint/testing/contract-test/src/lib.rs index e5a0eeac2c..cd8e1ded93 100644 --- a/fendermint/testing/contract-test/src/lib.rs +++ b/fendermint/testing/contract-test/src/lib.rs @@ -7,6 +7,7 @@ use fendermint_vm_core::Timestamp; use fvm_shared::clock::ChainEpoch; use std::{future::Future, sync::Arc}; +use fendermint_crypto::PublicKey; use fendermint_vm_genesis::Genesis; use fendermint_vm_interpreter::fvm::PowerUpdates; use fendermint_vm_interpreter::genesis::{create_test_genesis_state, GenesisOutput}; @@ -20,7 +21,6 @@ use fendermint_vm_interpreter::{ ExecInterpreter, }; use fvm::engine::MultiEngine; -use fendermint_crypto::PublicKey; pub mod ipc; @@ -129,7 +129,11 @@ where self.begin_block_with_validator(block_height, None).await } - pub async fn begin_block_with_validator(&self, block_height: ChainEpoch, maybe_validator: Option) -> Result<()> { + pub async fn begin_block_with_validator( + &self, + block_height: ChainEpoch, + maybe_validator: Option, + ) -> Result<()> { let mut block_hash: [u8; 32] = [0; 32]; let _ = block_hash.as_mut().write_i64::(block_height); @@ -137,9 +141,10 @@ where let mut state_params = self.state_params.clone(); state_params.timestamp = Timestamp(block_height as u64); - let mut state = FvmExecState::new(db, self.multi_engine.as_ref(), block_height, state_params) - .context("error creating new state")? - .with_block_hash(block_hash); + let mut state = + FvmExecState::new(db, self.multi_engine.as_ref(), block_height, state_params) + .context("error creating new state")? + .with_block_hash(block_hash); if let Some(validator) = maybe_validator { state = state.with_validator(validator); } @@ -154,9 +159,8 @@ where Ok(()) } - pub async fn execute_msgs(&self, msgs: Vec) -> Result<()> { - let _ret = self + self .modify_exec_state(|mut s| async { for msg in msgs { let (a, out) = self.interpreter.deliver(s, msg).await?; @@ -169,9 +173,7 @@ where Ok((s, ())) }) .await - .context("execute msgs failed")?; - - Ok(()) + .context("execute msgs failed") } pub async fn end_block(&self, _block_height: ChainEpoch) -> Result<()> { diff --git a/fendermint/testing/contract-test/tests/gas_market.rs b/fendermint/testing/contract-test/tests/gas_market.rs index 0e0be5260c..8070a8f525 100644 --- a/fendermint/testing/contract-test/tests/gas_market.rs +++ b/fendermint/testing/contract-test/tests/gas_market.rs @@ -8,6 +8,7 @@ use async_trait::async_trait; use fendermint_actor_gas_market::{GasMarketReading, SetConstants}; use fendermint_contract_test::Tester; use fendermint_crypto::{PublicKey, SecretKey}; +use fendermint_vm_actor_interface::eam::EthAddress; use fendermint_vm_actor_interface::gas::GAS_MARKET_ACTOR_ADDR; use fendermint_vm_actor_interface::system; use fendermint_vm_core::Timestamp; @@ -27,7 +28,6 @@ use lazy_static::lazy_static; use rand::rngs::StdRng; use rand::SeedableRng; use tendermint_rpc::Client; -use fendermint_vm_actor_interface::eam::EthAddress; lazy_static! { static ref ADDR: Address = @@ -190,11 +190,17 @@ async fn test_gas_market_premium_distribution() { // iterate over all the upgrades let height = 1; - tester.begin_block_with_validator(height, Some(validator)).await.unwrap(); + tester + .begin_block_with_validator(height, Some(validator)) + .await + .unwrap(); let initial_balance = tester .modify_exec_state(|state| async { let tree = state.state_tree(); - let balance = tree.get_actor_by_address(&evm_address)?.map(|v| v.balance).unwrap_or(TokenAmount::zero()); + let balance = tree + .get_actor_by_address(&evm_address)? + .map(|v| v.balance) + .unwrap_or(TokenAmount::zero()); Ok((state, balance)) }) .await @@ -206,14 +212,20 @@ async fn test_gas_market_premium_distribution() { let final_balance = tester .modify_exec_state(|state| async { let tree = state.state_tree(); - let balance = tree.get_actor_by_address(&evm_address)?.map(|v| v.balance).unwrap_or(TokenAmount::zero()); + let balance = tree + .get_actor_by_address(&evm_address)? + .map(|v| v.balance) + .unwrap_or(TokenAmount::zero()); Ok((state, balance)) }) .await .unwrap(); tester.commit().await.unwrap(); - assert!(final_balance > initial_balance, "validator balance should have increased") + assert!( + final_balance > initial_balance, + "validator balance should have increased" + ) } pub fn current_reading( From 706e44e0c7e484b32ccab2c97870aeebe7a4cde2 Mon Sep 17 00:00:00 2001 From: cryptoAtwill Date: Thu, 12 Sep 2024 10:37:26 +0800 Subject: [PATCH 63/69] base fee rotation --- fendermint/actors/gas_market/src/lib.rs | 11 ++++++-- fendermint/testing/contract-test/src/lib.rs | 25 +++++++++-------- .../vm/interpreter/src/fvm/gas/actor.rs | 27 ++++++++++++------- fendermint/vm/interpreter/src/fvm/gas/mod.rs | 4 +++ .../vm/interpreter/src/fvm/state/exec.rs | 7 +++-- 5 files changed, 48 insertions(+), 26 deletions(-) diff --git a/fendermint/actors/gas_market/src/lib.rs b/fendermint/actors/gas_market/src/lib.rs index cbabdc202a..d9b316d584 100644 --- a/fendermint/actors/gas_market/src/lib.rs +++ b/fendermint/actors/gas_market/src/lib.rs @@ -53,6 +53,11 @@ pub struct BlockGasUtilization { pub block_gas_used: Gas, } +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone)] +pub struct BlockGasUtilizationRet { + pub base_fee: TokenAmount, +} + pub struct EIP1559GasMarketActor {} #[derive(FromPrimitive)] @@ -108,12 +113,14 @@ impl EIP1559GasMarketActor { fn update_utilization( rt: &impl Runtime, utilization: BlockGasUtilization, - ) -> Result<(), ActorError> { + ) -> Result { rt.validate_immediate_caller_is(std::iter::once(&SYSTEM_ACTOR_ADDR))?; rt.transaction(|st: &mut EIP1559GasState, _rt| { st.base_fee = st.next_base_fee(utilization.block_gas_used); - Ok(()) + Ok(BlockGasUtilizationRet { + base_fee: st.base_fee.clone(), + }) }) } } diff --git a/fendermint/testing/contract-test/src/lib.rs b/fendermint/testing/contract-test/src/lib.rs index cd8e1ded93..fb9d5236c5 100644 --- a/fendermint/testing/contract-test/src/lib.rs +++ b/fendermint/testing/contract-test/src/lib.rs @@ -160,20 +160,19 @@ where } pub async fn execute_msgs(&self, msgs: Vec) -> Result<()> { - self - .modify_exec_state(|mut s| async { - for msg in msgs { - let (a, out) = self.interpreter.deliver(s, msg).await?; - if let Some(e) = out.apply_ret.failure_info { - println!("failed: {}", e); - return Err(anyhow!("err in msg deliver")); - } - s = a; + self.modify_exec_state(|mut s| async { + for msg in msgs { + let (a, out) = self.interpreter.deliver(s, msg).await?; + if let Some(e) = out.apply_ret.failure_info { + println!("failed: {}", e); + return Err(anyhow!("err in msg deliver")); } - Ok((s, ())) - }) - .await - .context("execute msgs failed") + s = a; + } + Ok((s, ())) + }) + .await + .context("execute msgs failed") } pub async fn end_block(&self, _block_height: ChainEpoch) -> Result<()> { diff --git a/fendermint/vm/interpreter/src/fvm/gas/actor.rs b/fendermint/vm/interpreter/src/fvm/gas/actor.rs index e0da5b38c2..6efe355882 100644 --- a/fendermint/vm/interpreter/src/fvm/gas/actor.rs +++ b/fendermint/vm/interpreter/src/fvm/gas/actor.rs @@ -1,11 +1,11 @@ // Copyright 2022-2024 Protocol Labs // SPDX-License-Identifier: Apache-2.0, MIT -use crate::fvm::gas::{Available, Gas, GasMarket, GasUtilization}; +use crate::fvm::gas::{Available, CommitRet, Gas, GasMarket, GasUtilization}; use crate::fvm::FvmMessage; -use anyhow::Context; +use anyhow::{anyhow, Context}; -use fendermint_actor_gas_market::{GasMarketReading, SetConstants}; +use fendermint_actor_gas_market::{BlockGasUtilizationRet, GasMarketReading, SetConstants}; use fendermint_crypto::PublicKey; use fendermint_vm_actor_interface::eam::EthAddress; use fendermint_vm_actor_interface::gas::GAS_MARKET_ACTOR_ADDR; @@ -18,6 +18,8 @@ use fvm_shared::METHOD_SEND; #[derive(Default)] pub struct ActorGasMarket { + /// The base fee for fvm + base_fee: TokenAmount, /// The total gas premium for the miner gas_premium: TokenAmount, /// The block gas limit @@ -90,6 +92,7 @@ impl ActorGasMarket { ) -> anyhow::Result { let reading = Self::current_reading(executor, block_height)?; Ok(Self { + base_fee: reading.base_fee, gas_premium: TokenAmount::from_atto(0), block_gas_limit: reading.block_gas_limit, block_gas_used: 0, @@ -106,10 +109,10 @@ impl ActorGasMarket { executor: &mut E, block_height: ChainEpoch, validator: Option, - ) -> anyhow::Result<()> { + ) -> anyhow::Result { + self.distribute_reward(executor, block_height, validator)?; self.commit_constants(executor, block_height)?; - self.commit_utilization(executor, block_height)?; - self.distribute_reward(executor, block_height, validator) + self.commit_utilization(executor, block_height) } fn distribute_reward( @@ -175,7 +178,7 @@ impl ActorGasMarket { &self, executor: &mut E, block_height: ChainEpoch, - ) -> anyhow::Result<()> { + ) -> anyhow::Result { let block_gas_used = self.block_gas_used.min(self.block_gas_limit); let params = fvm_ipld_encoding::RawBytes::serialize( fendermint_actor_gas_market::BlockGasUtilization { block_gas_used }, @@ -195,8 +198,14 @@ impl ActorGasMarket { gas_premium: Default::default(), }; - self.apply_implicit_message(msg, executor)?; - Ok(()) + let apply_ret = self.apply_implicit_message(msg, executor)?; + let r = fvm_ipld_encoding::from_slice::( + &apply_ret.msg_receipt.return_data, + ) + .context("failed to parse gas utilization result")?; + Ok(CommitRet { + base_fee: r.base_fee, + }) } fn apply_implicit_message( diff --git a/fendermint/vm/interpreter/src/fvm/gas/mod.rs b/fendermint/vm/interpreter/src/fvm/gas/mod.rs index e372deb395..3d189276f1 100644 --- a/fendermint/vm/interpreter/src/fvm/gas/mod.rs +++ b/fendermint/vm/interpreter/src/fvm/gas/mod.rs @@ -12,6 +12,10 @@ pub struct Available { pub block_gas: Gas, } +pub struct CommitRet { + pub base_fee: TokenAmount, +} + pub struct GasUtilization { gas_used: Gas, gas_premium: TokenAmount, diff --git a/fendermint/vm/interpreter/src/fvm/state/exec.rs b/fendermint/vm/interpreter/src/fvm/state/exec.rs index 10497ce209..ea439486c7 100644 --- a/fendermint/vm/interpreter/src/fvm/state/exec.rs +++ b/fendermint/vm/interpreter/src/fvm/state/exec.rs @@ -300,8 +300,11 @@ where pub fn update_gas_market(&mut self) -> anyhow::Result<()> { let height = self.block_height(); - self.gas_market - .commit(&mut self.executor, height, self.validator_pubkey) + let ret = self + .gas_market + .commit(&mut self.executor, height, self.validator_pubkey)?; + self.params.base_fee = ret.base_fee; + Ok(()) } /// Update the circulating supply, effective from the next block. From 4ff965b9e55ae7f2fd2b88492bbecadc5abb71ad Mon Sep 17 00:00:00 2001 From: cryptoAtwill Date: Thu, 12 Sep 2024 14:21:31 +0800 Subject: [PATCH 64/69] more tests --- fendermint/testing/contract-test/src/lib.rs | 25 ++++--- .../testing/contract-test/tests/gas_market.rs | 67 ++++++++++++++++++- 2 files changed, 77 insertions(+), 15 deletions(-) diff --git a/fendermint/testing/contract-test/src/lib.rs b/fendermint/testing/contract-test/src/lib.rs index cd8e1ded93..fb9d5236c5 100644 --- a/fendermint/testing/contract-test/src/lib.rs +++ b/fendermint/testing/contract-test/src/lib.rs @@ -160,20 +160,19 @@ where } pub async fn execute_msgs(&self, msgs: Vec) -> Result<()> { - self - .modify_exec_state(|mut s| async { - for msg in msgs { - let (a, out) = self.interpreter.deliver(s, msg).await?; - if let Some(e) = out.apply_ret.failure_info { - println!("failed: {}", e); - return Err(anyhow!("err in msg deliver")); - } - s = a; + self.modify_exec_state(|mut s| async { + for msg in msgs { + let (a, out) = self.interpreter.deliver(s, msg).await?; + if let Some(e) = out.apply_ret.failure_info { + println!("failed: {}", e); + return Err(anyhow!("err in msg deliver")); } - Ok((s, ())) - }) - .await - .context("execute msgs failed") + s = a; + } + Ok((s, ())) + }) + .await + .context("execute msgs failed") } pub async fn end_block(&self, _block_height: ChainEpoch) -> Result<()> { diff --git a/fendermint/testing/contract-test/tests/gas_market.rs b/fendermint/testing/contract-test/tests/gas_market.rs index 8070a8f525..5695d08eb8 100644 --- a/fendermint/testing/contract-test/tests/gas_market.rs +++ b/fendermint/testing/contract-test/tests/gas_market.rs @@ -16,7 +16,7 @@ use fendermint_vm_genesis::{Account, Actor, ActorMeta, Genesis, PermissionMode, use fendermint_vm_interpreter::fvm::gas::GasMarket; use fendermint_vm_interpreter::fvm::state::FvmExecState; use fendermint_vm_interpreter::fvm::store::memory::MemoryBlockstore; -use fendermint_vm_interpreter::fvm::upgrades::UpgradeScheduler; +use fendermint_vm_interpreter::fvm::upgrades::{Upgrade, UpgradeScheduler}; use fendermint_vm_interpreter::fvm::FvmMessageInterpreter; use fvm_shared::address::Address; use fvm_shared::bigint::Zero; @@ -45,8 +45,14 @@ fn my_secret_key() -> SecretKey { /// Creates a default tester with validator public key async fn default_tester() -> (Tester, PublicKey) { + tester_with_upgrader(UpgradeScheduler::new()).await +} + +/// Creates a default tester with validator public key +async fn tester_with_upgrader( + upgrade_scheduler: UpgradeScheduler, +) -> (Tester, PublicKey) { let validator = my_secret_key().public_key(); - let upgrade_scheduler = UpgradeScheduler::new(); let interpreter: FvmMessageInterpreter = FvmMessageInterpreter::new(NeverCallClient, None, 1.05, 1.05, false, upgrade_scheduler); @@ -228,6 +234,63 @@ async fn test_gas_market_premium_distribution() { ) } +#[tokio::test] +async fn test_gas_market_upgrade() { + let mut upgrader = UpgradeScheduler::new(); + + let total_gas_limit = 100; + upgrader + .add( + Upgrade::new(CHAIN_NAME, 1, Some(1), |state| { + println!( + "[Upgrade at height {}] Update gas market params", + state.block_height() + ); + + let mut gas_constants = SetConstants::default(); + gas_constants.block_gas_limit = 100; + + state.gas_market_mut().set_constants(gas_constants); + + Ok(()) + }) + .unwrap(), + ) + .unwrap(); + + let (mut tester, _) = tester_with_upgrader(upgrader).await; + + let height = 1; + tester.begin_block(height).await.unwrap(); + let reading = tester + .modify_exec_state(|mut state| async { + let reading = current_reading(&mut state, height)?; + Ok((state, reading)) + }) + .await + .unwrap(); + assert_ne!( + reading.block_gas_limit, total_gas_limit, + "gas limit should not equal at start" + ); + tester.end_block(height).await.unwrap(); + tester.commit().await.unwrap(); + + let height = 2; + tester.begin_block(height).await.unwrap(); + let reading = tester + .modify_exec_state(|mut state| async { + let reading = current_reading(&mut state, height)?; + Ok((state, reading)) + }) + .await + .unwrap(); + assert_eq!( + reading.block_gas_limit, total_gas_limit, + "gas limit should equal after upgrade" + ); +} + pub fn current_reading( state: &mut FvmExecState, block_height: ChainEpoch, From 4b2eb9e15b682d91f1cac77f2d4c753eef42ce6f Mon Sep 17 00:00:00 2001 From: Sander Pick Date: Thu, 3 Oct 2024 07:57:51 -0700 Subject: [PATCH 65/69] fix: tracing deserialization (#1163) --- docs/fendermint/observability.md | 4 ++-- fendermint/app/settings/src/lib.rs | 6 +++++- ipc/observability/src/config.rs | 2 ++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/docs/fendermint/observability.md b/docs/fendermint/observability.md index 79ae7a93ac..255c3cf849 100644 --- a/docs/fendermint/observability.md +++ b/docs/fendermint/observability.md @@ -357,9 +357,9 @@ directory = "/path/to/log/directory" max_log_files = 5 # Number of files to keep after rotation rotation = "daily" # Options: minutely, hourly, daily, never ## Optional: filter events by domain -domain_filter = "Bottomup, Consenesus, Mpool, Execution, Topdown, TracingError" +domain_filter = ["Bottomup", "Consensus", "Mpool", "Execution", "Topdown", "System"] ## Optional: filter events by event name -events_filter = "ParentFinalityAcquired, ParentRpcCalled" +events_filter = ["ParentFinalityAcquired", "ParentRpcCalled"] ``` By configuring these options, you can control the behavior of metrics and tracing, enabling fine-grained monitoring and logging for your application. diff --git a/fendermint/app/settings/src/lib.rs b/fendermint/app/settings/src/lib.rs index 1230b3b818..566040dcb2 100644 --- a/fendermint/app/settings/src/lib.rs +++ b/fendermint/app/settings/src/lib.rs @@ -326,12 +326,16 @@ impl Settings { .ignore_empty(true) // otherwise "" will be parsed as a list item .try_parsing(true) // required for list separator .list_separator(",") // need to list keys explicitly below otherwise it can't pase simple `String` type + .with_list_parse_key("tracing.file.domain_filter") + .with_list_parse_key("tracing.file.events_filter") .with_list_parse_key("resolver.connection.external_addresses") .with_list_parse_key("resolver.discovery.static_addresses") .with_list_parse_key("resolver.membership.static_subnets") .with_list_parse_key("eth.cors.allowed_origins") .with_list_parse_key("eth.cors.allowed_methods") - .with_list_parse_key("eth.cors.allowed_headers"), + .with_list_parse_key("eth.cors.allowed_headers") + .with_list_parse_key("eth.tracing.file.domain_filter") + .with_list_parse_key("eth.tracing.file.events_filter"), )) // Set the home directory based on what was passed to the CLI, // so everything in the config can be relative to it. diff --git a/ipc/observability/src/config.rs b/ipc/observability/src/config.rs index 50973f767a..1a73fa905a 100644 --- a/ipc/observability/src/config.rs +++ b/ipc/observability/src/config.rs @@ -12,6 +12,7 @@ use tracing_subscriber::filter::EnvFilter; #[serde_as] #[derive(Debug, Deserialize, Clone, Default, strum::EnumString, strum::Display)] #[strum(serialize_all = "snake_case")] +#[serde(rename_all = "lowercase")] pub enum LogLevel { Off, Error, @@ -32,6 +33,7 @@ impl From for EnvFilter { #[serde_as] #[derive(Debug, Deserialize, Clone, strum::EnumString, strum::Display)] #[strum(serialize_all = "snake_case")] +#[serde(rename_all = "lowercase")] pub enum RotationKind { Minutely, Hourly, From 5b8c2da4c7256b060c958a2bb8271addab011145 Mon Sep 17 00:00:00 2001 From: raulk Date: Fri, 4 Oct 2024 16:58:25 +0100 Subject: [PATCH 66/69] fix Cargo.lock. --- Cargo.lock | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b5b4988e2e..1388824c7e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8807,12 +8807,6 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b" -[[package]] -name = "snap" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b" - [[package]] name = "snow" version = "0.9.6" From d0892f21a1d4c7e22106abe6786fb68477e3c37e Mon Sep 17 00:00:00 2001 From: raulk Date: Sat, 5 Oct 2024 01:12:17 +0100 Subject: [PATCH 67/69] fix(cli): subnet get-validator: add support for 0x Eth addrs. (#1164) --- ipc/cli/src/commands/subnet/validator.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/ipc/cli/src/commands/subnet/validator.rs b/ipc/cli/src/commands/subnet/validator.rs index f73f5669f6..ad5b79b8d8 100644 --- a/ipc/cli/src/commands/subnet/validator.rs +++ b/ipc/cli/src/commands/subnet/validator.rs @@ -2,15 +2,15 @@ // SPDX-License-Identifier: MIT //! Get the validator information +use crate::{get_ipc_provider, CommandLineHandler, GlobalArguments}; use async_trait::async_trait; use clap::Args; use fvm_shared::address::Address; use ipc_api::subnet_id::SubnetID; +use ipc_types::EthAddress; use std::fmt::Debug; use std::str::FromStr; -use crate::{get_ipc_provider, CommandLineHandler, GlobalArguments}; - /// The command to get the validator information pub(crate) struct ValidatorInfo; @@ -23,7 +23,11 @@ impl CommandLineHandler for ValidatorInfo { let provider = get_ipc_provider(global)?; let subnet = SubnetID::from_str(&arguments.subnet)?; - let validator = Address::from_str(&arguments.validator)?; + // Attempt to parse the validator address as an EthAddress first; if not, parse as a + // Filecoin address. + let validator: Address = EthAddress::from_str(&arguments.validator) + .map(EthAddress::into) + .or_else(|_| Address::from_str(&arguments.validator))?; let validator_info = provider.get_validator_info(&subnet, &validator).await?; println!("{}", validator_info); @@ -37,6 +41,9 @@ impl CommandLineHandler for ValidatorInfo { pub(crate) struct ValidatorInfoArgs { #[arg(long, help = "The subnet id to query validator info")] pub subnet: String, - #[arg(long, help = "The validator address")] + #[arg( + long, + help = "The validator address, in 0x Eth format or Filecoin address format" + )] pub validator: String, } From 37b6cd3287a76163ed196e674368e82070bed03e Mon Sep 17 00:00:00 2001 From: cryptoAtwill Date: Mon, 7 Oct 2024 18:07:10 +0800 Subject: [PATCH 68/69] skeleton implementation --- Cargo.lock | 20 ++ .../GatewayActorModifiers.json | 340 +++++++++--------- .../.storage-layouts/GatewayDiamond.json | 338 ++++++++--------- .../.storage-layouts/SubnetActorDiamond.json | 278 +++++++------- .../SubnetActorModifiers.json | 280 +++++++-------- contracts/binding/src/lib.rs | 61 ---- contracts/contracts/errors/IPCErrors.sol | 4 + .../gateway/router/CheckpointingFacet.sol | 6 + .../contracts/lib/LibGatewayActorStorage.sol | 17 + .../contracts/lib/LibSubnetActorStorage.sol | 19 - .../IValidatorRewarder.sol | 4 +- .../reward/ValidatorActivityTracker.sol | 75 ++++ .../contracts/reward/ValidatorReward.sol | 54 +++ .../reward/ValidatorRewardParentFacet.sol | 145 ++++++++ contracts/contracts/structs/CrossNet.sol | 8 +- .../subnet/SubnetActorRewardFacet.sol | 11 - contracts/test/IntegrationTestBase.sol | 6 +- contracts/test/helpers/SelectorLibrary.sol | 4 +- .../test/integration/GatewayDiamond.t.sol | 47 ++- .../integration/GatewayDiamondToken.t.sol | 9 +- contracts/test/integration/MultiSubnet.t.sol | 8 +- .../test/integration/SubnetActorDiamond.t.sol | 51 ++- .../vm/interpreter/src/fvm/checkpoint.rs | 3 + fendermint/vm/interpreter/src/fvm/exec.rs | 2 + fendermint/vm/reward/Cargo.toml | 21 ++ fendermint/vm/reward/src/lib.rs | 32 ++ 26 files changed, 1089 insertions(+), 754 deletions(-) rename contracts/contracts/{interfaces => reward}/IValidatorRewarder.sol (81%) create mode 100644 contracts/contracts/reward/ValidatorActivityTracker.sol create mode 100644 contracts/contracts/reward/ValidatorReward.sol create mode 100644 contracts/contracts/reward/ValidatorRewardParentFacet.sol create mode 100644 fendermint/vm/reward/Cargo.toml create mode 100644 fendermint/vm/reward/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index f50cd93960..1c4886caf9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3472,6 +3472,26 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "fendermint_vm_validater_reward" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "cid", + "ethers", + "fendermint_crypto", + "fendermint_tracing", + "fendermint_vm_event", + "fvm_ipld_encoding", + "fvm_shared", + "hex", + "ipc-observability", + "serde", + "thiserror", + "tracing", +] + [[package]] name = "ff" version = "0.12.1" diff --git a/contracts/.storage-layouts/GatewayActorModifiers.json b/contracts/.storage-layouts/GatewayActorModifiers.json index c1987575a4..b2f59a8944 100644 --- a/contracts/.storage-layouts/GatewayActorModifiers.json +++ b/contracts/.storage-layouts/GatewayActorModifiers.json @@ -1,12 +1,12 @@ { "storage": [ { - "astId": 11979, + "astId": 11959, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "s", "offset": 0, "slot": "0", - "type": "t_struct(GatewayActorStorage)11965_storage" + "type": "t_struct(GatewayActorStorage)11945_storage" } ], "types": { @@ -27,14 +27,14 @@ "label": "bytes32[]", "numberOfBytes": "32" }, - "t_array(t_struct(IpcEnvelope)18702_storage)dyn_storage": { - "base": "t_struct(IpcEnvelope)18702_storage", + "t_array(t_struct(IpcEnvelope)19408_storage)dyn_storage": { + "base": "t_struct(IpcEnvelope)19408_storage", "encoding": "dynamic_array", "label": "struct IpcEnvelope[]", "numberOfBytes": "32" }, - "t_array(t_struct(Validator)18966_storage)dyn_storage": { - "base": "t_struct(Validator)18966_storage", + "t_array(t_struct(Validator)19672_storage)dyn_storage": { + "base": "t_struct(Validator)19672_storage", "encoding": "dynamic_array", "label": "struct Validator[]", "numberOfBytes": "32" @@ -54,22 +54,22 @@ "label": "bytes", "numberOfBytes": "32" }, - "t_enum(IpcMsgKind)18679": { + "t_enum(IpcMsgKind)19385": { "encoding": "inplace", "label": "enum IpcMsgKind", "numberOfBytes": "1" }, - "t_enum(PermissionMode)18912": { + "t_enum(PermissionMode)19618": { "encoding": "inplace", "label": "enum PermissionMode", "numberOfBytes": "1" }, - "t_enum(QuorumObjKind)18748": { + "t_enum(QuorumObjKind)19454": { "encoding": "inplace", "label": "enum QuorumObjKind", "numberOfBytes": "1" }, - "t_enum(StakingOperation)18835": { + "t_enum(StakingOperation)19541": { "encoding": "inplace", "label": "enum StakingOperation", "numberOfBytes": "1" @@ -81,12 +81,12 @@ "numberOfBytes": "32", "value": "t_bytes_storage" }, - "t_mapping(t_address,t_struct(ValidatorInfo)18907_storage)": { + "t_mapping(t_address,t_struct(ValidatorInfo)19613_storage)": { "encoding": "mapping", "key": "t_address", "label": "mapping(address => struct ValidatorInfo)", "numberOfBytes": "32", - "value": "t_struct(ValidatorInfo)18907_storage" + "value": "t_struct(ValidatorInfo)19613_storage" }, "t_mapping(t_address,t_uint16)": { "encoding": "mapping", @@ -95,19 +95,19 @@ "numberOfBytes": "32", "value": "t_uint16" }, - "t_mapping(t_bytes32,t_struct(IpcEnvelope)18702_storage)": { + "t_mapping(t_bytes32,t_struct(IpcEnvelope)19408_storage)": { "encoding": "mapping", "key": "t_bytes32", "label": "mapping(bytes32 => struct IpcEnvelope)", "numberOfBytes": "32", - "value": "t_struct(IpcEnvelope)18702_storage" + "value": "t_struct(IpcEnvelope)19408_storage" }, - "t_mapping(t_bytes32,t_struct(Subnet)18829_storage)": { + "t_mapping(t_bytes32,t_struct(Subnet)19535_storage)": { "encoding": "mapping", "key": "t_bytes32", "label": "mapping(bytes32 => struct Subnet)", "numberOfBytes": "32", - "value": "t_struct(Subnet)18829_storage" + "value": "t_struct(Subnet)19535_storage" }, "t_mapping(t_bytes32,t_uint256)": { "encoding": "mapping", @@ -137,40 +137,40 @@ "numberOfBytes": "32", "value": "t_struct(AddressSet)3459_storage" }, - "t_mapping(t_uint256,t_struct(BottomUpCheckpoint)18654_storage)": { + "t_mapping(t_uint256,t_struct(BottomUpCheckpoint)19331_storage)": { "encoding": "mapping", "key": "t_uint256", "label": "mapping(uint256 => struct BottomUpCheckpoint)", "numberOfBytes": "32", - "value": "t_struct(BottomUpCheckpoint)18654_storage" + "value": "t_struct(BottomUpCheckpoint)19331_storage" }, - "t_mapping(t_uint256,t_struct(BottomUpMsgBatch)18668_storage)": { + "t_mapping(t_uint256,t_struct(BottomUpMsgBatch)19374_storage)": { "encoding": "mapping", "key": "t_uint256", "label": "mapping(uint256 => struct BottomUpMsgBatch)", "numberOfBytes": "32", - "value": "t_struct(BottomUpMsgBatch)18668_storage" + "value": "t_struct(BottomUpMsgBatch)19374_storage" }, - "t_mapping(t_uint256,t_struct(ParentFinality)18634_storage)": { + "t_mapping(t_uint256,t_struct(ParentFinality)19311_storage)": { "encoding": "mapping", "key": "t_uint256", "label": "mapping(uint256 => struct ParentFinality)", "numberOfBytes": "32", - "value": "t_struct(ParentFinality)18634_storage" + "value": "t_struct(ParentFinality)19311_storage" }, - "t_mapping(t_uint256,t_struct(QuorumInfo)18765_storage)": { + "t_mapping(t_uint256,t_struct(QuorumInfo)19471_storage)": { "encoding": "mapping", "key": "t_uint256", "label": "mapping(uint256 => struct QuorumInfo)", "numberOfBytes": "32", - "value": "t_struct(QuorumInfo)18765_storage" + "value": "t_struct(QuorumInfo)19471_storage" }, - "t_mapping(t_uint64,t_struct(StakingChange)18844_storage)": { + "t_mapping(t_uint64,t_struct(StakingChange)19550_storage)": { "encoding": "mapping", "key": "t_uint64", "label": "mapping(uint64 => struct StakingChange)", "numberOfBytes": "32", - "value": "t_struct(StakingChange)18844_storage" + "value": "t_struct(StakingChange)19550_storage" }, "t_struct(AddressSet)3459_storage": { "encoding": "inplace", @@ -187,20 +187,20 @@ ], "numberOfBytes": "64" }, - "t_struct(BottomUpCheckpoint)18654_storage": { + "t_struct(BottomUpCheckpoint)19331_storage": { "encoding": "inplace", "label": "struct BottomUpCheckpoint", "members": [ { - "astId": 18639, + "astId": 19316, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "subnetID", "offset": 0, "slot": "0", - "type": "t_struct(SubnetID)18814_storage" + "type": "t_struct(SubnetID)19520_storage" }, { - "astId": 18642, + "astId": 19319, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "blockHeight", "offset": 0, @@ -208,7 +208,7 @@ "type": "t_uint256" }, { - "astId": 18645, + "astId": 19322, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "blockHash", "offset": 0, @@ -216,7 +216,7 @@ "type": "t_bytes32" }, { - "astId": 18648, + "astId": 19325, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "nextConfigurationNumber", "offset": 0, @@ -224,30 +224,30 @@ "type": "t_uint64" }, { - "astId": 18653, + "astId": 19330, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "msgs", "offset": 0, "slot": "5", - "type": "t_array(t_struct(IpcEnvelope)18702_storage)dyn_storage" + "type": "t_array(t_struct(IpcEnvelope)19408_storage)dyn_storage" } ], "numberOfBytes": "192" }, - "t_struct(BottomUpMsgBatch)18668_storage": { + "t_struct(BottomUpMsgBatch)19374_storage": { "encoding": "inplace", "label": "struct BottomUpMsgBatch", "members": [ { - "astId": 18659, + "astId": 19365, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "subnetID", "offset": 0, "slot": "0", - "type": "t_struct(SubnetID)18814_storage" + "type": "t_struct(SubnetID)19520_storage" }, { - "astId": 18662, + "astId": 19368, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "blockHeight", "offset": 0, @@ -255,12 +255,12 @@ "type": "t_uint256" }, { - "astId": 18667, + "astId": 19373, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "msgs", "offset": 0, "slot": "3", - "type": "t_array(t_struct(IpcEnvelope)18702_storage)dyn_storage" + "type": "t_array(t_struct(IpcEnvelope)19408_storage)dyn_storage" } ], "numberOfBytes": "128" @@ -280,12 +280,12 @@ ], "numberOfBytes": "64" }, - "t_struct(FvmAddress)18733_storage": { + "t_struct(FvmAddress)19439_storage": { "encoding": "inplace", "label": "struct FvmAddress", "members": [ { - "astId": 18730, + "astId": 19436, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "addrType", "offset": 0, @@ -293,7 +293,7 @@ "type": "t_uint8" }, { - "astId": 18732, + "astId": 19438, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "payload", "offset": 0, @@ -303,12 +303,12 @@ ], "numberOfBytes": "64" }, - "t_struct(GatewayActorStorage)11965_storage": { + "t_struct(GatewayActorStorage)11945_storage": { "encoding": "inplace", "label": "struct GatewayActorStorage", "members": [ { - "astId": 11877, + "astId": 11857, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "latestParentHeight", "offset": 0, @@ -316,7 +316,7 @@ "type": "t_uint256" }, { - "astId": 11880, + "astId": 11860, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "bottomUpCheckPeriod", "offset": 0, @@ -324,7 +324,7 @@ "type": "t_uint256" }, { - "astId": 11883, + "astId": 11863, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "bottomUpMsgBatchPeriod", "offset": 0, @@ -332,7 +332,7 @@ "type": "t_uint256" }, { - "astId": 11886, + "astId": 11866, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "bottomUpNonce", "offset": 0, @@ -340,7 +340,7 @@ "type": "t_uint64" }, { - "astId": 11889, + "astId": 11869, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "appliedTopDownNonce", "offset": 8, @@ -348,7 +348,7 @@ "type": "t_uint64" }, { - "astId": 11892, + "astId": 11872, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "totalSubnets", "offset": 16, @@ -356,7 +356,7 @@ "type": "t_uint64" }, { - "astId": 11895, + "astId": 11875, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "maxMsgsPerBottomUpBatch", "offset": 24, @@ -364,7 +364,7 @@ "type": "t_uint64" }, { - "astId": 11898, + "astId": 11878, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "majorityPercentage", "offset": 0, @@ -372,7 +372,7 @@ "type": "t_uint8" }, { - "astId": 11901, + "astId": 11881, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "commitSha", "offset": 0, @@ -380,7 +380,7 @@ "type": "t_bytes32" }, { - "astId": 11904, + "astId": 11884, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "maxTreeDepth", "offset": 0, @@ -388,7 +388,7 @@ "type": "t_uint8" }, { - "astId": 11907, + "astId": 11887, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "generalPurposeCrossMsg", "offset": 1, @@ -396,7 +396,7 @@ "type": "t_bool" }, { - "astId": 11910, + "astId": 11890, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "multiLevelCrossMsg", "offset": 2, @@ -404,87 +404,87 @@ "type": "t_bool" }, { - "astId": 11914, + "astId": 11894, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "currentMembership", "offset": 0, "slot": "7", - "type": "t_struct(Membership)18974_storage" + "type": "t_struct(Membership)19680_storage" }, { - "astId": 11918, + "astId": 11898, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "lastMembership", "offset": 0, "slot": "9", - "type": "t_struct(Membership)18974_storage" + "type": "t_struct(Membership)19680_storage" }, { - "astId": 11922, + "astId": 11902, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "checkpointQuorumMap", "offset": 0, "slot": "11", - "type": "t_struct(QuorumMap)18797_storage" + "type": "t_struct(QuorumMap)19503_storage" }, { - "astId": 11926, + "astId": 11906, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "networkName", "offset": 0, "slot": "18", - "type": "t_struct(SubnetID)18814_storage" + "type": "t_struct(SubnetID)19520_storage" }, { - "astId": 11930, + "astId": 11910, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "validatorsTracker", "offset": 0, "slot": "20", - "type": "t_struct(ParentValidatorsTracker)18950_storage" + "type": "t_struct(ParentValidatorsTracker)19656_storage" }, { - "astId": 11936, + "astId": 11916, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "subnets", "offset": 0, "slot": "31", - "type": "t_mapping(t_bytes32,t_struct(Subnet)18829_storage)" + "type": "t_mapping(t_bytes32,t_struct(Subnet)19535_storage)" }, { - "astId": 11942, + "astId": 11922, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "finalitiesMap", "offset": 0, "slot": "32", - "type": "t_mapping(t_uint256,t_struct(ParentFinality)18634_storage)" + "type": "t_mapping(t_uint256,t_struct(ParentFinality)19311_storage)" }, { - "astId": 11948, + "astId": 11928, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "postbox", "offset": 0, "slot": "33", - "type": "t_mapping(t_bytes32,t_struct(IpcEnvelope)18702_storage)" + "type": "t_mapping(t_bytes32,t_struct(IpcEnvelope)19408_storage)" }, { - "astId": 11954, + "astId": 11934, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "bottomUpCheckpoints", "offset": 0, "slot": "34", - "type": "t_mapping(t_uint256,t_struct(BottomUpCheckpoint)18654_storage)" + "type": "t_mapping(t_uint256,t_struct(BottomUpCheckpoint)19331_storage)" }, { - "astId": 11960, + "astId": 11940, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "bottomUpMsgBatches", "offset": 0, "slot": "35", - "type": "t_mapping(t_uint256,t_struct(BottomUpMsgBatch)18668_storage)" + "type": "t_mapping(t_uint256,t_struct(BottomUpMsgBatch)19374_storage)" }, { - "astId": 11964, + "astId": 11944, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "subnetKeys", "offset": 0, @@ -494,59 +494,59 @@ ], "numberOfBytes": "1216" }, - "t_struct(IPCAddress)18958_storage": { + "t_struct(IPCAddress)19664_storage": { "encoding": "inplace", "label": "struct IPCAddress", "members": [ { - "astId": 18954, + "astId": 19660, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "subnetId", "offset": 0, "slot": "0", - "type": "t_struct(SubnetID)18814_storage" + "type": "t_struct(SubnetID)19520_storage" }, { - "astId": 18957, + "astId": 19663, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "rawAddress", "offset": 0, "slot": "2", - "type": "t_struct(FvmAddress)18733_storage" + "type": "t_struct(FvmAddress)19439_storage" } ], "numberOfBytes": "128" }, - "t_struct(IpcEnvelope)18702_storage": { + "t_struct(IpcEnvelope)19408_storage": { "encoding": "inplace", "label": "struct IpcEnvelope", "members": [ { - "astId": 18684, + "astId": 19390, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "kind", "offset": 0, "slot": "0", - "type": "t_enum(IpcMsgKind)18679" + "type": "t_enum(IpcMsgKind)19385" }, { - "astId": 18688, + "astId": 19394, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "to", "offset": 0, "slot": "1", - "type": "t_struct(IPCAddress)18958_storage" + "type": "t_struct(IPCAddress)19664_storage" }, { - "astId": 18692, + "astId": 19398, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "from", "offset": 0, "slot": "5", - "type": "t_struct(IPCAddress)18958_storage" + "type": "t_struct(IPCAddress)19664_storage" }, { - "astId": 18695, + "astId": 19401, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "nonce", "offset": 0, @@ -554,7 +554,7 @@ "type": "t_uint64" }, { - "astId": 18698, + "astId": 19404, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "value", "offset": 0, @@ -562,7 +562,7 @@ "type": "t_uint256" }, { - "astId": 18701, + "astId": 19407, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "message", "offset": 0, @@ -572,35 +572,35 @@ ], "numberOfBytes": "384" }, - "t_struct(MaxPQ)17125_storage": { + "t_struct(MaxPQ)17052_storage": { "encoding": "inplace", "label": "struct MaxPQ", "members": [ { - "astId": 17124, + "astId": 17051, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "inner", "offset": 0, "slot": "0", - "type": "t_struct(PQ)18373_storage" + "type": "t_struct(PQ)18300_storage" } ], "numberOfBytes": "96" }, - "t_struct(Membership)18974_storage": { + "t_struct(Membership)19680_storage": { "encoding": "inplace", "label": "struct Membership", "members": [ { - "astId": 18971, + "astId": 19677, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "validators", "offset": 0, "slot": "0", - "type": "t_array(t_struct(Validator)18966_storage)dyn_storage" + "type": "t_array(t_struct(Validator)19672_storage)dyn_storage" }, { - "astId": 18973, + "astId": 19679, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "configurationNumber", "offset": 0, @@ -610,27 +610,27 @@ ], "numberOfBytes": "64" }, - "t_struct(MinPQ)17743_storage": { + "t_struct(MinPQ)17670_storage": { "encoding": "inplace", "label": "struct MinPQ", "members": [ { - "astId": 17742, + "astId": 17669, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "inner", "offset": 0, "slot": "0", - "type": "t_struct(PQ)18373_storage" + "type": "t_struct(PQ)18300_storage" } ], "numberOfBytes": "96" }, - "t_struct(PQ)18373_storage": { + "t_struct(PQ)18300_storage": { "encoding": "inplace", "label": "struct PQ", "members": [ { - "astId": 18362, + "astId": 18289, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "size", "offset": 0, @@ -638,7 +638,7 @@ "type": "t_uint16" }, { - "astId": 18367, + "astId": 18294, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "addressToPos", "offset": 0, @@ -646,7 +646,7 @@ "type": "t_mapping(t_address,t_uint16)" }, { - "astId": 18372, + "astId": 18299, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "posToAddress", "offset": 0, @@ -656,12 +656,12 @@ ], "numberOfBytes": "96" }, - "t_struct(ParentFinality)18634_storage": { + "t_struct(ParentFinality)19311_storage": { "encoding": "inplace", "label": "struct ParentFinality", "members": [ { - "astId": 18631, + "astId": 19308, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "height", "offset": 0, @@ -669,7 +669,7 @@ "type": "t_uint256" }, { - "astId": 18633, + "astId": 19310, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "blockHash", "offset": 0, @@ -679,35 +679,35 @@ ], "numberOfBytes": "64" }, - "t_struct(ParentValidatorsTracker)18950_storage": { + "t_struct(ParentValidatorsTracker)19656_storage": { "encoding": "inplace", "label": "struct ParentValidatorsTracker", "members": [ { - "astId": 18946, + "astId": 19652, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "validators", "offset": 0, "slot": "0", - "type": "t_struct(ValidatorSet)18942_storage" + "type": "t_struct(ValidatorSet)19648_storage" }, { - "astId": 18949, + "astId": 19655, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "changes", "offset": 0, "slot": "9", - "type": "t_struct(StakingChangeLog)18865_storage" + "type": "t_struct(StakingChangeLog)19571_storage" } ], "numberOfBytes": "352" }, - "t_struct(QuorumInfo)18765_storage": { + "t_struct(QuorumInfo)19471_storage": { "encoding": "inplace", "label": "struct QuorumInfo", "members": [ { - "astId": 18752, + "astId": 19458, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "hash", "offset": 0, @@ -715,7 +715,7 @@ "type": "t_bytes32" }, { - "astId": 18755, + "astId": 19461, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "rootHash", "offset": 0, @@ -723,7 +723,7 @@ "type": "t_bytes32" }, { - "astId": 18758, + "astId": 19464, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "threshold", "offset": 0, @@ -731,7 +731,7 @@ "type": "t_uint256" }, { - "astId": 18761, + "astId": 19467, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "currentWeight", "offset": 0, @@ -739,7 +739,7 @@ "type": "t_uint256" }, { - "astId": 18764, + "astId": 19470, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "reached", "offset": 0, @@ -749,20 +749,20 @@ ], "numberOfBytes": "160" }, - "t_struct(QuorumMap)18797_storage": { + "t_struct(QuorumMap)19503_storage": { "encoding": "inplace", "label": "struct QuorumMap", "members": [ { - "astId": 18770, + "astId": 19476, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "quorumObjKind", "offset": 0, "slot": "0", - "type": "t_enum(QuorumObjKind)18748" + "type": "t_enum(QuorumObjKind)19454" }, { - "astId": 18773, + "astId": 19479, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "retentionHeight", "offset": 0, @@ -770,15 +770,15 @@ "type": "t_uint256" }, { - "astId": 18779, + "astId": 19485, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "quorumInfo", "offset": 0, "slot": "2", - "type": "t_mapping(t_uint256,t_struct(QuorumInfo)18765_storage)" + "type": "t_mapping(t_uint256,t_struct(QuorumInfo)19471_storage)" }, { - "astId": 18783, + "astId": 19489, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "incompleteQuorums", "offset": 0, @@ -786,7 +786,7 @@ "type": "t_struct(UintSet)3616_storage" }, { - "astId": 18789, + "astId": 19495, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "quorumSignatureSenders", "offset": 0, @@ -794,7 +794,7 @@ "type": "t_mapping(t_uint256,t_struct(AddressSet)3459_storage)" }, { - "astId": 18796, + "astId": 19502, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "quorumSignatures", "offset": 0, @@ -827,20 +827,20 @@ ], "numberOfBytes": "64" }, - "t_struct(StakingChange)18844_storage": { + "t_struct(StakingChange)19550_storage": { "encoding": "inplace", "label": "struct StakingChange", "members": [ { - "astId": 18839, + "astId": 19545, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "op", "offset": 0, "slot": "0", - "type": "t_enum(StakingOperation)18835" + "type": "t_enum(StakingOperation)19541" }, { - "astId": 18841, + "astId": 19547, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "payload", "offset": 0, @@ -848,7 +848,7 @@ "type": "t_bytes_storage" }, { - "astId": 18843, + "astId": 19549, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "validator", "offset": 0, @@ -858,12 +858,12 @@ ], "numberOfBytes": "96" }, - "t_struct(StakingChangeLog)18865_storage": { + "t_struct(StakingChangeLog)19571_storage": { "encoding": "inplace", "label": "struct StakingChangeLog", "members": [ { - "astId": 18855, + "astId": 19561, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "nextConfigurationNumber", "offset": 0, @@ -871,7 +871,7 @@ "type": "t_uint64" }, { - "astId": 18858, + "astId": 19564, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "startConfigurationNumber", "offset": 8, @@ -879,22 +879,22 @@ "type": "t_uint64" }, { - "astId": 18864, + "astId": 19570, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "changes", "offset": 0, "slot": "1", - "type": "t_mapping(t_uint64,t_struct(StakingChange)18844_storage)" + "type": "t_mapping(t_uint64,t_struct(StakingChange)19550_storage)" } ], "numberOfBytes": "64" }, - "t_struct(Subnet)18829_storage": { + "t_struct(Subnet)19535_storage": { "encoding": "inplace", "label": "struct Subnet", "members": [ { - "astId": 18817, + "astId": 19523, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "stake", "offset": 0, @@ -902,7 +902,7 @@ "type": "t_uint256" }, { - "astId": 18819, + "astId": 19525, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "genesisEpoch", "offset": 0, @@ -910,7 +910,7 @@ "type": "t_uint256" }, { - "astId": 18821, + "astId": 19527, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "circSupply", "offset": 0, @@ -918,7 +918,7 @@ "type": "t_uint256" }, { - "astId": 18823, + "astId": 19529, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "topDownNonce", "offset": 0, @@ -926,7 +926,7 @@ "type": "t_uint64" }, { - "astId": 18825, + "astId": 19531, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "appliedBottomUpNonce", "offset": 8, @@ -934,22 +934,22 @@ "type": "t_uint64" }, { - "astId": 18828, + "astId": 19534, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "id", "offset": 0, "slot": "4", - "type": "t_struct(SubnetID)18814_storage" + "type": "t_struct(SubnetID)19520_storage" } ], "numberOfBytes": "192" }, - "t_struct(SubnetID)18814_storage": { + "t_struct(SubnetID)19520_storage": { "encoding": "inplace", "label": "struct SubnetID", "members": [ { - "astId": 18809, + "astId": 19515, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "root", "offset": 0, @@ -957,7 +957,7 @@ "type": "t_uint64" }, { - "astId": 18813, + "astId": 19519, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "route", "offset": 0, @@ -982,12 +982,12 @@ ], "numberOfBytes": "64" }, - "t_struct(Validator)18966_storage": { + "t_struct(Validator)19672_storage": { "encoding": "inplace", "label": "struct Validator", "members": [ { - "astId": 18961, + "astId": 19667, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "weight", "offset": 0, @@ -995,7 +995,7 @@ "type": "t_uint256" }, { - "astId": 18963, + "astId": 19669, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "addr", "offset": 0, @@ -1003,7 +1003,7 @@ "type": "t_address" }, { - "astId": 18965, + "astId": 19671, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "metadata", "offset": 0, @@ -1013,12 +1013,12 @@ ], "numberOfBytes": "96" }, - "t_struct(ValidatorInfo)18907_storage": { + "t_struct(ValidatorInfo)19613_storage": { "encoding": "inplace", "label": "struct ValidatorInfo", "members": [ { - "astId": 18899, + "astId": 19605, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "federatedPower", "offset": 0, @@ -1026,7 +1026,7 @@ "type": "t_uint256" }, { - "astId": 18901, + "astId": 19607, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "confirmedCollateral", "offset": 0, @@ -1034,7 +1034,7 @@ "type": "t_uint256" }, { - "astId": 18903, + "astId": 19609, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "totalCollateral", "offset": 0, @@ -1042,7 +1042,7 @@ "type": "t_uint256" }, { - "astId": 18906, + "astId": 19612, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "metadata", "offset": 0, @@ -1052,20 +1052,20 @@ ], "numberOfBytes": "128" }, - "t_struct(ValidatorSet)18942_storage": { + "t_struct(ValidatorSet)19648_storage": { "encoding": "inplace", "label": "struct ValidatorSet", "members": [ { - "astId": 18921, + "astId": 19627, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "permissionMode", "offset": 0, "slot": "0", - "type": "t_enum(PermissionMode)18912" + "type": "t_enum(PermissionMode)19618" }, { - "astId": 18924, + "astId": 19630, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "activeLimit", "offset": 1, @@ -1073,7 +1073,7 @@ "type": "t_uint16" }, { - "astId": 18927, + "astId": 19633, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "totalConfirmedCollateral", "offset": 0, @@ -1081,28 +1081,28 @@ "type": "t_uint256" }, { - "astId": 18933, + "astId": 19639, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "validators", "offset": 0, "slot": "2", - "type": "t_mapping(t_address,t_struct(ValidatorInfo)18907_storage)" + "type": "t_mapping(t_address,t_struct(ValidatorInfo)19613_storage)" }, { - "astId": 18937, + "astId": 19643, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "activeValidators", "offset": 0, "slot": "3", - "type": "t_struct(MinPQ)17743_storage" + "type": "t_struct(MinPQ)17670_storage" }, { - "astId": 18941, + "astId": 19647, "contract": "contracts/lib/LibGatewayActorStorage.sol:GatewayActorModifiers", "label": "waitingValidators", "offset": 0, "slot": "6", - "type": "t_struct(MaxPQ)17125_storage" + "type": "t_struct(MaxPQ)17052_storage" } ], "numberOfBytes": "288" diff --git a/contracts/.storage-layouts/GatewayDiamond.json b/contracts/.storage-layouts/GatewayDiamond.json index 8d68e6165a..a40c2ec87b 100644 --- a/contracts/.storage-layouts/GatewayDiamond.json +++ b/contracts/.storage-layouts/GatewayDiamond.json @@ -6,7 +6,7 @@ "label": "s", "offset": 0, "slot": "0", - "type": "t_struct(GatewayActorStorage)11965_storage" + "type": "t_struct(GatewayActorStorage)11945_storage" } ], "types": { @@ -27,14 +27,14 @@ "label": "bytes32[]", "numberOfBytes": "32" }, - "t_array(t_struct(IpcEnvelope)18702_storage)dyn_storage": { - "base": "t_struct(IpcEnvelope)18702_storage", + "t_array(t_struct(IpcEnvelope)19408_storage)dyn_storage": { + "base": "t_struct(IpcEnvelope)19408_storage", "encoding": "dynamic_array", "label": "struct IpcEnvelope[]", "numberOfBytes": "32" }, - "t_array(t_struct(Validator)18966_storage)dyn_storage": { - "base": "t_struct(Validator)18966_storage", + "t_array(t_struct(Validator)19672_storage)dyn_storage": { + "base": "t_struct(Validator)19672_storage", "encoding": "dynamic_array", "label": "struct Validator[]", "numberOfBytes": "32" @@ -54,22 +54,22 @@ "label": "bytes", "numberOfBytes": "32" }, - "t_enum(IpcMsgKind)18679": { + "t_enum(IpcMsgKind)19385": { "encoding": "inplace", "label": "enum IpcMsgKind", "numberOfBytes": "1" }, - "t_enum(PermissionMode)18912": { + "t_enum(PermissionMode)19618": { "encoding": "inplace", "label": "enum PermissionMode", "numberOfBytes": "1" }, - "t_enum(QuorumObjKind)18748": { + "t_enum(QuorumObjKind)19454": { "encoding": "inplace", "label": "enum QuorumObjKind", "numberOfBytes": "1" }, - "t_enum(StakingOperation)18835": { + "t_enum(StakingOperation)19541": { "encoding": "inplace", "label": "enum StakingOperation", "numberOfBytes": "1" @@ -81,12 +81,12 @@ "numberOfBytes": "32", "value": "t_bytes_storage" }, - "t_mapping(t_address,t_struct(ValidatorInfo)18907_storage)": { + "t_mapping(t_address,t_struct(ValidatorInfo)19613_storage)": { "encoding": "mapping", "key": "t_address", "label": "mapping(address => struct ValidatorInfo)", "numberOfBytes": "32", - "value": "t_struct(ValidatorInfo)18907_storage" + "value": "t_struct(ValidatorInfo)19613_storage" }, "t_mapping(t_address,t_uint16)": { "encoding": "mapping", @@ -95,19 +95,19 @@ "numberOfBytes": "32", "value": "t_uint16" }, - "t_mapping(t_bytes32,t_struct(IpcEnvelope)18702_storage)": { + "t_mapping(t_bytes32,t_struct(IpcEnvelope)19408_storage)": { "encoding": "mapping", "key": "t_bytes32", "label": "mapping(bytes32 => struct IpcEnvelope)", "numberOfBytes": "32", - "value": "t_struct(IpcEnvelope)18702_storage" + "value": "t_struct(IpcEnvelope)19408_storage" }, - "t_mapping(t_bytes32,t_struct(Subnet)18829_storage)": { + "t_mapping(t_bytes32,t_struct(Subnet)19535_storage)": { "encoding": "mapping", "key": "t_bytes32", "label": "mapping(bytes32 => struct Subnet)", "numberOfBytes": "32", - "value": "t_struct(Subnet)18829_storage" + "value": "t_struct(Subnet)19535_storage" }, "t_mapping(t_bytes32,t_uint256)": { "encoding": "mapping", @@ -137,40 +137,40 @@ "numberOfBytes": "32", "value": "t_struct(AddressSet)3459_storage" }, - "t_mapping(t_uint256,t_struct(BottomUpCheckpoint)18654_storage)": { + "t_mapping(t_uint256,t_struct(BottomUpCheckpoint)19331_storage)": { "encoding": "mapping", "key": "t_uint256", "label": "mapping(uint256 => struct BottomUpCheckpoint)", "numberOfBytes": "32", - "value": "t_struct(BottomUpCheckpoint)18654_storage" + "value": "t_struct(BottomUpCheckpoint)19331_storage" }, - "t_mapping(t_uint256,t_struct(BottomUpMsgBatch)18668_storage)": { + "t_mapping(t_uint256,t_struct(BottomUpMsgBatch)19374_storage)": { "encoding": "mapping", "key": "t_uint256", "label": "mapping(uint256 => struct BottomUpMsgBatch)", "numberOfBytes": "32", - "value": "t_struct(BottomUpMsgBatch)18668_storage" + "value": "t_struct(BottomUpMsgBatch)19374_storage" }, - "t_mapping(t_uint256,t_struct(ParentFinality)18634_storage)": { + "t_mapping(t_uint256,t_struct(ParentFinality)19311_storage)": { "encoding": "mapping", "key": "t_uint256", "label": "mapping(uint256 => struct ParentFinality)", "numberOfBytes": "32", - "value": "t_struct(ParentFinality)18634_storage" + "value": "t_struct(ParentFinality)19311_storage" }, - "t_mapping(t_uint256,t_struct(QuorumInfo)18765_storage)": { + "t_mapping(t_uint256,t_struct(QuorumInfo)19471_storage)": { "encoding": "mapping", "key": "t_uint256", "label": "mapping(uint256 => struct QuorumInfo)", "numberOfBytes": "32", - "value": "t_struct(QuorumInfo)18765_storage" + "value": "t_struct(QuorumInfo)19471_storage" }, - "t_mapping(t_uint64,t_struct(StakingChange)18844_storage)": { + "t_mapping(t_uint64,t_struct(StakingChange)19550_storage)": { "encoding": "mapping", "key": "t_uint64", "label": "mapping(uint64 => struct StakingChange)", "numberOfBytes": "32", - "value": "t_struct(StakingChange)18844_storage" + "value": "t_struct(StakingChange)19550_storage" }, "t_struct(AddressSet)3459_storage": { "encoding": "inplace", @@ -187,20 +187,20 @@ ], "numberOfBytes": "64" }, - "t_struct(BottomUpCheckpoint)18654_storage": { + "t_struct(BottomUpCheckpoint)19331_storage": { "encoding": "inplace", "label": "struct BottomUpCheckpoint", "members": [ { - "astId": 18639, + "astId": 19316, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "subnetID", "offset": 0, "slot": "0", - "type": "t_struct(SubnetID)18814_storage" + "type": "t_struct(SubnetID)19520_storage" }, { - "astId": 18642, + "astId": 19319, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "blockHeight", "offset": 0, @@ -208,7 +208,7 @@ "type": "t_uint256" }, { - "astId": 18645, + "astId": 19322, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "blockHash", "offset": 0, @@ -216,7 +216,7 @@ "type": "t_bytes32" }, { - "astId": 18648, + "astId": 19325, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "nextConfigurationNumber", "offset": 0, @@ -224,30 +224,30 @@ "type": "t_uint64" }, { - "astId": 18653, + "astId": 19330, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "msgs", "offset": 0, "slot": "5", - "type": "t_array(t_struct(IpcEnvelope)18702_storage)dyn_storage" + "type": "t_array(t_struct(IpcEnvelope)19408_storage)dyn_storage" } ], "numberOfBytes": "192" }, - "t_struct(BottomUpMsgBatch)18668_storage": { + "t_struct(BottomUpMsgBatch)19374_storage": { "encoding": "inplace", "label": "struct BottomUpMsgBatch", "members": [ { - "astId": 18659, + "astId": 19365, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "subnetID", "offset": 0, "slot": "0", - "type": "t_struct(SubnetID)18814_storage" + "type": "t_struct(SubnetID)19520_storage" }, { - "astId": 18662, + "astId": 19368, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "blockHeight", "offset": 0, @@ -255,12 +255,12 @@ "type": "t_uint256" }, { - "astId": 18667, + "astId": 19373, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "msgs", "offset": 0, "slot": "3", - "type": "t_array(t_struct(IpcEnvelope)18702_storage)dyn_storage" + "type": "t_array(t_struct(IpcEnvelope)19408_storage)dyn_storage" } ], "numberOfBytes": "128" @@ -280,12 +280,12 @@ ], "numberOfBytes": "64" }, - "t_struct(FvmAddress)18733_storage": { + "t_struct(FvmAddress)19439_storage": { "encoding": "inplace", "label": "struct FvmAddress", "members": [ { - "astId": 18730, + "astId": 19436, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "addrType", "offset": 0, @@ -293,7 +293,7 @@ "type": "t_uint8" }, { - "astId": 18732, + "astId": 19438, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "payload", "offset": 0, @@ -303,12 +303,12 @@ ], "numberOfBytes": "64" }, - "t_struct(GatewayActorStorage)11965_storage": { + "t_struct(GatewayActorStorage)11945_storage": { "encoding": "inplace", "label": "struct GatewayActorStorage", "members": [ { - "astId": 11877, + "astId": 11857, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "latestParentHeight", "offset": 0, @@ -316,7 +316,7 @@ "type": "t_uint256" }, { - "astId": 11880, + "astId": 11860, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "bottomUpCheckPeriod", "offset": 0, @@ -324,7 +324,7 @@ "type": "t_uint256" }, { - "astId": 11883, + "astId": 11863, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "bottomUpMsgBatchPeriod", "offset": 0, @@ -332,7 +332,7 @@ "type": "t_uint256" }, { - "astId": 11886, + "astId": 11866, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "bottomUpNonce", "offset": 0, @@ -340,7 +340,7 @@ "type": "t_uint64" }, { - "astId": 11889, + "astId": 11869, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "appliedTopDownNonce", "offset": 8, @@ -348,7 +348,7 @@ "type": "t_uint64" }, { - "astId": 11892, + "astId": 11872, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "totalSubnets", "offset": 16, @@ -356,7 +356,7 @@ "type": "t_uint64" }, { - "astId": 11895, + "astId": 11875, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "maxMsgsPerBottomUpBatch", "offset": 24, @@ -364,7 +364,7 @@ "type": "t_uint64" }, { - "astId": 11898, + "astId": 11878, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "majorityPercentage", "offset": 0, @@ -372,7 +372,7 @@ "type": "t_uint8" }, { - "astId": 11901, + "astId": 11881, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "commitSha", "offset": 0, @@ -380,7 +380,7 @@ "type": "t_bytes32" }, { - "astId": 11904, + "astId": 11884, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "maxTreeDepth", "offset": 0, @@ -388,7 +388,7 @@ "type": "t_uint8" }, { - "astId": 11907, + "astId": 11887, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "generalPurposeCrossMsg", "offset": 1, @@ -396,7 +396,7 @@ "type": "t_bool" }, { - "astId": 11910, + "astId": 11890, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "multiLevelCrossMsg", "offset": 2, @@ -404,87 +404,87 @@ "type": "t_bool" }, { - "astId": 11914, + "astId": 11894, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "currentMembership", "offset": 0, "slot": "7", - "type": "t_struct(Membership)18974_storage" + "type": "t_struct(Membership)19680_storage" }, { - "astId": 11918, + "astId": 11898, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "lastMembership", "offset": 0, "slot": "9", - "type": "t_struct(Membership)18974_storage" + "type": "t_struct(Membership)19680_storage" }, { - "astId": 11922, + "astId": 11902, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "checkpointQuorumMap", "offset": 0, "slot": "11", - "type": "t_struct(QuorumMap)18797_storage" + "type": "t_struct(QuorumMap)19503_storage" }, { - "astId": 11926, + "astId": 11906, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "networkName", "offset": 0, "slot": "18", - "type": "t_struct(SubnetID)18814_storage" + "type": "t_struct(SubnetID)19520_storage" }, { - "astId": 11930, + "astId": 11910, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "validatorsTracker", "offset": 0, "slot": "20", - "type": "t_struct(ParentValidatorsTracker)18950_storage" + "type": "t_struct(ParentValidatorsTracker)19656_storage" }, { - "astId": 11936, + "astId": 11916, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "subnets", "offset": 0, "slot": "31", - "type": "t_mapping(t_bytes32,t_struct(Subnet)18829_storage)" + "type": "t_mapping(t_bytes32,t_struct(Subnet)19535_storage)" }, { - "astId": 11942, + "astId": 11922, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "finalitiesMap", "offset": 0, "slot": "32", - "type": "t_mapping(t_uint256,t_struct(ParentFinality)18634_storage)" + "type": "t_mapping(t_uint256,t_struct(ParentFinality)19311_storage)" }, { - "astId": 11948, + "astId": 11928, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "postbox", "offset": 0, "slot": "33", - "type": "t_mapping(t_bytes32,t_struct(IpcEnvelope)18702_storage)" + "type": "t_mapping(t_bytes32,t_struct(IpcEnvelope)19408_storage)" }, { - "astId": 11954, + "astId": 11934, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "bottomUpCheckpoints", "offset": 0, "slot": "34", - "type": "t_mapping(t_uint256,t_struct(BottomUpCheckpoint)18654_storage)" + "type": "t_mapping(t_uint256,t_struct(BottomUpCheckpoint)19331_storage)" }, { - "astId": 11960, + "astId": 11940, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "bottomUpMsgBatches", "offset": 0, "slot": "35", - "type": "t_mapping(t_uint256,t_struct(BottomUpMsgBatch)18668_storage)" + "type": "t_mapping(t_uint256,t_struct(BottomUpMsgBatch)19374_storage)" }, { - "astId": 11964, + "astId": 11944, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "subnetKeys", "offset": 0, @@ -494,59 +494,59 @@ ], "numberOfBytes": "1216" }, - "t_struct(IPCAddress)18958_storage": { + "t_struct(IPCAddress)19664_storage": { "encoding": "inplace", "label": "struct IPCAddress", "members": [ { - "astId": 18954, + "astId": 19660, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "subnetId", "offset": 0, "slot": "0", - "type": "t_struct(SubnetID)18814_storage" + "type": "t_struct(SubnetID)19520_storage" }, { - "astId": 18957, + "astId": 19663, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "rawAddress", "offset": 0, "slot": "2", - "type": "t_struct(FvmAddress)18733_storage" + "type": "t_struct(FvmAddress)19439_storage" } ], "numberOfBytes": "128" }, - "t_struct(IpcEnvelope)18702_storage": { + "t_struct(IpcEnvelope)19408_storage": { "encoding": "inplace", "label": "struct IpcEnvelope", "members": [ { - "astId": 18684, + "astId": 19390, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "kind", "offset": 0, "slot": "0", - "type": "t_enum(IpcMsgKind)18679" + "type": "t_enum(IpcMsgKind)19385" }, { - "astId": 18688, + "astId": 19394, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "to", "offset": 0, "slot": "1", - "type": "t_struct(IPCAddress)18958_storage" + "type": "t_struct(IPCAddress)19664_storage" }, { - "astId": 18692, + "astId": 19398, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "from", "offset": 0, "slot": "5", - "type": "t_struct(IPCAddress)18958_storage" + "type": "t_struct(IPCAddress)19664_storage" }, { - "astId": 18695, + "astId": 19401, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "nonce", "offset": 0, @@ -554,7 +554,7 @@ "type": "t_uint64" }, { - "astId": 18698, + "astId": 19404, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "value", "offset": 0, @@ -562,7 +562,7 @@ "type": "t_uint256" }, { - "astId": 18701, + "astId": 19407, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "message", "offset": 0, @@ -572,35 +572,35 @@ ], "numberOfBytes": "384" }, - "t_struct(MaxPQ)17125_storage": { + "t_struct(MaxPQ)17052_storage": { "encoding": "inplace", "label": "struct MaxPQ", "members": [ { - "astId": 17124, + "astId": 17051, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "inner", "offset": 0, "slot": "0", - "type": "t_struct(PQ)18373_storage" + "type": "t_struct(PQ)18300_storage" } ], "numberOfBytes": "96" }, - "t_struct(Membership)18974_storage": { + "t_struct(Membership)19680_storage": { "encoding": "inplace", "label": "struct Membership", "members": [ { - "astId": 18971, + "astId": 19677, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "validators", "offset": 0, "slot": "0", - "type": "t_array(t_struct(Validator)18966_storage)dyn_storage" + "type": "t_array(t_struct(Validator)19672_storage)dyn_storage" }, { - "astId": 18973, + "astId": 19679, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "configurationNumber", "offset": 0, @@ -610,27 +610,27 @@ ], "numberOfBytes": "64" }, - "t_struct(MinPQ)17743_storage": { + "t_struct(MinPQ)17670_storage": { "encoding": "inplace", "label": "struct MinPQ", "members": [ { - "astId": 17742, + "astId": 17669, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "inner", "offset": 0, "slot": "0", - "type": "t_struct(PQ)18373_storage" + "type": "t_struct(PQ)18300_storage" } ], "numberOfBytes": "96" }, - "t_struct(PQ)18373_storage": { + "t_struct(PQ)18300_storage": { "encoding": "inplace", "label": "struct PQ", "members": [ { - "astId": 18362, + "astId": 18289, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "size", "offset": 0, @@ -638,7 +638,7 @@ "type": "t_uint16" }, { - "astId": 18367, + "astId": 18294, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "addressToPos", "offset": 0, @@ -646,7 +646,7 @@ "type": "t_mapping(t_address,t_uint16)" }, { - "astId": 18372, + "astId": 18299, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "posToAddress", "offset": 0, @@ -656,12 +656,12 @@ ], "numberOfBytes": "96" }, - "t_struct(ParentFinality)18634_storage": { + "t_struct(ParentFinality)19311_storage": { "encoding": "inplace", "label": "struct ParentFinality", "members": [ { - "astId": 18631, + "astId": 19308, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "height", "offset": 0, @@ -669,7 +669,7 @@ "type": "t_uint256" }, { - "astId": 18633, + "astId": 19310, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "blockHash", "offset": 0, @@ -679,35 +679,35 @@ ], "numberOfBytes": "64" }, - "t_struct(ParentValidatorsTracker)18950_storage": { + "t_struct(ParentValidatorsTracker)19656_storage": { "encoding": "inplace", "label": "struct ParentValidatorsTracker", "members": [ { - "astId": 18946, + "astId": 19652, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "validators", "offset": 0, "slot": "0", - "type": "t_struct(ValidatorSet)18942_storage" + "type": "t_struct(ValidatorSet)19648_storage" }, { - "astId": 18949, + "astId": 19655, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "changes", "offset": 0, "slot": "9", - "type": "t_struct(StakingChangeLog)18865_storage" + "type": "t_struct(StakingChangeLog)19571_storage" } ], "numberOfBytes": "352" }, - "t_struct(QuorumInfo)18765_storage": { + "t_struct(QuorumInfo)19471_storage": { "encoding": "inplace", "label": "struct QuorumInfo", "members": [ { - "astId": 18752, + "astId": 19458, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "hash", "offset": 0, @@ -715,7 +715,7 @@ "type": "t_bytes32" }, { - "astId": 18755, + "astId": 19461, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "rootHash", "offset": 0, @@ -723,7 +723,7 @@ "type": "t_bytes32" }, { - "astId": 18758, + "astId": 19464, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "threshold", "offset": 0, @@ -731,7 +731,7 @@ "type": "t_uint256" }, { - "astId": 18761, + "astId": 19467, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "currentWeight", "offset": 0, @@ -739,7 +739,7 @@ "type": "t_uint256" }, { - "astId": 18764, + "astId": 19470, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "reached", "offset": 0, @@ -749,20 +749,20 @@ ], "numberOfBytes": "160" }, - "t_struct(QuorumMap)18797_storage": { + "t_struct(QuorumMap)19503_storage": { "encoding": "inplace", "label": "struct QuorumMap", "members": [ { - "astId": 18770, + "astId": 19476, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "quorumObjKind", "offset": 0, "slot": "0", - "type": "t_enum(QuorumObjKind)18748" + "type": "t_enum(QuorumObjKind)19454" }, { - "astId": 18773, + "astId": 19479, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "retentionHeight", "offset": 0, @@ -770,15 +770,15 @@ "type": "t_uint256" }, { - "astId": 18779, + "astId": 19485, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "quorumInfo", "offset": 0, "slot": "2", - "type": "t_mapping(t_uint256,t_struct(QuorumInfo)18765_storage)" + "type": "t_mapping(t_uint256,t_struct(QuorumInfo)19471_storage)" }, { - "astId": 18783, + "astId": 19489, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "incompleteQuorums", "offset": 0, @@ -786,7 +786,7 @@ "type": "t_struct(UintSet)3616_storage" }, { - "astId": 18789, + "astId": 19495, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "quorumSignatureSenders", "offset": 0, @@ -794,7 +794,7 @@ "type": "t_mapping(t_uint256,t_struct(AddressSet)3459_storage)" }, { - "astId": 18796, + "astId": 19502, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "quorumSignatures", "offset": 0, @@ -827,20 +827,20 @@ ], "numberOfBytes": "64" }, - "t_struct(StakingChange)18844_storage": { + "t_struct(StakingChange)19550_storage": { "encoding": "inplace", "label": "struct StakingChange", "members": [ { - "astId": 18839, + "astId": 19545, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "op", "offset": 0, "slot": "0", - "type": "t_enum(StakingOperation)18835" + "type": "t_enum(StakingOperation)19541" }, { - "astId": 18841, + "astId": 19547, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "payload", "offset": 0, @@ -848,7 +848,7 @@ "type": "t_bytes_storage" }, { - "astId": 18843, + "astId": 19549, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "validator", "offset": 0, @@ -858,12 +858,12 @@ ], "numberOfBytes": "96" }, - "t_struct(StakingChangeLog)18865_storage": { + "t_struct(StakingChangeLog)19571_storage": { "encoding": "inplace", "label": "struct StakingChangeLog", "members": [ { - "astId": 18855, + "astId": 19561, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "nextConfigurationNumber", "offset": 0, @@ -871,7 +871,7 @@ "type": "t_uint64" }, { - "astId": 18858, + "astId": 19564, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "startConfigurationNumber", "offset": 8, @@ -879,22 +879,22 @@ "type": "t_uint64" }, { - "astId": 18864, + "astId": 19570, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "changes", "offset": 0, "slot": "1", - "type": "t_mapping(t_uint64,t_struct(StakingChange)18844_storage)" + "type": "t_mapping(t_uint64,t_struct(StakingChange)19550_storage)" } ], "numberOfBytes": "64" }, - "t_struct(Subnet)18829_storage": { + "t_struct(Subnet)19535_storage": { "encoding": "inplace", "label": "struct Subnet", "members": [ { - "astId": 18817, + "astId": 19523, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "stake", "offset": 0, @@ -902,7 +902,7 @@ "type": "t_uint256" }, { - "astId": 18819, + "astId": 19525, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "genesisEpoch", "offset": 0, @@ -910,7 +910,7 @@ "type": "t_uint256" }, { - "astId": 18821, + "astId": 19527, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "circSupply", "offset": 0, @@ -918,7 +918,7 @@ "type": "t_uint256" }, { - "astId": 18823, + "astId": 19529, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "topDownNonce", "offset": 0, @@ -926,7 +926,7 @@ "type": "t_uint64" }, { - "astId": 18825, + "astId": 19531, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "appliedBottomUpNonce", "offset": 8, @@ -934,22 +934,22 @@ "type": "t_uint64" }, { - "astId": 18828, + "astId": 19534, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "id", "offset": 0, "slot": "4", - "type": "t_struct(SubnetID)18814_storage" + "type": "t_struct(SubnetID)19520_storage" } ], "numberOfBytes": "192" }, - "t_struct(SubnetID)18814_storage": { + "t_struct(SubnetID)19520_storage": { "encoding": "inplace", "label": "struct SubnetID", "members": [ { - "astId": 18809, + "astId": 19515, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "root", "offset": 0, @@ -957,7 +957,7 @@ "type": "t_uint64" }, { - "astId": 18813, + "astId": 19519, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "route", "offset": 0, @@ -982,12 +982,12 @@ ], "numberOfBytes": "64" }, - "t_struct(Validator)18966_storage": { + "t_struct(Validator)19672_storage": { "encoding": "inplace", "label": "struct Validator", "members": [ { - "astId": 18961, + "astId": 19667, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "weight", "offset": 0, @@ -995,7 +995,7 @@ "type": "t_uint256" }, { - "astId": 18963, + "astId": 19669, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "addr", "offset": 0, @@ -1003,7 +1003,7 @@ "type": "t_address" }, { - "astId": 18965, + "astId": 19671, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "metadata", "offset": 0, @@ -1013,12 +1013,12 @@ ], "numberOfBytes": "96" }, - "t_struct(ValidatorInfo)18907_storage": { + "t_struct(ValidatorInfo)19613_storage": { "encoding": "inplace", "label": "struct ValidatorInfo", "members": [ { - "astId": 18899, + "astId": 19605, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "federatedPower", "offset": 0, @@ -1026,7 +1026,7 @@ "type": "t_uint256" }, { - "astId": 18901, + "astId": 19607, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "confirmedCollateral", "offset": 0, @@ -1034,7 +1034,7 @@ "type": "t_uint256" }, { - "astId": 18903, + "astId": 19609, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "totalCollateral", "offset": 0, @@ -1042,7 +1042,7 @@ "type": "t_uint256" }, { - "astId": 18906, + "astId": 19612, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "metadata", "offset": 0, @@ -1052,20 +1052,20 @@ ], "numberOfBytes": "128" }, - "t_struct(ValidatorSet)18942_storage": { + "t_struct(ValidatorSet)19648_storage": { "encoding": "inplace", "label": "struct ValidatorSet", "members": [ { - "astId": 18921, + "astId": 19627, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "permissionMode", "offset": 0, "slot": "0", - "type": "t_enum(PermissionMode)18912" + "type": "t_enum(PermissionMode)19618" }, { - "astId": 18924, + "astId": 19630, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "activeLimit", "offset": 1, @@ -1073,7 +1073,7 @@ "type": "t_uint16" }, { - "astId": 18927, + "astId": 19633, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "totalConfirmedCollateral", "offset": 0, @@ -1081,28 +1081,28 @@ "type": "t_uint256" }, { - "astId": 18933, + "astId": 19639, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "validators", "offset": 0, "slot": "2", - "type": "t_mapping(t_address,t_struct(ValidatorInfo)18907_storage)" + "type": "t_mapping(t_address,t_struct(ValidatorInfo)19613_storage)" }, { - "astId": 18937, + "astId": 19643, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "activeValidators", "offset": 0, "slot": "3", - "type": "t_struct(MinPQ)17743_storage" + "type": "t_struct(MinPQ)17670_storage" }, { - "astId": 18941, + "astId": 19647, "contract": "contracts/GatewayDiamond.sol:GatewayDiamond", "label": "waitingValidators", "offset": 0, "slot": "6", - "type": "t_struct(MaxPQ)17125_storage" + "type": "t_struct(MaxPQ)17052_storage" } ], "numberOfBytes": "288" diff --git a/contracts/.storage-layouts/SubnetActorDiamond.json b/contracts/.storage-layouts/SubnetActorDiamond.json index 1a7511cd67..0d89b74926 100644 --- a/contracts/.storage-layouts/SubnetActorDiamond.json +++ b/contracts/.storage-layouts/SubnetActorDiamond.json @@ -6,7 +6,7 @@ "label": "s", "offset": 0, "slot": "0", - "type": "t_struct(SubnetActorStorage)16379_storage" + "type": "t_struct(SubnetActorStorage)16306_storage" } ], "types": { @@ -27,14 +27,14 @@ "label": "bytes32[]", "numberOfBytes": "32" }, - "t_array(t_struct(IpcEnvelope)18702_storage)dyn_storage": { - "base": "t_struct(IpcEnvelope)18702_storage", + "t_array(t_struct(IpcEnvelope)19408_storage)dyn_storage": { + "base": "t_struct(IpcEnvelope)19408_storage", "encoding": "dynamic_array", "label": "struct IpcEnvelope[]", "numberOfBytes": "32" }, - "t_array(t_struct(Validator)18966_storage)dyn_storage": { - "base": "t_struct(Validator)18966_storage", + "t_array(t_struct(Validator)19672_storage)dyn_storage": { + "base": "t_struct(Validator)19672_storage", "encoding": "dynamic_array", "label": "struct Validator[]", "numberOfBytes": "32" @@ -54,7 +54,7 @@ "label": "bytes", "numberOfBytes": "32" }, - "t_enum(AssetKind)18987": { + "t_enum(AssetKind)19693": { "encoding": "inplace", "label": "enum AssetKind", "numberOfBytes": "1" @@ -64,17 +64,17 @@ "label": "enum ConsensusType", "numberOfBytes": "1" }, - "t_enum(IpcMsgKind)18679": { + "t_enum(IpcMsgKind)19385": { "encoding": "inplace", "label": "enum IpcMsgKind", "numberOfBytes": "1" }, - "t_enum(PermissionMode)18912": { + "t_enum(PermissionMode)19618": { "encoding": "inplace", "label": "enum PermissionMode", "numberOfBytes": "1" }, - "t_enum(StakingOperation)18835": { + "t_enum(StakingOperation)19541": { "encoding": "inplace", "label": "enum StakingOperation", "numberOfBytes": "1" @@ -91,19 +91,19 @@ "numberOfBytes": "32", "value": "t_string_storage" }, - "t_mapping(t_address,t_struct(AddressStakingReleases)18884_storage)": { + "t_mapping(t_address,t_struct(AddressStakingReleases)19590_storage)": { "encoding": "mapping", "key": "t_address", "label": "mapping(address => struct AddressStakingReleases)", "numberOfBytes": "32", - "value": "t_struct(AddressStakingReleases)18884_storage" + "value": "t_struct(AddressStakingReleases)19590_storage" }, - "t_mapping(t_address,t_struct(ValidatorInfo)18907_storage)": { + "t_mapping(t_address,t_struct(ValidatorInfo)19613_storage)": { "encoding": "mapping", "key": "t_address", "label": "mapping(address => struct ValidatorInfo)", "numberOfBytes": "32", - "value": "t_struct(ValidatorInfo)18907_storage" + "value": "t_struct(ValidatorInfo)19613_storage" }, "t_mapping(t_address,t_uint16)": { "encoding": "mapping", @@ -133,26 +133,26 @@ "numberOfBytes": "32", "value": "t_address" }, - "t_mapping(t_uint16,t_struct(StakingRelease)18873_storage)": { + "t_mapping(t_uint16,t_struct(StakingRelease)19579_storage)": { "encoding": "mapping", "key": "t_uint16", "label": "mapping(uint16 => struct StakingRelease)", "numberOfBytes": "32", - "value": "t_struct(StakingRelease)18873_storage" + "value": "t_struct(StakingRelease)19579_storage" }, - "t_mapping(t_uint256,t_struct(BottomUpCheckpoint)18654_storage)": { + "t_mapping(t_uint256,t_struct(BottomUpCheckpoint)19331_storage)": { "encoding": "mapping", "key": "t_uint256", "label": "mapping(uint256 => struct BottomUpCheckpoint)", "numberOfBytes": "32", - "value": "t_struct(BottomUpCheckpoint)18654_storage" + "value": "t_struct(BottomUpCheckpoint)19331_storage" }, - "t_mapping(t_uint64,t_struct(StakingChange)18844_storage)": { + "t_mapping(t_uint64,t_struct(StakingChange)19550_storage)": { "encoding": "mapping", "key": "t_uint64", "label": "mapping(uint64 => struct StakingChange)", "numberOfBytes": "32", - "value": "t_struct(StakingChange)18844_storage" + "value": "t_struct(StakingChange)19550_storage" }, "t_string_storage": { "encoding": "bytes", @@ -174,12 +174,12 @@ ], "numberOfBytes": "64" }, - "t_struct(AddressStakingReleases)18884_storage": { + "t_struct(AddressStakingReleases)19590_storage": { "encoding": "inplace", "label": "struct AddressStakingReleases", "members": [ { - "astId": 18876, + "astId": 19582, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "length", "offset": 0, @@ -187,7 +187,7 @@ "type": "t_uint16" }, { - "astId": 18878, + "astId": 19584, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "startIdx", "offset": 2, @@ -195,30 +195,30 @@ "type": "t_uint16" }, { - "astId": 18883, + "astId": 19589, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "releases", "offset": 0, "slot": "1", - "type": "t_mapping(t_uint16,t_struct(StakingRelease)18873_storage)" + "type": "t_mapping(t_uint16,t_struct(StakingRelease)19579_storage)" } ], "numberOfBytes": "64" }, - "t_struct(Asset)18983_storage": { + "t_struct(Asset)19689_storage": { "encoding": "inplace", "label": "struct Asset", "members": [ { - "astId": 18979, + "astId": 19685, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "kind", "offset": 0, "slot": "0", - "type": "t_enum(AssetKind)18987" + "type": "t_enum(AssetKind)19693" }, { - "astId": 18982, + "astId": 19688, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "tokenAddress", "offset": 1, @@ -228,20 +228,20 @@ ], "numberOfBytes": "32" }, - "t_struct(BottomUpCheckpoint)18654_storage": { + "t_struct(BottomUpCheckpoint)19331_storage": { "encoding": "inplace", "label": "struct BottomUpCheckpoint", "members": [ { - "astId": 18639, + "astId": 19316, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "subnetID", "offset": 0, "slot": "0", - "type": "t_struct(SubnetID)18814_storage" + "type": "t_struct(SubnetID)19520_storage" }, { - "astId": 18642, + "astId": 19319, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "blockHeight", "offset": 0, @@ -249,7 +249,7 @@ "type": "t_uint256" }, { - "astId": 18645, + "astId": 19322, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "blockHash", "offset": 0, @@ -257,7 +257,7 @@ "type": "t_bytes32" }, { - "astId": 18648, + "astId": 19325, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "nextConfigurationNumber", "offset": 0, @@ -265,22 +265,22 @@ "type": "t_uint64" }, { - "astId": 18653, + "astId": 19330, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "msgs", "offset": 0, "slot": "5", - "type": "t_array(t_struct(IpcEnvelope)18702_storage)dyn_storage" + "type": "t_array(t_struct(IpcEnvelope)19408_storage)dyn_storage" } ], "numberOfBytes": "192" }, - "t_struct(FvmAddress)18733_storage": { + "t_struct(FvmAddress)19439_storage": { "encoding": "inplace", "label": "struct FvmAddress", "members": [ { - "astId": 18730, + "astId": 19436, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "addrType", "offset": 0, @@ -288,7 +288,7 @@ "type": "t_uint8" }, { - "astId": 18732, + "astId": 19438, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "payload", "offset": 0, @@ -298,59 +298,59 @@ ], "numberOfBytes": "64" }, - "t_struct(IPCAddress)18958_storage": { + "t_struct(IPCAddress)19664_storage": { "encoding": "inplace", "label": "struct IPCAddress", "members": [ { - "astId": 18954, + "astId": 19660, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "subnetId", "offset": 0, "slot": "0", - "type": "t_struct(SubnetID)18814_storage" + "type": "t_struct(SubnetID)19520_storage" }, { - "astId": 18957, + "astId": 19663, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "rawAddress", "offset": 0, "slot": "2", - "type": "t_struct(FvmAddress)18733_storage" + "type": "t_struct(FvmAddress)19439_storage" } ], "numberOfBytes": "128" }, - "t_struct(IpcEnvelope)18702_storage": { + "t_struct(IpcEnvelope)19408_storage": { "encoding": "inplace", "label": "struct IpcEnvelope", "members": [ { - "astId": 18684, + "astId": 19390, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "kind", "offset": 0, "slot": "0", - "type": "t_enum(IpcMsgKind)18679" + "type": "t_enum(IpcMsgKind)19385" }, { - "astId": 18688, + "astId": 19394, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "to", "offset": 0, "slot": "1", - "type": "t_struct(IPCAddress)18958_storage" + "type": "t_struct(IPCAddress)19664_storage" }, { - "astId": 18692, + "astId": 19398, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "from", "offset": 0, "slot": "5", - "type": "t_struct(IPCAddress)18958_storage" + "type": "t_struct(IPCAddress)19664_storage" }, { - "astId": 18695, + "astId": 19401, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "nonce", "offset": 0, @@ -358,7 +358,7 @@ "type": "t_uint64" }, { - "astId": 18698, + "astId": 19404, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "value", "offset": 0, @@ -366,7 +366,7 @@ "type": "t_uint256" }, { - "astId": 18701, + "astId": 19407, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "message", "offset": 0, @@ -376,42 +376,42 @@ ], "numberOfBytes": "384" }, - "t_struct(MaxPQ)17125_storage": { + "t_struct(MaxPQ)17052_storage": { "encoding": "inplace", "label": "struct MaxPQ", "members": [ { - "astId": 17124, + "astId": 17051, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "inner", "offset": 0, "slot": "0", - "type": "t_struct(PQ)18373_storage" + "type": "t_struct(PQ)18300_storage" } ], "numberOfBytes": "96" }, - "t_struct(MinPQ)17743_storage": { + "t_struct(MinPQ)17670_storage": { "encoding": "inplace", "label": "struct MinPQ", "members": [ { - "astId": 17742, + "astId": 17669, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "inner", "offset": 0, "slot": "0", - "type": "t_struct(PQ)18373_storage" + "type": "t_struct(PQ)18300_storage" } ], "numberOfBytes": "96" }, - "t_struct(PQ)18373_storage": { + "t_struct(PQ)18300_storage": { "encoding": "inplace", "label": "struct PQ", "members": [ { - "astId": 18362, + "astId": 18289, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "size", "offset": 0, @@ -419,7 +419,7 @@ "type": "t_uint16" }, { - "astId": 18367, + "astId": 18294, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "addressToPos", "offset": 0, @@ -427,7 +427,7 @@ "type": "t_mapping(t_address,t_uint16)" }, { - "astId": 18372, + "astId": 18299, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "posToAddress", "offset": 0, @@ -460,20 +460,20 @@ ], "numberOfBytes": "64" }, - "t_struct(StakingChange)18844_storage": { + "t_struct(StakingChange)19550_storage": { "encoding": "inplace", "label": "struct StakingChange", "members": [ { - "astId": 18839, + "astId": 19545, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "op", "offset": 0, "slot": "0", - "type": "t_enum(StakingOperation)18835" + "type": "t_enum(StakingOperation)19541" }, { - "astId": 18841, + "astId": 19547, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "payload", "offset": 0, @@ -481,7 +481,7 @@ "type": "t_bytes_storage" }, { - "astId": 18843, + "astId": 19549, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "validator", "offset": 0, @@ -491,12 +491,12 @@ ], "numberOfBytes": "96" }, - "t_struct(StakingChangeLog)18865_storage": { + "t_struct(StakingChangeLog)19571_storage": { "encoding": "inplace", "label": "struct StakingChangeLog", "members": [ { - "astId": 18855, + "astId": 19561, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "nextConfigurationNumber", "offset": 0, @@ -504,7 +504,7 @@ "type": "t_uint64" }, { - "astId": 18858, + "astId": 19564, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "startConfigurationNumber", "offset": 8, @@ -512,22 +512,22 @@ "type": "t_uint64" }, { - "astId": 18864, + "astId": 19570, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "changes", "offset": 0, "slot": "1", - "type": "t_mapping(t_uint64,t_struct(StakingChange)18844_storage)" + "type": "t_mapping(t_uint64,t_struct(StakingChange)19550_storage)" } ], "numberOfBytes": "64" }, - "t_struct(StakingRelease)18873_storage": { + "t_struct(StakingRelease)19579_storage": { "encoding": "inplace", "label": "struct StakingRelease", "members": [ { - "astId": 18869, + "astId": 19575, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "releaseAt", "offset": 0, @@ -535,7 +535,7 @@ "type": "t_uint256" }, { - "astId": 18872, + "astId": 19578, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "amount", "offset": 0, @@ -545,12 +545,12 @@ ], "numberOfBytes": "64" }, - "t_struct(StakingReleaseQueue)18895_storage": { + "t_struct(StakingReleaseQueue)19601_storage": { "encoding": "inplace", "label": "struct StakingReleaseQueue", "members": [ { - "astId": 18888, + "astId": 19594, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "lockingDuration", "offset": 0, @@ -558,22 +558,22 @@ "type": "t_uint256" }, { - "astId": 18894, + "astId": 19600, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "releases", "offset": 0, "slot": "1", - "type": "t_mapping(t_address,t_struct(AddressStakingReleases)18884_storage)" + "type": "t_mapping(t_address,t_struct(AddressStakingReleases)19590_storage)" } ], "numberOfBytes": "64" }, - "t_struct(SubnetActorStorage)16379_storage": { + "t_struct(SubnetActorStorage)16306_storage": { "encoding": "inplace", "label": "struct SubnetActorStorage", "members": [ { - "astId": 16286, + "astId": 16213, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "genesisCircSupply", "offset": 0, @@ -581,7 +581,7 @@ "type": "t_uint256" }, { - "astId": 16289, + "astId": 16216, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "lastBottomUpCheckpointHeight", "offset": 0, @@ -589,7 +589,7 @@ "type": "t_uint256" }, { - "astId": 16292, + "astId": 16219, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "minActivationCollateral", "offset": 0, @@ -597,7 +597,7 @@ "type": "t_uint256" }, { - "astId": 16295, + "astId": 16222, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "bottomUpCheckPeriod", "offset": 0, @@ -605,7 +605,7 @@ "type": "t_uint256" }, { - "astId": 16297, + "astId": 16224, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "currentSubnetHash", "offset": 0, @@ -613,7 +613,7 @@ "type": "t_bytes32" }, { - "astId": 16300, + "astId": 16227, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "ipcGatewayAddr", "offset": 0, @@ -621,7 +621,7 @@ "type": "t_address" }, { - "astId": 16303, + "astId": 16230, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "maxMsgsPerBottomUpBatch", "offset": 20, @@ -629,7 +629,7 @@ "type": "t_uint64" }, { - "astId": 16306, + "astId": 16233, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "majorityPercentage", "offset": 28, @@ -637,7 +637,7 @@ "type": "t_uint8" }, { - "astId": 16309, + "astId": 16236, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "powerScale", "offset": 29, @@ -645,7 +645,7 @@ "type": "t_int8" }, { - "astId": 16313, + "astId": 16240, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "consensus", "offset": 30, @@ -653,7 +653,7 @@ "type": "t_enum(ConsensusType)5500" }, { - "astId": 16316, + "astId": 16243, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "bootstrapped", "offset": 31, @@ -661,7 +661,7 @@ "type": "t_bool" }, { - "astId": 16319, + "astId": 16246, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "minValidators", "offset": 0, @@ -669,7 +669,7 @@ "type": "t_uint64" }, { - "astId": 16322, + "astId": 16249, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "killed", "offset": 8, @@ -677,55 +677,55 @@ "type": "t_bool" }, { - "astId": 16326, + "astId": 16253, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "supplySource", "offset": 0, "slot": "7", - "type": "t_struct(Asset)18983_storage" + "type": "t_struct(Asset)19689_storage" }, { - "astId": 16330, + "astId": 16257, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "collateralSource", "offset": 0, "slot": "8", - "type": "t_struct(Asset)18983_storage" + "type": "t_struct(Asset)19689_storage" }, { - "astId": 16334, + "astId": 16261, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "parentId", "offset": 0, "slot": "9", - "type": "t_struct(SubnetID)18814_storage" + "type": "t_struct(SubnetID)19520_storage" }, { - "astId": 16338, + "astId": 16265, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "validatorSet", "offset": 0, "slot": "11", - "type": "t_struct(ValidatorSet)18942_storage" + "type": "t_struct(ValidatorSet)19648_storage" }, { - "astId": 16342, + "astId": 16269, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "changeSet", "offset": 0, "slot": "20", - "type": "t_struct(StakingChangeLog)18865_storage" + "type": "t_struct(StakingChangeLog)19571_storage" }, { - "astId": 16346, + "astId": 16273, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "releaseQueue", "offset": 0, "slot": "22", - "type": "t_struct(StakingReleaseQueue)18895_storage" + "type": "t_struct(StakingReleaseQueue)19601_storage" }, { - "astId": 16351, + "astId": 16278, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "bootstrapNodes", "offset": 0, @@ -733,7 +733,7 @@ "type": "t_mapping(t_address,t_string_storage)" }, { - "astId": 16355, + "astId": 16282, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "bootstrapOwners", "offset": 0, @@ -741,23 +741,23 @@ "type": "t_struct(AddressSet)3459_storage" }, { - "astId": 16361, + "astId": 16288, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "committedCheckpoints", "offset": 0, "slot": "27", - "type": "t_mapping(t_uint256,t_struct(BottomUpCheckpoint)18654_storage)" + "type": "t_mapping(t_uint256,t_struct(BottomUpCheckpoint)19331_storage)" }, { - "astId": 16366, + "astId": 16293, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "genesisValidators", "offset": 0, "slot": "28", - "type": "t_array(t_struct(Validator)18966_storage)dyn_storage" + "type": "t_array(t_struct(Validator)19672_storage)dyn_storage" }, { - "astId": 16371, + "astId": 16298, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "genesisBalance", "offset": 0, @@ -765,7 +765,7 @@ "type": "t_mapping(t_address,t_uint256)" }, { - "astId": 16375, + "astId": 16302, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "genesisBalanceKeys", "offset": 0, @@ -773,7 +773,7 @@ "type": "t_array(t_address)dyn_storage" }, { - "astId": 16378, + "astId": 16305, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "validatorGater", "offset": 0, @@ -783,12 +783,12 @@ ], "numberOfBytes": "1024" }, - "t_struct(SubnetID)18814_storage": { + "t_struct(SubnetID)19520_storage": { "encoding": "inplace", "label": "struct SubnetID", "members": [ { - "astId": 18809, + "astId": 19515, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "root", "offset": 0, @@ -796,7 +796,7 @@ "type": "t_uint64" }, { - "astId": 18813, + "astId": 19519, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "route", "offset": 0, @@ -806,12 +806,12 @@ ], "numberOfBytes": "64" }, - "t_struct(Validator)18966_storage": { + "t_struct(Validator)19672_storage": { "encoding": "inplace", "label": "struct Validator", "members": [ { - "astId": 18961, + "astId": 19667, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "weight", "offset": 0, @@ -819,7 +819,7 @@ "type": "t_uint256" }, { - "astId": 18963, + "astId": 19669, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "addr", "offset": 0, @@ -827,7 +827,7 @@ "type": "t_address" }, { - "astId": 18965, + "astId": 19671, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "metadata", "offset": 0, @@ -837,12 +837,12 @@ ], "numberOfBytes": "96" }, - "t_struct(ValidatorInfo)18907_storage": { + "t_struct(ValidatorInfo)19613_storage": { "encoding": "inplace", "label": "struct ValidatorInfo", "members": [ { - "astId": 18899, + "astId": 19605, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "federatedPower", "offset": 0, @@ -850,7 +850,7 @@ "type": "t_uint256" }, { - "astId": 18901, + "astId": 19607, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "confirmedCollateral", "offset": 0, @@ -858,7 +858,7 @@ "type": "t_uint256" }, { - "astId": 18903, + "astId": 19609, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "totalCollateral", "offset": 0, @@ -866,7 +866,7 @@ "type": "t_uint256" }, { - "astId": 18906, + "astId": 19612, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "metadata", "offset": 0, @@ -876,20 +876,20 @@ ], "numberOfBytes": "128" }, - "t_struct(ValidatorSet)18942_storage": { + "t_struct(ValidatorSet)19648_storage": { "encoding": "inplace", "label": "struct ValidatorSet", "members": [ { - "astId": 18921, + "astId": 19627, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "permissionMode", "offset": 0, "slot": "0", - "type": "t_enum(PermissionMode)18912" + "type": "t_enum(PermissionMode)19618" }, { - "astId": 18924, + "astId": 19630, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "activeLimit", "offset": 1, @@ -897,7 +897,7 @@ "type": "t_uint16" }, { - "astId": 18927, + "astId": 19633, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "totalConfirmedCollateral", "offset": 0, @@ -905,28 +905,28 @@ "type": "t_uint256" }, { - "astId": 18933, + "astId": 19639, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "validators", "offset": 0, "slot": "2", - "type": "t_mapping(t_address,t_struct(ValidatorInfo)18907_storage)" + "type": "t_mapping(t_address,t_struct(ValidatorInfo)19613_storage)" }, { - "astId": 18937, + "astId": 19643, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "activeValidators", "offset": 0, "slot": "3", - "type": "t_struct(MinPQ)17743_storage" + "type": "t_struct(MinPQ)17670_storage" }, { - "astId": 18941, + "astId": 19647, "contract": "contracts/SubnetActorDiamond.sol:SubnetActorDiamond", "label": "waitingValidators", "offset": 0, "slot": "6", - "type": "t_struct(MaxPQ)17125_storage" + "type": "t_struct(MaxPQ)17052_storage" } ], "numberOfBytes": "288" diff --git a/contracts/.storage-layouts/SubnetActorModifiers.json b/contracts/.storage-layouts/SubnetActorModifiers.json index dee7289072..baf89be370 100644 --- a/contracts/.storage-layouts/SubnetActorModifiers.json +++ b/contracts/.storage-layouts/SubnetActorModifiers.json @@ -1,12 +1,12 @@ { "storage": [ { - "astId": 16393, + "astId": 16320, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "s", "offset": 0, "slot": "0", - "type": "t_struct(SubnetActorStorage)16379_storage" + "type": "t_struct(SubnetActorStorage)16306_storage" } ], "types": { @@ -27,14 +27,14 @@ "label": "bytes32[]", "numberOfBytes": "32" }, - "t_array(t_struct(IpcEnvelope)18702_storage)dyn_storage": { - "base": "t_struct(IpcEnvelope)18702_storage", + "t_array(t_struct(IpcEnvelope)19408_storage)dyn_storage": { + "base": "t_struct(IpcEnvelope)19408_storage", "encoding": "dynamic_array", "label": "struct IpcEnvelope[]", "numberOfBytes": "32" }, - "t_array(t_struct(Validator)18966_storage)dyn_storage": { - "base": "t_struct(Validator)18966_storage", + "t_array(t_struct(Validator)19672_storage)dyn_storage": { + "base": "t_struct(Validator)19672_storage", "encoding": "dynamic_array", "label": "struct Validator[]", "numberOfBytes": "32" @@ -54,7 +54,7 @@ "label": "bytes", "numberOfBytes": "32" }, - "t_enum(AssetKind)18987": { + "t_enum(AssetKind)19693": { "encoding": "inplace", "label": "enum AssetKind", "numberOfBytes": "1" @@ -64,17 +64,17 @@ "label": "enum ConsensusType", "numberOfBytes": "1" }, - "t_enum(IpcMsgKind)18679": { + "t_enum(IpcMsgKind)19385": { "encoding": "inplace", "label": "enum IpcMsgKind", "numberOfBytes": "1" }, - "t_enum(PermissionMode)18912": { + "t_enum(PermissionMode)19618": { "encoding": "inplace", "label": "enum PermissionMode", "numberOfBytes": "1" }, - "t_enum(StakingOperation)18835": { + "t_enum(StakingOperation)19541": { "encoding": "inplace", "label": "enum StakingOperation", "numberOfBytes": "1" @@ -91,19 +91,19 @@ "numberOfBytes": "32", "value": "t_string_storage" }, - "t_mapping(t_address,t_struct(AddressStakingReleases)18884_storage)": { + "t_mapping(t_address,t_struct(AddressStakingReleases)19590_storage)": { "encoding": "mapping", "key": "t_address", "label": "mapping(address => struct AddressStakingReleases)", "numberOfBytes": "32", - "value": "t_struct(AddressStakingReleases)18884_storage" + "value": "t_struct(AddressStakingReleases)19590_storage" }, - "t_mapping(t_address,t_struct(ValidatorInfo)18907_storage)": { + "t_mapping(t_address,t_struct(ValidatorInfo)19613_storage)": { "encoding": "mapping", "key": "t_address", "label": "mapping(address => struct ValidatorInfo)", "numberOfBytes": "32", - "value": "t_struct(ValidatorInfo)18907_storage" + "value": "t_struct(ValidatorInfo)19613_storage" }, "t_mapping(t_address,t_uint16)": { "encoding": "mapping", @@ -133,26 +133,26 @@ "numberOfBytes": "32", "value": "t_address" }, - "t_mapping(t_uint16,t_struct(StakingRelease)18873_storage)": { + "t_mapping(t_uint16,t_struct(StakingRelease)19579_storage)": { "encoding": "mapping", "key": "t_uint16", "label": "mapping(uint16 => struct StakingRelease)", "numberOfBytes": "32", - "value": "t_struct(StakingRelease)18873_storage" + "value": "t_struct(StakingRelease)19579_storage" }, - "t_mapping(t_uint256,t_struct(BottomUpCheckpoint)18654_storage)": { + "t_mapping(t_uint256,t_struct(BottomUpCheckpoint)19331_storage)": { "encoding": "mapping", "key": "t_uint256", "label": "mapping(uint256 => struct BottomUpCheckpoint)", "numberOfBytes": "32", - "value": "t_struct(BottomUpCheckpoint)18654_storage" + "value": "t_struct(BottomUpCheckpoint)19331_storage" }, - "t_mapping(t_uint64,t_struct(StakingChange)18844_storage)": { + "t_mapping(t_uint64,t_struct(StakingChange)19550_storage)": { "encoding": "mapping", "key": "t_uint64", "label": "mapping(uint64 => struct StakingChange)", "numberOfBytes": "32", - "value": "t_struct(StakingChange)18844_storage" + "value": "t_struct(StakingChange)19550_storage" }, "t_string_storage": { "encoding": "bytes", @@ -174,12 +174,12 @@ ], "numberOfBytes": "64" }, - "t_struct(AddressStakingReleases)18884_storage": { + "t_struct(AddressStakingReleases)19590_storage": { "encoding": "inplace", "label": "struct AddressStakingReleases", "members": [ { - "astId": 18876, + "astId": 19582, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "length", "offset": 0, @@ -187,7 +187,7 @@ "type": "t_uint16" }, { - "astId": 18878, + "astId": 19584, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "startIdx", "offset": 2, @@ -195,30 +195,30 @@ "type": "t_uint16" }, { - "astId": 18883, + "astId": 19589, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "releases", "offset": 0, "slot": "1", - "type": "t_mapping(t_uint16,t_struct(StakingRelease)18873_storage)" + "type": "t_mapping(t_uint16,t_struct(StakingRelease)19579_storage)" } ], "numberOfBytes": "64" }, - "t_struct(Asset)18983_storage": { + "t_struct(Asset)19689_storage": { "encoding": "inplace", "label": "struct Asset", "members": [ { - "astId": 18979, + "astId": 19685, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "kind", "offset": 0, "slot": "0", - "type": "t_enum(AssetKind)18987" + "type": "t_enum(AssetKind)19693" }, { - "astId": 18982, + "astId": 19688, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "tokenAddress", "offset": 1, @@ -228,20 +228,20 @@ ], "numberOfBytes": "32" }, - "t_struct(BottomUpCheckpoint)18654_storage": { + "t_struct(BottomUpCheckpoint)19331_storage": { "encoding": "inplace", "label": "struct BottomUpCheckpoint", "members": [ { - "astId": 18639, + "astId": 19316, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "subnetID", "offset": 0, "slot": "0", - "type": "t_struct(SubnetID)18814_storage" + "type": "t_struct(SubnetID)19520_storage" }, { - "astId": 18642, + "astId": 19319, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "blockHeight", "offset": 0, @@ -249,7 +249,7 @@ "type": "t_uint256" }, { - "astId": 18645, + "astId": 19322, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "blockHash", "offset": 0, @@ -257,7 +257,7 @@ "type": "t_bytes32" }, { - "astId": 18648, + "astId": 19325, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "nextConfigurationNumber", "offset": 0, @@ -265,22 +265,22 @@ "type": "t_uint64" }, { - "astId": 18653, + "astId": 19330, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "msgs", "offset": 0, "slot": "5", - "type": "t_array(t_struct(IpcEnvelope)18702_storage)dyn_storage" + "type": "t_array(t_struct(IpcEnvelope)19408_storage)dyn_storage" } ], "numberOfBytes": "192" }, - "t_struct(FvmAddress)18733_storage": { + "t_struct(FvmAddress)19439_storage": { "encoding": "inplace", "label": "struct FvmAddress", "members": [ { - "astId": 18730, + "astId": 19436, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "addrType", "offset": 0, @@ -288,7 +288,7 @@ "type": "t_uint8" }, { - "astId": 18732, + "astId": 19438, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "payload", "offset": 0, @@ -298,59 +298,59 @@ ], "numberOfBytes": "64" }, - "t_struct(IPCAddress)18958_storage": { + "t_struct(IPCAddress)19664_storage": { "encoding": "inplace", "label": "struct IPCAddress", "members": [ { - "astId": 18954, + "astId": 19660, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "subnetId", "offset": 0, "slot": "0", - "type": "t_struct(SubnetID)18814_storage" + "type": "t_struct(SubnetID)19520_storage" }, { - "astId": 18957, + "astId": 19663, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "rawAddress", "offset": 0, "slot": "2", - "type": "t_struct(FvmAddress)18733_storage" + "type": "t_struct(FvmAddress)19439_storage" } ], "numberOfBytes": "128" }, - "t_struct(IpcEnvelope)18702_storage": { + "t_struct(IpcEnvelope)19408_storage": { "encoding": "inplace", "label": "struct IpcEnvelope", "members": [ { - "astId": 18684, + "astId": 19390, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "kind", "offset": 0, "slot": "0", - "type": "t_enum(IpcMsgKind)18679" + "type": "t_enum(IpcMsgKind)19385" }, { - "astId": 18688, + "astId": 19394, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "to", "offset": 0, "slot": "1", - "type": "t_struct(IPCAddress)18958_storage" + "type": "t_struct(IPCAddress)19664_storage" }, { - "astId": 18692, + "astId": 19398, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "from", "offset": 0, "slot": "5", - "type": "t_struct(IPCAddress)18958_storage" + "type": "t_struct(IPCAddress)19664_storage" }, { - "astId": 18695, + "astId": 19401, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "nonce", "offset": 0, @@ -358,7 +358,7 @@ "type": "t_uint64" }, { - "astId": 18698, + "astId": 19404, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "value", "offset": 0, @@ -366,7 +366,7 @@ "type": "t_uint256" }, { - "astId": 18701, + "astId": 19407, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "message", "offset": 0, @@ -376,42 +376,42 @@ ], "numberOfBytes": "384" }, - "t_struct(MaxPQ)17125_storage": { + "t_struct(MaxPQ)17052_storage": { "encoding": "inplace", "label": "struct MaxPQ", "members": [ { - "astId": 17124, + "astId": 17051, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "inner", "offset": 0, "slot": "0", - "type": "t_struct(PQ)18373_storage" + "type": "t_struct(PQ)18300_storage" } ], "numberOfBytes": "96" }, - "t_struct(MinPQ)17743_storage": { + "t_struct(MinPQ)17670_storage": { "encoding": "inplace", "label": "struct MinPQ", "members": [ { - "astId": 17742, + "astId": 17669, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "inner", "offset": 0, "slot": "0", - "type": "t_struct(PQ)18373_storage" + "type": "t_struct(PQ)18300_storage" } ], "numberOfBytes": "96" }, - "t_struct(PQ)18373_storage": { + "t_struct(PQ)18300_storage": { "encoding": "inplace", "label": "struct PQ", "members": [ { - "astId": 18362, + "astId": 18289, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "size", "offset": 0, @@ -419,7 +419,7 @@ "type": "t_uint16" }, { - "astId": 18367, + "astId": 18294, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "addressToPos", "offset": 0, @@ -427,7 +427,7 @@ "type": "t_mapping(t_address,t_uint16)" }, { - "astId": 18372, + "astId": 18299, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "posToAddress", "offset": 0, @@ -460,20 +460,20 @@ ], "numberOfBytes": "64" }, - "t_struct(StakingChange)18844_storage": { + "t_struct(StakingChange)19550_storage": { "encoding": "inplace", "label": "struct StakingChange", "members": [ { - "astId": 18839, + "astId": 19545, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "op", "offset": 0, "slot": "0", - "type": "t_enum(StakingOperation)18835" + "type": "t_enum(StakingOperation)19541" }, { - "astId": 18841, + "astId": 19547, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "payload", "offset": 0, @@ -481,7 +481,7 @@ "type": "t_bytes_storage" }, { - "astId": 18843, + "astId": 19549, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "validator", "offset": 0, @@ -491,12 +491,12 @@ ], "numberOfBytes": "96" }, - "t_struct(StakingChangeLog)18865_storage": { + "t_struct(StakingChangeLog)19571_storage": { "encoding": "inplace", "label": "struct StakingChangeLog", "members": [ { - "astId": 18855, + "astId": 19561, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "nextConfigurationNumber", "offset": 0, @@ -504,7 +504,7 @@ "type": "t_uint64" }, { - "astId": 18858, + "astId": 19564, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "startConfigurationNumber", "offset": 8, @@ -512,22 +512,22 @@ "type": "t_uint64" }, { - "astId": 18864, + "astId": 19570, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "changes", "offset": 0, "slot": "1", - "type": "t_mapping(t_uint64,t_struct(StakingChange)18844_storage)" + "type": "t_mapping(t_uint64,t_struct(StakingChange)19550_storage)" } ], "numberOfBytes": "64" }, - "t_struct(StakingRelease)18873_storage": { + "t_struct(StakingRelease)19579_storage": { "encoding": "inplace", "label": "struct StakingRelease", "members": [ { - "astId": 18869, + "astId": 19575, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "releaseAt", "offset": 0, @@ -535,7 +535,7 @@ "type": "t_uint256" }, { - "astId": 18872, + "astId": 19578, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "amount", "offset": 0, @@ -545,12 +545,12 @@ ], "numberOfBytes": "64" }, - "t_struct(StakingReleaseQueue)18895_storage": { + "t_struct(StakingReleaseQueue)19601_storage": { "encoding": "inplace", "label": "struct StakingReleaseQueue", "members": [ { - "astId": 18888, + "astId": 19594, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "lockingDuration", "offset": 0, @@ -558,22 +558,22 @@ "type": "t_uint256" }, { - "astId": 18894, + "astId": 19600, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "releases", "offset": 0, "slot": "1", - "type": "t_mapping(t_address,t_struct(AddressStakingReleases)18884_storage)" + "type": "t_mapping(t_address,t_struct(AddressStakingReleases)19590_storage)" } ], "numberOfBytes": "64" }, - "t_struct(SubnetActorStorage)16379_storage": { + "t_struct(SubnetActorStorage)16306_storage": { "encoding": "inplace", "label": "struct SubnetActorStorage", "members": [ { - "astId": 16286, + "astId": 16213, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "genesisCircSupply", "offset": 0, @@ -581,7 +581,7 @@ "type": "t_uint256" }, { - "astId": 16289, + "astId": 16216, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "lastBottomUpCheckpointHeight", "offset": 0, @@ -589,7 +589,7 @@ "type": "t_uint256" }, { - "astId": 16292, + "astId": 16219, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "minActivationCollateral", "offset": 0, @@ -597,7 +597,7 @@ "type": "t_uint256" }, { - "astId": 16295, + "astId": 16222, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "bottomUpCheckPeriod", "offset": 0, @@ -605,7 +605,7 @@ "type": "t_uint256" }, { - "astId": 16297, + "astId": 16224, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "currentSubnetHash", "offset": 0, @@ -613,7 +613,7 @@ "type": "t_bytes32" }, { - "astId": 16300, + "astId": 16227, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "ipcGatewayAddr", "offset": 0, @@ -621,7 +621,7 @@ "type": "t_address" }, { - "astId": 16303, + "astId": 16230, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "maxMsgsPerBottomUpBatch", "offset": 20, @@ -629,7 +629,7 @@ "type": "t_uint64" }, { - "astId": 16306, + "astId": 16233, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "majorityPercentage", "offset": 28, @@ -637,7 +637,7 @@ "type": "t_uint8" }, { - "astId": 16309, + "astId": 16236, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "powerScale", "offset": 29, @@ -645,7 +645,7 @@ "type": "t_int8" }, { - "astId": 16313, + "astId": 16240, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "consensus", "offset": 30, @@ -653,7 +653,7 @@ "type": "t_enum(ConsensusType)5500" }, { - "astId": 16316, + "astId": 16243, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "bootstrapped", "offset": 31, @@ -661,7 +661,7 @@ "type": "t_bool" }, { - "astId": 16319, + "astId": 16246, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "minValidators", "offset": 0, @@ -669,7 +669,7 @@ "type": "t_uint64" }, { - "astId": 16322, + "astId": 16249, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "killed", "offset": 8, @@ -677,55 +677,55 @@ "type": "t_bool" }, { - "astId": 16326, + "astId": 16253, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "supplySource", "offset": 0, "slot": "7", - "type": "t_struct(Asset)18983_storage" + "type": "t_struct(Asset)19689_storage" }, { - "astId": 16330, + "astId": 16257, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "collateralSource", "offset": 0, "slot": "8", - "type": "t_struct(Asset)18983_storage" + "type": "t_struct(Asset)19689_storage" }, { - "astId": 16334, + "astId": 16261, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "parentId", "offset": 0, "slot": "9", - "type": "t_struct(SubnetID)18814_storage" + "type": "t_struct(SubnetID)19520_storage" }, { - "astId": 16338, + "astId": 16265, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "validatorSet", "offset": 0, "slot": "11", - "type": "t_struct(ValidatorSet)18942_storage" + "type": "t_struct(ValidatorSet)19648_storage" }, { - "astId": 16342, + "astId": 16269, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "changeSet", "offset": 0, "slot": "20", - "type": "t_struct(StakingChangeLog)18865_storage" + "type": "t_struct(StakingChangeLog)19571_storage" }, { - "astId": 16346, + "astId": 16273, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "releaseQueue", "offset": 0, "slot": "22", - "type": "t_struct(StakingReleaseQueue)18895_storage" + "type": "t_struct(StakingReleaseQueue)19601_storage" }, { - "astId": 16351, + "astId": 16278, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "bootstrapNodes", "offset": 0, @@ -733,7 +733,7 @@ "type": "t_mapping(t_address,t_string_storage)" }, { - "astId": 16355, + "astId": 16282, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "bootstrapOwners", "offset": 0, @@ -741,23 +741,23 @@ "type": "t_struct(AddressSet)3459_storage" }, { - "astId": 16361, + "astId": 16288, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "committedCheckpoints", "offset": 0, "slot": "27", - "type": "t_mapping(t_uint256,t_struct(BottomUpCheckpoint)18654_storage)" + "type": "t_mapping(t_uint256,t_struct(BottomUpCheckpoint)19331_storage)" }, { - "astId": 16366, + "astId": 16293, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "genesisValidators", "offset": 0, "slot": "28", - "type": "t_array(t_struct(Validator)18966_storage)dyn_storage" + "type": "t_array(t_struct(Validator)19672_storage)dyn_storage" }, { - "astId": 16371, + "astId": 16298, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "genesisBalance", "offset": 0, @@ -765,7 +765,7 @@ "type": "t_mapping(t_address,t_uint256)" }, { - "astId": 16375, + "astId": 16302, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "genesisBalanceKeys", "offset": 0, @@ -773,7 +773,7 @@ "type": "t_array(t_address)dyn_storage" }, { - "astId": 16378, + "astId": 16305, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "validatorGater", "offset": 0, @@ -783,12 +783,12 @@ ], "numberOfBytes": "1024" }, - "t_struct(SubnetID)18814_storage": { + "t_struct(SubnetID)19520_storage": { "encoding": "inplace", "label": "struct SubnetID", "members": [ { - "astId": 18809, + "astId": 19515, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "root", "offset": 0, @@ -796,7 +796,7 @@ "type": "t_uint64" }, { - "astId": 18813, + "astId": 19519, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "route", "offset": 0, @@ -806,12 +806,12 @@ ], "numberOfBytes": "64" }, - "t_struct(Validator)18966_storage": { + "t_struct(Validator)19672_storage": { "encoding": "inplace", "label": "struct Validator", "members": [ { - "astId": 18961, + "astId": 19667, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "weight", "offset": 0, @@ -819,7 +819,7 @@ "type": "t_uint256" }, { - "astId": 18963, + "astId": 19669, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "addr", "offset": 0, @@ -827,7 +827,7 @@ "type": "t_address" }, { - "astId": 18965, + "astId": 19671, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "metadata", "offset": 0, @@ -837,12 +837,12 @@ ], "numberOfBytes": "96" }, - "t_struct(ValidatorInfo)18907_storage": { + "t_struct(ValidatorInfo)19613_storage": { "encoding": "inplace", "label": "struct ValidatorInfo", "members": [ { - "astId": 18899, + "astId": 19605, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "federatedPower", "offset": 0, @@ -850,7 +850,7 @@ "type": "t_uint256" }, { - "astId": 18901, + "astId": 19607, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "confirmedCollateral", "offset": 0, @@ -858,7 +858,7 @@ "type": "t_uint256" }, { - "astId": 18903, + "astId": 19609, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "totalCollateral", "offset": 0, @@ -866,7 +866,7 @@ "type": "t_uint256" }, { - "astId": 18906, + "astId": 19612, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "metadata", "offset": 0, @@ -876,20 +876,20 @@ ], "numberOfBytes": "128" }, - "t_struct(ValidatorSet)18942_storage": { + "t_struct(ValidatorSet)19648_storage": { "encoding": "inplace", "label": "struct ValidatorSet", "members": [ { - "astId": 18921, + "astId": 19627, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "permissionMode", "offset": 0, "slot": "0", - "type": "t_enum(PermissionMode)18912" + "type": "t_enum(PermissionMode)19618" }, { - "astId": 18924, + "astId": 19630, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "activeLimit", "offset": 1, @@ -897,7 +897,7 @@ "type": "t_uint16" }, { - "astId": 18927, + "astId": 19633, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "totalConfirmedCollateral", "offset": 0, @@ -905,28 +905,28 @@ "type": "t_uint256" }, { - "astId": 18933, + "astId": 19639, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "validators", "offset": 0, "slot": "2", - "type": "t_mapping(t_address,t_struct(ValidatorInfo)18907_storage)" + "type": "t_mapping(t_address,t_struct(ValidatorInfo)19613_storage)" }, { - "astId": 18937, + "astId": 19643, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "activeValidators", "offset": 0, "slot": "3", - "type": "t_struct(MinPQ)17743_storage" + "type": "t_struct(MinPQ)17670_storage" }, { - "astId": 18941, + "astId": 19647, "contract": "contracts/lib/LibSubnetActorStorage.sol:SubnetActorModifiers", "label": "waitingValidators", "offset": 0, "slot": "6", - "type": "t_struct(MaxPQ)17125_storage" + "type": "t_struct(MaxPQ)17052_storage" } ], "numberOfBytes": "288" diff --git a/contracts/binding/src/lib.rs b/contracts/binding/src/lib.rs index dd59524fb0..508f367f36 100644 --- a/contracts/binding/src/lib.rs +++ b/contracts/binding/src/lib.rs @@ -1,64 +1,3 @@ // DO NOT EDIT! This file was generated by build.rs #[macro_use] mod convert; -#[allow(clippy::all)] -pub mod checkpointing_facet; -#[allow(clippy::all)] -pub mod diamond_cut_facet; -#[allow(clippy::all)] -pub mod diamond_loupe_facet; -#[allow(clippy::all)] -pub mod gateway_diamond; -#[allow(clippy::all)] -pub mod gateway_getter_facet; -#[allow(clippy::all)] -pub mod gateway_manager_facet; -#[allow(clippy::all)] -pub mod gateway_messenger_facet; -#[allow(clippy::all)] -pub mod i_diamond; -#[allow(clippy::all)] -pub mod lib_gateway; -#[allow(clippy::all)] -pub mod lib_quorum; -#[allow(clippy::all)] -pub mod lib_staking; -#[allow(clippy::all)] -pub mod lib_staking_change_log; -#[allow(clippy::all)] -pub mod ownership_facet; -#[allow(clippy::all)] -pub mod register_subnet_facet; -#[allow(clippy::all)] -pub mod subnet_actor_checkpointing_facet; -#[allow(clippy::all)] -pub mod subnet_actor_diamond; -#[allow(clippy::all)] -pub mod subnet_actor_getter_facet; -#[allow(clippy::all)] -pub mod subnet_actor_manager_facet; -#[allow(clippy::all)] -pub mod subnet_actor_pause_facet; -#[allow(clippy::all)] -pub mod subnet_actor_reward_facet; -#[allow(clippy::all)] -pub mod subnet_getter_facet; -#[allow(clippy::all)] -pub mod subnet_registry_diamond; -#[allow(clippy::all)] -pub mod top_down_finality_facet; -#[allow(clippy::all)] -pub mod xnet_messaging_facet; - -// The list of contracts need to convert FvmAddress to fvm_shared::Address -fvm_address_conversion!(gateway_manager_facet); -fvm_address_conversion!(gateway_getter_facet); -fvm_address_conversion!(xnet_messaging_facet); -fvm_address_conversion!(gateway_messenger_facet); -fvm_address_conversion!(subnet_actor_checkpointing_facet); -fvm_address_conversion!(subnet_actor_getter_facet); -fvm_address_conversion!(lib_gateway); - -// The list of contracts that need to convert common types between each other -common_type_conversion!(subnet_actor_getter_facet, checkpointing_facet); -common_type_conversion!(subnet_actor_getter_facet, xnet_messaging_facet); diff --git a/contracts/contracts/errors/IPCErrors.sol b/contracts/contracts/errors/IPCErrors.sol index 7f838e1a42..cd629c07d4 100644 --- a/contracts/contracts/errors/IPCErrors.sol +++ b/contracts/contracts/errors/IPCErrors.sol @@ -80,6 +80,10 @@ error InvalidFederationPayload(); error DuplicatedGenesisValidator(); error NotEnoughGenesisValidators(); error ValidatorPowerChangeDenied(); +error CommitmentAlreadyInitialized(); +error SubnetNoTargetCommitment(); +error ValidatorAlreadyClaimed(); + enum InvalidXnetMessageReason { Sender, diff --git a/contracts/contracts/gateway/router/CheckpointingFacet.sol b/contracts/contracts/gateway/router/CheckpointingFacet.sol index 1b55a015a8..91f54d2cbf 100644 --- a/contracts/contracts/gateway/router/CheckpointingFacet.sol +++ b/contracts/contracts/gateway/router/CheckpointingFacet.sol @@ -16,6 +16,7 @@ import {BatchNotCreated, InvalidBatchEpoch, BatchAlreadyExists, NotEnoughSubnetC import {CrossMsgHelper} from "../../lib/CrossMsgHelper.sol"; import {IpcEnvelope, SubnetID} from "../../structs/CrossNet.sol"; import {SubnetIDHelper} from "../../lib/SubnetIDHelper.sol"; +import {LibValidatorRewardParent} from "../../reward/ValidatorRewardParentFacet.sol"; contract CheckpointingFacet is GatewayActorModifiers { using SubnetIDHelper for SubnetID; @@ -41,6 +42,11 @@ contract CheckpointingFacet is GatewayActorModifiers { LibGateway.checkMsgLength(checkpoint.msgs); execBottomUpMsgs(checkpoint.msgs, subnet); + + LibValidatorRewardParent.initNewDistribution( + checkpoint.validatorReward.commitment, + checkpoint.subnetID + ); } /// @notice creates a new bottom-up checkpoint diff --git a/contracts/contracts/lib/LibGatewayActorStorage.sol b/contracts/contracts/lib/LibGatewayActorStorage.sol index 4cb63536e2..8e98f78f30 100644 --- a/contracts/contracts/lib/LibGatewayActorStorage.sol +++ b/contracts/contracts/lib/LibGatewayActorStorage.sol @@ -102,3 +102,20 @@ contract GatewayActorModifiers { _; } } + +contract SystemContract { + using FilAddress for address; + using FilAddress for address payable; + using AccountHelper for address; + + function _systemActorOnly() private view { + if (!msg.sender.isSystemActor()) { + revert NotSystemActor(); + } + } + + modifier systemActorOnly() { + _systemActorOnly(); + _; + } +} \ No newline at end of file diff --git a/contracts/contracts/lib/LibSubnetActorStorage.sol b/contracts/contracts/lib/LibSubnetActorStorage.sol index 474f84c0f4..eaf800ca5a 100644 --- a/contracts/contracts/lib/LibSubnetActorStorage.sol +++ b/contracts/contracts/lib/LibSubnetActorStorage.sol @@ -65,25 +65,6 @@ import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet address[] genesisBalanceKeys; /// @notice The validator gater, if address(0), no validator gating is performed address validatorGater; - - /// BEGIN Validator Rewards. - - /// @notice The validator rewarder. - /// If address(0), this subnet does not process activity summaries, but instead forwards them to the parent - /// network via bottom-up checkpoints. - /// If address(0), and this is the root network, summaries are discarded (> /dev/null). - /// TODO(rewarder): set this address correctly from the constructor. - address validatorRewarder; - /// @notice Summaries pending to be processed. - /// If the validator rewarder is non-zero, these denote summaries presentable at this level. - /// If the validator rewarder is zero, these summaries must be relayed upwards in the next bottom-up checkpoint. - /// Partitioned by subnet ID, in the sequence they must be presented. - /// TODO(rewarder): optimize this pair of data structures. - mapping(SubnetID => bytes32[]) pendingSummaries; - /// @notice Index over presentable summaries back to the subnet ID, so we can locate them quickly when they're presented. - /// Only used if the validator rewarder is non-zero. - /// TODO(rewarder): optimize this pair of data structures. - mapping(bytes32 => SubnetID) presentableSummaries; } library LibSubnetActorStorage { diff --git a/contracts/contracts/interfaces/IValidatorRewarder.sol b/contracts/contracts/reward/IValidatorRewarder.sol similarity index 81% rename from contracts/contracts/interfaces/IValidatorRewarder.sol rename to contracts/contracts/reward/IValidatorRewarder.sol index 6001dfb227..3a3e8e3de6 100644 --- a/contracts/contracts/interfaces/IValidatorRewarder.sol +++ b/contracts/contracts/reward/IValidatorRewarder.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.23; import {SubnetID} from "../structs/Subnet.sol"; +import {ActivitySummary} from "./ValidatorReward.sol"; /// @title ValidatorRewarder interface. /// @@ -12,7 +13,6 @@ import {SubnetID} from "../structs/Subnet.sol"; interface IValidatorRewarder { /// @notice Called by the subnet manager contract to instruct the rewarder to process the subnet summary and /// disburse any relevant rewards. - /// The /// @dev This method should revert if the summary is invalid; this will cause the - function disburseRewards(SubnetID memory id, ActivitySummary memory summary) external; + function disburseRewards(SubnetID calldata id, address validator, ActivitySummary calldata summary) external; } diff --git a/contracts/contracts/reward/ValidatorActivityTracker.sol b/contracts/contracts/reward/ValidatorActivityTracker.sol new file mode 100644 index 0000000000..b0c1344e57 --- /dev/null +++ b/contracts/contracts/reward/ValidatorActivityTracker.sol @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity ^0.8.23; + +import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; + +import {ValidatorSummary, ActivitySummary} from "./ValidatorReward.sol"; +import {SystemContract} from "../lib/LibGatewayActorStorage.sol"; + + +/// The validator reward facet for the child subnet, i.e. for child subnet to track validators's activies +/// and create the commitment. +contract ValidatorActivityTracker is SystemContract { + using EnumerableSet for EnumerableSet.AddressSet; + + /// @dev The starting height of validator's mining activities since the last purged block + uint64 startHeight; + /// @dev The list of validator who have participated in mining since `startHeight` + EnumerableSet.AddressSet validators; + /// Tracks the number of blocks a validator has committed since `startHeight` + mapping(address => uint64) blocksCommitted; + + /// Validators claim their reward for doing work in the child subnet + function recordValidatorActivity(address validator) external systemActorOnly { + blocksCommitted[validator] += 1; + + if (!validators.contains(validator)) { + validators.add(validator); + } + } + + /// Reads the current validator summary + function getSummary() external view returns(ActivitySummary memory summary) { + summary.blockRange = [startHeight, block.number]; + + // prepare the activities + uint256 num_validators = validators.length(); + + summary.activities = new ValidatorSummary[](num_validators); + for (uint256 i = 0; i < num_validators; ) { + address validator = validators.at(i); + bytes memory metadata = new bytes(0); + + summary.activities[i] = ValidatorSummary({ + validator: validator, + blocksCommitted: blocksCommitted[validator], + metadata: metadata + }); + + unchecked { + i++; + } + } + } + + /// Reads the current validator summary and purge the data accordingly + /// @dev Call this method only when bottom up checkpoint needs to be created + function purgeActivities() external systemActorOnly { + // prepare the activities + uint256 num_validators = validators.length(); + + for (uint256 i = num_validators - 1; i >= 0; ) { + address validator = validators.at(i); + + delete blocksCommitted[validator]; + validators.remove(validator); + + unchecked { + if (i == 0) { break; } + i--; + } + } + + startHeight = uint64(block.number); + } +} diff --git a/contracts/contracts/reward/ValidatorReward.sol b/contracts/contracts/reward/ValidatorReward.sol new file mode 100644 index 0000000000..e880cb1309 --- /dev/null +++ b/contracts/contracts/reward/ValidatorReward.sol @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity ^0.8.23; + +/// The commitment for the validator rewards that child subnet submits to the parent subnet +/// together with a bottom up checkpoint +struct ValidatorRewardCommitment { + /// The commitment for the parent subnet + bytes32 commitment; + + // TODO: add relayed rewarder commitment +} + +/// The summary for a single validator +struct ValidatorSummary { + /// @dev The validator whose activity we're reporting about. + address validator; + /// @dev The number of blocks committed by each validator in the position they appear in the validators array. + /// If there is a configuration change applied at this checkpoint, this carries information about the _old_ validator set. + uint64 blocksCommitted; + /// @dev Other metadata + bytes metadata; +} + +/// A summary of validator's activity in the child subnet. This is submitted to the parent for reward distribution. +struct ActivitySummary { + /// @dev The block range the activity summary spans; these are the local heights of the start and the end, inclusive. + uint256[2] blockRange; + ValidatorSummary[] activities; +} + +library LibActivitySummary { + function numValidators(ActivitySummary calldata self) internal pure returns(uint64) { + return uint64(self.activities.length); + } + + function commitment(ActivitySummary calldata self) internal pure returns(bytes32) { + return keccak256(abi.encode(self)); + } + + function containsValidator(ActivitySummary calldata self, address validator) internal pure returns(bool) { + uint256 len = self.activities.length; + for (uint256 i = 0; i < len; ) { + if (self.activities[i].validator == validator) { + return true; + } + + unchecked { + i++; + } + } + + return false; + } +} \ No newline at end of file diff --git a/contracts/contracts/reward/ValidatorRewardParentFacet.sol b/contracts/contracts/reward/ValidatorRewardParentFacet.sol new file mode 100644 index 0000000000..5be88e76fe --- /dev/null +++ b/contracts/contracts/reward/ValidatorRewardParentFacet.sol @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity ^0.8.23; + +import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; + +import {Pausable} from "../lib/LibPausable.sol"; +import {ReentrancyGuard} from "../lib/LibReentrancyGuard.sol"; +import {NotValidator, SubnetNoTargetCommitment, CommitmentAlreadyInitialized, ValidatorAlreadyClaimed} from "../errors/IPCErrors.sol"; +import {ValidatorSummary, ActivitySummary, LibActivitySummary} from "./ValidatorReward.sol"; +import {IValidatorRewarder} from "./IValidatorRewarder.sol"; +import {SubnetIDHelper} from "../lib/SubnetIDHelper.sol"; +import {SubnetID} from "../structs/Subnet.sol"; + +/// The validator reward facet for the parent subnet, i.e. for validators in the child subnet +/// to claim their reward in the parent subnet, which should be the current subnet this facet +/// is deployed. +contract ValidatorRewardParentFacet is ReentrancyGuard, Pausable { + using LibActivitySummary for ActivitySummary; + + /// Validators claim their reward for doing work in the child subnet + function claim(SubnetID calldata subnetId, ActivitySummary calldata summary) external nonReentrant whenNotPaused { + if (!summary.containsValidator(msg.sender)) { + revert NotValidator(msg.sender); + } + + // todo: check subnet is active + + ValidatorRewardParentStorage storage s = LibValidatorRewardParent.facetStorage(); + + if (s.validatorRewarder == address(0)) { + handleRelay(); + } else { + handleDistribution(s, subnetId, summary); + } + } + + function handleRelay() internal pure { + revert("not implemented yet"); + } + + function handleDistribution(ValidatorRewardParentStorage storage s, SubnetID calldata subnetId, ActivitySummary calldata summary) internal { + bytes32 commitment = summary.commitment(); + + LibValidatorRewardParent.ensureValidCommitment(s, commitment, subnetId); + + LibValidatorRewardParent.validatorTryClaim(s, commitment, msg.sender); + IValidatorRewarder(s.validatorRewarder).disburseRewards(subnetId, msg.sender, summary); + + LibValidatorRewardParent.tryPurgeCommitment(s, subnetId, commitment, summary.numValidators()); + } +} + +/// The activity summary commiment that is currently under reward distribution +struct RewardDistribution { + /// The checkpoint height that this distribution + uint64 checkpointHeight; + /// The list of validators that have claimed the reward + EnumerableSet.AddressSet claimed; +} + +/// Used by the SubnetActor to track the rewards for each validator +struct ValidatorRewardParentStorage { + /// @notice The contract address for validator rewarder + address validatorRewarder; + /// @notice Summaries pending to be processed. + /// If the validator rewarder is non-zero, these denote summaries presentable at this level. + /// If the validator rewarder is zero, these summaries must be relayed upwards in the next bottom-up checkpoint. + /// Partitioned by subnet ID (hash), in the sequence they must be presented. + mapping(bytes32 => EnumerableSet.Bytes32Set) commitmentsToDistribution; + /// @notice Index over presentable summaries back to the subnet ID, so we can locate them quickly when they're presented. + /// Only used if the validator rewarder is non-zero. + /// TODO(rewarder): optimize this pair of data structures. + mapping(bytes32 => RewardDistribution) distributions; +} + +library LibValidatorRewardParent { + bytes32 private constant NAMESPACE = keccak256("validator.reward.parent"); + + using SubnetIDHelper for SubnetID; + using EnumerableSet for EnumerableSet.Bytes32Set; + using EnumerableSet for EnumerableSet.AddressSet; + + function facetStorage() internal pure returns (ValidatorRewardParentStorage storage ds) { + bytes32 position = NAMESPACE; + assembly { + ds.slot := position + } + return ds; + } + + function listCommitments(ValidatorRewardParentStorage storage ds, SubnetID calldata subnetId) internal view returns(bytes32[] memory) { + bytes32 subnetKey = subnetId.toHash(); + return ds.commitmentsToDistribution[subnetKey].values(); + } + + function initNewDistribution(bytes32 commitment, SubnetID calldata subnetId) internal { + ValidatorRewardParentStorage storage ds = facetStorage(); + + bytes32 subnetKey = subnetId.toHash(); + + if (ds.commitmentsToDistribution[subnetKey].contains(commitment)) { + revert CommitmentAlreadyInitialized(); + } + + ds.commitmentsToDistribution[subnetKey].add(commitment); + ds.distributions[commitment].checkpointHeight = uint64(block.number); + } + + function ensureValidCommitment(ValidatorRewardParentStorage storage ds, bytes32 commitment, SubnetID calldata subnetId) internal view { + bytes32 subnetKey = subnetId.toHash(); + + if (!ds.commitmentsToDistribution[subnetKey].contains(commitment)) { + revert SubnetNoTargetCommitment(); + } + + // Note: ideally we should check the commitment actually exists, but we dont have to as + // Note: the code will ensure if commitmentsToDistribution contains the commitment, + // Note: the commitment will have distribution + // if (ds.distributions[commitment].checkpointHeight == 0) { + // revert CommitmentNotFound(); + // } + } + + /// Validator tries to claim the reward. The validator can only claim the reward if the validator + /// has not claimed before + function validatorTryClaim(ValidatorRewardParentStorage storage ds, bytes32 commitment, address validator) internal { + if(ds.distributions[commitment].claimed.contains(validator)) { + revert ValidatorAlreadyClaimed(); + } + + ds.distributions[commitment].claimed.add(validator); + } + + /// Try to remove the commiment in the target subnet when ALL VALIDATORS HAVE CLAIMED. + function tryPurgeCommitment(ValidatorRewardParentStorage storage ds, SubnetID calldata subnetId, bytes32 commitment, uint64 totalValidators) internal { + bytes32 subnetKey = subnetId.toHash(); + + if (ds.distributions[commitment].claimed.length() < totalValidators) { + return; + } + + ds.commitmentsToDistribution[subnetKey].remove(commitment); + delete ds.distributions[commitment]; + } +} \ No newline at end of file diff --git a/contracts/contracts/structs/CrossNet.sol b/contracts/contracts/structs/CrossNet.sol index 8c96fb1b9a..598eabdacf 100644 --- a/contracts/contracts/structs/CrossNet.sol +++ b/contracts/contracts/structs/CrossNet.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.23; import {SubnetID, IPCAddress} from "./Subnet.sol"; import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; +import {ValidatorRewardCommitment} from "../reward/ValidatorReward.sol"; uint64 constant MAX_MSGS_PER_BATCH = 10; uint256 constant BATCH_PERIOD = 100; @@ -29,11 +30,8 @@ struct BottomUpCheckpoint { uint64 nextConfigurationNumber; /// @dev Batch of messages to execute. IpcEnvelope[] msgs; - /// @dev A commitment to the summary of our chain activity since the previous checkpoint and this one. - bytes32 summary; - /// @dev Summaries relayed upwards from descendants of this subnet. - /// NOTE: Not merkelized to keep it simple, but we will merkelize later to scale better. - RelayedSummary[] relayedSummaries; + /// @dev The validator reward commitment from child subnet to parent subnet + ValidatorRewardCommitment validatorReward; } struct ActivitySummary { diff --git a/contracts/contracts/subnet/SubnetActorRewardFacet.sol b/contracts/contracts/subnet/SubnetActorRewardFacet.sol index 4862e352bb..f34ed6f1ff 100644 --- a/contracts/contracts/subnet/SubnetActorRewardFacet.sol +++ b/contracts/contracts/subnet/SubnetActorRewardFacet.sol @@ -14,17 +14,6 @@ import {Asset} from "../structs/Subnet.sol"; contract SubnetActorRewardFacet is SubnetActorModifiers, ReentrancyGuard, Pausable { using AssetHelper for Asset; - // TODO(rewards): add this function so that relayers can submit summaries to process reward payouts in the root network. - function submitSummary(SubnetID subnetId, ActivitySummary memory summary) external nonReentrant whenNotPaused { - // TODO(rewards): - // 1. Check that the subnet is active. - // 2. Check that the subnet has a non-zero ValidatorRewarder. - // 3. Hash the activity summary to get the commitment. - // 4. Validate that the commitment is pending and presentable, and validate that it matches the expected subnet. - // 5. Send the summary to the ValidatorRewarder#disburseRewards. - // 6. If OK (not reverted), drop the summary from the pending and presentable commitments. - } - /// @notice Validator claims their released collateral. function claim() external nonReentrant whenNotPaused { uint256 amount = LibStaking.claimCollateral(msg.sender); diff --git a/contracts/test/IntegrationTestBase.sol b/contracts/test/IntegrationTestBase.sol index 84eaf54565..d826ff63fb 100644 --- a/contracts/test/IntegrationTestBase.sol +++ b/contracts/test/IntegrationTestBase.sol @@ -46,6 +46,9 @@ import {GatewayFacetsHelper} from "./helpers/GatewayFacetsHelper.sol"; import {SubnetActorFacetsHelper} from "./helpers/SubnetActorFacetsHelper.sol"; import {DiamondFacetsHelper} from "./helpers/DiamondFacetsHelper.sol"; +import {ValidatorRewardCommitment} from "../../contracts/reward/ValidatorReward.sol"; + + struct TestSubnetDefinition { GatewayDiamond gateway; address gatewayAddr; @@ -913,7 +916,8 @@ contract IntegrationTestBase is Test, TestParams, TestRegistry, TestSubnetActor, blockHeight: h, blockHash: keccak256(abi.encode(h)), nextConfigurationNumber: nextConfigNum - 1, - msgs: new IpcEnvelope[](0) + msgs: new IpcEnvelope[](0), + validatorReward: ValidatorRewardCommitment({ commitment: bytes32(uint256(nextConfigNum))}) }); vm.deal(address(saDiamond), 100 ether); diff --git a/contracts/test/helpers/SelectorLibrary.sol b/contracts/test/helpers/SelectorLibrary.sol index adcdc5e965..4d3b853a69 100644 --- a/contracts/test/helpers/SelectorLibrary.sol +++ b/contracts/test/helpers/SelectorLibrary.sol @@ -48,7 +48,7 @@ library SelectorLibrary { if (keccak256(abi.encodePacked(facetName)) == keccak256(abi.encodePacked("CheckpointingFacet"))) { return abi.decode( - hex"0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000453b4e7bf00000000000000000000000000000000000000000000000000000000fba0fa4d00000000000000000000000000000000000000000000000000000000dc749b0500000000000000000000000000000000000000000000000000000000ac81837900000000000000000000000000000000000000000000000000000000", + hex"0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000453b4e7bf00000000000000000000000000000000000000000000000000000000024ad232000000000000000000000000000000000000000000000000000000005e6de63200000000000000000000000000000000000000000000000000000000ac81837900000000000000000000000000000000000000000000000000000000", (bytes4[]) ); } @@ -97,7 +97,7 @@ library SelectorLibrary { if (keccak256(abi.encodePacked(facetName)) == keccak256(abi.encodePacked("SubnetActorCheckpointingFacet"))) { return abi.decode( - hex"0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000279979f5700000000000000000000000000000000000000000000000000000000cc2dc2b900000000000000000000000000000000000000000000000000000000", + hex"00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002e72f09ca00000000000000000000000000000000000000000000000000000000cc2dc2b900000000000000000000000000000000000000000000000000000000", (bytes4[]) ); } diff --git a/contracts/test/integration/GatewayDiamond.t.sol b/contracts/test/integration/GatewayDiamond.t.sol index 0759b98f08..5acf8954a0 100644 --- a/contracts/test/integration/GatewayDiamond.t.sol +++ b/contracts/test/integration/GatewayDiamond.t.sol @@ -39,6 +39,8 @@ import {GatewayFacetsHelper} from "../helpers/GatewayFacetsHelper.sol"; import {SubnetActorDiamond} from "../../contracts/SubnetActorDiamond.sol"; import {SubnetActorFacetsHelper} from "../helpers/SubnetActorFacetsHelper.sol"; +import {ValidatorRewardCommitment} from "../../contracts/reward/ValidatorReward.sol"; + contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeTokenMock { using SubnetIDHelper for SubnetID; using CrossMsgHelper for IpcEnvelope; @@ -1067,7 +1069,8 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT blockHeight: 0, blockHash: keccak256("block1"), nextConfigurationNumber: 1, - msgs: new IpcEnvelope[](0) + msgs: new IpcEnvelope[](0), + validatorReward: ValidatorRewardCommitment({ commitment: bytes32(0)}) }); BottomUpCheckpoint memory checkpoint = BottomUpCheckpoint({ @@ -1075,7 +1078,8 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT blockHeight: gatewayDiamond.getter().bottomUpCheckPeriod(), blockHash: keccak256("block1"), nextConfigurationNumber: 1, - msgs: new IpcEnvelope[](0) + msgs: new IpcEnvelope[](0), + validatorReward: ValidatorRewardCommitment({ commitment: bytes32(0)}) }); // failed to create a checkpoint with zero membership weight @@ -1116,7 +1120,8 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT blockHeight: d, blockHash: keccak256("block"), nextConfigurationNumber: 2, - msgs: new IpcEnvelope[](0) + msgs: new IpcEnvelope[](0), + validatorReward: ValidatorRewardCommitment({ commitment: bytes32(0)}) }); vm.startPrank(FilAddress.SYSTEM_ACTOR); @@ -1139,7 +1144,8 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT blockHeight: gatewayDiamond.getter().bottomUpCheckPeriod(), blockHash: keccak256("block1"), nextConfigurationNumber: 1, - msgs: new IpcEnvelope[](0) + msgs: new IpcEnvelope[](0), + validatorReward: ValidatorRewardCommitment({ commitment: bytes32(0)}) }); vm.expectRevert(InvalidCheckpointSource.selector); @@ -1160,7 +1166,8 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT blockHeight: gatewayDiamond.getter().bottomUpCheckPeriod(), blockHash: keccak256("block1"), nextConfigurationNumber: 1, - msgs: new IpcEnvelope[](0) + msgs: new IpcEnvelope[](0), + validatorReward: ValidatorRewardCommitment({ commitment: bytes32(0)}) }); vm.prank(caller); @@ -1206,7 +1213,8 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT blockHeight: gatewayDiamond.getter().bottomUpCheckPeriod(), blockHash: keccak256("block1"), nextConfigurationNumber: 1, - msgs: msgs + msgs: msgs, + validatorReward: ValidatorRewardCommitment({ commitment: bytes32(0)}) }); vm.prank(caller); @@ -1226,7 +1234,8 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT blockHeight: gatewayDiamond.getter().bottomUpCheckPeriod(), blockHash: keccak256("block1"), nextConfigurationNumber: 1, - msgs: new IpcEnvelope[](0) + msgs: new IpcEnvelope[](0), + validatorReward: ValidatorRewardCommitment({ commitment: bytes32(0)}) }); BottomUpCheckpoint memory checkpoint2 = BottomUpCheckpoint({ @@ -1234,7 +1243,8 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT blockHeight: 2 * gatewayDiamond.getter().bottomUpCheckPeriod(), blockHash: keccak256("block2"), nextConfigurationNumber: 1, - msgs: new IpcEnvelope[](0) + msgs: new IpcEnvelope[](0), + validatorReward: ValidatorRewardCommitment({ commitment: bytes32(0)}) }); // create a checkpoint @@ -1298,7 +1308,8 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT blockHeight: gatewayDiamond.getter().bottomUpCheckPeriod(), blockHash: keccak256("block"), nextConfigurationNumber: 1, - msgs: new IpcEnvelope[](0) + msgs: new IpcEnvelope[](0), + validatorReward: ValidatorRewardCommitment({ commitment: bytes32(0)}) }); // create a checkpoint @@ -1359,7 +1370,8 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT blockHeight: gatewayDiamond.getter().bottomUpCheckPeriod(), blockHash: keccak256("block"), nextConfigurationNumber: 1, - msgs: new IpcEnvelope[](0) + msgs: new IpcEnvelope[](0), + validatorReward: ValidatorRewardCommitment({ commitment: bytes32(0)}) }); // create a checkpoint @@ -1442,7 +1454,8 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT blockHeight: gatewayDiamond.getter().bottomUpCheckPeriod(), blockHash: keccak256("block"), nextConfigurationNumber: 1, - msgs: new IpcEnvelope[](0) + msgs: new IpcEnvelope[](0), + validatorReward: ValidatorRewardCommitment({ commitment: bytes32(0)}) }); // create a checkpoint @@ -1476,7 +1489,8 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT blockHeight: gatewayDiamond.getter().bottomUpCheckPeriod(), blockHash: keccak256("block"), nextConfigurationNumber: 1, - msgs: new IpcEnvelope[](0) + msgs: new IpcEnvelope[](0), + validatorReward: ValidatorRewardCommitment({ commitment: bytes32(0)}) }); // create a checkpoint @@ -1520,7 +1534,8 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT blockHeight: gatewayDiamond.getter().bottomUpCheckPeriod(), blockHash: keccak256("block"), nextConfigurationNumber: 1, - msgs: new IpcEnvelope[](0) + msgs: new IpcEnvelope[](0), + validatorReward: ValidatorRewardCommitment({ commitment: bytes32(0)}) }); // create a checkpoint @@ -1568,7 +1583,8 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT blockHeight: i * gatewayDiamond.getter().bottomUpCheckPeriod(), blockHash: keccak256("block"), nextConfigurationNumber: 1, - msgs: new IpcEnvelope[](0) + msgs: new IpcEnvelope[](0), + validatorReward: ValidatorRewardCommitment({ commitment: bytes32(0)}) }); gatewayDiamond.checkpointer().createBottomUpCheckpoint(checkpoint, membershipRoot, 10); @@ -1631,7 +1647,8 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT blockHeight: gatewayDiamond.getter().bottomUpCheckPeriod(), blockHash: keccak256("block1"), nextConfigurationNumber: 1, - msgs: msgs + msgs: msgs, + validatorReward: ValidatorRewardCommitment({ commitment: bytes32(0)}) }); vm.prank(caller); diff --git a/contracts/test/integration/GatewayDiamondToken.t.sol b/contracts/test/integration/GatewayDiamondToken.t.sol index 74f6223cf3..3955f24a9f 100644 --- a/contracts/test/integration/GatewayDiamondToken.t.sol +++ b/contracts/test/integration/GatewayDiamondToken.t.sol @@ -33,6 +33,9 @@ import {IERC20Errors} from "@openzeppelin/contracts/interfaces/draft-IERC6093.so import {GatewayFacetsHelper} from "../helpers/GatewayFacetsHelper.sol"; +import {ValidatorRewardCommitment} from "../../contracts/reward/ValidatorReward.sol"; + + contract GatewayDiamondTokenTest is Test, IntegrationTestBase { using SubnetIDHelper for SubnetID; using CrossMsgHelper for IpcEnvelope; @@ -163,7 +166,8 @@ contract GatewayDiamondTokenTest is Test, IntegrationTestBase { blockHash: blockhash(block.number), blockHeight: gatewayDiamond.getter().bottomUpCheckPeriod(), nextConfigurationNumber: 0, - msgs: msgs + msgs: msgs, + validatorReward: ValidatorRewardCommitment({ commitment: bytes32(0)}) }); vm.prank(address(saDiamond)); @@ -221,7 +225,8 @@ contract GatewayDiamondTokenTest is Test, IntegrationTestBase { blockHash: blockhash(block.number), blockHeight: gatewayDiamond.getter().bottomUpCheckPeriod(), nextConfigurationNumber: 0, - msgs: msgs + msgs: msgs, + validatorReward: ValidatorRewardCommitment({ commitment: bytes32(0)}) }); // Verify that we received the call and that the recipient has the tokens. diff --git a/contracts/test/integration/MultiSubnet.t.sol b/contracts/test/integration/MultiSubnet.t.sol index 088f828fb0..1ac22d585f 100644 --- a/contracts/test/integration/MultiSubnet.t.sol +++ b/contracts/test/integration/MultiSubnet.t.sol @@ -45,6 +45,8 @@ import {SubnetActorFacetsHelper} from "../helpers/SubnetActorFacetsHelper.sol"; import "forge-std/console.sol"; +import {ValidatorRewardCommitment} from "../../contracts/reward/ValidatorReward.sol"; + contract MultiSubnetTest is Test, IntegrationTestBase { using SubnetIDHelper for SubnetID; using CrossMsgHelper for IpcEnvelope; @@ -1348,7 +1350,8 @@ contract MultiSubnetTest is Test, IntegrationTestBase { blockHeight: batch.blockHeight, blockHash: keccak256("block1"), nextConfigurationNumber: 0, - msgs: batch.msgs + msgs: batch.msgs, + validatorReward: ValidatorRewardCommitment({ commitment: bytes32(0)}) }); vm.startPrank(FilAddress.SYSTEM_ACTOR); @@ -1377,7 +1380,8 @@ contract MultiSubnetTest is Test, IntegrationTestBase { blockHeight: e, blockHash: keccak256("block1"), nextConfigurationNumber: 0, - msgs: msgs + msgs: msgs, + validatorReward: ValidatorRewardCommitment({ commitment: bytes32(0)}) }); vm.startPrank(FilAddress.SYSTEM_ACTOR); diff --git a/contracts/test/integration/SubnetActorDiamond.t.sol b/contracts/test/integration/SubnetActorDiamond.t.sol index 85f57e1356..442b653e9b 100644 --- a/contracts/test/integration/SubnetActorDiamond.t.sol +++ b/contracts/test/integration/SubnetActorDiamond.t.sol @@ -43,6 +43,8 @@ import {GatewayFacetsHelper} from "../helpers/GatewayFacetsHelper.sol"; import {ERC20PresetFixedSupply} from "../helpers/ERC20PresetFixedSupply.sol"; import {SubnetValidatorGater} from "../../contracts/examples/SubnetValidatorGater.sol"; +import {ValidatorRewardCommitment} from "../../contracts/reward/ValidatorReward.sol"; + contract SubnetActorDiamondTest is Test, IntegrationTestBase { using SubnetIDHelper for SubnetID; using FilAddress for address; @@ -688,7 +690,8 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { blockHeight: saDiamond.getter().bottomUpCheckPeriod(), blockHash: keccak256("block1"), nextConfigurationNumber: 0, - msgs: msgs + msgs: msgs, + validatorReward: ValidatorRewardCommitment({ commitment: bytes32(0)}) }); BottomUpCheckpoint memory checkpointWithIncorrectHeight = BottomUpCheckpoint({ @@ -696,7 +699,8 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { blockHeight: 1, blockHash: keccak256("block1"), nextConfigurationNumber: 0, - msgs: msgs + msgs: msgs, + validatorReward: ValidatorRewardCommitment({ commitment: bytes32(0)}) }); vm.deal(address(saDiamond), 100 ether); @@ -796,7 +800,8 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { blockHeight: 1, blockHash: keccak256("block1"), nextConfigurationNumber: 0, - msgs: msgs + msgs: msgs, + validatorReward: ValidatorRewardCommitment({ commitment: bytes32(0)}) }); BottomUpCheckpoint memory checkpointWithIncorrectHeight = BottomUpCheckpoint({ @@ -804,7 +809,8 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { blockHeight: 1, blockHash: keccak256("block1"), nextConfigurationNumber: 0, - msgs: new IpcEnvelope[](0) + msgs: new IpcEnvelope[](0), + validatorReward: ValidatorRewardCommitment({ commitment: bytes32(uint256(1))}) }); vm.deal(address(saDiamond), 100 ether); @@ -833,6 +839,7 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { // submit another again checkpoint.blockHeight = 2; + checkpoint.validatorReward = ValidatorRewardCommitment({ commitment: bytes32(uint256(2))}); hash = keccak256(abi.encode(checkpoint)); for (uint256 i = 0; i < 3; i++) { @@ -888,7 +895,8 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { blockHeight: 1, blockHash: keccak256("block1"), nextConfigurationNumber: 0, - msgs: msgs + msgs: msgs, + validatorReward: ValidatorRewardCommitment({ commitment: bytes32(uint256(1) )}) }); submitCheckpointInternal(checkpoint, validators, signatures, keys); require(saDiamond.getter().lastBottomUpCheckpointHeight() == 1, " checkpoint height incorrect"); @@ -900,7 +908,8 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { blockHeight: 3, blockHash: keccak256("block2"), nextConfigurationNumber: 0, - msgs: msgs + msgs: msgs, + validatorReward: ValidatorRewardCommitment({ commitment: bytes32(uint256(2))}) }); submitCheckpointInternal(checkpoint, validators, signatures, keys); require(saDiamond.getter().lastBottomUpCheckpointHeight() == 3, " checkpoint height incorrect"); @@ -911,7 +920,8 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { blockHeight: 2, blockHash: keccak256("block1"), nextConfigurationNumber: 0, - msgs: msgs + msgs: msgs, + validatorReward: ValidatorRewardCommitment({ commitment: bytes32(uint256(3))}) }); vm.expectRevert(BottomUpCheckpointAlreadySubmitted.selector); submitCheckpointInternal(checkpoint, validators, signatures, keys); @@ -922,7 +932,8 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { blockHeight: saDiamond.getter().bottomUpCheckPeriod() + 1, blockHash: keccak256("block2"), nextConfigurationNumber: 0, - msgs: msgs + msgs: msgs, + validatorReward: ValidatorRewardCommitment({ commitment: bytes32(uint256(4))}) }); vm.expectRevert(CannotSubmitFutureCheckpoint.selector); submitCheckpointInternal(checkpoint, validators, signatures, keys); @@ -932,7 +943,8 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { blockHeight: saDiamond.getter().bottomUpCheckPeriod(), blockHash: keccak256("block2"), nextConfigurationNumber: 0, - msgs: new IpcEnvelope[](0) + msgs: new IpcEnvelope[](0), + validatorReward: ValidatorRewardCommitment({ commitment: bytes32(uint256(5))}) }); submitCheckpointInternal(checkpoint, validators, signatures, keys); require( @@ -945,7 +957,8 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { blockHeight: saDiamond.getter().bottomUpCheckPeriod() + 1, blockHash: keccak256("block2"), nextConfigurationNumber: 0, - msgs: msgs + msgs: msgs, + validatorReward: ValidatorRewardCommitment({ commitment: bytes32(uint256(6))}) }); submitCheckpointInternal(checkpoint, validators, signatures, keys); require( @@ -958,7 +971,8 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { blockHeight: saDiamond.getter().bottomUpCheckPeriod() + 2, blockHash: keccak256("block2"), nextConfigurationNumber: 0, - msgs: msgs + msgs: msgs, + validatorReward: ValidatorRewardCommitment({ commitment: bytes32(uint256(7))}) }); submitCheckpointInternal(checkpoint, validators, signatures, keys); require( @@ -971,7 +985,8 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { blockHeight: saDiamond.getter().bottomUpCheckPeriod() + 3, blockHash: keccak256("block2"), nextConfigurationNumber: 0, - msgs: new IpcEnvelope[](0) + msgs: new IpcEnvelope[](0), + validatorReward: ValidatorRewardCommitment({ commitment: bytes32(uint256(8))}) }); vm.expectRevert(InvalidCheckpointEpoch.selector); submitCheckpointInternal(checkpoint, validators, signatures, keys); @@ -981,7 +996,8 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { blockHeight: saDiamond.getter().bottomUpCheckPeriod() * 2, blockHash: keccak256("block2"), nextConfigurationNumber: 0, - msgs: new IpcEnvelope[](0) + msgs: new IpcEnvelope[](0), + validatorReward: ValidatorRewardCommitment({ commitment: bytes32(uint256(9))}) }); submitCheckpointInternal(checkpoint, validators, signatures, keys); require( @@ -994,7 +1010,8 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { blockHeight: saDiamond.getter().bottomUpCheckPeriod() * 3, blockHash: keccak256("block2"), nextConfigurationNumber: 0, - msgs: new IpcEnvelope[](0) + msgs: new IpcEnvelope[](0), + validatorReward: ValidatorRewardCommitment({ commitment: bytes32(uint256(10))}) }); submitCheckpointInternal(checkpoint, validators, signatures, keys); require( @@ -1035,7 +1052,8 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { blockHeight: saDiamond.getter().bottomUpCheckPeriod(), blockHash: keccak256("block1"), nextConfigurationNumber: 0, - msgs: msgs + msgs: msgs, + validatorReward: ValidatorRewardCommitment({ commitment: bytes32(0)}) }); vm.deal(address(saDiamond), 100 ether); @@ -1078,7 +1096,8 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { blockHeight: 2 * saDiamond.getter().bottomUpCheckPeriod(), blockHash: keccak256("block2"), nextConfigurationNumber: 0, - msgs: msgs + msgs: msgs, + validatorReward: ValidatorRewardCommitment({ commitment: bytes32(uint256(1))}) }); hash = keccak256(abi.encode(checkpoint)); diff --git a/fendermint/vm/interpreter/src/fvm/checkpoint.rs b/fendermint/vm/interpreter/src/fvm/checkpoint.rs index 19890678d2..04b40653e2 100644 --- a/fendermint/vm/interpreter/src/fvm/checkpoint.rs +++ b/fendermint/vm/interpreter/src/fvm/checkpoint.rs @@ -110,6 +110,7 @@ where block_hash, next_configuration_number, msgs, + validatorReward: state.validator_reward().get_activities_summary()?.commitment(), }; // Save the checkpoint in the ledger. @@ -118,6 +119,8 @@ where .create_bottom_up_checkpoint(state, checkpoint.clone(), &curr_power_table.0) .context("failed to store checkpoint")?; + state.validator_reward().purge_activities()?; + // Figure out the power updates if there was some change in the configuration. let power_updates = if next_configuration_number == 0 { PowerUpdates(Vec::new()) diff --git a/fendermint/vm/interpreter/src/fvm/exec.rs b/fendermint/vm/interpreter/src/fvm/exec.rs index f53e630a9e..65df21af6d 100644 --- a/fendermint/vm/interpreter/src/fvm/exec.rs +++ b/fendermint/vm/interpreter/src/fvm/exec.rs @@ -186,6 +186,8 @@ where } async fn end(&self, mut state: Self::State) -> anyhow::Result<(Self::State, Self::EndOutput)> { + state.validator_reward_mut().track_block_mined(); + // TODO: Consider doing this async, since it's purely informational and not consensus-critical. let _ = checkpoint::emit_trace_if_check_checkpoint_finalized(&self.gateway, &mut state) .inspect_err(|e| { diff --git a/fendermint/vm/reward/Cargo.toml b/fendermint/vm/reward/Cargo.toml new file mode 100644 index 0000000000..40324a289f --- /dev/null +++ b/fendermint/vm/reward/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "fendermint_vm_validater_reward" +description = "The top down checkpoint mechanism for ipc protocol integration" +version = "0.1.0" +authors.workspace = true +edition.workspace = true +license.workspace = true + +[dependencies] +anyhow = { workspace = true } +async-trait = { workspace = true } +cid = { workspace = true } +ethers = { workspace = true, option = true } +fvm_shared = { workspace = true } +thiserror = { workspace = true } +tracing = { workspace = true } + +fendermint_crypto = {path = "../../crypto" } +fendermint_vm_event = { path = "../event" } +fendermint_tracing = { path = "../../tracing" } +ipc-observability = { workspace = true } \ No newline at end of file diff --git a/fendermint/vm/reward/src/lib.rs b/fendermint/vm/reward/src/lib.rs new file mode 100644 index 0000000000..c628a40e87 --- /dev/null +++ b/fendermint/vm/reward/src/lib.rs @@ -0,0 +1,32 @@ +// Copyright 2022-2024 Protocol Labs +// SPDX-License-Identifier: Apache-2.0, MIT + +use std::collections::HashMap; +use std::fmt::Debug; +use fvm_shared::clock::ChainEpoch; +use fendermint_crypto::PublicKey; + +pub struct BlockMined { + validator: PublicKey, + height: ChainEpoch, +} + +#[derive(Debug, Clone)] +pub struct ActivitySummary { + block_range: (ChainEpoch, ChainEpoch), + details: HashMap +} + +/// Tracks the validator activities in the current blockchain +pub trait ValidatorActivityTracker { + type ValidatorSummaryDetail: Clone + Debug + TryInto>; + + /// Mark the validator has mined the target block. + fn track_block_mined(&mut self, block: BlockMined) -> anyhow::Result<()>; + + /// Get the validators activities summary since the checkpoint height + fn get_activities_summary(&self) -> anyhow::Result>; + + /// Purge the current validator activities summary + fn purge_activities(&mut self) -> anyhow::Result<()>; +} \ No newline at end of file From 80eabcb34dbd3f156faa2fc2a37f803e063c0def Mon Sep 17 00:00:00 2001 From: cryptoAtwill Date: Tue, 8 Oct 2024 21:57:39 +0800 Subject: [PATCH 69/69] merkle proof --- Cargo.lock | 41 +++--- Cargo.toml | 2 + contracts/binding/src/lib.rs | 61 ++++++++ .../Activity.sol} | 8 +- .../IValidatorRewarder.sol | 4 +- .../activities/LibActivityMerkleVerifier.sol | 19 +++ .../ValidatorActivityTracker.sol | 4 +- .../ValidatorRewardParentFacet.sol | 135 +++++++++++++----- contracts/contracts/errors/IPCErrors.sol | 1 + .../gateway/router/CheckpointingFacet.sol | 5 +- contracts/contracts/structs/CrossNet.sol | 6 +- contracts/test/IntegrationTestBase.sol | 4 +- .../test/integration/GatewayDiamond.t.sol | 32 ++--- .../integration/GatewayDiamondToken.t.sol | 6 +- contracts/test/integration/MultiSubnet.t.sol | 6 +- .../test/integration/SubnetActorDiamond.t.sol | 36 ++--- fendermint/actors/activity-tracker/Cargo.toml | 35 +++++ fendermint/actors/activity-tracker/src/lib.rs | 86 +++++++++++ .../actors/activity-tracker/src/state.rs | 81 +++++++++++ .../src/activities/mod.rs} | 3 + .../vm/interpreter/src/fvm/checkpoint.rs | 7 - fendermint/vm/interpreter/src/lib.rs | 1 + fendermint/vm/reward/Cargo.toml | 21 --- 23 files changed, 462 insertions(+), 142 deletions(-) rename contracts/contracts/{reward/ValidatorReward.sol => activities/Activity.sol} (89%) rename contracts/contracts/{reward => activities}/IValidatorRewarder.sol (81%) create mode 100644 contracts/contracts/activities/LibActivityMerkleVerifier.sol rename contracts/contracts/{reward => activities}/ValidatorActivityTracker.sol (95%) rename contracts/contracts/{reward => activities}/ValidatorRewardParentFacet.sol (53%) create mode 100644 fendermint/actors/activity-tracker/Cargo.toml create mode 100644 fendermint/actors/activity-tracker/src/lib.rs create mode 100644 fendermint/actors/activity-tracker/src/state.rs rename fendermint/vm/{reward/src/lib.rs => interpreter/src/activities/mod.rs} (90%) delete mode 100644 fendermint/vm/reward/Cargo.toml diff --git a/Cargo.lock b/Cargo.lock index 1c4886caf9..177a9922b4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2777,6 +2777,27 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "fendermint_actor_activity_tracker" +version = "0.1.0" +dependencies = [ + "anyhow", + "cid", + "fil_actor_eam", + "fil_actors_evm_shared", + "fil_actors_runtime", + "frc42_dispatch", + "fvm_ipld_blockstore", + "fvm_ipld_encoding", + "fvm_shared", + "hex-literal 0.4.1", + "log", + "multihash 0.18.1", + "num-derive 0.3.3", + "num-traits", + "serde", +] + [[package]] name = "fendermint_actor_chainmetadata" version = "0.1.0" @@ -3472,26 +3493,6 @@ dependencies = [ "tracing-subscriber", ] -[[package]] -name = "fendermint_vm_validater_reward" -version = "0.1.0" -dependencies = [ - "anyhow", - "async-trait", - "cid", - "ethers", - "fendermint_crypto", - "fendermint_tracing", - "fendermint_vm_event", - "fvm_ipld_encoding", - "fvm_shared", - "hex", - "ipc-observability", - "serde", - "thiserror", - "tracing", -] - [[package]] name = "ff" version = "0.12.1" diff --git a/Cargo.toml b/Cargo.toml index ccc3db09b4..a91fa5cf0a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,7 @@ members = [ "fendermint/vm/*", "fendermint/actors", "fendermint/actors/chainmetadata", + "fendermint/actors/activity-tracker", ] [workspace.package] @@ -217,6 +218,7 @@ fil_actor_eam = { git = "https://github.com/filecoin-project/builtin-actors", ta fil_actors_runtime = { git = "https://github.com/filecoin-project/builtin-actors", tag = "v12.0.0" } fendermint_actor_eam = { path = "./fendermint/actors/eam" } +fendermint_actor_activity_tracker= { path = "./fendermint/actors/activity-tracker" } cid = { version = "0.10.1", default-features = false, features = [ "serde-codec", diff --git a/contracts/binding/src/lib.rs b/contracts/binding/src/lib.rs index 508f367f36..dd59524fb0 100644 --- a/contracts/binding/src/lib.rs +++ b/contracts/binding/src/lib.rs @@ -1,3 +1,64 @@ // DO NOT EDIT! This file was generated by build.rs #[macro_use] mod convert; +#[allow(clippy::all)] +pub mod checkpointing_facet; +#[allow(clippy::all)] +pub mod diamond_cut_facet; +#[allow(clippy::all)] +pub mod diamond_loupe_facet; +#[allow(clippy::all)] +pub mod gateway_diamond; +#[allow(clippy::all)] +pub mod gateway_getter_facet; +#[allow(clippy::all)] +pub mod gateway_manager_facet; +#[allow(clippy::all)] +pub mod gateway_messenger_facet; +#[allow(clippy::all)] +pub mod i_diamond; +#[allow(clippy::all)] +pub mod lib_gateway; +#[allow(clippy::all)] +pub mod lib_quorum; +#[allow(clippy::all)] +pub mod lib_staking; +#[allow(clippy::all)] +pub mod lib_staking_change_log; +#[allow(clippy::all)] +pub mod ownership_facet; +#[allow(clippy::all)] +pub mod register_subnet_facet; +#[allow(clippy::all)] +pub mod subnet_actor_checkpointing_facet; +#[allow(clippy::all)] +pub mod subnet_actor_diamond; +#[allow(clippy::all)] +pub mod subnet_actor_getter_facet; +#[allow(clippy::all)] +pub mod subnet_actor_manager_facet; +#[allow(clippy::all)] +pub mod subnet_actor_pause_facet; +#[allow(clippy::all)] +pub mod subnet_actor_reward_facet; +#[allow(clippy::all)] +pub mod subnet_getter_facet; +#[allow(clippy::all)] +pub mod subnet_registry_diamond; +#[allow(clippy::all)] +pub mod top_down_finality_facet; +#[allow(clippy::all)] +pub mod xnet_messaging_facet; + +// The list of contracts need to convert FvmAddress to fvm_shared::Address +fvm_address_conversion!(gateway_manager_facet); +fvm_address_conversion!(gateway_getter_facet); +fvm_address_conversion!(xnet_messaging_facet); +fvm_address_conversion!(gateway_messenger_facet); +fvm_address_conversion!(subnet_actor_checkpointing_facet); +fvm_address_conversion!(subnet_actor_getter_facet); +fvm_address_conversion!(lib_gateway); + +// The list of contracts that need to convert common types between each other +common_type_conversion!(subnet_actor_getter_facet, checkpointing_facet); +common_type_conversion!(subnet_actor_getter_facet, xnet_messaging_facet); diff --git a/contracts/contracts/reward/ValidatorReward.sol b/contracts/contracts/activities/Activity.sol similarity index 89% rename from contracts/contracts/reward/ValidatorReward.sol rename to contracts/contracts/activities/Activity.sol index e880cb1309..a9792598e7 100644 --- a/contracts/contracts/reward/ValidatorReward.sol +++ b/contracts/contracts/activities/Activity.sol @@ -1,11 +1,11 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.23; -/// The commitment for the validator rewards that child subnet submits to the parent subnet +/// The commitments for the child subnet activities that should be submitted to the parent subnet /// together with a bottom up checkpoint -struct ValidatorRewardCommitment { - /// The commitment for the parent subnet - bytes32 commitment; +struct ActivityCommitment { + /// The activity summary for validators + bytes32 summary; // TODO: add relayed rewarder commitment } diff --git a/contracts/contracts/reward/IValidatorRewarder.sol b/contracts/contracts/activities/IValidatorRewarder.sol similarity index 81% rename from contracts/contracts/reward/IValidatorRewarder.sol rename to contracts/contracts/activities/IValidatorRewarder.sol index 3a3e8e3de6..8468ea3040 100644 --- a/contracts/contracts/reward/IValidatorRewarder.sol +++ b/contracts/contracts/activities/IValidatorRewarder.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.23; import {SubnetID} from "../structs/Subnet.sol"; -import {ActivitySummary} from "./ValidatorReward.sol"; +import {ValidatorSummary} from "./Activity.sol"; /// @title ValidatorRewarder interface. /// @@ -14,5 +14,5 @@ interface IValidatorRewarder { /// @notice Called by the subnet manager contract to instruct the rewarder to process the subnet summary and /// disburse any relevant rewards. /// @dev This method should revert if the summary is invalid; this will cause the - function disburseRewards(SubnetID calldata id, address validator, ActivitySummary calldata summary) external; + function disburseRewards(SubnetID calldata id, ValidatorSummary calldata summary) external; } diff --git a/contracts/contracts/activities/LibActivityMerkleVerifier.sol b/contracts/contracts/activities/LibActivityMerkleVerifier.sol new file mode 100644 index 0000000000..4c98c085b6 --- /dev/null +++ b/contracts/contracts/activities/LibActivityMerkleVerifier.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity ^0.8.23; + +import {SubnetID} from "../structs/Subnet.sol"; +import {InvalidProof} from "../errors/IPCErrors.sol"; +import {ValidatorSummary} from "./Activity.sol"; +import {MerkleProof} from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol"; + +/// Verifies the proof to the commitment in subnet activity summary +library LibActivityMerkleVerifier { + function ensureValidProof(bytes32 commitment, ValidatorSummary calldata summary, bytes32[] calldata proof) internal pure { + // Constructing leaf: https://github.com/OpenZeppelin/merkle-tree#leaf-hash + bytes32 leaf = keccak256(bytes.concat(keccak256(abi.encode(summary.validator, summary.blocksCommitted, summary.metadata)))); + bool valid = MerkleProof.verify({proof: proof, root: commitment, leaf: leaf}); + if (!valid) { + revert InvalidProof(); + } + } +} diff --git a/contracts/contracts/reward/ValidatorActivityTracker.sol b/contracts/contracts/activities/ValidatorActivityTracker.sol similarity index 95% rename from contracts/contracts/reward/ValidatorActivityTracker.sol rename to contracts/contracts/activities/ValidatorActivityTracker.sol index b0c1344e57..e0bd032288 100644 --- a/contracts/contracts/reward/ValidatorActivityTracker.sol +++ b/contracts/contracts/activities/ValidatorActivityTracker.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.23; import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; -import {ValidatorSummary, ActivitySummary} from "./ValidatorReward.sol"; +import {ValidatorSummary, ActivitySummary} from "./Activity.sol"; import {SystemContract} from "../lib/LibGatewayActorStorage.sol"; @@ -54,7 +54,7 @@ contract ValidatorActivityTracker is SystemContract { /// Reads the current validator summary and purge the data accordingly /// @dev Call this method only when bottom up checkpoint needs to be created - function purgeActivities() external systemActorOnly { + function purge_activities() external systemActorOnly { // prepare the activities uint256 num_validators = validators.length(); diff --git a/contracts/contracts/reward/ValidatorRewardParentFacet.sol b/contracts/contracts/activities/ValidatorRewardParentFacet.sol similarity index 53% rename from contracts/contracts/reward/ValidatorRewardParentFacet.sol rename to contracts/contracts/activities/ValidatorRewardParentFacet.sol index 5be88e76fe..e7e7b9ebc4 100644 --- a/contracts/contracts/reward/ValidatorRewardParentFacet.sol +++ b/contracts/contracts/activities/ValidatorRewardParentFacet.sol @@ -2,14 +2,16 @@ pragma solidity ^0.8.23; import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; +import {EnumerableMap} from "@openzeppelin/contracts/utils/structs/EnumerableMap.sol"; import {Pausable} from "../lib/LibPausable.sol"; import {ReentrancyGuard} from "../lib/LibReentrancyGuard.sol"; import {NotValidator, SubnetNoTargetCommitment, CommitmentAlreadyInitialized, ValidatorAlreadyClaimed} from "../errors/IPCErrors.sol"; -import {ValidatorSummary, ActivitySummary, LibActivitySummary} from "./ValidatorReward.sol"; +import {ValidatorSummary, ActivitySummary, LibActivitySummary} from "./Activity.sol"; import {IValidatorRewarder} from "./IValidatorRewarder.sol"; import {SubnetIDHelper} from "../lib/SubnetIDHelper.sol"; import {SubnetID} from "../structs/Subnet.sol"; +import {LibActivityMerkleVerifier} from "./LibActivityMerkleVerifier.sol"; /// The validator reward facet for the parent subnet, i.e. for validators in the child subnet /// to claim their reward in the parent subnet, which should be the current subnet this facet @@ -18,35 +20,46 @@ contract ValidatorRewardParentFacet is ReentrancyGuard, Pausable { using LibActivitySummary for ActivitySummary; /// Validators claim their reward for doing work in the child subnet - function claim(SubnetID calldata subnetId, ActivitySummary calldata summary) external nonReentrant whenNotPaused { - if (!summary.containsValidator(msg.sender)) { + function claim( + SubnetID calldata subnetId, + uint64 checkpointHeight, + ValidatorSummary calldata summary, + bytes32[] calldata proof + ) external nonReentrant whenNotPaused { + // note: No need to check if the subnet is active. If the subnet is not active, the checkpointHeight + // note: will never exist. + + if (msg.sender != summary.validator) { revert NotValidator(msg.sender); } - // todo: check subnet is active - ValidatorRewardParentStorage storage s = LibValidatorRewardParent.facetStorage(); if (s.validatorRewarder == address(0)) { - handleRelay(); - } else { - handleDistribution(s, subnetId, summary); + return handleRelay(); } + + bytes32 commitment = LibValidatorRewardParent.ensureValidCommitment(s, subnetId, checkpointHeight); + LibActivityMerkleVerifier.ensureValidProof(commitment, summary, proof); + + handleDistribution(s, subnetId, commitment, summary); + } function handleRelay() internal pure { revert("not implemented yet"); } - function handleDistribution(ValidatorRewardParentStorage storage s, SubnetID calldata subnetId, ActivitySummary calldata summary) internal { - bytes32 commitment = summary.commitment(); - - LibValidatorRewardParent.ensureValidCommitment(s, commitment, subnetId); + function handleDistribution( + ValidatorRewardParentStorage storage s, + SubnetID calldata subnetId, + bytes32 commitment, + ValidatorSummary calldata summary + ) internal { + LibValidatorRewardParent.validatorTryClaim(s, commitment, summary.validator); + IValidatorRewarder(s.validatorRewarder).disburseRewards(subnetId, summary); - LibValidatorRewardParent.validatorTryClaim(s, commitment, msg.sender); - IValidatorRewarder(s.validatorRewarder).disburseRewards(subnetId, msg.sender, summary); - - LibValidatorRewardParent.tryPurgeCommitment(s, subnetId, commitment, summary.numValidators()); + // LibValidatorRewardParent.tryPurgeCommitment(s, subnetId, commitment, summary.numValidators()); } } @@ -54,6 +67,8 @@ contract ValidatorRewardParentFacet is ReentrancyGuard, Pausable { struct RewardDistribution { /// The checkpoint height that this distribution uint64 checkpointHeight; + /// Total number of valdators to claim the distribution + uint64 totalValidators; /// The list of validators that have claimed the reward EnumerableSet.AddressSet claimed; } @@ -62,63 +77,100 @@ struct RewardDistribution { struct ValidatorRewardParentStorage { /// @notice The contract address for validator rewarder address validatorRewarder; - /// @notice Summaries pending to be processed. + /// @notice Summaries look up pending to be processed. /// If the validator rewarder is non-zero, these denote summaries presentable at this level. /// If the validator rewarder is zero, these summaries must be relayed upwards in the next bottom-up checkpoint. - /// Partitioned by subnet ID (hash), in the sequence they must be presented. - mapping(bytes32 => EnumerableSet.Bytes32Set) commitmentsToDistribution; + /// Partitioned by subnet ID (hash) then by checkpoint block height in the child subnet to the commitment + mapping(bytes32 => EnumerableMap.Bytes32ToBytes32Map) commitments; /// @notice Index over presentable summaries back to the subnet ID, so we can locate them quickly when they're presented. /// Only used if the validator rewarder is non-zero. - /// TODO(rewarder): optimize this pair of data structures. mapping(bytes32 => RewardDistribution) distributions; } +/// The payload for list commitments query +struct ListCommimentDetail { + /// The child subnet checkpoint height + uint64 checkpointHeight; + /// The actual commiment of the activities + bytes32 commitment; +} + library LibValidatorRewardParent { bytes32 private constant NAMESPACE = keccak256("validator.reward.parent"); using SubnetIDHelper for SubnetID; - using EnumerableSet for EnumerableSet.Bytes32Set; + using EnumerableMap for EnumerableMap.Bytes32ToBytes32Map; using EnumerableSet for EnumerableSet.AddressSet; - function facetStorage() internal pure returns (ValidatorRewardParentStorage storage ds) { - bytes32 position = NAMESPACE; - assembly { - ds.slot := position - } - return ds; - } + // =========== External library functions ============= + + function listCommitments(SubnetID calldata subnetId) internal view returns(ListCommimentDetail[] memory listDetails) { + ValidatorRewardParentStorage storage ds = facetStorage(); - function listCommitments(ValidatorRewardParentStorage storage ds, SubnetID calldata subnetId) internal view returns(bytes32[] memory) { bytes32 subnetKey = subnetId.toHash(); - return ds.commitmentsToDistribution[subnetKey].values(); + + uint256 size = ds.commitments[subnetKey].length(); + listDetails = new ListCommimentDetail[](size); + + for (uint256 i = 0; i < size; ) { + (bytes32 heightBytes32, bytes32 commitment) = ds.commitments[subnetKey].at(i); + + listDetails[i] = ListCommimentDetail({ + checkpointHeight: uint64(uint256(heightBytes32)), + commitment: commitment + }); + + unchecked { + i++; + } + } + + return listDetails; } - function initNewDistribution(bytes32 commitment, SubnetID calldata subnetId) internal { + function initNewDistribution(uint64 checkpointHeight, bytes32 commitment, SubnetID calldata subnetId) internal { ValidatorRewardParentStorage storage ds = facetStorage(); bytes32 subnetKey = subnetId.toHash(); - if (ds.commitmentsToDistribution[subnetKey].contains(commitment)) { + if (ds.distributions[commitment].checkpointHeight != 0) { revert CommitmentAlreadyInitialized(); } - ds.commitmentsToDistribution[subnetKey].add(commitment); - ds.distributions[commitment].checkpointHeight = uint64(block.number); + ds.commitments[subnetKey].set(bytes32(uint256(checkpointHeight)), commitment); + ds.distributions[commitment].checkpointHeight = checkpointHeight; } - function ensureValidCommitment(ValidatorRewardParentStorage storage ds, bytes32 commitment, SubnetID calldata subnetId) internal view { + // ============ Internal library functions ============ + + function facetStorage() internal pure returns (ValidatorRewardParentStorage storage ds) { + bytes32 position = NAMESPACE; + assembly { + ds.slot := position + } + return ds; + } + + function ensureValidCommitment( + ValidatorRewardParentStorage storage ds, + SubnetID calldata subnetId, + uint64 checkpointHeight + ) internal view returns(bytes32) { bytes32 subnetKey = subnetId.toHash(); - if (!ds.commitmentsToDistribution[subnetKey].contains(commitment)) { + (bool exists, bytes32 commitment) = ds.commitments[subnetKey].tryGet(bytes32(uint256(checkpointHeight))); + if (!exists) { revert SubnetNoTargetCommitment(); } // Note: ideally we should check the commitment actually exists, but we dont have to as - // Note: the code will ensure if commitmentsToDistribution contains the commitment, + // Note: the code will ensure if commitments contains the commitment, // Note: the commitment will have distribution // if (ds.distributions[commitment].checkpointHeight == 0) { // revert CommitmentNotFound(); // } + + return commitment; } /// Validator tries to claim the reward. The validator can only claim the reward if the validator @@ -132,14 +184,19 @@ library LibValidatorRewardParent { } /// Try to remove the commiment in the target subnet when ALL VALIDATORS HAVE CLAIMED. - function tryPurgeCommitment(ValidatorRewardParentStorage storage ds, SubnetID calldata subnetId, bytes32 commitment, uint64 totalValidators) internal { + function tryPurgeCommitment( + ValidatorRewardParentStorage storage ds, + SubnetID calldata subnetId, + bytes32 commitment, + uint64 totalValidators + ) internal { bytes32 subnetKey = subnetId.toHash(); if (ds.distributions[commitment].claimed.length() < totalValidators) { return; } - ds.commitmentsToDistribution[subnetKey].remove(commitment); + delete ds.commitments[subnetKey]; delete ds.distributions[commitment]; } } \ No newline at end of file diff --git a/contracts/contracts/errors/IPCErrors.sol b/contracts/contracts/errors/IPCErrors.sol index cd629c07d4..f65028d14c 100644 --- a/contracts/contracts/errors/IPCErrors.sol +++ b/contracts/contracts/errors/IPCErrors.sol @@ -83,6 +83,7 @@ error ValidatorPowerChangeDenied(); error CommitmentAlreadyInitialized(); error SubnetNoTargetCommitment(); error ValidatorAlreadyClaimed(); +error InvalidProof(); enum InvalidXnetMessageReason { diff --git a/contracts/contracts/gateway/router/CheckpointingFacet.sol b/contracts/contracts/gateway/router/CheckpointingFacet.sol index 91f54d2cbf..b4df277572 100644 --- a/contracts/contracts/gateway/router/CheckpointingFacet.sol +++ b/contracts/contracts/gateway/router/CheckpointingFacet.sol @@ -16,7 +16,7 @@ import {BatchNotCreated, InvalidBatchEpoch, BatchAlreadyExists, NotEnoughSubnetC import {CrossMsgHelper} from "../../lib/CrossMsgHelper.sol"; import {IpcEnvelope, SubnetID} from "../../structs/CrossNet.sol"; import {SubnetIDHelper} from "../../lib/SubnetIDHelper.sol"; -import {LibValidatorRewardParent} from "../../reward/ValidatorRewardParentFacet.sol"; +import {LibValidatorRewardParent} from "../../activities/ValidatorRewardParentFacet.sol"; contract CheckpointingFacet is GatewayActorModifiers { using SubnetIDHelper for SubnetID; @@ -44,7 +44,8 @@ contract CheckpointingFacet is GatewayActorModifiers { execBottomUpMsgs(checkpoint.msgs, subnet); LibValidatorRewardParent.initNewDistribution( - checkpoint.validatorReward.commitment, + uint64(checkpoint.blockHeight), + checkpoint.activities.summary, checkpoint.subnetID ); } diff --git a/contracts/contracts/structs/CrossNet.sol b/contracts/contracts/structs/CrossNet.sol index 598eabdacf..c11d21236b 100644 --- a/contracts/contracts/structs/CrossNet.sol +++ b/contracts/contracts/structs/CrossNet.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.23; import {SubnetID, IPCAddress} from "./Subnet.sol"; import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; -import {ValidatorRewardCommitment} from "../reward/ValidatorReward.sol"; +import {ActivityCommitment} from "../activities/Activity.sol"; uint64 constant MAX_MSGS_PER_BATCH = 10; uint256 constant BATCH_PERIOD = 100; @@ -30,8 +30,8 @@ struct BottomUpCheckpoint { uint64 nextConfigurationNumber; /// @dev Batch of messages to execute. IpcEnvelope[] msgs; - /// @dev The validator reward commitment from child subnet to parent subnet - ValidatorRewardCommitment validatorReward; + /// @dev The activity commitment from child subnet to parent subnet + ActivityCommitment activities; } struct ActivitySummary { diff --git a/contracts/test/IntegrationTestBase.sol b/contracts/test/IntegrationTestBase.sol index d826ff63fb..967319f1a3 100644 --- a/contracts/test/IntegrationTestBase.sol +++ b/contracts/test/IntegrationTestBase.sol @@ -46,7 +46,7 @@ import {GatewayFacetsHelper} from "./helpers/GatewayFacetsHelper.sol"; import {SubnetActorFacetsHelper} from "./helpers/SubnetActorFacetsHelper.sol"; import {DiamondFacetsHelper} from "./helpers/DiamondFacetsHelper.sol"; -import {ValidatorRewardCommitment} from "../../contracts/reward/ValidatorReward.sol"; +import {ActivityCommitment} from "../../contracts/activities/Activity.sol"; struct TestSubnetDefinition { @@ -917,7 +917,7 @@ contract IntegrationTestBase is Test, TestParams, TestRegistry, TestSubnetActor, blockHash: keccak256(abi.encode(h)), nextConfigurationNumber: nextConfigNum - 1, msgs: new IpcEnvelope[](0), - validatorReward: ValidatorRewardCommitment({ commitment: bytes32(uint256(nextConfigNum))}) + activities: ActivityCommitment({ summary: bytes32(uint256(nextConfigNum))}) }); vm.deal(address(saDiamond), 100 ether); diff --git a/contracts/test/integration/GatewayDiamond.t.sol b/contracts/test/integration/GatewayDiamond.t.sol index 5acf8954a0..d9e45d0598 100644 --- a/contracts/test/integration/GatewayDiamond.t.sol +++ b/contracts/test/integration/GatewayDiamond.t.sol @@ -39,7 +39,7 @@ import {GatewayFacetsHelper} from "../helpers/GatewayFacetsHelper.sol"; import {SubnetActorDiamond} from "../../contracts/SubnetActorDiamond.sol"; import {SubnetActorFacetsHelper} from "../helpers/SubnetActorFacetsHelper.sol"; -import {ValidatorRewardCommitment} from "../../contracts/reward/ValidatorReward.sol"; +import {ActivityCommitment} from "../../contracts/activities/Activity.sol"; contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeTokenMock { using SubnetIDHelper for SubnetID; @@ -1070,7 +1070,7 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT blockHash: keccak256("block1"), nextConfigurationNumber: 1, msgs: new IpcEnvelope[](0), - validatorReward: ValidatorRewardCommitment({ commitment: bytes32(0)}) + activities: ActivityCommitment({ summary: bytes32(0)}) }); BottomUpCheckpoint memory checkpoint = BottomUpCheckpoint({ @@ -1079,7 +1079,7 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT blockHash: keccak256("block1"), nextConfigurationNumber: 1, msgs: new IpcEnvelope[](0), - validatorReward: ValidatorRewardCommitment({ commitment: bytes32(0)}) + activities: ActivityCommitment({ summary: bytes32(0)}) }); // failed to create a checkpoint with zero membership weight @@ -1121,7 +1121,7 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT blockHash: keccak256("block"), nextConfigurationNumber: 2, msgs: new IpcEnvelope[](0), - validatorReward: ValidatorRewardCommitment({ commitment: bytes32(0)}) + activities: ActivityCommitment({ summary: bytes32(0)}) }); vm.startPrank(FilAddress.SYSTEM_ACTOR); @@ -1145,7 +1145,7 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT blockHash: keccak256("block1"), nextConfigurationNumber: 1, msgs: new IpcEnvelope[](0), - validatorReward: ValidatorRewardCommitment({ commitment: bytes32(0)}) + activities: ActivityCommitment({ summary: bytes32(0)}) }); vm.expectRevert(InvalidCheckpointSource.selector); @@ -1167,7 +1167,7 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT blockHash: keccak256("block1"), nextConfigurationNumber: 1, msgs: new IpcEnvelope[](0), - validatorReward: ValidatorRewardCommitment({ commitment: bytes32(0)}) + activities: ActivityCommitment({ summary: bytes32(0)}) }); vm.prank(caller); @@ -1214,7 +1214,7 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT blockHash: keccak256("block1"), nextConfigurationNumber: 1, msgs: msgs, - validatorReward: ValidatorRewardCommitment({ commitment: bytes32(0)}) + activities: ActivityCommitment({ summary: bytes32(0)}) }); vm.prank(caller); @@ -1235,7 +1235,7 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT blockHash: keccak256("block1"), nextConfigurationNumber: 1, msgs: new IpcEnvelope[](0), - validatorReward: ValidatorRewardCommitment({ commitment: bytes32(0)}) + activities: ActivityCommitment({ summary: bytes32(0)}) }); BottomUpCheckpoint memory checkpoint2 = BottomUpCheckpoint({ @@ -1244,7 +1244,7 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT blockHash: keccak256("block2"), nextConfigurationNumber: 1, msgs: new IpcEnvelope[](0), - validatorReward: ValidatorRewardCommitment({ commitment: bytes32(0)}) + activities: ActivityCommitment({ summary: bytes32(0)}) }); // create a checkpoint @@ -1309,7 +1309,7 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT blockHash: keccak256("block"), nextConfigurationNumber: 1, msgs: new IpcEnvelope[](0), - validatorReward: ValidatorRewardCommitment({ commitment: bytes32(0)}) + activities: ActivityCommitment({ summary: bytes32(0)}) }); // create a checkpoint @@ -1371,7 +1371,7 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT blockHash: keccak256("block"), nextConfigurationNumber: 1, msgs: new IpcEnvelope[](0), - validatorReward: ValidatorRewardCommitment({ commitment: bytes32(0)}) + activities: ActivityCommitment({ summary: bytes32(0)}) }); // create a checkpoint @@ -1455,7 +1455,7 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT blockHash: keccak256("block"), nextConfigurationNumber: 1, msgs: new IpcEnvelope[](0), - validatorReward: ValidatorRewardCommitment({ commitment: bytes32(0)}) + activities: ActivityCommitment({ summary: bytes32(0)}) }); // create a checkpoint @@ -1490,7 +1490,7 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT blockHash: keccak256("block"), nextConfigurationNumber: 1, msgs: new IpcEnvelope[](0), - validatorReward: ValidatorRewardCommitment({ commitment: bytes32(0)}) + activities: ActivityCommitment({ summary: bytes32(0)}) }); // create a checkpoint @@ -1535,7 +1535,7 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT blockHash: keccak256("block"), nextConfigurationNumber: 1, msgs: new IpcEnvelope[](0), - validatorReward: ValidatorRewardCommitment({ commitment: bytes32(0)}) + activities: ActivityCommitment({ summary: bytes32(0)}) }); // create a checkpoint @@ -1584,7 +1584,7 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT blockHash: keccak256("block"), nextConfigurationNumber: 1, msgs: new IpcEnvelope[](0), - validatorReward: ValidatorRewardCommitment({ commitment: bytes32(0)}) + activities: ActivityCommitment({ summary: bytes32(0)}) }); gatewayDiamond.checkpointer().createBottomUpCheckpoint(checkpoint, membershipRoot, 10); @@ -1648,7 +1648,7 @@ contract GatewayActorDiamondTest is Test, IntegrationTestBase, SubnetWithNativeT blockHash: keccak256("block1"), nextConfigurationNumber: 1, msgs: msgs, - validatorReward: ValidatorRewardCommitment({ commitment: bytes32(0)}) + activities: ActivityCommitment({ summary: bytes32(0)}) }); vm.prank(caller); diff --git a/contracts/test/integration/GatewayDiamondToken.t.sol b/contracts/test/integration/GatewayDiamondToken.t.sol index 3955f24a9f..f191f152b5 100644 --- a/contracts/test/integration/GatewayDiamondToken.t.sol +++ b/contracts/test/integration/GatewayDiamondToken.t.sol @@ -33,7 +33,7 @@ import {IERC20Errors} from "@openzeppelin/contracts/interfaces/draft-IERC6093.so import {GatewayFacetsHelper} from "../helpers/GatewayFacetsHelper.sol"; -import {ValidatorRewardCommitment} from "../../contracts/reward/ValidatorReward.sol"; +import {ActivityCommitment} from "../../contracts/activities/Activity.sol"; contract GatewayDiamondTokenTest is Test, IntegrationTestBase { @@ -167,7 +167,7 @@ contract GatewayDiamondTokenTest is Test, IntegrationTestBase { blockHeight: gatewayDiamond.getter().bottomUpCheckPeriod(), nextConfigurationNumber: 0, msgs: msgs, - validatorReward: ValidatorRewardCommitment({ commitment: bytes32(0)}) + activities: ActivityCommitment({ summary: bytes32(0)}) }); vm.prank(address(saDiamond)); @@ -226,7 +226,7 @@ contract GatewayDiamondTokenTest is Test, IntegrationTestBase { blockHeight: gatewayDiamond.getter().bottomUpCheckPeriod(), nextConfigurationNumber: 0, msgs: msgs, - validatorReward: ValidatorRewardCommitment({ commitment: bytes32(0)}) + activities: ActivityCommitment({ summary: bytes32(0)}) }); // Verify that we received the call and that the recipient has the tokens. diff --git a/contracts/test/integration/MultiSubnet.t.sol b/contracts/test/integration/MultiSubnet.t.sol index 1ac22d585f..5fdee611f8 100644 --- a/contracts/test/integration/MultiSubnet.t.sol +++ b/contracts/test/integration/MultiSubnet.t.sol @@ -45,7 +45,7 @@ import {SubnetActorFacetsHelper} from "../helpers/SubnetActorFacetsHelper.sol"; import "forge-std/console.sol"; -import {ValidatorRewardCommitment} from "../../contracts/reward/ValidatorReward.sol"; +import {ActivityCommitment} from "../../contracts/activities/Activity.sol"; contract MultiSubnetTest is Test, IntegrationTestBase { using SubnetIDHelper for SubnetID; @@ -1351,7 +1351,7 @@ contract MultiSubnetTest is Test, IntegrationTestBase { blockHash: keccak256("block1"), nextConfigurationNumber: 0, msgs: batch.msgs, - validatorReward: ValidatorRewardCommitment({ commitment: bytes32(0)}) + activities: ActivityCommitment({ summary: bytes32(0)}) }); vm.startPrank(FilAddress.SYSTEM_ACTOR); @@ -1381,7 +1381,7 @@ contract MultiSubnetTest is Test, IntegrationTestBase { blockHash: keccak256("block1"), nextConfigurationNumber: 0, msgs: msgs, - validatorReward: ValidatorRewardCommitment({ commitment: bytes32(0)}) + activities: ActivityCommitment({ summary: bytes32(0)}) }); vm.startPrank(FilAddress.SYSTEM_ACTOR); diff --git a/contracts/test/integration/SubnetActorDiamond.t.sol b/contracts/test/integration/SubnetActorDiamond.t.sol index 442b653e9b..64ada4509b 100644 --- a/contracts/test/integration/SubnetActorDiamond.t.sol +++ b/contracts/test/integration/SubnetActorDiamond.t.sol @@ -43,7 +43,7 @@ import {GatewayFacetsHelper} from "../helpers/GatewayFacetsHelper.sol"; import {ERC20PresetFixedSupply} from "../helpers/ERC20PresetFixedSupply.sol"; import {SubnetValidatorGater} from "../../contracts/examples/SubnetValidatorGater.sol"; -import {ValidatorRewardCommitment} from "../../contracts/reward/ValidatorReward.sol"; +import {ActivityCommitment} from "../../contracts/activities/Activity.sol"; contract SubnetActorDiamondTest is Test, IntegrationTestBase { using SubnetIDHelper for SubnetID; @@ -691,7 +691,7 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { blockHash: keccak256("block1"), nextConfigurationNumber: 0, msgs: msgs, - validatorReward: ValidatorRewardCommitment({ commitment: bytes32(0)}) + activities: ActivityCommitment({ summary: bytes32(0)}) }); BottomUpCheckpoint memory checkpointWithIncorrectHeight = BottomUpCheckpoint({ @@ -700,7 +700,7 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { blockHash: keccak256("block1"), nextConfigurationNumber: 0, msgs: msgs, - validatorReward: ValidatorRewardCommitment({ commitment: bytes32(0)}) + activities: ActivityCommitment({ summary: bytes32(0)}) }); vm.deal(address(saDiamond), 100 ether); @@ -801,7 +801,7 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { blockHash: keccak256("block1"), nextConfigurationNumber: 0, msgs: msgs, - validatorReward: ValidatorRewardCommitment({ commitment: bytes32(0)}) + activities: ActivityCommitment({ summary: bytes32(0)}) }); BottomUpCheckpoint memory checkpointWithIncorrectHeight = BottomUpCheckpoint({ @@ -810,7 +810,7 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { blockHash: keccak256("block1"), nextConfigurationNumber: 0, msgs: new IpcEnvelope[](0), - validatorReward: ValidatorRewardCommitment({ commitment: bytes32(uint256(1))}) + activities: ActivityCommitment({ summary: bytes32(uint256(1))}) }); vm.deal(address(saDiamond), 100 ether); @@ -839,7 +839,7 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { // submit another again checkpoint.blockHeight = 2; - checkpoint.validatorReward = ValidatorRewardCommitment({ commitment: bytes32(uint256(2))}); + checkpoint.activities = ActivityCommitment({ summary: bytes32(uint256(2))}); hash = keccak256(abi.encode(checkpoint)); for (uint256 i = 0; i < 3; i++) { @@ -896,7 +896,7 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { blockHash: keccak256("block1"), nextConfigurationNumber: 0, msgs: msgs, - validatorReward: ValidatorRewardCommitment({ commitment: bytes32(uint256(1) )}) + activities: ActivityCommitment({ summary: bytes32(uint256(1) )}) }); submitCheckpointInternal(checkpoint, validators, signatures, keys); require(saDiamond.getter().lastBottomUpCheckpointHeight() == 1, " checkpoint height incorrect"); @@ -909,7 +909,7 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { blockHash: keccak256("block2"), nextConfigurationNumber: 0, msgs: msgs, - validatorReward: ValidatorRewardCommitment({ commitment: bytes32(uint256(2))}) + activities: ActivityCommitment({ summary: bytes32(uint256(2))}) }); submitCheckpointInternal(checkpoint, validators, signatures, keys); require(saDiamond.getter().lastBottomUpCheckpointHeight() == 3, " checkpoint height incorrect"); @@ -921,7 +921,7 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { blockHash: keccak256("block1"), nextConfigurationNumber: 0, msgs: msgs, - validatorReward: ValidatorRewardCommitment({ commitment: bytes32(uint256(3))}) + activities: ActivityCommitment({ summary: bytes32(uint256(3))}) }); vm.expectRevert(BottomUpCheckpointAlreadySubmitted.selector); submitCheckpointInternal(checkpoint, validators, signatures, keys); @@ -933,7 +933,7 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { blockHash: keccak256("block2"), nextConfigurationNumber: 0, msgs: msgs, - validatorReward: ValidatorRewardCommitment({ commitment: bytes32(uint256(4))}) + activities: ActivityCommitment({ summary: bytes32(uint256(4))}) }); vm.expectRevert(CannotSubmitFutureCheckpoint.selector); submitCheckpointInternal(checkpoint, validators, signatures, keys); @@ -944,7 +944,7 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { blockHash: keccak256("block2"), nextConfigurationNumber: 0, msgs: new IpcEnvelope[](0), - validatorReward: ValidatorRewardCommitment({ commitment: bytes32(uint256(5))}) + activities: ActivityCommitment({ summary: bytes32(uint256(5))}) }); submitCheckpointInternal(checkpoint, validators, signatures, keys); require( @@ -958,7 +958,7 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { blockHash: keccak256("block2"), nextConfigurationNumber: 0, msgs: msgs, - validatorReward: ValidatorRewardCommitment({ commitment: bytes32(uint256(6))}) + activities: ActivityCommitment({ summary: bytes32(uint256(6))}) }); submitCheckpointInternal(checkpoint, validators, signatures, keys); require( @@ -972,7 +972,7 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { blockHash: keccak256("block2"), nextConfigurationNumber: 0, msgs: msgs, - validatorReward: ValidatorRewardCommitment({ commitment: bytes32(uint256(7))}) + activities: ActivityCommitment({ summary: bytes32(uint256(7))}) }); submitCheckpointInternal(checkpoint, validators, signatures, keys); require( @@ -986,7 +986,7 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { blockHash: keccak256("block2"), nextConfigurationNumber: 0, msgs: new IpcEnvelope[](0), - validatorReward: ValidatorRewardCommitment({ commitment: bytes32(uint256(8))}) + activities: ActivityCommitment({ summary: bytes32(uint256(8))}) }); vm.expectRevert(InvalidCheckpointEpoch.selector); submitCheckpointInternal(checkpoint, validators, signatures, keys); @@ -997,7 +997,7 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { blockHash: keccak256("block2"), nextConfigurationNumber: 0, msgs: new IpcEnvelope[](0), - validatorReward: ValidatorRewardCommitment({ commitment: bytes32(uint256(9))}) + activities: ActivityCommitment({ summary: bytes32(uint256(9))}) }); submitCheckpointInternal(checkpoint, validators, signatures, keys); require( @@ -1011,7 +1011,7 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { blockHash: keccak256("block2"), nextConfigurationNumber: 0, msgs: new IpcEnvelope[](0), - validatorReward: ValidatorRewardCommitment({ commitment: bytes32(uint256(10))}) + activities: ActivityCommitment({ summary: bytes32(uint256(10))}) }); submitCheckpointInternal(checkpoint, validators, signatures, keys); require( @@ -1053,7 +1053,7 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { blockHash: keccak256("block1"), nextConfigurationNumber: 0, msgs: msgs, - validatorReward: ValidatorRewardCommitment({ commitment: bytes32(0)}) + activities: ActivityCommitment({ summary: bytes32(0)}) }); vm.deal(address(saDiamond), 100 ether); @@ -1097,7 +1097,7 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase { blockHash: keccak256("block2"), nextConfigurationNumber: 0, msgs: msgs, - validatorReward: ValidatorRewardCommitment({ commitment: bytes32(uint256(1))}) + activities: ActivityCommitment({ summary: bytes32(uint256(1))}) }); hash = keccak256(abi.encode(checkpoint)); diff --git a/fendermint/actors/activity-tracker/Cargo.toml b/fendermint/actors/activity-tracker/Cargo.toml new file mode 100644 index 0000000000..886262036e --- /dev/null +++ b/fendermint/actors/activity-tracker/Cargo.toml @@ -0,0 +1,35 @@ +[package] +name = "fendermint_actor_activity_tracker" +description = "Tracks fendermint block mining activities" +license.workspace = true +edition.workspace = true +authors.workspace = true +version = "0.1.0" + +[lib] +## lib is necessary for integration tests +## cdylib is necessary for Wasm build +crate-type = ["cdylib", "lib"] + +[dependencies] +anyhow = { workspace = true } +cid = { workspace = true } +fil_actor_eam = { workspace = true } +fil_actors_runtime = { workspace = true } +fvm_ipld_blockstore = { workspace = true } +fvm_ipld_encoding = { workspace = true } +fvm_shared = { workspace = true } +log = { workspace = true } +multihash = { workspace = true } +num-derive = { workspace = true } +num-traits = { workspace = true } +serde = { workspace = true } +hex-literal = { workspace = true } +frc42_dispatch = { workspace = true } + +[dev-dependencies] +fil_actors_evm_shared = { workspace = true } +fil_actors_runtime = { workspace = true, features = ["test_utils"] } + +[features] +fil-actor = ["fil_actors_runtime/fil-actor"] diff --git a/fendermint/actors/activity-tracker/src/lib.rs b/fendermint/actors/activity-tracker/src/lib.rs new file mode 100644 index 0000000000..7f9d895012 --- /dev/null +++ b/fendermint/actors/activity-tracker/src/lib.rs @@ -0,0 +1,86 @@ +// Copyright 2021-2023 Protocol Labs +// SPDX-License-Identifier: Apache-2.0, MIT + +use fil_actors_runtime::runtime::{ActorCode, Runtime}; +use fil_actors_runtime::{actor_dispatch, ActorError}; +use fil_actors_runtime::builtin::singletons::SYSTEM_ACTOR_ADDR; +use fvm_ipld_blockstore::Blockstore; +use fvm_shared::address::Address; +use fvm_shared::{ActorID, MethodNum}; +use num_derive::FromPrimitive; + +pub use crate::state::{ValidatorSummary}; +use crate::state::State; + +mod state; + +#[cfg(feature = "fil-actor")] +fil_actors_runtime::wasm_trampoline!(ActivityTrackerActor); + +pub const IPC_ACTIVITY_TRACKER_ACTOR_NAME: &str = "activity"; + +pub struct ActivityTrackerActor; + +#[derive(Deserialize, Serialize, Debug, Clone)] +pub struct BlockedMinedParams { + validator: Address, +} + +#[derive(FromPrimitive)] +#[repr(u64)] +pub enum Method { + BlockMined = frc42_dispatch::method_hash!("BlockMined"), + GetActivities = frc42_dispatch::method_hash!("GetActivities"), + PurgeActivities = frc42_dispatch::method_hash!("PurgeActivities"), +} + +impl ActivityTrackerActor { + pub fn constructor(rt: &impl Runtime) -> Result<(), ActorError> { + let st = State::new(rt.store())?; + rt.create(&st)?; + + Ok(()) + } + + pub fn block_mined(rt: &impl Runtime, block: BlockedMinedParams) -> Result<(), ActorError> { + rt.validate_immediate_caller_is(std::iter::once(&SYSTEM_ACTOR_ADDR))?; + + rt.transaction(|st: &mut State, rt| { + st.incr_validator_block_committed(rt, &block.validator) + })?; + + Ok(()) + } + + pub fn purge_activities(rt: &impl Runtime) -> Result<(), ActorError> { + rt.validate_immediate_caller_is(std::iter::once(&SYSTEM_ACTOR_ADDR))?; + + rt.transaction(|st: &mut State, rt| { + st.purge_validator_block_committed(rt)?; + st.reset_start_height(rt) + })?; + + Ok(()) + } + + pub fn get_activities(rt: &impl Runtime) -> Result, ActorError> { + let state: State = rt.state()?; + state.validator_activities(rt) + } + +} + +impl ActorCode for ActivityTrackerActor { + type Methods = Method; + + fn name() -> &'static str { + CHAINMETADATA_ACTOR_NAME + } + + actor_dispatch! { + Constructor => constructor, + PushBlockHash => push_block_hash, + LookbackLen => lookback_len, + GetBlockHash => get_block_hash, + } +} diff --git a/fendermint/actors/activity-tracker/src/state.rs b/fendermint/actors/activity-tracker/src/state.rs new file mode 100644 index 0000000000..299d596496 --- /dev/null +++ b/fendermint/actors/activity-tracker/src/state.rs @@ -0,0 +1,81 @@ +// Copyright 2021-2023 Protocol Labs +// SPDX-License-Identifier: Apache-2.0, MIT + +use std::collections::HashMap; +use cid::Cid; +use fil_actors_runtime::runtime::Runtime; +use fil_actors_runtime::{ActorError, Map2, DEFAULT_HAMT_CONFIG}; +use fvm_ipld_blockstore::Blockstore; +use fvm_shared::address::Address; +use fvm_shared::ActorID; +use fvm_shared::clock::ChainEpoch; +use serde::{Deserialize, Serialize}; + +pub type BlockCommittedMap = Map2; +pub type BlockCommitted = u64; + +#[derive(Deserialize, Serialize, Debug, Clone)] +pub struct ValidatorSummary { + validator: Address, + block_committed: BlockCommitted, +} + +#[derive(Deserialize, Serialize, Debug, Clone)] +pub struct State { + pub start_height: ChainEpoch, + pub blocks_committed: Cid, // BlockCommittedMap +} + +impl State { + pub fn new( + store: &BS, + ) -> Result { + let mut deployers_map = BlockCommittedMap::empty(store, DEFAULT_HAMT_CONFIG, "empty"); + Ok(State { start_height: 0, blocks_committed: deployers_map.flush()? }) + } + + pub fn reset_start_height(&mut self, rt: &impl Runtime) -> Result<(), ActorError> { + self.start_height = rt.curr_epoch(); + Ok(()) + } + + pub fn purge_validator_block_committed(&mut self, rt: &impl Runtime) -> Result<(), ActorError> { + let all_validators = self.validator_activities(rt)?; + let mut validators = BlockCommittedMap::load(rt.store(), &self.blocks_committed, DEFAULT_HAMT_CONFIG, "verifiers")?; + + for (v, _) in all_validators { + validators.delete(&v)?; + } + + self.blocks_committed = validators.flush()?; + + Ok(()) + } + + pub fn incr_validator_block_committed(&self, rt: &impl Runtime, validator: &Address) -> Result<(), ActorError> { + let mut validators = BlockCommittedMap::load(rt.store(), &self.blocks_committed, DEFAULT_HAMT_CONFIG, "verifiers")?; + + let v = if let Some(v) = validators.get(validator)? { + *v + 1 + } else { + 1 + }; + + validators.set(validator, v)?; + + Ok(()) + } + + pub fn validator_activities(&self, rt: &impl Runtime) -> Result, ActorError> { + let mut result = vec![]; + + let validators = BlockCommittedMap::load(rt.store(), &self.blocks_committed, DEFAULT_HAMT_CONFIG, "verifiers")?; + validators.for_each(|(k, v)| { + result.push(ValidatorSummary{ validator: k, block_committed: v}); + Ok(()) + })?; + + Ok(result) + } +} + diff --git a/fendermint/vm/reward/src/lib.rs b/fendermint/vm/interpreter/src/activities/mod.rs similarity index 90% rename from fendermint/vm/reward/src/lib.rs rename to fendermint/vm/interpreter/src/activities/mod.rs index c628a40e87..280de1aebc 100644 --- a/fendermint/vm/reward/src/lib.rs +++ b/fendermint/vm/interpreter/src/activities/mod.rs @@ -1,6 +1,9 @@ // Copyright 2022-2024 Protocol Labs // SPDX-License-Identifier: Apache-2.0, MIT +//! Tracks the current blockchain block mining activities and propagates to the parent subnet if +//! needed. + use std::collections::HashMap; use std::fmt::Debug; use fvm_shared::clock::ChainEpoch; diff --git a/fendermint/vm/interpreter/src/fvm/checkpoint.rs b/fendermint/vm/interpreter/src/fvm/checkpoint.rs index 04b40653e2..f3791f36c7 100644 --- a/fendermint/vm/interpreter/src/fvm/checkpoint.rs +++ b/fendermint/vm/interpreter/src/fvm/checkpoint.rs @@ -96,13 +96,6 @@ where let num_msgs = msgs.len(); - // TODO(rewards): query block producers for the blocks from the last checkpointed epoch to the current one. - // Ideally keep a live cache of block producers, append to it when new blocks are committed, and prune it when generating a checkpoint. - // But for now, we can try to keep it simple and query CometBFT, although that adds latency. - // If we do this, this method seems to be the quickest way: https://docs.cometbft.com/main/rpc/#/Info/block_search - - // TODO(rewards): populate the ActivitySummary struct with the information above, and pass it to the create_bottom_up_checkpoint call. - // Construct checkpoint. let checkpoint = BottomUpCheckpoint { subnet_id, diff --git a/fendermint/vm/interpreter/src/lib.rs b/fendermint/vm/interpreter/src/lib.rs index 40868b6cdf..deaa6ed117 100644 --- a/fendermint/vm/interpreter/src/lib.rs +++ b/fendermint/vm/interpreter/src/lib.rs @@ -10,6 +10,7 @@ pub mod signed; #[cfg(feature = "arb")] mod arb; +mod activities; /// Prepare and process transaction proposals. #[async_trait] diff --git a/fendermint/vm/reward/Cargo.toml b/fendermint/vm/reward/Cargo.toml deleted file mode 100644 index 40324a289f..0000000000 --- a/fendermint/vm/reward/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ -[package] -name = "fendermint_vm_validater_reward" -description = "The top down checkpoint mechanism for ipc protocol integration" -version = "0.1.0" -authors.workspace = true -edition.workspace = true -license.workspace = true - -[dependencies] -anyhow = { workspace = true } -async-trait = { workspace = true } -cid = { workspace = true } -ethers = { workspace = true, option = true } -fvm_shared = { workspace = true } -thiserror = { workspace = true } -tracing = { workspace = true } - -fendermint_crypto = {path = "../../crypto" } -fendermint_vm_event = { path = "../event" } -fendermint_tracing = { path = "../../tracing" } -ipc-observability = { workspace = true } \ No newline at end of file