unc_parameters/
vm.rs

1use crate::cost::{ExtCostsConfig, ParameterCost};
2use borsh::BorshSerialize;
3use std::collections::hash_map::DefaultHasher;
4use std::hash::{Hash, Hasher};
5use unc_primitives_core::config::AccountIdValidityRulesVersion;
6use unc_primitives_core::types::Gas;
7
8// NOTE that VMKind is part of serialization protocol, so we cannot remove entries from this list
9// if particular VM reached publicly visible networks.
10//
11// Additionally, this is public only for the purposes of internal tools like the estimator. This
12// API should otherwise be considered a private configuration of the `unc-vm-runner`
13// crate.
14#[derive(
15    Clone,
16    Copy,
17    Debug,
18    Hash,
19    BorshSerialize,
20    PartialEq,
21    Eq,
22    strum::EnumString,
23    serde::Serialize,
24    serde::Deserialize,
25)]
26#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
27pub enum VMKind {
28    /// Wasmer 0.17.x VM.
29    Wasmer0,
30    /// Wasmtime VM.
31    Wasmtime,
32    /// Wasmer 2.x VM.
33    Wasmer2,
34    /// UncVM.
35    UncVm,
36}
37
38impl VMKind {
39    pub fn replace_with_wasmtime_if_unsupported(self) -> Self {
40        if cfg!(not(target_arch = "x86_64")) {
41            Self::Wasmtime
42        } else {
43            self
44        }
45    }
46}
47
48/// This enum represents if a storage_get call will be performed through flat storage or trie
49#[derive(PartialEq, Eq, Hash, Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
50pub enum StorageGetMode {
51    FlatStorage,
52    Trie,
53}
54
55/// Describes limits for VM and Runtime.
56/// TODO #4139: consider switching to strongly-typed wrappers instead of raw quantities
57#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, Hash, PartialEq, Eq)]
58pub struct LimitConfig {
59    /// Max amount of gas that can be used, excluding gas attached to promises.
60    pub max_gas_burnt: Gas,
61
62    /// How tall the stack is allowed to grow?
63    ///
64    /// See <https://wiki.parity.io/WebAssembly-StackHeight> to find out how the stack frame cost
65    /// is calculated.
66    pub max_stack_height: u32,
67    /// Whether a legacy version of stack limiting should be used, see
68    /// [`ContractPrepareVersion`].
69    #[serde(default = "ContractPrepareVersion::v0")]
70    pub contract_prepare_version: ContractPrepareVersion,
71
72    /// The initial number of memory pages.
73    /// NOTE: It's not a limiter itself, but it's a value we use for initial_memory_pages.
74    pub initial_memory_pages: u32,
75    /// What is the maximal memory pages amount is allowed to have for a contract.
76    pub max_memory_pages: u32,
77
78    /// Limit of memory used by registers.
79    pub registers_memory_limit: u64,
80    /// Maximum number of bytes that can be stored in a single register.
81    pub max_register_size: u64,
82    /// Maximum number of registers that can be used simultaneously.
83    ///
84    /// Note that due to an implementation quirk [read: a bug] in VMLogic, if we
85    /// have this number of registers, no subsequent writes to the registers
86    /// will succeed even if they replace an existing register.
87    pub max_number_registers: u64,
88
89    /// Maximum number of log entries.
90    pub max_number_logs: u64,
91    /// Maximum total length in bytes of all log messages.
92    pub max_total_log_length: u64,
93
94    /// Max total prepaid gas for all function call actions per receipt.
95    pub max_total_prepaid_gas: Gas,
96
97    /// Max number of actions per receipt.
98    pub max_actions_per_receipt: u64,
99    /// Max total length of all method names (including terminating character) for a function call
100    /// permission access key.
101    pub max_number_bytes_method_names: u64,
102    /// Max length of any method name (without terminating character).
103    pub max_length_method_name: u64,
104    /// Max length of arguments in a function call action.
105    pub max_arguments_length: u64,
106    /// Max length of returned data
107    pub max_length_returned_data: u64,
108    /// Max contract size
109    pub max_contract_size: u64,
110    /// Max transaction size
111    pub max_transaction_size: u64,
112    /// Max storage key size
113    pub max_length_storage_key: u64,
114    /// Max storage value size
115    pub max_length_storage_value: u64,
116    /// Max number of promises that a function call can create
117    pub max_promises_per_function_call_action: u64,
118    /// Max number of input data dependencies
119    pub max_number_input_data_dependencies: u64,
120    /// If present, stores max number of functions in one contract
121    #[serde(skip_serializing_if = "Option::is_none")]
122    pub max_functions_number_per_contract: Option<u64>,
123    /// If present, stores the secondary stack limit as implemented by wasmer2.
124    ///
125    /// This limit should never be hit normally.
126    #[serde(default = "wasmer2_stack_limit_default")]
127    pub wasmer2_stack_limit: i32,
128    /// If present, stores max number of locals declared globally in one contract
129    #[serde(skip_serializing_if = "Option::is_none")]
130    pub max_locals_per_contract: Option<u64>,
131    /// Whether to enforce account_id well-formedness where it wasn't enforced
132    /// historically.
133    #[serde(default = "AccountIdValidityRulesVersion::v0")]
134    pub account_id_validity_rules_version: AccountIdValidityRulesVersion,
135}
136
137/// Dynamic configuration parameters required for the WASM runtime to
138/// execute a smart contract.
139///
140/// This (`VMConfig`) and `RuntimeFeesConfig` combined are sufficient to define
141/// protocol specific behavior of the contract runtime. The former contains
142/// configuration for the WASM runtime specifically, while the latter contains
143/// configuration for the transaction runtime and WASM runtime.
144#[derive(Clone, Debug, Hash, PartialEq, Eq)]
145pub struct Config {
146    /// Costs for runtime externals
147    pub ext_costs: ExtCostsConfig,
148
149    /// Gas cost of a growing memory by single page.
150    pub grow_mem_cost: u32,
151
152    /// Gas cost of a regular operation.
153    pub regular_op_cost: u32,
154
155    /// The kind of the VM implementation to use
156    pub vm_kind: VMKind,
157
158    /// Disable the fix for the #9393 issue in unc-vm-runner.
159    pub disable_9393_fix: bool,
160
161    /// Set to `StorageGetMode::FlatStorage` in order to enable the `FlatStorageReads` protocol
162    /// feature.
163    pub storage_get_mode: StorageGetMode,
164
165    /// Enable the `FixContractLoadingCost` protocol feature.
166    pub fix_contract_loading_cost: bool,
167
168    /// Enable the `ImplicitAccountCreation` protocol feature.
169    pub implicit_account_creation: bool,
170
171    /// Enable the host functions added by the `MathExtension` protocol feature.
172    pub math_extension: bool,
173
174    /// Enable the host functions added by the `Ed25519Verify` protocol feature.
175    pub ed25519_verify: bool,
176
177    /// Enable the host functions added by the `AltBn128` protocol feature.
178    pub alt_bn128: bool,
179
180    /// Enable the `FunctionCallWeight` protocol feature.
181    pub function_call_weight: bool,
182
183    /// Enable the `EthAccounts` protocol feature.
184    pub eth_accounts: bool,
185
186    /// Describes limits for VM and Runtime.
187    pub limit_config: LimitConfig,
188}
189
190impl Config {
191    /// Computes non-cryptographically-proof hash. The computation is fast but not cryptographically
192    /// secure.
193    pub fn non_crypto_hash(&self) -> u64 {
194        let mut s = DefaultHasher::new();
195        self.hash(&mut s);
196        s.finish()
197    }
198
199    pub fn make_free(&mut self) {
200        self.ext_costs = ExtCostsConfig {
201            costs: unc_primitives_core::enum_map::enum_map! {
202                _ => ParameterCost { gas: 0, compute: 0 }
203            },
204        };
205        self.grow_mem_cost = 0;
206        self.regular_op_cost = 0;
207        self.limit_config.max_gas_burnt = u64::MAX;
208    }
209}
210
211fn wasmer2_stack_limit_default() -> i32 {
212    100 * 1024
213}
214
215/// Our original code for limiting WASM stack was buggy. We fixed that, but we
216/// still have to use old (`V0`) limiter for old protocol versions.
217///
218/// This struct here exists to enforce that the value in the config is either
219/// `0` or `1`. We could have used a `bool` instead, but there's a chance that
220/// our current impl isn't perfect either and would need further tweaks in the
221/// future.
222#[derive(
223    Debug,
224    Clone,
225    Copy,
226    Hash,
227    PartialEq,
228    Eq,
229    serde_repr::Serialize_repr,
230    serde_repr::Deserialize_repr,
231)]
232#[repr(u8)]
233pub enum ContractPrepareVersion {
234    /// Oldest, buggiest version.
235    ///
236    /// Don't use it unless specifically to support old protocol version.
237    V0,
238    /// Old, slow and buggy version.
239    ///
240    /// Better than V0, but don’t use this nevertheless.
241    V1,
242    /// finite-wasm 0.3.0 based contract preparation code.
243    V2,
244}
245
246impl ContractPrepareVersion {
247    pub fn v0() -> ContractPrepareVersion {
248        ContractPrepareVersion::V0
249    }
250}