1#![recursion_limit = "256"]
2use darling::FromDeriveInput;
3use proc_macro::TokenStream;
4use quote::quote;
5use std::convert::TryInto;
6use syn::{parse_macro_input, DataEnum, DataStruct, DeriveInput, Ident};
7
8const MAX_UNION_SELECTOR: u8 = 127;
11
12#[derive(Debug, FromDeriveInput)]
13#[darling(attributes(tree_hash))]
14struct StructOpts {
15 #[darling(default)]
16 enum_behaviour: Option<String>,
17}
18
19const ENUM_TRANSPARENT: &str = "transparent";
20const ENUM_UNION: &str = "union";
21const ENUM_VARIANTS: &[&str] = &[ENUM_TRANSPARENT, ENUM_UNION];
22const NO_ENUM_BEHAVIOUR_ERROR: &str = "enums require an \"enum_behaviour\" attribute, \
23 e.g., #[tree_hash(enum_behaviour = \"transparent\")]";
24
25enum EnumBehaviour {
26 Transparent,
27 Union,
28}
29
30impl EnumBehaviour {
31 pub fn new(s: Option<String>) -> Option<Self> {
32 s.map(|s| match s.as_ref() {
33 ENUM_TRANSPARENT => EnumBehaviour::Transparent,
34 ENUM_UNION => EnumBehaviour::Union,
35 other => panic!(
36 "{} is an invalid enum_behaviour, use either {:?}",
37 other, ENUM_VARIANTS
38 ),
39 })
40 }
41}
42
43fn get_hashable_fields(struct_data: &syn::DataStruct) -> Vec<&syn::Ident> {
49 get_hashable_fields_and_their_caches(struct_data)
50 .into_iter()
51 .map(|(ident, _)| ident)
52 .collect()
53}
54
55fn get_hashable_fields_and_their_caches(
57 struct_data: &syn::DataStruct,
58) -> Vec<(&syn::Ident, syn::Type)> {
59 struct_data
60 .fields
61 .iter()
62 .filter_map(|f| {
63 if should_skip_hashing(f) {
64 None
65 } else {
66 let ident = f
67 .ident
68 .as_ref()
69 .expect("tree_hash_derive only supports named struct fields");
70 Some((ident, f.ty.clone()))
71 }
72 })
73 .collect()
74}
75
76fn should_skip_hashing(field: &syn::Field) -> bool {
80 field.attrs.iter().any(|attr| {
81 attr.path().is_ident("tree_hash") && attr.parse_args::<Ident>().unwrap() == "skip_hashing"
82 })
83}
84
85#[proc_macro_derive(TreeHash, attributes(tree_hash))]
89pub fn tree_hash_derive(input: TokenStream) -> TokenStream {
90 let item = parse_macro_input!(input as DeriveInput);
91 let opts = StructOpts::from_derive_input(&item).unwrap();
92 let enum_opt = EnumBehaviour::new(opts.enum_behaviour);
93
94 match &item.data {
95 syn::Data::Struct(s) => {
96 if enum_opt.is_some() {
97 panic!("enum_behaviour is invalid for structs");
98 }
99 tree_hash_derive_struct(&item, s)
100 }
101 syn::Data::Enum(s) => match enum_opt.expect(NO_ENUM_BEHAVIOUR_ERROR) {
102 EnumBehaviour::Transparent => tree_hash_derive_enum_transparent(&item, s),
103 EnumBehaviour::Union => tree_hash_derive_enum_union(&item, s),
104 },
105 _ => panic!("tree_hash_derive only supports structs and enums."),
106 }
107}
108
109fn tree_hash_derive_struct(item: &DeriveInput, struct_data: &DataStruct) -> TokenStream {
110 let name = &item.ident;
111 let (impl_generics, ty_generics, where_clause) = &item.generics.split_for_impl();
112
113 let idents = get_hashable_fields(struct_data);
114 let num_leaves = idents.len();
115
116 let output = quote! {
117 impl #impl_generics tree_hash::TreeHash for #name #ty_generics #where_clause {
118 fn tree_hash_type() -> tree_hash::TreeHashType {
119 tree_hash::TreeHashType::Container
120 }
121
122 fn tree_hash_packed_encoding(&self) -> tree_hash::PackedEncoding {
123 unreachable!("Struct should never be packed.")
124 }
125
126 fn tree_hash_packing_factor() -> usize {
127 unreachable!("Struct should never be packed.")
128 }
129
130 fn tree_hash_root(&self) -> tree_hash::Hash256 {
131 let mut hasher = tree_hash::MerkleHasher::with_leaves(#num_leaves);
132
133 #(
134 hasher.write(self.#idents.tree_hash_root().as_slice())
135 .expect("tree hash derive should not apply too many leaves");
136 )*
137
138 hasher.finish().expect("tree hash derive should not have a remaining buffer")
139 }
140 }
141 };
142 output.into()
143}
144
145fn tree_hash_derive_enum_transparent(
162 derive_input: &DeriveInput,
163 enum_data: &DataEnum,
164) -> TokenStream {
165 let name = &derive_input.ident;
166 let (impl_generics, ty_generics, where_clause) = &derive_input.generics.split_for_impl();
167
168 let (patterns, type_exprs): (Vec<_>, Vec<_>) = enum_data
169 .variants
170 .iter()
171 .map(|variant| {
172 let variant_name = &variant.ident;
173
174 if variant.fields.len() != 1 {
175 panic!("TreeHash can only be derived for enums with 1 field per variant");
176 }
177
178 let pattern = quote! {
179 #name::#variant_name(ref inner)
180 };
181
182 let ty = &(&variant.fields).into_iter().next().unwrap().ty;
183 let type_expr = quote! {
184 <#ty as tree_hash::TreeHash>::tree_hash_type()
185 };
186 (pattern, type_expr)
187 })
188 .unzip();
189
190 let output = quote! {
191 impl #impl_generics tree_hash::TreeHash for #name #ty_generics #where_clause {
192 fn tree_hash_type() -> tree_hash::TreeHashType {
193 #(
194 assert_eq!(
195 #type_exprs,
196 tree_hash::TreeHashType::Container,
197 "all variants must be of container type"
198 );
199 )*
200 tree_hash::TreeHashType::Container
201 }
202
203 fn tree_hash_packed_encoding(&self) -> tree_hash::PackedEncoding {
204 unreachable!("Enum should never be packed")
205 }
206
207 fn tree_hash_packing_factor() -> usize {
208 unreachable!("Enum should never be packed")
209 }
210
211 fn tree_hash_root(&self) -> tree_hash::Hash256 {
212 match self {
213 #(
214 #patterns => inner.tree_hash_root(),
215 )*
216 }
217 }
218 }
219 };
220 output.into()
221}
222
223fn tree_hash_derive_enum_union(derive_input: &DeriveInput, enum_data: &DataEnum) -> TokenStream {
233 let name = &derive_input.ident;
234 let (impl_generics, ty_generics, where_clause) = &derive_input.generics.split_for_impl();
235
236 let patterns: Vec<_> = enum_data
237 .variants
238 .iter()
239 .map(|variant| {
240 let variant_name = &variant.ident;
241
242 if variant.fields.len() != 1 {
243 panic!("TreeHash can only be derived for enums with 1 field per variant");
244 }
245
246 quote! {
247 #name::#variant_name(ref inner)
248 }
249 })
250 .collect();
251
252 let union_selectors = compute_union_selectors(patterns.len());
253
254 let output = quote! {
255 impl #impl_generics tree_hash::TreeHash for #name #ty_generics #where_clause {
256 fn tree_hash_type() -> tree_hash::TreeHashType {
257 tree_hash::TreeHashType::Container
258 }
259
260 fn tree_hash_packed_encoding(&self) -> tree_hash::PackedEncoding {
261 unreachable!("Enum should never be packed")
262 }
263
264 fn tree_hash_packing_factor() -> usize {
265 unreachable!("Enum should never be packed")
266 }
267
268 fn tree_hash_root(&self) -> tree_hash::Hash256 {
269 match self {
270 #(
271 #patterns => {
272 let root = inner.tree_hash_root();
273 let selector = #union_selectors;
274 tree_hash::mix_in_selector(&root, selector)
275 .expect("derive macro should prevent out-of-bounds selectors")
276 },
277 )*
278 }
279 }
280 }
281 };
282 output.into()
283}
284
285fn compute_union_selectors(num_variants: usize) -> Vec<u8> {
286 let union_selectors = (0..num_variants)
287 .map(|i| {
288 i.try_into()
289 .expect("union selector exceeds u8::max_value, union has too many variants")
290 })
291 .collect::<Vec<u8>>();
292
293 let highest_selector = union_selectors
294 .last()
295 .copied()
296 .expect("0-variant union is not permitted");
297
298 assert!(
299 highest_selector <= MAX_UNION_SELECTOR,
300 "union selector {} exceeds limit of {}, enum has too many variants",
301 highest_selector,
302 MAX_UNION_SELECTOR
303 );
304
305 union_selectors
306}