darkfi/runtime/
vm_runtime.rs

1/* This file is part of DarkFi (https://dark.fi)
2 *
3 * Copyright (C) 2020-2026 Dyne.org foundation
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Affero General Public License as
7 * published by the Free Software Foundation, either version 3 of the
8 * License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 */
18
19use std::{
20    cell::{Cell, RefCell},
21    collections::BTreeMap,
22    sync::Arc,
23};
24
25use darkfi_sdk::{
26    crypto::contract_id::{
27        ContractId, SMART_CONTRACT_MONOTREE_DB_NAME, SMART_CONTRACT_ZKAS_DB_NAME,
28    },
29    tx::TransactionHash,
30    wasm, AsHex,
31};
32use darkfi_serial::serialize;
33use parking_lot::Mutex;
34use tracing::{debug, error, info};
35use wasmer::{
36    imports, sys::CompilerConfig, wasmparser::Operator, AsStoreMut, AsStoreRef, Function,
37    FunctionEnv, Instance, Memory, MemoryType, MemoryView, Module, Pages, Store, Value,
38    WASM_PAGE_SIZE,
39};
40use wasmer_compiler_singlepass::Singlepass;
41use wasmer_middlewares::{
42    metering::{get_remaining_points, set_remaining_points, MeteringPoints},
43    Metering,
44};
45
46use super::{import, import::db::DbHandle, memory::MemoryManipulation};
47use crate::{blockchain::BlockchainOverlayPtr, Error, Result};
48
49/// Name of the wasm linear memory in our guest module
50const MEMORY: &str = "memory";
51
52/// Gas limit for a single contract call (Single WASM instance)
53pub const GAS_LIMIT: u64 = 400_000_000;
54
55// ANCHOR: contract-section
56#[derive(Clone, Copy, PartialEq)]
57pub enum ContractSection {
58    /// Setup function of a contract
59    Deploy,
60    /// Entrypoint function of a contract
61    Exec,
62    /// Apply function of a contract
63    Update,
64    /// Metadata
65    Metadata,
66    /// Placeholder state before any initialization
67    Null,
68}
69// ANCHOR_END: contract-section
70
71impl ContractSection {
72    pub const fn name(&self) -> &str {
73        match self {
74            Self::Deploy => "__initialize",
75            Self::Exec => "__entrypoint",
76            Self::Update => "__update",
77            Self::Metadata => "__metadata",
78            Self::Null => unreachable!(),
79        }
80    }
81}
82
83/// Transaction-local state db.
84///
85/// This is an in-memory BTreeMap that works equivalently to the existing
86/// blockchain DB in contracts, except its lifetime is during a single
87/// transaction execution.
88pub type TxLocalState = BTreeMap<ContractId, BTreeMap<[u8; 32], BTreeMap<Vec<u8>, Vec<u8>>>>;
89
90/// The WASM VM runtime environment instantiated for every smart contract that runs.
91pub struct Env {
92    /// Blockchain overlay access
93    pub blockchain: BlockchainOverlayPtr,
94    /// Overlay tree handles used with `db_*`
95    pub db_handles: RefCell<Vec<DbHandle>>,
96    /// Transaction-local db handles used with `db_*_local`
97    pub local_db_handles: RefCell<Vec<DbHandle>>,
98    /// Transaction-local state
99    pub tx_local: Arc<Mutex<TxLocalState>>,
100    /// The contract ID being executed
101    pub contract_id: ContractId,
102    /// The compiled wasm bincode being executed,
103    pub contract_bincode: Vec<u8>,
104    /// The contract section being executed
105    pub contract_section: ContractSection,
106    /// State update produced by a smart contract function call
107    pub contract_return_data: Cell<Option<Vec<u8>>>,
108    /// Logs produced by the contract
109    pub logs: RefCell<Vec<String>>,
110    /// Direct memory access to the VM
111    pub memory: Option<Memory>,
112    /// Object store for transferring memory from the host to VM
113    pub objects: RefCell<Vec<Vec<u8>>>,
114    /// Block height number runtime verifies against.
115    /// For unconfirmed txs, this will be the current max height in the chain.
116    pub verifying_block_height: u32,
117    /// Currently configured block time target, in seconds
118    pub block_target: u32,
119    /// The hash for this transaction the runtime is being run against.
120    pub tx_hash: TransactionHash,
121    /// The index for this call in the transaction
122    pub call_idx: u8,
123    /// Parent `Instance`
124    pub instance: Option<Arc<Instance>>,
125}
126
127impl Env {
128    /// Provide safe access to the memory
129    /// (it must be initialized before it can be used)
130    ///
131    ///     // ctx: FunctionEnvMut<Env>
132    ///     let env = ctx.data();
133    ///     let memory = env.memory_view(&ctx);
134    ///
135    pub fn memory_view<'a>(&'a self, store: &'a impl AsStoreRef) -> MemoryView<'a> {
136        self.memory().view(store)
137    }
138
139    /// Get memory, that needs to have been set fist
140    pub fn memory(&self) -> &Memory {
141        self.memory.as_ref().unwrap()
142    }
143
144    /// Subtract given gas cost from remaining gas in the current runtime
145    pub fn subtract_gas(&mut self, ctx: &mut impl AsStoreMut, gas: u64) {
146        match get_remaining_points(ctx, self.instance.as_ref().unwrap()) {
147            MeteringPoints::Remaining(rem) => {
148                if gas > rem {
149                    set_remaining_points(ctx, self.instance.as_ref().unwrap(), 0);
150                } else {
151                    set_remaining_points(ctx, self.instance.as_ref().unwrap(), rem - gas);
152                }
153            }
154            MeteringPoints::Exhausted => {
155                set_remaining_points(ctx, self.instance.as_ref().unwrap(), 0);
156            }
157        }
158    }
159}
160
161/// Define a wasm runtime.
162pub struct Runtime {
163    /// A wasm instance
164    pub instance: Arc<Instance>,
165    /// A wasm store (global state)
166    pub store: Store,
167    // Wrapper for [`Env`], defined above.
168    pub ctx: FunctionEnv<Env>,
169}
170
171impl Runtime {
172    /// Create a new wasm runtime instance that contains the given wasm module.
173    #[allow(clippy::too_many_arguments)]
174    pub fn new(
175        wasm_bytes: &[u8],
176        blockchain: BlockchainOverlayPtr,
177        tx_local: Arc<Mutex<TxLocalState>>,
178        contract_id: ContractId,
179        verifying_block_height: u32,
180        block_target: u32,
181        tx_hash: TransactionHash,
182        call_idx: u8,
183    ) -> Result<Self> {
184        info!(target: "runtime::vm_runtime", "[WASM] Instantiating a new runtime");
185        // This function will be called for each `Operator` encountered during
186        // the wasm module execution. It should return the cost of the operator
187        // that it received as its first argument. For now, every wasm opcode
188        // has a cost of `1`.
189        // https://docs.rs/wasmparser/latest/wasmparser/enum.Operator.html
190        let cost_function = |_operator: &Operator| -> u64 { 1 };
191
192        // `Metering` needs to be configured with a limit and a cost function.
193        // For each `Operator`, the metering middleware will call the cost
194        // function and subtract the cost from the remaining points.
195        let metering = Arc::new(Metering::new(GAS_LIMIT, cost_function));
196
197        // Define the compiler and middleware, engine, and store
198        let mut compiler_config = Singlepass::new();
199        compiler_config.push_middleware(metering);
200        let mut store = Store::new(compiler_config);
201
202        // Create a larger Memory for the instance
203        let memory_type = MemoryType::new(
204            Pages(256),        // init: 16 MB (256 * 64KB)
205            Some(Pages(4096)), // max: 256 MB
206            false,
207        );
208        let memory = Memory::new(&mut store, memory_type)?;
209
210        debug!(target: "runtime::vm_runtime", "Compiling module");
211        let module = Module::new(&store, wasm_bytes)?;
212
213        // Initialize data
214        let db_handles = RefCell::new(vec![]);
215        let local_db_handles = RefCell::new(vec![]);
216        let logs = RefCell::new(vec![]);
217
218        // Initialize a tx-local db for the calling contract
219        tx_local.lock().entry(contract_id).or_default();
220
221        debug!(target: "runtime::vm_runtime", "Importing functions");
222
223        let ctx = FunctionEnv::new(
224            &mut store,
225            Env {
226                blockchain,
227                db_handles,
228                local_db_handles,
229                tx_local,
230                contract_id,
231                contract_bincode: wasm_bytes.to_vec(),
232                contract_section: ContractSection::Null,
233                contract_return_data: Cell::new(None),
234                logs,
235                memory: Some(memory.clone()),
236                objects: RefCell::new(vec![]),
237                verifying_block_height,
238                block_target,
239                tx_hash,
240                call_idx,
241                instance: None,
242            },
243        );
244
245        let imports = imports! {
246            "env" => {
247                "memory" => memory,
248
249                "drk_log_" => Function::new_typed_with_env(
250                    &mut store,
251                    &ctx,
252                    import::util::drk_log,
253                ),
254
255                "set_return_data_" => Function::new_typed_with_env(
256                    &mut store,
257                    &ctx,
258                    import::util::set_return_data,
259                ),
260
261                "db_init_" => Function::new_typed_with_env(
262                    &mut store,
263                    &ctx,
264                    import::db::db_init,
265                ),
266
267                "db_lookup_" => Function::new_typed_with_env(
268                    &mut store,
269                    &ctx,
270                    import::db::db_lookup,
271                ),
272
273                "db_lookup_local_" => Function::new_typed_with_env(
274                    &mut store,
275                    &ctx,
276                    import::db::db_lookup_local,
277                ),
278
279                "db_get_" => Function::new_typed_with_env(
280                    &mut store,
281                    &ctx,
282                    import::db::db_get,
283                ),
284
285                "db_get_local_" => Function::new_typed_with_env(
286                    &mut store,
287                    &ctx,
288                    import::db::db_get_local,
289                ),
290
291                "db_contains_key_" => Function::new_typed_with_env(
292                    &mut store,
293                    &ctx,
294                    import::db::db_contains_key,
295                ),
296
297                "db_contains_key_local_" => Function::new_typed_with_env(
298                    &mut store,
299                    &ctx,
300                    import::db::db_contains_key_local,
301                ),
302
303                "db_set_" => Function::new_typed_with_env(
304                    &mut store,
305                    &ctx,
306                    import::db::db_set,
307                ),
308
309                "db_set_local_" => Function::new_typed_with_env(
310                    &mut store,
311                    &ctx,
312                    import::db::db_set_local,
313                ),
314
315                "db_del_" => Function::new_typed_with_env(
316                    &mut store,
317                    &ctx,
318                    import::db::db_del,
319                ),
320
321                "db_del_local_" => Function::new_typed_with_env(
322                    &mut store,
323                    &ctx,
324                    import::db::db_del_local,
325                ),
326
327                "zkas_db_set_" => Function::new_typed_with_env(
328                    &mut store,
329                    &ctx,
330                    import::db::zkas_db_set,
331                ),
332
333                "get_object_bytes_" => Function::new_typed_with_env(
334                    &mut store,
335                    &ctx,
336                    import::util::get_object_bytes,
337                ),
338
339                "get_object_size_" => Function::new_typed_with_env(
340                    &mut store,
341                    &ctx,
342                    import::util::get_object_size,
343                ),
344
345                "merkle_add_" => Function::new_typed_with_env(
346                    &mut store,
347                    &ctx,
348                    import::merkle::merkle_add,
349                ),
350
351                "merkle_add_local_" => Function::new_typed_with_env(
352                    &mut store,
353                    &ctx,
354                    import::merkle::merkle_add_local,
355                ),
356
357                "sparse_merkle_insert_batch_" => Function::new_typed_with_env(
358                    &mut store,
359                    &ctx,
360                    import::smt::sparse_merkle_insert_batch,
361                ),
362
363                "get_verifying_block_height_" => Function::new_typed_with_env(
364                    &mut store,
365                    &ctx,
366                    import::util::get_verifying_block_height,
367                ),
368
369                "get_block_target_" => Function::new_typed_with_env(
370                    &mut store,
371                    &ctx,
372                    import::util::get_block_target,
373                ),
374
375                "get_tx_hash_" => Function::new_typed_with_env(
376                    &mut store,
377                    &ctx,
378                    import::util::get_tx_hash,
379                ),
380
381                "get_call_index_" => Function::new_typed_with_env(
382                    &mut store,
383                    &ctx,
384                    import::util::get_call_index,
385                ),
386
387                "get_blockchain_time_" => Function::new_typed_with_env(
388                    &mut store,
389                    &ctx,
390                    import::util::get_blockchain_time,
391                ),
392
393                "get_last_block_height_" => Function::new_typed_with_env(
394                    &mut store,
395                    &ctx,
396                    import::util::get_last_block_height,
397                ),
398
399                "get_tx_" => Function::new_typed_with_env(
400                    &mut store,
401                    &ctx,
402                    import::util::get_tx,
403                ),
404
405                "get_tx_location_" => Function::new_typed_with_env(
406                    &mut store,
407                    &ctx,
408                    import::util::get_tx_location,
409                ),
410            }
411        };
412
413        debug!(target: "runtime::vm_runtime", "Instantiating module");
414        let instance = Arc::new(Instance::new(&mut store, &module, &imports)?);
415
416        let env_mut = ctx.as_mut(&mut store);
417        env_mut.memory = Some(instance.exports.get_with_generics(MEMORY)?);
418        env_mut.instance = Some(Arc::clone(&instance));
419
420        Ok(Self { instance, store, ctx })
421    }
422
423    /// Call a contract method defined by a [`ContractSection`] using a supplied
424    /// payload. Returns a `Vec<u8>` corresponding to the result data of the call.
425    /// For calls that do not return any data, an empty `Vec<u8>` is returned.
426    fn call(&mut self, section: ContractSection, payload: &[u8]) -> Result<Vec<u8>> {
427        debug!(target: "runtime::vm_runtime", "Calling {} method", section.name());
428
429        let env_mut = self.ctx.as_mut(&mut self.store);
430        env_mut.contract_section = section;
431        // Verify contract's return data is empty, or quit.
432        assert!(env_mut.contract_return_data.take().is_none());
433
434        // Clear the logs
435        let _ = env_mut.logs.take();
436
437        // Serialize the payload for the format the wasm runtime is expecting.
438        let payload = Self::serialize_payload(&env_mut.contract_id, payload);
439
440        // Allocate enough memory for the payload and copy it into the memory.
441        let pages_required = payload.len() / WASM_PAGE_SIZE + 1;
442        self.set_memory_page_size(pages_required as u32)?;
443        self.copy_to_memory(&payload)?;
444
445        debug!(target: "runtime::vm_runtime", "Getting {} function", section.name());
446        let entrypoint = self.instance.exports.get_function(section.name())?;
447
448        // Call the entrypoint. On success, `call` returns a WASM [`Value`]. (The
449        // value may be empty.) This value functions similarly to a UNIX exit code.
450        // The following section is intended to unwrap the exit code and handle fatal
451        // errors in the Wasmer runtime. The value itself and the return data of the
452        // contract are processed later.
453        debug!(target: "runtime::vm_runtime", "Executing wasm");
454        let ret = match entrypoint.call(&mut self.store, &[Value::I32(0_i32)]) {
455            Ok(retvals) => {
456                self.print_logs();
457                info!(target: "runtime::vm_runtime", "[WASM] {}", self.gas_info());
458                retvals
459            }
460            Err(e) => {
461                self.print_logs();
462                info!(target: "runtime::vm_runtime", "[WASM] {}", self.gas_info());
463                // WasmerRuntimeError panics are handled here. Return from run() immediately.
464                error!(target: "runtime::vm_runtime", "[WASM] Wasmer Runtime Error: {e:#?}");
465                return Err(e.into())
466            }
467        };
468
469        debug!(target: "runtime::vm_runtime", "wasm executed successfully");
470
471        // Move the contract's return data into `retdata`.
472        let env_mut = self.ctx.as_mut(&mut self.store);
473        env_mut.contract_section = ContractSection::Null;
474        let retdata = env_mut.contract_return_data.take().unwrap_or_default();
475
476        // Determine the return value of the contract call. If `ret` is empty,
477        // assumed that the contract call was successful.
478        let retval: i64 = match ret.len() {
479            0 => {
480                // Return a success value if there is no return value from
481                // the contract.
482                debug!(target: "runtime::vm_runtime", "Contract has no return value (expected)");
483                wasm::entrypoint::SUCCESS
484            }
485            _ => {
486                match ret[0] {
487                    Value::I64(v) => {
488                        debug!(target: "runtime::vm_runtime", "Contract returned: {:?}", ret[0]);
489                        v
490                    }
491                    // The only supported return type is i64, so panic if another
492                    // value is returned.
493                    _ => unreachable!("Got unexpected result return value: {ret:?}"),
494                }
495            }
496        };
497
498        // Check the integer return value of the call. A value of `entrypoint::SUCCESS` (i.e. zero)
499        // corresponds to a successful contract call; in this case, we return the contract's
500        // result data. Otherwise, map the integer return value to a [`ContractError`].
501        match retval {
502            wasm::entrypoint::SUCCESS => Ok(retdata),
503            _ => {
504                let err = darkfi_sdk::error::ContractError::from(retval);
505                error!(target: "runtime::vm_runtime", "[WASM] Contract returned: {err:?}");
506                Err(Error::ContractError(err))
507            }
508        }
509    }
510
511    /// This function runs when a smart contract is initially deployed, or re-deployed.
512    ///
513    /// The runtime will look for an `__initialize` symbol in the wasm code, and execute
514    /// it if found. Optionally, it is possible to pass in a payload for any kind of special
515    /// instructions the developer wants to manage in the initialize function.
516    ///
517    /// This process is supposed to set up the overlay trees for storing the smart contract
518    /// state, and it can create, delete, modify, read, and write to databases it's allowed to.
519    /// The permissions for this are handled by the `ContractId` in the overlay db API so we
520    /// assume that the contract is only able to do write operations on its own overlay trees.
521    pub fn deploy(&mut self, payload: &[u8]) -> Result<()> {
522        let cid = self.ctx.as_ref(&self.store).contract_id;
523        info!(target: "runtime::vm_runtime", "[WASM] Running deploy() for ContractID: {cid}");
524
525        // Scoped for borrows
526        {
527            let env_mut = self.ctx.as_mut(&mut self.store);
528            // We always want to have the zkas db as index 0 in db handles and batches when
529            // deploying.
530            let contracts = &env_mut.blockchain.lock().unwrap().contracts;
531
532            // Open or create the zkas db tree for this contract
533            let zkas_tree_handle =
534                match contracts.lookup(&env_mut.contract_id, SMART_CONTRACT_ZKAS_DB_NAME) {
535                    Ok(v) => v,
536                    Err(_) => contracts.init(&env_mut.contract_id, SMART_CONTRACT_ZKAS_DB_NAME)?,
537                };
538
539            // Create the monotree db tree for this contract,
540            // if it doesn't exists.
541            if contracts.lookup(&env_mut.contract_id, SMART_CONTRACT_MONOTREE_DB_NAME).is_err() {
542                contracts.init(&env_mut.contract_id, SMART_CONTRACT_MONOTREE_DB_NAME)?;
543            }
544
545            let mut db_handles = env_mut.db_handles.borrow_mut();
546            db_handles.push(DbHandle::new(env_mut.contract_id, zkas_tree_handle));
547        }
548
549        //debug!(target: "runtime::vm_runtime", "[WASM] payload: {payload:?}");
550        let _ = self.call(ContractSection::Deploy, payload)?;
551
552        // Update the wasm bincode in the ContractStore wasm tree if the deploy exec passed successfully.
553        let env_mut = self.ctx.as_mut(&mut self.store);
554        env_mut
555            .blockchain
556            .lock()
557            .unwrap()
558            .contracts
559            .insert(env_mut.contract_id, &env_mut.contract_bincode)?;
560
561        info!(target: "runtime::vm_runtime", "[WASM] Successfully deployed ContractID: {cid}");
562        Ok(())
563    }
564
565    /// This function runs first in the entire scheme of executing a smart contract.
566    ///
567    /// The runtime will look for a `__metadata` symbol in the wasm code and execute it.
568    /// It is supposed to correctly extract public inputs for any ZK proofs included
569    /// in the contract calls, and also extract the public keys used to verify the
570    /// call/transaction signatures.
571    pub fn metadata(&mut self, payload: &[u8]) -> Result<Vec<u8>> {
572        let cid = self.ctx.as_ref(&self.store).contract_id;
573        info!(target: "runtime::vm_runtime", "[WASM] Running metadata() for ContractID: {cid}");
574
575        debug!(target: "runtime::vm_runtime", "metadata payload: {}", payload.hex());
576        let ret = self.call(ContractSection::Metadata, payload)?;
577        debug!(target: "runtime::vm_runtime", "metadata returned: {:?}", ret.hex());
578
579        info!(target: "runtime::vm_runtime", "[WASM] Successfully got metadata ContractID: {cid}");
580        Ok(ret)
581    }
582
583    /// This function runs when someone wants to execute a smart contract.
584    ///
585    /// The runtime will look for an `__entrypoint` symbol in the wasm code, and
586    /// execute it if found. A payload is also passed as an instruction that can
587    /// be used inside the vm by the runtime.
588    pub fn exec(&mut self, payload: &[u8]) -> Result<Vec<u8>> {
589        let cid = self.ctx.as_ref(&self.store).contract_id;
590        info!(target: "runtime::vm_runtime", "[WASM] Running exec() for ContractID: {cid}");
591
592        debug!(target: "runtime::vm_runtime", "exec payload: {}", payload.hex());
593        let ret = self.call(ContractSection::Exec, payload)?;
594        debug!(target: "runtime::vm_runtime", "exec returned: {:?}", ret.hex());
595
596        info!(target: "runtime::vm_runtime", "[WASM] Successfully executed ContractID: {cid}");
597        Ok(ret)
598    }
599
600    /// This function runs after successful execution of `exec` and tries to
601    /// apply the state change to the overlay databases.
602    ///
603    /// The runtime will lok for an `__update` symbol in the wasm code, and execute
604    /// it if found. The function does not take an arbitrary payload, but just takes
605    /// a state update from `env` and passes it into the wasm runtime.
606    pub fn apply(&mut self, update: &[u8]) -> Result<()> {
607        let cid = self.ctx.as_ref(&self.store).contract_id;
608        info!(target: "runtime::vm_runtime", "[WASM] Running apply() for ContractID: {cid}");
609
610        debug!(target: "runtime::vm_runtime", "apply payload: {:?}", update.hex());
611        let ret = self.call(ContractSection::Update, update)?;
612        debug!(target: "runtime::vm_runtime", "apply returned: {:?}", ret.hex());
613
614        info!(target: "runtime::vm_runtime", "[WASM] Successfully applied ContractID: {cid}");
615        Ok(())
616    }
617
618    /// Prints the wasm contract logs.
619    fn print_logs(&self) {
620        let logs = self.ctx.as_ref(&self.store).logs.borrow();
621        for msg in logs.iter() {
622            info!(target: "runtime::vm_runtime", "[WASM] Contract log: {msg}");
623        }
624    }
625
626    /// Calculate the remaining gas using wasm's concept
627    /// of metering points.
628    pub fn gas_used(&mut self) -> u64 {
629        let remaining_points = get_remaining_points(&mut self.store, &self.instance);
630
631        match remaining_points {
632            MeteringPoints::Remaining(rem) => {
633                if rem > GAS_LIMIT {
634                    // This should never occur, but catch it explicitly to avoid
635                    // potential underflow issues when calculating `remaining_points`.
636                    unreachable!("Remaining wasm points exceed GAS_LIMIT");
637                }
638                GAS_LIMIT - rem
639            }
640            MeteringPoints::Exhausted => GAS_LIMIT + 1,
641        }
642    }
643
644    // Return a message informing the user whether there is any
645    // gas remaining. Values equal to GAS_LIMIT are not considered
646    // to be exhausted. e.g. Using 100/100 gas should not give a
647    // 'gas exhausted' message.
648    fn gas_info(&mut self) -> String {
649        let gas_used = self.gas_used();
650
651        if gas_used > GAS_LIMIT {
652            format!("Gas fully exhausted: {gas_used}/{GAS_LIMIT}")
653        } else {
654            format!("Gas used: {gas_used}/{GAS_LIMIT}")
655        }
656    }
657
658    /// Set the memory page size. Returns the previous memory size.
659    fn set_memory_page_size(&mut self, pages: u32) -> Result<Pages> {
660        // Grab memory by value
661        let memory = self.take_memory();
662        // Modify the memory
663        let ret = memory.grow(&mut self.store, Pages(pages))?;
664        // Replace the memory back again
665        self.ctx.as_mut(&mut self.store).memory = Some(memory);
666        Ok(ret)
667    }
668
669    /// Take Memory by value. Needed to modify the Memory object
670    /// Will panic if memory isn't set.
671    fn take_memory(&mut self) -> Memory {
672        let env_memory = &mut self.ctx.as_mut(&mut self.store).memory;
673        let memory = env_memory.take();
674        memory.expect("memory should be set")
675    }
676
677    /// Copy payload to the start of the memory
678    fn copy_to_memory(&self, payload: &[u8]) -> Result<()> {
679        // Payload is copied to index 0.
680        // Get the memory view
681        let env = self.ctx.as_ref(&self.store);
682        let memory_view = env.memory_view(&self.store);
683        memory_view.write_slice(payload, 0)
684    }
685
686    /// Serialize contract payload to the format accepted by the runtime functions.
687    /// We keep the same payload as a slice of bytes, and prepend it with a [`ContractId`],
688    /// and then a little-endian u64 to tell the payload's length.
689    fn serialize_payload(cid: &ContractId, payload: &[u8]) -> Vec<u8> {
690        let ser_cid = serialize(cid);
691        let payload_len = payload.len();
692        let mut out = Vec::with_capacity(ser_cid.len() + 8 + payload_len);
693        out.extend_from_slice(&ser_cid);
694        out.extend_from_slice(&(payload_len as u64).to_le_bytes());
695        out.extend_from_slice(payload);
696        out
697    }
698}