mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-09 20:11:09 +00:00
Make subxt-core ready for publishing (#1508)
* Move Extrinsic decoding things to subxt_core and various tidy-ups * A couple more fixes and fmt * first pass moving tx logic to subxt_core * cargo fmt * fix wasm example * clippy * more clippy * WIP Adding examples and such * Move storage functionality more fully to subxt_core and nice examples for storage and txs * Add example for events * consistify how addresses/payloads are exposed in subxt-core and add runtime API fns * Add runtime API core example * fmt * remove scale-info patch * Add a little to the top level docs * swap args around * clippy * cargo fmt and fix wasm-example * doc fixes * no-std-ise new subxt-core additions * alloc, not core * more no-std fixes * A couple more fixes * Add back extrinsic decode test
This commit is contained in:
@@ -181,16 +181,20 @@ impl<T: Config + Send + Sync + 'static> Backend<T> for LegacyBackend<T> {
|
||||
|
||||
async fn current_runtime_version(&self) -> Result<RuntimeVersion, Error> {
|
||||
let details = self.methods.state_get_runtime_version(None).await?;
|
||||
Ok(RuntimeVersion::new(
|
||||
details.spec_version,
|
||||
details.transaction_version,
|
||||
))
|
||||
Ok(RuntimeVersion {
|
||||
spec_version: details.spec_version,
|
||||
transaction_version: details.transaction_version,
|
||||
})
|
||||
}
|
||||
|
||||
async fn stream_runtime_version(&self) -> Result<StreamOfResults<RuntimeVersion>, Error> {
|
||||
let sub = self.methods.state_subscribe_runtime_version().await?;
|
||||
let sub =
|
||||
sub.map(|r| r.map(|v| RuntimeVersion::new(v.spec_version, v.transaction_version)));
|
||||
let sub = sub.map(|r| {
|
||||
r.map(|v| RuntimeVersion {
|
||||
spec_version: v.spec_version,
|
||||
transaction_version: v.transaction_version,
|
||||
})
|
||||
});
|
||||
Ok(StreamOf(Box::pin(sub)))
|
||||
}
|
||||
|
||||
|
||||
@@ -416,7 +416,10 @@ impl<T: Config + Send + Sync + 'static> Backend<T> for UnstableBackend<T> {
|
||||
RuntimeEvent::Valid(ev) => ev,
|
||||
};
|
||||
|
||||
let runtime_version = RuntimeVersion::new(runtime_details.spec.spec_version, runtime_details.spec.transaction_version);
|
||||
let runtime_version = RuntimeVersion {
|
||||
spec_version: runtime_details.spec.spec_version,
|
||||
transaction_version: runtime_details.spec.transaction_version
|
||||
};
|
||||
std::future::ready(Some(Ok(runtime_version)))
|
||||
});
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
use crate::{
|
||||
backend::BlockRef,
|
||||
blocks::{extrinsic_types::ExtrinsicPartTypeIds, Extrinsics},
|
||||
blocks::Extrinsics,
|
||||
client::{OfflineClientT, OnlineClientT},
|
||||
config::{Config, Header},
|
||||
error::{BlockError, DecodeError, Error},
|
||||
@@ -79,7 +79,6 @@ where
|
||||
|
||||
/// Fetch and return the extrinsics in the block body.
|
||||
pub async fn extrinsics(&self) -> Result<Extrinsics<T, C>, Error> {
|
||||
let ids = ExtrinsicPartTypeIds::new(&self.client.metadata())?;
|
||||
let block_hash = self.header.hash();
|
||||
let Some(extrinsics) = self.client.backend().block_body(block_hash).await? else {
|
||||
return Err(BlockError::not_found(block_hash).into());
|
||||
@@ -89,9 +88,8 @@ where
|
||||
self.client.clone(),
|
||||
extrinsics,
|
||||
self.cached_events.clone(),
|
||||
ids,
|
||||
block_hash,
|
||||
))
|
||||
)?)
|
||||
}
|
||||
|
||||
/// Work with storage.
|
||||
|
||||
+136
-800
File diff suppressed because it is too large
Load Diff
@@ -15,7 +15,7 @@ pub use block_types::Block;
|
||||
pub use blocks_client::BlocksClient;
|
||||
pub use extrinsic_types::{
|
||||
ExtrinsicDetails, ExtrinsicEvents, ExtrinsicSignedExtension, ExtrinsicSignedExtensions,
|
||||
Extrinsics, StaticExtrinsic,
|
||||
Extrinsics, FoundExtrinsic, StaticExtrinsic,
|
||||
};
|
||||
|
||||
// We get account nonce info in tx_client, too, so re-use the logic:
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
//! );
|
||||
//! ```
|
||||
//!
|
||||
//! All valid runtime calls implement [`crate::runtime_api::RuntimeApiPayload`], a trait which
|
||||
//! All valid runtime calls implement [`crate::runtime_api::PayloadT`], a trait which
|
||||
//! describes how to encode the runtime call arguments and what return type to decode from the
|
||||
//! response.
|
||||
//!
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
//! - `runtime::storage().foo().bar_iter3(u8, bool, u16)`: iterate over all of the entries in the "bar" map under
|
||||
//! a given `u8`, `bool` and `u16` value.
|
||||
//!
|
||||
//! All valid storage queries implement [`crate::storage::StorageAddress`]. As well as describing
|
||||
//! All valid storage queries implement [`crate::storage::AddressT`]. As well as describing
|
||||
//! how to build a valid storage query, this trait also has some associated types that determine the
|
||||
//! shape of the result you'll get back, and determine what you can do with it (ie, can you iterate
|
||||
//! over storage entries using it).
|
||||
@@ -124,7 +124,7 @@
|
||||
//!
|
||||
//! For more advanced use cases, have a look at [`crate::storage::Storage::fetch_raw`] and
|
||||
//! [`crate::storage::Storage::fetch_raw_keys`]. Both of these take raw bytes as arguments, which can be
|
||||
//! obtained from a [`crate::storage::StorageAddress`] by using
|
||||
//! obtained from a [`crate::storage::AddressT`] by using
|
||||
//! [`crate::storage::StorageClient::address_bytes()`] or
|
||||
//! [`crate::storage::StorageClient::address_root_bytes()`].
|
||||
//!
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
//! represents any type of data that can be SCALE encoded or decoded. It can be serialized,
|
||||
//! deserialized and parsed from/to strings.
|
||||
//!
|
||||
//! A valid transaction payload is just something that implements the [`crate::tx::TxPayload`] trait;
|
||||
//! A valid transaction payload is just something that implements the [`crate::tx::PayloadT`] trait;
|
||||
//! you can implement this trait on your own custom types if the built-in ones are not suitable for
|
||||
//! your needs.
|
||||
//!
|
||||
|
||||
@@ -17,13 +17,20 @@ use subxt_core::client::{ClientState, RuntimeVersion};
|
||||
pub trait OfflineClientT<T: Config>: Clone + Send + Sync + 'static {
|
||||
/// Return the provided [`Metadata`].
|
||||
fn metadata(&self) -> Metadata;
|
||||
|
||||
/// Return the provided genesis hash.
|
||||
fn genesis_hash(&self) -> T::Hash;
|
||||
|
||||
/// Return the provided [`RuntimeVersion`].
|
||||
fn runtime_version(&self) -> RuntimeVersion;
|
||||
|
||||
/// Return the [subxt_core::client::ClientState] (metadata, runtime version and genesis hash).
|
||||
fn client_state(&self) -> ClientState<T> {
|
||||
ClientState::new(self.genesis_hash(), self.runtime_version(), self.metadata())
|
||||
ClientState {
|
||||
genesis_hash: self.genesis_hash(),
|
||||
runtime_version: self.runtime_version(),
|
||||
metadata: self.metadata(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Work with transactions.
|
||||
@@ -77,37 +84,30 @@ impl<T: Config> OfflineClient<T> {
|
||||
runtime_version: RuntimeVersion,
|
||||
metadata: impl Into<Metadata>,
|
||||
) -> OfflineClient<T> {
|
||||
let metadata = metadata.into();
|
||||
|
||||
OfflineClient {
|
||||
inner: Arc::new(ClientState::new(
|
||||
inner: Arc::new(ClientState {
|
||||
genesis_hash,
|
||||
runtime_version,
|
||||
metadata.into(),
|
||||
)),
|
||||
metadata,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the genesis hash.
|
||||
pub fn genesis_hash(&self) -> T::Hash {
|
||||
self.inner.genesis_hash()
|
||||
self.inner.genesis_hash
|
||||
}
|
||||
|
||||
/// Return the runtime version.
|
||||
pub fn runtime_version(&self) -> RuntimeVersion {
|
||||
self.inner.runtime_version()
|
||||
self.inner.runtime_version
|
||||
}
|
||||
|
||||
/// Return the [`Metadata`] used in this client.
|
||||
pub fn metadata(&self) -> Metadata {
|
||||
self.inner.metadata()
|
||||
}
|
||||
|
||||
/// Return the [subxt_core::client::ClientState] (metadata, runtime version and genesis hash).
|
||||
pub fn client_state(&self) -> ClientState<T> {
|
||||
ClientState::new(
|
||||
self.inner.genesis_hash(),
|
||||
self.inner.runtime_version(),
|
||||
self.inner.metadata(),
|
||||
)
|
||||
self.inner.metadata.clone()
|
||||
}
|
||||
|
||||
// Just a copy of the most important trait methods so that people
|
||||
@@ -149,9 +149,6 @@ impl<T: Config> OfflineClientT<T> for OfflineClient<T> {
|
||||
fn metadata(&self) -> Metadata {
|
||||
self.metadata()
|
||||
}
|
||||
fn client_state(&self) -> ClientState<T> {
|
||||
self.client_state()
|
||||
}
|
||||
}
|
||||
|
||||
// For ergonomics; cloning a client is deliberately fairly cheap (via Arc),
|
||||
|
||||
@@ -228,7 +228,7 @@ impl<T: Config> OnlineClient<T> {
|
||||
/// let mut update_stream = updater.runtime_updates().await.unwrap();
|
||||
///
|
||||
/// while let Some(Ok(update)) = update_stream.next().await {
|
||||
/// let version = update.runtime_version().spec_version();
|
||||
/// let version = update.runtime_version().spec_version;
|
||||
///
|
||||
/// match updater.apply_update(update) {
|
||||
/// Ok(()) => {
|
||||
@@ -286,16 +286,6 @@ impl<T: Config> OnlineClient<T> {
|
||||
inner.runtime_version
|
||||
}
|
||||
|
||||
/// Return the [subxt_core::client::ClientState] (metadata, runtime version and genesis hash).
|
||||
pub fn client_state(&self) -> ClientState<T> {
|
||||
let inner = self.inner.read().expect("shouldn't be poisoned");
|
||||
ClientState::new(
|
||||
inner.genesis_hash,
|
||||
inner.runtime_version,
|
||||
inner.metadata.clone(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Change the [`RuntimeVersion`] used in this client.
|
||||
///
|
||||
/// # Warning
|
||||
@@ -371,9 +361,14 @@ impl<T: Config> OfflineClientT<T> for OnlineClient<T> {
|
||||
fn runtime_version(&self) -> RuntimeVersion {
|
||||
self.runtime_version()
|
||||
}
|
||||
|
||||
// This is provided by default, but we can optimise here and only lock once:
|
||||
fn client_state(&self) -> ClientState<T> {
|
||||
self.client_state()
|
||||
let inner = self.inner.read().expect("shouldn't be poisoned");
|
||||
ClientState {
|
||||
genesis_hash: inner.genesis_hash,
|
||||
runtime_version: inner.runtime_version,
|
||||
metadata: inner.metadata.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -551,7 +546,7 @@ async fn wait_runtime_upgrade_in_finalized_block<T: Config>(
|
||||
|
||||
// We are waiting for the chain to have the same spec version
|
||||
// as sent out via the runtime subscription.
|
||||
if spec_version == runtime_version.spec_version() {
|
||||
if spec_version == runtime_version.spec_version {
|
||||
break block_ref;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use super::ConstantAddress;
|
||||
use crate::{client::OfflineClientT, error::Error, Config};
|
||||
use derive_where::derive_where;
|
||||
use subxt_core::constants::address::AddressT;
|
||||
|
||||
/// A client for accessing constants.
|
||||
#[derive_where(Clone; Client)]
|
||||
@@ -28,19 +28,16 @@ impl<T: Config, Client: OfflineClientT<T>> ConstantsClient<T, Client> {
|
||||
/// if the address is valid (or if it's not possible to check since the address has no validation hash).
|
||||
/// Return an error if the address was not valid or something went wrong trying to validate it (ie
|
||||
/// the pallet or constant in question do not exist at all).
|
||||
pub fn validate<Address: ConstantAddress>(&self, address: &Address) -> Result<(), Error> {
|
||||
pub fn validate<Address: AddressT>(&self, address: &Address) -> Result<(), Error> {
|
||||
let metadata = self.client.metadata();
|
||||
subxt_core::constants::validate_constant(&metadata, address).map_err(Error::from)
|
||||
subxt_core::constants::validate(address, &metadata).map_err(Error::from)
|
||||
}
|
||||
|
||||
/// Access the constant at the address given, returning the type defined by this address.
|
||||
/// This is probably used with addresses given from static codegen, although you can manually
|
||||
/// construct your own, too.
|
||||
pub fn at<Address: ConstantAddress>(
|
||||
&self,
|
||||
address: &Address,
|
||||
) -> Result<Address::Target, Error> {
|
||||
pub fn at<Address: AddressT>(&self, address: &Address) -> Result<Address::Target, Error> {
|
||||
let metadata = self.client.metadata();
|
||||
subxt_core::constants::get_constant(&metadata, address).map_err(Error::from)
|
||||
subxt_core::constants::get(address, &metadata).map_err(Error::from)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,4 +7,4 @@
|
||||
mod constants_client;
|
||||
|
||||
pub use constants_client::ConstantsClient;
|
||||
pub use subxt_core::constants::{dynamic, Address, ConstantAddress, DynamicAddress};
|
||||
pub use subxt_core::constants::address::{dynamic, Address, AddressT, DynamicAddress};
|
||||
|
||||
@@ -2,10 +2,7 @@ use crate::client::OfflineClientT;
|
||||
use crate::{Config, Error};
|
||||
use derive_where::derive_where;
|
||||
|
||||
use subxt_core::custom_values::{
|
||||
get_custom_value, get_custom_value_bytes, validate_custom_value, CustomValueAddress,
|
||||
};
|
||||
use subxt_core::utils::Yes;
|
||||
use subxt_core::custom_values::address::{AddressT, Yes};
|
||||
|
||||
/// A client for accessing custom values stored in the metadata.
|
||||
#[derive_where(Clone; Client)]
|
||||
@@ -27,29 +24,26 @@ impl<T, Client> CustomValuesClient<T, Client> {
|
||||
impl<T: Config, Client: OfflineClientT<T>> CustomValuesClient<T, Client> {
|
||||
/// Access a custom value by the address it is registered under. This can be just a [str] to get back a dynamic value,
|
||||
/// or a static address from the generated static interface to get a value of a static type returned.
|
||||
pub fn at<Address: CustomValueAddress<IsDecodable = Yes> + ?Sized>(
|
||||
pub fn at<Address: AddressT<IsDecodable = Yes> + ?Sized>(
|
||||
&self,
|
||||
address: &Address,
|
||||
) -> Result<Address::Target, Error> {
|
||||
get_custom_value(&self.client.metadata(), address).map_err(Into::into)
|
||||
subxt_core::custom_values::get(address, &self.client.metadata()).map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Access the bytes of a custom value by the address it is registered under.
|
||||
pub fn bytes_at<Address: CustomValueAddress + ?Sized>(
|
||||
pub fn bytes_at<Address: AddressT + ?Sized>(
|
||||
&self,
|
||||
address: &Address,
|
||||
) -> Result<Vec<u8>, Error> {
|
||||
get_custom_value_bytes(&self.client.metadata(), address).map_err(Into::into)
|
||||
subxt_core::custom_values::get_bytes(address, &self.client.metadata()).map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Run the validation logic against some custom value address you'd like to access. Returns `Ok(())`
|
||||
/// if the address is valid (or if it's not possible to check since the address has no validation hash).
|
||||
/// Returns an error if the address was not valid (wrong name, type or raw bytes)
|
||||
pub fn validate<Address: CustomValueAddress + ?Sized>(
|
||||
&self,
|
||||
address: &Address,
|
||||
) -> Result<(), Error> {
|
||||
validate_custom_value(&self.client.metadata(), address).map_err(Into::into)
|
||||
pub fn validate<Address: AddressT + ?Sized>(&self, address: &Address) -> Result<(), Error> {
|
||||
subxt_core::custom_values::validate(address, &self.client.metadata()).map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,14 +107,17 @@ mod tests {
|
||||
};
|
||||
|
||||
let metadata: subxt_metadata::Metadata = frame_metadata.try_into().unwrap();
|
||||
Metadata::new(metadata)
|
||||
Metadata::from(metadata)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decoding() {
|
||||
let client = OfflineClient::<SubstrateConfig>::new(
|
||||
Default::default(),
|
||||
RuntimeVersion::new(0, 0),
|
||||
RuntimeVersion {
|
||||
spec_version: 0,
|
||||
transaction_version: 0,
|
||||
},
|
||||
mock_metadata(),
|
||||
);
|
||||
let custom_value_client = CustomValuesClient::new(client);
|
||||
|
||||
@@ -7,4 +7,4 @@
|
||||
mod custom_values_client;
|
||||
|
||||
pub use custom_values_client::CustomValuesClient;
|
||||
pub use subxt_core::custom_values::{CustomValueAddress, StaticAddress};
|
||||
pub use subxt_core::custom_values::address::{AddressT, StaticAddress, Yes};
|
||||
|
||||
+21
-8
@@ -6,6 +6,8 @@
|
||||
|
||||
mod dispatch_error;
|
||||
|
||||
use subxt_core::error::{BlockError as CoreBlockError, Error as CoreError};
|
||||
|
||||
crate::macros::cfg_unstable_light_client! {
|
||||
pub use subxt_lightclient::LightClientError;
|
||||
}
|
||||
@@ -80,15 +82,16 @@ pub enum Error {
|
||||
Other(String),
|
||||
}
|
||||
|
||||
impl From<subxt_core::Error> for Error {
|
||||
fn from(value: subxt_core::Error) -> Self {
|
||||
impl From<CoreError> for Error {
|
||||
fn from(value: CoreError) -> Self {
|
||||
match value {
|
||||
subxt_core::Error::Codec(e) => Error::Codec(e),
|
||||
subxt_core::Error::Metadata(e) => Error::Metadata(e),
|
||||
subxt_core::Error::StorageAddress(e) => Error::StorageAddress(e),
|
||||
subxt_core::Error::Decode(e) => Error::Decode(e),
|
||||
subxt_core::Error::Encode(e) => Error::Encode(e),
|
||||
subxt_core::Error::ExtrinsicParams(e) => Error::ExtrinsicParams(e),
|
||||
CoreError::Codec(e) => Error::Codec(e),
|
||||
CoreError::Metadata(e) => Error::Metadata(e),
|
||||
CoreError::StorageAddress(e) => Error::StorageAddress(e),
|
||||
CoreError::Decode(e) => Error::Decode(e),
|
||||
CoreError::Encode(e) => Error::Encode(e),
|
||||
CoreError::ExtrinsicParams(e) => Error::ExtrinsicParams(e),
|
||||
CoreError::Block(e) => Error::Block(e.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -175,6 +178,16 @@ pub enum BlockError {
|
||||
DecodingError(codec::Error),
|
||||
}
|
||||
|
||||
impl From<CoreBlockError> for BlockError {
|
||||
fn from(value: CoreBlockError) -> Self {
|
||||
match value {
|
||||
CoreBlockError::MissingType => BlockError::MissingType,
|
||||
CoreBlockError::UnsupportedVersion(n) => BlockError::UnsupportedVersion(n),
|
||||
CoreBlockError::DecodingError(e) => BlockError::DecodingError(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BlockError {
|
||||
/// Produce an error that a block with the given hash cannot be found.
|
||||
pub fn not_found(hash: impl AsRef<[u8]>) -> BlockError {
|
||||
|
||||
@@ -64,7 +64,7 @@ where
|
||||
};
|
||||
|
||||
let event_bytes = get_event_bytes(client.backend(), block_ref.hash()).await?;
|
||||
Ok(Events::decode_from(client.metadata(), event_bytes))
|
||||
Ok(Events::decode_from(event_bytes, client.metadata()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,5 +24,5 @@ where
|
||||
C: OnlineClientT<T>,
|
||||
{
|
||||
let event_bytes = events_client::get_event_bytes(client.backend(), block_hash).await?;
|
||||
Ok(Events::<T>::decode_from(metadata, event_bytes))
|
||||
Ok(Events::<T>::decode_from(event_bytes, metadata))
|
||||
}
|
||||
|
||||
+1
-1
@@ -71,7 +71,7 @@ pub mod config {
|
||||
|
||||
/// Types representing the metadata obtained from a node.
|
||||
pub mod metadata {
|
||||
pub use subxt_core::metadata::{DecodeWithMetadata, EncodeWithMetadata, Metadata, MetadataExt};
|
||||
pub use subxt_core::metadata::{DecodeWithMetadata, EncodeWithMetadata, Metadata};
|
||||
// Expose metadata types under a sub module in case somebody needs to reference them:
|
||||
pub use subxt_metadata as types;
|
||||
}
|
||||
|
||||
@@ -9,4 +9,4 @@ mod runtime_types;
|
||||
|
||||
pub use runtime_client::RuntimeApiClient;
|
||||
pub use runtime_types::RuntimeApi;
|
||||
pub use subxt_core::runtime_api::{dynamic, DynamicRuntimeApiPayload, Payload, RuntimeApiPayload};
|
||||
pub use subxt_core::runtime_api::payload::{dynamic, DynamicPayload, Payload, PayloadT};
|
||||
|
||||
@@ -2,19 +2,17 @@
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use super::PayloadT;
|
||||
use crate::{
|
||||
backend::{BackendExt, BlockRef},
|
||||
client::OnlineClientT,
|
||||
error::{Error, MetadataError},
|
||||
metadata::DecodeWithMetadata,
|
||||
error::Error,
|
||||
Config,
|
||||
};
|
||||
use codec::Decode;
|
||||
use derive_where::derive_where;
|
||||
use std::{future::Future, marker::PhantomData};
|
||||
|
||||
use super::RuntimeApiPayload;
|
||||
|
||||
/// Execute runtime API calls.
|
||||
#[derive_where(Clone; Client)]
|
||||
pub struct RuntimeApi<T: Config, Client> {
|
||||
@@ -39,6 +37,14 @@ where
|
||||
T: Config,
|
||||
Client: OnlineClientT<T>,
|
||||
{
|
||||
/// Run the validation logic against some runtime API payload you'd like to use. Returns `Ok(())`
|
||||
/// if the payload is valid (or if it's not possible to check since the payload has no validation hash).
|
||||
/// Return an error if the payload was not valid or something went wrong trying to validate it (ie
|
||||
/// the runtime API in question do not exist at all)
|
||||
pub fn validate<Call: PayloadT>(&self, payload: &Call) -> Result<(), Error> {
|
||||
subxt_core::runtime_api::validate(payload, &self.client.metadata()).map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Execute a raw runtime API call.
|
||||
pub fn call_raw<'a, Res: Decode>(
|
||||
&self,
|
||||
@@ -59,7 +65,7 @@ where
|
||||
}
|
||||
|
||||
/// Execute a runtime API call.
|
||||
pub fn call<Call: RuntimeApiPayload>(
|
||||
pub fn call<Call: PayloadT>(
|
||||
&self,
|
||||
payload: Call,
|
||||
) -> impl Future<Output = Result<Call::ReturnType, Error>> {
|
||||
@@ -70,39 +76,21 @@ where
|
||||
async move {
|
||||
let metadata = client.metadata();
|
||||
|
||||
let api_trait = metadata.runtime_api_trait_by_name_err(payload.trait_name())?;
|
||||
let api_method = api_trait
|
||||
.method_by_name(payload.method_name())
|
||||
.ok_or_else(|| {
|
||||
MetadataError::RuntimeMethodNotFound(payload.method_name().to_owned())
|
||||
})?;
|
||||
|
||||
// Validate the runtime API payload hash against the compile hash from codegen.
|
||||
if let Some(static_hash) = payload.validation_hash() {
|
||||
let Some(runtime_hash) = api_trait.method_hash(payload.method_name()) else {
|
||||
return Err(MetadataError::IncompatibleCodegen.into());
|
||||
};
|
||||
if static_hash != runtime_hash {
|
||||
return Err(MetadataError::IncompatibleCodegen.into());
|
||||
}
|
||||
}
|
||||
subxt_core::runtime_api::validate(&payload, &metadata)?;
|
||||
|
||||
// Encode the arguments of the runtime call.
|
||||
// For static payloads (codegen) this is pass-through, bytes are not altered.
|
||||
// For dynamic payloads this relies on `scale_value::encode_as_fields_to`.
|
||||
let params = payload.encode_args(&metadata)?;
|
||||
let call_name = format!("{}_{}", payload.trait_name(), payload.method_name());
|
||||
let call_name = subxt_core::runtime_api::call_name(&payload);
|
||||
let call_args = subxt_core::runtime_api::call_args(&payload, &metadata)?;
|
||||
|
||||
// Make the call.
|
||||
let bytes = client
|
||||
.backend()
|
||||
.call(&call_name, Some(params.as_slice()), block_hash)
|
||||
.call(&call_name, Some(call_args.as_slice()), block_hash)
|
||||
.await?;
|
||||
|
||||
let value = <Call::ReturnType as DecodeWithMetadata>::decode_with_metadata(
|
||||
&mut &bytes[..],
|
||||
api_method.output_ty(),
|
||||
&metadata,
|
||||
)?;
|
||||
// Decode the response.
|
||||
let value = subxt_core::runtime_api::decode_value(&mut &*bytes, &payload, &metadata)?;
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,19 +8,7 @@ mod storage_client;
|
||||
mod storage_type;
|
||||
|
||||
pub use storage_client::StorageClient;
|
||||
|
||||
pub use storage_type::{Storage, StorageKeyValuePair};
|
||||
|
||||
/// Types representing an address which describes where a storage
|
||||
/// entry lives and how to properly decode it.
|
||||
pub mod address {
|
||||
pub use subxt_core::storage::address::{
|
||||
dynamic, Address, DynamicAddress, StaticStorageKey, StorageAddress, StorageKey,
|
||||
};
|
||||
}
|
||||
|
||||
pub use subxt_core::storage::StorageKey;
|
||||
|
||||
// For consistency with other modules, also expose
|
||||
// the basic address stuff at the root of the module.
|
||||
pub use address::{dynamic, Address, DynamicAddress, StorageAddress};
|
||||
pub use subxt_core::storage::address::{
|
||||
dynamic, Address, AddressT, DynamicAddress, StaticStorageKey, StorageKey,
|
||||
};
|
||||
|
||||
@@ -2,10 +2,7 @@
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use super::{
|
||||
storage_type::{validate_storage_address, Storage},
|
||||
StorageAddress,
|
||||
};
|
||||
use super::storage_type::Storage;
|
||||
use crate::{
|
||||
backend::BlockRef,
|
||||
client::{OfflineClientT, OnlineClientT},
|
||||
@@ -14,6 +11,7 @@ use crate::{
|
||||
};
|
||||
use derive_where::derive_where;
|
||||
use std::{future::Future, marker::PhantomData};
|
||||
use subxt_core::storage::address::AddressT;
|
||||
|
||||
/// Query the runtime storage.
|
||||
#[derive_where(Clone; Client)]
|
||||
@@ -41,29 +39,23 @@ where
|
||||
/// if the address is valid (or if it's not possible to check since the address has no validation hash).
|
||||
/// Return an error if the address was not valid or something went wrong trying to validate it (ie
|
||||
/// the pallet or storage entry in question do not exist at all).
|
||||
pub fn validate<Address: StorageAddress>(&self, address: &Address) -> Result<(), Error> {
|
||||
let metadata = self.client.metadata();
|
||||
let pallet_metadata = metadata.pallet_by_name_err(address.pallet_name())?;
|
||||
validate_storage_address(address, pallet_metadata)
|
||||
pub fn validate<Address: AddressT>(&self, address: &Address) -> Result<(), Error> {
|
||||
subxt_core::storage::validate(address, &self.client.metadata()).map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Convert some storage address into the raw bytes that would be submitted to the node in order
|
||||
/// to retrieve the entries at the root of the associated address.
|
||||
pub fn address_root_bytes<Address: StorageAddress>(&self, address: &Address) -> Vec<u8> {
|
||||
subxt_core::storage::utils::storage_address_root_bytes(address)
|
||||
pub fn address_root_bytes<Address: AddressT>(&self, address: &Address) -> Vec<u8> {
|
||||
subxt_core::storage::get_address_root_bytes(address)
|
||||
}
|
||||
|
||||
/// Convert some storage address into the raw bytes that would be submitted to the node in order
|
||||
/// to retrieve an entry. This fails if [`StorageAddress::append_entry_bytes`] does; in the built-in
|
||||
/// to retrieve an entry. This fails if [`AddressT::append_entry_bytes`] does; in the built-in
|
||||
/// implementation this would be if the pallet and storage entry being asked for is not available on the
|
||||
/// node you're communicating with, or if the metadata is missing some type information (which should not
|
||||
/// happen).
|
||||
pub fn address_bytes<Address: StorageAddress>(
|
||||
&self,
|
||||
address: &Address,
|
||||
) -> Result<Vec<u8>, Error> {
|
||||
subxt_core::storage::utils::storage_address_bytes(address, &self.client.metadata())
|
||||
.map_err(Into::into)
|
||||
pub fn address_bytes<Address: AddressT>(&self, address: &Address) -> Result<Vec<u8>, Error> {
|
||||
subxt_core::storage::get_address_bytes(address, &self.client.metadata()).map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,23 +2,19 @@
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use subxt_core::storage::address::{StorageAddress, StorageHashers, StorageKey};
|
||||
use subxt_core::utils::Yes;
|
||||
|
||||
use crate::{
|
||||
backend::{BackendExt, BlockRef},
|
||||
client::OnlineClientT,
|
||||
error::{Error, MetadataError, StorageAddressError},
|
||||
metadata::{DecodeWithMetadata, Metadata},
|
||||
metadata::DecodeWithMetadata,
|
||||
Config,
|
||||
};
|
||||
use codec::Decode;
|
||||
use derive_where::derive_where;
|
||||
use futures::StreamExt;
|
||||
|
||||
use std::{future::Future, marker::PhantomData};
|
||||
|
||||
use subxt_metadata::{PalletMetadata, StorageEntryMetadata, StorageEntryType};
|
||||
use subxt_core::storage::address::{AddressT, StorageHashers, StorageKey};
|
||||
use subxt_core::utils::Yes;
|
||||
|
||||
/// This is returned from a couple of storage functions.
|
||||
pub use crate::backend::StreamOfResults;
|
||||
@@ -119,26 +115,22 @@ where
|
||||
address: &'address Address,
|
||||
) -> impl Future<Output = Result<Option<Address::Target>, Error>> + 'address
|
||||
where
|
||||
Address: StorageAddress<IsFetchable = Yes> + 'address,
|
||||
Address: AddressT<IsFetchable = Yes> + 'address,
|
||||
{
|
||||
let client = self.clone();
|
||||
async move {
|
||||
let metadata = client.client.metadata();
|
||||
let (pallet, entry) =
|
||||
lookup_entry_details(address.pallet_name(), address.entry_name(), &metadata)?;
|
||||
|
||||
// Metadata validation checks whether the static address given
|
||||
// is likely to actually correspond to a real storage entry or not.
|
||||
// if not, it means static codegen doesn't line up with runtime
|
||||
// metadata.
|
||||
validate_storage_address(address, pallet)?;
|
||||
subxt_core::storage::validate(address, &metadata)?;
|
||||
|
||||
// Look up the return type ID to enable DecodeWithMetadata:
|
||||
let lookup_bytes =
|
||||
subxt_core::storage::utils::storage_address_bytes(address, &metadata)?;
|
||||
let lookup_bytes = subxt_core::storage::get_address_bytes(address, &metadata)?;
|
||||
if let Some(data) = client.fetch_raw(lookup_bytes).await? {
|
||||
let val =
|
||||
decode_storage_with_metadata::<Address::Target>(&mut &*data, &metadata, entry)?;
|
||||
let val = subxt_core::storage::decode_value(&mut &*data, address, &metadata)?;
|
||||
Ok(Some(val))
|
||||
} else {
|
||||
Ok(None)
|
||||
@@ -152,24 +144,16 @@ where
|
||||
address: &'address Address,
|
||||
) -> impl Future<Output = Result<Address::Target, Error>> + 'address
|
||||
where
|
||||
Address: StorageAddress<IsFetchable = Yes, IsDefaultable = Yes> + 'address,
|
||||
Address: AddressT<IsFetchable = Yes, IsDefaultable = Yes> + 'address,
|
||||
{
|
||||
let client = self.clone();
|
||||
async move {
|
||||
let pallet_name = address.pallet_name();
|
||||
let entry_name = address.entry_name();
|
||||
// Metadata validation happens via .fetch():
|
||||
if let Some(data) = client.fetch(address).await? {
|
||||
Ok(data)
|
||||
} else {
|
||||
let metadata = client.client.metadata();
|
||||
let (_pallet_metadata, storage_entry) =
|
||||
lookup_entry_details(pallet_name, entry_name, &metadata)?;
|
||||
|
||||
let return_ty_id = return_type_from_storage_entry_type(storage_entry.entry_type());
|
||||
let bytes = &mut storage_entry.default_bytes();
|
||||
|
||||
let val = Address::Target::decode_with_metadata(bytes, return_ty_id, &metadata)?;
|
||||
let val = subxt_core::storage::default_value(address, &metadata)?;
|
||||
Ok(val)
|
||||
}
|
||||
}
|
||||
@@ -211,21 +195,24 @@ where
|
||||
address: Address,
|
||||
) -> impl Future<Output = Result<StreamOfResults<StorageKeyValuePair<Address>>, Error>> + 'static
|
||||
where
|
||||
Address: StorageAddress<IsIterable = Yes> + 'static,
|
||||
Address: AddressT<IsIterable = Yes> + 'static,
|
||||
Address::Keys: 'static + Sized,
|
||||
{
|
||||
let client = self.client.clone();
|
||||
let block_ref = self.block_ref.clone();
|
||||
async move {
|
||||
let metadata = client.metadata();
|
||||
let (pallet, entry) =
|
||||
lookup_entry_details(address.pallet_name(), address.entry_name(), &metadata)?;
|
||||
let (_pallet, entry) = subxt_core::storage::lookup_storage_entry_details(
|
||||
address.pallet_name(),
|
||||
address.entry_name(),
|
||||
&metadata,
|
||||
)?;
|
||||
|
||||
// Metadata validation checks whether the static address given
|
||||
// is likely to actually correspond to a real storage entry or not.
|
||||
// if not, it means static codegen doesn't line up with runtime
|
||||
// metadata.
|
||||
validate_storage_address(&address, pallet)?;
|
||||
subxt_core::storage::validate(&address, &metadata)?;
|
||||
|
||||
// Look up the return type for flexible decoding. Do this once here to avoid
|
||||
// potentially doing it every iteration if we used `decode_storage_with_metadata`
|
||||
@@ -236,8 +223,7 @@ where
|
||||
let hashers = StorageHashers::new(entry, metadata.types())?;
|
||||
|
||||
// The address bytes of this entry:
|
||||
let address_bytes =
|
||||
subxt_core::storage::utils::storage_address_bytes(&address, &metadata)?;
|
||||
let address_bytes = subxt_core::storage::get_address_bytes(&address, &metadata)?;
|
||||
let s = client
|
||||
.backend()
|
||||
.storage_fetch_descendant_values(address_bytes, block_ref.hash())
|
||||
@@ -327,7 +313,7 @@ fn strip_storage_address_root_bytes(address_bytes: &mut &[u8]) -> Result<(), Sto
|
||||
/// A pair of keys and values together with all the bytes that make up the storage address.
|
||||
/// `keys` is `None` if non-concat hashers are used. In this case the keys could not be extracted back from the key_bytes.
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub struct StorageKeyValuePair<T: StorageAddress> {
|
||||
pub struct StorageKeyValuePair<T: AddressT> {
|
||||
/// The bytes that make up the address of the storage entry.
|
||||
pub key_bytes: Vec<u8>,
|
||||
/// The keys that can be used to construct the address of this storage entry.
|
||||
@@ -335,65 +321,3 @@ pub struct StorageKeyValuePair<T: StorageAddress> {
|
||||
/// The value of the storage entry.
|
||||
pub value: T::Target,
|
||||
}
|
||||
|
||||
/// Validate a storage address against the metadata.
|
||||
pub(crate) fn validate_storage_address<Address: StorageAddress>(
|
||||
address: &Address,
|
||||
pallet: PalletMetadata<'_>,
|
||||
) -> Result<(), Error> {
|
||||
if let Some(hash) = address.validation_hash() {
|
||||
validate_storage(pallet, address.entry_name(), hash)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Return details about the given storage entry.
|
||||
fn lookup_entry_details<'a>(
|
||||
pallet_name: &str,
|
||||
entry_name: &str,
|
||||
metadata: &'a Metadata,
|
||||
) -> Result<(PalletMetadata<'a>, &'a StorageEntryMetadata), Error> {
|
||||
let pallet_metadata = metadata.pallet_by_name_err(pallet_name)?;
|
||||
let storage_metadata = pallet_metadata
|
||||
.storage()
|
||||
.ok_or_else(|| MetadataError::StorageNotFoundInPallet(pallet_name.to_owned()))?;
|
||||
let storage_entry = storage_metadata
|
||||
.entry_by_name(entry_name)
|
||||
.ok_or_else(|| MetadataError::StorageEntryNotFound(entry_name.to_owned()))?;
|
||||
Ok((pallet_metadata, storage_entry))
|
||||
}
|
||||
|
||||
/// Validate a storage entry against the metadata.
|
||||
fn validate_storage(
|
||||
pallet: PalletMetadata<'_>,
|
||||
storage_name: &str,
|
||||
hash: [u8; 32],
|
||||
) -> Result<(), Error> {
|
||||
let Some(expected_hash) = pallet.storage_hash(storage_name) else {
|
||||
return Err(MetadataError::IncompatibleCodegen.into());
|
||||
};
|
||||
if expected_hash != hash {
|
||||
return Err(MetadataError::IncompatibleCodegen.into());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Fetch the return type out of a [`StorageEntryType`].
|
||||
fn return_type_from_storage_entry_type(entry: &StorageEntryType) -> u32 {
|
||||
match entry {
|
||||
StorageEntryType::Plain(ty) => *ty,
|
||||
StorageEntryType::Map { value_ty, .. } => *value_ty,
|
||||
}
|
||||
}
|
||||
|
||||
/// Given some bytes, a pallet and storage name, decode the response.
|
||||
fn decode_storage_with_metadata<T: DecodeWithMetadata>(
|
||||
bytes: &mut &[u8],
|
||||
metadata: &Metadata,
|
||||
storage_metadata: &StorageEntryMetadata,
|
||||
) -> Result<T, Error> {
|
||||
let ty = storage_metadata.entry_type();
|
||||
let return_ty = return_type_from_storage_entry_type(ty);
|
||||
let val = T::decode_with_metadata(bytes, return_ty, metadata)?;
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
// 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.
|
||||
|
||||
//! these utility methods complement the [`StorageAddress`] trait, but
|
||||
//! aren't things that should ever be overridden, and so don't exist on
|
||||
//! the trait itself.
|
||||
|
||||
use subxt_metadata::StorageHasher;
|
||||
|
||||
use super::StorageAddress;
|
||||
use crate::{error::Error, metadata::Metadata};
|
||||
|
||||
/// Return the root of a given [`StorageAddress`]: hash the pallet name and entry name
|
||||
/// and append those bytes to the output.
|
||||
pub fn write_storage_address_root_bytes<Address: StorageAddress>(
|
||||
addr: &Address,
|
||||
out: &mut Vec<u8>,
|
||||
) {
|
||||
out.extend(sp_crypto_hashing::twox_128(addr.pallet_name().as_bytes()));
|
||||
out.extend(sp_crypto_hashing::twox_128(addr.entry_name().as_bytes()));
|
||||
}
|
||||
|
||||
/// Outputs the [`storage_address_root_bytes`] as well as any additional bytes that represent
|
||||
/// a lookup in a storage map at that location.
|
||||
pub fn storage_address_bytes<Address: StorageAddress>(
|
||||
addr: &Address,
|
||||
metadata: &Metadata,
|
||||
) -> Result<Vec<u8>, Error> {
|
||||
let mut bytes = Vec::new();
|
||||
write_storage_address_root_bytes(addr, &mut bytes);
|
||||
addr.append_entry_bytes(metadata, &mut bytes)?;
|
||||
Ok(bytes)
|
||||
}
|
||||
|
||||
/// Outputs a vector containing the bytes written by [`write_storage_address_root_bytes`].
|
||||
pub fn storage_address_root_bytes<Address: StorageAddress>(addr: &Address) -> Vec<u8> {
|
||||
let mut bytes = Vec::new();
|
||||
write_storage_address_root_bytes(addr, &mut bytes);
|
||||
bytes
|
||||
}
|
||||
|
||||
/// Take some SCALE encoded bytes and a [`StorageHasher`] and hash the bytes accordingly.
|
||||
pub fn hash_bytes(input: &[u8], hasher: StorageHasher, bytes: &mut Vec<u8>) {
|
||||
match hasher {
|
||||
StorageHasher::Identity => bytes.extend(input),
|
||||
StorageHasher::Blake2_128 => bytes.extend(sp_crypto_hashing::blake2_128(input)),
|
||||
StorageHasher::Blake2_128Concat => {
|
||||
bytes.extend(sp_crypto_hashing::blake2_128(input));
|
||||
bytes.extend(input);
|
||||
}
|
||||
StorageHasher::Blake2_256 => bytes.extend(sp_crypto_hashing::blake2_256(input)),
|
||||
StorageHasher::Twox128 => bytes.extend(sp_crypto_hashing::twox_128(input)),
|
||||
StorageHasher::Twox256 => bytes.extend(sp_crypto_hashing::twox_256(input)),
|
||||
StorageHasher::Twox64Concat => {
|
||||
bytes.extend(sp_crypto_hashing::twox_64(input));
|
||||
bytes.extend(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
+7
-12
@@ -14,21 +14,16 @@ use crate::macros::cfg_substrate_compat;
|
||||
mod tx_client;
|
||||
mod tx_progress;
|
||||
|
||||
pub use subxt_core::tx as tx_payload;
|
||||
pub use subxt_core::tx::signer;
|
||||
|
||||
// The PairSigner impl currently relies on Substrate bits and pieces, so make it an optional
|
||||
// feature if we want to avoid needing sp_core and sp_runtime.
|
||||
cfg_substrate_compat! {
|
||||
pub use signer::PairSigner;
|
||||
pub use subxt_core::tx::signer::PairSigner;
|
||||
}
|
||||
|
||||
pub use self::{
|
||||
signer::Signer,
|
||||
tx_client::{
|
||||
PartialExtrinsic, SubmittableExtrinsic, TransactionInvalid, TransactionUnknown, TxClient,
|
||||
ValidationResult,
|
||||
},
|
||||
tx_payload::{dynamic, DynamicPayload, Payload, TxPayload},
|
||||
tx_progress::{TxInBlock, TxProgress, TxStatus},
|
||||
pub use subxt_core::tx::payload::{dynamic, DynamicPayload, Payload, PayloadT};
|
||||
pub use subxt_core::tx::signer::{self, Signer};
|
||||
pub use tx_client::{
|
||||
PartialExtrinsic, SubmittableExtrinsic, TransactionInvalid, TransactionUnknown, TxClient,
|
||||
ValidationResult,
|
||||
};
|
||||
pub use tx_progress::{TxInBlock, TxProgress, TxStatus};
|
||||
|
||||
+56
-153
@@ -2,22 +2,16 @@
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
||||
use crate::{
|
||||
backend::{BackendExt, BlockRef, TransactionStatus},
|
||||
client::{OfflineClientT, OnlineClientT},
|
||||
config::{
|
||||
Config, ExtrinsicParams, ExtrinsicParamsEncoder, Hasher, Header, RefineParams,
|
||||
RefineParamsData,
|
||||
},
|
||||
error::{BlockError, Error, MetadataError},
|
||||
tx::{Signer as SignerT, TxPayload, TxProgress},
|
||||
utils::{Encoded, PhantomDataSendSync},
|
||||
config::{Config, ExtrinsicParams, Header, RefineParams, RefineParamsData},
|
||||
error::{BlockError, Error},
|
||||
tx::{PayloadT, Signer as SignerT, TxProgress},
|
||||
utils::PhantomDataSendSync,
|
||||
};
|
||||
use codec::{Compact, Decode, Encode};
|
||||
use derive_where::derive_where;
|
||||
use sp_crypto_hashing::blake2_256;
|
||||
|
||||
/// A client for working with transactions.
|
||||
#[derive_where(Clone; Client)]
|
||||
@@ -43,65 +37,30 @@ impl<T: Config, C: OfflineClientT<T>> TxClient<T, C> {
|
||||
/// the pallet or call in question do not exist at all).
|
||||
pub fn validate<Call>(&self, call: &Call) -> Result<(), Error>
|
||||
where
|
||||
Call: TxPayload,
|
||||
Call: PayloadT,
|
||||
{
|
||||
if let Some(details) = call.validation_details() {
|
||||
let expected_hash = self
|
||||
.client
|
||||
.metadata()
|
||||
.pallet_by_name_err(details.pallet_name)?
|
||||
.call_hash(details.call_name)
|
||||
.ok_or_else(|| MetadataError::CallNameNotFound(details.call_name.to_owned()))?;
|
||||
|
||||
if details.hash != expected_hash {
|
||||
return Err(MetadataError::IncompatibleCodegen.into());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
subxt_core::tx::validate(call, &self.client.metadata()).map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Return the SCALE encoded bytes representing the call data of the transaction.
|
||||
pub fn call_data<Call>(&self, call: &Call) -> Result<Vec<u8>, Error>
|
||||
where
|
||||
Call: TxPayload,
|
||||
Call: PayloadT,
|
||||
{
|
||||
let metadata = self.client.metadata();
|
||||
let mut bytes = Vec::new();
|
||||
call.encode_call_data_to(&metadata, &mut bytes)?;
|
||||
Ok(bytes)
|
||||
subxt_core::tx::call_data(call, &self.client.metadata()).map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Creates an unsigned extrinsic without submitting it.
|
||||
pub fn create_unsigned<Call>(&self, call: &Call) -> Result<SubmittableExtrinsic<T, C>, Error>
|
||||
where
|
||||
Call: TxPayload,
|
||||
Call: PayloadT,
|
||||
{
|
||||
// 1. Validate this call against the current node metadata if the call comes
|
||||
// with a hash allowing us to do so.
|
||||
self.validate(call)?;
|
||||
|
||||
// 2. Encode extrinsic
|
||||
let extrinsic = {
|
||||
let mut encoded_inner = Vec::new();
|
||||
// transaction protocol version (4) (is not signed, so no 1 bit at the front).
|
||||
4u8.encode_to(&mut encoded_inner);
|
||||
// encode call data after this byte.
|
||||
call.encode_call_data_to(&self.client.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(SubmittableExtrinsic::from_bytes(
|
||||
self.client.clone(),
|
||||
extrinsic,
|
||||
))
|
||||
subxt_core::tx::create_unsigned(call, &self.client.metadata())
|
||||
.map(|tx| SubmittableExtrinsic {
|
||||
client: self.client.clone(),
|
||||
inner: tx,
|
||||
})
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Create a partial extrinsic.
|
||||
@@ -114,25 +73,14 @@ impl<T: Config, C: OfflineClientT<T>> TxClient<T, C> {
|
||||
params: <T::ExtrinsicParams as ExtrinsicParams<T>>::Params,
|
||||
) -> Result<PartialExtrinsic<T, C>, Error>
|
||||
where
|
||||
Call: TxPayload,
|
||||
Call: PayloadT,
|
||||
{
|
||||
// 1. Validate this call against the current node metadata if the call comes
|
||||
// with a hash allowing us to do so.
|
||||
self.validate(call)?;
|
||||
|
||||
// 2. SCALE encode call data to bytes (pallet u8, call u8, call params).
|
||||
let call_data = self.call_data(call)?;
|
||||
|
||||
// 3. Construct our custom additional/extra params.
|
||||
let additional_and_extra_params =
|
||||
<T::ExtrinsicParams as ExtrinsicParams<T>>::new(&self.client.client_state(), params)?;
|
||||
|
||||
// Return these details, ready to construct a signed extrinsic from.
|
||||
Ok(PartialExtrinsic {
|
||||
client: self.client.clone(),
|
||||
call_data,
|
||||
additional_and_extra_params,
|
||||
})
|
||||
subxt_core::tx::create_partial_signed(call, &self.client.client_state(), params)
|
||||
.map(|tx| PartialExtrinsic {
|
||||
client: self.client.clone(),
|
||||
inner: tx,
|
||||
})
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Creates a signed extrinsic without submitting it.
|
||||
@@ -146,19 +94,15 @@ impl<T: Config, C: OfflineClientT<T>> TxClient<T, C> {
|
||||
params: <T::ExtrinsicParams as ExtrinsicParams<T>>::Params,
|
||||
) -> Result<SubmittableExtrinsic<T, C>, Error>
|
||||
where
|
||||
Call: TxPayload,
|
||||
Call: PayloadT,
|
||||
Signer: SignerT<T>,
|
||||
{
|
||||
// 1. Validate this call against the current node metadata if the call comes
|
||||
// with a hash allowing us to do so.
|
||||
self.validate(call)?;
|
||||
|
||||
// 2. Gather the "additional" and "extra" params along with the encoded call data,
|
||||
// ready to be signed.
|
||||
let partial_signed = self.create_partial_signed_offline(call, params)?;
|
||||
|
||||
// 3. Sign and construct an extrinsic from these details.
|
||||
Ok(partial_signed.sign(signer))
|
||||
subxt_core::tx::create_signed(call, &self.client.client_state(), signer, params)
|
||||
.map(|tx| SubmittableExtrinsic {
|
||||
client: self.client.clone(),
|
||||
inner: tx,
|
||||
})
|
||||
.map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,7 +149,7 @@ where
|
||||
mut params: <T::ExtrinsicParams as ExtrinsicParams<T>>::Params,
|
||||
) -> Result<PartialExtrinsic<T, C>, Error>
|
||||
where
|
||||
Call: TxPayload,
|
||||
Call: PayloadT,
|
||||
{
|
||||
// Refine the params by adding account nonce and latest block information:
|
||||
self.refine_params(account_id, &mut params).await?;
|
||||
@@ -221,7 +165,7 @@ where
|
||||
params: <T::ExtrinsicParams as ExtrinsicParams<T>>::Params,
|
||||
) -> Result<SubmittableExtrinsic<T, C>, Error>
|
||||
where
|
||||
Call: TxPayload,
|
||||
Call: PayloadT,
|
||||
Signer: SignerT<T>,
|
||||
{
|
||||
// 1. Validate this call against the current node metadata if the call comes
|
||||
@@ -249,7 +193,7 @@ where
|
||||
signer: &Signer,
|
||||
) -> Result<TxProgress<T, C>, Error>
|
||||
where
|
||||
Call: TxPayload,
|
||||
Call: PayloadT,
|
||||
Signer: SignerT<T>,
|
||||
<T::ExtrinsicParams as ExtrinsicParams<T>>::Params: Default,
|
||||
{
|
||||
@@ -268,7 +212,7 @@ where
|
||||
params: <T::ExtrinsicParams as ExtrinsicParams<T>>::Params,
|
||||
) -> Result<TxProgress<T, C>, Error>
|
||||
where
|
||||
Call: TxPayload,
|
||||
Call: PayloadT,
|
||||
Signer: SignerT<T>,
|
||||
{
|
||||
self.create_signed(call, signer, params)
|
||||
@@ -293,7 +237,7 @@ where
|
||||
signer: &Signer,
|
||||
) -> Result<T::Hash, Error>
|
||||
where
|
||||
Call: TxPayload,
|
||||
Call: PayloadT,
|
||||
Signer: SignerT<T>,
|
||||
<T::ExtrinsicParams as ExtrinsicParams<T>>::Params: Default,
|
||||
{
|
||||
@@ -315,7 +259,7 @@ where
|
||||
params: <T::ExtrinsicParams as ExtrinsicParams<T>>::Params,
|
||||
) -> Result<T::Hash, Error>
|
||||
where
|
||||
Call: TxPayload,
|
||||
Call: PayloadT,
|
||||
Signer: SignerT<T>,
|
||||
{
|
||||
self.create_signed(call, signer, params)
|
||||
@@ -328,8 +272,7 @@ where
|
||||
/// This payload contains the information needed to produce an extrinsic.
|
||||
pub struct PartialExtrinsic<T: Config, C> {
|
||||
client: C,
|
||||
call_data: Vec<u8>,
|
||||
additional_and_extra_params: T::ExtrinsicParams,
|
||||
inner: subxt_core::tx::PartialTransaction<T>,
|
||||
}
|
||||
|
||||
impl<T, C> PartialExtrinsic<T, C>
|
||||
@@ -337,34 +280,16 @@ where
|
||||
T: Config,
|
||||
C: OfflineClientT<T>,
|
||||
{
|
||||
// Obtain bytes representing the signer payload and run call some function
|
||||
// with them. This can avoid an allocation in some cases when compared to
|
||||
// [`PartialExtrinsic::signer_payload()`].
|
||||
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_extra_to(&mut bytes);
|
||||
self.additional_and_extra_params
|
||||
.encode_additional_to(&mut bytes);
|
||||
if bytes.len() > 256 {
|
||||
f(Cow::Borrowed(blake2_256(&bytes).as_ref()))
|
||||
} else {
|
||||
f(Cow::Owned(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the 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())
|
||||
self.inner.signer_payload()
|
||||
}
|
||||
|
||||
/// Return the bytes representing the call data for this partially constructed
|
||||
/// extrinsic.
|
||||
pub fn call_data(&self) -> &[u8] {
|
||||
&self.call_data
|
||||
self.inner.call_data()
|
||||
}
|
||||
|
||||
/// Convert this [`PartialExtrinsic`] into a [`SubmittableExtrinsic`], ready to submit.
|
||||
@@ -374,10 +299,10 @@ where
|
||||
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_address_and_signature(&signer.address(), &signature)
|
||||
SubmittableExtrinsic {
|
||||
client: self.client.clone(),
|
||||
inner: self.inner.sign(signer),
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert this [`PartialExtrinsic`] into a [`SubmittableExtrinsic`], ready to submit.
|
||||
@@ -389,40 +314,19 @@ where
|
||||
address: &T::Address,
|
||||
signature: &T::Signature,
|
||||
) -> SubmittableExtrinsic<T, C> {
|
||||
// Encode the extrinsic (into the format expected by protocol version 4)
|
||||
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
|
||||
address.encode_to(&mut encoded_inner);
|
||||
// the signature
|
||||
signature.encode_to(&mut encoded_inner);
|
||||
// attach custom extra params
|
||||
self.additional_and_extra_params
|
||||
.encode_extra_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.
|
||||
SubmittableExtrinsic::from_bytes(self.client.clone(), extrinsic)
|
||||
SubmittableExtrinsic {
|
||||
client: self.client.clone(),
|
||||
inner: self
|
||||
.inner
|
||||
.sign_with_address_and_signature(address, signature),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This represents an extrinsic that has been signed and is ready to submit.
|
||||
pub struct SubmittableExtrinsic<T, C> {
|
||||
client: C,
|
||||
encoded: Encoded,
|
||||
marker: std::marker::PhantomData<T>,
|
||||
inner: subxt_core::tx::Transaction<T>,
|
||||
}
|
||||
|
||||
impl<T, C> SubmittableExtrinsic<T, C>
|
||||
@@ -440,25 +344,24 @@ where
|
||||
pub fn from_bytes(client: C, tx_bytes: Vec<u8>) -> Self {
|
||||
Self {
|
||||
client,
|
||||
encoded: Encoded(tx_bytes),
|
||||
marker: std::marker::PhantomData,
|
||||
inner: subxt_core::tx::Transaction::from_bytes(tx_bytes),
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate and return the hash of the extrinsic, based on the configured hasher.
|
||||
pub fn hash(&self) -> T::Hash {
|
||||
T::Hasher::hash_of(&self.encoded)
|
||||
self.inner.hash()
|
||||
}
|
||||
|
||||
/// Returns the SCALE encoded extrinsic bytes.
|
||||
pub fn encoded(&self) -> &[u8] {
|
||||
&self.encoded.0
|
||||
self.inner.encoded()
|
||||
}
|
||||
|
||||
/// Consumes [`SubmittableExtrinsic`] and returns the SCALE encoded
|
||||
/// extrinsic bytes.
|
||||
pub fn into_encoded(self) -> Vec<u8> {
|
||||
self.encoded.0
|
||||
self.inner.into_encoded()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -479,7 +382,7 @@ where
|
||||
let sub = self
|
||||
.client
|
||||
.backend()
|
||||
.submit_transaction(&self.encoded.0)
|
||||
.submit_transaction(self.encoded())
|
||||
.await?;
|
||||
|
||||
Ok(TxProgress::new(sub, self.client.clone(), ext_hash))
|
||||
@@ -495,7 +398,7 @@ where
|
||||
let mut sub = self
|
||||
.client
|
||||
.backend()
|
||||
.submit_transaction(&self.encoded.0)
|
||||
.submit_transaction(self.encoded())
|
||||
.await?;
|
||||
|
||||
// If we get a bad status or error back straight away then error, else return the hash.
|
||||
@@ -543,7 +446,7 @@ where
|
||||
let block_hash = at.into().hash();
|
||||
|
||||
// Approach taken from https://github.com/paritytech/json-rpc-interface-spec/issues/55.
|
||||
let mut params = Vec::with_capacity(8 + self.encoded.0.len() + 8);
|
||||
let mut params = Vec::with_capacity(8 + self.encoded().len() + 8);
|
||||
2u8.encode_to(&mut params);
|
||||
params.extend(self.encoded().iter());
|
||||
block_hash.encode_to(&mut params);
|
||||
|
||||
Reference in New Issue
Block a user