mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 10:01:17 +00:00
seal: Remove ext_dispatch_call and ext_get_runtime_storage (#6464)
Those are way too hard to audit and make only sense with specific chains. They shouldn't be in the core API.
This commit is contained in:
committed by
GitHub
parent
a3a42f599a
commit
b14b472edf
@@ -582,7 +582,6 @@ impl pallet_contracts::Trait for Runtime {
|
||||
type Time = Timestamp;
|
||||
type Randomness = RandomnessCollectiveFlip;
|
||||
type Currency = Balances;
|
||||
type Call = Call;
|
||||
type Event = Event;
|
||||
type DetermineContractAddress = pallet_contracts::SimpleAddressDeterminer<Runtime>;
|
||||
type TrieIdGenerator = pallet_contracts::TrieIdFromParentCounter<Runtime>;
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
(module
|
||||
(import "env" "ext_dispatch_call" (func $ext_dispatch_call (param i32 i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
|
||||
(func (export "call")
|
||||
(call $ext_dispatch_call
|
||||
(i32.const 8) ;; Pointer to the start of encoded call buffer
|
||||
(i32.const 11) ;; Length of the buffer
|
||||
)
|
||||
)
|
||||
(func (export "deploy"))
|
||||
|
||||
(data (i32.const 8) "\00\00\03\00\00\00\00\00\00\00\C8")
|
||||
)
|
||||
@@ -1,15 +0,0 @@
|
||||
(module
|
||||
(import "env" "ext_dispatch_call" (func $ext_dispatch_call (param i32 i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
|
||||
(func (export "call")
|
||||
(call $ext_dispatch_call
|
||||
(i32.const 8) ;; Pointer to the start of encoded call buffer
|
||||
(i32.const 11) ;; Length of the buffer
|
||||
)
|
||||
(unreachable) ;; trap so that the top level transaction fails
|
||||
)
|
||||
(func (export "deploy"))
|
||||
|
||||
(data (i32.const 8) "\00\00\03\00\00\00\00\00\00\00\C8")
|
||||
)
|
||||
@@ -1,74 +0,0 @@
|
||||
(module
|
||||
(import "env" "ext_get_runtime_storage"
|
||||
(func $ext_get_runtime_storage (param i32 i32) (result i32))
|
||||
)
|
||||
(import "env" "ext_scratch_size" (func $ext_scratch_size (result i32)))
|
||||
(import "env" "ext_scratch_read" (func $ext_scratch_read (param i32 i32 i32)))
|
||||
(import "env" "ext_scratch_write" (func $ext_scratch_write (param i32 i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
|
||||
(func (export "deploy"))
|
||||
|
||||
(func $assert (param i32)
|
||||
(block $ok
|
||||
(br_if $ok
|
||||
(get_local 0)
|
||||
)
|
||||
(unreachable)
|
||||
)
|
||||
)
|
||||
|
||||
(func $call (export "call")
|
||||
;; Load runtime storage for the first key and assert that it exists.
|
||||
(call $assert
|
||||
(i32.eq
|
||||
(call $ext_get_runtime_storage
|
||||
(i32.const 16)
|
||||
(i32.const 4)
|
||||
)
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
|
||||
;; assert $ext_scratch_size == 4
|
||||
(call $assert
|
||||
(i32.eq
|
||||
(call $ext_scratch_size)
|
||||
(i32.const 4)
|
||||
)
|
||||
)
|
||||
|
||||
;; copy contents of the scratch buffer into the contract's memory.
|
||||
(call $ext_scratch_read
|
||||
(i32.const 4) ;; Pointer in memory to the place where to copy.
|
||||
(i32.const 0) ;; Offset from the start of the scratch buffer.
|
||||
(i32.const 4) ;; Count of bytes to copy.
|
||||
)
|
||||
|
||||
;; assert that contents of the buffer is equal to the i32 value of 0x14144020.
|
||||
(call $assert
|
||||
(i32.eq
|
||||
(i32.load
|
||||
(i32.const 4)
|
||||
)
|
||||
(i32.const 0x14144020)
|
||||
)
|
||||
)
|
||||
|
||||
;; Load the second key and assert that it doesn't exist.
|
||||
(call $assert
|
||||
(i32.eq
|
||||
(call $ext_get_runtime_storage
|
||||
(i32.const 20)
|
||||
(i32.const 4)
|
||||
)
|
||||
(i32.const 1)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
;; The first key, 4 bytes long.
|
||||
(data (i32.const 16) "\01\02\03\04")
|
||||
;; The second key, 4 bytes long.
|
||||
(data (i32.const 20) "\02\03\04\05")
|
||||
)
|
||||
@@ -51,8 +51,8 @@
|
||||
|
||||
;; Code hash of SET_RENT
|
||||
(data (i32.const 264)
|
||||
"\c2\1c\41\10\a5\22\d8\59\1c\4c\77\35\dd\2d\bf\a1"
|
||||
"\13\0b\50\93\76\9b\92\31\97\b7\c5\74\26\aa\38\2a"
|
||||
"\ab\d6\58\65\1e\83\6e\4a\18\0d\f2\6d\bc\42\ba\e9"
|
||||
"\3d\64\76\e5\30\5b\33\46\bb\4d\43\99\38\21\ee\32"
|
||||
)
|
||||
|
||||
;; Rent allowance
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
(module
|
||||
(import "env" "ext_dispatch_call" (func $ext_dispatch_call (param i32 i32)))
|
||||
(import "env" "ext_transfer" (func $ext_transfer (param i32 i32 i32 i32) (result i32)))
|
||||
(import "env" "ext_set_storage" (func $ext_set_storage (param i32 i32 i32)))
|
||||
(import "env" "ext_clear_storage" (func $ext_clear_storage (param i32)))
|
||||
(import "env" "ext_set_rent_allowance" (func $ext_set_rent_allowance (param i32 i32)))
|
||||
@@ -23,11 +23,13 @@
|
||||
)
|
||||
)
|
||||
|
||||
;; transfer 50 to ALICE
|
||||
;; transfer 50 to CHARLIE
|
||||
(func $call_2
|
||||
(call $ext_dispatch_call
|
||||
(i32.const 68)
|
||||
(i32.const 11)
|
||||
(call $assert
|
||||
(i32.eq
|
||||
(call $ext_transfer (i32.const 68) (i32.const 8) (i32.const 76) (i32.const 8))
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -96,6 +98,9 @@
|
||||
;; Encoding of 10 in balance
|
||||
(data (i32.const 0) "\28")
|
||||
|
||||
;; Encoding of call transfer 50 to CHARLIE
|
||||
(data (i32.const 68) "\00\00\03\00\00\00\00\00\00\00\C8")
|
||||
;; encoding of Charlies's account id
|
||||
(data (i32.const 68) "\03")
|
||||
|
||||
;; encoding of 50 balance
|
||||
(data (i32.const 76) "\32")
|
||||
)
|
||||
|
||||
@@ -29,7 +29,6 @@ use frame_support::{
|
||||
};
|
||||
|
||||
pub type AccountIdOf<T> = <T as frame_system::Trait>::AccountId;
|
||||
pub type CallOf<T> = <T as Trait>::Call;
|
||||
pub type MomentOf<T> = <<T as Trait>::Time as Time>::Moment;
|
||||
pub type SeedOf<T> = <T as frame_system::Trait>::Hash;
|
||||
pub type BlockNumberOf<T> = <T as frame_system::Trait>::BlockNumber;
|
||||
@@ -151,9 +150,6 @@ pub trait Ext {
|
||||
input_data: Vec<u8>,
|
||||
) -> ExecResult;
|
||||
|
||||
/// Notes a call dispatch.
|
||||
fn note_dispatch_call(&mut self, call: CallOf<Self::T>);
|
||||
|
||||
/// Restores the given destination contract sacrificing the current one.
|
||||
///
|
||||
/// Since this function removes the self contract eagerly, if succeeded, no further actions should
|
||||
@@ -274,23 +270,11 @@ impl<T: Trait> Token<T> for ExecFeeToken {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(any(feature = "std", test), derive(PartialEq, Eq, Clone))]
|
||||
#[derive(sp_runtime::RuntimeDebug)]
|
||||
pub enum DeferredAction<T: Trait> {
|
||||
DispatchRuntimeCall {
|
||||
/// The account id of the contract who dispatched this call.
|
||||
origin: T::AccountId,
|
||||
/// The call to dispatch.
|
||||
call: <T as Trait>::Call,
|
||||
},
|
||||
}
|
||||
|
||||
pub struct ExecutionContext<'a, T: Trait + 'a, V, L> {
|
||||
pub caller: Option<&'a ExecutionContext<'a, T, V, L>>,
|
||||
pub self_account: T::AccountId,
|
||||
pub self_trie_id: Option<TrieId>,
|
||||
pub depth: usize,
|
||||
pub deferred: Vec<DeferredAction<T>>,
|
||||
pub config: &'a Config<T>,
|
||||
pub vm: &'a V,
|
||||
pub loader: &'a L,
|
||||
@@ -314,7 +298,6 @@ where
|
||||
self_trie_id: None,
|
||||
self_account: origin,
|
||||
depth: 0,
|
||||
deferred: Vec::new(),
|
||||
config: &cfg,
|
||||
vm: &vm,
|
||||
loader: &loader,
|
||||
@@ -331,7 +314,6 @@ where
|
||||
self_trie_id: trie_id,
|
||||
self_account: dest,
|
||||
depth: self.depth + 1,
|
||||
deferred: Vec::new(),
|
||||
config: self.config,
|
||||
vm: self.vm,
|
||||
loader: self.loader,
|
||||
@@ -532,21 +514,14 @@ where
|
||||
where F: FnOnce(&mut ExecutionContext<T, V, L>) -> ExecResult
|
||||
{
|
||||
use frame_support::storage::TransactionOutcome::*;
|
||||
let (output, deferred) = {
|
||||
let mut nested = self.nested(dest, trie_id);
|
||||
let output = frame_support::storage::with_transaction(|| {
|
||||
let output = func(&mut nested);
|
||||
match output {
|
||||
Ok(ref rv) if rv.is_success() => Commit(output),
|
||||
_ => Rollback(output),
|
||||
}
|
||||
})?;
|
||||
(output, nested.deferred)
|
||||
};
|
||||
if output.is_success() {
|
||||
self.deferred.extend(deferred);
|
||||
}
|
||||
Ok(output)
|
||||
let mut nested = self.nested(dest, trie_id);
|
||||
frame_support::storage::with_transaction(|| {
|
||||
let output = func(&mut nested);
|
||||
match output {
|
||||
Ok(ref rv) if rv.is_success() => Commit(output),
|
||||
_ => Rollback(output),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns whether a contract, identified by address, is currently live in the execution
|
||||
@@ -767,13 +742,6 @@ where
|
||||
self.ctx.call(to.clone(), value, gas_meter, input_data)
|
||||
}
|
||||
|
||||
fn note_dispatch_call(&mut self, call: CallOf<Self::T>) {
|
||||
self.ctx.deferred.push(DeferredAction::DispatchRuntimeCall {
|
||||
origin: self.ctx.self_account.clone(),
|
||||
call,
|
||||
});
|
||||
}
|
||||
|
||||
fn restore_to(
|
||||
&mut self,
|
||||
dest: AccountIdOf<Self::T>,
|
||||
|
||||
@@ -106,16 +106,13 @@ use sp_runtime::{
|
||||
},
|
||||
RuntimeDebug,
|
||||
};
|
||||
use frame_support::dispatch::{
|
||||
PostDispatchInfo, DispatchResult, Dispatchable, DispatchResultWithPostInfo
|
||||
};
|
||||
use frame_support::{
|
||||
Parameter, decl_module, decl_event, decl_storage, decl_error,
|
||||
parameter_types, IsSubType, storage::child::ChildInfo,
|
||||
decl_module, decl_event, decl_storage, decl_error,
|
||||
parameter_types, storage::child::ChildInfo,
|
||||
dispatch::{DispatchResult, DispatchResultWithPostInfo},
|
||||
traits::{OnUnbalanced, Currency, Get, Time, Randomness},
|
||||
};
|
||||
use frame_support::traits::{OnUnbalanced, Currency, Get, Time, Randomness};
|
||||
use frame_support::weights::GetDispatchInfo;
|
||||
use frame_system::{self as system, ensure_signed, RawOrigin, ensure_root};
|
||||
use frame_system::{self as system, ensure_signed, ensure_root};
|
||||
use pallet_contracts_primitives::{RentProjection, ContractAccessError};
|
||||
use frame_support::weights::Weight;
|
||||
|
||||
@@ -321,12 +318,6 @@ pub trait Trait: frame_system::Trait {
|
||||
/// The currency in which fees are paid and contract balances are held.
|
||||
type Currency: Currency<Self::AccountId>;
|
||||
|
||||
/// The outer call dispatch type.
|
||||
type Call:
|
||||
Parameter +
|
||||
Dispatchable<PostInfo=PostDispatchInfo, Origin=<Self as frame_system::Trait>::Origin> +
|
||||
IsSubType<Module<Self>, Self> + GetDispatchInfo;
|
||||
|
||||
/// The overarching event type.
|
||||
type Event: From<Event<Self>> + Into<<Self as frame_system::Trait>::Event>;
|
||||
|
||||
@@ -644,30 +635,7 @@ impl<T: Trait> Module<T> {
|
||||
let vm = WasmVm::new(&cfg.schedule);
|
||||
let loader = WasmLoader::new(&cfg.schedule);
|
||||
let mut ctx = ExecutionContext::top_level(origin.clone(), &cfg, &vm, &loader);
|
||||
|
||||
let result = func(&mut ctx, gas_meter);
|
||||
|
||||
// Execute deferred actions.
|
||||
ctx.deferred.into_iter().for_each(|deferred| {
|
||||
use self::exec::DeferredAction::*;
|
||||
match deferred {
|
||||
DispatchRuntimeCall {
|
||||
origin: who,
|
||||
call,
|
||||
} => {
|
||||
let info = call.get_dispatch_info();
|
||||
let result = call.dispatch(RawOrigin::Signed(who.clone()).into());
|
||||
let post_info = match result {
|
||||
Ok(post_info) => post_info,
|
||||
Err(err) => err.post_info,
|
||||
};
|
||||
gas_meter.refund(post_info.calc_unspent(&info));
|
||||
Self::deposit_event(RawEvent::Dispatched(who, result.is_ok()));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
result
|
||||
func(&mut ctx, gas_meter)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -173,7 +173,6 @@ impl Trait for Test {
|
||||
type Time = Timestamp;
|
||||
type Randomness = Randomness;
|
||||
type Currency = Balances;
|
||||
type Call = Call;
|
||||
type DetermineContractAddress = DummyContractAddressFor;
|
||||
type Event = MetaEvent;
|
||||
type TrieIdGenerator = DummyTrieIdGenerator;
|
||||
@@ -446,233 +445,6 @@ fn instantiate_and_call_and_deposit_event() {
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dispatch_call() {
|
||||
// This test can fail due to the encoding changes. In case it becomes too annoying
|
||||
// let's rewrite so as we use this module controlled call or we serialize it in runtime.
|
||||
let encoded = Encode::encode(&Call::Balances(pallet_balances::Call::transfer(CHARLIE, 50)));
|
||||
assert_eq!(&encoded[..], &hex!("00000300000000000000C8")[..]);
|
||||
|
||||
let (wasm, code_hash) = compile_module::<Test>("dispatch_call").unwrap();
|
||||
|
||||
ExtBuilder::default()
|
||||
.existential_deposit(50)
|
||||
.build()
|
||||
.execute_with(|| {
|
||||
let _ = Balances::deposit_creating(&ALICE, 1_000_000);
|
||||
|
||||
assert_ok!(Contracts::put_code(Origin::signed(ALICE), wasm));
|
||||
|
||||
// Let's keep this assert even though it's redundant. If you ever need to update the
|
||||
// wasm source this test will fail and will show you the actual hash.
|
||||
assert_eq!(System::events(), vec![
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: MetaEvent::system(frame_system::RawEvent::NewAccount(1)),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: MetaEvent::balances(pallet_balances::RawEvent::Endowed(1, 1_000_000)),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: MetaEvent::contracts(RawEvent::CodeStored(code_hash.into())),
|
||||
topics: vec![],
|
||||
},
|
||||
]);
|
||||
|
||||
assert_ok!(Contracts::instantiate(
|
||||
Origin::signed(ALICE),
|
||||
100,
|
||||
GAS_LIMIT,
|
||||
code_hash.into(),
|
||||
vec![],
|
||||
));
|
||||
|
||||
assert_ok!(Contracts::call(
|
||||
Origin::signed(ALICE),
|
||||
BOB, // newly created account
|
||||
0,
|
||||
GAS_LIMIT,
|
||||
vec![],
|
||||
));
|
||||
|
||||
pretty_assertions::assert_eq!(System::events(), vec![
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: MetaEvent::system(frame_system::RawEvent::NewAccount(1)),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: MetaEvent::balances(pallet_balances::RawEvent::Endowed(1, 1_000_000)),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: MetaEvent::contracts(RawEvent::CodeStored(code_hash.into())),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: MetaEvent::system(frame_system::RawEvent::NewAccount(BOB)),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: MetaEvent::balances(
|
||||
pallet_balances::RawEvent::Endowed(BOB, 100)
|
||||
),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: MetaEvent::balances(
|
||||
pallet_balances::RawEvent::Transfer(ALICE, BOB, 100)
|
||||
),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: MetaEvent::contracts(RawEvent::Instantiated(ALICE, BOB)),
|
||||
topics: vec![],
|
||||
},
|
||||
|
||||
// Dispatching the call.
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: MetaEvent::system(frame_system::RawEvent::NewAccount(CHARLIE)),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: MetaEvent::balances(
|
||||
pallet_balances::RawEvent::Endowed(CHARLIE, 50)
|
||||
),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: MetaEvent::balances(
|
||||
pallet_balances::RawEvent::Transfer(BOB, CHARLIE, 50)
|
||||
),
|
||||
topics: vec![],
|
||||
},
|
||||
|
||||
// Event emitted as a result of dispatch.
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: MetaEvent::contracts(RawEvent::Dispatched(BOB, true)),
|
||||
topics: vec![],
|
||||
}
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dispatch_call_not_dispatched_after_top_level_transaction_failure() {
|
||||
// This test can fail due to the encoding changes. In case it becomes too annoying
|
||||
// let's rewrite so as we use this module controlled call or we serialize it in runtime.
|
||||
let encoded = Encode::encode(&Call::Balances(pallet_balances::Call::transfer(CHARLIE, 50)));
|
||||
assert_eq!(&encoded[..], &hex!("00000300000000000000C8")[..]);
|
||||
|
||||
let (wasm, code_hash) = compile_module::<Test>("dispatch_call_then_trap").unwrap();
|
||||
|
||||
ExtBuilder::default()
|
||||
.existential_deposit(50)
|
||||
.build()
|
||||
.execute_with(|| {
|
||||
let _ = Balances::deposit_creating(&ALICE, 1_000_000);
|
||||
|
||||
assert_ok!(Contracts::put_code(Origin::signed(ALICE), wasm));
|
||||
|
||||
// Let's keep this assert even though it's redundant. If you ever need to update the
|
||||
// wasm source this test will fail and will show you the actual hash.
|
||||
assert_eq!(System::events(), vec![
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: MetaEvent::system(frame_system::RawEvent::NewAccount(1)),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: MetaEvent::balances(pallet_balances::RawEvent::Endowed(1, 1_000_000)),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: MetaEvent::contracts(RawEvent::CodeStored(code_hash.into())),
|
||||
topics: vec![],
|
||||
},
|
||||
]);
|
||||
|
||||
assert_ok!(Contracts::instantiate(
|
||||
Origin::signed(ALICE),
|
||||
100,
|
||||
GAS_LIMIT,
|
||||
code_hash.into(),
|
||||
vec![],
|
||||
));
|
||||
|
||||
// Call the newly instantiated contract. The contract is expected to dispatch a call
|
||||
// and then trap.
|
||||
assert_err_ignore_postinfo!(
|
||||
Contracts::call(
|
||||
Origin::signed(ALICE),
|
||||
BOB, // newly created account
|
||||
0,
|
||||
GAS_LIMIT,
|
||||
vec![],
|
||||
),
|
||||
"contract trapped during execution"
|
||||
);
|
||||
pretty_assertions::assert_eq!(System::events(), vec![
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: MetaEvent::system(frame_system::RawEvent::NewAccount(1)),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: MetaEvent::balances(pallet_balances::RawEvent::Endowed(1, 1_000_000)),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: MetaEvent::contracts(RawEvent::CodeStored(code_hash.into())),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: MetaEvent::system(frame_system::RawEvent::NewAccount(BOB)),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: MetaEvent::balances(
|
||||
pallet_balances::RawEvent::Endowed(BOB, 100)
|
||||
),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: MetaEvent::balances(
|
||||
pallet_balances::RawEvent::Transfer(ALICE, BOB, 100)
|
||||
),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: MetaEvent::contracts(RawEvent::Instantiated(ALICE, BOB)),
|
||||
topics: vec![],
|
||||
},
|
||||
// ABSENCE of events which would be caused by dispatched Balances::transfer call
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn run_out_of_gas() {
|
||||
let (wasm, code_hash) = compile_module::<Test>("run_out_of_gas").unwrap();
|
||||
@@ -1773,38 +1545,6 @@ fn cannot_self_destruct_in_constructor() {
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_runtime_storage() {
|
||||
let (wasm, code_hash) = compile_module::<Test>("get_runtime_storage").unwrap();
|
||||
ExtBuilder::default()
|
||||
.existential_deposit(50)
|
||||
.build()
|
||||
.execute_with(|| {
|
||||
let _ = Balances::deposit_creating(&ALICE, 1_000_000);
|
||||
|
||||
frame_support::storage::unhashed::put_raw(
|
||||
&[1, 2, 3, 4],
|
||||
0x14144020u32.to_le_bytes().to_vec().as_ref()
|
||||
);
|
||||
|
||||
assert_ok!(Contracts::put_code(Origin::signed(ALICE), wasm));
|
||||
assert_ok!(Contracts::instantiate(
|
||||
Origin::signed(ALICE),
|
||||
100,
|
||||
GAS_LIMIT,
|
||||
code_hash.into(),
|
||||
vec![],
|
||||
));
|
||||
assert_ok!(Contracts::call(
|
||||
Origin::signed(ALICE),
|
||||
BOB,
|
||||
0,
|
||||
GAS_LIMIT,
|
||||
vec![],
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn crypto_hashes() {
|
||||
let (wasm, code_hash) = compile_module::<Test>("crypto_hashes").unwrap();
|
||||
|
||||
@@ -206,7 +206,6 @@ mod tests {
|
||||
instantiates: Vec<InstantiateEntry>,
|
||||
terminations: Vec<TerminationEntry>,
|
||||
transfers: Vec<TransferEntry>,
|
||||
dispatches: Vec<DispatchEntry>,
|
||||
restores: Vec<RestoreEntry>,
|
||||
// (topics, data)
|
||||
events: Vec<(Vec<H256>, Vec<u8>)>,
|
||||
@@ -299,9 +298,6 @@ mod tests {
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
fn note_dispatch_call(&mut self, call: Call) {
|
||||
self.dispatches.push(DispatchEntry(call));
|
||||
}
|
||||
fn restore_to(
|
||||
&mut self,
|
||||
dest: u64,
|
||||
@@ -421,9 +417,6 @@ mod tests {
|
||||
) -> ExecResult {
|
||||
(**self).call(to, value, gas_meter, input_data)
|
||||
}
|
||||
fn note_dispatch_call(&mut self, call: Call) {
|
||||
(**self).note_dispatch_call(call)
|
||||
}
|
||||
fn restore_to(
|
||||
&mut self,
|
||||
dest: u64,
|
||||
@@ -1238,44 +1231,6 @@ mod tests {
|
||||
).unwrap();
|
||||
}
|
||||
|
||||
const CODE_DISPATCH_CALL: &str = r#"
|
||||
(module
|
||||
(import "env" "ext_dispatch_call" (func $ext_dispatch_call (param i32 i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
|
||||
(func (export "call")
|
||||
(call $ext_dispatch_call
|
||||
(i32.const 8) ;; Pointer to the start of encoded call buffer
|
||||
(i32.const 13) ;; Length of the buffer
|
||||
)
|
||||
)
|
||||
(func (export "deploy"))
|
||||
|
||||
(data (i32.const 8) "\00\01\2A\00\00\00\00\00\00\00\E5\14\00")
|
||||
)
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
fn dispatch_call() {
|
||||
// This test can fail due to the encoding changes. In case it becomes too annoying
|
||||
// let's rewrite so as we use this module controlled call or we serialize it in runtime.
|
||||
|
||||
let mut mock_ext = MockExt::default();
|
||||
let _ = execute(
|
||||
CODE_DISPATCH_CALL,
|
||||
vec![],
|
||||
&mut mock_ext,
|
||||
&mut GasMeter::new(GAS_LIMIT),
|
||||
).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
&mock_ext.dispatches,
|
||||
&[DispatchEntry(
|
||||
Call::Balances(pallet_balances::Call::set_balance(42, 1337, 0)),
|
||||
)]
|
||||
);
|
||||
}
|
||||
|
||||
const CODE_RETURN_FROM_START_FN: &str = r#"
|
||||
(module
|
||||
(import "env" "ext_return" (func $ext_return (param i32 i32)))
|
||||
@@ -1883,103 +1838,4 @@ mod tests {
|
||||
assert_eq!(output, ExecReturnValue { status: 17, data: hex!("5566778899").to_vec() });
|
||||
assert!(!output.is_success());
|
||||
}
|
||||
|
||||
const CODE_GET_RUNTIME_STORAGE: &str = r#"
|
||||
(module
|
||||
(import "env" "ext_get_runtime_storage"
|
||||
(func $ext_get_runtime_storage (param i32 i32) (result i32))
|
||||
)
|
||||
(import "env" "ext_scratch_size" (func $ext_scratch_size (result i32)))
|
||||
(import "env" "ext_scratch_read" (func $ext_scratch_read (param i32 i32 i32)))
|
||||
(import "env" "ext_scratch_write" (func $ext_scratch_write (param i32 i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
|
||||
(func (export "deploy"))
|
||||
|
||||
(func $assert (param i32)
|
||||
(block $ok
|
||||
(br_if $ok
|
||||
(get_local 0)
|
||||
)
|
||||
(unreachable)
|
||||
)
|
||||
)
|
||||
|
||||
(func $call (export "call")
|
||||
;; Load runtime storage for the first key and assert that it exists.
|
||||
(call $assert
|
||||
(i32.eq
|
||||
(call $ext_get_runtime_storage
|
||||
(i32.const 16)
|
||||
(i32.const 4)
|
||||
)
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
|
||||
;; assert $ext_scratch_size == 4
|
||||
(call $assert
|
||||
(i32.eq
|
||||
(call $ext_scratch_size)
|
||||
(i32.const 4)
|
||||
)
|
||||
)
|
||||
|
||||
;; copy contents of the scratch buffer into the contract's memory.
|
||||
(call $ext_scratch_read
|
||||
(i32.const 4) ;; Pointer in memory to the place where to copy.
|
||||
(i32.const 0) ;; Offset from the start of the scratch buffer.
|
||||
(i32.const 4) ;; Count of bytes to copy.
|
||||
)
|
||||
|
||||
;; assert that contents of the buffer is equal to the i32 value of 0x14144020.
|
||||
(call $assert
|
||||
(i32.eq
|
||||
(i32.load
|
||||
(i32.const 4)
|
||||
)
|
||||
(i32.const 0x14144020)
|
||||
)
|
||||
)
|
||||
|
||||
;; Load the second key and assert that it doesn't exist.
|
||||
(call $assert
|
||||
(i32.eq
|
||||
(call $ext_get_runtime_storage
|
||||
(i32.const 20)
|
||||
(i32.const 4)
|
||||
)
|
||||
(i32.const 1)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
;; The first key, 4 bytes long.
|
||||
(data (i32.const 16) "\01\02\03\04")
|
||||
;; The second key, 4 bytes long.
|
||||
(data (i32.const 20) "\02\03\04\05")
|
||||
)
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
fn get_runtime_storage() {
|
||||
let mut gas_meter = GasMeter::new(GAS_LIMIT);
|
||||
let mock_ext = MockExt::default();
|
||||
|
||||
// "\01\02\03\04" - Some(0x14144020)
|
||||
// "\02\03\04\05" - None
|
||||
*mock_ext.runtime_storage_keys.borrow_mut() = [
|
||||
([1, 2, 3, 4].to_vec(), Some(0x14144020u32.to_le_bytes().to_vec())),
|
||||
([2, 3, 4, 5].to_vec().to_vec(), None),
|
||||
]
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect();
|
||||
let _ = execute(
|
||||
CODE_GET_RUNTIME_STORAGE,
|
||||
vec![],
|
||||
mock_ext,
|
||||
&mut gas_meter,
|
||||
).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@ use sp_io::hashing::{
|
||||
blake2_128,
|
||||
sha2_256,
|
||||
};
|
||||
use frame_support::weights::GetDispatchInfo;
|
||||
|
||||
/// The value returned from ext_call and ext_instantiate contract external functions if the call or
|
||||
/// instantiation traps. This value is chosen as if the execution does not trap, the return value
|
||||
@@ -162,8 +161,6 @@ pub enum RuntimeToken {
|
||||
/// The given number of bytes is read from the sandbox memory and
|
||||
/// is returned as the return data buffer of the call.
|
||||
ReturnData(u32),
|
||||
/// Dispatched a call with the given weight.
|
||||
DispatchWithWeight(Gas),
|
||||
/// (topic_count, data_bytes): A buffer of the given size is posted as an event indexed with the
|
||||
/// given number of topics.
|
||||
DepositEvent(u32, u32),
|
||||
@@ -204,7 +201,6 @@ impl<T: Trait> Token<T> for RuntimeToken {
|
||||
data_and_topics_cost.checked_add(metadata.event_base_cost)
|
||||
)
|
||||
},
|
||||
DispatchWithWeight(gas) => gas.checked_add(metadata.dispatch_base_cost),
|
||||
};
|
||||
|
||||
value.unwrap_or_else(|| Bounded::max_value())
|
||||
@@ -785,30 +781,6 @@ define_env!(Env, <E: Ext>,
|
||||
Ok(())
|
||||
},
|
||||
|
||||
// Decodes the given buffer as a `T::Call` and adds it to the list
|
||||
// of to-be-dispatched calls.
|
||||
//
|
||||
// All calls made it to the top-level context will be dispatched before
|
||||
// finishing the execution of the calling extrinsic.
|
||||
ext_dispatch_call(ctx, call_ptr: u32, call_len: u32) => {
|
||||
let call: <<E as Ext>::T as Trait>::Call =
|
||||
read_sandbox_memory_as(ctx, call_ptr, call_len)?;
|
||||
|
||||
// We already deducted the len costs when reading from the sandbox.
|
||||
// Bill on the actual weight of the dispatched call.
|
||||
let info = call.get_dispatch_info();
|
||||
charge_gas(
|
||||
&mut ctx.gas_meter,
|
||||
ctx.schedule,
|
||||
&mut ctx.special_trap,
|
||||
RuntimeToken::DispatchWithWeight(info.weight)
|
||||
)?;
|
||||
|
||||
ctx.ext.note_dispatch_call(call);
|
||||
|
||||
Ok(())
|
||||
},
|
||||
|
||||
// Try to restore the given destination contract sacrificing the caller.
|
||||
//
|
||||
// This function will compute a tombstone hash from the caller's storage and the given code hash
|
||||
@@ -1005,32 +977,6 @@ define_env!(Env, <E: Ext>,
|
||||
Ok(())
|
||||
},
|
||||
|
||||
// Retrieve the value under the given key from the **runtime** storage and return 0.
|
||||
// If there is no entry under the given key then this function will return 1 and
|
||||
// clear the scratch buffer.
|
||||
//
|
||||
// - key_ptr: the pointer into the linear memory where the requested value is placed.
|
||||
// - key_len: the length of the key in bytes.
|
||||
ext_get_runtime_storage(ctx, key_ptr: u32, key_len: u32) -> u32 => {
|
||||
// Steal the scratch buffer so that we hopefully save an allocation for the `key_buf`.
|
||||
read_sandbox_memory_into_scratch(ctx, key_ptr, key_len)?;
|
||||
let key_buf = mem::replace(&mut ctx.scratch_buf, Vec::new());
|
||||
|
||||
match ctx.ext.get_runtime_storage(&key_buf) {
|
||||
Some(value_buf) => {
|
||||
// The given value exists.
|
||||
ctx.scratch_buf = value_buf;
|
||||
Ok(0)
|
||||
}
|
||||
None => {
|
||||
// Put back the `key_buf` and allow its allocation to be reused.
|
||||
ctx.scratch_buf = key_buf;
|
||||
ctx.scratch_buf.clear();
|
||||
Ok(1)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Computes the SHA2 256-bit hash on the given input buffer.
|
||||
//
|
||||
// Returns the result directly into the given output buffer.
|
||||
|
||||
Reference in New Issue
Block a user