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}