mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-28 15:37:56 +00:00
Codegen for custom values in metadata (#1117)
* work in progress * add custom types access * nit * custom values client * adjust light client * adjust doc comments * adjust book for custom values in code gen * format and check docs * work in progress * add custom types access * nit * custom values client * adjust light client * codegen and validation * adjust docs * use ignore in docs in book * change iter implementation * use validation hash and other codegen changes * add ui test for custom values codegen * allow 'latest' metadata to be returned from the fallback code (#1127) * nits * fix validation check * fix comments * nits --------- Co-authored-by: James Wilson <james@jsdw.me>
This commit is contained in:
@@ -1,15 +1,23 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::dynamic::DecodedValueThunk;
|
||||
use crate::metadata::DecodeWithMetadata;
|
||||
|
||||
/// This represents the address of a custom value in in the metadata.
|
||||
/// Anything, that implements the [CustomValueAddress] trait can be used, to fetch
|
||||
/// custom values from the metadata.
|
||||
/// The trait is implemented by [str] for dynamic loopup and [StaticAddress] for static queries.
|
||||
pub trait CustomValueAddress {
|
||||
/// The type of the custom value.
|
||||
type Target: DecodeWithMetadata;
|
||||
|
||||
/// the name (key) by which the custom value can be accessed in the metadata.
|
||||
fn name(&self) -> &str;
|
||||
|
||||
/// An optional hash which, if present, can be checked against node metadata.
|
||||
fn validation_hash(&self) -> Option<[u8; 32]> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl CustomValueAddress for str {
|
||||
@@ -19,3 +27,43 @@ impl CustomValueAddress for str {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// A static address to a custom value.
|
||||
pub struct StaticAddress<R> {
|
||||
name: &'static str,
|
||||
hash: Option<[u8; 32]>,
|
||||
phantom: PhantomData<R>,
|
||||
}
|
||||
|
||||
impl<R> StaticAddress<R> {
|
||||
#[doc(hidden)]
|
||||
/// Creates a new StaticAddress.
|
||||
pub fn new_static(name: &'static str, hash: [u8; 32]) -> Self {
|
||||
StaticAddress {
|
||||
name,
|
||||
hash: Some(hash),
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Do not validate this custom value prior to accessing it.
|
||||
pub fn unvalidated(self) -> Self {
|
||||
Self {
|
||||
name: self.name,
|
||||
hash: None,
|
||||
phantom: self.phantom,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: DecodeWithMetadata> CustomValueAddress for StaticAddress<R> {
|
||||
type Target = R;
|
||||
|
||||
fn name(&self) -> &str {
|
||||
self.name
|
||||
}
|
||||
|
||||
fn validation_hash(&self) -> Option<[u8; 32]> {
|
||||
self.hash
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,9 +30,13 @@ impl<T: Config, Client: OfflineClientT<T>> CustomValuesClient<T, Client> {
|
||||
&self,
|
||||
address: &Address,
|
||||
) -> Result<Address::Target, Error> {
|
||||
// 1. Validate custom value shape if hash given:
|
||||
self.validate(address)?;
|
||||
|
||||
// 2. Attempt to decode custom value:
|
||||
let metadata = self.client.metadata();
|
||||
let custom_value = metadata
|
||||
.custom()
|
||||
let custom = metadata.custom();
|
||||
let custom_value = custom
|
||||
.get(address.name())
|
||||
.ok_or_else(|| MetadataError::CustomValueNameNotFound(address.name().to_string()))?;
|
||||
|
||||
@@ -43,6 +47,30 @@ impl<T: Config, Client: OfflineClientT<T>> CustomValuesClient<T, Client> {
|
||||
)?;
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
let metadata = self.client.metadata();
|
||||
if let Some(actual_hash) = address.validation_hash() {
|
||||
let custom = metadata.custom();
|
||||
let custom_value = custom
|
||||
.get(address.name())
|
||||
.ok_or_else(|| MetadataError::CustomValueNameNotFound(address.name().into()))?;
|
||||
let expected_hash = custom_value.hash();
|
||||
if actual_hash != expected_hash {
|
||||
return Err(MetadataError::IncompatibleCodegen.into());
|
||||
}
|
||||
}
|
||||
if metadata.custom().get(address.name()).is_none() {
|
||||
return Err(MetadataError::IncompatibleCodegen.into());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -7,5 +7,5 @@
|
||||
mod custom_value_address;
|
||||
mod custom_values_client;
|
||||
|
||||
pub use custom_value_address::CustomValueAddress;
|
||||
pub use custom_value_address::{CustomValueAddress, StaticAddress};
|
||||
pub use custom_values_client::CustomValuesClient;
|
||||
|
||||
Reference in New Issue
Block a user