mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 15:11:03 +00:00
Support constructing and submitting V5 transactions (#1931)
* TransactionExtensions basic support for V5 VerifySignature and renames * WIP: subxt-core v5 transaction support * Subxt to support V5 extrinsics * WIP tests failing with wsm trap error * Actually encode mortality to fix tx encode issue * fmt * rename to sign_with_account_and_signature * Add explicit methods for v4 and v5 ext construction * clippy * fix wasm example and no mut self where not needed * fix doc example * another doc fix * Add tests for tx encoding and fix v5 encode issue * add copyright and todo * refactor APIs to have clear v4/v5 split in core and slightly nicer split in subxt proper * rename Partial/SubmittableExtrinsic to *Transaction * Remove SignerT::address since it's not needed * doc fixes * fmt * doc fixes * Fix comment number * Clarify panic behaviour of inject_signature * fmt
This commit is contained in:
@@ -17,7 +17,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Decode each signed extrinsic in the block dynamically
|
||||
let extrinsics = block.extrinsics().await?;
|
||||
for ext in extrinsics.iter() {
|
||||
let Some(signed_extensions) = ext.signed_extensions() else {
|
||||
let Some(transaction_extensions) = ext.transaction_extensions() else {
|
||||
continue; // we do not look at inherents in this example
|
||||
};
|
||||
|
||||
@@ -25,8 +25,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let fields = ext.field_values()?;
|
||||
|
||||
println!(" {}/{}", meta.pallet.name(), meta.variant.name);
|
||||
println!(" Signed Extensions:");
|
||||
for signed_ext in signed_extensions.iter() {
|
||||
println!(" Transaction Extensions:");
|
||||
for signed_ext in transaction_extensions.iter() {
|
||||
// We only want to take a look at these 3 signed extensions, because the others all just have unit fields.
|
||||
if ["CheckMortality", "CheckNonce", "ChargeTransactionPayment"]
|
||||
.contains(&signed_ext.name())
|
||||
|
||||
@@ -30,7 +30,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
for transfer in extrinsics.find::<TransferKeepAlive>() {
|
||||
let transfer = transfer?;
|
||||
|
||||
let Some(extensions) = transfer.details.signed_extensions() else {
|
||||
let Some(extensions) = transfer.details.transaction_extensions() else {
|
||||
panic!("TransferKeepAlive should be signed")
|
||||
};
|
||||
|
||||
|
||||
@@ -48,11 +48,11 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!(" {}", event_values);
|
||||
}
|
||||
|
||||
println!(" Signed Extensions:");
|
||||
if let Some(signed_extensions) = ext.signed_extensions() {
|
||||
for signed_extension in signed_extensions.iter() {
|
||||
let name = signed_extension.name();
|
||||
let value = signed_extension.value()?.to_string();
|
||||
println!(" Transaction Extensions:");
|
||||
if let Some(transaction_extensions) = ext.transaction_extensions() {
|
||||
for transaction_extension in transaction_extensions.iter() {
|
||||
let name = transaction_extension.name();
|
||||
let value = transaction_extension.value()?.to_string();
|
||||
println!(" {name}: {value}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,12 +38,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let current_nonce = rpc
|
||||
.system_account_next_index(&alice.public_key().into())
|
||||
.await?;
|
||||
let current_header = rpc.chain_get_header(None).await?.unwrap();
|
||||
|
||||
let ext_params = Params::new()
|
||||
.mortal(¤t_header, 8)
|
||||
.nonce(current_nonce)
|
||||
.build();
|
||||
let ext_params = Params::new().mortal(8).nonce(current_nonce).build();
|
||||
|
||||
let balance_transfer = polkadot::tx()
|
||||
.balances()
|
||||
@@ -51,7 +47,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
let ext_hash = api
|
||||
.tx()
|
||||
.create_signed_offline(&balance_transfer, &alice, ext_params)?
|
||||
.create_partial_offline(&balance_transfer, ext_params)?
|
||||
.sign(&alice)
|
||||
.submit()
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
use codec::Encode;
|
||||
use subxt::client::ClientState;
|
||||
use subxt::config::{
|
||||
Config, ExtrinsicParams, ExtrinsicParamsEncoder, ExtrinsicParamsError, RefineParams,
|
||||
transaction_extensions::Params, Config, ExtrinsicParams, ExtrinsicParamsEncoder,
|
||||
ExtrinsicParamsError,
|
||||
};
|
||||
use subxt_signer::sr25519::dev;
|
||||
|
||||
@@ -53,7 +54,7 @@ impl CustomExtrinsicParamsBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> RefineParams<T> for CustomExtrinsicParamsBuilder {}
|
||||
impl<T: Config> Params<T> for CustomExtrinsicParamsBuilder {}
|
||||
|
||||
// Describe how to fetch and then encode the params:
|
||||
impl<T: Config> ExtrinsicParams<T> for CustomExtrinsicParams<T> {
|
||||
@@ -69,14 +70,12 @@ impl<T: Config> ExtrinsicParams<T> for CustomExtrinsicParams<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> RefineParams<T> for CustomExtrinsicParams<T> {}
|
||||
|
||||
// Encode the relevant params when asked:
|
||||
impl<T: Config> ExtrinsicParamsEncoder for CustomExtrinsicParams<T> {
|
||||
fn encode_extra_to(&self, v: &mut Vec<u8>) {
|
||||
fn encode_value_to(&self, v: &mut Vec<u8>) {
|
||||
(self.tip, self.foo).encode_to(v);
|
||||
}
|
||||
fn encode_additional_to(&self, v: &mut Vec<u8>) {
|
||||
fn encode_implicit_to(&self, v: &mut Vec<u8>) {
|
||||
self.genesis_hash.encode_to(v)
|
||||
}
|
||||
}
|
||||
|
||||
+22
-21
@@ -3,7 +3,7 @@ use codec::Encode;
|
||||
use scale_encode::EncodeAsType;
|
||||
use scale_info::PortableRegistry;
|
||||
use subxt::client::ClientState;
|
||||
use subxt::config::signed_extensions;
|
||||
use subxt::config::transaction_extensions;
|
||||
use subxt::config::{
|
||||
Config, DefaultExtrinsicParamsBuilder, ExtrinsicParams, ExtrinsicParamsEncoder,
|
||||
ExtrinsicParamsError,
|
||||
@@ -25,53 +25,54 @@ impl Config for CustomConfig {
|
||||
type Signature = subxt::utils::MultiSignature;
|
||||
type Hasher = subxt::config::substrate::BlakeTwo256;
|
||||
type Header = subxt::config::substrate::SubstrateHeader<u32, Self::Hasher>;
|
||||
type ExtrinsicParams = signed_extensions::AnyOf<
|
||||
type ExtrinsicParams = transaction_extensions::AnyOf<
|
||||
Self,
|
||||
(
|
||||
// Load in the existing signed extensions we're interested in
|
||||
// (if the extension isn't actually needed it'll just be ignored):
|
||||
signed_extensions::CheckSpecVersion,
|
||||
signed_extensions::CheckTxVersion,
|
||||
signed_extensions::CheckNonce,
|
||||
signed_extensions::CheckGenesis<Self>,
|
||||
signed_extensions::CheckMortality<Self>,
|
||||
signed_extensions::ChargeAssetTxPayment<Self>,
|
||||
signed_extensions::ChargeTransactionPayment,
|
||||
signed_extensions::CheckMetadataHash,
|
||||
transaction_extensions::VerifySignature<Self>,
|
||||
transaction_extensions::CheckSpecVersion,
|
||||
transaction_extensions::CheckTxVersion,
|
||||
transaction_extensions::CheckNonce,
|
||||
transaction_extensions::CheckGenesis<Self>,
|
||||
transaction_extensions::CheckMortality<Self>,
|
||||
transaction_extensions::ChargeAssetTxPayment<Self>,
|
||||
transaction_extensions::ChargeTransactionPayment,
|
||||
transaction_extensions::CheckMetadataHash,
|
||||
// And add a new one of our own:
|
||||
CustomSignedExtension,
|
||||
CustomTransactionExtension,
|
||||
),
|
||||
>;
|
||||
type AssetId = u32;
|
||||
}
|
||||
|
||||
// Our custom signed extension doesn't do much:
|
||||
pub struct CustomSignedExtension;
|
||||
pub struct CustomTransactionExtension;
|
||||
|
||||
// Give the extension a name; this allows `AnyOf` to look it
|
||||
// up in the chain metadata in order to know when and if to use it.
|
||||
impl<T: Config> signed_extensions::SignedExtension<T> for CustomSignedExtension {
|
||||
impl<T: Config> transaction_extensions::TransactionExtension<T> for CustomTransactionExtension {
|
||||
type Decoded = ();
|
||||
fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool {
|
||||
identifier == "CustomSignedExtension"
|
||||
identifier == "CustomTransactionExtension"
|
||||
}
|
||||
}
|
||||
|
||||
// Gather together any params we need for our signed extension, here none.
|
||||
impl<T: Config> ExtrinsicParams<T> for CustomSignedExtension {
|
||||
impl<T: Config> ExtrinsicParams<T> for CustomTransactionExtension {
|
||||
type Params = ();
|
||||
|
||||
fn new(_client: &ClientState<T>, _params: Self::Params) -> Result<Self, ExtrinsicParamsError> {
|
||||
Ok(CustomSignedExtension)
|
||||
Ok(CustomTransactionExtension)
|
||||
}
|
||||
}
|
||||
|
||||
// Encode whatever the extension needs to provide when asked:
|
||||
impl ExtrinsicParamsEncoder for CustomSignedExtension {
|
||||
fn encode_extra_to(&self, v: &mut Vec<u8>) {
|
||||
impl ExtrinsicParamsEncoder for CustomTransactionExtension {
|
||||
fn encode_value_to(&self, v: &mut Vec<u8>) {
|
||||
"Hello".encode_to(v);
|
||||
}
|
||||
fn encode_additional_to(&self, v: &mut Vec<u8>) {
|
||||
fn encode_implicit_to(&self, v: &mut Vec<u8>) {
|
||||
true.encode_to(v)
|
||||
}
|
||||
}
|
||||
@@ -84,8 +85,8 @@ impl ExtrinsicParamsEncoder for CustomSignedExtension {
|
||||
pub fn custom(
|
||||
params: DefaultExtrinsicParamsBuilder<CustomConfig>,
|
||||
) -> <<CustomConfig as Config>::ExtrinsicParams as ExtrinsicParams<CustomConfig>>::Params {
|
||||
let (a, b, c, d, e, f, g, h) = params.build();
|
||||
(a, b, c, d, e, f, g, h, ())
|
||||
let (a, b, c, d, e, f, g, h, i) = params.build();
|
||||
(a, b, c, d, e, f, g, h, i, ())
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
@@ -69,10 +69,6 @@ mod pair_signer {
|
||||
self.account_id.clone()
|
||||
}
|
||||
|
||||
fn address(&self) -> <PolkadotConfig as Config>::Address {
|
||||
self.account_id.clone().into()
|
||||
}
|
||||
|
||||
fn sign(&self, signer_payload: &[u8]) -> <PolkadotConfig as Config>::Signature {
|
||||
let signature = self.signer.sign(signer_payload);
|
||||
MultiSignature::Sr25519(signature.0)
|
||||
|
||||
@@ -10,7 +10,7 @@ pub mod polkadot {}
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), BoxedError> {
|
||||
// Spawned tasks require things held across await points to impl Send,
|
||||
// so we use one to demonstrate that this is possible with `PartialExtrinsic`
|
||||
// so we use one to demonstrate that this is possible with `PartialTransaction`
|
||||
tokio::spawn(signing_example()).await??;
|
||||
Ok(())
|
||||
}
|
||||
@@ -25,9 +25,9 @@ async fn signing_example() -> Result<(), BoxedError> {
|
||||
let alice = dev::alice();
|
||||
|
||||
// Create partial tx, ready to be signed.
|
||||
let partial_tx = api
|
||||
let mut partial_tx = api
|
||||
.tx()
|
||||
.create_partial_signed(
|
||||
.create_partial(
|
||||
&balance_transfer_tx,
|
||||
&alice.public_key().to_account_id(),
|
||||
Default::default(),
|
||||
@@ -35,13 +35,13 @@ async fn signing_example() -> Result<(), BoxedError> {
|
||||
.await?;
|
||||
|
||||
// Simulate taking some time to get a signature back, in part to
|
||||
// show that the `PartialExtrinsic` can be held across await points.
|
||||
// show that the `PartialTransaction` can be held across await points.
|
||||
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
|
||||
let signature = alice.sign(&partial_tx.signer_payload());
|
||||
|
||||
// Sign the transaction.
|
||||
let tx = partial_tx
|
||||
.sign_with_address_and_signature(&alice.public_key().to_address(), &signature.into());
|
||||
.sign_with_account_and_signature(&alice.public_key().to_account_id(), &signature.into());
|
||||
|
||||
// Submit it.
|
||||
tx.submit_and_watch()
|
||||
|
||||
@@ -15,14 +15,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let dest = dev::bob().public_key().into();
|
||||
let tx = polkadot::tx().balances().transfer_allow_death(dest, 10_000);
|
||||
|
||||
let latest_block = api.blocks().at_latest().await?;
|
||||
|
||||
// Configure the transaction parameters; we give a small tip and set the
|
||||
// transaction to live for 32 blocks from the `latest_block` above.
|
||||
let tx_params = Params::new()
|
||||
.tip(1_000)
|
||||
.mortal(latest_block.header(), 32)
|
||||
.build();
|
||||
let tx_params = Params::new().tip(1_000).mortal(32).build();
|
||||
|
||||
// submit the transaction:
|
||||
let from = dev::alice();
|
||||
|
||||
Reference in New Issue
Block a user