Newer
Older
Alexander Theißen
committed
)?)
/// Computes the BLAKE2 128-bit hash on the given input buffer.
/// See [`pallet_contracts_uapi::HostFn::hash_blake2_128`].
Sasha Gryaznov
committed
#[prefixed_alias]
fn hash_blake2_128(
Alexander Theißen
committed
ctx: _,
memory: _,
input_ptr: u32,
input_len: u32,
output_ptr: u32,
) -> Result<(), TrapReason> {
ctx.charge_gas(RuntimeCosts::HashBlake128(input_len))?;
Alexander Theißen
committed
Ok(ctx.compute_hash_on_intermediate_buffer(
memory, blake2_128, input_ptr, input_len, output_ptr,
)?)
/// Call into the chain extension provided by the chain if any.
/// See [`pallet_contracts_uapi::HostFn::call_chain_extension`].
Sasha Gryaznov
committed
#[prefixed_alias]
fn call_chain_extension(
Alexander Theißen
committed
ctx: _,
memory: _,
Alexander Theißen
committed
id: u32,
input_ptr: u32,
input_len: u32,
output_ptr: u32,
output_len_ptr: u32,
) -> Result<u32, TrapReason> {
use crate::chain_extension::{ChainExtension, Environment, RetVal};
if !<E::T as Config>::ChainExtension::enabled() {
return Err(Error::<E::T>::NoChainExtension.into())
}
Alexander Theißen
committed
let mut chain_extension = ctx.chain_extension.take().expect(
"Constructor initializes with `Some`. This is the only place where it is set to `None`.\
It is always reset to `Some` afterwards. qed"
);
Alexander Theißen
committed
let env =
Environment::new(ctx, memory, id, input_ptr, input_len, output_ptr, output_len_ptr);
Alexander Theißen
committed
let ret = match chain_extension.call(env)? {
RetVal::Converging(val) => Ok(val),
RetVal::Diverging { flags, data } =>
Err(TrapReason::Return(ReturnData { flags: flags.bits(), data })),
Alexander Theißen
committed
};
ctx.chain_extension = Some(chain_extension);
ret
/// Emit a custom debug message.
///
/// No newlines are added to the supplied message.
Sasha Gryaznov
committed
/// Specifying invalid UTF-8 just drops the message with no trap.
///
/// This is a no-op if debug message recording is disabled which is always the case
/// when the code is executing on-chain. The message is interpreted as UTF-8 and
/// appended to the debug buffer which is then supplied to the calling RPC client.
///
/// # Note
///
/// Even though no action is taken when debug message recording is disabled there is still
/// a non trivial overhead (and weight cost) associated with calling this function. Contract
/// languages should remove calls to this function (either at runtime or compile time) when
/// not being executed as an RPC. For example, they could allow users to disable logging
/// through compile time flags (cargo features) for on-chain deployment. Additionally, the
/// return value of this function can be cached in order to prevent further calls at runtime.
Sasha Gryaznov
committed
#[prefixed_alias]
fn debug_message(
Alexander Theißen
committed
ctx: _,
memory: _,
str_ptr: u32,
str_len: u32,
) -> Result<ReturnErrorCode, TrapReason> {
Sasha Gryaznov
committed
let str_len = str_len.min(DebugBufferVec::<E::T>::bound() as u32);
ctx.charge_gas(RuntimeCosts::DebugMessage(str_len))?;
if ctx.ext.append_debug_buffer("") {
Alexander Theißen
committed
let data = ctx.read_sandbox_memory(memory, str_ptr, str_len)?;
Sasha Gryaznov
committed
if let Some(msg) = core::str::from_utf8(&data).ok() {
ctx.ext.append_debug_buffer(msg);
}
/// Call some dispatchable of the runtime.
/// See [`frame_support::traits::call_runtime`].
Sasha Gryaznov
committed
fn call_runtime(
Alexander Theißen
committed
ctx: _,
memory: _,
call_ptr: u32,
call_len: u32,
) -> Result<ReturnErrorCode, TrapReason> {
use frame_support::dispatch::GetDispatchInfo;
ctx.charge_gas(RuntimeCosts::CopyFromContract(call_len))?;
let call: <E::T as Config>::RuntimeCall =
Alexander Theißen
committed
ctx.read_sandbox_memory_as_unbounded(memory, call_ptr, call_len)?;
ctx.call_dispatchable::<CallRuntimeFailed>(
call.get_dispatch_info(),
RuntimeCosts::CallRuntime,
|ctx| ctx.ext.call_runtime(call),
)
}
/// Execute an XCM program locally, using the contract's address as the origin.
/// See [`pallet_contracts_uapi::HostFn::execute_xcm`].
#[unstable]
fn xcm_execute(
ctx: _,
memory: _,
msg_ptr: u32,
msg_len: u32,
) -> Result<ReturnErrorCode, TrapReason> {
use frame_support::dispatch::DispatchInfo;
use xcm::VersionedXcm;
use xcm_builder::{ExecuteController, ExecuteControllerWeightInfo};
ctx.charge_gas(RuntimeCosts::CopyFromContract(msg_len))?;
let message: VersionedXcm<CallOf<E::T>> =
ctx.read_sandbox_memory_as_unbounded(memory, msg_ptr, msg_len)?;
ensure_executable::<E::T>(&message)?;
<<E::T as Config>::Xcm as ExecuteController<_, _>>::WeightInfo::execute();
let weight = ctx.ext.gas_meter().gas_left().max(execute_weight);
let dispatch_info = DispatchInfo { weight, ..Default::default() };
ctx.call_dispatchable::<XcmExecutionFailed>(
dispatch_info,
RuntimeCosts::CallXcmExecute,
|ctx| {
let origin = crate::RawOrigin::Signed(ctx.ext.address().clone()).into();
let weight_used = <<E::T as Config>::Xcm>::execute(
weight.saturating_sub(execute_weight),
)?;
Ok(Some(weight_used.saturating_add(execute_weight)).into())
},
)
}
/// Send an XCM program from the contract to the specified destination.
/// See [`pallet_contracts_uapi::HostFn::send_xcm`].
#[unstable]
fn xcm_send(
ctx: _,
memory: _,
dest_ptr: u32,
msg_ptr: u32,
msg_len: u32,
output_ptr: u32,
) -> Result<ReturnErrorCode, TrapReason> {
use xcm::{VersionedLocation, VersionedXcm};
use xcm_builder::{SendController, SendControllerWeightInfo};
ctx.charge_gas(RuntimeCosts::CopyFromContract(msg_len))?;
let dest: VersionedLocation = ctx.read_sandbox_memory_as(memory, dest_ptr)?;
let message: VersionedXcm<()> =
ctx.read_sandbox_memory_as_unbounded(memory, msg_ptr, msg_len)?;
let weight = <<E::T as Config>::Xcm as SendController<_>>::WeightInfo::send();
ctx.charge_gas(RuntimeCosts::CallRuntime(weight))?;
let origin = crate::RawOrigin::Signed(ctx.ext.address().clone()).into();
match <<E::T as Config>::Xcm>::send(origin, dest.into(), message.into()) {
Ok(message_id) => {
ctx.write_sandbox_memory(memory, output_ptr, &message_id.encode())?;
Err(e) => {
if ctx.ext.append_debug_buffer("") {
ctx.ext.append_debug_buffer("seal0::xcm_send failed with: ");
ctx.ext.append_debug_buffer(e.into());
};
Ok(ReturnErrorCode::XcmSendFailed)
}
/// Recovers the ECDSA public key from the given message hash and signature.
/// See [`pallet_contracts_uapi::HostFn::ecdsa_recover`].
Sasha Gryaznov
committed
#[prefixed_alias]
fn ecdsa_recover(
Alexander Theißen
committed
ctx: _,
memory: _,
signature_ptr: u32,
message_hash_ptr: u32,
output_ptr: u32,
) -> Result<ReturnErrorCode, TrapReason> {
ctx.charge_gas(RuntimeCosts::EcdsaRecovery)?;
let mut signature: [u8; 65] = [0; 65];
Alexander Theißen
committed
ctx.read_sandbox_memory_into_buf(memory, signature_ptr, &mut signature)?;
let mut message_hash: [u8; 32] = [0; 32];
Alexander Theißen
committed
ctx.read_sandbox_memory_into_buf(memory, message_hash_ptr, &mut message_hash)?;
let result = ctx.ext.ecdsa_recover(&signature, &message_hash);
match result {
Ok(pub_key) => {
// Write the recovered compressed ecdsa public key back into the sandboxed output
// buffer.
Alexander Theißen
committed
ctx.write_sandbox_memory(memory, output_ptr, pub_key.as_ref())?;
},
Err(_) => Ok(ReturnErrorCode::EcdsaRecoveryFailed),
}
/// See [`pallet_contracts_uapi::HostFn::sr25519_verify`].
fn sr25519_verify(
ctx: _,
memory: _,
signature_ptr: u32,
pub_key_ptr: u32,
message_len: u32,
message_ptr: u32,
) -> Result<ReturnErrorCode, TrapReason> {
ctx.charge_gas(RuntimeCosts::Sr25519Verify(message_len))?;
let mut signature: [u8; 64] = [0; 64];
ctx.read_sandbox_memory_into_buf(memory, signature_ptr, &mut signature)?;
let mut pub_key: [u8; 32] = [0; 32];
ctx.read_sandbox_memory_into_buf(memory, pub_key_ptr, &mut pub_key)?;
let message: Vec<u8> = ctx.read_sandbox_memory(memory, message_ptr, message_len)?;
if ctx.ext.sr25519_verify(&signature, &message, &pub_key) {
Ok(ReturnErrorCode::Sr25519VerifyFailed)
/// Replace the contract code at the specified address with new code.
/// See [`pallet_contracts_uapi::HostFn::set_code_hash`].
Sasha Gryaznov
committed
#[prefixed_alias]
fn set_code_hash(ctx: _, memory: _, code_hash_ptr: u32) -> Result<ReturnErrorCode, TrapReason> {
ctx.charge_gas(RuntimeCosts::SetCodeHash)?;
Alexander Theißen
committed
let code_hash: CodeHash<<E as Ext>::T> =
ctx.read_sandbox_memory_as(memory, code_hash_ptr)?;
match ctx.ext.set_code_hash(code_hash) {
Err(err) => {
let code = Runtime::<E>::err_into_return_code(err)?;
Ok(code)
},
Ok(()) => Ok(ReturnErrorCode::Success),
Sasha Gryaznov
committed
/// Calculates Ethereum address from the ECDSA compressed public key and stores
/// See [`pallet_contracts_uapi::HostFn::ecdsa_to_eth_address`].
Sasha Gryaznov
committed
#[prefixed_alias]
fn ecdsa_to_eth_address(
Alexander Theißen
committed
ctx: _,
memory: _,
key_ptr: u32,
out_ptr: u32,
) -> Result<ReturnErrorCode, TrapReason> {
Sasha Gryaznov
committed
ctx.charge_gas(RuntimeCosts::EcdsaToEthAddress)?;
let mut compressed_key: [u8; 33] = [0; 33];
Alexander Theißen
committed
ctx.read_sandbox_memory_into_buf(memory, key_ptr, &mut compressed_key)?;
Sasha Gryaznov
committed
let result = ctx.ext.ecdsa_to_eth_address(&compressed_key);
match result {
Ok(eth_address) => {
Alexander Theißen
committed
ctx.write_sandbox_memory(memory, out_ptr, eth_address.as_ref())?;
Sasha Gryaznov
committed
},
Err(_) => Ok(ReturnErrorCode::EcdsaRecoveryFailed),
Sasha Gryaznov
committed
}
/// Returns the number of times the currently executing contract exists on the call stack in
/// addition to the calling instance.
/// See [`pallet_contracts_uapi::HostFn::reentrance_count`].
#[unstable]
Alexander Theißen
committed
fn reentrance_count(ctx: _, memory: _) -> Result<u32, TrapReason> {
ctx.charge_gas(RuntimeCosts::ReentrantCount)?;
Alexander Theißen
committed
Ok(ctx.ext.reentrance_count())
}
/// Returns the number of times specified contract exists on the call stack. Delegated calls are
/// not counted as separate calls.
/// See [`pallet_contracts_uapi::HostFn::account_reentrance_count`].
#[unstable]
Alexander Theißen
committed
fn account_reentrance_count(ctx: _, memory: _, account_ptr: u32) -> Result<u32, TrapReason> {
ctx.charge_gas(RuntimeCosts::AccountEntranceCount)?;
let account_id: <<E as Ext>::T as frame_system::Config>::AccountId =
Alexander Theißen
committed
ctx.read_sandbox_memory_as(memory, account_ptr)?;
Ok(ctx.ext.account_reentrance_count(&account_id))
}
/// Returns a nonce that is unique per contract instantiation.
/// See [`pallet_contracts_uapi::HostFn::instantiation_nonce`].
fn instantiation_nonce(ctx: _, _memory: _) -> Result<u64, TrapReason> {
ctx.charge_gas(RuntimeCosts::InstantiationNonce)?;
Ok(ctx.ext.nonce())
}
/// Adds a new delegate dependency to the contract.
/// See [`pallet_contracts_uapi::HostFn::lock_delegate_dependency`].
fn lock_delegate_dependency(ctx: _, memory: _, code_hash_ptr: u32) -> Result<(), TrapReason> {
ctx.charge_gas(RuntimeCosts::LockDelegateDependency)?;
let code_hash = ctx.read_sandbox_memory_as(memory, code_hash_ptr)?;
ctx.ext.lock_delegate_dependency(code_hash)?;
Ok(())
}
/// Removes the delegate dependency from the contract.
/// see [`pallet_contracts_uapi::HostFn::unlock_delegate_dependency`].
fn unlock_delegate_dependency(ctx: _, memory: _, code_hash_ptr: u32) -> Result<(), TrapReason> {
ctx.charge_gas(RuntimeCosts::UnlockDelegateDependency)?;
let code_hash = ctx.read_sandbox_memory_as(memory, code_hash_ptr)?;
ctx.ext.unlock_delegate_dependency(&code_hash)?;