srml-contracts: Deferred actions (#3255)

* Switch to deferred actions

* Make restore_to a deferred action.

* Bump version.

* Review fixes.
This commit is contained in:
Sergei Pepyakin
2019-08-01 09:52:59 +02:00
committed by Gavin Wood
parent 780942192e
commit de02aee156
6 changed files with 337 additions and 171 deletions
+92 -27
View File
@@ -90,6 +90,15 @@ pub trait Ext {
/// Notes a call dispatch.
fn note_dispatch_call(&mut self, call: CallOf<Self::T>);
/// Notes a restoration request.
fn note_restore_to(
&mut self,
dest: AccountIdOf<Self::T>,
code_hash: CodeHash<Self::T>,
rent_allowance: BalanceOf<Self::T>,
delta: Vec<StorageKey>,
);
/// Returns a reference to the account id of the caller.
fn caller(&self) -> &AccountIdOf<Self::T>;
@@ -254,13 +263,41 @@ impl<T: Trait> Token<T> for ExecFeeToken {
}
}
#[cfg_attr(any(feature = "std", test), derive(Debug, PartialEq, Eq, Clone))]
pub enum DeferredAction<T: Trait> {
DepositEvent {
/// A list of topics this event will be deposited with.
topics: Vec<T::Hash>,
/// The event to deposit.
event: Event<T>,
},
DispatchRuntimeCall {
/// The account id of the contract who dispatched this call.
origin: T::AccountId,
/// The call to dispatch.
call: T::Call,
},
RestoreTo {
/// The account id of the contract which is removed during the restoration and transfers
/// its storage to the restored contract.
donor: T::AccountId,
/// The account id of the restored contract.
dest: T::AccountId,
/// The code hash of the restored contract.
code_hash: CodeHash<T>,
/// The initial rent allowance to set.
rent_allowance: BalanceOf<T>,
/// The keys to delete upon restoration.
delta: Vec<StorageKey>,
},
}
pub struct ExecutionContext<'a, T: Trait + 'a, V, L> {
pub self_account: T::AccountId,
pub self_trie_id: Option<TrieId>,
pub overlay: OverlayAccountDb<'a, T>,
pub depth: usize,
pub events: Vec<IndexedEvent<T>>,
pub calls: Vec<(T::AccountId, T::Call)>,
pub deferred: Vec<DeferredAction<T>>,
pub config: &'a Config<T>,
pub vm: &'a V,
pub loader: &'a L,
@@ -284,8 +321,7 @@ where
self_account: origin,
overlay: OverlayAccountDb::<T>::new(&DirectAccountDb),
depth: 0,
events: Vec::new(),
calls: Vec::new(),
deferred: Vec::new(),
config: &cfg,
vm: &vm,
loader: &loader,
@@ -302,8 +338,7 @@ where
self_account: dest,
overlay: OverlayAccountDb::new(&self.overlay),
depth: self.depth + 1,
events: Vec::new(),
calls: Vec::new(),
deferred: Vec::new(),
config: self.config,
vm: self.vm,
loader: self.loader,
@@ -435,7 +470,7 @@ where
.into_result()?;
// Deposit an instantiation event.
nested.events.push(IndexedEvent {
nested.deferred.push(DeferredAction::DepositEvent {
event: RawEvent::Instantiated(caller.clone(), dest.clone()),
topics: Vec::new(),
});
@@ -464,15 +499,14 @@ where
-> Result<Vec<u8>, &'static str>
where F: FnOnce(&mut ExecutionContext<T, V, L>) -> Result<Vec<u8>, &'static str>
{
let (output_data, change_set, events, calls) = {
let (output_data, change_set, deferred) = {
let mut nested = self.nested(dest, trie_id);
let output_data = func(&mut nested)?;
(output_data, nested.overlay.into_change_set(), nested.events, nested.calls)
(output_data, nested.overlay.into_change_set(), nested.deferred)
};
self.overlay.commit(change_set);
self.events.extend(events);
self.calls.extend(calls);
self.deferred.extend(deferred);
Ok(output_data)
}
@@ -592,7 +626,7 @@ fn transfer<'a, T: Trait, V: Vm<T>, L: Loader<T>>(
if transactor != dest {
ctx.overlay.set_balance(transactor, new_from_balance);
ctx.overlay.set_balance(dest, new_to_balance);
ctx.events.push(IndexedEvent {
ctx.deferred.push(DeferredAction::DepositEvent {
event: RawEvent::Transfer(transactor.clone(), dest.clone(), value),
topics: Vec::new(),
});
@@ -656,11 +690,27 @@ where
.call(to.clone(), value, gas_meter, input_data, empty_output_buf)
}
/// Notes a call dispatch.
fn note_dispatch_call(&mut self, call: CallOf<Self::T>) {
self.ctx.calls.push(
(self.ctx.self_account.clone(), call)
);
self.ctx.deferred.push(DeferredAction::DispatchRuntimeCall {
origin: self.ctx.self_account.clone(),
call,
});
}
fn note_restore_to(
&mut self,
dest: AccountIdOf<Self::T>,
code_hash: CodeHash<Self::T>,
rent_allowance: BalanceOf<Self::T>,
delta: Vec<StorageKey>,
) {
self.ctx.deferred.push(DeferredAction::RestoreTo {
donor: self.ctx.self_account.clone(),
dest,
code_hash,
rent_allowance,
delta,
});
}
fn address(&self) -> &T::AccountId {
@@ -688,7 +738,7 @@ where
}
fn deposit_event(&mut self, topics: Vec<T::Hash>, data: Vec<u8>) {
self.ctx.events.push(IndexedEvent {
self.ctx.deferred.push(DeferredAction::DepositEvent {
topics,
event: RawEvent::Contract(self.ctx.self_account.clone(), data),
});
@@ -724,7 +774,7 @@ where
mod tests {
use super::{
BalanceOf, ExecFeeToken, ExecutionContext, Ext, Loader, EmptyOutputBuf, TransferFeeKind, TransferFeeToken,
Vm, VmExecResult, InstantiateReceipt, RawEvent, IndexedEvent,
Vm, VmExecResult, InstantiateReceipt, RawEvent, DeferredAction,
};
use crate::account_db::AccountDb;
use crate::gas::GasMeter;
@@ -741,6 +791,21 @@ mod tests {
const BOB: u64 = 2;
const CHARLIE: u64 = 3;
impl<'a, T, V, L> ExecutionContext<'a, T, V, L>
where T: crate::Trait
{
fn events(&self) -> Vec<DeferredAction<T>> {
self.deferred
.iter()
.filter(|action| match *action {
DeferredAction::DepositEvent { .. } => true,
_ => false,
})
.cloned()
.collect()
}
}
struct MockCtx<'a> {
ext: &'a mut dyn Ext<T = Test>,
input_data: &'a [u8],
@@ -1326,12 +1391,12 @@ mod tests {
// Check that the newly created account has the expected code hash and
// there are instantiation event.
assert_eq!(ctx.overlay.get_code_hash(&created_contract_address).unwrap(), dummy_ch);
assert_eq!(&ctx.events, &[
IndexedEvent {
assert_eq!(&ctx.events(), &[
DeferredAction::DepositEvent {
event: RawEvent::Transfer(ALICE, created_contract_address, 100),
topics: Vec::new(),
},
IndexedEvent {
DeferredAction::DepositEvent {
event: RawEvent::Instantiated(ALICE, created_contract_address),
topics: Vec::new(),
}
@@ -1384,16 +1449,16 @@ mod tests {
// Check that the newly created account has the expected code hash and
// there are instantiation event.
assert_eq!(ctx.overlay.get_code_hash(&created_contract_address).unwrap(), dummy_ch);
assert_eq!(&ctx.events, &[
IndexedEvent {
assert_eq!(&ctx.events(), &[
DeferredAction::DepositEvent {
event: RawEvent::Transfer(ALICE, BOB, 20),
topics: Vec::new(),
},
IndexedEvent {
DeferredAction::DepositEvent {
event: RawEvent::Transfer(BOB, created_contract_address, 15),
topics: Vec::new(),
},
IndexedEvent {
DeferredAction::DepositEvent {
event: RawEvent::Instantiated(BOB, created_contract_address),
topics: Vec::new(),
},
@@ -1441,8 +1506,8 @@ mod tests {
// The contract wasn't created so we don't expect to see an instantiation
// event here.
assert_eq!(&ctx.events, &[
IndexedEvent {
assert_eq!(&ctx.events(), &[
DeferredAction::DepositEvent {
event: RawEvent::Transfer(ALICE, BOB, 20),
topics: Vec::new(),
},