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