Skip to main content

piecrust/
session.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
7use std::borrow::Cow;
8use std::collections::btree_set::Iter;
9use std::collections::{BTreeMap, BTreeSet};
10use std::fmt::{Debug, Formatter};
11use std::mem;
12use std::ptr::NonNull;
13use std::sync::{Arc, mpsc};
14
15use bytecheck::CheckBytes;
16use dusk_wasmtime::{
17    Engine, LinearMemory, MemoryCreator, MemoryType, Module as WasmtimeModule,
18};
19use piecrust_uplink::{
20    ARGBUF_LEN, CONTRACT_ID_BYTES, ContractId, Event, SCRATCH_BUF_BYTES,
21};
22use rkyv::ser::Serializer;
23use rkyv::ser::serializers::{
24    BufferScratch, BufferSerializer, CompositeSerializer,
25};
26use rkyv::validation::validators::DefaultValidator;
27use rkyv::{Archive, Deserialize, Infallible, Serialize, check_archived_root};
28
29use crate::call_tree::{CallTree, CallTreeElem};
30use crate::contract::{ContractData, ContractMetadata, WrappedContract};
31use crate::error::Error::{self, InitalizationError, PersistenceError};
32use crate::instance::WrappedInstance;
33use crate::store::{ContractSession, PAGE_SIZE, PageOpening};
34use crate::types::StandardBufSerializer;
35use crate::vm::{HostQueries, HostQuery};
36
37const MAX_META_SIZE: usize = ARGBUF_LEN;
38// Host stack limits in our current runtime effectively allow around 64 nested
39// inter-contract calls; we cap at 48 to keep safety margin. Observed mainnet
40// depth is currently <= 6.
41pub(crate) const MAX_CALL_DEPTH: usize = 48;
42const _: () = assert!(MAX_CALL_DEPTH <= ARGBUF_LEN / CONTRACT_ID_BYTES);
43pub const INIT_METHOD: &str = "init";
44
45/// A running mutation to a state.
46///
47/// `Session`s are spawned using a [`VM`] instance, and can be used to [`call`]
48/// contracts with to modify their state. A sequence of these calls may then be
49/// [`commit`]ed to, or discarded by simply allowing the session to drop.
50///
51/// New contracts are to be `deploy`ed in the context of a session.
52///
53/// [`VM`]: crate::VM
54/// [`call`]: Session::call
55/// [`commit`]: Session::commit
56pub struct Session {
57    engine: Engine,
58    inner: NonNull<SessionInner>,
59    original: bool,
60}
61
62unsafe impl Send for Session {}
63
64impl Debug for Session {
65    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
66        f.debug_struct("Session")
67            .field("inner", &self.inner)
68            .field("original", &self.original)
69            .finish()
70    }
71}
72
73/// A session is created by leaking an using `Box::leak` on a `SessionInner`.
74/// Therefore, the memory needs to be recovered.
75impl Drop for Session {
76    fn drop(&mut self) {
77        if self.original {
78            // ensure the stack is cleared and all instances are removed and
79            // reclaimed on the drop of a session.
80            self.clear_call_tree_and_instances();
81
82            // SAFETY: this is safe since we guarantee that there is no aliasing
83            // when a session drops.
84            unsafe {
85                let _ = Box::from_raw(self.inner.as_ptr());
86            }
87        }
88    }
89}
90
91/// A hook called before each inter-contract call.
92///
93/// Receives the callee contract ID, the function name, and the raw argument
94/// bytes. Returns `Ok(())` to allow the call, or `Err(reason)` to reject it
95/// with a descriptive message.
96#[cfg(feature = "call-hook")]
97pub type CallHook =
98    Box<dyn Fn(&ContractId, &str, &[u8]) -> Result<(), String> + Send + Sync>;
99
100struct SessionInner {
101    current: ContractId,
102
103    call_tree: CallTree,
104    instances: BTreeMap<ContractId, Box<WrappedInstance>>,
105    compiled_modules: BTreeMap<ContractId, WasmtimeModule>,
106    debug: Vec<String>,
107    data: SessionData,
108
109    contract_session: ContractSession,
110    host_queries: HostQueries,
111    buffer: Vec<u8>,
112
113    feeder: Option<mpsc::Sender<Vec<u8>>>,
114    events: Vec<Event>,
115
116    #[cfg(feature = "call-hook")]
117    call_hook: Option<CallHook>,
118}
119
120impl Debug for SessionInner {
121    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
122        f.debug_struct("SessionInner")
123            .field("current", &self.current)
124            .field("call_tree", &self.call_tree)
125            .field("instances_len", &self.instances.len())
126            .field("compiled_modules_len", &self.compiled_modules.len())
127            .field("debug_len", &self.debug.len())
128            .field("data", &self.data)
129            .field("buffer_len", &self.buffer.len())
130            .field("events_len", &self.events.len())
131            .finish()
132    }
133}
134
135struct SessionMemoryCreator {
136    inner: NonNull<SessionInner>,
137}
138
139unsafe impl Send for SessionMemoryCreator {}
140
141unsafe impl Sync for SessionMemoryCreator {}
142
143unsafe impl MemoryCreator for SessionMemoryCreator {
144    /// This new memory is created for the contract currently at the top of the
145    /// call tree.
146    fn new_memory(
147        &self,
148        _ty: MemoryType,
149        minimum: usize,
150        _maximum: Option<usize>,
151        _reserved_size_in_bytes: Option<usize>,
152        _guard_size_in_bytes: usize,
153    ) -> Result<Box<dyn LinearMemory>, String> {
154        // SAFETY: wasmtime calls this synchronously during instance creation.
155        // No other path concurrently accesses `SessionInner` in this callback.
156        let inner = unsafe { &mut *self.inner.as_ptr() };
157        let contract = inner.current;
158
159        let contract_data =
160            inner.contract_session.contract(contract).map_err(|err| {
161                format!("Failed to get contract from session: {err:?}")
162            })?;
163
164        let mut memory = contract_data
165            .expect("Contract data should exist at this point")
166            .memory;
167
168        if memory.is_new() {
169            memory.set_current_len(minimum);
170        }
171
172        Ok(Box::new(memory))
173    }
174}
175
176impl Session {
177    fn inner(&self) -> &SessionInner {
178        unsafe { self.inner.as_ref() }
179    }
180
181    fn inner_mut(&mut self) -> &mut SessionInner {
182        unsafe { self.inner.as_mut() }
183    }
184
185    pub(crate) fn new(
186        engine: Engine,
187        contract_session: ContractSession,
188        host_queries: HostQueries,
189        data: SessionData,
190    ) -> Self {
191        let inner = SessionInner {
192            current: ContractId::from_bytes([0; CONTRACT_ID_BYTES]),
193            call_tree: CallTree::new(),
194            instances: BTreeMap::new(),
195            compiled_modules: BTreeMap::new(),
196            debug: vec![],
197            data,
198            contract_session,
199            host_queries,
200            buffer: vec![0; PAGE_SIZE],
201            feeder: None,
202            events: vec![],
203            #[cfg(feature = "call-hook")]
204            call_hook: None,
205        };
206
207        // This implementation purposefully boxes and leaks the `SessionInner`.
208        let inner = Box::leak(Box::new(inner));
209
210        let mut session = Self {
211            engine: engine.clone(),
212            inner: NonNull::from(inner),
213            original: true,
214        };
215
216        let mut config = engine.config().clone();
217        config.with_host_memory(Arc::new(SessionMemoryCreator {
218            inner: session.inner,
219        }));
220
221        session.engine = Engine::new(&config)
222            .expect("Engine configuration is set at compile time");
223
224        session
225    }
226
227    /// Clone the given session. We explicitly **do not** implement the
228    /// [`Clone`] trait here, since we don't want allow the user to clone a
229    /// session.
230    ///
231    /// This keeps cloning internal and preserves ownership/drop invariants
232    /// around the shared `SessionInner` pointer.
233    pub(crate) fn clone(&self) -> Self {
234        Self {
235            engine: self.engine.clone(),
236            inner: self.inner,
237            original: false,
238        }
239    }
240
241    /// Return a reference to the engine used in this session.
242    pub(crate) fn engine(&self) -> &Engine {
243        &self.engine
244    }
245
246    /// Deploy a contract, returning its [`ContractId`] and an optional
247    /// [`CallReceipt`] for the `init` function execution. The ID is computed
248    /// using a `blake3` hash of the `bytecode`. Contracts using the `memory64`
249    /// proposal are accepted in just the same way as 32-bit contracts, and
250    /// their handling is totally transparent.
251    ///
252    /// Since a deployment may execute some contract initialization code, that
253    /// code will be metered and executed with the given `gas_limit`. If the
254    /// contract exports an `init` function, the returned receipt contains
255    /// the gas spent, events emitted, and call tree produced by that call.
256    ///
257    /// # Errors
258    /// It is possible that a collision between contract IDs occurs, even for
259    /// different contract IDs. This is due to the fact that all contracts have
260    /// to fit into a sparse merkle tree with `2^32` positions, and as such
261    /// a 256-bit number has to be mapped into a 32-bit number.
262    ///
263    /// If such a collision occurs, [`PersistenceError`] will be returned.
264    ///
265    /// [`ContractId`]: ContractId
266    /// [`CallReceipt`]: CallReceipt
267    /// [`PersistenceError`]: PersistenceError
268    ///
269    /// # Panics
270    /// If `deploy_data` does not specify an owner, this will panic.
271    pub fn deploy<'a, A, R, D>(
272        &mut self,
273        bytecode: &[u8],
274        deploy_data: D,
275        gas_limit: u64,
276    ) -> Result<(ContractId, Option<CallReceipt<R>>), Error>
277    where
278        A: 'a + for<'b> Serialize<StandardBufSerializer<'b>>,
279        R: Archive,
280        R::Archived: Deserialize<R, Infallible>
281            + for<'b> CheckBytes<DefaultValidator<'b>>,
282        D: Into<ContractData<'a, A>>,
283    {
284        let deploy_data = deploy_data.into();
285
286        let mut init_arg = None;
287        if let Some(arg) = deploy_data.init_arg {
288            let mut sbuf = [0u8; SCRATCH_BUF_BYTES];
289            let scratch = BufferScratch::new(&mut sbuf);
290            let ser = BufferSerializer::new(&mut self.inner_mut().buffer[..]);
291            let mut ser = CompositeSerializer::new(ser, scratch, Infallible);
292
293            ser.serialize_value(arg)?;
294            let pos = ser.pos();
295
296            init_arg = Some(self.inner().buffer[0..pos].to_vec());
297        }
298
299        let (contract_id, receipt) = self.deploy_raw(
300            deploy_data.contract_id,
301            bytecode,
302            init_arg,
303            deploy_data
304                .owner
305                .expect("Owner must be specified when deploying a contract"),
306            gas_limit,
307        )?;
308
309        let receipt = receipt.map(|r| r.deserialize()).transpose()?;
310        Ok((contract_id, receipt))
311    }
312
313    /// Deploy a contract, returning its [`ContractId`] and an optional
314    /// [`CallReceipt`] for the `init` function execution. If ID is not
315    /// provided, it is computed using a `blake3` hash of the `bytecode`.
316    /// Contracts using the `memory64` proposal are accepted in just the same
317    /// way as 32-bit contracts, and their handling is totally transparent.
318    ///
319    /// Since a deployment may execute some contract initialization code, that
320    /// code will be metered and executed with the given `gas_limit`. If the
321    /// contract exports an `init` function, the returned receipt contains
322    /// the gas spent, events emitted, and call tree produced by that call.
323    ///
324    /// # Errors
325    /// It is possible that a collision between contract IDs occurs, even for
326    /// different contract IDs. This is due to the fact that all contracts have
327    /// to fit into a sparse merkle tree with `2^32` positions, and as such
328    /// a 256-bit number has to be mapped into a 32-bit number.
329    ///
330    /// If such a collision occurs, [`PersistenceError`] will be returned.
331    ///
332    /// [`ContractId`]: ContractId
333    /// [`CallReceipt`]: CallReceipt
334    /// [`PersistenceError`]: PersistenceError
335    #[allow(clippy::type_complexity)]
336    pub fn deploy_raw(
337        &mut self,
338        contract_id: Option<ContractId>,
339        bytecode: &[u8],
340        init_arg: Option<Vec<u8>>,
341        owner: Vec<u8>,
342        gas_limit: u64,
343    ) -> Result<(ContractId, Option<CallReceipt<Vec<u8>>>), Error> {
344        let contract_id = contract_id.unwrap_or({
345            let hash = blake3::hash(bytecode);
346            ContractId::from_bytes(hash.into())
347        });
348        let receipt =
349            self.do_deploy(contract_id, bytecode, init_arg, owner, gas_limit)?;
350
351        Ok((contract_id, receipt))
352    }
353
354    #[allow(clippy::too_many_arguments)]
355    fn do_deploy(
356        &mut self,
357        contract_id: ContractId,
358        bytecode: &[u8],
359        arg: Option<Vec<u8>>,
360        owner: Vec<u8>,
361        gas_limit: u64,
362    ) -> Result<Option<CallReceipt<Vec<u8>>>, Error> {
363        if self
364            .inner_mut()
365            .contract_session
366            .contract_deployed(contract_id)
367        {
368            return Err(InitalizationError(
369                "Deployed error already exists".into(),
370            ));
371        }
372
373        let wrapped_contract =
374            WrappedContract::new(&self.engine, bytecode, None::<&[u8]>)?;
375        let contract_metadata = ContractMetadata { contract_id, owner };
376        let metadata_bytes = Self::serialize_data(&contract_metadata)?;
377
378        self.inner_mut()
379            .contract_session
380            .deploy(
381                contract_id,
382                bytecode,
383                wrapped_contract.as_bytes(),
384                contract_metadata,
385                metadata_bytes.as_slice(),
386            )
387            .map_err(|err| PersistenceError(Arc::new(err)))?;
388        self.inner_mut().compiled_modules.remove(&contract_id);
389
390        let instantiate = || {
391            self.create_instance(contract_id)?;
392            let has_init = self
393                .instance(&contract_id)
394                .expect("instance should exist")
395                .is_function_exported(INIT_METHOD);
396
397            if has_init {
398                // If no argument was provided, we call the init method anyway,
399                // but with an empty argument. The alternative is to panic, but
400                // that assumes that the caller of `deploy` knows that the
401                // contract has an init method in the first place, which might
402                // not be the case, such as when ingesting untrusted bytecode.
403                let arg = arg.unwrap_or_default();
404                let (data, gas_spent, call_tree) =
405                    self.call_inner(contract_id, INIT_METHOD, arg, gas_limit)?;
406                let events = mem::take(&mut self.inner_mut().events);
407                return Ok(Some(CallReceipt {
408                    gas_limit,
409                    gas_spent,
410                    events,
411                    call_tree,
412                    data,
413                }));
414            }
415
416            Ok(None)
417        };
418
419        instantiate().inspect_err(|_| {
420            self.inner_mut()
421                .contract_session
422                .remove_contract(&contract_id);
423            self.inner_mut().compiled_modules.remove(&contract_id);
424        })
425    }
426
427    /// Execute a call on the current state of this session.
428    ///
429    /// Calls are atomic, meaning that on failure their execution doesn't modify
430    /// the state. They are also metered, and will execute with the given
431    /// `gas_limit`. This value should never be 0.
432    ///
433    /// # Errors
434    /// The call may error during execution for a wide array of reasons, the
435    /// most common ones being running against the gas limit and a contract
436    /// panic. Calling the 'init' method is not allowed except for when called
437    /// from the deploy method.
438    pub fn call<A, R>(
439        &mut self,
440        contract: ContractId,
441        fn_name: &str,
442        fn_arg: &A,
443        gas_limit: u64,
444    ) -> Result<CallReceipt<R>, Error>
445    where
446        A: for<'b> Serialize<StandardBufSerializer<'b>>,
447        A::Archived: for<'b> CheckBytes<DefaultValidator<'b>>,
448        R: Archive,
449        R::Archived: Deserialize<R, Infallible>
450            + for<'b> CheckBytes<DefaultValidator<'b>>,
451    {
452        if fn_name == INIT_METHOD {
453            return Err(InitalizationError("init call not allowed".into()));
454        }
455
456        let mut sbuf = [0u8; SCRATCH_BUF_BYTES];
457        let scratch = BufferScratch::new(&mut sbuf);
458        let ser = BufferSerializer::new(&mut self.inner_mut().buffer[..]);
459        let mut ser = CompositeSerializer::new(ser, scratch, Infallible);
460
461        ser.serialize_value(fn_arg)?;
462        let pos = ser.pos();
463
464        let receipt = self.call_raw(
465            contract,
466            fn_name,
467            self.inner().buffer[..pos].to_vec(),
468            gas_limit,
469        )?;
470
471        receipt.deserialize()
472    }
473
474    /// Execute a raw call on the current state of this session.
475    ///
476    /// Raw calls do not specify the type of the argument or of the return. The
477    /// caller is responsible for serializing the argument as the target
478    /// `contract` expects.
479    ///
480    /// For more information about calls see [`call`].
481    ///
482    /// [`call`]: Session::call
483    pub fn call_raw<V: Into<Vec<u8>>>(
484        &mut self,
485        contract: ContractId,
486        fn_name: &str,
487        fn_arg: V,
488        gas_limit: u64,
489    ) -> Result<CallReceipt<Vec<u8>>, Error> {
490        if fn_name == INIT_METHOD {
491            return Err(InitalizationError("init call not allowed".into()));
492        }
493
494        let (data, gas_spent, call_tree) =
495            self.call_inner(contract, fn_name, fn_arg.into(), gas_limit)?;
496        let events = mem::take(&mut self.inner_mut().events);
497
498        Ok(CallReceipt {
499            gas_limit,
500            gas_spent,
501            events,
502            call_tree,
503            data,
504        })
505    }
506
507    /// Migrates a `contract` to a new `bytecode`, performing modifications to
508    /// its state as specified by the closure.
509    ///
510    /// The closure takes a contract ID of where the new contract will be
511    /// available during the migration, and a mutable reference to a session,
512    /// allowing the caller to perform calls and other operations on the new
513    /// (and old) contract.
514    ///
515    /// At the end of the migration, the new contract will be available at the
516    /// given `contract` ID, and the old contract will be removed from the
517    /// state.
518    ///
519    /// If the `owner` of a contract is not set, it will be set to the owner of
520    /// the contract being replaced. If it is set, then it will be used as the
521    /// new owner.
522    ///
523    /// # Errors
524    /// The migration may error during execution for a myriad of reasons. The
525    /// caller is encouraged to drop the `Session` should an error occur as it
526    /// will more than likely be left in an inconsistent state.
527    ///
528    /// # Panics
529    /// If the owner of the new contract is not set in `deploy_data`, and the
530    /// contract being replaced does not exist, this will panic.
531    pub fn migrate<'a, A, D, F>(
532        mut self,
533        contract: ContractId,
534        bytecode: &[u8],
535        deploy_data: D,
536        deploy_gas_limit: u64,
537        closure: F,
538    ) -> Result<Self, Error>
539    where
540        A: 'a + for<'b> Serialize<StandardBufSerializer<'b>>,
541        D: Into<ContractData<'a, A>>,
542        F: FnOnce(ContractId, &mut Session) -> Result<(), Error>,
543    {
544        let mut new_contract_data = deploy_data.into();
545
546        // If the contract being replaced exists, and the caller did not specify
547        // an owner, set the owner to the owner of the contract being replaced.
548        if let Some(old_contract_data) = self
549            .inner_mut()
550            .contract_session
551            .contract(contract)
552            .map_err(|err| PersistenceError(Arc::new(err)))?
553        {
554            if new_contract_data.owner.is_none() {
555                new_contract_data.owner =
556                    Some(old_contract_data.metadata.data().owner.clone());
557            }
558        }
559
560        let (new_contract, _init_receipt) = self.deploy::<_, (), _>(
561            bytecode,
562            new_contract_data,
563            deploy_gas_limit,
564        )?;
565
566        closure(new_contract, &mut self)?;
567
568        self.inner_mut()
569            .contract_session
570            .replace(contract, new_contract)?;
571        self.inner_mut().compiled_modules.remove(&contract);
572        self.inner_mut().compiled_modules.remove(&new_contract);
573
574        Ok(self)
575    }
576
577    /// Execute a *feeder* call on the current state of this session.
578    ///
579    /// Feeder calls are used to have the contract be able to report larger
580    /// amounts of data to the host via the channel included in this call.
581    ///
582    /// These calls should be performed with a large amount of gas, since the
583    /// contracts may spend quite a large amount in an effort to report data.
584    pub fn feeder_call<A, R>(
585        &mut self,
586        contract: ContractId,
587        fn_name: &str,
588        fn_arg: &A,
589        gas_limit: u64,
590        feeder: mpsc::Sender<Vec<u8>>,
591    ) -> Result<CallReceipt<R>, Error>
592    where
593        A: for<'b> Serialize<StandardBufSerializer<'b>>,
594        A::Archived: for<'b> CheckBytes<DefaultValidator<'b>>,
595        R: Archive,
596        R::Archived: Deserialize<R, Infallible>
597            + for<'b> CheckBytes<DefaultValidator<'b>>,
598    {
599        self.inner_mut().feeder = Some(feeder);
600        let r = self.call(contract, fn_name, fn_arg, gas_limit);
601        self.inner_mut().feeder = None;
602        r
603    }
604
605    /// Execute a raw *feeder* call on the current state of this session.
606    ///
607    /// See [`feeder_call`] and [`call_raw`] for more information of this type
608    /// of call.
609    ///
610    /// [`feeder_call`]: [`Session::feeder_call`]
611    /// [`call_raw`]: [`Session::call_raw`]
612    pub fn feeder_call_raw<V: Into<Vec<u8>>>(
613        &mut self,
614        contract: ContractId,
615        fn_name: &str,
616        fn_arg: V,
617        gas_limit: u64,
618        feeder: mpsc::Sender<Vec<u8>>,
619    ) -> Result<CallReceipt<Vec<u8>>, Error> {
620        self.inner_mut().feeder = Some(feeder);
621        let r = self.call_raw(contract, fn_name, fn_arg, gas_limit);
622        self.inner_mut().feeder = None;
623        r
624    }
625
626    /// Returns the current length of the memory of the given contract.
627    ///
628    /// If the contract does not exist, it will return `None`.
629    pub fn memory_len(
630        &mut self,
631        contract_id: ContractId,
632    ) -> Result<Option<usize>, Error> {
633        Ok(self
634            .inner_mut()
635            .contract_session
636            .contract(contract_id)
637            .map_err(|err| PersistenceError(Arc::new(err)))?
638            .map(|data| data.memory.current_len()))
639    }
640
641    pub(crate) fn instance(
642        &mut self,
643        contract_id: &ContractId,
644    ) -> Option<&mut WrappedInstance> {
645        self.inner_mut()
646            .instances
647            .get_mut(contract_id)
648            .map(Box::as_mut)
649    }
650
651    fn clear_call_tree_and_instances(&mut self) {
652        self.inner_mut().call_tree.clear();
653        self.inner_mut().instances.clear();
654    }
655
656    /// Return the state root of the current state of the session.
657    ///
658    /// The state root is the root of a merkle tree whose leaves are the hashes
659    /// of the state of of each contract, ordered by their contract ID.
660    ///
661    /// It also doubles as the ID of a commit - the commit root.
662    pub fn root(&self) -> [u8; 32] {
663        self.inner().contract_session.root().into()
664    }
665
666    /// Returns an iterator over the pages (and their indices) of a contract's
667    /// memory, together with a proof of their inclusion in the state.
668    ///
669    /// The proof is a Merkle inclusion proof, and the caller is able to verify
670    /// it by using [`verify`], and matching the root with the one returned by
671    /// [`root`].
672    ///
673    /// [`verify`]: PageOpening::verify
674    /// [`root`]: Session::root
675    pub fn memory_pages(
676        &self,
677        contract: ContractId,
678    ) -> Option<impl Iterator<Item = (usize, &[u8], PageOpening)>> {
679        self.inner().contract_session.memory_pages(contract)
680    }
681
682    pub(crate) fn push_event(&mut self, event: Event) {
683        self.inner_mut().events.push(event);
684    }
685
686    pub(crate) fn event_checkpoint(&self) -> usize {
687        self.inner().events.len()
688    }
689
690    pub(crate) fn revert_events_from(&mut self, checkpoint: usize) {
691        for event in self.inner_mut().events.iter_mut().skip(checkpoint) {
692            event.reverted = true;
693        }
694    }
695
696    pub(crate) fn push_feed(&mut self, data: Vec<u8>) -> Result<(), Error> {
697        let feed = self.inner().feeder.as_ref().ok_or(Error::MissingFeed)?;
698        feed.send(data).map_err(Error::FeedPulled)
699    }
700
701    fn new_instance(
702        &mut self,
703        contract_id: ContractId,
704    ) -> Result<WrappedInstance, Error> {
705        let store_data = self
706            .inner_mut()
707            .contract_session
708            .contract(contract_id)
709            .map_err(|err| PersistenceError(Arc::new(err)))?
710            .ok_or(Error::ContractDoesNotExist(contract_id))?;
711
712        let module = if let Some(module) =
713            self.inner().compiled_modules.get(&contract_id)
714        {
715            module.clone()
716        } else {
717            let module = unsafe {
718                WasmtimeModule::deserialize(
719                    &self.engine,
720                    store_data.module.serialize(),
721                )?
722            };
723            self.inner_mut()
724                .compiled_modules
725                .insert(contract_id, module.clone());
726            module
727        };
728
729        self.inner_mut().current = contract_id;
730
731        let instance = WrappedInstance::new(
732            self.clone(),
733            contract_id,
734            &module,
735            store_data.memory,
736        )?;
737
738        Ok(instance)
739    }
740
741    pub(crate) fn host_query_arc(
742        &self,
743        name: &str,
744    ) -> Option<Arc<dyn HostQuery>> {
745        self.inner().host_queries.get_arc(name)
746    }
747
748    pub(crate) fn nth_from_top(&self, n: usize) -> Option<CallTreeElem> {
749        self.inner().call_tree.nth_parent(n)
750    }
751
752    pub(crate) fn call_ids(&self) -> Vec<&ContractId> {
753        self.inner().call_tree.call_ids()
754    }
755
756    /// Creates a new instance of the given contract, returning its memory
757    /// length.
758    fn create_instance(
759        &mut self,
760        contract: ContractId,
761    ) -> Result<usize, Error> {
762        let instance = self.new_instance(contract)?;
763        if self.inner().instances.contains_key(&contract) {
764            panic!("Contract already in the stack: {contract:?}");
765        }
766
767        let mem_len = instance.mem_len();
768
769        self.inner_mut()
770            .instances
771            .insert(contract, Box::new(instance));
772        Ok(mem_len)
773    }
774
775    pub(crate) fn push_callstack(
776        &mut self,
777        contract_id: ContractId,
778        limit: u64,
779    ) -> Result<CallTreeElem, Error> {
780        let current_depth = self.inner().call_tree.depth();
781        if current_depth >= MAX_CALL_DEPTH {
782            return Err(Error::SessionError(
783                format!("Maximum call depth exceeded ({MAX_CALL_DEPTH})")
784                    .into(),
785            ));
786        }
787
788        let mem_len =
789            if let Some(instance) = self.inner().instances.get(&contract_id) {
790                instance.mem_len()
791            } else {
792                self.create_instance(contract_id)?
793            };
794
795        self.inner_mut().call_tree.push(CallTreeElem {
796            contract_id,
797            limit,
798            spent: 0,
799            mem_len,
800        });
801
802        Ok(self
803            .inner()
804            .call_tree
805            .nth_parent(0)
806            .expect("We just pushed an element to the stack"))
807    }
808
809    pub(crate) fn move_up_call_tree(&mut self, spent: u64) {
810        self.inner_mut().call_tree.move_up(spent);
811    }
812
813    pub(crate) fn move_up_prune_call_tree(&mut self) {
814        self.inner_mut().call_tree.move_up_prune();
815    }
816
817    pub(crate) fn revert_callstack(&mut self) -> Result<(), std::io::Error> {
818        let call_tree: Vec<_> =
819            self.inner().call_tree.iter().copied().collect();
820        for elem in call_tree {
821            let instance = self
822                .instance(&elem.contract_id)
823                .expect("instance should exist");
824            instance.revert()?;
825            instance.set_len(elem.mem_len);
826        }
827
828        Ok(())
829    }
830
831    /// Commits the given session to disk, consuming the session and returning
832    /// its state root.
833    pub fn commit(mut self) -> Result<[u8; 32], Error> {
834        self.inner_mut()
835            .contract_session
836            .commit()
837            .map(Into::into)
838            .map_err(|err| PersistenceError(Arc::new(err)))
839    }
840
841    #[cfg(feature = "debug")]
842    pub(crate) fn register_debug<M: Into<String>>(&mut self, msg: M) {
843        self.inner_mut().debug.push(msg.into());
844    }
845
846    pub fn with_debug<C, R>(&self, c: C) -> R
847    where
848        C: FnOnce(&[String]) -> R,
849    {
850        c(&self.inner().debug)
851    }
852
853    /// Returns the value of a metadata item.
854    pub fn meta(&self, name: &str) -> Option<Vec<u8>> {
855        self.inner().data.get(name)
856    }
857
858    /// Set the value of a metadata item.
859    ///
860    /// Returns the previous value of the metadata item.
861    pub fn set_meta<S, V>(
862        &mut self,
863        name: S,
864        value: V,
865    ) -> Result<Option<Vec<u8>>, Error>
866    where
867        S: Into<Cow<'static, str>>,
868        V: for<'a> Serialize<StandardBufSerializer<'a>>,
869    {
870        let data = Self::serialize_data(&value)?;
871        Ok(self.inner_mut().data.set(name, data))
872    }
873
874    /// Remove a metadata item.
875    ///
876    /// Returns the value of the removed item (if any).
877    pub fn remove_meta<S>(&mut self, name: S) -> Option<Vec<u8>>
878    where
879        S: Into<Cow<'static, str>>,
880    {
881        self.inner_mut().data.remove(name)
882    }
883
884    pub fn serialize_data<V>(value: &V) -> Result<Vec<u8>, Error>
885    where
886        V: for<'a> Serialize<StandardBufSerializer<'a>>,
887    {
888        let mut buf = [0u8; MAX_META_SIZE];
889        let mut sbuf = [0u8; SCRATCH_BUF_BYTES];
890
891        let ser = BufferSerializer::new(&mut buf[..]);
892        let scratch = BufferScratch::new(&mut sbuf);
893
894        let mut serializer =
895            StandardBufSerializer::new(ser, scratch, Infallible);
896        serializer.serialize_value(value)?;
897
898        let pos = serializer.pos();
899
900        Ok(buf[..pos].to_vec())
901    }
902
903    fn call_inner(
904        &mut self,
905        contract: ContractId,
906        fname: &str,
907        fdata: Vec<u8>,
908        limit: u64,
909    ) -> Result<(Vec<u8>, u64, CallTree), Error> {
910        let event_checkpoint = self.event_checkpoint();
911        let stack_element = self.push_callstack(contract, limit)?;
912        {
913            let instance = self
914                .instance(&stack_element.contract_id)
915                .expect("instance should exist");
916            instance
917                .snap()
918                .map_err(|err| Error::MemorySnapshotFailure {
919                    reason: None,
920                    io: Arc::new(err),
921                })?;
922        }
923
924        let ret_len = {
925            let instance = self
926                .instance(&stack_element.contract_id)
927                .expect("instance should exist");
928            let arg_len = instance.write_bytes_to_arg_buffer(&fdata)?;
929            instance
930                .call(fname, arg_len, limit)
931                .map_err(Error::normalize)
932        };
933
934        let ret_len = match ret_len {
935            Ok(ret_len) => ret_len,
936            Err(err) => {
937                let err = if let Err(io_err) = self.revert_callstack() {
938                    Error::MemorySnapshotFailure {
939                        reason: Some(Arc::new(err)),
940                        io: Arc::new(io_err),
941                    }
942                } else {
943                    err
944                };
945                self.revert_events_from(event_checkpoint);
946                self.move_up_prune_call_tree();
947                self.clear_call_tree_and_instances();
948                return Err(err);
949            }
950        };
951
952        let (ret, spent) = {
953            let instance = self
954                .instance(&stack_element.contract_id)
955                .expect("instance should exist");
956            let ret = instance.read_bytes_from_arg_buffer(ret_len as u32);
957            let spent = limit - instance.get_remaining_gas();
958            (ret, spent)
959        };
960
961        let call_tree: Vec<_> =
962            self.inner().call_tree.iter().copied().collect();
963        for elem in call_tree {
964            let instance = self
965                .instance(&elem.contract_id)
966                .expect("instance should exist");
967            instance
968                .apply()
969                .map_err(|err| Error::MemorySnapshotFailure {
970                    reason: None,
971                    io: Arc::new(err),
972                })?;
973        }
974
975        let mut call_tree = CallTree::new();
976        mem::swap(&mut self.inner_mut().call_tree, &mut call_tree);
977        call_tree.update_spent(spent);
978
979        self.clear_call_tree_and_instances();
980
981        Ok((ret, spent, call_tree))
982    }
983
984    pub fn contract_metadata(
985        &mut self,
986        contract_id: &ContractId,
987    ) -> Option<&ContractMetadata> {
988        self.inner_mut()
989            .contract_session
990            .contract_metadata(contract_id)
991    }
992
993    /// Set a hook that is called before each inter-contract call.
994    ///
995    /// The hook receives the callee contract ID, the function name, and the
996    /// raw argument bytes.
997    #[cfg(feature = "call-hook")]
998    pub fn set_call_hook(&mut self, hook: CallHook) -> Option<CallHook> {
999        self.inner_mut().call_hook.replace(hook)
1000    }
1001
1002    /// Remove the call hook, if one is set.
1003    #[cfg(feature = "call-hook")]
1004    pub fn clear_call_hook(&mut self) -> Option<CallHook> {
1005        self.inner_mut().call_hook.take()
1006    }
1007
1008    /// Run the call hook, if one is set.
1009    ///
1010    /// Returns `Ok(())` if the call is allowed (or no hook is set), or
1011    /// `Err(reason)` if the hook rejects.
1012    #[cfg(feature = "call-hook")]
1013    pub(crate) fn call_hook(
1014        &self,
1015        callee: &ContractId,
1016        fn_name: &str,
1017        arg: &[u8],
1018    ) -> Result<(), String> {
1019        if let Some(hook) = &self.inner().call_hook {
1020            hook(callee, fn_name, arg)
1021        } else {
1022            Ok(())
1023        }
1024    }
1025}
1026
1027/// The receipt given for a call execution using one of either [`call`] or
1028/// [`call_raw`]. This receipt is also returned within a tuple when
1029/// a Contract is deployed and its `init` method is executed.
1030///
1031/// [`call`]: [`Session::call`]
1032/// [`call_raw`]: [`Session::call_raw`]
1033#[derive(Debug)]
1034pub struct CallReceipt<T> {
1035    /// The amount of gas spent in the execution of the call.
1036    pub gas_spent: u64,
1037    /// The limit used in during this execution.
1038    pub gas_limit: u64,
1039
1040    /// The events emitted during the execution of the call.
1041    pub events: Vec<Event>,
1042    /// The call tree produced during the execution.
1043    pub call_tree: CallTree,
1044
1045    /// The data returned by the called contract.
1046    pub data: T,
1047}
1048
1049impl CallReceipt<Vec<u8>> {
1050    /// Deserializes a `CallReceipt<Vec<u8>>` into a `CallReceipt<T>` using
1051    /// `rkyv`.
1052    fn deserialize<T>(self) -> Result<CallReceipt<T>, Error>
1053    where
1054        T: Archive,
1055        T::Archived: Deserialize<T, Infallible>
1056            + for<'b> CheckBytes<DefaultValidator<'b>>,
1057    {
1058        let ta = check_archived_root::<T>(&self.data[..])?;
1059        let data = ta.deserialize(&mut Infallible)?;
1060
1061        Ok(CallReceipt {
1062            gas_spent: self.gas_spent,
1063            gas_limit: self.gas_limit,
1064            events: self.events,
1065            call_tree: self.call_tree,
1066            data,
1067        })
1068    }
1069}
1070
1071#[derive(Debug, Default)]
1072pub struct SessionData {
1073    data: BTreeMap<Cow<'static, str>, Vec<u8>>,
1074    pub base: Option<[u8; 32]>,
1075    excluded_host_queries: BTreeSet<String>,
1076}
1077
1078impl SessionData {
1079    pub fn builder() -> SessionDataBuilder {
1080        SessionDataBuilder {
1081            data: BTreeMap::new(),
1082            base: None,
1083            excluded_host_queries: BTreeSet::new(),
1084        }
1085    }
1086
1087    fn get(&self, name: &str) -> Option<Vec<u8>> {
1088        self.data.get(name).cloned()
1089    }
1090
1091    fn set<S>(&mut self, name: S, data: Vec<u8>) -> Option<Vec<u8>>
1092    where
1093        S: Into<Cow<'static, str>>,
1094    {
1095        self.data.insert(name.into(), data)
1096    }
1097
1098    fn remove<S>(&mut self, name: S) -> Option<Vec<u8>>
1099    where
1100        S: Into<Cow<'static, str>>,
1101    {
1102        self.data.remove(&name.into())
1103    }
1104
1105    pub fn excluded_host_queries(&self) -> Iter<String> {
1106        self.excluded_host_queries.iter()
1107    }
1108}
1109
1110impl From<SessionDataBuilder> for SessionData {
1111    fn from(builder: SessionDataBuilder) -> Self {
1112        builder.build()
1113    }
1114}
1115
1116pub struct SessionDataBuilder {
1117    data: BTreeMap<Cow<'static, str>, Vec<u8>>,
1118    base: Option<[u8; 32]>,
1119    excluded_host_queries: BTreeSet<String>,
1120}
1121
1122impl SessionDataBuilder {
1123    pub fn insert<S, V>(mut self, name: S, value: V) -> Result<Self, Error>
1124    where
1125        S: Into<Cow<'static, str>>,
1126        V: for<'a> Serialize<StandardBufSerializer<'a>>,
1127    {
1128        let data = Session::serialize_data(&value)?;
1129        self.data.insert(name.into(), data);
1130        Ok(self)
1131    }
1132
1133    pub fn base(mut self, base: [u8; 32]) -> Self {
1134        self.base = Some(base);
1135        self
1136    }
1137
1138    pub fn exclude_hq(mut self, name: String) -> Self {
1139        self.excluded_host_queries.insert(name);
1140        self
1141    }
1142
1143    fn build(&self) -> SessionData {
1144        SessionData {
1145            data: self.data.clone(),
1146            base: self.base,
1147            excluded_host_queries: self.excluded_host_queries.clone(),
1148        }
1149    }
1150}