Merge remote-tracking branch 'origin/master' into lexnv/light_client_support

This commit is contained in:
Alexandru Vasile
2023-05-31 17:57:04 +03:00
22 changed files with 2791 additions and 79 deletions
+2 -5
View File
@@ -366,10 +366,7 @@ where
/// Fetch the metadata for this extrinsic.
pub fn extrinsic_metadata(&self) -> Result<ExtrinsicMetadataDetails, Error> {
let pallet = self
.metadata
.pallet_by_index(self.pallet_index())
.ok_or_else(|| MetadataError::PalletIndexNotFound(self.pallet_index()))?;
let pallet = self.metadata.pallet_by_index_err(self.pallet_index())?;
let variant = pallet
.call_variant_by_index(self.variant_index())
.ok_or_else(|| MetadataError::VariantIndexNotFound(self.variant_index()))?;
@@ -563,7 +560,7 @@ impl<T: Config> ExtrinsicEvents<T> {
///
/// This works in the same way that [`events::Events::iter()`] does, with the
/// exception that it filters out events not related to the submitted extrinsic.
pub fn iter(&self) -> impl Iterator<Item = Result<events::EventDetails, Error>> + '_ {
pub fn iter(&self) -> impl Iterator<Item = Result<events::EventDetails<T>, Error>> + '_ {
self.events.iter().filter(|ev| {
ev.as_ref()
.map(|ev| ev.phase() == events::Phase::ApplyExtrinsic(self.idx))
+3 -6
View File
@@ -39,8 +39,7 @@ impl<T: Config, Client: OfflineClientT<T>> ConstantsClient<T, Client> {
let expected_hash = self
.client
.metadata()
.pallet_by_name(address.pallet_name())
.ok_or_else(|| MetadataError::PalletNameNotFound(address.pallet_name().to_owned()))?
.pallet_by_name_err(address.pallet_name())?
.constant_hash(address.constant_name())
.ok_or_else(|| {
MetadataError::ConstantNameNotFound(address.constant_name().to_owned())
@@ -65,10 +64,8 @@ impl<T: Config, Client: OfflineClientT<T>> ConstantsClient<T, Client> {
self.validate(address)?;
// 2. Attempt to decode the constant into the type given:
let pallet = metadata
.pallet_by_name(address.pallet_name())
.ok_or_else(|| MetadataError::PalletNameNotFound(address.pallet_name().to_owned()))?;
let constant = pallet
let constant = metadata
.pallet_by_name_err(address.pallet_name())?
.constant_by_name(address.constant_name())
.ok_or_else(|| {
MetadataError::ConstantNameNotFound(address.constant_name().to_owned())
+1 -5
View File
@@ -154,11 +154,7 @@ impl std::fmt::Display for ModuleError {
impl ModuleError {
/// Return more details about this error.
pub fn details(&self) -> Result<ModuleErrorDetails, MetadataError> {
let pallet = self
.metadata
.pallet_by_index(self.raw.pallet_index)
.ok_or(MetadataError::PalletIndexNotFound(self.raw.pallet_index))?;
let pallet = self.metadata.pallet_by_index_err(self.raw.pallet_index)?;
let variant = pallet
.error_variant_by_index(self.raw.error[0])
.ok_or_else(|| MetadataError::VariantIndexNotFound(self.raw.error[0]))?;
+64 -24
View File
@@ -121,7 +121,7 @@ impl<T: Config> Events<T> {
// use of it with our `FilterEvents` stuff.
pub fn iter(
&self,
) -> impl Iterator<Item = Result<EventDetails, Error>> + Send + Sync + 'static {
) -> impl Iterator<Item = Result<EventDetails<T>, Error>> + Send + Sync + 'static {
// The event bytes ignoring the compact encoded length on the front:
let event_bytes = self.event_bytes.clone();
let metadata = self.metadata.clone();
@@ -133,12 +133,7 @@ impl<T: Config> Events<T> {
if event_bytes.len() <= pos || num_events == index {
None
} else {
match EventDetails::decode_from::<T>(
metadata.clone(),
event_bytes.clone(),
pos,
index,
) {
match EventDetails::decode_from(metadata.clone(), event_bytes.clone(), pos, index) {
Ok(event_details) => {
// Skip over decoded bytes in next iteration:
pos += event_details.bytes().len();
@@ -189,7 +184,7 @@ impl<T: Config> Events<T> {
/// The event details.
#[derive(Debug, Clone)]
pub struct EventDetails {
pub struct EventDetails<T: Config> {
phase: Phase,
/// The index of the event in the list of events in a given block.
index: u32,
@@ -205,16 +200,17 @@ pub struct EventDetails {
// end of everything (fields + topics)
end_idx: usize,
metadata: Metadata,
topics: Vec<T::Hash>,
}
impl EventDetails {
impl<T: Config> EventDetails<T> {
// Attempt to dynamically decode a single event from our events input.
fn decode_from<T: Config>(
fn decode_from(
metadata: Metadata,
all_bytes: Arc<[u8]>,
start_idx: usize,
index: u32,
) -> Result<EventDetails, Error> {
) -> Result<EventDetails<T>, Error> {
let input = &mut &all_bytes[start_idx..];
let phase = Phase::decode(input)?;
@@ -227,9 +223,7 @@ impl EventDetails {
let event_fields_start_idx = all_bytes.len() - input.len();
// Get metadata for the event:
let event_pallet = metadata
.pallet_by_index(pallet_index)
.ok_or(MetadataError::PalletIndexNotFound(pallet_index))?;
let event_pallet = metadata.pallet_by_index_err(pallet_index)?;
let event_variant = event_pallet
.event_variant_by_index(variant_index)
.ok_or(MetadataError::VariantIndexNotFound(variant_index))?;
@@ -254,9 +248,8 @@ impl EventDetails {
// the end of the field bytes.
let event_fields_end_idx = all_bytes.len() - input.len();
// topics come after the event data in EventRecord. They aren't used for
// anything at the moment, so just decode and throw them away.
let _topics = Vec::<T::Hash>::decode(input)?;
// topics come after the event data in EventRecord.
let topics = Vec::<T::Hash>::decode(input)?;
// what bytes did we skip over in total, including topics.
let end_idx = all_bytes.len() - input.len();
@@ -271,6 +264,7 @@ impl EventDetails {
end_idx,
all_bytes,
metadata,
topics,
})
}
@@ -387,6 +381,11 @@ impl EventDetails {
&self.metadata,
)
}
/// Return the topics associated with this event.
pub fn topics(&self) -> &[T::Hash] {
&self.topics
}
}
/// Details for the given event plucked from the metadata.
@@ -469,14 +468,21 @@ pub(crate) mod test_utils {
topics: Vec<<SubstrateConfig as Config>::Hash>,
}
impl<E: Encode> EventRecord<E> {
/// Create a new event record with the given phase, event, and topics.
pub fn new(phase: Phase, event: E, topics: Vec<<SubstrateConfig as Config>::Hash>) -> Self {
Self {
phase,
event: AllEvents::Test(event),
topics,
}
}
}
/// Build an EventRecord, which encoded events in the format expected
/// to be handed back from storage queries to System.Events.
pub fn event_record<E: Encode>(phase: Phase, event: E) -> EventRecord<E> {
EventRecord {
phase,
event: AllEvents::Test(event),
topics: vec![],
}
EventRecord::new(phase, event, vec![])
}
/// Build fake metadata consisting of a single pallet that knows
@@ -566,10 +572,12 @@ pub(crate) mod test_utils {
#[cfg(test)]
mod tests {
use super::{
test_utils::{event_record, events, events_raw, AllEvents},
test_utils::{event_record, events, events_raw, AllEvents, EventRecord},
*,
};
use crate::SubstrateConfig;
use codec::Encode;
use primitive_types::H256;
use scale_info::TypeInfo;
use scale_value::Value;
@@ -598,7 +606,7 @@ mod tests {
// Just for convenience, pass in the metadata type constructed
// by the `metadata` function above to simplify caller code.
metadata: &Metadata,
actual: EventDetails,
actual: EventDetails<SubstrateConfig>,
expected: TestRawEventDetails,
) {
let types = &metadata.types();
@@ -952,4 +960,36 @@ mod tests {
);
assert!(event_details.next().is_none());
}
#[test]
fn topics() {
#[derive(Clone, Debug, PartialEq, Decode, Encode, TypeInfo, scale_decode::DecodeAsType)]
enum Event {
A(u8, bool, Vec<String>),
}
// Create fake metadata that knows about our single event, above:
let metadata = metadata::<Event>();
// Encode our events in the format we expect back from a node, and
// construct an Events object to iterate them:
let event = Event::A(1, true, vec!["Hi".into()]);
let topics = vec![H256::from_low_u64_le(123), H256::from_low_u64_le(456)];
let events = events::<Event>(
metadata,
vec![EventRecord::new(
Phase::ApplyExtrinsic(123),
event,
topics.clone(),
)],
);
let ev = events
.iter()
.next()
.expect("one event expected")
.expect("event should be extracted OK");
assert_eq!(topics, ev.topics());
}
}
+28
View File
@@ -2,6 +2,7 @@
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
use crate::error::MetadataError;
use std::sync::Arc;
/// A cheaply clone-able representation of the runtime metadata received from a node.
@@ -23,6 +24,33 @@ impl Metadata {
inner: Arc::new(md),
}
}
/// Identical to `metadata.pallet_by_name()`, but returns an error if the pallet is not found.
pub fn pallet_by_name_err(
&self,
name: &str,
) -> Result<subxt_metadata::PalletMetadata, MetadataError> {
self.pallet_by_name(name)
.ok_or_else(|| MetadataError::PalletNameNotFound(name.to_owned()))
}
/// Identical to `metadata.pallet_by_index()`, but returns an error if the pallet is not found.
pub fn pallet_by_index_err(
&self,
index: u8,
) -> Result<subxt_metadata::PalletMetadata, MetadataError> {
self.pallet_by_index(index)
.ok_or(MetadataError::PalletIndexNotFound(index))
}
/// Identical to `metadata.runtime_api_trait_by_name()`, but returns an error if the trait is not found.
pub fn runtime_api_trait_by_name_err(
&self,
name: &str,
) -> Result<subxt_metadata::RuntimeApiMetadata, MetadataError> {
self.runtime_api_trait_by_name(name)
.ok_or_else(|| MetadataError::RuntimeTraitNotFound(name.to_owned()))
}
}
impl From<subxt_metadata::Metadata> for Metadata {
+2 -4
View File
@@ -88,10 +88,8 @@ impl<ArgsData: EncodeAsFields, ReturnTy: DecodeWithMetadata> RuntimeApiPayload
}
fn encode_args_to(&self, metadata: &Metadata, out: &mut Vec<u8>) -> Result<(), Error> {
let api_trait = metadata
.runtime_api_trait_by_name(&self.trait_name)
.ok_or_else(|| MetadataError::RuntimeTraitNotFound((*self.trait_name).to_owned()))?;
let api_method = api_trait
let api_method = metadata
.runtime_api_trait_by_name_err(&self.trait_name)?
.method_by_name(&self.method_name)
.ok_or_else(|| MetadataError::RuntimeMethodNotFound((*self.method_name).to_owned()))?;
+1 -5
View File
@@ -70,11 +70,7 @@ where
async move {
let metadata = client.metadata();
let api_trait = metadata
.runtime_api_trait_by_name(payload.trait_name())
.ok_or_else(|| {
MetadataError::RuntimeTraitNotFound(payload.trait_name().to_owned())
})?;
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(|| {
+1 -3
View File
@@ -138,9 +138,7 @@ where
}
fn append_entry_bytes(&self, metadata: &Metadata, bytes: &mut Vec<u8>) -> Result<(), Error> {
let pallet = metadata
.pallet_by_name(self.pallet_name())
.ok_or_else(|| MetadataError::PalletNameNotFound(self.pallet_name().to_owned()))?;
let pallet = metadata.pallet_by_name_err(self.pallet_name())?;
let storage = pallet
.storage()
.ok_or_else(|| MetadataError::StorageNotFoundInPallet(self.pallet_name().to_owned()))?;
+2 -4
View File
@@ -9,7 +9,7 @@ use super::{
use crate::{
client::{OfflineClientT, OnlineClientT},
error::{Error, MetadataError},
error::Error,
Config,
};
use derivative::Derivative;
@@ -44,9 +44,7 @@ where
/// 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(address.pallet_name())
.ok_or_else(|| MetadataError::PalletNameNotFound(address.pallet_name().to_owned()))?;
let pallet_metadata = metadata.pallet_by_name_err(address.pallet_name())?;
validate_storage_address(address, pallet_metadata)
}
+1 -3
View File
@@ -321,9 +321,7 @@ fn lookup_entry_details<'a>(
entry_name: &str,
metadata: &'a Metadata,
) -> Result<(PalletMetadata<'a>, &'a StorageEntryMetadata), Error> {
let pallet_metadata = metadata
.pallet_by_name(pallet_name)
.ok_or_else(|| MetadataError::PalletNameNotFound(pallet_name.to_owned()))?;
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()))?;
+3 -4
View File
@@ -50,8 +50,7 @@ impl<T: Config, C: OfflineClientT<T>> TxClient<T, C> {
let expected_hash = self
.client
.metadata()
.pallet_by_name(details.pallet_name)
.ok_or_else(|| MetadataError::PalletNameNotFound(details.pallet_name.to_owned()))?
.pallet_by_name_err(details.pallet_name)?
.call_hash(details.call_name)
.ok_or_else(|| MetadataError::CallNameNotFound(details.call_name.to_owned()))?;
@@ -358,10 +357,10 @@ where
/// An address, and something representing a signature that can be SCALE encoded, are both
/// needed in order to construct it. If you have a `Signer` to hand, you can use
/// [`PartialExtrinsic::sign()`] instead.
pub fn sign_with_address_and_signature<S: Encode>(
pub fn sign_with_address_and_signature(
&self,
address: &T::Address,
signature: &S,
signature: &T::Signature,
) -> SubmittableExtrinsic<T, C> {
// Encode the extrinsic (into the format expected by protocol version 4)
let extrinsic = {
+1 -3
View File
@@ -141,9 +141,7 @@ impl Payload<Composite<()>> {
impl<CallData: EncodeAsFields> TxPayload for Payload<CallData> {
fn encode_call_data_to(&self, metadata: &Metadata, out: &mut Vec<u8>) -> Result<(), Error> {
let pallet = metadata
.pallet_by_name(&self.pallet_name)
.ok_or_else(|| MetadataError::PalletNameNotFound((*self.pallet_name).to_owned()))?;
let pallet = metadata.pallet_by_name_err(&self.pallet_name)?;
let call = pallet
.call_variant_by_name(&self.call_name)
.ok_or_else(|| MetadataError::CallNameNotFound((*self.call_name).to_owned()))?;