1use core::ops::Range;
7use std::{collections::BTreeSet, convert::TryFrom};
8
9#[cfg(with_metrics)]
10use linera_base::prometheus_util::MeasureLatency as _;
11use linera_base::{
12 crypto::CryptoHash,
13 data_types::{Bytecode, Resources, SendMessageRequest, StreamUpdate},
14 ensure,
15 identifiers::{AccountOwner, ApplicationId, ChainId, StreamName},
16 vm::{EvmQuery, VmRuntime},
17};
18use revm::{primitives::Bytes, InspectCommitEvm, InspectEvm, Inspector};
19use revm_context::{
20 result::{ExecutionResult, Output, SuccessReason},
21 BlockEnv, Cfg, ContextTr, Evm, Journal, LocalContextTr, TxEnv,
22};
23use revm_database::WrapDatabaseRef;
24use revm_handler::{
25 instructions::EthInstructions, EthPrecompiles, MainnetContext, PrecompileProvider,
26};
27use revm_interpreter::{
28 CallInput, CallInputs, CallOutcome, CreateInputs, CreateOutcome, CreateScheme, Gas, InputsImpl,
29 InstructionResult, InterpreterResult,
30};
31use revm_primitives::{address, hardfork::SpecId, Address, Log, TxKind};
32use revm_state::EvmState;
33use serde::{Deserialize, Serialize};
34
35use crate::{
36 evm::database::{DatabaseRuntime, StorageStats, EVM_SERVICE_GAS_LIMIT},
37 BaseRuntime, ContractRuntime, ContractSyncRuntimeHandle, EvmExecutionError, EvmRuntime,
38 ExecutionError, ServiceRuntime, ServiceSyncRuntimeHandle, UserContract, UserContractInstance,
39 UserContractModule, UserService, UserServiceInstance, UserServiceModule,
40};
41
42const EXECUTE_MESSAGE_SELECTOR: &[u8] = &[173, 125, 234, 205];
45
46const PROCESS_STREAMS_SELECTOR: &[u8] = &[227, 9, 189, 153];
49
50const INSTANTIATE_SELECTOR: &[u8] = &[156, 163, 60, 158];
53
54fn forbid_execute_operation_origin(vec: &[u8]) -> Result<(), EvmExecutionError> {
55 if vec == EXECUTE_MESSAGE_SELECTOR {
56 return Err(EvmExecutionError::IllegalOperationCall(
57 "function execute_message".to_string(),
58 ));
59 }
60 if vec == PROCESS_STREAMS_SELECTOR {
61 return Err(EvmExecutionError::IllegalOperationCall(
62 "function process_streams".to_string(),
63 ));
64 }
65 if vec == INSTANTIATE_SELECTOR {
66 return Err(EvmExecutionError::IllegalOperationCall(
67 "function instantiate".to_string(),
68 ));
69 }
70 Ok(())
71}
72
73fn ensure_message_length(actual_length: usize, min_length: usize) -> Result<(), EvmExecutionError> {
74 ensure!(
75 actual_length >= min_length,
76 EvmExecutionError::OperationIsTooShort
77 );
78 Ok(())
79}
80
81fn ensure_selector_presence(
82 module: &[u8],
83 selector: &[u8],
84 fct_name: &str,
85) -> Result<(), EvmExecutionError> {
86 if !has_selector(module, selector) {
87 return Err(EvmExecutionError::MissingFunction(fct_name.to_string()));
88 }
89 Ok(())
90}
91
92const INTERPRETER_RESULT_SELECTOR: &[u8] = &[1, 2, 3, 4];
95
96#[cfg(test)]
97mod tests {
98 use revm_primitives::keccak256;
99
100 use crate::evm::revm::{
101 EXECUTE_MESSAGE_SELECTOR, INSTANTIATE_SELECTOR, PROCESS_STREAMS_SELECTOR,
102 };
103
104 #[test]
107 fn check_execute_message_selector() {
108 let selector = &keccak256("execute_message(bytes)".as_bytes())[..4];
109 assert_eq!(selector, EXECUTE_MESSAGE_SELECTOR);
110 }
111
112 #[test]
113 fn check_process_streams_selector() {
114 use alloy_sol_types::{sol, SolCall};
115 sol! {
116 struct InternalCryptoHash {
117 bytes32 value;
118 }
119
120 struct InternalApplicationId {
121 InternalCryptoHash application_description_hash;
122 }
123
124 struct InternalGenericApplicationId {
125 uint8 choice;
126 InternalApplicationId user;
127 }
128
129 struct InternalStreamName {
130 bytes stream_name;
131 }
132
133 struct InternalStreamId {
134 InternalGenericApplicationId application_id;
135 InternalStreamName stream_name;
136 }
137
138 struct InternalChainId {
139 InternalCryptoHash value;
140 }
141
142 struct InternalStreamUpdate {
143 InternalChainId chain_id;
144 InternalStreamId stream_id;
145 uint32 previous_index;
146 uint32 next_index;
147 }
148
149 function process_streams(InternalStreamUpdate[] internal_streams);
150 }
151 assert_eq!(
152 process_streamsCall::SIGNATURE,
153 "process_streams((((bytes32)),((uint8,((bytes32))),(bytes)),uint32,uint32)[])"
154 );
155 assert_eq!(process_streamsCall::SELECTOR, PROCESS_STREAMS_SELECTOR);
156 }
157
158 #[test]
159 fn check_instantiate_selector() {
160 let selector = &keccak256("instantiate(bytes)".as_bytes())[..4];
161 assert_eq!(selector, INSTANTIATE_SELECTOR);
162 }
163}
164
165fn has_selector(module: &[u8], selector: &[u8]) -> bool {
166 let push4 = 0x63; let mut vec = vec![push4];
168 vec.extend(selector);
169 module.windows(5).any(|window| window == vec)
170}
171
172#[cfg(with_metrics)]
173mod metrics {
174 use std::sync::LazyLock;
175
176 use linera_base::prometheus_util::{exponential_bucket_latencies, register_histogram_vec};
177 use prometheus::HistogramVec;
178
179 pub static CONTRACT_INSTANTIATION_LATENCY: LazyLock<HistogramVec> = LazyLock::new(|| {
180 register_histogram_vec(
181 "evm_contract_instantiation_latency",
182 "EVM contract instantiation latency",
183 &[],
184 exponential_bucket_latencies(1.0),
185 )
186 });
187
188 pub static SERVICE_INSTANTIATION_LATENCY: LazyLock<HistogramVec> = LazyLock::new(|| {
189 register_histogram_vec(
190 "evm_service_instantiation_latency",
191 "EVM service instantiation latency",
192 &[],
193 exponential_bucket_latencies(1.0),
194 )
195 });
196}
197
198fn get_revm_instantiation_bytes(value: Vec<u8>) -> Vec<u8> {
199 use alloy_primitives::Bytes;
200 use alloy_sol_types::{sol, SolCall};
201 sol! {
202 function instantiate(bytes value);
203 }
204 let bytes = Bytes::from(value);
205 let argument = instantiateCall { value: bytes };
206 argument.abi_encode()
207}
208
209fn get_revm_execute_message_bytes(value: Vec<u8>) -> Vec<u8> {
210 use alloy_primitives::Bytes;
211 use alloy_sol_types::{sol, SolCall};
212 sol! {
213 function execute_message(bytes value);
214 }
215 let value = Bytes::from(value);
216 let argument = execute_messageCall { value };
217 argument.abi_encode()
218}
219
220fn get_revm_process_streams_bytes(streams: Vec<StreamUpdate>) -> Vec<u8> {
221 use alloy_primitives::{Bytes, B256};
223 use alloy_sol_types::{sol, SolCall};
224 use linera_base::identifiers::{GenericApplicationId, StreamId};
225 sol! {
226 struct InternalCryptoHash {
227 bytes32 value;
228 }
229
230 struct InternalApplicationId {
231 InternalCryptoHash application_description_hash;
232 }
233
234 struct InternalGenericApplicationId {
235 uint8 choice;
236 InternalApplicationId user;
237 }
238
239 struct InternalStreamName {
240 bytes stream_name;
241 }
242
243 struct InternalStreamId {
244 InternalGenericApplicationId application_id;
245 InternalStreamName stream_name;
246 }
247
248 struct InternalChainId {
249 InternalCryptoHash value;
250 }
251
252 struct InternalStreamUpdate {
253 InternalChainId chain_id;
254 InternalStreamId stream_id;
255 uint32 previous_index;
256 uint32 next_index;
257 }
258
259 function process_streams(InternalStreamUpdate[] internal_streams);
260 }
261
262 fn crypto_hash_to_internal_crypto_hash(hash: CryptoHash) -> InternalCryptoHash {
263 let hash: [u64; 4] = <[u64; 4]>::from(hash);
264 let hash: [u8; 32] = linera_base::crypto::u64_array_to_be_bytes(hash);
265 let value: B256 = hash.into();
266 InternalCryptoHash { value }
267 }
268
269 fn chain_id_to_internal_chain_id(chain_id: ChainId) -> InternalChainId {
270 let value = crypto_hash_to_internal_crypto_hash(chain_id.0);
271 InternalChainId { value }
272 }
273
274 fn application_id_to_internal_application_id(
275 application_id: ApplicationId,
276 ) -> InternalApplicationId {
277 let application_description_hash =
278 crypto_hash_to_internal_crypto_hash(application_id.application_description_hash);
279 InternalApplicationId {
280 application_description_hash,
281 }
282 }
283
284 fn stream_name_to_internal_stream_name(stream_name: StreamName) -> InternalStreamName {
285 let stream_name = Bytes::from(stream_name.0);
286 InternalStreamName { stream_name }
287 }
288
289 fn generic_application_id_to_internal_generic_application_id(
290 generic_application_id: GenericApplicationId,
291 ) -> InternalGenericApplicationId {
292 match generic_application_id {
293 GenericApplicationId::System => {
294 let application_description_hash = InternalCryptoHash { value: B256::ZERO };
295 InternalGenericApplicationId {
296 choice: 0,
297 user: InternalApplicationId {
298 application_description_hash,
299 },
300 }
301 }
302 GenericApplicationId::User(application_id) => InternalGenericApplicationId {
303 choice: 1,
304 user: application_id_to_internal_application_id(application_id),
305 },
306 }
307 }
308
309 fn stream_id_to_internal_stream_id(stream_id: StreamId) -> InternalStreamId {
310 let application_id =
311 generic_application_id_to_internal_generic_application_id(stream_id.application_id);
312 let stream_name = stream_name_to_internal_stream_name(stream_id.stream_name);
313 InternalStreamId {
314 application_id,
315 stream_name,
316 }
317 }
318
319 fn stream_update_to_internal_stream_update(
320 stream_update: StreamUpdate,
321 ) -> InternalStreamUpdate {
322 let chain_id = chain_id_to_internal_chain_id(stream_update.chain_id);
323 let stream_id = stream_id_to_internal_stream_id(stream_update.stream_id);
324 InternalStreamUpdate {
325 chain_id,
326 stream_id,
327 previous_index: stream_update.previous_index,
328 next_index: stream_update.next_index,
329 }
330 }
331
332 let internal_streams = streams
333 .into_iter()
334 .map(stream_update_to_internal_stream_update)
335 .collect::<Vec<_>>();
336
337 let fct_call = process_streamsCall { internal_streams };
338 fct_call.abi_encode()
339}
340
341#[derive(Clone)]
342pub enum EvmContractModule {
343 #[cfg(with_revm)]
344 Revm { module: Vec<u8> },
345}
346
347impl EvmContractModule {
348 pub async fn new(
350 contract_bytecode: Bytecode,
351 runtime: EvmRuntime,
352 ) -> Result<Self, EvmExecutionError> {
353 match runtime {
354 #[cfg(with_revm)]
355 EvmRuntime::Revm => Self::from_revm(contract_bytecode).await,
356 }
357 }
358
359 #[cfg(with_fs)]
361 pub async fn from_file(
362 contract_bytecode_file: impl AsRef<std::path::Path>,
363 runtime: EvmRuntime,
364 ) -> Result<Self, EvmExecutionError> {
365 Self::new(
366 Bytecode::load_from_file(contract_bytecode_file)
367 .await
368 .map_err(anyhow::Error::from)
369 .map_err(EvmExecutionError::LoadContractModule)?,
370 runtime,
371 )
372 .await
373 }
374
375 pub async fn from_revm(contract_bytecode: Bytecode) -> Result<Self, EvmExecutionError> {
377 let module = contract_bytecode.bytes;
378 Ok(EvmContractModule::Revm { module })
379 }
380}
381
382impl UserContractModule for EvmContractModule {
383 fn instantiate(
384 &self,
385 runtime: ContractSyncRuntimeHandle,
386 ) -> Result<UserContractInstance, ExecutionError> {
387 #[cfg(with_metrics)]
388 let _instantiation_latency = metrics::CONTRACT_INSTANTIATION_LATENCY.measure_latency();
389
390 let instance: UserContractInstance = match self {
391 #[cfg(with_revm)]
392 EvmContractModule::Revm { module } => {
393 Box::new(RevmContractInstance::prepare(module.to_vec(), runtime))
394 }
395 };
396
397 Ok(instance)
398 }
399}
400
401#[derive(Clone)]
403pub enum EvmServiceModule {
404 #[cfg(with_revm)]
405 Revm { module: Vec<u8> },
406}
407
408impl EvmServiceModule {
409 pub async fn new(
411 service_bytecode: Bytecode,
412 runtime: EvmRuntime,
413 ) -> Result<Self, EvmExecutionError> {
414 match runtime {
415 #[cfg(with_revm)]
416 EvmRuntime::Revm => Self::from_revm(service_bytecode).await,
417 }
418 }
419
420 #[cfg(with_fs)]
422 pub async fn from_file(
423 service_bytecode_file: impl AsRef<std::path::Path>,
424 runtime: EvmRuntime,
425 ) -> Result<Self, EvmExecutionError> {
426 Self::new(
427 Bytecode::load_from_file(service_bytecode_file)
428 .await
429 .map_err(anyhow::Error::from)
430 .map_err(EvmExecutionError::LoadServiceModule)?,
431 runtime,
432 )
433 .await
434 }
435
436 pub async fn from_revm(contract_bytecode: Bytecode) -> Result<Self, EvmExecutionError> {
438 let module = contract_bytecode.bytes;
439 Ok(EvmServiceModule::Revm { module })
440 }
441}
442
443impl UserServiceModule for EvmServiceModule {
444 fn instantiate(
445 &self,
446 runtime: ServiceSyncRuntimeHandle,
447 ) -> Result<UserServiceInstance, ExecutionError> {
448 #[cfg(with_metrics)]
449 let _instantiation_latency = metrics::SERVICE_INSTANTIATION_LATENCY.measure_latency();
450
451 let instance: UserServiceInstance = match self {
452 #[cfg(with_revm)]
453 EvmServiceModule::Revm { module } => {
454 Box::new(RevmServiceInstance::prepare(module.to_vec(), runtime))
455 }
456 };
457
458 Ok(instance)
459 }
460}
461
462type Ctx<'a, Runtime> = MainnetContext<WrapDatabaseRef<&'a mut DatabaseRuntime<Runtime>>>;
463
464const PRECOMPILE_ADDRESS: Address = address!("000000000000000000000000000000000000000b");
467
468const ZERO_ADDRESS: Address = address!("0000000000000000000000000000000000000000");
472
473const SERVICE_ADDRESS: Address = address!("0000000000000000000000000000000000002000");
475
476fn address_to_user_application_id(address: Address) -> ApplicationId {
477 let mut vec = vec![0_u8; 32];
478 vec[..20].copy_from_slice(address.as_ref());
479 ApplicationId::new(CryptoHash::try_from(&vec as &[u8]).unwrap())
480}
481
482#[derive(Debug, Serialize, Deserialize)]
484enum BaseRuntimePrecompile {
485 ChainId,
487 ApplicationCreatorChainId,
489 ChainOwnership,
491 ReadDataBlob { hash: CryptoHash },
493 AssertDataBlobExists { hash: CryptoHash },
495}
496
497#[derive(Debug, Serialize, Deserialize)]
499enum ContractRuntimePrecompile {
500 TryCallApplication {
502 target: ApplicationId,
503 argument: Vec<u8>,
504 },
505 ValidationRound,
507 SendMessage {
509 destination: ChainId,
510 message: Vec<u8>,
511 },
512 MessageId,
514 MessageIsBouncing,
516 Emit {
518 stream_name: StreamName,
519 value: Vec<u8>,
520 },
521 ReadEvent {
523 chain_id: ChainId,
524 stream_name: StreamName,
525 index: u32,
526 },
527 SubscribeToEvents {
529 chain_id: ChainId,
530 application_id: ApplicationId,
531 stream_name: StreamName,
532 },
533 UnsubscribeFromEvents {
535 chain_id: ChainId,
536 application_id: ApplicationId,
537 stream_name: StreamName,
538 },
539}
540
541#[derive(Debug, Serialize, Deserialize)]
543enum ServiceRuntimePrecompile {
544 TryQueryApplication {
546 target: ApplicationId,
547 argument: Vec<u8>,
548 },
549}
550
551#[derive(Debug, Serialize, Deserialize)]
553enum RuntimePrecompile {
554 Base(BaseRuntimePrecompile),
555 Contract(ContractRuntimePrecompile),
556 Service(ServiceRuntimePrecompile),
557}
558
559fn get_precompile_output(
560 output: Vec<u8>,
561 gas_limit: u64,
562) -> Result<Option<InterpreterResult>, String> {
563 let output = Bytes::from(output);
568 let result = InstructionResult::default();
569 let gas = Gas::new(gas_limit);
570 Ok(Some(InterpreterResult {
571 result,
572 output,
573 gas,
574 }))
575}
576
577fn get_precompile_argument<Ctx: ContextTr>(context: &mut Ctx, input: &CallInput) -> Vec<u8> {
578 let mut argument = Vec::new();
579 get_argument(context, &mut argument, input);
580 argument
581}
582
583fn base_runtime_call<Runtime: BaseRuntime>(
584 request: BaseRuntimePrecompile,
585 context: &mut Ctx<'_, Runtime>,
586) -> Result<Vec<u8>, ExecutionError> {
587 let mut runtime = context
588 .db()
589 .0
590 .runtime
591 .lock()
592 .expect("The lock should be possible");
593 match request {
594 BaseRuntimePrecompile::ChainId => {
595 let chain_id = runtime.chain_id()?;
596 Ok(bcs::to_bytes(&chain_id)?)
597 }
598 BaseRuntimePrecompile::ApplicationCreatorChainId => {
599 let chain_id = runtime.application_creator_chain_id()?;
600 Ok(bcs::to_bytes(&chain_id)?)
601 }
602 BaseRuntimePrecompile::ChainOwnership => {
603 let chain_ownership = runtime.chain_ownership()?;
604 Ok(bcs::to_bytes(&chain_ownership)?)
605 }
606 BaseRuntimePrecompile::ReadDataBlob { hash } => runtime.read_data_blob(&hash),
607 BaseRuntimePrecompile::AssertDataBlobExists { hash } => {
608 runtime.assert_data_blob_exists(&hash)?;
609 Ok(Vec::new())
610 }
611 }
612}
613
614fn precompile_addresses() -> BTreeSet<Address> {
615 let mut addresses = BTreeSet::new();
616 for address in EthPrecompiles::default().warm_addresses() {
617 addresses.insert(address);
618 }
619 addresses.insert(PRECOMPILE_ADDRESS);
620 addresses
621}
622
623#[derive(Debug, Default)]
624struct ContractPrecompile {
625 inner: EthPrecompiles,
626}
627
628impl<'a, Runtime: ContractRuntime> PrecompileProvider<Ctx<'a, Runtime>> for ContractPrecompile {
629 type Output = InterpreterResult;
630
631 fn set_spec(&mut self, spec: <<Ctx<'a, Runtime> as ContextTr>::Cfg as Cfg>::Spec) -> bool {
632 <EthPrecompiles as PrecompileProvider<Ctx<'a, Runtime>>>::set_spec(&mut self.inner, spec)
633 }
634
635 fn run(
636 &mut self,
637 context: &mut Ctx<'a, Runtime>,
638 address: &Address,
639 inputs: &InputsImpl,
640 is_static: bool,
641 gas_limit: u64,
642 ) -> Result<Option<InterpreterResult>, String> {
643 if address == &PRECOMPILE_ADDRESS {
644 let input = get_precompile_argument(context, &inputs.input);
645 let output = Self::call_or_fail(&input, context)
646 .map_err(|error| format!("ContractPrecompile error: {error}"))?;
647 return get_precompile_output(output, gas_limit);
648 }
649 self.inner
650 .run(context, address, inputs, is_static, gas_limit)
651 }
652
653 fn warm_addresses(&self) -> Box<impl Iterator<Item = Address>> {
654 let mut addresses = self.inner.warm_addresses().collect::<Vec<Address>>();
655 addresses.push(PRECOMPILE_ADDRESS);
656 Box::new(addresses.into_iter())
657 }
658
659 fn contains(&self, address: &Address) -> bool {
660 address == &PRECOMPILE_ADDRESS || self.inner.contains(address)
661 }
662}
663
664impl<'a> ContractPrecompile {
665 fn contract_runtime_call<Runtime: ContractRuntime>(
666 request: ContractRuntimePrecompile,
667 context: &mut Ctx<'a, Runtime>,
668 ) -> Result<Vec<u8>, ExecutionError> {
669 let mut runtime = context
670 .db()
671 .0
672 .runtime
673 .lock()
674 .expect("The lock should be possible");
675 match request {
676 ContractRuntimePrecompile::TryCallApplication { target, argument } => {
677 let authenticated = true;
678 runtime.try_call_application(authenticated, target, argument)
679 }
680 ContractRuntimePrecompile::ValidationRound => {
681 let value = runtime.validation_round()?;
682 Ok(bcs::to_bytes(&value)?)
683 }
684 ContractRuntimePrecompile::SendMessage {
685 destination,
686 message,
687 } => {
688 let authenticated = true;
689 let is_tracked = true;
690 let grant = Resources::default();
691 let send_message_request = SendMessageRequest {
692 destination,
693 authenticated,
694 is_tracked,
695 grant,
696 message,
697 };
698 runtime.send_message(send_message_request)?;
699 Ok(vec![])
700 }
701 ContractRuntimePrecompile::MessageId => {
702 let message_id = runtime.message_id()?;
703 Ok(bcs::to_bytes(&message_id)?)
704 }
705 ContractRuntimePrecompile::MessageIsBouncing => {
706 let result = runtime.message_is_bouncing()?;
707 Ok(bcs::to_bytes(&result)?)
708 }
709 ContractRuntimePrecompile::Emit { stream_name, value } => {
710 let result = runtime.emit(stream_name, value)?;
711 Ok(bcs::to_bytes(&result)?)
712 }
713 ContractRuntimePrecompile::ReadEvent {
714 chain_id,
715 stream_name,
716 index,
717 } => runtime.read_event(chain_id, stream_name, index),
718 ContractRuntimePrecompile::SubscribeToEvents {
719 chain_id,
720 application_id,
721 stream_name,
722 } => {
723 runtime.subscribe_to_events(chain_id, application_id, stream_name)?;
724 Ok(vec![])
725 }
726 ContractRuntimePrecompile::UnsubscribeFromEvents {
727 chain_id,
728 application_id,
729 stream_name,
730 } => {
731 runtime.unsubscribe_from_events(chain_id, application_id, stream_name)?;
732 Ok(vec![])
733 }
734 }
735 }
736
737 fn call_or_fail<Runtime: ContractRuntime>(
738 input: &[u8],
739 context: &mut Ctx<'a, Runtime>,
740 ) -> Result<Vec<u8>, ExecutionError> {
741 match bcs::from_bytes(input)? {
742 RuntimePrecompile::Base(base_tag) => base_runtime_call(base_tag, context),
743 RuntimePrecompile::Contract(contract_tag) => {
744 Self::contract_runtime_call(contract_tag, context)
745 }
746 RuntimePrecompile::Service(_) => Err(EvmExecutionError::PrecompileError(
747 "Service tags are not available in GeneralContractCall".to_string(),
748 )
749 .into()),
750 }
751 }
752}
753
754#[derive(Debug, Default)]
755struct ServicePrecompile {
756 inner: EthPrecompiles,
757}
758
759impl<'a> ServicePrecompile {
760 fn service_runtime_call<Runtime: ServiceRuntime>(
761 request: ServiceRuntimePrecompile,
762 context: &mut Ctx<'a, Runtime>,
763 ) -> Result<Vec<u8>, ExecutionError> {
764 let mut runtime = context
765 .db()
766 .0
767 .runtime
768 .lock()
769 .expect("The lock should be possible");
770 match request {
771 ServiceRuntimePrecompile::TryQueryApplication { target, argument } => {
772 runtime.try_query_application(target, argument)
773 }
774 }
775 }
776
777 fn call_or_fail<Runtime: ServiceRuntime>(
778 input: &[u8],
779 context: &mut Ctx<'a, Runtime>,
780 ) -> Result<Vec<u8>, ExecutionError> {
781 match bcs::from_bytes(input)? {
782 RuntimePrecompile::Base(base_tag) => base_runtime_call(base_tag, context),
783 RuntimePrecompile::Contract(_) => Err(EvmExecutionError::PrecompileError(
784 "Contract calls are not available in GeneralServiceCall".to_string(),
785 )
786 .into()),
787 RuntimePrecompile::Service(service_tag) => {
788 Self::service_runtime_call(service_tag, context)
789 }
790 }
791 }
792}
793
794impl<'a, Runtime: ServiceRuntime> PrecompileProvider<Ctx<'a, Runtime>> for ServicePrecompile {
795 type Output = InterpreterResult;
796
797 fn set_spec(&mut self, spec: <<Ctx<'a, Runtime> as ContextTr>::Cfg as Cfg>::Spec) -> bool {
798 <EthPrecompiles as PrecompileProvider<Ctx<'a, Runtime>>>::set_spec(&mut self.inner, spec)
799 }
800
801 fn run(
802 &mut self,
803 context: &mut Ctx<'a, Runtime>,
804 address: &Address,
805 inputs: &InputsImpl,
806 is_static: bool,
807 gas_limit: u64,
808 ) -> Result<Option<InterpreterResult>, String> {
809 if address == &PRECOMPILE_ADDRESS {
810 let input = get_precompile_argument(context, &inputs.input);
811 let output = Self::call_or_fail(&input, context)
812 .map_err(|error| format!("ServicePrecompile error: {error}"))?;
813 return get_precompile_output(output, gas_limit);
814 }
815 self.inner
816 .run(context, address, inputs, is_static, gas_limit)
817 }
818
819 fn warm_addresses(&self) -> Box<impl Iterator<Item = Address>> {
820 let mut addresses = self.inner.warm_addresses().collect::<Vec<Address>>();
821 addresses.push(PRECOMPILE_ADDRESS);
822 Box::new(addresses.into_iter())
823 }
824
825 fn contains(&self, address: &Address) -> bool {
826 address == &PRECOMPILE_ADDRESS || self.inner.contains(address)
827 }
828}
829
830fn map_result_call_outcome(
831 result: Result<Option<CallOutcome>, ExecutionError>,
832) -> Option<CallOutcome> {
833 match result {
834 Err(_error) => {
835 let result = InstructionResult::Revert;
839 let output = Bytes::default();
840 let gas = Gas::default();
841 let result = InterpreterResult {
842 result,
843 output,
844 gas,
845 };
846 let memory_offset = Range::default();
847 Some(CallOutcome {
848 result,
849 memory_offset,
850 })
851 }
852 Ok(result) => result,
853 }
854}
855
856fn get_interpreter_result(
857 result: &[u8],
858 inputs: &mut CallInputs,
859) -> Result<InterpreterResult, ExecutionError> {
860 let mut result = bcs::from_bytes::<InterpreterResult>(result)?;
861 result.gas = Gas::new(inputs.gas_limit);
864 Ok(result)
865}
866
867struct CallInterceptorContract<Runtime> {
868 db: DatabaseRuntime<Runtime>,
869 contract_address: Address,
871 precompile_addresses: BTreeSet<Address>,
872}
873
874impl<Runtime> Clone for CallInterceptorContract<Runtime> {
875 fn clone(&self) -> Self {
876 Self {
877 db: self.db.clone(),
878 contract_address: self.contract_address,
879 precompile_addresses: self.precompile_addresses.clone(),
880 }
881 }
882}
883
884fn get_argument<Ctx: ContextTr>(context: &mut Ctx, argument: &mut Vec<u8>, input: &CallInput) {
885 match input {
886 CallInput::Bytes(bytes) => {
887 argument.extend(bytes.to_vec());
888 }
889 CallInput::SharedBuffer(range) => {
890 if let Some(slice) = context.local().shared_memory_buffer_slice(range.clone()) {
891 argument.extend(&*slice);
892 }
893 }
894 };
895}
896
897fn get_call_argument<Ctx: ContextTr>(context: &mut Ctx, inputs: &CallInputs) -> Vec<u8> {
898 let mut argument = INTERPRETER_RESULT_SELECTOR.to_vec();
899 get_argument(context, &mut argument, &inputs.input);
900 argument
901}
902
903impl<'a, Runtime: ContractRuntime> Inspector<Ctx<'a, Runtime>>
904 for CallInterceptorContract<Runtime>
905{
906 fn create(
907 &mut self,
908 _context: &mut Ctx<'a, Runtime>,
909 inputs: &mut CreateInputs,
910 ) -> Option<CreateOutcome> {
911 inputs.scheme = CreateScheme::Custom {
912 address: self.contract_address,
913 };
914 None
915 }
916
917 fn call(
918 &mut self,
919 context: &mut Ctx<'a, Runtime>,
920 inputs: &mut CallInputs,
921 ) -> Option<CallOutcome> {
922 let result = self.call_or_fail(context, inputs);
923 map_result_call_outcome(result)
924 }
925}
926
927impl<Runtime: ContractRuntime> CallInterceptorContract<Runtime> {
928 fn call_or_fail(
929 &mut self,
930 context: &mut Ctx<'_, Runtime>,
931 inputs: &mut CallInputs,
932 ) -> Result<Option<CallOutcome>, ExecutionError> {
933 if self.precompile_addresses.contains(&inputs.target_address)
939 || inputs.target_address == self.contract_address
940 {
941 return Ok(None);
944 }
945 let target = address_to_user_application_id(inputs.target_address);
947 let argument = get_call_argument(context, inputs);
948 let authenticated = true;
949 let result = {
950 let mut runtime = self.db.runtime.lock().expect("The lock should be possible");
951 runtime.try_call_application(authenticated, target, argument)?
952 };
953 let call_outcome = CallOutcome {
954 result: get_interpreter_result(&result, inputs)?,
955 memory_offset: inputs.return_memory_offset.clone(),
956 };
957 Ok(Some(call_outcome))
958 }
959}
960
961struct CallInterceptorService<Runtime> {
962 db: DatabaseRuntime<Runtime>,
963 contract_address: Address,
965 precompile_addresses: BTreeSet<Address>,
966}
967
968impl<Runtime> Clone for CallInterceptorService<Runtime> {
969 fn clone(&self) -> Self {
970 Self {
971 db: self.db.clone(),
972 contract_address: self.contract_address,
973 precompile_addresses: self.precompile_addresses.clone(),
974 }
975 }
976}
977
978impl<'a, Runtime: ServiceRuntime> Inspector<Ctx<'a, Runtime>> for CallInterceptorService<Runtime> {
979 fn create(
980 &mut self,
981 _context: &mut Ctx<'a, Runtime>,
982 inputs: &mut CreateInputs,
983 ) -> Option<CreateOutcome> {
984 inputs.scheme = CreateScheme::Custom {
985 address: self.contract_address,
986 };
987 None
988 }
989
990 fn call(
991 &mut self,
992 context: &mut Ctx<'a, Runtime>,
993 inputs: &mut CallInputs,
994 ) -> Option<CallOutcome> {
995 let result = self.call_or_fail(context, inputs);
996 map_result_call_outcome(result)
997 }
998}
999
1000impl<Runtime: ServiceRuntime> CallInterceptorService<Runtime> {
1001 fn call_or_fail(
1002 &mut self,
1003 context: &mut Ctx<'_, Runtime>,
1004 inputs: &mut CallInputs,
1005 ) -> Result<Option<CallOutcome>, ExecutionError> {
1006 if self.precompile_addresses.contains(&inputs.target_address)
1012 || inputs.target_address == self.contract_address
1013 {
1014 return Ok(None);
1017 }
1018 let target = address_to_user_application_id(inputs.target_address);
1020 let argument = get_call_argument(context, inputs);
1021 let result = {
1022 let evm_query = EvmQuery::Query(argument);
1023 let evm_query = serde_json::to_vec(&evm_query)?;
1024 let mut runtime = self.db.runtime.lock().expect("The lock should be possible");
1025 runtime.try_query_application(target, evm_query)?
1026 };
1027 let call_outcome = CallOutcome {
1028 result: get_interpreter_result(&result, inputs)?,
1029 memory_offset: inputs.return_memory_offset.clone(),
1030 };
1031 Ok(Some(call_outcome))
1032 }
1033}
1034
1035pub struct RevmContractInstance<Runtime> {
1036 module: Vec<u8>,
1037 db: DatabaseRuntime<Runtime>,
1038}
1039
1040enum EvmTxKind {
1041 Create,
1042 Call,
1043}
1044
1045#[derive(Debug)]
1046struct ExecutionResultSuccess {
1047 reason: SuccessReason,
1048 gas_final: u64,
1049 logs: Vec<Log>,
1050 output: Output,
1051}
1052
1053impl ExecutionResultSuccess {
1054 fn interpreter_result_and_logs(self) -> Result<(u64, Vec<u8>, Vec<Log>), ExecutionError> {
1055 let result: InstructionResult = self.reason.into();
1056 let Output::Call(output) = self.output else {
1057 unreachable!("The output should have been created from a EvmTxKind::Call");
1058 };
1059 let gas = Gas::new(0);
1060 let result = InterpreterResult {
1061 result,
1062 output,
1063 gas,
1064 };
1065 let result = bcs::to_bytes(&result)?;
1066 Ok((self.gas_final, result, self.logs))
1067 }
1068
1069 fn output_and_logs(self) -> (u64, Vec<u8>, Vec<Log>) {
1070 let Output::Call(output) = self.output else {
1071 unreachable!("The output should have been created from a EvmTxKind::Call");
1072 };
1073 let output = output.as_ref().to_vec();
1074 (self.gas_final, output, self.logs)
1075 }
1076
1077 fn check_contract_initialization(&self, expected_address: Address) -> Result<(), String> {
1079 let Output::Create(_, contract_address) = self.output else {
1081 return Err("Input should be ExmTxKind::Create".to_string());
1082 };
1083 let contract_address = contract_address.ok_or("Deployment failed")?;
1085 if contract_address == expected_address {
1087 Ok(())
1088 } else {
1089 Err("Contract address is not the same as ApplicationId".to_string())
1090 }
1091 }
1092}
1093
1094impl<Runtime> UserContract for RevmContractInstance<Runtime>
1095where
1096 Runtime: ContractRuntime,
1097{
1098 fn instantiate(&mut self, argument: Vec<u8>) -> Result<(), ExecutionError> {
1099 self.db.set_contract_address()?;
1100 let caller = self.get_msg_address()?;
1101 self.initialize_contract(caller)?;
1102 if has_selector(&self.module, INSTANTIATE_SELECTOR) {
1103 let instantiation_argument = serde_json::from_slice::<Vec<u8>>(&argument)?;
1104 let argument = get_revm_instantiation_bytes(instantiation_argument);
1105 let result = self.transact_commit(EvmTxKind::Call, argument, caller)?;
1106 self.write_logs(result.logs, "instantiate")?;
1107 }
1108 Ok(())
1109 }
1110
1111 fn execute_operation(&mut self, operation: Vec<u8>) -> Result<Vec<u8>, ExecutionError> {
1112 self.db.set_contract_address()?;
1113 ensure_message_length(operation.len(), 4)?;
1114 let caller = self.get_msg_address()?;
1115 let (gas_final, output, logs) = if &operation[..4] == INTERPRETER_RESULT_SELECTOR {
1116 ensure_message_length(operation.len(), 8)?;
1117 forbid_execute_operation_origin(&operation[4..8])?;
1118 let result = self.init_transact_commit(operation[4..].to_vec(), caller)?;
1119 result.interpreter_result_and_logs()?
1120 } else {
1121 ensure_message_length(operation.len(), 4)?;
1122 forbid_execute_operation_origin(&operation[..4])?;
1123 let result = self.init_transact_commit(operation, caller)?;
1124 result.output_and_logs()
1125 };
1126 self.consume_fuel(gas_final)?;
1127 self.write_logs(logs, "operation")?;
1128 Ok(output)
1129 }
1130
1131 fn execute_message(&mut self, message: Vec<u8>) -> Result<(), ExecutionError> {
1132 self.db.set_contract_address()?;
1133 ensure_selector_presence(
1134 &self.module,
1135 EXECUTE_MESSAGE_SELECTOR,
1136 "function execute_message(bytes)",
1137 )?;
1138 let operation = get_revm_execute_message_bytes(message);
1139 let caller = self.get_msg_address()?;
1140 self.execute_no_return_operation(operation, "message", caller)
1141 }
1142
1143 fn process_streams(&mut self, streams: Vec<StreamUpdate>) -> Result<(), ExecutionError> {
1144 self.db.set_contract_address()?;
1145 let operation = get_revm_process_streams_bytes(streams);
1146 ensure_selector_presence(
1147 &self.module,
1148 PROCESS_STREAMS_SELECTOR,
1149 "function process_streams(LineraTypes.StreamUpdate[] memory streams)",
1150 )?;
1151 let caller = Address::ZERO;
1153 self.execute_no_return_operation(operation, "process_streams", caller)
1154 }
1155
1156 fn finalize(&mut self) -> Result<(), ExecutionError> {
1157 Ok(())
1158 }
1159}
1160
1161fn process_execution_result(
1162 storage_stats: StorageStats,
1163 result: ExecutionResult,
1164) -> Result<ExecutionResultSuccess, EvmExecutionError> {
1165 match result {
1166 ExecutionResult::Success {
1167 reason,
1168 gas_used,
1169 gas_refunded,
1170 logs,
1171 output,
1172 } => {
1173 let mut gas_final = gas_used;
1174 gas_final -= storage_stats.storage_costs();
1175 assert_eq!(gas_refunded, storage_stats.storage_refund());
1176 if !matches!(reason, SuccessReason::Return) {
1177 Err(EvmExecutionError::NoReturnInterpreter {
1178 reason,
1179 gas_used,
1180 gas_refunded,
1181 logs,
1182 output,
1183 })
1184 } else {
1185 Ok(ExecutionResultSuccess {
1186 reason,
1187 gas_final,
1188 logs,
1189 output,
1190 })
1191 }
1192 }
1193 ExecutionResult::Revert { gas_used, output } => {
1194 Err(EvmExecutionError::Revert { gas_used, output })
1195 }
1196 ExecutionResult::Halt { gas_used, reason } => {
1197 Err(EvmExecutionError::Halt { gas_used, reason })
1198 }
1199 }
1200}
1201
1202impl<Runtime> RevmContractInstance<Runtime>
1203where
1204 Runtime: ContractRuntime,
1205{
1206 pub fn prepare(module: Vec<u8>, runtime: Runtime) -> Self {
1207 let db = DatabaseRuntime::new(runtime);
1208 Self { module, db }
1209 }
1210
1211 fn execute_no_return_operation(
1212 &mut self,
1213 operation: Vec<u8>,
1214 origin: &str,
1215 caller: Address,
1216 ) -> Result<(), ExecutionError> {
1217 let result = self.init_transact_commit(operation, caller)?;
1218 let (gas_final, output, logs) = result.output_and_logs();
1219 self.consume_fuel(gas_final)?;
1220 self.write_logs(logs, origin)?;
1221 assert_eq!(output.len(), 0);
1222 Ok(())
1223 }
1224
1225 fn init_transact_commit(
1227 &mut self,
1228 vec: Vec<u8>,
1229 caller: Address,
1230 ) -> Result<ExecutionResultSuccess, ExecutionError> {
1231 if !self.db.is_initialized()? {
1235 self.initialize_contract(caller)?;
1236 }
1237 self.transact_commit(EvmTxKind::Call, vec, caller)
1238 }
1239
1240 fn initialize_contract(&mut self, caller: Address) -> Result<(), ExecutionError> {
1242 let mut vec_init = self.module.clone();
1243 let constructor_argument = self.db.constructor_argument()?;
1244 vec_init.extend_from_slice(&constructor_argument);
1245 let result = self.transact_commit(EvmTxKind::Create, vec_init, caller)?;
1246 result
1247 .check_contract_initialization(self.db.contract_address)
1248 .map_err(EvmExecutionError::IncorrectContractCreation)?;
1249 self.write_logs(result.logs, "deploy")
1250 }
1251
1252 fn get_msg_address(&self) -> Result<Address, ExecutionError> {
1265 let mut runtime = self.db.runtime.lock().expect("The lock should be possible");
1266 let application_id = runtime.authenticated_caller_id()?;
1267 if let Some(application_id) = application_id {
1268 return Ok(if application_id.is_evm() {
1269 application_id.evm_address()
1270 } else {
1271 Address::ZERO
1272 });
1273 };
1274 let account_owner = runtime.authenticated_signer()?;
1275 if let Some(AccountOwner::Address20(address)) = account_owner {
1276 return Ok(Address::from(address));
1277 };
1278 Ok(ZERO_ADDRESS)
1279 }
1280
1281 fn transact_commit(
1282 &mut self,
1283 ch: EvmTxKind,
1284 input: Vec<u8>,
1285 caller: Address,
1286 ) -> Result<ExecutionResultSuccess, ExecutionError> {
1287 let data = Bytes::from(input);
1288 let kind = match ch {
1289 EvmTxKind::Create => TxKind::Create,
1290 EvmTxKind::Call => TxKind::Call(self.db.contract_address),
1291 };
1292 let inspector = CallInterceptorContract {
1293 db: self.db.clone(),
1294 contract_address: self.db.contract_address,
1295 precompile_addresses: precompile_addresses(),
1296 };
1297 let block_env = self.db.get_contract_block_env()?;
1298 let gas_limit = {
1299 let mut runtime = self.db.runtime.lock().expect("The lock should be possible");
1300 runtime.remaining_fuel(VmRuntime::Evm)?
1301 };
1302 let nonce = self.db.get_nonce(&caller)?;
1303 let result = {
1304 let ctx: revm_context::Context<
1305 BlockEnv,
1306 _,
1307 _,
1308 _,
1309 Journal<WrapDatabaseRef<&mut DatabaseRuntime<Runtime>>>,
1310 (),
1311 > = revm_context::Context::<BlockEnv, _, _, _, _, _>::new(
1312 WrapDatabaseRef(&mut self.db),
1313 SpecId::PRAGUE,
1314 )
1315 .with_block(block_env);
1316 let instructions = EthInstructions::new_mainnet();
1317 let mut evm = Evm::new_with_inspector(
1318 ctx,
1319 inspector.clone(),
1320 instructions,
1321 ContractPrecompile::default(),
1322 );
1323 evm.inspect_commit(
1324 TxEnv {
1325 kind,
1326 data,
1327 nonce,
1328 gas_limit,
1329 caller,
1330 ..TxEnv::default()
1331 },
1332 inspector,
1333 )
1334 .map_err(|error| {
1335 let error = format!("{:?}", error);
1336 EvmExecutionError::TransactCommitError(error)
1337 })
1338 }?;
1339 let storage_stats = self.db.take_storage_stats();
1340 self.db.commit_changes()?;
1341 Ok(process_execution_result(storage_stats, result)?)
1342 }
1343
1344 fn consume_fuel(&mut self, gas_final: u64) -> Result<(), ExecutionError> {
1345 let mut runtime = self.db.runtime.lock().expect("The lock should be possible");
1346 runtime.consume_fuel(gas_final, VmRuntime::Evm)
1347 }
1348
1349 fn write_logs(&mut self, logs: Vec<Log>, origin: &str) -> Result<(), ExecutionError> {
1350 if !logs.is_empty() {
1352 let mut runtime = self.db.runtime.lock().expect("The lock should be possible");
1353 let block_height = runtime.block_height()?;
1354 let stream_name = bcs::to_bytes("ethereum_event")?;
1355 let stream_name = StreamName(stream_name);
1356 for log in &logs {
1357 let value = bcs::to_bytes(&(origin, block_height.0, log))?;
1358 runtime.emit(stream_name.clone(), value)?;
1359 }
1360 }
1361 Ok(())
1362 }
1363}
1364
1365pub struct RevmServiceInstance<Runtime> {
1366 module: Vec<u8>,
1367 db: DatabaseRuntime<Runtime>,
1368}
1369
1370impl<Runtime> RevmServiceInstance<Runtime>
1371where
1372 Runtime: ServiceRuntime,
1373{
1374 pub fn prepare(module: Vec<u8>, runtime: Runtime) -> Self {
1375 let db = DatabaseRuntime::new(runtime);
1376 Self { module, db }
1377 }
1378}
1379
1380impl<Runtime> UserService for RevmServiceInstance<Runtime>
1381where
1382 Runtime: ServiceRuntime,
1383{
1384 fn handle_query(&mut self, argument: Vec<u8>) -> Result<Vec<u8>, ExecutionError> {
1385 self.db.set_contract_address()?;
1386 let evm_query = serde_json::from_slice(&argument)?;
1387 let query = match evm_query {
1388 EvmQuery::Query(vec) => vec,
1389 EvmQuery::Mutation(operation) => {
1390 let mut runtime = self.db.runtime.lock().expect("The lock should be possible");
1391 runtime.schedule_operation(operation)?;
1392 return Ok(Vec::new());
1393 }
1394 };
1395
1396 ensure_message_length(query.len(), 4)?;
1397 let answer = if &query[..4] == INTERPRETER_RESULT_SELECTOR {
1401 let result = self.init_transact(query[4..].to_vec())?;
1402 let (_gas_final, answer, _logs) = result.interpreter_result_and_logs()?;
1403 answer
1404 } else {
1405 let result = self.init_transact(query)?;
1406 let (_gas_final, output, _logs) = result.output_and_logs();
1407 serde_json::to_vec(&output)?
1408 };
1409 Ok(answer)
1410 }
1411}
1412
1413impl<Runtime> RevmServiceInstance<Runtime>
1414where
1415 Runtime: ServiceRuntime,
1416{
1417 fn init_transact(&mut self, vec: Vec<u8>) -> Result<ExecutionResultSuccess, ExecutionError> {
1418 if !self.db.is_initialized()? {
1422 let changes = {
1423 let mut vec_init = self.module.clone();
1424 let constructor_argument = self.db.constructor_argument()?;
1425 vec_init.extend_from_slice(&constructor_argument);
1426 let (result, changes) = self.transact(TxKind::Create, vec_init)?;
1427 result
1428 .check_contract_initialization(self.db.contract_address)
1429 .map_err(EvmExecutionError::IncorrectContractCreation)?;
1430 changes
1431 };
1432 self.db.changes = changes;
1433 }
1434 ensure_message_length(vec.len(), 4)?;
1435 forbid_execute_operation_origin(&vec[..4])?;
1436 let kind = TxKind::Call(self.db.contract_address);
1437 let (execution_result, _) = self.transact(kind, vec)?;
1438 Ok(execution_result)
1439 }
1440
1441 fn transact(
1442 &mut self,
1443 kind: TxKind,
1444 input: Vec<u8>,
1445 ) -> Result<(ExecutionResultSuccess, EvmState), ExecutionError> {
1446 let data = Bytes::from(input);
1447 let block_env = self.db.get_service_block_env()?;
1448 let inspector = CallInterceptorService {
1449 db: self.db.clone(),
1450 contract_address: self.db.contract_address,
1451 precompile_addresses: precompile_addresses(),
1452 };
1453 let caller = SERVICE_ADDRESS;
1454 let nonce = self.db.get_nonce(&caller)?;
1455 let result_state = {
1456 let ctx: revm_context::Context<
1457 BlockEnv,
1458 _,
1459 _,
1460 _,
1461 Journal<WrapDatabaseRef<&mut DatabaseRuntime<Runtime>>>,
1462 (),
1463 > = revm_context::Context::<BlockEnv, _, _, _, _, _>::new(
1464 WrapDatabaseRef(&mut self.db),
1465 SpecId::PRAGUE,
1466 )
1467 .with_block(block_env);
1468 let instructions = EthInstructions::new_mainnet();
1469 let mut evm = Evm::new_with_inspector(
1470 ctx,
1471 inspector.clone(),
1472 instructions,
1473 ServicePrecompile::default(),
1474 );
1475 evm.inspect(
1476 TxEnv {
1477 kind,
1478 data,
1479 nonce,
1480 caller,
1481 gas_limit: EVM_SERVICE_GAS_LIMIT,
1482 ..TxEnv::default()
1483 },
1484 inspector,
1485 )
1486 .map_err(|error| {
1487 let error = format!("{:?}", error);
1488 EvmExecutionError::TransactCommitError(error)
1489 })
1490 }?;
1491 let storage_stats = self.db.take_storage_stats();
1492 Ok((
1493 process_execution_result(storage_stats, result_state.result)?,
1494 result_state.state,
1495 ))
1496 }
1497}