Newer
Older
) -> Result<(), TrapReason> {
ctx.charge_gas(RuntimeCosts::HashKeccak256(input_len))?;
Alexander Theißen
committed
Ok(ctx.compute_hash_on_intermediate_buffer(
memory, keccak_256, input_ptr, input_len, output_ptr,
)?)
/// Computes the BLAKE2 256-bit hash on the given input buffer.
/// See [`pallet_contracts_uapi::HostFn::hash_blake2_256`].
Sasha Gryaznov
committed
#[prefixed_alias]
fn hash_blake2_256(
Alexander Theißen
committed
ctx: _,
memory: _,
input_ptr: u32,
input_len: u32,
output_ptr: u32,
) -> Result<(), TrapReason> {
ctx.charge_gas(RuntimeCosts::HashBlake256(input_len))?;
Alexander Theißen
committed
Ok(ctx.compute_hash_on_intermediate_buffer(
memory, blake2_256, input_ptr, input_len, output_ptr,
)?)
/// 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(), |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,
output_ptr: u32,
) -> Result<ReturnErrorCode, TrapReason> {
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
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)?;
let execute_weight =
<<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() };
ensure_executable::<E::T>(&message)?;
ctx.call_dispatchable::<XcmExecutionFailed, _>(dispatch_info, |ctx| {
let origin = crate::RawOrigin::Signed(ctx.ext.address().clone()).into();
let outcome = <<E::T as Config>::Xcm>::execute(
origin,
Box::new(message),
weight.saturating_sub(execute_weight),
)?;
ctx.write_sandbox_memory(memory, output_ptr, &outcome.encode())?;
let pre_dispatch_weight =
<<E::T as Config>::Xcm as ExecuteController<_, _>>::WeightInfo::execute();
Ok(Some(outcome.weight_used().saturating_add(pre_dispatch_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::{VersionedMultiLocation, VersionedXcm};
use xcm_builder::{SendController, SendControllerWeightInfo};
ctx.charge_gas(RuntimeCosts::CopyFromContract(msg_len))?;
let dest: VersionedMultiLocation = 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`].
#[unstable]
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::InstantationNonce)?;
Ok(ctx.ext.nonce())
}
/// Adds a new delegate dependency to the contract.
/// See [`pallet_contracts_uapi::HostFn::add_delegate_dependency`].
#[unstable]
fn add_delegate_dependency(ctx: _, memory: _, code_hash_ptr: u32) -> Result<(), TrapReason> {
ctx.charge_gas(RuntimeCosts::AddDelegateDependency)?;
let code_hash = ctx.read_sandbox_memory_as(memory, code_hash_ptr)?;
ctx.ext.add_delegate_dependency(code_hash)?;
Ok(())
}
/// Removes the delegate dependency from the contract.
/// see [`pallet_contracts_uapi::HostFn::remove_delegate_dependency`].
#[unstable]
fn remove_delegate_dependency(ctx: _, memory: _, code_hash_ptr: u32) -> Result<(), TrapReason> {
ctx.charge_gas(RuntimeCosts::RemoveDelegateDependency)?;
let code_hash = ctx.read_sandbox_memory_as(memory, code_hash_ptr)?;
ctx.ext.remove_delegate_dependency(&code_hash)?;
Ok(())
}