Skip to content

Commit

Permalink
Improve Verifier Trait: Redeemer type and block number access (#172)
Browse files Browse the repository at this point in the history
Signed-off-by: Matteo Muraca <mmuraca247@gmail.com>
Co-authored-by: Matteo Muraca <mmuraca247@gmail.com>
Co-authored-by: Gorzorg <otrebla-p@hotmail.it>
  • Loading branch information
3 people committed Feb 15, 2024
1 parent ec9de09 commit 1ad2343
Show file tree
Hide file tree
Showing 10 changed files with 547 additions and 307 deletions.
18 changes: 17 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ async-io = "2.0"
async-trait = "0.1.73"
clap = "4.3.0"
color-print = "0.3.4"
convert_case = "0.6.0"
hex-literal = "0.4.1"
jsonrpsee = "0.16.2"
log = "0.4"
Expand Down
Empty file added runtime-expanded.rs
Empty file.
1 change: 1 addition & 0 deletions tuxedo-core/aggregator/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ name = "aggregator"
version = "0.1.0"

[dependencies]
convert_case = { workspace = true }
quote = { workspace = true }
syn = { features = [ "extra-traits", "full" ], workspace = true }

Expand Down
75 changes: 72 additions & 3 deletions tuxedo-core/aggregator/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use convert_case::{Case, Casing};
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, Ident, ItemEnum};
Expand Down Expand Up @@ -73,21 +74,89 @@ pub fn aggregate(_: TokenStream, body: TokenStream) -> TokenStream {
pub fn tuxedo_verifier(_: TokenStream, body: TokenStream) -> TokenStream {
let ast = parse_macro_input!(body as ItemEnum);
let original_code = ast.clone();
let vis = ast.vis;

let outer_type = ast.ident;
let variants = ast.variants.into_iter().map(|v| v.ident);
let variant_type_pairs = ast.variants.iter().map(|variant| {
// Make sure there is only a single field, and if not, give a helpful error
assert!(
variant.fields.len() == 1,
"Each variant must have a single unnamed field"
);
(
variant.ident.clone(),
variant
.fields
.iter()
.next()
.expect("exactly one field per variant")
.ty
.clone(),
)
});
let variants = variant_type_pairs.clone().map(|(v, _t)| v);
let inner_types = variant_type_pairs.map(|(_v, t)| t);

// Set up the name of the new associated type.
let mut redeemer_type_name = outer_type.to_string();
redeemer_type_name.push_str("Redeemer");
let redeemer_type = Ident::new(&redeemer_type_name, outer_type.span());

// TODO there must be a better way to do this, right?
let inner_types2 = inner_types.clone();
let variants2 = variants.clone();
let variants3 = variants.clone();

let as_variants = variants.clone().map(|v| {
let s = format!("as_{}", v);
let s = s.to_case(Case::Snake);
Ident::new(&s, v.span())
});
let as_variants2 = as_variants.clone();

let output = quote! {

// Preserve the original enum, and write the From impls
#[tuxedo_core::aggregate]
#original_code

/// This type is generated by the `#[tuxedo_verifier]` macro.
/// It is a combined redeemer type for the redeemers of each individual verifier.
///
/// This type is accessible downstream as `<OuterVerifier as Verifier>::Redeemer`
#[derive(Debug, Decode)]
#vis enum #redeemer_type {
#(
#variants(<#inner_types as tuxedo_core::Verifier>::Redeemer),
)*
}

// Put a bunch of methods like `.as_variant1()` on the aggregate redeemer type
// These are necessary when unwrapping the onion.
// Might be that we should have a helper macro for this as well
impl #redeemer_type {
#(
pub fn #as_variants(&self) -> Option<&<#inner_types2 as tuxedo_core::Verifier>::Redeemer> {
match self {
Self::#variants2(inner) => Some(inner),
_ => None,
}
}
)*
}

impl tuxedo_core::Verifier for #outer_type {
fn verify(&self, simplified_tx: &[u8], redeemer: &[u8]) -> bool {

type Redeemer = #redeemer_type;

fn verify(&self, simplified_tx: &[u8], block_number: u32, redeemer: &Self::Redeemer) -> bool {
match self {
#(
Self::#variants(inner) => inner.verify(simplified_tx, redeemer),
Self::#variants3(inner) => inner.verify(
simplified_tx,
block_number,
redeemer.#as_variants2().expect("redeemer variant exists because the macro constructed that type.")
),
)*
}
}
Expand Down
27 changes: 19 additions & 8 deletions tuxedo-core/src/executive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,12 @@ use sp_std::{collections::btree_set::BTreeSet, vec::Vec};
/// in the proper generic types.
pub struct Executive<B, V, C>(PhantomData<(B, V, C)>);

impl<B: BlockT<Extrinsic = Transaction<V, C>>, V: Verifier, C: ConstraintChecker<V>>
Executive<B, V, C>
impl<B, V, C> Executive<B, V, C>
where
B: BlockT<Extrinsic = Transaction<V, C>>,
B::Header: HeaderT<Number = u32>, // Tuxedo always uses u32 for block number.
V: Verifier,
C: ConstraintChecker<V>,
{
/// Does pool-style validation of a tuxedo transaction.
/// Does not commit anything to storage.
Expand Down Expand Up @@ -75,10 +79,12 @@ impl<B: BlockT<Extrinsic = Transaction<V, C>>, V: Verifier, C: ConstraintChecker
let mut missing_inputs = Vec::new();
for input in transaction.inputs.iter() {
if let Some(input_utxo) = TransparentUtxoSet::<V>::peek_utxo(&input.output_ref) {
let redeemer = V::Redeemer::decode(&mut &input.redeemer[..])
.map_err(|_| UtxoError::VerifierError)?;
ensure!(
input_utxo
.verifier
.verify(&stripped_encoded, &input.redeemer),
.verify(&stripped_encoded, Self::block_height(), &redeemer),
UtxoError::VerifierError
);
input_utxos.push(input_utxo);
Expand Down Expand Up @@ -475,7 +481,7 @@ impl<B: BlockT<Extrinsic = Transaction<V, C>>, V: Verifier, C: ConstraintChecker
mod tests {
use sp_core::H256;
use sp_io::TestExternalities;
use sp_runtime::transaction_validity::ValidTransactionBuilder;
use sp_runtime::{generic::Header, transaction_validity::ValidTransactionBuilder};

use crate::{
constraint_checker::testing::TestConstraintChecker,
Expand Down Expand Up @@ -619,10 +625,15 @@ mod tests {
ext.insert(output_ref.encode(), output.encode());
}

// Write the pre-header
if let Some(pre_header) = self.pre_header {
ext.insert(HEADER_KEY.to_vec(), pre_header.encode());
}
// Write a pre-header. If none was supplied, create a use a default one.
let pre_header = self.pre_header.unwrap_or(Header {
parent_hash: Default::default(),
number: 0,
state_root: H256::zero(),
extrinsics_root: H256::zero(),
digest: Default::default(),
});
ext.insert(HEADER_KEY.to_vec(), pre_header.encode());

// Write the noted extrinsics
ext.insert(EXTRINSIC_KEY.to_vec(), self.noted_extrinsics.encode());
Expand Down
Loading

0 comments on commit 1ad2343

Please sign in to comment.