casper_executor_wasm_interface/
executor.rs

1use std::sync::Arc;
2
3use borsh::BorshSerialize;
4use bytes::Bytes;
5use casper_storage::{
6    global_state::{error::Error as GlobalStateError, GlobalStateReader},
7    tracking_copy::TrackingCopyCache,
8    AddressGenerator, TrackingCopy,
9};
10use casper_types::{
11    account::AccountHash, contract_messages::Messages, execution::Effects, BlockHash, BlockTime,
12    Digest, HashAddr, Key, TransactionHash,
13};
14use parking_lot::RwLock;
15use thiserror::Error;
16
17use crate::{CallError, GasUsage, InternalHostError, WasmPreparationError};
18
19/// Request to execute a Wasm contract.
20pub struct ExecuteRequest {
21    /// Initiator's address.
22    pub initiator: AccountHash,
23    /// Caller's address key.
24    ///
25    /// Either a `[`Key::Account`]` or a `[`Key::AddressableEntity`].
26    pub caller_key: Key,
27    /// Gas limit.
28    pub gas_limit: u64,
29    /// Target for execution.
30    pub execution_kind: ExecutionKind,
31    /// Input data.
32    pub input: Bytes,
33    /// Value transferred to the contract.
34    pub transferred_value: u64,
35    /// Transaction hash.
36    pub transaction_hash: TransactionHash,
37    /// Address generator.
38    ///
39    /// This can be either seeded and created as part of the builder or shared across chain of
40    /// execution requests.
41    pub address_generator: Arc<RwLock<AddressGenerator>>,
42    /// Chain name.
43    ///
44    /// This is very important ingredient for deriving contract hashes on the network.
45    pub chain_name: Arc<str>,
46    /// Block time represented as a unix timestamp.
47    pub block_time: BlockTime,
48    /// State root hash of the global state in which the transaction will be executed.
49    pub state_hash: Digest,
50    /// Parent block hash.
51    pub parent_block_hash: BlockHash,
52    /// Block height.
53    pub block_height: u64,
54}
55
56/// Builder for `ExecuteRequest`.
57#[derive(Default)]
58pub struct ExecuteRequestBuilder {
59    initiator: Option<AccountHash>,
60    caller_key: Option<Key>,
61    gas_limit: Option<u64>,
62    target: Option<ExecutionKind>,
63    input: Option<Bytes>,
64    value: Option<u64>,
65    transaction_hash: Option<TransactionHash>,
66    address_generator: Option<Arc<RwLock<AddressGenerator>>>,
67    chain_name: Option<Arc<str>>,
68    block_time: Option<BlockTime>,
69    state_hash: Option<Digest>,
70    parent_block_hash: Option<BlockHash>,
71    block_height: Option<u64>,
72}
73
74impl ExecuteRequestBuilder {
75    /// Set the initiator's address.
76    #[must_use]
77    pub fn with_initiator(mut self, initiator: AccountHash) -> Self {
78        self.initiator = Some(initiator);
79        self
80    }
81
82    /// Set the caller's key.
83    #[must_use]
84    pub fn with_caller_key(mut self, caller_key: Key) -> Self {
85        self.caller_key = Some(caller_key);
86        self
87    }
88
89    /// Set the gas limit.
90    #[must_use]
91    pub fn with_gas_limit(mut self, gas_limit: u64) -> Self {
92        self.gas_limit = Some(gas_limit);
93        self
94    }
95
96    /// Set the target for execution.
97    #[must_use]
98    pub fn with_target(mut self, target: ExecutionKind) -> Self {
99        self.target = Some(target);
100        self
101    }
102
103    /// Pass input data.
104    #[must_use]
105    pub fn with_input(mut self, input: Bytes) -> Self {
106        self.input = Some(input);
107        self
108    }
109
110    /// Pass input data that can be serialized.
111    #[must_use]
112    pub fn with_serialized_input<T: BorshSerialize>(self, input: T) -> Self {
113        let input = borsh::to_vec(&input)
114            .map(Bytes::from)
115            .expect("should serialize input");
116        self.with_input(input)
117    }
118
119    /// Pass value to be sent to the contract.
120    #[must_use]
121    pub fn with_transferred_value(mut self, value: u64) -> Self {
122        self.value = Some(value);
123        self
124    }
125
126    /// Set the transaction hash.
127    #[must_use]
128    pub fn with_transaction_hash(mut self, transaction_hash: TransactionHash) -> Self {
129        self.transaction_hash = Some(transaction_hash);
130        self
131    }
132
133    /// Set the address generator.
134    ///
135    /// This can be either seeded and created as part of the builder or shared across chain of
136    /// execution requests.
137    #[must_use]
138    pub fn with_address_generator(mut self, address_generator: AddressGenerator) -> Self {
139        self.address_generator = Some(Arc::new(RwLock::new(address_generator)));
140        self
141    }
142
143    /// Set the shared address generator.
144    ///
145    /// This is useful when the address generator is shared across a chain of multiple execution
146    /// requests.
147    #[must_use]
148    pub fn with_shared_address_generator(
149        mut self,
150        address_generator: Arc<RwLock<AddressGenerator>>,
151    ) -> Self {
152        self.address_generator = Some(address_generator);
153        self
154    }
155
156    /// Set the chain name.
157    #[must_use]
158    pub fn with_chain_name<T: Into<Arc<str>>>(mut self, chain_name: T) -> Self {
159        self.chain_name = Some(chain_name.into());
160        self
161    }
162
163    /// Set the block time.
164    #[must_use]
165    pub fn with_block_time(mut self, block_time: BlockTime) -> Self {
166        self.block_time = Some(block_time);
167        self
168    }
169
170    /// Set the state hash.
171    #[must_use]
172    pub fn with_state_hash(mut self, state_hash: Digest) -> Self {
173        self.state_hash = Some(state_hash);
174        self
175    }
176
177    /// Set the parent block hash.
178    #[must_use]
179    pub fn with_parent_block_hash(mut self, parent_block_hash: BlockHash) -> Self {
180        self.parent_block_hash = Some(parent_block_hash);
181        self
182    }
183
184    /// Set the block height.
185    #[must_use]
186    pub fn with_block_height(mut self, block_height: u64) -> Self {
187        self.block_height = Some(block_height);
188        self
189    }
190
191    /// Build the `ExecuteRequest`.
192    pub fn build(self) -> Result<ExecuteRequest, &'static str> {
193        let initiator = self.initiator.ok_or("Initiator is not set")?;
194        let caller_key = self.caller_key.ok_or("Caller is not set")?;
195        let gas_limit = self.gas_limit.ok_or("Gas limit is not set")?;
196        let execution_kind = self.target.ok_or("Target is not set")?;
197        let input = self.input.ok_or("Input is not set")?;
198        let transferred_value = self.value.ok_or("Value is not set")?;
199        let transaction_hash = self.transaction_hash.ok_or("Transaction hash is not set")?;
200        let address_generator = self
201            .address_generator
202            .ok_or("Address generator is not set")?;
203        let chain_name = self.chain_name.ok_or("Chain name is not set")?;
204        let block_time = self.block_time.ok_or("Block time is not set")?;
205        let state_hash = self.state_hash.ok_or("State hash is not set")?;
206        let parent_block_hash = self
207            .parent_block_hash
208            .ok_or("Parent block hash is not set")?;
209        let block_height = self.block_height.ok_or("Block height is not set")?;
210        Ok(ExecuteRequest {
211            initiator,
212            caller_key,
213            gas_limit,
214            execution_kind,
215            input,
216            transferred_value,
217            transaction_hash,
218            address_generator,
219            chain_name,
220            block_time,
221            state_hash,
222            parent_block_hash,
223            block_height,
224        })
225    }
226}
227
228/// Result of executing a Wasm contract.
229#[derive(Debug)]
230pub struct ExecuteResult {
231    /// Error while executing Wasm: traps, memory access errors, etc.
232    pub host_error: Option<CallError>,
233    /// Output produced by the Wasm contract.
234    pub output: Option<Bytes>,
235    /// Gas usage.
236    pub gas_usage: GasUsage,
237    /// Effects produced by the execution.
238    pub effects: Effects,
239    /// Cache of tracking copy effects produced by the execution.
240    pub cache: TrackingCopyCache,
241    /// Messages produced by the execution.
242    pub messages: Messages,
243}
244
245impl ExecuteResult {
246    /// Returns the host error.
247    pub fn effects(&self) -> &Effects {
248        &self.effects
249    }
250
251    pub fn into_effects(self) -> Effects {
252        self.effects
253    }
254
255    pub fn host_error(&self) -> Option<&CallError> {
256        self.host_error.as_ref()
257    }
258
259    pub fn output(&self) -> Option<&Bytes> {
260        self.output.as_ref()
261    }
262
263    pub fn gas_usage(&self) -> &GasUsage {
264        &self.gas_usage
265    }
266}
267
268/// Result of executing a Wasm contract on a state provider.
269#[derive(Debug)]
270pub struct ExecuteWithProviderResult {
271    /// Error while executing Wasm: traps, memory access errors, etc.
272    pub host_error: Option<CallError>,
273    /// Output produced by the Wasm contract.
274    output: Option<Bytes>,
275    /// Gas usage.
276    gas_usage: GasUsage,
277    /// Effects produced by the execution.
278    effects: Effects,
279    /// Post state hash.
280    post_state_hash: Digest,
281    /// Messages produced by the execution.
282    messages: Messages,
283}
284
285impl ExecuteWithProviderResult {
286    #[must_use]
287    pub fn new(
288        host_error: Option<CallError>,
289        output: Option<Bytes>,
290        gas_usage: GasUsage,
291        effects: Effects,
292        post_state_hash: Digest,
293        messages: Messages,
294    ) -> Self {
295        Self {
296            host_error,
297            output,
298            gas_usage,
299            effects,
300            post_state_hash,
301            messages,
302        }
303    }
304
305    pub fn output(&self) -> Option<&Bytes> {
306        self.output.as_ref()
307    }
308
309    pub fn gas_usage(&self) -> &GasUsage {
310        &self.gas_usage
311    }
312
313    pub fn effects(&self) -> &Effects {
314        &self.effects
315    }
316
317    #[must_use]
318    pub fn post_state_hash(&self) -> Digest {
319        self.post_state_hash
320    }
321
322    pub fn messages(&self) -> &Messages {
323        &self.messages
324    }
325}
326
327/// Target for Wasm execution.
328#[derive(Debug, Clone, PartialEq, Eq)]
329pub enum ExecutionKind {
330    /// Execute Wasm bytes directly.
331    SessionBytes(Bytes),
332    /// Execute a stored contract by its address.
333    Stored {
334        /// Address of the contract.
335        address: HashAddr,
336        /// Entry point to call.
337        entry_point: String,
338    },
339}
340
341/// Error that can occur during execution, before the Wasm virtual machine is involved.
342///
343/// This error is returned by the `execute` function. It contains information about the error that
344/// occurred.
345#[derive(Debug, Error)]
346pub enum ExecuteError {
347    /// Error while preparing Wasm instance: export not found, validation, compilation errors, etc.
348    ///
349    /// No wasm was executed at this point.
350    #[error("Wasm error error: {0}")]
351    WasmPreparation(#[from] WasmPreparationError),
352    /// Error while executing Wasm: traps, memory access errors, etc.
353    #[error("Internal host error: {0}")]
354    InternalHost(#[from] InternalHostError),
355    #[error("Code not found")]
356    CodeNotFound(HashAddr),
357}
358
359#[derive(Debug, Error)]
360pub enum ExecuteWithProviderError {
361    /// Error while accessing global state.
362    #[error("Global state error: {0}")]
363    GlobalState(#[from] GlobalStateError),
364    #[error(transparent)]
365    Execute(#[from] ExecuteError),
366}
367
368/// Executor trait.
369///
370/// An executor is responsible for executing Wasm contracts. This implies that the executor is able
371/// to prepare Wasm instances, execute them, and handle errors that occur during execution.
372///
373/// Trait bounds also implying that the executor has to support interior mutability, as it may need
374/// to update its internal state during execution of a single or a chain of multiple contracts.
375pub trait Executor: Clone + Send {
376    fn execute<R: GlobalStateReader + 'static>(
377        &self,
378        tracking_copy: TrackingCopy<R>,
379        execute_request: ExecuteRequest,
380    ) -> Result<ExecuteResult, ExecuteError>;
381}