A rust implementation of Alexey Akhunov's multiproof algorithm

Overview

CircleCI Crates.io

multiproof.rs

A rust implementation of Alexey Akhunov's multiproof algorithm.

At the time of creation, multiproof is still a work in progress and this code makes a series of assumptions that are to be discussed and updated in order to achieve complete compatibility. Here is a non-exhaustive list of assumptions:

  • The initial LEAF, BRANCH, ADD, HASHER and EXTENSION model is still in use,
  • HASHER always has a parameter of 0. This is clearly and issue with this code as several distrinct trees end up having the same hash.

Installation

This code uses features from rust nightly. Install it by typing:

rustup install nightly

You can then run the tests with:

cargo +nightly test

Usage

Creating trees

Start with an empty tree:

let mut tree_root = Node::default();

This creates a mutable tree root, which is a node with 16 (currently empty) children.

You can use insert_leaf to add a (key,value) pair to that tree. This example adds (0x11111..111, 0x22222..222) to the tree that was created above:

new_root.insert(&NibbleKey::from(vec![1u8; 32]), vec![2u8; 32]).unwrap();

Note that the key format is &NibbleKey, and no longer Vec<u8>.

Calculating hashes

The hash function will walk the tree and calculate the hash representation.

let hash = new_root.hash();

Creating the proof

Call make_multiproof with the root of the tree and the list of keys to be changed. It returns a Multiproof object, which can be sent to the verifier over the network; The example below will create a proof for leaf 0x11...11:

let proof = make_multiproof(&new_root, vec![NibbleKey::from(vec![1u8; 32])]).unwrap();

Verifying proof

Call the rebuild function on the output of make_multiproof:

let root = proof.rebuild().unwrap();

Examples

See unit tests.

Changelog

