-
Notifications
You must be signed in to change notification settings - Fork 122
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
Refactor MT & Generic Implementation #120
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just commenting on the interface, haven't looked at the implementation yet.
One thing I think is missing, that we use extensively in wallet/EsQS/validator interactions, is the ability to extract a commitment and frontier from a MerkleTree
, and then later rebuild a MerkleTree
from the commitment/frontier. The actual commitment/frontier combo can be opaque (e.g. an associated type), the important thing is that there is some commitment operation which gives you something concise which you can then use to reinstantiate a MerkleTree
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good work to kickstart more discussion and refactoring work. 👍
One thing I struggle with, is the unclear abstraction of our trait MT
, should we incline towards a Set accumulator or a Vector accumulator?
- API like
append()
orpush()
is more on the "vector accumulator" side, whereas (unordered)insert
is leaning towards "set accumulator" - Our Interval Merkle Tree is insertion order dependent, thus should more behave like a Vector accumulator. (quite honestly, so is our current merkle_tree impl as an append-only MT)
primitives/src/merkle_tree/mod.rs
Outdated
mod merkle_tree_impl; | ||
mod merkle_tree_traits; | ||
|
||
pub use merkle_tree_traits::*; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
imo,
- bring trait definitions up, directly in this
merkle_tree/mod.rs
(as we did forpcs/mod.rs
) - rename
trait MerkleTreeScheme
orMerkleTreeTrait
? - the actual implementation can be in files such as:
standard_mt.rs
append_only_mt.rs
or maybeforgettable_mt.rs
with struct namestruct MerkleTree
/// Merkle tree element type | ||
type ElementType: ElementType<F>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what about type Element: Default + ... + AsRef<[F]>;
and remove ElementType
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
again,
- inline this and also
IndexType
- add a
trait ToFields
orAsFieldSlice
or sth to capture the extra interface requirement - rename to
type Element
, similarlytype Index
pub trait Hasher<F: Field> { | ||
/// Digest a list of values | ||
fn digest(data: &[F]) -> F; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
some thoughts (debatable):
- if we expect a stateless hash function (i.e. no
&self
or&mut self
as input), then we can completely remove this trait, and just accepttype Hash: FnOnce(input: &GenericArray<F, Self::LeafArity>) -> F
- naming it
trait Hasher
will potentially collide with std/libcore's Hasher, also, Hasher should be a trait that ourRescueHash
should implement. - a better name could derive from
trait Digest
and borrow some API design there.
my current thinking is in favor of option 1.
/// Exsistential proof | ||
type Proof; | ||
/// Batch proof | ||
type BatchProof; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- should we call it just
Proof
, orMerkleProof
,MembershipProof
, or else? - why the need for
BatchProof
here? in another word, should batch proof a struct-specific types & API, should it be a universal one? - Proofs should at least
type Proof: Clone + Serialize + ..
- I hesitate on the word "existential". where do we put non-membership proof then?
pub enum LookupResult<F, P> { | ||
/// The value at the given index, and a proof of validity | ||
Ok(F, P), | ||
/// The index is valid but we do not have the leaf in memory |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
without trait bound, <F, P>
is quite unintelligible.
- either explicitly add trait bounds to them
- or generic over
LookupResult<T: MerkleTreeScheme<F>, F: Field>
andOk(F, T::Proof)
, since this enum is meaningless without association with a specific MT scheme.
pub trait UpdatableMerkleTree<F: Field>: MerkleTree<F> { | ||
/// Update the leaf value at a given position | ||
/// * `pos` - zero-based index of the leaf in the tree | ||
/// * `elem` - newly updated element | ||
fn update(&mut self, pos: u64, elem: &Self::ElementType) -> Result<(), PrimitivesError>; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
imo, fn update
should not be in the trait,
classical categorization of accumulators,
normally you only support insert
, if you further support delete
, then you are a dynamic accumulator
(I guess "update" really means insert or delete for an accumulator)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is more about a sparse(key/value) Merkle tree. Where you have to provide a key/index to insert an element.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or should we call it RandomAccessMerkleTree
?
With 2 interfaces provided.
insert(pos, elem)
will error if given position is occupied.overwrite(pos, elem)
will ignore whether it's occupied.
IMO it's more like a vector/map accumulator. To access an element you need to specify a position to it.
UptablableMerkleTree is just implemented. See latest commitment. |
fn verify( | ||
&self, | ||
pos: Self::IndexType, | ||
proof: impl Borrow<Self::MembershipProof>, | ||
) -> Result<bool, PrimitivesError>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we have pos
as input param? Intuitively just a proof is enough and practically, the pos info is embedded in proof
.
As we discuss before, we want to make our |
…trainted generic struct/fn
…ems/jellyfish into chengyu/mt-refactor
* enable CI run on mt-refactor branch * Refactor MT & Generic Implementation (#120) * Initial commit * structured + impl * Base merkle tree impl * merkle_tree_impl * generic indextype * Refactored code for further SMT impl. * Batch insertion implementation. * Major refactoring & forget implementation. * Remember implemented. * cargo fmt * Big refactor. Leaf now digest index to prevent forge attack. * unit test for Forgettable merkle tree. * cleanup * Clear Arity & replace F::zero() with default() * trait bound refactor * New example. * cargo fmt * More example. * ElementType->Element; rename trait, impl and module; remove missing license check * IndexType refactor * Refactor DigestAlgorithm * Pull out Element,Index,NodeValue as traits; add trait bound to unconstrainted generic struct/fn * Refactor capacity interface * Renaming & better crate import. * var renaming * MerkleProof API refactor Co-authored-by: Alex Xiong <alex.xiong.tech@gmail.com> * Universal Merkle tree implementation (#132) * A new macro for general merkle tree implementation * Better trait bound for data types * New Universal Merkle tree implementation, including non-membership proof * Better comments for examples. * Address comments * add back the module * randomize the lookup test * fixup rebase * WIP * adapt after rebase * Fix the order of hashing elements, zero goes first as per spec * test for all fields * add back the failure case test for merkle root * fix warnings, clean up unused code * further cleanup * consistency in u32 vs u64 type definition in tests * Address review comments: better naming * fix formatting * Add prelude; prelude now includes 2 canonical instantiation of rescue merkle tree. * Adopted from sub PR. * Add an explicit comment about concrete MT type used * rename variables * add back the benches * Merge artefacts cleanup Co-authored-by: Alex Xiong <alex.xiong.tech@gmail.com> Co-authored-by: MRain <linmrain@gmail.com>
Description
Part of: #88
Before we can merge this PR, please make sure that all the following items have been
checked off. If any of the checklist items are not applicable, please leave them but
write a little note why.
Pending
section inCHANGELOG.md
Files changed
in the GitHub PR explorer