cosmwasm_vm/config.rs
1use std::{collections::HashSet, path::PathBuf};
2
3use serde::{Deserialize, Serialize};
4
5use crate::Size;
6
7const DEFAULT_MEMORY_LIMIT: u32 = 512; // in pages
8/// As of March 2023, on Juno mainnet the largest value for production contracts
9/// is 485. Most are between 100 and 300.
10const DEFAULT_TABLE_SIZE_LIMIT: u32 = 2500; // entries
11
12/// We keep this number high since failing early gives less detailed error messages. Especially
13/// when a user accidentally includes wasm-bindgen, they get a bunch of unsupported imports.
14const DEFAULT_MAX_IMPORTS: usize = 100;
15
16const DEFAULT_MAX_FUNCTIONS: usize = 20_000;
17
18const DEFAULT_MAX_FUNCTION_PARAMS: usize = 100;
19
20const DEFAULT_MAX_TOTAL_FUNCTION_PARAMS: usize = 10_000;
21
22const DEFAULT_MAX_FUNCTION_RESULTS: usize = 1;
23
24/// Various configurations for the VM.
25#[derive(Clone, Debug, Serialize, Deserialize)]
26#[non_exhaustive]
27pub struct Config {
28 /// Configuration for limitations placed on Wasm files.
29 /// This defines a few limits on the Wasm file that are checked during static validation before
30 /// storing the Wasm file.
31 pub wasm_limits: WasmLimits,
32
33 /// Configuration for the cache.
34 pub cache: CacheOptions,
35}
36
37impl Config {
38 pub fn new(cache: CacheOptions) -> Self {
39 Self {
40 wasm_limits: WasmLimits::default(),
41 cache,
42 }
43 }
44}
45
46/// Limits for static validation of Wasm files. These are checked before storing the Wasm file.
47/// All limits are optional because they are coming from the Go-side and have default values.
48#[derive(Clone, Debug, Default, Serialize, Deserialize)]
49#[non_exhaustive]
50pub struct WasmLimits {
51 /// Maximum number of memory pages that a module can request.
52 ///
53 /// Every Wasm memory has an initial size and an optional maximum size,
54 /// both measured in Wasm pages. This limit applies to the initial size.
55 pub initial_memory_limit_pages: Option<u32>,
56 /// The upper limit for the `max` value of each table. CosmWasm contracts have
57 /// initial=max for 1 table. See
58 ///
59 /// ```plain
60 /// $ wasm-objdump --section=table -x packages/vm/testdata/hackatom.wasm
61 /// Section Details:
62 ///
63 /// Table[1]:
64 /// - table[0] type=funcref initial=161 max=161
65 /// ```
66 ///
67 pub table_size_limit_elements: Option<u32>,
68 /// If the contract has more than this amount of imports, it will be rejected
69 /// during static validation before even looking into the imports.
70 pub max_imports: Option<usize>,
71
72 /// The maximum number of functions a contract can have.
73 /// Any contract with more functions than this will be rejected during static validation.
74 pub max_functions: Option<usize>,
75
76 /// The maximum number of parameters a Wasm function can have.
77 pub max_function_params: Option<usize>,
78 /// The maximum total number of parameters of all functions in the Wasm.
79 /// For each function in the Wasm, take the number of parameters and sum all of these up.
80 /// If that sum exceeds this limit, the Wasm will be rejected during static validation.
81 ///
82 /// Be careful when adjusting this limit, as it prevents an attack where a small Wasm file
83 /// explodes in size when compiled.
84 pub max_total_function_params: Option<usize>,
85
86 /// The maximum number of results a Wasm function type can have.
87 pub max_function_results: Option<usize>,
88}
89
90impl WasmLimits {
91 pub fn initial_memory_limit_pages(&self) -> u32 {
92 self.initial_memory_limit_pages
93 .unwrap_or(DEFAULT_MEMORY_LIMIT)
94 }
95
96 pub fn table_size_limit_elements(&self) -> u32 {
97 self.table_size_limit_elements
98 .unwrap_or(DEFAULT_TABLE_SIZE_LIMIT)
99 }
100
101 pub fn max_imports(&self) -> usize {
102 self.max_imports.unwrap_or(DEFAULT_MAX_IMPORTS)
103 }
104
105 pub fn max_functions(&self) -> usize {
106 self.max_functions.unwrap_or(DEFAULT_MAX_FUNCTIONS)
107 }
108
109 pub fn max_function_params(&self) -> usize {
110 self.max_function_params
111 .unwrap_or(DEFAULT_MAX_FUNCTION_PARAMS)
112 }
113
114 pub fn max_total_function_params(&self) -> usize {
115 self.max_total_function_params
116 .unwrap_or(DEFAULT_MAX_TOTAL_FUNCTION_PARAMS)
117 }
118
119 pub fn max_function_results(&self) -> usize {
120 self.max_function_results
121 .unwrap_or(DEFAULT_MAX_FUNCTION_RESULTS)
122 }
123}
124
125#[derive(Clone, Debug, Serialize, Deserialize)]
126#[non_exhaustive]
127pub struct CacheOptions {
128 /// The base directory of this cache.
129 ///
130 /// If this does not exist, it will be created. Not sure if this behaviour
131 /// is desired but wasmd relies on it.
132 pub base_dir: PathBuf,
133 pub available_capabilities: HashSet<String>,
134 /// Memory limit for the cache, in bytes.
135 pub memory_cache_size_bytes: Size,
136 /// Memory limit for instances, in bytes. Use a value that is divisible by the Wasm page size 65536,
137 /// e.g. full MiBs.
138 pub instance_memory_limit_bytes: Size,
139}
140
141impl CacheOptions {
142 pub fn new(
143 base_dir: impl Into<PathBuf>,
144 available_capabilities: impl Into<HashSet<String>>,
145 memory_cache_size_bytes: Size,
146 instance_memory_limit_bytes: Size,
147 ) -> Self {
148 Self {
149 base_dir: base_dir.into(),
150 available_capabilities: available_capabilities.into(),
151 memory_cache_size_bytes,
152 instance_memory_limit_bytes,
153 }
154 }
155}