use super::*;
use crate::{
AccountIdOf, CodeInfo, Config, ContractBlob, Error, SENTINEL, Weight,
address::AddressMapper,
debug::DebugSettings,
exec::Ext,
limits,
primitives::ExecReturnValue,
vm::{BytecodeType, ExportedFunction, RuntimeCosts, calculate_code_deposit},
};
use alloc::vec::Vec;
use core::mem;
use frame_support::traits::Get;
use pallet_revive_proc_macro::define_env;
use pallet_revive_uapi::{CallFlags, ReturnErrorCode, ReturnFlags};
use sp_core::U256;
use sp_io::hashing::keccak_256;
use sp_runtime::{DispatchError, SaturatedConversion};
impl<T: Config> ContractBlob<T> {
pub fn prepare_call<E: Ext<T = T>>(
self,
mut runtime: Runtime<E, polkavm::RawInstance>,
entry_point: ExportedFunction,
aux_data_size: u32,
) -> Result<PreparedCall<E>, ExecError> {
let mut config = polkavm::Config::default();
let pvm_logs_enabled = DebugSettings::is_pvm_logs_enabled::<T>();
config.set_imperfect_logger_filtering_workaround(!pvm_logs_enabled);
config.set_backend(Some(polkavm::BackendKind::Interpreter));
config.set_cache_enabled(false);
#[cfg(feature = "std")]
if std::env::var_os("REVIVE_USE_COMPILER").is_some() {
log::warn!(target: LOG_TARGET, "Using PolkaVM compiler backend because env var REVIVE_USE_COMPILER is set");
config.set_backend(Some(polkavm::BackendKind::Compiler));
}
let engine = polkavm::Engine::new(&config).expect(
"on-chain (no_std) use of interpreter is hard coded.
interpreter is available on all platforms; qed",
);
let mut module_config = polkavm::ModuleConfig::new();
module_config.set_page_size(limits::PAGE_SIZE);
module_config.set_gas_metering(Some(polkavm::GasMeteringKind::Sync));
module_config.set_aux_data_size(aux_data_size);
let module =
polkavm::Module::new(&engine, &module_config, self.code.into()).map_err(|err| {
log::debug!(target: LOG_TARGET, "failed to create polkavm module: {err:?}");
Error::<T>::CodeRejected
})?;
let entry_program_counter = module
.exports()
.find(|export| export.symbol().as_bytes() == entry_point.identifier().as_bytes())
.ok_or_else(|| <Error<T>>::CodeRejected)?
.program_counter();
let gas_limit_polkavm: polkavm::Gas = runtime.ext().frame_meter_mut().sync_to_executor();
let mut instance = module.instantiate().map_err(|err| {
log::debug!(target: LOG_TARGET, "failed to instantiate polkavm module: {err:?}");
Error::<T>::CodeRejected
})?;
instance.set_gas(gas_limit_polkavm);
instance
.set_interpreter_cache_size_limit(Some(polkavm::SetCacheSizeLimitArgs {
max_block_size: limits::code::BASIC_BLOCK_SIZE,
max_cache_size_bytes: limits::code::INTERPRETER_CACHE_BYTES
.try_into()
.map_err(|_| Error::<T>::CodeRejected)?,
}))
.map_err(|_| Error::<T>::CodeRejected)?;
instance.prepare_call_untyped(entry_program_counter, &[]);
Ok(PreparedCall { module, instance, runtime })
}
}
impl<T: Config> ContractBlob<T> {
pub fn from_pvm_code(code: Vec<u8>, owner: AccountIdOf<T>) -> Result<Self, DispatchError> {
let available_syscalls = list_syscalls();
let code = limits::code::enforce::<T>(code, available_syscalls)?;
let code_len = code.len() as u32;
let deposit = calculate_code_deposit::<T>(code_len);
let code_info = CodeInfo {
owner,
deposit,
refcount: 0,
code_len,
code_type: BytecodeType::Pvm,
behaviour_version: Default::default(),
};
let code_hash = H256(sp_io::hashing::keccak_256(&code));
Ok(ContractBlob { code, code_info, code_hash })
}
}
impl<'a, E: Ext, M: PolkaVmInstance<E::T>> Runtime<'a, E, M> {
pub fn handle_interrupt(
&mut self,
interrupt: Result<polkavm::InterruptKind, polkavm::Error>,
module: &polkavm::Module,
instance: &mut M,
) -> Option<ExecResult> {
use polkavm::InterruptKind::*;
match interrupt {
Err(error) => {
log::error!(target: LOG_TARGET, "polkavm execution error: {error}");
Some(Err(Error::<E::T>::ExecutionFailed.into()))
},
Ok(Finished) => {
Some(Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: Vec::new() }))
},
Ok(Trap) => Some(Err(Error::<E::T>::ContractTrapped.into())),
Ok(Segfault(_)) => Some(Err(Error::<E::T>::ExecutionFailed.into())),
Ok(NotEnoughGas) => Some(Err(Error::<E::T>::OutOfGas.into())),
Ok(Step) => None,
Ok(Ecalli(idx)) => {
if cfg!(feature = "runtime-benchmarks") && idx == SENTINEL {
return Some(Ok(ExecReturnValue {
flags: ReturnFlags::empty(),
data: Vec::new(),
}));
}
let Some(syscall_symbol) = module.imports().get(idx) else {
return Some(Err(<Error<E::T>>::InvalidSyscall.into()));
};
match self.handle_ecall(instance, syscall_symbol.as_bytes()) {
Ok(None) => None,
Ok(Some(return_value)) => {
instance.write_output(return_value);
None
},
Err(TrapReason::Return(ReturnData { flags, data })) => {
match ReturnFlags::from_bits(flags) {
None => Some(Err(Error::<E::T>::InvalidCallFlags.into())),
Some(flags) => Some(Ok(ExecReturnValue { flags, data })),
}
},
Err(TrapReason::Termination) => Some(Ok(Default::default())),
Err(TrapReason::SupervisorError(error)) => Some(Err(error.into())),
}
},
}
}
}
#[define_env]
pub mod env {
#[cfg(feature = "runtime-benchmarks")]
fn noop(&mut self, memory: &mut M) -> Result<(), TrapReason> {
Ok(())
}
#[mutating]
fn set_storage(
&mut self,
memory: &mut M,
flags: u32,
key_ptr: u32,
key_len: u32,
value_ptr: u32,
value_len: u32,
) -> Result<u32, TrapReason> {
self.set_storage(
memory,
flags,
key_ptr,
key_len,
StorageValue::Memory { ptr: value_ptr, len: value_len },
)
}
#[mutating]
fn set_storage_or_clear(
&mut self,
memory: &mut M,
flags: u32,
key_ptr: u32,
value_ptr: u32,
) -> Result<u32, TrapReason> {
let value = memory.read(value_ptr, 32)?;
if value.iter().all(|&b| b == 0) {
self.clear_storage(memory, flags, key_ptr, SENTINEL)
} else {
self.set_storage(memory, flags, key_ptr, SENTINEL, StorageValue::Value(value))
}
}
fn get_storage(
&mut self,
memory: &mut M,
flags: u32,
key_ptr: u32,
key_len: u32,
out_ptr: u32,
out_len_ptr: u32,
) -> Result<ReturnErrorCode, TrapReason> {
self.get_storage(
memory,
flags,
key_ptr,
key_len,
out_ptr,
StorageReadMode::VariableOutput { output_len_ptr: out_len_ptr },
)
}
fn get_storage_or_zero(
&mut self,
memory: &mut M,
flags: u32,
key_ptr: u32,
out_ptr: u32,
) -> Result<(), TrapReason> {
let _ = self.get_storage(
memory,
flags,
key_ptr,
SENTINEL,
out_ptr,
StorageReadMode::FixedOutput32,
)?;
Ok(())
}
fn call(
&mut self,
memory: &mut M,
flags_and_callee: u64,
ref_time_limit: u64,
proof_size_limit: u64,
deposit_and_value: u64,
input_data: u64,
output_data: u64,
) -> Result<ReturnErrorCode, TrapReason> {
let (flags, callee_ptr) = extract_hi_lo(flags_and_callee);
let (deposit_ptr, value_ptr) = extract_hi_lo(deposit_and_value);
let (input_data_len, input_data_ptr) = extract_hi_lo(input_data);
let (output_len_ptr, output_ptr) = extract_hi_lo(output_data);
let weight = Weight::from_parts(ref_time_limit, proof_size_limit);
self.charge_gas(RuntimeCosts::CopyFromContract(32))?;
let deposit_limit = memory.read_u256(deposit_ptr)?;
self.call(
memory,
CallFlags::from_bits(flags).ok_or(Error::<E::T>::InvalidCallFlags)?,
CallType::Call { value_ptr },
callee_ptr,
&CallResources::from_weight_and_deposit(weight, deposit_limit),
input_data_ptr,
input_data_len,
output_ptr,
output_len_ptr,
)
}
fn call_evm(
&mut self,
memory: &mut M,
flags: u32,
callee: u32,
value_ptr: u32,
gas: u64,
input_data: u64,
output_data: u64,
) -> Result<ReturnErrorCode, TrapReason> {
let (input_data_len, input_data_ptr) = extract_hi_lo(input_data);
let (output_len_ptr, output_ptr) = extract_hi_lo(output_data);
let resources = if gas == u64::MAX {
CallResources::NoLimits
} else {
self.charge_gas(RuntimeCosts::CopyFromContract(32))?;
let value = memory.read_u256(value_ptr)?;
let add_stipend = !value.is_zero() || gas == revm::interpreter::gas::CALL_STIPEND;
CallResources::from_ethereum_gas(gas.into(), add_stipend)
};
self.call(
memory,
CallFlags::from_bits(flags).ok_or(Error::<E::T>::InvalidCallFlags)?,
CallType::Call { value_ptr },
callee,
&resources,
input_data_ptr,
input_data_len,
output_ptr,
output_len_ptr,
)
}
fn delegate_call(
&mut self,
memory: &mut M,
flags_and_callee: u64,
ref_time_limit: u64,
proof_size_limit: u64,
deposit_ptr: u32,
input_data: u64,
output_data: u64,
) -> Result<ReturnErrorCode, TrapReason> {
let (flags, address_ptr) = extract_hi_lo(flags_and_callee);
let (input_data_len, input_data_ptr) = extract_hi_lo(input_data);
let (output_len_ptr, output_ptr) = extract_hi_lo(output_data);
let weight = Weight::from_parts(ref_time_limit, proof_size_limit);
self.charge_gas(RuntimeCosts::CopyFromContract(32))?;
let deposit_limit = memory.read_u256(deposit_ptr)?;
self.call(
memory,
CallFlags::from_bits(flags).ok_or(Error::<E::T>::InvalidCallFlags)?,
CallType::DelegateCall,
address_ptr,
&CallResources::from_weight_and_deposit(weight, deposit_limit),
input_data_ptr,
input_data_len,
output_ptr,
output_len_ptr,
)
}
fn delegate_call_evm(
&mut self,
memory: &mut M,
flags: u32,
callee: u32,
gas: u64,
input_data: u64,
output_data: u64,
) -> Result<ReturnErrorCode, TrapReason> {
let (input_data_len, input_data_ptr) = extract_hi_lo(input_data);
let (output_len_ptr, output_ptr) = extract_hi_lo(output_data);
let resources = if gas == u64::MAX {
CallResources::NoLimits
} else {
CallResources::from_ethereum_gas(gas.into(), false)
};
self.call(
memory,
CallFlags::from_bits(flags).ok_or(Error::<E::T>::InvalidCallFlags)?,
CallType::DelegateCall,
callee,
&resources,
input_data_ptr,
input_data_len,
output_ptr,
output_len_ptr,
)
}
#[mutating]
fn instantiate(
&mut self,
memory: &mut M,
ref_time_limit: u64,
proof_size_limit: u64,
deposit_and_value: u64,
input_data: u64,
output_data: u64,
address_and_salt: u64,
) -> Result<ReturnErrorCode, TrapReason> {
let (deposit_ptr, value_ptr) = extract_hi_lo(deposit_and_value);
let (input_data_len, code_hash_ptr) = extract_hi_lo(input_data);
let (output_len_ptr, output_ptr) = extract_hi_lo(output_data);
let (address_ptr, salt_ptr) = extract_hi_lo(address_and_salt);
let Some(input_data_ptr) = code_hash_ptr.checked_add(32) else {
return Err(Error::<E::T>::OutOfBounds.into());
};
let Some(input_data_len) = input_data_len.checked_sub(32) else {
return Err(Error::<E::T>::OutOfBounds.into());
};
self.instantiate(
memory,
code_hash_ptr,
Weight::from_parts(ref_time_limit, proof_size_limit),
deposit_ptr,
value_ptr,
input_data_ptr,
input_data_len,
address_ptr,
output_ptr,
output_len_ptr,
salt_ptr,
)
}
fn call_data_size(&mut self, memory: &mut M) -> Result<u64, TrapReason> {
self.charge_gas(RuntimeCosts::CallDataSize)?;
Ok(self
.input_data
.as_ref()
.map(|input| input.len().try_into().expect("usize fits into u64; qed"))
.unwrap_or_default())
}
fn call_data_copy(
&mut self,
memory: &mut M,
out_ptr: u32,
out_len: u32,
offset: u32,
) -> Result<(), TrapReason> {
self.charge_gas(RuntimeCosts::CallDataCopy(out_len))?;
let Some(input) = self.input_data.as_ref() else {
return Err(Error::<E::T>::InputForwarded.into());
};
let start = offset as usize;
if start >= input.len() {
memory.zero(out_ptr, out_len)?;
return Ok(());
}
let end = start.saturating_add(out_len as usize).min(input.len());
memory.write(out_ptr, &input[start..end])?;
let bytes_written = (end - start) as u32;
memory.zero(out_ptr.saturating_add(bytes_written), out_len - bytes_written)?;
Ok(())
}
fn call_data_load(
&mut self,
memory: &mut M,
out_ptr: u32,
offset: u32,
) -> Result<(), TrapReason> {
self.charge_gas(RuntimeCosts::CallDataLoad)?;
let Some(input) = self.input_data.as_ref() else {
return Err(Error::<E::T>::InputForwarded.into());
};
let mut data = [0; 32];
let start = offset as usize;
let data = if start >= input.len() {
data } else {
let end = start.saturating_add(32).min(input.len());
data[..end - start].copy_from_slice(&input[start..end]);
data.reverse();
data };
self.write_fixed_sandbox_output(memory, out_ptr, &data, false, already_charged)?;
Ok(())
}
fn seal_return(
&mut self,
memory: &mut M,
flags: u32,
data_ptr: u32,
data_len: u32,
) -> Result<(), TrapReason> {
self.charge_gas(RuntimeCosts::CopyFromContract(data_len))?;
if data_len > limits::CALLDATA_BYTES {
Err(<Error<E::T>>::ReturnDataTooLarge)?;
}
Err(TrapReason::Return(ReturnData { flags, data: memory.read(data_ptr, data_len)? }))
}
fn caller(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
self.charge_gas(RuntimeCosts::Caller)?;
let caller = <E::T as Config>::AddressMapper::to_address(self.ext.caller().account_id()?);
Ok(self.write_fixed_sandbox_output(
memory,
out_ptr,
caller.as_bytes(),
false,
already_charged,
)?)
}
fn origin(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
self.charge_gas(RuntimeCosts::Origin)?;
let origin = <E::T as Config>::AddressMapper::to_address(self.ext.origin().account_id()?);
Ok(self.write_fixed_sandbox_output(
memory,
out_ptr,
origin.as_bytes(),
false,
already_charged,
)?)
}
fn code_hash(&mut self, memory: &mut M, addr_ptr: u32, out_ptr: u32) -> Result<(), TrapReason> {
self.charge_gas(RuntimeCosts::CodeHash)?;
let address = memory.read_h160(addr_ptr)?;
Ok(self.write_fixed_sandbox_output(
memory,
out_ptr,
&self.ext.code_hash(&address).as_bytes(),
false,
already_charged,
)?)
}
fn code_size(&mut self, memory: &mut M, addr_ptr: u32) -> Result<u64, TrapReason> {
self.charge_gas(RuntimeCosts::CodeSize)?;
let address = memory.read_h160(addr_ptr)?;
Ok(self.ext.code_size(&address))
}
fn address(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
self.charge_gas(RuntimeCosts::Address)?;
let address = self.ext.address();
Ok(self.write_fixed_sandbox_output(
memory,
out_ptr,
address.as_bytes(),
false,
already_charged,
)?)
}
fn get_immutable_data(
&mut self,
memory: &mut M,
out_ptr: u32,
out_len_ptr: u32,
) -> Result<(), TrapReason> {
let len = self.ext.immutable_data_len();
self.charge_gas(RuntimeCosts::GetImmutableData(len))?;
let data = self.ext.get_immutable_data()?;
self.write_sandbox_output(memory, out_ptr, out_len_ptr, &data, false, already_charged)?;
Ok(())
}
fn set_immutable_data(&mut self, memory: &mut M, ptr: u32, len: u32) -> Result<(), TrapReason> {
if len > limits::IMMUTABLE_BYTES {
return Err(Error::<E::T>::OutOfBounds.into());
}
self.charge_gas(RuntimeCosts::SetImmutableData(len))?;
let buf = memory.read(ptr, len)?;
let data = buf.try_into().expect("bailed out earlier; qed");
self.ext.set_immutable_data(data)?;
Ok(())
}
fn balance(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
self.charge_gas(RuntimeCosts::Balance)?;
Ok(self.write_fixed_sandbox_output(
memory,
out_ptr,
&self.ext.balance().to_little_endian(),
false,
already_charged,
)?)
}
fn balance_of(
&mut self,
memory: &mut M,
addr_ptr: u32,
out_ptr: u32,
) -> Result<(), TrapReason> {
self.charge_gas(RuntimeCosts::BalanceOf)?;
let address = memory.read_h160(addr_ptr)?;
Ok(self.write_fixed_sandbox_output(
memory,
out_ptr,
&self.ext.balance_of(&address).to_little_endian(),
false,
already_charged,
)?)
}
fn chain_id(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
Ok(self.write_fixed_sandbox_output(
memory,
out_ptr,
&U256::from(<E::T as Config>::ChainId::get()).to_little_endian(),
false,
|_| Some(RuntimeCosts::CopyToContract(32)),
)?)
}
fn gas_limit(&mut self, memory: &mut M) -> Result<u64, TrapReason> {
self.charge_gas(RuntimeCosts::GasLimit)?;
Ok(self.ext.gas_limit())
}
fn value_transferred(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
self.charge_gas(RuntimeCosts::ValueTransferred)?;
Ok(self.write_fixed_sandbox_output(
memory,
out_ptr,
&self.ext.value_transferred().to_little_endian(),
false,
already_charged,
)?)
}
fn gas_price(&mut self, memory: &mut M) -> Result<u64, TrapReason> {
self.charge_gas(RuntimeCosts::GasPrice)?;
Ok(self.ext.effective_gas_price().saturated_into())
}
fn base_fee(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
self.charge_gas(RuntimeCosts::BaseFee)?;
Ok(self.write_fixed_sandbox_output(
memory,
out_ptr,
&Pallet::<E::T>::evm_base_fee().to_little_endian(),
false,
already_charged,
)?)
}
fn now(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
self.charge_gas(RuntimeCosts::Now)?;
Ok(self.write_fixed_sandbox_output(
memory,
out_ptr,
&self.ext.now().to_little_endian(),
false,
already_charged,
)?)
}
#[mutating]
fn deposit_event(
&mut self,
memory: &mut M,
topics_ptr: u32,
num_topic: u32,
data_ptr: u32,
data_len: u32,
) -> Result<(), TrapReason> {
self.charge_gas(RuntimeCosts::DepositEvent { num_topic, len: data_len })?;
if num_topic > limits::NUM_EVENT_TOPICS {
return Err(Error::<E::T>::TooManyTopics.into());
}
if data_len > limits::EVENT_BYTES {
return Err(Error::<E::T>::ValueTooLarge.into());
}
let topics: Vec<H256> = match num_topic {
0 => Vec::new(),
_ => {
let mut v = Vec::with_capacity(num_topic as usize);
let topics_len = num_topic * H256::len_bytes() as u32;
let buf = memory.read(topics_ptr, topics_len)?;
for chunk in buf.chunks_exact(H256::len_bytes()) {
v.push(H256::from_slice(chunk));
}
v
},
};
let event_data = memory.read(data_ptr, data_len)?;
self.ext.deposit_event(topics, event_data);
Ok(())
}
fn block_number(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
self.charge_gas(RuntimeCosts::BlockNumber)?;
Ok(self.write_fixed_sandbox_output(
memory,
out_ptr,
&self.ext.block_number().to_little_endian(),
false,
already_charged,
)?)
}
fn block_hash(
&mut self,
memory: &mut M,
block_number_ptr: u32,
out_ptr: u32,
) -> Result<(), TrapReason> {
self.charge_gas(RuntimeCosts::BlockHash)?;
let block_number = memory.read_u256(block_number_ptr)?;
let block_hash = self.ext.block_hash(block_number).unwrap_or(H256::zero());
Ok(self.write_fixed_sandbox_output(
memory,
out_ptr,
&block_hash.as_bytes(),
false,
already_charged,
)?)
}
fn block_author(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
self.charge_gas(RuntimeCosts::BlockAuthor)?;
let block_author = self.ext.block_author();
Ok(self.write_fixed_sandbox_output(
memory,
out_ptr,
&block_author.as_bytes(),
false,
already_charged,
)?)
}
fn hash_keccak_256(
&mut self,
memory: &mut M,
input_ptr: u32,
input_len: u32,
output_ptr: u32,
) -> Result<(), TrapReason> {
self.charge_gas(RuntimeCosts::HashKeccak256(input_len))?;
Ok(self.compute_hash_on_intermediate_buffer(
memory, keccak_256, input_ptr, input_len, output_ptr,
)?)
}
fn return_data_size(&mut self, memory: &mut M) -> Result<u64, TrapReason> {
self.charge_gas(RuntimeCosts::ReturnDataSize)?;
Ok(self
.ext
.last_frame_output()
.data
.len()
.try_into()
.expect("usize fits into u64; qed"))
}
fn return_data_copy(
&mut self,
memory: &mut M,
out_ptr: u32,
out_len_ptr: u32,
offset: u32,
) -> Result<(), TrapReason> {
let output = mem::take(self.ext.last_frame_output_mut());
let result = if offset as usize > output.data.len() {
Err(Error::<E::T>::OutOfBounds.into())
} else {
self.write_sandbox_output(
memory,
out_ptr,
out_len_ptr,
&output.data[offset as usize..],
false,
|len| Some(RuntimeCosts::CopyToContract(len)),
)
};
*self.ext.last_frame_output_mut() = output;
Ok(result?)
}
fn ref_time_left(&mut self, memory: &mut M) -> Result<u64, TrapReason> {
self.charge_gas(RuntimeCosts::RefTimeLeft)?;
Ok(self.ext.gas_left())
}
fn consume_all_gas(&mut self, memory: &mut M) -> Result<(), TrapReason> {
self.ext.frame_meter_mut().consume_all_weight();
Err(TrapReason::Return(ReturnData {
flags: ReturnFlags::REVERT.bits(),
data: Default::default(),
}))
}
fn ecdsa_to_eth_address(
&mut self,
memory: &mut M,
key_ptr: u32,
out_ptr: u32,
) -> Result<ReturnErrorCode, TrapReason> {
self.charge_gas(RuntimeCosts::EcdsaToEthAddress)?;
let mut compressed_key: [u8; 33] = [0; 33];
memory.read_into_buf(key_ptr, &mut compressed_key)?;
let result = self.ext.ecdsa_to_eth_address(&compressed_key);
match result {
Ok(eth_address) => {
memory.write(out_ptr, eth_address.as_ref())?;
Ok(ReturnErrorCode::Success)
},
Err(_) => Ok(ReturnErrorCode::EcdsaRecoveryFailed),
}
}
fn sr25519_verify(
&mut self,
memory: &mut M,
signature_ptr: u32,
pub_key_ptr: u32,
message_len: u32,
message_ptr: u32,
) -> Result<ReturnErrorCode, TrapReason> {
self.charge_gas(RuntimeCosts::Sr25519Verify(message_len))?;
let mut signature: [u8; 64] = [0; 64];
memory.read_into_buf(signature_ptr, &mut signature)?;
let mut pub_key: [u8; 32] = [0; 32];
memory.read_into_buf(pub_key_ptr, &mut pub_key)?;
let message: Vec<u8> = memory.read(message_ptr, message_len)?;
if self.ext.sr25519_verify(&signature, &message, &pub_key) {
Ok(ReturnErrorCode::Success)
} else {
Ok(ReturnErrorCode::Sr25519VerifyFailed)
}
}
#[mutating]
fn terminate(&mut self, memory: &mut M, beneficiary_ptr: u32) -> Result<(), TrapReason> {
let charged = self.charge_gas(RuntimeCosts::Terminate { code_removed: true })?;
let beneficiary = memory.read_h160(beneficiary_ptr)?;
if matches!(self.ext.terminate_if_same_tx(&beneficiary)?, crate::CodeRemoved::No) {
self.adjust_gas(charged, RuntimeCosts::Terminate { code_removed: false });
}
Err(TrapReason::Termination)
}
}