casper_executor_wasm/
lib.rs

1pub mod install;
2pub(crate) mod system;
3
4use std::{
5    collections::{BTreeSet, VecDeque},
6    sync::Arc,
7};
8
9use bytes::Bytes;
10use casper_execution_engine::{
11    engine_state::{BlockInfo, Error as EngineError, ExecutableItem, ExecutionEngineV1},
12    execution::ExecError,
13};
14use casper_executor_wasm_common::{
15    chain_utils,
16    error::{CallError, TrapCode},
17    flags::ReturnFlags,
18};
19use casper_executor_wasm_host::context::Context;
20use casper_executor_wasm_interface::{
21    executor::{
22        ExecuteError, ExecuteRequest, ExecuteRequestBuilder, ExecuteResult,
23        ExecuteWithProviderError, ExecuteWithProviderResult, ExecutionKind, Executor,
24    },
25    ConfigBuilder, GasUsage, VMError, WasmInstance,
26};
27use casper_executor_wasmer_backend::WasmerEngine;
28use casper_storage::{
29    global_state::{
30        error::Error as GlobalStateError,
31        state::{CommitProvider, StateProvider},
32        GlobalStateReader,
33    },
34    TrackingCopy,
35};
36use casper_types::{
37    account::AccountHash,
38    addressable_entity::{ActionThresholds, AssociatedKeys},
39    bytesrepr, AddressableEntity, ByteCode, ByteCodeAddr, ByteCodeHash, ByteCodeKind,
40    ContractRuntimeTag, Digest, EntityAddr, EntityKind, Gas, Groups, InitiatorAddr, Key,
41    MessageLimits, Package, PackageHash, PackageStatus, Phase, ProtocolVersion, StorageCosts,
42    StoredValue, TransactionInvocationTarget, URef, WasmV2Config, U512,
43};
44use either::Either;
45use install::{InstallContractError, InstallContractRequest, InstallContractResult};
46use parking_lot::RwLock;
47use system::{MintArgs, MintTransferArgs};
48use tracing::{error, warn};
49
50const DEFAULT_WASM_ENTRY_POINT: &str = "call";
51
52const DEFAULT_MINT_TRANSFER_GAS_COST: u64 = 1; // NOTE: Require gas while executing and set this to at least 100_000_000 (or use chainspec)
53
54#[derive(Copy, Clone, Debug)]
55pub enum ExecutorKind {
56    /// Ahead of time compiled Wasm.
57    ///
58    /// This is the default executor kind.
59    Compiled,
60}
61
62#[derive(Copy, Clone, Debug)]
63pub struct ExecutorConfig {
64    memory_limit: u32,
65    executor_kind: ExecutorKind,
66    wasm_config: WasmV2Config,
67    storage_costs: StorageCosts,
68    message_limits: MessageLimits,
69}
70
71impl ExecutorConfigBuilder {
72    pub fn new() -> ExecutorConfigBuilder {
73        ExecutorConfigBuilder::default()
74    }
75}
76
77#[derive(Default)]
78pub struct ExecutorConfigBuilder {
79    memory_limit: Option<u32>,
80    executor_kind: Option<ExecutorKind>,
81    wasm_config: Option<WasmV2Config>,
82    storage_costs: Option<StorageCosts>,
83    message_limits: Option<MessageLimits>,
84}
85
86impl ExecutorConfigBuilder {
87    /// Set the memory limit.
88    pub fn with_memory_limit(mut self, memory_limit: u32) -> Self {
89        self.memory_limit = Some(memory_limit);
90        self
91    }
92
93    /// Set the executor kind.
94    pub fn with_executor_kind(mut self, executor_kind: ExecutorKind) -> Self {
95        self.executor_kind = Some(executor_kind);
96        self
97    }
98
99    /// Set the wasm config.
100    pub fn with_wasm_config(mut self, wasm_config: WasmV2Config) -> Self {
101        self.wasm_config = Some(wasm_config);
102        self
103    }
104
105    /// Set the wasm config.
106    pub fn with_storage_costs(mut self, storage_costs: StorageCosts) -> Self {
107        self.storage_costs = Some(storage_costs);
108        self
109    }
110
111    /// Set the message limits.
112    pub fn with_message_limits(mut self, message_limits: MessageLimits) -> Self {
113        self.message_limits = Some(message_limits);
114        self
115    }
116
117    /// Build the `ExecutorConfig`.
118    pub fn build(self) -> Result<ExecutorConfig, &'static str> {
119        let memory_limit = self.memory_limit.ok_or("Memory limit is not set")?;
120        let executor_kind = self.executor_kind.ok_or("Executor kind is not set")?;
121        let wasm_config = self.wasm_config.ok_or("Wasm config is not set")?;
122        let storage_costs = self.storage_costs.ok_or("Storage costs are not set")?;
123        let message_limits = self.message_limits.ok_or("Message limits are not set")?;
124
125        Ok(ExecutorConfig {
126            memory_limit,
127            executor_kind,
128            wasm_config,
129            storage_costs,
130            message_limits,
131        })
132    }
133}
134
135#[derive(Clone)]
136pub struct ExecutorV2 {
137    config: ExecutorConfig,
138    compiled_wasm_engine: Arc<WasmerEngine>,
139    execution_stack: Arc<RwLock<VecDeque<ExecutionKind>>>,
140    execution_engine_v1: Arc<ExecutionEngineV1>,
141}
142
143impl ExecutorV2 {
144    pub fn install_contract<R>(
145        &self,
146        state_root_hash: Digest,
147        state_provider: &R,
148        install_request: InstallContractRequest,
149    ) -> Result<InstallContractResult, InstallContractError>
150    where
151        R: StateProvider + CommitProvider,
152        <R as StateProvider>::Reader: 'static,
153    {
154        let mut tracking_copy = match state_provider.checkout(state_root_hash) {
155            Ok(Some(tracking_copy)) => {
156                TrackingCopy::new(tracking_copy, 1, state_provider.enable_entity())
157            }
158            Ok(None) => {
159                return Err(InstallContractError::GlobalState(
160                    GlobalStateError::RootNotFound,
161                ))
162            }
163            Err(error) => return Err(error.into()),
164        };
165
166        let InstallContractRequest {
167            initiator,
168            gas_limit,
169            wasm_bytes,
170            entry_point,
171            input,
172            transferred_value,
173            address_generator,
174            transaction_hash,
175            chain_name,
176            block_time,
177            seed,
178            state_hash,
179            parent_block_hash,
180            block_height,
181        } = install_request;
182
183        let bytecode_hash = chain_utils::compute_wasm_bytecode_hash(&wasm_bytes);
184
185        let caller_key = Key::Account(initiator);
186        let _source_purse = get_purse_for_entity(&mut tracking_copy, caller_key);
187
188        // 1. Store package hash
189        let smart_contract_addr: [u8; 32] = chain_utils::compute_predictable_address(
190            chain_name.as_bytes(),
191            initiator.value(),
192            bytecode_hash,
193            seed,
194        );
195
196        let mut smart_contract = Package::new(
197            Default::default(),
198            Default::default(),
199            Groups::default(),
200            PackageStatus::Unlocked,
201        );
202
203        let protocol_version = ProtocolVersion::V2_0_0;
204
205        let protocol_version_major = protocol_version.value().major;
206        let next_version = smart_contract.next_entity_version_for(protocol_version_major);
207        let entity_hash =
208            chain_utils::compute_next_contract_hash_version(smart_contract_addr, next_version);
209        let entity_version_key = smart_contract.insert_entity_version(
210            protocol_version_major,
211            EntityAddr::SmartContract(entity_hash),
212        );
213        debug_assert_eq!(entity_version_key.entity_version(), next_version);
214
215        let smart_contract_addr = chain_utils::compute_predictable_address(
216            chain_name.as_bytes(),
217            initiator.value(),
218            bytecode_hash,
219            seed,
220        );
221
222        tracking_copy.write(
223            Key::SmartContract(smart_contract_addr),
224            StoredValue::SmartContract(smart_contract),
225        );
226
227        // 2. Store wasm
228
229        let bytecode = ByteCode::new(ByteCodeKind::V2CasperWasm, wasm_bytes.clone().into());
230        let bytecode_addr = ByteCodeAddr::V2CasperWasm(bytecode_hash);
231
232        tracking_copy.write(
233            Key::ByteCode(bytecode_addr),
234            StoredValue::ByteCode(bytecode),
235        );
236
237        // 3. Store addressable entity
238        let addressable_entity_key = Key::AddressableEntity(EntityAddr::SmartContract(entity_hash));
239
240        // TODO: abort(str) as an alternative to trap
241        let main_purse: URef = match system::mint_mint(
242            &mut tracking_copy,
243            transaction_hash,
244            Arc::clone(&address_generator),
245            MintArgs {
246                initial_balance: U512::zero(),
247            },
248        ) {
249            Ok(uref) => uref,
250            Err(mint_error) => {
251                error!(?mint_error, "Failed to create a purse");
252                return Err(InstallContractError::SystemContract(
253                    CallError::CalleeTrapped(TrapCode::UnreachableCodeReached),
254                ));
255            }
256        };
257
258        let addressable_entity = AddressableEntity::new(
259            PackageHash::new(smart_contract_addr),
260            ByteCodeHash::new(bytecode_hash),
261            ProtocolVersion::V2_0_0,
262            main_purse,
263            AssociatedKeys::default(),
264            ActionThresholds::default(),
265            EntityKind::SmartContract(ContractRuntimeTag::VmCasperV2),
266        );
267
268        tracking_copy.write(
269            addressable_entity_key,
270            StoredValue::AddressableEntity(addressable_entity),
271        );
272
273        let ctor_gas_usage = match entry_point {
274            Some(entry_point_name) => {
275                let input = input.unwrap_or_default();
276                let execute_request = ExecuteRequestBuilder::default()
277                    .with_initiator(initiator)
278                    .with_caller_key(caller_key)
279                    .with_target(ExecutionKind::Stored {
280                        address: smart_contract_addr,
281                        entry_point: entry_point_name,
282                    })
283                    .with_gas_limit(gas_limit)
284                    .with_input(input)
285                    .with_transferred_value(transferred_value)
286                    .with_transaction_hash(transaction_hash)
287                    .with_shared_address_generator(address_generator)
288                    .with_chain_name(chain_name)
289                    .with_block_time(block_time)
290                    .with_state_hash(state_hash)
291                    .with_parent_block_hash(parent_block_hash)
292                    .with_block_height(block_height)
293                    .build()
294                    .expect("should build");
295
296                let forked_tc = tracking_copy.fork2();
297
298                match Self::execute_with_tracking_copy(self, forked_tc, execute_request) {
299                    Ok(ExecuteResult {
300                        host_error,
301                        output,
302                        gas_usage,
303                        effects,
304                        cache,
305                        messages,
306                    }) => {
307                        if let Some(host_error) = host_error {
308                            return Err(InstallContractError::Constructor { host_error });
309                        }
310
311                        tracking_copy.apply_changes(effects, cache, messages);
312
313                        if let Some(output) = output {
314                            warn!(?output, "unexpected output from constructor");
315                        }
316
317                        gas_usage
318                    }
319                    Err(error) => {
320                        error!(%error, "unable to execute constructor");
321                        return Err(InstallContractError::Execute(error));
322                    }
323                }
324            }
325            None => {
326                // TODO: Calculate storage gas cost etc. and make it the base cost, then add
327                // constructor gas cost
328                GasUsage::new(gas_limit, gas_limit)
329            }
330        };
331
332        let effects = tracking_copy.effects();
333
334        match state_provider.commit_effects(state_root_hash, effects.clone()) {
335            Ok(post_state_hash) => Ok(InstallContractResult {
336                smart_contract_addr,
337                gas_usage: ctor_gas_usage,
338                effects,
339                post_state_hash,
340            }),
341            Err(error) => Err(InstallContractError::GlobalState(error)),
342        }
343    }
344
345    fn execute_with_tracking_copy<R: GlobalStateReader + 'static>(
346        &self,
347        mut tracking_copy: TrackingCopy<R>,
348        execute_request: ExecuteRequest,
349    ) -> Result<ExecuteResult, ExecuteError> {
350        let ExecuteRequest {
351            initiator,
352            caller_key,
353            gas_limit,
354            execution_kind,
355            input,
356            transferred_value,
357            transaction_hash,
358            address_generator,
359            chain_name,
360            block_time,
361            state_hash,
362            parent_block_hash,
363            block_height,
364        } = execute_request;
365
366        // TODO: Purse uref does not need to be optional once value transfers to WasmBytes are
367        // supported. let caller_entity_addr = EntityAddr::new_account(caller);
368        let source_purse = get_purse_for_entity(&mut tracking_copy, caller_key);
369
370        let (wasm_bytes, export_or_selector): (_, Either<&str, u32>) = match &execution_kind {
371            ExecutionKind::SessionBytes(wasm_bytes) => {
372                // self.execute_wasm(tracking_copy, address, gas_limit, wasm_bytes, input)
373                (wasm_bytes.clone(), Either::Left(DEFAULT_WASM_ENTRY_POINT))
374            }
375            ExecutionKind::Stored {
376                address: smart_contract_addr,
377                entry_point,
378            } => {
379                let smart_contract_key = Key::SmartContract(*smart_contract_addr);
380                let legacy_key = Key::Hash(*smart_contract_addr);
381
382                let mut contract = tracking_copy
383                    .read_first(&[&legacy_key, &smart_contract_key])
384                    .expect("should read contract");
385
386                // let entity_addr: EntityAddr;
387
388                // Resolve indirection - get the latest version from the smart contract package
389                // versions. let old_contract = contract.clone();
390                // let latest_version_key;
391                if let Some(StoredValue::SmartContract(smart_contract_package)) = &contract {
392                    let contract_hash = smart_contract_package
393                        .versions()
394                        .latest()
395                        .expect("should have last entry");
396                    let entity_addr = EntityAddr::SmartContract(contract_hash.value());
397                    let latest_version_key = Key::AddressableEntity(entity_addr);
398                    assert_ne!(&entity_addr.value(), smart_contract_addr);
399                    let new_contract = tracking_copy
400                        .read(&latest_version_key)
401                        .expect("should read latest version");
402                    contract = new_contract;
403                };
404
405                match contract {
406                    Some(StoredValue::AddressableEntity(addressable_entity)) => {
407                        let wasm_key = match addressable_entity.kind() {
408                            EntityKind::System(_) => todo!(),
409                            EntityKind::Account(_) => todo!(),
410                            EntityKind::SmartContract(ContractRuntimeTag::VmCasperV1) => {
411                                // We need to short circuit here to execute v1 contracts with legacy
412                                // execut
413
414                                let block_info = BlockInfo::new(
415                                    state_hash,
416                                    block_time,
417                                    parent_block_hash,
418                                    block_height,
419                                    self.execution_engine_v1.config().protocol_version(),
420                                );
421
422                                let entity_addr = EntityAddr::SmartContract(*smart_contract_addr);
423
424                                return self.execute_legacy_wasm_byte_code(
425                                    initiator,
426                                    &entity_addr,
427                                    entry_point.clone(),
428                                    &input,
429                                    &mut tracking_copy,
430                                    block_info,
431                                    transaction_hash,
432                                    gas_limit,
433                                );
434                            }
435                            EntityKind::SmartContract(ContractRuntimeTag::VmCasperV2) => {
436                                Key::ByteCode(ByteCodeAddr::V2CasperWasm(
437                                    addressable_entity.byte_code_addr(),
438                                ))
439                            }
440                        };
441
442                        // Note: Bytecode stored in the GlobalStateReader has a "kind" option -
443                        // currently we know we have a v2 bytecode as the stored contract is of "V2"
444                        // variant.
445                        let wasm_bytes = tracking_copy
446                            .read(&wasm_key)
447                            .expect("should read wasm")
448                            .expect("should have wasm bytes")
449                            .into_byte_code()
450                            .expect("should be byte code")
451                            .take_bytes();
452
453                        if transferred_value != 0 {
454                            let args = {
455                                let maybe_to = None;
456                                let source = source_purse;
457                                let target = addressable_entity.main_purse();
458                                let amount = transferred_value;
459                                let id = None;
460                                MintTransferArgs {
461                                    maybe_to,
462                                    source,
463                                    target,
464                                    amount: amount.into(),
465                                    id,
466                                }
467                            };
468
469                            match system::mint_transfer(
470                                &mut tracking_copy,
471                                transaction_hash,
472                                Arc::clone(&address_generator),
473                                args,
474                            ) {
475                                Ok(()) => {
476                                    // Transfer succeed, go on
477                                }
478                                Err(error) => {
479                                    return Ok(ExecuteResult {
480                                        host_error: Some(error),
481                                        output: None,
482                                        gas_usage: GasUsage::new(
483                                            gas_limit,
484                                            gas_limit - DEFAULT_MINT_TRANSFER_GAS_COST,
485                                        ),
486                                        effects: tracking_copy.effects(),
487                                        cache: tracking_copy.cache(),
488                                        messages: tracking_copy.messages(),
489                                    });
490                                }
491                            }
492                        }
493
494                        (Bytes::from(wasm_bytes), Either::Left(entry_point.as_str()))
495                    }
496                    Some(StoredValue::Contract(_legacy_contract)) => {
497                        let block_info = BlockInfo::new(
498                            state_hash,
499                            block_time,
500                            parent_block_hash,
501                            block_height,
502                            self.execution_engine_v1.config().protocol_version(),
503                        );
504
505                        let entity_addr = EntityAddr::SmartContract(*smart_contract_addr);
506
507                        return self.execute_legacy_wasm_byte_code(
508                            initiator,
509                            &entity_addr,
510                            entry_point.clone(),
511                            &input,
512                            &mut tracking_copy,
513                            block_info,
514                            transaction_hash,
515                            gas_limit,
516                        );
517                    }
518                    Some(stored_value) => {
519                        todo!(
520                            "Unexpected {stored_value:?} under key {:?}",
521                            &execution_kind
522                        );
523                    }
524                    None => {
525                        panic!("No code found in {smart_contract_key:?}");
526                    }
527                }
528            }
529        };
530
531        let vm = Arc::clone(&self.compiled_wasm_engine);
532
533        let mut initial_tracking_copy = tracking_copy.fork2();
534
535        // Derive callee key from the execution target.
536        let callee_key = match &execution_kind {
537            ExecutionKind::Stored {
538                address: smart_contract_addr,
539                ..
540            } => Key::SmartContract(*smart_contract_addr),
541            ExecutionKind::SessionBytes(_wasm_bytes) => Key::Account(initiator),
542        };
543
544        let context = Context {
545            initiator,
546            config: self.config.wasm_config,
547            storage_costs: self.config.storage_costs,
548            caller: caller_key,
549            callee: callee_key,
550            transferred_value,
551            tracking_copy,
552            executor: self.clone(),
553            address_generator: Arc::clone(&address_generator),
554            transaction_hash,
555            chain_name,
556            input,
557            block_time,
558            message_limits: self.config.message_limits,
559        };
560
561        let wasm_instance_config = ConfigBuilder::new()
562            .with_gas_limit(gas_limit)
563            .with_memory_limit(self.config.memory_limit)
564            .build();
565
566        let mut instance = vm.instantiate(wasm_bytes, context, wasm_instance_config)?;
567
568        self.push_execution_stack(execution_kind.clone());
569        let (vm_result, gas_usage) = match export_or_selector {
570            Either::Left(export_name) => instance.call_export(export_name),
571            Either::Right(_entry_point) => todo!("Restore selectors"), /* instance.call_export(&
572                                                                        * entry_point), */
573        };
574
575        let top_execution_kind = self
576            .pop_execution_stack()
577            .expect("should have execution kind"); // SAFETY: We just pushed
578        debug_assert_eq!(&top_execution_kind, &execution_kind);
579
580        let context = instance.teardown();
581
582        let Context {
583            tracking_copy: final_tracking_copy,
584            ..
585        } = context;
586
587        match vm_result {
588            Ok(()) => Ok(ExecuteResult {
589                host_error: None,
590                output: None,
591                gas_usage,
592                effects: final_tracking_copy.effects(),
593                cache: final_tracking_copy.cache(),
594                messages: final_tracking_copy.messages(),
595            }),
596            Err(VMError::Return { flags, data }) => {
597                let host_error = if flags.contains(ReturnFlags::REVERT) {
598                    // The contract has reverted.
599                    Some(CallError::CalleeReverted)
600                } else {
601                    // Merge the tracking copy parts since the execution has succeeded.
602                    initial_tracking_copy.apply_changes(
603                        final_tracking_copy.effects(),
604                        final_tracking_copy.cache(),
605                        final_tracking_copy.messages(),
606                    );
607
608                    None
609                };
610
611                Ok(ExecuteResult {
612                    host_error,
613                    output: data,
614                    gas_usage,
615                    effects: initial_tracking_copy.effects(),
616                    cache: initial_tracking_copy.cache(),
617                    messages: initial_tracking_copy.messages(),
618                })
619            }
620            Err(VMError::OutOfGas) => Ok(ExecuteResult {
621                host_error: Some(CallError::CalleeGasDepleted),
622                output: None,
623                gas_usage,
624                effects: final_tracking_copy.effects(),
625                cache: final_tracking_copy.cache(),
626                messages: final_tracking_copy.messages(),
627            }),
628            Err(VMError::Trap(trap_code)) => Ok(ExecuteResult {
629                host_error: Some(CallError::CalleeTrapped(trap_code)),
630                output: None,
631                gas_usage,
632                effects: initial_tracking_copy.effects(),
633                cache: initial_tracking_copy.cache(),
634                messages: initial_tracking_copy.messages(),
635            }),
636            Err(VMError::Export(export_error)) => {
637                error!(?export_error, "export error");
638                Ok(ExecuteResult {
639                    host_error: Some(CallError::NotCallable),
640                    output: None,
641                    gas_usage,
642                    effects: initial_tracking_copy.effects(),
643                    cache: initial_tracking_copy.cache(),
644                    messages: initial_tracking_copy.messages(),
645                })
646            }
647            Err(VMError::Internal(host_error)) => {
648                error!(?host_error, "host error");
649                Ok(ExecuteResult {
650                    host_error: Some(CallError::InternalHost),
651                    output: None,
652                    gas_usage,
653                    effects: initial_tracking_copy.effects(),
654                    cache: initial_tracking_copy.cache(),
655                    messages: initial_tracking_copy.messages(),
656                })
657            }
658        }
659    }
660
661    #[allow(clippy::too_many_arguments)]
662    fn execute_legacy_wasm_byte_code<R>(
663        &self,
664        initiator: AccountHash,
665        entity_addr: &EntityAddr,
666        entry_point: String,
667        input: &Bytes,
668        tracking_copy: &mut TrackingCopy<R>,
669        block_info: BlockInfo,
670        transaction_hash: casper_types::TransactionHash,
671        gas_limit: u64,
672    ) -> Result<ExecuteResult, ExecuteError>
673    where
674        R: GlobalStateReader + 'static,
675    {
676        let authorization_keys = BTreeSet::from_iter([initiator]);
677        let initiator_addr = InitiatorAddr::AccountHash(initiator);
678        let executable_item =
679            ExecutableItem::Invocation(TransactionInvocationTarget::ByHash(entity_addr.value()));
680        let entry_point = entry_point.clone();
681        let args = bytesrepr::deserialize_from_slice(input).expect("should deserialize");
682        let phase = Phase::Session;
683
684        let wasm_v1_result = {
685            let forked_tc = tracking_copy.fork2();
686            self.execution_engine_v1.execute_with_tracking_copy(
687                forked_tc,
688                block_info,
689                transaction_hash,
690                Gas::from(gas_limit),
691                initiator_addr,
692                executable_item,
693                entry_point,
694                args,
695                authorization_keys,
696                phase,
697            )
698        };
699
700        let effects = wasm_v1_result.effects();
701        let messages = wasm_v1_result.messages();
702
703        match wasm_v1_result.cache() {
704            Some(cache) => {
705                tracking_copy.apply_changes(effects.clone(), cache.clone(), messages.clone());
706            }
707            None => {
708                debug_assert!(
709                    effects.is_empty(),
710                    "effects should be empty if there is no cache"
711                );
712            }
713        }
714
715        let gas_consumed = wasm_v1_result
716            .consumed()
717            .value()
718            .try_into()
719            .expect("Should convert consumed gas to u64");
720
721        let mut output = wasm_v1_result
722            .ret()
723            .map(|ret| bytesrepr::serialize(ret).unwrap())
724            .map(Bytes::from);
725
726        let host_error = match wasm_v1_result.error() {
727            Some(EngineError::Exec(ExecError::GasLimit)) => Some(CallError::CalleeGasDepleted),
728            Some(EngineError::Exec(ExecError::Revert(revert_code))) => {
729                assert!(output.is_none(), "output should be None"); // ExecutionEngineV1 sets output to None when error occurred.
730                let revert_code: u32 = (*revert_code).into();
731                output = Some(revert_code.to_le_bytes().to_vec().into()); // Pass serialized revert code as output.
732                Some(CallError::CalleeReverted)
733            }
734            Some(_) => Some(CallError::CalleeTrapped(TrapCode::UnreachableCodeReached)),
735            None => None,
736        };
737
738        // TODO: Support multisig
739
740        // TODO: Convert this to a host error as if it was executed.
741
742        // SAFETY: Gas limit is first promoted from u64 to u512, and we know
743        // consumed gas under v1 would not exceed the imposed limit therefore an
744        // unwrap here is safe.
745
746        let remaining_points = gas_limit.checked_sub(gas_consumed).unwrap();
747
748        let fork2 = tracking_copy.fork2();
749        Ok(ExecuteResult {
750            host_error,
751            output,
752            gas_usage: GasUsage::new(gas_limit, remaining_points),
753            effects: fork2.effects(),
754            cache: fork2.cache(),
755            messages: fork2.messages(),
756        })
757    }
758
759    pub fn execute_with_provider<R>(
760        &self,
761        state_root_hash: Digest,
762        state_provider: &R,
763        execute_request: ExecuteRequest,
764    ) -> Result<ExecuteWithProviderResult, ExecuteWithProviderError>
765    where
766        R: StateProvider + CommitProvider,
767        <R as StateProvider>::Reader: 'static,
768    {
769        let tracking_copy = match state_provider.checkout(state_root_hash) {
770            Ok(Some(tracking_copy)) => tracking_copy,
771            Ok(None) => {
772                return Err(ExecuteWithProviderError::GlobalState(
773                    GlobalStateError::RootNotFound,
774                ))
775            }
776            Err(global_state_error) => return Err(global_state_error.into()),
777        };
778
779        let tracking_copy = TrackingCopy::new(tracking_copy, 1, state_provider.enable_entity());
780
781        match self.execute_with_tracking_copy(tracking_copy, execute_request) {
782            Ok(ExecuteResult {
783                host_error,
784                output,
785                gas_usage,
786                effects,
787                cache: _,
788                messages,
789            }) => match state_provider.commit_effects(state_root_hash, effects.clone()) {
790                Ok(post_state_hash) => Ok(ExecuteWithProviderResult::new(
791                    host_error,
792                    output,
793                    gas_usage,
794                    effects,
795                    post_state_hash,
796                    messages,
797                )),
798                Err(error) => Err(error.into()),
799            },
800            Err(error) => Err(ExecuteWithProviderError::Execute(error)),
801        }
802    }
803}
804
805impl ExecutorV2 {
806    /// Create a new `ExecutorV2` instance.
807    pub fn new(config: ExecutorConfig, execution_engine_v1: Arc<ExecutionEngineV1>) -> Self {
808        let wasm_engine = match config.executor_kind {
809            ExecutorKind::Compiled => WasmerEngine::new(),
810        };
811        ExecutorV2 {
812            config,
813            compiled_wasm_engine: Arc::new(wasm_engine),
814            execution_stack: Default::default(),
815            execution_engine_v1,
816        }
817    }
818
819    /// Push the execution stack.
820    pub(crate) fn push_execution_stack(&self, execution_kind: ExecutionKind) {
821        let mut execution_stack = self.execution_stack.write();
822        execution_stack.push_back(execution_kind);
823    }
824
825    /// Pop the execution stack.
826    pub(crate) fn pop_execution_stack(&self) -> Option<ExecutionKind> {
827        let mut execution_stack = self.execution_stack.write();
828        execution_stack.pop_back()
829    }
830}
831
832impl Executor for ExecutorV2 {
833    /// Execute a Wasm contract.
834    ///
835    /// # Errors
836    /// Returns an error if the execution fails. This can happen if the Wasm instance cannot be
837    /// prepared. Otherwise, returns the result of the execution with a gas usage attached which
838    /// means a successful execution (that may or may not have produced an error such as a trap,
839    /// return, or out of gas).
840    fn execute<R: GlobalStateReader + 'static>(
841        &self,
842        tracking_copy: TrackingCopy<R>,
843        execute_request: ExecuteRequest,
844    ) -> Result<ExecuteResult, ExecuteError> {
845        self.execute_with_tracking_copy(tracking_copy, execute_request)
846    }
847}
848
849fn get_purse_for_entity<R: GlobalStateReader>(
850    tracking_copy: &mut TrackingCopy<R>,
851    entity_key: Key,
852) -> casper_types::URef {
853    let stored_value = tracking_copy
854        .read(&entity_key)
855        .expect("should read account")
856        .expect("should have account");
857    match stored_value {
858        StoredValue::CLValue(addressable_entity_key) => {
859            let key = addressable_entity_key
860                .into_t::<Key>()
861                .expect("should be key");
862            let stored_value = tracking_copy
863                .read(&key)
864                .expect("should read account")
865                .expect("should have account");
866
867            let addressable_entity = stored_value
868                .into_addressable_entity()
869                .expect("should be addressable entity");
870
871            addressable_entity.main_purse()
872        }
873        StoredValue::Account(account) => account.main_purse(),
874        StoredValue::SmartContract(smart_contract_package) => {
875            let contract_hash = smart_contract_package
876                .versions()
877                .latest()
878                .expect("should have last entry");
879            let entity_addr = EntityAddr::SmartContract(contract_hash.value());
880            let latest_version_key = Key::AddressableEntity(entity_addr);
881            let new_contract = tracking_copy
882                .read(&latest_version_key)
883                .expect("should read latest version");
884            let addressable_entity = new_contract
885                .expect("should have addressable entity")
886                .into_addressable_entity()
887                .expect("should be addressable entity");
888            addressable_entity.main_purse()
889        }
890        other => panic!("should be account or contract received {other:?}"),
891    }
892}