Contracts: Add transfer event (#801)

This commit is contained in:
Sergey Pepyakin
2018-09-25 20:49:18 +01:00
committed by Gav Wood
parent a613c62dc1
commit 98e0a3a55a
7 changed files with 142 additions and 31 deletions
+1
View File
@@ -2546,6 +2546,7 @@ version = "0.1.0"
dependencies = [
"assert_matches 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-codec 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-codec-derive 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-wasm 0.31.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pwasm-utils 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)",
+2 -1
View File
@@ -174,6 +174,7 @@ impl treasury::Trait for Runtime {
impl contract::Trait for Runtime {
type Gas = u64;
type DetermineContractAddress = contract::SimpleAddressDeterminator<Runtime>;
type Event = Event;
}
impl DigestItem for Log {
@@ -208,7 +209,7 @@ construct_runtime!(
CouncilVoting: council_voting::{Module, Call, Storage, Event<T>},
CouncilMotions: council_motions::{Module, Call, Storage, Event<T>, Origin},
Treasury: treasury,
Contract: contract::{Module, Call, Config},
Contract: contract::{Module, Call, Config, Event<T>},
}
);
+1
View File
@@ -661,6 +661,7 @@ name = "srml-contract"
version = "0.1.0"
dependencies = [
"parity-codec 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-codec-derive 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-wasm 0.31.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pwasm-utils 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)",
+3 -1
View File
@@ -7,7 +7,8 @@ authors = ["Parity Technologies <admin@parity.io>"]
serde = { version = "1.0", default_features = false }
serde_derive = { version = "1.0", optional = true }
pwasm-utils = { version = "0.3", default_features = false }
parity-codec = { version = "2.0", default_features = false }
parity-codec = { version = "~2.0.1", default_features = false }
parity-codec-derive = { version = "~2.0.1", default-features = false }
parity-wasm = { version = "0.31", default_features = false }
substrate-primitives = { path = "../../core/primitives", default_features = false }
sr-primitives = { path = "../../core/sr-primitives", default_features = false }
@@ -28,6 +29,7 @@ std = [
"serde_derive",
"serde/std",
"parity-codec/std",
"parity-codec-derive/std",
"substrate-primitives/std",
"sr-primitives/std",
"sr-io/std",
+32 -26
View File
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
use super::{CodeOf, MaxDepth, ContractAddressFor, Module, Trait};
use super::{CodeOf, MaxDepth, ContractAddressFor, Module, Trait, Event, RawEvent};
use account_db::{AccountDb, OverlayAccountDb};
use gas::GasMeter;
use vm;
@@ -37,6 +37,7 @@ pub struct ExecutionContext<'a, T: Trait + 'a> {
pub self_account: T::AccountId,
pub overlay: OverlayAccountDb<'a, T>,
pub depth: usize,
pub events: Vec<Event<T>>,
}
impl<'a, T: Trait> ExecutionContext<'a, T> {
@@ -61,9 +62,16 @@ impl<'a, T: Trait> ExecutionContext<'a, T> {
let dest_code = <CodeOf<T>>::get(&dest);
let change_set = {
let (change_set, events) = {
let mut overlay = OverlayAccountDb::new(&self.overlay);
let mut nested = ExecutionContext {
overlay: overlay,
self_account: dest.clone(),
depth: self.depth + 1,
events: Vec::new(),
};
if value > T::Balance::zero() {
transfer(
gas_meter,
@@ -71,16 +79,10 @@ impl<'a, T: Trait> ExecutionContext<'a, T> {
&self.self_account,
&dest,
value,
&mut overlay,
&mut nested,
)?;
}
let mut nested = ExecutionContext {
overlay: overlay,
self_account: dest.clone(),
depth: self.depth + 1,
};
if !dest_code.is_empty() {
vm::execute(
&dest_code,
@@ -95,10 +97,11 @@ impl<'a, T: Trait> ExecutionContext<'a, T> {
).map_err(|_| "vm execute returned error while call")?;
}
nested.overlay.into_change_set()
(nested.overlay.into_change_set(), nested.events)
};
self.overlay.commit(change_set);
self.events.extend(events);
Ok(CallReceipt)
}
@@ -126,9 +129,16 @@ impl<'a, T: Trait> ExecutionContext<'a, T> {
return Err("contract already exists");
}
let change_set = {
let (change_set, events) = {
let mut overlay = OverlayAccountDb::new(&self.overlay);
let mut nested = ExecutionContext {
overlay: overlay,
self_account: dest.clone(),
depth: self.depth + 1,
events: Vec::new(),
};
if endowment > T::Balance::zero() {
transfer(
gas_meter,
@@ -136,16 +146,10 @@ impl<'a, T: Trait> ExecutionContext<'a, T> {
&self.self_account,
&dest,
endowment,
&mut overlay,
&mut nested,
)?;
}
let mut nested = ExecutionContext {
overlay: overlay,
self_account: dest.clone(),
depth: self.depth + 1,
};
let mut contract_code = Vec::new();
vm::execute(
init_code,
@@ -160,10 +164,11 @@ impl<'a, T: Trait> ExecutionContext<'a, T> {
).map_err(|_| "vm execute returned error while create")?;
nested.overlay.set_code(&dest, contract_code);
nested.overlay.into_change_set()
(nested.overlay.into_change_set(), nested.events)
};
self.overlay.commit(change_set);
self.events.extend(events);
Ok(CreateReceipt {
address: dest,
@@ -183,15 +188,15 @@ impl<'a, T: Trait> ExecutionContext<'a, T> {
/// Note, that the fee is denominated in `T::Balance` units, but
/// charged in `T::Gas` from the provided `gas_meter`. This means
/// that the actual amount charged might differ.
fn transfer<T: Trait>(
fn transfer<'a, T: Trait>(
gas_meter: &mut GasMeter<T>,
contract_create: bool,
transactor: &T::AccountId,
dest: &T::AccountId,
value: T::Balance,
overlay: &mut OverlayAccountDb<T>,
ctx: &mut ExecutionContext<'a, T>,
) -> Result<(), &'static str> {
let would_create = overlay.get_balance(dest).is_zero();
let would_create = ctx.overlay.get_balance(dest).is_zero();
let fee: T::Balance = if contract_create {
<Module<T>>::contract_fee()
@@ -207,7 +212,7 @@ fn transfer<T: Trait>(
return Err("not enough gas to pay transfer fee");
}
let from_balance = overlay.get_balance(transactor);
let from_balance = ctx.overlay.get_balance(transactor);
let new_from_balance = match from_balance.checked_sub(&value) {
Some(b) => b,
None => return Err("balance too low to send value"),
@@ -217,15 +222,16 @@ fn transfer<T: Trait>(
}
<T as balances::Trait>::EnsureAccountLiquid::ensure_account_liquid(transactor)?;
let to_balance = overlay.get_balance(dest);
let to_balance = ctx.overlay.get_balance(dest);
let new_to_balance = match to_balance.checked_add(&value) {
Some(b) => b,
None => return Err("destination balance too high to receive value"),
};
if transactor != dest {
overlay.set_balance(transactor, new_from_balance);
overlay.set_balance(dest, new_to_balance);
ctx.overlay.set_balance(transactor, new_from_balance);
ctx.overlay.set_balance(dest, new_to_balance);
ctx.events.push(RawEvent::Transfer(transactor.clone(), dest.clone(), value));
}
Ok(())
+30
View File
@@ -59,6 +59,9 @@ extern crate serde_derive;
#[cfg(feature = "std")]
extern crate serde;
#[macro_use]
extern crate parity_codec_derive;
extern crate parity_wasm;
extern crate pwasm_utils;
@@ -116,6 +119,9 @@ pub trait Trait: balances::Trait {
// As<u32> is needed for wasm-utils
type Gas: Parameter + Default + Codec + SimpleArithmetic + Copy + As<Self::Balance> + As<u64> + As<u32>;
/// The overarching event type.
type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
}
pub trait ContractAddressFor<AccountId: Sized> {
@@ -169,6 +175,17 @@ decl_module! {
}
}
decl_event! {
pub enum Event<T>
where
<T as balances::Trait>::Balance,
<T as system::Trait>::AccountId
{
/// Transfer happened `from` -> `to` with given `value` as part of a `message-call` or `create`.
Transfer(AccountId, AccountId, Balance),
}
}
decl_storage! {
trait Store for Module<T: Trait> as Contract {
/// The fee required to create a contract. At least as big as staking's ReclaimRebate.
@@ -206,6 +223,11 @@ impl<T: Trait> double_map::StorageDoubleMap for StorageOf<T> {
}
impl<T: Trait> Module<T> {
/// Deposit one of this module's events.
fn deposit_event(event: Event<T>) {
<system::Module<T>>::deposit_event(<T as Trait>::Event::from(event).into());
}
/// Make a call to a specified account, optionally transferring some balance.
fn call(
origin: <T as system::Trait>::Origin,
@@ -226,6 +248,7 @@ impl<T: Trait> Module<T> {
self_account: origin.clone(),
depth: 0,
overlay: OverlayAccountDb::<T>::new(&account_db::DirectAccountDb),
events: Vec::new(),
};
let mut output_data = Vec::new();
@@ -234,6 +257,9 @@ impl<T: Trait> Module<T> {
if let Ok(_) = result {
// Commit all changes that made it thus far into the persistant storage.
account_db::DirectAccountDb.commit(ctx.overlay.into_change_set());
// Then deposit all events produced.
ctx.events.into_iter().for_each(Self::deposit_event);
}
// Refund cost of the unused gas.
@@ -273,12 +299,16 @@ impl<T: Trait> Module<T> {
self_account: origin.clone(),
depth: 0,
overlay: OverlayAccountDb::<T>::new(&account_db::DirectAccountDb),
events: Vec::new(),
};
let result = ctx.create(origin.clone(), endowment, &mut gas_meter, &ctor_code, &data);
if let Ok(_) = result {
// Commit all changes that made it thus far into the persistant storage.
account_db::DirectAccountDb.commit(ctx.overlay.into_change_set());
// Then deposit all events produced.
ctx.events.into_iter().for_each(Self::deposit_event);
}
// Refund cost of the unused gas.
+73 -3
View File
@@ -21,16 +21,26 @@ use runtime_primitives::traits::{BlakeTwo256};
use runtime_primitives::BuildStorage;
use runtime_support::StorageMap;
use substrate_primitives::{Blake2Hasher};
use system::{Phase, EventRecord};
use wabt;
use {
runtime_io, balances, system, CodeOf, ContractAddressFor,
GenesisConfig, Module, StorageOf, Trait,
GenesisConfig, Module, StorageOf, Trait, RawEvent,
};
impl_outer_origin! {
pub enum Origin for Test {}
}
mod contract {
pub use super::super::*;
}
impl_outer_event! {
pub enum MetaEvent for Test {
balances<T>, contract<T>,
}
}
#[derive(Clone, Eq, PartialEq)]
pub struct Test;
impl system::Trait for Test {
@@ -42,7 +52,7 @@ impl system::Trait for Test {
type Digest = Digest;
type AccountId = u64;
type Header = Header;
type Event = ();
type Event = MetaEvent;
type Log = DigestItem;
}
impl balances::Trait for Test {
@@ -50,15 +60,17 @@ impl balances::Trait for Test {
type AccountIndex = u64;
type OnFreeBalanceZero = Contract;
type EnsureAccountLiquid = ();
type Event = ();
type Event = MetaEvent;
}
impl Trait for Test {
type Gas = u64;
type DetermineContractAddress = DummyContractAddressFor;
type Event = MetaEvent;
}
type Balances = balances::Module<Test>;
type Contract = Module<Test>;
type System = system::Module<Test>;
pub struct DummyContractAddressFor;
impl ContractAddressFor<u64> for DummyContractAddressFor {
@@ -205,6 +217,27 @@ fn contract_transfer() {
Balances::free_balance(&CONTRACT_SHOULD_TRANSFER_TO),
CONTRACT_SHOULD_TRANSFER_VALUE,
);
assert_eq!(System::events(), vec![
EventRecord {
phase: Phase::ApplyExtrinsic(0),
event: MetaEvent::balances(
balances::RawEvent::NewAccount(
CONTRACT_SHOULD_TRANSFER_TO,
0,
balances::NewAccountOutcome::NoHint
)
),
},
EventRecord {
phase: Phase::ApplyExtrinsic(0),
event: MetaEvent::contract(RawEvent::Transfer(0, 1, 3)),
},
EventRecord {
phase: Phase::ApplyExtrinsic(0),
event: MetaEvent::contract(RawEvent::Transfer(1, CONTRACT_SHOULD_TRANSFER_TO, 6)),
},
]);
});
}
@@ -316,6 +349,13 @@ fn contract_transfer_oog() {
assert_eq!(Balances::free_balance(&1), 14);
// But `ext_call` should not.
assert_eq!(Balances::free_balance(&CONTRACT_SHOULD_TRANSFER_TO), 0);
assert_eq!(System::events(), vec![
EventRecord {
phase: Phase::ApplyExtrinsic(0),
event: MetaEvent::contract(RawEvent::Transfer(0, 1, 3)),
},
]);
});
}
@@ -464,6 +504,27 @@ fn contract_create() {
assert_eq!(Balances::free_balance(&1), 8);
assert_eq!(Balances::free_balance(&derived_address), 3);
assert_eq!(System::events(), vec![
EventRecord {
phase: Phase::ApplyExtrinsic(0),
event: MetaEvent::balances(
balances::RawEvent::NewAccount(
derived_address,
0,
balances::NewAccountOutcome::NoHint
)
),
},
EventRecord {
phase: Phase::ApplyExtrinsic(0),
event: MetaEvent::contract(RawEvent::Transfer(0, 1, 11)),
},
EventRecord {
phase: Phase::ApplyExtrinsic(0),
event: MetaEvent::contract(RawEvent::Transfer(1, 2, 3)),
},
]);
// Initiate transfer to the newly created contract.
assert_ok!(Contract::call(Origin::signed(0), derived_address, 22, 100_000, Vec::new()));
@@ -516,6 +577,13 @@ fn top_level_create() {
assert_eq!(Balances::free_balance(&derived_address), 30 + 11);
assert_eq!(<CodeOf<Test>>::get(&derived_address), code_transfer);
assert_eq!(System::events(), vec![
EventRecord {
phase: Phase::ApplyExtrinsic(0),
event: MetaEvent::contract(RawEvent::Transfer(0, derived_address, 11)),
},
]);
});
}
@@ -643,6 +711,8 @@ fn top_level_call_refunds_even_if_fails() {
);
assert_eq!(Balances::free_balance(&0), 100_000_000 - (4 * 3) - (4 * 135));
assert_eq!(System::events(), vec![]);
});
}