Files
pezkuwi-subxt/subxt/src/config/extrinsic_params.rs
T
James Wilson a2b8dde5e6 Add 'Static' type and improve type substitution codegen to accept it (#886)
* Add Static type which defers to Encode/Decode and impls EncodeAsType/DecodeAsType

* rename to static_type and impl Deref/Mut

* Improve type substitution in codegen so that concrete types can be swapped in

* A couple of comment tweaks and no need for a macro export

* Extend type substitution logic to work recursively on destination type

* cargo fmt

* Fix a couple of comments

* update ui test outpuot

* Add docs and missing_docs lint

* Add test for replacing multiple of Ident

* Update codegen/src/error.rs

Co-authored-by: Niklas Adolfsson <niklasadolfsson1@gmail.com>

* update copyright year and fix ui test

* simplify another error

---------

Co-authored-by: Niklas Adolfsson <niklasadolfsson1@gmail.com>
2023-03-31 16:56:19 +01:00

259 lines
9.4 KiB
Rust

// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! This module contains a trait which controls the parameters that must
//! be provided in order to successfully construct an extrinsic. A basic
//! implementation of the trait is provided ([`BaseExtrinsicParams`]) which is
//! used by the provided Substrate and Polkadot configuration.
use crate::{utils::Encoded, Config};
use codec::{Compact, Decode, Encode};
use core::fmt::Debug;
use derivative::Derivative;
use serde::{Deserialize, Serialize};
/// This trait allows you to configure the "signed extra" and
/// "additional" parameters that are signed and used in transactions.
/// see [`BaseExtrinsicParams`] for an implementation that is compatible with
/// a Polkadot node.
pub trait ExtrinsicParams<Index, Hash>: Debug + 'static {
/// These parameters can be provided to the constructor along with
/// some default parameters that `subxt` understands, in order to
/// help construct your [`ExtrinsicParams`] object.
type OtherParams;
/// Construct a new instance of our [`ExtrinsicParams`]
fn new(
spec_version: u32,
tx_version: u32,
nonce: Index,
genesis_hash: Hash,
other_params: Self::OtherParams,
) -> Self;
/// This is expected to SCALE encode the "signed extra" parameters
/// to some buffer that has been provided. These are the parameters
/// which are sent along with the transaction, as well as taken into
/// account when signing the transaction.
fn encode_extra_to(&self, v: &mut Vec<u8>);
/// This is expected to SCALE encode the "additional" parameters
/// to some buffer that has been provided. These parameters are _not_
/// sent along with the transaction, but are taken into account when
/// signing it, meaning the client and node must agree on their values.
fn encode_additional_to(&self, v: &mut Vec<u8>);
}
/// 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(Derivative)]
#[derivative(Debug(bound = "Tip: Debug"))]
pub struct BaseExtrinsicParams<T: Config, Tip: Debug> {
era: Era,
nonce: T::Index,
tip: Tip,
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 [`super::substrate::SubstrateExtrinsicParamsBuilder`] for a version of this
/// tailored towards Substrate, or [`super::polkadot::PolkadotExtrinsicParamsBuilder`] for a
/// version tailored to Polkadot.
#[derive(Derivative)]
#[derivative(
Debug(bound = "Tip: Debug"),
Clone(bound = "Tip: Clone"),
Copy(bound = "Tip: Copy"),
PartialEq(bound = "Tip: PartialEq")
)]
pub struct BaseExtrinsicParamsBuilder<T: Config, Tip> {
era: Era,
mortality_checkpoint: Option<T::Hash>,
tip: Tip,
}
impl<T: Config, Tip: Default> BaseExtrinsicParamsBuilder<T, Tip> {
/// Instantiate the default set of [`BaseExtrinsicParamsBuilder`]
pub fn new() -> Self {
Self::default()
}
/// 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
}
}
impl<T: Config, Tip: Default> Default for BaseExtrinsicParamsBuilder<T, Tip> {
fn default() -> Self {
Self {
era: Era::Immortal,
mortality_checkpoint: None,
tip: Tip::default(),
}
}
}
impl<T: Config, Tip: Debug + Encode + 'static> ExtrinsicParams<T::Index, T::Hash>
for BaseExtrinsicParams<T, Tip>
{
type OtherParams = BaseExtrinsicParamsBuilder<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 {
BaseExtrinsicParams {
era: other_params.era,
mortality_checkpoint: other_params.mortality_checkpoint.unwrap_or(genesis_hash),
tip: other_params.tip,
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).encode_to(v);
}
fn encode_additional_to(&self, v: &mut Vec<u8>) {
(
self.spec_version,
self.transaction_version,
self.genesis_hash,
self.mortality_checkpoint,
)
.encode_to(v);
}
}
// Dev note: This and related bits taken from `sp_runtime::generic::Era`
/// An era to describe the longevity of a transaction.
#[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)]
pub enum Era {
/// The transaction is valid forever. The genesis hash must be present in the signed content.
Immortal,
/// Period and phase are encoded:
/// - The period of validity from the block hash found in the signing material.
/// - The phase in the period that this transaction's lifetime begins (and, importantly,
/// implies which block hash is included in the signature material). If the `period` is
/// greater than 1 << 12, then it will be a factor of the times greater than 1<<12 that
/// `period` is.
///
/// When used on `FRAME`-based runtimes, `period` cannot exceed `BlockHashCount` parameter
/// of `system` module.
Mortal(Period, Phase),
}
/// Era period
pub type Period = u64;
/// Era phase
pub type Phase = u64;
// E.g. with period == 4:
// 0 10 20 30 40
// 0123456789012345678901234567890123456789012
// |...|
// authored -/ \- expiry
// phase = 1
// n = Q(current - phase, period) + phase
impl Era {
/// Create a new era based on a period (which should be a power of two between 4 and 65536
/// inclusive) and a block number on which it should start (or, for long periods, be shortly
/// after the start).
///
/// If using `Era` in the context of `FRAME` runtime, make sure that `period`
/// does not exceed `BlockHashCount` parameter passed to `system` module, since that
/// prunes old blocks and renders transactions immediately invalid.
pub fn mortal(period: u64, current: u64) -> Self {
let period = period
.checked_next_power_of_two()
.unwrap_or(1 << 16)
.clamp(4, 1 << 16);
let phase = current % period;
let quantize_factor = (period >> 12).max(1);
let quantized_phase = phase / quantize_factor * quantize_factor;
Self::Mortal(period, quantized_phase)
}
/// Create an "immortal" transaction.
pub fn immortal() -> Self {
Self::Immortal
}
}
// Both copied from `sp_runtime::generic::Era`; this is the wire interface and so
// it's really the most important bit here.
impl Encode for Era {
fn encode_to<T: codec::Output + ?Sized>(&self, output: &mut T) {
match self {
Self::Immortal => output.push_byte(0),
Self::Mortal(period, phase) => {
let quantize_factor = (*period >> 12).max(1);
let encoded = (period.trailing_zeros() - 1).clamp(1, 15) as u16
| ((phase / quantize_factor) << 4) as u16;
encoded.encode_to(output);
}
}
}
}
impl Decode for Era {
fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
let first = input.read_byte()?;
if first == 0 {
Ok(Self::Immortal)
} else {
let encoded = first as u64 + ((input.read_byte()? as u64) << 8);
let period = 2 << (encoded % (1 << 4));
let quantize_factor = (period >> 12).max(1);
let phase = (encoded >> 4) * quantize_factor;
if period >= 4 && phase < period {
Ok(Self::Mortal(period, phase))
} else {
Err("Invalid period and phase".into())
}
}
}
}