Newer
Older
ctx.charge_gas(RuntimeCosts::GasLeft)?;
Sasha Gryaznov
committed
let gas_left = &ctx.ext.gas_meter().gas_left().encode();
Alexander Theißen
committed
Ok(ctx.write_sandbox_output(
memory,
out_ptr,
out_len_ptr,
gas_left,
false,
already_charged,
)?)
Sasha Gryaznov
committed
/// Stores the *free* balance of the current account 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
/// The data is encoded as `T::Balance`.
Sasha Gryaznov
committed
#[prefixed_alias]
Alexander Theißen
committed
fn balance(ctx: _, memory: _, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> {
ctx.charge_gas(RuntimeCosts::Balance)?;
Alexander Theißen
committed
Ok(ctx.write_sandbox_output(
Alexander Theißen
committed
memory,
out_ptr,
out_len_ptr,
&ctx.ext.balance().encode(),
false,
already_charged,
Alexander Theißen
committed
)?)
/// Stores the value transferred along with this call/instantiate into the supplied buffer.
///
/// The value is stored to linear memory at the address pointed to by `out_ptr`.
Sasha Gryaznov
committed
/// `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
/// The data is encoded as `T::Balance`.
Sasha Gryaznov
committed
#[prefixed_alias]
fn value_transferred(
Alexander Theißen
committed
ctx: _,
memory: _,
out_ptr: u32,
out_len_ptr: u32,
) -> Result<(), TrapReason> {
ctx.charge_gas(RuntimeCosts::ValueTransferred)?;
Alexander Theißen
committed
Ok(ctx.write_sandbox_output(
Alexander Theißen
committed
memory,
out_ptr,
out_len_ptr,
&ctx.ext.value_transferred().encode(),
false,
already_charged,
Alexander Theißen
committed
)?)
/// Stores a random number for the current block and the given subject 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
/// The data is encoded as `T::Hash`.
Sasha Gryaznov
committed
#[prefixed_alias]
Sasha Gryaznov
committed
fn random(
Alexander Theißen
committed
ctx: _,
memory: _,
subject_ptr: u32,
subject_len: u32,
out_ptr: u32,
out_len_ptr: u32,
) -> Result<(), TrapReason> {
ctx.charge_gas(RuntimeCosts::Random)?;
if subject_len > ctx.ext.schedule().limits.subject_len {
return Err(Error::<E::T>::RandomSubjectTooLong.into())
Alexander Theißen
committed
let subject_buf = ctx.read_sandbox_memory(memory, subject_ptr, subject_len)?;
Ok(ctx.write_sandbox_output(
Alexander Theißen
committed
memory,
out_ptr,
out_len_ptr,
&ctx.ext.random(&subject_buf).0.encode(),
false,
already_charged,
/// Stores a random number for the current block and the given subject 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.
///
/// The data is encoded as (T::Hash, frame_system::pallet_prelude::BlockNumberFor::<T>).
///
/// # Changes from v0
///
/// In addition to the seed it returns the block number since which it was determinable
/// by chain observers.
///
/// # Note
///
/// The returned seed should only be used to distinguish commitments made before
/// the returned block number. If the block number is too early (i.e. commitments were
/// made afterwards), then ensure no further commitments may be made and repeatedly
/// call this on later blocks until the block number returned is later than the latest
/// commitment.
#[version(1)]
Sasha Gryaznov
committed
#[prefixed_alias]
Sasha Gryaznov
committed
fn random(
Alexander Theißen
committed
ctx: _,
memory: _,
subject_ptr: u32,
subject_len: u32,
out_ptr: u32,
out_len_ptr: u32,
) -> Result<(), TrapReason> {
ctx.charge_gas(RuntimeCosts::Random)?;
if subject_len > ctx.ext.schedule().limits.subject_len {
return Err(Error::<E::T>::RandomSubjectTooLong.into())
Alexander Theißen
committed
let subject_buf = ctx.read_sandbox_memory(memory, subject_ptr, subject_len)?;
Alexander Theißen
committed
Ok(ctx.write_sandbox_output(
Alexander Theißen
committed
memory,
out_ptr,
out_len_ptr,
&ctx.ext.random(&subject_buf).encode(),
false,
already_charged,
Alexander Theißen
committed
)?)
/// Load the latest block timestamp 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]
Alexander Theißen
committed
fn now(ctx: _, memory: _, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> {
ctx.charge_gas(RuntimeCosts::Now)?;
Alexander Theißen
committed
Ok(ctx.write_sandbox_output(
Alexander Theißen
committed
memory,
out_ptr,
out_len_ptr,
&ctx.ext.now().encode(),
false,
already_charged,
Alexander Theißen
committed
)?)
/// Stores the minimum balance (a.k.a. existential deposit) into the supplied buffer.
///
Sasha Gryaznov
committed
/// The data is encoded as `T::Balance`.
Sasha Gryaznov
committed
#[prefixed_alias]
Alexander Theißen
committed
fn minimum_balance(
ctx: _,
memory: _,
out_ptr: u32,
out_len_ptr: u32,
) -> Result<(), TrapReason> {
ctx.charge_gas(RuntimeCosts::MinimumBalance)?;
Alexander Theißen
committed
Ok(ctx.write_sandbox_output(
Alexander Theißen
committed
memory,
out_ptr,
out_len_ptr,
&ctx.ext.minimum_balance().encode(),
false,
already_charged,
Alexander Theißen
committed
)?)
/// Stores the tombstone deposit 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
/// There is no longer a tombstone deposit. This function always returns `0`.
Sasha Gryaznov
committed
#[prefixed_alias]
Sasha Gryaznov
committed
fn tombstone_deposit(
Alexander Theißen
committed
ctx: _,
memory: _,
out_ptr: u32,
out_len_ptr: u32,
) -> Result<(), TrapReason> {
ctx.charge_gas(RuntimeCosts::Balance)?;
let deposit = <BalanceOf<E::T>>::zero().encode();
Alexander Theißen
committed
Ok(ctx.write_sandbox_output(
memory,
out_ptr,
out_len_ptr,
&deposit,
false,
already_charged,
)?)
/// Was used to restore the given destination contract sacrificing the caller.
///
/// # Note
///
/// The state rent functionality was removed. This is stub only exists for
Sasha Gryaznov
committed
#[prefixed_alias]
Sasha Gryaznov
committed
fn restore_to(
Alexander Theißen
committed
ctx: _,
memory: _,
_rent_allowance_len: u32,
_delta_count: u32,
) -> Result<(), TrapReason> {
Sasha Gryaznov
committed
ctx.charge_gas(RuntimeCosts::DebugMessage(0))?;
/// Was used to restore the given destination contract sacrificing the caller.
///
/// # Note
///
/// The state rent functionality was removed. This is stub only exists for
#[version(1)]
Sasha Gryaznov
committed
#[prefixed_alias]
Sasha Gryaznov
committed
fn restore_to(
Alexander Theißen
committed
ctx: _,
memory: _,
_dest_ptr: u32,
_code_hash_ptr: u32,
_rent_allowance_ptr: u32,
_delta_ptr: u32,
_delta_count: u32,
) -> Result<(), TrapReason> {
Sasha Gryaznov
committed
ctx.charge_gas(RuntimeCosts::DebugMessage(0))?;
/// Was used to set rent allowance of the contract.
///
/// # Note
///
/// The state rent functionality was removed. This is stub only exists for
Sasha Gryaznov
committed
#[prefixed_alias]
Sasha Gryaznov
committed
fn set_rent_allowance(
Alexander Theißen
committed
ctx: _,
memory: _,
_value_ptr: u32,
_value_len: u32,
) -> Result<(), TrapReason> {
Sasha Gryaznov
committed
ctx.charge_gas(RuntimeCosts::DebugMessage(0))?;
/// Was used to set rent allowance of the contract.
///
/// # Note
///
/// The state rent functionality was removed. This is stub only exists for
#[version(1)]
Sasha Gryaznov
committed
#[prefixed_alias]
Alexander Theißen
committed
fn set_rent_allowance(ctx: _, _memory: _, _value_ptr: u32) -> Result<(), TrapReason> {
Sasha Gryaznov
committed
ctx.charge_gas(RuntimeCosts::DebugMessage(0))?;
/// Was used to store the rent allowance into the supplied buffer.
///
/// # Note
///
/// The state rent functionality was removed. This is stub only exists for
Sasha Gryaznov
committed
#[prefixed_alias]
Alexander Theißen
committed
fn rent_allowance(ctx: _, memory: _, 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(
Alexander Theißen
committed
memory,
out_ptr,
out_len_ptr,
&rent_allowance,
false,
already_charged,
Alexander Theißen
committed
)?)
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
/// Deposit a contract event with the data buffer and optional list of topics. There is a limit
/// on the maximum number of topics specified by `event_topics`.
///
/// - `topics_ptr`: a pointer to the buffer of topics encoded as `Vec<T::Hash>`. The value of
/// this is ignored if `topics_len` is set to `0`. The topics list can't contain duplicates.
/// - `topics_len`: the length of the topics buffer. Pass 0 if you want to pass an empty
/// vector.
/// - `data_ptr`: a pointer to a raw data buffer which will saved along the event.
/// - `data_len`: the length of the data buffer.
#[prefixed_alias]
fn deposit_event(
ctx: _,
memory: _,
topics_ptr: u32,
topics_len: u32,
data_ptr: u32,
data_len: u32,
) -> Result<(), TrapReason> {
let num_topic = topics_len
.checked_div(sp_std::mem::size_of::<TopicOf<E::T>>() as u32)
.ok_or("Zero sized topics are not allowed")?;
ctx.charge_gas(RuntimeCosts::DepositEvent { num_topic, len: data_len })?;
if data_len > ctx.ext.max_value_size() {
return Err(Error::<E::T>::ValueTooLarge.into())
}
let topics: Vec<TopicOf<<E as Ext>::T>> = match topics_len {
0 => Vec::new(),
_ => ctx.read_sandbox_memory_as_unbounded(memory, 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())
}
let event_data = ctx.read_sandbox_memory(memory, data_ptr, data_len)?;
ctx.ext.deposit_event(topics, event_data);
Ok(())
}
/// 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]
Alexander Theißen
committed
fn block_number(ctx: _, memory: _, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> {
ctx.charge_gas(RuntimeCosts::BlockNumber)?;
Alexander Theißen
committed
Ok(ctx.write_sandbox_output(
Alexander Theißen
committed
memory,
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(
Alexander Theißen
committed
ctx: _,
memory: _,
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(
memory, 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(
Alexander Theißen
committed
ctx: _,
memory: _,
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(
memory, 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(
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.
///
/// 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(
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.
///
/// 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(
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<ReturnCode, 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);
}
Sasha Gryaznov
committed
Ok(ReturnCode::Success)
/// 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
///
/// - `call_ptr`: the pointer into the linear memory where the input data is placed.
/// - `call_len`: the length of the input data in bytes.
///
/// # Return Value
///
/// Returns `ReturnCode::Success` when the dispatchable was successfully executed and
/// returned `Ok`. When the dispatchable was exeuted but returned an error
/// `ReturnCode::CallRuntimeFailed` 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 generally easier it cannot be
/// used in all 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.
Sasha Gryaznov
committed
fn call_runtime(
Alexander Theißen
committed
ctx: _,
memory: _,
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 =
Alexander Theißen
committed
ctx.read_sandbox_memory_as_unbounded(memory, 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(e) => {
if ctx.ext.append_debug_buffer("") {
ctx.ext.append_debug_buffer("seal0::call_runtime failed with: ");
ctx.ext.append_debug_buffer(e.into());
};
Ok(ReturnCode::CallRuntimeFailed)
},
}
/// 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
///
Sasha Gryaznov
committed
/// - `ReturnCode::EcdsaRecoverFailed`
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<ReturnCode, 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())?;
Ok(ReturnCode::Success)
},
Err(_) => Ok(ReturnCode::EcdsaRecoverFailed),
}
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
/// Verify a sr25519 signature
///
/// # Parameters
///
/// - `signature_ptr`: the pointer into the linear memory where the signature is placed. Should
/// be a value of 64 bytes.
/// - `pub_key_ptr`: the pointer into the linear memory where the public key is placed. Should
/// be a value of 32 bytes.
/// - `message_len`: the length of the message payload.
/// - `message_ptr`: the pointer into the linear memory where the message is placed.
///
/// # Errors
///
/// - `ReturnCode::Sr25519VerifyFailed
#[unstable]
fn sr25519_verify(
ctx: _,
memory: _,
signature_ptr: u32,
pub_key_ptr: u32,
message_len: u32,
message_ptr: u32,
) -> Result<ReturnCode, 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(ReturnCode::Success)
} else {
Ok(ReturnCode::Sr25519VerifyFailed)
}
}
/// 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
Sasha Gryaznov
committed
/// would revert the changes made by [`set_code_hash()`][`Self::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
///
Sasha Gryaznov
committed
/// - `ReturnCode::CodeNotFound`
Sasha Gryaznov
committed
#[prefixed_alias]
Alexander Theißen
committed
fn set_code_hash(ctx: _, memory: _, code_hash_ptr: u32) -> Result<ReturnCode, 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(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
///
Sasha Gryaznov
committed
/// - `ReturnCode::EcdsaRecoverFailed`
Sasha Gryaznov
committed
#[prefixed_alias]
fn ecdsa_to_eth_address(
Alexander Theißen
committed
ctx: _,
memory: _,
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];
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
Ok(ReturnCode::Success)
},
Err(_) => Ok(ReturnCode::EcdsaRecoverFailed),
}
/// Returns the number of times the currently executing contract exists on the call stack in
/// addition to the calling instance.
///
/// # Return Value
///
Sasha Gryaznov
committed
/// Returns `0` when there is no reentrancy.
#[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.
///
/// # Parameters
///
/// - `account_ptr`: a pointer to the contract address.
///
/// # Return Value
///
Sasha Gryaznov
committed
/// Returns `0` when the contract does not exist on the call stack.
#[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.
///
/// The nonce is incremented for each successful contract instantiation. This is a
/// sensible default salt for contract instantiations.
fn instantiation_nonce(ctx: _, _memory: _) -> Result<u64, TrapReason> {
ctx.charge_gas(RuntimeCosts::InstantationNonce)?;
Ok(ctx.ext.nonce())
}
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
/// Adds a new delegate dependency to the contract.
///
/// # Parameters
///
/// - `code_hash_ptr`: A pointer to the code hash of the 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.
///
/// # Parameters
///
/// - `code_hash_ptr`: A pointer to the code hash of the 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(())
}