fix: Convert vendor/pezkuwi-subxt from submodule to regular directory

This commit is contained in:
2025-12-19 16:45:24 +03:00
parent 9a52edf0df
commit fdd023c499
393 changed files with 154124 additions and 1 deletions
+458
View File
@@ -0,0 +1,458 @@
// Copyright 2019-2024 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! Construct and sign transactions.
//!
//! # Example
//!
//! ```rust
//! use pezkuwi_subxt_signer::sr25519::dev;
//! use pezkuwi_subxt_macro::subxt;
//! use pezkuwi_subxt_core::config::{PolkadotConfig, HashFor};
//! use pezkuwi_subxt_core::config::DefaultExtrinsicParamsBuilder as Params;
//! use pezkuwi_subxt_core::tx;
//! use pezkuwi_subxt_core::utils::H256;
//! use pezkuwi_subxt_core::Metadata;
//!
//! // If we generate types without `subxt`, we need to point to `::pezkuwi_subxt_core`:
//! #[subxt(
//! crate = "::pezkuwi_subxt_core",
//! runtime_metadata_path = "../artifacts/polkadot_metadata_small.scale",
//! )]
//! pub mod polkadot {}
//!
//! // Gather some other information about the chain that we'll need to construct valid extrinsics:
//! let state = tx::ClientState::<PolkadotConfig> {
//! metadata: {
//! let metadata_bytes = include_bytes!("../../../artifacts/polkadot_metadata_small.scale");
//! Metadata::decode_from(&metadata_bytes[..]).unwrap()
//! },
//! genesis_hash: {
//! let h = "91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3";
//! let bytes = hex::decode(h).unwrap();
//! H256::from_slice(&bytes)
//! },
//! runtime_version: tx::RuntimeVersion {
//! spec_version: 9370,
//! transaction_version: 20,
//! }
//! };
//!
//! // Now we can build a balance transfer extrinsic.
//! let dest = dev::bob().public_key().into();
//! let call = polkadot::tx().balances().transfer_allow_death(dest, 10_000);
//! let params = Params::new().tip(1_000).nonce(0).build();
//!
//! // We can validate that this lines up with the given metadata:
//! tx::validate(&call, &state.metadata).unwrap();
//!
//! // We can build a signed transaction:
//! let signed_call = tx::create_v4_signed(&call, &state, params)
//! .unwrap()
//! .sign(&dev::alice());
//!
//! // And log it:
//! println!("Tx: 0x{}", hex::encode(signed_call.encoded()));
//! ```
pub mod payload;
pub mod signer;
use crate::Metadata;
use crate::config::{Config, ExtrinsicParams, ExtrinsicParamsEncoder, HashFor, Hasher};
use crate::error::ExtrinsicError;
use crate::utils::Encoded;
use alloc::borrow::Cow;
use alloc::string::ToString;
use alloc::vec::Vec;
use codec::{Compact, Encode};
use payload::Payload;
use signer::Signer as SignerT;
use pezsp_crypto_hashing::blake2_256;
// Expose these here since we expect them in some calls below.
pub use crate::client::{ClientState, RuntimeVersion};
/// Run the validation logic against some extrinsic you'd like to submit. Returns `Ok(())`
/// if the call is valid (or if it's not possible to check since the call has no validation hash).
/// Return an error if the call was not valid or something went wrong trying to validate it (ie
/// the pallet or call in question do not exist at all).
pub fn validate<Call: Payload>(call: &Call, metadata: &Metadata) -> Result<(), ExtrinsicError> {
let Some(details) = call.validation_details() else {
return Ok(());
};
let pallet_name = details.pallet_name;
let call_name = details.call_name;
let expected_hash = metadata
.pallet_by_name(pallet_name)
.ok_or_else(|| ExtrinsicError::PalletNameNotFound(pallet_name.to_string()))?
.call_hash(call_name)
.ok_or_else(|| ExtrinsicError::CallNameNotFound {
pallet_name: pallet_name.to_string(),
call_name: call_name.to_string(),
})?;
if details.hash != expected_hash {
Err(ExtrinsicError::IncompatibleCodegen)
} else {
Ok(())
}
}
/// Returns the suggested transaction versions to build for a given chain, or an error
/// if Subxt doesn't support any version expected by the chain.
///
/// If the result is [`TransactionVersion::V4`], use the `v4` methods in this module. If it's
/// [`TransactionVersion::V5`], use the `v5` ones.
pub fn suggested_version(metadata: &Metadata) -> Result<TransactionVersion, ExtrinsicError> {
let versions = metadata.extrinsic().supported_versions();
if versions.contains(&4) {
Ok(TransactionVersion::V4)
} else if versions.contains(&5) {
Ok(TransactionVersion::V5)
} else {
Err(ExtrinsicError::UnsupportedVersion)
}
}
/// The transaction versions supported by Subxt.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum TransactionVersion {
/// v4 transactions (signed and unsigned transactions)
V4,
/// v5 transactions (bare and general transactions)
V5,
}
/// Return the SCALE encoded bytes representing the call data of the transaction.
pub fn call_data<Call: Payload>(
call: &Call,
metadata: &Metadata,
) -> Result<Vec<u8>, ExtrinsicError> {
let mut bytes = Vec::new();
call.encode_call_data_to(metadata, &mut bytes)?;
Ok(bytes)
}
/// Creates a V4 "unsigned" transaction without submitting it.
pub fn create_v4_unsigned<T: Config, Call: Payload>(
call: &Call,
metadata: &Metadata,
) -> Result<Transaction<T>, ExtrinsicError> {
create_unsigned_at_version(call, 4, metadata)
}
/// Creates a V5 "bare" transaction without submitting it.
pub fn create_v5_bare<T: Config, Call: Payload>(
call: &Call,
metadata: &Metadata,
) -> Result<Transaction<T>, ExtrinsicError> {
create_unsigned_at_version(call, 5, metadata)
}
// Create a V4 "unsigned" transaction or V5 "bare" transaction.
fn create_unsigned_at_version<T: Config, Call: Payload>(
call: &Call,
tx_version: u8,
metadata: &Metadata,
) -> Result<Transaction<T>, ExtrinsicError> {
// 1. Validate this call against the current node metadata if the call comes
// with a hash allowing us to do so.
validate(call, metadata)?;
// 2. Encode extrinsic
let extrinsic = {
let mut encoded_inner = Vec::new();
// encode the transaction version first.
tx_version.encode_to(&mut encoded_inner);
// encode call data after this byte.
call.encode_call_data_to(metadata, &mut encoded_inner)?;
// now, prefix byte length:
let len = Compact(
u32::try_from(encoded_inner.len()).expect("extrinsic size expected to be <4GB"),
);
let mut encoded = Vec::new();
len.encode_to(&mut encoded);
encoded.extend(encoded_inner);
encoded
};
// Wrap in Encoded to ensure that any more "encode" calls leave it in the right state.
Ok(Transaction::from_bytes(extrinsic))
}
/// Construct a v4 extrinsic, ready to be signed.
pub fn create_v4_signed<T: Config, Call: Payload>(
call: &Call,
client_state: &ClientState<T>,
params: <T::ExtrinsicParams as ExtrinsicParams<T>>::Params,
) -> Result<PartialTransactionV4<T>, ExtrinsicError> {
// 1. Validate this call against the current node metadata if the call comes
// with a hash allowing us to do so.
validate(call, &client_state.metadata)?;
// 2. SCALE encode call data to bytes (pallet u8, call u8, call params).
let call_data = call_data(call, &client_state.metadata)?;
// 3. Construct our custom additional/extra params.
let additional_and_extra_params =
<T::ExtrinsicParams as ExtrinsicParams<T>>::new(client_state, params)?;
// Return these details, ready to construct a signed extrinsic from.
Ok(PartialTransactionV4 {
call_data,
additional_and_extra_params,
})
}
/// Construct a v5 "general" extrinsic, ready to be signed or emitted as is.
pub fn create_v5_general<T: Config, Call: Payload>(
call: &Call,
client_state: &ClientState<T>,
params: <T::ExtrinsicParams as ExtrinsicParams<T>>::Params,
) -> Result<PartialTransactionV5<T>, ExtrinsicError> {
// 1. Validate this call against the current node metadata if the call comes
// with a hash allowing us to do so.
validate(call, &client_state.metadata)?;
// 2. Work out which TX extension version to target based on metadata.
let tx_extensions_version = client_state
.metadata
.extrinsic()
.transaction_extension_version_to_use_for_encoding();
// 3. SCALE encode call data to bytes (pallet u8, call u8, call params).
let call_data = call_data(call, &client_state.metadata)?;
// 4. Construct our custom additional/extra params.
let additional_and_extra_params =
<T::ExtrinsicParams as ExtrinsicParams<T>>::new(client_state, params)?;
// Return these details, ready to construct a signed extrinsic from.
Ok(PartialTransactionV5 {
call_data,
additional_and_extra_params,
tx_extensions_version,
})
}
/// A partially constructed V4 extrinsic, ready to be signed.
pub struct PartialTransactionV4<T: Config> {
call_data: Vec<u8>,
additional_and_extra_params: T::ExtrinsicParams,
}
impl<T: Config> PartialTransactionV4<T> {
/// Return the bytes representing the call data for this partially constructed
/// extrinsic.
pub fn call_data(&self) -> &[u8] {
&self.call_data
}
// Obtain bytes representing the signer payload and run call some function
// with them. This can avoid an allocation in some cases.
fn with_signer_payload<F, R>(&self, f: F) -> R
where
F: for<'a> FnOnce(Cow<'a, [u8]>) -> R,
{
let mut bytes = self.call_data.clone();
self.additional_and_extra_params
.encode_signer_payload_value_to(&mut bytes);
self.additional_and_extra_params
.encode_implicit_to(&mut bytes);
if bytes.len() > 256 {
f(Cow::Borrowed(&blake2_256(&bytes)))
} else {
f(Cow::Owned(bytes))
}
}
/// Return the V4 signer payload for this extrinsic. These are the bytes that must
/// be signed in order to produce a valid signature for the extrinsic.
pub fn signer_payload(&self) -> Vec<u8> {
self.with_signer_payload(|bytes| bytes.to_vec())
}
/// Convert this [`PartialTransactionV4`] into a V4 signed [`Transaction`], ready to submit.
/// The provided `signer` is responsible for providing the "from" address for the transaction,
/// as well as providing a signature to attach to it.
pub fn sign<Signer>(&self, signer: &Signer) -> Transaction<T>
where
Signer: SignerT<T>,
{
// Given our signer, we can sign the payload representing this extrinsic.
let signature = self.with_signer_payload(|bytes| signer.sign(&bytes));
// Now, use the signature and "from" address to build the extrinsic.
self.sign_with_account_and_signature(signer.account_id(), &signature)
}
/// Convert this [`PartialTransactionV4`] into a V4 signed [`Transaction`], ready to submit.
/// The provided `address` and `signature` will be used.
pub fn sign_with_account_and_signature(
&self,
account_id: T::AccountId,
signature: &T::Signature,
) -> Transaction<T> {
let extrinsic = {
let mut encoded_inner = Vec::new();
// "is signed" + transaction protocol version (4)
(0b10000000 + 4u8).encode_to(&mut encoded_inner);
// from address for signature
let address: T::Address = account_id.into();
address.encode_to(&mut encoded_inner);
// the signature
signature.encode_to(&mut encoded_inner);
// attach custom extra params
self.additional_and_extra_params
.encode_value_to(&mut encoded_inner);
// and now, call data (remembering that it's been encoded already and just needs appending)
encoded_inner.extend(&self.call_data);
// now, prefix byte length:
let len = Compact(
u32::try_from(encoded_inner.len()).expect("extrinsic size expected to be <4GB"),
);
let mut encoded = Vec::new();
len.encode_to(&mut encoded);
encoded.extend(encoded_inner);
encoded
};
// Return an extrinsic ready to be submitted.
Transaction::from_bytes(extrinsic)
}
}
/// A partially constructed V5 general extrinsic, ready to be signed or emitted as-is.
pub struct PartialTransactionV5<T: Config> {
call_data: Vec<u8>,
additional_and_extra_params: T::ExtrinsicParams,
tx_extensions_version: u8,
}
impl<T: Config> PartialTransactionV5<T> {
/// Return the bytes representing the call data for this partially constructed
/// extrinsic.
pub fn call_data(&self) -> &[u8] {
&self.call_data
}
/// Return the V5 signer payload for this extrinsic. These are the bytes that must
/// be signed in order to produce a valid signature for the extrinsic.
pub fn signer_payload(&self) -> [u8; 32] {
let mut bytes = self.call_data.clone();
self.additional_and_extra_params
.encode_signer_payload_value_to(&mut bytes);
self.additional_and_extra_params
.encode_implicit_to(&mut bytes);
blake2_256(&bytes)
}
/// Convert this [`PartialTransactionV5`] into a V5 "general" [`Transaction`].
///
/// This transaction has not been explicitly signed. Use [`Self::sign`]
/// or [`Self::sign_with_account_and_signature`] if you wish to provide a
/// signature (this is usually a necessary step).
pub fn to_transaction(&self) -> Transaction<T> {
let extrinsic = {
let mut encoded_inner = Vec::new();
// "is general" + transaction protocol version (5)
(0b01000000 + 5u8).encode_to(&mut encoded_inner);
// Encode versions for the transaction extensions
self.tx_extensions_version.encode_to(&mut encoded_inner);
// Encode the actual transaction extensions values
self.additional_and_extra_params
.encode_value_to(&mut encoded_inner);
// and now, call data (remembering that it's been encoded already and just needs appending)
encoded_inner.extend(&self.call_data);
// now, prefix byte length:
let len = Compact(
u32::try_from(encoded_inner.len()).expect("extrinsic size expected to be <4GB"),
);
let mut encoded = Vec::new();
len.encode_to(&mut encoded);
encoded.extend(encoded_inner);
encoded
};
// Return an extrinsic ready to be submitted.
Transaction::from_bytes(extrinsic)
}
/// Convert this [`PartialTransactionV5`] into a V5 "general" [`Transaction`] with a signature.
///
/// Signing the transaction injects the signature into the transaction extension data, which is why
/// this method borrows self mutably. Signing repeatedly will override the previous signature.
pub fn sign<Signer>(&mut self, signer: &Signer) -> Transaction<T>
where
Signer: SignerT<T>,
{
// Given our signer, we can sign the payload representing this extrinsic.
let signature = signer.sign(&self.signer_payload());
// Now, use the signature and "from" account to build the extrinsic.
self.sign_with_account_and_signature(&signer.account_id(), &signature)
}
/// Convert this [`PartialTransactionV5`] into a V5 "general" [`Transaction`] with a signature.
/// Prefer [`Self::sign`] if you have a [`SignerT`] instance to use.
///
/// Signing the transaction injects the signature into the transaction extension data, which is why
/// this method borrows self mutably. Signing repeatedly will override the previous signature.
pub fn sign_with_account_and_signature(
&mut self,
account_id: &T::AccountId,
signature: &T::Signature,
) -> Transaction<T> {
// Inject the signature into the transaction extensions
// before constructing it.
self.additional_and_extra_params
.inject_signature(account_id, signature);
self.to_transaction()
}
}
/// This represents a signed transaction that's ready to be submitted.
/// Use [`Transaction::encoded()`] or [`Transaction::into_encoded()`] to
/// get the bytes for it, or [`Transaction::hash_with()`] to hash the transaction
/// given an instance of [`Config::Hasher`].
pub struct Transaction<T> {
encoded: Encoded,
marker: core::marker::PhantomData<T>,
}
impl<T: Config> Transaction<T> {
/// Create a [`Transaction`] from some already-signed and prepared
/// extrinsic bytes,
pub fn from_bytes(tx_bytes: Vec<u8>) -> Self {
Self {
encoded: Encoded(tx_bytes),
marker: core::marker::PhantomData,
}
}
/// Calculate and return the hash of the extrinsic, based on the provided hasher.
/// If you don't have a hasher to hand, you can construct one using the metadata
/// with `T::Hasher::new(&metadata)`. This will create a hasher suitable for the
/// current chain where possible.
pub fn hash_with(&self, hasher: T::Hasher) -> HashFor<T> {
hasher.hash_of(&self.encoded)
}
/// Returns the SCALE encoded extrinsic bytes.
pub fn encoded(&self) -> &[u8] {
&self.encoded.0
}
/// Consumes this [`Transaction`] and returns the SCALE encoded
/// extrinsic bytes.
pub fn into_encoded(self) -> Vec<u8> {
self.encoded.0
}
}
+279
View File
@@ -0,0 +1,279 @@
// Copyright 2019-2024 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 the trait and types used to represent
//! transactions that can be submitted.
use crate::Metadata;
use crate::error::ExtrinsicError;
use alloc::borrow::Cow;
use alloc::boxed::Box;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use codec::Encode;
use scale_encode::EncodeAsFields;
use scale_value::{Composite, Value, ValueDef, Variant};
/// This represents a transaction payload that can be submitted
/// to a node.
pub trait Payload {
/// Encode call data to the provided output.
fn encode_call_data_to(
&self,
metadata: &Metadata,
out: &mut Vec<u8>,
) -> Result<(), ExtrinsicError>;
/// Encode call data and return the output. This is a convenience
/// wrapper around [`Payload::encode_call_data_to`].
fn encode_call_data(&self, metadata: &Metadata) -> Result<Vec<u8>, ExtrinsicError> {
let mut v = Vec::new();
self.encode_call_data_to(metadata, &mut v)?;
Ok(v)
}
/// Returns the details needed to validate the call, which
/// include a statically generated hash, the pallet name,
/// and the call name.
fn validation_details(&self) -> Option<ValidationDetails<'_>> {
None
}
}
macro_rules! boxed_payload {
($ty:path) => {
impl<T: Payload + ?Sized> Payload for $ty {
fn encode_call_data_to(
&self,
metadata: &Metadata,
out: &mut Vec<u8>,
) -> Result<(), ExtrinsicError> {
self.as_ref().encode_call_data_to(metadata, out)
}
fn encode_call_data(&self, metadata: &Metadata) -> Result<Vec<u8>, ExtrinsicError> {
self.as_ref().encode_call_data(metadata)
}
fn validation_details(&self) -> Option<ValidationDetails<'_>> {
self.as_ref().validation_details()
}
}
};
}
boxed_payload!(Box<T>);
#[cfg(feature = "std")]
boxed_payload!(std::sync::Arc<T>);
#[cfg(feature = "std")]
boxed_payload!(std::rc::Rc<T>);
/// Details required to validate the shape of a transaction payload against some metadata.
pub struct ValidationDetails<'a> {
/// The pallet name.
pub pallet_name: &'a str,
/// The call name.
pub call_name: &'a str,
/// A hash (this is generated at compile time in our codegen)
/// to compare against the runtime code.
pub hash: [u8; 32],
}
/// A transaction payload containing some generic `CallData`.
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct DefaultPayload<CallData> {
pallet_name: Cow<'static, str>,
call_name: Cow<'static, str>,
call_data: CallData,
validation_hash: Option<[u8; 32]>,
}
/// The payload type used by static codegen.
pub type StaticPayload<Calldata> = DefaultPayload<Calldata>;
/// The type of a payload typically used for dynamic transaction payloads.
pub type DynamicPayload = DefaultPayload<Composite<()>>;
impl<CallData> DefaultPayload<CallData> {
/// Create a new [`DefaultPayload`].
pub fn new(
pallet_name: impl Into<String>,
call_name: impl Into<String>,
call_data: CallData,
) -> Self {
DefaultPayload {
pallet_name: Cow::Owned(pallet_name.into()),
call_name: Cow::Owned(call_name.into()),
call_data,
validation_hash: None,
}
}
/// Create a new [`DefaultPayload`] using static strings for the pallet and call name.
/// This is only expected to be used from codegen.
#[doc(hidden)]
pub fn new_static(
pallet_name: &'static str,
call_name: &'static str,
call_data: CallData,
validation_hash: [u8; 32],
) -> Self {
DefaultPayload {
pallet_name: Cow::Borrowed(pallet_name),
call_name: Cow::Borrowed(call_name),
call_data,
validation_hash: Some(validation_hash),
}
}
/// Do not validate this call prior to submitting it.
pub fn unvalidated(self) -> Self {
Self {
validation_hash: None,
..self
}
}
/// Returns the call data.
pub fn call_data(&self) -> &CallData {
&self.call_data
}
/// Returns the pallet name.
pub fn pallet_name(&self) -> &str {
&self.pallet_name
}
/// Returns the call name.
pub fn call_name(&self) -> &str {
&self.call_name
}
}
impl DefaultPayload<Composite<()>> {
/// Convert the dynamic `Composite` payload into a [`Value`].
/// This is useful if you want to use this as an argument for a
/// larger dynamic call that wants to use this as a nested call.
pub fn into_value(self) -> Value<()> {
let call = Value {
context: (),
value: ValueDef::Variant(Variant {
name: self.call_name.into_owned(),
values: self.call_data,
}),
};
Value::unnamed_variant(self.pallet_name, [call])
}
}
impl<CallData: EncodeAsFields> Payload for DefaultPayload<CallData> {
fn encode_call_data_to(
&self,
metadata: &Metadata,
out: &mut Vec<u8>,
) -> Result<(), ExtrinsicError> {
let pallet = metadata
.pallet_by_name(&self.pallet_name)
.ok_or_else(|| ExtrinsicError::PalletNameNotFound(self.pallet_name.to_string()))?;
let call = pallet
.call_variant_by_name(&self.call_name)
.ok_or_else(|| ExtrinsicError::CallNameNotFound {
pallet_name: pallet.name().to_string(),
call_name: self.call_name.to_string(),
})?;
let pallet_index = pallet.call_index();
let call_index = call.index;
pallet_index.encode_to(out);
call_index.encode_to(out);
let mut fields = call
.fields
.iter()
.map(|f| scale_encode::Field::new(f.ty.id, f.name.as_deref()));
self.call_data
.encode_as_fields_to(&mut fields, metadata.types(), out)
.map_err(ExtrinsicError::CannotEncodeCallData)?;
Ok(())
}
fn validation_details(&self) -> Option<ValidationDetails<'_>> {
self.validation_hash.map(|hash| ValidationDetails {
pallet_name: &self.pallet_name,
call_name: &self.call_name,
hash,
})
}
}
/// Construct a transaction at runtime; essentially an alias to [`DefaultPayload::new()`]
/// which provides a [`Composite`] value for the call data.
pub fn dynamic(
pallet_name: impl Into<String>,
call_name: impl Into<String>,
call_data: impl Into<Composite<()>>,
) -> DynamicPayload {
DefaultPayload::new(pallet_name, call_name, call_data.into())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Metadata;
use codec::Decode;
use scale_value::Composite;
fn test_metadata() -> Metadata {
let metadata_bytes = include_bytes!("../../../artifacts/polkadot_metadata_small.scale");
Metadata::decode(&mut &metadata_bytes[..]).expect("Valid metadata")
}
#[test]
fn encode_call_with_incompatible_types_returns_error() {
let metadata = test_metadata();
let incompatible_data = Composite::named([
("dest", scale_value::Value::bool(true)), // Boolean instead of MultiAddress
("value", scale_value::Value::string("not_a_number")), // String instead of u128
]);
let payload = DefaultPayload::new("Balances", "transfer_allow_death", incompatible_data);
let mut out = Vec::new();
let result = payload.encode_call_data_to(&metadata, &mut out);
assert!(
result.is_err(),
"Expected error when encoding with incompatible types"
);
}
#[test]
fn encode_call_with_valid_data_succeeds() {
let metadata = test_metadata();
// Create a valid payload to ensure our error handling doesn't break valid cases
// For MultiAddress, we'll use the Id variant with a 32-byte account
let valid_address =
scale_value::Value::unnamed_variant("Id", [scale_value::Value::from_bytes([0u8; 32])]);
let valid_data = Composite::named([
("dest", valid_address),
("value", scale_value::Value::u128(1000)),
]);
let payload = DefaultPayload::new("Balances", "transfer_allow_death", valid_data);
// This should succeed
let mut out = Vec::new();
let result = payload.encode_call_data_to(&metadata, &mut out);
assert!(
result.is_ok(),
"Expected success when encoding with valid data"
);
assert!(!out.is_empty(), "Expected encoded output to be non-empty");
}
}
+22
View File
@@ -0,0 +1,22 @@
// Copyright 2019-2024 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! A library to **sub**mit e**xt**rinsics to a
//! [substrate](https://github.com/paritytech/substrate) node via RPC.
use crate::Config;
/// Signing transactions requires a [`Signer`]. This is responsible for
/// providing the "from" account that the transaction is being signed by,
/// as well as actually signing a SCALE encoded payload.
pub trait Signer<T: Config> {
/// Return the "from" account ID.
fn account_id(&self) -> T::AccountId;
/// Takes a signer payload for an extrinsic, and returns a signature based on it.
///
/// Some signers may fail, for instance because the hardware on which the keys are located has
/// refused the operation.
fn sign(&self, signer_payload: &[u8]) -> T::Signature;
}