diff --git a/.changelog/unreleased/improvements/2091-gas-fee-updates.md b/.changelog/unreleased/improvements/2091-gas-fee-updates.md new file mode 100644 index 0000000000..2f7fab3b89 --- /dev/null +++ b/.changelog/unreleased/improvements/2091-gas-fee-updates.md @@ -0,0 +1,3 @@ +- Updated the gas costs. Introduced a local validator configuration + parameter to set the accepted tokens and amounts for fees. + ([\#2091](https://github.com/anoma/namada/pull/2091)) \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 9da4a10125..e8593c48b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4088,6 +4088,7 @@ dependencies = [ "rand 0.8.5", "rand_core 0.6.4", "sha2 0.9.9", + "tempfile", ] [[package]] diff --git a/Makefile b/Makefile index 1fd4e64edc..def8ce2e16 100644 --- a/Makefile +++ b/Makefile @@ -248,7 +248,7 @@ clean: $(cargo) clean bench: - $(cargo) bench + $(cargo) bench --package namada_benchmarks build-doc: $(cargo) doc --no-deps diff --git a/apps/src/bin/namada-node/cli.rs b/apps/src/bin/namada-node/cli.rs index 7ee24251be..7477d3d6af 100644 --- a/apps/src/bin/namada-node/cli.rs +++ b/apps/src/bin/namada-node/cli.rs @@ -3,6 +3,7 @@ use eyre::{Context, Result}; use namada::types::time::{DateTimeUtc, Utc}; use namada_apps::cli::{self, cmds}; +use namada_apps::config::ValidatorLocalConfig; use namada_apps::node::ledger; pub fn main() -> Result<()> { @@ -57,6 +58,23 @@ pub fn main() -> Result<()> { ); } } + cmds::Config::UpdateLocalConfig(cmds::LocalConfig(args)) => { + // Validate the new config + let updated_config = std::fs::read(args.config_path).unwrap(); + let _validator_local_config: ValidatorLocalConfig = + toml::from_slice(&updated_config).unwrap(); + + // Update the validator configuration file with the new one + let config_path = ctx + .global_args + .base_dir + .join(format!( + "{}", + ctx.chain.unwrap().config.ledger.chain_id + )) + .join("validator_local_config.toml"); + std::fs::write(config_path, updated_config).unwrap(); + } }, } Ok(()) diff --git a/apps/src/lib/bench_utils.rs b/apps/src/lib/bench_utils.rs index 4f12c5b6b1..4c8eda1ae2 100644 --- a/apps/src/lib/bench_utils.rs +++ b/apps/src/lib/bench_utils.rs @@ -86,13 +86,13 @@ use tempfile::TempDir; use crate::cli::context::FromContext; use crate::cli::Context; +use crate::config; use crate::config::global::GlobalConfig; use crate::config::TendermintMode; use crate::facade::tendermint_proto::abci::RequestInitChain; use crate::facade::tendermint_proto::google::protobuf::Timestamp; use crate::node::ledger::shell::Shell; use crate::wallet::{defaults, CliWalletUtils}; -use crate::{config, wasm_loader}; pub const WASM_DIR: &str = "../wasm"; pub const TX_BOND_WASM: &str = "tx_bond.wasm"; @@ -111,6 +111,10 @@ pub const TX_WITHDRAW_WASM: &str = "tx_withdraw.wasm"; pub const TX_INIT_ACCOUNT_WASM: &str = "tx_init_account.wasm"; pub const TX_INIT_VALIDATOR_WASM: &str = "tx_init_validator.wasm"; +pub const TX_RESIGN_STEWARD: &str = "tx_resign_steward.wasm"; +pub const TX_UPDATE_STEWARD_COMMISSION: &str = + "tx_update_steward_commission.wasm"; +pub const TX_BRIDGE_POOL_WASM: &str = "tx_bridge_pool.wasm"; pub const VP_VALIDATOR_WASM: &str = "vp_validator.wasm"; pub const ALBERT_PAYMENT_ADDRESS: &str = "albert_payment"; @@ -127,8 +131,8 @@ static SHELL_INIT: Once = Once::new(); pub struct BenchShell { pub inner: Shell, - /// NOTE: Temporary directory should be dropped last since Shell need to - /// flush data on drop + // NOTE: Temporary directory should be dropped last since Shell need to + // flush data on drop tempdir: TempDir, } @@ -183,6 +187,8 @@ impl Default for BenchShell { 2, ) .unwrap(); + // Commit tx hashes to storage + shell.commit(); // Bond from Albert to validator let bond = Bond { @@ -190,20 +196,19 @@ impl Default for BenchShell { amount: Amount::native_whole(1000), source: Some(defaults::albert_address()), }; - let signed_tx = generate_tx( - TX_BOND_WASM, - bond, - None, - None, - Some(&defaults::albert_keypair()), - ); - let params = proof_of_stake::read_pos_params(&shell.wl_storage).unwrap(); let mut bench_shell = BenchShell { inner: shell, tempdir, }; + let signed_tx = bench_shell.generate_tx( + TX_BOND_WASM, + bond, + None, + None, + Some(&defaults::albert_keypair()), + ); bench_shell.execute_tx(&signed_tx); bench_shell.wl_storage.commit_tx(); @@ -212,7 +217,7 @@ impl Default for BenchShell { let content_section = Section::ExtraData(Code::new(vec![])); let voting_start_epoch = Epoch(2 + params.pipeline_len + params.unbonding_len); - let signed_tx = generate_tx( + let signed_tx = bench_shell.generate_tx( TX_INIT_PROPOSAL_WASM, InitProposalData { id: None, @@ -247,6 +252,108 @@ impl Default for BenchShell { } impl BenchShell { + pub fn generate_tx( + &self, + wasm_code_path: &str, + data: impl BorshSerialize, + shielded: Option, + extra_sections: Option>, + signer: Option<&SecretKey>, + ) -> Tx { + let mut tx = + Tx::from_type(namada::types::transaction::TxType::Decrypted( + namada::types::transaction::DecryptedTx::Decrypted, + )); + + // NOTE: here we use the code hash to avoid including the cost for the + // wasm validation. The wasm codes (both txs and vps) are always + // in cache so we don't end up computing the cost to read and + // compile the code which is the desired behaviour + let code_hash = self + .read_storage_key(&Key::wasm_hash(wasm_code_path)) + .unwrap(); + tx.set_code(Code::from_hash(code_hash)); + tx.set_data(Data::new(borsh::to_vec(&data).unwrap())); + + if let Some(transaction) = shielded { + tx.add_section(Section::MaspTx(transaction)); + } + + if let Some(sections) = extra_sections { + for section in sections { + if let Section::ExtraData(_) = section { + tx.add_section(section); + } + } + } + + if let Some(signer) = signer { + tx.add_section(Section::Signature(Signature::new( + vec![tx.raw_header_hash()], + [(0, signer.clone())].into_iter().collect(), + None, + ))); + } + + tx + } + + pub fn generate_ibc_tx(&self, wasm_code_path: &str, msg: impl Msg) -> Tx { + // This function avoid serializaing the tx data with Borsh + let mut tx = + Tx::from_type(namada::types::transaction::TxType::Decrypted( + namada::types::transaction::DecryptedTx::Decrypted, + )); + let code_hash = self + .read_storage_key(&Key::wasm_hash(wasm_code_path)) + .unwrap(); + tx.set_code(Code::from_hash(code_hash)); + + let mut data = vec![]; + prost::Message::encode(&msg.to_any(), &mut data).unwrap(); + tx.set_data(Data::new(data)); + + // NOTE: the Ibc VP doesn't actually check the signature + tx + } + + pub fn generate_ibc_transfer_tx(&self) -> Tx { + let token = PrefixedCoin { + denom: address::nam().to_string().parse().unwrap(), + amount: Amount::native_whole(1000) + .to_string_native() + .split('.') + .next() + .unwrap() + .to_string() + .parse() + .unwrap(), + }; + + let timeout_height = TimeoutHeight::At(IbcHeight::new(0, 100).unwrap()); + + let now: namada::tendermint::Time = + DateTimeUtc::now().try_into().unwrap(); + let now: IbcTimestamp = now.into(); + let timeout_timestamp = + (now + std::time::Duration::new(3600, 0)).unwrap(); + + let msg = MsgTransfer { + port_id_on_a: PortId::transfer(), + chan_id_on_a: ChannelId::new(5), + packet_data: PacketData { + token, + sender: defaults::albert_address().to_string().into(), + receiver: defaults::bertha_address().to_string().into(), + memo: "".parse().unwrap(), + }, + timeout_height_on_b: timeout_height, + timeout_timestamp_on_b: timeout_timestamp, + }; + + self.generate_ibc_tx(TX_IBC_WASM, msg) + } + pub fn execute_tx(&mut self, tx: &Tx) { run::tx( &self.inner.wl_storage.storage, @@ -277,18 +384,58 @@ impl BenchShell { .unwrap(); } - pub fn init_ibc_channel(&mut self) { - // Set connection open + pub fn init_ibc_client_state(&mut self, addr_key: Key) -> ClientId { + // Set client state let client_id = ClientId::new( ClientType::new("01-tendermint".to_string()).unwrap(), 1, ) .unwrap(); + let client_state_key = addr_key.join(&Key::from( + IbcPath::ClientState( + namada::ibc::core::ics24_host::path::ClientStatePath( + client_id.clone(), + ), + ) + .to_string() + .to_db_key(), + )); + let client_state = ClientState::new( + IbcChainId::from(ChainId::default().to_string()), + TrustThreshold::ONE_THIRD, + std::time::Duration::new(1, 0), + std::time::Duration::new(2, 0), + std::time::Duration::new(1, 0), + IbcHeight::new(0, 1).unwrap(), + ProofSpecs::cosmos(), + vec![], + AllowUpdate { + after_expiry: true, + after_misbehaviour: true, + }, + ) + .unwrap(); + let bytes = >::encode_vec(&client_state); + self.wl_storage + .storage + .write(&client_state_key, bytes) + .expect("write failed"); + + client_id + } + + pub fn init_ibc_connection(&mut self) -> (Key, ClientId) { + // Set client state + let addr_key = + Key::from(Address::Internal(InternalAddress::Ibc).to_db_key()); + let client_id = self.init_ibc_client_state(addr_key.clone()); + + // Set connection open let connection = ConnectionEnd::new( ConnectionState::Open, client_id.clone(), Counterparty::new( - client_id, + client_id.clone(), Some(ConnectionId::new(1)), CommitmentPrefix::try_from(b"ibc".to_vec()).unwrap(), ), @@ -297,9 +444,6 @@ impl BenchShell { ) .unwrap(); - let addr_key = - Key::from(Address::Internal(InternalAddress::Ibc).to_db_key()); - let connection_key = connection_key(&NamadaConnectionId::new(1)); self.wl_storage .storage @@ -326,6 +470,12 @@ impl BenchShell { .write(&cap_key, PortId::transfer().as_bytes()) .unwrap(); + (addr_key, client_id) + } + + pub fn init_ibc_channel(&mut self) { + let (addr_key, client_id) = self.init_ibc_connection(); + // Set Channel open let counterparty = ChannelCounterparty::new( PortId::transfer(), @@ -346,42 +496,6 @@ impl BenchShell { .write(&channel_key, channel.encode_vec()) .unwrap(); - // Set client state - let client_id = ClientId::new( - ClientType::new("01-tendermint".to_string()).unwrap(), - 1, - ) - .unwrap(); - let client_state_key = addr_key.join(&Key::from( - IbcPath::ClientState( - namada::ibc::core::ics24_host::path::ClientStatePath( - client_id.clone(), - ), - ) - .to_string() - .to_db_key(), - )); - let client_state = ClientState::new( - IbcChainId::from(ChainId::default().to_string()), - TrustThreshold::ONE_THIRD, - std::time::Duration::new(1, 0), - std::time::Duration::new(2, 0), - std::time::Duration::new(1, 0), - IbcHeight::new(0, 1).unwrap(), - ProofSpecs::cosmos(), - vec![], - AllowUpdate { - after_expiry: true, - after_misbehaviour: true, - }, - ) - .unwrap(); - let bytes = >::encode_vec(&client_state); - self.wl_storage - .storage - .write(&client_state_key, bytes) - .expect("write failed"); - // Set consensus state let now: namada::tendermint::Time = DateTimeUtc::now().try_into().unwrap(); @@ -412,66 +526,6 @@ impl BenchShell { } } -pub fn generate_tx( - wasm_code_path: &str, - data: impl BorshSerialize, - shielded: Option, - extra_section: Option>, - signer: Option<&SecretKey>, -) -> Tx { - let mut tx = Tx::from_type(namada::types::transaction::TxType::Decrypted( - namada::types::transaction::DecryptedTx::Decrypted, - )); - - // NOTE: don't use the hash to avoid computing the cost of loading the wasm - // code - tx.set_code(Code::new(wasm_loader::read_wasm_or_exit( - WASM_DIR, - wasm_code_path, - ))); - tx.set_data(Data::new(data.serialize_to_vec())); - - if let Some(transaction) = shielded { - tx.add_section(Section::MaspTx(transaction)); - } - - if let Some(sections) = extra_section { - for section in sections { - if let Section::ExtraData(_) = section { - tx.add_section(section); - } - } - } - - if let Some(signer) = signer { - tx.add_section(Section::Signature(Signature::new( - vec![tx.raw_header_hash()], - [(0, signer.clone())].into_iter().collect(), - None, - ))); - } - - tx -} - -pub fn generate_ibc_tx(wasm_code_path: &str, msg: impl Msg) -> Tx { - // This function avoid serializaing the tx data with Borsh - let mut tx = Tx::from_type(namada::types::transaction::TxType::Decrypted( - namada::types::transaction::DecryptedTx::Decrypted, - )); - tx.set_code(Code::new(wasm_loader::read_wasm_or_exit( - WASM_DIR, - wasm_code_path, - ))); - - let mut data = vec![]; - prost::Message::encode(&msg.to_any(), &mut data).unwrap(); - tx.set_data(Data::new(data)); - - // NOTE: the Ibc VP doesn't actually check the signature - tx -} - pub fn generate_foreign_key_tx(signer: &SecretKey) -> Tx { let wasm_code = std::fs::read("../wasm_for_tests/tx_write.wasm").unwrap(); @@ -495,41 +549,6 @@ pub fn generate_foreign_key_tx(signer: &SecretKey) -> Tx { tx } -pub fn generate_ibc_transfer_tx() -> Tx { - let token = PrefixedCoin { - denom: address::nam().to_string().parse().unwrap(), - amount: Amount::native_whole(1000) - .to_string_native() - .split('.') - .next() - .unwrap() - .to_string() - .parse() - .unwrap(), - }; - - let timeout_height = TimeoutHeight::At(IbcHeight::new(0, 100).unwrap()); - - let now: namada::tendermint::Time = DateTimeUtc::now().try_into().unwrap(); - let now: IbcTimestamp = now.into(); - let timeout_timestamp = (now + std::time::Duration::new(3600, 0)).unwrap(); - - let msg = MsgTransfer { - port_id_on_a: PortId::transfer(), - chan_id_on_a: ChannelId::new(5), - packet_data: PacketData { - token, - sender: defaults::albert_address().to_string().into(), - receiver: defaults::bertha_address().to_string().into(), - memo: "".parse().unwrap(), - }, - timeout_height_on_b: timeout_height, - timeout_timestamp_on_b: timeout_timestamp, - }; - - generate_ibc_tx(TX_IBC_WASM, msg) -} - pub struct BenchShieldedCtx { pub shielded: ShieldedContext, pub shell: BenchShell, @@ -822,7 +841,7 @@ impl BenchShieldedCtx { ) }); - generate_tx( + self.shell.generate_tx( TX_TRANSFER_WASM, Transfer { source: source.effective_address(), diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index b9b4d15780..4f4ee68ed1 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -1087,15 +1087,19 @@ pub mod cmds { #[derive(Clone, Debug)] pub enum Config { Gen(ConfigGen), + UpdateLocalConfig(LocalConfig), } impl SubCmd for Config { const CMD: &'static str = "config"; fn parse(matches: &ArgMatches) -> Option { - matches - .subcommand_matches(Self::CMD) - .and_then(|matches| SubCmd::parse(matches).map(Self::Gen)) + matches.subcommand_matches(Self::CMD).and_then(|matches| { + let gen = SubCmd::parse(matches).map(Self::Gen); + let gas_tokens = + SubCmd::parse(matches).map(Self::UpdateLocalConfig); + gen.or(gas_tokens) + }) } fn def() -> App { @@ -1104,6 +1108,7 @@ pub mod cmds { .arg_required_else_help(true) .about("Configuration sub-commands.") .subcommand(ConfigGen::def()) + .subcommand(LocalConfig::def()) } } @@ -1123,6 +1128,25 @@ pub mod cmds { } } + #[derive(Clone, Debug)] + pub struct LocalConfig(pub args::UpdateLocalConfig); + + impl SubCmd for LocalConfig { + const CMD: &'static str = "update-local-config"; + + fn parse(matches: &ArgMatches) -> Option { + matches + .subcommand_matches(Self::CMD) + .map(|matches| Self(args::UpdateLocalConfig::parse(matches))) + } + + fn def() -> App { + App::new(Self::CMD) + .about("Update the validator's local configuration.") + .add_args::() + } + } + #[derive(Clone, Debug)] pub struct QueryResult(pub args::QueryResult); @@ -2658,7 +2682,6 @@ pub mod args { pub const TX_UNBOND_WASM: &str = "tx_unbond.wasm"; pub const TX_UNJAIL_VALIDATOR_WASM: &str = "tx_unjail_validator.wasm"; pub const TX_REDELEGATE_WASM: &str = "tx_redelegate.wasm"; - pub const TX_UPDATE_VP_WASM: &str = "tx_update_vp.wasm"; pub const TX_UPDATE_STEWARD_COMMISSION: &str = "tx_update_steward_commission.wasm"; pub const TX_VOTE_PROPOSAL: &str = "tx_vote_proposal.wasm"; @@ -2748,7 +2771,7 @@ pub mod args { pub const FEE_PAYER_OPT: ArgOpt = arg_opt("gas-payer"); pub const FORCE: ArgFlag = flag("force"); pub const GAS_LIMIT: ArgDefault = - arg_default("gas-limit", DefaultFn(|| GasLimit::from(20_000))); + arg_default("gas-limit", DefaultFn(|| GasLimit::from(25_000))); pub const FEE_TOKEN: ArgDefaultFromCtx = arg_default_from_ctx("gas-token", DefaultFn(|| "NAM".parse().unwrap())); pub const FEE_PAYER: Arg = arg("fee-payer"); @@ -3029,6 +3052,25 @@ pub mod args { } } + #[derive(Clone, Debug)] + pub struct UpdateLocalConfig { + pub config_path: PathBuf, + } + + impl Args for UpdateLocalConfig { + fn parse(matches: &ArgMatches) -> Self { + let config_path = DATA_PATH.parse(matches); + Self { config_path } + } + + fn def(app: App) -> App { + app.arg(DATA_PATH.def().help( + "The path to the toml file containing the updated local \ + configuration.", + )) + } + } + /// Convert CLI args to SDK args, with contextual data. pub trait CliToSdk: Args { /// Convert CLI args to SDK args, with contextual data. diff --git a/apps/src/lib/config/mod.rs b/apps/src/lib/config/mod.rs index 735391d056..b61a622bd9 100644 --- a/apps/src/lib/config/mod.rs +++ b/apps/src/lib/config/mod.rs @@ -5,6 +5,7 @@ pub mod genesis; pub mod global; pub mod utils; +use std::collections::HashMap; use std::fs::{create_dir_all, File}; use std::io::Write; use std::path::{Path, PathBuf}; @@ -41,6 +42,12 @@ pub struct Config { pub ledger: Ledger, } +#[derive(Debug, Serialize, Deserialize)] +pub struct ValidatorLocalConfig { + pub accepted_gas_tokens: + HashMap, +} + #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] pub enum TendermintMode { Full, @@ -52,23 +59,12 @@ impl TendermintMode { pub fn to_str(&self) -> &str { match *self { TendermintMode::Full => "full", - TendermintMode::Validator => "validator", + TendermintMode::Validator { .. } => "validator", TendermintMode::Seed => "seed", } } } -impl From for TendermintMode { - fn from(mode: String) -> Self { - match mode.as_str() { - "full" => TendermintMode::Full, - "validator" => TendermintMode::Validator, - "seed" => TendermintMode::Seed, - _ => panic!("Unrecognized mode"), - } - } -} - /// An action to be performed at a /// certain block height. #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/apps/src/lib/node/ledger/mod.rs b/apps/src/lib/node/ledger/mod.rs index 5d4d1845a9..e38e8233c2 100644 --- a/apps/src/lib/node/ledger/mod.rs +++ b/apps/src/lib/node/ledger/mod.rs @@ -437,28 +437,30 @@ fn start_abci_broadcaster_shell( let (broadcaster_sender, broadcaster_receiver) = mpsc::unbounded_channel(); // Start broadcaster - let broadcaster = - if matches!(config.shell.tendermint_mode, TendermintMode::Validator) { - let (bc_abort_send, bc_abort_recv) = - tokio::sync::oneshot::channel::<()>(); - - spawner - .spawn_abortable("Broadcaster", move |aborter| async move { - // Construct a service for broadcasting protocol txs from - // the ledger - let mut broadcaster = - Broadcaster::new(rpc_address, broadcaster_receiver); - broadcaster.run(bc_abort_recv).await; - tracing::info!("Broadcaster is no longer running."); - - drop(aborter); - }) - .with_cleanup(async move { - let _ = bc_abort_send.send(()); - }) - } else { - spawn_dummy_task(()) - }; + let broadcaster = if matches!( + config.shell.tendermint_mode, + TendermintMode::Validator { .. } + ) { + let (bc_abort_send, bc_abort_recv) = + tokio::sync::oneshot::channel::<()>(); + + spawner + .spawn_abortable("Broadcaster", move |aborter| async move { + // Construct a service for broadcasting protocol txs from + // the ledger + let mut broadcaster = + Broadcaster::new(rpc_address, broadcaster_receiver); + broadcaster.run(bc_abort_recv).await; + tracing::info!("Broadcaster is no longer running."); + + drop(aborter); + }) + .with_cleanup(async move { + let _ = bc_abort_send.send(()); + }) + } else { + spawn_dummy_task(()) + }; // Setup DB cache, it must outlive the DB instance that's in the shell let db_cache = @@ -506,7 +508,7 @@ fn start_abci_broadcaster_shell( .spawn(move || { tracing::info!("Namada ledger node started."); match tendermint_mode { - TendermintMode::Validator => { + TendermintMode::Validator { .. } => { tracing::info!("This node is a validator"); } TendermintMode::Full | TendermintMode::Seed => { @@ -654,7 +656,10 @@ async fn maybe_start_ethereum_oracle( spawner: &mut AbortableSpawner, config: &config::Ledger, ) -> EthereumOracleTask { - if !matches!(config.shell.tendermint_mode, TendermintMode::Validator) { + if !matches!( + config.shell.tendermint_mode, + TendermintMode::Validator { .. } + ) { return EthereumOracleTask::NotEnabled { handle: spawn_dummy_task(()), }; diff --git a/apps/src/lib/node/ledger/shell/mod.rs b/apps/src/lib/node/ledger/shell/mod.rs index 50d84dc807..70dc956ce8 100644 --- a/apps/src/lib/node/ledger/shell/mod.rs +++ b/apps/src/lib/node/ledger/shell/mod.rs @@ -74,8 +74,7 @@ use thiserror::Error; use tokio::sync::mpsc::{Receiver, UnboundedSender}; use super::ethereum_oracle::{self as oracle, last_processed_block}; -use crate::config; -use crate::config::{genesis, TendermintMode}; +use crate::config::{self, genesis, TendermintMode, ValidatorLocalConfig}; use crate::facade::tendermint_proto::abci::{ Misbehavior as Evidence, MisbehaviorType as EvidenceType, ValidatorUpdate, }; @@ -225,6 +224,7 @@ pub(super) enum ShellMode { data: ValidatorData, broadcast_sender: UnboundedSender>, eth_oracle: Option, + local_config: Option, }, Full, Seed, @@ -488,12 +488,29 @@ where ); let mut wallet = crate::wallet::load(wallet_path) .expect("Validator node must have a wallet"); + let validator_local_config_path = + wallet_path.join("validator_local_config.toml"); + + let validator_local_config: Option = + if Path::is_file(&validator_local_config_path) { + Some( + toml::from_slice( + &std::fs::read(validator_local_config_path) + .unwrap(), + ) + .unwrap(), + ) + } else { + None + }; + wallet .take_validator_data() .map(|data| ShellMode::Validator { data, broadcast_sender, eth_oracle, + local_config: validator_local_config, }) .expect( "Validator data should have been stored in the \ @@ -516,6 +533,7 @@ where }, broadcast_sender, eth_oracle, + local_config: None, } } } @@ -1223,7 +1241,7 @@ where TxType::Wrapper(wrapper) => { // Tx gas limit let mut gas_meter = TxGasMeter::new(wrapper.gas_limit); - if gas_meter.add_tx_size_gas(tx_bytes).is_err() { + if gas_meter.add_wrapper_gas(tx_bytes).is_err() { response.code = ErrorCodes::TxGasLimit.into(); response.log = "{INVALID_MSG}: Wrapper transactions \ exceeds its gas limit" @@ -1291,6 +1309,7 @@ where &mut self.vp_wasm_cache.clone(), &mut self.tx_wasm_cache.clone(), None, + false, ) { response.code = ErrorCodes::FeeError.into(); response.log = format!("{INVALID_MSG}: {e}"); @@ -1329,20 +1348,49 @@ where vp_wasm_cache: &mut VpCache, tx_wasm_cache: &mut TxCache, block_proposer: Option<&Address>, + is_prepare_proposal: bool, ) -> Result<()> where CA: 'static + WasmCacheAccess + Sync, { // Check that fee token is an allowed one - let minimum_gas_price = namada::ledger::parameters::read_gas_cost( - &self.wl_storage, - &wrapper.fee.token, - ) - .expect("Must be able to read gas cost parameter") - .ok_or(Error::TxApply(protocol::Error::FeeError(format!( - "The provided {} token is not allowed for fee payment", - wrapper.fee.token - ))))?; + let minimum_gas_price = { + let proposer_local_config = if is_prepare_proposal { + if let ShellMode::Validator { + ref local_config, .. + } = self.mode + { + local_config.as_ref() + } else { + None + } + } else { + None + }; + + match proposer_local_config { + Some(config) => config + .accepted_gas_tokens + .get(&wrapper.fee.token) + .ok_or(Error::TxApply(protocol::Error::FeeError(format!( + "The provided {} token is not accepted by the block \ + proposer for fee payment", + wrapper.fee.token + ))))? + .to_owned(), + None => namada::ledger::parameters::read_gas_cost( + &self.wl_storage, + &wrapper.fee.token, + ) + .expect("Must be able to read gas cost parameter") + .ok_or(Error::TxApply( + protocol::Error::FeeError(format!( + "The provided {} token is not allowed for fee payment", + wrapper.fee.token + )), + ))?, + } + }; if wrapper.fee.amount_per_gas_unit < minimum_gas_price { // The fees do not match the minimum required @@ -2757,7 +2805,7 @@ mod shell_tests { Tx::from_type(TxType::Wrapper(Box::new(WrapperTx::new( Fee { amount_per_gas_unit: 100.into(), - token: address::btc(), + token: address::apfel(), }, crate::wallet::defaults::albert_keypair().ref_to(), Epoch(0), diff --git a/apps/src/lib/node/ledger/shell/prepare_proposal.rs b/apps/src/lib/node/ledger/shell/prepare_proposal.rs index 0f652d5130..37d4ef02ad 100644 --- a/apps/src/lib/node/ledger/shell/prepare_proposal.rs +++ b/apps/src/lib/node/ledger/shell/prepare_proposal.rs @@ -243,7 +243,7 @@ where if let TxType::Wrapper(wrapper) = tx.header().tx_type { // Check tx gas limit for tx size let mut tx_gas_meter = TxGasMeter::new(wrapper.gas_limit); - tx_gas_meter.add_tx_size_gas(tx_bytes).map_err(|_| ())?; + tx_gas_meter.add_wrapper_gas(tx_bytes).map_err(|_| ())?; self.replay_protection_checks(&tx, temp_wl_storage) .map_err(|_| ())?; @@ -256,6 +256,7 @@ where vp_wasm_cache, tx_wasm_cache, Some(block_proposer), + true, ) { Ok(()) => Ok(u64::from(wrapper.gas_limit)), Err(_) => Err(()), @@ -524,6 +525,7 @@ mod test_prepare_proposal { use namada::types::vote_extensions::VoteExtension; use super::*; + use crate::config::ValidatorLocalConfig; #[cfg(feature = "abcipp")] use crate::facade::tendermint_proto::abci::{ ExtendedCommitInfo, ExtendedVoteInfo, @@ -1465,6 +1467,57 @@ mod test_prepare_proposal { assert!(result.txs.is_empty()); } + // Check that a wrapper using a token not accepted byt the validator for fee + // payment is not included in the block + #[test] + fn test_fee_non_accepted_token() { + let (mut shell, _recv, _, _) = test_utils::setup(); + // Update local validator configuration for gas tokens + if let ShellMode::Validator { local_config, .. } = &mut shell.mode { + // Remove the allowed btc + *local_config = Some(ValidatorLocalConfig { + accepted_gas_tokens: std::collections::HashMap::from([( + namada::core::types::address::nam(), + Amount::from(1), + )]), + }); + } + + let wrapper = WrapperTx::new( + Fee { + amount_per_gas_unit: 100.into(), + token: address::btc(), + }, + crate::wallet::defaults::albert_keypair().ref_to(), + Epoch(0), + GAS_LIMIT_MULTIPLIER.into(), + None, + ); + + let mut wrapper_tx = Tx::from_type(TxType::Wrapper(Box::new(wrapper))); + wrapper_tx.header.chain_id = shell.chain_id.clone(); + wrapper_tx.set_code(Code::new("wasm_code".as_bytes().to_owned())); + wrapper_tx + .set_data(Data::new("transaction data".as_bytes().to_owned())); + wrapper_tx.add_section(Section::Signature(Signature::new( + wrapper_tx.sechashes(), + [(0, crate::wallet::defaults::albert_keypair())] + .into_iter() + .collect(), + None, + ))); + + let req = RequestPrepareProposal { + txs: vec![wrapper_tx.to_bytes()], + max_tx_bytes: 0, + time: None, + ..Default::default() + }; + let result = shell.prepare_proposal(req); + eprintln!("Proposal: {:?}", result.txs); + assert!(result.txs.is_empty()); + } + // Check that a wrapper using a non-whitelisted token for fee payment is not // included in the block #[test] @@ -1474,7 +1527,7 @@ mod test_prepare_proposal { let wrapper = WrapperTx::new( Fee { amount_per_gas_unit: 100.into(), - token: address::btc(), + token: address::apfel(), }, crate::wallet::defaults::albert_keypair().ref_to(), Epoch(0), @@ -1506,7 +1559,57 @@ mod test_prepare_proposal { assert!(result.txs.is_empty()); } - // Check that a wrapper setting a fee amount lower than the minimum required + // Check that a wrapper setting a fee amount lower than the minimum accepted + // by the validator is not included in the block + #[test] + fn test_fee_wrong_minimum_accepted_amount() { + let (mut shell, _recv, _, _) = test_utils::setup(); + // Update local validator configuration for gas tokens + if let ShellMode::Validator { local_config, .. } = &mut shell.mode { + // Remove btc and increase minimum for nam + *local_config = Some(ValidatorLocalConfig { + accepted_gas_tokens: std::collections::HashMap::from([( + namada::core::types::address::nam(), + Amount::from(100), + )]), + }); + } + + let wrapper = WrapperTx::new( + Fee { + amount_per_gas_unit: 10.into(), + token: shell.wl_storage.storage.native_token.clone(), + }, + crate::wallet::defaults::albert_keypair().ref_to(), + Epoch(0), + GAS_LIMIT_MULTIPLIER.into(), + None, + ); + let mut wrapper_tx = Tx::from_type(TxType::Wrapper(Box::new(wrapper))); + wrapper_tx.header.chain_id = shell.chain_id.clone(); + wrapper_tx.set_code(Code::new("wasm_code".as_bytes().to_owned())); + wrapper_tx + .set_data(Data::new("transaction data".as_bytes().to_owned())); + wrapper_tx.add_section(Section::Signature(Signature::new( + wrapper_tx.sechashes(), + [(0, crate::wallet::defaults::albert_keypair())] + .into_iter() + .collect(), + None, + ))); + + let req = RequestPrepareProposal { + txs: vec![wrapper_tx.to_bytes()], + max_tx_bytes: 0, + time: None, + ..Default::default() + }; + let result = shell.prepare_proposal(req); + eprintln!("Proposal: {:?}", result.txs); + assert!(result.txs.is_empty()); + } + + // Check that a wrapper setting a fee amount lower than the minimum allowed // is not included in the block #[test] fn test_fee_wrong_minimum_amount() { diff --git a/apps/src/lib/node/ledger/shell/process_proposal.rs b/apps/src/lib/node/ledger/shell/process_proposal.rs index e337a7b5af..007623bb32 100644 --- a/apps/src/lib/node/ledger/shell/process_proposal.rs +++ b/apps/src/lib/node/ledger/shell/process_proposal.rs @@ -764,7 +764,7 @@ where // valid transaction and avoid wasting block // resources (ABCI only) let mut tx_gas_meter = TxGasMeter::new(wrapper.gas_limit); - if tx_gas_meter.add_tx_size_gas(tx_bytes).is_err() { + if tx_gas_meter.add_wrapper_gas(tx_bytes).is_err() { // Account for the tx's resources even in case of an error. // Ignore any allocation error let _ = metadata @@ -859,6 +859,7 @@ where vp_wasm_cache, tx_wasm_cache, Some(block_proposer), + false, ) { Ok(()) => TxResult { code: ErrorCodes::Ok.into(), @@ -1699,8 +1700,9 @@ mod test_process_proposal { response.result.info, String::from( "Error trying to apply a transaction: Error while processing \ - transaction's fees: Insufficient transparent balance to pay \ - fees" + transaction's fees: Transparent balance of wrapper's signer \ + was insufficient to pay fee. All the available transparent \ + funds have been moved to the block proposer" ) ); } @@ -1764,8 +1766,9 @@ mod test_process_proposal { response.result.info, String::from( "Error trying to apply a transaction: Error while processing \ - transaction's fees: Insufficient transparent balance to pay \ - fees" + transaction's fees: Transparent balance of wrapper's signer \ + was insufficient to pay fee. All the available transparent \ + funds have been moved to the block proposer" ) ); } @@ -2514,7 +2517,7 @@ mod test_process_proposal { Tx::from_type(TxType::Wrapper(Box::new(WrapperTx::new( Fee { amount_per_gas_unit: 100.into(), - token: address::btc(), + token: address::apfel(), }, crate::wallet::defaults::albert_keypair().ref_to(), Epoch(0), diff --git a/apps/src/lib/node/ledger/storage/mod.rs b/apps/src/lib/node/ledger/storage/mod.rs index 7a1f44e6f9..f8b7c7aade 100644 --- a/apps/src/lib/node/ledger/storage/mod.rs +++ b/apps/src/lib/node/ledger/storage/mod.rs @@ -54,6 +54,7 @@ mod tests { use std::collections::HashMap; use itertools::Itertools; + use namada::ledger::gas::STORAGE_ACCESS_GAS_PER_BYTE; use namada::ledger::parameters::{EpochDuration, Parameters}; use namada::ledger::storage::write_log::WriteLog; use namada::ledger::storage::{ @@ -91,10 +92,10 @@ mod tests { // before insertion let (result, gas) = storage.has_key(&key).expect("has_key failed"); assert!(!result); - assert_eq!(gas, key.len() as u64); + assert_eq!(gas, key.len() as u64 * STORAGE_ACCESS_GAS_PER_BYTE); let (result, gas) = storage.read(&key).expect("read failed"); assert_eq!(result, None); - assert_eq!(gas, key.len() as u64); + assert_eq!(gas, key.len() as u64 * STORAGE_ACCESS_GAS_PER_BYTE); // insert storage.write(&key, value_bytes).expect("write failed"); @@ -102,13 +103,17 @@ mod tests { // read let (result, gas) = storage.has_key(&key).expect("has_key failed"); assert!(result); - assert_eq!(gas, key.len() as u64); + assert_eq!(gas, key.len() as u64 * STORAGE_ACCESS_GAS_PER_BYTE); let (result, gas) = storage.read(&key).expect("read failed"); let read_value: u64 = types::decode(result.expect("value doesn't exist")) .expect("decoding failed"); assert_eq!(read_value, value); - assert_eq!(gas, key.len() as u64 + value_bytes_len as u64); + assert_eq!( + gas, + (key.len() as u64 + value_bytes_len as u64) + * STORAGE_ACCESS_GAS_PER_BYTE + ); // delete storage.delete(&key).expect("delete failed"); @@ -276,7 +281,7 @@ mod tests { storage.commit_block(batch).expect("commit failed"); let (iter, gas) = storage.iter_prefix(&prefix); - assert_eq!(gas, prefix.len() as u64); + assert_eq!(gas, (prefix.len() as u64) * STORAGE_ACCESS_GAS_PER_BYTE); for (k, v, gas) in iter { match expected.pop() { Some((expected_key, expected_val)) => { @@ -312,7 +317,7 @@ mod tests { let (vp, gas) = storage.validity_predicate(&addr).expect("VP load failed"); assert_eq!(vp, None); - assert_eq!(gas, key.len() as u64); + assert_eq!(gas, (key.len() as u64) * STORAGE_ACCESS_GAS_PER_BYTE); // insert let vp1 = Hash::sha256("vp1".as_bytes()); @@ -322,7 +327,10 @@ mod tests { let (vp_code_hash, gas) = storage.validity_predicate(&addr).expect("VP load failed"); assert_eq!(vp_code_hash.expect("no VP"), vp1); - assert_eq!(gas, (key.len() + vp1.len()) as u64); + assert_eq!( + gas, + ((key.len() + vp1.len()) as u64) * STORAGE_ACCESS_GAS_PER_BYTE + ); } proptest! { diff --git a/benches/Cargo.toml b/benches/Cargo.toml index 0aa1688a79..1816da11da 100644 --- a/benches/Cargo.toml +++ b/benches/Cargo.toml @@ -48,4 +48,5 @@ criterion = { version = "0.5", features = ["html_reports"] } ferveo-common.workspace = true rand_core.workspace = true rand.workspace = true +tempfile.workspace = true sha2.workspace = true diff --git a/benches/host_env.rs b/benches/host_env.rs index f2e2c3ee2a..dd82b1ba6f 100644 --- a/benches/host_env.rs +++ b/benches/host_env.rs @@ -1,16 +1,23 @@ -use std::collections::HashSet; +use std::collections::{HashMap, HashSet}; -use borsh_ext::BorshSerializeExt; use criterion::{criterion_group, criterion_main, Criterion}; use namada::core::types::account::AccountPublicKeysMap; use namada::core::types::address; use namada::core::types::token::{Amount, Transfer}; -use namada::proto::{Data, Section, Signature}; +use namada::ledger::storage::DB; +use namada::proto::Signature; +use namada::vm::wasm::TxCache; +use namada_apps::bench_utils::{ + BenchShell, TX_INIT_PROPOSAL_WASM, TX_REVEAL_PK_WASM, TX_TRANSFER_WASM, + TX_UPDATE_ACCOUNT_WASM, VP_VALIDATOR_WASM, WASM_DIR, +}; use namada_apps::wallet::defaults; +use namada_apps::wasm_loader; -/// Benchmarks the validation of a single signature on a single `Section` of a -/// transaction +// Benchmarks the validation of a single signature on a single `Section` of a +// transaction fn tx_section_signature_validation(c: &mut Criterion) { + let shell = BenchShell::default(); let transfer_data = Transfer { source: defaults::albert_address(), target: defaults::bertha_address(), @@ -19,8 +26,14 @@ fn tx_section_signature_validation(c: &mut Criterion) { key: None, shielded: None, }; - let section = Section::Data(Data::new(transfer_data.serialize_to_vec())); - let section_hash = section.get_hash(); + let tx = shell.generate_tx( + TX_TRANSFER_WASM, + transfer_data, + None, + None, + Some(&defaults::albert_keypair()), + ); + let section_hash = tx.header_hash(); let pkim = AccountPublicKeysMap::from_iter([ defaults::albert_keypair().to_public() @@ -41,5 +54,285 @@ fn tx_section_signature_validation(c: &mut Criterion) { }); } -criterion_group!(host_env, tx_section_signature_validation); +fn compile_wasm(c: &mut Criterion) { + let mut group = c.benchmark_group("compile_wasm"); + let mut txs: HashMap<&str, Vec> = HashMap::default(); + + for tx in [ + TX_TRANSFER_WASM, + TX_INIT_PROPOSAL_WASM, + TX_REVEAL_PK_WASM, + TX_UPDATE_ACCOUNT_WASM, + VP_VALIDATOR_WASM, + ] { + let wasm_code = wasm_loader::read_wasm_or_exit(WASM_DIR, tx); + txs.insert(tx, wasm_code); + } + + // Test the compilation of a few different transactions + for (wasm, wasm_code) in txs { + // Extract the throughput, together with the + // wall-time, so that we can than invert it to calculate the + // desired metric (time/byte) + let len = wasm_code.len() as u64; + group.throughput(criterion::Throughput::Bytes(len)); + group.bench_function(format!("Wasm: {wasm}, size: {len}"), |b| { + b.iter_batched_ref( + || { + let mut shell = BenchShell::default(); + // Re-initialize the tx cache to make sure we are not + // reading the precompiled modules from there + let tempdir = tempfile::tempdir().unwrap(); + let path = tempdir.path().canonicalize().unwrap(); + shell.tx_wasm_cache = TxCache::new(path, 50 * 1024 * 1024); + + (shell, tempdir) + }, + |(shell, _tempdir)| { + shell + .tx_wasm_cache + .compile_or_fetch(&wasm_code) + .unwrap() + .unwrap() + }, + criterion::BatchSize::LargeInput, + ) + }); + } + + group.finish(); +} + +fn untrusted_wasm_validation(c: &mut Criterion) { + let mut group = c.benchmark_group("untrusted_wasm_validation"); + let mut txs: HashMap<&str, Vec> = HashMap::default(); + + for tx in [ + TX_TRANSFER_WASM, + TX_INIT_PROPOSAL_WASM, + TX_REVEAL_PK_WASM, + TX_UPDATE_ACCOUNT_WASM, + ] { + let wasm_code = wasm_loader::read_wasm_or_exit(WASM_DIR, tx); + + txs.insert(tx, wasm_code); + } + + // Test the validation of a few different transactions + for (tx, wasm_code) in txs { + // Extract the throughput, together with the wall-time, so that we can + // than invert it to calculate the desired metric (time/byte) + let len = wasm_code.len() as u64; + group.throughput(criterion::Throughput::Bytes(len)); + group.bench_function(format!("Tx: {tx}, size: {len}"), |b| { + b.iter(|| namada::vm::validate_untrusted_wasm(&wasm_code).unwrap()) + }); + } + group.finish(); +} + +// Generate some variable-length keys with some hardcoded lengths for the values +fn generate_random_keys_sized() -> Vec<(String, u64)> { + vec![ + ("bench".to_string(), 1), + ("bench".to_string(), 1_000), + ("bench".to_string(), 200_000), + ("bench/test/key/middle/size".to_string(), 20), + ("bench/test/key/middle/size".to_string(), 5_000), + ("bench/test/key/middle/size".to_string(), 5_000_000), + ( + format!( + "very/long/{}/bench/test/storage/{}/key/for/benchmark/\ + purposes/{}", + defaults::albert_address(), + defaults::christel_address(), + defaults::bertha_address() + ), + 50, + ), + ( + format!( + "very/long/{}/bench/test/storage/{}/key/for/benchmark/\ + purposes/{}", + defaults::albert_address(), + defaults::christel_address(), + defaults::bertha_address() + ), + 8_000, + ), + ( + format!( + "very/long/{}/bench/test/storage/{}/key/for/benchmark/\ + purposes/{}", + defaults::albert_address(), + defaults::christel_address(), + defaults::bertha_address() + ), + 50_000_000, + ), + ] +} + +fn write_log_read(c: &mut Criterion) { + let mut group = c.benchmark_group("write_log_read"); + let mut shell = BenchShell::default(); + + for (key, value_len) in generate_random_keys_sized() { + let key = namada::core::types::storage::Key::parse(key).unwrap(); + // Extract the throughput, together with the wall-time, so that we can + // than invert it to calculate the desired metric (time/byte) + // NOTE: criterion states that the throughput is measured on the + // processed bytes but in this case we are interested in the input + + // output bytes, i.e. the combined legth of the key and value red, so we + // set this as the throughput parameter + let throughput_len = value_len + key.len() as u64; + group.throughput(criterion::Throughput::Bytes(throughput_len)); + // Generate random bytes for the value and write it to storage + let value: Vec = (0..value_len).map(|_| rand::random()).collect(); + shell.wl_storage.write_log.write(&key, value).unwrap(); + + group.bench_function( + format!("key: {key}, bytes: {throughput_len}"), + |b| { + b.iter_with_large_drop(|| { + shell.wl_storage.write_log.read(&key).0.unwrap() + }) + }, + ); + } + + group.finish(); +} + +fn storage_read(c: &mut Criterion) { + let mut group = c.benchmark_group("storage_read"); + let mut shell = BenchShell::default(); + + for (key, value_len) in generate_random_keys_sized() { + let key = namada::core::types::storage::Key::parse(key).unwrap(); + // Extract the throughput, together with the wall-time, so that we can + // than invert it to calculate the desired metric (time/byte) + // NOTE: criterion states that the throughput is measured on the + // processed bytes but in this case we are interested in the input + + // output bytes, i.e. the combined legth of the key and value red, so we + // set this as the throughput parameter + let throughput_len = value_len + key.len() as u64; + group.throughput(criterion::Throughput::Bytes(throughput_len)); + // Generate random bytes for the value and write it to storage + let value: Vec = (0..value_len).map(|_| rand::random()).collect(); + // NOTE: just like for storage writes, we don't have control on when + // data is actually flushed to disk, so just benchmark the read function + // without caring if data is actually in memory or on disk + shell.wl_storage.storage.write(&key, &value).unwrap(); + + group.bench_function( + format!("key: {key}, bytes: {throughput_len}"), + |b| { + b.iter_with_large_drop(|| { + shell + .wl_storage + .storage + .db + .read_subspace_val(&key) + .unwrap() + .unwrap() + }) + }, + ); + } + + group.finish(); +} + +fn write_log_write(c: &mut Criterion) { + let mut group = c.benchmark_group("write_log_write"); + let mut shell = BenchShell::default(); + + for (key, value_len) in generate_random_keys_sized() { + let key = namada::core::types::storage::Key::parse(key).unwrap(); + // Extract the throughput, together with the wall-time, so that we can + // than invert it to calculate the desired metric (time/byte) + // NOTE: criterion states that the throughput is measured on the + // processed bytes but in this case we are interested in the input + + // output bytes, i.e. the combined legth of the key and value written, + // so we set this as the throughput parameter + let throughput_len = value_len + key.len() as u64; + group.throughput(criterion::Throughput::Bytes(throughput_len)); + + group.bench_function( + format!("key: {key}, bytes: {throughput_len}"), + |b| { + b.iter_batched( + || { + // Generate random bytes for the value + (0..value_len).map(|_| rand::random()).collect() + }, + |value| { + shell.wl_storage.write_log.write(&key, value).unwrap() + }, + criterion::BatchSize::SmallInput, + ) + }, + ); + } + + group.finish(); +} + +fn storage_write(c: &mut Criterion) { + let mut group = c.benchmark_group("storage_write"); + let mut shell = BenchShell::default(); + + for (key, value_len) in generate_random_keys_sized() { + let key = namada::core::types::storage::Key::parse(key).unwrap(); + // Extract the throughput, together with the wall-time, so that we can + // than invert it to calculate the desired metric (time/byte) + // NOTE: criterion states that the throughput is measured on the + // processed bytes but in this case we are interested in the input + + // output bytes, i.e. the combined legth of the key and value written, + // so we set this as the throughput parameter + let throughput_len = value_len + key.len() as u64; + group.throughput(criterion::Throughput::Bytes(throughput_len)); + let block_height = shell.wl_storage.storage.block.height; + + group.bench_function( + format!("key: {key}, bytes: {throughput_len}"), + |b| { + b.iter_batched_ref( + || { + // Generate random bytes for the value + (0..value_len).map(|_| rand::random()).collect() + }, + |value: &mut Vec| { + // NOTE: RocksDB actually flushes data to the OS buffer + // that will eventually be committed to the actual + // storage. We don't really have control on this so we + // just benchmark the write operation here without + // focusing on the hardware write + shell + .wl_storage + .storage + .db + .write_subspace_val(block_height, &key, value) + .unwrap(); + }, + criterion::BatchSize::SmallInput, + ) + }, + ); + } + + group.finish(); +} + +criterion_group!( + host_env, + tx_section_signature_validation, + compile_wasm, + untrusted_wasm_validation, + write_log_read, + storage_read, + write_log_write, + storage_write, +); criterion_main!(host_env); diff --git a/benches/native_vps.rs b/benches/native_vps.rs index eecfbd6a32..aa6bace8dd 100644 --- a/benches/native_vps.rs +++ b/benches/native_vps.rs @@ -1,4 +1,6 @@ -use std::collections::BTreeSet; +use std::cell::RefCell; +use std::collections::{BTreeSet, HashMap}; +use std::rc::Rc; use std::str::FromStr; use criterion::{criterion_group, criterion_main, Criterion}; @@ -6,8 +8,12 @@ use namada::core::ledger::governance::storage::proposal::ProposalType; use namada::core::ledger::governance::storage::vote::{ StorageProposalVote, VoteType, }; +use namada::core::ledger::ibc::{IbcActions, TransferModule}; +use namada::core::ledger::pgf::storage::steward::StewardDetail; +use namada::core::ledger::storage_api::{StorageRead, StorageWrite}; use namada::core::types::address::{self, Address}; use namada::core::types::token::{Amount, Transfer}; +use namada::eth_bridge::storage::whitelist; use namada::ibc::core::ics02_client::client_type::ClientType; use namada::ibc::core::ics03_connection::connection::Counterparty; use namada::ibc::core::ics03_connection::msgs::conn_open_init::MsgConnectionOpenInit; @@ -19,23 +25,32 @@ use namada::ibc::core::ics23_commitment::commitment::CommitmentPrefix; use namada::ibc::core::ics24_host::identifier::{ ClientId, ConnectionId, PortId, }; +use namada::ledger::eth_bridge::read_native_erc20_address; use namada::ledger::gas::{TxGasMeter, VpGasMeter}; use namada::ledger::governance::GovernanceVp; +use namada::ledger::native_vp::ethereum_bridge::bridge_pool_vp::BridgePoolVp; +use namada::ledger::native_vp::ethereum_bridge::nut::NonUsableTokens; +use namada::ledger::native_vp::ethereum_bridge::vp::EthBridge; +use namada::ledger::native_vp::ibc::context::PseudoExecutionContext; use namada::ledger::native_vp::ibc::Ibc; use namada::ledger::native_vp::multitoken::MultitokenVp; +use namada::ledger::native_vp::parameters::ParametersVp; use namada::ledger::native_vp::{Ctx, NativeVp}; -use namada::ledger::storage_api::StorageRead; +use namada::ledger::pgf::PgfVp; +use namada::ledger::pos::PosVP; use namada::proof_of_stake; -use namada::proto::{Code, Section}; +use namada::proof_of_stake::KeySeg; +use namada::proto::{Code, Section, Tx}; use namada::types::address::InternalAddress; +use namada::types::eth_bridge_pool::{GasFee, PendingTransfer}; use namada::types::storage::{Epoch, TxIndex}; use namada::types::transaction::governance::{ InitProposalData, VoteProposalData, }; use namada_apps::bench_utils::{ - generate_foreign_key_tx, generate_ibc_transfer_tx, generate_ibc_tx, - generate_tx, BenchShell, TX_IBC_WASM, TX_INIT_PROPOSAL_WASM, - TX_TRANSFER_WASM, TX_VOTE_PROPOSAL_WASM, + generate_foreign_key_tx, BenchShell, TX_BRIDGE_POOL_WASM, TX_IBC_WASM, + TX_INIT_PROPOSAL_WASM, TX_RESIGN_STEWARD, TX_TRANSFER_WASM, + TX_UPDATE_STEWARD_COMMISSION, TX_VOTE_PROPOSAL_WASM, }; use namada_apps::wallet::defaults; @@ -58,7 +73,7 @@ fn governance(c: &mut Criterion) { "delegator_vote" => { // Advance to the proposal voting period shell.advance_epoch(); - generate_tx( + shell.generate_tx( TX_VOTE_PROPOSAL_WASM, VoteProposalData { id: 0, @@ -74,7 +89,7 @@ fn governance(c: &mut Criterion) { "validator_vote" => { // Advance to the proposal voting period shell.advance_epoch(); - generate_tx( + shell.generate_tx( TX_VOTE_PROPOSAL_WASM, VoteProposalData { id: 0, @@ -98,7 +113,7 @@ fn governance(c: &mut Criterion) { shell.wl_storage.get_block_epoch().unwrap().next(), voting_start_epoch ); - generate_tx( + shell.generate_tx( TX_INIT_PROPOSAL_WASM, InitProposalData { id: None, @@ -148,7 +163,7 @@ fn governance(c: &mut Criterion) { shell.wl_storage.get_block_epoch().unwrap().next(), voting_start_epoch ); - generate_tx( + shell.generate_tx( TX_INIT_PROPOSAL_WASM, InitProposalData { id: Some(1), @@ -211,12 +226,6 @@ fn governance(c: &mut Criterion) { group.finish(); } -// TODO: missing native vps -// - pos -// - parameters -// - eth bridge -// - eth bridge pool - // TODO: uncomment when SlashFund internal address is brought back // fn slash_fund(c: &mut Criterion) { // let mut group = c.benchmark_group("vp_slash_fund"); @@ -226,7 +235,7 @@ fn governance(c: &mut Criterion) { // generate_foreign_key_tx(&defaults::albert_keypair()); // let content_section = Section::ExtraData(Code::new(vec![])); -// let governance_proposal = generate_tx( +// let governance_proposal = shell.generate_tx( // TX_INIT_PROPOSAL_WASM, // InitProposalData { // id: None, @@ -292,8 +301,10 @@ fn governance(c: &mut Criterion) { fn ibc(c: &mut Criterion) { let mut group = c.benchmark_group("vp_ibc"); + let shell = BenchShell::default(); - // Connection handshake + // NOTE: Ibc encompass a variety of different messages that can be executed, + // here we only benchmark a few of those Connection handshake let msg = MsgConnectionOpenInit { client_id_on_a: ClientId::new( ClientType::new("01-tendermint".to_string()).unwrap(), @@ -309,7 +320,7 @@ fn ibc(c: &mut Criterion) { delay_period: std::time::Duration::new(100, 0), signer: defaults::albert_address().to_string().into(), }; - let open_connection = generate_ibc_tx(TX_IBC_WASM, msg); + let open_connection = shell.generate_ibc_tx(TX_IBC_WASM, msg); // Channel handshake let msg = MsgChannelOpenInit { @@ -322,10 +333,10 @@ fn ibc(c: &mut Criterion) { }; // Avoid serializing the data again with borsh - let open_channel = generate_ibc_tx(TX_IBC_WASM, msg); + let open_channel = shell.generate_ibc_tx(TX_IBC_WASM, msg); // Ibc transfer - let outgoing_transfer = generate_ibc_transfer_tx(); + let outgoing_transfer = shell.generate_ibc_transfer_tx(); for (signed_tx, bench_name) in [open_connection, open_channel, outgoing_transfer] @@ -333,7 +344,21 @@ fn ibc(c: &mut Criterion) { .zip(["open_connection", "open_channel", "outgoing_transfer"]) { let mut shell = BenchShell::default(); - shell.init_ibc_channel(); + // Initialize the state according to the target tx + match bench_name { + "open_connection" => { + let _ = shell.init_ibc_client_state( + namada::core::types::storage::Key::from( + Address::Internal(InternalAddress::Ibc).to_db_key(), + ), + ); + } + "open_channel" => { + let _ = shell.init_ibc_connection(); + } + "outgoing_transfer" => shell.init_ibc_channel(), + _ => panic!("Unexpected bench test"), + } shell.execute_tx(signed_tx); let (verifiers, keys_changed) = shell @@ -375,12 +400,13 @@ fn ibc(c: &mut Criterion) { } fn vp_multitoken(c: &mut Criterion) { - let mut group = c.benchmark_group("vp_token"); + let mut group = c.benchmark_group("vp_multitoken"); + let shell = BenchShell::default(); let foreign_key_write = generate_foreign_key_tx(&defaults::albert_keypair()); - let transfer = generate_tx( + let transfer = shell.generate_tx( TX_TRANSFER_WASM, Transfer { source: defaults::albert_address(), @@ -438,11 +464,696 @@ fn vp_multitoken(c: &mut Criterion) { } } +fn pgf(c: &mut Criterion) { + let mut group = c.benchmark_group("vp_pgf"); + + for bench_name in [ + "foreign_key_write", + "remove_steward", + "steward_inflation_rate", + ] { + let mut shell = BenchShell::default(); + namada::core::ledger::pgf::storage::keys::stewards_handle() + .insert( + &mut shell.wl_storage, + defaults::albert_address(), + StewardDetail::base(defaults::albert_address()), + ) + .unwrap(); + + let signed_tx = match bench_name { + "foreign_key_write" => { + generate_foreign_key_tx(&defaults::albert_keypair()) + } + "remove_steward" => shell.generate_tx( + TX_RESIGN_STEWARD, + defaults::albert_address(), + None, + None, + Some(&defaults::albert_keypair()), + ), + "steward_inflation_rate" => { + let data = + namada::types::transaction::pgf::UpdateStewardCommission { + steward: defaults::albert_address(), + commission: HashMap::from([( + defaults::albert_address(), + namada::types::dec::Dec::zero(), + )]), + }; + shell.generate_tx( + TX_UPDATE_STEWARD_COMMISSION, + data, + None, + None, + Some(&defaults::albert_keypair()), + ) + } + _ => panic!("Unexpected bench test"), + }; + + // Run the tx to validate + shell.execute_tx(&signed_tx); + + let (verifiers, keys_changed) = shell + .wl_storage + .write_log + .verifiers_and_changed_keys(&BTreeSet::default()); + + let pgf = PgfVp { + ctx: Ctx::new( + &Address::Internal(InternalAddress::Pgf), + &shell.wl_storage.storage, + &shell.wl_storage.write_log, + &signed_tx, + &TxIndex(0), + VpGasMeter::new_from_tx_meter(&TxGasMeter::new_from_sub_limit( + u64::MAX.into(), + )), + &keys_changed, + &verifiers, + shell.vp_wasm_cache.clone(), + ), + }; + + group.bench_function(bench_name, |b| { + b.iter(|| { + assert!( + pgf.validate_tx( + &signed_tx, + pgf.ctx.keys_changed, + pgf.ctx.verifiers, + ) + .unwrap() + ) + }) + }); + } + + group.finish(); +} + +fn eth_bridge_nut(c: &mut Criterion) { + let mut shell = BenchShell::default(); + let native_erc20_addres = + read_native_erc20_address(&shell.wl_storage).unwrap(); + + let signed_tx = { + let data = PendingTransfer{ + transfer: namada::types::eth_bridge_pool::TransferToEthereum { + kind: namada::types::eth_bridge_pool::TransferToEthereumKind::Erc20, + asset: native_erc20_addres, + recipient: namada::types::ethereum_events::EthAddress([1u8; 20]), + sender: defaults::albert_address(), + amount: Amount::from(1), + }, + gas_fee: GasFee{ + amount: Amount::from(100), + payer: defaults::albert_address(), + token: shell.wl_storage.storage.native_token.clone(), + }, + }; + shell.generate_tx( + TX_BRIDGE_POOL_WASM, + data, + None, + None, + Some(&defaults::albert_keypair()), + ) + }; + + // Run the tx to validate + shell.execute_tx(&signed_tx); + + let (verifiers, keys_changed) = shell + .wl_storage + .write_log + .verifiers_and_changed_keys(&BTreeSet::default()); + + let vp_address = + Address::Internal(InternalAddress::Nut(native_erc20_addres)); + let nut = NonUsableTokens { + ctx: Ctx::new( + &vp_address, + &shell.wl_storage.storage, + &shell.wl_storage.write_log, + &signed_tx, + &TxIndex(0), + VpGasMeter::new_from_tx_meter(&TxGasMeter::new_from_sub_limit( + u64::MAX.into(), + )), + &keys_changed, + &verifiers, + shell.vp_wasm_cache.clone(), + ), + }; + + c.bench_function("vp_eth_bridge_nut", |b| { + b.iter(|| { + assert!( + nut.validate_tx( + &signed_tx, + nut.ctx.keys_changed, + nut.ctx.verifiers, + ) + .unwrap() + ) + }) + }); +} + +fn eth_bridge(c: &mut Criterion) { + let mut shell = BenchShell::default(); + let native_erc20_addres = + read_native_erc20_address(&shell.wl_storage).unwrap(); + + let signed_tx = { + let data = PendingTransfer{ + transfer: namada::types::eth_bridge_pool::TransferToEthereum { + kind: namada::types::eth_bridge_pool::TransferToEthereumKind::Erc20, + asset: native_erc20_addres, + recipient: namada::types::ethereum_events::EthAddress([1u8; 20]), + sender: defaults::albert_address(), + amount: Amount::from(1), + }, + gas_fee: GasFee{ + amount: Amount::from(100), + payer: defaults::albert_address(), + token: shell.wl_storage.storage.native_token.clone(), + }, + }; + shell.generate_tx( + TX_BRIDGE_POOL_WASM, + data, + None, + None, + Some(&defaults::albert_keypair()), + ) + }; + + // Run the tx to validate + shell.execute_tx(&signed_tx); + + let (verifiers, keys_changed) = shell + .wl_storage + .write_log + .verifiers_and_changed_keys(&BTreeSet::default()); + + let vp_address = Address::Internal(InternalAddress::EthBridge); + let eth_bridge = EthBridge { + ctx: Ctx::new( + &vp_address, + &shell.wl_storage.storage, + &shell.wl_storage.write_log, + &signed_tx, + &TxIndex(0), + VpGasMeter::new_from_tx_meter(&TxGasMeter::new_from_sub_limit( + u64::MAX.into(), + )), + &keys_changed, + &verifiers, + shell.vp_wasm_cache.clone(), + ), + }; + + c.bench_function("vp_eth_bridge", |b| { + b.iter(|| { + assert!( + eth_bridge + .validate_tx( + &signed_tx, + eth_bridge.ctx.keys_changed, + eth_bridge.ctx.verifiers, + ) + .unwrap() + ) + }) + }); +} + +fn eth_bridge_pool(c: &mut Criterion) { + // NOTE: this vp is one of the most expensive but its cost comes from the + // numerous accesses to storage that we already account for, so no need to + // benchmark specific sections of it like for the ibc native vp + let mut shell = BenchShell::default(); + let native_erc20_addres = + read_native_erc20_address(&shell.wl_storage).unwrap(); + + // Whitelist NAM token + let cap_key = whitelist::Key { + asset: native_erc20_addres, + suffix: whitelist::KeyType::Cap, + } + .into(); + shell + .wl_storage + .write(&cap_key, Amount::from(1_000)) + .unwrap(); + + let whitelisted_key = whitelist::Key { + asset: native_erc20_addres, + suffix: whitelist::KeyType::Whitelisted, + } + .into(); + shell.wl_storage.write(&whitelisted_key, true).unwrap(); + + let denom_key = whitelist::Key { + asset: native_erc20_addres, + suffix: whitelist::KeyType::Denomination, + } + .into(); + shell.wl_storage.write(&denom_key, 0).unwrap(); + + let signed_tx = { + let data = PendingTransfer{ + transfer: namada::types::eth_bridge_pool::TransferToEthereum { + kind: namada::types::eth_bridge_pool::TransferToEthereumKind::Erc20, + asset: native_erc20_addres, + recipient: namada::types::ethereum_events::EthAddress([1u8; 20]), + sender: defaults::albert_address(), + amount: Amount::from(1), + }, + gas_fee: GasFee{ + amount: Amount::from(100), + payer: defaults::albert_address(), + token: shell.wl_storage.storage.native_token.clone(), + }, + }; + shell.generate_tx( + TX_BRIDGE_POOL_WASM, + data, + None, + None, + Some(&defaults::albert_keypair()), + ) + }; + + // Run the tx to validate + shell.execute_tx(&signed_tx); + + let (verifiers, keys_changed) = shell + .wl_storage + .write_log + .verifiers_and_changed_keys(&BTreeSet::default()); + + let vp_address = Address::Internal(InternalAddress::EthBridgePool); + let bridge_pool = BridgePoolVp { + ctx: Ctx::new( + &vp_address, + &shell.wl_storage.storage, + &shell.wl_storage.write_log, + &signed_tx, + &TxIndex(0), + VpGasMeter::new_from_tx_meter(&TxGasMeter::new_from_sub_limit( + u64::MAX.into(), + )), + &keys_changed, + &verifiers, + shell.vp_wasm_cache.clone(), + ), + }; + + c.bench_function("vp_eth_bridge_pool", |b| { + b.iter(|| { + assert!( + bridge_pool + .validate_tx( + &signed_tx, + bridge_pool.ctx.keys_changed, + bridge_pool.ctx.verifiers, + ) + .unwrap() + ) + }) + }); +} + +fn parameters(c: &mut Criterion) { + let mut group = c.benchmark_group("vp_parameters"); + + for bench_name in ["foreign_key_write", "parameter_change"] { + let mut shell = BenchShell::default(); + + let signed_tx = match bench_name { + "foreign_key_write" => { + let tx = generate_foreign_key_tx(&defaults::albert_keypair()); + // Run the tx to validate + shell.execute_tx(&tx); + tx + } + "parameter_change" => { + // Simulate governance proposal to modify a parameter + let min_proposal_fund_key = + namada::core::ledger::governance::storage::keys::get_min_proposal_fund_key(); + shell + .wl_storage + .write(&min_proposal_fund_key, 1_000) + .unwrap(); + + let proposal_key = namada::core::ledger::governance::storage::keys::get_proposal_execution_key(0); + shell.wl_storage.write(&proposal_key, 0).unwrap(); + + // Return a dummy tx for validation + let mut tx = Tx::from_type( + namada::types::transaction::TxType::Decrypted( + namada::types::transaction::DecryptedTx::Decrypted, + ), + ); + tx.set_data(namada::proto::Data::new( + borsh::to_vec(&0).unwrap(), + )); + tx + } + _ => panic!("Unexpected bench test"), + }; + + let (verifiers, keys_changed) = shell + .wl_storage + .write_log + .verifiers_and_changed_keys(&BTreeSet::default()); + + let vp_address = Address::Internal(InternalAddress::Parameters); + let parameters = ParametersVp { + ctx: Ctx::new( + &vp_address, + &shell.wl_storage.storage, + &shell.wl_storage.write_log, + &signed_tx, + &TxIndex(0), + VpGasMeter::new_from_tx_meter(&TxGasMeter::new_from_sub_limit( + u64::MAX.into(), + )), + &keys_changed, + &verifiers, + shell.vp_wasm_cache.clone(), + ), + }; + + group.bench_function(bench_name, |b| { + b.iter(|| { + assert!( + parameters + .validate_tx( + &signed_tx, + parameters.ctx.keys_changed, + parameters.ctx.verifiers, + ) + .unwrap() + ) + }) + }); + } + + group.finish(); +} + +fn pos(c: &mut Criterion) { + let mut group = c.benchmark_group("vp_pos"); + + for bench_name in ["foreign_key_write", "parameter_change"] { + let mut shell = BenchShell::default(); + + let signed_tx = match bench_name { + "foreign_key_write" => { + let tx = generate_foreign_key_tx(&defaults::albert_keypair()); + // Run the tx to validate + shell.execute_tx(&tx); + tx + } + "parameter_change" => { + // Simulate governance proposal to modify a parameter + let min_proposal_fund_key = + namada::core::ledger::governance::storage::keys::get_min_proposal_fund_key(); + shell + .wl_storage + .write(&min_proposal_fund_key, 1_000) + .unwrap(); + + let proposal_key = namada::core::ledger::governance::storage::keys::get_proposal_execution_key(0); + shell.wl_storage.write(&proposal_key, 0).unwrap(); + + // Return a dummy tx for validation + let mut tx = Tx::from_type( + namada::types::transaction::TxType::Decrypted( + namada::types::transaction::DecryptedTx::Decrypted, + ), + ); + tx.set_data(namada::proto::Data::new( + borsh::to_vec(&0).unwrap(), + )); + tx + } + _ => panic!("Unexpected bench test"), + }; + + let (verifiers, keys_changed) = shell + .wl_storage + .write_log + .verifiers_and_changed_keys(&BTreeSet::default()); + + let vp_address = Address::Internal(InternalAddress::PoS); + let pos = PosVP { + ctx: Ctx::new( + &vp_address, + &shell.wl_storage.storage, + &shell.wl_storage.write_log, + &signed_tx, + &TxIndex(0), + VpGasMeter::new_from_tx_meter(&TxGasMeter::new_from_sub_limit( + u64::MAX.into(), + )), + &keys_changed, + &verifiers, + shell.vp_wasm_cache.clone(), + ), + }; + + group.bench_function(bench_name, |b| { + b.iter(|| { + assert!( + pos.validate_tx( + &signed_tx, + pos.ctx.keys_changed, + pos.ctx.verifiers, + ) + .unwrap() + ) + }) + }); + } + + group.finish(); +} + +fn ibc_vp_validate_action(c: &mut Criterion) { + let mut group = c.benchmark_group("vp_ibc_validate_action"); + let shell = BenchShell::default(); + + // Connection handshake + let msg = MsgConnectionOpenInit { + client_id_on_a: ClientId::new( + ClientType::new("01-tendermint".to_string()).unwrap(), + 1, + ) + .unwrap(), + counterparty: Counterparty::new( + ClientId::from_str("01-tendermint-1").unwrap(), + None, + CommitmentPrefix::try_from(b"ibc".to_vec()).unwrap(), + ), + version: Some(Version::default()), + delay_period: std::time::Duration::new(100, 0), + signer: defaults::albert_address().to_string().into(), + }; + let open_connection = shell.generate_ibc_tx(TX_IBC_WASM, msg); + + // Channel handshake + let msg = MsgChannelOpenInit { + port_id_on_a: PortId::transfer(), + connection_hops_on_a: vec![ConnectionId::new(1)], + port_id_on_b: PortId::transfer(), + ordering: Order::Unordered, + signer: defaults::albert_address().to_string().into(), + version_proposal: ChannelVersion::new("ics20-1".to_string()), + }; + + // Avoid serializing the data again with borsh + let open_channel = shell.generate_ibc_tx(TX_IBC_WASM, msg); + + // Ibc transfer + let outgoing_transfer = shell.generate_ibc_transfer_tx(); + + for (signed_tx, bench_name) in + [open_connection, open_channel, outgoing_transfer] + .iter() + .zip(["open_connection", "open_channel", "outgoing_transfer"]) + { + let mut shell = BenchShell::default(); + // Initialize the state according to the target tx + match bench_name { + "open_connection" => { + let _ = shell.init_ibc_client_state( + namada::core::types::storage::Key::from( + Address::Internal(InternalAddress::Ibc).to_db_key(), + ), + ); + } + "open_channel" => { + let _ = shell.init_ibc_connection(); + } + "outgoing_transfer" => shell.init_ibc_channel(), + _ => panic!("Unexpected bench test"), + } + + shell.execute_tx(signed_tx); + let tx_data = signed_tx.data().unwrap(); + let (verifiers, keys_changed) = shell + .wl_storage + .write_log + .verifiers_and_changed_keys(&BTreeSet::default()); + + let ibc = Ibc { + ctx: Ctx::new( + &Address::Internal(InternalAddress::Ibc), + &shell.wl_storage.storage, + &shell.wl_storage.write_log, + signed_tx, + &TxIndex(0), + VpGasMeter::new_from_tx_meter(&TxGasMeter::new_from_sub_limit( + u64::MAX.into(), + )), + &keys_changed, + &verifiers, + shell.vp_wasm_cache.clone(), + ), + }; + let exec_ctx = PseudoExecutionContext::new(ibc.ctx.pre()); + let ctx = Rc::new(RefCell::new(exec_ctx)); + let mut actions = IbcActions::new(ctx.clone()); + actions.set_validation_params(ibc.validation_params().unwrap()); + + let module = TransferModule::new(ctx); + actions.add_transfer_route(module.module_id(), module); + + group.bench_function(bench_name, |b| { + b.iter(|| actions.validate(&tx_data).unwrap()) + }); + } + + group.finish(); +} + +fn ibc_vp_execute_action(c: &mut Criterion) { + let mut group = c.benchmark_group("vp_ibc_execute_action"); + let shell = BenchShell::default(); + + // Connection handshake + let msg = MsgConnectionOpenInit { + client_id_on_a: ClientId::new( + ClientType::new("01-tendermint".to_string()).unwrap(), + 1, + ) + .unwrap(), + counterparty: Counterparty::new( + ClientId::from_str("01-tendermint-1").unwrap(), + None, + CommitmentPrefix::try_from(b"ibc".to_vec()).unwrap(), + ), + version: Some(Version::default()), + delay_period: std::time::Duration::new(100, 0), + signer: defaults::albert_address().to_string().into(), + }; + let open_connection = shell.generate_ibc_tx(TX_IBC_WASM, msg); + + // Channel handshake + let msg = MsgChannelOpenInit { + port_id_on_a: PortId::transfer(), + connection_hops_on_a: vec![ConnectionId::new(1)], + port_id_on_b: PortId::transfer(), + ordering: Order::Unordered, + signer: defaults::albert_address().to_string().into(), + version_proposal: ChannelVersion::new("ics20-1".to_string()), + }; + + // Avoid serializing the data again with borsh + let open_channel = shell.generate_ibc_tx(TX_IBC_WASM, msg); + + // Ibc transfer + let outgoing_transfer = shell.generate_ibc_transfer_tx(); + + for (signed_tx, bench_name) in + [open_connection, open_channel, outgoing_transfer] + .iter() + .zip(["open_connection", "open_channel", "outgoing_transfer"]) + { + let mut shell = BenchShell::default(); + // Initialize the state according to the target tx + match bench_name { + "open_connection" => { + let _ = shell.init_ibc_client_state( + namada::core::types::storage::Key::from( + Address::Internal(InternalAddress::Ibc).to_db_key(), + ), + ); + } + "open_channel" => { + let _ = shell.init_ibc_connection(); + } + "outgoing_transfer" => shell.init_ibc_channel(), + _ => panic!("Unexpected bench test"), + } + + shell.execute_tx(signed_tx); + let tx_data = signed_tx.data().unwrap(); + let (verifiers, keys_changed) = shell + .wl_storage + .write_log + .verifiers_and_changed_keys(&BTreeSet::default()); + + let ibc = Ibc { + ctx: Ctx::new( + &Address::Internal(InternalAddress::Ibc), + &shell.wl_storage.storage, + &shell.wl_storage.write_log, + signed_tx, + &TxIndex(0), + VpGasMeter::new_from_tx_meter(&TxGasMeter::new_from_sub_limit( + u64::MAX.into(), + )), + &keys_changed, + &verifiers, + shell.vp_wasm_cache.clone(), + ), + }; + let exec_ctx = PseudoExecutionContext::new(ibc.ctx.pre()); + let ctx = Rc::new(RefCell::new(exec_ctx)); + let mut actions = IbcActions::new(ctx.clone()); + actions.set_validation_params(ibc.validation_params().unwrap()); + + let module = TransferModule::new(ctx); + actions.add_transfer_route(module.module_id(), module); + + group.bench_function(bench_name, |b| { + b.iter(|| actions.execute(&tx_data).unwrap()) + }); + } + + group.finish(); +} + criterion_group!( native_vps, governance, // slash_fund, ibc, - vp_multitoken + vp_multitoken, + pgf, + eth_bridge_nut, + eth_bridge, + eth_bridge_pool, + parameters, + pos, + ibc_vp_validate_action, + ibc_vp_execute_action ); criterion_main!(native_vps); diff --git a/benches/process_wrapper.rs b/benches/process_wrapper.rs index d6fbe9b483..dde61b0943 100644 --- a/benches/process_wrapper.rs +++ b/benches/process_wrapper.rs @@ -7,7 +7,7 @@ use namada::types::key::RefTo; use namada::types::storage::BlockHeight; use namada::types::time::DateTimeUtc; use namada::types::transaction::{Fee, WrapperTx}; -use namada_apps::bench_utils::{generate_tx, BenchShell, TX_TRANSFER_WASM}; +use namada_apps::bench_utils::{BenchShell, TX_TRANSFER_WASM}; use namada_apps::node::ledger::shell::process_proposal::ValidationMeta; use namada_apps::wallet::defaults; @@ -18,7 +18,7 @@ fn process_tx(c: &mut Criterion) { shell.wl_storage.storage.last_block.as_mut().unwrap().height = BlockHeight(2); - let mut tx = generate_tx( + let mut tx = shell.generate_tx( TX_TRANSFER_WASM, Transfer { source: defaults::albert_address(), @@ -42,6 +42,8 @@ fn process_tx(c: &mut Criterion) { defaults::albert_keypair().ref_to(), 0.into(), 1_000_000.into(), + // NOTE: The unshield operation has to be gas-free so don't include + // it here None, ), ))); @@ -92,7 +94,7 @@ fn process_tx(c: &mut Criterion) { 0 ) }, - criterion::BatchSize::LargeInput, + criterion::BatchSize::SmallInput, ) }); } diff --git a/benches/txs.rs b/benches/txs.rs index ac24b6ab4f..c98f085279 100644 --- a/benches/txs.rs +++ b/benches/txs.rs @@ -1,18 +1,36 @@ +use std::collections::HashMap; +use std::str::FromStr; + use criterion::{criterion_group, criterion_main, Criterion}; use namada::core::ledger::governance::storage::proposal::ProposalType; use namada::core::ledger::governance::storage::vote::{ StorageProposalVote, VoteType, }; +use namada::core::ledger::pgf::storage::steward::StewardDetail; use namada::core::types::key::{ common, SecretKey as SecretKeyInterface, SigScheme, }; use namada::core::types::token::Amount; use namada::core::types::transaction::account::{InitAccount, UpdateAccount}; use namada::core::types::transaction::pos::InitValidator; +use namada::ibc::core::ics02_client::client_type::ClientType; +use namada::ibc::core::ics03_connection::connection::Counterparty; +use namada::ibc::core::ics03_connection::msgs::conn_open_init::MsgConnectionOpenInit; +use namada::ibc::core::ics03_connection::version::Version; +use namada::ibc::core::ics04_channel::channel::Order; +use namada::ibc::core::ics04_channel::msgs::MsgChannelOpenInit; +use namada::ibc::core::ics04_channel::Version as ChannelVersion; +use namada::ibc::core::ics23_commitment::commitment::CommitmentPrefix; +use namada::ibc::core::ics24_host::identifier::{ + ClientId, ConnectionId, PortId, +}; +use namada::ledger::eth_bridge::read_native_erc20_address; use namada::ledger::storage_api::StorageRead; use namada::proof_of_stake::types::SlashType; -use namada::proof_of_stake::{self, read_pos_params}; +use namada::proof_of_stake::{self, read_pos_params, KeySeg}; use namada::proto::{Code, Section}; +use namada::types::address::Address; +use namada::types::eth_bridge_pool::{GasFee, PendingTransfer}; use namada::types::hash::Hash; use namada::types::key::{ed25519, secp256k1, PublicKey, RefTo}; use namada::types::masp::{TransferSource, TransferTarget}; @@ -25,20 +43,21 @@ use namada::types::transaction::pos::{ }; use namada::types::transaction::EllipticCurve; use namada_apps::bench_utils::{ - generate_ibc_transfer_tx, generate_tx, BenchShell, BenchShieldedCtx, - ALBERT_PAYMENT_ADDRESS, ALBERT_SPENDING_KEY, BERTHA_PAYMENT_ADDRESS, - TX_BOND_WASM, TX_CHANGE_VALIDATOR_COMMISSION_WASM, TX_INIT_ACCOUNT_WASM, - TX_INIT_PROPOSAL_WASM, TX_INIT_VALIDATOR_WASM, TX_REDELEGATE_WASM, + BenchShell, BenchShieldedCtx, ALBERT_PAYMENT_ADDRESS, ALBERT_SPENDING_KEY, + BERTHA_PAYMENT_ADDRESS, TX_BOND_WASM, TX_BRIDGE_POOL_WASM, + TX_CHANGE_VALIDATOR_COMMISSION_WASM, TX_IBC_WASM, TX_INIT_ACCOUNT_WASM, + TX_INIT_PROPOSAL_WASM, TX_REDELEGATE_WASM, TX_RESIGN_STEWARD, TX_REVEAL_PK_WASM, TX_UNBOND_WASM, TX_UNJAIL_VALIDATOR_WASM, - TX_UPDATE_ACCOUNT_WASM, TX_VOTE_PROPOSAL_WASM, TX_WITHDRAW_WASM, - VP_VALIDATOR_WASM, + TX_UPDATE_ACCOUNT_WASM, TX_UPDATE_STEWARD_COMMISSION, + TX_VOTE_PROPOSAL_WASM, TX_WITHDRAW_WASM, VP_VALIDATOR_WASM, }; use namada_apps::wallet::defaults; use rand::rngs::StdRng; use rand::SeedableRng; use sha2::Digest; -// TODO: need to benchmark tx_bridge_pool.wasm +const TX_INIT_VALIDATOR_WASM: &str = "tx_init_validator.wasm"; + fn transfer(c: &mut Criterion) { let mut group = c.benchmark_group("transfer"); let amount = Amount::native_whole(500); @@ -108,7 +127,7 @@ fn transfer(c: &mut Criterion) { |(shielded_ctx, signed_tx)| { shielded_ctx.shell.execute_tx(signed_tx); }, - criterion::BatchSize::LargeInput, + criterion::BatchSize::SmallInput, ) }); } @@ -119,7 +138,8 @@ fn transfer(c: &mut Criterion) { fn bond(c: &mut Criterion) { let mut group = c.benchmark_group("bond"); - let bond = generate_tx( + let shell = BenchShell::default(); + let bond = shell.generate_tx( TX_BOND_WASM, Bond { validator: defaults::validator_address(), @@ -131,7 +151,7 @@ fn bond(c: &mut Criterion) { Some(&defaults::albert_keypair()), ); - let self_bond = generate_tx( + let self_bond = shell.generate_tx( TX_BOND_WASM, Bond { validator: defaults::validator_address(), @@ -150,7 +170,7 @@ fn bond(c: &mut Criterion) { b.iter_batched_ref( BenchShell::default, |shell| shell.execute_tx(signed_tx), - criterion::BatchSize::LargeInput, + criterion::BatchSize::SmallInput, ) }); } @@ -161,7 +181,8 @@ fn bond(c: &mut Criterion) { fn unbond(c: &mut Criterion) { let mut group = c.benchmark_group("unbond"); - let unbond = generate_tx( + let shell = BenchShell::default(); + let unbond = shell.generate_tx( TX_UNBOND_WASM, Bond { validator: defaults::validator_address(), @@ -173,7 +194,7 @@ fn unbond(c: &mut Criterion) { Some(&defaults::albert_keypair()), ); - let self_unbond = generate_tx( + let self_unbond = shell.generate_tx( TX_UNBOND_WASM, Bond { validator: defaults::validator_address(), @@ -192,7 +213,7 @@ fn unbond(c: &mut Criterion) { b.iter_batched_ref( BenchShell::default, |shell| shell.execute_tx(signed_tx), - criterion::BatchSize::LargeInput, + criterion::BatchSize::SmallInput, ) }); } @@ -202,8 +223,9 @@ fn unbond(c: &mut Criterion) { fn withdraw(c: &mut Criterion) { let mut group = c.benchmark_group("withdraw"); + let shell = BenchShell::default(); - let withdraw = generate_tx( + let withdraw = shell.generate_tx( TX_WITHDRAW_WASM, Withdraw { validator: defaults::validator_address(), @@ -214,7 +236,7 @@ fn withdraw(c: &mut Criterion) { Some(&defaults::albert_keypair()), ); - let self_withdraw = generate_tx( + let self_withdraw = shell.generate_tx( TX_WITHDRAW_WASM, Withdraw { validator: defaults::validator_address(), @@ -236,7 +258,7 @@ fn withdraw(c: &mut Criterion) { // Unbond funds let unbond_tx = match bench_name { - "withdraw" => generate_tx( + "withdraw" => shell.generate_tx( TX_UNBOND_WASM, Bond { validator: defaults::validator_address(), @@ -247,7 +269,7 @@ fn withdraw(c: &mut Criterion) { None, Some(&defaults::albert_keypair()), ), - "self_withdraw" => generate_tx( + "self_withdraw" => shell.generate_tx( TX_UNBOND_WASM, Bond { validator: defaults::validator_address(), @@ -278,7 +300,7 @@ fn withdraw(c: &mut Criterion) { shell }, |shell| shell.execute_tx(signed_tx), - criterion::BatchSize::LargeInput, + criterion::BatchSize::SmallInput, ) }); } @@ -287,10 +309,10 @@ fn withdraw(c: &mut Criterion) { } fn redelegate(c: &mut Criterion) { - let mut group = c.benchmark_group("redelegate"); + let shell = BenchShell::default(); let redelegation = |dest_validator| { - generate_tx( + shell.generate_tx( TX_REDELEGATE_WASM, Redelegation { src_validator: defaults::validator_address(), @@ -304,7 +326,7 @@ fn redelegate(c: &mut Criterion) { ) }; - group.bench_function("redelegate", |b| { + c.bench_function("redelegate", |b| { b.iter_batched_ref( || { let shell = BenchShell::default(); @@ -316,11 +338,9 @@ fn redelegate(c: &mut Criterion) { (shell, redelegation(validator_2)) }, |(shell, tx)| shell.execute_tx(tx), - criterion::BatchSize::LargeInput, + criterion::BatchSize::SmallInput, ) }); - - group.finish(); } fn reveal_pk(c: &mut Criterion) { @@ -329,8 +349,9 @@ fn reveal_pk(c: &mut Criterion) { ed25519::SigScheme::generate(&mut csprng) .try_to_sk() .unwrap(); + let shell = BenchShell::default(); - let tx = generate_tx( + let tx = shell.generate_tx( TX_REVEAL_PK_WASM, new_implicit_account.to_public(), None, @@ -342,12 +363,12 @@ fn reveal_pk(c: &mut Criterion) { b.iter_batched_ref( BenchShell::default, |shell| shell.execute_tx(&tx), - criterion::BatchSize::LargeInput, + criterion::BatchSize::SmallInput, ) }); } -fn update_vp(c: &mut Criterion) { +fn update_account(c: &mut Criterion) { let shell = BenchShell::default(); let vp_code_hash: Hash = shell .read_storage_key(&Key::wasm_hash(VP_VALIDATOR_WASM)) @@ -364,7 +385,7 @@ fn update_vp(c: &mut Criterion) { public_keys: vec![defaults::albert_keypair().ref_to()], threshold: None, }; - let vp = generate_tx( + let vp = shell.generate_tx( TX_UPDATE_ACCOUNT_WASM, data, None, @@ -372,11 +393,11 @@ fn update_vp(c: &mut Criterion) { Some(&defaults::albert_keypair()), ); - c.bench_function("update_vp", |b| { + c.bench_function("update_account", |b| { b.iter_batched_ref( BenchShell::default, |shell| shell.execute_tx(&vp), - criterion::BatchSize::LargeInput, + criterion::BatchSize::SmallInput, ) }); } @@ -404,7 +425,7 @@ fn init_account(c: &mut Criterion) { vp_code_hash: extra_hash, threshold: 1, }; - let tx = generate_tx( + let tx = shell.generate_tx( TX_INIT_ACCOUNT_WASM, data, None, @@ -416,7 +437,7 @@ fn init_account(c: &mut Criterion) { b.iter_batched_ref( BenchShell::default, |shell| shell.execute_tx(&tx), - criterion::BatchSize::LargeInput, + criterion::BatchSize::SmallInput, ) }); } @@ -434,7 +455,7 @@ fn init_proposal(c: &mut Criterion) { "minimal_proposal" => { let content_section = Section::ExtraData(Code::new(vec![])); - generate_tx( + shell.generate_tx( TX_INIT_PROPOSAL_WASM, InitProposalData { id: None, @@ -484,7 +505,7 @@ fn init_proposal(c: &mut Criterion) { as _ ])); - generate_tx( + shell.generate_tx( TX_INIT_PROPOSAL_WASM, InitProposalData { id: Some(1), @@ -508,7 +529,7 @@ fn init_proposal(c: &mut Criterion) { (shell, signed_tx) }, |(shell, signed_tx)| shell.execute_tx(signed_tx), - criterion::BatchSize::LargeInput, + criterion::BatchSize::SmallInput, ) }); } @@ -518,7 +539,8 @@ fn init_proposal(c: &mut Criterion) { fn vote_proposal(c: &mut Criterion) { let mut group = c.benchmark_group("vote_proposal"); - let delegator_vote = generate_tx( + let shell = BenchShell::default(); + let delegator_vote = shell.generate_tx( TX_VOTE_PROPOSAL_WASM, VoteProposalData { id: 0, @@ -531,7 +553,7 @@ fn vote_proposal(c: &mut Criterion) { Some(&defaults::albert_keypair()), ); - let validator_vote = generate_tx( + let validator_vote = shell.generate_tx( TX_VOTE_PROPOSAL_WASM, VoteProposalData { id: 0, @@ -552,7 +574,7 @@ fn vote_proposal(c: &mut Criterion) { b.iter_batched_ref( BenchShell::default, |shell| shell.execute_tx(signed_tx), - criterion::BatchSize::LargeInput, + criterion::BatchSize::SmallInput, ) }); } @@ -618,7 +640,7 @@ fn init_validator(c: &mut Criterion) { max_commission_rate_change: namada::types::dec::Dec::default(), validator_vp_code_hash: extra_hash, }; - let tx = generate_tx( + let tx = shell.generate_tx( TX_INIT_VALIDATOR_WASM, data, None, @@ -630,13 +652,14 @@ fn init_validator(c: &mut Criterion) { b.iter_batched_ref( BenchShell::default, |shell| shell.execute_tx(&tx), - criterion::BatchSize::LargeInput, + criterion::BatchSize::SmallInput, ) }); } fn change_validator_commission(c: &mut Criterion) { - let signed_tx = generate_tx( + let shell = BenchShell::default(); + let signed_tx = shell.generate_tx( TX_CHANGE_VALIDATOR_COMMISSION_WASM, CommissionChange { validator: defaults::validator_address(), @@ -651,30 +674,89 @@ fn change_validator_commission(c: &mut Criterion) { b.iter_batched_ref( BenchShell::default, |shell| shell.execute_tx(&signed_tx), - criterion::BatchSize::LargeInput, + criterion::BatchSize::SmallInput, ) }); } fn ibc(c: &mut Criterion) { - let signed_tx = generate_ibc_transfer_tx(); - - c.bench_function("ibc_transfer", |b| { - b.iter_batched_ref( - || { - let mut shell = BenchShell::default(); - shell.init_ibc_channel(); + let mut group = c.benchmark_group("tx_ibc"); + let shell = BenchShell::default(); - shell - }, - |shell| shell.execute_tx(&signed_tx), - criterion::BatchSize::LargeInput, + // Connection handshake + let msg = MsgConnectionOpenInit { + client_id_on_a: ClientId::new( + ClientType::new("01-tendermint".to_string()).unwrap(), + 1, ) - }); + .unwrap(), + counterparty: Counterparty::new( + ClientId::from_str("01-tendermint-1").unwrap(), + None, + CommitmentPrefix::try_from(b"ibc".to_vec()).unwrap(), + ), + version: Some(Version::default()), + delay_period: std::time::Duration::new(100, 0), + signer: defaults::albert_address().to_string().into(), + }; + let open_connection = shell.generate_ibc_tx(TX_IBC_WASM, msg); + + // Channel handshake + let msg = MsgChannelOpenInit { + port_id_on_a: PortId::transfer(), + connection_hops_on_a: vec![ConnectionId::new(1)], + port_id_on_b: PortId::transfer(), + ordering: Order::Unordered, + signer: defaults::albert_address().to_string().into(), + version_proposal: ChannelVersion::new("ics20-1".to_string()), + }; + + // Avoid serializing the data again with borsh + let open_channel = shell.generate_ibc_tx(TX_IBC_WASM, msg); + + // Ibc transfer + let outgoing_transfer = shell.generate_ibc_transfer_tx(); + + // NOTE: Ibc encompass a variety of different messages that can be executed, + // here we only benchmark a few of those + for (signed_tx, bench_name) in + [open_connection, open_channel, outgoing_transfer] + .iter() + .zip(["open_connection", "open_channel", "outgoing_transfer"]) + { + group.bench_function(bench_name, |b| { + b.iter_batched_ref( + || { + let mut shell = BenchShell::default(); + // Initialize the state according to the target tx + match bench_name { + "open_connection" => { + let _ = shell.init_ibc_client_state( + namada::core::types::storage::Key::from( + Address::Internal(namada::types::address::InternalAddress::Ibc).to_db_key(), + ), + ); + } + "open_channel" => { + let _ = shell.init_ibc_connection(); + } + "outgoing_transfer" => shell.init_ibc_channel(), + _ => panic!("Unexpected bench test"), + } + shell + }, + |shell| shell.execute_tx(signed_tx), + criterion::BatchSize::SmallInput, + ) + }); + } + + group.finish(); } fn unjail_validator(c: &mut Criterion) { - let signed_tx = generate_tx( + let shell = BenchShell::default(); + let signed_tx = shell.generate_tx( TX_UNJAIL_VALIDATOR_WASM, defaults::validator_address(), None, @@ -713,7 +795,106 @@ fn unjail_validator(c: &mut Criterion) { shell }, |shell| shell.execute_tx(&signed_tx), - criterion::BatchSize::LargeInput, + criterion::BatchSize::SmallInput, + ) + }); +} + +fn tx_bridge_pool(c: &mut Criterion) { + let shell = BenchShell::default(); + + let data = PendingTransfer { + transfer: namada::types::eth_bridge_pool::TransferToEthereum { + kind: namada::types::eth_bridge_pool::TransferToEthereumKind::Erc20, + asset: read_native_erc20_address(&shell.wl_storage).unwrap(), + recipient: namada::types::ethereum_events::EthAddress([1u8; 20]), + sender: defaults::albert_address(), + amount: Amount::from(1), + }, + gas_fee: GasFee { + amount: Amount::from(100), + payer: defaults::albert_address(), + token: shell.wl_storage.storage.native_token.clone(), + }, + }; + let tx = shell.generate_tx( + TX_BRIDGE_POOL_WASM, + data, + None, + None, + Some(&defaults::albert_keypair()), + ); + c.bench_function("bridge pool", |b| { + b.iter_batched_ref( + BenchShell::default, + |shell| shell.execute_tx(&tx), + criterion::BatchSize::SmallInput, + ) + }); +} + +fn resign_steward(c: &mut Criterion) { + c.bench_function("resign steward", |b| { + b.iter_batched_ref( + || { + let mut shell = BenchShell::default(); + namada::core::ledger::pgf::storage::keys::stewards_handle() + .insert( + &mut shell.wl_storage, + defaults::albert_address(), + StewardDetail::base(defaults::albert_address()), + ) + .unwrap(); + + let tx = shell.generate_tx( + TX_RESIGN_STEWARD, + defaults::albert_address(), + None, + None, + Some(&defaults::albert_keypair()), + ); + + (shell, tx) + }, + |(shell, tx)| shell.execute_tx(tx), + criterion::BatchSize::SmallInput, + ) + }); +} + +fn update_steward_commission(c: &mut Criterion) { + c.bench_function("update steward commission", |b| { + b.iter_batched_ref( + || { + let mut shell = BenchShell::default(); + namada::core::ledger::pgf::storage::keys::stewards_handle() + .insert( + &mut shell.wl_storage, + defaults::albert_address(), + StewardDetail::base(defaults::albert_address()), + ) + .unwrap(); + + let data = + namada::types::transaction::pgf::UpdateStewardCommission { + steward: defaults::albert_address(), + commission: HashMap::from([( + defaults::albert_address(), + namada::types::dec::Dec::zero(), + )]), + }; + let tx = shell.generate_tx( + TX_UPDATE_STEWARD_COMMISSION, + data, + None, + None, + Some(&defaults::albert_keypair()), + ); + + (shell, tx) + }, + |(shell, tx)| shell.execute_tx(tx), + criterion::BatchSize::SmallInput, ) }); } @@ -726,13 +907,16 @@ criterion_group!( withdraw, redelegate, reveal_pk, - update_vp, + update_account, init_account, init_proposal, vote_proposal, init_validator, change_validator_commission, ibc, - unjail_validator + unjail_validator, + tx_bridge_pool, + resign_steward, + update_steward_commission ); criterion_main!(whitelisted_txs); diff --git a/benches/vps.rs b/benches/vps.rs index 77dfdcae87..9023e38bcd 100644 --- a/benches/vps.rs +++ b/benches/vps.rs @@ -20,7 +20,7 @@ use namada::types::transaction::governance::VoteProposalData; use namada::types::transaction::pos::{Bond, CommissionChange}; use namada::vm::wasm::run; use namada_apps::bench_utils::{ - generate_foreign_key_tx, generate_tx, BenchShell, BenchShieldedCtx, + generate_foreign_key_tx, BenchShell, BenchShieldedCtx, ALBERT_PAYMENT_ADDRESS, ALBERT_SPENDING_KEY, BERTHA_PAYMENT_ADDRESS, TX_BOND_WASM, TX_CHANGE_VALIDATOR_COMMISSION_WASM, TX_REVEAL_PK_WASM, TX_TRANSFER_WASM, TX_UNBOND_WASM, TX_UPDATE_ACCOUNT_WASM, @@ -43,7 +43,7 @@ fn vp_user(c: &mut Criterion) { let foreign_key_write = generate_foreign_key_tx(&defaults::albert_keypair()); - let transfer = generate_tx( + let transfer = shell.generate_tx( TX_TRANSFER_WASM, Transfer { source: defaults::albert_address(), @@ -58,7 +58,7 @@ fn vp_user(c: &mut Criterion) { Some(&defaults::albert_keypair()), ); - let received_transfer = generate_tx( + let received_transfer = shell.generate_tx( TX_TRANSFER_WASM, Transfer { source: defaults::bertha_address(), @@ -88,7 +88,7 @@ fn vp_user(c: &mut Criterion) { public_keys: vec![defaults::albert_keypair().to_public()], threshold: None, }; - let vp = generate_tx( + let vp = shell.generate_tx( TX_UPDATE_ACCOUNT_WASM, data, None, @@ -96,7 +96,7 @@ fn vp_user(c: &mut Criterion) { Some(&defaults::albert_keypair()), ); - let vote = generate_tx( + let vote = shell.generate_tx( TX_VOTE_PROPOSAL_WASM, VoteProposalData { id: 0, @@ -109,7 +109,7 @@ fn vp_user(c: &mut Criterion) { Some(&defaults::albert_keypair()), ); - let pos = generate_tx( + let pos = shell.generate_tx( TX_UNBOND_WASM, Bond { validator: defaults::validator_address(), @@ -148,6 +148,8 @@ fn vp_user(c: &mut Criterion) { group.bench_function(bench_name, |b| { b.iter(|| { assert!( + // NOTE: the wasm code is always in cache so we don't + // include here the cost to read and compile the vp code run::vp( vp_code_hash, signed_tx, @@ -184,7 +186,8 @@ fn vp_implicit(c: &mut Criterion) { let foreign_key_write = generate_foreign_key_tx(&defaults::albert_keypair()); - let transfer = generate_tx( + let shell = BenchShell::default(); + let transfer = shell.generate_tx( TX_TRANSFER_WASM, Transfer { source: Address::from(&implicit_account.to_public()), @@ -199,7 +202,7 @@ fn vp_implicit(c: &mut Criterion) { Some(&implicit_account), ); - let received_transfer = generate_tx( + let received_transfer = shell.generate_tx( TX_TRANSFER_WASM, Transfer { source: defaults::bertha_address(), @@ -214,7 +217,7 @@ fn vp_implicit(c: &mut Criterion) { Some(&defaults::bertha_keypair()), ); - let reveal_pk = generate_tx( + let reveal_pk = shell.generate_tx( TX_REVEAL_PK_WASM, &implicit_account.to_public(), None, @@ -222,7 +225,7 @@ fn vp_implicit(c: &mut Criterion) { None, ); - let pos = generate_tx( + let pos = shell.generate_tx( TX_BOND_WASM, Bond { validator: defaults::validator_address(), @@ -234,7 +237,7 @@ fn vp_implicit(c: &mut Criterion) { Some(&implicit_account), ); - let vote = generate_tx( + let vote = shell.generate_tx( TX_VOTE_PROPOSAL_WASM, VoteProposalData { id: 0, @@ -327,7 +330,7 @@ fn vp_validator(c: &mut Criterion) { let foreign_key_write = generate_foreign_key_tx(&defaults::albert_keypair()); - let transfer = generate_tx( + let transfer = shell.generate_tx( TX_TRANSFER_WASM, Transfer { source: defaults::validator_address(), @@ -342,7 +345,7 @@ fn vp_validator(c: &mut Criterion) { Some(&defaults::validator_keypair()), ); - let received_transfer = generate_tx( + let received_transfer = shell.generate_tx( TX_TRANSFER_WASM, Transfer { source: defaults::bertha_address(), @@ -369,7 +372,7 @@ fn vp_validator(c: &mut Criterion) { public_keys: vec![defaults::validator_keypair().to_public()], threshold: None, }; - let vp = generate_tx( + let vp = shell.generate_tx( TX_UPDATE_ACCOUNT_WASM, data, None, @@ -377,7 +380,7 @@ fn vp_validator(c: &mut Criterion) { Some(&defaults::validator_keypair()), ); - let commission_rate = generate_tx( + let commission_rate = shell.generate_tx( TX_CHANGE_VALIDATOR_COMMISSION_WASM, CommissionChange { validator: defaults::validator_address(), @@ -388,7 +391,7 @@ fn vp_validator(c: &mut Criterion) { Some(&defaults::validator_keypair()), ); - let vote = generate_tx( + let vote = shell.generate_tx( TX_VOTE_PROPOSAL_WASM, VoteProposalData { id: 0, @@ -401,7 +404,7 @@ fn vp_validator(c: &mut Criterion) { Some(&defaults::validator_keypair()), ); - let pos = generate_tx( + let pos = shell.generate_tx( TX_UNBOND_WASM, Bond { validator: defaults::validator_address(), diff --git a/core/src/ledger/gas.rs b/core/src/ledger/gas.rs index 1403111a55..0fefccd983 100644 --- a/core/src/ledger/gas.rs +++ b/core/src/ledger/gas.rs @@ -20,30 +20,45 @@ pub enum Error { BlockGasExceeded, #[error("Overflow during gas operations")] GasOverflow, - #[error("Error converting to u64")] - ConversionError, } -const TX_SIZE_GAS_PER_BYTE: u64 = 10; -const COMPILE_GAS_PER_BYTE: u64 = 1; +const COMPILE_GAS_PER_BYTE: u64 = 24; const PARALLEL_GAS_DIVIDER: u64 = 10; - -/// The cost of accessing the storage, per byte -pub const STORAGE_ACCESS_GAS_PER_BYTE: u64 = 1; -/// The cost of writing to storage, per byte -pub const STORAGE_WRITE_GAS_PER_BYTE: u64 = 100; +const WASM_CODE_VALIDATION_GAS_PER_BYTE: u64 = 1; +const WRAPPER_TX_VALIDATION_GAS: u64 = 58_371; +const STORAGE_OCCUPATION_GAS_PER_BYTE: u64 = + 100 + PHYSICAL_STORAGE_LATENCY_PER_BYTE; +// NOTE: this accounts for the latency of a physical drive access. For read +// accesses we have no way to tell if data was in cache or in storage. Moreover, +// the latency shouldn't really be accounted per single byte but rather per +// storage blob but this would make it more tedious to compute gas in the +// codebase. For these two reasons we just set an arbitrary value (based on +// actual SSDs latency) per byte here +const PHYSICAL_STORAGE_LATENCY_PER_BYTE: u64 = 75; + +/// The cost of accessing data from memory (both read and write mode), per byte +pub const MEMORY_ACCESS_GAS_PER_BYTE: u64 = 2; +/// The cost of accessing data from storage, per byte +pub const STORAGE_ACCESS_GAS_PER_BYTE: u64 = + 3 + PHYSICAL_STORAGE_LATENCY_PER_BYTE; +/// The cost of writing data to storage, per byte +pub const STORAGE_WRITE_GAS_PER_BYTE: u64 = + MEMORY_ACCESS_GAS_PER_BYTE + 848 + STORAGE_OCCUPATION_GAS_PER_BYTE; /// The cost of verifying a signle signature of a transaction -pub const VERIFY_TX_SIG_GAS_COST: u64 = 10; -/// The cost of accessing the WASM memory, per byte -pub const VM_MEMORY_ACCESS_GAS_PER_BYTE: u64 = 1; -/// The cost for requesting one more page in wasm (64KB) -pub const WASM_MEMORY_PAGE_GAS_COST: u32 = 100; +pub const VERIFY_TX_SIG_GAS: u64 = 9_793; +/// The cost for requesting one more page in wasm (64KiB) +pub const WASM_MEMORY_PAGE_GAS: u32 = + MEMORY_ACCESS_GAS_PER_BYTE as u32 * 64 * 1_024; +/// The cost to validate an Ibc action +pub const IBC_ACTION_VALIDATE_GAS: u64 = 7_511; +/// The cost to execute an Ibc action +pub const IBC_ACTION_EXECUTE_GAS: u64 = 47_452; /// Gas module result for functions that may fail pub type Result = std::result::Result; /// Decimal scale of Gas units -const SCALE: u64 = 1_000; +const SCALE: u64 = 10_000; /// Helper function to retrieve the `max_block_gas` protocol parameter from /// storage @@ -167,6 +182,15 @@ pub trait GasMetering { ) } + /// Add the gas for validating untrusted wasm code + fn add_wasm_validation_gas(&mut self, bytes_len: u64) -> Result<()> { + self.consume( + bytes_len + .checked_mul(WASM_CODE_VALIDATION_GAS_PER_BYTE) + .ok_or(Error::GasOverflow)?, + ) + } + /// Get the gas consumed by the tx alone fn get_tx_consumed_gas(&self) -> Gas; @@ -244,15 +268,16 @@ impl TxGasMeter { } } - /// Add the gas for the space that the transaction requires in the block - pub fn add_tx_size_gas(&mut self, tx_bytes: &[u8]) -> Result<()> { - let bytes_len: u64 = tx_bytes - .len() - .try_into() - .map_err(|_| Error::ConversionError)?; + /// Add the gas required by a wrapper transaction which is comprised of: + /// - cost of validating the wrapper tx + /// - space that the transaction requires in the block + pub fn add_wrapper_gas(&mut self, tx_bytes: &[u8]) -> Result<()> { + self.consume(WRAPPER_TX_VALIDATION_GAS)?; + + let bytes_len = tx_bytes.len() as u64; self.consume( bytes_len - .checked_mul(TX_SIZE_GAS_PER_BYTE) + .checked_mul(STORAGE_OCCUPATION_GAS_PER_BYTE) .ok_or(Error::GasOverflow)?, ) } diff --git a/core/src/ledger/storage/mod.rs b/core/src/ledger/storage/mod.rs index b484246228..1c194640d5 100644 --- a/core/src/ledger/storage/mod.rs +++ b/core/src/ledger/storage/mod.rs @@ -30,6 +30,7 @@ pub use self::masp_conversions::update_allowed_conversions; pub use self::masp_conversions::{ calculate_masp_rewards, encode_asset_type, ConversionState, }; +use super::gas::MEMORY_ACCESS_GAS_PER_BYTE; use crate::ledger::eth_bridge::storage::bridge_pool::is_pending_transfer_key; use crate::ledger::gas::{ STORAGE_ACCESS_GAS_PER_BYTE, STORAGE_WRITE_GAS_PER_BYTE, @@ -52,6 +53,7 @@ use crate::types::internal::TxQueue; use crate::types::storage::{ BlockHash, BlockHeight, BlockResults, Epoch, Epochs, EthEventsQueue, Header, Key, KeySeg, MembershipProof, TxIndex, BLOCK_HASH_LENGTH, + BLOCK_HEIGHT_LENGTH, EPOCH_TYPE_LENGTH, }; use crate::types::time::DateTimeUtc; use crate::types::{ethereum_structs, token}; @@ -669,7 +671,7 @@ where ) } - /// Returns a prefix iterator and the gas cost + /// Returns an iterator over the block results pub fn iter_results(&self) -> (>::PrefixIter, u64) { (self.db.iter_results(), 0) } @@ -770,20 +772,23 @@ where pub fn get_chain_id(&self) -> (String, u64) { ( self.chain_id.to_string(), - CHAIN_ID_LENGTH as u64 * STORAGE_ACCESS_GAS_PER_BYTE, + CHAIN_ID_LENGTH as u64 * MEMORY_ACCESS_GAS_PER_BYTE, ) } /// Get the block height pub fn get_block_height(&self) -> (BlockHeight, u64) { - (self.block.height, STORAGE_ACCESS_GAS_PER_BYTE) + ( + self.block.height, + BLOCK_HEIGHT_LENGTH as u64 * MEMORY_ACCESS_GAS_PER_BYTE, + ) } /// Get the block hash pub fn get_block_hash(&self) -> (BlockHash, u64) { ( self.block.hash.clone(), - BLOCK_HASH_LENGTH as u64 * STORAGE_ACCESS_GAS_PER_BYTE, + BLOCK_HASH_LENGTH as u64 * MEMORY_ACCESS_GAS_PER_BYTE, ) } @@ -945,12 +950,18 @@ where /// Get the current (yet to be committed) block epoch pub fn get_current_epoch(&self) -> (Epoch, u64) { - (self.block.epoch, STORAGE_ACCESS_GAS_PER_BYTE) + ( + self.block.epoch, + EPOCH_TYPE_LENGTH as u64 * MEMORY_ACCESS_GAS_PER_BYTE, + ) } /// Get the epoch of the last committed block pub fn get_last_epoch(&self) -> (Epoch, u64) { - (self.last_epoch, STORAGE_ACCESS_GAS_PER_BYTE) + ( + self.last_epoch, + EPOCH_TYPE_LENGTH as u64 * MEMORY_ACCESS_GAS_PER_BYTE, + ) } /// Initialize the first epoch. The first epoch begins at genesis time. @@ -976,7 +987,14 @@ where ) -> Result<(Option
, u64)> { match height { Some(h) if h == self.get_block_height().0 => { - Ok((self.header.clone(), STORAGE_ACCESS_GAS_PER_BYTE)) + let header = self.header.clone(); + let gas = match header { + Some(ref header) => { + header.encoded_len() as u64 * MEMORY_ACCESS_GAS_PER_BYTE + } + None => MEMORY_ACCESS_GAS_PER_BYTE, + }; + Ok((header, gas)) } Some(h) => match self.db.read_block_header(h)? { Some(header) => { diff --git a/core/src/ledger/storage/wl_storage.rs b/core/src/ledger/storage/wl_storage.rs index 4d613365c0..8b4f1a5750 100644 --- a/core/src/ledger/storage/wl_storage.rs +++ b/core/src/ledger/storage/wl_storage.rs @@ -306,7 +306,7 @@ where storage_iter, write_log_iter, }, - gas::STORAGE_ACCESS_GAS_PER_BYTE, + prefix.len() as u64 * gas::STORAGE_ACCESS_GAS_PER_BYTE, ) } @@ -331,7 +331,7 @@ where storage_iter, write_log_iter, }, - gas::STORAGE_ACCESS_GAS_PER_BYTE, + prefix.len() as u64 * gas::STORAGE_ACCESS_GAS_PER_BYTE, ) } diff --git a/core/src/ledger/storage/write_log.rs b/core/src/ledger/storage/write_log.rs index e563374146..0cb7b91a2e 100644 --- a/core/src/ledger/storage/write_log.rs +++ b/core/src/ledger/storage/write_log.rs @@ -8,7 +8,7 @@ use thiserror::Error; use crate::ledger; use crate::ledger::gas::{ - STORAGE_ACCESS_GAS_PER_BYTE, STORAGE_WRITE_GAS_PER_BYTE, + MEMORY_ACCESS_GAS_PER_BYTE, STORAGE_WRITE_GAS_PER_BYTE, }; use crate::ledger::replay_protection::{ get_replay_protection_all_subkey, get_replay_protection_last_subkey, @@ -169,9 +169,9 @@ impl WriteLog { key.len() + value.len() } }; - (Some(v), gas as u64 * STORAGE_ACCESS_GAS_PER_BYTE) + (Some(v), gas as u64 * MEMORY_ACCESS_GAS_PER_BYTE) } - None => (None, key.len() as u64 * STORAGE_ACCESS_GAS_PER_BYTE), + None => (None, key.len() as u64 * MEMORY_ACCESS_GAS_PER_BYTE), } } @@ -197,9 +197,9 @@ impl WriteLog { key.len() + value.len() } }; - (Some(v), gas as u64 * STORAGE_ACCESS_GAS_PER_BYTE) + (Some(v), gas as u64 * MEMORY_ACCESS_GAS_PER_BYTE) } - None => (None, key.len() as u64 * STORAGE_ACCESS_GAS_PER_BYTE), + None => (None, key.len() as u64 * MEMORY_ACCESS_GAS_PER_BYTE), } } @@ -302,7 +302,7 @@ impl WriteLog { }; // Temp writes are not propagated to db so just charge the cost of // accessing storage - Ok((gas as u64 * STORAGE_ACCESS_GAS_PER_BYTE, size_diff)) + Ok((gas as u64 * MEMORY_ACCESS_GAS_PER_BYTE, size_diff)) } /// Delete a key and its value, and return the gas cost and the size @@ -383,7 +383,7 @@ impl WriteLog { .iter() .fold(0, |acc, (k, v)| acc + k.len() + v.len()); self.ibc_events.insert(event); - len as u64 * STORAGE_ACCESS_GAS_PER_BYTE + len as u64 * MEMORY_ACCESS_GAS_PER_BYTE } /// Get the storage keys changed and accounts keys initialized in the @@ -750,7 +750,7 @@ mod tests { // read a non-existing key let (value, gas) = write_log.read(&key); assert!(value.is_none()); - assert_eq!(gas, key.len() as u64); + assert_eq!(gas, (key.len() as u64) * MEMORY_ACCESS_GAS_PER_BYTE); // delete a non-existing key let (gas, diff) = write_log.delete(&key).unwrap(); @@ -774,7 +774,10 @@ mod tests { } _ => panic!("unexpected read result"), } - assert_eq!(gas, (key.len() + inserted.len()) as u64); + assert_eq!( + gas, + ((key.len() + inserted.len()) as u64) * MEMORY_ACCESS_GAS_PER_BYTE + ); // update the value let updated = "updated".as_bytes().to_vec(); @@ -804,7 +807,7 @@ mod tests { StorageModification::Delete => {} _ => panic!("unexpected result"), } - assert_eq!(gas, key.len() as u64 * STORAGE_ACCESS_GAS_PER_BYTE); + assert_eq!(gas, key.len() as u64 * MEMORY_ACCESS_GAS_PER_BYTE); // insert again let reinserted = "reinserted".as_bytes().to_vec(); @@ -841,7 +844,7 @@ mod tests { } assert_eq!( gas, - (vp_key.len() + vp_hash.len()) as u64 * STORAGE_ACCESS_GAS_PER_BYTE + (vp_key.len() + vp_hash.len()) as u64 * MEMORY_ACCESS_GAS_PER_BYTE ); // get all diff --git a/core/src/proto/types.rs b/core/src/proto/types.rs index c650162339..ebb279a1eb 100644 --- a/core/src/proto/types.rs +++ b/core/src/proto/types.rs @@ -26,9 +26,7 @@ use sha2::{Digest, Sha256}; use thiserror::Error; use super::generated::types; -use crate::ledger::gas::{ - self, GasMetering, VpGasMeter, VERIFY_TX_SIG_GAS_COST, -}; +use crate::ledger::gas::{self, GasMetering, VpGasMeter, VERIFY_TX_SIG_GAS}; use crate::ledger::storage::{KeccakHasher, Sha256Hasher, StorageHasher}; #[cfg(any(feature = "tendermint", feature = "tendermint-abcipp"))] use crate::tendermint_proto::abci::ResponseDeliverTx; @@ -594,7 +592,7 @@ impl Signature { { if let Some(meter) = gas_meter { meter - .consume(VERIFY_TX_SIG_GAS_COST) + .consume(VERIFY_TX_SIG_GAS) .map_err(VerifySigError::OutOfGas)?; } common::SigScheme::verify_signature( @@ -619,7 +617,7 @@ impl Signature { { if let Some(meter) = gas_meter { meter - .consume(VERIFY_TX_SIG_GAS_COST) + .consume(VERIFY_TX_SIG_GAS) .map_err(VerifySigError::OutOfGas)?; } common::SigScheme::verify_signature( diff --git a/core/src/types/storage.rs b/core/src/types/storage.rs index 5dba69ed36..6889173373 100644 --- a/core/src/types/storage.rs +++ b/core/src/types/storage.rs @@ -53,6 +53,12 @@ pub type Result = std::result::Result; /// The length of the block's hash string pub const BLOCK_HASH_LENGTH: usize = 32; +/// The length of the transaction index +pub const TX_INDEX_LENGTH: usize = 4; +/// The length of the block height +pub const BLOCK_HEIGHT_LENGTH: usize = 8; +/// The length of the epoch type +pub const EPOCH_TYPE_LENGTH: usize = 8; /// The separator of storage key segments pub const KEY_SEGMENT_SEPARATOR: char = '/'; diff --git a/shared/src/ledger/native_vp/ibc/context.rs b/shared/src/ledger/native_vp/ibc/context.rs index 7926f3d838..5da26c52d7 100644 --- a/shared/src/ledger/native_vp/ibc/context.rs +++ b/shared/src/ledger/native_vp/ibc/context.rs @@ -23,6 +23,7 @@ use super::Error; use crate::ledger::native_vp::CtxPreStorageRead; use crate::vm::WasmCacheAccess; +/// Pseudo execution environment context for ibc native vp #[derive(Debug)] pub struct PseudoExecutionContext<'view, 'a, DB, H, CA> where @@ -44,6 +45,7 @@ where H: 'static + StorageHasher, CA: 'static + WasmCacheAccess, { + /// Generate new pseudo execution context pub fn new(ctx: CtxPreStorageRead<'view, 'a, DB, H, CA>) -> Self { Self { store: HashMap::new(), @@ -52,11 +54,16 @@ where } } - pub fn get_changed_keys(&self) -> HashSet<&Key> { + /// Get the set of changed keys + pub(crate) fn get_changed_keys(&self) -> HashSet<&Key> { self.store.keys().filter(|k| is_ibc_key(k)).collect() } - pub fn get_changed_value(&self, key: &Key) -> Option<&StorageModification> { + /// Get the changed value + pub(crate) fn get_changed_value( + &self, + key: &Key, + ) -> Option<&StorageModification> { self.store.get(key) } } @@ -282,6 +289,7 @@ where { } +/// Ibc native vp validation context #[derive(Debug)] pub struct VpValidationContext<'view, 'a, DB, H, CA> where @@ -299,6 +307,7 @@ where H: 'static + StorageHasher, CA: 'static + WasmCacheAccess, { + /// Generate a new ibc vp validation context pub fn new(ctx: CtxPreStorageRead<'view, 'a, DB, H, CA>) -> Self { Self { ctx } } diff --git a/shared/src/ledger/native_vp/ibc/mod.rs b/shared/src/ledger/native_vp/ibc/mod.rs index 902bef0a5b..f69b8c71c0 100644 --- a/shared/src/ledger/native_vp/ibc/mod.rs +++ b/shared/src/ledger/native_vp/ibc/mod.rs @@ -1,6 +1,6 @@ //! IBC integration as a native validity predicate -mod context; +pub mod context; use std::cell::RefCell; use std::collections::{BTreeSet, HashSet}; @@ -8,6 +8,9 @@ use std::rc::Rc; use std::time::Duration; use context::{PseudoExecutionContext, VpValidationContext}; +use namada_core::ledger::gas::{ + IBC_ACTION_EXECUTE_GAS, IBC_ACTION_VALIDATE_GAS, +}; use namada_core::ledger::ibc::{ Error as ActionError, IbcActions, TransferModule, ValidationParams, }; @@ -104,6 +107,10 @@ where let mut actions = IbcActions::new(ctx.clone()); let module = TransferModule::new(ctx.clone()); actions.add_transfer_route(module.module_id(), module); + // Charge gas for the expensive execution + self.ctx + .charge_gas(IBC_ACTION_EXECUTE_GAS) + .map_err(Error::NativeVpError)?; actions.execute(tx_data)?; let changed_ibc_keys: HashSet<&Key> = @@ -146,10 +153,15 @@ where let module = TransferModule::new(ctx); actions.add_transfer_route(module.module_id(), module); + // Charge gas for the expensive validation + self.ctx + .charge_gas(IBC_ACTION_VALIDATE_GAS) + .map_err(Error::NativeVpError)?; actions.validate(tx_data).map_err(Error::IbcAction) } - fn validation_params(&self) -> VpResult { + /// Retrieve the validation params + pub fn validation_params(&self) -> VpResult { let chain_id = self.ctx.get_chain_id().map_err(Error::NativeVpError)?; let proof_specs = ledger_storage::ics23_specs::ibc_proof_specs::(); let pos_params = diff --git a/shared/src/ledger/native_vp/mod.rs b/shared/src/ledger/native_vp/mod.rs index 60c30daeb3..a590a1e433 100644 --- a/shared/src/ledger/native_vp/mod.rs +++ b/shared/src/ledger/native_vp/mod.rs @@ -11,6 +11,7 @@ use std::collections::BTreeSet; use borsh::BorshDeserialize; use eyre::WrapErr; +use namada_core::ledger::gas::GasMetering; pub use namada_core::ledger::vp_env::VpEnv; use namada_core::types::validity_predicate::VpSentinel; @@ -567,8 +568,10 @@ where unimplemented!("no masp native vp") } - fn charge_gas(&self, _used_gas: u64) -> Result<(), storage_api::Error> { - unimplemented!("Native vps don't consume whitelisted gas") + fn charge_gas(&self, used_gas: u64) -> Result<(), storage_api::Error> { + self.gas_meter.borrow_mut().consume(used_gas).map_err(|_| { + Error::SimpleMessage("Gas limit exceeded in native vp") + }) } fn get_tx_code_hash(&self) -> Result, storage_api::Error> { diff --git a/shared/src/ledger/protocol/mod.rs b/shared/src/ledger/protocol/mod.rs index 07430c1ccf..837bdabd86 100644 --- a/shared/src/ledger/protocol/mod.rs +++ b/shared/src/ledger/protocol/mod.rs @@ -253,6 +253,9 @@ where )?; // Account for gas + shell_params.tx_gas_meter.add_wrapper_gas(tx_bytes)?; + + // If wrapper was succesful, write inner tx hash to storage shell_params .tx_gas_meter .add_tx_size_gas(tx_bytes) @@ -411,40 +414,10 @@ where ) .map_err(|e| Error::FeeError(e.to_string())) } else { - // Balance was insufficient for fee payment - #[cfg(not(any(feature = "abciplus", feature = "abcipp")))] - { - // Move all the available funds in the transparent - // balance of the fee payer - token_transfer( - wl_storage, - &wrapper.fee.token, - &wrapper.fee_payer(), - block_proposer, - balance, - ) - .map_err(|e| Error::FeeError(e.to_string()))?; - - return Err(Error::FeeError( - "Transparent balance of wrapper's signer was \ - insufficient to pay fee. All the available \ - transparent funds have been moved to the block \ - proposer" - .to_string(), - )); - } - #[cfg(any(feature = "abciplus", feature = "abcipp"))] - return Err(Error::FeeError( - "Insufficient transparent balance to pay fees".to_string(), - )); - } - } - Err(e) => { - // Fee overflow - #[cfg(not(any(feature = "abciplus", feature = "abcipp")))] - { - // Move all the available funds in the transparent balance of - // the fee payer + // Balance was insufficient for fee payment, move all the + // available funds in the transparent balance of + // the fee payer. This branch should only be taken when using + // ABCI token_transfer( wl_storage, &wrapper.fee.token, @@ -454,15 +427,32 @@ where ) .map_err(|e| Error::FeeError(e.to_string()))?; - return Err(Error::FeeError(format!( - "{}. All the available transparent funds have been moved \ - to the block proposer", - e - ))); + Err(Error::FeeError( + "Transparent balance of wrapper's signer was insufficient \ + to pay fee. All the available transparent funds have \ + been moved to the block proposer" + .to_string(), + )) } + } + Err(e) => { + // Fee overflow, move all the available funds in the transparent + // balance of the fee payer. This branch should only be + // taken when using ABCI + token_transfer( + wl_storage, + &wrapper.fee.token, + &wrapper.fee_payer(), + block_proposer, + balance, + ) + .map_err(|e| Error::FeeError(e.to_string()))?; - #[cfg(any(feature = "abciplus", feature = "abcipp"))] - return Err(Error::FeeError(e.to_string())); + Err(Error::FeeError(format!( + "{}. All the available transparent funds have been moved to \ + the block proposer", + e + ))) } } } diff --git a/shared/src/ledger/vp_host_fns.rs b/shared/src/ledger/vp_host_fns.rs index 757b2d688b..1aa8419cb0 100644 --- a/shared/src/ledger/vp_host_fns.rs +++ b/shared/src/ledger/vp_host_fns.rs @@ -2,15 +2,15 @@ use std::num::TryFromIntError; -use namada_core::types::address::Address; -use namada_core::types::hash::Hash; +use namada_core::ledger::gas::MEMORY_ACCESS_GAS_PER_BYTE; +use namada_core::types::address::{Address, ESTABLISHED_ADDRESS_BYTES_LEN}; +use namada_core::types::hash::{Hash, HASH_LENGTH}; use namada_core::types::storage::{ - BlockHash, BlockHeight, Epoch, Header, Key, TxIndex, + BlockHash, BlockHeight, Epoch, Header, Key, TxIndex, TX_INDEX_LENGTH, }; use namada_core::types::validity_predicate::VpSentinel; use thiserror::Error; -use super::gas::STORAGE_ACCESS_GAS_PER_BYTE; use crate::ledger::gas; use crate::ledger::gas::{GasMetering, VpGasMeter}; use crate::ledger::storage::write_log::WriteLog; @@ -303,11 +303,11 @@ pub fn get_tx_code_hash( tx: &Tx, sentinel: &mut VpSentinel, ) -> EnvResult> { + add_gas(gas_meter, HASH_LENGTH as u64 * MEMORY_ACCESS_GAS_PER_BYTE)?; let hash = tx .get_section(tx.code_sechash()) .and_then(|x| Section::code_sec(x.as_ref())) .map(|x| x.code.hash()); - add_gas(gas_meter, STORAGE_ACCESS_GAS_PER_BYTE, sentinel)?; Ok(hash) } @@ -334,7 +334,10 @@ pub fn get_tx_index( tx_index: &TxIndex, sentinel: &mut VpSentinel, ) -> EnvResult { - add_gas(gas_meter, STORAGE_ACCESS_GAS_PER_BYTE, sentinel)?; + add_gas( + gas_meter, + TX_INDEX_LENGTH as u64 * MEMORY_ACCESS_GAS_PER_BYTE, + )?; Ok(*tx_index) } @@ -348,7 +351,10 @@ where DB: storage::DB + for<'iter> storage::DBIter<'iter>, H: StorageHasher, { - add_gas(gas_meter, STORAGE_ACCESS_GAS_PER_BYTE, sentinel)?; + add_gas( + gas_meter, + ESTABLISHED_ADDRESS_BYTES_LEN as u64 * MEMORY_ACCESS_GAS_PER_BYTE, + )?; Ok(storage.native_token.clone()) } diff --git a/shared/src/vm/host_env.rs b/shared/src/vm/host_env.rs index a7540bcf62..a4cdfffcfa 100644 --- a/shared/src/vm/host_env.rs +++ b/shared/src/vm/host_env.rs @@ -7,8 +7,12 @@ use std::num::TryFromIntError; use borsh::{BorshDeserialize, BorshSerialize}; use borsh_ext::BorshSerializeExt; use masp_primitives::transaction::Transaction; -use namada_core::ledger::gas::{GasMetering, TxGasMeter}; +use namada_core::ledger::gas::{ + GasMetering, TxGasMeter, MEMORY_ACCESS_GAS_PER_BYTE, +}; +use namada_core::types::address::ESTABLISHED_ADDRESS_BYTES_LEN; use namada_core::types::internal::KeyVal; +use namada_core::types::storage::TX_INDEX_LENGTH; use namada_core::types::transaction::TxSentinel; use namada_core::types::validity_predicate::VpSentinel; use thiserror::Error; @@ -18,7 +22,7 @@ use super::wasm::TxCache; #[cfg(feature = "wasm-runtime")] use super::wasm::VpCache; use super::WasmCacheAccess; -use crate::ledger::gas::{self, VpGasMeter, STORAGE_ACCESS_GAS_PER_BYTE}; +use crate::ledger::gas::{self, VpGasMeter}; use crate::ledger::storage::write_log::{self, WriteLog}; use crate::ledger::storage::{self, Storage, StorageHasher}; use crate::ledger::vp_host_fns; @@ -1412,10 +1416,12 @@ where let addr = Address::decode(&addr).map_err(TxRuntimeError::AddressError)?; let verifiers = unsafe { env.ctx.verifiers.get() }; - verifiers.insert(addr); // This is not a storage write, use the same multiplier used for a storage // read - tx_charge_gas(env, addr_len * STORAGE_ACCESS_GAS_PER_BYTE) + tx_charge_gas(env, addr_len * MEMORY_ACCESS_GAS_PER_BYTE)?; + verifiers.insert(addr); + + Ok(()) } /// Update a validity predicate function exposed to the wasm VM Tx environment @@ -1545,8 +1551,8 @@ where H: StorageHasher, CA: WasmCacheAccess, { + tx_charge_gas(env, TX_INDEX_LENGTH as u64 * MEMORY_ACCESS_GAS_PER_BYTE)?; let tx_index = unsafe { env.ctx.tx_index.get() }; - tx_charge_gas(env, crate::vm::host_env::gas::STORAGE_ACCESS_GAS_PER_BYTE)?; Ok(tx_index.0) } @@ -1621,8 +1627,12 @@ where H: StorageHasher, CA: WasmCacheAccess, { + // Gas for getting the native token address from storage + tx_charge_gas( + env, + ESTABLISHED_ADDRESS_BYTES_LEN as u64 * MEMORY_ACCESS_GAS_PER_BYTE, + )?; let storage = unsafe { env.ctx.storage.get() }; - tx_charge_gas(env, STORAGE_ACCESS_GAS_PER_BYTE)?; let native_token = storage.native_token.clone(); let native_token_string = native_token.encode(); let gas = env @@ -1647,6 +1657,7 @@ where let (header, gas) = storage .get_block_header(Some(BlockHeight(height))) .map_err(TxRuntimeError::StorageError)?; + tx_charge_gas(env, gas)?; Ok(match header { Some(h) => { let value = h.serialize_to_vec(); @@ -1656,7 +1667,6 @@ where .map_err(TxRuntimeError::NumConversionError)?; let result_buffer = unsafe { env.ctx.result_buffer.get() }; result_buffer.replace(value); - tx_charge_gas(env, gas)?; len } None => HostEnvResult::Fail.to_i64(), @@ -2556,10 +2566,7 @@ where CA: WasmCacheAccess, { let tx_index = unsafe { ctx.tx_index.get() }; - ibc_tx_charge_gas( - ctx, - crate::vm::host_env::gas::STORAGE_ACCESS_GAS_PER_BYTE, - )?; + ibc_tx_charge_gas(ctx, MEMORY_ACCESS_GAS_PER_BYTE)?; Ok(TxIndex(tx_index.0)) } diff --git a/shared/src/vm/wasm/memory.rs b/shared/src/vm/wasm/memory.rs index 9b04160556..b93ecb79f1 100644 --- a/shared/src/vm/wasm/memory.rs +++ b/shared/src/vm/wasm/memory.rs @@ -6,7 +6,7 @@ use std::str::Utf8Error; use std::sync::Arc; use borsh_ext::BorshSerializeExt; -use namada_core::ledger::gas::VM_MEMORY_ACCESS_GAS_PER_BYTE; +use namada_core::ledger::gas::MEMORY_ACCESS_GAS_PER_BYTE; use thiserror::Error; use wasmer::{ vm, BaseTunables, HostEnvInitError, LazyInit, Memory, MemoryError, @@ -256,13 +256,16 @@ impl VmMemory for WasmMemory { fn read_bytes(&self, offset: u64, len: usize) -> Result<(Vec, u64)> { let memory = self.inner.get_ref().ok_or(Error::UninitializedMemory)?; let bytes = read_memory_bytes(memory, offset, len)?; - let gas = bytes.len() as u64 * VM_MEMORY_ACCESS_GAS_PER_BYTE; + let gas = bytes.len() as u64 * MEMORY_ACCESS_GAS_PER_BYTE; Ok((bytes, gas)) } /// Write bytes into memory at the given offset and return the gas cost fn write_bytes(&self, offset: u64, bytes: impl AsRef<[u8]>) -> Result { - let gas = bytes.as_ref().len() as u64 * VM_MEMORY_ACCESS_GAS_PER_BYTE; + // No need for a separate gas multiplier for writes since we are only + // writing to memory and we already charge gas for every memory page + // allocated + let gas = bytes.as_ref().len() as u64 * MEMORY_ACCESS_GAS_PER_BYTE; let memory = self.inner.get_ref().ok_or(Error::UninitializedMemory)?; write_memory_bytes(memory, offset, bytes)?; Ok(gas) diff --git a/shared/src/vm/wasm/run.rs b/shared/src/vm/wasm/run.rs index d4ce052124..acfaf9b70a 100644 --- a/shared/src/vm/wasm/run.rs +++ b/shared/src/vm/wasm/run.rs @@ -4,9 +4,7 @@ use std::collections::BTreeSet; use std::marker::PhantomData; use borsh::BorshDeserialize; -use namada_core::ledger::gas::{ - GasMetering, TxGasMeter, WASM_MEMORY_PAGE_GAS_COST, -}; +use namada_core::ledger::gas::{GasMetering, TxGasMeter, WASM_MEMORY_PAGE_GAS}; use namada_core::ledger::storage::write_log::StorageModification; use namada_core::types::transaction::TxSentinel; use namada_core::types::validity_predicate::VpSentinel; @@ -564,13 +562,13 @@ where Ok((module, store)) } Commitment::Id(code) => { + let tx_len = code.len() as u64; gas_meter - .add_compiling_gas( - u64::try_from(code.len()) - .map_err(|e| Error::ConversionError(e.to_string()))?, - ) + .add_wasm_validation_gas(tx_len) .map_err(|e| Error::GasError(e.to_string()))?; validate_untrusted_wasm(code).map_err(Error::ValidationError)?; + + gas_meter.add_compiling_gas(tx_len)?; match wasm_cache.compile_or_fetch(code)? { Some((module, store)) => Ok((module, store)), None => Err(Error::NoCompiledWasmCode), @@ -584,7 +582,7 @@ fn get_gas_rules() -> wasm_instrument::gas_metering::ConstantCostRules { // NOTE: costs set to 0 don't actually trigger the injection of a call to // the gas host function (no useless instructions are injected) let instruction_cost = 0; - let memory_grow_cost = WASM_MEMORY_PAGE_GAS_COST; + let memory_grow_cost = WASM_MEMORY_PAGE_GAS; let call_per_local_cost = 0; wasm_instrument::gas_metering::ConstantCostRules::new( instruction_cost, @@ -609,7 +607,7 @@ mod tests { use crate::types::validity_predicate::EvalVp; use crate::vm::wasm; - const TX_GAS_LIMIT: u64 = 100_000_000; + const TX_GAS_LIMIT: u64 = 10_000_000_000; /// Test that when a transaction wasm goes over the stack-height limit, the /// execution is aborted. diff --git a/tests/src/vm_host_env/vp.rs b/tests/src/vm_host_env/vp.rs index 22b2562ed3..5233a2affd 100644 --- a/tests/src/vm_host_env/vp.rs +++ b/tests/src/vm_host_env/vp.rs @@ -77,7 +77,7 @@ impl Default for TestVpEnv { wl_storage, iterators: PrefixIterators::default(), gas_meter: VpGasMeter::new_from_tx_meter( - &TxGasMeter::new_from_sub_limit(10_000_000.into()), + &TxGasMeter::new_from_sub_limit(10_000_000_000.into()), ), sentinel: VpSentinel::default(), tx, diff --git a/wasm/wasm_source/src/tx_bond.rs b/wasm/wasm_source/src/tx_bond.rs index 9f0e71d005..ebce8af37d 100644 --- a/wasm/wasm_source/src/tx_bond.rs +++ b/wasm/wasm_source/src/tx_bond.rs @@ -2,7 +2,7 @@ use namada_tx_prelude::*; -#[transaction(gas = 160000)] +#[transaction(gas = 1342908)] fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { let signed = tx_data; let data = signed.data().ok_or_err_msg("Missing data").map_err(|err| { diff --git a/wasm/wasm_source/src/tx_bridge_pool.rs b/wasm/wasm_source/src/tx_bridge_pool.rs index 364e597550..321005d23e 100644 --- a/wasm/wasm_source/src/tx_bridge_pool.rs +++ b/wasm/wasm_source/src/tx_bridge_pool.rs @@ -6,7 +6,7 @@ use eth_bridge_pool::{GasFee, PendingTransfer, TransferToEthereum}; use namada_tx_prelude::borsh_ext::BorshSerializeExt; use namada_tx_prelude::*; -#[transaction(gas = 100000)] +#[transaction(gas = 1038546)] fn apply_tx(ctx: &mut Ctx, signed: Tx) -> TxResult { let data = signed.data().ok_or_err_msg("Missing data").map_err(|err| { ctx.set_commitment_sentinel(); diff --git a/wasm/wasm_source/src/tx_change_validator_commission.rs b/wasm/wasm_source/src/tx_change_validator_commission.rs index dd0a1be0c1..0cb09b682e 100644 --- a/wasm/wasm_source/src/tx_change_validator_commission.rs +++ b/wasm/wasm_source/src/tx_change_validator_commission.rs @@ -3,7 +3,7 @@ use namada_tx_prelude::transaction::pos::CommissionChange; use namada_tx_prelude::*; -#[transaction(gas = 220000)] +#[transaction(gas = 1319787)] fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { let signed = tx_data; let data = signed.data().ok_or_err_msg("Missing data").map_err(|err| { diff --git a/wasm/wasm_source/src/tx_ibc.rs b/wasm/wasm_source/src/tx_ibc.rs index 2a6d2fcae3..bf06ebbbca 100644 --- a/wasm/wasm_source/src/tx_ibc.rs +++ b/wasm/wasm_source/src/tx_ibc.rs @@ -5,7 +5,7 @@ use namada_tx_prelude::*; -#[transaction(gas = 1240000)] +#[transaction(gas = 585022)] fn apply_tx(_ctx: &mut Ctx, _tx_data: Tx) -> TxResult { // let signed = tx_data; // let data = signed.data().ok_or_err_msg("Missing data").or_else(|err| { diff --git a/wasm/wasm_source/src/tx_init_account.rs b/wasm/wasm_source/src/tx_init_account.rs index da8ddc21f1..236d1335a2 100644 --- a/wasm/wasm_source/src/tx_init_account.rs +++ b/wasm/wasm_source/src/tx_init_account.rs @@ -3,7 +3,7 @@ use namada_tx_prelude::*; -#[transaction(gas = 230000)] +#[transaction(gas = 885069)] fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { let signed = tx_data; let data = signed.data().ok_or_err_msg("Missing data").map_err(|err| { diff --git a/wasm/wasm_source/src/tx_init_proposal.rs b/wasm/wasm_source/src/tx_init_proposal.rs index 8c860d9d22..64aa02bcb1 100644 --- a/wasm/wasm_source/src/tx_init_proposal.rs +++ b/wasm/wasm_source/src/tx_init_proposal.rs @@ -2,7 +2,7 @@ use namada_tx_prelude::*; -#[transaction(gas = 40000)] +#[transaction(gas = 969395)] fn apply_tx(ctx: &mut Ctx, tx: Tx) -> TxResult { let data = tx.data().ok_or_err_msg("Missing data").map_err(|err| { ctx.set_commitment_sentinel(); diff --git a/wasm/wasm_source/src/tx_init_validator.rs b/wasm/wasm_source/src/tx_init_validator.rs index bea7ed0a9f..554b4a8ec8 100644 --- a/wasm/wasm_source/src/tx_init_validator.rs +++ b/wasm/wasm_source/src/tx_init_validator.rs @@ -4,7 +4,7 @@ use namada_tx_prelude::transaction::pos::InitValidator; use namada_tx_prelude::*; -#[transaction(gas = 730000)] +#[transaction(gas = 4395397)] fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { let signed = tx_data; let data = signed.data().ok_or_err_msg("Missing data").map_err(|err| { diff --git a/wasm/wasm_source/src/tx_redelegate.rs b/wasm/wasm_source/src/tx_redelegate.rs index 84f45093aa..820d2b935f 100644 --- a/wasm/wasm_source/src/tx_redelegate.rs +++ b/wasm/wasm_source/src/tx_redelegate.rs @@ -3,7 +3,7 @@ use namada_tx_prelude::*; -#[transaction(gas = 460000)] +#[transaction(gas = 2453242)] fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { let signed = tx_data; let data = signed.data().ok_or_err_msg("Missing data").map_err(|err| { diff --git a/wasm/wasm_source/src/tx_resign_steward.rs b/wasm/wasm_source/src/tx_resign_steward.rs index 79ea99ab2f..10d8045895 100644 --- a/wasm/wasm_source/src/tx_resign_steward.rs +++ b/wasm/wasm_source/src/tx_resign_steward.rs @@ -2,7 +2,7 @@ use namada_tx_prelude::*; -#[transaction(gas = 40000)] +#[transaction(gas = 1058710)] fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { let signed = tx_data; let data = signed.data().ok_or_err_msg("Missing data").map_err(|err| { diff --git a/wasm/wasm_source/src/tx_reveal_pk.rs b/wasm/wasm_source/src/tx_reveal_pk.rs index ba2078d806..f5a23d0e1f 100644 --- a/wasm/wasm_source/src/tx_reveal_pk.rs +++ b/wasm/wasm_source/src/tx_reveal_pk.rs @@ -6,7 +6,7 @@ use namada_tx_prelude::key::common; use namada_tx_prelude::*; -#[transaction(gas = 170000)] +#[transaction(gas = 919818)] fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { let signed = tx_data; let data = signed.data().ok_or_err_msg("Missing data").map_err(|err| { diff --git a/wasm/wasm_source/src/tx_transfer.rs b/wasm/wasm_source/src/tx_transfer.rs index 9164ff1656..e92750e016 100644 --- a/wasm/wasm_source/src/tx_transfer.rs +++ b/wasm/wasm_source/src/tx_transfer.rs @@ -4,7 +4,7 @@ use namada_tx_prelude::*; -#[transaction(gas = 110000)] +#[transaction(gas = 1703358)] fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { let signed = tx_data; let data = signed.data().ok_or_err_msg("Missing data").map_err(|err| { diff --git a/wasm/wasm_source/src/tx_unbond.rs b/wasm/wasm_source/src/tx_unbond.rs index a440692d43..497d70ae6d 100644 --- a/wasm/wasm_source/src/tx_unbond.rs +++ b/wasm/wasm_source/src/tx_unbond.rs @@ -3,7 +3,7 @@ use namada_tx_prelude::*; -#[transaction(gas = 430000)] +#[transaction(gas = 2645941)] fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { let signed = tx_data; let data = signed.data().ok_or_err_msg("Missing data").map_err(|err| { diff --git a/wasm/wasm_source/src/tx_unjail_validator.rs b/wasm/wasm_source/src/tx_unjail_validator.rs index 9beeb8086a..7a11152876 100644 --- a/wasm/wasm_source/src/tx_unjail_validator.rs +++ b/wasm/wasm_source/src/tx_unjail_validator.rs @@ -3,7 +3,7 @@ use namada_tx_prelude::*; -#[transaction(gas = 340000)] +#[transaction(gas = 1641054)] fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { let signed = tx_data; let data = signed.data().ok_or_err_msg("Missing data").map_err(|err| { diff --git a/wasm/wasm_source/src/tx_update_account.rs b/wasm/wasm_source/src/tx_update_account.rs index a4279ca6a2..f7f6b5729a 100644 --- a/wasm/wasm_source/src/tx_update_account.rs +++ b/wasm/wasm_source/src/tx_update_account.rs @@ -5,7 +5,7 @@ use namada_tx_prelude::key::pks_handle; use namada_tx_prelude::*; -#[transaction(gas = 140000)] +#[transaction(gas = 968137)] fn apply_tx(ctx: &mut Ctx, tx: Tx) -> TxResult { let signed = tx; let data = signed.data().ok_or_err_msg("Missing data").map_err(|err| { diff --git a/wasm/wasm_source/src/tx_update_steward_commission.rs b/wasm/wasm_source/src/tx_update_steward_commission.rs index 0d98bd1ba1..164414bcd8 100644 --- a/wasm/wasm_source/src/tx_update_steward_commission.rs +++ b/wasm/wasm_source/src/tx_update_steward_commission.rs @@ -3,7 +3,7 @@ use namada_tx_prelude::transaction::pgf::UpdateStewardCommission; use namada_tx_prelude::*; -#[transaction(gas = 40000)] +#[transaction(gas = 1222239)] fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { let signed = tx_data; let data = signed.data().ok_or_err_msg("Missing data").map_err(|err| { diff --git a/wasm/wasm_source/src/tx_update_vp.rs b/wasm/wasm_source/src/tx_update_vp.rs deleted file mode 100644 index 7d372ee749..0000000000 --- a/wasm/wasm_source/src/tx_update_vp.rs +++ /dev/null @@ -1,34 +0,0 @@ -//! A tx for updating an account's validity predicate. -//! This tx wraps the validity predicate inside `SignedTxData` as -//! its input as declared in `shared` crate. - -use namada_tx_prelude::*; - -#[transaction(gas = 40000)] -fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { - let signed = tx_data; - let data = signed.data().ok_or_err_msg("Missing data").map_err(|err| { - ctx.set_commitment_sentinel(); - err - })?; - let update_vp = transaction::UpdateVp::try_from_slice(&data[..]) - .wrap_err("failed to decode UpdateVp")?; - - debug_log!("update VP for: {:#?}", update_vp.addr); - let vp_code_hash = signed - .get_section(&update_vp.vp_code_hash) - .ok_or_err_msg("vp code section not found") - .map_err(|err| { - ctx.set_commitment_sentinel(); - err - })? - .extra_data_sec() - .ok_or_err_msg("vp code section must be tagged as extra") - .map_err(|err| { - ctx.set_commitment_sentinel(); - err - })? - .code - .hash(); - ctx.update_validity_predicate(&update_vp.addr, vp_code_hash) -} diff --git a/wasm/wasm_source/src/tx_vote_proposal.rs b/wasm/wasm_source/src/tx_vote_proposal.rs index d23820ab8a..66f0c77bf1 100644 --- a/wasm/wasm_source/src/tx_vote_proposal.rs +++ b/wasm/wasm_source/src/tx_vote_proposal.rs @@ -2,7 +2,7 @@ use namada_tx_prelude::*; -#[transaction(gas = 120000)] +#[transaction(gas = 840866)] fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { let signed = tx_data; let data = signed.data().ok_or_err_msg("Missing data").map_err(|err| { diff --git a/wasm/wasm_source/src/tx_withdraw.rs b/wasm/wasm_source/src/tx_withdraw.rs index 4ba555c9dc..ca07bf4538 100644 --- a/wasm/wasm_source/src/tx_withdraw.rs +++ b/wasm/wasm_source/src/tx_withdraw.rs @@ -3,7 +3,7 @@ use namada_tx_prelude::*; -#[transaction(gas = 260000)] +#[transaction(gas = 1119469)] fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { let signed = tx_data; let data = signed.data().ok_or_err_msg("Missing data").map_err(|err| { diff --git a/wasm/wasm_source/src/vp_implicit.rs b/wasm/wasm_source/src/vp_implicit.rs index c6c41fa363..be62ed3494 100644 --- a/wasm/wasm_source/src/vp_implicit.rs +++ b/wasm/wasm_source/src/vp_implicit.rs @@ -47,7 +47,7 @@ impl<'a> From<&'a storage::Key> for KeyType<'a> { } } -#[validity_predicate(gas = 40000)] +#[validity_predicate(gas = 118452)] fn validate_tx( ctx: &Ctx, tx_data: Tx, diff --git a/wasm/wasm_source/src/vp_masp.rs b/wasm/wasm_source/src/vp_masp.rs index b22fceafff..c04ba1b844 100644 --- a/wasm/wasm_source/src/vp_masp.rs +++ b/wasm/wasm_source/src/vp_masp.rs @@ -72,7 +72,7 @@ fn convert_amount( (asset_type, amount) } -#[validity_predicate(gas = 8030000)] +#[validity_predicate(gas = 62210635)] fn validate_tx( ctx: &Ctx, tx_data: Tx, diff --git a/wasm/wasm_source/src/vp_user.rs b/wasm/wasm_source/src/vp_user.rs index b7c120532f..333f84315a 100644 --- a/wasm/wasm_source/src/vp_user.rs +++ b/wasm/wasm_source/src/vp_user.rs @@ -48,7 +48,7 @@ impl<'a> From<&'a storage::Key> for KeyType<'a> { } } -#[validity_predicate(gas = 60000)] +#[validity_predicate(gas = 137325)] fn validate_tx( ctx: &Ctx, tx_data: Tx, diff --git a/wasm/wasm_source/src/vp_validator.rs b/wasm/wasm_source/src/vp_validator.rs index 88383803d0..5f6c263496 100644 --- a/wasm/wasm_source/src/vp_validator.rs +++ b/wasm/wasm_source/src/vp_validator.rs @@ -48,7 +48,7 @@ impl<'a> From<&'a storage::Key> for KeyType<'a> { } } -#[validity_predicate(gas = 50000)] +#[validity_predicate(gas = 134761)] fn validate_tx( ctx: &Ctx, tx_data: Tx,