Skip to content

Preliminary gindex support #49

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,15 @@ 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"

[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" }
185 changes: 184 additions & 1 deletion src/tree_hash.rs
Original file line number Diff line number Diff line change
@@ -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`.
Expand Down Expand Up @@ -39,3 +44,181 @@ where
}
}
}

impl<T, const I: usize, const N: usize> Resolve<VecIndex<I, N>> for VariableList<T, U<N>>
where
T: TreeHash,
Const<N>: 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::<T>(N);

let pos = get_vector_item_position::<T>(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<u64, U10>,
}

// 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<FieldX3> for Nested3 {
type Output = Nested2;

fn gindex(parent_index: usize) -> usize {
parent_index * <FieldX3 as Field>::NUM_FIELDS.next_power_of_two()
+ <FieldX3 as Field>::INDEX
}
}

impl Resolve<FieldY3> for Nested3 {
type Output = Nested1;

fn gindex(parent_index: usize) -> usize {
parent_index * <FieldY3 as Field>::NUM_FIELDS.next_power_of_two()
+ <FieldY3 as Field>::INDEX
}
}

impl Resolve<FieldX2> for Nested2 {
type Output = Nested1;

fn gindex(parent_index: usize) -> usize {
parent_index * <FieldX2 as Field>::NUM_FIELDS.next_power_of_two()
+ <FieldX2 as Field>::INDEX
}
}

impl Resolve<FieldY2> for Nested2 {
type Output = Nested1;

fn gindex(parent_index: usize) -> usize {
parent_index * <FieldY2 as Field>::NUM_FIELDS.next_power_of_two()
+ <FieldY2 as Field>::INDEX
}
}

impl Resolve<FieldX1> for Nested1 {
type Output = u64;

fn gindex(parent_index: usize) -> usize {
parent_index * <FieldX1 as Field>::NUM_FIELDS.next_power_of_two()
+ <FieldX1 as Field>::INDEX
}
}

impl Resolve<FieldY1> for Nested1 {
type Output = VariableList<u64, U10>;

fn gindex(parent_index: usize) -> usize {
parent_index * <FieldY1 as Field>::NUM_FIELDS.next_power_of_two()
+ <FieldY1 as Field>::INDEX
}
}

// x3.x2.x1
type FieldX3X2X1 = Path<FieldX3, Path<FieldX2, FieldX1>>;

// x3.x2.x1
type FieldX3X2Y1 = Path<FieldX3, Path<FieldX2, FieldY1>>;

// x3.y2.y1.5
type FieldX3Y2Y1I5 = Path<FieldX3, Path<FieldY2, Path<FieldY1, VecIndex<5, 10>>>>;

// 0.x3.y2.y1.5
type FieldI0X3Y2Y1I5 =
Path<VecIndex<0, 5>, Path<FieldX3, Path<FieldY2, Path<FieldY1, VecIndex<5, 10>>>>>;

// This evaluates to u64 at compile-time.
type TypeOfFieldX3X2X1 = <Nested3 as Resolve<FieldX3X2X1>>::Output;

#[test]
fn gindex_basics() {
// This works but just shows compile-time field resolution.
let x: TypeOfFieldX3X2X1 = 0u64;

// Gindex computation.
assert_eq!(<Nested3 as Resolve<FieldX3X2X1>>::gindex(1), 8);
assert_eq!(<Nested3 as Resolve<FieldX3X2Y1>>::gindex(1), 9);

// FIXME: Not sure if these values are correct
assert_eq!(<Nested3 as Resolve<FieldX3Y2Y1I5>>::gindex(1), 89);
assert_eq!(
<VariableList<Nested3, U5> as Resolve<FieldI0X3Y2Y1I5>>::gindex(1),
1049
);
}
}
Loading