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