ab_executor_native/
context.rs

1mod ffi_call;
2
3use crate::context::ffi_call::make_ffi_call;
4use ab_contracts_common::env::{EnvState, ExecutorContext, MethodContext, PreparedMethod};
5use ab_contracts_common::method::{ExternalArgs, MethodFingerprint};
6use ab_contracts_common::{Address, ContractError, ExitCode, ShardIndex};
7use ab_executor_slots::NestedSlots;
8use ab_system_contract_address_allocator::ffi::allocate_address::AddressAllocatorAllocateAddressArgs;
9use halfbrown::HashMap;
10use std::cell::UnsafeCell;
11use std::ffi::c_void;
12use std::ptr::NonNull;
13use tracing::{error, info_span};
14
15#[derive(Debug, Copy, Clone)]
16pub(super) struct MethodDetails {
17    pub(super) recommended_state_capacity: u32,
18    pub(super) recommended_slot_capacity: u32,
19    pub(super) recommended_tmp_capacity: u32,
20    pub(super) method_metadata: &'static [u8],
21    pub(super) ffi_fn: unsafe extern "C" fn(NonNull<NonNull<c_void>>) -> ExitCode,
22}
23
24#[derive(Debug)]
25pub(super) struct NativeExecutorContext<'a> {
26    shard_index: ShardIndex,
27    system_allocator_address: Address,
28    /// Indexed by contract's code and method fingerprint
29    methods_by_code: &'a HashMap<(&'static [u8], &'static MethodFingerprint), MethodDetails>,
30    slots: UnsafeCell<NestedSlots<'a>>,
31    allow_env_mutation: bool,
32}
33
34impl<'a> ExecutorContext for NativeExecutorContext<'a> {
35    fn call(
36        &self,
37        previous_env_state: &EnvState,
38        prepared_method: &mut PreparedMethod<'_>,
39    ) -> Result<(), ContractError> {
40        // SAFETY: `NativeExecutorContext` is not `Sync`, slots instance was provided as `&mut` in
41        // the constructor (meaning exclusive access) and this function is the only place where it
42        // is accessed without recursive calls to itself
43        // TODO: This ignores the lifetime by going through the pointer, find a way to make it work
44        //  with the inherited lifetime
45        let slots = unsafe { self.slots.get().as_mut_unchecked() };
46
47        let PreparedMethod {
48            contract,
49            fingerprint,
50            external_args,
51            method_context,
52            ..
53        } = prepared_method;
54
55        let env_state = EnvState {
56            shard_index: self.shard_index,
57            padding_0: Default::default(),
58            own_address: *contract,
59            context: match method_context {
60                MethodContext::Keep => previous_env_state.context,
61                MethodContext::Reset => Address::NULL,
62                MethodContext::Replace => previous_env_state.own_address,
63            },
64            caller: previous_env_state.own_address,
65        };
66
67        let span = info_span!("NativeExecutorContext", %contract);
68        let _span_guard = span.enter();
69
70        let method_details = {
71            let code = slots.get_code(*contract).ok_or_else(|| {
72                error!("Contract or its code not found");
73                ContractError::NotFound
74            })?;
75            *self
76                .methods_by_code
77                .get(&(code.as_slice(), fingerprint))
78                .ok_or_else(|| {
79                    let code = String::from_utf8_lossy(code.as_slice());
80                    error!(
81                        %code,
82                        %fingerprint,
83                        "Contract's code or fingerprint not found in methods map"
84                    );
85                    ContractError::NotImplemented
86                })?
87        };
88        let is_allocate_new_address_method = contract == &self.system_allocator_address
89            && fingerprint == &AddressAllocatorAllocateAddressArgs::FINGERPRINT;
90
91        make_ffi_call(
92            self.allow_env_mutation,
93            is_allocate_new_address_method,
94            slots,
95            *contract,
96            method_details,
97            external_args,
98            env_state,
99            |slots, allow_env_mutation| self.new_nested(slots, allow_env_mutation),
100        )
101    }
102}
103
104impl<'a> NativeExecutorContext<'a> {
105    #[inline(always)]
106    pub(super) fn new(
107        shard_index: ShardIndex,
108        methods_by_code: &'a HashMap<(&'static [u8], &'static MethodFingerprint), MethodDetails>,
109        slots: NestedSlots<'a>,
110        allow_env_mutation: bool,
111    ) -> Self {
112        Self {
113            shard_index,
114            system_allocator_address: Address::system_address_allocator(shard_index),
115            methods_by_code,
116            slots: UnsafeCell::new(slots),
117            allow_env_mutation,
118        }
119    }
120
121    #[inline(always)]
122    fn new_nested(
123        &self,
124        slots: NestedSlots<'a>,
125        allow_env_mutation: bool,
126    ) -> NativeExecutorContext<'a> {
127        Self {
128            shard_index: self.shard_index,
129            system_allocator_address: self.system_allocator_address,
130            methods_by_code: self.methods_by_code,
131            slots: UnsafeCell::new(slots),
132            allow_env_mutation,
133        }
134    }
135}