1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
use crate::cost::{ExtCostsConfig, ParameterCost};
use borsh::BorshSerialize;
use near_primitives_core::config::AccountIdValidityRulesVersion;
use near_primitives_core::types::Gas;
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};

// NOTE that VMKind is part of serialization protocol, so we cannot remove entries from this list
// if particular VM reached publicly visible networks.
//
// Additionally, this is public only for the purposes of internal tools like the estimator. This
// API should otherwise be considered a private configuration of the `near-vm-runner`
// crate.
#[derive(
    Clone,
    Copy,
    Debug,
    Hash,
    BorshSerialize,
    PartialEq,
    Eq,
    strum::EnumString,
    serde::Serialize,
    serde::Deserialize,
)]
#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
pub enum VMKind {
    /// Wasmer 0.17.x VM.
    Wasmer0,
    /// Wasmtime VM.
    Wasmtime,
    /// Wasmer 2.x VM.
    Wasmer2,
    /// NearVM.
    NearVm,
}

impl VMKind {
    pub fn replace_with_wasmtime_if_unsupported(self) -> Self {
        if cfg!(not(target_arch = "x86_64")) {
            Self::Wasmtime
        } else {
            self
        }
    }
}

/// This enum represents if a storage_get call will be performed through flat storage or trie
#[derive(PartialEq, Eq, Hash, Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
pub enum StorageGetMode {
    FlatStorage,
    Trie,
}

/// Describes limits for VM and Runtime.
/// TODO #4139: consider switching to strongly-typed wrappers instead of raw quantities
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, Hash, PartialEq, Eq)]
pub struct LimitConfig {
    /// Max amount of gas that can be used, excluding gas attached to promises.
    pub max_gas_burnt: Gas,

    /// How tall the stack is allowed to grow?
    ///
    /// See <https://wiki.parity.io/WebAssembly-StackHeight> to find out how the stack frame cost
    /// is calculated.
    pub max_stack_height: u32,
    /// Whether a legacy version of stack limiting should be used, see
    /// [`ContractPrepareVersion`].
    #[serde(default = "ContractPrepareVersion::v0")]
    pub contract_prepare_version: ContractPrepareVersion,

    /// The initial number of memory pages.
    /// NOTE: It's not a limiter itself, but it's a value we use for initial_memory_pages.
    pub initial_memory_pages: u32,
    /// What is the maximal memory pages amount is allowed to have for a contract.
    pub max_memory_pages: u32,

    /// Limit of memory used by registers.
    pub registers_memory_limit: u64,
    /// Maximum number of bytes that can be stored in a single register.
    pub max_register_size: u64,
    /// Maximum number of registers that can be used simultaneously.
    ///
    /// Note that due to an implementation quirk [read: a bug] in VMLogic, if we
    /// have this number of registers, no subsequent writes to the registers
    /// will succeed even if they replace an existing register.
    pub max_number_registers: u64,

    /// Maximum number of log entries.
    pub max_number_logs: u64,
    /// Maximum total length in bytes of all log messages.
    pub max_total_log_length: u64,

    /// Max total prepaid gas for all function call actions per receipt.
    pub max_total_prepaid_gas: Gas,

    /// Max number of actions per receipt.
    pub max_actions_per_receipt: u64,
    /// Max total length of all method names (including terminating character) for a function call
    /// permission access key.
    pub max_number_bytes_method_names: u64,
    /// Max length of any method name (without terminating character).
    pub max_length_method_name: u64,
    /// Max length of arguments in a function call action.
    pub max_arguments_length: u64,
    /// Max length of returned data
    pub max_length_returned_data: u64,
    /// Max contract size
    pub max_contract_size: u64,
    /// Max transaction size
    pub max_transaction_size: u64,
    /// Max storage key size
    pub max_length_storage_key: u64,
    /// Max storage value size
    pub max_length_storage_value: u64,
    /// Max number of promises that a function call can create
    pub max_promises_per_function_call_action: u64,
    /// Max number of input data dependencies
    pub max_number_input_data_dependencies: u64,
    /// If present, stores max number of functions in one contract
    #[serde(skip_serializing_if = "Option::is_none")]
    pub max_functions_number_per_contract: Option<u64>,
    /// If present, stores the secondary stack limit as implemented by wasmer2.
    ///
    /// This limit should never be hit normally.
    #[serde(default = "wasmer2_stack_limit_default")]
    pub wasmer2_stack_limit: i32,
    /// If present, stores max number of locals declared globally in one contract
    #[serde(skip_serializing_if = "Option::is_none")]
    pub max_locals_per_contract: Option<u64>,
    /// Whether to enforce account_id well-formedness where it wasn't enforced
    /// historically.
    #[serde(default = "AccountIdValidityRulesVersion::v0")]
    pub account_id_validity_rules_version: AccountIdValidityRulesVersion,
}

