mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-09 20:11:09 +00:00
Contracts: Add transfer event (#801)
This commit is contained in:
committed by
Gav Wood
parent
a613c62dc1
commit
98e0a3a55a
Generated
+1
@@ -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)",
|
||||
|
||||
@@ -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>},
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
Generated
+1
@@ -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)",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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(())
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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![]);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user