diff --git a/tuxedo-core/aggregator/src/lib.rs b/tuxedo-core/aggregator/src/lib.rs index 4a167dbb..86d82df0 100644 --- a/tuxedo-core/aggregator/src/lib.rs +++ b/tuxedo-core/aggregator/src/lib.rs @@ -73,9 +73,39 @@ 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 variants4 = variants.clone(); let output = quote! { @@ -83,11 +113,42 @@ pub fn tuxedo_verifier(_: TokenStream, body: TokenStream) -> TokenStream { #[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 `::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 { + #( + //TODO I would rather the function be called as_variant2, + // but my macro n00b skills can't figure it out. + #[allow(non_snake_case)] + pub fn #variants2(&self) -> Option<&<#inner_types2 as tuxedo_core::Verifier>::Redeemer> { + match self { + Self::#variants3(inner) => Some(inner), + _ => None, + } + } + )* + } + impl tuxedo_core::Verifier for #outer_type { - fn verify(&self, simplified_tx: &[u8], block_number: u32, 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, block_number, redeemer), + Self::#variants4(inner) => inner.verify(simplified_tx, block_number, redeemer.#variants4().expect("redeemer variant exists because the macro constructed that type.")), )* } }