ab_executor_native/
context.rs1mod 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 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 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}