diff --git a/Cargo.toml b/Cargo.toml index 4099d4a..34e927c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ ethereum_serde_utils = "0.8.0" ethereum_ssz = "0.9.0" serde = "1.0.0" serde_derive = "1.0.0" -typenum = "1.12.0" +typenum = { version = "1.12.0", features = ["const-generics"] } smallvec = "1.8.0" arbitrary = { version = "1.0", features = ["derive"], optional = true } itertools = "0.13.0" @@ -24,3 +24,7 @@ itertools = "0.13.0" [dev-dependencies] serde_json = "1.0.0" tree_hash_derive = "0.10.0" + +[patch.crates-io] +tree_hash = { path = "../tree_hash/tree_hash" } +tree_hash_derive = { path = "../tree_hash/tree_hash_derive" } diff --git a/src/tree_hash.rs b/src/tree_hash.rs index 675d0ea..50e5997 100644 --- a/src/tree_hash.rs +++ b/src/tree_hash.rs @@ -1,5 +1,10 @@ +use crate::VariableList; +use tree_hash::prototype::{get_vector_item_position, vector_chunk_count, Resolve, VecIndex}; use tree_hash::{Hash256, MerkleHasher, TreeHash, TreeHashType}; -use typenum::Unsigned; +use typenum::{ + generic_const_mappings::{Const, ToUInt, U}, + Unsigned, +}; /// A helper function providing common functionality between the `TreeHash` implementations for /// `FixedVector` and `VariableList`. @@ -39,3 +44,181 @@ where } } } + +impl Resolve> for VariableList> +where + T: TreeHash, + Const: ToUInt, +{ + type Output = T; + + fn gindex(parent_index: usize) -> usize { + // Base index is 2 due to length mixin. + let base_index = 2; + + // Chunk count takes into account packing of leaves. + let chunk_count = vector_chunk_count::(N); + + let pos = get_vector_item_position::(I); + + // Gindex of Nth element of this vector. + parent_index * base_index * chunk_count.next_power_of_two() + pos + } +} + +#[cfg(test)] +mod test { + use super::*; + use tree_hash::prototype::{Field, Path, Resolve, VecIndex}; + use tree_hash_derive::TreeHash; + use typenum::{U10, U5}; + + // Some example structs. + #[derive(TreeHash)] + struct Nested3 { + x3: Nested2, + y3: Nested1, + } + + #[derive(TreeHash)] + struct Nested2 { + x2: Nested1, + y2: Nested1, + } + + #[derive(TreeHash)] + struct Nested1 { + x1: u64, + y1: VariableList, + } + + // Fields of Nested3 (these would be generated). + struct FieldX3; + struct FieldY3; + + impl Field for FieldX3 { + const NUM_FIELDS: usize = 2; + const INDEX: usize = 0; + } + + impl Field for FieldY3 { + const NUM_FIELDS: usize = 2; + const INDEX: usize = 1; + } + + // Fields of Nested2 (generated). + struct FieldX2; + struct FieldY2; + + impl Field for FieldX2 { + const NUM_FIELDS: usize = 2; + const INDEX: usize = 0; + } + + impl Field for FieldY2 { + const NUM_FIELDS: usize = 2; + const INDEX: usize = 1; + } + + // Fields of Nested1 (generated). + struct FieldX1; + struct FieldY1; + + impl Field for FieldX1 { + const NUM_FIELDS: usize = 2; + const INDEX: usize = 0; + } + + impl Field for FieldY1 { + const NUM_FIELDS: usize = 2; + const INDEX: usize = 1; + } + + // Implementations of Resolve (generated). + impl Resolve for Nested3 { + type Output = Nested2; + + fn gindex(parent_index: usize) -> usize { + parent_index * ::NUM_FIELDS.next_power_of_two() + + ::INDEX + } + } + + impl Resolve for Nested3 { + type Output = Nested1; + + fn gindex(parent_index: usize) -> usize { + parent_index * ::NUM_FIELDS.next_power_of_two() + + ::INDEX + } + } + + impl Resolve for Nested2 { + type Output = Nested1; + + fn gindex(parent_index: usize) -> usize { + parent_index * ::NUM_FIELDS.next_power_of_two() + + ::INDEX + } + } + + impl Resolve for Nested2 { + type Output = Nested1; + + fn gindex(parent_index: usize) -> usize { + parent_index * ::NUM_FIELDS.next_power_of_two() + + ::INDEX + } + } + + impl Resolve for Nested1 { + type Output = u64; + + fn gindex(parent_index: usize) -> usize { + parent_index * ::NUM_FIELDS.next_power_of_two() + + ::INDEX + } + } + + impl Resolve for Nested1 { + type Output = VariableList; + + fn gindex(parent_index: usize) -> usize { + parent_index * ::NUM_FIELDS.next_power_of_two() + + ::INDEX + } + } + + // x3.x2.x1 + type FieldX3X2X1 = Path>; + + // x3.x2.x1 + type FieldX3X2Y1 = Path>; + + // x3.y2.y1.5 + type FieldX3Y2Y1I5 = Path>>>; + + // 0.x3.y2.y1.5 + type FieldI0X3Y2Y1I5 = + Path, Path>>>>; + + // This evaluates to u64 at compile-time. + type TypeOfFieldX3X2X1 = >::Output; + + #[test] + fn gindex_basics() { + // This works but just shows compile-time field resolution. + let x: TypeOfFieldX3X2X1 = 0u64; + + // Gindex computation. + assert_eq!(>::gindex(1), 8); + assert_eq!(>::gindex(1), 9); + + // FIXME: Not sure if these values are correct + assert_eq!(>::gindex(1), 89); + assert_eq!( + as Resolve>::gindex(1), + 1049 + ); + } +}