/// Dynamic configuration parameters required for the WASM runtime to
/// execute a smart contract.
///
/// This (`VMConfig`) and `RuntimeFeesConfig` combined are sufficient to define
/// protocol specific behavior of the contract runtime. The former contains
/// configuration for the WASM runtime specifically, while the latter contains
/// configuration for the transaction runtime and WASM runtime.
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct Config {
    /// Costs for runtime externals
    pub ext_costs: ExtCostsConfig,

    /// Gas cost of a growing memory by single page.
    pub grow_mem_cost: u32,

    /// Gas cost of a regular operation.
    pub regular_op_cost: u32,

    /// The kind of the VM implementation to use
    pub vm_kind: VMKind,

    /// Disable the fix for the #9393 issue in near-vm-runner.
    pub disable_9393_fix: bool,

    /// Set to `StorageGetMode::FlatStorage` in order to enable the `FlatStorageReads` protocol
    /// feature.
    pub storage_get_mode: StorageGetMode,

    /// Enable the `FixContractLoadingCost` protocol feature.
    pub fix_contract_loading_cost: bool,

    /// Enable the `ImplicitAccountCreation` protocol feature.
    pub implicit_account_creation: bool,

    /// Enable the host functions added by the `MathExtension` protocol feature.
    pub math_extension: bool,

    /// Enable the host functions added by the `Ed25519Verify` protocol feature.
    pub ed25519_verify: bool,

    /// Enable the host functions added by the `AltBn128` protocol feature.
    pub alt_bn128: bool,

    /// Enable the `FunctionCallWeight` protocol feature.
    pub function_call_weight: bool,

    /// Enable the `EthImplicitAccounts` protocol feature.
    pub eth_implicit_accounts: bool,

    /// Describes limits for VM and Runtime.
    pub limit_config: LimitConfig,
}

impl Config {
    /// Computes non-cryptographically-proof hash. The computation is fast but not cryptographically
    /// secure.
    pub fn non_crypto_hash(&self) -> u64 {
        let mut s = DefaultHasher::new();
        self.hash(&mut s);
        s.finish()
    }

    pub fn make_free(&mut self) {
        self.ext_costs = ExtCostsConfig {
            costs: near_primitives_core::enum_map::enum_map! {
                _ => ParameterCost { gas: 0, compute: 0 }
            },
        };
        self.grow_mem_cost = 0;
        self.regular_op_cost = 0;
        self.limit_config.max_gas_burnt = u64::MAX;
    }
}

fn wasmer2_stack_limit_default() -> i32 {
    100 * 1024
}

/// Our original code for limiting WASM stack was buggy. We fixed that, but we
/// still have to use old (`V0`) limiter for old protocol versions.
///
/// This struct here exists to enforce that the value in the config is either
/// `0` or `1`. We could have used a `bool` instead, but there's a chance that
/// our current impl isn't perfect either and would need further tweaks in the
/// future.
#[derive(
    Debug,
    Clone,
    Copy,
    Hash,
    PartialEq,
    Eq,
    serde_repr::Serialize_repr,
    serde_repr::Deserialize_repr,
)]
#[repr(u8)]
pub enum ContractPrepareVersion {
    /// Oldest, buggiest version.
    ///
    /// Don't use it unless specifically to support old protocol version.
    V0,
    /// Old, slow and buggy version.
    ///
    /// Better than V0, but don’t use this nevertheless.
    V1,
    /// finite-wasm 0.3.0 based contract preparation code.
    V2,
}

impl ContractPrepareVersion {
    pub fn v0() -> ContractPrepareVersion {
        ContractPrepareVersion::V0
    }
}