Skip to content

Commit

Permalink
Rework submit and watch extrinsics (#606)
Browse files Browse the repository at this point in the history
* Rename submit_and_watch_extrinsic_until into submit_and_watch_extrinsic_until_without_events

Rename submit_and_watch_opaque_extrinsic_until into submit_and_watch_opaque_extrinsic_until_without_events

submit_and_watch_extrinsic_until returns the event when status is InBlock or Finalized

Clippy

Clippy

* deprecation since "0.14.0" for submit_and_watch_extrinsic_until_success

* move submit_and_watch_extrinsic_until

* Improve doc

* test submit_and_watch_extrinsic_until for status Ready

* small changes

---------

Co-authored-by: echevrier <[email protected]>
  • Loading branch information
echevrier and echevrier authored Jul 6, 2023
1 parent dd6ca5e commit 58083b6
Show file tree
Hide file tree
Showing 14 changed files with 167 additions and 49 deletions.
2 changes: 1 addition & 1 deletion examples/examples/compose_extrinsic_offline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ async fn main() {

// Send and watch extrinsic until in block (online).
let block_hash = api
.submit_and_watch_extrinsic_until(xt, XtStatus::InBlock)
.submit_and_watch_extrinsic_until_without_events(xt, XtStatus::InBlock)
.unwrap()
.block_hash
.unwrap();
Expand Down
8 changes: 5 additions & 3 deletions examples/examples/contract_instantiate_with_code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use sp_keyring::AccountKeyring;
use substrate_api_client::{
ac_compose_macros::primitives::AssetRuntimeConfig, ac_node_api::StaticEvent,
ac_primitives::ExtrinsicSigner, extrinsic::ContractsExtrinsics, rpc::JsonrpseeClient, Api,
SubmitAndWatch, SubmitAndWatchUntilSuccess, XtStatus,
SubmitAndWatch, XtStatus,
};

// To test this example in CI, we run it against the Substrate kitchensink node. Therefore, we use the AssetRuntimeConfig
Expand Down Expand Up @@ -69,7 +69,7 @@ async fn main() {
);

println!("[+] Creating a contract instance with extrinsic:\n\n{:?}\n", xt);
let report = api.submit_and_watch_extrinsic_until_success(xt, false).unwrap();
let report = api.submit_and_watch_extrinsic_until(xt, XtStatus::InBlock).unwrap();
println!("[+] Extrinsic is in Block. Hash: {:?}\n", report.block_hash.unwrap());

println!("[+] Waiting for the contracts.Instantiated event");
Expand All @@ -89,6 +89,8 @@ async fn main() {
let xt = api.contract_call(contract.into(), 500_000, 500_000, vec![0u8]);

println!("[+] Calling the contract with extrinsic Extrinsic:\n{:?}\n\n", xt);
let report = api.submit_and_watch_extrinsic_until(xt, XtStatus::Finalized).unwrap();
let report = api
.submit_and_watch_extrinsic_until_without_events(xt, XtStatus::Finalized)
.unwrap();
println!("[+] Extrinsic got finalized. Extrinsic Hash: {:?}", report.extrinsic_hash);
}
2 changes: 1 addition & 1 deletion examples/examples/custom_nonce.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ async fn main() {
println!("[+] Composed Extrinsic:\n {:?}\n", xt);

// Send and watch extrinsic until InBlock.
let result = api.submit_and_watch_extrinsic_until(xt, XtStatus::InBlock);
let result = api.submit_and_watch_extrinsic_until_without_events(xt, XtStatus::InBlock);
println!("Returned Result {:?}", result);
match result {
Err(Error::UnexpectedTxStatus(UnexpectedTxStatus::Future)) => {
Expand Down
4 changes: 2 additions & 2 deletions examples/examples/event_error_details.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use substrate_api_client::{
ac_primitives::{AssetRuntimeConfig, ExtrinsicSigner},
extrinsic::BalancesExtrinsics,
rpc::JsonrpseeClient,
Api, GetAccountInformation, SubmitAndWatchUntilSuccess,
Api, GetAccountInformation, SubmitAndWatch, XtStatus,
};

// To test this example in CI, we run it against the Substrate kitchensink node. Therefore, we use the AssetRuntimeConfig
Expand Down Expand Up @@ -51,7 +51,7 @@ async fn main() {
println!("[+] Composed extrinsic: {:?}\n", xt);

// Send and watch extrinsic until InBlock.
let result = api.submit_and_watch_extrinsic_until_success(xt, false);
let result = api.submit_and_watch_extrinsic_until(xt, XtStatus::InBlock);
println!("[+] Transaction got included into the TxPool.");

// We expect the transfer to fail as Alice wants to transfer all her balance.
Expand Down
2 changes: 1 addition & 1 deletion examples/examples/get_account_identity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ async fn main() {

// Send and watch extrinsic until InBlock.
let _block_hash = api
.submit_and_watch_extrinsic_until(xt, XtStatus::InBlock)
.submit_and_watch_extrinsic_until_without_events(xt, XtStatus::InBlock)
.unwrap()
.block_hash
.unwrap();
Expand Down
8 changes: 5 additions & 3 deletions examples/examples/staking_batch_payout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use substrate_api_client::{
ac_primitives::{AssetRuntimeConfig, ExtrinsicSigner},
extrinsic::{StakingExtrinsics, UtilityExtrinsics},
rpc::JsonrpseeClient,
Api, GetStorage, SubmitAndWatch, SubmitAndWatchUntilSuccess, XtStatus,
Api, GetStorage, SubmitAndWatch, XtStatus,
};

const MAX_BATCHED_TRANSACTION: u32 = 9;
Expand Down Expand Up @@ -71,7 +71,7 @@ async fn main() {
// Sidenote: We could theoretically force a new era with sudo, but this takes at least 10 minutes ( = 1 epoch) in the
// kitchensink rutime. We don't want to wait that long.
let payout_staker_xt = api.payout_stakers(0, validator_stash);
let result = api.submit_and_watch_extrinsic_until_success(payout_staker_xt, false);
let result = api.submit_and_watch_extrinsic_until(payout_staker_xt, XtStatus::InBlock);
assert!(result.is_err());
assert!(format!("{result:?}").contains("InvalidEraToReward"));

Expand Down Expand Up @@ -130,7 +130,9 @@ async fn main() {
num_of_unclaimed_payouts -= tx_limit_in_current_batch;
let batch_xt = api.batch(payout_calls);

let report = api.submit_and_watch_extrinsic_until(batch_xt, XtStatus::InBlock).unwrap();
let report = api
.submit_and_watch_extrinsic_until_without_events(batch_xt, XtStatus::InBlock)
.unwrap();
results.push(format!("{report:?}"));
}
println!("{:?}", results);
Expand Down
2 changes: 1 addition & 1 deletion examples/examples/sudo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ async fn main() {

// Send and watch extrinsic until in block.
let block_hash = api
.submit_and_watch_extrinsic_until(xt, XtStatus::InBlock)
.submit_and_watch_extrinsic_until_without_events(xt, XtStatus::InBlock)
.unwrap()
.block_hash
.unwrap();
Expand Down
2 changes: 1 addition & 1 deletion examples/examples/transfer_with_tungstenite_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ fn main() {

// Send and watch extrinsic until in block.
let block_hash = api
.submit_and_watch_extrinsic_until(xt, XtStatus::InBlock)
.submit_and_watch_extrinsic_until_without_events(xt, XtStatus::InBlock)
.unwrap()
.block_hash
.unwrap();
Expand Down
2 changes: 1 addition & 1 deletion examples/examples/transfer_with_ws_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ fn main() {

// Send and watch extrinsic until in block.
let block_hash = api
.submit_and_watch_extrinsic_until(xt, XtStatus::InBlock)
.submit_and_watch_extrinsic_until_without_events(xt, XtStatus::InBlock)
.unwrap()
.block_hash
.unwrap();
Expand Down
6 changes: 3 additions & 3 deletions src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ pub use api_client::Api;
pub use error::{Error, Result};
pub use rpc_api::{
FetchEvents, GetAccountInformation, GetBalance, GetChainInfo, GetStorage,
GetTransactionPayment, SubmitAndWatch, SubmitAndWatchUntilSuccess, SubmitExtrinsic,
SubscribeChain, SubscribeEvents, SystemApi,
GetTransactionPayment, SubmitAndWatch, SubmitExtrinsic, SubscribeChain, SubscribeEvents,
SystemApi,
};

pub mod api_client;
Expand Down Expand Up @@ -64,7 +64,7 @@ impl<Hash: Decode> ExtrinsicReport<Hash> {
/// Simplified TransactionStatus to allow the user to choose until when to watch
/// an extrinsic.
// Indexes must match the TransactionStatus::as_u8 from below.
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[derive(Debug, PartialEq, PartialOrd, Eq, Copy, Clone)]
pub enum XtStatus {
Ready = 1,
Broadcast = 2,
Expand Down
151 changes: 131 additions & 20 deletions src/api/rpc_api/author.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,13 +106,77 @@ pub trait SubmitAndWatch {

/// Submit an extrinsic and watch it until the desired status
/// is reached, if no error is encountered previously.
///
/// If watched until `InBlock` or `Finalized`, this function will
/// return an error if the extrinsic was not successfully executed.
/// If it was successful, a report containing the following is returned:
/// - extrinsic hash
/// - hash of the block the extrinsic was included in
/// - last known extrinsic (transaction) status
/// - associated events of the extrinsic
///
/// If not watched until at least `InBlock`, this function will not know if the extrinsic
/// has been executed on chain or not and will therefore not return an error if execution fails.
/// An error will be returned if the extrinsic has failed to be sent or if it has not been
/// included into the transaction pool of the node.
/// If no error occurs, a report containing the following is returned:
/// - extrinsic hash
/// - last known extrinsic (transaction) status
///
/// This method is blocking.
async fn submit_and_watch_extrinsic_until<Address, Call, Signature, SignedExtra>(
&self,
extrinsic: UncheckedExtrinsicV4<Address, Call, Signature, SignedExtra>,
watch_until: XtStatus,
) -> Result<ExtrinsicReport<Self::Hash>>
where
Address: Encode,
Call: Encode,
Signature: Encode,
SignedExtra: Encode;

/// Submit an encoded, opaque extrinsic until the desired status
/// is reached, if no error is encountered previously.
///
/// If watched until `InBlock` or `Finalized`, this function will
/// return an error if the extrinsic was not successfully executed.
/// If it was successful, a report containing the following is returned:
/// - extrinsic hash
/// - hash of the block the extrinsic was included in
/// - last known extrinsic (transaction) status
/// - associated events of the extrinsic (only for InBlock or Finalized)
///
/// If not watched until at least `InBlock`, this function will not know if the extrinsic
/// has been executed on chain or not and will therefore not return an error if execution fails..
/// An error will be returned, if the extrinsic has failed to be sent or if it has not been
/// included into the transaction pool of the node.
/// If no error occurs, a report containing the following is returned:
/// - extrinsic hash
/// - last known extrinsic (transaction) status
///
/// This method is blocking.
async fn submit_and_watch_opaque_extrinsic_until(
&self,
encoded_extrinsic: &Bytes,
watch_until: XtStatus,
) -> Result<ExtrinsicReport<Self::Hash>>;

/// Submit an extrinsic and watch it until the desired status
/// is reached, if no error is encountered previously.
/// The events are not fetched. So no events are listed in the report.
/// To fetch the triggered events, please use submit_and_watch_extrinsic_until.
/// Upon success, a report containing the following information is returned:
/// - extrinsic hash
/// - if watched until at least `InBlock`:
/// hash of the block the extrinsic was included in
/// - last known extrinsic (transaction) status
/// This method is blocking.
async fn submit_and_watch_extrinsic_until<Address, Call, Signature, SignedExtra>(
async fn submit_and_watch_extrinsic_until_without_events<
Address,
Call,
Signature,
SignedExtra,
>(
&self,
extrinsic: UncheckedExtrinsicV4<Address, Call, Signature, SignedExtra>,
watch_until: XtStatus,
Expand All @@ -125,13 +189,15 @@ pub trait SubmitAndWatch {

/// Submit an encoded, opaque extrinsic and watch it until the desired status
/// is reached, if no error is encountered previously.
/// The events are not fetched. So no events are listed in the report.
/// To fetch the triggered events, please use submit_and_watch_opaque_extrinsic_until.
/// Upon success, a report containing the following information is returned:
/// - extrinsic hash
/// - if watched until at least `InBlock`:
/// hash of the block the extrinsic was included in
/// - last known extrinsic (transaction) status
/// This method is blocking.
async fn submit_and_watch_opaque_extrinsic_until(
async fn submit_and_watch_opaque_extrinsic_until_without_events(
&self,
encoded_extrinsic: &Bytes,
watch_until: XtStatus,
Expand All @@ -153,6 +219,10 @@ pub trait SubmitAndWatchUntilSuccess {
/// - last known extrinsic (transaction) status
/// - associated events of the extrinsic
/// This method is blocking.
#[deprecated(
since = "0.14.0",
note = "please use `SubmitAndWatch::submit_and_watch_extrinsic_until` instead, this will be removed in the next release."
)]
async fn submit_and_watch_extrinsic_until_success<Address, Call, Signature, SignedExtra>(
&self,
extrinsic: UncheckedExtrinsicV4<Address, Call, Signature, SignedExtra>,
Expand All @@ -174,6 +244,10 @@ pub trait SubmitAndWatchUntilSuccess {
/// - last known extrinsic (transaction) status
/// - associated events of the extrinsic
/// This method is blocking.
#[deprecated(
since = "0.14.0",
note = "please use `SubmitAndWatch::submit_and_watch_opaque_extrinsic_until` instead, this will be removed in the next release."
)]
async fn submit_and_watch_opaque_extrinsic_until_success(
&self,
encoded_extrinsic: &Bytes,
Expand All @@ -185,7 +259,7 @@ pub trait SubmitAndWatchUntilSuccess {
impl<T, Client> SubmitAndWatch for Api<T, Client>
where
T: Config,
Client: Subscribe,
Client: Subscribe + Request,
{
type Client = Client;
type Hash = T::Hash;
Expand Down Expand Up @@ -235,6 +309,52 @@ where
&self,
encoded_extrinsic: &Bytes,
watch_until: XtStatus,
) -> Result<ExtrinsicReport<Self::Hash>> {
let mut report = self
.submit_and_watch_opaque_extrinsic_until_without_events(encoded_extrinsic, watch_until)
.await?;

if watch_until < XtStatus::InBlock {
return Ok(report)
}
let block_hash = report.block_hash.ok_or(Error::BlockHashNotFound)?;
let extrinsic_events =
self.fetch_events_for_extrinsic(block_hash, report.extrinsic_hash).await?;
// Ensure that the extrins has been successful. If not, return an error.
for event in &extrinsic_events {
event.check_if_failed()?;
}
report.events = Some(extrinsic_events);
Ok(report)
}

async fn submit_and_watch_extrinsic_until_without_events<
Address,
Call,
Signature,
SignedExtra,
>(
&self,
extrinsic: UncheckedExtrinsicV4<Address, Call, Signature, SignedExtra>,
watch_until: XtStatus,
) -> Result<ExtrinsicReport<Self::Hash>>
where
Address: Encode,
Call: Encode,
Signature: Encode,
SignedExtra: Encode,
{
self.submit_and_watch_opaque_extrinsic_until_without_events(
&extrinsic.encode().into(),
watch_until,
)
.await
}

async fn submit_and_watch_opaque_extrinsic_until_without_events(
&self,
encoded_extrinsic: &Bytes,
watch_until: XtStatus,
) -> Result<ExtrinsicReport<Self::Hash>> {
let tx_hash = T::Hasher::hash_of(&encoded_extrinsic.0);
let mut subscription: TransactionSubscriptionFor<Self::Client, Self::Hash> =
Expand Down Expand Up @@ -284,11 +404,13 @@ where
Signature: Encode,
SignedExtra: Encode,
{
self.submit_and_watch_opaque_extrinsic_until_success(
&extrinsic.encode().into(),
wait_for_finalized,
)
.await
let xt_status = match wait_for_finalized {
true => XtStatus::Finalized,
false => XtStatus::InBlock,
};

self.submit_and_watch_opaque_extrinsic_until(&extrinsic.encode().into(), xt_status)
.await
}

async fn submit_and_watch_opaque_extrinsic_until_success(
Expand All @@ -300,18 +422,7 @@ where
true => XtStatus::Finalized,
false => XtStatus::InBlock,
};
let mut report = self
.submit_and_watch_opaque_extrinsic_until(encoded_extrinsic, xt_status)
.await?;

let block_hash = report.block_hash.ok_or(Error::BlockHashNotFound)?;
let extrinsic_events =
self.fetch_events_for_extrinsic(block_hash, report.extrinsic_hash).await?;
// Ensure that the extrins has been successful. If not, return an error.
for event in &extrinsic_events {
event.check_if_failed()?;
}
report.events = Some(extrinsic_events);
Ok(report)
self.submit_and_watch_opaque_extrinsic_until(encoded_extrinsic, xt_status).await
}
}
Loading

0 comments on commit 58083b6

Please sign in to comment.