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
+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),
}