Rework Subxt API to support offline and dynamic transactions (#593)

* WIP API changes

* debug impls

* Get main crate compiling with first round of changes

* Some tidy up

* Add WithExtrinsicParams, and have SubstrateConfig + PolkadotConfig, not DefaultConfig

* move transaction into extrinsic folder

* Add runtime updates back to OnlineClient

* rework to be 'client first' to fit better with storage + events

* add support for events to Client

* tidy dupe trait bound

* Wire storage into client, but need to remove static reliance

* various tidy up and start stripping codegen to remove bits we dont need now

* First pass updating calls and constants codegen

* WIP storage client updates

* First pass migrated runtime storage over to new format

* pass over codegen to generate StorageAddresses and throw other stuff out

* don't need a Call trait any more

* shuffle things around a bit

* Various proc_macro fixes to get 'cargo check' working

* organise what's exposed from subxt

* Get first example working; balance_transfer_with_params

* get balance_transfer example compiling

* get concurrent_storage_requests.rs example compiling

* get fetch_all_accounts example compiling

* get a bunch more of the examples compiling

* almost get final example working; type mismatch to look into

* wee tweaks

* move StorageAddress to separate file

* pass Defaultable/Iterable info to StorageAddress in codegen

* fix storage validation ne, and partial run through example code

* Remove static iteration and strip a generic param from everything

* fix doc tests in subxt crate

* update test utils and start fixing frame tests

* fix frame staking tests

* fix the rest of the test compile issues, Borrow on storage values

* cargo fmt

* remove extra logging during tests

* Appease clippy and no more need for into_iter on events

* cargo fmt

* fix dryRun tests by waiting for blocks

* wait for blocks instead of sleeping or other test hacks

* cargo fmt

* Fix doc links

* Traitify StorageAddress

* remove out-of-date doc comments

* optimise decoding storage a little

* cleanup tx stuff, trait for TxPayload, remove Err type param and decode at runtime

* clippy fixes

* fix doc links

* fix doc example

* constant address trait for consistency

* fix a typo and remove EncodeWithMetadata stuff

* Put EventDetails behind a proper interface and allow decoding into top level event, too

* fix docs

* tweak StorageAddress docs

* re-export StorageAddress at root for consistency

* fix clippy things

* Add support for dynamic values

* fix double encoding of storage map key after refactor

* clippy fix

* Fixes and add a dynamic usage example (needs new scale_value release)

* bump scale_value version

* cargo fmt

* Tweak event bits

* cargo fmt

* Add a test and bump scale-value to 0.4.0 to support this

* remove unnecessary vec from dynamic example

* Various typo/grammar fixes

Co-authored-by: Alexandru Vasile <60601340+lexnv@users.noreply.github.com>

* Address PR nits

* Undo accidental rename in changelog

* Small PR nits/tidyups

* fix tests; codegen change against latest substrate

* tweak storage address util names

* move error decoding to DecodeError and expose

* impl some basic traits on the extrinsic param builder

Co-authored-by: Alexandru Vasile <60601340+lexnv@users.noreply.github.com>
This commit is contained in:
James Wilson
2022-08-08 11:55:20 +01:00
committed by GitHub
parent 7a09ac6cd7
commit e48f0e3b1d
84 changed files with 23097 additions and 35863 deletions
+169 -84
View File
@@ -4,10 +4,10 @@
use crate::{
node_runtime::{
self,
balances,
runtime_types,
system,
DispatchError,
},
pair_signer,
test_context,
@@ -22,32 +22,36 @@ use sp_runtime::{
AccountId32,
MultiAddress,
};
use subxt::Error;
use subxt::error::{
DispatchError,
Error,
};
#[tokio::test]
async fn tx_basic_transfer() -> Result<(), subxt::Error<DispatchError>> {
async fn tx_basic_transfer() -> Result<(), subxt::Error> {
let alice = pair_signer(AccountKeyring::Alice.pair());
let bob = pair_signer(AccountKeyring::Bob.pair());
let bob_address = bob.account_id().clone().into();
let cxt = test_context().await;
let api = &cxt.api;
let ctx = test_context().await;
let api = ctx.client();
let alice_account_addr = node_runtime::storage().system().account(alice.account_id());
let bob_account_addr = node_runtime::storage().system().account(bob.account_id());
let alice_pre = api
.storage()
.system()
.account(alice.account_id(), None)
.fetch_or_default(&alice_account_addr, None)
.await?;
let bob_pre = api
.storage()
.system()
.account(bob.account_id(), None)
.fetch_or_default(&bob_account_addr, None)
.await?;
let tx = node_runtime::tx().balances().transfer(bob_address, 10_000);
let events = api
.tx()
.balances()
.transfer(bob_address, 10_000)?
.sign_and_submit_then_watch_default(&alice)
.sign_and_submit_then_watch_default(&tx, &alice)
.await?
.wait_for_finalized_success()
.await?;
@@ -69,13 +73,11 @@ async fn tx_basic_transfer() -> Result<(), subxt::Error<DispatchError>> {
let alice_post = api
.storage()
.system()
.account(alice.account_id(), None)
.fetch_or_default(&alice_account_addr, None)
.await?;
let bob_post = api
.storage()
.system()
.account(bob.account_id(), None)
.fetch_or_default(&bob_account_addr, None)
.await?;
assert!(alice_pre.data.free - 10_000 >= alice_post.data.free);
@@ -84,26 +86,118 @@ async fn tx_basic_transfer() -> Result<(), subxt::Error<DispatchError>> {
}
#[tokio::test]
async fn multiple_transfers_work_nonce_incremented(
) -> Result<(), subxt::Error<DispatchError>> {
async fn tx_dynamic_transfer() -> Result<(), subxt::Error> {
use subxt::ext::scale_value::{
At,
Composite,
Value,
};
let alice = pair_signer(AccountKeyring::Alice.pair());
let bob = pair_signer(AccountKeyring::Bob.pair());
let ctx = test_context().await;
let api = ctx.client();
let alice_account_addr = subxt::dynamic::storage(
"System",
"Account",
vec![Value::from_bytes(&alice.account_id())],
);
let bob_account_addr = subxt::dynamic::storage(
"System",
"Account",
vec![Value::from_bytes(&bob.account_id())],
);
let alice_pre = api
.storage()
.fetch_or_default(&alice_account_addr, None)
.await?;
let bob_pre = api
.storage()
.fetch_or_default(&bob_account_addr, None)
.await?;
let tx = subxt::dynamic::tx(
"Balances",
"transfer",
vec![
Value::unnamed_variant("Id", vec![Value::from_bytes(&bob.account_id())]),
Value::u128(10_000u128),
],
);
let events = api
.tx()
.sign_and_submit_then_watch_default(&tx, &alice)
.await?
.wait_for_finalized_success()
.await?;
let event_fields = events
.iter()
.filter_map(|ev| ev.ok())
.find(|ev| ev.pallet_name() == "Balances" && ev.variant_name() == "Transfer")
.expect("Failed to find Transfer event")
.field_values()
.map_context(|_| ());
let expected_fields = Composite::Named(vec![
(
"from".into(),
Value::unnamed_composite(vec![Value::from_bytes(&alice.account_id())]),
),
(
"to".into(),
Value::unnamed_composite(vec![Value::from_bytes(&bob.account_id())]),
),
("amount".into(), Value::u128(10_000)),
]);
assert_eq!(event_fields, expected_fields);
let alice_post = api
.storage()
.fetch_or_default(&alice_account_addr, None)
.await?;
let bob_post = api
.storage()
.fetch_or_default(&bob_account_addr, None)
.await?;
let alice_pre_free = alice_pre.at("data").at("free").unwrap().as_u128().unwrap();
let alice_post_free = alice_post.at("data").at("free").unwrap().as_u128().unwrap();
let bob_pre_free = bob_pre.at("data").at("free").unwrap().as_u128().unwrap();
let bob_post_free = bob_post.at("data").at("free").unwrap().as_u128().unwrap();
assert!(alice_pre_free - 10_000 >= alice_post_free);
assert_eq!(bob_pre_free + 10_000, bob_post_free);
Ok(())
}
#[tokio::test]
async fn multiple_transfers_work_nonce_incremented() -> Result<(), subxt::Error> {
let alice = pair_signer(AccountKeyring::Alice.pair());
let bob = pair_signer(AccountKeyring::Bob.pair());
let bob_address: MultiAddress<AccountId32, u32> = bob.account_id().clone().into();
let cxt = test_context().await;
let api = &cxt.api;
let ctx = test_context().await;
let api = ctx.client();
let bob_account_addr = node_runtime::storage().system().account(bob.account_id());
let bob_pre = api
.storage()
.system()
.account(bob.account_id(), None)
.fetch_or_default(&bob_account_addr, None)
.await?;
let tx = node_runtime::tx()
.balances()
.transfer(bob_address.clone(), 10_000);
for _ in 0..3 {
api
.tx()
.balances()
.transfer(bob_address.clone(), 10_000)?
.sign_and_submit_then_watch_default(&alice)
.sign_and_submit_then_watch_default(&tx, &alice)
.await?
.wait_for_in_block() // Don't need to wait for finalization; this is quicker.
.await?
@@ -113,8 +207,7 @@ async fn multiple_transfers_work_nonce_incremented(
let bob_post = api
.storage()
.system()
.account(bob.account_id(), None)
.fetch_or_default(&bob_account_addr, None)
.await?;
assert_eq!(bob_pre.data.free + 30_000, bob_post.data.free);
@@ -123,45 +216,40 @@ async fn multiple_transfers_work_nonce_incremented(
#[tokio::test]
async fn storage_total_issuance() {
let cxt = test_context().await;
let total_issuance = cxt
.api
.storage()
.balances()
.total_issuance(None)
.await
.unwrap();
let ctx = test_context().await;
let api = ctx.client();
let addr = node_runtime::storage().balances().total_issuance();
let total_issuance = api.storage().fetch_or_default(&addr, None).await.unwrap();
assert_ne!(total_issuance, 0);
}
#[tokio::test]
async fn storage_balance_lock() -> Result<(), subxt::Error<DispatchError>> {
async fn storage_balance_lock() -> Result<(), subxt::Error> {
let bob = pair_signer(AccountKeyring::Bob.pair());
let charlie = AccountKeyring::Charlie.to_account_id();
let cxt = test_context().await;
let ctx = test_context().await;
let api = ctx.client();
cxt.api
.tx()
.staking()
.bond(
charlie.into(),
100_000_000_000_000,
runtime_types::pallet_staking::RewardDestination::Stash,
)?
.sign_and_submit_then_watch_default(&bob)
let tx = node_runtime::tx().staking().bond(
charlie.into(),
100_000_000_000_000,
runtime_types::pallet_staking::RewardDestination::Stash,
);
api.tx()
.sign_and_submit_then_watch_default(&tx, &bob)
.await?
.wait_for_finalized_success()
.await?
.find_first::<system::events::ExtrinsicSuccess>()?
.expect("No ExtrinsicSuccess Event found");
let locked_account = AccountKeyring::Bob.to_account_id();
let locks = cxt
.api
.storage()
let locks_addr = node_runtime::storage()
.balances()
.locks(&locked_account, None)
.await?;
.locks(&AccountKeyring::Bob.to_account_id());
let locks = api.storage().fetch_or_default(&locks_addr, None).await?;
assert_eq!(
locks.0,
@@ -177,38 +265,37 @@ async fn storage_balance_lock() -> Result<(), subxt::Error<DispatchError>> {
#[tokio::test]
async fn transfer_error() {
tracing_subscriber::fmt::try_init().ok();
let alice = pair_signer(AccountKeyring::Alice.pair());
let alice_addr = alice.account_id().clone().into();
let hans = pair_signer(Pair::generate().0);
let hans_address = hans.account_id().clone().into();
let ctx = test_context().await;
let api = ctx.client();
ctx.api
.tx()
let to_hans_tx = node_runtime::tx()
.balances()
.transfer(hans_address, 100_000_000_000_000_000)
.unwrap()
.sign_and_submit_then_watch_default(&alice)
.transfer(hans_address, 100_000_000_000_000_000);
let to_alice_tx = node_runtime::tx()
.balances()
.transfer(alice_addr, 100_000_000_000_000_000);
api.tx()
.sign_and_submit_then_watch_default(&to_hans_tx, &alice)
.await
.unwrap()
.wait_for_finalized_success()
.await
.unwrap();
let res = ctx
.api
let res = api
.tx()
.balances()
.transfer(alice_addr, 100_000_000_000_000_000)
.unwrap()
.sign_and_submit_then_watch_default(&hans)
.sign_and_submit_then_watch_default(&to_alice_tx, &hans)
.await
.unwrap()
.wait_for_finalized_success()
.await;
if let Err(Error::Module(err)) = res {
if let Err(Error::Runtime(DispatchError::Module(err))) = res {
assert_eq!(err.pallet, "Balances");
assert_eq!(err.error, "InsufficientBalance");
} else {
@@ -218,19 +305,17 @@ async fn transfer_error() {
#[tokio::test]
async fn transfer_implicit_subscription() {
tracing_subscriber::fmt::try_init().ok();
let alice = pair_signer(AccountKeyring::Alice.pair());
let bob = AccountKeyring::Bob.to_account_id();
let bob_addr = bob.clone().into();
let cxt = test_context().await;
let ctx = test_context().await;
let api = ctx.client();
let event = cxt
.api
let to_bob_tx = node_runtime::tx().balances().transfer(bob_addr, 10_000);
let event = api
.tx()
.balances()
.transfer(bob_addr, 10_000)
.unwrap()
.sign_and_submit_then_watch_default(&alice)
.sign_and_submit_then_watch_default(&to_bob_tx, &alice)
.await
.unwrap()
.wait_for_finalized_success()
@@ -252,19 +337,19 @@ async fn transfer_implicit_subscription() {
#[tokio::test]
async fn constant_existential_deposit() {
let cxt = test_context().await;
let locked_metadata = cxt.client().metadata();
let metadata = locked_metadata.read();
let ctx = test_context().await;
let api = ctx.client();
// get and decode constant manually via metadata:
let metadata = api.metadata();
let balances_metadata = metadata.pallet("Balances").unwrap();
let constant_metadata = balances_metadata.constant("ExistentialDeposit").unwrap();
let existential_deposit = u128::decode(&mut &constant_metadata.value[..]).unwrap();
assert_eq!(existential_deposit, 100_000_000_000_000);
assert_eq!(
existential_deposit,
cxt.api
.constants()
.balances()
.existential_deposit()
.unwrap()
);
// constant address for API access:
let addr = node_runtime::constants().balances().existential_deposit();
// Make sure thetwo are identical:
assert_eq!(existential_deposit, api.constants().at(&addr).unwrap());
}
@@ -7,57 +7,46 @@ use sp_keyring::AccountKeyring;
use crate::{
node_runtime::{
self,
contracts::{
calls::TransactionApi,
events,
storage,
},
contracts::events,
system,
DispatchError,
},
test_context,
NodeRuntimeParams,
TestContext,
};
use sp_core::sr25519::Pair;
use sp_runtime::MultiAddress;
use subxt::{
Client,
tx::{
PairSigner,
TxProgress,
},
Config,
DefaultConfig,
Error,
PairSigner,
TransactionProgress,
OnlineClient,
SubstrateConfig,
};
struct ContractsTestContext {
cxt: TestContext,
signer: PairSigner<DefaultConfig, Pair>,
signer: PairSigner<SubstrateConfig, Pair>,
}
type Hash = <DefaultConfig as Config>::Hash;
type AccountId = <DefaultConfig as Config>::AccountId;
type Hash = <SubstrateConfig as Config>::Hash;
type AccountId = <SubstrateConfig as Config>::AccountId;
impl ContractsTestContext {
async fn init() -> Self {
tracing_subscriber::fmt::try_init().ok();
let cxt = test_context().await;
let signer = PairSigner::new(AccountKeyring::Alice.pair());
Self { cxt, signer }
}
fn client(&self) -> &Client<DefaultConfig> {
fn client(&self) -> OnlineClient<SubstrateConfig> {
self.cxt.client()
}
fn contracts_tx(&self) -> TransactionApi<DefaultConfig, NodeRuntimeParams> {
self.cxt.api.tx().contracts()
}
async fn instantiate_with_code(
&self,
) -> Result<(Hash, AccountId), Error<DispatchError>> {
async fn instantiate_with_code(&self) -> Result<(Hash, AccountId), Error> {
tracing::info!("instantiate_with_code:");
const CONTRACT: &str = r#"
(module
@@ -67,20 +56,19 @@ impl ContractsTestContext {
"#;
let code = wabt::wat2wasm(CONTRACT).expect("invalid wabt");
let instantiate_tx = node_runtime::tx().contracts().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
);
let events = self
.cxt
.api
.client()
.tx()
.contracts()
.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_default(&self.signer)
.sign_and_submit_then_watch_default(&instantiate_tx, &self.signer)
.await?
.wait_for_finalized_success()
.await?;
@@ -108,19 +96,21 @@ impl ContractsTestContext {
code_hash: Hash,
data: Vec<u8>,
salt: Vec<u8>,
) -> Result<AccountId, Error<DispatchError>> {
) -> Result<AccountId, Error> {
// call instantiate extrinsic
let instantiate_tx = node_runtime::tx().contracts().instantiate(
100_000_000_000_000_000, // endowment
500_000_000_000, // gas_limit
None, // storage_deposit_limit
code_hash,
data,
salt,
);
let result = self
.contracts_tx()
.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_default(&self.signer)
.client()
.tx()
.sign_and_submit_then_watch_default(&instantiate_tx, &self.signer)
.await?
.wait_for_finalized_success()
.await?;
@@ -137,21 +127,20 @@ impl ContractsTestContext {
&self,
contract: AccountId,
input_data: Vec<u8>,
) -> Result<
TransactionProgress<'_, DefaultConfig, DispatchError, node_runtime::Event>,
Error<DispatchError>,
> {
) -> Result<TxProgress<SubstrateConfig, OnlineClient<SubstrateConfig>>, Error> {
tracing::info!("call: {:?}", contract);
let call_tx = node_runtime::tx().contracts().call(
MultiAddress::Id(contract),
0, // value
500_000_000, // gas_limit
None, // storage_deposit_limit
input_data,
);
let result = self
.contracts_tx()
.call(
MultiAddress::Id(contract),
0, // value
500_000_000, // gas_limit
None, // storage_deposit_limit
input_data,
)?
.sign_and_submit_then_watch_default(&self.signer)
.client()
.tx()
.sign_and_submit_then_watch_default(&call_tx, &self.signer)
.await?;
tracing::info!("Call result: {:?}", result);
@@ -190,19 +179,17 @@ async fn tx_call() {
let cxt = ContractsTestContext::init().await;
let (_, contract) = cxt.instantiate_with_code().await.unwrap();
let contract_info = cxt
.cxt
.api
.storage()
let info_addr = node_runtime::storage()
.contracts()
.contract_info_of(&contract, None)
.await;
.contract_info_of(&contract);
let contract_info = cxt.client().storage().fetch(&info_addr, None).await;
assert!(contract_info.is_ok());
let keys = cxt
.client()
.storage()
.fetch_keys::<storage::ContractInfoOf>(5, None, None)
.fetch_keys(&info_addr.to_bytes(), 10, None, None)
.await
.unwrap()
.iter()
+96 -93
View File
@@ -4,12 +4,12 @@
use crate::{
node_runtime::{
self,
runtime_types::pallet_staking::{
RewardDestination,
ValidatorPrefs,
},
staking,
DispatchError,
},
pair_signer,
test_context,
@@ -20,7 +20,10 @@ use sp_core::{
Pair,
};
use sp_keyring::AccountKeyring;
use subxt::Error;
use subxt::error::{
DispatchError,
Error,
};
/// Helper function to generate a crypto pair from seed
fn get_from_seed(seed: &str) -> sr25519::Pair {
@@ -37,14 +40,17 @@ fn default_validator_prefs() -> ValidatorPrefs {
#[tokio::test]
async fn validate_with_controller_account() {
let alice = pair_signer(AccountKeyring::Alice.pair());
let ctx = test_context().await;
ctx.api
.tx()
let api = ctx.client();
let alice = pair_signer(AccountKeyring::Alice.pair());
let tx = node_runtime::tx()
.staking()
.validate(default_validator_prefs())
.unwrap()
.sign_and_submit_then_watch_default(&alice)
.validate(default_validator_prefs());
api.tx()
.sign_and_submit_then_watch_default(&tx, &alice)
.await
.unwrap()
.wait_for_finalized_success()
@@ -53,19 +59,23 @@ async fn validate_with_controller_account() {
}
#[tokio::test]
async fn validate_not_possible_for_stash_account() -> Result<(), Error<DispatchError>> {
let alice_stash = pair_signer(get_from_seed("Alice//stash"));
async fn validate_not_possible_for_stash_account() -> Result<(), Error> {
let ctx = test_context().await;
let announce_validator = ctx
.api
.tx()
let api = ctx.client();
let alice_stash = pair_signer(get_from_seed("Alice//stash"));
let tx = node_runtime::tx()
.staking()
.validate(default_validator_prefs())?
.sign_and_submit_then_watch_default(&alice_stash)
.validate(default_validator_prefs());
let announce_validator = api
.tx()
.sign_and_submit_then_watch_default(&tx, &alice_stash)
.await?
.wait_for_finalized_success()
.await;
assert_matches!(announce_validator, Err(Error::Module(err)) => {
assert_matches!(announce_validator, Err(Error::Runtime(DispatchError::Module(err))) => {
assert_eq!(err.pallet, "Staking");
assert_eq!(err.error, "NotController");
});
@@ -74,16 +84,18 @@ async fn validate_not_possible_for_stash_account() -> Result<(), Error<DispatchE
#[tokio::test]
async fn nominate_with_controller_account() {
let ctx = test_context().await;
let api = ctx.client();
let alice = pair_signer(AccountKeyring::Alice.pair());
let bob = pair_signer(AccountKeyring::Bob.pair());
let ctx = test_context().await;
ctx.api
.tx()
let tx = node_runtime::tx()
.staking()
.nominate(vec![bob.account_id().clone().into()])
.unwrap()
.sign_and_submit_then_watch_default(&alice)
.nominate(vec![bob.account_id().clone().into()]);
api.tx()
.sign_and_submit_then_watch_default(&tx, &alice)
.await
.unwrap()
.wait_for_finalized_success()
@@ -92,22 +104,26 @@ async fn nominate_with_controller_account() {
}
#[tokio::test]
async fn nominate_not_possible_for_stash_account() -> Result<(), Error<DispatchError>> {
async fn nominate_not_possible_for_stash_account() -> Result<(), Error> {
let ctx = test_context().await;
let api = ctx.client();
let alice_stash = pair_signer(get_from_seed("Alice//stash"));
let bob = pair_signer(AccountKeyring::Bob.pair());
let ctx = test_context().await;
let nomination = ctx
.api
.tx()
let tx = node_runtime::tx()
.staking()
.nominate(vec![bob.account_id().clone().into()])?
.sign_and_submit_then_watch_default(&alice_stash)
.await?
.nominate(vec![bob.account_id().clone().into()]);
let nomination = api
.tx()
.sign_and_submit_then_watch_default(&tx, &alice_stash)
.await
.unwrap()
.wait_for_finalized_success()
.await;
assert_matches!(nomination, Err(Error::Module(err)) => {
assert_matches!(nomination, Err(Error::Runtime(DispatchError::Module(err))) => {
assert_eq!(err.pallet, "Staking");
assert_eq!(err.error, "NotController");
});
@@ -115,52 +131,45 @@ async fn nominate_not_possible_for_stash_account() -> Result<(), Error<DispatchE
}
#[tokio::test]
async fn chill_works_for_controller_only() -> Result<(), Error<DispatchError>> {
async fn chill_works_for_controller_only() -> Result<(), Error> {
let ctx = test_context().await;
let api = ctx.client();
let alice_stash = pair_signer(get_from_seed("Alice//stash"));
let bob_stash = pair_signer(get_from_seed("Bob//stash"));
let alice = pair_signer(AccountKeyring::Alice.pair());
let ctx = test_context().await;
// this will fail the second time, which is why this is one test, not two
ctx.api
.tx()
let nominate_tx = node_runtime::tx()
.staking()
.nominate(vec![bob_stash.account_id().clone().into()])?
.sign_and_submit_then_watch_default(&alice)
.nominate(vec![bob_stash.account_id().clone().into()]);
api.tx()
.sign_and_submit_then_watch_default(&nominate_tx, &alice)
.await?
.wait_for_finalized_success()
.await?;
let ledger = ctx
.api
.storage()
.staking()
.ledger(alice.account_id(), None)
.await?
.unwrap();
let ledger_addr = node_runtime::storage().staking().ledger(alice.account_id());
let ledger = api.storage().fetch(&ledger_addr, None).await?.unwrap();
assert_eq!(alice_stash.account_id(), &ledger.stash);
let chill = ctx
.api
let chill_tx = node_runtime::tx().staking().chill();
let chill = api
.tx()
.staking()
.chill()?
.sign_and_submit_then_watch_default(&alice_stash)
.sign_and_submit_then_watch_default(&chill_tx, &alice_stash)
.await?
.wait_for_finalized_success()
.await;
assert_matches!(chill, Err(Error::Module(err)) => {
assert_matches!(chill, Err(Error::Runtime(DispatchError::Module(err))) => {
assert_eq!(err.pallet, "Staking");
assert_eq!(err.error, "NotController");
});
let is_chilled = ctx
.api
let is_chilled = api
.tx()
.staking()
.chill()?
.sign_and_submit_then_watch_default(&alice)
.sign_and_submit_then_watch_default(&chill_tx, &alice)
.await?
.wait_for_finalized_success()
.await?
@@ -171,43 +180,35 @@ async fn chill_works_for_controller_only() -> Result<(), Error<DispatchError>> {
}
#[tokio::test]
async fn tx_bond() -> Result<(), Error<DispatchError>> {
let alice = pair_signer(AccountKeyring::Alice.pair());
async fn tx_bond() -> Result<(), Error> {
let ctx = test_context().await;
let api = ctx.client();
let bond = ctx
.api
let alice = pair_signer(AccountKeyring::Alice.pair());
let bond_tx = node_runtime::tx().staking().bond(
AccountKeyring::Bob.to_account_id().into(),
100_000_000_000_000,
RewardDestination::Stash,
);
let bond = api
.tx()
.staking()
.bond(
AccountKeyring::Bob.to_account_id().into(),
100_000_000_000_000,
RewardDestination::Stash,
)
.unwrap()
.sign_and_submit_then_watch_default(&alice)
.sign_and_submit_then_watch_default(&bond_tx, &alice)
.await?
.wait_for_finalized_success()
.await;
assert!(bond.is_ok());
let bond_again = ctx
.api
let bond_again = api
.tx()
.staking()
.bond(
AccountKeyring::Bob.to_account_id().into(),
100_000_000_000_000,
RewardDestination::Stash,
)
.unwrap()
.sign_and_submit_then_watch_default(&alice)
.sign_and_submit_then_watch_default(&bond_tx, &alice)
.await?
.wait_for_finalized_success()
.await;
assert_matches!(bond_again, Err(Error::Module(err)) => {
assert_matches!(bond_again, Err(Error::Runtime(DispatchError::Module(err))) => {
assert_eq!(err.pallet, "Staking");
assert_eq!(err.error, "AlreadyBonded");
});
@@ -215,35 +216,37 @@ async fn tx_bond() -> Result<(), Error<DispatchError>> {
}
#[tokio::test]
async fn storage_history_depth() -> Result<(), Error<DispatchError>> {
async fn storage_history_depth() -> Result<(), Error> {
let ctx = test_context().await;
let history_depth = ctx.api.storage().staking().history_depth(None).await?;
let api = ctx.client();
let history_depth_addr = node_runtime::storage().staking().history_depth();
let history_depth = api
.storage()
.fetch_or_default(&history_depth_addr, None)
.await?;
assert_eq!(history_depth, 84);
Ok(())
}
#[tokio::test]
async fn storage_current_era() -> Result<(), Error<DispatchError>> {
async fn storage_current_era() -> Result<(), Error> {
let ctx = test_context().await;
let _current_era = ctx
.api
let api = ctx.client();
let current_era_addr = node_runtime::storage().staking().current_era();
let _current_era = api
.storage()
.staking()
.current_era(None)
.fetch(&current_era_addr, None)
.await?
.expect("current era always exists");
Ok(())
}
#[tokio::test]
async fn storage_era_reward_points() -> Result<(), Error<DispatchError>> {
let cxt = test_context().await;
let current_era_result = cxt
.api
.storage()
.staking()
.eras_reward_points(&0, None)
.await;
async fn storage_era_reward_points() -> Result<(), Error> {
let ctx = test_context().await;
let api = ctx.client();
let reward_points_addr = node_runtime::storage().staking().eras_reward_points(&0);
let current_era_result = api.storage().fetch(&reward_points_addr, None).await;
assert!(current_era_result.is_ok());
Ok(())
+16 -16
View File
@@ -4,35 +4,35 @@
use crate::{
node_runtime::{
self,
runtime_types,
sudo,
DispatchError,
},
pair_signer,
test_context,
};
use sp_keyring::AccountKeyring;
type Call = runtime_types::node_runtime::Call;
type Call = runtime_types::kitchensink_runtime::Call;
type BalancesCall = runtime_types::pallet_balances::pallet::Call;
#[tokio::test]
async fn test_sudo() -> Result<(), subxt::Error<DispatchError>> {
async fn test_sudo() -> Result<(), subxt::Error> {
let ctx = test_context().await;
let api = ctx.client();
let alice = pair_signer(AccountKeyring::Alice.pair());
let bob = AccountKeyring::Bob.to_account_id().into();
let cxt = test_context().await;
let call = Call::Balances(BalancesCall::transfer {
dest: bob,
value: 10_000,
});
let tx = node_runtime::tx().sudo().sudo(call);
let found_event = cxt
.api
let found_event = api
.tx()
.sudo()
.sudo(call)?
.sign_and_submit_then_watch_default(&alice)
.sign_and_submit_then_watch_default(&tx, &alice)
.await?
.wait_for_finalized_success()
.await?
@@ -43,22 +43,22 @@ async fn test_sudo() -> Result<(), subxt::Error<DispatchError>> {
}
#[tokio::test]
async fn test_sudo_unchecked_weight() -> Result<(), subxt::Error<DispatchError>> {
async fn test_sudo_unchecked_weight() -> Result<(), subxt::Error> {
let ctx = test_context().await;
let api = ctx.client();
let alice = pair_signer(AccountKeyring::Alice.pair());
let bob = AccountKeyring::Bob.to_account_id().into();
let cxt = test_context().await;
let call = Call::Balances(BalancesCall::transfer {
dest: bob,
value: 10_000,
});
let tx = node_runtime::tx().sudo().sudo_unchecked_weight(call, 0);
let found_event = cxt
.api
let found_event = api
.tx()
.sudo()
.sudo_unchecked_weight(call, 0)?
.sign_and_submit_then_watch_default(&alice)
.sign_and_submit_then_watch_default(&tx, &alice)
.await?
.wait_for_finalized_success()
.await?
+20 -15
View File
@@ -4,8 +4,8 @@
use crate::{
node_runtime::{
self,
system,
DispatchError,
},
pair_signer,
test_context,
@@ -14,15 +14,17 @@ use assert_matches::assert_matches;
use sp_keyring::AccountKeyring;
#[tokio::test]
async fn storage_account() -> Result<(), subxt::Error<DispatchError>> {
async fn storage_account() -> Result<(), subxt::Error> {
let ctx = test_context().await;
let api = ctx.client();
let alice = pair_signer(AccountKeyring::Alice.pair());
let cxt = test_context().await;
let account_info = cxt
.api
let account_info_addr = node_runtime::storage().system().account(alice.account_id());
let account_info = api
.storage()
.system()
.account(alice.account_id(), None)
.fetch_or_default(&account_info_addr, None)
.await;
assert_matches!(account_info, Ok(_));
@@ -30,16 +32,19 @@ async fn storage_account() -> Result<(), subxt::Error<DispatchError>> {
}
#[tokio::test]
async fn tx_remark_with_event() -> Result<(), subxt::Error<DispatchError>> {
let alice = pair_signer(AccountKeyring::Alice.pair());
let cxt = test_context().await;
async fn tx_remark_with_event() -> Result<(), subxt::Error> {
let ctx = test_context().await;
let api = ctx.client();
let found_event = cxt
.api
.tx()
let alice = pair_signer(AccountKeyring::Alice.pair());
let tx = node_runtime::tx()
.system()
.remark_with_event(b"remarkable".to_vec())?
.sign_and_submit_then_watch_default(&alice)
.remark_with_event(b"remarkable".to_vec());
let found_event = api
.tx()
.sign_and_submit_then_watch_default(&tx, &alice)
.await?
.wait_for_finalized_success()
.await?
@@ -2,13 +2,20 @@
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
use crate::test_context;
use crate::{
node_runtime,
test_context,
};
#[tokio::test]
async fn storage_get_current_timestamp() {
let cxt = test_context().await;
let ctx = test_context().await;
let api = ctx.client();
let timestamp = cxt.api.storage().timestamp().now(None).await;
let timestamp = api
.storage()
.fetch(&node_runtime::storage().timestamp().now(), None)
.await;
assert!(timestamp.is_ok())
}