diff --git a/substrate/frame/contracts/CHANGELOG.md b/substrate/frame/contracts/CHANGELOG.md index ef69e050a2..68f35a444d 100644 --- a/substrate/frame/contracts/CHANGELOG.md +++ b/substrate/frame/contracts/CHANGELOG.md @@ -20,7 +20,11 @@ In other words: Upgrading this pallet will not break pre-existing contracts. ### Added +- Add new version of `seal_random` which exposes additional information. +[1](https://github.com/paritytech/substrate/pull/8329) + - Add `seal_rent_params` contract callable function. +[1](https://github.com/paritytech/substrate/pull/8231) ## [v3.0.0] 2021-02-25 diff --git a/substrate/frame/contracts/src/exec.rs b/substrate/frame/contracts/src/exec.rs index a0752d9e05..602a004dac 100644 --- a/substrate/frame/contracts/src/exec.rs +++ b/substrate/frame/contracts/src/exec.rs @@ -243,7 +243,7 @@ pub trait Ext: sealing::Sealed { fn tombstone_deposit(&self) -> BalanceOf; /// Returns a random number for the current block with the given subject. - fn random(&self, subject: &[u8]) -> SeedOf; + fn random(&self, subject: &[u8]) -> (SeedOf, BlockNumberOf); /// Deposit an event with the given topics. /// @@ -845,10 +845,8 @@ where self.value_transferred } - fn random(&self, subject: &[u8]) -> SeedOf { - // TODO: change API to expose randomness freshness - // https://github.com/paritytech/substrate/issues/8297 - T::Randomness::random(subject).0 + fn random(&self, subject: &[u8]) -> (SeedOf, BlockNumberOf) { + T::Randomness::random(subject) } fn now(&self) -> &MomentOf { diff --git a/substrate/frame/contracts/src/wasm/env_def/macros.rs b/substrate/frame/contracts/src/wasm/env_def/macros.rs index 3c10d3225e..cfb529d293 100644 --- a/substrate/frame/contracts/src/wasm/env_def/macros.rs +++ b/substrate/frame/contracts/src/wasm/env_def/macros.rs @@ -43,21 +43,23 @@ macro_rules! gen_signature { macro_rules! gen_signature_dispatch { ( + $needle_module:ident, $needle_name:ident, $needle_sig:ident ; + $module:ident, $name:ident - ( $ctx:ident $( , $names:ident : $params:ty )* ) $( -> $returns:ty )* , $($rest:tt)* ) => { - if stringify!($name).as_bytes() == $needle_name { + ( $ctx:ident $( , $names:ident : $params:ty )* ) $( -> $returns:ty )* , $($rest:tt)* + ) => { + if stringify!($module).as_bytes() == $needle_module && stringify!($name).as_bytes() == $needle_name { let signature = gen_signature!( ( $( $params ),* ) $( -> $returns )* ); if $needle_sig == &signature { return true; } } else { - gen_signature_dispatch!($needle_name, $needle_sig ; $($rest)*); + gen_signature_dispatch!($needle_module, $needle_name, $needle_sig ; $($rest)*); } }; - ( $needle_name:ident, $needle_sig:ident ; ) => { - }; + ( $needle_module:ident, $needle_name:ident, $needle_sig:ident ; ) => {}; } /// Unmarshall arguments and then execute `body` expression and return its result. @@ -151,10 +153,11 @@ macro_rules! register_func { ( $reg_cb:ident, < E: $seal_ty:tt > ; ) => {}; ( $reg_cb:ident, < E: $seal_ty:tt > ; - $name:ident ( $ctx:ident $( , $names:ident : $params:ty )* ) + $module:ident $name:ident ( $ctx:ident $( , $names:ident : $params:ty )* ) $( -> $returns:ty )* => $body:tt $($rest:tt)* ) => { $reg_cb( + stringify!($module).as_bytes(), stringify!($name).as_bytes(), { define_func!( @@ -176,14 +179,17 @@ macro_rules! register_func { /// and reject the code if any imported function has a mismatched signature. macro_rules! define_env { ( $init_name:ident , < E: $seal_ty:tt > , - $( $name:ident ( $ctx:ident $( , $names:ident : $params:ty )* ) + $( [$module:ident] $name:ident ( $ctx:ident $( , $names:ident : $params:ty )* ) $( -> $returns:ty )* => $body:tt , )* ) => { pub struct $init_name; impl $crate::wasm::env_def::ImportSatisfyCheck for $init_name { - fn can_satisfy(name: &[u8], func_type: &parity_wasm::elements::FunctionType) -> bool { - gen_signature_dispatch!( name, func_type ; $( $name ( $ctx $(, $names : $params )* ) $( -> $returns )* , )* ); + fn can_satisfy(module: &[u8], name: &[u8], func_type: &parity_wasm::elements::FunctionType) -> bool { + gen_signature_dispatch!( + module, name, func_type ; + $( $module, $name ( $ctx $(, $names : $params )* ) $( -> $returns )* , )* + ); return false; } @@ -195,8 +201,12 @@ macro_rules! define_env { sp_core::crypto::UncheckedFrom<::Hash> + AsRef<[u8]> { - fn impls)>(f: &mut F) { - register_func!(f, < E: $seal_ty > ; $( $name ( $ctx $( , $names : $params )* ) $( -> $returns)* => $body )* ); + fn impls)>(f: &mut F) { + register_func!( + f, + < E: $seal_ty > ; + $( $module $name ( $ctx $( , $names : $params )* ) $( -> $returns)* => $body )* + ); } } }; @@ -327,7 +337,7 @@ mod tests { use crate::wasm::env_def::ImportSatisfyCheck; define_env!(Env, , - seal_gas( _ctx, amount: u32 ) => { + [seal0] seal_gas( _ctx, amount: u32 ) => { let amount = Weight::from(amount); if !amount.is_zero() { Ok(()) @@ -337,7 +347,11 @@ mod tests { }, ); - assert!(Env::can_satisfy(b"seal_gas", &FunctionType::new(vec![ValueType::I32], None))); - assert!(!Env::can_satisfy(b"not_exists", &FunctionType::new(vec![], None))); + assert!( + Env::can_satisfy(b"seal0", b"seal_gas",&FunctionType::new(vec![ValueType::I32], None)) + ); + assert!( + !Env::can_satisfy(b"seal0", b"not_exists", &FunctionType::new(vec![], None)) + ); } } diff --git a/substrate/frame/contracts/src/wasm/env_def/mod.rs b/substrate/frame/contracts/src/wasm/env_def/mod.rs index 997ec29e02..6d33444b04 100644 --- a/substrate/frame/contracts/src/wasm/env_def/mod.rs +++ b/substrate/frame/contracts/src/wasm/env_def/mod.rs @@ -74,7 +74,7 @@ pub type HostFunc = ) -> Result; pub trait FunctionImplProvider { - fn impls)>(f: &mut F); + fn impls)>(f: &mut F); } /// This trait can be used to check whether the host environment can satisfy @@ -83,5 +83,5 @@ pub trait ImportSatisfyCheck { /// Returns `true` if the host environment contains a function with /// the specified name and its type matches to the given type, or `false` /// otherwise. - fn can_satisfy(name: &[u8], func_type: &FunctionType) -> bool; + fn can_satisfy(module: &[u8], name: &[u8], func_type: &FunctionType) -> bool; } diff --git a/substrate/frame/contracts/src/wasm/mod.rs b/substrate/frame/contracts/src/wasm/mod.rs index fc442473ff..f7fde5ba17 100644 --- a/substrate/frame/contracts/src/wasm/mod.rs +++ b/substrate/frame/contracts/src/wasm/mod.rs @@ -192,8 +192,8 @@ where let mut imports = sp_sandbox::EnvironmentDefinitionBuilder::new(); imports.add_memory(self::prepare::IMPORT_MODULE_MEMORY, "memory", memory.clone()); - runtime::Env::impls(&mut |name, func_ptr| { - imports.add_host_func(self::prepare::IMPORT_MODULE_FN, name, func_ptr); + runtime::Env::impls(&mut |module, name, func_ptr| { + imports.add_host_func(module, name, func_ptr); }); let mut runtime = Runtime::new( @@ -246,7 +246,7 @@ mod tests { use super::*; use crate::{ CodeHash, BalanceOf, Error, Pallet as Contracts, - exec::{Ext, StorageKey, AccountIdOf, Executable, RentParams}, + exec::{Ext, StorageKey, AccountIdOf, Executable, SeedOf, BlockNumberOf, RentParams}, gas::GasMeter, tests::{Test, Call, ALICE, BOB}, }; @@ -414,8 +414,8 @@ mod tests { fn tombstone_deposit(&self) -> u64 { 16 } - fn random(&self, subject: &[u8]) -> H256 { - H256::from_slice(subject) + fn random(&self, subject: &[u8]) -> (SeedOf, BlockNumberOf) { + (H256::from_slice(subject), 42) } fn deposit_event(&mut self, topics: Vec, data: Vec) { self.events.push((topics, data)) @@ -515,7 +515,7 @@ mod tests { fn tombstone_deposit(&self) -> u64 { (**self).tombstone_deposit() } - fn random(&self, subject: &[u8]) -> H256 { + fn random(&self, subject: &[u8]) -> (SeedOf, BlockNumberOf) { (**self).random(subject) } fn deposit_event(&mut self, topics: Vec, data: Vec) { @@ -1531,6 +1531,85 @@ mod tests { ); } + const CODE_RANDOM_V1: &str = r#" +(module + (import "seal1" "seal_random" (func $seal_random (param i32 i32 i32 i32))) + (import "seal0" "seal_return" (func $seal_return (param i32 i32 i32))) + (import "env" "memory" (memory 1 1)) + + ;; [0,128) is reserved for the result of PRNG. + + ;; the subject used for the PRNG. [128,160) + (data (i32.const 128) + "\00\01\02\03\04\05\06\07\08\09\0A\0B\0C\0D\0E\0F" + "\00\01\02\03\04\05\06\07\08\09\0A\0B\0C\0D\0E\0F" + ) + + ;; size of our buffer is 128 bytes + (data (i32.const 160) "\80") + + (func $assert (param i32) + (block $ok + (br_if $ok + (get_local 0) + ) + (unreachable) + ) + ) + + (func (export "call") + ;; This stores the block random seed in the buffer + (call $seal_random + (i32.const 128) ;; Pointer in memory to the start of the subject buffer + (i32.const 32) ;; The subject buffer's length + (i32.const 0) ;; Pointer to the output buffer + (i32.const 160) ;; Pointer to the output buffer length + ) + + ;; assert len == 32 + (call $assert + (i32.eq + (i32.load (i32.const 160)) + (i32.const 40) + ) + ) + + ;; return the random data + (call $seal_return + (i32.const 0) + (i32.const 0) + (i32.const 40) + ) + ) + (func (export "deploy")) +) +"#; + + #[test] + fn random_v1() { + let mut gas_meter = GasMeter::new(GAS_LIMIT); + + let output = execute( + CODE_RANDOM_V1, + vec![], + MockExt::default(), + &mut gas_meter, + ).unwrap(); + + // The mock ext just returns the same data that was passed as the subject. + assert_eq!( + output, + ExecReturnValue { + flags: ReturnFlags::empty(), + data: ( + hex!("000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F"), + 42u64, + ).encode(), + }, + ); + } + + const CODE_DEPOSIT_EVENT: &str = r#" (module (import "seal0" "seal_deposit_event" (func $seal_deposit_event (param i32 i32 i32 i32))) diff --git a/substrate/frame/contracts/src/wasm/prepare.rs b/substrate/frame/contracts/src/wasm/prepare.rs index d9c5ed0c20..4bb70e805e 100644 --- a/substrate/frame/contracts/src/wasm/prepare.rs +++ b/substrate/frame/contracts/src/wasm/prepare.rs @@ -28,11 +28,7 @@ use parity_wasm::elements::{self, Internal, External, MemoryType, Type, ValueTyp use sp_runtime::traits::Hash; use sp_std::prelude::*; -/// Currently, all imported functions must be located inside this module. We might support -/// additional modules for versioning later. -pub const IMPORT_MODULE_FN: &str = "seal0"; - -/// Imported memory must be located inside this module. The reason for that is that current +/// Imported memory must be located inside this module. The reason for hardcoding is that current /// compiler toolchains might not support specifying other modules than "env" for memory imports. pub const IMPORT_MODULE_MEMORY: &str = "env"; @@ -194,7 +190,7 @@ impl<'a, T: Config> ContractModule<'a, T> { let contract_module = pwasm_utils::inject_gas_counter( self.module, &gas_rules, - IMPORT_MODULE_FN + "seal0", ).map_err(|_| "gas instrumentation failed")?; Ok(ContractModule { module: contract_module, @@ -325,12 +321,7 @@ impl<'a, T: Config> ContractModule<'a, T> { let type_idx = match import.external() { &External::Table(_) => return Err("Cannot import tables"), &External::Global(_) => return Err("Cannot import globals"), - &External::Function(ref type_idx) => { - if import.module() != IMPORT_MODULE_FN { - return Err("Invalid module for imported function"); - } - type_idx - }, + &External::Function(ref type_idx) => type_idx, &External::Memory(ref memory_type) => { if import.module() != IMPORT_MODULE_MEMORY { return Err("Invalid module for imported memory"); @@ -363,7 +354,9 @@ impl<'a, T: Config> ContractModule<'a, T> { } if import_fn_banlist.iter().any(|f| import.field().as_bytes() == *f) - || !C::can_satisfy(import.field().as_bytes(), func_ty) + || !C::can_satisfy( + import.module().as_bytes(), import.field().as_bytes(), func_ty, + ) { return Err("module imports a non-existent function"); } @@ -498,7 +491,7 @@ pub mod benchmarking { use parity_wasm::elements::FunctionType; impl ImportSatisfyCheck for () { - fn can_satisfy(_name: &[u8], _func_type: &FunctionType) -> bool { + fn can_satisfy(_module: &[u8], _name: &[u8], _func_type: &FunctionType) -> bool { true } } @@ -543,14 +536,17 @@ mod tests { // Define test environment for tests. We need ImportSatisfyCheck // implementation from it. So actual implementations doesn't matter. define_env!(Test, , - panic(_ctx) => { unreachable!(); }, + [seal0] panic(_ctx) => { unreachable!(); }, // gas is an implementation defined function and a contract can't import it. - gas(_ctx, _amount: u32) => { unreachable!(); }, + [seal0] gas(_ctx, _amount: u32) => { unreachable!(); }, - nop(_ctx, _unused: u64) => { unreachable!(); }, + [seal0] nop(_ctx, _unused: u64) => { unreachable!(); }, - seal_println(_ctx, _ptr: u32, _len: u32) => { unreachable!(); }, + // new version of nop with other data type for argumebt + [seal1] nop(_ctx, _unused: i32) => { unreachable!(); }, + + [seal0] seal_println(_ctx, _ptr: u32, _len: u32) => { unreachable!(); }, ); } @@ -904,30 +900,16 @@ mod tests { Err("Invalid module for imported memory") ); - // functions are in "env" and not in "seal0" - prepare_test!(function_not_in_env, + prepare_test!(function_in_other_module_works, r#" (module - (import "env" "nop" (func (param i64))) + (import "seal1" "nop" (func (param i32))) (func (export "call")) (func (export "deploy")) ) "#, - Err("Invalid module for imported function") - ); - - // functions are in "seal0" and not in in some arbitrary module - prepare_test!(function_not_arbitrary_module, - r#" - (module - (import "any_module" "nop" (func (param i64))) - - (func (export "call")) - (func (export "deploy")) - ) - "#, - Err("Invalid module for imported function") + Ok(_) ); // wrong signature diff --git a/substrate/frame/contracts/src/wasm/runtime.rs b/substrate/frame/contracts/src/wasm/runtime.rs index 8e3c2244f1..802207e4fe 100644 --- a/substrate/frame/contracts/src/wasm/runtime.rs +++ b/substrate/frame/contracts/src/wasm/runtime.rs @@ -629,7 +629,7 @@ define_env!(Env, , // This call is supposed to be called only by instrumentation injected code. // // - amount: How much gas is used. - gas(ctx, amount: u32) => { + [seal0] gas(ctx, amount: u32) => { ctx.charge_gas(RuntimeToken::MeteringBlock(amount))?; Ok(()) }, @@ -649,7 +649,7 @@ define_env!(Env, , // // - If value length exceeds the configured maximum value length of a storage entry. // - Upon trying to set an empty storage entry (value length is 0). - seal_set_storage(ctx, key_ptr: u32, value_ptr: u32, value_len: u32) => { + [seal0] seal_set_storage(ctx, key_ptr: u32, value_ptr: u32, value_len: u32) => { ctx.charge_gas(RuntimeToken::SetStorage(value_len))?; if value_len > ctx.ext.max_value_size() { Err(Error::::ValueTooLarge)?; @@ -665,7 +665,7 @@ define_env!(Env, , // # Parameters // // - `key_ptr`: pointer into the linear memory where the location to clear the value is placed. - seal_clear_storage(ctx, key_ptr: u32) => { + [seal0] seal_clear_storage(ctx, key_ptr: u32) => { ctx.charge_gas(RuntimeToken::ClearStorage)?; let mut key: StorageKey = [0; 32]; ctx.read_sandbox_memory_into_buf(key_ptr, &mut key)?; @@ -684,7 +684,7 @@ define_env!(Env, , // # Errors // // `ReturnCode::KeyNotFound` - seal_get_storage(ctx, key_ptr: u32, out_ptr: u32, out_len_ptr: u32) -> ReturnCode => { + [seal0] seal_get_storage(ctx, key_ptr: u32, out_ptr: u32, out_len_ptr: u32) -> ReturnCode => { ctx.charge_gas(RuntimeToken::GetStorageBase)?; let mut key: StorageKey = [0; 32]; ctx.read_sandbox_memory_into_buf(key_ptr, &mut key)?; @@ -713,7 +713,7 @@ define_env!(Env, , // // `ReturnCode::BelowSubsistenceThreshold` // `ReturnCode::TransferFailed` - seal_transfer( + [seal0] seal_transfer( ctx, account_ptr: u32, account_len: u32, @@ -767,7 +767,7 @@ define_env!(Env, , // `ReturnCode::BelowSubsistenceThreshold` // `ReturnCode::TransferFailed` // `ReturnCode::NotCallable` - seal_call( + [seal0] seal_call( ctx, callee_ptr: u32, callee_len: u32, @@ -868,7 +868,7 @@ define_env!(Env, , // `ReturnCode::TransferFailed` // `ReturnCode::NewContractNotFunded` // `ReturnCode::CodeNotFound` - seal_instantiate( + [seal0] seal_instantiate( ctx, code_hash_ptr: u32, code_hash_len: u32, @@ -950,7 +950,7 @@ define_env!(Env, , // - The contract is live i.e is already on the call stack. // - Failed to send the balance to the beneficiary. // - The deletion queue is full. - seal_terminate( + [seal0] seal_terminate( ctx, beneficiary_ptr: u32, beneficiary_len: u32 @@ -981,7 +981,7 @@ define_env!(Env, , // # Note // // This function can only be called once. Calling it multiple times will trigger a trap. - seal_input(ctx, out_ptr: u32, out_len_ptr: u32) => { + [seal0] seal_input(ctx, out_ptr: u32, out_len_ptr: u32) => { ctx.charge_gas(RuntimeToken::InputBase)?; if let Some(input) = ctx.input_data.take() { ctx.write_sandbox_output(out_ptr, out_len_ptr, &input, false, |len| { @@ -1010,7 +1010,7 @@ define_env!(Env, , // --- msb --- // // Using a reserved bit triggers a trap. - seal_return(ctx, flags: u32, data_ptr: u32, data_len: u32) => { + [seal0] seal_return(ctx, flags: u32, data_ptr: u32, data_len: u32) => { ctx.charge_gas(RuntimeToken::Return(data_len))?; Err(TrapReason::Return(ReturnData { flags, @@ -1028,7 +1028,7 @@ define_env!(Env, , // If this is a top-level call (i.e. initiated by an extrinsic) the origin address of the // extrinsic will be returned. Otherwise, if this call is initiated by another contract then the // address of the contract will be returned. The value is encoded as T::AccountId. - seal_caller(ctx, out_ptr: u32, out_len_ptr: u32) => { + [seal0] seal_caller(ctx, out_ptr: u32, out_len_ptr: u32) => { ctx.charge_gas(RuntimeToken::Caller)?; Ok(ctx.write_sandbox_output( out_ptr, out_len_ptr, &ctx.ext.caller().encode(), false, already_charged @@ -1041,7 +1041,7 @@ define_env!(Env, , // `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. - seal_address(ctx, out_ptr: u32, out_len_ptr: u32) => { + [seal0] seal_address(ctx, out_ptr: u32, out_len_ptr: u32) => { ctx.charge_gas(RuntimeToken::Address)?; Ok(ctx.write_sandbox_output( out_ptr, out_len_ptr, &ctx.ext.address().encode(), false, already_charged @@ -1061,7 +1061,7 @@ define_env!(Env, , // // It is recommended to avoid specifying very small values for `gas` as the prices for a single // gas can be smaller than one. - seal_weight_to_fee(ctx, gas: u64, out_ptr: u32, out_len_ptr: u32) => { + [seal0] seal_weight_to_fee(ctx, gas: u64, out_ptr: u32, out_len_ptr: u32) => { ctx.charge_gas(RuntimeToken::WeightToFee)?; Ok(ctx.write_sandbox_output( out_ptr, out_len_ptr, &ctx.ext.get_weight_price(gas).encode(), false, already_charged @@ -1076,7 +1076,7 @@ define_env!(Env, , // space at `out_ptr` is less than the size of the value a trap is triggered. // // The data is encoded as Gas. - seal_gas_left(ctx, out_ptr: u32, out_len_ptr: u32) => { + [seal0] seal_gas_left(ctx, out_ptr: u32, out_len_ptr: u32) => { ctx.charge_gas(RuntimeToken::GasLeft)?; Ok(ctx.write_sandbox_output( out_ptr, out_len_ptr, &ctx.gas_meter.gas_left().encode(), false, already_charged @@ -1091,7 +1091,7 @@ define_env!(Env, , // space at `out_ptr` is less than the size of the value a trap is triggered. // // The data is encoded as T::Balance. - seal_balance(ctx, out_ptr: u32, out_len_ptr: u32) => { + [seal0] seal_balance(ctx, out_ptr: u32, out_len_ptr: u32) => { ctx.charge_gas(RuntimeToken::Balance)?; Ok(ctx.write_sandbox_output( out_ptr, out_len_ptr, &ctx.ext.balance().encode(), false, already_charged @@ -1106,7 +1106,7 @@ define_env!(Env, , // space at `out_ptr` is less than the size of the value a trap is triggered. // // The data is encoded as T::Balance. - seal_value_transferred(ctx, out_ptr: u32, out_len_ptr: u32) => { + [seal0] seal_value_transferred(ctx, out_ptr: u32, out_len_ptr: u32) => { ctx.charge_gas(RuntimeToken::ValueTransferred)?; Ok(ctx.write_sandbox_output( out_ptr, out_len_ptr, &ctx.ext.value_transferred().encode(), false, already_charged @@ -1121,7 +1121,43 @@ define_env!(Env, , // space at `out_ptr` is less than the size of the value a trap is triggered. // // The data is encoded as T::Hash. - seal_random(ctx, subject_ptr: u32, subject_len: u32, out_ptr: u32, out_len_ptr: u32) => { + // + // # Deprecation + // + // This function is deprecated. Users should migrate to the version in the "seal1" module. + [seal0] seal_random(ctx, subject_ptr: u32, subject_len: u32, out_ptr: u32, out_len_ptr: u32) => { + ctx.charge_gas(RuntimeToken::Random)?; + if subject_len > ctx.ext.schedule().limits.subject_len { + Err(Error::::RandomSubjectTooLong)?; + } + let subject_buf = ctx.read_sandbox_memory(subject_ptr, subject_len)?; + Ok(ctx.write_sandbox_output( + 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, T::BlockNumber). + // + // # 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. + [seal1] seal_random(ctx, subject_ptr: u32, subject_len: u32, out_ptr: u32, out_len_ptr: u32) => { ctx.charge_gas(RuntimeToken::Random)?; if subject_len > ctx.ext.schedule().limits.subject_len { Err(Error::::RandomSubjectTooLong)?; @@ -1138,7 +1174,7 @@ define_env!(Env, , // `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. - seal_now(ctx, out_ptr: u32, out_len_ptr: u32) => { + [seal0] seal_now(ctx, out_ptr: u32, out_len_ptr: u32) => { ctx.charge_gas(RuntimeToken::Now)?; Ok(ctx.write_sandbox_output( out_ptr, out_len_ptr, &ctx.ext.now().encode(), false, already_charged @@ -1148,7 +1184,7 @@ define_env!(Env, , // Stores the minimum balance (a.k.a. existential deposit) into the supplied buffer. // // The data is encoded as T::Balance. - seal_minimum_balance(ctx, out_ptr: u32, out_len_ptr: u32) => { + [seal0] seal_minimum_balance(ctx, out_ptr: u32, out_len_ptr: u32) => { ctx.charge_gas(RuntimeToken::MinimumBalance)?; Ok(ctx.write_sandbox_output( out_ptr, out_len_ptr, &ctx.ext.minimum_balance().encode(), false, already_charged @@ -1170,7 +1206,7 @@ define_env!(Env, , // a contract to leave a tombstone the balance of the contract must not go // below the sum of existential deposit and the tombstone deposit. The sum // is commonly referred as subsistence threshold in code. - seal_tombstone_deposit(ctx, out_ptr: u32, out_len_ptr: u32) => { + [seal0] seal_tombstone_deposit(ctx, out_ptr: u32, out_len_ptr: u32) => { ctx.charge_gas(RuntimeToken::TombstoneDeposit)?; Ok(ctx.write_sandbox_output( out_ptr, out_len_ptr, &ctx.ext.tombstone_deposit().encode(), false, already_charged @@ -1208,7 +1244,7 @@ define_env!(Env, , // - Tombstone hashes do not match. // - The calling contract is already present on the call stack. // - The supplied code_hash does not exist on-chain. - seal_restore_to( + [seal0] seal_restore_to( ctx, dest_ptr: u32, dest_len: u32, @@ -1279,8 +1315,13 @@ define_env!(Env, , // - 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. - seal_deposit_event(ctx, topics_ptr: u32, topics_len: u32, data_ptr: u32, data_len: u32) => { - + [seal0] seal_deposit_event( + ctx, + topics_ptr: u32, + topics_len: u32, + data_ptr: u32, + data_len: u32 + ) => { fn has_duplicates(items: &mut Vec) -> bool { // # Warning // @@ -1336,7 +1377,7 @@ define_env!(Env, , // - value_ptr: a pointer to the buffer with value, how much to allow for rent // Should be decodable as a `T::Balance`. Traps otherwise. // - value_len: length of the value buffer. - seal_set_rent_allowance(ctx, value_ptr: u32, value_len: u32) => { + [seal0] seal_set_rent_allowance(ctx, value_ptr: u32, value_len: u32) => { ctx.charge_gas(RuntimeToken::SetRentAllowance)?; let value: BalanceOf<::T> = ctx.read_sandbox_memory_as(value_ptr, value_len)?; @@ -1353,7 +1394,7 @@ define_env!(Env, , // space at `out_ptr` is less than the size of the value a trap is triggered. // // The data is encoded as T::Balance. - seal_rent_allowance(ctx, out_ptr: u32, out_len_ptr: u32) => { + [seal0] seal_rent_allowance(ctx, out_ptr: u32, out_len_ptr: u32) => { ctx.charge_gas(RuntimeToken::RentAllowance)?; Ok(ctx.write_sandbox_output( out_ptr, out_len_ptr, &ctx.ext.rent_allowance().encode(), false, already_charged @@ -1363,7 +1404,7 @@ define_env!(Env, , // Prints utf8 encoded string from the data buffer. // Only available on `--dev` chains. // This function may be removed at any time, superseded by a more general contract debugging feature. - seal_println(ctx, str_ptr: u32, str_len: u32) => { + [seal0] seal_println(ctx, str_ptr: u32, str_len: u32) => { let data = ctx.read_sandbox_memory(str_ptr, str_len)?; if let Ok(utf8) = core::str::from_utf8(&data) { log::info!(target: "runtime::contracts", "seal_println: {}", utf8); @@ -1377,7 +1418,7 @@ define_env!(Env, , // `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. - seal_block_number(ctx, out_ptr: u32, out_len_ptr: u32) => { + [seal0] seal_block_number(ctx, out_ptr: u32, out_len_ptr: u32) => { ctx.charge_gas(RuntimeToken::BlockNumber)?; Ok(ctx.write_sandbox_output( out_ptr, out_len_ptr, &ctx.ext.block_number().encode(), false, already_charged @@ -1404,7 +1445,7 @@ define_env!(Env, , // - `output_ptr`: the pointer into the linear memory where the output // data is placed. The function will write the result // directly into this buffer. - seal_hash_sha2_256(ctx, input_ptr: u32, input_len: u32, output_ptr: u32) => { + [seal0] seal_hash_sha2_256(ctx, input_ptr: u32, input_len: u32, output_ptr: u32) => { ctx.charge_gas(RuntimeToken::HashSha256(input_len))?; Ok(ctx.compute_hash_on_intermediate_buffer(sha2_256, input_ptr, input_len, output_ptr)?) }, @@ -1429,7 +1470,7 @@ define_env!(Env, , // - `output_ptr`: the pointer into the linear memory where the output // data is placed. The function will write the result // directly into this buffer. - seal_hash_keccak_256(ctx, input_ptr: u32, input_len: u32, output_ptr: u32) => { + [seal0] seal_hash_keccak_256(ctx, input_ptr: u32, input_len: u32, output_ptr: u32) => { ctx.charge_gas(RuntimeToken::HashKeccak256(input_len))?; Ok(ctx.compute_hash_on_intermediate_buffer(keccak_256, input_ptr, input_len, output_ptr)?) }, @@ -1454,7 +1495,7 @@ define_env!(Env, , // - `output_ptr`: the pointer into the linear memory where the output // data is placed. The function will write the result // directly into this buffer. - seal_hash_blake2_256(ctx, input_ptr: u32, input_len: u32, output_ptr: u32) => { + [seal0] seal_hash_blake2_256(ctx, input_ptr: u32, input_len: u32, output_ptr: u32) => { ctx.charge_gas(RuntimeToken::HashBlake256(input_len))?; Ok(ctx.compute_hash_on_intermediate_buffer(blake2_256, input_ptr, input_len, output_ptr)?) }, @@ -1479,7 +1520,7 @@ define_env!(Env, , // - `output_ptr`: the pointer into the linear memory where the output // data is placed. The function will write the result // directly into this buffer. - seal_hash_blake2_128(ctx, input_ptr: u32, input_len: u32, output_ptr: u32) => { + [seal0] seal_hash_blake2_128(ctx, input_ptr: u32, input_len: u32, output_ptr: u32) => { ctx.charge_gas(RuntimeToken::HashBlake128(input_len))?; Ok(ctx.compute_hash_on_intermediate_buffer(blake2_128, input_ptr, input_len, output_ptr)?) }, @@ -1495,7 +1536,7 @@ define_env!(Env, , // // If no chain extension exists the contract will trap with the `NoChainExtension` // module error. - seal_call_chain_extension( + [seal0] seal_call_chain_extension( ctx, func_id: u32, input_ptr: u32, @@ -1531,7 +1572,7 @@ define_env!(Env, , // The returned information was collected and cached when the current contract call // started execution. Any change to those values that happens due to actions of the // current call or contracts that are called by this contract are not considered. - seal_rent_params(ctx, out_ptr: u32, out_len_ptr: u32) => { + [seal0] seal_rent_params(ctx, out_ptr: u32, out_len_ptr: u32) => { ctx.charge_gas(RuntimeToken::RentParams)?; Ok(ctx.write_sandbox_output( out_ptr, out_len_ptr, &ctx.ext.rent_params().encode(), false, already_charged