Fix contracts instantiation (#51)

* Move contracts tests to contracts module

* Add test for instantiating a contract

* Fix InstantiateArgs encoding

* Ignore PhantomData primitives, they are 0

* Increase endowment and log code hash

* Update SystemEvent

* Fix codec import

* Oops

* Write buffer directly to output to avoid encoding vec

* Debug with StorageChanges encoded as hex

* Logging and format code
This commit is contained in:
Andrew Jones
2019-12-17 09:39:56 +00:00
committed by GitHub
parent f1926f55cf
commit daa1415297
6 changed files with 159 additions and 58 deletions
+1
View File
@@ -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" }
+9 -6
View File
@@ -201,7 +201,7 @@ impl<T: System + Balances + 'static> EventsDecoder<T> {
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<T: System + Balances + 'static> EventsDecoder<T> {
} 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::<u8>::new();
self.decode_raw_bytes(
@@ -243,6 +238,14 @@ impl<T: System + Balances + 'static> EventsDecoder<T> {
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(),
+133 -12
View File
@@ -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<T: Contracts> {
pub struct InstantiateArgs<T: Contracts> {
#[codec(compact)]
endowment: <T as Balances>::Balance,
#[codec(compact)]
gas_limit: Gas,
@@ -67,7 +77,7 @@ pub struct CallArgs<T: Contracts> {
/// its `codehash`.
/// You can instantiate contracts only with stored code.
pub fn put_code(gas_limit: Gas, code: Vec<u8>) -> Call<PutCodeArgs> {
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<u8>) -> Call<PutCodeArgs> {
///
/// - 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<T: Contracts>(
pub fn instantiate<T: Contracts>(
endowment: <T as Balances>::Balance,
gas_limit: Gas,
code_hash: <T as System>::Hash,
data: Vec<u8>,
) -> Call<CreateArgs<T>> {
) -> Call<InstantiateArgs<T>> {
Call::new(
MODULE,
CREATE,
CreateArgs {
calls::INSTANTIATE,
InstantiateArgs {
endowment,
gas_limit,
code_hash,
@@ -117,7 +127,7 @@ pub fn call<T: Contracts>(
) -> Call<CallArgs<T>> {
Call::new(
MODULE,
CALL,
calls::CALL,
CallArgs {
dest,
value,
@@ -126,3 +136,114 @@ pub fn call<T: Contracts>(
},
)
}
#[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 = <Runtime as System>::AccountId;
fn put_code<T, P, S>(
client: &Client<T, S>,
signer: P,
) -> impl Future<Item = Option<Result<T::Hash, CodecError>>, Error = Error>
where
T: System + Balances + Send + Sync,
T::Address: From<T::AccountId>,
P: Pair,
P::Signature: Codec,
S: 'static,
S: Verify + Codec + From<P::Signature>,
S::Signer: From<P::Public> + IdentifyAccount<AccountId = T::AccountId>,
{
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::<T::Hash>(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::<Runtime>(
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"
);
}
}
+4 -2
View File
@@ -169,11 +169,13 @@ pub fn set_code(code: Vec<u8>) -> Call<SetCode> {
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),
}
+1 -36
View File
@@ -421,7 +421,7 @@ mod tests {
type Address = <Runtime as System>::Address;
type Balance = <Runtime as Balances>::Balance;
fn test_setup() -> (tokio::runtime::Runtime, Client<Runtime>) {
pub(crate) fn test_setup() -> (tokio::runtime::Runtime, Client<Runtime>) {
env_logger::try_init().ok();
let mut rt = tokio::runtime::Runtime::new().unwrap();
let client_future = ClientBuilder::<Runtime>::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::<<Runtime as System>::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() {
+11 -2
View File
@@ -196,11 +196,20 @@ impl<T: System + Balances + 'static> Rpc<T> {
{
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<StorageChangeSet<<T as System>::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::<Vec<_>>()
);
event
});
self.state