informalsystems / tendermint-rs

Client libraries for Tendermint/CometBFT in Rust!
Apache License 2.0
591 stars 216 forks source link

Generalize simple_merkle #73

Open liamsi opened 4 years ago

liamsi commented 4 years ago

It would be good to make simple merkle a trait (and/or use generics) to make it take any parameter that can be hashable or to make it easy to swap the underlying hash algo. See comment in: https://github.com/interchainio/tendermint-rs/pull/59#pullrequestreview-319176856

While at it, we can also rename the method's name: https://github.com/interchainio/tendermint-rs/pull/59#discussion_r346802972

tarcieri commented 4 years ago

Some precedent:

liamsi commented 4 years ago

If someone wants to work on this in the meantime, I would suggest the following API for a start:

trait MerkelizedHasher {
    /// Adds data to be merkelized.
    fn add<D: Into<Vec<u8>>>(&self, d: D);
    /// Takes all data and returns the hash.
    fn merkelize<H: Digest>(&self) -> Hash; // the return elem also needs to be sth, generic
}

I'm not saying this needs to be a trait for now. Instead this could be a struct. It would be cool if the (inner/leaf node) Hasher (here called Digest) would be replaceable, and we can collect the data to be merkelized with add (or a smarter name) to make this look more understandable:

https://github.com/interchainio/tendermint-rs/blob/24955d18a99f3f3a846f519b770de5887159a0fa/tendermint/src/block/header.rs#L104-L128

Note that there is currently no need to replace underlying hash algo. In the go code it can be swapped via swapping tmhash. This could also be done later or when need arises. I just think that if this (simple merkle) was rather seen as a useful library to merkelize structs / their fields, it would feel natural if we can replace the underlying hasher, too.

tarcieri commented 4 years ago

Something else you might want to look at for inspiration is the Rust Hash and Hasher traits:

https://doc.rust-lang.org/std/hash/trait.Hash.html

pub trait Hash {
    fn hash<H>(&self, state: &mut H)
    where
        H: Hasher;

    fn hash_slice<H>(data: &[Self], state: &mut H)
    where
        H: Hasher;
}

https://doc.rust-lang.org/std/hash/trait.Hasher.html

pub trait Hasher {
    fn finish(&self) -> u64;
    fn write(&mut self, bytes: &[u8]);
    // [...]
}