[do-not-merge] Bridge hub sample call with custom ExtrinsicParams

This commit is contained in:
Branislav Kontur
2022-07-19 13:13:58 +02:00
parent b3f6ff1cf8
commit bca3a3b38a
3 changed files with 226 additions and 0 deletions
+97
View File
@@ -0,0 +1,97 @@
// Copyright 2019-2022 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! ```bash
//! ./target/release/subxt metadata --url ws://localhost:9946 > artifacts/bridge_metadata.scale
//! ```
use sp_keyring::AccountKeyring;
use subxt::{
extrinsic::{
BridgeHubExtrinsicParams,
BridgeHubExtrinsicParamsBuilder,
},
ClientBuilder,
DefaultConfig,
PairSigner,
};
#[subxt::subxt(runtime_metadata_path = "../artifacts/bridge_metadata.scale")]
pub mod polkadot {}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
// max_size 50 is pallet's config T::StringMaxLength in runtime, so only 50 will pass
let result = call_set_name("123456", 51, 1).await;
assert!(result.is_err());
eprintln!("Result failed as expected: {:?}", result);
let result = call_set_name("123456", 50, 51).await;
assert!(result.is_err());
eprintln!("Result failed as expected: {:?}", result);
let result = call_set_name("12345678910", 50, 1).await;
assert!(result.is_err());
eprintln!("Result failed as expected: {:?}", result);
let result = call_set_name("123456789", 50, 1).await;
assert!(result.is_err());
eprintln!("Result failed as expected: {:?}", result);
assert!(call_set_name("1234567", 50, 1).await.is_ok());
assert!(call_set_name("123", 50, 1).await.is_ok());
println!("Hurraaa, test passed");
Ok(())
}
/// This is the highest level approach to using this API. We use `wait_for_finalized_success`
/// to wait for the transaction to make it into a finalized block, and also ensure that the
/// transaction was successful according to the associated events.
async fn call_set_name(
data: &str,
max_size: u32,
priority: u8,
) -> Result<(), Box<dyn std::error::Error>> {
let signer = PairSigner::new(AccountKeyring::Alice.pair());
let params = BridgeHubExtrinsicParamsBuilder::new(max_size, priority);
let api = ClientBuilder::new()
.set_url("ws://127.0.0.1:9946")
.build()
.await?
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, BridgeHubExtrinsicParams<DefaultConfig>>>();
let data = data.as_bytes().to_vec();
let data =
polkadot::runtime_types::sp_runtime::bounded::bounded_vec::BoundedVec::<u8>(data);
let result = api
.tx()
.bridge_hub_sample()
.set_name(data)?
.sign_and_submit_then_watch(&signer, params)
.await?
.wait_for_finalized_success()
.await?;
for (idx, event) in result.iter().enumerate() {
println!("Bridge event ({}): {:?}", idx, event);
}
let success_event = result
.find_first::<polkadot::system::events::ExtrinsicSuccess>()
.expect("decode error");
if let Some(event) = success_event {
println!("Bridge success: {:?}", event);
} else {
panic!("Failed to call bridge: {:?}", result);
}
Ok(())
}
+2
View File
@@ -26,6 +26,8 @@ pub use self::{
AssetTip,
BaseExtrinsicParams,
BaseExtrinsicParamsBuilder,
BridgeHubExtrinsicParams,
BridgeHubExtrinsicParamsBuilder,
Era,
ExtrinsicParams,
PlainTip,
+127
View File
@@ -56,6 +56,13 @@ pub type SubstrateExtrinsicParams<T> = BaseExtrinsicParams<T, AssetTip>;
/// This is what you provide to methods like `sign_and_submit()`.
pub type SubstrateExtrinsicParamsBuilder<T> = BaseExtrinsicParamsBuilder<T, AssetTip>;
/// A struct representing the signed extra and additional parameters required
/// to construct a transaction for the default substrate node.
pub type BridgeHubExtrinsicParams<T> = ExtendedExtrinsicParams<T, PlainTip>;
/// A builder which leads to [`SubstrateExtrinsicParams`] being constructed.
/// This is what you provide to methods like `sign_and_submit()`.
pub type BridgeHubExtrinsicParamsBuilder<T> = ExtendedExtrinsicParamsBuilder<T, PlainTip>;
/// A struct representing the signed extra and additional parameters required
/// to construct a transaction for a polkadot node.
pub type PolkadotExtrinsicParams<T> = BaseExtrinsicParams<T, PlainTip>;
@@ -175,6 +182,126 @@ impl<T: Config, Tip: Debug + Encode> ExtrinsicParams<T> for BaseExtrinsicParams<
}
}
/// An implementation of [`ExtrinsicParams`] that is suitable for constructing
/// extrinsics that can be sent to a node with the same signed extra and additional
/// parameters as a Polkadot/Substrate node. The way that tip payments are specified
/// differs between Substrate and Polkadot nodes, and so we are generic over that in
/// order to support both here with relative ease.
///
/// If your node differs in the "signed extra" and "additional" parameters expected
/// to be sent/signed with a transaction, then you can define your own type which
/// implements the [`ExtrinsicParams`] trait.
#[derive(Debug)]
pub struct ExtendedExtrinsicParams<T: Config, Tip: Debug> {
era: Era,
nonce: T::Index,
tip: Tip,
max_size: u32,
priority: u8,
spec_version: u32,
transaction_version: u32,
genesis_hash: T::Hash,
mortality_checkpoint: T::Hash,
marker: std::marker::PhantomData<T>,
}
/// This builder allows you to provide the parameters that can be configured in order to
/// construct a [`BaseExtrinsicParams`] value. This implements [`Default`], which allows
/// [`BaseExtrinsicParams`] to be used with convenience methods like `sign_and_submit_default()`.
///
/// Prefer to use [`SubstrateExtrinsicParamsBuilder`] for a version of this tailored towards
/// Substrate, or [`PolkadotExtrinsicParamsBuilder`] for a version tailored to Polkadot.
pub struct ExtendedExtrinsicParamsBuilder<T: Config, Tip> {
era: Era,
mortality_checkpoint: Option<T::Hash>,
tip: Tip,
max_size: u32,
priority: u8,
}
impl<T: Config, Tip: Default> ExtendedExtrinsicParamsBuilder<T, Tip> {
/// Instantiate the default set of [`BaseExtrinsicParamsBuilder`]
pub fn new(max_size: u32, priority: u8) -> Self {
Self {
era: Era::Immortal,
mortality_checkpoint: None,
tip: Tip::default(),
max_size,
priority,
}
}
/// Set the [`Era`], which defines how long the transaction will be valid for
/// (it can be either immortal, or it can be mortal and expire after a certain amount
/// of time). The second argument is the block hash after which the transaction
/// becomes valid, and must align with the era phase (see the [`Era::Mortal`] docs
/// for more detail on that).
pub fn era(mut self, era: Era, checkpoint: T::Hash) -> Self {
self.era = era;
self.mortality_checkpoint = Some(checkpoint);
self
}
/// Set the tip you'd like to give to the block author
/// for this transaction.
pub fn tip(mut self, tip: impl Into<Tip>) -> Self {
self.tip = tip.into();
self
}
/// Set the priority
pub fn priority(mut self, priority: u8) -> Self {
self.priority = priority;
self
}
}
impl<T: Config, Tip: Debug + Encode> ExtrinsicParams<T> for ExtendedExtrinsicParams<T, Tip> {
type OtherParams = ExtendedExtrinsicParamsBuilder<T, Tip>;
fn new(
// Provided from subxt client:
spec_version: u32,
transaction_version: u32,
nonce: T::Index,
genesis_hash: T::Hash,
// Provided externally:
other_params: Self::OtherParams,
) -> Self {
ExtendedExtrinsicParams {
era: other_params.era,
mortality_checkpoint: other_params
.mortality_checkpoint
.unwrap_or(genesis_hash),
tip: other_params.tip,
max_size: other_params.max_size,
priority: other_params.priority,
nonce,
spec_version,
transaction_version,
genesis_hash,
marker: std::marker::PhantomData,
}
}
fn encode_extra_to(&self, v: &mut Vec<u8>) {
let nonce: u64 = self.nonce.into();
let tip = Encoded(self.tip.encode());
(self.era, Compact(nonce), tip, self.priority).encode_to(v);
}
fn encode_additional_to(&self, v: &mut Vec<u8>) {
(
self.spec_version,
self.transaction_version,
self.genesis_hash,
self.mortality_checkpoint,
self.max_size,
)
.encode_to(v);
}
}
/// A tip payment.
#[derive(Copy, Clone, Debug, Default, Encode)]
pub struct PlainTip {