diff --git a/Cargo.toml b/Cargo.toml index 1ed86c0e54..6be87ed520 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ frame-system = { git = "https://github.com/paritytech/substrate/", package = "fr pallet-balances = { git = "https://github.com/paritytech/substrate/", package = "pallet-balances" } pallet-contracts = { git = "https://github.com/paritytech/substrate/", package = "pallet-contracts" } pallet-indices = { git = "https://github.com/paritytech/substrate/", package = "pallet-indices" } +hex = "0.4.0" sc-rpc-api = { git = "https://github.com/paritytech/substrate/", package = "sc-rpc-api" } sp-rpc = { git = "https://github.com/paritytech/substrate/", package = "sp-rpc" } sp-core = { git = "https://github.com/paritytech/substrate/", package = "sp-core" } diff --git a/src/events.rs b/src/events.rs index e01649693d..64b53fb47b 100644 --- a/src/events.rs +++ b/src/events.rs @@ -201,7 +201,7 @@ impl EventsDecoder { if let Some(size) = self.type_sizes.get(name) { let mut buf = vec![0; *size]; input.read(&mut buf)?; - buf.encode_to(output); + output.write(&buf); } else { return Err(EventsError::TypeSizeUnavailable(name.to_owned())) } @@ -231,11 +231,6 @@ impl EventsDecoder { } else { let event_variant = input.read_byte()? as u8; let event_metadata = module.event(event_variant)?; - log::debug!( - "decoding event '{}::{}'", - module.name(), - event_metadata.name - ); let mut event_data = Vec::::new(); self.decode_raw_bytes( @@ -243,6 +238,14 @@ impl EventsDecoder { input, &mut event_data, )?; + + log::debug!( + "received event '{}::{}', raw bytes: {}", + module.name(), + event_metadata.name, + hex::encode(&event_data), + ); + RuntimeEvent::Raw(RawEvent { module: module.name().to_string(), variant: event_metadata.name.clone(), diff --git a/src/frame/contracts.rs b/src/frame/contracts.rs index 21c9b076d0..d9b3a1a9b6 100644 --- a/src/frame/contracts.rs +++ b/src/frame/contracts.rs @@ -24,9 +24,18 @@ use crate::frame::{ use codec::Encode; const MODULE: &str = "Contracts"; -const PUT_CODE: &str = "put_code"; -const CREATE: &str = "create"; -const CALL: &str = "call"; + +mod calls { + pub const PUT_CODE: &str = "put_code"; + pub const INSTANTIATE: &str = "instantiate"; + pub const CALL: &str = "call"; +} + +#[allow(unused)] +mod events { + pub const CODE_STORED: &str = "CodeStored"; + pub const INSTANTIATED: &str = "Instantiated"; +} /// Gas units are chosen to be represented by u64 so that gas metering /// instructions can operate on them efficiently. @@ -45,7 +54,8 @@ pub struct PutCodeArgs { /// Arguments for creating an instance of a contract #[derive(Encode)] -pub struct CreateArgs { +pub struct InstantiateArgs { + #[codec(compact)] endowment: ::Balance, #[codec(compact)] gas_limit: Gas, @@ -67,7 +77,7 @@ pub struct CallArgs { /// its `codehash`. /// You can instantiate contracts only with stored code. pub fn put_code(gas_limit: Gas, code: Vec) -> Call { - Call::new(MODULE, PUT_CODE, PutCodeArgs { gas_limit, code }) + Call::new(MODULE, calls::PUT_CODE, PutCodeArgs { gas_limit, code }) } /// Creates a new contract from the `codehash` generated by `put_code`, @@ -77,22 +87,22 @@ pub fn put_code(gas_limit: Gas, code: Vec) -> Call { /// /// - The destination address is computed based on the sender and hash of /// the code. -/// - The smart-contract account is created at the computed address. -/// - The `ctor_code` is executed in the context of the newly-created +/// - The smart-contract account is instantiated at the computed address. +/// - The `ctor_code` is executed in the context of the newly-instantiated /// account. Buffer returned after the execution is saved as the `code`https://www.bbc.co.uk/ /// of the account. That code will be invoked upon any call received by /// this account. /// - The contract is initialized. -pub fn create( +pub fn instantiate( endowment: ::Balance, gas_limit: Gas, code_hash: ::Hash, data: Vec, -) -> Call> { +) -> Call> { Call::new( MODULE, - CREATE, - CreateArgs { + calls::INSTANTIATE, + InstantiateArgs { endowment, gas_limit, code_hash, @@ -117,7 +127,7 @@ pub fn call( ) -> Call> { Call::new( MODULE, - CALL, + calls::CALL, CallArgs { dest, value, @@ -126,3 +136,114 @@ pub fn call( }, ) } + +#[cfg(test)] +mod tests { + use codec::{ + Codec, + Error as CodecError, + }; + use futures::Future; + use sp_core::Pair; + use sp_keyring::AccountKeyring; + use sp_runtime::traits::{ + IdentifyAccount, + Verify, + }; + + use super::events; + use crate::{ + frame::contracts::MODULE, + tests::test_setup, + Balances, + Client, + DefaultNodeRuntime as Runtime, + Error, + System, + }; + + type AccountId = ::AccountId; + + fn put_code( + client: &Client, + signer: P, + ) -> impl Future>, Error = Error> + where + T: System + Balances + Send + Sync, + T::Address: From, + P: Pair, + P::Signature: Codec, + S: 'static, + S: Verify + Codec + From, + S::Signer: From + IdentifyAccount, + { + const CONTRACT: &str = r#" +(module + (func (export "call")) + (func (export "deploy")) +) +"#; + let wasm = wabt::wat2wasm(CONTRACT).expect("invalid wabt"); + + client.xt(signer, None).and_then(|xt| { + xt.submit_and_watch(super::put_code(500_000, wasm)) + .map(|result| result.find_event::(MODULE, events::CODE_STORED)) + }) + } + + #[test] + #[ignore] // requires locally running substrate node + fn tx_put_code() { + let (mut rt, client) = test_setup(); + + let signer = AccountKeyring::Alice.pair(); + let code_hash = rt.block_on(put_code(&client, signer)).unwrap(); + + assert!( + code_hash.is_some(), + "Contracts CodeStored event should be present" + ); + assert!( + code_hash.unwrap().is_ok(), + "CodeStored Hash should decode successfully" + ); + } + + #[test] + #[ignore] // requires locally running substrate node + fn tx_instantiate() { + let (mut rt, client) = test_setup(); + + let signer = AccountKeyring::Alice.pair(); + let code_hash = rt + .block_on(put_code(&client, signer.clone())) + .unwrap() + .unwrap() + .unwrap(); + + println!("{:?}", code_hash); + + let instantiate = client.xt(signer, None).and_then(move |xt| { + xt.submit_and_watch(super::instantiate::( + 100_000_000_000_000, + 500_000, + code_hash, + Vec::new(), + )) + .map(|result| { + result.find_event::<(AccountId, AccountId)>(MODULE, events::INSTANTIATED) + }) + }); + + let result = rt.block_on(instantiate).unwrap(); + + assert!( + result.is_some(), + "Contracts Instantiated event should be present" + ); + assert!( + result.unwrap().is_ok(), + "Instantiated Event should decode successfully" + ); + } +} diff --git a/src/frame/system.rs b/src/frame/system.rs index 36beaefae5..236d451dde 100644 --- a/src/frame/system.rs +++ b/src/frame/system.rs @@ -169,11 +169,13 @@ pub fn set_code(code: Vec) -> Call { Call::new(MODULE, SET_CODE, code) } +use frame_support::weights::DispatchInfo; + /// Event for the System module. #[derive(Clone, Debug, codec::Decode)] pub enum SystemEvent { /// An extrinsic completed successfully. - ExtrinsicSuccess(frame_support::weights::DispatchInfo), + ExtrinsicSuccess(DispatchInfo), /// An extrinsic failed. - ExtrinsicFailed(sp_runtime::DispatchError), + ExtrinsicFailed(sp_runtime::DispatchError, DispatchInfo), } diff --git a/src/lib.rs b/src/lib.rs index e2f283f9b9..b7265b38e9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -421,7 +421,7 @@ mod tests { type Address = ::Address; type Balance = ::Balance; - fn test_setup() -> (tokio::runtime::Runtime, Client) { + pub(crate) fn test_setup() -> (tokio::runtime::Runtime, Client) { env_logger::try_init().ok(); let mut rt = tokio::runtime::Runtime::new().unwrap(); let client_future = ClientBuilder::::new().build(); @@ -450,41 +450,6 @@ mod tests { rt.block_on(transfer).unwrap(); } - #[test] - #[ignore] // requires locally running substrate node - fn test_tx_contract_put_code() { - let (mut rt, client) = test_setup(); - - let signer = AccountKeyring::Alice.pair(); - let xt = rt.block_on(client.xt(signer, None)).unwrap(); - - const CONTRACT: &str = r#" -(module - (func (export "call")) - (func (export "deploy")) -) -"#; - let wasm = wabt::wat2wasm(CONTRACT).expect("invalid wabt"); - - let put_code = xt.submit_and_watch(contracts::put_code(500_000, wasm)); - - let success = rt - .block_on(put_code) - .expect("Extrinsic should be included in a block"); - - let code_hash = - success.find_event::<::Hash>("Contracts", "CodeStored"); - - assert!( - code_hash.is_some(), - "Contracts CodeStored event should be present" - ); - assert!( - code_hash.unwrap().is_ok(), - "CodeStored Hash should decode successfully" - ); - } - #[test] #[ignore] // requires locally running substrate node fn test_getting_hash() { diff --git a/src/rpc.rs b/src/rpc.rs index 8473c19f67..f917a1146d 100644 --- a/src/rpc.rs +++ b/src/rpc.rs @@ -196,11 +196,20 @@ impl Rpc { { let mut storage_key = twox_128(b"System").to_vec(); storage_key.extend(twox_128(b"Events").to_vec()); - log::debug!("Events storage key {:?}", storage_key); + log::debug!("Events storage key {:?}", hex::encode(&storage_key)); let closure: MapClosure::Hash>> = Box::new(|event| { - log::info!("Event {:?}", event); + log::debug!( + "Event {:?}", + event + .changes + .iter() + .map(|(k, v)| { + (hex::encode(&k.0), v.as_ref().map(|v| hex::encode(&v.0))) + }) + .collect::>() + ); event }); self.state