mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-09 20:11:09 +00:00
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:
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user