|
| 1 | +use convert_case::{Case, Casing}; |
1 | 2 | use proc_macro::TokenStream;
|
2 | 3 | use quote::quote;
|
3 | 4 | use syn::{parse_macro_input, Ident, ItemEnum};
|
@@ -73,21 +74,89 @@ pub fn aggregate(_: TokenStream, body: TokenStream) -> TokenStream {
|
73 | 74 | pub fn tuxedo_verifier(_: TokenStream, body: TokenStream) -> TokenStream {
|
74 | 75 | let ast = parse_macro_input!(body as ItemEnum);
|
75 | 76 | let original_code = ast.clone();
|
| 77 | + let vis = ast.vis; |
76 | 78 |
|
77 | 79 | let outer_type = ast.ident;
|
78 |
| - let variants = ast.variants.into_iter().map(|v| v.ident); |
| 80 | + let variant_type_pairs = ast.variants.iter().map(|variant| { |
| 81 | + // Make sure there is only a single field, and if not, give a helpful error |
| 82 | + assert!( |
| 83 | + variant.fields.len() == 1, |
| 84 | + "Each variant must have a single unnamed field" |
| 85 | + ); |
| 86 | + ( |
| 87 | + variant.ident.clone(), |
| 88 | + variant |
| 89 | + .fields |
| 90 | + .iter() |
| 91 | + .next() |
| 92 | + .expect("exactly one field per variant") |
| 93 | + .ty |
| 94 | + .clone(), |
| 95 | + ) |
| 96 | + }); |
| 97 | + let variants = variant_type_pairs.clone().map(|(v, _t)| v); |
| 98 | + let inner_types = variant_type_pairs.map(|(_v, t)| t); |
| 99 | + |
| 100 | + // Set up the name of the new associated type. |
| 101 | + let mut redeemer_type_name = outer_type.to_string(); |
| 102 | + redeemer_type_name.push_str("Redeemer"); |
| 103 | + let redeemer_type = Ident::new(&redeemer_type_name, outer_type.span()); |
| 104 | + |
| 105 | + // TODO there must be a better way to do this, right? |
| 106 | + let inner_types2 = inner_types.clone(); |
| 107 | + let variants2 = variants.clone(); |
| 108 | + let variants3 = variants.clone(); |
| 109 | + |
| 110 | + let as_variants = variants.clone().map(|v| { |
| 111 | + let s = format!("as_{}", v); |
| 112 | + let s = s.to_case(Case::Snake); |
| 113 | + Ident::new(&s, v.span()) |
| 114 | + }); |
| 115 | + let as_variants2 = as_variants.clone(); |
79 | 116 |
|
80 | 117 | let output = quote! {
|
81 | 118 |
|
82 | 119 | // Preserve the original enum, and write the From impls
|
83 | 120 | #[tuxedo_core::aggregate]
|
84 | 121 | #original_code
|
85 | 122 |
|
| 123 | + /// This type is generated by the `#[tuxedo_verifier]` macro. |
| 124 | + /// It is a combined redeemer type for the redeemers of each individual verifier. |
| 125 | + /// |
| 126 | + /// This type is accessible downstream as `<OuterVerifier as Verifier>::Redeemer` |
| 127 | + #[derive(Debug, Decode)] |
| 128 | + #vis enum #redeemer_type { |
| 129 | + #( |
| 130 | + #variants(<#inner_types as tuxedo_core::Verifier>::Redeemer), |
| 131 | + )* |
| 132 | + } |
| 133 | + |
| 134 | + // Put a bunch of methods like `.as_variant1()` on the aggregate redeemer type |
| 135 | + // These are necessary when unwrapping the onion. |
| 136 | + // Might be that we should have a helper macro for this as well |
| 137 | + impl #redeemer_type { |
| 138 | + #( |
| 139 | + pub fn #as_variants(&self) -> Option<&<#inner_types2 as tuxedo_core::Verifier>::Redeemer> { |
| 140 | + match self { |
| 141 | + Self::#variants2(inner) => Some(inner), |
| 142 | + _ => None, |
| 143 | + } |
| 144 | + } |
| 145 | + )* |
| 146 | + } |
| 147 | + |
86 | 148 | impl tuxedo_core::Verifier for #outer_type {
|
87 |
| - fn verify(&self, simplified_tx: &[u8], redeemer: &[u8]) -> bool { |
| 149 | + |
| 150 | + type Redeemer = #redeemer_type; |
| 151 | + |
| 152 | + fn verify(&self, simplified_tx: &[u8], block_number: u32, redeemer: &Self::Redeemer) -> bool { |
88 | 153 | match self {
|
89 | 154 | #(
|
90 |
| - Self::#variants(inner) => inner.verify(simplified_tx, redeemer), |
| 155 | + Self::#variants3(inner) => inner.verify( |
| 156 | + simplified_tx, |
| 157 | + block_number, |
| 158 | + redeemer.#as_variants2().expect("redeemer variant exists because the macro constructed that type.") |
| 159 | + ), |
91 | 160 | )*
|
92 | 161 | }
|
93 | 162 | }
|
|
0 commit comments