Skip to main content

linera_execution/evm/
inputs.rs

1// Copyright (c) Zefchain Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4//! Code specific to the input of functions, that is selectors,
5//! constructor argument and instantiation argument.
6
7use alloy_primitives::Bytes;
8use linera_base::{
9    crypto::CryptoHash,
10    data_types::StreamUpdate,
11    ensure,
12    identifiers::{ApplicationId, ChainId, GenericApplicationId, StreamId, StreamName},
13};
14use revm_primitives::{address, Address, B256, U256};
15
16use crate::EvmExecutionError;
17
18alloy_sol_types::sol! {
19    struct InternalApplicationId {
20        bytes32 application_description_hash;
21    }
22
23    struct InternalGenericApplicationId {
24        uint8 choice;
25        InternalApplicationId user;
26    }
27
28    struct InternalStreamName {
29        bytes stream_name;
30    }
31
32    struct InternalStreamId {
33        InternalGenericApplicationId application_id;
34        InternalStreamName stream_name;
35    }
36
37    struct InternalChainId {
38        bytes32 value;
39    }
40
41    struct InternalStreamUpdate {
42        InternalChainId chain_id;
43        InternalStreamId stream_id;
44        uint32 previous_index;
45        uint32 first_index;
46        uint32 next_index;
47    }
48
49    function process_streams(InternalStreamUpdate[] internal_streams);
50
51    function summarize_events(InternalStreamUpdate[] internal_streams);
52}
53
54fn crypto_hash_to_internal_crypto_hash(hash: CryptoHash) -> B256 {
55    let hash = <[u64; 4]>::from(hash);
56    let hash = linera_base::crypto::u64_array_to_be_bytes(hash);
57    hash.into()
58}
59
60impl From<ApplicationId> for InternalApplicationId {
61    fn from(application_id: ApplicationId) -> InternalApplicationId {
62        let application_description_hash =
63            crypto_hash_to_internal_crypto_hash(application_id.application_description_hash);
64        InternalApplicationId {
65            application_description_hash,
66        }
67    }
68}
69
70impl From<GenericApplicationId> for InternalGenericApplicationId {
71    fn from(generic_application_id: GenericApplicationId) -> InternalGenericApplicationId {
72        match generic_application_id {
73            GenericApplicationId::System => {
74                let application_description_hash = B256::ZERO;
75                InternalGenericApplicationId {
76                    choice: 0,
77                    user: InternalApplicationId {
78                        application_description_hash,
79                    },
80                }
81            }
82            GenericApplicationId::User(application_id) => InternalGenericApplicationId {
83                choice: 1,
84                user: application_id.into(),
85            },
86        }
87    }
88}
89
90impl From<ChainId> for InternalChainId {
91    fn from(chain_id: ChainId) -> InternalChainId {
92        let value = crypto_hash_to_internal_crypto_hash(chain_id.0);
93        InternalChainId { value }
94    }
95}
96
97impl From<StreamName> for InternalStreamName {
98    fn from(stream_name: StreamName) -> InternalStreamName {
99        let stream_name = Bytes::from(stream_name.0);
100        InternalStreamName { stream_name }
101    }
102}
103
104impl From<StreamId> for InternalStreamId {
105    fn from(stream_id: StreamId) -> InternalStreamId {
106        let application_id = stream_id.application_id.into();
107        let stream_name = stream_id.stream_name.into();
108        InternalStreamId {
109            application_id,
110            stream_name,
111        }
112    }
113}
114
115impl From<StreamUpdate> for InternalStreamUpdate {
116    fn from(stream_update: StreamUpdate) -> InternalStreamUpdate {
117        let chain_id = stream_update.chain_id.into();
118        let stream_id = stream_update.stream_id.into();
119        InternalStreamUpdate {
120            chain_id,
121            stream_id,
122            previous_index: stream_update.previous_index,
123            first_index: stream_update.first_index,
124            next_index: stream_update.next_index,
125        }
126    }
127}
128
129// This is the precompile address that contains the Linera specific
130// functionalities accessed from the EVM.
131pub(crate) const PRECOMPILE_ADDRESS: Address = address!("000000000000000000000000000000000000000b");
132
133// This is the zero address used when no address can be obtained from `authenticated_owner`
134// and `authenticated_caller_id`. This scenario does not occur if an Address20 user calls or
135// if an EVM contract calls another EVM contract.
136pub(crate) const ZERO_ADDRESS: Address = address!("0000000000000000000000000000000000000000");
137
138// This is the address being used for service calls.
139pub(crate) const SERVICE_ADDRESS: Address = address!("0000000000000000000000000000000000002000");
140
141/// This is the address used for getting ethers and transfering them to.
142pub(crate) const FAUCET_ADDRESS: Address = address!("0000000000000000000000000000000000004000");
143pub(crate) const FAUCET_BALANCE: U256 = U256::from_limbs([
144    0xffffffffffffffff,
145    0xffffffffffffffff,
146    0xffffffffffffffff,
147    0x7fffffffffffffff,
148]);
149
150/// This is the selector of `execute_message` that should be called
151/// only from a submitted message
152pub(crate) const EXECUTE_MESSAGE_SELECTOR: &[u8] = &[173, 125, 234, 205];
153
154/// This is the selector of `process_streams` that should be called
155/// only from a submitted message
156pub(crate) const PROCESS_STREAMS_SELECTOR: &[u8] =
157    &<process_streamsCall as alloy_sol_types::SolCall>::SELECTOR;
158
159/// This is the selector of `summarize_events`, which is called by the system on a
160/// checkpoint and never from a submitted operation.
161pub(crate) const SUMMARIZE_EVENTS_SELECTOR: &[u8] =
162    &<summarize_eventsCall as alloy_sol_types::SolCall>::SELECTOR;
163
164/// This is the selector of `instantiate` that should be called
165/// only when creating a new instance of a shared contract
166pub(crate) const INSTANTIATE_SELECTOR: &[u8] = &[156, 163, 60, 158];
167
168pub(crate) fn forbid_execute_operation_origin(vec: &[u8]) -> Result<(), EvmExecutionError> {
169    ensure!(
170        vec != EXECUTE_MESSAGE_SELECTOR,
171        EvmExecutionError::IllegalOperationCall("function execute_message".to_string(),)
172    );
173    ensure!(
174        vec != PROCESS_STREAMS_SELECTOR,
175        EvmExecutionError::IllegalOperationCall("function process_streams".to_string(),)
176    );
177    ensure!(
178        vec != SUMMARIZE_EVENTS_SELECTOR,
179        EvmExecutionError::IllegalOperationCall("function summarize_events".to_string(),)
180    );
181    ensure!(
182        vec != INSTANTIATE_SELECTOR,
183        EvmExecutionError::IllegalOperationCall("function instantiate".to_string(),)
184    );
185    Ok(())
186}
187
188pub(crate) fn ensure_message_length(
189    actual_length: usize,
190    min_length: usize,
191) -> Result<(), EvmExecutionError> {
192    ensure!(
193        actual_length >= min_length,
194        EvmExecutionError::OperationIsTooShort
195    );
196    Ok(())
197}
198
199pub(crate) fn ensure_selector_presence(
200    module: &[u8],
201    selector: &[u8],
202    fct_name: &str,
203) -> Result<(), EvmExecutionError> {
204    ensure!(
205        has_selector(module, selector),
206        EvmExecutionError::MissingFunction(fct_name.to_string())
207    );
208    Ok(())
209}
210
211pub(crate) fn has_selector(module: &[u8], selector: &[u8]) -> bool {
212    let push4 = 0x63; // An EVM instruction
213    let mut vec = vec![push4];
214    vec.extend(selector);
215    module.windows(5).any(|window| window == vec)
216}
217
218pub(crate) fn get_revm_instantiation_bytes(value: Vec<u8>) -> Vec<u8> {
219    use alloy_primitives::Bytes;
220    use alloy_sol_types::{sol, SolCall};
221    sol! {
222        function instantiate(bytes value);
223    }
224    let bytes = Bytes::from(value);
225    let argument = instantiateCall { value: bytes };
226    argument.abi_encode()
227}
228
229pub(crate) fn get_revm_execute_message_bytes(value: Vec<u8>) -> Vec<u8> {
230    use alloy_primitives::Bytes;
231    use alloy_sol_types::{sol, SolCall};
232    sol! {
233        function execute_message(bytes value);
234    }
235    let value = Bytes::from(value);
236    let argument = execute_messageCall { value };
237    argument.abi_encode()
238}
239
240pub(crate) fn get_revm_process_streams_bytes(streams: Vec<StreamUpdate>) -> Vec<u8> {
241    use alloy_sol_types::SolCall;
242
243    let internal_streams = streams.into_iter().map(StreamUpdate::into).collect();
244
245    let fct_call = process_streamsCall { internal_streams };
246    fct_call.abi_encode()
247}
248
249pub(crate) fn get_revm_summarize_events_bytes(streams: Vec<StreamUpdate>) -> Vec<u8> {
250    use alloy_sol_types::SolCall;
251
252    let internal_streams = streams.into_iter().map(StreamUpdate::into).collect();
253
254    let fct_call = summarize_eventsCall { internal_streams };
255    fct_call.abi_encode()
256}
257
258#[cfg(test)]
259mod tests {
260    use revm_primitives::keccak256;
261
262    use crate::evm::inputs::{
263        process_streamsCall, EXECUTE_MESSAGE_SELECTOR, INSTANTIATE_SELECTOR,
264        PROCESS_STREAMS_SELECTOR,
265    };
266
267    // The function keccak256 is not const so we cannot build the execute_message
268    // selector directly.
269    #[test]
270    fn check_execute_message_selector() {
271        let selector = &keccak256("execute_message(bytes)".as_bytes())[..4];
272        assert_eq!(selector, EXECUTE_MESSAGE_SELECTOR);
273    }
274
275    #[test]
276    fn check_process_streams_selector() {
277        use alloy_sol_types::SolCall;
278        assert_eq!(
279            process_streamsCall::SIGNATURE,
280            "process_streams(((bytes32),((uint8,(bytes32)),(bytes)),uint32,uint32,uint32)[])"
281        );
282        assert_eq!(process_streamsCall::SELECTOR, PROCESS_STREAMS_SELECTOR);
283    }
284
285    #[test]
286    fn check_instantiate_selector() {
287        let selector = &keccak256("instantiate(bytes)".as_bytes())[..4];
288        assert_eq!(selector, INSTANTIATE_SELECTOR);
289    }
290}