-
Notifications
You must be signed in to change notification settings - Fork 254
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Call a contract #165
Call a contract #165
Changes from 12 commits
9a86802
0e7bc2d
92bcd26
e5800f4
a958819
c6906c0
477cac9
f12013e
e7c9eb2
4b5ef63
f6f9c61
635f958
ae82617
2c85d21
3f4ae68
779cb18
6684552
93b4e61
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -91,6 +91,7 @@ pub struct CallCall<'a, T: Contracts> { | |
/// Address of the contract. | ||
pub dest: &'a <T as System>::Address, | ||
/// Value to transfer to the contract. | ||
#[codec(compact)] | ||
pub value: <T as Balances>::Balance, | ||
/// Gas limit. | ||
#[codec(compact)] | ||
|
@@ -115,44 +116,164 @@ pub struct InstantiatedEvent<T: Contracts> { | |
pub contract: <T as System>::AccountId, | ||
} | ||
|
||
/// Contract execution event. | ||
/// | ||
/// Raised upon successful executionFailing of a contract call | ||
#[derive(Clone, Debug, Eq, PartialEq, Event, Decode)] | ||
pub struct ContractExecutionEvent<T: Contracts> { | ||
/// Caller of the contract. | ||
pub caller: <T as System>::AccountId, | ||
/// Raw contract event data | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this SCALE encoded data that is runtime dependent or can we say anything at all about the content? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The data is the encoded contract event, I will update the comment |
||
pub data: Vec<u8>, | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use sp_keyring::AccountKeyring; | ||
|
||
use super::*; | ||
use crate::{ | ||
balances::*, | ||
system::*, | ||
Client, | ||
ClientBuilder, | ||
ContractsTemplateRuntime, | ||
Error, | ||
ExtrinsicSuccess, | ||
PairSigner, | ||
Signer, | ||
}; | ||
use sp_core::{ | ||
crypto::AccountId32, | ||
sr25519::Pair, | ||
}; | ||
use std::sync::atomic::{ | ||
AtomicU32, | ||
Ordering, | ||
}; | ||
|
||
fn contract_wasm() -> Vec<u8> { | ||
const CONTRACT: &str = r#" | ||
(module | ||
(func (export "call")) | ||
(func (export "deploy")) | ||
) | ||
"#; | ||
wabt::wat2wasm(CONTRACT).expect("invalid wabt") | ||
static STASH_NONCE: std::sync::atomic::AtomicU32 = AtomicU32::new(0); | ||
|
||
struct TestContext { | ||
client: Client<ContractsTemplateRuntime>, | ||
signer: PairSigner<ContractsTemplateRuntime, Pair>, | ||
} | ||
|
||
impl TestContext { | ||
async fn init() -> Self { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is useful enough that it might be worth making it generic over the runtime and put it in a |
||
env_logger::try_init().ok(); | ||
|
||
let client = ClientBuilder::<ContractsTemplateRuntime>::new() | ||
.build() | ||
.await | ||
.expect("Error creating client"); | ||
let mut stash = PairSigner::new(AccountKeyring::Alice.pair()); | ||
let nonce = client | ||
.account(&stash.account_id(), None) | ||
.await | ||
.unwrap() | ||
.nonce; | ||
let local_nonce = STASH_NONCE | ||
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(x + 1)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not |
||
.unwrap(); | ||
|
||
stash.set_nonce(nonce + local_nonce); | ||
|
||
let signer = Self::generate_account(&client, &mut stash).await; | ||
|
||
TestContext { client, signer } | ||
} | ||
|
||
/// generate a new keypair for an account, and fund it so it can perform smart contract operations | ||
async fn generate_account( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as above: consider making this available to other code. |
||
client: &Client<ContractsTemplateRuntime>, | ||
stash: &mut PairSigner<ContractsTemplateRuntime, Pair>, | ||
) -> PairSigner<ContractsTemplateRuntime, Pair> { | ||
use sp_core::Pair as _; | ||
let new_account = Pair::generate().0; | ||
let new_account_id: AccountId32 = new_account.public().into(); | ||
// fund the account | ||
let endowment = 200_000_000_000_000; | ||
let _ = client | ||
.transfer_and_watch(stash, &new_account_id, endowment) | ||
.await | ||
.expect("New account balance transfer failed"); | ||
stash.increment_nonce(); | ||
PairSigner::new(new_account) | ||
} | ||
|
||
async fn put_code( | ||
&self, | ||
) -> Result<CodeStoredEvent<ContractsTemplateRuntime>, Error> { | ||
const CONTRACT: &str = r#" | ||
(module | ||
(func (export "call")) | ||
(func (export "deploy")) | ||
) | ||
"#; | ||
let code = wabt::wat2wasm(CONTRACT).expect("invalid wabt"); | ||
|
||
let result = self.client.put_code_and_watch(&self.signer, &code).await?; | ||
let code_stored = result.code_stored()?.ok_or_else(|| { | ||
Error::Other("Failed to find a CodeStored event".into()) | ||
})?; | ||
log::info!("Code hash: {:?}", code_stored.code_hash); | ||
Ok(code_stored) | ||
} | ||
|
||
async fn instantiate( | ||
dvdplm marked this conversation as resolved.
Show resolved
Hide resolved
|
||
&self, | ||
code_hash: &<ContractsTemplateRuntime as System>::Hash, | ||
data: &[u8], | ||
) -> Result<InstantiatedEvent<ContractsTemplateRuntime>, Error> { | ||
// call instantiate extrinsic | ||
let result = self | ||
.client | ||
.instantiate_and_watch( | ||
&self.signer, | ||
100_000_000_000_000, // endowment | ||
500_000_000, // gas_limit | ||
code_hash, | ||
data, | ||
) | ||
.await?; | ||
|
||
log::info!("Instantiate result: {:?}", result); | ||
let instantiated = result.instantiated()?.ok_or_else(|| { | ||
Error::Other("Failed to find a Instantiated event".into()) | ||
})?; | ||
|
||
Ok(instantiated) | ||
} | ||
|
||
async fn call( | ||
&self, | ||
contract: &<ContractsTemplateRuntime as System>::Address, | ||
input_data: &[u8], | ||
) -> Result<ExtrinsicSuccess<ContractsTemplateRuntime>, Error> { | ||
let result = self | ||
.client | ||
.call_and_watch( | ||
&self.signer, | ||
contract, | ||
0, // value | ||
500_000_000, // gas_limit | ||
input_data, | ||
) | ||
.await?; | ||
log::info!("Call result: {:?}", result); | ||
Ok(result) | ||
} | ||
} | ||
|
||
#[async_std::test] | ||
#[cfg(feature = "integration-tests")] | ||
async fn tx_put_code() { | ||
env_logger::try_init().ok(); | ||
|
||
let signer = PairSigner::new(AccountKeyring::Alice.pair()); | ||
let client = ClientBuilder::<ContractsTemplateRuntime>::new() | ||
.build() | ||
.await | ||
.unwrap(); | ||
|
||
let code = contract_wasm(); | ||
let result = client.put_code_and_watch(&signer, &code).await.unwrap(); | ||
let code_stored = result.code_stored().unwrap(); | ||
let ctx = TestContext::init().await; | ||
let code_stored = ctx.put_code().await; | ||
|
||
assert!( | ||
code_stored.is_some(), | ||
code_stored.is_ok(), | ||
format!( | ||
"Error calling put_code and receiving CodeStored Event: {:?}", | ||
code_stored | ||
|
@@ -163,39 +284,29 @@ mod tests { | |
#[async_std::test] | ||
#[cfg(feature = "integration-tests")] | ||
async fn tx_instantiate() { | ||
env_logger::try_init().ok(); | ||
let signer = PairSigner::new(AccountKeyring::Bob.pair()); | ||
let client = ClientBuilder::<ContractsTemplateRuntime>::new() | ||
.build() | ||
.await | ||
.unwrap(); | ||
|
||
// call put_code extrinsic | ||
let code = contract_wasm(); | ||
let result = client.put_code_and_watch(&signer, &code).await.unwrap(); | ||
let code_stored = result.code_stored().unwrap(); | ||
let code_hash = code_stored.unwrap().code_hash; | ||
|
||
log::info!("Code hash: {:?}", code_hash); | ||
|
||
// call instantiate extrinsic | ||
let result = client | ||
.instantiate_and_watch( | ||
&signer, | ||
100_000_000_000_000, // endowment | ||
500_000_000, // gas_limit | ||
&code_hash, | ||
&[], // data | ||
) | ||
.await | ||
.unwrap(); | ||
let ctx = TestContext::init().await; | ||
let code_stored = ctx.put_code().await.unwrap(); | ||
|
||
let instantiated = ctx.instantiate(&code_stored.code_hash, &[]).await; | ||
|
||
assert!( | ||
instantiated.is_ok(), | ||
format!("Error instantiating contract: {:?}", instantiated) | ||
); | ||
} | ||
|
||
#[async_std::test] | ||
#[cfg(feature = "integration-tests")] | ||
async fn tx_call() { | ||
let ctx = TestContext::init().await; | ||
let code_stored = ctx.put_code().await.unwrap(); | ||
|
||
log::info!("Instantiate result: {:?}", result); | ||
let event = result.instantiated().unwrap(); | ||
let instantiated = ctx.instantiate(&code_stored.code_hash, &[]).await.unwrap(); | ||
let executed = ctx.call(&instantiated.contract, &[]).await; | ||
|
||
assert!( | ||
event.is_some(), | ||
format!("Error instantiating contract: {:?}", result) | ||
executed.is_ok(), | ||
format!("Error calling contract: {:?}", executed) | ||
); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.