Newer
Older
_ => ctx.read_sandbox_memory_as_unbounded(topics_ptr, topics_len)?,
// If there are more than `event_topics`, then trap.
if topics.len() > ctx.ext.schedule().limits.event_topics as usize {
return Err(Error::<E::T>::TooManyTopics.into())
}
// Check for duplicate topics. If there are any, then trap.
// Complexity O(n * log(n)) and no additional allocations.
// This also sorts the topics.
if has_duplicates(&mut topics) {
return Err(Error::<E::T>::DuplicateTopics.into())
let event_data = ctx.read_sandbox_memory(data_ptr, data_len)?;
ctx.ext.deposit_event(topics, event_data);
/// Was used to set rent allowance of the contract.
///
/// # Note
///
/// The state rent functionality was removed. This is stub only exists for
/// backwards compatiblity.
Sasha Gryaznov
committed
#[prefixed_alias]
fn set_rent_allowance(
ctx: Runtime<E>,
_value_ptr: u32,
_value_len: u32,
) -> Result<(), TrapReason> {
ctx.charge_gas(RuntimeCosts::DebugMessage)?;
/// Was used to set rent allowance of the contract.
///
/// # Note
///
/// The state rent functionality was removed. This is stub only exists for
/// backwards compatiblity.
#[version(1)]
Sasha Gryaznov
committed
#[prefixed_alias]
fn set_rent_allowance(ctx: Runtime<E>, _value_ptr: u32) -> Result<(), TrapReason> {
ctx.charge_gas(RuntimeCosts::DebugMessage)?;
/// Was used to store the rent allowance into the supplied buffer.
///
/// # Note
///
/// The state rent functionality was removed. This is stub only exists for
/// backwards compatiblity.
Sasha Gryaznov
committed
#[prefixed_alias]
fn rent_allowance(ctx: Runtime<E>, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> {
ctx.charge_gas(RuntimeCosts::Balance)?;
let rent_allowance = <BalanceOf<E::T>>::max_value().encode();
Alexander Theißen
committed
Ok(ctx.write_sandbox_output(
out_ptr,
out_len_ptr,
&rent_allowance,
false,
already_charged,
Alexander Theißen
committed
)?)
/// Stores the current block number of the current contract into the supplied buffer.
///
/// The value is stored to linear memory at the address pointed to by `out_ptr`.
/// `out_len_ptr` must point to a u32 value that describes the available space at
/// `out_ptr`. This call overwrites it with the size of the value. If the available
/// space at `out_ptr` is less than the size of the value a trap is triggered.
Sasha Gryaznov
committed
#[prefixed_alias]
fn block_number(ctx: Runtime<E>, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> {
ctx.charge_gas(RuntimeCosts::BlockNumber)?;
Alexander Theißen
committed
Ok(ctx.write_sandbox_output(
out_ptr,
out_len_ptr,
&ctx.ext.block_number().encode(),
false,
already_charged,
Alexander Theißen
committed
)?)
/// Computes the SHA2 256-bit hash on the given input buffer.
///
/// Returns the result directly into the given output buffer.
///
/// # Note
///
/// - The `input` and `output` buffer may overlap.
/// - The output buffer is expected to hold at least 32 bytes (256 bits).
/// - It is the callers responsibility to provide an output buffer that is large enough to hold
/// the expected amount of bytes returned by the chosen hash function.
///
/// # Parameters
///
/// - `input_ptr`: the pointer into the linear memory where the input data is placed.
/// - `input_len`: the length of the input data in bytes.
/// - `output_ptr`: the pointer into the linear memory where the output data is placed. The
/// function will write the result directly into this buffer.
Sasha Gryaznov
committed
#[prefixed_alias]
fn hash_sha2_256(
ctx: Runtime<E>,
input_ptr: u32,
input_len: u32,
output_ptr: u32,
) -> Result<(), TrapReason> {
ctx.charge_gas(RuntimeCosts::HashSha256(input_len))?;
Alexander Theißen
committed
Ok(ctx.compute_hash_on_intermediate_buffer(sha2_256, input_ptr, input_len, output_ptr)?)
/// Computes the KECCAK 256-bit hash on the given input buffer.
///
/// Returns the result directly into the given output buffer.
///
/// # Note
///
/// - The `input` and `output` buffer may overlap.
/// - The output buffer is expected to hold at least 32 bytes (256 bits).
/// - It is the callers responsibility to provide an output buffer that is large enough to hold
/// the expected amount of bytes returned by the chosen hash function.
///
/// # Parameters
///
/// - `input_ptr`: the pointer into the linear memory where the input data is placed.
/// - `input_len`: the length of the input data in bytes.
/// - `output_ptr`: the pointer into the linear memory where the output data is placed. The
/// function will write the result directly into this buffer.
Sasha Gryaznov
committed
#[prefixed_alias]
fn hash_keccak_256(
ctx: Runtime<E>,
input_ptr: u32,
input_len: u32,
output_ptr: u32,
) -> Result<(), TrapReason> {
ctx.charge_gas(RuntimeCosts::HashKeccak256(input_len))?;
Alexander Theißen
committed
Ok(ctx.compute_hash_on_intermediate_buffer(keccak_256, input_ptr, input_len, output_ptr)?)
/// Computes the BLAKE2 256-bit hash on the given input buffer.
///
/// Returns the result directly into the given output buffer.
///
/// # Note
///
/// - The `input` and `output` buffer may overlap.
/// - The output buffer is expected to hold at least 32 bytes (256 bits).
/// - It is the callers responsibility to provide an output buffer that is large enough to hold
/// the expected amount of bytes returned by the chosen hash function.
///
/// # Parameters
///
/// - `input_ptr`: the pointer into the linear memory where the input data is placed.
/// - `input_len`: the length of the input data in bytes.
/// - `output_ptr`: the pointer into the linear memory where the output data is placed. The
/// function will write the result directly into this buffer.
Sasha Gryaznov
committed
#[prefixed_alias]
fn hash_blake2_256(
ctx: Runtime<E>,
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(blake2_256, input_ptr, input_len, output_ptr)?)
/// Computes the BLAKE2 128-bit hash on the given input buffer.
///
/// Returns the result directly into the given output buffer.
///
/// # Note
///
/// - The `input` and `output` buffer may overlap.
/// - The output buffer is expected to hold at least 16 bytes (128 bits).
/// - It is the callers responsibility to provide an output buffer that is large enough to hold
/// the expected amount of bytes returned by the chosen hash function.
///
/// # Parameters
///
/// - `input_ptr`: the pointer into the linear memory where the input data is placed.
/// - `input_len`: the length of the input data in bytes.
/// - `output_ptr`: the pointer into the linear memory where the output data is placed. The
/// function will write the result directly into this buffer.
Sasha Gryaznov
committed
#[prefixed_alias]
fn hash_blake2_128(
ctx: Runtime<E>,
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(blake2_128, input_ptr, input_len, output_ptr)?)
/// Call into the chain extension provided by the chain if any.
///
/// Handling of the input values is up to the specific chain extension and so is the
/// return value. The extension can decide to use the inputs as primitive inputs or as
/// in/out arguments by interpreting them as pointers. Any caller of this function
/// must therefore coordinate with the chain that it targets.
///
/// # Note
///
/// If no chain extension exists the contract will trap with the `NoChainExtension`
/// module error.
Sasha Gryaznov
committed
#[prefixed_alias]
fn call_chain_extension(
ctx: Runtime<E>,
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"
);
let env = Environment::new(ctx, id, input_ptr, input_len, output_ptr, output_len_ptr);
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.
/// Specifying invalid UTF-8 triggers a 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(
ctx: Runtime<E>,
str_ptr: u32,
str_len: u32,
) -> Result<ReturnCode, TrapReason> {
ctx.charge_gas(RuntimeCosts::DebugMessage)?;
if ctx.ext.append_debug_buffer("") {
let data = ctx.read_sandbox_memory(str_ptr, str_len)?;
let msg =
core::str::from_utf8(&data).map_err(|_| <Error<E::T>>::DebugMessageInvalidUTF8)?;
ctx.ext.append_debug_buffer(msg);
return Ok(ReturnCode::Success)
}
Ok(ReturnCode::LoggingDisabled)
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
/// Call some dispatchable of the runtime.
///
/// This function decodes the passed in data as the overarching `Call` type of the
/// runtime and dispatches it. The weight as specified in the runtime is charged
/// from the gas meter. Any weight refunds made by the dispatchable are considered.
///
/// The filter specified by `Config::CallFilter` is attached to the origin of
/// the dispatched call.
///
/// # Parameters
///
/// - `input_ptr`: the pointer into the linear memory where the input data is placed.
/// - `input_len`: the length of the input data in bytes.
///
/// # Return Value
///
/// Returns `ReturnCode::Success` when the dispatchable was succesfully executed and
/// returned `Ok`. When the dispatchable was exeuted but returned an error
/// `ReturnCode::CallRuntimeReturnedError` is returned. The full error is not
/// provided because it is not guaranteed to be stable.
///
/// # Comparison with `ChainExtension`
///
/// Just as a chain extension this API allows the runtime to extend the functionality
/// of contracts. While making use of this function is generelly easier it cannot be
/// used in call cases. Consider writing a chain extension if you need to do perform
/// one of the following tasks:
///
/// - Return data.
/// - Provide functionality **exclusively** to contracts.
/// - Provide custom weights.
/// - Avoid the need to keep the `Call` data structure stable.
///
/// # Unstable
///
/// This function is unstable and subject to change (or removal) in the future. Do not
/// deploy a contract using it to a production chain.
#[unstable]
Sasha Gryaznov
committed
#[prefixed_alias]
fn call_runtime(
ctx: Runtime<E>,
call_ptr: u32,
call_len: u32,
) -> Result<ReturnCode, TrapReason> {
use frame_support::dispatch::{extract_actual_weight, GetDispatchInfo};
ctx.charge_gas(RuntimeCosts::CopyFromContract(call_len))?;
let call: <E::T as Config>::RuntimeCall =
ctx.read_sandbox_memory_as_unbounded(call_ptr, call_len)?;
let dispatch_info = call.get_dispatch_info();
let charged = ctx.charge_gas(RuntimeCosts::CallRuntime(dispatch_info.weight))?;
let result = ctx.ext.call_runtime(call);
let actual_weight = extract_actual_weight(&result, &dispatch_info);
ctx.adjust_gas(charged, RuntimeCosts::CallRuntime(actual_weight));
match result {
Ok(_) => Ok(ReturnCode::Success),
Err(_) => Ok(ReturnCode::CallRuntimeReturnedError),
}
/// Recovers the ECDSA public key from the given message hash and signature.
///
/// Writes the public key into the given output buffer.
/// Assumes the secp256k1 curve.
///
/// # Parameters
///
/// - `signature_ptr`: the pointer into the linear memory where the signature is placed. Should
/// be decodable as a 65 bytes. Traps otherwise.
/// - `message_hash_ptr`: the pointer into the linear memory where the message hash is placed.
/// Should be decodable as a 32 bytes. Traps otherwise.
/// - `output_ptr`: the pointer into the linear memory where the output data is placed. The
/// buffer should be 33 bytes. The function will write the result directly into this buffer.
///
/// # Errors
///
/// `ReturnCode::EcdsaRecoverFailed`
Sasha Gryaznov
committed
#[prefixed_alias]
fn ecdsa_recover(
ctx: Runtime<E>,
signature_ptr: u32,
message_hash_ptr: u32,
output_ptr: u32,
) -> Result<ReturnCode, TrapReason> {
ctx.charge_gas(RuntimeCosts::EcdsaRecovery)?;
let mut signature: [u8; 65] = [0; 65];
ctx.read_sandbox_memory_into_buf(signature_ptr, &mut signature)?;
let mut message_hash: [u8; 32] = [0; 32];
ctx.read_sandbox_memory_into_buf(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.
ctx.write_sandbox_memory(output_ptr, pub_key.as_ref())?;
Ok(ReturnCode::Success)
},
Err(_) => Ok(ReturnCode::EcdsaRecoverFailed),
}
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
/// Replace the contract code at the specified address with new code.
///
/// # Note
///
/// There are a couple of important considerations which must be taken into account when
/// using this API:
///
/// 1. The storage at the code address will remain untouched. This means that contract
/// developers must ensure that the storage layout of the new code is compatible with that of
/// the old code.
///
/// 2. Contracts using this API can't be assumed as having deterministic addresses. Said another
/// way, when using this API you lose the guarantee that an address always identifies a specific
/// code hash.
/// 3. If a contract calls into itself after changing its code the new call would use
/// the new code. However, if the original caller panics after returning from the sub call it
/// would revert the changes made by `seal_set_code_hash` and the next caller would use
/// the old code.
///
/// # Parameters
///
/// - `code_hash_ptr`: A pointer to the buffer that contains the new code hash.
///
/// # Errors
///
/// `ReturnCode::CodeNotFound`
Sasha Gryaznov
committed
#[prefixed_alias]
fn set_code_hash(ctx: Runtime<E>, code_hash_ptr: u32) -> Result<ReturnCode, TrapReason> {
ctx.charge_gas(RuntimeCosts::SetCodeHash)?;
let code_hash: CodeHash<<E as Ext>::T> = ctx.read_sandbox_memory_as(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(ReturnCode::Success),
Sasha Gryaznov
committed
/// Calculates Ethereum address from the ECDSA compressed public key and stores
/// it into the supplied buffer.
///
/// # Parameters
///
/// - `key_ptr`: a pointer to the ECDSA compressed public key. Should be decodable as a 33 bytes
/// value. Traps otherwise.
/// - `out_ptr`: the pointer into the linear memory where the output data is placed. The
/// function will write the result directly into this buffer.
///
/// The value is stored to linear memory at the address pointed to by `out_ptr`.
/// If the available space at `out_ptr` is less than the size of the value a trap is triggered.
///
/// # Errors
///
/// `ReturnCode::EcdsaRecoverFailed`
Sasha Gryaznov
committed
#[prefixed_alias]
fn ecdsa_to_eth_address(
ctx: Runtime<E>,
key_ptr: u32,
out_ptr: u32,
) -> Result<ReturnCode, TrapReason> {
Sasha Gryaznov
committed
ctx.charge_gas(RuntimeCosts::EcdsaToEthAddress)?;
let mut compressed_key: [u8; 33] = [0; 33];
Sasha Gryaznov
committed
ctx.read_sandbox_memory_into_buf(key_ptr, &mut compressed_key)?;
let result = ctx.ext.ecdsa_to_eth_address(&compressed_key);
match result {
Ok(eth_address) => {
ctx.write_sandbox_memory(out_ptr, eth_address.as_ref())?;
Ok(ReturnCode::Success)
},
Err(_) => Ok(ReturnCode::EcdsaRecoverFailed),
}