Skip to content

Commit 385d214

Browse files
authored
Call a contract (paritytech#165)
* Revert contracts put_code test to pure code (not using the macro) * Test contract instantiate * Fmt * Extract put_code and new_client functions * Generate fresh accounts for contract tests to allow reruns without a chain purge * Fetch and increment nonce to allow concurrent test runs * fmt * Failing contract call test * Fmt and fix compilation * Fix error message for contract call * Fix call test * Update contract execution event comment * Remove redundant feature flags, now on module * Update event data comment * Use fetch_add * Fmt
1 parent 8e2a4f0 commit 385d214

File tree

1 file changed

+158
-52
lines changed

1 file changed

+158
-52
lines changed

src/frame/contracts.rs

Lines changed: 158 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ pub struct CallCall<'a, T: Contracts> {
9191
/// Address of the contract.
9292
pub dest: &'a <T as System>::Address,
9393
/// Value to transfer to the contract.
94+
#[codec(compact)]
9495
pub value: <T as Balances>::Balance,
9596
/// Gas limit.
9697
#[codec(compact)]
@@ -115,45 +116,162 @@ pub struct InstantiatedEvent<T: Contracts> {
115116
pub contract: <T as System>::AccountId,
116117
}
117118

119+
/// Contract execution event.
120+
///
121+
/// Emitted upon successful execution of a contract, if any contract events were produced.
122+
#[derive(Clone, Debug, Eq, PartialEq, Event, Decode)]
123+
pub struct ContractExecutionEvent<T: Contracts> {
124+
/// Caller of the contract.
125+
pub caller: <T as System>::AccountId,
126+
/// SCALE encoded contract event data.
127+
pub data: Vec<u8>,
128+
}
129+
118130
#[cfg(test)]
119131
#[cfg(feature = "integration-tests")]
120132
mod tests {
121133
use sp_keyring::AccountKeyring;
122134

123135
use super::*;
124136
use crate::{
137+
balances::*,
138+
system::*,
139+
Client,
125140
ClientBuilder,
126141
ContractsTemplateRuntime,
142+
Error,
143+
ExtrinsicSuccess,
127144
PairSigner,
145+
Signer,
146+
};
147+
use sp_core::{
148+
crypto::AccountId32,
149+
sr25519::Pair,
150+
};
151+
use std::sync::atomic::{
152+
AtomicU32,
153+
Ordering,
128154
};
129155

130-
fn contract_wasm() -> Vec<u8> {
131-
const CONTRACT: &str = r#"
132-
(module
133-
(func (export "call"))
134-
(func (export "deploy"))
135-
)
136-
"#;
137-
wabt::wat2wasm(CONTRACT).expect("invalid wabt")
156+
static STASH_NONCE: std::sync::atomic::AtomicU32 = AtomicU32::new(0);
157+
158+
struct TestContext {
159+
client: Client<ContractsTemplateRuntime>,
160+
signer: PairSigner<ContractsTemplateRuntime, Pair>,
138161
}
139162

140-
#[async_std::test]
141-
#[cfg(feature = "integration-tests")]
142-
async fn tx_put_code() {
143-
env_logger::try_init().ok();
163+
impl TestContext {
164+
async fn init() -> Self {
165+
env_logger::try_init().ok();
166+
167+
let client = ClientBuilder::<ContractsTemplateRuntime>::new()
168+
.build()
169+
.await
170+
.expect("Error creating client");
171+
let mut stash = PairSigner::new(AccountKeyring::Alice.pair());
172+
let nonce = client
173+
.account(&stash.account_id(), None)
174+
.await
175+
.unwrap()
176+
.nonce;
177+
let local_nonce = STASH_NONCE.fetch_add(1, Ordering::SeqCst);
178+
179+
stash.set_nonce(nonce + local_nonce);
180+
181+
let signer = Self::generate_account(&client, &mut stash).await;
182+
183+
TestContext { client, signer }
184+
}
185+
186+
/// generate a new keypair for an account, and fund it so it can perform smart contract operations
187+
async fn generate_account(
188+
client: &Client<ContractsTemplateRuntime>,
189+
stash: &mut PairSigner<ContractsTemplateRuntime, Pair>,
190+
) -> PairSigner<ContractsTemplateRuntime, Pair> {
191+
use sp_core::Pair as _;
192+
let new_account = Pair::generate().0;
193+
let new_account_id: AccountId32 = new_account.public().into();
194+
// fund the account
195+
let endowment = 200_000_000_000_000;
196+
let _ = client
197+
.transfer_and_watch(stash, &new_account_id, endowment)
198+
.await
199+
.expect("New account balance transfer failed");
200+
stash.increment_nonce();
201+
PairSigner::new(new_account)
202+
}
144203

145-
let signer = PairSigner::new(AccountKeyring::Alice.pair());
146-
let client = ClientBuilder::<ContractsTemplateRuntime>::new()
147-
.build()
148-
.await
149-
.unwrap();
204+
async fn put_code(
205+
&self,
206+
) -> Result<CodeStoredEvent<ContractsTemplateRuntime>, Error> {
207+
const CONTRACT: &str = r#"
208+
(module
209+
(func (export "call"))
210+
(func (export "deploy"))
211+
)
212+
"#;
213+
let code = wabt::wat2wasm(CONTRACT).expect("invalid wabt");
150214

151-
let code = contract_wasm();
152-
let result = client.put_code_and_watch(&signer, &code).await.unwrap();
153-
let code_stored = result.code_stored().unwrap();
215+
let result = self.client.put_code_and_watch(&self.signer, &code).await?;
216+
let code_stored = result.code_stored()?.ok_or_else(|| {
217+
Error::Other("Failed to find a CodeStored event".into())
218+
})?;
219+
log::info!("Code hash: {:?}", code_stored.code_hash);
220+
Ok(code_stored)
221+
}
222+
223+
async fn instantiate(
224+
&self,
225+
code_hash: &<ContractsTemplateRuntime as System>::Hash,
226+
data: &[u8],
227+
) -> Result<InstantiatedEvent<ContractsTemplateRuntime>, Error> {
228+
// call instantiate extrinsic
229+
let result = self
230+
.client
231+
.instantiate_and_watch(
232+
&self.signer,
233+
100_000_000_000_000, // endowment
234+
500_000_000, // gas_limit
235+
code_hash,
236+
data,
237+
)
238+
.await?;
239+
240+
log::info!("Instantiate result: {:?}", result);
241+
let instantiated = result.instantiated()?.ok_or_else(|| {
242+
Error::Other("Failed to find a Instantiated event".into())
243+
})?;
244+
245+
Ok(instantiated)
246+
}
247+
248+
async fn call(
249+
&self,
250+
contract: &<ContractsTemplateRuntime as System>::Address,
251+
input_data: &[u8],
252+
) -> Result<ExtrinsicSuccess<ContractsTemplateRuntime>, Error> {
253+
let result = self
254+
.client
255+
.call_and_watch(
256+
&self.signer,
257+
contract,
258+
0, // value
259+
500_000_000, // gas_limit
260+
input_data,
261+
)
262+
.await?;
263+
log::info!("Call result: {:?}", result);
264+
Ok(result)
265+
}
266+
}
267+
268+
#[async_std::test]
269+
async fn tx_put_code() {
270+
let ctx = TestContext::init().await;
271+
let code_stored = ctx.put_code().await;
154272

155273
assert!(
156-
code_stored.is_some(),
274+
code_stored.is_ok(),
157275
format!(
158276
"Error calling put_code and receiving CodeStored Event: {:?}",
159277
code_stored
@@ -162,41 +280,29 @@ mod tests {
162280
}
163281

164282
#[async_std::test]
165-
#[cfg(feature = "integration-tests")]
166283
async fn tx_instantiate() {
167-
env_logger::try_init().ok();
168-
let signer = PairSigner::new(AccountKeyring::Bob.pair());
169-
let client = ClientBuilder::<ContractsTemplateRuntime>::new()
170-
.build()
171-
.await
172-
.unwrap();
173-
174-
// call put_code extrinsic
175-
let code = contract_wasm();
176-
let result = client.put_code_and_watch(&signer, &code).await.unwrap();
177-
let code_stored = result.code_stored().unwrap();
178-
let code_hash = code_stored.unwrap().code_hash;
179-
180-
log::info!("Code hash: {:?}", code_hash);
181-
182-
// call instantiate extrinsic
183-
let result = client
184-
.instantiate_and_watch(
185-
&signer,
186-
100_000_000_000_000, // endowment
187-
500_000_000, // gas_limit
188-
&code_hash,
189-
&[], // data
190-
)
191-
.await
192-
.unwrap();
284+
let ctx = TestContext::init().await;
285+
let code_stored = ctx.put_code().await.unwrap();
286+
287+
let instantiated = ctx.instantiate(&code_stored.code_hash, &[]).await;
288+
289+
assert!(
290+
instantiated.is_ok(),
291+
format!("Error instantiating contract: {:?}", instantiated)
292+
);
293+
}
294+
295+
#[async_std::test]
296+
async fn tx_call() {
297+
let ctx = TestContext::init().await;
298+
let code_stored = ctx.put_code().await.unwrap();
193299

194-
log::info!("Instantiate result: {:?}", result);
195-
let event = result.instantiated().unwrap();
300+
let instantiated = ctx.instantiate(&code_stored.code_hash, &[]).await.unwrap();
301+
let executed = ctx.call(&instantiated.contract, &[]).await;
196302

197303
assert!(
198-
event.is_some(),
199-
format!("Error instantiating contract: {:?}", result)
304+
executed.is_ok(),
305+
format!("Error calling contract: {:?}", executed)
200306
);
201307
}
202308
}

0 commit comments

Comments
 (0)