From 470d1ae3ec73458f3163a168f8c1d0bcb0b05415 Mon Sep 17 00:00:00 2001 From: Oleksandr Pravdyvyi Date: Fri, 28 Feb 2025 12:32:54 +0200 Subject: [PATCH] fix: gas calculation model --- common/src/lib.rs | 4 + node_core/src/config.rs | 35 +++++++ node_core/src/lib.rs | 7 +- node_rpc/src/types/err_rpc.rs | 6 ++ node_runner/configs/debug/node_config.json | 11 +- zkvm/src/gas_calculator.rs | 113 +++++++++++++++++++++ zkvm/src/lib.rs | 25 +++++ 7 files changed, 198 insertions(+), 3 deletions(-) create mode 100644 zkvm/src/gas_calculator.rs diff --git a/common/src/lib.rs b/common/src/lib.rs index 0ebfcdf..2049ae6 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -52,6 +52,10 @@ pub enum ExecutionFailureKind { SequencerClientError(#[from] SequencerClientError), #[error("Datebase returned error : {0:?}")] MonoTreeError(#[from] monotree::Errors), + #[error("Insufficient gas for operation")] + InsufficientGasError, + #[error("Can not pay for operation")] + InsufficientFundsError, } impl ExecutionFailureKind { diff --git a/node_core/src/config.rs b/node_core/src/config.rs index f282a21..d92e358 100644 --- a/node_core/src/config.rs +++ b/node_core/src/config.rs @@ -1,6 +1,39 @@ use std::path::PathBuf; use serde::{Deserialize, Serialize}; +use zkvm::gas_calculator::GasCalculator; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct GasConfig { + /// Gas spent per deploying one byte of data + pub gas_fee_per_byte_deploy: u64, + /// Gas spent per reading one byte of data in VM + pub gas_fee_per_input_buffer_runtime: u64, + /// Gas spent per one byte of contract data in runtime + pub gas_fee_per_byte_runtime: u64, + /// Cost of one gas of runtime in public balance + pub gas_cost_runtime: u64, + /// Cost of one gas of deployment in public balance + pub gas_cost_deploy: u64, + /// Gas limit for deployment + pub gas_limit_deploy: u64, + /// Gas limit for runtime + pub gas_limit_runtime: u64, +} + +impl From for zkvm::gas_calculator::GasCalculator { + fn from(value: GasConfig) -> Self { + GasCalculator::new( + value.gas_fee_per_byte_deploy, + value.gas_fee_per_input_buffer_runtime, + value.gas_fee_per_byte_runtime, + value.gas_cost_runtime, + value.gas_cost_deploy, + value.gas_limit_deploy, + value.gas_limit_runtime, + ) + } +} #[derive(Debug, Clone, Serialize, Deserialize)] pub struct NodeConfig { @@ -14,4 +47,6 @@ pub struct NodeConfig { pub seq_poll_timeout_secs: u64, ///Port to listen pub port: u16, + ///Gas config + pub gas_config: GasConfig, } diff --git a/node_core/src/lib.rs b/node_core/src/lib.rs index 28c0bb5..0eca3f5 100644 --- a/node_core/src/lib.rs +++ b/node_core/src/lib.rs @@ -23,8 +23,9 @@ use storage::NodeChainStore; use tokio::{sync::RwLock, task::JoinHandle}; use utxo::utxo_core::UTXO; use zkvm::{ - prove_mint_utxo, prove_mint_utxo_multiple_assets, prove_send_utxo, prove_send_utxo_deshielded, - prove_send_utxo_multiple_assets_one_receiver, prove_send_utxo_shielded, + gas_calculator::GasCalculator, prove_mint_utxo, prove_mint_utxo_multiple_assets, + prove_send_utxo, prove_send_utxo_deshielded, prove_send_utxo_multiple_assets_one_receiver, + prove_send_utxo_shielded, }; pub const BLOCK_GEN_DELAY_SECS: u64 = 20; @@ -70,6 +71,7 @@ pub struct NodeCore { pub node_config: NodeConfig, pub db_updater_handle: JoinHandle>, pub sequencer_client: Arc, + pub gas_calculator: GasCalculator, } impl NodeCore { @@ -141,6 +143,7 @@ impl NodeCore { node_config: config.clone(), db_updater_handle: updater_handle, sequencer_client: client.clone(), + gas_calculator: GasCalculator::from(config.gas_config), }) } diff --git a/node_rpc/src/types/err_rpc.rs b/node_rpc/src/types/err_rpc.rs index 9eafb93..df80de9 100644 --- a/node_rpc/src/types/err_rpc.rs +++ b/node_rpc/src/types/err_rpc.rs @@ -71,6 +71,12 @@ pub fn cast_common_execution_error_into_rpc_error(comm_exec_err: ExecutionFailur ExecutionFailureKind::AmountMismatchError => { RpcError::new_internal_error(None, &error_string) } + ExecutionFailureKind::InsufficientGasError => { + RpcError::new_internal_error(None, &error_string) + } + ExecutionFailureKind::InsufficientFundsError => { + RpcError::new_internal_error(None, &error_string) + } ExecutionFailureKind::SequencerClientError(seq_cli_err) => { cast_seq_client_error_into_rpc_error(seq_cli_err) } diff --git a/node_runner/configs/debug/node_config.json b/node_runner/configs/debug/node_config.json index e1dd6b7..f8c064e 100644 --- a/node_runner/configs/debug/node_config.json +++ b/node_runner/configs/debug/node_config.json @@ -3,5 +3,14 @@ "override_rust_log": null, "sequencer_addr": "http://127.0.0.1:3040", "seq_poll_timeout_secs": 10, - "port": 3041 + "port": 3041, + "gas_config": { + "gas_fee_per_byte_deploy": 100, + "gas_fee_per_input_buffer_runtime": 1000, + "gas_fee_per_byte_runtime": 10, + "gas_cost_runtime": 100, + "gas_cost_deploy": 1000, + "gas_limit_deploy": 30000000, + "gas_limit_runtime": 30000000 + } } \ No newline at end of file diff --git a/zkvm/src/gas_calculator.rs b/zkvm/src/gas_calculator.rs new file mode 100644 index 0000000..ac2c737 --- /dev/null +++ b/zkvm/src/gas_calculator.rs @@ -0,0 +1,113 @@ +#[derive(Debug, Clone)] +pub struct GasCalculator { + /// Gas spent per deploying one byte of data + gas_fee_per_byte_deploy: u64, + /// Gas spent per reading one byte of data in VM + gas_fee_per_input_buffer_runtime: u64, + /// Gas spent per one byte of contract data in runtime + gas_fee_per_byte_runtime: u64, + /// Cost of one gas of runtime in public balance + gas_cost_runtime: u64, + /// Cost of one gas of deployment in public balance + gas_cost_deploy: u64, + /// Gas limit for deployment + gas_limit_deploy: u64, + /// Gas limit for runtime + gas_limit_runtime: u64, +} + +impl GasCalculator { + pub fn new( + gas_fee_per_byte_deploy: u64, + gas_fee_per_input_buffer_runtime: u64, + gas_fee_per_byte_runtime: u64, + gas_cost_runtime: u64, + gas_cost_deploy: u64, + gas_limit_deploy: u64, + gas_limit_runtime: u64, + ) -> Self { + Self { + gas_fee_per_byte_deploy, + gas_fee_per_input_buffer_runtime, + gas_fee_per_byte_runtime, + gas_cost_deploy, + gas_cost_runtime, + gas_limit_deploy, + gas_limit_runtime, + } + } + + pub fn gas_fee_per_byte_deploy(&self) -> u64 { + self.gas_fee_per_byte_deploy + } + + pub fn gas_fee_per_input_buffer_runtime(&self) -> u64 { + self.gas_fee_per_input_buffer_runtime + } + + pub fn gas_fee_per_byte_runtime(&self) -> u64 { + self.gas_fee_per_byte_runtime + } + + pub fn gas_cost_runtime(&self) -> u64 { + self.gas_cost_runtime + } + + pub fn gas_cost_deploy(&self) -> u64 { + self.gas_cost_deploy + } + + pub fn gas_limit_deploy(&self) -> u64 { + self.gas_limit_deploy + } + + pub fn gas_limit_runtime(&self) -> u64 { + self.gas_limit_runtime + } + + ///Returns Option + /// + /// Some(_) - in case if `gas` < `gas_limit_deploy` + /// + /// None - else + pub fn gas_deploy(&self, elf: &[u8]) -> Option { + let gas = self.gas_fee_per_byte_deploy() * (elf.len() as u64); + + if gas < self.gas_limit_deploy() { + Some(gas) + } else { + None + } + } + + pub fn gas_runtime(&self, elf: &[u8]) -> u64 { + self.gas_fee_per_byte_runtime() * (elf.len() as u64) + } + + pub fn gas_input_buffer(&self, input_length: usize) -> u64 { + self.gas_fee_per_input_buffer_runtime() * (input_length as u64) + } + + ///Returns Option + /// + /// Some(_) - in case if `gas` < `gas_limit_runtime` + /// + /// None - else + pub fn gas_runtime_full(&self, elf: &[u8], input_length: usize) -> Option { + let gas = self.gas_runtime(elf) + self.gas_input_buffer(input_length); + + if gas < self.gas_limit_runtime() { + Some(gas) + } else { + None + } + } + + pub fn deploy_cost(&self, deploy_gas: u64) -> u64 { + deploy_gas * self.gas_cost_deploy() + } + + pub fn runtime_cost(&self, runtime_gas: u64) -> u64 { + runtime_gas * self.gas_cost_runtime() + } +} diff --git a/zkvm/src/lib.rs b/zkvm/src/lib.rs index 93692ed..b531bee 100644 --- a/zkvm/src/lib.rs +++ b/zkvm/src/lib.rs @@ -1,8 +1,33 @@ use accounts::account_core::AccountAddress; use common::ExecutionFailureKind; use risc0_zkvm::{default_executor, default_prover, sha::Digest, ExecutorEnv, Receipt}; +use serde::Serialize; use utxo::utxo_core::{UTXOPayload, UTXO}; +pub mod gas_calculator; + +pub fn gas_limits_check( + input_buffer: INP, + elf: &[u8], + gas_calculator: &gas_calculator::GasCalculator, + attached_funds: u64, +) -> Result<(), ExecutionFailureKind> { + let mut input_buffer_len: usize = 0; + input_buffer_len += serde_json::to_vec(&input_buffer).unwrap().len(); + + let gas_limit = gas_calculator + .gas_runtime_full(elf, input_buffer_len) + .ok_or(ExecutionFailureKind::InsufficientGasError)?; + + let cost = gas_calculator.runtime_cost(gas_limit); + + if cost > attached_funds { + return Err(ExecutionFailureKind::InsufficientFundsError); + } + + Ok(()) +} + pub fn prove_mint_utxo( amount_to_mint: u128, owner: AccountAddress,