Implement new API for sign_and_submit_then_watch (#354)

* WIP Implementing new event subscription API

* back to lifetimes, fix example

* no more need for accept_weak_inclusion

* thread lifetime through to prevent 'temporary dropped' issue

* make working with events a little nicer

* Get tests compiling

* fmt and clippy

* _name back to name

* dont take ownership, just have stronger note

* Attempt to fix test

* remove commented-out code

* Add a couple more helper methods and a test

* Remove custom ExtrinsicFailed handling; treat them like other events

* Handle runtime errors in TransactionProgress related bits

* cargo fmt + clippy

* Fix some of the failing tests

* remove unused import

* fix transfer_error test

* Fix compile errors against new substrate latest

* Comment tweaks, and force test-runtime rebuild

* Drop the TransactionProgress subscription when we hit 'end' statuses

* cargo fmt

* find_event to find_first_event and helper to return all matching events

* TransactionProgressStatus to TransactionStatus

* Copy and improve docs on TransactionStatus from substrate

* debug impl for Client to avoid manual debug impls elsewhere

* Add and tweak comments, specifically a note about block inclusion on errors

* clippy + fmt

* Fix docs

* Ignore 'error' statuses and adhere to the substrate docs

* tweak and improve some comments per @dvdplm's suggestions

* Break transaction* structs into separate file

* fmt and fix doc link
This commit is contained in:
James Wilson
2021-12-09 12:23:08 +00:00
committed by GitHub
parent 55aafa25d8
commit 5db9b73899
16 changed files with 691 additions and 380 deletions
+62 -25
View File
@@ -41,7 +41,7 @@ use subxt::{
};
#[async_std::test]
async fn tx_basic_transfer() {
async fn tx_basic_transfer() -> Result<(), subxt::Error> {
let alice = PairSigner::<DefaultConfig, _>::new(AccountKeyring::Alice.pair());
let bob = PairSigner::<DefaultConfig, _>::new(AccountKeyring::Bob.pair());
let bob_address = bob.account_id().clone().into();
@@ -52,28 +52,27 @@ async fn tx_basic_transfer() {
.storage()
.system()
.account(alice.account_id().clone(), None)
.await
.unwrap();
.await?;
let bob_pre = api
.storage()
.system()
.account(bob.account_id().clone(), None)
.await
.unwrap();
.await?;
let result = api
let events = api
.tx()
.balances()
.transfer(bob_address, 10_000)
.sign_and_submit_then_watch(&alice)
.await
.unwrap();
let event = result
.find_event::<balances::events::Transfer>()
.unwrap()
.unwrap();
let _extrinsic_success = result
.find_event::<system::events::ExtrinsicSuccess>()
.await?
.wait_for_finalized_success()
.await?;
let event = events
.find_first_event::<balances::events::Transfer>()
.expect("Failed to decode balances::events::Transfer")
.expect("Failed to find balances::events::Transfer");
let _extrinsic_success = events
.find_first_event::<system::events::ExtrinsicSuccess>()
.expect("Failed to decode ExtrinisicSuccess")
.expect("Failed to find ExtrinisicSuccess");
@@ -88,17 +87,16 @@ async fn tx_basic_transfer() {
.storage()
.system()
.account(alice.account_id().clone(), None)
.await
.unwrap();
.await?;
let bob_post = api
.storage()
.system()
.account(bob.account_id().clone(), None)
.await
.unwrap();
.await?;
assert!(alice_pre.data.free - 10_000 >= alice_post.data.free);
assert_eq!(bob_pre.data.free + 10_000, bob_post.data.free);
Ok(())
}
#[async_std::test]
@@ -120,8 +118,7 @@ async fn storage_balance_lock() -> Result<(), subxt::Error> {
let charlie = AccountKeyring::Charlie.to_account_id();
let cxt = test_context().await;
let result = cxt
.api
cxt.api
.tx()
.staking()
.bond(
@@ -130,10 +127,11 @@ async fn storage_balance_lock() -> Result<(), subxt::Error> {
runtime_types::pallet_staking::RewardDestination::Stash,
)
.sign_and_submit_then_watch(&bob)
.await?;
let success = result.find_event::<system::events::ExtrinsicSuccess>()?;
assert!(success.is_some(), "No ExtrinsicSuccess Event found");
.await?
.wait_for_finalized_success()
.await?
.find_first_event::<system::events::ExtrinsicSuccess>()?
.expect("No ExtrinsicSuccess Event found");
let locks = cxt
.api
@@ -169,6 +167,9 @@ async fn transfer_error() {
.transfer(hans_address, 100_000_000_000_000_000)
.sign_and_submit_then_watch(&alice)
.await
.unwrap()
.wait_for_finalized_success()
.await
.unwrap();
let res = cxt
@@ -177,6 +178,9 @@ async fn transfer_error() {
.balances()
.transfer(alice_addr, 100_000_000_000_000_000)
.sign_and_submit_then_watch(&hans)
.await
.unwrap()
.wait_for_finalized_success()
.await;
if let Err(Error::Runtime(RuntimeError::Module(error))) = res {
@@ -187,7 +191,7 @@ async fn transfer_error() {
};
assert_eq!(error, error2);
} else {
panic!("expected an error");
panic!("expected a runtime module error");
}
}
@@ -223,6 +227,39 @@ async fn transfer_subscription() {
);
}
#[async_std::test]
async fn transfer_implicit_subscription() {
env_logger::try_init().ok();
let alice = PairSigner::<DefaultConfig, _>::new(AccountKeyring::Alice.pair());
let bob = AccountKeyring::Bob.to_account_id();
let bob_addr = bob.clone().into();
let cxt = test_context().await;
let event = cxt
.api
.tx()
.balances()
.transfer(bob_addr, 10_000)
.sign_and_submit_then_watch(&alice)
.await
.unwrap()
.wait_for_finalized_success()
.await
.unwrap()
.find_first_event::<balances::events::Transfer>()
.expect("Can decode events")
.expect("Can find balance transfer event");
assert_eq!(
event,
balances::events::Transfer {
from: alice.account_id().clone(),
to: bob.clone(),
amount: 10_000
}
);
}
#[async_std::test]
async fn constant_existential_deposit() {
let cxt = test_context().await;
+18 -11
View File
@@ -35,8 +35,8 @@ use subxt::{
Client,
Config,
Error,
ExtrinsicSuccess,
PairSigner,
TransactionProgress,
};
struct ContractsTestContext {
@@ -73,7 +73,7 @@ impl ContractsTestContext {
"#;
let code = wabt::wat2wasm(CONTRACT).expect("invalid wabt");
let result = self
let events = self
.cxt
.api
.tx()
@@ -81,26 +81,29 @@ impl ContractsTestContext {
.instantiate_with_code(
100_000_000_000_000_000, // endowment
500_000_000_000, // gas_limit
None, // storage_deposit_limit
code,
vec![], // data
vec![], // salt
)
.sign_and_submit_then_watch(&self.signer)
.await?
.wait_for_finalized_success()
.await?;
let code_stored = result
.find_event::<events::CodeStored>()?
let code_stored = events
.find_first_event::<events::CodeStored>()?
.ok_or_else(|| Error::Other("Failed to find a CodeStored event".into()))?;
let instantiated = result
.find_event::<events::Instantiated>()?
let instantiated = events
.find_first_event::<events::Instantiated>()?
.ok_or_else(|| Error::Other("Failed to find a Instantiated event".into()))?;
let _extrinsic_success = result
.find_event::<system::events::ExtrinsicSuccess>()?
let _extrinsic_success = events
.find_first_event::<system::events::ExtrinsicSuccess>()?
.ok_or_else(|| {
Error::Other("Failed to find a ExtrinsicSuccess event".into())
})?;
log::info!(" Block hash: {:?}", result.block);
log::info!(" Block hash: {:?}", events.block_hash());
log::info!(" Code hash: {:?}", code_stored.code_hash);
log::info!(" Contract address: {:?}", instantiated.contract);
Ok((code_stored.code_hash, instantiated.contract))
@@ -118,16 +121,19 @@ impl ContractsTestContext {
.instantiate(
100_000_000_000_000_000, // endowment
500_000_000_000, // gas_limit
None, // storage_deposit_limit
code_hash,
data,
salt,
)
.sign_and_submit_then_watch(&self.signer)
.await?
.wait_for_finalized_success()
.await?;
log::info!("Instantiate result: {:?}", result);
let instantiated = result
.find_event::<events::Instantiated>()?
.find_first_event::<events::Instantiated>()?
.ok_or_else(|| Error::Other("Failed to find a Instantiated event".into()))?;
Ok(instantiated.contract)
@@ -137,7 +143,7 @@ impl ContractsTestContext {
&self,
contract: AccountId,
input_data: Vec<u8>,
) -> Result<ExtrinsicSuccess<DefaultConfig>, Error> {
) -> Result<TransactionProgress<'_, DefaultConfig>, Error> {
log::info!("call: {:?}", contract);
let result = self
.contracts_tx()
@@ -145,6 +151,7 @@ impl ContractsTestContext {
MultiAddress::Id(contract),
0, // value
500_000_000, // gas_limit
None, // storage_deposit_limit
input_data,
)
.sign_and_submit_then_watch(&self.signer)
+33 -23
View File
@@ -21,7 +21,6 @@ use crate::{
ValidatorPrefs,
},
staking,
system,
DefaultConfig,
},
test_context,
@@ -55,21 +54,19 @@ fn default_validator_prefs() -> ValidatorPrefs {
}
#[async_std::test]
async fn validate_with_controller_account() -> Result<(), Error> {
async fn validate_with_controller_account() {
let alice = PairSigner::<DefaultConfig, _>::new(AccountKeyring::Alice.pair());
let cxt = test_context().await;
let result = cxt
.api
cxt.api
.tx()
.staking()
.validate(default_validator_prefs())
.sign_and_submit_then_watch(&alice)
.await?;
let success = result.find_event::<system::events::ExtrinsicSuccess>()?;
assert!(success.is_some());
Ok(())
.await
.unwrap()
.wait_for_finalized_success()
.await
.expect("should be successful");
}
#[async_std::test]
@@ -82,6 +79,8 @@ async fn validate_not_possible_for_stash_account() -> Result<(), Error> {
.staking()
.validate(default_validator_prefs())
.sign_and_submit_then_watch(&alice_stash)
.await?
.wait_for_finalized_success()
.await;
assert_matches!(announce_validator, Err(Error::Runtime(RuntimeError::Module(module_err))) => {
assert_eq!(module_err.pallet, "Staking");
@@ -91,23 +90,21 @@ async fn validate_not_possible_for_stash_account() -> Result<(), Error> {
}
#[async_std::test]
async fn nominate_with_controller_account() -> Result<(), Error> {
async fn nominate_with_controller_account() {
let alice = PairSigner::<DefaultConfig, _>::new(AccountKeyring::Alice.pair());
let bob = PairSigner::<DefaultConfig, _>::new(AccountKeyring::Bob.pair());
let cxt = test_context().await;
let result = cxt
.api
cxt.api
.tx()
.staking()
.nominate(vec![bob.account_id().clone().into()])
.sign_and_submit_then_watch(&alice)
.await?;
let success = result.find_event::<system::events::ExtrinsicSuccess>()?;
assert!(success.is_some());
Ok(())
.await
.unwrap()
.wait_for_finalized_success()
.await
.expect("should be successful");
}
#[async_std::test]
@@ -123,6 +120,8 @@ async fn nominate_not_possible_for_stash_account() -> Result<(), Error> {
.staking()
.nominate(vec![bob.account_id().clone().into()])
.sign_and_submit_then_watch(&alice_stash)
.await?
.wait_for_finalized_success()
.await;
assert_matches!(nomination, Err(Error::Runtime(RuntimeError::Module(module_err))) => {
@@ -147,6 +146,8 @@ async fn chill_works_for_controller_only() -> Result<(), Error> {
.staking()
.nominate(vec![bob_stash.account_id().clone().into()])
.sign_and_submit_then_watch(&alice)
.await?
.wait_for_finalized_success()
.await?;
let ledger = cxt
@@ -164,6 +165,8 @@ async fn chill_works_for_controller_only() -> Result<(), Error> {
.staking()
.chill()
.sign_and_submit_then_watch(&alice_stash)
.await?
.wait_for_finalized_success()
.await;
assert_matches!(chill, Err(Error::Runtime(RuntimeError::Module(module_err))) => {
@@ -171,15 +174,18 @@ async fn chill_works_for_controller_only() -> Result<(), Error> {
assert_eq!(module_err.error, "NotController");
});
let result = cxt
let is_chilled = cxt
.api
.tx()
.staking()
.chill()
.sign_and_submit_then_watch(&alice)
.await?;
let chill = result.find_event::<staking::events::Chilled>()?;
assert!(chill.is_some());
.await?
.wait_for_finalized_success()
.await?
.has_event::<staking::events::Chilled>()?;
assert!(is_chilled);
Ok(())
}
@@ -198,6 +204,8 @@ async fn tx_bond() -> Result<(), Error> {
RewardDestination::Stash,
)
.sign_and_submit_then_watch(&alice)
.await?
.wait_for_finalized_success()
.await;
assert!(bond.is_ok());
@@ -212,6 +220,8 @@ async fn tx_bond() -> Result<(), Error> {
RewardDestination::Stash,
)
.sign_and_submit_then_watch(&alice)
.await?
.wait_for_finalized_success()
.await;
assert_matches!(bond_again, Err(Error::Runtime(RuntimeError::Module(module_err))) => {
+17 -13
View File
@@ -22,7 +22,6 @@ use crate::{
},
test_context,
};
use assert_matches::assert_matches;
use sp_keyring::AccountKeyring;
use subxt::extrinsic::PairSigner;
@@ -30,7 +29,7 @@ type Call = runtime_types::node_runtime::Call;
type BalancesCall = runtime_types::pallet_balances::pallet::Call;
#[async_std::test]
async fn test_sudo() {
async fn test_sudo() -> Result<(), subxt::Error> {
let alice = PairSigner::<DefaultConfig, _>::new(AccountKeyring::Alice.pair());
let bob = AccountKeyring::Bob.to_account_id().into();
let cxt = test_context().await;
@@ -40,20 +39,23 @@ async fn test_sudo() {
value: 10_000,
});
let res = cxt
let found_event = cxt
.api
.tx()
.sudo()
.sudo(call)
.sign_and_submit_then_watch(&alice)
.await
.unwrap();
let sudid = res.find_event::<sudo::events::Sudid>();
assert_matches!(sudid, Ok(Some(_)))
.await?
.wait_for_finalized_success()
.await?
.has_event::<sudo::events::Sudid>()?;
assert!(found_event);
Ok(())
}
#[async_std::test]
async fn test_sudo_unchecked_weight() {
async fn test_sudo_unchecked_weight() -> Result<(), subxt::Error> {
let alice = PairSigner::<DefaultConfig, _>::new(AccountKeyring::Alice.pair());
let bob = AccountKeyring::Bob.to_account_id().into();
let cxt = test_context().await;
@@ -63,15 +65,17 @@ async fn test_sudo_unchecked_weight() {
value: 10_000,
});
let res = cxt
let found_event = cxt
.api
.tx()
.sudo()
.sudo_unchecked_weight(call, 0)
.sign_and_submit_then_watch(&alice)
.await
.unwrap();
.await?
.wait_for_finalized_success()
.await?
.has_event::<sudo::events::Sudid>()?;
let sudid = res.find_event::<sudo::events::Sudid>();
assert_matches!(sudid, Ok(Some(_)))
assert!(found_event);
Ok(())
}
+12 -8
View File
@@ -29,7 +29,7 @@ use subxt::extrinsic::{
};
#[async_std::test]
async fn storage_account() {
async fn storage_account() -> Result<(), subxt::Error> {
let alice = PairSigner::<DefaultConfig, _>::new(AccountKeyring::Alice.pair());
let cxt = test_context().await;
@@ -39,23 +39,27 @@ async fn storage_account() {
.system()
.account(alice.account_id().clone(), None)
.await;
assert_matches!(account_info, Ok(_))
assert_matches!(account_info, Ok(_));
Ok(())
}
#[async_std::test]
async fn tx_remark_with_event() {
async fn tx_remark_with_event() -> Result<(), subxt::Error> {
let alice = PairSigner::<DefaultConfig, _>::new(AccountKeyring::Alice.pair());
let cxt = test_context().await;
let result = cxt
let found_event = cxt
.api
.tx()
.system()
.remark_with_event(b"remarkable".to_vec())
.sign_and_submit_then_watch(&alice)
.await
.unwrap();
.await?
.wait_for_finalized_success()
.await?
.has_event::<system::events::Remarked>()?;
let remarked = result.find_event::<system::events::Remarked>();
assert_matches!(remarked, Ok(Some(_)));
assert!(found_event);
Ok(())
}