1use super::*;
19
20use crate::{
21 AccountIdOf, CodeInfo, Config, ContractBlob, Error, SENTINEL, Weight,
22 address::AddressMapper,
23 debug::DebugSettings,
24 exec::Ext,
25 limits,
26 primitives::ExecReturnValue,
27 vm::{BytecodeType, ExportedFunction, RuntimeCosts, calculate_code_deposit},
28};
29use alloc::vec::Vec;
30use core::mem;
31use frame_support::traits::Get;
32use pallet_revive_proc_macro::define_env;
33use pallet_revive_uapi::{CallFlags, ReturnErrorCode, ReturnFlags};
34use sp_core::U256;
35use sp_io::hashing::keccak_256;
36use sp_runtime::{DispatchError, SaturatedConversion};
37
38impl<T: Config> ContractBlob<T> {
39 pub fn prepare_call<E: Ext<T = T>>(
45 self,
46 mut runtime: Runtime<E, polkavm::RawInstance>,
47 entry_point: ExportedFunction,
48 aux_data_size: u32,
49 ) -> Result<PreparedCall<E>, ExecError> {
50 let mut config = polkavm::Config::default();
51 let pvm_logs_enabled = DebugSettings::is_pvm_logs_enabled::<T>();
56 config.set_imperfect_logger_filtering_workaround(!pvm_logs_enabled);
57 config.set_backend(Some(polkavm::BackendKind::Interpreter));
58 config.set_cache_enabled(false);
59 #[cfg(feature = "std")]
60 if std::env::var_os("REVIVE_USE_COMPILER").is_some() {
61 log::warn!(target: LOG_TARGET, "Using PolkaVM compiler backend because env var REVIVE_USE_COMPILER is set");
62 config.set_backend(Some(polkavm::BackendKind::Compiler));
63 }
64 let engine = polkavm::Engine::new(&config).expect(
65 "on-chain (no_std) use of interpreter is hard coded.
66 interpreter is available on all platforms; qed",
67 );
68
69 let mut module_config = polkavm::ModuleConfig::new();
70 module_config.set_page_size(limits::PAGE_SIZE);
71 module_config.set_gas_metering(Some(polkavm::GasMeteringKind::Sync));
72 module_config.set_aux_data_size(aux_data_size);
73 let module =
74 polkavm::Module::new(&engine, &module_config, self.code.into()).map_err(|err| {
75 log::debug!(target: LOG_TARGET, "failed to create polkavm module: {err:?}");
76 Error::<T>::CodeRejected
77 })?;
78
79 let entry_program_counter = module
80 .exports()
81 .find(|export| export.symbol().as_bytes() == entry_point.identifier().as_bytes())
82 .ok_or_else(|| <Error<T>>::CodeRejected)?
83 .program_counter();
84
85 let gas_limit_polkavm: polkavm::Gas = runtime.ext().frame_meter_mut().sync_to_executor();
86
87 let mut instance = module.instantiate().map_err(|err| {
88 log::debug!(target: LOG_TARGET, "failed to instantiate polkavm module: {err:?}");
89 Error::<T>::CodeRejected
90 })?;
91
92 instance.set_gas(gas_limit_polkavm);
93 instance
94 .set_interpreter_cache_size_limit(Some(polkavm::SetCacheSizeLimitArgs {
95 max_block_size: limits::code::BASIC_BLOCK_SIZE,
96 max_cache_size_bytes: limits::code::INTERPRETER_CACHE_BYTES
97 .try_into()
98 .map_err(|_| Error::<T>::CodeRejected)?,
99 }))
100 .map_err(|_| Error::<T>::CodeRejected)?;
101 instance.prepare_call_untyped(entry_program_counter, &[]);
102
103 Ok(PreparedCall { module, instance, runtime })
104 }
105}
106
107impl<T: Config> ContractBlob<T> {
108 pub fn from_pvm_code(code: Vec<u8>, owner: AccountIdOf<T>) -> Result<Self, DispatchError> {
110 let available_syscalls = list_syscalls();
113 let code = limits::code::enforce::<T>(code, available_syscalls)?;
114
115 let code_len = code.len() as u32;
116 let deposit = calculate_code_deposit::<T>(code_len);
117
118 let code_info = CodeInfo {
119 owner,
120 deposit,
121 refcount: 0,
122 code_len,
123 code_type: BytecodeType::Pvm,
124 behaviour_version: Default::default(),
125 };
126 let code_hash = H256(sp_io::hashing::keccak_256(&code));
127 Ok(ContractBlob { code, code_info, code_hash })
128 }
129}
130
131impl<'a, E: Ext, M: PolkaVmInstance<E::T>> Runtime<'a, E, M> {
132 pub fn handle_interrupt(
133 &mut self,
134 interrupt: Result<polkavm::InterruptKind, polkavm::Error>,
135 module: &polkavm::Module,
136 instance: &mut M,
137 ) -> Option<ExecResult> {
138 use polkavm::InterruptKind::*;
139
140 match interrupt {
141 Err(error) => {
142 log::error!(target: LOG_TARGET, "polkavm execution error: {error}");
144 Some(Err(Error::<E::T>::ExecutionFailed.into()))
145 },
146 Ok(Finished) => {
147 Some(Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: Vec::new() }))
148 },
149 Ok(Trap) => Some(Err(Error::<E::T>::ContractTrapped.into())),
150 Ok(Segfault(_)) => Some(Err(Error::<E::T>::ExecutionFailed.into())),
151 Ok(NotEnoughGas) => Some(Err(Error::<E::T>::OutOfGas.into())),
152 Ok(Step) => None,
153 Ok(Ecalli(idx)) => {
154 if cfg!(feature = "runtime-benchmarks") && idx == SENTINEL {
159 return Some(Ok(ExecReturnValue {
160 flags: ReturnFlags::empty(),
161 data: Vec::new(),
162 }));
163 }
164 let Some(syscall_symbol) = module.imports().get(idx) else {
165 return Some(Err(<Error<E::T>>::InvalidSyscall.into()));
166 };
167 match self.handle_ecall(instance, syscall_symbol.as_bytes()) {
168 Ok(None) => None,
169 Ok(Some(return_value)) => {
170 instance.write_output(return_value);
171 None
172 },
173 Err(TrapReason::Return(ReturnData { flags, data })) => {
174 match ReturnFlags::from_bits(flags) {
175 None => Some(Err(Error::<E::T>::InvalidCallFlags.into())),
176 Some(flags) => Some(Ok(ExecReturnValue { flags, data })),
177 }
178 },
179 Err(TrapReason::Termination) => Some(Ok(Default::default())),
180 Err(TrapReason::SupervisorError(error)) => Some(Err(error.into())),
181 }
182 },
183 }
184 }
185}
186
187#[define_env]
195pub mod env {
196 #[cfg(feature = "runtime-benchmarks")]
198 fn noop(&mut self, memory: &mut M) -> Result<(), TrapReason> {
199 Ok(())
200 }
201
202 #[mutating]
205 fn set_storage(
206 &mut self,
207 memory: &mut M,
208 flags: u32,
209 key_ptr: u32,
210 key_len: u32,
211 value_ptr: u32,
212 value_len: u32,
213 ) -> Result<u32, TrapReason> {
214 self.set_storage(
215 memory,
216 flags,
217 key_ptr,
218 key_len,
219 StorageValue::Memory { ptr: value_ptr, len: value_len },
220 )
221 }
222
223 #[mutating]
226 fn set_storage_or_clear(
227 &mut self,
228 memory: &mut M,
229 flags: u32,
230 key_ptr: u32,
231 value_ptr: u32,
232 ) -> Result<u32, TrapReason> {
233 let value = memory.read(value_ptr, 32)?;
234
235 if value.iter().all(|&b| b == 0) {
236 self.clear_storage(memory, flags, key_ptr, SENTINEL)
237 } else {
238 self.set_storage(memory, flags, key_ptr, SENTINEL, StorageValue::Value(value))
239 }
240 }
241
242 fn get_storage(
245 &mut self,
246 memory: &mut M,
247 flags: u32,
248 key_ptr: u32,
249 key_len: u32,
250 out_ptr: u32,
251 out_len_ptr: u32,
252 ) -> Result<ReturnErrorCode, TrapReason> {
253 self.get_storage(
254 memory,
255 flags,
256 key_ptr,
257 key_len,
258 out_ptr,
259 StorageReadMode::VariableOutput { output_len_ptr: out_len_ptr },
260 )
261 }
262
263 fn get_storage_or_zero(
266 &mut self,
267 memory: &mut M,
268 flags: u32,
269 key_ptr: u32,
270 out_ptr: u32,
271 ) -> Result<(), TrapReason> {
272 let _ = self.get_storage(
273 memory,
274 flags,
275 key_ptr,
276 SENTINEL,
277 out_ptr,
278 StorageReadMode::FixedOutput32,
279 )?;
280
281 Ok(())
282 }
283
284 fn call(
287 &mut self,
288 memory: &mut M,
289 flags_and_callee: u64,
290 ref_time_limit: u64,
291 proof_size_limit: u64,
292 deposit_and_value: u64,
293 input_data: u64,
294 output_data: u64,
295 ) -> Result<ReturnErrorCode, TrapReason> {
296 let (flags, callee_ptr) = extract_hi_lo(flags_and_callee);
297 let (deposit_ptr, value_ptr) = extract_hi_lo(deposit_and_value);
298 let (input_data_len, input_data_ptr) = extract_hi_lo(input_data);
299 let (output_len_ptr, output_ptr) = extract_hi_lo(output_data);
300 let weight = Weight::from_parts(ref_time_limit, proof_size_limit);
301
302 self.charge_gas(RuntimeCosts::CopyFromContract(32))?;
303 let deposit_limit = memory.read_u256(deposit_ptr)?;
304
305 self.call(
306 memory,
307 CallFlags::from_bits(flags).ok_or(Error::<E::T>::InvalidCallFlags)?,
308 CallType::Call { value_ptr },
309 callee_ptr,
310 &CallResources::from_weight_and_deposit(weight, deposit_limit),
311 input_data_ptr,
312 input_data_len,
313 output_ptr,
314 output_len_ptr,
315 )
316 }
317
318 fn call_evm(
321 &mut self,
322 memory: &mut M,
323 flags: u32,
324 callee: u32,
325 value_ptr: u32,
326 gas: u64,
327 input_data: u64,
328 output_data: u64,
329 ) -> Result<ReturnErrorCode, TrapReason> {
330 let (input_data_len, input_data_ptr) = extract_hi_lo(input_data);
331 let (output_len_ptr, output_ptr) = extract_hi_lo(output_data);
332 let resources = if gas == u64::MAX {
333 CallResources::NoLimits
334 } else {
335 self.charge_gas(RuntimeCosts::CopyFromContract(32))?;
336 let value = memory.read_u256(value_ptr)?;
337 let add_stipend = !value.is_zero() || gas == revm::interpreter::gas::CALL_STIPEND;
339 CallResources::from_ethereum_gas(gas.into(), add_stipend)
340 };
341
342 self.call(
343 memory,
344 CallFlags::from_bits(flags).ok_or(Error::<E::T>::InvalidCallFlags)?,
345 CallType::Call { value_ptr },
346 callee,
347 &resources,
348 input_data_ptr,
349 input_data_len,
350 output_ptr,
351 output_len_ptr,
352 )
353 }
354
355 fn delegate_call(
358 &mut self,
359 memory: &mut M,
360 flags_and_callee: u64,
361 ref_time_limit: u64,
362 proof_size_limit: u64,
363 deposit_ptr: u32,
364 input_data: u64,
365 output_data: u64,
366 ) -> Result<ReturnErrorCode, TrapReason> {
367 let (flags, address_ptr) = extract_hi_lo(flags_and_callee);
368 let (input_data_len, input_data_ptr) = extract_hi_lo(input_data);
369 let (output_len_ptr, output_ptr) = extract_hi_lo(output_data);
370 let weight = Weight::from_parts(ref_time_limit, proof_size_limit);
371
372 self.charge_gas(RuntimeCosts::CopyFromContract(32))?;
373 let deposit_limit = memory.read_u256(deposit_ptr)?;
374
375 self.call(
376 memory,
377 CallFlags::from_bits(flags).ok_or(Error::<E::T>::InvalidCallFlags)?,
378 CallType::DelegateCall,
379 address_ptr,
380 &CallResources::from_weight_and_deposit(weight, deposit_limit),
381 input_data_ptr,
382 input_data_len,
383 output_ptr,
384 output_len_ptr,
385 )
386 }
387
388 fn delegate_call_evm(
391 &mut self,
392 memory: &mut M,
393 flags: u32,
394 callee: u32,
395 gas: u64,
396 input_data: u64,
397 output_data: u64,
398 ) -> Result<ReturnErrorCode, TrapReason> {
399 let (input_data_len, input_data_ptr) = extract_hi_lo(input_data);
400 let (output_len_ptr, output_ptr) = extract_hi_lo(output_data);
401 let resources = if gas == u64::MAX {
402 CallResources::NoLimits
403 } else {
404 CallResources::from_ethereum_gas(gas.into(), false)
405 };
406
407 self.call(
408 memory,
409 CallFlags::from_bits(flags).ok_or(Error::<E::T>::InvalidCallFlags)?,
410 CallType::DelegateCall,
411 callee,
412 &resources,
413 input_data_ptr,
414 input_data_len,
415 output_ptr,
416 output_len_ptr,
417 )
418 }
419
420 #[mutating]
423 fn instantiate(
424 &mut self,
425 memory: &mut M,
426 ref_time_limit: u64,
427 proof_size_limit: u64,
428 deposit_and_value: u64,
429 input_data: u64,
430 output_data: u64,
431 address_and_salt: u64,
432 ) -> Result<ReturnErrorCode, TrapReason> {
433 let (deposit_ptr, value_ptr) = extract_hi_lo(deposit_and_value);
434 let (input_data_len, code_hash_ptr) = extract_hi_lo(input_data);
435 let (output_len_ptr, output_ptr) = extract_hi_lo(output_data);
436 let (address_ptr, salt_ptr) = extract_hi_lo(address_and_salt);
437 let Some(input_data_ptr) = code_hash_ptr.checked_add(32) else {
438 return Err(Error::<E::T>::OutOfBounds.into());
439 };
440 let Some(input_data_len) = input_data_len.checked_sub(32) else {
441 return Err(Error::<E::T>::OutOfBounds.into());
442 };
443
444 self.instantiate(
445 memory,
446 code_hash_ptr,
447 Weight::from_parts(ref_time_limit, proof_size_limit),
448 deposit_ptr,
449 value_ptr,
450 input_data_ptr,
451 input_data_len,
452 address_ptr,
453 output_ptr,
454 output_len_ptr,
455 salt_ptr,
456 )
457 }
458
459 fn call_data_size(&mut self, memory: &mut M) -> Result<u64, TrapReason> {
462 self.charge_gas(RuntimeCosts::CallDataSize)?;
463 Ok(self
464 .input_data
465 .as_ref()
466 .map(|input| input.len().try_into().expect("usize fits into u64; qed"))
467 .unwrap_or_default())
468 }
469
470 fn call_data_copy(
473 &mut self,
474 memory: &mut M,
475 out_ptr: u32,
476 out_len: u32,
477 offset: u32,
478 ) -> Result<(), TrapReason> {
479 self.charge_gas(RuntimeCosts::CallDataCopy(out_len))?;
480
481 let Some(input) = self.input_data.as_ref() else {
482 return Err(Error::<E::T>::InputForwarded.into());
483 };
484
485 let start = offset as usize;
486 if start >= input.len() {
487 memory.zero(out_ptr, out_len)?;
488 return Ok(());
489 }
490
491 let end = start.saturating_add(out_len as usize).min(input.len());
492 memory.write(out_ptr, &input[start..end])?;
493
494 let bytes_written = (end - start) as u32;
495 memory.zero(out_ptr.saturating_add(bytes_written), out_len - bytes_written)?;
496
497 Ok(())
498 }
499
500 fn call_data_load(
503 &mut self,
504 memory: &mut M,
505 out_ptr: u32,
506 offset: u32,
507 ) -> Result<(), TrapReason> {
508 self.charge_gas(RuntimeCosts::CallDataLoad)?;
509
510 let Some(input) = self.input_data.as_ref() else {
511 return Err(Error::<E::T>::InputForwarded.into());
512 };
513
514 let mut data = [0; 32];
515 let start = offset as usize;
516 let data = if start >= input.len() {
517 data } else {
519 let end = start.saturating_add(32).min(input.len());
520 data[..end - start].copy_from_slice(&input[start..end]);
521 data.reverse();
522 data };
524
525 self.write_fixed_sandbox_output(memory, out_ptr, &data, false, already_charged)?;
526
527 Ok(())
528 }
529
530 fn seal_return(
533 &mut self,
534 memory: &mut M,
535 flags: u32,
536 data_ptr: u32,
537 data_len: u32,
538 ) -> Result<(), TrapReason> {
539 self.charge_gas(RuntimeCosts::CopyFromContract(data_len))?;
540 if data_len > limits::CALLDATA_BYTES {
541 Err(<Error<E::T>>::ReturnDataTooLarge)?;
542 }
543 Err(TrapReason::Return(ReturnData { flags, data: memory.read(data_ptr, data_len)? }))
544 }
545
546 fn caller(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
549 self.charge_gas(RuntimeCosts::Caller)?;
550 let caller = <E::T as Config>::AddressMapper::to_address(self.ext.caller().account_id()?);
551 Ok(self.write_fixed_sandbox_output(
552 memory,
553 out_ptr,
554 caller.as_bytes(),
555 false,
556 already_charged,
557 )?)
558 }
559
560 fn origin(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
563 self.charge_gas(RuntimeCosts::Origin)?;
564 let origin = <E::T as Config>::AddressMapper::to_address(self.ext.origin().account_id()?);
565 Ok(self.write_fixed_sandbox_output(
566 memory,
567 out_ptr,
568 origin.as_bytes(),
569 false,
570 already_charged,
571 )?)
572 }
573
574 fn code_hash(&mut self, memory: &mut M, addr_ptr: u32, out_ptr: u32) -> Result<(), TrapReason> {
577 self.charge_gas(RuntimeCosts::CodeHash)?;
578 let address = memory.read_h160(addr_ptr)?;
579 Ok(self.write_fixed_sandbox_output(
580 memory,
581 out_ptr,
582 &self.ext.code_hash(&address).as_bytes(),
583 false,
584 already_charged,
585 )?)
586 }
587
588 fn code_size(&mut self, memory: &mut M, addr_ptr: u32) -> Result<u64, TrapReason> {
591 self.charge_gas(RuntimeCosts::CodeSize)?;
592 let address = memory.read_h160(addr_ptr)?;
593 Ok(self.ext.code_size(&address))
594 }
595
596 fn address(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
599 self.charge_gas(RuntimeCosts::Address)?;
600 let address = self.ext.address();
601 Ok(self.write_fixed_sandbox_output(
602 memory,
603 out_ptr,
604 address.as_bytes(),
605 false,
606 already_charged,
607 )?)
608 }
609
610 fn get_immutable_data(
613 &mut self,
614 memory: &mut M,
615 out_ptr: u32,
616 out_len_ptr: u32,
617 ) -> Result<(), TrapReason> {
618 let is_delegate = self.ext.is_delegate_call();
619 let charge_len =
620 if is_delegate { limits::IMMUTABLE_BYTES } else { self.ext.immutable_data_len() };
621 let charged = self.charge_gas(RuntimeCosts::GetImmutableData(charge_len))?;
622 let data = self.ext.get_immutable_data()?;
623 if is_delegate {
624 self.adjust_gas(charged, RuntimeCosts::GetImmutableData(data.len() as u32));
625 }
626 self.write_sandbox_output(memory, out_ptr, out_len_ptr, &data, false, already_charged)?;
627 Ok(())
628 }
629
630 fn set_immutable_data(&mut self, memory: &mut M, ptr: u32, len: u32) -> Result<(), TrapReason> {
633 if len > limits::IMMUTABLE_BYTES {
634 return Err(Error::<E::T>::OutOfBounds.into());
635 }
636 self.charge_gas(RuntimeCosts::SetImmutableData(len))?;
637 let buf = memory.read(ptr, len)?;
638 let data = buf.try_into().expect("bailed out earlier; qed");
639 self.ext.set_immutable_data(data)?;
640 Ok(())
641 }
642
643 fn balance(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
646 self.charge_gas(RuntimeCosts::Balance)?;
647 Ok(self.write_fixed_sandbox_output(
648 memory,
649 out_ptr,
650 &self.ext.balance().to_little_endian(),
651 false,
652 already_charged,
653 )?)
654 }
655
656 fn balance_of(
659 &mut self,
660 memory: &mut M,
661 addr_ptr: u32,
662 out_ptr: u32,
663 ) -> Result<(), TrapReason> {
664 self.charge_gas(RuntimeCosts::BalanceOf)?;
665 let address = memory.read_h160(addr_ptr)?;
666 Ok(self.write_fixed_sandbox_output(
667 memory,
668 out_ptr,
669 &self.ext.balance_of(&address).to_little_endian(),
670 false,
671 already_charged,
672 )?)
673 }
674
675 fn chain_id(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
678 Ok(self.write_fixed_sandbox_output(
679 memory,
680 out_ptr,
681 &U256::from(<E::T as Config>::ChainId::get()).to_little_endian(),
682 false,
683 |_| Some(RuntimeCosts::CopyToContract(32)),
684 )?)
685 }
686
687 fn gas_limit(&mut self, memory: &mut M) -> Result<u64, TrapReason> {
690 self.charge_gas(RuntimeCosts::GasLimit)?;
691 Ok(self.ext.gas_limit())
692 }
693
694 fn value_transferred(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
697 self.charge_gas(RuntimeCosts::ValueTransferred)?;
698 Ok(self.write_fixed_sandbox_output(
699 memory,
700 out_ptr,
701 &self.ext.value_transferred().to_little_endian(),
702 false,
703 already_charged,
704 )?)
705 }
706
707 fn gas_price(&mut self, memory: &mut M) -> Result<u64, TrapReason> {
710 self.charge_gas(RuntimeCosts::GasPrice)?;
711 Ok(self.ext.effective_gas_price().saturated_into())
712 }
713
714 fn base_fee(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
717 self.charge_gas(RuntimeCosts::BaseFee)?;
718 Ok(self.write_fixed_sandbox_output(
719 memory,
720 out_ptr,
721 &Pallet::<E::T>::evm_base_fee().to_little_endian(),
722 false,
723 already_charged,
724 )?)
725 }
726
727 fn now(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
730 self.charge_gas(RuntimeCosts::Now)?;
731 Ok(self.write_fixed_sandbox_output(
732 memory,
733 out_ptr,
734 &self.ext.now().to_little_endian(),
735 false,
736 already_charged,
737 )?)
738 }
739
740 #[mutating]
743 fn deposit_event(
744 &mut self,
745 memory: &mut M,
746 topics_ptr: u32,
747 num_topic: u32,
748 data_ptr: u32,
749 data_len: u32,
750 ) -> Result<(), TrapReason> {
751 self.charge_gas(RuntimeCosts::DepositEvent { num_topic, len: data_len })?;
752
753 if num_topic > limits::NUM_EVENT_TOPICS {
754 return Err(Error::<E::T>::TooManyTopics.into());
755 }
756
757 if data_len > limits::EVENT_BYTES {
758 return Err(Error::<E::T>::ValueTooLarge.into());
759 }
760
761 let topics: Vec<H256> = match num_topic {
762 0 => Vec::new(),
763 _ => {
764 let mut v = Vec::with_capacity(num_topic as usize);
765 let topics_len = num_topic * H256::len_bytes() as u32;
766 let buf = memory.read(topics_ptr, topics_len)?;
767 for chunk in buf.chunks_exact(H256::len_bytes()) {
768 v.push(H256::from_slice(chunk));
769 }
770 v
771 },
772 };
773
774 let event_data = memory.read(data_ptr, data_len)?;
775 self.ext.deposit_event(topics, event_data);
776 Ok(())
777 }
778
779 fn block_number(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
782 self.charge_gas(RuntimeCosts::BlockNumber)?;
783 Ok(self.write_fixed_sandbox_output(
784 memory,
785 out_ptr,
786 &self.ext.block_number().to_little_endian(),
787 false,
788 already_charged,
789 )?)
790 }
791
792 fn block_hash(
795 &mut self,
796 memory: &mut M,
797 block_number_ptr: u32,
798 out_ptr: u32,
799 ) -> Result<(), TrapReason> {
800 self.charge_gas(RuntimeCosts::BlockHash)?;
801 let block_number = memory.read_u256(block_number_ptr)?;
802 let block_hash = self.ext.block_hash(block_number).unwrap_or(H256::zero());
803 Ok(self.write_fixed_sandbox_output(
804 memory,
805 out_ptr,
806 &block_hash.as_bytes(),
807 false,
808 already_charged,
809 )?)
810 }
811
812 fn block_author(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
815 self.charge_gas(RuntimeCosts::BlockAuthor)?;
816 let block_author = self.ext.block_author();
817
818 Ok(self.write_fixed_sandbox_output(
819 memory,
820 out_ptr,
821 &block_author.as_bytes(),
822 false,
823 already_charged,
824 )?)
825 }
826
827 fn hash_keccak_256(
830 &mut self,
831 memory: &mut M,
832 input_ptr: u32,
833 input_len: u32,
834 output_ptr: u32,
835 ) -> Result<(), TrapReason> {
836 self.charge_gas(RuntimeCosts::HashKeccak256(input_len))?;
837 Ok(self.compute_hash_on_intermediate_buffer(
838 memory, keccak_256, input_ptr, input_len, output_ptr,
839 )?)
840 }
841
842 fn return_data_size(&mut self, memory: &mut M) -> Result<u64, TrapReason> {
845 self.charge_gas(RuntimeCosts::ReturnDataSize)?;
846 Ok(self
847 .ext
848 .last_frame_output()
849 .data
850 .len()
851 .try_into()
852 .expect("usize fits into u64; qed"))
853 }
854
855 fn return_data_copy(
858 &mut self,
859 memory: &mut M,
860 out_ptr: u32,
861 out_len_ptr: u32,
862 offset: u32,
863 ) -> Result<(), TrapReason> {
864 let output = mem::take(self.ext.last_frame_output_mut());
865 let result = if offset as usize > output.data.len() {
866 Err(Error::<E::T>::OutOfBounds.into())
867 } else {
868 self.write_sandbox_output(
869 memory,
870 out_ptr,
871 out_len_ptr,
872 &output.data[offset as usize..],
873 false,
874 |len| Some(RuntimeCosts::CopyToContract(len)),
875 )
876 };
877 *self.ext.last_frame_output_mut() = output;
878 Ok(result?)
879 }
880
881 fn ref_time_left(&mut self, memory: &mut M) -> Result<u64, TrapReason> {
888 self.charge_gas(RuntimeCosts::RefTimeLeft)?;
889 Ok(self.ext.gas_left())
890 }
891
892 fn consume_all_gas(&mut self, memory: &mut M) -> Result<(), TrapReason> {
896 self.ext.frame_meter_mut().consume_all_weight();
897 Err(TrapReason::Return(ReturnData {
898 flags: ReturnFlags::REVERT.bits(),
899 data: Default::default(),
900 }))
901 }
902
903 fn ecdsa_to_eth_address(
906 &mut self,
907 memory: &mut M,
908 key_ptr: u32,
909 out_ptr: u32,
910 ) -> Result<ReturnErrorCode, TrapReason> {
911 self.charge_gas(RuntimeCosts::EcdsaToEthAddress)?;
912 let mut compressed_key: [u8; 33] = [0; 33];
913 memory.read_into_buf(key_ptr, &mut compressed_key)?;
914 let result = self.ext.ecdsa_to_eth_address(&compressed_key);
915 match result {
916 Ok(eth_address) => {
917 memory.write(out_ptr, eth_address.as_ref())?;
918 Ok(ReturnErrorCode::Success)
919 },
920 Err(_) => Ok(ReturnErrorCode::EcdsaRecoveryFailed),
921 }
922 }
923
924 fn sr25519_verify(
927 &mut self,
928 memory: &mut M,
929 signature_ptr: u32,
930 pub_key_ptr: u32,
931 message_len: u32,
932 message_ptr: u32,
933 ) -> Result<ReturnErrorCode, TrapReason> {
934 self.charge_gas(RuntimeCosts::Sr25519Verify(message_len))?;
935
936 let mut signature: [u8; 64] = [0; 64];
937 memory.read_into_buf(signature_ptr, &mut signature)?;
938
939 let mut pub_key: [u8; 32] = [0; 32];
940 memory.read_into_buf(pub_key_ptr, &mut pub_key)?;
941
942 let message: Vec<u8> = memory.read(message_ptr, message_len)?;
943
944 if self.ext.sr25519_verify(&signature, &message, &pub_key) {
945 Ok(ReturnErrorCode::Success)
946 } else {
947 Ok(ReturnErrorCode::Sr25519VerifyFailed)
948 }
949 }
950
951 #[mutating]
955 fn terminate(&mut self, memory: &mut M, beneficiary_ptr: u32) -> Result<(), TrapReason> {
956 let charged = self.charge_gas(RuntimeCosts::Terminate { code_removed: true })?;
957 let beneficiary = memory.read_h160(beneficiary_ptr)?;
958 if matches!(self.ext.terminate_if_same_tx(&beneficiary)?, crate::CodeRemoved::No) {
959 self.adjust_gas(charged, RuntimeCosts::Terminate { code_removed: false });
960 }
961 Err(TrapReason::Termination)
962 }
963}
964
965#[cfg(test)]
966mod tests {
967 use super::*;
968 use pallet_revive_types::runtime_api::*;
969 use strum::VariantArray;
970
971 #[test]
972 fn wire_has_all_syscalls() {
973 let wire_syscalls = PolkavmSyscallV1::VARIANTS
974 .iter()
975 .copied()
976 .map(<&'static str>::from)
977 .collect::<Vec<_>>();
978 let canonical_syscalls = list_trace_ops()
979 .iter()
980 .map(|bytes| str::from_utf8(bytes).unwrap())
981 .collect::<Vec<_>>();
982
983 assert_eq!(
984 wire_syscalls, canonical_syscalls,
985 "There is a difference between the syscalls defined for the wire and the syscalls \
986 available in the system. Wire = {wire_syscalls:?}. Available = {canonical_syscalls:?}"
987 )
988 }
989}