0.1.9

  • Add the Hashable trait and use methods M2 and M3 (see https://ethresear.ch/t/binary-trie-format/7621/6)
  • Implement From<Vec<bool>> and Into<Vec<bool>>
  • Binary keys use bool as a key type
  • Use extensions for binary tries
  • Bugfix: check that keys have the same length in insert.
  • Fuzzing: introduce tests for nibblekey and Node::inset.

0.1.8

  • keys method on Node in order to get the list of keys present in the tree.
  • Fixes #61 - if several keys have the same prefix leading to a Leaf object, don't return an error; instead, add that key to the proof, as a proof that all the extra keys are missing.

0.1.7

  • Accept the insertion of empty keys

0.1.6

  • Fix a bug in even-length hex prefix calculations

0.1.5

  • Export ByteKey to Vec
  • Implement fmt::Display for NibbleKey

0.1.4

  • Support for binary trees
  • CBOR encoding of proofs

0.1.3

  • Allow inserts to overwrite existing leaves
  • Make has_key part of the tree trait
  • Bugfix in NibbleKey index calculation
  • README updates

0.1.2

  • Export all submodules

0.1.1

  • Export node::* from crate
Comments
  • Support for null keys

    Support for null keys

    This addresses #28 and adds the possibility to provide make_multiproof with keys that are not present in the tree. The result will be a leaf with no value, meant to represent a null key. The tree will be rebuilt by replacing that key with an EmptySlot.

    There are currently two problems with this PR, which is created for discussion purposes:

    • [ ] It doesn't generate all the keys, as evidenced by the failing unit test
    • [ ] It is only looking at the leaf level, so it could generate things like extension node > branch node with only empty slots. This is actually a valid representation, yet it's not the most efficient one.
    opened by gballet 7
  • Multiproof with tree trait

    Multiproof with tree trait

    This PR prepares the ground for supporting several tree formats. This addresses #10 and supersedes #42 and #43

    • It adds a Tree trait that supports operations like insertion and iteration over the set of child nodes, then re-implements Node in terms of this trait.
    • This trait is parameterized by another trait, describing the key and value formats by requiring the definition of the Key and Value types, making is easier to switch between a nibble-/byte-/binary-based key type.
    • The Multiproof structure is parameterized by the tree type, to be able to reconstruct different types of trees from a single proof type.
    • It makes rebuild a method of Multiproof, which is the first step in supporting different proof types (the end goal is to support proofs that use instructions and proofs that do not)
    • It also effectively deprecates the use of the LEAF(i) operand i as expressed in #19
    opened by gballet 3
  • Binary key support + key trait

    Binary key support + key trait

    This is preparation work for the support of binary trees.

    • It introduces a Key trait that will have to be implemented by all objects that can be used as keys;
    • It introduces BinaryKey that implements Key and represents a bit-wise string;
    • It rewrites ByteKey and NibbleKey as an implementation of Key; and
    • it moves ByteKey, NibbleKey and BinaryKey to their own modules.
    opened by gballet 2
  • Some code cleanup

    Some code cleanup

    A series of changes to clean the code up:

    • Remove the use for NibbleKey::new since From<Vec<u8>> has been implemented;
    • Rely on hex::decode instead of having a custom function
    • Change the implementation of display to write the regular data, instead of writing its "debug" version
    • Remove some comments

    I realize that the reason @sina created a custom hex conversion function might have been done with the hope of reducing the size of the final binary. Is that so?

    opened by gballet 2
  • EmptyNode now default root, make insertion into empty node possible

    EmptyNode now default root, make insertion into empty node possible

    This is preparation work for #28

    So far it wasn't possible to insert a key into an empty node. This now makes it possible (by replacing the empty node with a leaf), and is closer to the behavior of geth. It is also going to be necessary in order to insert null entries for proofs.

    opened by gballet 2
  • Add failing test case for hashing embedded nodes

    Add failing test case for hashing embedded nodes

    If a tree includes embedded nodes (those with encoding < 32), the computed hash() is wrong. This is due to the fact that rlp.encode([[1, 2], [3, 4]]) != rlp.encode([[1, 2], rlp.encode([3, 4]]). I.e. the parent node shouldn't serialize the encoding of the embedded node, but the embedded node in raw form (array).

    Edit: the expected root was generated by the js merkle-patricia-tree library.

    opened by s1na 2
  • Add tree trait

    Add tree trait

    This PR prepares the ground for supporting several tree formats. This addresses #10 and supersedes #42

    As a result, it adds a trait Tree that supports operations like insertion and iteration over the set of child nodes, then re-implements Node in terms of this trait.

    opened by gballet 1
  • Turn insert_leaf into self.insert + add tree trait

    Turn insert_leaf into self.insert + add tree trait

    This is in preparation for supporting several tree formats (which is the basis for subsequently supporting several instruction & proof formats).

    • It makes insert act on a mutable self reference, instead of using the insert_leaf global function.
    • It declares a Tree trait, that implements functions are a tree structure needs to implement to be able to work with this algorithm.

    insert_leaf is still part of the current API, although it will be removed at a later time, when other tree structures are fully supported.

    opened by gballet 1
  • Keys are not truncated when parsing an Extension

    Keys are not truncated when parsing an Extension

    In make_multiproof I think the goal had been to truncate keys of keyvals when parsing an Extension, but the key is simply being cloned:

    https://github.com/gballet/multiproof-rs/blob/269211ec3dee159bb46314390e6d8cbda40381e1/src/lib.rs#L492-L499

    opened by s1na 1
  • Make the tree implementation a trait

    Make the tree implementation a trait

    The current hexary Patricia Tree structure used in Ethereum poses some challenges and dropping it for another more manageable structure like binary or balanced trees is often discussed.

    As a result, we want to abstract all tree interactions through a trait to be able to easily change the underlying tree implementation.

    opened by gballet 1
  • re-enable linter in circleCI

    re-enable linter in circleCI

    rustfmt is not available on ARM, so I can't run it. Now that the code is going to become a bit more stable, the linter in circle CI needs to be re-enabled and cargo fmt needs to be run in nightly mode.

    opened by gballet 1
  • [WIP] use iterators to progress over binary keys

    [WIP] use iterators to progress over binary keys

    The way binary keys are being implemented is making it impossible to implement Index<Range<usize>>. This approach uses an iterator and currently only implements it on BinaryTree.

    Left to do:

    • [ ] Use IntoIterator
    • [ ] Use FromIterator
    • [x] Get rid of rewind
    opened by gballet 0
  • [DONTMERGE] Use iterator for keys

    [DONTMERGE] Use iterator for keys

    This PR needs to be rebased, only applies iterators to Extension nodes and is only here for discussing the following points:

    • [ ] Index's index -> &[u8] makes it impossible to make bit keys and nibble keys as simple Vec<u8>: the data needs to be expanded in another Vec<u8> in which each byte represents one bit/nibble
    • [ ] the need for rewind
    • [ ] the general API and code of chop_common
    opened by gballet 1
  • Tree's new_* methods are MPT-specific

    Tree's new_* methods are MPT-specific

    This is a tracker for @s1na's question on #44

    These new_* methods seem to be specific to the MPT tree. Is there maybe a way in Rust to only define them on Node and then cast a Tree to a Node when these methods are needed?

    AFAIK, there isn't. This being said, indeed a difference should be made, e.g. by introducing an inherited TreeWithExtension : Tree trait.

    question 
    opened by gballet 0
  • Re-constructing paths of leaves during verification

    Re-constructing paths of leaves during verification

    This is more related to an EE wanting to do token transfers using this multiproof algorithm. I'm not sure if it makes sense to merge it in the repo.

    EE receives an array of txes which include among other things a signature from sender and the address of the recipient. The EE also receives a multiproof. In order to make sure addresses of the sender and recipient of the tx corresponds to the leaves sent in the multiproof, the EE needs to re-construct the path to those leaves when it's verifying the multiproof. Here's my attempt at how one could do this:

    • Initialize an array of addrs with same length as leaves
    • When a leaf is read in via LEAF add its partial path to corresponding index of addrs, and add this index to the leaf node being pushed on stack
    • In BRANCH, EXTENSION or ADD:
      • if the node popped from stack is a leaf, read its index and prepend a partial path to its addrs entry. Also add its addrs index (for the leaf) to the node being pushed on stack
      • else, read list of indices for all leaves this node is pointing to and prepend their paths with the partial path (e.g. branch idx, or extkey)
    • After verification is done, all these paths need to be encoded form their nibbles form to get the addresses
    opened by s1na 1
  • Add HASH opcode

    Add HASH opcode

    Right now rebuild returns the root Node. In order to compute the root hash, the partial trie has to be walked down and up once. To improve the efficiency of verifiers we could either add a new opcode or change the semantic of the existing ones to allow the verifier to produce the correct hash once every instruction has been processed.

    LEAF and EXTENSION opcodes could be modified to directly hash the node and push the hash on the stack. HASHER just reads in a hash from the sequence and doesn't need to be modified. The only question is how to handle branches, because it's not exactly clear when all the children have been added to the branch. One simple option is to introduce a HASH opcode which expects a branch node on top of stack and hashes it. There are other options too.

    opened by s1na 1
Owner
Guillaume Ballet
Guillaume Ballet
Rust implementation of AstroBWT Proof-Of-Work algorithm

AstroBWT AstroBWT is a proof-of-work (PoW) algorithm based on Burrows-Wheeler transform (BWT). Developed and used by the DERO Project for Mobile (arm)

null 6 Mar 7, 2022
Pure Rust implementation of the Double Ratchet algorithm

Double Ratchet A pure Rust implementation of the Double Ratchet, as specified by Trevor Perrin and Moxie Marlinspike. The Double Ratchet allows two us

Sebastian 52 Nov 25, 2022
A Rust implementation of the AGCWD algorithm

AGCWD is described in the paper "Efficient Contrast Enhancement Using Adaptive Gamma Correction With Weighting Distribution".

Takeru Ohta 2 Jan 17, 2022
A SIMD-accelerated Adler-32 rolling hash algorithm implementation.

simd-adler32 A SIMD-accelerated Adler-32 rolling hash algorithm implementation. Features No dependencies Support no_std (with default-features = false

Marvin Countryman 23 Dec 19, 2022
An implementation of the A-Star pathfinding algorithm tailored for traversing a bespoke collection of weighted hexagons.

An implementation of the A-Star pathfinding algorithm tailored for traversing a bespoke collection of weighted hexagons. It's intended to calculate the most optimal path to a target hexagon where you are traversing from the centre of one hexagon to the next along a line orthogonal to a hexagon edge

null 19 Dec 11, 2022
A simple implementation of the astar pathfinding algorithm from red blob games

A simple implementation of the astar pathfinding algorithm from red blob games. In order to use the pathfinder you must have a path map for it to navi

sark 6 Nov 22, 2022
nsga is an opinionated implementation of the NSGA-II (Non-dominated Sorting Genetic Algorithm)

nsga nsga is an opinionated implementation of the NSGA-II (Non-dominated Sorting Genetic Algorithm), a multi-objective genetic optimization algorithm.

Max Kuznetsov 8 Oct 1, 2022
Genetic Algorithm library in Rust

RsGenetic Summary and Features RsGenetic is a framework for executing genetic algorithms in Rust. It is designed to have a simple but modular API. Exa

Mathieu De Coster 74 Dec 27, 2022
Example of a genetic algorithm in Rust and Python

Example of a genetic algorithm in Rust and Python Monkey typewriter Finding the phrase 'To be or not to be. That is the question.' Inspired by the exa

sotrh 2 Jan 27, 2022
hubpack is an algorithm for converting Rust values to bytes and back.

hubpack is an algorithm for converting Rust values to bytes and back. It was originally designed for encoding messages sent between embedded programs. It is designed for use with serde.

Cliff L. Biffle 6 Nov 11, 2022
Execute genetic algorithm (GA) simulations in a customizable and extensible way.

genevo genevo provides building blocks to run simulations of optimization and search problems using genetic algorithms (GA). The vision for genevo is

Innoave 110 Dec 21, 2022
A Trig-less Line of Sight Algorithm in Two Dimensions

In many examples of 2D line-of-sight algorithms, expensive operations like trigonometry are used. Additionally, some methods have intentional inaccuracies in them for the sake of simplicity. Here, we give an algorithm which does not fudge the numbers, and uses only basic arithmetic: addition, subtraction, multiplication, and division. This is not intended to replace the existing algorithms, or even be more efficient in practice.

null 22 Dec 22, 2022
The labs of Raft consensus algorithm based on MadSim.

MadRaft The labs of Raft consensus algorithm based on MadSim. Some codes are derived from MIT 6.824 and PingCAP Talent Plan: Raft Lab. Thanks for thei

MadSys Research Group 48 Dec 28, 2022
Maximum Relevance - Minimum redundancy feature selection algorithm

mrmr Maximum Relevance - Minimum redundancy feature selection algorithm implemented in Rust. Usage CLI app. It gets input from a csv dataset with head

Jorge 2 Jan 1, 2022
Library that implements different versions of PPMD algorithm (compress and decompress)

ppmd-rs Library that implements different versions of PPMD algorithm (compress and decompress) Dependencies Rust 1.58 or newer Cargo How to build Clon

Alexander Zaitsev 3 Jun 20, 2022
Online algorithm for mean and variance, with support for uneven weights

welford Online algorithm for mean and variance, with support for uneven weights. This implements the Welford's online algorithm for computing mean and

Felipe S. S. Schneider 1 Sep 8, 2022
Rust implementation of real-coded GA for solving optimization problems and training of neural networks

revonet Rust implementation of real-coded genetic algorithm for solving optimization problems and training of neural networks. The latter is also know

Yury Tsoy 19 Aug 11, 2022
Raft implementation in Rust

rsraft Raft implementation in Rust. The aim of this project is implementing the Raft Consensus Algorithm as described in the paper, with the goal of f

Lauro Caetano 42 Dec 24, 2022