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:
Tadeo Hepperle
2023-08-24 09:50:44 +02:00
committed by GitHub
parent a8dbd9d6d5
commit b413e5e84e
12 changed files with 354 additions and 43 deletions
@@ -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)]
+1 -1
View File
@@ -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;