dusk_vm/
lib.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4//
5// Copyright (c) DUSK NETWORK. All rights reserved.
6
7//![doc = include_str!("../README.md")]
8
9#![deny(missing_docs)]
10#![deny(clippy::all)]
11#![deny(unused_crate_dependencies)]
12#![deny(unused_extern_crates)]
13
14extern crate alloc;
15
16pub use self::execute::{execute, gen_contract_id, Config as ExecutionConfig};
17pub use piecrust::{
18    CallReceipt, CallTree, CallTreeElem, ContractData, Error, PageOpening,
19    Session,
20};
21
22use alloc::vec::Vec;
23use std::fmt::{self, Debug, Formatter};
24use std::path::{Path, PathBuf};
25use std::thread;
26
27use dusk_core::abi::{Metadata, Query};
28use piecrust::{SessionData, VM as PiecrustVM};
29
30use self::host_queries::{
31    host_hash, host_poseidon_hash, host_verify_bls, host_verify_bls_multisig,
32    host_verify_groth16_bn254, host_verify_plonk, host_verify_schnorr,
33};
34
35pub(crate) mod cache;
36mod execute;
37pub mod host_queries;
38
39/// The Virtual Machine (VM) for executing smart contracts in the Dusk Network.
40///
41/// The `VM` struct serves as the core for managing the network's state,
42/// executing smart contracts, and interfacing with host functions. It supports
43/// both persistent and ephemeral sessions for handling transactions, contract
44/// queries and contract deployments.
45pub struct VM(PiecrustVM);
46
47impl From<PiecrustVM> for VM {
48    fn from(piecrust_vm: PiecrustVM) -> Self {
49        VM(piecrust_vm)
50    }
51}
52
53impl Debug for VM {
54    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
55        self.0.fmt(f)
56    }
57}
58
59impl VM {
60    /// Creates a new instance of the virtual machine.
61    ///
62    /// This method initializes the VM with a given root directory and
63    /// registers the necessary host-queries for contract execution.
64    ///
65    /// # Arguments
66    /// * `root_dir` - The path to the root directory for the VM's state
67    ///   storage. This directory will be used to save any future session
68    ///   commits made by this `VM` instance.
69    ///
70    /// # Returns
71    /// A new `VM` instance.
72    ///
73    /// # Errors
74    /// If the directory contains unparseable or inconsistent data.
75    ///
76    /// # Examples
77    /// ```rust
78    /// use dusk_vm::VM;
79    ///
80    /// let vm = VM::new("/path/to/root_dir");
81    /// ```
82    pub fn new(
83        root_dir: impl AsRef<Path> + Into<PathBuf>,
84    ) -> Result<Self, Error> {
85        let mut vm: Self = PiecrustVM::new(root_dir)?.into();
86        vm.register_host_queries();
87        Ok(vm)
88    }
89
90    /// Creates an ephemeral VM instance.
91    ///
92    /// This method initializes a VM that operates in memory without persisting
93    /// state. It is useful for testing or temporary computations.
94    ///
95    /// # Returns
96    /// A new ephemeral `VM` instance.
97    ///
98    /// # Errors
99    /// If creating a temporary directory fails.
100    ///
101    /// # Examples
102    /// ```rust
103    /// use dusk_vm::VM;
104    ///
105    /// let vm = VM::ephemeral();
106    /// ```
107    pub fn ephemeral() -> Result<VM, Error> {
108        let mut vm: Self = PiecrustVM::ephemeral()?.into();
109        vm.register_host_queries();
110        Ok(vm)
111    }
112
113    /// Creates a new session for transaction execution.
114    ///
115    /// This method initializes a session with a specific base state commit,
116    /// chain identifier, and block height. Sessions allow for isolated
117    /// transaction execution without directly affecting the persistent VM
118    /// state until finalized.
119    ///
120    /// # Arguments
121    /// * `base` - A 32-byte array representing the base state from which the
122    ///   session begins.
123    /// * `chain_id` - The identifier of the network.
124    /// * `block_height` - The current block height at which the session is
125    ///   created.
126    ///
127    /// # Returns
128    /// A `Result` containing a `Session` instance for executing transactions,
129    /// or an error if the session cannot be initialized.
130    ///
131    /// # Errors
132    /// If base commit is provided but does not exist.
133    ///
134    /// # Examples
135    /// ```rust
136    /// use dusk_vm::VM;
137    ///
138    /// const CHAIN_ID: u8 = 42;
139    ///
140    /// // create a genesis session
141    /// let vm = VM::ephemeral().unwrap();
142    /// let session = vm.genesis_session(CHAIN_ID);
143    ///
144    /// // [...] apply changes to the network through the running session
145    ///
146    /// // commit the changes
147    /// let base = session.commit().unwrap();
148    ///
149    /// // spawn a new session on top of the base-commit
150    /// let block_height = 21;
151    /// let session = vm.session(base, CHAIN_ID, block_height).unwrap();
152    /// ```
153    pub fn session(
154        &self,
155        base: [u8; 32],
156        chain_id: u8,
157        block_height: u64,
158    ) -> Result<Session, Error> {
159        self.0.session(
160            SessionData::builder()
161                .base(base)
162                .insert(Metadata::CHAIN_ID, chain_id)?
163                .insert(Metadata::BLOCK_HEIGHT, block_height)?,
164        )
165    }
166
167    /// Initializes a session for setting up the genesis block.
168    ///
169    /// This method creates a session specifically for defining the genesis
170    /// block, which serves as the starting state of the network. The
171    /// genesis session uses the specified chain ID.
172    ///
173    /// # Arguments
174    /// * `chain_id` - The identifier of the blockchain chain for which the
175    ///   genesis state is initialized.
176    ///
177    /// # Returns
178    /// A `Session` instance for defining the genesis block.
179    ///
180    /// # Examples
181    /// ```rust
182    /// use dusk_vm::VM;
183    ///
184    /// const CHAIN_ID: u8 = 42;
185    ///
186    /// let vm = VM::ephemeral().unwrap();
187    /// let genesis_session = vm.genesis_session(CHAIN_ID);
188    /// ```
189    pub fn genesis_session(&self, chain_id: u8) -> Session {
190        self.0
191            .session(
192                SessionData::builder()
193                    .insert(Metadata::CHAIN_ID, chain_id)
194                    .expect("Inserting chain ID in metadata should succeed")
195                    .insert(Metadata::BLOCK_HEIGHT, 0)
196                    .expect(
197                        "Inserting block height in metadata should succeed",
198                    ),
199            )
200            .expect("Creating a genesis session should always succeed")
201    }
202
203    /// Retrieves all pending commits in the VM.
204    ///
205    /// This method fetches unfinalized state changes for inspection or
206    /// processing.
207    ///
208    /// # Returns
209    /// A vector of commits.
210    pub fn commits(&self) -> Vec<[u8; 32]> {
211        self.0.commits()
212    }
213
214    /// Deletes a specified commit from the VM.
215    ///
216    /// # Arguments
217    /// * `commit` - The commit to be deleted.
218    pub fn delete_commit(&self, root: [u8; 32]) -> Result<(), Error> {
219        self.0.delete_commit(root)
220    }
221
222    /// Finalizes a specified commit, applying its state changes permanently.
223    ///
224    /// # Arguments
225    /// * `commit` - The commit to be finalized.
226    pub fn finalize_commit(&self, root: [u8; 32]) -> Result<(), Error> {
227        self.0.finalize_commit(root)
228    }
229
230    /// Returns the root directory of the VM.
231    ///
232    /// This is either the directory passed in by using [`new`], or the
233    /// temporary directory created using [`ephemeral`].
234    pub fn root_dir(&self) -> &Path {
235        self.0.root_dir()
236    }
237
238    /// Returns a reference to the synchronization thread.
239    pub fn sync_thread(&self) -> &thread::Thread {
240        self.0.sync_thread()
241    }
242
243    fn register_host_queries(&mut self) {
244        self.0.register_host_query(Query::HASH, host_hash);
245        self.0
246            .register_host_query(Query::POSEIDON_HASH, host_poseidon_hash);
247        self.0
248            .register_host_query(Query::VERIFY_PLONK, host_verify_plonk);
249        self.0.register_host_query(
250            Query::VERIFY_GROTH16_BN254,
251            host_verify_groth16_bn254,
252        );
253        self.0
254            .register_host_query(Query::VERIFY_SCHNORR, host_verify_schnorr);
255        self.0
256            .register_host_query(Query::VERIFY_BLS, host_verify_bls);
257        self.0.register_host_query(
258            Query::VERIFY_BLS_MULTISIG,
259            host_verify_bls_multisig,
260        );
261    }
262}