132 Commits

Author SHA1 Message Date
Bobbin Threadbare
18302d68e0 Merge pull request #154 from 0xPolygonMiden/next
Tracking PR for v0.6.0 release
2023-06-25 02:06:21 -07:00
Bobbin Threadbare
858f95d4a1 chore: update changelog 2023-06-25 01:54:34 -07:00
Bobbin Threadbare
b2d6866d41 refactor: rename Merkle store Node into StoreNode 2023-06-25 01:42:21 -07:00
Bobbin Threadbare
f52ac29a02 Merge pull request #162 from 0xPolygonMiden/frisitano-tx-executor
Introduce data access recording capabilities
2023-06-23 23:33:58 -07:00
Bobbin Threadbare
f08644e4df refactor: simplify recording MerkleStore structure 2023-06-23 23:19:12 -07:00
frisitano
679a30e02e feat: introduce recorder objects 2023-06-23 14:26:57 +01:00
Bobbin Threadbare
cede2e57da Merge pull request #161 from 0xPolygonMiden/bobbin-smt-empty-value
Add `EMPTY_VALUE` associated constant to SMTs
2023-06-14 09:48:14 -07:00
Bobbin Threadbare
4215e83ae5 feat: add EMPTY_VALUE const to SMTs 2023-06-13 22:53:14 -07:00
Bobbin Threadbare
fe5cac9edc fix: compilation errors 2023-06-13 22:43:08 -07:00
Bobbin Threadbare
53d52b8adc Merge pull request #156 from 0xPolygonMiden/andrew-partial-mt
Partial Merkle tree implementation
2023-06-13 22:10:26 -07:00
Bobbin Threadbare
1be64fc43d Merge pull request #157 from 0xPolygonMiden/tohrnii-digest
refactor: refactor crypto APIs to use RpoDigest instead of Word
2023-06-13 15:06:47 -07:00
Bobbin Threadbare
049ae32cbf chore: clean up test code 2023-06-13 14:40:31 -07:00
Andrey Khmuro
b9def61e28 refactor: improve tests, add error tests 2023-06-13 16:14:07 +03:00
tohrnii
0e0a3fda4f refactor: refactor to clean up and simplify things 2023-06-13 10:53:41 +01:00
tohrnii
fe9aa8c28c refactor: refactor crypto APIs to use RpoDigest instead of Word 2023-06-09 21:27:09 +01:00
Andrey Khmuro
766702e37a refactor: improve tests, small fixes 2023-06-09 13:53:50 +03:00
Andrey Khmuro
218a64b5c7 refactor: small fixes 2023-06-07 17:31:38 +03:00
Andrey Khmuro
2708a23649 refactor: optimize code, fix bugs 2023-06-06 01:36:53 +03:00
Andrey Khmuro
43f1a4cb64 refactor: MerkleStore clippy fix 2023-06-06 01:36:53 +03:00
Andrey Khmuro
55cc71dadf fix: fix add_path func leaf determination 2023-06-06 01:36:53 +03:00
Andrey Khmuro
ebf71c2dc7 refactor: optimize code, remove not momentarily necessary functions 2023-06-06 01:36:53 +03:00
Andrey Khmuro
b4324475b6 feat: change constructor from with_leaves to with_paths 2023-06-06 01:36:53 +03:00
Andrey Khmuro
23f448fb33 feat: partial Merkle tree 2023-06-06 01:36:53 +03:00
Bobbin Threadbare
59f7723221 chore: update crete version to v0.6.0 2023-05-26 14:49:58 -07:00
Bobbin Threadbare
2ed880d976 chore: add TieredSmt to readme 2023-05-26 14:41:22 -07:00
Bobbin Threadbare
daa27f49f2 Merge pull request #140 from 0xPolygonMiden/next
Tracking PR for v0.5 release
2023-05-26 14:36:20 -07:00
Bobbin Threadbare
dcda57f71a chore: update changelog 2023-05-26 14:32:17 -07:00
Bobbin Threadbare
d9e3211418 Merge pull request #153 from 0xPolygonMiden/bobbin-tsmt-iter
Tiered SMT iterators
2023-05-20 22:52:59 -07:00
Bobbin Threadbare
21e7a5c07d feat: implement iterators over contents of TieredSmt 2023-05-20 22:47:07 -07:00
Bobbin Threadbare
02673ff87e Merge pull request #152 from 0xPolygonMiden/bobbin-tsmt
Basic Tiered MST
2023-05-16 15:42:34 -07:00
Bobbin Threadbare
b768eade4d feat: added handling of bottom tier to TieredSmt 2023-05-16 15:38:05 -07:00
Bobbin Threadbare
51ce07cc34 feat: implement basic TieredSmt 2023-05-12 11:33:34 -07:00
Bobbin Threadbare
550738bd94 Merge pull request #151 from 0xPolygonMiden/bobbin-mstore-subset
MerkleStore subset and more
2023-05-11 00:41:01 -07:00
Bobbin Threadbare
629494b601 feat: add leaves() iterator to SimpleSmt 2023-05-11 00:37:16 -07:00
Bobbin Threadbare
13aeda5a27 feat: add subset() to MerkleStore 2023-05-09 18:38:21 -07:00
Bobbin Threadbare
e5aba870a2 Merge pull request #149 from 0xPolygonMiden/bobbin-simple-smt
SimpleSmt updates
2023-05-08 07:35:00 -07:00
Bobbin Threadbare
fcf03478ba refactor: update SimpleSmt interfaces 2023-05-08 00:12:24 -07:00
frisitano
0ddd0db89b Merge pull request #148 from 0xPolygonMiden/frisitano-mmr-accumulator
refactor: Mmr accumulator
2023-05-05 17:56:46 +08:00
frisitano
2100d6c861 refactor(mmr): expose method to join mmr peaks in a vector and pad 2023-05-05 12:03:32 +08:00
Bobbin Threadbare
52409ac039 Merge pull request #146 from 0xPolygonMiden/frisitano-merkle-store-inner-nodes
feat: add .inner_nodes() to [MerkleStore]
2023-05-04 13:49:36 -07:00
frisitano
4555fc918f feat: add .inner_nodes() to [MerkleStore] 2023-05-04 19:15:52 +07:00
Bobbin Threadbare
52db23cd42 chore: update crate version to v0.5.0 2023-04-21 15:48:18 -07:00
Bobbin Threadbare
09025b4014 Merge pull request #129 from 0xPolygonMiden/next
Tracking PR for v0.4 release
2023-04-21 15:38:33 -07:00
Bobbin Threadbare
e983e940b2 chore: update changelog 2023-04-21 14:42:08 -07:00
Bobbin Threadbare
ae4e27b6c7 Merge pull request #139 from 0xPolygonMiden/hacka-support-adding-existing-structures-to-store
store: support adding existing structures
2023-04-21 14:32:52 -07:00
Bobbin Threadbare
130ae3d12a feat: add inner node iterator to MerklePath 2023-04-21 14:27:58 -07:00
Bobbin Threadbare
22c9f382c4 fix: serialization test 2023-04-21 11:39:49 -07:00
Bobbin Threadbare
9be4253f19 feat: remove clone requirement for MerkleStore From constructors 2023-04-21 11:22:36 -07:00
Augusto F. Hack
59595a2e04 feat: added From convertions for the MerkleStore 2023-04-21 14:47:58 +02:00
Augusto F. Hack
eb316f51bc store: remove SimpleSMT/MerkleTree/Mmr add/with methods 2023-04-21 14:47:48 +02:00
Augusto F. Hack
8161477d6a store: support adding existing structures 2023-04-20 13:45:31 +02:00
Augusto Hack
158167356d Merge pull request #138 from 0xPolygonMiden/hacka-merge-support-for-leaves
feat: allow merging of leaves
2023-04-17 12:29:13 +02:00
Augusto F. Hack
3996374a8b feat: allow merging of leaves
Consider the case of a MMR with one entry, and a new entry is being
added. Both of these values are quite unique, they are at the same time
the root and only leaf of their corresponding tree. Currently this
representation is not supported by the [MerkleStore], so the leaves are
not in it. Once the two values are merged, they both become leaves of a
new tree under the new parent, and the existing validation didn't permit
that promotion from happening.

This lifts the validation, and changes the method to clarify that not
only `root` are being merged, by arbitrary nodes of a tree (leafs,
internal, or roots), with arbitrary mixing of each.
2023-04-17 12:21:51 +02:00
Augusto Hack
7fa03c7967 Merge pull request #137 from 0xPolygonMiden/frisitano-reexport-mmr-proof
re-export mmr proof
2023-04-14 14:34:58 +02:00
frisitano
79915cc346 feat: re-export MmrProof 2023-04-14 13:25:19 +01:00
Augusto Hack
45412b5cec Merge pull request #134 from 0xPolygonMiden/add-rustfmt-config
config: add rustfmt config
2023-04-11 17:58:06 +02:00
Augusto F. Hack
bbb1e641a3 config: add rustfmt config 2023-04-11 17:38:39 +02:00
Bobbin Threadbare
e02507d11e chore: update version to v0.4.0 2023-04-08 12:46:53 -07:00
Bobbin Threadbare
b5eb68e46c Merge pull request #120 from 0xPolygonMiden/next
Tracking PR for v0.3 release
2023-04-07 23:55:43 -07:00
Bobbin Threadbare
61db888b2c chore: update crate version to v0.3 2023-04-07 23:44:27 -07:00
Bobbin Threadbare
051167f2e5 Merge pull request #76 from 0xPolygonMiden/bobbin-blake3-opt
BLAKE3 hash_elements() optimization
2023-04-07 23:12:41 -07:00
Victor Lopes
498bc93c15 Merge pull request #125 from 0xPolygonMiden/vlopes11-store-get-leaf-depth
feat: add `MerkleStore::get_leaf_depth`
2023-04-06 23:13:54 +02:00
Victor Lopez
00ffc1568a feat: add MerkleStore::get_leaf_depth
This commit introduces `get_leaf_depth`, a tiered SMT helpers that will
retrieve the depth of a leaf for a given root, capped by `64`.

closes #119
2023-04-06 23:01:38 +02:00
Augusto Hack
cbf51dd3e2 Merge pull request #127 from 0xPolygonMiden/hacka-optimized-peak-hash
mmr: optimized peak hash for Miden VM
2023-04-06 19:38:48 +02:00
Augusto F. Hack
ab903a2229 mmr: optimized peak hash for Miden VM 2023-04-06 18:22:01 +02:00
Bobbin Threadbare
86dba195b4 Merge pull request #124 from 0xPolygonMiden/bobbin-merkle-fixes
Merkle fixes
2023-04-05 12:20:41 -07:00
Bobbin Threadbare
bd557bc68c fix: add validation to NodeIndex constructor and remove BitIterator 2023-04-05 12:08:00 -07:00
Augusto Hack
cf94ac07b7 Merge pull request #121 from 0xPolygonMiden/hacka-simple-smt-parent-node-iterator
feat: add parent node iterator for SimpleSMT
2023-04-05 00:46:32 +02:00
Augusto Hack
d873866f52 Merge pull request #118 from 0xPolygonMiden/hacka-support-mmr-in-the-merkle-store
feat: add support for MMR to the MerkleStore
2023-04-04 23:13:43 +02:00
Augusto F. Hack
9275dd00ad feat: add parent node iterator for SimpleSMT 2023-04-04 22:33:26 +02:00
Augusto F. Hack
429d3bab6f feat: add support for MMR to the MerkleStore 2023-04-04 22:33:01 +02:00
Augusto Hack
f19fe6e739 Merge pull request #117 from 0xPolygonMiden/hacka-simplify-consuming-merkle-tree
feat: add node iterator to MerkleTree
2023-04-04 22:14:38 +02:00
Augusto F. Hack
1df4318399 feat: add node iterator to MerkleTree 2023-04-04 22:11:21 +02:00
Bobbin Threadbare
433b467953 feat: optimized hash_elements for blake3 hasher 2023-04-04 01:06:51 -07:00
Augusto Hack
f46d913b20 Merge pull request #116 from 0xPolygonMiden/hacka-remove-merke-store
Remove SimpleSmt store
2023-03-31 03:12:09 +02:00
Augusto F. Hack
f8a62dae76 chore: remove simple_smt::Store 2023-03-31 03:10:01 +02:00
Victor Lopes
49b9029b46 Merge pull request #115 from 0xPolygonMiden/vlopes11-store-smt-depth
feat: Add `depth` as store SMT argument
2023-03-30 01:19:30 +02:00
Victor Lopez
d37f3f5e84 feat: Add depth as store SMT argument
Prior to this commit, MerkleStore allowed the creation of Sparse Merkle
tree only with the maximum depth of 63. However, this doesn't fit the
Tiered Sparse Merkle tree requirements, as it will contain trees of
depth 16.

This commit adds the `depth` argument to the MerkleStore methods that
will create Sparse Merkle trees.
2023-03-30 01:13:05 +02:00
Bobbin Threadbare
9389f2fb40 Merge pull request #80 from 0xPolygonMiden/next
v0.2 tracking PR
2023-03-25 01:28:40 -07:00
Bobbin Threadbare
703692553d chore: add winterfell dependency update to changelog 2023-03-25 00:45:17 -07:00
Bobbin Threadbare
d68be83bc4 chore: add Mmr to readme and changelog 2023-03-25 00:00:24 -07:00
Bobbin Threadbare
80171af872 Merge pull request #114 from 0xPolygonMiden/v0.2.0-release-prep
Prepare v0.2 release
2023-03-24 23:50:41 -07:00
Augusto Hack
75af3d474b Merge pull request #113 from 0xPolygonMiden/hacka-merkle-store-fix-empty-roots
bugfix: fix internal nodes of for empty leafs of a SMT
2023-03-24 23:26:48 +01:00
Augusto F. Hack
9e6c8ff700 bugfix: fix internal nodes of for empty leafs of a SMT
The path returned by `EmptySubtreeRoots` starts at the root, and goes to
the leaf. The MerkleStore constructor assumed the other direction, so
the parent/child hashes were reversed.

This fixes the bug and adds a test.
2023-03-24 23:22:31 +01:00
Bobbin Threadbare
a58922756a chore: update crate versions, dependencies, and CHANGELOG 2023-03-24 14:58:19 -07:00
Augusto Hack
bf15e1331a Merge pull request #112 from 0xPolygonMiden/hacka-add-serde-to-merklestore
Add serde to merklestore
2023-03-24 21:49:50 +01:00
Augusto F. Hack
7957cc929a feat: added MerkleStore serde 2023-03-24 21:44:36 +01:00
Victor Lopes
854892ba9d Merge pull request #111 from 0xPolygonMiden/vlopes11-increase-empty-subtrees
feat: add empty subtree constants to cover u8::MAX depth
2023-03-23 22:50:37 +01:00
Bobbin Threadbare
ce38ee388d Merge pull request #104 from 0xPolygonMiden/hacka-store-docs
Store docs
2023-03-23 13:11:04 -07:00
Bobbin Threadbare
4d1b3628d3 Merge pull request #110 from 0xPolygonMiden/bobbin-pathset-fixes
Fix MerklePathSet issues
2023-03-23 13:10:21 -07:00
Augusto F. Hack
2d1bc3ba34 store: added user documentation on usage and purpose 2023-03-23 14:19:37 +01:00
Victor Lopez
2ff96f40cb feat: add empty subtree constants to cover u8::MAX depth
Prior to this commit, we limited the constants count to 64 for the empty
subtrees depth computation. This is a hard-assumption that every tree of
Miden will have a depth up to 64 - and will cause undefined behavior if
it doesn't.

With the introduction of `MerkleStore::merge_roots` and the deprecation
of `mtree_cwm` instruction from the VM, this assumption is broken and
the user might end with trees with depth greater than 64. This broken
assumption could lead to attack vectors.

We can easily fix that by extending the pre-computed hashes list to the
maximum of `u8` (i.e. 255). This will have zero impact on functionality,
and will be completely safe to use without hard assumptions.
2023-03-23 12:59:47 +01:00
Bobbin Threadbare
9531d2bd34 fix: to paths reduction of MerklePathSet 2023-03-23 01:12:02 -07:00
Bobbin Threadbare
c79351be99 Merge pull request #107 from 0xPolygonMiden/hacka-store-add-merkle-paths
store: added with_merkle_paths constructor
2023-03-22 16:14:45 -07:00
Bobbin Threadbare
b7678619b0 Merge pull request #103 from 0xPolygonMiden/hacka-format-merkle-tree
Format merkle tree
2023-03-22 15:40:16 -07:00
Augusto F. Hack
0375f31035 feat: added utility to format MerkleTree and MerklePath to hex
Example formatted MerkleTree:

```
880abe452320966617646e7740b014954300f19a28780a0889d62ff33f4b0534
  1ade1369091efa31201e9b60c9c28874d0ddce5362b335135a6bb4c917285983
  3e60a9c843b4bb19f7a0572102e6507195f5240767a396335fd21981b048b807
    0100000000000000000000000000000000000000000000000000000000000000
    0200000000000000000000000000000000000000000000000000000000000000
    0300000000000000000000000000000000000000000000000000000000000000
    0400000000000000000000000000000000000000000000000000000000000000
```

Example formatted MerklePath:

```
[0400000000000000000000000000000000000000000000000000000000000000, 1ade1369091efa31201e9b60c9c28874d0ddce5362b335135a6bb4c917285983]
```
2023-03-22 21:53:05 +01:00
Augusto Hack
c96047af9d Merge pull request #102 from 0xPolygonMiden/hacka-merkle-tree-assert-message
chore: clarified assert message
2023-03-22 17:54:54 +01:00
Augusto F. Hack
b250752883 store: added with_merkle_paths constructor
And unit tests for each constructor type.
2023-03-22 14:17:12 +01:00
Augusto Hack
482dab94c5 Merge pull request #101 from 0xPolygonMiden/hacka-fix-benchmark-code
Fix benchmark code
2023-03-22 13:46:22 +01:00
Augusto F. Hack
d6cbd178e1 chore: clarified assert message 2023-03-22 11:30:19 +01:00
Augusto F. Hack
ef342cec23 bugfix: fix store benchmark 2023-03-22 10:53:12 +01:00
Victor Lopes
7305a72295 Merge pull request #99 from 0xPolygonMiden/vlopes11-merkle-store-containers
feat: add merkle path containers and return them on tree update
2023-03-21 20:54:36 +01:00
Victor Lopez
84086bdb95 feat: add merkle path containers and return them on tree update
Returning tuples is often confusing as they don't convey meaning and it
should be used only when there is no possible ambiguity.

For `MerkleStore`, we had a couple of tuples being returned, and reading
the implementation was required in order to distinguish if they were
leaf values or computed roots.

This commit introduces two containers that will self-document these
returns: `RootPath` and `ValuePath`. It will also update `set_node` to
return both the new root & the new path, so we can prevent duplicated
traversals downstream when updating a node (one to update, the second to
fetch the new path/root).
2023-03-21 20:45:01 +01:00
Bobbin Threadbare
a681952982 Merge pull request #97 from 0xPolygonMiden/hacka-storage-benchmark
Storage benchmark
2023-03-21 11:43:12 -07:00
Augusto F. Hack
78e82f2ee6 feat: add benchmark for storages 2023-03-21 14:29:18 +01:00
Victor Lopes
f07ed69d2f Merge pull request #95 from 0xPolygonMiden/vlopes11-fix-merkle-store-bounds
fix: merkle store panics on bounds
2023-03-21 09:51:48 +01:00
Augusto F. Hack
17eb8d78d3 chore: storage -> store 2023-03-21 09:45:36 +01:00
Victor Lopez
8cb245dc1f bugfix: reverse merkle path to match other structures
The store builds the path from root to leaf, this updates the code to
return a path from leaf to root, as it is done by the other structures.

This also added custom error for missing root.
2023-03-21 09:45:29 +01:00
Victor Lopez
867b772d9a fix: merkle store panics on bounds
Prior to this commit, the MerkleStore panicked under certain bounds. It
will prevent such panics by using checked operations.

ilog2, for instance, will panic when the operand is zero. However, there
is a documentation rule enforcing the merkle tree to be size at least 2.
If this rule is checked, then the panic is impossible.
2023-03-18 02:20:11 +01:00
Bobbin Threadbare
33d37d82e2 Merge pull request #79 from 0xPolygonMiden/hacka-ignore-pre-commit-rev
ignore pre commit rev
2023-03-17 00:11:13 -07:00
Augusto Hack
5703fef226 Merge pull request #96 from 0xPolygonMiden/hacka-check-root-in-storage
bugfix: check if the requested root is in the storage
2023-03-16 23:30:56 +01:00
Augusto F. Hack
669ebb49fb bugfix: check if the requested root is in the storage 2023-03-16 23:26:02 +01:00
Victor Lopes
931bcc3cc3 Merge pull request #94 from 0xPolygonMiden/vlopes11-merkle-store-derive
refactor: add derive proc macros to merkle store
2023-03-16 19:13:02 +01:00
Victor Lopez
91667fd7de refactor: add derive proc macros to merkle store
This commit introduce common derive proc macros to MerkleStore. These
are required downstream as the in-memory storage can be cloned.

It also introduces constructors common to the other types of the crate
that will help to build a merkle store, using a build pattern.
2023-03-16 10:28:45 +01:00
Augusto Hack
e4ddf6ffaf Merge pull request #93 from 0xPolygonMiden/hacka-add-merkle-store
Add merkle store
2023-03-15 18:13:48 +01:00
Augusto F. Hack
88a646031f feat: add merkle store 2023-03-15 17:34:42 +01:00
Bobbin Threadbare
2871e4eb27 Merge pull request #87 from 0xPolygonMiden/vlopes11-36-simple-smt-prepare
feat: refactor simple smt to use empty subtree constants
2023-03-07 16:10:24 -08:00
Victor Lopez
3a6a4fcce6 feat: refactor simple smt to use empty subtree constants
Prior to this commit, there was an internal procedure with the merkle
trees to compute empty sub-tree for arbitrary depths.

However, this isn't ideal as this code can be reused in any merkle
implementation that uses RPO as backend.

This commit introduces a structure that will generate these empty
subtrees values.
2023-03-07 20:44:42 +01:00
Augusto Hack
7ffa0cd97d Merge pull request #67 from 0xPolygonMiden/hacka-merkle-mountain-range-memory-implementation
feat: merkle mountain range
2023-03-02 22:27:13 +01:00
Augusto F. Hack
32d37f1591 feat: merkle mountain range 2023-03-02 13:07:55 +01:00
Augusto F. Hack
bc12fcafe9 chore: ignore pre-commit rev 2023-03-01 18:32:24 +01:00
Augusto Hack
8c08243f7a Merge pull request #78 from 0xPolygonMiden/hacka-pre-commit
Add pre commit
2023-03-01 18:31:08 +01:00
Augusto F. Hack
956e4c6fad chore: initial run pre-commit 2023-03-01 17:45:57 +01:00
Augusto F. Hack
efa39e5ce0 feat: added pre-commit hook config 2023-03-01 17:45:33 +01:00
Bobbin Threadbare
ae3f14e0ff Merge pull request #74 from 0xPolygonMiden/hacka-node-index-docs
docs: mention tree form order of NodeIndex docs
2023-02-22 12:19:45 -08:00
Bobbin Threadbare
962a07292f Merge pull request #75 from 0xPolygonMiden/next
v0.1.4 release
2023-02-22 09:32:44 -08:00
Augusto F. Hack
dfb073f784 docs: mention tree form order of NodeIndex docs 2023-02-22 17:23:03 +01:00
Bobbin Threadbare
41c38b4b5d chore: changed version to v0.1.4 in Cargo.toml 2023-02-22 08:22:25 -08:00
Bobbin Threadbare
c4eb4a6b98 Merge pull request #73 from 0xPolygonMiden/vlopes11-72-add-winter-hasher
feat: re-export winter-crypto Hasher, Digest & ElementHasher
2023-02-22 08:15:58 -08:00
Victor Lopez
35b255b5eb feat: re-export winter-crypto Hasher, Digest & ElementHasher
This commit introduces the re-export of the listed primitives.

They will be used inside Miden to report the security level of the
picked primitive, as well as other functionality.

closes #72
2023-02-22 16:56:14 +01:00
Bobbin Threadbare
e94b0c70a9 Merge pull request #71 from 0xPolygonMiden/bobbin-dep-updates
Dependency updates
2023-02-20 23:55:43 -08:00
Bobbin Threadbare
e6bf497500 chore: update dependencies 2023-02-20 23:46:21 -08:00
43 changed files with 7699 additions and 612 deletions

2
.git-blame-ignore-revs Normal file
View File

@@ -0,0 +1,2 @@
# initial run of pre-commit
956e4c6fad779ef15eaa27702b26f05f65d31494

43
.pre-commit-config.yaml Normal file
View File

@@ -0,0 +1,43 @@
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v3.2.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-json
- id: check-toml
- id: pretty-format-json
- id: check-added-large-files
- id: check-case-conflict
- id: check-executables-have-shebangs
- id: check-merge-conflict
- id: detect-private-key
- repo: https://github.com/hackaugusto/pre-commit-cargo
rev: v1.0.0
hooks:
# Allows cargo fmt to modify the source code prior to the commit
- id: cargo
name: Cargo fmt
args: ["+stable", "fmt", "--all"]
stages: [commit]
# Requires code to be properly formatted prior to pushing upstream
- id: cargo
name: Cargo fmt --check
args: ["+stable", "fmt", "--all", "--check"]
stages: [push, manual]
- id: cargo
name: Cargo check --all-targets
args: ["+stable", "check", "--all-targets"]
- id: cargo
name: Cargo check --all-targets --no-default-features
args: ["+stable", "check", "--all-targets", "--no-default-features"]
- id: cargo
name: Cargo check --all-targets --all-features
args: ["+stable", "check", "--all-targets", "--all-features"]
# Unlike fmt, clippy will not be automatically applied
- id: cargo
name: Cargo clippy
args: ["+nightly", "clippy", "--workspace", "--", "--deny", "clippy::all", "--deny", "warnings"]

View File

@@ -1,3 +1,43 @@
## 0.6.0 (2023-06-25)
* [BREAKING] Added support for recording capabilities for `MerkleStore` (#162).
* [BREAKING] Refactored Merkle struct APIs to use `RpoDigest` instead of `Word` (#157).
* Added initial implementation of `PartialMerkleTree` (#156).
## 0.5.0 (2023-05-26)
* Implemented `TieredSmt` (#152, #153).
* Implemented ability to extract a subset of a `MerkleStore` (#151).
* Cleaned up `SimpleSmt` interface (#149).
* Decoupled hashing and padding of peaks in `Mmr` (#148).
* Added `inner_nodes()` to `MerkleStore` (#146).
## 0.4.0 (2023-04-21)
- Exported `MmrProof` from the crate (#137).
- Allowed merging of leaves in `MerkleStore` (#138).
- [BREAKING] Refactored how existing data structures are added to `MerkleStore` (#139).
## 0.3.0 (2023-04-08)
- Added `depth` parameter to SMT constructors in `MerkleStore` (#115).
- Optimized MMR peak hashing for Miden VM (#120).
- Added `get_leaf_depth` method to `MerkleStore` (#119).
- Added inner node iterators to `MerkleTree`, `SimpleSmt`, and `Mmr` (#117, #118, #121).
## 0.2.0 (2023-03-24)
- Implemented `Mmr` and related structs (#67).
- Implemented `MerkleStore` (#93, #94, #95, #107 #112).
- Added benchmarks for `MerkleStore` vs. other structs (#97).
- Added Merkle path containers (#99).
- Fixed depth handling in `MerklePathSet` (#110).
- Updated Winterfell dependency to v0.6.
## 0.1.4 (2023-02-22)
- Re-export winter-crypto Hasher, Digest & ElementHasher (#72)
## 0.1.3 (2023-02-20) ## 0.1.3 (2023-02-20)
- Updated Winterfell dependency to v0.5.1 (#68) - Updated Winterfell dependency to v0.5.1 (#68)

View File

@@ -1,14 +1,16 @@
[package] [package]
name = "miden-crypto" name = "miden-crypto"
version = "0.1.3" version = "0.6.0"
description = "Miden Cryptographic primitives" description = "Miden Cryptographic primitives"
authors = ["miden contributors"] authors = ["miden contributors"]
readme = "README.md" readme = "README.md"
license = "MIT" license = "MIT"
repository = "https://github.com/0xPolygonMiden/crypto" repository = "https://github.com/0xPolygonMiden/crypto"
documentation = "https://docs.rs/miden-crypto/0.6.0"
categories = ["cryptography", "no-std"] categories = ["cryptography", "no-std"]
keywords = ["miden", "crypto", "hash", "merkle"] keywords = ["miden", "crypto", "hash", "merkle"]
edition = "2021" edition = "2021"
rust-version = "1.67"
[[bench]] [[bench]]
name = "hash" name = "hash"
@@ -18,17 +20,21 @@ harness = false
name = "smt" name = "smt"
harness = false harness = false
[[bench]]
name = "store"
harness = false
[features] [features]
default = ["blake3/default", "std", "winter_crypto/default", "winter_math/default", "winter_utils/default"] default = ["blake3/default", "std", "winter_crypto/default", "winter_math/default", "winter_utils/default"]
std = ["blake3/std", "winter_crypto/std", "winter_math/std", "winter_utils/std"] std = ["blake3/std", "winter_crypto/std", "winter_math/std", "winter_utils/std"]
[dependencies] [dependencies]
blake3 = { version = "1.0", default-features = false } blake3 = { version = "1.3", default-features = false }
winter_crypto = { version = "0.5.1", package = "winter-crypto", default-features = false } winter_crypto = { version = "0.6", package = "winter-crypto", default-features = false }
winter_math = { version = "0.5.1", package = "winter-math", default-features = false } winter_math = { version = "0.6", package = "winter-math", default-features = false }
winter_utils = { version = "0.5.1", package = "winter-utils", default-features = false } winter_utils = { version = "0.6", package = "winter-utils", default-features = false }
[dev-dependencies] [dev-dependencies]
criterion = { version = "0.4", features = ["html_reports"] } criterion = { version = "0.5", features = ["html_reports"] }
proptest = "1.0.0" proptest = "1.1.0"
rand_utils = { version = "0.4", package = "winter-rand-utils" } rand_utils = { version = "0.6", package = "winter-rand-utils" }

View File

@@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2022 Polygon Miden Copyright (c) 2023 Polygon Miden
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@@ -12,15 +12,16 @@ For performance benchmarks of these hash functions and their comparison to other
## Merkle ## Merkle
[Merkle module](./src/merkle/) provides a set of data structures related to Merkle trees. All these data structures are implemented using the RPO hash function described above. The data structures are: [Merkle module](./src/merkle/) provides a set of data structures related to Merkle trees. All these data structures are implemented using the RPO hash function described above. The data structures are:
* `Mmr`: a Merkle mountain range structure designed to function as an append-only log.
* `MerkleTree`: a regular fully-balanced binary Merkle tree. The depth of this tree can be at most 64. * `MerkleTree`: a regular fully-balanced binary Merkle tree. The depth of this tree can be at most 64.
* `SimpleSmt`: a Sparse Merkle Tree, mapping 63-bit keys to 4-element leaf values.
* `MerklePathSet`: a collection of Merkle authentication paths all resolving to the same root. The length of the paths can be at most 64. * `MerklePathSet`: a collection of Merkle authentication paths all resolving to the same root. The length of the paths can be at most 64.
* `MerkleStore`: a collection of Merkle trees of different heights designed to efficiently store trees with common subtrees. When instantiated with `RecordingMap`, a Merkle store records all accesses to the original data.
* `PartialMerkleTree`: a partial view of a Merkle tree where some sub-trees may not be known. This is similar to a collection of Merkle paths all resolving to the same root. The length of the paths can be at most 64.
* `SimpleSmt`: a Sparse Merkle Tree (with no compaction), mapping 64-bit keys to 4-element values.
* `TieredSmt`: a Sparse Merkle tree (with compaction), mapping 4-element keys to 4-element values.
The module also contains additional supporting components such as `NodeIndex`, `MerklePath`, and `MerkleError` to assist with tree indexation, opening proofs, and reporting inconsistent arguments/state. The module also contains additional supporting components such as `NodeIndex`, `MerklePath`, and `MerkleError` to assist with tree indexation, opening proofs, and reporting inconsistent arguments/state.
## Extra
[Root module](./src/lib.rs) provides a set of constants, types, aliases, and utils required to use the primitives of this library.
## Crate features ## Crate features
This crate can be compiled with the following features: This crate can be compiled with the following features:

View File

@@ -28,7 +28,7 @@ The second scenario is that of sequential hashing where we take a sequence of le
| Function | BLAKE3 | SHA3 | Poseidon | Rp64_256 | RPO_256 | | Function | BLAKE3 | SHA3 | Poseidon | Rp64_256 | RPO_256 |
| ------------------- | -------| ------- | --------- | --------- | ------- | | ------------------- | -------| ------- | --------- | --------- | ------- |
| Apple M1 Pro | 1.1 us | 1.5 us | 19.4 us | 118 us | 70 us | | Apple M1 Pro | 1.0 us | 1.5 us | 19.4 us | 118 us | 70 us |
| Apple M2 | 1.0 us | 1.5 us | 17.4 us | 103 us | 65 us | | Apple M2 | 1.0 us | 1.5 us | 17.4 us | 103 us | 65 us |
| Amazon Graviton 3 | 1.4 us | | | | 114 us | | Amazon Graviton 3 | 1.4 us | | | | 114 us |
| AMD Ryzen 9 5950X | 0.8 us | 1.7 us | 15.7 us | 120 us | 72 us | | AMD Ryzen 9 5950X | 0.8 us | 1.7 us | 15.7 us | 120 us | 72 us |

View File

@@ -106,11 +106,5 @@ fn blake3_sequential(c: &mut Criterion) {
}); });
} }
criterion_group!( criterion_group!(hash_group, rpo256_2to1, rpo256_sequential, blake3_2to1, blake3_sequential);
hash_group,
rpo256_2to1,
rpo256_sequential,
blake3_2to1,
blake3_sequential
);
criterion_main!(hash_group); criterion_main!(hash_group);

View File

@@ -18,8 +18,8 @@ fn smt_rpo(c: &mut Criterion) {
(i, word) (i, word)
}) })
.collect(); .collect();
let tree = SimpleSmt::new(entries, depth).unwrap(); let tree = SimpleSmt::with_leaves(depth, entries).unwrap();
trees.push(tree); trees.push((tree, count));
} }
} }
@@ -29,10 +29,9 @@ fn smt_rpo(c: &mut Criterion) {
let mut insert = c.benchmark_group(format!("smt update_leaf")); let mut insert = c.benchmark_group(format!("smt update_leaf"));
for tree in trees.iter_mut() { for (tree, count) in trees.iter_mut() {
let depth = tree.depth(); let depth = tree.depth();
let count = tree.leaves_count() as u64; let key = *count >> 2;
let key = count >> 2;
insert.bench_with_input( insert.bench_with_input(
format!("simple smt(depth:{depth},count:{count})"), format!("simple smt(depth:{depth},count:{count})"),
&(key, leaf), &(key, leaf),
@@ -48,10 +47,9 @@ fn smt_rpo(c: &mut Criterion) {
let mut path = c.benchmark_group(format!("smt get_leaf_path")); let mut path = c.benchmark_group(format!("smt get_leaf_path"));
for tree in trees.iter_mut() { for (tree, count) in trees.iter_mut() {
let depth = tree.depth(); let depth = tree.depth();
let count = tree.leaves_count() as u64; let key = *count >> 2;
let key = count >> 2;
path.bench_with_input( path.bench_with_input(
format!("simple smt(depth:{depth},count:{count})"), format!("simple smt(depth:{depth},count:{count})"),
&key, &key,
@@ -75,10 +73,5 @@ criterion_main!(smt_group);
fn generate_word(seed: &mut [u8; 32]) -> Word { fn generate_word(seed: &mut [u8; 32]) -> Word {
swap(seed, &mut prng_array(*seed)); swap(seed, &mut prng_array(*seed));
let nums: [u64; 4] = prng_array(*seed); let nums: [u64; 4] = prng_array(*seed);
[ [Felt::new(nums[0]), Felt::new(nums[1]), Felt::new(nums[2]), Felt::new(nums[3])]
Felt::new(nums[0]),
Felt::new(nums[1]),
Felt::new(nums[2]),
Felt::new(nums[3]),
]
} }

481
benches/store.rs Normal file
View File

@@ -0,0 +1,481 @@
use criterion::{black_box, criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion};
use miden_crypto::merkle::{DefaultMerkleStore as MerkleStore, MerkleTree, NodeIndex, SimpleSmt};
use miden_crypto::Word;
use miden_crypto::{hash::rpo::RpoDigest, Felt};
use rand_utils::{rand_array, rand_value};
/// Since MerkleTree can only be created when a power-of-two number of elements is used, the sample
/// sizes are limited to that.
static BATCH_SIZES: [usize; 3] = [2usize.pow(4), 2usize.pow(7), 2usize.pow(10)];
/// Generates a random `RpoDigest`.
fn random_rpo_digest() -> RpoDigest {
rand_array::<Felt, 4>().into()
}
/// Generates a random `Word`.
fn random_word() -> Word {
rand_array::<Felt, 4>().into()
}
/// Generates an index at the specified depth in `0..range`.
fn random_index(range: u64, depth: u8) -> NodeIndex {
let value = rand_value::<u64>() % range;
NodeIndex::new(depth, value).unwrap()
}
/// Benchmarks getting an empty leaf from the SMT and MerkleStore backends.
fn get_empty_leaf_simplesmt(c: &mut Criterion) {
let mut group = c.benchmark_group("get_empty_leaf_simplesmt");
let depth = SimpleSmt::MAX_DEPTH;
let size = u64::MAX;
// both SMT and the store are pre-populated with empty hashes, accessing these values is what is
// being benchmarked here, so no values are inserted into the backends
let smt = SimpleSmt::new(depth).unwrap();
let store = MerkleStore::from(&smt);
let root = smt.root();
group.bench_function(BenchmarkId::new("SimpleSmt", depth), |b| {
b.iter_batched(
|| random_index(size, depth),
|index| black_box(smt.get_node(index)),
BatchSize::SmallInput,
)
});
group.bench_function(BenchmarkId::new("MerkleStore", depth), |b| {
b.iter_batched(
|| random_index(size, depth),
|index| black_box(store.get_node(root, index)),
BatchSize::SmallInput,
)
});
}
/// Benchmarks getting a leaf on Merkle trees and Merkle stores of varying power-of-two sizes.
fn get_leaf_merkletree(c: &mut Criterion) {
let mut group = c.benchmark_group("get_leaf_merkletree");
let random_data_size = BATCH_SIZES.into_iter().max().unwrap();
let random_data: Vec<RpoDigest> = (0..random_data_size).map(|_| random_rpo_digest()).collect();
for size in BATCH_SIZES {
let leaves = &random_data[..size];
let mtree_leaves: Vec<Word> = leaves.iter().map(|v| v.into()).collect();
let mtree = MerkleTree::new(mtree_leaves.clone()).unwrap();
let store = MerkleStore::from(&mtree);
let depth = mtree.depth();
let root = mtree.root();
let size_u64 = size as u64;
group.bench_function(BenchmarkId::new("MerkleTree", size), |b| {
b.iter_batched(
|| random_index(size_u64, depth),
|index| black_box(mtree.get_node(index)),
BatchSize::SmallInput,
)
});
group.bench_function(BenchmarkId::new("MerkleStore", size), |b| {
b.iter_batched(
|| random_index(size_u64, depth),
|index| black_box(store.get_node(root, index)),
BatchSize::SmallInput,
)
});
}
}
/// Benchmarks getting a leaf on SMT and Merkle stores of varying power-of-two sizes.
fn get_leaf_simplesmt(c: &mut Criterion) {
let mut group = c.benchmark_group("get_leaf_simplesmt");
let random_data_size = BATCH_SIZES.into_iter().max().unwrap();
let random_data: Vec<RpoDigest> = (0..random_data_size).map(|_| random_rpo_digest()).collect();
for size in BATCH_SIZES {
let leaves = &random_data[..size];
let smt_leaves = leaves
.iter()
.enumerate()
.map(|(c, v)| (c.try_into().unwrap(), v.into()))
.collect::<Vec<(u64, Word)>>();
let smt = SimpleSmt::with_leaves(SimpleSmt::MAX_DEPTH, smt_leaves.clone()).unwrap();
let store = MerkleStore::from(&smt);
let depth = smt.depth();
let root = smt.root();
let size_u64 = size as u64;
group.bench_function(BenchmarkId::new("SimpleSmt", size), |b| {
b.iter_batched(
|| random_index(size_u64, depth),
|index| black_box(smt.get_node(index)),
BatchSize::SmallInput,
)
});
group.bench_function(BenchmarkId::new("MerkleStore", size), |b| {
b.iter_batched(
|| random_index(size_u64, depth),
|index| black_box(store.get_node(root, index)),
BatchSize::SmallInput,
)
});
}
}
/// Benchmarks getting a node at half of the depth of an empty SMT and an empty Merkle store.
fn get_node_of_empty_simplesmt(c: &mut Criterion) {
let mut group = c.benchmark_group("get_node_of_empty_simplesmt");
let depth = SimpleSmt::MAX_DEPTH;
// both SMT and the store are pre-populated with the empty hashes, accessing the internal nodes
// of these values is what is being benchmarked here, so no values are inserted into the
// backends.
let smt = SimpleSmt::new(depth).unwrap();
let store = MerkleStore::from(&smt);
let root = smt.root();
let half_depth = depth / 2;
let half_size = 2_u64.pow(half_depth as u32);
group.bench_function(BenchmarkId::new("SimpleSmt", depth), |b| {
b.iter_batched(
|| random_index(half_size, half_depth),
|index| black_box(smt.get_node(index)),
BatchSize::SmallInput,
)
});
group.bench_function(BenchmarkId::new("MerkleStore", depth), |b| {
b.iter_batched(
|| random_index(half_size, half_depth),
|index| black_box(store.get_node(root, index)),
BatchSize::SmallInput,
)
});
}
/// Benchmarks getting a node at half of the depth of a Merkle tree and Merkle store of varying
/// power-of-two sizes.
fn get_node_merkletree(c: &mut Criterion) {
let mut group = c.benchmark_group("get_node_merkletree");
let random_data_size = BATCH_SIZES.into_iter().max().unwrap();
let random_data: Vec<RpoDigest> = (0..random_data_size).map(|_| random_rpo_digest()).collect();
for size in BATCH_SIZES {
let leaves = &random_data[..size];
let mtree_leaves: Vec<Word> = leaves.iter().map(|v| v.into()).collect();
let mtree = MerkleTree::new(mtree_leaves.clone()).unwrap();
let store = MerkleStore::from(&mtree);
let root = mtree.root();
let half_depth = mtree.depth() / 2;
let half_size = 2_u64.pow(half_depth as u32);
group.bench_function(BenchmarkId::new("MerkleTree", size), |b| {
b.iter_batched(
|| random_index(half_size, half_depth),
|index| black_box(mtree.get_node(index)),
BatchSize::SmallInput,
)
});
group.bench_function(BenchmarkId::new("MerkleStore", size), |b| {
b.iter_batched(
|| random_index(half_size, half_depth),
|index| black_box(store.get_node(root, index)),
BatchSize::SmallInput,
)
});
}
}
/// Benchmarks getting a node at half the depth on SMT and Merkle stores of varying power-of-two
/// sizes.
fn get_node_simplesmt(c: &mut Criterion) {
let mut group = c.benchmark_group("get_node_simplesmt");
let random_data_size = BATCH_SIZES.into_iter().max().unwrap();
let random_data: Vec<RpoDigest> = (0..random_data_size).map(|_| random_rpo_digest()).collect();
for size in BATCH_SIZES {
let leaves = &random_data[..size];
let smt_leaves = leaves
.iter()
.enumerate()
.map(|(c, v)| (c.try_into().unwrap(), v.into()))
.collect::<Vec<(u64, Word)>>();
let smt = SimpleSmt::with_leaves(SimpleSmt::MAX_DEPTH, smt_leaves.clone()).unwrap();
let store = MerkleStore::from(&smt);
let root = smt.root();
let half_depth = smt.depth() / 2;
let half_size = 2_u64.pow(half_depth as u32);
group.bench_function(BenchmarkId::new("SimpleSmt", size), |b| {
b.iter_batched(
|| random_index(half_size, half_depth),
|index| black_box(smt.get_node(index)),
BatchSize::SmallInput,
)
});
group.bench_function(BenchmarkId::new("MerkleStore", size), |b| {
b.iter_batched(
|| random_index(half_size, half_depth),
|index| black_box(store.get_node(root, index)),
BatchSize::SmallInput,
)
});
}
}
/// Benchmarks getting a path of a leaf on the Merkle tree and Merkle store backends.
fn get_leaf_path_merkletree(c: &mut Criterion) {
let mut group = c.benchmark_group("get_leaf_path_merkletree");
let random_data_size = BATCH_SIZES.into_iter().max().unwrap();
let random_data: Vec<RpoDigest> = (0..random_data_size).map(|_| random_rpo_digest()).collect();
for size in BATCH_SIZES {
let leaves = &random_data[..size];
let mtree_leaves: Vec<Word> = leaves.iter().map(|v| v.into()).collect();
let mtree = MerkleTree::new(mtree_leaves.clone()).unwrap();
let store = MerkleStore::from(&mtree);
let depth = mtree.depth();
let root = mtree.root();
let size_u64 = size as u64;
group.bench_function(BenchmarkId::new("MerkleTree", size), |b| {
b.iter_batched(
|| random_index(size_u64, depth),
|index| black_box(mtree.get_path(index)),
BatchSize::SmallInput,
)
});
group.bench_function(BenchmarkId::new("MerkleStore", size), |b| {
b.iter_batched(
|| random_index(size_u64, depth),
|index| black_box(store.get_path(root, index)),
BatchSize::SmallInput,
)
});
}
}
/// Benchmarks getting a path of a leaf on the SMT and Merkle store backends.
fn get_leaf_path_simplesmt(c: &mut Criterion) {
let mut group = c.benchmark_group("get_leaf_path_simplesmt");
let random_data_size = BATCH_SIZES.into_iter().max().unwrap();
let random_data: Vec<RpoDigest> = (0..random_data_size).map(|_| random_rpo_digest()).collect();
for size in BATCH_SIZES {
let leaves = &random_data[..size];
let smt_leaves = leaves
.iter()
.enumerate()
.map(|(c, v)| (c.try_into().unwrap(), v.into()))
.collect::<Vec<(u64, Word)>>();
let smt = SimpleSmt::with_leaves(SimpleSmt::MAX_DEPTH, smt_leaves.clone()).unwrap();
let store = MerkleStore::from(&smt);
let depth = smt.depth();
let root = smt.root();
let size_u64 = size as u64;
group.bench_function(BenchmarkId::new("SimpleSmt", size), |b| {
b.iter_batched(
|| random_index(size_u64, depth),
|index| black_box(smt.get_path(index)),
BatchSize::SmallInput,
)
});
group.bench_function(BenchmarkId::new("MerkleStore", size), |b| {
b.iter_batched(
|| random_index(size_u64, depth),
|index| black_box(store.get_path(root, index)),
BatchSize::SmallInput,
)
});
}
}
/// Benchmarks creation of the different storage backends
fn new(c: &mut Criterion) {
let mut group = c.benchmark_group("new");
let random_data_size = BATCH_SIZES.into_iter().max().unwrap();
let random_data: Vec<RpoDigest> = (0..random_data_size).map(|_| random_rpo_digest()).collect();
for size in BATCH_SIZES {
let leaves = &random_data[..size];
// MerkleTree constructor is optimized to work with vectors. Create a new copy of the data
// and pass it to the benchmark function
group.bench_function(BenchmarkId::new("MerkleTree::new", size), |b| {
b.iter_batched(
|| leaves.iter().map(|v| v.into()).collect::<Vec<Word>>(),
|l| black_box(MerkleTree::new(l)),
BatchSize::SmallInput,
)
});
// This could be done with `bench_with_input`, however to remove variables while comparing
// with MerkleTree it is using `iter_batched`
group.bench_function(BenchmarkId::new("MerkleStore::extend::MerkleTree", size), |b| {
b.iter_batched(
|| leaves.iter().map(|v| v.into()).collect::<Vec<Word>>(),
|l| {
let mtree = MerkleTree::new(l).unwrap();
black_box(MerkleStore::from(&mtree));
},
BatchSize::SmallInput,
)
});
group.bench_function(BenchmarkId::new("SimpleSmt::new", size), |b| {
b.iter_batched(
|| {
leaves
.iter()
.enumerate()
.map(|(c, v)| (c.try_into().unwrap(), v.into()))
.collect::<Vec<(u64, Word)>>()
},
|l| black_box(SimpleSmt::with_leaves(SimpleSmt::MAX_DEPTH, l)),
BatchSize::SmallInput,
)
});
group.bench_function(BenchmarkId::new("MerkleStore::extend::SimpleSmt", size), |b| {
b.iter_batched(
|| {
leaves
.iter()
.enumerate()
.map(|(c, v)| (c.try_into().unwrap(), v.into()))
.collect::<Vec<(u64, Word)>>()
},
|l| {
let smt = SimpleSmt::with_leaves(SimpleSmt::MAX_DEPTH, l).unwrap();
black_box(MerkleStore::from(&smt));
},
BatchSize::SmallInput,
)
});
}
}
/// Benchmarks updating a leaf on MerkleTree and MerkleStore backends.
fn update_leaf_merkletree(c: &mut Criterion) {
let mut group = c.benchmark_group("update_leaf_merkletree");
let random_data_size = BATCH_SIZES.into_iter().max().unwrap();
let random_data: Vec<RpoDigest> = (0..random_data_size).map(|_| random_rpo_digest()).collect();
for size in BATCH_SIZES {
let leaves = &random_data[..size];
let mtree_leaves: Vec<Word> = leaves.iter().map(|v| v.into()).collect();
let mut mtree = MerkleTree::new(mtree_leaves.clone()).unwrap();
let mut store = MerkleStore::from(&mtree);
let depth = mtree.depth();
let root = mtree.root();
let size_u64 = size as u64;
group.bench_function(BenchmarkId::new("MerkleTree", size), |b| {
b.iter_batched(
|| (rand_value::<u64>() % size_u64, random_word()),
|(index, value)| black_box(mtree.update_leaf(index, value)),
BatchSize::SmallInput,
)
});
let mut store_root = root;
group.bench_function(BenchmarkId::new("MerkleStore", size), |b| {
b.iter_batched(
|| (random_index(size_u64, depth), random_word()),
|(index, value)| {
// The MerkleTree automatically updates its internal root, the Store maintains
// the old root and adds the new one. Here we update the root to have a fair
// comparison
store_root = store.set_node(root, index, value.into()).unwrap().root;
black_box(store_root)
},
BatchSize::SmallInput,
)
});
}
}
/// Benchmarks updating a leaf on SMT and MerkleStore backends.
fn update_leaf_simplesmt(c: &mut Criterion) {
let mut group = c.benchmark_group("update_leaf_simplesmt");
let random_data_size = BATCH_SIZES.into_iter().max().unwrap();
let random_data: Vec<RpoDigest> = (0..random_data_size).map(|_| random_rpo_digest()).collect();
for size in BATCH_SIZES {
let leaves = &random_data[..size];
let smt_leaves = leaves
.iter()
.enumerate()
.map(|(c, v)| (c.try_into().unwrap(), v.into()))
.collect::<Vec<(u64, Word)>>();
let mut smt = SimpleSmt::with_leaves(SimpleSmt::MAX_DEPTH, smt_leaves.clone()).unwrap();
let mut store = MerkleStore::from(&smt);
let depth = smt.depth();
let root = smt.root();
let size_u64 = size as u64;
group.bench_function(BenchmarkId::new("SimpleSMT", size), |b| {
b.iter_batched(
|| (rand_value::<u64>() % size_u64, random_word()),
|(index, value)| black_box(smt.update_leaf(index, value)),
BatchSize::SmallInput,
)
});
let mut store_root = root;
group.bench_function(BenchmarkId::new("MerkleStore", size), |b| {
b.iter_batched(
|| (random_index(size_u64, depth), random_word()),
|(index, value)| {
// The MerkleTree automatically updates its internal root, the Store maintains
// the old root and adds the new one. Here we update the root to have a fair
// comparison
store_root = store.set_node(root, index, value.into()).unwrap().root;
black_box(store_root)
},
BatchSize::SmallInput,
)
});
}
}
criterion_group!(
store_group,
get_empty_leaf_simplesmt,
get_leaf_merkletree,
get_leaf_path_merkletree,
get_leaf_path_simplesmt,
get_leaf_simplesmt,
get_node_merkletree,
get_node_of_empty_simplesmt,
get_node_simplesmt,
new,
update_leaf_merkletree,
update_leaf_simplesmt,
);
criterion_main!(store_group);

20
rustfmt.toml Normal file
View File

@@ -0,0 +1,20 @@
edition = "2021"
array_width = 80
attr_fn_like_width = 80
chain_width = 80
#condense_wildcard_suffixes = true
#enum_discrim_align_threshold = 40
fn_call_width = 80
#fn_single_line = true
#format_code_in_doc_comments = true
#format_macro_matchers = true
#format_strings = true
#group_imports = "StdExternalCrate"
#hex_literal_case = "Lower"
#imports_granularity = "Crate"
newline_style = "Unix"
#normalize_doc_attributes = true
#reorder_impl_items = true
single_line_if_else_max_width = 60
use_field_init_shorthand = true
use_try_shorthand = true

View File

@@ -1,7 +1,5 @@
use super::{Digest, ElementHasher, Felt, FieldElement, Hasher, StarkField}; use super::{Digest, ElementHasher, Felt, FieldElement, Hasher, StarkField};
use crate::utils::{ use crate::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable};
uninit_vector, ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable,
};
use core::{ use core::{
mem::{size_of, transmute, transmute_copy}, mem::{size_of, transmute, transmute_copy},
ops::Deref, ops::Deref,
@@ -78,6 +76,7 @@ impl<const N: usize> Digest for Blake3Digest<N> {
// ================================================================================================ // ================================================================================================
/// 256-bit output blake3 hasher. /// 256-bit output blake3 hasher.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct Blake3_256; pub struct Blake3_256;
impl Hasher for Blake3_256 { impl Hasher for Blake3_256 {
@@ -141,6 +140,7 @@ impl Blake3_256 {
// ================================================================================================ // ================================================================================================
/// 192-bit output blake3 hasher. /// 192-bit output blake3 hasher.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct Blake3_192; pub struct Blake3_192;
impl Hasher for Blake3_192 { impl Hasher for Blake3_192 {
@@ -204,6 +204,7 @@ impl Blake3_192 {
// ================================================================================================ // ================================================================================================
/// 160-bit output blake3 hasher. /// 160-bit output blake3 hasher.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct Blake3_160; pub struct Blake3_160;
impl Hasher for Blake3_160 { impl Hasher for Blake3_160 {
@@ -269,10 +270,7 @@ impl Blake3_160 {
/// Zero-copy ref shrink to array. /// Zero-copy ref shrink to array.
fn shrink_bytes<const M: usize, const N: usize>(bytes: &[u8; M]) -> &[u8; N] { fn shrink_bytes<const M: usize, const N: usize>(bytes: &[u8; M]) -> &[u8; N] {
// compile-time assertion // compile-time assertion
assert!( assert!(M >= N, "N should fit in M so it can be safely transmuted into a smaller slice!");
M >= N,
"N should fit in M so it can be safely transmuted into a smaller slice!"
);
// safety: bytes len is asserted // safety: bytes len is asserted
unsafe { transmute(bytes) } unsafe { transmute(bytes) }
} }
@@ -287,15 +285,25 @@ where
let digest = if Felt::IS_CANONICAL { let digest = if Felt::IS_CANONICAL {
blake3::hash(E::elements_as_bytes(elements)) blake3::hash(E::elements_as_bytes(elements))
} else { } else {
let base_elements = E::as_base_elements(elements); let mut hasher = blake3::Hasher::new();
let blen = base_elements.len() << 3;
let mut bytes = unsafe { uninit_vector(blen) }; // BLAKE3 state is 64 bytes - so, we can absorb 64 bytes into the state in a single
for (idx, element) in base_elements.iter().enumerate() { // permutation. we move the elements into the hasher via the buffer to give the CPU
bytes[idx * 8..(idx + 1) * 8].copy_from_slice(&element.as_int().to_le_bytes()); // a chance to process multiple element-to-byte conversions in parallel
let mut buf = [0_u8; 64];
let mut chunk_iter = E::slice_as_base_elements(elements).chunks_exact(8);
for chunk in chunk_iter.by_ref() {
for i in 0..8 {
buf[i * 8..(i + 1) * 8].copy_from_slice(&chunk[i].as_int().to_le_bytes());
}
hasher.update(&buf);
} }
blake3::hash(&bytes) for element in chunk_iter.remainder() {
hasher.update(&element.as_int().to_le_bytes());
}
hasher.finalize()
}; };
*shrink_bytes(&digest.into()) *shrink_bytes(&digest.into())
} }

View File

@@ -1,6 +1,22 @@
use super::*; use super::*;
use crate::utils::collections::Vec; use crate::utils::collections::Vec;
use proptest::prelude::*; use proptest::prelude::*;
use rand_utils::rand_vector;
#[test]
fn blake3_hash_elements() {
// test multiple of 8
let elements = rand_vector::<Felt>(16);
let expected = compute_expected_element_hash(&elements);
let actual: [u8; 32] = hash_elements(&elements);
assert_eq!(&expected, &actual);
// test not multiple of 8
let elements = rand_vector::<Felt>(17);
let expected = compute_expected_element_hash(&elements);
let actual: [u8; 32] = hash_elements(&elements);
assert_eq!(&expected, &actual);
}
proptest! { proptest! {
#[test] #[test]
@@ -18,3 +34,14 @@ proptest! {
Blake3_256::hash(vec); Blake3_256::hash(vec);
} }
} }
// HELPER FUNCTIONS
// ================================================================================================
fn compute_expected_element_hash(elements: &[Felt]) -> blake3::Hash {
let mut bytes = Vec::new();
for element in elements.iter() {
bytes.extend_from_slice(&element.as_int().to_le_bytes());
}
blake3::hash(&bytes)
}

View File

@@ -1,5 +1,9 @@
use super::{Felt, FieldElement, StarkField, ONE, ZERO}; use super::{Felt, FieldElement, StarkField, ONE, ZERO};
use winter_crypto::{Digest, ElementHasher, Hasher};
pub mod blake; pub mod blake;
pub mod rpo; pub mod rpo;
// RE-EXPORTS
// ================================================================================================
pub use winter_crypto::{Digest, ElementHasher, Hasher};

View File

@@ -2,7 +2,7 @@ use super::{Digest, Felt, StarkField, DIGEST_SIZE, ZERO};
use crate::utils::{ use crate::utils::{
string::String, ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable, string::String, ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable,
}; };
use core::{cmp::Ordering, ops::Deref}; use core::{cmp::Ordering, fmt::Display, ops::Deref};
// DIGEST TRAIT IMPLEMENTATIONS // DIGEST TRAIT IMPLEMENTATIONS
// ================================================================================================ // ================================================================================================
@@ -11,7 +11,7 @@ use core::{cmp::Ordering, ops::Deref};
pub struct RpoDigest([Felt; DIGEST_SIZE]); pub struct RpoDigest([Felt; DIGEST_SIZE]);
impl RpoDigest { impl RpoDigest {
pub fn new(value: [Felt; DIGEST_SIZE]) -> Self { pub const fn new(value: [Felt; DIGEST_SIZE]) -> Self {
Self(value) Self(value)
} }
@@ -73,12 +73,46 @@ impl From<[Felt; DIGEST_SIZE]> for RpoDigest {
} }
} }
impl From<&RpoDigest> for [Felt; DIGEST_SIZE] {
fn from(value: &RpoDigest) -> Self {
value.0
}
}
impl From<RpoDigest> for [Felt; DIGEST_SIZE] { impl From<RpoDigest> for [Felt; DIGEST_SIZE] {
fn from(value: RpoDigest) -> Self { fn from(value: RpoDigest) -> Self {
value.0 value.0
} }
} }
impl From<&RpoDigest> for [u64; DIGEST_SIZE] {
fn from(value: &RpoDigest) -> Self {
[
value.0[0].as_int(),
value.0[1].as_int(),
value.0[2].as_int(),
value.0[3].as_int(),
]
}
}
impl From<RpoDigest> for [u64; DIGEST_SIZE] {
fn from(value: RpoDigest) -> Self {
[
value.0[0].as_int(),
value.0[1].as_int(),
value.0[2].as_int(),
value.0[3].as_int(),
]
}
}
impl From<&RpoDigest> for [u8; 32] {
fn from(value: &RpoDigest) -> Self {
value.as_bytes()
}
}
impl From<RpoDigest> for [u8; 32] { impl From<RpoDigest> for [u8; 32] {
fn from(value: RpoDigest) -> Self { fn from(value: RpoDigest) -> Self {
value.as_bytes() value.as_bytes()
@@ -106,14 +140,13 @@ impl Ord for RpoDigest {
// finally, we use `Felt::inner` instead of `Felt::as_int` so we avoid performing a // finally, we use `Felt::inner` instead of `Felt::as_int` so we avoid performing a
// montgomery reduction for every limb. that is safe because every inner element of the // montgomery reduction for every limb. that is safe because every inner element of the
// digest is guaranteed to be in its canonical form (that is, `x in [0,p)`). // digest is guaranteed to be in its canonical form (that is, `x in [0,p)`).
self.0 self.0.iter().map(Felt::inner).zip(other.0.iter().map(Felt::inner)).fold(
.iter() Ordering::Equal,
.map(Felt::inner) |ord, (a, b)| match ord {
.zip(other.0.iter().map(Felt::inner))
.fold(Ordering::Equal, |ord, (a, b)| match ord {
Ordering::Equal => a.cmp(&b), Ordering::Equal => a.cmp(&b),
_ => ord, _ => ord,
}) },
)
} }
} }
@@ -123,6 +156,15 @@ impl PartialOrd for RpoDigest {
} }
} }
impl Display for RpoDigest {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
for byte in self.as_bytes() {
write!(f, "{byte:02x}")?;
}
Ok(())
}
}
// TESTS // TESTS
// ================================================================================================ // ================================================================================================

View File

@@ -88,6 +88,7 @@ const INV_ALPHA: u64 = 10540996611094048183;
/// to deserialize them into field elements and then hash them using /// to deserialize them into field elements and then hash them using
/// [hash_elements()](Rpo256::hash_elements) function rather then hashing the serialized bytes /// [hash_elements()](Rpo256::hash_elements) function rather then hashing the serialized bytes
/// using [hash()](Rpo256::hash) function. /// using [hash()](Rpo256::hash) function.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct Rpo256(); pub struct Rpo256();
impl Hasher for Rpo256 { impl Hasher for Rpo256 {
@@ -211,7 +212,7 @@ impl ElementHasher for Rpo256 {
fn hash_elements<E: FieldElement<BaseField = Self::BaseField>>(elements: &[E]) -> Self::Digest { fn hash_elements<E: FieldElement<BaseField = Self::BaseField>>(elements: &[E]) -> Self::Digest {
// convert the elements into a list of base field elements // convert the elements into a list of base field elements
let elements = E::as_base_elements(elements); let elements = E::slice_as_base_elements(elements);
// initialize state to all zeros, except for the first element of the capacity part, which // initialize state to all zeros, except for the first element of the capacity part, which
// is set to 1 if the number of elements is not a multiple of RATE_WIDTH. // is set to 1 if the number of elements is not a multiple of RATE_WIDTH.

View File

@@ -2,7 +2,10 @@ use super::{
Felt, FieldElement, Hasher, Rpo256, RpoDigest, StarkField, ALPHA, INV_ALPHA, ONE, STATE_WIDTH, Felt, FieldElement, Hasher, Rpo256, RpoDigest, StarkField, ALPHA, INV_ALPHA, ONE, STATE_WIDTH,
ZERO, ZERO,
}; };
use crate::utils::collections::{BTreeSet, Vec}; use crate::{
utils::collections::{BTreeSet, Vec},
Word,
};
use core::convert::TryInto; use core::convert::TryInto;
use proptest::prelude::*; use proptest::prelude::*;
use rand_utils::rand_value; use rand_utils::rand_value;
@@ -203,7 +206,7 @@ fn sponge_bytes_with_remainder_length_wont_panic() {
// size. // size.
// //
// this is a preliminary test to the fuzzy-stress of proptest. // this is a preliminary test to the fuzzy-stress of proptest.
Rpo256::hash(&vec![0; 113]); Rpo256::hash(&[0; 113]);
} }
#[test] #[test]
@@ -227,12 +230,12 @@ fn sponge_zeroes_collision() {
proptest! { proptest! {
#[test] #[test]
fn rpo256_wont_panic_with_arbitrary_input(ref vec in any::<Vec<u8>>()) { fn rpo256_wont_panic_with_arbitrary_input(ref bytes in any::<Vec<u8>>()) {
Rpo256::hash(&vec); Rpo256::hash(bytes);
} }
} }
const EXPECTED: [[Felt; 4]; 19] = [ const EXPECTED: [Word; 19] = [
[ [
Felt::new(1502364727743950833), Felt::new(1502364727743950833),
Felt::new(5880949717274681448), Felt::new(5880949717274681448),

View File

@@ -6,21 +6,14 @@ extern crate alloc;
pub mod hash; pub mod hash;
pub mod merkle; pub mod merkle;
pub mod utils;
// RE-EXPORTS // RE-EXPORTS
// ================================================================================================ // ================================================================================================
pub use winter_crypto::{RandomCoin, RandomCoinError}; pub use winter_crypto::{RandomCoin, RandomCoinError};
pub use winter_math::{fields::f64::BaseElement as Felt, FieldElement, StarkField}; pub use winter_math::{fields::f64::BaseElement as Felt, FieldElement, StarkField};
pub mod utils {
pub use winter_utils::{
collections, string, uninit_vector, ByteReader, ByteWriter, Deserializable,
DeserializationError, Serializable, SliceReader,
};
}
// TYPE ALIASES // TYPE ALIASES
// ================================================================================================ // ================================================================================================

1591
src/merkle/empty_roots.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,24 @@
use super::{Felt, MerkleError, RpoDigest, StarkField}; use super::{Felt, MerkleError, RpoDigest, StarkField};
use core::fmt::Display;
// NODE INDEX // NODE INDEX
// ================================================================================================ // ================================================================================================
/// A Merkle tree address to an arbitrary node. /// Address to an arbitrary node in a binary tree using level order form.
///
/// The position is represented by the pair `(depth, pos)`, where for a given depth `d` elements
/// are numbered from $0..(2^d)-1$. Example:
///
/// ```ignore
/// depth
/// 0 0
/// 1 0 1
/// 2 0 1 2 3
/// 3 0 1 2 3 4 5 6 7
/// ```
///
/// The root is represented by the pair $(0, 0)$, its left child is $(1, 0)$ and its right child
/// $(1, 1)$.
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)] #[derive(Debug, Default, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
pub struct NodeIndex { pub struct NodeIndex {
depth: u8, depth: u8,
@@ -15,20 +30,43 @@ impl NodeIndex {
// -------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------
/// Creates a new node index. /// Creates a new node index.
pub const fn new(depth: u8, value: u64) -> Self { ///
/// # Errors
/// Returns an error if the `value` is greater than or equal to 2^{depth}.
pub const fn new(depth: u8, value: u64) -> Result<Self, MerkleError> {
if (64 - value.leading_zeros()) > depth as u32 {
Err(MerkleError::InvalidIndex { depth, value })
} else {
Ok(Self { depth, value })
}
}
/// Creates a new node index without checking its validity.
pub const fn new_unchecked(depth: u8, value: u64) -> Self {
debug_assert!((64 - value.leading_zeros()) <= depth as u32);
Self { depth, value } Self { depth, value }
} }
/// Creates a new node index for testing purposes.
///
/// # Panics
/// Panics if the `value` is greater than or equal to 2^{depth}.
#[cfg(test)]
pub fn make(depth: u8, value: u64) -> Self {
Self::new(depth, value).unwrap()
}
/// Creates a node index from a pair of field elements representing the depth and value. /// Creates a node index from a pair of field elements representing the depth and value.
/// ///
/// # Errors /// # Errors
/// /// Returns an error if:
/// Will error if the `u64` representation of the depth doesn't fit a `u8`. /// - `depth` doesn't fit in a `u8`.
/// - `value` is greater than or equal to 2^{depth}.
pub fn from_elements(depth: &Felt, value: &Felt) -> Result<Self, MerkleError> { pub fn from_elements(depth: &Felt, value: &Felt) -> Result<Self, MerkleError> {
let depth = depth.as_int(); let depth = depth.as_int();
let depth = u8::try_from(depth).map_err(|_| MerkleError::DepthTooBig(depth))?; let depth = u8::try_from(depth).map_err(|_| MerkleError::DepthTooBig(depth))?;
let value = value.as_int(); let value = value.as_int();
Ok(Self::new(depth, value)) Self::new(depth, value)
} }
/// Creates a new node index pointing to the root of the tree. /// Creates a new node index pointing to the root of the tree.
@@ -36,15 +74,23 @@ impl NodeIndex {
Self { depth: 0, value: 0 } Self { depth: 0, value: 0 }
} }
/// Mutates the instance and returns it, replacing the depth. /// Computes sibling index of the current node.
pub const fn with_depth(mut self, depth: u8) -> Self { pub const fn sibling(mut self) -> Self {
self.depth = depth; self.value ^= 1;
self self
} }
/// Computes the value of the sibling of the current node. /// Returns left child index of the current node.
pub fn sibling(mut self) -> Self { pub const fn left_child(mut self) -> Self {
self.value ^= 1; self.depth += 1;
self.value <<= 1;
self
}
/// Returns right child index of the current node.
pub const fn right_child(mut self) -> Self {
self.depth += 1;
self.value = (self.value << 1) + 1;
self self
} }
@@ -74,16 +120,11 @@ impl NodeIndex {
self.depth self.depth
} }
/// Returns the value of the current depth. /// Returns the value of this index.
pub const fn value(&self) -> u64 { pub const fn value(&self) -> u64 {
self.value self.value
} }
/// Returns true if the current value fits the current depth for a binary tree.
pub const fn is_valid(&self) -> bool {
self.value < (1 << self.depth as u64)
}
/// Returns true if the current instance points to a right sibling node. /// Returns true if the current instance points to a right sibling node.
pub const fn is_value_odd(&self) -> bool { pub const fn is_value_odd(&self) -> bool {
(self.value & 1) == 1 (self.value & 1) == 1
@@ -97,11 +138,26 @@ impl NodeIndex {
// STATE MUTATORS // STATE MUTATORS
// -------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------
/// Traverse one level towards the root, decrementing the depth by `1`. /// Traverses one level towards the root, decrementing the depth by `1`.
pub fn move_up(&mut self) -> &mut Self { pub fn move_up(&mut self) {
self.depth = self.depth.saturating_sub(1); self.depth = self.depth.saturating_sub(1);
self.value >>= 1; self.value >>= 1;
self }
/// Traverses towards the root until the specified depth is reached.
///
/// Assumes that the specified depth is smaller than the current depth.
pub fn move_up_to(&mut self, depth: u8) {
debug_assert!(depth < self.depth);
let delta = self.depth.saturating_sub(depth);
self.depth = self.depth.saturating_sub(delta);
self.value >>= delta as u32;
}
}
impl Display for NodeIndex {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "depth={}, value={}", self.depth, self.value)
} }
} }
@@ -110,14 +166,40 @@ mod tests {
use super::*; use super::*;
use proptest::prelude::*; use proptest::prelude::*;
#[test]
fn test_node_index_value_too_high() {
assert_eq!(NodeIndex::new(0, 0).unwrap(), NodeIndex { depth: 0, value: 0 });
match NodeIndex::new(0, 1) {
Err(MerkleError::InvalidIndex { depth, value }) => {
assert_eq!(depth, 0);
assert_eq!(value, 1);
}
_ => unreachable!(),
}
}
#[test]
fn test_node_index_can_represent_depth_64() {
assert!(NodeIndex::new(64, u64::MAX).is_ok());
}
prop_compose! {
fn node_index()(value in 0..2u64.pow(u64::BITS - 1)) -> NodeIndex {
// unwrap never panics because the range of depth is 0..u64::BITS
let mut depth = value.ilog2() as u8;
if value > (1 << depth) { // round up
depth += 1;
}
NodeIndex::new(depth, value).unwrap()
}
}
proptest! { proptest! {
#[test] #[test]
fn arbitrary_index_wont_panic_on_move_up( fn arbitrary_index_wont_panic_on_move_up(
depth in prop::num::u8::ANY, mut index in node_index(),
value in prop::num::u64::ANY,
count in prop::num::u8::ANY, count in prop::num::u8::ANY,
) { ) {
let mut index = NodeIndex::new(depth, value);
for _ in 0..count { for _ in 0..count {
index.move_up(); index.move_up();
} }

View File

@@ -1,6 +1,6 @@
use super::{Felt, MerkleError, MerklePath, NodeIndex, Rpo256, RpoDigest, Vec, Word}; use super::{InnerNodeInfo, MerkleError, MerklePath, NodeIndex, Rpo256, RpoDigest, Vec, Word};
use crate::{utils::uninit_vector, FieldElement}; use crate::utils::{string::String, uninit_vector, word_to_hex};
use core::slice; use core::{fmt, ops::Deref, slice};
use winter_math::log2; use winter_math::log2;
// MERKLE TREE // MERKLE TREE
@@ -9,7 +9,7 @@ use winter_math::log2;
/// A fully-balanced binary Merkle tree (i.e., a tree where the number of leaves is a power of two). /// A fully-balanced binary Merkle tree (i.e., a tree where the number of leaves is a power of two).
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct MerkleTree { pub struct MerkleTree {
nodes: Vec<Word>, nodes: Vec<RpoDigest>,
} }
impl MerkleTree { impl MerkleTree {
@@ -29,10 +29,12 @@ impl MerkleTree {
// create un-initialized vector to hold all tree nodes // create un-initialized vector to hold all tree nodes
let mut nodes = unsafe { uninit_vector(2 * n) }; let mut nodes = unsafe { uninit_vector(2 * n) };
nodes[0] = [Felt::ZERO; 4]; nodes[0] = RpoDigest::default();
// copy leaves into the second part of the nodes vector // copy leaves into the second part of the nodes vector
nodes[n..].copy_from_slice(&leaves); nodes[n..].iter_mut().zip(leaves).for_each(|(node, leaf)| {
*node = RpoDigest::from(leaf);
});
// re-interpret nodes as an array of two nodes fused together // re-interpret nodes as an array of two nodes fused together
// Safety: `nodes` will never move here as it is not bound to an external lifetime (i.e. // Safety: `nodes` will never move here as it is not bound to an external lifetime (i.e.
@@ -42,7 +44,7 @@ impl MerkleTree {
// calculate all internal tree nodes // calculate all internal tree nodes
for i in (1..n).rev() { for i in (1..n).rev() {
nodes[i] = Rpo256::merge(&pairs[i]).into(); nodes[i] = Rpo256::merge(&pairs[i]);
} }
Ok(Self { nodes }) Ok(Self { nodes })
@@ -52,7 +54,7 @@ impl MerkleTree {
// -------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------
/// Returns the root of this Merkle tree. /// Returns the root of this Merkle tree.
pub fn root(&self) -> Word { pub fn root(&self) -> RpoDigest {
self.nodes[1] self.nodes[1]
} }
@@ -69,13 +71,11 @@ impl MerkleTree {
/// Returns an error if: /// Returns an error if:
/// * The specified depth is greater than the depth of the tree. /// * The specified depth is greater than the depth of the tree.
/// * The specified index is not valid for the specified depth. /// * The specified index is not valid for the specified depth.
pub fn get_node(&self, index: NodeIndex) -> Result<Word, MerkleError> { pub fn get_node(&self, index: NodeIndex) -> Result<RpoDigest, MerkleError> {
if index.is_root() { if index.is_root() {
return Err(MerkleError::DepthTooSmall(index.depth())); return Err(MerkleError::DepthTooSmall(index.depth()));
} else if index.depth() > self.depth() { } else if index.depth() > self.depth() {
return Err(MerkleError::DepthTooBig(index.depth() as u64)); return Err(MerkleError::DepthTooBig(index.depth() as u64));
} else if !index.is_valid() {
return Err(MerkleError::InvalidIndex(index));
} }
let pos = index.to_scalar_index() as usize; let pos = index.to_scalar_index() as usize;
@@ -94,8 +94,6 @@ impl MerkleTree {
return Err(MerkleError::DepthTooSmall(index.depth())); return Err(MerkleError::DepthTooSmall(index.depth()));
} else if index.depth() > self.depth() { } else if index.depth() > self.depth() {
return Err(MerkleError::DepthTooBig(index.depth() as u64)); return Err(MerkleError::DepthTooBig(index.depth() as u64));
} else if !index.is_valid() {
return Err(MerkleError::InvalidIndex(index));
} }
// TODO should we create a helper in `NodeIndex` that will encapsulate traversal to root so // TODO should we create a helper in `NodeIndex` that will encapsulate traversal to root so
@@ -108,19 +106,43 @@ impl MerkleTree {
index.move_up(); index.move_up();
} }
debug_assert!(index.is_root(), "the path walk must go all the way to the root");
Ok(path.into()) Ok(path.into())
} }
// ITERATORS
// --------------------------------------------------------------------------------------------
/// Returns an iterator over the leaves of this [MerkleTree].
pub fn leaves(&self) -> impl Iterator<Item = (u64, &Word)> {
let leaves_start = self.nodes.len() / 2;
self.nodes
.iter()
.skip(leaves_start)
.enumerate()
.map(|(i, v)| (i as u64, v.deref()))
}
/// Returns n iterator over every inner node of this [MerkleTree].
///
/// The iterator order is unspecified.
pub fn inner_nodes(&self) -> InnerNodeIterator {
InnerNodeIterator {
nodes: &self.nodes,
index: 1, // index 0 is just padding, start at 1
}
}
// STATE MUTATORS
// --------------------------------------------------------------------------------------------
/// Replaces the leaf at the specified index with the provided value. /// Replaces the leaf at the specified index with the provided value.
/// ///
/// # Errors /// # Errors
/// Returns an error if the specified index value is not a valid leaf value for this tree. /// Returns an error if the specified index value is not a valid leaf value for this tree.
pub fn update_leaf<'a>(&'a mut self, index_value: u64, value: Word) -> Result<(), MerkleError> { pub fn update_leaf<'a>(&'a mut self, index_value: u64, value: Word) -> Result<(), MerkleError> {
let depth = self.depth(); let mut index = NodeIndex::new(self.depth(), index_value)?;
let mut index = NodeIndex::new(depth, index_value);
if !index.is_valid() {
return Err(MerkleError::InvalidIndex(index));
}
// we don't need to copy the pairs into a new address as we are logically guaranteed to not // we don't need to copy the pairs into a new address as we are logically guaranteed to not
// overlap write instructions. however, it's important to bind the lifetime of pairs to // overlap write instructions. however, it's important to bind the lifetime of pairs to
@@ -138,13 +160,13 @@ impl MerkleTree {
// update the current node // update the current node
let pos = index.to_scalar_index() as usize; let pos = index.to_scalar_index() as usize;
self.nodes[pos] = value; self.nodes[pos] = value.into();
// traverse to the root, updating each node with the merged values of its parents // traverse to the root, updating each node with the merged values of its parents
for _ in 0..index.depth() { for _ in 0..index.depth() {
index.move_up(); index.move_up();
let pos = index.to_scalar_index() as usize; let pos = index.to_scalar_index() as usize;
let value = Rpo256::merge(&pairs[pos]).into(); let value = Rpo256::merge(&pairs[pos]);
self.nodes[pos] = value; self.nodes[pos] = value;
} }
@@ -152,24 +174,102 @@ impl MerkleTree {
} }
} }
// ITERATORS
// ================================================================================================
/// An iterator over every inner node of the [MerkleTree].
///
/// Use this to extract the data of the tree, there is no guarantee on the order of the elements.
pub struct InnerNodeIterator<'a> {
nodes: &'a Vec<RpoDigest>,
index: usize,
}
impl<'a> Iterator for InnerNodeIterator<'a> {
type Item = InnerNodeInfo;
fn next(&mut self) -> Option<Self::Item> {
if self.index < self.nodes.len() / 2 {
let value = self.index;
let left = self.index * 2;
let right = left + 1;
self.index += 1;
Some(InnerNodeInfo {
value: self.nodes[value],
left: self.nodes[left],
right: self.nodes[right],
})
} else {
None
}
}
}
// UTILITY FUNCTIONS
// ================================================================================================
/// Utility to visualize a [MerkleTree] in text.
pub fn tree_to_text(tree: &MerkleTree) -> Result<String, fmt::Error> {
let indent = " ";
let mut s = String::new();
s.push_str(&word_to_hex(&tree.root())?);
s.push('\n');
for d in 1..=tree.depth() {
let entries = 2u64.pow(d.into());
for i in 0..entries {
let index = NodeIndex::new(d, i).expect("The index must always be valid");
let node = tree.get_node(index).expect("The node must always be found");
for _ in 0..d {
s.push_str(indent);
}
s.push_str(&word_to_hex(&node)?);
s.push('\n');
}
}
Ok(s)
}
/// Utility to visualize a [MerklePath] in text.
pub fn path_to_text(path: &MerklePath) -> Result<String, fmt::Error> {
let mut s = String::new();
s.push('[');
for el in path.iter() {
s.push_str(&word_to_hex(el)?);
s.push_str(", ");
}
// remove the last ", "
if path.len() != 0 {
s.pop();
s.pop();
}
s.push(']');
Ok(s)
}
// TESTS // TESTS
// ================================================================================================ // ================================================================================================
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::merkle::int_to_node; use crate::{
merkle::{digests_to_words, int_to_leaf, int_to_node, InnerNodeInfo},
Felt, Word, WORD_SIZE,
};
use core::mem::size_of; use core::mem::size_of;
use proptest::prelude::*; use proptest::prelude::*;
const LEAVES4: [Word; 4] = [ const LEAVES4: [RpoDigest; WORD_SIZE] =
int_to_node(1), [int_to_node(1), int_to_node(2), int_to_node(3), int_to_node(4)];
int_to_node(2),
int_to_node(3),
int_to_node(4),
];
const LEAVES8: [Word; 8] = [ const LEAVES8: [RpoDigest; 8] = [
int_to_node(1), int_to_node(1),
int_to_node(2), int_to_node(2),
int_to_node(3), int_to_node(3),
@@ -182,7 +282,7 @@ mod tests {
#[test] #[test]
fn build_merkle_tree() { fn build_merkle_tree() {
let tree = super::MerkleTree::new(LEAVES4.to_vec()).unwrap(); let tree = super::MerkleTree::new(digests_to_words(&LEAVES4)).unwrap();
assert_eq!(8, tree.nodes.len()); assert_eq!(8, tree.nodes.len());
// leaves were copied correctly // leaves were copied correctly
@@ -201,58 +301,46 @@ mod tests {
#[test] #[test]
fn get_leaf() { fn get_leaf() {
let tree = super::MerkleTree::new(LEAVES4.to_vec()).unwrap(); let tree = super::MerkleTree::new(digests_to_words(&LEAVES4)).unwrap();
// check depth 2 // check depth 2
assert_eq!(LEAVES4[0], tree.get_node(NodeIndex::new(2, 0)).unwrap()); assert_eq!(LEAVES4[0], tree.get_node(NodeIndex::make(2, 0)).unwrap());
assert_eq!(LEAVES4[1], tree.get_node(NodeIndex::new(2, 1)).unwrap()); assert_eq!(LEAVES4[1], tree.get_node(NodeIndex::make(2, 1)).unwrap());
assert_eq!(LEAVES4[2], tree.get_node(NodeIndex::new(2, 2)).unwrap()); assert_eq!(LEAVES4[2], tree.get_node(NodeIndex::make(2, 2)).unwrap());
assert_eq!(LEAVES4[3], tree.get_node(NodeIndex::new(2, 3)).unwrap()); assert_eq!(LEAVES4[3], tree.get_node(NodeIndex::make(2, 3)).unwrap());
// check depth 1 // check depth 1
let (_, node2, node3) = compute_internal_nodes(); let (_, node2, node3) = compute_internal_nodes();
assert_eq!(node2, tree.get_node(NodeIndex::new(1, 0)).unwrap()); assert_eq!(node2, tree.get_node(NodeIndex::make(1, 0)).unwrap());
assert_eq!(node3, tree.get_node(NodeIndex::new(1, 1)).unwrap()); assert_eq!(node3, tree.get_node(NodeIndex::make(1, 1)).unwrap());
} }
#[test] #[test]
fn get_path() { fn get_path() {
let tree = super::MerkleTree::new(LEAVES4.to_vec()).unwrap(); let tree = super::MerkleTree::new(digests_to_words(&LEAVES4)).unwrap();
let (_, node2, node3) = compute_internal_nodes(); let (_, node2, node3) = compute_internal_nodes();
// check depth 2 // check depth 2
assert_eq!( assert_eq!(vec![LEAVES4[1], node3], *tree.get_path(NodeIndex::make(2, 0)).unwrap());
vec![LEAVES4[1], node3], assert_eq!(vec![LEAVES4[0], node3], *tree.get_path(NodeIndex::make(2, 1)).unwrap());
*tree.get_path(NodeIndex::new(2, 0)).unwrap() assert_eq!(vec![LEAVES4[3], node2], *tree.get_path(NodeIndex::make(2, 2)).unwrap());
); assert_eq!(vec![LEAVES4[2], node2], *tree.get_path(NodeIndex::make(2, 3)).unwrap());
assert_eq!(
vec![LEAVES4[0], node3],
*tree.get_path(NodeIndex::new(2, 1)).unwrap()
);
assert_eq!(
vec![LEAVES4[3], node2],
*tree.get_path(NodeIndex::new(2, 2)).unwrap()
);
assert_eq!(
vec![LEAVES4[2], node2],
*tree.get_path(NodeIndex::new(2, 3)).unwrap()
);
// check depth 1 // check depth 1
assert_eq!(vec![node3], *tree.get_path(NodeIndex::new(1, 0)).unwrap()); assert_eq!(vec![node3], *tree.get_path(NodeIndex::make(1, 0)).unwrap());
assert_eq!(vec![node2], *tree.get_path(NodeIndex::new(1, 1)).unwrap()); assert_eq!(vec![node2], *tree.get_path(NodeIndex::make(1, 1)).unwrap());
} }
#[test] #[test]
fn update_leaf() { fn update_leaf() {
let mut tree = super::MerkleTree::new(LEAVES8.to_vec()).unwrap(); let mut tree = super::MerkleTree::new(digests_to_words(&LEAVES8)).unwrap();
// update one leaf // update one leaf
let value = 3; let value = 3;
let new_node = int_to_node(9); let new_node = int_to_leaf(9);
let mut expected_leaves = LEAVES8.to_vec(); let mut expected_leaves = digests_to_words(&LEAVES8);
expected_leaves[value as usize] = new_node; expected_leaves[value as usize] = new_node;
let expected_tree = super::MerkleTree::new(expected_leaves.clone()).unwrap(); let expected_tree = super::MerkleTree::new(expected_leaves.clone()).unwrap();
@@ -261,7 +349,7 @@ mod tests {
// update another leaf // update another leaf
let value = 6; let value = 6;
let new_node = int_to_node(10); let new_node = int_to_leaf(10);
expected_leaves[value as usize] = new_node; expected_leaves[value as usize] = new_node;
let expected_tree = super::MerkleTree::new(expected_leaves.clone()).unwrap(); let expected_tree = super::MerkleTree::new(expected_leaves.clone()).unwrap();
@@ -269,6 +357,40 @@ mod tests {
assert_eq!(expected_tree.nodes, tree.nodes); assert_eq!(expected_tree.nodes, tree.nodes);
} }
#[test]
fn nodes() -> Result<(), MerkleError> {
let tree = super::MerkleTree::new(digests_to_words(&LEAVES4)).unwrap();
let root = tree.root();
let l1n0 = tree.get_node(NodeIndex::make(1, 0))?;
let l1n1 = tree.get_node(NodeIndex::make(1, 1))?;
let l2n0 = tree.get_node(NodeIndex::make(2, 0))?;
let l2n1 = tree.get_node(NodeIndex::make(2, 1))?;
let l2n2 = tree.get_node(NodeIndex::make(2, 2))?;
let l2n3 = tree.get_node(NodeIndex::make(2, 3))?;
let nodes: Vec<InnerNodeInfo> = tree.inner_nodes().collect();
let expected = vec![
InnerNodeInfo {
value: root,
left: l1n0,
right: l1n1,
},
InnerNodeInfo {
value: l1n0,
left: l2n0,
right: l2n1,
},
InnerNodeInfo {
value: l1n1,
left: l2n2,
right: l2n3,
},
];
assert_eq!(nodes, expected);
Ok(())
}
proptest! { proptest! {
#[test] #[test]
fn arbitrary_word_can_be_represented_as_digest( fn arbitrary_word_can_be_represented_as_digest(
@@ -286,8 +408,8 @@ mod tests {
let digest = RpoDigest::from(word); let digest = RpoDigest::from(word);
// assert the addresses are different // assert the addresses are different
let word_ptr = (&word).as_ptr() as *const u8; let word_ptr = word.as_ptr() as *const u8;
let digest_ptr = (&digest).as_ptr() as *const u8; let digest_ptr = digest.as_ptr() as *const u8;
assert_ne!(word_ptr, digest_ptr); assert_ne!(word_ptr, digest_ptr);
// compare the bytes representation // compare the bytes representation
@@ -300,11 +422,13 @@ mod tests {
// HELPER FUNCTIONS // HELPER FUNCTIONS
// -------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------
fn compute_internal_nodes() -> (Word, Word, Word) { fn compute_internal_nodes() -> (RpoDigest, RpoDigest, RpoDigest) {
let node2 = Rpo256::hash_elements(&[LEAVES4[0], LEAVES4[1]].concat()); let node2 =
let node3 = Rpo256::hash_elements(&[LEAVES4[2], LEAVES4[3]].concat()); Rpo256::hash_elements(&[Word::from(LEAVES4[0]), Word::from(LEAVES4[1])].concat());
let node3 =
Rpo256::hash_elements(&[Word::from(LEAVES4[2]), Word::from(LEAVES4[3])].concat());
let root = Rpo256::merge(&[node2, node3]); let root = Rpo256::merge(&[node2, node3]);
(root.into(), node2.into(), node3.into()) (root, node2, node3)
} }
} }

View File

@@ -0,0 +1,90 @@
use super::{
super::{RpoDigest, Vec, ZERO},
Felt, MmrProof, Rpo256, Word,
};
#[derive(Debug, Clone, PartialEq)]
pub struct MmrPeaks {
/// The number of leaves is used to differentiate accumulators that have the same number of
/// peaks. This happens because the number of peaks goes up-and-down as the structure is used
/// causing existing trees to be merged and new ones to be created. As an example, every time
/// the MMR has a power-of-two number of leaves there is a single peak.
///
/// Every tree in the MMR forest has a distinct power-of-two size, this means only the right
/// most tree can have an odd number of elements (e.g. `1`). Additionally this means that the bits in
/// `num_leaves` conveniently encode the size of each individual tree.
///
/// Examples:
///
/// - With 5 leaves, the binary `0b101`. The number of set bits is equal the number
/// of peaks, in this case there are 2 peaks. The 0-indexed least-significant position of
/// the bit determines the number of elements of a tree, so the rightmost tree has `2**0`
/// elements and the left most has `2**2`.
/// - With 12 leaves, the binary is `0b1100`, this case also has 2 peaks, the
/// leftmost tree has `2**3=8` elements, and the right most has `2**2=4` elements.
pub num_leaves: usize,
/// All the peaks of every tree in the MMR forest. The peaks are always ordered by number of
/// leaves, starting from the peak with most children, to the one with least.
///
/// Invariant: The length of `peaks` must be equal to the number of true bits in `num_leaves`.
pub peaks: Vec<RpoDigest>,
}
impl MmrPeaks {
/// Hashes the peaks.
///
/// The procedure will:
/// - Flatten and pad the peaks to a vector of Felts.
/// - Hash the vector of Felts.
pub fn hash_peaks(&self) -> Word {
Rpo256::hash_elements(&self.flatten_and_pad_peaks()).into()
}
pub fn verify(&self, value: RpoDigest, opening: MmrProof) -> bool {
let root = &self.peaks[opening.peak_index()];
opening.merkle_path.verify(opening.relative_pos() as u64, value, root)
}
/// Flattens and pads the peaks to make hashing inside of the Miden VM easier.
///
/// The procedure will:
/// - Flatten the vector of Words into a vector of Felts.
/// - Pad the peaks with ZERO to an even number of words, this removes the need to handle RPO
/// padding.
/// - Pad the peaks to a minimum length of 16 words, which reduces the constant cost of
/// hashing.
pub fn flatten_and_pad_peaks(&self) -> Vec<Felt> {
let num_peaks = self.peaks.len();
// To achieve the padding rules above we calculate the length of the final vector.
// This is calculated as the number of field elements. Each peak is 4 field elements.
// The length is calculated as follows:
// - If there are less than 16 peaks, the data is padded to 16 peaks and as such requires
// 64 field elements.
// - If there are more than 16 peaks and the number of peaks is odd, the data is padded to
// an even number of peaks and as such requires `(num_peaks + 1) * 4` field elements.
// - If there are more than 16 peaks and the number of peaks is even, the data is not padded
// and as such requires `num_peaks * 4` field elements.
let len = if num_peaks < 16 {
64
} else if num_peaks % 2 == 1 {
(num_peaks + 1) * 4
} else {
num_peaks * 4
};
let mut elements = Vec::with_capacity(len);
elements.extend_from_slice(
&self
.peaks
.as_slice()
.iter()
.map(|digest| digest.into())
.collect::<Vec<Word>>()
.concat(),
);
elements.resize(len, ZERO);
elements
}
}

46
src/merkle/mmr/bit.rs Normal file
View File

@@ -0,0 +1,46 @@
/// Iterate over the bits of a `usize` and yields the bit positions for the true bits.
pub struct TrueBitPositionIterator {
value: usize,
}
impl TrueBitPositionIterator {
pub fn new(value: usize) -> TrueBitPositionIterator {
TrueBitPositionIterator { value }
}
}
impl Iterator for TrueBitPositionIterator {
type Item = u32;
fn next(&mut self) -> Option<<Self as Iterator>::Item> {
// trailing_zeros is computed with the intrinsic cttz. [Rust 1.67.0] x86 uses the `bsf`
// instruction. AArch64 uses the `rbit clz` instructions.
let zeros = self.value.trailing_zeros();
if zeros == usize::BITS {
None
} else {
let bit_position = zeros;
let mask = 1 << bit_position;
self.value ^= mask;
Some(bit_position)
}
}
}
impl DoubleEndedIterator for TrueBitPositionIterator {
fn next_back(&mut self) -> Option<<Self as Iterator>::Item> {
// trailing_zeros is computed with the intrinsic ctlz. [Rust 1.67.0] x86 uses the `bsr`
// instruction. AArch64 uses the `clz` instruction.
let zeros = self.value.leading_zeros();
if zeros == usize::BITS {
None
} else {
let bit_position = usize::BITS - zeros - 1;
let mask = 1 << bit_position;
self.value ^= mask;
Some(bit_position)
}
}
}

390
src/merkle/mmr/full.rs Normal file
View File

@@ -0,0 +1,390 @@
//! A fully materialized Merkle mountain range (MMR).
//!
//! A MMR is a forest structure, i.e. it is an ordered set of disjoint rooted trees. The trees are
//! ordered by size, from the most to least number of leaves. Every tree is a perfect binary tree,
//! meaning a tree has all its leaves at the same depth, and every inner node has a branch-factor
//! of 2 with both children set.
//!
//! Additionally the structure only supports adding leaves to the right-most tree, the one with the
//! least number of leaves. The structure preserves the invariant that each tree has different
//! depths, i.e. as part of adding adding a new element to the forest the trees with same depth are
//! merged, creating a new tree with depth d+1, this process is continued until the property is
//! restabilished.
use super::{
super::{InnerNodeInfo, MerklePath, RpoDigest, Vec},
bit::TrueBitPositionIterator,
MmrPeaks, MmrProof, Rpo256,
};
use core::fmt::{Display, Formatter};
#[cfg(feature = "std")]
use std::error::Error;
// MMR
// ===============================================================================================
/// A fully materialized Merkle Mountain Range, with every tree in the forest and all their
/// elements.
///
/// Since this is a full representation of the MMR, elements are never removed and the MMR will
/// grow roughly `O(2n)` in number of leaf elements.
#[derive(Debug, Clone)]
pub struct Mmr {
/// Refer to the `forest` method documentation for details of the semantics of this value.
pub(super) forest: usize,
/// Contains every element of the forest.
///
/// The trees are in postorder sequential representation. This representation allows for all
/// the elements of every tree in the forest to be stored in the same sequential buffer. It
/// also means new elements can be added to the forest, and merging of trees is very cheap with
/// no need to copy elements.
pub(super) nodes: Vec<RpoDigest>,
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum MmrError {
InvalidPosition(usize),
}
impl Display for MmrError {
fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), core::fmt::Error> {
match self {
MmrError::InvalidPosition(pos) => write!(fmt, "Mmr does not contain position {pos}"),
}
}
}
#[cfg(feature = "std")]
impl Error for MmrError {}
impl Default for Mmr {
fn default() -> Self {
Self::new()
}
}
impl Mmr {
// CONSTRUCTORS
// ============================================================================================
/// Constructor for an empty `Mmr`.
pub fn new() -> Mmr {
Mmr {
forest: 0,
nodes: Vec::new(),
}
}
// ACCESSORS
// ============================================================================================
/// Returns the MMR forest representation.
///
/// The forest value has the following interpretations:
/// - its value is the number of elements in the forest
/// - bit count corresponds to the number of trees in the forest
/// - each true bit position determines the depth of a tree in the forest
pub const fn forest(&self) -> usize {
self.forest
}
// FUNCTIONALITY
// ============================================================================================
/// Given a leaf position, returns the Merkle path to its corresponding peak. If the position
/// is greater-or-equal than the tree size an error is returned.
///
/// Note: The leaf position is the 0-indexed number corresponding to the order the leaves were
/// added, this corresponds to the MMR size _prior_ to adding the element. So the 1st element
/// has position 0, the second position 1, and so on.
pub fn open(&self, pos: usize) -> Result<MmrProof, MmrError> {
// find the target tree responsible for the MMR position
let tree_bit =
leaf_to_corresponding_tree(pos, self.forest).ok_or(MmrError::InvalidPosition(pos))?;
let forest_target = 1usize << tree_bit;
// isolate the trees before the target
let forest_before = self.forest & high_bitmask(tree_bit + 1);
let index_offset = nodes_in_forest(forest_before);
// find the root
let index = nodes_in_forest(forest_target) - 1;
// update the value position from global to the target tree
let relative_pos = pos - forest_before;
// collect the path and the final index of the target value
let (_, path) =
self.collect_merkle_path_and_value(tree_bit, relative_pos, index_offset, index);
Ok(MmrProof {
forest: self.forest,
position: pos,
merkle_path: MerklePath::new(path),
})
}
/// Returns the leaf value at position `pos`.
///
/// Note: The leaf position is the 0-indexed number corresponding to the order the leaves were
/// added, this corresponds to the MMR size _prior_ to adding the element. So the 1st element
/// has position 0, the second position 1, and so on.
pub fn get(&self, pos: usize) -> Result<RpoDigest, MmrError> {
// find the target tree responsible for the MMR position
let tree_bit =
leaf_to_corresponding_tree(pos, self.forest).ok_or(MmrError::InvalidPosition(pos))?;
let forest_target = 1usize << tree_bit;
// isolate the trees before the target
let forest_before = self.forest & high_bitmask(tree_bit + 1);
let index_offset = nodes_in_forest(forest_before);
// find the root
let index = nodes_in_forest(forest_target) - 1;
// update the value position from global to the target tree
let relative_pos = pos - forest_before;
// collect the path and the final index of the target value
let (value, _) =
self.collect_merkle_path_and_value(tree_bit, relative_pos, index_offset, index);
Ok(value)
}
/// Adds a new element to the MMR.
pub fn add(&mut self, el: RpoDigest) {
// Note: every node is also a tree of size 1, adding an element to the forest creates a new
// rooted-tree of size 1. This may temporarily break the invariant that every tree in the
// forest has different sizes, the loop below will eagerly merge trees of same size and
// restore the invariant.
self.nodes.push(el);
let mut left_offset = self.nodes.len().saturating_sub(2);
let mut right = el;
let mut left_tree = 1;
while self.forest & left_tree != 0 {
right = Rpo256::merge(&[self.nodes[left_offset], right]);
self.nodes.push(right);
left_offset = left_offset.saturating_sub(nodes_in_forest(left_tree));
left_tree <<= 1;
}
self.forest += 1;
}
/// Returns an accumulator representing the current state of the MMR.
pub fn accumulator(&self) -> MmrPeaks {
let peaks: Vec<RpoDigest> = TrueBitPositionIterator::new(self.forest)
.rev()
.map(|bit| nodes_in_forest(1 << bit))
.scan(0, |offset, el| {
*offset += el;
Some(*offset)
})
.map(|offset| self.nodes[offset - 1])
.collect();
MmrPeaks {
num_leaves: self.forest,
peaks,
}
}
/// An iterator over inner nodes in the MMR. The order of iteration is unspecified.
pub fn inner_nodes(&self) -> MmrNodes {
MmrNodes {
mmr: self,
forest: 0,
last_right: 0,
index: 0,
}
}
// UTILITIES
// ============================================================================================
/// Internal function used to collect the Merkle path of a value.
fn collect_merkle_path_and_value(
&self,
tree_bit: u32,
relative_pos: usize,
index_offset: usize,
mut index: usize,
) -> (RpoDigest, Vec<RpoDigest>) {
// collect the Merkle path
let mut tree_depth = tree_bit as usize;
let mut path = Vec::with_capacity(tree_depth + 1);
while tree_depth > 0 {
let bit = relative_pos & tree_depth;
let right_offset = index - 1;
let left_offset = right_offset - nodes_in_forest(tree_depth);
// Elements to the right have a higher position because they were
// added later. Therefore when the bit is true the node's path is
// to the right, and its sibling to the left.
let sibling = if bit != 0 {
index = right_offset;
self.nodes[index_offset + left_offset]
} else {
index = left_offset;
self.nodes[index_offset + right_offset]
};
tree_depth >>= 1;
path.push(sibling);
}
// the rest of the codebase has the elements going from leaf to root, adjust it here for
// easy of use/consistency sake
path.reverse();
let value = self.nodes[index_offset + index];
(value, path)
}
}
impl<T> From<T> for Mmr
where
T: IntoIterator<Item = RpoDigest>,
{
fn from(values: T) -> Self {
let mut mmr = Mmr::new();
for v in values {
mmr.add(v)
}
mmr
}
}
// ITERATOR
// ===============================================================================================
/// Yields inner nodes of the [Mmr].
pub struct MmrNodes<'a> {
/// [Mmr] being yielded, when its `forest` value is matched, the iterations is finished.
mmr: &'a Mmr,
/// Keeps track of the left nodes yielded so far waiting for a right pair, this matches the
/// semantics of the [Mmr]'s forest attribute, since that too works as a buffer of left nodes
/// waiting for a pair to be hashed together.
forest: usize,
/// Keeps track of the last right node yielded, after this value is set, the next iteration
/// will be its parent with its corresponding left node that has been yield already.
last_right: usize,
/// The current index in the `nodes` vector.
index: usize,
}
impl<'a> Iterator for MmrNodes<'a> {
type Item = InnerNodeInfo;
fn next(&mut self) -> Option<Self::Item> {
debug_assert!(self.last_right.count_ones() <= 1, "last_right tracks zero or one element");
// only parent nodes are emitted, remove the single node tree from the forest
let target = self.mmr.forest & (usize::MAX << 1);
if self.forest < target {
if self.last_right == 0 {
// yield the left leaf
debug_assert!(self.last_right == 0, "left must be before right");
self.forest |= 1;
self.index += 1;
// yield the right leaf
debug_assert!((self.forest & 1) == 1, "right must be after left");
self.last_right |= 1;
self.index += 1;
};
debug_assert!(
self.forest & self.last_right != 0,
"parent requires both a left and right",
);
// compute the number of nodes in the right tree, this is the offset to the
// previous left parent
let right_nodes = nodes_in_forest(self.last_right);
// the next parent position is one above the position of the pair
let parent = self.last_right << 1;
// the left node has been paired and the current parent yielded, removed it from the forest
self.forest ^= self.last_right;
if self.forest & parent == 0 {
// this iteration yielded the left parent node
debug_assert!(self.forest & 1 == 0, "next iteration yields a left leaf");
self.last_right = 0;
self.forest ^= parent;
} else {
// the left node of the parent level has been yielded already, this iteration
// was the right parent. Next iteration yields their parent.
self.last_right = parent;
}
// yields a parent
let value = self.mmr.nodes[self.index];
let right = self.mmr.nodes[self.index - 1];
let left = self.mmr.nodes[self.index - 1 - right_nodes];
self.index += 1;
let node = InnerNodeInfo { value, left, right };
Some(node)
} else {
None
}
}
}
// UTILITIES
// ===============================================================================================
/// Given a 0-indexed leaf position and the current forest, return the tree number responsible for
/// the position.
///
/// Note:
/// The result is a tree position `p`, it has the following interpretations. $p+1$ is the depth of
/// the tree, which corresponds to the size of a Merkle proof for that tree. $2^p$ is equal to the
/// number of leaves in this particular tree. and $2^(p+1)-1$ corresponds to size of the tree.
pub(crate) const fn leaf_to_corresponding_tree(pos: usize, forest: usize) -> Option<u32> {
if pos >= forest {
None
} else {
// - each bit in the forest is a unique tree and the bit position its power-of-two size
// - each tree owns a consecutive range of positions equal to its size from left-to-right
// - this means the first tree owns from `0` up to the `2^k_0` first positions, where `k_0`
// is the highest true bit position, the second tree from `2^k_0 + 1` up to `2^k_1` where
// `k_1` is the second higest bit, so on.
// - this means the highest bits work as a category marker, and the position is owned by
// the first tree which doesn't share a high bit with the position
let before = forest & pos;
let after = forest ^ before;
let tree = after.ilog2();
Some(tree)
}
}
/// Return a bitmask for the bits including and above the given position.
pub(crate) const fn high_bitmask(bit: u32) -> usize {
if bit > usize::BITS - 1 {
0
} else {
usize::MAX << bit
}
}
/// Return the total number of nodes of a given forest
///
/// Panics:
///
/// This will panic if the forest has size greater than `usize::MAX / 2`
pub(crate) const fn nodes_in_forest(forest: usize) -> usize {
// - the size of a perfect binary tree is $2^{k+1}-1$ or $2*2^k-1$
// - the forest represents the sum of $2^k$ so a single multiplication is necessary
// - the number of `-1` is the same as the number of trees, which is the same as the number
// bits set
let tree_count = forest.count_ones() as usize;
forest * 2 - tree_count
}

15
src/merkle/mmr/mod.rs Normal file
View File

@@ -0,0 +1,15 @@
mod accumulator;
mod bit;
mod full;
mod proof;
#[cfg(test)]
mod tests;
use super::{Felt, Rpo256, Word};
// REEXPORTS
// ================================================================================================
pub use accumulator::MmrPeaks;
pub use full::Mmr;
pub use proof::MmrProof;

33
src/merkle/mmr/proof.rs Normal file
View File

@@ -0,0 +1,33 @@
/// The representation of a single Merkle path.
use super::super::MerklePath;
use super::full::{high_bitmask, leaf_to_corresponding_tree};
#[derive(Debug, Clone, PartialEq)]
pub struct MmrProof {
/// The state of the MMR when the MmrProof was created.
pub forest: usize,
/// The position of the leaf value on this MmrProof.
pub position: usize,
/// The Merkle opening, starting from the value's sibling up to and excluding the root of the
/// responsible tree.
pub merkle_path: MerklePath,
}
impl MmrProof {
/// Converts the leaf global position into a local position that can be used to verify the
/// merkle_path.
pub fn relative_pos(&self) -> usize {
let tree_bit = leaf_to_corresponding_tree(self.position, self.forest)
.expect("position must be part of the forest");
let forest_before = self.forest & high_bitmask(tree_bit + 1);
self.position - forest_before
}
pub fn peak_index(&self) -> usize {
let root = leaf_to_corresponding_tree(self.position, self.forest)
.expect("position must be part of the forest");
(self.forest.count_ones() - root - 1) as usize
}
}

492
src/merkle/mmr/tests.rs Normal file
View File

@@ -0,0 +1,492 @@
use super::{
super::{InnerNodeInfo, Vec},
bit::TrueBitPositionIterator,
full::{high_bitmask, leaf_to_corresponding_tree, nodes_in_forest},
Mmr, MmrPeaks, Rpo256,
};
use crate::{
hash::rpo::RpoDigest,
merkle::{int_to_node, MerklePath},
Felt, Word,
};
#[test]
fn test_position_equal_or_higher_than_leafs_is_never_contained() {
let empty_forest = 0;
for pos in 1..1024 {
// pos is index, 0 based
// tree is a length counter, 1 based
// so a valid pos is always smaller, not equal, to tree
assert_eq!(leaf_to_corresponding_tree(pos, pos), None);
assert_eq!(leaf_to_corresponding_tree(pos, pos - 1), None);
// and empty forest has no trees, so no position is valid
assert_eq!(leaf_to_corresponding_tree(pos, empty_forest), None);
}
}
#[test]
fn test_position_zero_is_always_contained_by_the_highest_tree() {
for leaves in 1..1024usize {
let tree = leaves.ilog2();
assert_eq!(leaf_to_corresponding_tree(0, leaves), Some(tree));
}
}
#[test]
fn test_leaf_to_corresponding_tree() {
assert_eq!(leaf_to_corresponding_tree(0, 0b0001), Some(0));
assert_eq!(leaf_to_corresponding_tree(0, 0b0010), Some(1));
assert_eq!(leaf_to_corresponding_tree(0, 0b0011), Some(1));
assert_eq!(leaf_to_corresponding_tree(0, 0b1011), Some(3));
// position one is always owned by the left-most tree
assert_eq!(leaf_to_corresponding_tree(1, 0b0010), Some(1));
assert_eq!(leaf_to_corresponding_tree(1, 0b0011), Some(1));
assert_eq!(leaf_to_corresponding_tree(1, 0b1011), Some(3));
// position two starts as its own root, and then it is merged with the left-most tree
assert_eq!(leaf_to_corresponding_tree(2, 0b0011), Some(0));
assert_eq!(leaf_to_corresponding_tree(2, 0b0100), Some(2));
assert_eq!(leaf_to_corresponding_tree(2, 0b1011), Some(3));
// position tree is merged on the left-most tree
assert_eq!(leaf_to_corresponding_tree(3, 0b0011), None);
assert_eq!(leaf_to_corresponding_tree(3, 0b0100), Some(2));
assert_eq!(leaf_to_corresponding_tree(3, 0b1011), Some(3));
assert_eq!(leaf_to_corresponding_tree(4, 0b0101), Some(0));
assert_eq!(leaf_to_corresponding_tree(4, 0b0110), Some(1));
assert_eq!(leaf_to_corresponding_tree(4, 0b0111), Some(1));
assert_eq!(leaf_to_corresponding_tree(4, 0b1000), Some(3));
assert_eq!(leaf_to_corresponding_tree(12, 0b01101), Some(0));
assert_eq!(leaf_to_corresponding_tree(12, 0b01110), Some(1));
assert_eq!(leaf_to_corresponding_tree(12, 0b01111), Some(1));
assert_eq!(leaf_to_corresponding_tree(12, 0b10000), Some(4));
}
#[test]
fn test_high_bitmask() {
assert_eq!(high_bitmask(0), usize::MAX);
assert_eq!(high_bitmask(1), usize::MAX << 1);
assert_eq!(high_bitmask(usize::BITS - 2), 0b11usize.rotate_right(2));
assert_eq!(high_bitmask(usize::BITS - 1), 0b1usize.rotate_right(1));
assert_eq!(high_bitmask(usize::BITS), 0, "overflow should be handled");
}
#[test]
fn test_nodes_in_forest() {
assert_eq!(nodes_in_forest(0b0000), 0);
assert_eq!(nodes_in_forest(0b0001), 1);
assert_eq!(nodes_in_forest(0b0010), 3);
assert_eq!(nodes_in_forest(0b0011), 4);
assert_eq!(nodes_in_forest(0b0100), 7);
assert_eq!(nodes_in_forest(0b0101), 8);
assert_eq!(nodes_in_forest(0b0110), 10);
assert_eq!(nodes_in_forest(0b0111), 11);
assert_eq!(nodes_in_forest(0b1000), 15);
assert_eq!(nodes_in_forest(0b1001), 16);
assert_eq!(nodes_in_forest(0b1010), 18);
assert_eq!(nodes_in_forest(0b1011), 19);
}
#[test]
fn test_nodes_in_forest_single_bit() {
assert_eq!(nodes_in_forest(2usize.pow(0)), 2usize.pow(1) - 1);
assert_eq!(nodes_in_forest(2usize.pow(1)), 2usize.pow(2) - 1);
assert_eq!(nodes_in_forest(2usize.pow(2)), 2usize.pow(3) - 1);
assert_eq!(nodes_in_forest(2usize.pow(3)), 2usize.pow(4) - 1);
for bit in 0..(usize::BITS - 1) {
let size = 2usize.pow(bit + 1) - 1;
assert_eq!(nodes_in_forest(1usize << bit), size);
}
}
const LEAVES: [RpoDigest; 7] = [
int_to_node(0),
int_to_node(1),
int_to_node(2),
int_to_node(3),
int_to_node(4),
int_to_node(5),
int_to_node(6),
];
#[test]
fn test_mmr_simple() {
let mut postorder = Vec::new();
postorder.push(LEAVES[0]);
postorder.push(LEAVES[1]);
postorder.push(Rpo256::merge(&[LEAVES[0], LEAVES[1]]));
postorder.push(LEAVES[2]);
postorder.push(LEAVES[3]);
postorder.push(Rpo256::merge(&[LEAVES[2], LEAVES[3]]));
postorder.push(Rpo256::merge(&[postorder[2], postorder[5]]));
postorder.push(LEAVES[4]);
postorder.push(LEAVES[5]);
postorder.push(Rpo256::merge(&[LEAVES[4], LEAVES[5]]));
postorder.push(LEAVES[6]);
let mut mmr = Mmr::new();
assert_eq!(mmr.forest(), 0);
assert_eq!(mmr.nodes.len(), 0);
mmr.add(LEAVES[0]);
assert_eq!(mmr.forest(), 1);
assert_eq!(mmr.nodes.len(), 1);
assert_eq!(mmr.nodes.as_slice(), &postorder[0..mmr.nodes.len()]);
let acc = mmr.accumulator();
assert_eq!(acc.num_leaves, 1);
assert_eq!(acc.peaks, &[postorder[0]]);
mmr.add(LEAVES[1]);
assert_eq!(mmr.forest(), 2);
assert_eq!(mmr.nodes.len(), 3);
assert_eq!(mmr.nodes.as_slice(), &postorder[0..mmr.nodes.len()]);
let acc = mmr.accumulator();
assert_eq!(acc.num_leaves, 2);
assert_eq!(acc.peaks, &[postorder[2]]);
mmr.add(LEAVES[2]);
assert_eq!(mmr.forest(), 3);
assert_eq!(mmr.nodes.len(), 4);
assert_eq!(mmr.nodes.as_slice(), &postorder[0..mmr.nodes.len()]);
let acc = mmr.accumulator();
assert_eq!(acc.num_leaves, 3);
assert_eq!(acc.peaks, &[postorder[2], postorder[3]]);
mmr.add(LEAVES[3]);
assert_eq!(mmr.forest(), 4);
assert_eq!(mmr.nodes.len(), 7);
assert_eq!(mmr.nodes.as_slice(), &postorder[0..mmr.nodes.len()]);
let acc = mmr.accumulator();
assert_eq!(acc.num_leaves, 4);
assert_eq!(acc.peaks, &[postorder[6]]);
mmr.add(LEAVES[4]);
assert_eq!(mmr.forest(), 5);
assert_eq!(mmr.nodes.len(), 8);
assert_eq!(mmr.nodes.as_slice(), &postorder[0..mmr.nodes.len()]);
let acc = mmr.accumulator();
assert_eq!(acc.num_leaves, 5);
assert_eq!(acc.peaks, &[postorder[6], postorder[7]]);
mmr.add(LEAVES[5]);
assert_eq!(mmr.forest(), 6);
assert_eq!(mmr.nodes.len(), 10);
assert_eq!(mmr.nodes.as_slice(), &postorder[0..mmr.nodes.len()]);
let acc = mmr.accumulator();
assert_eq!(acc.num_leaves, 6);
assert_eq!(acc.peaks, &[postorder[6], postorder[9]]);
mmr.add(LEAVES[6]);
assert_eq!(mmr.forest(), 7);
assert_eq!(mmr.nodes.len(), 11);
assert_eq!(mmr.nodes.as_slice(), &postorder[0..mmr.nodes.len()]);
let acc = mmr.accumulator();
assert_eq!(acc.num_leaves, 7);
assert_eq!(acc.peaks, &[postorder[6], postorder[9], postorder[10]]);
}
#[test]
fn test_mmr_open() {
let mmr: Mmr = LEAVES.into();
let h01 = Rpo256::merge(&[LEAVES[0], LEAVES[1]]);
let h23 = Rpo256::merge(&[LEAVES[2], LEAVES[3]]);
// node at pos 7 is the root
assert!(mmr.open(7).is_err(), "Element 7 is not in the tree, result should be None");
// node at pos 6 is the root
let empty: MerklePath = MerklePath::new(vec![]);
let opening = mmr
.open(6)
.expect("Element 6 is contained in the tree, expected an opening result.");
assert_eq!(opening.merkle_path, empty);
assert_eq!(opening.forest, mmr.forest);
assert_eq!(opening.position, 6);
assert!(
mmr.accumulator().verify(LEAVES[6], opening),
"MmrProof should be valid for the current accumulator."
);
// nodes 4,5 are depth 1
let root_to_path = MerklePath::new(vec![LEAVES[4]]);
let opening = mmr
.open(5)
.expect("Element 5 is contained in the tree, expected an opening result.");
assert_eq!(opening.merkle_path, root_to_path);
assert_eq!(opening.forest, mmr.forest);
assert_eq!(opening.position, 5);
assert!(
mmr.accumulator().verify(LEAVES[5], opening),
"MmrProof should be valid for the current accumulator."
);
let root_to_path = MerklePath::new(vec![LEAVES[5]]);
let opening = mmr
.open(4)
.expect("Element 4 is contained in the tree, expected an opening result.");
assert_eq!(opening.merkle_path, root_to_path);
assert_eq!(opening.forest, mmr.forest);
assert_eq!(opening.position, 4);
assert!(
mmr.accumulator().verify(LEAVES[4], opening),
"MmrProof should be valid for the current accumulator."
);
// nodes 0,1,2,3 are detph 2
let root_to_path = MerklePath::new(vec![LEAVES[2], h01]);
let opening = mmr
.open(3)
.expect("Element 3 is contained in the tree, expected an opening result.");
assert_eq!(opening.merkle_path, root_to_path);
assert_eq!(opening.forest, mmr.forest);
assert_eq!(opening.position, 3);
assert!(
mmr.accumulator().verify(LEAVES[3], opening),
"MmrProof should be valid for the current accumulator."
);
let root_to_path = MerklePath::new(vec![LEAVES[3], h01]);
let opening = mmr
.open(2)
.expect("Element 2 is contained in the tree, expected an opening result.");
assert_eq!(opening.merkle_path, root_to_path);
assert_eq!(opening.forest, mmr.forest);
assert_eq!(opening.position, 2);
assert!(
mmr.accumulator().verify(LEAVES[2], opening),
"MmrProof should be valid for the current accumulator."
);
let root_to_path = MerklePath::new(vec![LEAVES[0], h23]);
let opening = mmr
.open(1)
.expect("Element 1 is contained in the tree, expected an opening result.");
assert_eq!(opening.merkle_path, root_to_path);
assert_eq!(opening.forest, mmr.forest);
assert_eq!(opening.position, 1);
assert!(
mmr.accumulator().verify(LEAVES[1], opening),
"MmrProof should be valid for the current accumulator."
);
let root_to_path = MerklePath::new(vec![LEAVES[1], h23]);
let opening = mmr
.open(0)
.expect("Element 0 is contained in the tree, expected an opening result.");
assert_eq!(opening.merkle_path, root_to_path);
assert_eq!(opening.forest, mmr.forest);
assert_eq!(opening.position, 0);
assert!(
mmr.accumulator().verify(LEAVES[0], opening),
"MmrProof should be valid for the current accumulator."
);
}
#[test]
fn test_mmr_get() {
let mmr: Mmr = LEAVES.into();
assert_eq!(mmr.get(0).unwrap(), LEAVES[0], "value at pos 0 must correspond");
assert_eq!(mmr.get(1).unwrap(), LEAVES[1], "value at pos 1 must correspond");
assert_eq!(mmr.get(2).unwrap(), LEAVES[2], "value at pos 2 must correspond");
assert_eq!(mmr.get(3).unwrap(), LEAVES[3], "value at pos 3 must correspond");
assert_eq!(mmr.get(4).unwrap(), LEAVES[4], "value at pos 4 must correspond");
assert_eq!(mmr.get(5).unwrap(), LEAVES[5], "value at pos 5 must correspond");
assert_eq!(mmr.get(6).unwrap(), LEAVES[6], "value at pos 6 must correspond");
assert!(mmr.get(7).is_err());
}
#[test]
fn test_mmr_invariants() {
let mut mmr = Mmr::new();
for v in 1..=1028 {
mmr.add(int_to_node(v));
let accumulator = mmr.accumulator();
assert_eq!(v as usize, mmr.forest(), "MMR leaf count must increase by one on every add");
assert_eq!(
v as usize, accumulator.num_leaves,
"MMR and its accumulator must match leaves count"
);
assert_eq!(
accumulator.num_leaves.count_ones() as usize,
accumulator.peaks.len(),
"bits on leaves must match the number of peaks"
);
let expected_nodes: usize = TrueBitPositionIterator::new(mmr.forest())
.map(|bit_pos| nodes_in_forest(1 << bit_pos))
.sum();
assert_eq!(
expected_nodes,
mmr.nodes.len(),
"the sum of every tree size must be equal to the number of nodes in the MMR (forest: {:b})",
mmr.forest(),
);
}
}
#[test]
fn test_bit_position_iterator() {
assert_eq!(TrueBitPositionIterator::new(0).count(), 0);
assert_eq!(TrueBitPositionIterator::new(0).rev().count(), 0);
assert_eq!(TrueBitPositionIterator::new(1).collect::<Vec<u32>>(), vec![0]);
assert_eq!(TrueBitPositionIterator::new(1).rev().collect::<Vec<u32>>(), vec![0],);
assert_eq!(TrueBitPositionIterator::new(2).collect::<Vec<u32>>(), vec![1]);
assert_eq!(TrueBitPositionIterator::new(2).rev().collect::<Vec<u32>>(), vec![1],);
assert_eq!(TrueBitPositionIterator::new(3).collect::<Vec<u32>>(), vec![0, 1],);
assert_eq!(TrueBitPositionIterator::new(3).rev().collect::<Vec<u32>>(), vec![1, 0],);
assert_eq!(
TrueBitPositionIterator::new(0b11010101).collect::<Vec<u32>>(),
vec![0, 2, 4, 6, 7],
);
assert_eq!(
TrueBitPositionIterator::new(0b11010101).rev().collect::<Vec<u32>>(),
vec![7, 6, 4, 2, 0],
);
}
#[test]
fn test_mmr_inner_nodes() {
let mmr: Mmr = LEAVES.into();
let nodes: Vec<InnerNodeInfo> = mmr.inner_nodes().collect();
let h01 = Rpo256::merge(&[LEAVES[0], LEAVES[1]]);
let h23 = Rpo256::merge(&[LEAVES[2], LEAVES[3]]);
let h0123 = Rpo256::merge(&[h01, h23]);
let h45 = Rpo256::merge(&[LEAVES[4], LEAVES[5]]);
let postorder = vec![
InnerNodeInfo {
value: h01,
left: LEAVES[0],
right: LEAVES[1],
},
InnerNodeInfo {
value: h23,
left: LEAVES[2],
right: LEAVES[3],
},
InnerNodeInfo {
value: h0123,
left: h01,
right: h23,
},
InnerNodeInfo {
value: h45,
left: LEAVES[4],
right: LEAVES[5],
},
];
assert_eq!(postorder, nodes);
}
#[test]
fn test_mmr_hash_peaks() {
let mmr: Mmr = LEAVES.into();
let peaks = mmr.accumulator();
let first_peak = Rpo256::merge(&[
Rpo256::merge(&[LEAVES[0], LEAVES[1]]),
Rpo256::merge(&[LEAVES[2], LEAVES[3]]),
]);
let second_peak = Rpo256::merge(&[LEAVES[4], LEAVES[5]]);
let third_peak = LEAVES[6];
// minimum length is 16
let mut expected_peaks = [first_peak, second_peak, third_peak].to_vec();
expected_peaks.resize(16, RpoDigest::default());
assert_eq!(
peaks.hash_peaks(),
*Rpo256::hash_elements(&digests_to_elements(&expected_peaks))
);
}
#[test]
fn test_mmr_peaks_hash_less_than_16() {
let mut peaks = Vec::new();
for i in 0..16 {
peaks.push(int_to_node(i));
let accumulator = MmrPeaks {
num_leaves: (1 << peaks.len()) - 1,
peaks: peaks.clone(),
};
// minimum length is 16
let mut expected_peaks = peaks.clone();
expected_peaks.resize(16, RpoDigest::default());
assert_eq!(
accumulator.hash_peaks(),
*Rpo256::hash_elements(&digests_to_elements(&expected_peaks))
);
}
}
#[test]
fn test_mmr_peaks_hash_odd() {
let peaks: Vec<_> = (0..=17).map(int_to_node).collect();
let accumulator = MmrPeaks {
num_leaves: (1 << peaks.len()) - 1,
peaks: peaks.clone(),
};
// odd length bigger than 16 is padded to the next even number
let mut expected_peaks = peaks;
expected_peaks.resize(18, RpoDigest::default());
assert_eq!(
accumulator.hash_peaks(),
*Rpo256::hash_elements(&digests_to_elements(&expected_peaks))
);
}
mod property_tests {
use super::leaf_to_corresponding_tree;
use proptest::prelude::*;
proptest! {
#[test]
fn test_last_position_is_always_contained_in_the_last_tree(leaves in any::<usize>().prop_filter("cant have an empty tree", |v| *v != 0)) {
let last_pos = leaves - 1;
let lowest_bit = leaves.trailing_zeros();
assert_eq!(
leaf_to_corresponding_tree(last_pos, leaves),
Some(lowest_bit),
);
}
}
proptest! {
#[test]
fn test_contained_tree_is_always_power_of_two((leaves, pos) in any::<usize>().prop_flat_map(|v| (Just(v), 0..v))) {
let tree = leaf_to_corresponding_tree(pos, leaves).expect("pos is smaller than leaves, there should always be a corresponding tree");
let mask = 1usize << tree;
assert!(tree < usize::BITS, "the result must be a bit in usize");
assert!(mask & leaves != 0, "the result should be a tree in leaves");
}
}
}
// HELPER FUNCTIONS
// ================================================================================================
fn digests_to_elements(digests: &[RpoDigest]) -> Vec<Felt> {
digests.iter().flat_map(Word::from).collect()
}

View File

@@ -1,18 +1,24 @@
use super::{ use super::{
hash::rpo::{Rpo256, RpoDigest}, hash::rpo::{Rpo256, RpoDigest},
utils::collections::{vec, BTreeMap, Vec}, utils::collections::{vec, BTreeMap, BTreeSet, KvMap, RecordingMap, Vec},
Felt, StarkField, Word, ZERO, Felt, StarkField, Word, WORD_SIZE, ZERO,
}; };
use core::fmt; use core::fmt;
// REEXPORTS
// ================================================================================================
mod empty_roots;
pub use empty_roots::EmptySubtreeRoots;
mod index; mod index;
pub use index::NodeIndex; pub use index::NodeIndex;
mod merkle_tree; mod merkle_tree;
pub use merkle_tree::MerkleTree; pub use merkle_tree::{path_to_text, tree_to_text, MerkleTree};
mod path; mod path;
pub use path::MerklePath; pub use path::{MerklePath, RootPath, ValuePath};
mod path_set; mod path_set;
pub use path_set::MerklePathSet; pub use path_set::MerklePathSet;
@@ -20,41 +26,66 @@ pub use path_set::MerklePathSet;
mod simple_smt; mod simple_smt;
pub use simple_smt::SimpleSmt; pub use simple_smt::SimpleSmt;
mod tiered_smt;
pub use tiered_smt::TieredSmt;
mod mmr;
pub use mmr::{Mmr, MmrPeaks, MmrProof};
mod store;
pub use store::{DefaultMerkleStore, MerkleStore, RecordingMerkleStore, StoreNode};
mod node;
pub use node::InnerNodeInfo;
mod partial_mt;
pub use partial_mt::PartialMerkleTree;
// ERRORS // ERRORS
// ================================================================================================ // ================================================================================================
#[derive(Clone, Debug)] #[derive(Clone, Debug, PartialEq, Eq)]
pub enum MerkleError { pub enum MerkleError {
ConflictingRoots(Vec<RpoDigest>),
DepthTooSmall(u8), DepthTooSmall(u8),
DepthTooBig(u64), DepthTooBig(u64),
NumLeavesNotPowerOfTwo(usize), DuplicateValuesForIndex(u64),
InvalidIndex(NodeIndex), DuplicateValuesForKey(RpoDigest),
InvalidIndex { depth: u8, value: u64 },
InvalidDepth { expected: u8, provided: u8 }, InvalidDepth { expected: u8, provided: u8 },
InvalidPath(MerklePath), InvalidPath(MerklePath),
InvalidEntriesCount(usize, usize), InvalidNumEntries(usize, usize),
NodeNotInSet(u64), NodeNotInSet(NodeIndex),
NodeNotInStore(RpoDigest, NodeIndex),
NumLeavesNotPowerOfTwo(usize),
RootNotInStore(RpoDigest),
} }
impl fmt::Display for MerkleError { impl fmt::Display for MerkleError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use MerkleError::*; use MerkleError::*;
match self { match self {
ConflictingRoots(roots) => write!(f, "the merkle paths roots do not match {roots:?}"),
DepthTooSmall(depth) => write!(f, "the provided depth {depth} is too small"), DepthTooSmall(depth) => write!(f, "the provided depth {depth} is too small"),
DepthTooBig(depth) => write!(f, "the provided depth {depth} is too big"), DepthTooBig(depth) => write!(f, "the provided depth {depth} is too big"),
NumLeavesNotPowerOfTwo(leaves) => { DuplicateValuesForIndex(key) => write!(f, "multiple values provided for key {key}"),
write!(f, "the leaves count {leaves} is not a power of 2") DuplicateValuesForKey(key) => write!(f, "multiple values provided for key {key}"),
} InvalidIndex{ depth, value} => write!(
InvalidIndex(index) => write!(
f, f,
"the index value {} is not valid for the depth {}", index.value(), index.depth() "the index value {value} is not valid for the depth {depth}"
), ),
InvalidDepth { expected, provided } => write!( InvalidDepth { expected, provided } => write!(
f, f,
"the provided depth {provided} is not valid for {expected}" "the provided depth {provided} is not valid for {expected}"
), ),
InvalidPath(_path) => write!(f, "the provided path is not valid"), InvalidPath(_path) => write!(f, "the provided path is not valid"),
InvalidEntriesCount(max, provided) => write!(f, "the provided number of entries is {provided}, but the maximum for the given depth is {max}"), InvalidNumEntries(max, provided) => write!(f, "the provided number of entries is {provided}, but the maximum for the given depth is {max}"),
NodeNotInSet(index) => write!(f, "the node indexed by {index} is not in the set"), NodeNotInSet(index) => write!(f, "the node with index ({index}) is not in the set"),
NodeNotInStore(hash, index) => write!(f, "the node {hash:?} with index ({index}) is not in the store"),
NumLeavesNotPowerOfTwo(leaves) => {
write!(f, "the leaves count {leaves} is not a power of 2")
}
RootNotInStore(root) => write!(f, "the root {:?} is not in the store", root),
} }
} }
} }
@@ -66,6 +97,16 @@ impl std::error::Error for MerkleError {}
// ================================================================================================ // ================================================================================================
#[cfg(test)] #[cfg(test)]
const fn int_to_node(value: u64) -> Word { const fn int_to_node(value: u64) -> RpoDigest {
RpoDigest::new([Felt::new(value), ZERO, ZERO, ZERO])
}
#[cfg(test)]
const fn int_to_leaf(value: u64) -> Word {
[Felt::new(value), ZERO, ZERO, ZERO] [Felt::new(value), ZERO, ZERO, ZERO]
} }
#[cfg(test)]
fn digests_to_words(digests: &[RpoDigest]) -> Vec<Word> {
digests.iter().map(|d| d.into()).collect()
}

9
src/merkle/node.rs Normal file
View File

@@ -0,0 +1,9 @@
use crate::hash::rpo::RpoDigest;
/// Representation of a node with two children used for iterating over containers.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct InnerNodeInfo {
pub value: RpoDigest,
pub left: RpoDigest,
pub right: RpoDigest,
}

View File

@@ -0,0 +1,329 @@
use super::{
BTreeMap, BTreeSet, MerkleError, MerklePath, NodeIndex, Rpo256, RpoDigest, ValuePath, Vec, ZERO,
};
use crate::utils::{format, string::String, word_to_hex};
use core::fmt;
#[cfg(test)]
mod tests;
// CONSTANTS
// ================================================================================================
/// Index of the root node.
const ROOT_INDEX: NodeIndex = NodeIndex::root();
/// An RpoDigest consisting of 4 ZERO elements.
const EMPTY_DIGEST: RpoDigest = RpoDigest::new([ZERO; 4]);
// PARTIAL MERKLE TREE
// ================================================================================================
/// A partial Merkle tree with NodeIndex keys and 4-element RpoDigest leaf values. Partial Merkle
/// Tree allows to create Merkle Tree by providing Merkle paths of different lengths.
///
/// The root of the tree is recomputed on each new leaf update.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PartialMerkleTree {
max_depth: u8,
nodes: BTreeMap<NodeIndex, RpoDigest>,
leaves: BTreeSet<NodeIndex>,
}
impl Default for PartialMerkleTree {
fn default() -> Self {
Self::new()
}
}
impl PartialMerkleTree {
// CONSTANTS
// --------------------------------------------------------------------------------------------
/// Minimum supported depth.
pub const MIN_DEPTH: u8 = 1;
/// Maximum supported depth.
pub const MAX_DEPTH: u8 = 64;
// CONSTRUCTORS
// --------------------------------------------------------------------------------------------
/// Returns a new empty [PartialMerkleTree].
pub fn new() -> Self {
PartialMerkleTree {
max_depth: 0,
nodes: BTreeMap::new(),
leaves: BTreeSet::new(),
}
}
/// Appends the provided paths iterator into the set.
///
/// Analogous to [Self::add_path].
pub fn with_paths<I>(paths: I) -> Result<Self, MerkleError>
where
I: IntoIterator<Item = (u64, RpoDigest, MerklePath)>,
{
// create an empty tree
let tree = PartialMerkleTree::new();
paths.into_iter().try_fold(tree, |mut tree, (index, value, path)| {
tree.add_path(index, value, path)?;
Ok(tree)
})
}
// PUBLIC ACCESSORS
// --------------------------------------------------------------------------------------------
/// Returns the root of this Merkle tree.
pub fn root(&self) -> RpoDigest {
self.nodes.get(&ROOT_INDEX).cloned().unwrap_or(EMPTY_DIGEST)
}
/// Returns the depth of this Merkle tree.
pub fn max_depth(&self) -> u8 {
self.max_depth
}
/// Returns a node at the specified NodeIndex.
///
/// # Errors
/// Returns an error if the specified NodeIndex is not contained in the nodes map.
pub fn get_node(&self, index: NodeIndex) -> Result<RpoDigest, MerkleError> {
self.nodes.get(&index).ok_or(MerkleError::NodeNotInSet(index)).map(|hash| *hash)
}
/// Returns true if provided index contains in the leaves set, false otherwise.
pub fn is_leaf(&self, index: NodeIndex) -> bool {
self.leaves.contains(&index)
}
/// Returns a vector of paths from every leaf to the root.
pub fn paths(&self) -> Vec<(NodeIndex, ValuePath)> {
let mut paths = Vec::new();
self.leaves.iter().for_each(|&leaf| {
paths.push((
leaf,
ValuePath {
value: self.get_node(leaf).expect("Failed to get leaf node"),
path: self.get_path(leaf).expect("Failed to get path"),
},
));
});
paths
}
/// Returns a Merkle path from the node at the specified index to the root.
///
/// The node itself is not included in the path.
///
/// # Errors
/// Returns an error if:
/// - the specified index has depth set to 0 or the depth is greater than the depth of this
/// Merkle tree.
/// - the specified index is not contained in the nodes map.
pub fn get_path(&self, mut index: NodeIndex) -> Result<MerklePath, MerkleError> {
if index.is_root() {
return Err(MerkleError::DepthTooSmall(index.depth()));
} else if index.depth() > self.max_depth() {
return Err(MerkleError::DepthTooBig(index.depth() as u64));
}
if !self.nodes.contains_key(&index) {
return Err(MerkleError::NodeNotInSet(index));
}
let mut path = Vec::new();
for _ in 0..index.depth() {
let sibling_index = index.sibling();
index.move_up();
let sibling =
self.nodes.get(&sibling_index).cloned().expect("Sibling node not in the map");
path.push(sibling);
}
Ok(MerklePath::new(path))
}
// ITERATORS
// --------------------------------------------------------------------------------------------
/// Returns an iterator over the leaves of this [PartialMerkleTree].
pub fn leaves(&self) -> impl Iterator<Item = (NodeIndex, RpoDigest)> + '_ {
self.leaves.iter().map(|&leaf| {
(
leaf,
self.get_node(leaf)
.unwrap_or_else(|_| panic!("Leaf with {leaf} is not in the nodes map")),
)
})
}
// STATE MUTATORS
// --------------------------------------------------------------------------------------------
/// Adds the nodes of the specified Merkle path to this [PartialMerkleTree]. The `index_value`
/// and `value` parameters specify the leaf node at which the path starts.
///
/// # Errors
/// Returns an error if:
/// - The depth of the specified node_index is greater than 64 or smaller than 1.
/// - The specified path is not consistent with other paths in the set (i.e., resolves to a
/// different root).
pub fn add_path(
&mut self,
index_value: u64,
value: RpoDigest,
path: MerklePath,
) -> Result<(), MerkleError> {
let index_value = NodeIndex::new(path.len() as u8, index_value)?;
Self::check_depth(index_value.depth())?;
self.update_depth(index_value.depth());
// add provided node and its sibling to the leaves set
self.leaves.insert(index_value);
let sibling_node_index = index_value.sibling();
self.leaves.insert(sibling_node_index);
// add provided node and its sibling to the nodes map
self.nodes.insert(index_value, value);
self.nodes.insert(sibling_node_index, path[0]);
// traverse to the root, updating the nodes
let mut index_value = index_value;
let node = Rpo256::merge(&index_value.build_node(value, path[0]));
let root = path.iter().skip(1).copied().fold(node, |node, hash| {
index_value.move_up();
// insert calculated node to the nodes map
self.nodes.insert(index_value, node);
// if the calculated node was a leaf, remove it from leaves set.
self.leaves.remove(&index_value);
let sibling_node = index_value.sibling();
// Insert node from Merkle path to the nodes map. This sibling node becomes a leaf only
// if it is a new node (it wasn't in nodes map).
// Node can be in 3 states: internal node, leaf of the tree and not a tree node at all.
// - Internal node can only stay in this state -- addition of a new path can't make it
// a leaf or remove it from the tree.
// - Leaf node can stay in the same state (remain a leaf) or can become an internal
// node. In the first case we don't need to do anything, and the second case is handled
// by the call of `self.leaves.remove(&index_value);`
// - New node can be a calculated node or a "sibling" node from a Merkle Path:
// --- Calculated node, obviously, never can be a leaf.
// --- Sibling node can be only a leaf, because otherwise it is not a new node.
if self.nodes.insert(sibling_node, hash).is_none() {
self.leaves.insert(sibling_node);
}
Rpo256::merge(&index_value.build_node(node, hash))
});
// if the path set is empty (the root is all ZEROs), set the root to the root of the added
// path; otherwise, the root of the added path must be identical to the current root
if self.root() == EMPTY_DIGEST {
self.nodes.insert(ROOT_INDEX, root);
} else if self.root() != root {
return Err(MerkleError::ConflictingRoots([self.root(), root].to_vec()));
}
Ok(())
}
/// Updates value of the leaf at the specified index returning the old leaf value.
///
/// This also recomputes all hashes between the leaf and the root, updating the root itself.
///
/// # Errors
/// Returns an error if:
/// - The depth of the specified node_index is greater than 64 or smaller than 1.
/// - The specified node index is not corresponding to the leaf.
pub fn update_leaf(
&mut self,
node_index: NodeIndex,
value: RpoDigest,
) -> Result<RpoDigest, MerkleError> {
// check correctness of the depth and update it
Self::check_depth(node_index.depth())?;
self.update_depth(node_index.depth());
// insert NodeIndex to the leaves Set
self.leaves.insert(node_index);
// add node value to the nodes Map
let old_value = self
.nodes
.insert(node_index, value)
.ok_or(MerkleError::NodeNotInSet(node_index))?;
// if the old value and new value are the same, there is nothing to update
if value == old_value {
return Ok(old_value);
}
let mut node_index = node_index;
let mut value = value;
for _ in 0..node_index.depth() {
let sibling = self.nodes.get(&node_index.sibling()).expect("sibling should exist");
value = Rpo256::merge(&node_index.build_node(value, *sibling));
node_index.move_up();
self.nodes.insert(node_index, value);
}
Ok(old_value)
}
// UTILITY FUNCTIONS
// --------------------------------------------------------------------------------------------
/// Utility to visualize a [PartialMerkleTree] in text.
pub fn print(&self) -> Result<String, fmt::Error> {
let indent = " ";
let mut s = String::new();
s.push_str("root: ");
s.push_str(&word_to_hex(&self.root())?);
s.push('\n');
for d in 1..=self.max_depth() {
let entries = 2u64.pow(d.into());
for i in 0..entries {
let index = NodeIndex::new(d, i).expect("The index must always be valid");
let node = self.get_node(index);
let node = match node {
Err(_) => continue,
Ok(node) => node,
};
for _ in 0..d {
s.push_str(indent);
}
s.push_str(&format!("({}, {}): ", index.depth(), index.value()));
s.push_str(&word_to_hex(&node)?);
s.push('\n');
}
}
Ok(s)
}
// HELPER METHODS
// --------------------------------------------------------------------------------------------
/// Updates depth value with the maximum of current and provided depth.
fn update_depth(&mut self, new_depth: u8) {
self.max_depth = new_depth.max(self.max_depth);
}
/// Returns an error if the depth is 0 or is greater than 64.
fn check_depth(depth: u8) -> Result<(), MerkleError> {
// validate the range of the depth.
if depth < Self::MIN_DEPTH {
return Err(MerkleError::DepthTooSmall(depth));
} else if Self::MAX_DEPTH < depth {
return Err(MerkleError::DepthTooBig(depth as u64));
}
Ok(())
}
}

View File

@@ -0,0 +1,313 @@
use super::{
super::{
digests_to_words, int_to_node, DefaultMerkleStore as MerkleStore, MerkleTree, NodeIndex,
PartialMerkleTree,
},
RpoDigest, ValuePath, Vec,
};
// TEST DATA
// ================================================================================================
const NODE10: NodeIndex = NodeIndex::new_unchecked(1, 0);
const NODE11: NodeIndex = NodeIndex::new_unchecked(1, 1);
const NODE20: NodeIndex = NodeIndex::new_unchecked(2, 0);
const NODE22: NodeIndex = NodeIndex::new_unchecked(2, 2);
const NODE23: NodeIndex = NodeIndex::new_unchecked(2, 3);
const NODE30: NodeIndex = NodeIndex::new_unchecked(3, 0);
const NODE31: NodeIndex = NodeIndex::new_unchecked(3, 1);
const NODE32: NodeIndex = NodeIndex::new_unchecked(3, 2);
const NODE33: NodeIndex = NodeIndex::new_unchecked(3, 3);
const VALUES8: [RpoDigest; 8] = [
int_to_node(30),
int_to_node(31),
int_to_node(32),
int_to_node(33),
int_to_node(34),
int_to_node(35),
int_to_node(36),
int_to_node(37),
];
// TESTS
// ================================================================================================
// For the Partial Merkle Tree tests we will use parts of the Merkle Tree which full form is
// illustrated below:
//
// __________ root __________
// / \
// ____ 10 ____ ____ 11 ____
// / \ / \
// 20 21 22 23
// / \ / \ / \ / \
// (30) (31) (32) (33) (34) (35) (36) (37)
//
// Where node number is a concatenation of its depth and index. For example, node with
// NodeIndex(3, 5) will be labeled as `35`. Leaves of the tree are shown as nodes with parenthesis
// (33).
/// Checks that root returned by `root()` function is equal to the expected one.
#[test]
fn get_root() {
let mt = MerkleTree::new(digests_to_words(&VALUES8)).unwrap();
let expected_root = mt.root();
let ms = MerkleStore::from(&mt);
let path33 = ms.get_path(expected_root, NODE33).unwrap();
let pmt = PartialMerkleTree::with_paths([(3, path33.value, path33.path)]).unwrap();
assert_eq!(pmt.root(), expected_root);
}
/// This test checks correctness of the `add_path()` and `get_path()` functions. First it creates a
/// PMT using `add_path()` by adding Merkle Paths from node 33 and node 22 to the empty PMT. Then
/// it checks that paths returned by `get_path()` function are equal to the expected ones.
#[test]
fn add_and_get_paths() {
let mt = MerkleTree::new(digests_to_words(&VALUES8)).unwrap();
let expected_root = mt.root();
let ms = MerkleStore::from(&mt);
let expected_path33 = ms.get_path(expected_root, NODE33).unwrap();
let expected_path22 = ms.get_path(expected_root, NODE22).unwrap();
let mut pmt = PartialMerkleTree::new();
pmt.add_path(3, expected_path33.value, expected_path33.path.clone()).unwrap();
pmt.add_path(2, expected_path22.value, expected_path22.path.clone()).unwrap();
let path33 = pmt.get_path(NODE33).unwrap();
let path22 = pmt.get_path(NODE22).unwrap();
let actual_root = pmt.root();
assert_eq!(expected_path33.path, path33);
assert_eq!(expected_path22.path, path22);
assert_eq!(expected_root, actual_root);
}
/// Checks that function `get_node` used on nodes 10 and 32 returns expected values.
#[test]
fn get_node() {
let mt = MerkleTree::new(digests_to_words(&VALUES8)).unwrap();
let expected_root = mt.root();
let ms = MerkleStore::from(&mt);
let path33 = ms.get_path(expected_root, NODE33).unwrap();
let pmt = PartialMerkleTree::with_paths([(3, path33.value, path33.path)]).unwrap();
assert_eq!(ms.get_node(expected_root, NODE32).unwrap(), pmt.get_node(NODE32).unwrap());
assert_eq!(ms.get_node(expected_root, NODE10).unwrap(), pmt.get_node(NODE10).unwrap());
}
/// Updates leaves of the PMT using `update_leaf()` function and checks that new root of the tree
/// is equal to the expected one.
#[test]
fn update_leaf() {
let mt = MerkleTree::new(digests_to_words(&VALUES8)).unwrap();
let root = mt.root();
let mut ms = MerkleStore::from(&mt);
let path33 = ms.get_path(root, NODE33).unwrap();
let mut pmt = PartialMerkleTree::with_paths([(3, path33.value, path33.path)]).unwrap();
let new_value32 = int_to_node(132);
let expected_root = ms.set_node(root, NODE32, new_value32).unwrap().root;
pmt.update_leaf(NODE32, new_value32).unwrap();
let actual_root = pmt.root();
assert_eq!(expected_root, actual_root);
let new_value20 = int_to_node(120);
let expected_root = ms.set_node(expected_root, NODE20, new_value20).unwrap().root;
pmt.update_leaf(NODE20, new_value20).unwrap();
let actual_root = pmt.root();
assert_eq!(expected_root, actual_root);
}
/// Checks that paths of the PMT returned by `paths()` function are equal to the expected ones.
#[test]
fn get_paths() {
let mt = MerkleTree::new(digests_to_words(&VALUES8)).unwrap();
let expected_root = mt.root();
let ms = MerkleStore::from(&mt);
let path33 = ms.get_path(expected_root, NODE33).unwrap();
let path22 = ms.get_path(expected_root, NODE22).unwrap();
let mut pmt = PartialMerkleTree::new();
pmt.add_path(3, path33.value, path33.path).unwrap();
pmt.add_path(2, path22.value, path22.path).unwrap();
// After PMT creation with path33 (33; 32, 20, 11) and path22 (22; 23, 10) we will have this
// tree:
//
// ______root______
// / \
// ___10___ ___11___
// / \ / \
// (20) 21 (22) (23)
// / \
// (32) (33)
//
// Which have leaf nodes 20, 22, 23, 32 and 33. Hence overall we will have 5 paths -- one path
// for each leaf.
let leaves = vec![NODE20, NODE22, NODE23, NODE32, NODE33];
let expected_paths: Vec<(NodeIndex, ValuePath)> = leaves
.iter()
.map(|&leaf| {
(
leaf,
ValuePath {
value: mt.get_node(leaf).unwrap(),
path: mt.get_path(leaf).unwrap(),
},
)
})
.collect();
let actual_paths = pmt.paths();
assert_eq!(expected_paths, actual_paths);
}
// Checks correctness of leaves determination when using the `leaves()` function.
#[test]
fn leaves() {
let mt = MerkleTree::new(digests_to_words(&VALUES8)).unwrap();
let expected_root = mt.root();
let ms = MerkleStore::from(&mt);
let path33 = ms.get_path(expected_root, NODE33).unwrap();
let path22 = ms.get_path(expected_root, NODE22).unwrap();
let mut pmt = PartialMerkleTree::with_paths([(3, path33.value, path33.path)]).unwrap();
// After PMT creation with path33 (33; 32, 20, 11) we will have this tree:
//
// ______root______
// / \
// ___10___ (11)
// / \
// (20) 21
// / \
// (32) (33)
//
// Which have leaf nodes 11, 20, 32 and 33.
let value11 = mt.get_node(NODE11).unwrap();
let value20 = mt.get_node(NODE20).unwrap();
let value32 = mt.get_node(NODE32).unwrap();
let value33 = mt.get_node(NODE33).unwrap();
let leaves = vec![(NODE11, value11), (NODE20, value20), (NODE32, value32), (NODE33, value33)];
let expected_leaves = leaves.iter().copied();
assert!(expected_leaves.eq(pmt.leaves()));
pmt.add_path(2, path22.value, path22.path).unwrap();
// After adding the path22 (22; 23, 10) to the existing PMT we will have this tree:
//
// ______root______
// / \
// ___10___ ___11___
// / \ / \
// (20) 21 (22) (23)
// / \
// (32) (33)
//
// Which have leaf nodes 20, 22, 23, 32 and 33.
let value20 = mt.get_node(NODE20).unwrap();
let value22 = mt.get_node(NODE22).unwrap();
let value23 = mt.get_node(NODE23).unwrap();
let value32 = mt.get_node(NODE32).unwrap();
let value33 = mt.get_node(NODE33).unwrap();
let leaves = vec![
(NODE20, value20),
(NODE22, value22),
(NODE23, value23),
(NODE32, value32),
(NODE33, value33),
];
let expected_leaves = leaves.iter().copied();
assert!(expected_leaves.eq(pmt.leaves()));
}
/// Checks that addition of the path with different root will cause an error.
#[test]
fn err_add_path() {
let path33 = vec![int_to_node(1), int_to_node(2), int_to_node(3)].into();
let path22 = vec![int_to_node(4), int_to_node(5)].into();
let mut pmt = PartialMerkleTree::new();
pmt.add_path(3, int_to_node(6), path33).unwrap();
assert!(pmt.add_path(2, int_to_node(7), path22).is_err());
}
/// Checks that the request of the node which is not in the PMT will cause an error.
#[test]
fn err_get_node() {
let mt = MerkleTree::new(digests_to_words(&VALUES8)).unwrap();
let expected_root = mt.root();
let ms = MerkleStore::from(&mt);
let path33 = ms.get_path(expected_root, NODE33).unwrap();
let pmt = PartialMerkleTree::with_paths([(3, path33.value, path33.path)]).unwrap();
assert!(pmt.get_node(NODE22).is_err());
assert!(pmt.get_node(NODE23).is_err());
assert!(pmt.get_node(NODE30).is_err());
assert!(pmt.get_node(NODE31).is_err());
}
/// Checks that the request of the path from the leaf which is not in the PMT will cause an error.
#[test]
fn err_get_path() {
let mt = MerkleTree::new(digests_to_words(&VALUES8)).unwrap();
let expected_root = mt.root();
let ms = MerkleStore::from(&mt);
let path33 = ms.get_path(expected_root, NODE33).unwrap();
let pmt = PartialMerkleTree::with_paths([(3, path33.value, path33.path)]).unwrap();
assert!(pmt.get_path(NODE22).is_err());
assert!(pmt.get_path(NODE23).is_err());
assert!(pmt.get_path(NODE30).is_err());
assert!(pmt.get_path(NODE31).is_err());
}
#[test]
fn err_update_leaf() {
let mt = MerkleTree::new(digests_to_words(&VALUES8)).unwrap();
let expected_root = mt.root();
let ms = MerkleStore::from(&mt);
let path33 = ms.get_path(expected_root, NODE33).unwrap();
let mut pmt = PartialMerkleTree::with_paths([(3, path33.value, path33.path)]).unwrap();
assert!(pmt.update_leaf(NODE22, int_to_node(22)).is_err());
assert!(pmt.update_leaf(NODE23, int_to_node(23)).is_err());
assert!(pmt.update_leaf(NODE30, int_to_node(30)).is_err());
assert!(pmt.update_leaf(NODE31, int_to_node(31)).is_err());
}

View File

@@ -1,4 +1,4 @@
use super::{vec, NodeIndex, Rpo256, Vec, Word}; use super::{vec, InnerNodeInfo, MerkleError, NodeIndex, Rpo256, RpoDigest, Vec};
use core::ops::{Deref, DerefMut}; use core::ops::{Deref, DerefMut};
// MERKLE PATH // MERKLE PATH
@@ -7,7 +7,7 @@ use core::ops::{Deref, DerefMut};
/// A merkle path container, composed of a sequence of nodes of a Merkle tree. /// A merkle path container, composed of a sequence of nodes of a Merkle tree.
#[derive(Clone, Debug, Default, PartialEq, Eq)] #[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct MerklePath { pub struct MerklePath {
nodes: Vec<Word>, nodes: Vec<RpoDigest>,
} }
impl MerklePath { impl MerklePath {
@@ -15,39 +15,67 @@ impl MerklePath {
// -------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------
/// Creates a new Merkle path from a list of nodes. /// Creates a new Merkle path from a list of nodes.
pub fn new(nodes: Vec<Word>) -> Self { pub fn new(nodes: Vec<RpoDigest>) -> Self {
Self { nodes } Self { nodes }
} }
// PROVIDERS // PROVIDERS
// -------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------
/// Computes the merkle root for this opening.
pub fn compute_root(&self, index_value: u64, node: Word) -> Word {
let mut index = NodeIndex::new(self.depth(), index_value);
self.nodes.iter().copied().fold(node, |node, sibling| {
// compute the node and move to the next iteration.
let input = index.build_node(node.into(), sibling.into());
index.move_up();
Rpo256::merge(&input).into()
})
}
/// Returns the depth in which this Merkle path proof is valid. /// Returns the depth in which this Merkle path proof is valid.
pub fn depth(&self) -> u8 { pub fn depth(&self) -> u8 {
self.nodes.len() as u8 self.nodes.len() as u8
} }
/// Computes the merkle root for this opening.
pub fn compute_root(&self, index: u64, node: RpoDigest) -> Result<RpoDigest, MerkleError> {
let mut index = NodeIndex::new(self.depth(), index)?;
let root = self.nodes.iter().copied().fold(node, |node, sibling| {
// compute the node and move to the next iteration.
let input = index.build_node(node, sibling);
index.move_up();
Rpo256::merge(&input)
});
Ok(root)
}
/// Verifies the Merkle opening proof towards the provided root. /// Verifies the Merkle opening proof towards the provided root.
/// ///
/// Returns `true` if `node` exists at `index` in a Merkle tree with `root`. /// Returns `true` if `node` exists at `index` in a Merkle tree with `root`.
pub fn verify(&self, index: u64, node: Word, root: &Word) -> bool { pub fn verify(&self, index: u64, node: RpoDigest, root: &RpoDigest) -> bool {
root == &self.compute_root(index, node) match self.compute_root(index, node) {
Ok(computed_root) => root == &computed_root,
Err(_) => false,
} }
} }
impl From<Vec<Word>> for MerklePath { /// Returns an iterator over every inner node of this [MerklePath].
fn from(path: Vec<Word>) -> Self { ///
/// The iteration order is unspecified.
///
/// # Errors
/// Returns an error if the specified index is not valid for this path.
pub fn inner_nodes(
&self,
index: u64,
node: RpoDigest,
) -> Result<InnerNodeIterator, MerkleError> {
Ok(InnerNodeIterator {
nodes: &self.nodes,
index: NodeIndex::new(self.depth(), index)?,
value: node,
})
}
}
impl From<MerklePath> for Vec<RpoDigest> {
fn from(path: MerklePath) -> Self {
path.nodes
}
}
impl From<Vec<RpoDigest>> for MerklePath {
fn from(path: Vec<RpoDigest>) -> Self {
Self::new(path) Self::new(path)
} }
} }
@@ -55,7 +83,7 @@ impl From<Vec<Word>> for MerklePath {
impl Deref for MerklePath { impl Deref for MerklePath {
// we use `Vec` here instead of slice so we can call vector mutation methods directly from the // we use `Vec` here instead of slice so we can call vector mutation methods directly from the
// merkle path (example: `Vec::remove`). // merkle path (example: `Vec::remove`).
type Target = Vec<Word>; type Target = Vec<RpoDigest>;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.nodes &self.nodes
@@ -68,17 +96,99 @@ impl DerefMut for MerklePath {
} }
} }
impl FromIterator<Word> for MerklePath { // ITERATORS
fn from_iter<T: IntoIterator<Item = Word>>(iter: T) -> Self { // ================================================================================================
impl FromIterator<RpoDigest> for MerklePath {
fn from_iter<T: IntoIterator<Item = RpoDigest>>(iter: T) -> Self {
Self::new(iter.into_iter().collect()) Self::new(iter.into_iter().collect())
} }
} }
impl IntoIterator for MerklePath { impl IntoIterator for MerklePath {
type Item = Word; type Item = RpoDigest;
type IntoIter = vec::IntoIter<Word>; type IntoIter = vec::IntoIter<RpoDigest>;
fn into_iter(self) -> Self::IntoIter { fn into_iter(self) -> Self::IntoIter {
self.nodes.into_iter() self.nodes.into_iter()
} }
} }
/// An iterator over internal nodes of a [MerklePath].
pub struct InnerNodeIterator<'a> {
nodes: &'a Vec<RpoDigest>,
index: NodeIndex,
value: RpoDigest,
}
impl<'a> Iterator for InnerNodeIterator<'a> {
type Item = InnerNodeInfo;
fn next(&mut self) -> Option<Self::Item> {
if !self.index.is_root() {
let sibling_pos = self.nodes.len() - self.index.depth() as usize;
let (left, right) = if self.index.is_value_odd() {
(self.nodes[sibling_pos], self.value)
} else {
(self.value, self.nodes[sibling_pos])
};
self.value = Rpo256::merge(&[left, right]);
self.index.move_up();
Some(InnerNodeInfo {
value: self.value,
left,
right,
})
} else {
None
}
}
}
// MERKLE PATH CONTAINERS
// ================================================================================================
/// A container for a [Word] value and its [MerklePath] opening.
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct ValuePath {
/// The node value opening for `path`.
pub value: RpoDigest,
/// The path from `value` to `root` (exclusive).
pub path: MerklePath,
}
/// A container for a [MerklePath] and its [Word] root.
///
/// This structure does not provide any guarantees regarding the correctness of the path to the
/// root. For more information, check [MerklePath::verify].
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct RootPath {
/// The node value opening for `path`.
pub root: RpoDigest,
/// The path from `value` to `root` (exclusive).
pub path: MerklePath,
}
// TESTS
// ================================================================================================
#[cfg(test)]
mod tests {
use crate::merkle::{int_to_node, MerklePath};
#[test]
fn test_inner_nodes() {
let nodes = vec![int_to_node(1), int_to_node(2), int_to_node(3), int_to_node(4)];
let merkle_path = MerklePath::new(nodes);
let index = 6;
let node = int_to_node(5);
let root = merkle_path.compute_root(index, node).unwrap();
let inner_root = merkle_path.inner_nodes(index, node).unwrap().last().unwrap().value;
assert_eq!(root, inner_root);
}
}

View File

@@ -1,4 +1,5 @@
use super::{BTreeMap, MerkleError, MerklePath, NodeIndex, Rpo256, Vec, Word, ZERO}; use super::{BTreeMap, MerkleError, MerklePath, NodeIndex, Rpo256, ValuePath, Vec};
use crate::{hash::rpo::RpoDigest, Word};
// MERKLE PATH SET // MERKLE PATH SET
// ================================================================================================ // ================================================================================================
@@ -6,7 +7,7 @@ use super::{BTreeMap, MerkleError, MerklePath, NodeIndex, Rpo256, Vec, Word, ZER
/// A set of Merkle paths. /// A set of Merkle paths.
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct MerklePathSet { pub struct MerklePathSet {
root: Word, root: RpoDigest,
total_depth: u8, total_depth: u8,
paths: BTreeMap<u64, MerklePath>, paths: BTreeMap<u64, MerklePath>,
} }
@@ -17,7 +18,7 @@ impl MerklePathSet {
/// Returns an empty MerklePathSet. /// Returns an empty MerklePathSet.
pub fn new(depth: u8) -> Self { pub fn new(depth: u8) -> Self {
let root = [ZERO; 4]; let root = RpoDigest::default();
let paths = BTreeMap::new(); let paths = BTreeMap::new();
Self { Self {
@@ -32,12 +33,10 @@ impl MerklePathSet {
/// Analogous to `[Self::add_path]`. /// Analogous to `[Self::add_path]`.
pub fn with_paths<I>(self, paths: I) -> Result<Self, MerkleError> pub fn with_paths<I>(self, paths: I) -> Result<Self, MerkleError>
where where
I: IntoIterator<Item = (u64, Word, MerklePath)>, I: IntoIterator<Item = (u64, RpoDigest, MerklePath)>,
{ {
paths paths.into_iter().try_fold(self, |mut set, (index, value, path)| {
.into_iter() set.add_path(index, value.into(), path)?;
.try_fold(self, |mut set, (index, value, path)| {
set.add_path(index, value, path)?;
Ok(set) Ok(set)
}) })
} }
@@ -46,7 +45,7 @@ impl MerklePathSet {
// -------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------
/// Returns the root to which all paths in this set resolve. /// Returns the root to which all paths in this set resolve.
pub const fn root(&self) -> Word { pub const fn root(&self) -> RpoDigest {
self.root self.root
} }
@@ -63,12 +62,7 @@ impl MerklePathSet {
/// Returns an error if: /// Returns an error if:
/// * The specified index is not valid for the depth of structure. /// * The specified index is not valid for the depth of structure.
/// * Requested node does not exist in the set. /// * Requested node does not exist in the set.
pub fn get_node(&self, index: NodeIndex) -> Result<Word, MerkleError> { pub fn get_node(&self, index: NodeIndex) -> Result<RpoDigest, MerkleError> {
if !index.with_depth(self.total_depth).is_valid() {
return Err(MerkleError::InvalidIndex(
index.with_depth(self.total_depth),
));
}
if index.depth() != self.total_depth { if index.depth() != self.total_depth {
return Err(MerkleError::InvalidDepth { return Err(MerkleError::InvalidDepth {
expected: self.total_depth, expected: self.total_depth,
@@ -76,15 +70,24 @@ impl MerklePathSet {
}); });
} }
let index_value = index.to_scalar_index(); let parity = index.value() & 1;
let parity = index_value & 1; let path_key = index.value() - parity;
let index_value = index_value / 2;
self.paths self.paths
.get(&index_value) .get(&path_key)
.ok_or(MerkleError::NodeNotInSet(index_value)) .ok_or(MerkleError::NodeNotInSet(index))
.map(|path| path[parity as usize]) .map(|path| path[parity as usize])
} }
/// Returns a leaf at the specified index.
///
/// # Errors
/// * The specified index is not valid for the depth of the structure.
/// * Leaf with the requested path does not exist in the set.
pub fn get_leaf(&self, index: u64) -> Result<Word, MerkleError> {
let index = NodeIndex::new(self.depth(), index)?;
Ok(self.get_node(index)?.into())
}
/// Returns a Merkle path to the node at the specified index. The node itself is /// Returns a Merkle path to the node at the specified index. The node itself is
/// not included in the path. /// not included in the path.
/// ///
@@ -93,9 +96,6 @@ impl MerklePathSet {
/// * The specified index is not valid for the depth of structure. /// * The specified index is not valid for the depth of structure.
/// * Node of the requested path does not exist in the set. /// * Node of the requested path does not exist in the set.
pub fn get_path(&self, index: NodeIndex) -> Result<MerklePath, MerkleError> { pub fn get_path(&self, index: NodeIndex) -> Result<MerklePath, MerkleError> {
if !index.with_depth(self.total_depth).is_valid() {
return Err(MerkleError::InvalidIndex(index));
}
if index.depth() != self.total_depth { if index.depth() != self.total_depth {
return Err(MerkleError::InvalidDepth { return Err(MerkleError::InvalidDepth {
expected: self.total_depth, expected: self.total_depth,
@@ -103,18 +103,39 @@ impl MerklePathSet {
}); });
} }
let index_value = index.to_scalar_index(); let parity = index.value() & 1;
let index = index_value / 2; let path_key = index.value() - parity;
let parity = index_value & 1; let mut path =
let mut path = self self.paths.get(&path_key).cloned().ok_or(MerkleError::NodeNotInSet(index))?;
.paths
.get(&index)
.cloned()
.ok_or(MerkleError::NodeNotInSet(index))?;
path.remove(parity as usize); path.remove(parity as usize);
Ok(path) Ok(path)
} }
/// Returns all paths in this path set together with their indexes.
pub fn to_paths(&self) -> Vec<(u64, ValuePath)> {
let mut result = Vec::with_capacity(self.paths.len() * 2);
for (&index, path) in self.paths.iter() {
// push path for the even index into the result
let path1 = ValuePath {
value: path[0],
path: MerklePath::new(path[1..].to_vec()),
};
result.push((index, path1));
// push path for the odd index into the result
let mut path2 = path.clone();
let leaf2 = path2.remove(1);
let path2 = ValuePath {
value: leaf2,
path: path2,
};
result.push((index + 1, path2));
}
result
}
// STATE MUTATORS // STATE MUTATORS
// -------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------
@@ -133,8 +154,7 @@ impl MerklePathSet {
value: Word, value: Word,
mut path: MerklePath, mut path: MerklePath,
) -> Result<(), MerkleError> { ) -> Result<(), MerkleError> {
let depth = (path.len() + 1) as u8; let mut index = NodeIndex::new(path.len() as u8, index_value)?;
let mut index = NodeIndex::new(depth, index_value);
if index.depth() != self.total_depth { if index.depth() != self.total_depth {
return Err(MerkleError::InvalidDepth { return Err(MerkleError::InvalidDepth {
expected: self.total_depth, expected: self.total_depth,
@@ -143,28 +163,27 @@ impl MerklePathSet {
} }
// update the current path // update the current path
let index_value = index.to_scalar_index();
let upper_index_value = index_value / 2;
let parity = index_value & 1; let parity = index_value & 1;
path.insert(parity as usize, value); path.insert(parity as usize, value.into());
// traverse to the root, updating the nodes // traverse to the root, updating the nodes
let root: Word = Rpo256::merge(&[path[0].into(), path[1].into()]).into(); let root = Rpo256::merge(&[path[0], path[1]]);
let root = path.iter().skip(2).copied().fold(root, |root, hash| { let root = path.iter().skip(2).copied().fold(root, |root, hash| {
index.move_up(); index.move_up();
Rpo256::merge(&index.build_node(root.into(), hash.into())).into() Rpo256::merge(&index.build_node(root, hash))
}); });
// if the path set is empty (the root is all ZEROs), set the root to the root of the added // if the path set is empty (the root is all ZEROs), set the root to the root of the added
// path; otherwise, the root of the added path must be identical to the current root // path; otherwise, the root of the added path must be identical to the current root
if self.root == [ZERO; 4] { if self.root == RpoDigest::default() {
self.root = root; self.root = root;
} else if self.root != root { } else if self.root != root {
return Err(MerkleError::InvalidPath(path)); return Err(MerkleError::ConflictingRoots([self.root, root].to_vec()));
} }
// finish updating the path // finish updating the path
self.paths.insert(upper_index_value, path); let path_key = index_value - parity;
self.paths.insert(path_key, path);
Ok(()) Ok(())
} }
@@ -174,41 +193,35 @@ impl MerklePathSet {
/// Returns an error if: /// Returns an error if:
/// * Requested node does not exist in the set. /// * Requested node does not exist in the set.
pub fn update_leaf(&mut self, base_index_value: u64, value: Word) -> Result<(), MerkleError> { pub fn update_leaf(&mut self, base_index_value: u64, value: Word) -> Result<(), MerkleError> {
let depth = self.depth(); let mut index = NodeIndex::new(self.depth(), base_index_value)?;
let mut index = NodeIndex::new(depth, base_index_value); let parity = index.value() & 1;
if !index.is_valid() { let path_key = index.value() - parity;
return Err(MerkleError::InvalidIndex(index)); let path = match self.paths.get_mut(&path_key) {
}
let path = match self
.paths
.get_mut(&index.clone().move_up().to_scalar_index())
{
Some(path) => path, Some(path) => path,
None => return Err(MerkleError::NodeNotInSet(base_index_value)), None => return Err(MerkleError::NodeNotInSet(index)),
}; };
// Fill old_hashes vector ----------------------------------------------------------------- // Fill old_hashes vector -----------------------------------------------------------------
let mut current_index = index; let mut current_index = index;
let mut old_hashes = Vec::with_capacity(path.len().saturating_sub(2)); let mut old_hashes = Vec::with_capacity(path.len().saturating_sub(2));
let mut root: Word = Rpo256::merge(&[path[0].into(), path[1].into()]).into(); let mut root = Rpo256::merge(&[path[0], path[1]]);
for hash in path.iter().skip(2).copied() { for hash in path.iter().skip(2).copied() {
old_hashes.push(root); old_hashes.push(root);
current_index.move_up(); current_index.move_up();
let input = current_index.build_node(hash.into(), root.into()); let input = current_index.build_node(hash, root);
root = Rpo256::merge(&input).into(); root = Rpo256::merge(&input);
} }
// Fill new_hashes vector ----------------------------------------------------------------- // Fill new_hashes vector -----------------------------------------------------------------
path[index.is_value_odd() as usize] = value; path[index.is_value_odd() as usize] = value.into();
let mut new_hashes = Vec::with_capacity(path.len().saturating_sub(2)); let mut new_hashes = Vec::with_capacity(path.len().saturating_sub(2));
let mut new_root: Word = Rpo256::merge(&[path[0].into(), path[1].into()]).into(); let mut new_root = Rpo256::merge(&[path[0], path[1]]);
for path_hash in path.iter().skip(2).copied() { for path_hash in path.iter().skip(2).copied() {
new_hashes.push(new_root); new_hashes.push(new_root);
index.move_up(); index.move_up();
let input = current_index.build_node(path_hash.into(), new_root.into()); let input = current_index.build_node(path_hash, new_root);
new_root = Rpo256::merge(&input).into(); new_root = Rpo256::merge(&input);
} }
self.root = new_root; self.root = new_root;
@@ -233,7 +246,7 @@ impl MerklePathSet {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::merkle::int_to_node; use crate::merkle::{int_to_leaf, int_to_node};
#[test] #[test]
fn get_root() { fn get_root() {
@@ -247,7 +260,7 @@ mod tests {
let root_exp = calculate_parent_hash(parent0, 0, parent1); let root_exp = calculate_parent_hash(parent0, 0, parent1);
let set = super::MerklePathSet::new(3) let set = super::MerklePathSet::new(2)
.with_paths([(0, leaf0, vec![leaf1, parent1].into())]) .with_paths([(0, leaf0, vec![leaf1, parent1].into())])
.unwrap(); .unwrap();
@@ -259,14 +272,13 @@ mod tests {
let path_6 = vec![int_to_node(7), int_to_node(45), int_to_node(123)]; let path_6 = vec![int_to_node(7), int_to_node(45), int_to_node(123)];
let hash_6 = int_to_node(6); let hash_6 = int_to_node(6);
let index = 6_u64; let index = 6_u64;
let depth = 4_u8; let depth = 3_u8;
let set = super::MerklePathSet::new(depth) let set = super::MerklePathSet::new(depth)
.with_paths([(index, hash_6, path_6.clone().into())]) .with_paths([(index, hash_6, path_6.clone().into())])
.unwrap(); .unwrap();
let stored_path_6 = set.get_path(NodeIndex::new(depth, index)).unwrap(); let stored_path_6 = set.get_path(NodeIndex::make(depth, index)).unwrap();
assert_eq!(path_6, *stored_path_6); assert_eq!(path_6, *stored_path_6);
assert!(set.get_path(NodeIndex::new(depth, 15_u64)).is_err())
} }
#[test] #[test]
@@ -274,16 +286,10 @@ mod tests {
let path_6 = vec![int_to_node(7), int_to_node(45), int_to_node(123)]; let path_6 = vec![int_to_node(7), int_to_node(45), int_to_node(123)];
let hash_6 = int_to_node(6); let hash_6 = int_to_node(6);
let index = 6_u64; let index = 6_u64;
let depth = 4_u8; let depth = 3_u8;
let set = MerklePathSet::new(depth) let set = MerklePathSet::new(depth).with_paths([(index, hash_6, path_6.into())]).unwrap();
.with_paths([(index, hash_6, path_6.into())])
.unwrap();
assert_eq!( assert_eq!(int_to_node(6u64), set.get_node(NodeIndex::make(depth, index)).unwrap());
int_to_node(6u64),
set.get_node(NodeIndex::new(depth, index)).unwrap()
);
assert!(set.get_node(NodeIndex::new(depth, 15_u64)).is_err());
} }
#[test] #[test]
@@ -304,7 +310,7 @@ mod tests {
let index_6 = 6_u64; let index_6 = 6_u64;
let index_5 = 5_u64; let index_5 = 5_u64;
let index_4 = 4_u64; let index_4 = 4_u64;
let depth = 4_u8; let depth = 3_u8;
let mut set = MerklePathSet::new(depth) let mut set = MerklePathSet::new(depth)
.with_paths([ .with_paths([
(index_6, hash_6, path_6.into()), (index_6, hash_6, path_6.into()),
@@ -313,20 +319,72 @@ mod tests {
]) ])
.unwrap(); .unwrap();
let new_hash_6 = int_to_node(100); let new_hash_6 = int_to_leaf(100);
let new_hash_5 = int_to_node(55); let new_hash_5 = int_to_leaf(55);
set.update_leaf(index_6, new_hash_6).unwrap(); set.update_leaf(index_6, new_hash_6).unwrap();
let new_path_4 = set.get_path(NodeIndex::new(depth, index_4)).unwrap(); let new_path_4 = set.get_path(NodeIndex::make(depth, index_4)).unwrap();
let new_hash_67 = calculate_parent_hash(new_hash_6, 14_u64, hash_7); let new_hash_67 = calculate_parent_hash(new_hash_6.into(), 14_u64, hash_7);
assert_eq!(new_hash_67, new_path_4[1]); assert_eq!(new_hash_67, new_path_4[1]);
set.update_leaf(index_5, new_hash_5).unwrap(); set.update_leaf(index_5, new_hash_5).unwrap();
let new_path_4 = set.get_path(NodeIndex::new(depth, index_4)).unwrap(); let new_path_4 = set.get_path(NodeIndex::make(depth, index_4)).unwrap();
let new_path_6 = set.get_path(NodeIndex::new(depth, index_6)).unwrap(); let new_path_6 = set.get_path(NodeIndex::make(depth, index_6)).unwrap();
let new_hash_45 = calculate_parent_hash(new_hash_5, 13_u64, hash_4); let new_hash_45 = calculate_parent_hash(new_hash_5.into(), 13_u64, hash_4);
assert_eq!(new_hash_45, new_path_6[1]); assert_eq!(new_hash_45, new_path_6[1]);
assert_eq!(new_hash_5, new_path_4[0]); assert_eq!(RpoDigest::from(new_hash_5), new_path_4[0]);
}
#[test]
fn depth_3_is_correct() {
let a = int_to_node(1);
let b = int_to_node(2);
let c = int_to_node(3);
let d = int_to_node(4);
let e = int_to_node(5);
let f = int_to_node(6);
let g = int_to_node(7);
let h = int_to_node(8);
let i = Rpo256::merge(&[a, b]);
let j = Rpo256::merge(&[c, d]);
let k = Rpo256::merge(&[e, f]);
let l = Rpo256::merge(&[g, h]);
let m = Rpo256::merge(&[i, j]);
let n = Rpo256::merge(&[k, l]);
let root = Rpo256::merge(&[m, n]);
let mut set = MerklePathSet::new(3);
let value = b;
let index = 1;
let path = MerklePath::new([a, j, n].to_vec());
set.add_path(index, value.into(), path).unwrap();
assert_eq!(*value, set.get_leaf(index).unwrap());
assert_eq!(root, set.root());
let value = e;
let index = 4;
let path = MerklePath::new([f, l, m].to_vec());
set.add_path(index, value.into(), path).unwrap();
assert_eq!(*value, set.get_leaf(index).unwrap());
assert_eq!(root, set.root());
let value = a;
let index = 0;
let path = MerklePath::new([b, j, n].to_vec());
set.add_path(index, value.into(), path).unwrap();
assert_eq!(*value, set.get_leaf(index).unwrap());
assert_eq!(root, set.root());
let value = h;
let index = 7;
let path = MerklePath::new([g, k, m].to_vec());
set.add_path(index, value.into(), path).unwrap();
assert_eq!(*value, set.get_leaf(index).unwrap());
assert_eq!(root, set.root());
} }
// HELPER FUNCTIONS // HELPER FUNCTIONS
@@ -340,11 +398,11 @@ mod tests {
/// - node — current node /// - node — current node
/// - node_pos — position of the current node /// - node_pos — position of the current node
/// - sibling — neighboring vertex in the tree /// - sibling — neighboring vertex in the tree
fn calculate_parent_hash(node: Word, node_pos: u64, sibling: Word) -> Word { fn calculate_parent_hash(node: RpoDigest, node_pos: u64, sibling: RpoDigest) -> RpoDigest {
if is_even(node_pos) { if is_even(node_pos) {
Rpo256::merge(&[node.into(), sibling.into()]).into() Rpo256::merge(&[node, sibling])
} else { } else {
Rpo256::merge(&[sibling.into(), node.into()]).into() Rpo256::merge(&[sibling, node])
} }
} }
} }

View File

@@ -1,4 +1,7 @@
use super::{BTreeMap, MerkleError, MerklePath, NodeIndex, Rpo256, RpoDigest, Vec, Word}; use super::{
BTreeMap, BTreeSet, EmptySubtreeRoots, InnerNodeInfo, MerkleError, MerklePath, NodeIndex,
Rpo256, RpoDigest, Vec, Word,
};
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
@@ -6,14 +9,16 @@ mod tests;
// SPARSE MERKLE TREE // SPARSE MERKLE TREE
// ================================================================================================ // ================================================================================================
/// A sparse Merkle tree with 63-bit keys and 4-element leaf values, without compaction. /// A sparse Merkle tree with 64-bit keys and 4-element leaf values, without compaction.
/// Manipulation and retrieval of leaves and internal nodes is provided by its internal `Store`. ///
/// The root of the tree is recomputed on each new leaf update. /// The root of the tree is recomputed on each new leaf update.
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct SimpleSmt { pub struct SimpleSmt {
root: Word,
depth: u8, depth: u8,
store: Store, root: RpoDigest,
leaves: BTreeMap<u64, Word>,
branches: BTreeMap<NodeIndex, BranchNode>,
empty_hashes: Vec<RpoDigest>,
} }
impl SimpleSmt { impl SimpleSmt {
@@ -24,45 +29,88 @@ impl SimpleSmt {
pub const MIN_DEPTH: u8 = 1; pub const MIN_DEPTH: u8 = 1;
/// Maximum supported depth. /// Maximum supported depth.
pub const MAX_DEPTH: u8 = 63; pub const MAX_DEPTH: u8 = 64;
/// Value of an empty leaf.
pub const EMPTY_VALUE: Word = super::empty_roots::EMPTY_WORD;
// CONSTRUCTORS // CONSTRUCTORS
// -------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------
/// Creates a new simple SMT. /// Returns a new [SimpleSmt] instantiated with the specified depth.
/// ///
/// The provided entries will be tuples of the leaves and their corresponding keys. /// All leaves in the returned tree are set to [ZERO; 4].
/// ///
/// # Errors /// # Errors
/// /// Returns an error if the depth is 0 or is greater than 64.
/// The function will fail if the provided entries count exceed the maximum tree capacity, that pub fn new(depth: u8) -> Result<Self, MerkleError> {
/// is `2^{depth}`.
pub fn new<R, I>(entries: R, depth: u8) -> Result<Self, MerkleError>
where
R: IntoIterator<IntoIter = I>,
I: Iterator<Item = (u64, Word)> + ExactSizeIterator,
{
let mut entries = entries.into_iter();
// validate the range of the depth. // validate the range of the depth.
let max = 1 << depth;
if depth < Self::MIN_DEPTH { if depth < Self::MIN_DEPTH {
return Err(MerkleError::DepthTooSmall(depth)); return Err(MerkleError::DepthTooSmall(depth));
} else if Self::MAX_DEPTH < depth { } else if Self::MAX_DEPTH < depth {
return Err(MerkleError::DepthTooBig(depth as u64)); return Err(MerkleError::DepthTooBig(depth as u64));
} else if entries.len() > max {
return Err(MerkleError::InvalidEntriesCount(max, entries.len()));
} }
let (store, root) = Store::new(depth); let empty_hashes = EmptySubtreeRoots::empty_hashes(depth).to_vec();
let mut tree = Self { root, depth, store }; let root = empty_hashes[0];
entries.try_for_each(|(key, leaf)| tree.insert_leaf(key, leaf))?;
Ok(Self {
root,
depth,
empty_hashes,
leaves: BTreeMap::new(),
branches: BTreeMap::new(),
})
}
/// Returns a new [SimpleSmt] instantiated with the specified depth and with leaves
/// set as specified by the provided entries.
///
/// All leaves omitted from the entries list are set to [ZERO; 4].
///
/// # Errors
/// Returns an error if:
/// - If the depth is 0 or is greater than 64.
/// - The number of entries exceeds the maximum tree capacity, that is 2^{depth}.
/// - The provided entries contain multiple values for the same key.
pub fn with_leaves<R, I>(depth: u8, entries: R) -> Result<Self, MerkleError>
where
R: IntoIterator<IntoIter = I>,
I: Iterator<Item = (u64, Word)> + ExactSizeIterator,
{
// create an empty tree
let mut tree = Self::new(depth)?;
// check if the number of leaves can be accommodated by the tree's depth; we use a min
// depth of 63 because we consider passing in a vector of size 2^64 infeasible.
let entries = entries.into_iter();
let max = 1 << tree.depth.min(63);
if entries.len() > max {
return Err(MerkleError::InvalidNumEntries(max, entries.len()));
}
// append leaves to the tree returning an error if a duplicate entry for the same key
// is found
let mut empty_entries = BTreeSet::new();
for (key, value) in entries {
let old_value = tree.update_leaf(key, value)?;
if old_value != Self::EMPTY_VALUE || empty_entries.contains(&key) {
return Err(MerkleError::DuplicateValuesForIndex(key));
}
// if we've processed an empty entry, add the key to the set of empty entry keys, and
// if this key was already in the set, return an error
if value == Self::EMPTY_VALUE && !empty_entries.insert(key) {
return Err(MerkleError::DuplicateValuesForIndex(key));
}
}
Ok(tree) Ok(tree)
} }
// PUBLIC ACCESSORS
// --------------------------------------------------------------------------------------------
/// Returns the root of this Merkle tree. /// Returns the root of this Merkle tree.
pub const fn root(&self) -> Word { pub const fn root(&self) -> RpoDigest {
self.root self.root
} }
@@ -71,193 +119,159 @@ impl SimpleSmt {
self.depth self.depth
} }
/// Returns the set count of the keys of the leaves. /// Returns a node at the specified index.
pub fn leaves_count(&self) -> usize {
self.store.leaves_count()
}
/// Returns a node at the specified key
/// ///
/// # Errors /// # Errors
/// Returns an error if: /// Returns an error if the specified index has depth set to 0 or the depth is greater than
/// * The specified depth is greater than the depth of the tree. /// the depth of this Merkle tree.
/// * The specified key does not exist pub fn get_node(&self, index: NodeIndex) -> Result<RpoDigest, MerkleError> {
pub fn get_node(&self, index: &NodeIndex) -> Result<Word, MerkleError> {
if index.is_root() { if index.is_root() {
Err(MerkleError::DepthTooSmall(index.depth())) Err(MerkleError::DepthTooSmall(index.depth()))
} else if index.depth() > self.depth() { } else if index.depth() > self.depth() {
Err(MerkleError::DepthTooBig(index.depth() as u64)) Err(MerkleError::DepthTooBig(index.depth() as u64))
} else if index.depth() == self.depth() { } else if index.depth() == self.depth() {
self.store.get_leaf_node(index.value()) // the lookup in empty_hashes could fail only if empty_hashes were not built correctly
// by the constructor as we check the depth of the lookup above.
Ok(RpoDigest::from(
self.get_leaf_node(index.value())
.unwrap_or_else(|| *self.empty_hashes[index.depth() as usize]),
))
} else { } else {
let branch_node = self.store.get_branch_node(index)?; Ok(self.get_branch_node(&index).parent())
Ok(Rpo256::merge(&[branch_node.left, branch_node.right]).into())
} }
} }
/// Returns a Merkle path from the node at the specified key to the root. The node itself is /// Returns a value of the leaf at the specified index.
/// not included in the path.
/// ///
/// # Errors /// # Errors
/// Returns an error if: /// Returns an error if the index is greater than the maximum tree capacity, that is 2^{depth}.
/// * The specified key does not exist as a branch or leaf node pub fn get_leaf(&self, index: u64) -> Result<Word, MerkleError> {
/// * The specified depth is greater than the depth of the tree. let index = NodeIndex::new(self.depth, index)?;
Ok(self.get_node(index)?.into())
}
/// Returns a Merkle path from the node at the specified index to the root.
///
/// The node itself is not included in the path.
///
/// # Errors
/// Returns an error if the specified index has depth set to 0 or the depth is greater than
/// the depth of this Merkle tree.
pub fn get_path(&self, mut index: NodeIndex) -> Result<MerklePath, MerkleError> { pub fn get_path(&self, mut index: NodeIndex) -> Result<MerklePath, MerkleError> {
if index.is_root() { if index.is_root() {
return Err(MerkleError::DepthTooSmall(index.depth())); return Err(MerkleError::DepthTooSmall(index.depth()));
} else if index.depth() > self.depth() { } else if index.depth() > self.depth() {
return Err(MerkleError::DepthTooBig(index.depth() as u64)); return Err(MerkleError::DepthTooBig(index.depth() as u64));
} else if index.depth() == self.depth() && !self.store.check_leaf_node_exists(index.value())
{
return Err(MerkleError::InvalidIndex(index.with_depth(self.depth())));
} }
let mut path = Vec::with_capacity(index.depth() as usize); let mut path = Vec::with_capacity(index.depth() as usize);
for _ in 0..index.depth() { for _ in 0..index.depth() {
let is_right = index.is_value_odd(); let is_right = index.is_value_odd();
index.move_up(); index.move_up();
let BranchNode { left, right } = self.store.get_branch_node(&index)?; let BranchNode { left, right } = self.get_branch_node(&index);
let value = if is_right { left } else { right }; let value = if is_right { left } else { right };
path.push(*value); path.push(value);
} }
Ok(path.into()) Ok(MerklePath::new(path))
} }
/// Return a Merkle path from the leaf at the specified key to the root. The leaf itself is not /// Return a Merkle path from the leaf at the specified index to the root.
/// included in the path. ///
/// The leaf itself is not included in the path.
/// ///
/// # Errors /// # Errors
/// Returns an error if: /// Returns an error if the index is greater than the maximum tree capacity, that is 2^{depth}.
/// * The specified key does not exist as a leaf node. pub fn get_leaf_path(&self, index: u64) -> Result<MerklePath, MerkleError> {
pub fn get_leaf_path(&self, key: u64) -> Result<MerklePath, MerkleError> { let index = NodeIndex::new(self.depth(), index)?;
self.get_path(NodeIndex::new(self.depth(), key)) self.get_path(index)
} }
/// Replaces the leaf located at the specified key, and recomputes hashes by walking up the tree // ITERATORS
// --------------------------------------------------------------------------------------------
/// Returns an iterator over the leaves of this [SimpleSmt].
pub fn leaves(&self) -> impl Iterator<Item = (u64, &Word)> {
self.leaves.iter().map(|(i, w)| (*i, w))
}
/// Returns an iterator over the inner nodes of this Merkle tree.
pub fn inner_nodes(&self) -> impl Iterator<Item = InnerNodeInfo> + '_ {
self.branches.values().map(|e| InnerNodeInfo {
value: e.parent(),
left: e.left,
right: e.right,
})
}
// STATE MUTATORS
// --------------------------------------------------------------------------------------------
/// Updates value of the leaf at the specified index returning the old leaf value.
///
/// This also recomputes all hashes between the leaf and the root, updating the root itself.
/// ///
/// # Errors /// # Errors
/// Returns an error if the specified key is not a valid leaf index for this tree. /// Returns an error if the index is greater than the maximum tree capacity, that is 2^{depth}.
pub fn update_leaf(&mut self, key: u64, value: Word) -> Result<(), MerkleError> { pub fn update_leaf(&mut self, index: u64, value: Word) -> Result<Word, MerkleError> {
if !self.store.check_leaf_node_exists(key) { let old_value = self.insert_leaf_node(index, value).unwrap_or(Self::EMPTY_VALUE);
return Err(MerkleError::InvalidIndex(NodeIndex::new(self.depth(), key)));
}
self.insert_leaf(key, value)?;
Ok(()) // if the old value and new value are the same, there is nothing to update
if value == old_value {
return Ok(value);
} }
/// Inserts a leaf located at the specified key, and recomputes hashes by walking up the tree let mut index = NodeIndex::new(self.depth(), index)?;
pub fn insert_leaf(&mut self, key: u64, value: Word) -> Result<(), MerkleError> {
self.store.insert_leaf_node(key, value);
// TODO consider using a map `index |-> word` instead of `index |-> (word, word)`
let mut index = NodeIndex::new(self.depth(), key);
let mut value = RpoDigest::from(value); let mut value = RpoDigest::from(value);
for _ in 0..index.depth() { for _ in 0..index.depth() {
let is_right = index.is_value_odd(); let is_right = index.is_value_odd();
index.move_up(); index.move_up();
let BranchNode { left, right } = self let BranchNode { left, right } = self.get_branch_node(&index);
.store let (left, right) = if is_right { (left, value) } else { (value, right) };
.get_branch_node(&index) self.insert_branch_node(index, left, right);
.unwrap_or_else(|_| self.store.get_empty_node(index.depth() as usize + 1));
let (left, right) = if is_right {
(left, value)
} else {
(value, right)
};
self.store.insert_branch_node(index, left, right);
value = Rpo256::merge(&[left, right]); value = Rpo256::merge(&[left, right]);
} }
self.root = value.into(); self.root = value;
Ok(()) Ok(old_value)
}
// HELPER METHODS
// --------------------------------------------------------------------------------------------
fn get_leaf_node(&self, key: u64) -> Option<Word> {
self.leaves.get(&key).copied()
}
fn insert_leaf_node(&mut self, key: u64, node: Word) -> Option<Word> {
self.leaves.insert(key, node)
}
fn get_branch_node(&self, index: &NodeIndex) -> BranchNode {
self.branches.get(index).cloned().unwrap_or_else(|| {
let node = self.empty_hashes[index.depth() as usize + 1];
BranchNode {
left: node,
right: node,
}
})
}
fn insert_branch_node(&mut self, index: NodeIndex, left: RpoDigest, right: RpoDigest) {
let branch = BranchNode { left, right };
self.branches.insert(index, branch);
} }
} }
// STORE // BRANCH NODE
// ================================================================================================ // ================================================================================================
/// A data store for sparse Merkle tree key-value pairs.
/// Leaves and branch nodes are stored separately in B-tree maps, indexed by key and (key, depth)
/// respectively. Hashes for blank subtrees at each layer are stored in `empty_hashes`, beginning
/// with the root hash of an empty tree, and ending with the zero value of a leaf node.
#[derive(Debug, Clone, PartialEq, Eq)]
struct Store {
branches: BTreeMap<NodeIndex, BranchNode>,
leaves: BTreeMap<u64, Word>,
empty_hashes: Vec<RpoDigest>,
depth: u8,
}
#[derive(Debug, Default, Clone, PartialEq, Eq)] #[derive(Debug, Default, Clone, PartialEq, Eq)]
struct BranchNode { struct BranchNode {
left: RpoDigest, left: RpoDigest,
right: RpoDigest, right: RpoDigest,
} }
impl Store { impl BranchNode {
fn new(depth: u8) -> (Self, Word) { fn parent(&self) -> RpoDigest {
let branches = BTreeMap::new(); Rpo256::merge(&[self.left, self.right])
let leaves = BTreeMap::new();
// Construct empty node digests for each layer of the tree
let empty_hashes: Vec<RpoDigest> = (0..depth + 1)
.scan(Word::default().into(), |state, _| {
let value = *state;
*state = Rpo256::merge(&[value, value]);
Some(value)
})
.collect::<Vec<_>>()
.into_iter()
.rev()
.collect();
let root = empty_hashes[0].into();
let store = Self {
branches,
leaves,
empty_hashes,
depth,
};
(store, root)
}
fn get_empty_node(&self, depth: usize) -> BranchNode {
let digest = self.empty_hashes[depth];
BranchNode {
left: digest,
right: digest,
}
}
fn check_leaf_node_exists(&self, key: u64) -> bool {
self.leaves.contains_key(&key)
}
fn get_leaf_node(&self, key: u64) -> Result<Word, MerkleError> {
self.leaves
.get(&key)
.cloned()
.ok_or(MerkleError::InvalidIndex(NodeIndex::new(self.depth, key)))
}
fn insert_leaf_node(&mut self, key: u64, node: Word) {
self.leaves.insert(key, node);
}
fn get_branch_node(&self, index: &NodeIndex) -> Result<BranchNode, MerkleError> {
self.branches
.get(index)
.cloned()
.ok_or(MerkleError::InvalidIndex(*index))
}
fn insert_branch_node(&mut self, index: NodeIndex, left: RpoDigest, right: RpoDigest) {
let branch = BranchNode { left, right };
self.branches.insert(index, branch);
}
fn leaves_count(&self) -> usize {
self.leaves.len()
} }
} }

View File

@@ -1,23 +1,21 @@
use super::{ use super::{
super::{MerkleTree, RpoDigest, SimpleSmt}, super::{InnerNodeInfo, MerkleError, MerkleTree, RpoDigest, SimpleSmt},
NodeIndex, Rpo256, Vec, Word, NodeIndex, Rpo256, Vec,
}; };
use crate::{Felt, FieldElement}; use crate::{
use core::iter; merkle::{digests_to_words, empty_roots::EMPTY_WORD, int_to_leaf, int_to_node},
use proptest::prelude::*; Word,
use rand_utils::prng_array; };
// TEST DATA
// ================================================================================================
const KEYS4: [u64; 4] = [0, 1, 2, 3]; const KEYS4: [u64; 4] = [0, 1, 2, 3];
const KEYS8: [u64; 8] = [0, 1, 2, 3, 4, 5, 6, 7]; const KEYS8: [u64; 8] = [0, 1, 2, 3, 4, 5, 6, 7];
const VALUES4: [Word; 4] = [ const VALUES4: [RpoDigest; 4] = [int_to_node(1), int_to_node(2), int_to_node(3), int_to_node(4)];
int_to_node(1),
int_to_node(2),
int_to_node(3),
int_to_node(4),
];
const VALUES8: [Word; 8] = [ const VALUES8: [RpoDigest; 8] = [
int_to_node(1), int_to_node(1),
int_to_node(2), int_to_node(2),
int_to_node(3), int_to_node(3),
@@ -28,136 +26,150 @@ const VALUES8: [Word; 8] = [
int_to_node(8), int_to_node(8),
]; ];
const ZERO_VALUES8: [Word; 8] = [int_to_node(0); 8]; const ZERO_VALUES8: [Word; 8] = [int_to_leaf(0); 8];
// TESTS
// ================================================================================================
#[test] #[test]
fn build_empty_tree() { fn build_empty_tree() {
let smt = SimpleSmt::new(iter::empty(), 3).unwrap(); // tree of depth 3
let smt = SimpleSmt::new(3).unwrap();
let mt = MerkleTree::new(ZERO_VALUES8.to_vec()).unwrap(); let mt = MerkleTree::new(ZERO_VALUES8.to_vec()).unwrap();
assert_eq!(mt.root(), smt.root()); assert_eq!(mt.root(), smt.root());
} }
#[test]
fn empty_digests_are_consistent() {
let depth = 5;
let root = SimpleSmt::new(iter::empty(), depth).unwrap().root();
let computed: [RpoDigest; 2] = (0..depth).fold([Default::default(); 2], |state, _| {
let digest = Rpo256::merge(&state);
[digest; 2]
});
assert_eq!(Word::from(computed[0]), root);
}
#[test] #[test]
fn build_sparse_tree() { fn build_sparse_tree() {
let mut smt = SimpleSmt::new(iter::empty(), 3).unwrap(); let mut smt = SimpleSmt::new(3).unwrap();
let mut values = ZERO_VALUES8.to_vec(); let mut values = ZERO_VALUES8.to_vec();
// insert single value // insert single value
let key = 6; let key = 6;
let new_node = int_to_node(7); let new_node = int_to_leaf(7);
values[key as usize] = new_node; values[key as usize] = new_node;
smt.insert_leaf(key, new_node) let old_value = smt.update_leaf(key, new_node).expect("Failed to update leaf");
.expect("Failed to insert leaf");
let mt2 = MerkleTree::new(values.clone()).unwrap(); let mt2 = MerkleTree::new(values.clone()).unwrap();
assert_eq!(mt2.root(), smt.root()); assert_eq!(mt2.root(), smt.root());
assert_eq!( assert_eq!(
mt2.get_path(NodeIndex::new(3, 6)).unwrap(), mt2.get_path(NodeIndex::make(3, 6)).unwrap(),
smt.get_path(NodeIndex::new(3, 6)).unwrap() smt.get_path(NodeIndex::make(3, 6)).unwrap()
); );
assert_eq!(old_value, EMPTY_WORD);
// insert second value at distinct leaf branch // insert second value at distinct leaf branch
let key = 2; let key = 2;
let new_node = int_to_node(3); let new_node = int_to_leaf(3);
values[key as usize] = new_node; values[key as usize] = new_node;
smt.insert_leaf(key, new_node) let old_value = smt.update_leaf(key, new_node).expect("Failed to update leaf");
.expect("Failed to insert leaf");
let mt3 = MerkleTree::new(values).unwrap(); let mt3 = MerkleTree::new(values).unwrap();
assert_eq!(mt3.root(), smt.root()); assert_eq!(mt3.root(), smt.root());
assert_eq!( assert_eq!(
mt3.get_path(NodeIndex::new(3, 2)).unwrap(), mt3.get_path(NodeIndex::make(3, 2)).unwrap(),
smt.get_path(NodeIndex::new(3, 2)).unwrap() smt.get_path(NodeIndex::make(3, 2)).unwrap()
); );
assert_eq!(old_value, EMPTY_WORD);
} }
#[test] #[test]
fn build_full_tree() { fn test_depth2_tree() {
let tree = SimpleSmt::new(KEYS4.into_iter().zip(VALUES4.into_iter()), 2).unwrap(); let tree =
SimpleSmt::with_leaves(2, KEYS4.into_iter().zip(digests_to_words(&VALUES4).into_iter()))
.unwrap();
// check internal structure
let (root, node2, node3) = compute_internal_nodes(); let (root, node2, node3) = compute_internal_nodes();
assert_eq!(root, tree.root()); assert_eq!(root, tree.root());
assert_eq!(node2, tree.get_node(&NodeIndex::new(1, 0)).unwrap()); assert_eq!(node2, tree.get_node(NodeIndex::make(1, 0)).unwrap());
assert_eq!(node3, tree.get_node(&NodeIndex::new(1, 1)).unwrap()); assert_eq!(node3, tree.get_node(NodeIndex::make(1, 1)).unwrap());
// check get_node()
assert_eq!(VALUES4[0], tree.get_node(NodeIndex::make(2, 0)).unwrap());
assert_eq!(VALUES4[1], tree.get_node(NodeIndex::make(2, 1)).unwrap());
assert_eq!(VALUES4[2], tree.get_node(NodeIndex::make(2, 2)).unwrap());
assert_eq!(VALUES4[3], tree.get_node(NodeIndex::make(2, 3)).unwrap());
// check get_path(): depth 2
assert_eq!(vec![VALUES4[1], node3], *tree.get_path(NodeIndex::make(2, 0)).unwrap());
assert_eq!(vec![VALUES4[0], node3], *tree.get_path(NodeIndex::make(2, 1)).unwrap());
assert_eq!(vec![VALUES4[3], node2], *tree.get_path(NodeIndex::make(2, 2)).unwrap());
assert_eq!(vec![VALUES4[2], node2], *tree.get_path(NodeIndex::make(2, 3)).unwrap());
// check get_path(): depth 1
assert_eq!(vec![node3], *tree.get_path(NodeIndex::make(1, 0)).unwrap());
assert_eq!(vec![node2], *tree.get_path(NodeIndex::make(1, 1)).unwrap());
} }
#[test] #[test]
fn get_values() { fn test_inner_node_iterator() -> Result<(), MerkleError> {
let tree = SimpleSmt::new(KEYS4.into_iter().zip(VALUES4.into_iter()), 2).unwrap(); let tree =
SimpleSmt::with_leaves(2, KEYS4.into_iter().zip(digests_to_words(&VALUES4).into_iter()))
.unwrap();
// check depth 2 // check depth 2
assert_eq!(VALUES4[0], tree.get_node(&NodeIndex::new(2, 0)).unwrap()); assert_eq!(VALUES4[0], tree.get_node(NodeIndex::make(2, 0)).unwrap());
assert_eq!(VALUES4[1], tree.get_node(&NodeIndex::new(2, 1)).unwrap()); assert_eq!(VALUES4[1], tree.get_node(NodeIndex::make(2, 1)).unwrap());
assert_eq!(VALUES4[2], tree.get_node(&NodeIndex::new(2, 2)).unwrap()); assert_eq!(VALUES4[2], tree.get_node(NodeIndex::make(2, 2)).unwrap());
assert_eq!(VALUES4[3], tree.get_node(&NodeIndex::new(2, 3)).unwrap()); assert_eq!(VALUES4[3], tree.get_node(NodeIndex::make(2, 3)).unwrap());
}
#[test] // get parent nodes
fn get_path() { let root = tree.root();
let tree = SimpleSmt::new(KEYS4.into_iter().zip(VALUES4.into_iter()), 2).unwrap(); let l1n0 = tree.get_node(NodeIndex::make(1, 0))?;
let l1n1 = tree.get_node(NodeIndex::make(1, 1))?;
let l2n0 = tree.get_node(NodeIndex::make(2, 0))?;
let l2n1 = tree.get_node(NodeIndex::make(2, 1))?;
let l2n2 = tree.get_node(NodeIndex::make(2, 2))?;
let l2n3 = tree.get_node(NodeIndex::make(2, 3))?;
let (_, node2, node3) = compute_internal_nodes(); let nodes: Vec<InnerNodeInfo> = tree.inner_nodes().collect();
let expected = vec![
InnerNodeInfo {
value: root,
left: l1n0,
right: l1n1,
},
InnerNodeInfo {
value: l1n0,
left: l2n0,
right: l2n1,
},
InnerNodeInfo {
value: l1n1,
left: l2n2,
right: l2n3,
},
];
assert_eq!(nodes, expected);
// check depth 2 Ok(())
assert_eq!(
vec![VALUES4[1], node3],
*tree.get_path(NodeIndex::new(2, 0)).unwrap()
);
assert_eq!(
vec![VALUES4[0], node3],
*tree.get_path(NodeIndex::new(2, 1)).unwrap()
);
assert_eq!(
vec![VALUES4[3], node2],
*tree.get_path(NodeIndex::new(2, 2)).unwrap()
);
assert_eq!(
vec![VALUES4[2], node2],
*tree.get_path(NodeIndex::new(2, 3)).unwrap()
);
// check depth 1
assert_eq!(vec![node3], *tree.get_path(NodeIndex::new(1, 0)).unwrap());
assert_eq!(vec![node2], *tree.get_path(NodeIndex::new(1, 1)).unwrap());
} }
#[test] #[test]
fn update_leaf() { fn update_leaf() {
let mut tree = SimpleSmt::new(KEYS8.into_iter().zip(VALUES8.into_iter()), 3).unwrap(); let mut tree =
SimpleSmt::with_leaves(3, KEYS8.into_iter().zip(digests_to_words(&VALUES8).into_iter()))
.unwrap();
// update one value // update one value
let key = 3; let key = 3;
let new_node = int_to_node(9); let new_node = int_to_leaf(9);
let mut expected_values = VALUES8.to_vec(); let mut expected_values = digests_to_words(&VALUES8);
expected_values[key] = new_node; expected_values[key] = new_node;
let expected_tree = SimpleSmt::new( let expected_tree = MerkleTree::new(expected_values.clone()).unwrap();
KEYS8.into_iter().zip(expected_values.clone().into_iter()),
3,
)
.unwrap();
tree.update_leaf(key as u64, new_node).unwrap(); let old_leaf = tree.update_leaf(key as u64, new_node).unwrap();
assert_eq!(expected_tree.root, tree.root); assert_eq!(expected_tree.root(), tree.root);
assert_eq!(old_leaf, *VALUES8[key]);
// update another value // update another value
let key = 6; let key = 6;
let new_node = int_to_node(10); let new_node = int_to_leaf(10);
expected_values[key] = new_node; expected_values[key] = new_node;
let expected_tree = let expected_tree = MerkleTree::new(expected_values.clone()).unwrap();
SimpleSmt::new(KEYS8.into_iter().zip(expected_values.into_iter()), 3).unwrap();
tree.update_leaf(key as u64, new_node).unwrap(); let old_leaf = tree.update_leaf(key as u64, new_node).unwrap();
assert_eq!(expected_tree.root, tree.root); assert_eq!(expected_tree.root(), tree.root);
assert_eq!(old_leaf, *VALUES8[key]);
} }
#[test] #[test]
@@ -170,34 +182,34 @@ fn small_tree_opening_is_consistent() {
// / \ / \ / \ / \ // / \ / \ / \ / \
// a b 0 0 c 0 0 d // a b 0 0 c 0 0 d
let z = Word::from(RpoDigest::default()); let z = EMPTY_WORD;
let a = Word::from(Rpo256::merge(&[z.into(); 2])); let a = Word::from(Rpo256::merge(&[z.into(); 2]));
let b = Word::from(Rpo256::merge(&[a.into(); 2])); let b = Word::from(Rpo256::merge(&[a.into(); 2]));
let c = Word::from(Rpo256::merge(&[b.into(); 2])); let c = Word::from(Rpo256::merge(&[b.into(); 2]));
let d = Word::from(Rpo256::merge(&[c.into(); 2])); let d = Word::from(Rpo256::merge(&[c.into(); 2]));
let e = Word::from(Rpo256::merge(&[a.into(), b.into()])); let e = Rpo256::merge(&[a.into(), b.into()]);
let f = Word::from(Rpo256::merge(&[z.into(), z.into()])); let f = Rpo256::merge(&[z.into(), z.into()]);
let g = Word::from(Rpo256::merge(&[c.into(), z.into()])); let g = Rpo256::merge(&[c.into(), z.into()]);
let h = Word::from(Rpo256::merge(&[z.into(), d.into()])); let h = Rpo256::merge(&[z.into(), d.into()]);
let i = Word::from(Rpo256::merge(&[e.into(), f.into()])); let i = Rpo256::merge(&[e, f]);
let j = Word::from(Rpo256::merge(&[g.into(), h.into()])); let j = Rpo256::merge(&[g, h]);
let k = Word::from(Rpo256::merge(&[i.into(), j.into()])); let k = Rpo256::merge(&[i, j]);
let depth = 3; let depth = 3;
let entries = vec![(0, a), (1, b), (4, c), (7, d)]; let entries = vec![(0, a), (1, b), (4, c), (7, d)];
let tree = SimpleSmt::new(entries, depth).unwrap(); let tree = SimpleSmt::with_leaves(depth, entries).unwrap();
assert_eq!(tree.root(), Word::from(k)); assert_eq!(tree.root(), k);
let cases: Vec<(u8, u64, Vec<Word>)> = vec![ let cases: Vec<(u8, u64, Vec<RpoDigest>)> = vec![
(3, 0, vec![b, f, j]), (3, 0, vec![b.into(), f, j]),
(3, 1, vec![a, f, j]), (3, 1, vec![a.into(), f, j]),
(3, 4, vec![z, h, i]), (3, 4, vec![z.into(), h, i]),
(3, 7, vec![z, g, i]), (3, 7, vec![z.into(), g, i]),
(2, 0, vec![f, j]), (2, 0, vec![f, j]),
(2, 1, vec![e, j]), (2, 1, vec![e, j]),
(2, 2, vec![h, i]), (2, 2, vec![h, i]),
@@ -207,75 +219,45 @@ fn small_tree_opening_is_consistent() {
]; ];
for (depth, key, path) in cases { for (depth, key, path) in cases {
let opening = tree.get_path(NodeIndex::new(depth, key)).unwrap(); let opening = tree.get_path(NodeIndex::make(depth, key)).unwrap();
assert_eq!(path, *opening); assert_eq!(path, *opening);
} }
} }
proptest! {
#[test] #[test]
fn arbitrary_openings_single_leaf( fn fail_on_duplicates() {
depth in SimpleSmt::MIN_DEPTH..SimpleSmt::MAX_DEPTH, let entries = [(1_u64, int_to_leaf(1)), (5, int_to_leaf(2)), (1_u64, int_to_leaf(3))];
key in prop::num::u64::ANY, let smt = SimpleSmt::with_leaves(64, entries);
leaf in prop::num::u64::ANY, assert!(smt.is_err());
) {
let mut tree = SimpleSmt::new(iter::empty(), depth).unwrap();
let key = key % (1 << depth as u64); let entries = [(1_u64, int_to_leaf(0)), (5, int_to_leaf(2)), (1_u64, int_to_leaf(0))];
let leaf = int_to_node(leaf); let smt = SimpleSmt::with_leaves(64, entries);
assert!(smt.is_err());
tree.insert_leaf(key, leaf.into()).unwrap(); let entries = [(1_u64, int_to_leaf(0)), (5, int_to_leaf(2)), (1_u64, int_to_leaf(1))];
tree.get_leaf_path(key).unwrap(); let smt = SimpleSmt::with_leaves(64, entries);
assert!(smt.is_err());
// traverse to root, fetching all paths let entries = [(1_u64, int_to_leaf(1)), (5, int_to_leaf(2)), (1_u64, int_to_leaf(0))];
for d in 1..depth { let smt = SimpleSmt::with_leaves(64, entries);
let k = key >> (depth - d); assert!(smt.is_err());
tree.get_path(NodeIndex::new(d, k)).unwrap();
}
} }
#[test] #[test]
fn arbitrary_openings_multiple_leaves( fn with_no_duplicates_empty_node() {
depth in SimpleSmt::MIN_DEPTH..SimpleSmt::MAX_DEPTH, let entries = [(1_u64, int_to_leaf(0)), (5, int_to_leaf(2))];
count in 2u8..10u8, let smt = SimpleSmt::with_leaves(64, entries);
ref seed in any::<[u8; 32]>() assert!(smt.is_ok());
) {
let mut tree = SimpleSmt::new(iter::empty(), depth).unwrap();
let mut seed = *seed;
let leaves = (1 << depth) - 1;
for _ in 0..count {
seed = prng_array(seed);
let mut key = [0u8; 8];
let mut leaf = [0u8; 8];
key.copy_from_slice(&seed[..8]);
leaf.copy_from_slice(&seed[8..16]);
let key = u64::from_le_bytes(key);
let key = key % leaves;
let leaf = u64::from_le_bytes(leaf);
let leaf = int_to_node(leaf);
tree.insert_leaf(key, leaf).unwrap();
tree.get_leaf_path(key).unwrap();
}
}
} }
// HELPER FUNCTIONS // HELPER FUNCTIONS
// -------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------
fn compute_internal_nodes() -> (Word, Word, Word) { fn compute_internal_nodes() -> (RpoDigest, RpoDigest, RpoDigest) {
let node2 = Rpo256::hash_elements(&[VALUES4[0], VALUES4[1]].concat()); let node2 = Rpo256::merge(&[VALUES4[0], VALUES4[1]]);
let node3 = Rpo256::hash_elements(&[VALUES4[2], VALUES4[3]].concat()); let node3 = Rpo256::merge(&[VALUES4[2], VALUES4[3]]);
let root = Rpo256::merge(&[node2, node3]); let root = Rpo256::merge(&[node2, node3]);
(root.into(), node2.into(), node3.into()) (root, node2, node3)
}
const fn int_to_node(value: u64) -> Word {
[Felt::new(value), Felt::ZERO, Felt::ZERO, Felt::ZERO]
} }

557
src/merkle/store/mod.rs Normal file
View File

@@ -0,0 +1,557 @@
use super::{
mmr::Mmr, BTreeMap, EmptySubtreeRoots, InnerNodeInfo, KvMap, MerkleError, MerklePath,
MerklePathSet, MerkleTree, NodeIndex, RecordingMap, RootPath, Rpo256, RpoDigest, SimpleSmt,
TieredSmt, ValuePath, Vec,
};
use crate::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable};
use core::borrow::Borrow;
#[cfg(test)]
mod tests;
// MERKLE STORE
// ================================================================================================
/// A default [MerkleStore] which uses a simple [BTreeMap] as the backing storage.
pub type DefaultMerkleStore = MerkleStore<BTreeMap<RpoDigest, StoreNode>>;
/// A [MerkleStore] with recording capabilities which uses [RecordingMap] as the backing storage.
pub type RecordingMerkleStore = MerkleStore<RecordingMap<RpoDigest, StoreNode>>;
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
pub struct StoreNode {
left: RpoDigest,
right: RpoDigest,
}
/// An in-memory data store for Merkelized data.
///
/// This is a in memory data store for Merkle trees, this store allows all the nodes of multiple
/// trees to live as long as necessary and without duplication, this allows the implementation of
/// space efficient persistent data structures.
///
/// Example usage:
///
/// ```rust
/// # use miden_crypto::{ZERO, Felt, Word};
/// # use miden_crypto::merkle::{NodeIndex, MerkleStore, MerkleTree};
/// # use miden_crypto::hash::rpo::Rpo256;
/// # const fn int_to_node(value: u64) -> Word {
/// # [Felt::new(value), ZERO, ZERO, ZERO]
/// # }
/// # let A = int_to_node(1);
/// # let B = int_to_node(2);
/// # let C = int_to_node(3);
/// # let D = int_to_node(4);
/// # let E = int_to_node(5);
/// # let F = int_to_node(6);
/// # let G = int_to_node(7);
/// # let H0 = int_to_node(8);
/// # let H1 = int_to_node(9);
/// # let T0 = MerkleTree::new([A, B, C, D, E, F, G, H0].to_vec()).expect("even number of leaves provided");
/// # let T1 = MerkleTree::new([A, B, C, D, E, F, G, H1].to_vec()).expect("even number of leaves provided");
/// # let ROOT0 = T0.root();
/// # let ROOT1 = T1.root();
/// let mut store: MerkleStore = MerkleStore::new();
///
/// // the store is initialized with the SMT empty nodes
/// assert_eq!(store.num_internal_nodes(), 255);
///
/// let tree1 = MerkleTree::new(vec![A, B, C, D, E, F, G, H0]).unwrap();
/// let tree2 = MerkleTree::new(vec![A, B, C, D, E, F, G, H1]).unwrap();
///
/// // populates the store with two merkle trees, common nodes are shared
/// store.extend(tree1.inner_nodes());
/// store.extend(tree2.inner_nodes());
///
/// // every leaf except the last are the same
/// for i in 0..7 {
/// let idx0 = NodeIndex::new(3, i).unwrap();
/// let d0 = store.get_node(ROOT0, idx0).unwrap();
/// let idx1 = NodeIndex::new(3, i).unwrap();
/// let d1 = store.get_node(ROOT1, idx1).unwrap();
/// assert_eq!(d0, d1, "Both trees have the same leaf at pos {i}");
/// }
///
/// // The leafs A-B-C-D are the same for both trees, so are their 2 immediate parents
/// for i in 0..4 {
/// let idx0 = NodeIndex::new(3, i).unwrap();
/// let d0 = store.get_path(ROOT0, idx0).unwrap();
/// let idx1 = NodeIndex::new(3, i).unwrap();
/// let d1 = store.get_path(ROOT1, idx1).unwrap();
/// assert_eq!(d0.path[0..2], d1.path[0..2], "Both sub-trees are equal up to two levels");
/// }
///
/// // Common internal nodes are shared, the two added trees have a total of 30, but the store has
/// // only 10 new entries, corresponding to the 10 unique internal nodes of these trees.
/// assert_eq!(store.num_internal_nodes() - 255, 10);
/// ```
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct MerkleStore<T: KvMap<RpoDigest, StoreNode> = BTreeMap<RpoDigest, StoreNode>> {
nodes: T,
}
impl<T: KvMap<RpoDigest, StoreNode>> Default for MerkleStore<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: KvMap<RpoDigest, StoreNode>> MerkleStore<T> {
// CONSTRUCTORS
// --------------------------------------------------------------------------------------------
/// Creates an empty `MerkleStore` instance.
pub fn new() -> MerkleStore<T> {
// pre-populate the store with the empty hashes
let nodes = empty_hashes().into_iter().collect();
MerkleStore { nodes }
}
// PUBLIC ACCESSORS
// --------------------------------------------------------------------------------------------
/// Return a count of the non-leaf nodes in the store.
pub fn num_internal_nodes(&self) -> usize {
self.nodes.len()
}
/// Returns the node at `index` rooted on the tree `root`.
///
/// # Errors
/// This method can return the following errors:
/// - `RootNotInStore` if the `root` is not present in the store.
/// - `NodeNotInStore` if a node needed to traverse from `root` to `index` is not present in
/// the store.
pub fn get_node(&self, root: RpoDigest, index: NodeIndex) -> Result<RpoDigest, MerkleError> {
let mut hash = root;
// corner case: check the root is in the store when called with index `NodeIndex::root()`
self.nodes.get(&hash).ok_or(MerkleError::RootNotInStore(hash))?;
for i in (0..index.depth()).rev() {
let node = self.nodes.get(&hash).ok_or(MerkleError::NodeNotInStore(hash, index))?;
let bit = (index.value() >> i) & 1;
hash = if bit == 0 { node.left } else { node.right }
}
Ok(hash)
}
/// Returns the node at the specified `index` and its opening to the `root`.
///
/// The path starts at the sibling of the target leaf.
///
/// # Errors
/// This method can return the following errors:
/// - `RootNotInStore` if the `root` is not present in the store.
/// - `NodeNotInStore` if a node needed to traverse from `root` to `index` is not present in
/// the store.
pub fn get_path(&self, root: RpoDigest, index: NodeIndex) -> Result<ValuePath, MerkleError> {
let mut hash = root;
let mut path = Vec::with_capacity(index.depth().into());
// corner case: check the root is in the store when called with index `NodeIndex::root()`
self.nodes.get(&hash).ok_or(MerkleError::RootNotInStore(hash))?;
for i in (0..index.depth()).rev() {
let node = self.nodes.get(&hash).ok_or(MerkleError::NodeNotInStore(hash, index))?;
let bit = (index.value() >> i) & 1;
hash = if bit == 0 {
path.push(node.right);
node.left
} else {
path.push(node.left);
node.right
}
}
// the path is computed from root to leaf, so it must be reversed
path.reverse();
Ok(ValuePath {
value: hash,
path: MerklePath::new(path),
})
}
/// Reconstructs a path from the root until a leaf or empty node and returns its depth.
///
/// The `tree_depth` parameter defines up to which depth the tree will be traversed, starting
/// from `root`. The maximum value the argument accepts is [u64::BITS].
///
/// The traversed path from leaf to root will start at the least significant bit of `index`,
/// and will be executed for `tree_depth` bits.
///
/// # Errors
/// Will return an error if:
/// - The provided root is not found.
/// - The path from the root continues to a depth greater than `tree_depth`.
/// - The provided `tree_depth` is greater than `64.
/// - The provided `index` is not valid for a depth equivalent to `tree_depth`. For more
/// information, check [NodeIndex::new].
pub fn get_leaf_depth(
&self,
root: RpoDigest,
tree_depth: u8,
index: u64,
) -> Result<u8, MerkleError> {
// validate depth and index
if tree_depth > 64 {
return Err(MerkleError::DepthTooBig(tree_depth as u64));
}
NodeIndex::new(tree_depth, index)?;
// it's not illegal to have a maximum depth of `0`; we should just return the root in that
// case. this check will simplify the implementation as we could overflow bits for depth
// `0`.
if tree_depth == 0 {
return Ok(0);
}
// check if the root exists, providing the proper error report if it doesn't
let empty = EmptySubtreeRoots::empty_hashes(tree_depth);
let mut hash = root;
if !self.nodes.contains_key(&hash) {
return Err(MerkleError::RootNotInStore(hash));
}
// we traverse from root to leaf, so the path is reversed
let mut path = (index << (64 - tree_depth)).reverse_bits();
// iterate every depth and reconstruct the path from root to leaf
for depth in 0..tree_depth {
// we short-circuit if an empty node has been found
if hash == empty[depth as usize] {
return Ok(depth);
}
// fetch the children pair, mapped by its parent hash
let children = match self.nodes.get(&hash) {
Some(node) => node,
None => return Ok(depth),
};
// traverse down
hash = if path & 1 == 0 { children.left } else { children.right };
path >>= 1;
}
// at max depth assert it doesn't have sub-trees
if self.nodes.contains_key(&hash) {
return Err(MerkleError::DepthTooBig(tree_depth as u64 + 1));
}
// depleted bits; return max depth
Ok(tree_depth)
}
// DATA EXTRACTORS
// --------------------------------------------------------------------------------------------
/// Returns a subset of this Merkle store such that the returned Merkle store contains all
/// nodes which are descendants of the specified roots.
///
/// The roots for which no descendants exist in this Merkle store are ignored.
pub fn subset<I, R>(&self, roots: I) -> MerkleStore<T>
where
I: Iterator<Item = R>,
R: Borrow<RpoDigest>,
{
let mut store = MerkleStore::new();
for root in roots {
let root = *root.borrow();
store.clone_tree_from(root, self);
}
store
}
/// Iterator over the inner nodes of the [MerkleStore].
pub fn inner_nodes(&self) -> impl Iterator<Item = InnerNodeInfo> + '_ {
self.nodes.iter().map(|(r, n)| InnerNodeInfo {
value: *r,
left: n.left,
right: n.right,
})
}
// STATE MUTATORS
// --------------------------------------------------------------------------------------------
/// Adds all the nodes of a Merkle path represented by `path`, opening to `node`. Returns the
/// new root.
///
/// This will compute the sibling elements determined by the Merkle `path` and `node`, and
/// include all the nodes into the store.
pub fn add_merkle_path(
&mut self,
index: u64,
node: RpoDigest,
path: MerklePath,
) -> Result<RpoDigest, MerkleError> {
let root = path.inner_nodes(index, node)?.fold(RpoDigest::default(), |_, node| {
let value: RpoDigest = node.value;
let left: RpoDigest = node.left;
let right: RpoDigest = node.right;
debug_assert_eq!(Rpo256::merge(&[left, right]), value);
self.nodes.insert(value, StoreNode { left, right });
node.value
});
Ok(root)
}
/// Adds all the nodes of multiple Merkle paths into the store.
///
/// This will compute the sibling elements for each Merkle `path` and include all the nodes
/// into the store.
///
/// For further reference, check [MerkleStore::add_merkle_path].
pub fn add_merkle_paths<I>(&mut self, paths: I) -> Result<(), MerkleError>
where
I: IntoIterator<Item = (u64, RpoDigest, MerklePath)>,
{
for (index_value, node, path) in paths.into_iter() {
self.add_merkle_path(index_value, node, path)?;
}
Ok(())
}
/// Appends the provided [MerklePathSet] into the store.
///
/// For further reference, check [MerkleStore::add_merkle_path].
pub fn add_merkle_path_set(
&mut self,
path_set: &MerklePathSet,
) -> Result<RpoDigest, MerkleError> {
let root = path_set.root();
for (index, path) in path_set.to_paths() {
self.add_merkle_path(index, path.value, path.path)?;
}
Ok(root)
}
/// Sets a node to `value`.
///
/// # Errors
/// This method can return the following errors:
/// - `RootNotInStore` if the `root` is not present in the store.
/// - `NodeNotInStore` if a node needed to traverse from `root` to `index` is not present in
/// the store.
pub fn set_node(
&mut self,
mut root: RpoDigest,
index: NodeIndex,
value: RpoDigest,
) -> Result<RootPath, MerkleError> {
let node = value;
let ValuePath { value, path } = self.get_path(root, index)?;
// performs the update only if the node value differs from the opening
if node != value {
root = self.add_merkle_path(index.value(), node, path.clone())?;
}
Ok(RootPath { root, path })
}
/// Merges two elements and adds the resulting node into the store.
///
/// Merges arbitrary values. They may be leafs, nodes, or a mixture of both.
pub fn merge_roots(
&mut self,
left_root: RpoDigest,
right_root: RpoDigest,
) -> Result<RpoDigest, MerkleError> {
let parent = Rpo256::merge(&[left_root, right_root]);
self.nodes.insert(
parent,
StoreNode {
left: left_root,
right: right_root,
},
);
Ok(parent)
}
// DESTRUCTURING
// --------------------------------------------------------------------------------------------
/// Returns the inner storage of this MerkleStore while consuming `self`.
pub fn into_inner(self) -> T {
self.nodes
}
// HELPER METHODS
// --------------------------------------------------------------------------------------------
/// Recursively clones a tree with the specified root from the specified source into self.
///
/// If the source store does not contain a tree with the specified root, this is a noop.
fn clone_tree_from(&mut self, root: RpoDigest, source: &Self) {
// process the node only if it is in the source
if let Some(node) = source.nodes.get(&root) {
// if the node has already been inserted, no need to process it further as all of its
// descendants should be already cloned from the source store
if self.nodes.insert(root, *node).is_none() {
self.clone_tree_from(node.left, source);
self.clone_tree_from(node.right, source);
}
}
}
}
// CONVERSIONS
// ================================================================================================
impl<T: KvMap<RpoDigest, StoreNode>> From<&MerkleTree> for MerkleStore<T> {
fn from(value: &MerkleTree) -> Self {
let nodes = combine_nodes_with_empty_hashes(value.inner_nodes()).collect();
Self { nodes }
}
}
impl<T: KvMap<RpoDigest, StoreNode>> From<&SimpleSmt> for MerkleStore<T> {
fn from(value: &SimpleSmt) -> Self {
let nodes = combine_nodes_with_empty_hashes(value.inner_nodes()).collect();
Self { nodes }
}
}
impl<T: KvMap<RpoDigest, StoreNode>> From<&Mmr> for MerkleStore<T> {
fn from(value: &Mmr) -> Self {
let nodes = combine_nodes_with_empty_hashes(value.inner_nodes()).collect();
Self { nodes }
}
}
impl<T: KvMap<RpoDigest, StoreNode>> From<&TieredSmt> for MerkleStore<T> {
fn from(value: &TieredSmt) -> Self {
let nodes = combine_nodes_with_empty_hashes(value.inner_nodes()).collect();
Self { nodes }
}
}
impl<T: KvMap<RpoDigest, StoreNode>> From<T> for MerkleStore<T> {
fn from(values: T) -> Self {
let nodes = values.into_iter().chain(empty_hashes().into_iter()).collect();
Self { nodes }
}
}
impl<T: KvMap<RpoDigest, StoreNode>> FromIterator<InnerNodeInfo> for MerkleStore<T> {
fn from_iter<I: IntoIterator<Item = InnerNodeInfo>>(iter: I) -> Self {
let nodes = combine_nodes_with_empty_hashes(iter.into_iter()).collect();
Self { nodes }
}
}
impl<T: KvMap<RpoDigest, StoreNode>> FromIterator<(RpoDigest, StoreNode)> for MerkleStore<T> {
fn from_iter<I: IntoIterator<Item = (RpoDigest, StoreNode)>>(iter: I) -> Self {
let nodes = iter.into_iter().chain(empty_hashes().into_iter()).collect();
Self { nodes }
}
}
// ITERATORS
// ================================================================================================
impl<T: KvMap<RpoDigest, StoreNode>> Extend<InnerNodeInfo> for MerkleStore<T> {
fn extend<I: IntoIterator<Item = InnerNodeInfo>>(&mut self, iter: I) {
self.nodes.extend(iter.into_iter().map(|info| {
(
info.value,
StoreNode {
left: info.left,
right: info.right,
},
)
}));
}
}
// SERIALIZATION
// ================================================================================================
impl Serializable for StoreNode {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
self.left.write_into(target);
self.right.write_into(target);
}
}
impl Deserializable for StoreNode {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let left = RpoDigest::read_from(source)?;
let right = RpoDigest::read_from(source)?;
Ok(StoreNode { left, right })
}
}
impl<T: KvMap<RpoDigest, StoreNode>> Serializable for MerkleStore<T> {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
target.write_u64(self.nodes.len() as u64);
for (k, v) in self.nodes.iter() {
k.write_into(target);
v.write_into(target);
}
}
}
impl<T: KvMap<RpoDigest, StoreNode>> Deserializable for MerkleStore<T> {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let len = source.read_u64()?;
let mut nodes: Vec<(RpoDigest, StoreNode)> = Vec::with_capacity(len as usize);
for _ in 0..len {
let key = RpoDigest::read_from(source)?;
let value = StoreNode::read_from(source)?;
nodes.push((key, value));
}
Ok(nodes.into_iter().collect())
}
}
// HELPER FUNCTIONS
// ================================================================================================
/// Creates empty hashes for all the subtrees of a tree with a max depth of 255.
fn empty_hashes() -> impl IntoIterator<Item = (RpoDigest, StoreNode)> {
let subtrees = EmptySubtreeRoots::empty_hashes(255);
subtrees.iter().rev().copied().zip(subtrees.iter().rev().skip(1).copied()).map(
|(child, parent)| {
(
parent,
StoreNode {
left: child,
right: child,
},
)
},
)
}
/// Consumes an iterator of [InnerNodeInfo] and returns an iterator of `(value, node)` tuples
/// which includes the nodes associate with roots of empty subtrees up to a depth of 255.
fn combine_nodes_with_empty_hashes(
nodes: impl IntoIterator<Item = InnerNodeInfo>,
) -> impl Iterator<Item = (RpoDigest, StoreNode)> {
nodes
.into_iter()
.map(|info| {
(
info.value,
StoreNode {
left: info.left,
right: info.right,
},
)
})
.chain(empty_hashes().into_iter())
}

867
src/merkle/store/tests.rs Normal file
View File

@@ -0,0 +1,867 @@
use super::{
DefaultMerkleStore as MerkleStore, EmptySubtreeRoots, MerkleError, MerklePath, NodeIndex,
RecordingMerkleStore, RpoDigest,
};
use crate::{
hash::rpo::Rpo256,
merkle::{digests_to_words, int_to_leaf, int_to_node, MerklePathSet, MerkleTree, SimpleSmt},
Felt, Word, ONE, WORD_SIZE, ZERO,
};
#[cfg(feature = "std")]
use super::{Deserializable, Serializable};
#[cfg(feature = "std")]
use std::error::Error;
// TEST DATA
// ================================================================================================
const KEYS4: [u64; 4] = [0, 1, 2, 3];
const VALUES4: [RpoDigest; 4] = [int_to_node(1), int_to_node(2), int_to_node(3), int_to_node(4)];
const KEYS8: [u64; 8] = [0, 1, 2, 3, 4, 5, 6, 7];
const VALUES8: [RpoDigest; 8] = [
int_to_node(1),
int_to_node(2),
int_to_node(3),
int_to_node(4),
int_to_node(5),
int_to_node(6),
int_to_node(7),
int_to_node(8),
];
// TESTS
// ================================================================================================
#[test]
fn test_root_not_in_store() -> Result<(), MerkleError> {
let mtree = MerkleTree::new(digests_to_words(&VALUES4))?;
let store = MerkleStore::from(&mtree);
assert_eq!(
store.get_node(VALUES4[0], NodeIndex::make(mtree.depth(), 0)),
Err(MerkleError::RootNotInStore(VALUES4[0])),
"Leaf 0 is not a root"
);
assert_eq!(
store.get_path(VALUES4[0], NodeIndex::make(mtree.depth(), 0)),
Err(MerkleError::RootNotInStore(VALUES4[0])),
"Leaf 0 is not a root"
);
Ok(())
}
#[test]
fn test_merkle_tree() -> Result<(), MerkleError> {
let mtree = MerkleTree::new(digests_to_words(&VALUES4))?;
let store = MerkleStore::from(&mtree);
// STORE LEAVES ARE CORRECT -------------------------------------------------------------------
// checks the leaves in the store corresponds to the expected values
assert_eq!(
store.get_node(mtree.root(), NodeIndex::make(mtree.depth(), 0)),
Ok(VALUES4[0]),
"node 0 must be in the tree"
);
assert_eq!(
store.get_node(mtree.root(), NodeIndex::make(mtree.depth(), 1)),
Ok(VALUES4[1]),
"node 1 must be in the tree"
);
assert_eq!(
store.get_node(mtree.root(), NodeIndex::make(mtree.depth(), 2)),
Ok(VALUES4[2]),
"node 2 must be in the tree"
);
assert_eq!(
store.get_node(mtree.root(), NodeIndex::make(mtree.depth(), 3)),
Ok(VALUES4[3]),
"node 3 must be in the tree"
);
// STORE LEAVES MATCH TREE --------------------------------------------------------------------
// sanity check the values returned by the store and the tree
assert_eq!(
mtree.get_node(NodeIndex::make(mtree.depth(), 0)),
store.get_node(mtree.root(), NodeIndex::make(mtree.depth(), 0)),
"node 0 must be the same for both MerkleTree and MerkleStore"
);
assert_eq!(
mtree.get_node(NodeIndex::make(mtree.depth(), 1)),
store.get_node(mtree.root(), NodeIndex::make(mtree.depth(), 1)),
"node 1 must be the same for both MerkleTree and MerkleStore"
);
assert_eq!(
mtree.get_node(NodeIndex::make(mtree.depth(), 2)),
store.get_node(mtree.root(), NodeIndex::make(mtree.depth(), 2)),
"node 2 must be the same for both MerkleTree and MerkleStore"
);
assert_eq!(
mtree.get_node(NodeIndex::make(mtree.depth(), 3)),
store.get_node(mtree.root(), NodeIndex::make(mtree.depth(), 3)),
"node 3 must be the same for both MerkleTree and MerkleStore"
);
// STORE MERKLE PATH MATCHS ==============================================================
// assert the merkle path returned by the store is the same as the one in the tree
let result = store.get_path(mtree.root(), NodeIndex::make(mtree.depth(), 0)).unwrap();
assert_eq!(
VALUES4[0], result.value,
"Value for merkle path at index 0 must match leaf value"
);
assert_eq!(
mtree.get_path(NodeIndex::make(mtree.depth(), 0)),
Ok(result.path),
"merkle path for index 0 must be the same for the MerkleTree and MerkleStore"
);
let result = store.get_path(mtree.root(), NodeIndex::make(mtree.depth(), 1)).unwrap();
assert_eq!(
VALUES4[1], result.value,
"Value for merkle path at index 0 must match leaf value"
);
assert_eq!(
mtree.get_path(NodeIndex::make(mtree.depth(), 1)),
Ok(result.path),
"merkle path for index 1 must be the same for the MerkleTree and MerkleStore"
);
let result = store.get_path(mtree.root(), NodeIndex::make(mtree.depth(), 2)).unwrap();
assert_eq!(
VALUES4[2], result.value,
"Value for merkle path at index 0 must match leaf value"
);
assert_eq!(
mtree.get_path(NodeIndex::make(mtree.depth(), 2)),
Ok(result.path),
"merkle path for index 0 must be the same for the MerkleTree and MerkleStore"
);
let result = store.get_path(mtree.root(), NodeIndex::make(mtree.depth(), 3)).unwrap();
assert_eq!(
VALUES4[3], result.value,
"Value for merkle path at index 0 must match leaf value"
);
assert_eq!(
mtree.get_path(NodeIndex::make(mtree.depth(), 3)),
Ok(result.path),
"merkle path for index 0 must be the same for the MerkleTree and MerkleStore"
);
Ok(())
}
#[test]
fn test_empty_roots() {
let store = MerkleStore::default();
let mut root = RpoDigest::default();
for depth in 0..255 {
root = Rpo256::merge(&[root; 2]);
assert!(
store.get_node(root, NodeIndex::make(0, 0)).is_ok(),
"The root of the empty tree of depth {depth} must be registered"
);
}
}
#[test]
fn test_leaf_paths_for_empty_trees() -> Result<(), MerkleError> {
let store = MerkleStore::default();
// Starts at 1 because leafs are not included in the store.
// Ends at 64 because it is not possible to represent an index of a depth greater than 64,
// because a u64 is used to index the leaf.
for depth in 1..64 {
let smt = SimpleSmt::new(depth)?;
let index = NodeIndex::make(depth, 0);
let store_path = store.get_path(smt.root(), index)?;
let smt_path = smt.get_path(index)?;
assert_eq!(
store_path.value,
RpoDigest::default(),
"the leaf of an empty tree is always ZERO"
);
assert_eq!(
store_path.path, smt_path,
"the returned merkle path does not match the computed values"
);
assert_eq!(
store_path.path.compute_root(depth.into(), RpoDigest::default()).unwrap(),
smt.root(),
"computed root from the path must match the empty tree root"
);
}
Ok(())
}
#[test]
fn test_get_invalid_node() {
let mtree =
MerkleTree::new(digests_to_words(&VALUES4)).expect("creating a merkle tree must work");
let store = MerkleStore::from(&mtree);
let _ = store.get_node(mtree.root(), NodeIndex::make(mtree.depth(), 3));
}
#[test]
fn test_add_sparse_merkle_tree_one_level() -> Result<(), MerkleError> {
let keys2: [u64; 2] = [0, 1];
let leaves2: [Word; 2] = [int_to_leaf(1), int_to_leaf(2)];
let smt = SimpleSmt::with_leaves(1, keys2.into_iter().zip(leaves2.into_iter())).unwrap();
let store = MerkleStore::from(&smt);
let idx = NodeIndex::make(1, 0);
assert_eq!(smt.get_node(idx).unwrap(), leaves2[0].into());
assert_eq!(store.get_node(smt.root(), idx).unwrap(), smt.get_node(idx).unwrap());
let idx = NodeIndex::make(1, 1);
assert_eq!(smt.get_node(idx).unwrap(), leaves2[1].into());
assert_eq!(store.get_node(smt.root(), idx).unwrap(), smt.get_node(idx).unwrap());
Ok(())
}
#[test]
fn test_sparse_merkle_tree() -> Result<(), MerkleError> {
let smt = SimpleSmt::with_leaves(
SimpleSmt::MAX_DEPTH,
KEYS4.into_iter().zip(digests_to_words(&VALUES4).into_iter()),
)
.unwrap();
let store = MerkleStore::from(&smt);
// STORE LEAVES ARE CORRECT ==============================================================
// checks the leaves in the store corresponds to the expected values
assert_eq!(
store.get_node(smt.root(), NodeIndex::make(smt.depth(), 0)),
Ok(VALUES4[0]),
"node 0 must be in the tree"
);
assert_eq!(
store.get_node(smt.root(), NodeIndex::make(smt.depth(), 1)),
Ok(VALUES4[1]),
"node 1 must be in the tree"
);
assert_eq!(
store.get_node(smt.root(), NodeIndex::make(smt.depth(), 2)),
Ok(VALUES4[2]),
"node 2 must be in the tree"
);
assert_eq!(
store.get_node(smt.root(), NodeIndex::make(smt.depth(), 3)),
Ok(VALUES4[3]),
"node 3 must be in the tree"
);
assert_eq!(
store.get_node(smt.root(), NodeIndex::make(smt.depth(), 4)),
Ok(RpoDigest::default()),
"unmodified node 4 must be ZERO"
);
// STORE LEAVES MATCH TREE ===============================================================
// sanity check the values returned by the store and the tree
assert_eq!(
smt.get_node(NodeIndex::make(smt.depth(), 0)),
store.get_node(smt.root(), NodeIndex::make(smt.depth(), 0)),
"node 0 must be the same for both SparseMerkleTree and MerkleStore"
);
assert_eq!(
smt.get_node(NodeIndex::make(smt.depth(), 1)),
store.get_node(smt.root(), NodeIndex::make(smt.depth(), 1)),
"node 1 must be the same for both SparseMerkleTree and MerkleStore"
);
assert_eq!(
smt.get_node(NodeIndex::make(smt.depth(), 2)),
store.get_node(smt.root(), NodeIndex::make(smt.depth(), 2)),
"node 2 must be the same for both SparseMerkleTree and MerkleStore"
);
assert_eq!(
smt.get_node(NodeIndex::make(smt.depth(), 3)),
store.get_node(smt.root(), NodeIndex::make(smt.depth(), 3)),
"node 3 must be the same for both SparseMerkleTree and MerkleStore"
);
assert_eq!(
smt.get_node(NodeIndex::make(smt.depth(), 4)),
store.get_node(smt.root(), NodeIndex::make(smt.depth(), 4)),
"node 4 must be the same for both SparseMerkleTree and MerkleStore"
);
// STORE MERKLE PATH MATCHS ==============================================================
// assert the merkle path returned by the store is the same as the one in the tree
let result = store.get_path(smt.root(), NodeIndex::make(smt.depth(), 0)).unwrap();
assert_eq!(
VALUES4[0], result.value,
"Value for merkle path at index 0 must match leaf value"
);
assert_eq!(
smt.get_path(NodeIndex::make(smt.depth(), 0)),
Ok(result.path),
"merkle path for index 0 must be the same for the MerkleTree and MerkleStore"
);
let result = store.get_path(smt.root(), NodeIndex::make(smt.depth(), 1)).unwrap();
assert_eq!(
VALUES4[1], result.value,
"Value for merkle path at index 1 must match leaf value"
);
assert_eq!(
smt.get_path(NodeIndex::make(smt.depth(), 1)),
Ok(result.path),
"merkle path for index 1 must be the same for the MerkleTree and MerkleStore"
);
let result = store.get_path(smt.root(), NodeIndex::make(smt.depth(), 2)).unwrap();
assert_eq!(
VALUES4[2], result.value,
"Value for merkle path at index 2 must match leaf value"
);
assert_eq!(
smt.get_path(NodeIndex::make(smt.depth(), 2)),
Ok(result.path),
"merkle path for index 2 must be the same for the MerkleTree and MerkleStore"
);
let result = store.get_path(smt.root(), NodeIndex::make(smt.depth(), 3)).unwrap();
assert_eq!(
VALUES4[3], result.value,
"Value for merkle path at index 3 must match leaf value"
);
assert_eq!(
smt.get_path(NodeIndex::make(smt.depth(), 3)),
Ok(result.path),
"merkle path for index 3 must be the same for the MerkleTree and MerkleStore"
);
let result = store.get_path(smt.root(), NodeIndex::make(smt.depth(), 4)).unwrap();
assert_eq!(
RpoDigest::default(),
result.value,
"Value for merkle path at index 4 must match leaf value"
);
assert_eq!(
smt.get_path(NodeIndex::make(smt.depth(), 4)),
Ok(result.path),
"merkle path for index 4 must be the same for the MerkleTree and MerkleStore"
);
Ok(())
}
#[test]
fn test_add_merkle_paths() -> Result<(), MerkleError> {
let mtree = MerkleTree::new(digests_to_words(&VALUES4))?;
let i0 = 0;
let p0 = mtree.get_path(NodeIndex::make(2, i0)).unwrap();
let i1 = 1;
let p1 = mtree.get_path(NodeIndex::make(2, i1)).unwrap();
let i2 = 2;
let p2 = mtree.get_path(NodeIndex::make(2, i2)).unwrap();
let i3 = 3;
let p3 = mtree.get_path(NodeIndex::make(2, i3)).unwrap();
let paths = [
(i0, VALUES4[i0 as usize], p0),
(i1, VALUES4[i1 as usize], p1),
(i2, VALUES4[i2 as usize], p2),
(i3, VALUES4[i3 as usize], p3),
];
let mut store = MerkleStore::default();
store.add_merkle_paths(paths.clone()).expect("the valid paths must work");
let depth = 2;
let set = MerklePathSet::new(depth).with_paths(paths).unwrap();
// STORE LEAVES ARE CORRECT ==============================================================
// checks the leaves in the store corresponds to the expected values
assert_eq!(
store.get_node(set.root(), NodeIndex::make(set.depth(), 0)),
Ok(VALUES4[0]),
"node 0 must be in the set"
);
assert_eq!(
store.get_node(set.root(), NodeIndex::make(set.depth(), 1)),
Ok(VALUES4[1]),
"node 1 must be in the set"
);
assert_eq!(
store.get_node(set.root(), NodeIndex::make(set.depth(), 2)),
Ok(VALUES4[2]),
"node 2 must be in the set"
);
assert_eq!(
store.get_node(set.root(), NodeIndex::make(set.depth(), 3)),
Ok(VALUES4[3]),
"node 3 must be in the set"
);
// STORE LEAVES MATCH SET ================================================================
// sanity check the values returned by the store and the set
assert_eq!(
set.get_node(NodeIndex::make(set.depth(), 0)),
store.get_node(set.root(), NodeIndex::make(set.depth(), 0)),
"node 0 must be the same for both SparseMerkleTree and MerkleStore"
);
assert_eq!(
set.get_node(NodeIndex::make(set.depth(), 1)),
store.get_node(set.root(), NodeIndex::make(set.depth(), 1)),
"node 1 must be the same for both SparseMerkleTree and MerkleStore"
);
assert_eq!(
set.get_node(NodeIndex::make(set.depth(), 2)),
store.get_node(set.root(), NodeIndex::make(set.depth(), 2)),
"node 2 must be the same for both SparseMerkleTree and MerkleStore"
);
assert_eq!(
set.get_node(NodeIndex::make(set.depth(), 3)),
store.get_node(set.root(), NodeIndex::make(set.depth(), 3)),
"node 3 must be the same for both SparseMerkleTree and MerkleStore"
);
// STORE MERKLE PATH MATCHS ==============================================================
// assert the merkle path returned by the store is the same as the one in the set
let result = store.get_path(set.root(), NodeIndex::make(set.depth(), 0)).unwrap();
assert_eq!(
VALUES4[0], result.value,
"Value for merkle path at index 0 must match leaf value"
);
assert_eq!(
set.get_path(NodeIndex::make(set.depth(), 0)),
Ok(result.path),
"merkle path for index 0 must be the same for the MerkleTree and MerkleStore"
);
let result = store.get_path(set.root(), NodeIndex::make(set.depth(), 1)).unwrap();
assert_eq!(
VALUES4[1], result.value,
"Value for merkle path at index 0 must match leaf value"
);
assert_eq!(
set.get_path(NodeIndex::make(set.depth(), 1)),
Ok(result.path),
"merkle path for index 1 must be the same for the MerkleTree and MerkleStore"
);
let result = store.get_path(set.root(), NodeIndex::make(set.depth(), 2)).unwrap();
assert_eq!(
VALUES4[2], result.value,
"Value for merkle path at index 0 must match leaf value"
);
assert_eq!(
set.get_path(NodeIndex::make(set.depth(), 2)),
Ok(result.path),
"merkle path for index 0 must be the same for the MerkleTree and MerkleStore"
);
let result = store.get_path(set.root(), NodeIndex::make(set.depth(), 3)).unwrap();
assert_eq!(
VALUES4[3], result.value,
"Value for merkle path at index 0 must match leaf value"
);
assert_eq!(
set.get_path(NodeIndex::make(set.depth(), 3)),
Ok(result.path),
"merkle path for index 0 must be the same for the MerkleTree and MerkleStore"
);
Ok(())
}
#[test]
fn wont_open_to_different_depth_root() {
let empty = EmptySubtreeRoots::empty_hashes(64);
let a = [Felt::new(1); 4];
let b = [Felt::new(2); 4];
// Compute the root for a different depth. We cherry-pick this specific depth to prevent a
// regression to a bug in the past that allowed the user to fetch a node at a depth lower than
// the inserted path of a Merkle tree.
let mut root = Rpo256::merge(&[a.into(), b.into()]);
for depth in (1..=63).rev() {
root = Rpo256::merge(&[root, empty[depth]]);
}
// For this example, the depth of the Merkle tree is 1, as we have only two leaves. Here we
// attempt to fetch a node on the maximum depth, and it should fail because the root shouldn't
// exist for the set.
let mtree = MerkleTree::new(vec![a, b]).unwrap();
let store = MerkleStore::from(&mtree);
let index = NodeIndex::root();
let err = store.get_node(root, index).err().unwrap();
assert_eq!(err, MerkleError::RootNotInStore(root));
}
#[test]
fn store_path_opens_from_leaf() {
let a = [Felt::new(1); 4];
let b = [Felt::new(2); 4];
let c = [Felt::new(3); 4];
let d = [Felt::new(4); 4];
let e = [Felt::new(5); 4];
let f = [Felt::new(6); 4];
let g = [Felt::new(7); 4];
let h = [Felt::new(8); 4];
let i = Rpo256::merge(&[a.into(), b.into()]);
let j = Rpo256::merge(&[c.into(), d.into()]);
let k = Rpo256::merge(&[e.into(), f.into()]);
let l = Rpo256::merge(&[g.into(), h.into()]);
let m = Rpo256::merge(&[i, j]);
let n = Rpo256::merge(&[k, l]);
let root = Rpo256::merge(&[m, n]);
let mtree = MerkleTree::new(vec![a, b, c, d, e, f, g, h]).unwrap();
let store = MerkleStore::from(&mtree);
let path = store.get_path(root, NodeIndex::make(3, 1)).unwrap().path;
let expected = MerklePath::new([a.into(), j, n].to_vec());
assert_eq!(path, expected);
}
#[test]
fn test_set_node() -> Result<(), MerkleError> {
let mtree = MerkleTree::new(digests_to_words(&VALUES4))?;
let mut store = MerkleStore::from(&mtree);
let value = int_to_node(42);
let index = NodeIndex::make(mtree.depth(), 0);
let new_root = store.set_node(mtree.root(), index, value)?.root;
assert_eq!(store.get_node(new_root, index), Ok(value), "Value must have changed");
Ok(())
}
#[test]
fn test_constructors() -> Result<(), MerkleError> {
let mtree = MerkleTree::new(digests_to_words(&VALUES4))?;
let store = MerkleStore::from(&mtree);
let depth = mtree.depth();
let leaves = 2u64.pow(depth.into());
for index in 0..leaves {
let index = NodeIndex::make(depth, index);
let value_path = store.get_path(mtree.root(), index)?;
assert_eq!(mtree.get_path(index)?, value_path.path);
}
let depth = 32;
let smt = SimpleSmt::with_leaves(
depth,
KEYS4.into_iter().zip(digests_to_words(&VALUES4).into_iter()),
)
.unwrap();
let store = MerkleStore::from(&smt);
let depth = smt.depth();
for key in KEYS4 {
let index = NodeIndex::make(depth, key);
let value_path = store.get_path(smt.root(), index)?;
assert_eq!(smt.get_path(index)?, value_path.path);
}
let d = 2;
let paths = [
(0, VALUES4[0], mtree.get_path(NodeIndex::make(d, 0)).unwrap()),
(1, VALUES4[1], mtree.get_path(NodeIndex::make(d, 1)).unwrap()),
(2, VALUES4[2], mtree.get_path(NodeIndex::make(d, 2)).unwrap()),
(3, VALUES4[3], mtree.get_path(NodeIndex::make(d, 3)).unwrap()),
];
let mut store1 = MerkleStore::default();
store1.add_merkle_paths(paths.clone())?;
let mut store2 = MerkleStore::default();
store2.add_merkle_path(0, VALUES4[0], mtree.get_path(NodeIndex::make(d, 0))?)?;
store2.add_merkle_path(1, VALUES4[1], mtree.get_path(NodeIndex::make(d, 1))?)?;
store2.add_merkle_path(2, VALUES4[2], mtree.get_path(NodeIndex::make(d, 2))?)?;
store2.add_merkle_path(3, VALUES4[3], mtree.get_path(NodeIndex::make(d, 3))?)?;
let set = MerklePathSet::new(d).with_paths(paths).unwrap();
for key in [0, 1, 2, 3] {
let index = NodeIndex::make(d, key);
let value_path1 = store1.get_path(set.root(), index)?;
let value_path2 = store2.get_path(set.root(), index)?;
assert_eq!(value_path1, value_path2);
let index = NodeIndex::make(d, key);
assert_eq!(set.get_path(index)?, value_path1.path);
}
Ok(())
}
#[test]
fn node_path_should_be_truncated_by_midtier_insert() {
let key = 0b11010010_11001100_11001100_11001100_11001100_11001100_11001100_11001100_u64;
let mut store = MerkleStore::new();
let root: RpoDigest = EmptySubtreeRoots::empty_hashes(64)[0];
// insert first node - works as expected
let depth = 64;
let node = RpoDigest::from([Felt::new(key); WORD_SIZE]);
let index = NodeIndex::new(depth, key).unwrap();
let root = store.set_node(root, index, node).unwrap().root;
let result = store.get_node(root, index).unwrap();
let path = store.get_path(root, index).unwrap().path;
assert_eq!(node, result);
assert_eq!(path.depth(), depth);
assert!(path.verify(index.value(), result, &root));
// flip the first bit of the key and insert the second node on a different depth
let key = key ^ (1 << 63);
let key = key >> 8;
let depth = 56;
let node = RpoDigest::from([Felt::new(key); WORD_SIZE]);
let index = NodeIndex::new(depth, key).unwrap();
let root = store.set_node(root, index, node).unwrap().root;
let result = store.get_node(root, index).unwrap();
let path = store.get_path(root, index).unwrap().path;
assert_eq!(node, result);
assert_eq!(path.depth(), depth);
assert!(path.verify(index.value(), result, &root));
// attempt to fetch a path of the second node to depth 64
// should fail because the previously inserted node will remove its sub-tree from the set
let key = key << 8;
let index = NodeIndex::new(64, key).unwrap();
assert!(store.get_node(root, index).is_err());
}
#[test]
fn get_leaf_depth_works_depth_64() {
let mut store = MerkleStore::new();
let mut root: RpoDigest = EmptySubtreeRoots::empty_hashes(64)[0];
let key = u64::MAX;
// this will create a rainbow tree and test all opening to depth 64
for d in 0..64 {
let k = key & (u64::MAX >> d);
let node = RpoDigest::from([Felt::new(k); WORD_SIZE]);
let index = NodeIndex::new(64, k).unwrap();
// assert the leaf doesn't exist before the insert. the returned depth should always
// increment with the paths count of the set, as they are insersecting one another up to
// the first bits of the used key.
assert_eq!(d, store.get_leaf_depth(root, 64, k).unwrap());
// insert and assert the correct depth
root = store.set_node(root, index, node).unwrap().root;
assert_eq!(64, store.get_leaf_depth(root, 64, k).unwrap());
}
}
#[test]
fn get_leaf_depth_works_with_incremental_depth() {
let mut store = MerkleStore::new();
let mut root: RpoDigest = EmptySubtreeRoots::empty_hashes(64)[0];
// insert some path to the left of the root and assert it
let key = 0b01001011_10110110_00001101_01110100_00111011_10101101_00000100_01000001_u64;
assert_eq!(0, store.get_leaf_depth(root, 64, key).unwrap());
let depth = 64;
let index = NodeIndex::new(depth, key).unwrap();
let node = RpoDigest::from([Felt::new(key); WORD_SIZE]);
root = store.set_node(root, index, node).unwrap().root;
assert_eq!(depth, store.get_leaf_depth(root, 64, key).unwrap());
// flip the key to the right of the root and insert some content on depth 16
let key = 0b11001011_10110110_00000000_00000000_00000000_00000000_00000000_00000000_u64;
assert_eq!(1, store.get_leaf_depth(root, 64, key).unwrap());
let depth = 16;
let index = NodeIndex::new(depth, key >> (64 - depth)).unwrap();
let node = RpoDigest::from([Felt::new(key); WORD_SIZE]);
root = store.set_node(root, index, node).unwrap().root;
assert_eq!(depth, store.get_leaf_depth(root, 64, key).unwrap());
// attempt the sibling of the previous leaf
let key = 0b11001011_10110111_00000000_00000000_00000000_00000000_00000000_00000000_u64;
assert_eq!(16, store.get_leaf_depth(root, 64, key).unwrap());
let index = NodeIndex::new(depth, key >> (64 - depth)).unwrap();
let node = RpoDigest::from([Felt::new(key); WORD_SIZE]);
root = store.set_node(root, index, node).unwrap().root;
assert_eq!(depth, store.get_leaf_depth(root, 64, key).unwrap());
// move down to the next depth and assert correct behavior
let key = 0b11001011_10110100_00000000_00000000_00000000_00000000_00000000_00000000_u64;
assert_eq!(15, store.get_leaf_depth(root, 64, key).unwrap());
let depth = 17;
let index = NodeIndex::new(depth, key >> (64 - depth)).unwrap();
let node = RpoDigest::from([Felt::new(key); WORD_SIZE]);
root = store.set_node(root, index, node).unwrap().root;
assert_eq!(depth, store.get_leaf_depth(root, 64, key).unwrap());
}
#[test]
fn get_leaf_depth_works_with_depth_8() {
let mut store = MerkleStore::new();
let mut root: RpoDigest = EmptySubtreeRoots::empty_hashes(8)[0];
// insert some random, 8 depth keys. `a` diverges from the first bit
let a = 0b01101001_u64;
let b = 0b10011001_u64;
let c = 0b10010110_u64;
let d = 0b11110110_u64;
for k in [a, b, c, d] {
let index = NodeIndex::new(8, k).unwrap();
let node = RpoDigest::from([Felt::new(k); WORD_SIZE]);
root = store.set_node(root, index, node).unwrap().root;
}
// assert all leaves returns the inserted depth
for k in [a, b, c, d] {
assert_eq!(8, store.get_leaf_depth(root, 8, k).unwrap());
}
// flip last bit of a and expect it to return the the same depth, but for an empty node
assert_eq!(8, store.get_leaf_depth(root, 8, 0b01101000_u64).unwrap());
// flip fourth bit of a and expect an empty node on depth 4
assert_eq!(4, store.get_leaf_depth(root, 8, 0b01111001_u64).unwrap());
// flip third bit of a and expect an empty node on depth 3
assert_eq!(3, store.get_leaf_depth(root, 8, 0b01001001_u64).unwrap());
// flip second bit of a and expect an empty node on depth 2
assert_eq!(2, store.get_leaf_depth(root, 8, 0b00101001_u64).unwrap());
// flip fourth bit of c and expect an empty node on depth 4
assert_eq!(4, store.get_leaf_depth(root, 8, 0b10000110_u64).unwrap());
// flip second bit of d and expect an empty node on depth 3 as depth 2 conflicts with b and c
assert_eq!(3, store.get_leaf_depth(root, 8, 0b10110110_u64).unwrap());
// duplicate the tree on `a` and assert the depth is short-circuited by such sub-tree
let index = NodeIndex::new(8, a).unwrap();
root = store.set_node(root, index, root).unwrap().root;
assert_eq!(Err(MerkleError::DepthTooBig(9)), store.get_leaf_depth(root, 8, a));
}
// SUBSET EXTRACTION
// ================================================================================================
#[test]
fn mstore_subset() {
// add a Merkle tree of depth 3 to the store
let mtree = MerkleTree::new(digests_to_words(&VALUES8)).unwrap();
let mut store = MerkleStore::default();
let empty_store_num_nodes = store.nodes.len();
store.extend(mtree.inner_nodes());
// build 3 subtrees contained within the above Merkle tree; note that subtree2 is a subset
// of subtree1
let subtree1 = MerkleTree::new(digests_to_words(&VALUES8[..4])).unwrap();
let subtree2 = MerkleTree::new(digests_to_words(&VALUES8[2..4])).unwrap();
let subtree3 = MerkleTree::new(digests_to_words(&VALUES8[6..])).unwrap();
// --- extract all 3 subtrees ---------------------------------------------
let substore = store.subset([subtree1.root(), subtree2.root(), subtree3.root()].iter());
// number of nodes should increase by 4: 3 nodes form subtree1 and 1 node from subtree3
assert_eq!(substore.nodes.len(), empty_store_num_nodes + 4);
// make sure paths that all subtrees are in the store
check_mstore_subtree(&substore, &subtree1);
check_mstore_subtree(&substore, &subtree2);
check_mstore_subtree(&substore, &subtree3);
// --- extract subtrees 1 and 3 -------------------------------------------
// this should give the same result as above as subtree2 is nested withing subtree1
let substore = store.subset([subtree1.root(), subtree3.root()].iter());
// number of nodes should increase by 4: 3 nodes form subtree1 and 1 node from subtree3
assert_eq!(substore.nodes.len(), empty_store_num_nodes + 4);
// make sure paths that all subtrees are in the store
check_mstore_subtree(&substore, &subtree1);
check_mstore_subtree(&substore, &subtree2);
check_mstore_subtree(&substore, &subtree3);
}
fn check_mstore_subtree(store: &MerkleStore, subtree: &MerkleTree) {
for (i, value) in subtree.leaves() {
let index = NodeIndex::new(subtree.depth(), i).unwrap();
let path1 = store.get_path(subtree.root(), index).unwrap();
assert_eq!(*path1.value, *value);
let path2 = subtree.get_path(index).unwrap();
assert_eq!(path1.path, path2);
}
}
// SERIALIZATION
// ================================================================================================
#[cfg(feature = "std")]
#[test]
fn test_serialization() -> Result<(), Box<dyn Error>> {
let mtree = MerkleTree::new(digests_to_words(&VALUES4))?;
let store = MerkleStore::from(&mtree);
let decoded = MerkleStore::read_from_bytes(&store.to_bytes()).expect("deserialization failed");
assert_eq!(store, decoded);
Ok(())
}
// MERKLE RECORDER
// ================================================================================================
#[test]
fn test_recorder() {
// instantiate recorder from MerkleTree and SimpleSmt
let mtree = MerkleTree::new(digests_to_words(&VALUES4)).unwrap();
let smtree = SimpleSmt::with_leaves(
64,
KEYS8.into_iter().zip(VALUES8.into_iter().map(|x| x.into()).rev()),
)
.unwrap();
let mut recorder: RecordingMerkleStore =
mtree.inner_nodes().chain(smtree.inner_nodes()).collect();
// get nodes from both trees and make sure they are correct
let index_0 = NodeIndex::new(mtree.depth(), 0).unwrap();
let node = recorder.get_node(mtree.root(), index_0).unwrap();
assert_eq!(node, mtree.get_node(index_0).unwrap());
let index_1 = NodeIndex::new(smtree.depth(), 1).unwrap();
let node = recorder.get_node(smtree.root(), index_1).unwrap();
assert_eq!(node, smtree.get_node(index_1).unwrap());
// insert a value and assert that when we request it next time it is accurate
let new_value = [ZERO, ZERO, ONE, ONE].into();
let index_2 = NodeIndex::new(smtree.depth(), 2).unwrap();
let root = recorder.set_node(smtree.root(), index_2, new_value).unwrap().root;
assert_eq!(recorder.get_node(root, index_2).unwrap(), new_value);
// construct the proof
let rec_map = recorder.into_inner();
let proof = rec_map.into_proof();
let merkle_store: MerkleStore = proof.into();
// make sure the proof contains all nodes from both trees
let node = merkle_store.get_node(mtree.root(), index_0).unwrap();
assert_eq!(node, mtree.get_node(index_0).unwrap());
let node = merkle_store.get_node(smtree.root(), index_1).unwrap();
assert_eq!(node, smtree.get_node(index_1).unwrap());
let node = merkle_store.get_node(smtree.root(), index_2).unwrap();
assert_eq!(node, smtree.get_leaf(index_2.value()).unwrap().into());
// assert that is doesnt contain nodes that were not recorded
let not_recorded_index = NodeIndex::new(smtree.depth(), 4).unwrap();
assert!(merkle_store.get_node(smtree.root(), not_recorded_index).is_err());
assert!(smtree.get_node(not_recorded_index).is_ok());
}

View File

@@ -0,0 +1,485 @@
use super::{
BTreeMap, BTreeSet, EmptySubtreeRoots, Felt, InnerNodeInfo, MerkleError, MerklePath, NodeIndex,
Rpo256, RpoDigest, StarkField, Vec, Word, ZERO,
};
use core::cmp;
#[cfg(test)]
mod tests;
// TIERED SPARSE MERKLE TREE
// ================================================================================================
/// Tiered (compacted) Sparse Merkle tree mapping 256-bit keys to 256-bit values. Both keys and
/// values are represented by 4 field elements.
///
/// Leaves in the tree can exist only on specific depths called "tiers". These depths are: 16, 32,
/// 48, and 64. Initially, when a tree is empty, it is equivalent to an empty Sparse Merkle tree
/// of depth 64 (i.e., leaves at depth 64 are set to [ZERO; 4]). As non-empty values are inserted
/// into the tree they are added to the first available tier.
///
/// For example, when the first key-value is inserted, it will be stored in a node at depth 16
/// such that the first 16 bits of the key determine the position of the node at depth 16. If
/// another value with a key sharing the same 16-bit prefix is inserted, both values move into
/// the next tier (depth 32). This process is repeated until values end up at tier 64. If multiple
/// values have keys with a common 64-bit prefix, such key-value pairs are stored in a sorted list
/// at the last tier (depth = 64).
///
/// To differentiate between internal and leaf nodes, node values are computed as follows:
/// - Internal nodes: hash(left_child, right_child).
/// - Leaf node at depths 16, 32, or 64: hash(rem_key, value, domain=depth).
/// - Leaf node at depth 64: hash([rem_key_0, value_0, ..., rem_key_n, value_n, domain=64]).
///
/// Where rem_key is computed by replacing d most significant bits of the key with zeros where d
/// is depth (i.e., for a leaf at depth 16, we replace 16 most significant bits of the key with 0).
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TieredSmt {
root: RpoDigest,
nodes: BTreeMap<NodeIndex, RpoDigest>,
upper_leaves: BTreeMap<NodeIndex, RpoDigest>, // node_index |-> key map
bottom_leaves: BTreeMap<u64, BottomLeaf>, // leaves of depth 64
values: BTreeMap<RpoDigest, Word>,
}
impl TieredSmt {
// CONSTANTS
// --------------------------------------------------------------------------------------------
/// The number of levels between tiers.
const TIER_SIZE: u8 = 16;
/// Depths at which leaves can exist in a tiered SMT.
const TIER_DEPTHS: [u8; 4] = [16, 32, 48, 64];
/// Maximum node depth. This is also the bottom tier of the tree.
const MAX_DEPTH: u8 = 64;
/// Value of an empty leaf.
pub const EMPTY_VALUE: Word = super::empty_roots::EMPTY_WORD;
// CONSTRUCTORS
// --------------------------------------------------------------------------------------------
/// Returns a new [TieredSmt] instantiated with the specified key-value pairs.
///
/// # Errors
/// Returns an error if the provided entries contain multiple values for the same key.
pub fn with_leaves<R, I>(entries: R) -> Result<Self, MerkleError>
where
R: IntoIterator<IntoIter = I>,
I: Iterator<Item = (RpoDigest, Word)> + ExactSizeIterator,
{
// create an empty tree
let mut tree = Self::default();
// append leaves to the tree returning an error if a duplicate entry for the same key
// is found
let mut empty_entries = BTreeSet::new();
for (key, value) in entries {
let old_value = tree.insert(key, value);
if old_value != Self::EMPTY_VALUE || empty_entries.contains(&key) {
return Err(MerkleError::DuplicateValuesForKey(key));
}
// if we've processed an empty entry, add the key to the set of empty entry keys, and
// if this key was already in the set, return an error
if value == Self::EMPTY_VALUE && !empty_entries.insert(key) {
return Err(MerkleError::DuplicateValuesForKey(key));
}
}
Ok(tree)
}
// PUBLIC ACCESSORS
// --------------------------------------------------------------------------------------------
/// Returns the root of this Merkle tree.
pub const fn root(&self) -> RpoDigest {
self.root
}
/// Returns a node at the specified index.
///
/// # Errors
/// Returns an error if:
/// - The specified index depth is 0 or greater than 64.
/// - The node with the specified index does not exists in the Merkle tree. This is possible
/// when a leaf node with the same index prefix exists at a tier higher than the requested
/// node.
pub fn get_node(&self, index: NodeIndex) -> Result<RpoDigest, MerkleError> {
self.validate_node_access(index)?;
Ok(self.get_node_unchecked(&index))
}
/// Returns a Merkle path from the node at the specified index to the root.
///
/// The node itself is not included in the path.
///
/// # Errors
/// Returns an error if:
/// - The specified index depth is 0 or greater than 64.
/// - The node with the specified index does not exists in the Merkle tree. This is possible
/// when a leaf node with the same index prefix exists at a tier higher than the node to
/// which the path is requested.
pub fn get_path(&self, mut index: NodeIndex) -> Result<MerklePath, MerkleError> {
self.validate_node_access(index)?;
let mut path = Vec::with_capacity(index.depth() as usize);
for _ in 0..index.depth() {
let node = self.get_node_unchecked(&index.sibling());
path.push(node);
index.move_up();
}
Ok(path.into())
}
/// Returns the value associated with the specified key.
///
/// If nothing was inserted into this tree for the specified key, [ZERO; 4] is returned.
pub fn get_value(&self, key: RpoDigest) -> Word {
match self.values.get(&key) {
Some(value) => *value,
None => Self::EMPTY_VALUE,
}
}
// STATE MUTATORS
// --------------------------------------------------------------------------------------------
/// Inserts the provided value into the tree under the specified key and returns the value
/// previously stored under this key.
///
/// If the value for the specified key was not previously set, [ZERO; 4] is returned.
pub fn insert(&mut self, key: RpoDigest, value: Word) -> Word {
// insert the value into the key-value map, and if nothing has changed, return
let old_value = self.values.insert(key, value).unwrap_or(Self::EMPTY_VALUE);
if old_value == value {
return old_value;
}
// determine the index for the value node; this index could have 3 different meanings:
// - it points to a root of an empty subtree (excluding depth = 64); in this case, we can
// replace the node with the value node immediately.
// - it points to a node at the bottom tier (i.e., depth = 64); in this case, we need to
// process bottom-tier insertion which will be handled by insert_node().
// - it points to a leaf node; this node could be a node with the same key or a different
// key with a common prefix; in the latter case, we'll need to move the leaf to a lower
// tier; for this scenario the `leaf_key` will contain the key of the leaf node
let (mut index, leaf_key) = self.get_insert_location(&key);
// if the returned index points to a leaf, and this leaf is for a different key, we need
// to move the leaf to a lower tier
if let Some(other_key) = leaf_key {
if other_key != key {
// determine how far down the tree should we move the existing leaf
let common_prefix_len = get_common_prefix_tier(&key, &other_key);
let depth = cmp::min(common_prefix_len + Self::TIER_SIZE, Self::MAX_DEPTH);
// move the leaf to the new location; this requires first removing the existing
// index, re-computing node value, and inserting the node at a new location
let other_index = key_to_index(&other_key, depth);
let other_value = *self.values.get(&other_key).expect("no value for other key");
self.upper_leaves.remove(&index).expect("other node key not in map");
self.insert_node(other_index, other_key, other_value);
// the new leaf also needs to move down to the same tier
index = key_to_index(&key, depth);
}
}
// insert the node and return the old value
self.insert_node(index, key, value);
old_value
}
// ITERATORS
// --------------------------------------------------------------------------------------------
/// Returns an iterator over all inner nodes of this [TieredSmt] (i.e., nodes not at depths 16
/// 32, 48, or 64).
///
/// The iterator order is unspecified.
pub fn inner_nodes(&self) -> impl Iterator<Item = InnerNodeInfo> + '_ {
self.nodes.iter().filter_map(|(index, node)| {
if is_inner_node(index) {
Some(InnerNodeInfo {
value: *node,
left: self.get_node_unchecked(&index.left_child()),
right: self.get_node_unchecked(&index.right_child()),
})
} else {
None
}
})
}
/// Returns an iterator over upper leaves (i.e., depth = 16, 32, or 48) for this [TieredSmt].
///
/// Each yielded item is a (node, key, value) tuple where key is a full un-truncated key (i.e.,
/// with key[3] element unmodified).
///
/// The iterator order is unspecified.
pub fn upper_leaves(&self) -> impl Iterator<Item = (RpoDigest, RpoDigest, Word)> + '_ {
self.upper_leaves.iter().map(|(index, key)| {
let node = self.get_node_unchecked(index);
let value = self.get_value(*key);
(node, *key, value)
})
}
/// Returns an iterator over bottom leaves (i.e., depth = 64) of this [TieredSmt].
///
/// Each yielded item consists of the hash of the leaf and its contents, where contents is
/// a vector containing key-value pairs of entries storied in this leaf. Note that keys are
/// un-truncated keys (i.e., with key[3] element unmodified).
///
/// The iterator order is unspecified.
pub fn bottom_leaves(&self) -> impl Iterator<Item = (RpoDigest, Vec<(RpoDigest, Word)>)> + '_ {
self.bottom_leaves.values().map(|leaf| (leaf.hash(), leaf.contents()))
}
// HELPER METHODS
// --------------------------------------------------------------------------------------------
/// Checks if the specified index is valid in the context of this Merkle tree.
///
/// # Errors
/// Returns an error if:
/// - The specified index depth is 0 or greater than 64.
/// - The node for the specified index does not exists in the Merkle tree. This is possible
/// when an ancestors of the specified index is a leaf node.
fn validate_node_access(&self, index: NodeIndex) -> Result<(), MerkleError> {
if index.is_root() {
return Err(MerkleError::DepthTooSmall(index.depth()));
} else if index.depth() > Self::MAX_DEPTH {
return Err(MerkleError::DepthTooBig(index.depth() as u64));
} else {
// make sure that there are no leaf nodes in the ancestors of the index; since leaf
// nodes can live at specific depth, we just need to check these depths.
let tier = get_index_tier(&index);
let mut tier_index = index;
for &depth in Self::TIER_DEPTHS[..tier].iter().rev() {
tier_index.move_up_to(depth);
if self.upper_leaves.contains_key(&tier_index) {
return Err(MerkleError::NodeNotInSet(index));
}
}
}
Ok(())
}
/// Returns a node at the specified index. If the node does not exist at this index, a root
/// for an empty subtree at the index's depth is returned.
///
/// Unlike [TieredSmt::get_node()] this does not perform any checks to verify that the returned
/// node is valid in the context of this tree.
fn get_node_unchecked(&self, index: &NodeIndex) -> RpoDigest {
match self.nodes.get(index) {
Some(node) => *node,
None => EmptySubtreeRoots::empty_hashes(Self::MAX_DEPTH)[index.depth() as usize],
}
}
/// Returns an index at which a node for the specified key should be inserted. If a leaf node
/// already exists at that index, returns the key associated with that leaf node.
///
/// In case the index falls into the bottom tier (depth = 64), leaf node key is not returned
/// as the bottom tier may contain multiple key-value pairs in the same leaf.
fn get_insert_location(&self, key: &RpoDigest) -> (NodeIndex, Option<RpoDigest>) {
// traverse the tree from the root down checking nodes at tiers 16, 32, and 48. Return if
// a node at any of the tiers is either a leaf or a root of an empty subtree.
let mse = Word::from(key)[3].as_int();
for depth in (Self::TIER_DEPTHS[0]..Self::MAX_DEPTH).step_by(Self::TIER_SIZE as usize) {
let index = NodeIndex::new_unchecked(depth, mse >> (Self::MAX_DEPTH - depth));
if let Some(leaf_key) = self.upper_leaves.get(&index) {
return (index, Some(*leaf_key));
} else if !self.nodes.contains_key(&index) {
return (index, None);
}
}
// if we got here, that means all of the nodes checked so far are internal nodes, and
// the new node would need to be inserted in the bottom tier.
let index = NodeIndex::new_unchecked(Self::MAX_DEPTH, mse);
(index, None)
}
/// Inserts the provided key-value pair at the specified index and updates the root of this
/// Merkle tree by recomputing the path to the root.
fn insert_node(&mut self, mut index: NodeIndex, key: RpoDigest, value: Word) {
let depth = index.depth();
// insert the key into index-key map and compute the new value of the node
let mut node = if index.depth() == Self::MAX_DEPTH {
// for the bottom tier, we add the key-value pair to the existing leaf, or create a
// new leaf with this key-value pair
self.bottom_leaves
.entry(index.value())
.and_modify(|leaves| leaves.add_value(key, value))
.or_insert(BottomLeaf::new(key, value))
.hash()
} else {
// for the upper tiers, we just update the index-key map and compute the value of the
// node
self.upper_leaves.insert(index, key);
// the node value is computed as: hash(remaining_key || value, domain = depth)
let remaining_path = get_remaining_path(key, depth.into());
Rpo256::merge_in_domain(&[remaining_path, value.into()], depth.into())
};
// insert the node and update the path from the node to the root
for _ in 0..index.depth() {
self.nodes.insert(index, node);
let sibling = self.get_node_unchecked(&index.sibling());
node = Rpo256::merge(&index.build_node(node, sibling));
index.move_up();
}
// update the root
self.nodes.insert(NodeIndex::root(), node);
self.root = node;
}
}
impl Default for TieredSmt {
fn default() -> Self {
Self {
root: EmptySubtreeRoots::empty_hashes(Self::MAX_DEPTH)[0],
nodes: BTreeMap::new(),
upper_leaves: BTreeMap::new(),
bottom_leaves: BTreeMap::new(),
values: BTreeMap::new(),
}
}
}
// HELPER FUNCTIONS
// ================================================================================================
/// Returns the remaining path for the specified key at the specified depth.
///
/// Remaining path is computed by setting n most significant bits of the key to zeros, where n is
/// the specified depth.
fn get_remaining_path(key: RpoDigest, depth: u32) -> RpoDigest {
let mut key = Word::from(key);
key[3] = if depth == 64 {
ZERO
} else {
// remove `depth` bits from the most significant key element
((key[3].as_int() << depth) >> depth).into()
};
key.into()
}
/// Returns index for the specified key inserted at the specified depth.
///
/// The value for the key is computed by taking n most significant bits from the most significant
/// element of the key, where n is the specified depth.
fn key_to_index(key: &RpoDigest, depth: u8) -> NodeIndex {
let mse = Word::from(key)[3].as_int();
let value = match depth {
16 | 32 | 48 | 64 => mse >> ((TieredSmt::MAX_DEPTH - depth) as u32),
_ => unreachable!("invalid depth: {depth}"),
};
NodeIndex::new_unchecked(depth, value)
}
/// Returns tiered common prefix length between the most significant elements of the provided keys.
///
/// Specifically:
/// - returns 64 if the most significant elements are equal.
/// - returns 48 if the common prefix is between 48 and 63 bits.
/// - returns 32 if the common prefix is between 32 and 47 bits.
/// - returns 16 if the common prefix is between 16 and 31 bits.
/// - returns 0 if the common prefix is fewer than 16 bits.
fn get_common_prefix_tier(key1: &RpoDigest, key2: &RpoDigest) -> u8 {
let e1 = Word::from(key1)[3].as_int();
let e2 = Word::from(key2)[3].as_int();
let ex = (e1 ^ e2).leading_zeros() as u8;
(ex / 16) * 16
}
/// Returns a tier for the specified index.
///
/// The tiers are defined as follows:
/// - Tier 0: depth 0 through 16 (inclusive).
/// - Tier 1: depth 17 through 32 (inclusive).
/// - Tier 2: depth 33 through 48 (inclusive).
/// - Tier 3: depth 49 through 64 (inclusive).
const fn get_index_tier(index: &NodeIndex) -> usize {
debug_assert!(index.depth() <= TieredSmt::MAX_DEPTH, "invalid depth");
match index.depth() {
0..=16 => 0,
17..=32 => 1,
33..=48 => 2,
_ => 3,
}
}
/// Returns true if the specified index is an index for an inner node (i.e., the depth is not 16,
/// 32, 48, or 64).
const fn is_inner_node(index: &NodeIndex) -> bool {
!matches!(index.depth(), 16 | 32 | 48 | 64)
}
// BOTTOM LEAF
// ================================================================================================
/// Stores contents of the bottom leaf (i.e., leaf at depth = 64) in a [TieredSmt].
///
/// Bottom leaf can contain one or more key-value pairs all sharing the same 64-bit key prefix.
/// The values are sorted by key to make sure the structure of the leaf is independent of the
/// insertion order. This guarantees that a leaf with the same set of key-value pairs always has
/// the same hash value.
#[derive(Debug, Clone, PartialEq, Eq)]
struct BottomLeaf {
prefix: u64,
values: BTreeMap<[u64; 4], Word>,
}
impl BottomLeaf {
/// Returns a new [BottomLeaf] with a single key-value pair added.
pub fn new(key: RpoDigest, value: Word) -> Self {
let prefix = Word::from(key)[3].as_int();
let mut values = BTreeMap::new();
let key = get_remaining_path(key, TieredSmt::MAX_DEPTH as u32);
values.insert(key.into(), value);
Self { prefix, values }
}
/// Adds a new key-value pair to this leaf.
pub fn add_value(&mut self, key: RpoDigest, value: Word) {
let key = get_remaining_path(key, TieredSmt::MAX_DEPTH as u32);
self.values.insert(key.into(), value);
}
/// Computes a hash of this leaf.
pub fn hash(&self) -> RpoDigest {
let mut elements = Vec::with_capacity(self.values.len() * 2);
for (key, val) in self.values.iter() {
key.iter().for_each(|&v| elements.push(Felt::new(v)));
elements.extend_from_slice(val.as_slice());
}
// TODO: hash in domain
Rpo256::hash_elements(&elements)
}
/// Returns contents of this leaf as a vector of (key, value) pairs.
///
/// The keys are returned in their un-truncated form.
pub fn contents(&self) -> Vec<(RpoDigest, Word)> {
self.values
.iter()
.map(|(key, val)| {
let key = RpoDigest::from([
Felt::new(key[0]),
Felt::new(key[1]),
Felt::new(key[2]),
Felt::new(self.prefix),
]);
(key, *val)
})
.collect()
}
}

View File

@@ -0,0 +1,441 @@
use super::{
super::{super::ONE, Felt, MerkleStore, WORD_SIZE, ZERO},
get_remaining_path, EmptySubtreeRoots, InnerNodeInfo, NodeIndex, Rpo256, RpoDigest, TieredSmt,
Vec, Word,
};
#[test]
fn tsmt_insert_one() {
let mut smt = TieredSmt::default();
let mut store = MerkleStore::default();
let raw = 0b_01101001_01101100_00011111_11111111_10010110_10010011_11100000_00000000_u64;
let key = RpoDigest::from([ONE, ONE, ONE, Felt::new(raw)]);
let value = [ONE; WORD_SIZE];
// since the tree is empty, the first node will be inserted at depth 16 and the index will be
// 16 most significant bits of the key
let index = NodeIndex::make(16, raw >> 48);
let leaf_node = build_leaf_node(key, value, 16);
let tree_root = store.set_node(smt.root(), index, leaf_node).unwrap().root;
smt.insert(key, value);
assert_eq!(smt.root(), tree_root);
// make sure the value was inserted, and the node is at the expected index
assert_eq!(smt.get_value(key), value);
assert_eq!(smt.get_node(index).unwrap(), leaf_node);
// make sure the paths we get from the store and the tree match
let expected_path = store.get_path(tree_root, index).unwrap();
assert_eq!(smt.get_path(index).unwrap(), expected_path.path);
// make sure inner nodes match
let expected_nodes = get_non_empty_nodes(&store);
let actual_nodes = smt.inner_nodes().collect::<Vec<_>>();
assert_eq!(actual_nodes.len(), expected_nodes.len());
actual_nodes.iter().for_each(|node| assert!(expected_nodes.contains(node)));
// make sure leaves are returned correctly
let mut leaves = smt.upper_leaves();
assert_eq!(leaves.next(), Some((leaf_node, key, value)));
assert_eq!(leaves.next(), None);
}
#[test]
fn tsmt_insert_two_16() {
let mut smt = TieredSmt::default();
let mut store = MerkleStore::default();
// --- insert the first value ---------------------------------------------
let raw_a = 0b_10101010_10101010_00011111_11111111_10010110_10010011_11100000_00000000_u64;
let key_a = RpoDigest::from([ONE, ONE, ONE, Felt::new(raw_a)]);
let val_a = [ONE; WORD_SIZE];
smt.insert(key_a, val_a);
// --- insert the second value --------------------------------------------
// the key for this value has the same 16-bit prefix as the key for the first value,
// thus, on insertions, both values should be pushed to depth 32 tier
let raw_b = 0b_10101010_10101010_10011111_11111111_10010110_10010011_11100000_00000000_u64;
let key_b = RpoDigest::from([ONE, ONE, ONE, Felt::new(raw_b)]);
let val_b = [Felt::new(2); WORD_SIZE];
smt.insert(key_b, val_b);
// --- build Merkle store with equivalent data ----------------------------
let mut tree_root = get_init_root();
let index_a = NodeIndex::make(32, raw_a >> 32);
let leaf_node_a = build_leaf_node(key_a, val_a, 32);
tree_root = store.set_node(tree_root, index_a, leaf_node_a).unwrap().root;
let index_b = NodeIndex::make(32, raw_b >> 32);
let leaf_node_b = build_leaf_node(key_b, val_b, 32);
tree_root = store.set_node(tree_root, index_b, leaf_node_b).unwrap().root;
// --- verify that data is consistent between store and tree --------------
assert_eq!(smt.root(), tree_root);
assert_eq!(smt.get_value(key_a), val_a);
assert_eq!(smt.get_node(index_a).unwrap(), leaf_node_a);
let expected_path = store.get_path(tree_root, index_a).unwrap().path;
assert_eq!(smt.get_path(index_a).unwrap(), expected_path);
assert_eq!(smt.get_value(key_b), val_b);
assert_eq!(smt.get_node(index_b).unwrap(), leaf_node_b);
let expected_path = store.get_path(tree_root, index_b).unwrap().path;
assert_eq!(smt.get_path(index_b).unwrap(), expected_path);
// make sure inner nodes match - the store contains more entries because it keeps track of
// all prior state - so, we don't check that the number of inner nodes is the same in both
let expected_nodes = get_non_empty_nodes(&store);
let actual_nodes = smt.inner_nodes().collect::<Vec<_>>();
actual_nodes.iter().for_each(|node| assert!(expected_nodes.contains(node)));
// make sure leaves are returned correctly
let mut leaves = smt.upper_leaves();
assert_eq!(leaves.next(), Some((leaf_node_a, key_a, val_a)));
assert_eq!(leaves.next(), Some((leaf_node_b, key_b, val_b)));
assert_eq!(leaves.next(), None);
}
#[test]
fn tsmt_insert_two_32() {
let mut smt = TieredSmt::default();
let mut store = MerkleStore::default();
// --- insert the first value ---------------------------------------------
let raw_a = 0b_10101010_10101010_00011111_11111111_10010110_10010011_11100000_00000000_u64;
let key_a = RpoDigest::from([ONE, ONE, ONE, Felt::new(raw_a)]);
let val_a = [ONE; WORD_SIZE];
smt.insert(key_a, val_a);
// --- insert the second value --------------------------------------------
// the key for this value has the same 32-bit prefix as the key for the first value,
// thus, on insertions, both values should be pushed to depth 48 tier
let raw_b = 0b_10101010_10101010_00011111_11111111_00010110_10010011_11100000_00000000_u64;
let key_b = RpoDigest::from([ONE, ONE, ONE, Felt::new(raw_b)]);
let val_b = [Felt::new(2); WORD_SIZE];
smt.insert(key_b, val_b);
// --- build Merkle store with equivalent data ----------------------------
let mut tree_root = get_init_root();
let index_a = NodeIndex::make(48, raw_a >> 16);
let leaf_node_a = build_leaf_node(key_a, val_a, 48);
tree_root = store.set_node(tree_root, index_a, leaf_node_a).unwrap().root;
let index_b = NodeIndex::make(48, raw_b >> 16);
let leaf_node_b = build_leaf_node(key_b, val_b, 48);
tree_root = store.set_node(tree_root, index_b, leaf_node_b).unwrap().root;
// --- verify that data is consistent between store and tree --------------
assert_eq!(smt.root(), tree_root);
assert_eq!(smt.get_value(key_a), val_a);
assert_eq!(smt.get_node(index_a).unwrap(), leaf_node_a);
let expected_path = store.get_path(tree_root, index_a).unwrap().path;
assert_eq!(smt.get_path(index_a).unwrap(), expected_path);
assert_eq!(smt.get_value(key_b), val_b);
assert_eq!(smt.get_node(index_b).unwrap(), leaf_node_b);
let expected_path = store.get_path(tree_root, index_b).unwrap().path;
assert_eq!(smt.get_path(index_b).unwrap(), expected_path);
// make sure inner nodes match - the store contains more entries because it keeps track of
// all prior state - so, we don't check that the number of inner nodes is the same in both
let expected_nodes = get_non_empty_nodes(&store);
let actual_nodes = smt.inner_nodes().collect::<Vec<_>>();
actual_nodes.iter().for_each(|node| assert!(expected_nodes.contains(node)));
}
#[test]
fn tsmt_insert_three() {
let mut smt = TieredSmt::default();
let mut store = MerkleStore::default();
// --- insert the first value ---------------------------------------------
let raw_a = 0b_10101010_10101010_00011111_11111111_10010110_10010011_11100000_00000000_u64;
let key_a = RpoDigest::from([ONE, ONE, ONE, Felt::new(raw_a)]);
let val_a = [ONE; WORD_SIZE];
smt.insert(key_a, val_a);
// --- insert the second value --------------------------------------------
// the key for this value has the same 16-bit prefix as the key for the first value,
// thus, on insertions, both values should be pushed to depth 32 tier
let raw_b = 0b_10101010_10101010_10011111_11111111_10010110_10010011_11100000_00000000_u64;
let key_b = RpoDigest::from([ONE, ONE, ONE, Felt::new(raw_b)]);
let val_b = [Felt::new(2); WORD_SIZE];
smt.insert(key_b, val_b);
// --- insert the third value ---------------------------------------------
// the key for this value has the same 16-bit prefix as the keys for the first two,
// values; thus, on insertions, it will be inserted into depth 32 tier, but will not
// affect locations of the other two values
let raw_c = 0b_10101010_10101010_11011111_11111111_10010110_10010011_11100000_00000000_u64;
let key_c = RpoDigest::from([ONE, ONE, ONE, Felt::new(raw_c)]);
let val_c = [Felt::new(3); WORD_SIZE];
smt.insert(key_c, val_c);
// --- build Merkle store with equivalent data ----------------------------
let mut tree_root = get_init_root();
let index_a = NodeIndex::make(32, raw_a >> 32);
let leaf_node_a = build_leaf_node(key_a, val_a, 32);
tree_root = store.set_node(tree_root, index_a, leaf_node_a).unwrap().root;
let index_b = NodeIndex::make(32, raw_b >> 32);
let leaf_node_b = build_leaf_node(key_b, val_b, 32);
tree_root = store.set_node(tree_root, index_b, leaf_node_b).unwrap().root;
let index_c = NodeIndex::make(32, raw_c >> 32);
let leaf_node_c = build_leaf_node(key_c, val_c, 32);
tree_root = store.set_node(tree_root, index_c, leaf_node_c).unwrap().root;
// --- verify that data is consistent between store and tree --------------
assert_eq!(smt.root(), tree_root);
assert_eq!(smt.get_value(key_a), val_a);
assert_eq!(smt.get_node(index_a).unwrap(), leaf_node_a);
let expected_path = store.get_path(tree_root, index_a).unwrap().path;
assert_eq!(smt.get_path(index_a).unwrap(), expected_path);
assert_eq!(smt.get_value(key_b), val_b);
assert_eq!(smt.get_node(index_b).unwrap(), leaf_node_b);
let expected_path = store.get_path(tree_root, index_b).unwrap().path;
assert_eq!(smt.get_path(index_b).unwrap(), expected_path);
assert_eq!(smt.get_value(key_c), val_c);
assert_eq!(smt.get_node(index_c).unwrap(), leaf_node_c);
let expected_path = store.get_path(tree_root, index_c).unwrap().path;
assert_eq!(smt.get_path(index_c).unwrap(), expected_path);
// make sure inner nodes match - the store contains more entries because it keeps track of
// all prior state - so, we don't check that the number of inner nodes is the same in both
let expected_nodes = get_non_empty_nodes(&store);
let actual_nodes = smt.inner_nodes().collect::<Vec<_>>();
actual_nodes.iter().for_each(|node| assert!(expected_nodes.contains(node)));
}
#[test]
fn tsmt_update() {
let mut smt = TieredSmt::default();
let mut store = MerkleStore::default();
// --- insert a value into the tree ---------------------------------------
let raw = 0b_01101001_01101100_00011111_11111111_10010110_10010011_11100000_00000000_u64;
let key = RpoDigest::from([ONE, ONE, ONE, Felt::new(raw)]);
let value_a = [ONE; WORD_SIZE];
smt.insert(key, value_a);
// --- update the value ---------------------------------------------------
let value_b = [Felt::new(2); WORD_SIZE];
smt.insert(key, value_b);
// --- verify consistency -------------------------------------------------
let mut tree_root = get_init_root();
let index = NodeIndex::make(16, raw >> 48);
let leaf_node = build_leaf_node(key, value_b, 16);
tree_root = store.set_node(tree_root, index, leaf_node).unwrap().root;
assert_eq!(smt.root(), tree_root);
assert_eq!(smt.get_value(key), value_b);
assert_eq!(smt.get_node(index).unwrap(), leaf_node);
let expected_path = store.get_path(tree_root, index).unwrap().path;
assert_eq!(smt.get_path(index).unwrap(), expected_path);
// make sure inner nodes match - the store contains more entries because it keeps track of
// all prior state - so, we don't check that the number of inner nodes is the same in both
let expected_nodes = get_non_empty_nodes(&store);
let actual_nodes = smt.inner_nodes().collect::<Vec<_>>();
actual_nodes.iter().for_each(|node| assert!(expected_nodes.contains(node)));
}
// BOTTOM TIER TESTS
// ================================================================================================
#[test]
fn tsmt_bottom_tier() {
let mut smt = TieredSmt::default();
let mut store = MerkleStore::default();
// common prefix for the keys
let prefix = 0b_10101010_10101010_00011111_11111111_10010110_10010011_11100000_00000000_u64;
// --- insert the first value ---------------------------------------------
let key_a = RpoDigest::from([ONE, ONE, ONE, Felt::new(prefix)]);
let val_a = [ONE; WORD_SIZE];
smt.insert(key_a, val_a);
// --- insert the second value --------------------------------------------
// this key has the same 64-bit prefix and thus both values should end up in the same
// node at depth 64
let key_b = RpoDigest::from([ZERO, ONE, ONE, Felt::new(prefix)]);
let val_b = [Felt::new(2); WORD_SIZE];
smt.insert(key_b, val_b);
// --- build Merkle store with equivalent data ----------------------------
let index = NodeIndex::make(64, prefix);
// to build bottom leaf we sort by key starting with the least significant element, thus
// key_b is smaller than key_a.
let leaf_node = build_bottom_leaf_node(&[key_b, key_a], &[val_b, val_a]);
let mut tree_root = get_init_root();
tree_root = store.set_node(tree_root, index, leaf_node).unwrap().root;
// --- verify that data is consistent between store and tree --------------
assert_eq!(smt.root(), tree_root);
assert_eq!(smt.get_value(key_a), val_a);
assert_eq!(smt.get_value(key_b), val_b);
assert_eq!(smt.get_node(index).unwrap(), leaf_node);
let expected_path = store.get_path(tree_root, index).unwrap().path;
assert_eq!(smt.get_path(index).unwrap(), expected_path);
// make sure inner nodes match - the store contains more entries because it keeps track of
// all prior state - so, we don't check that the number of inner nodes is the same in both
let expected_nodes = get_non_empty_nodes(&store);
let actual_nodes = smt.inner_nodes().collect::<Vec<_>>();
actual_nodes.iter().for_each(|node| assert!(expected_nodes.contains(node)));
// make sure leaves are returned correctly
let mut leaves = smt.bottom_leaves();
assert_eq!(leaves.next(), Some((leaf_node, vec![(key_b, val_b), (key_a, val_a)])));
assert_eq!(leaves.next(), None);
}
#[test]
fn tsmt_bottom_tier_two() {
let mut smt = TieredSmt::default();
let mut store = MerkleStore::default();
// --- insert the first value ---------------------------------------------
let raw_a = 0b_10101010_10101010_00011111_11111111_10010110_10010011_11100000_00000000_u64;
let key_a = RpoDigest::from([ONE, ONE, ONE, Felt::new(raw_a)]);
let val_a = [ONE; WORD_SIZE];
smt.insert(key_a, val_a);
// --- insert the second value --------------------------------------------
// the key for this value has the same 48-bit prefix as the key for the first value,
// thus, on insertions, both should end up in different nodes at depth 64
let raw_b = 0b_10101010_10101010_00011111_11111111_10010110_10010011_01100000_00000000_u64;
let key_b = RpoDigest::from([ONE, ONE, ONE, Felt::new(raw_b)]);
let val_b = [Felt::new(2); WORD_SIZE];
smt.insert(key_b, val_b);
// --- build Merkle store with equivalent data ----------------------------
let mut tree_root = get_init_root();
let index_a = NodeIndex::make(64, raw_a);
let leaf_node_a = build_bottom_leaf_node(&[key_a], &[val_a]);
tree_root = store.set_node(tree_root, index_a, leaf_node_a).unwrap().root;
let index_b = NodeIndex::make(64, raw_b);
let leaf_node_b = build_bottom_leaf_node(&[key_b], &[val_b]);
tree_root = store.set_node(tree_root, index_b, leaf_node_b).unwrap().root;
// --- verify that data is consistent between store and tree --------------
assert_eq!(smt.root(), tree_root);
assert_eq!(smt.get_value(key_a), val_a);
assert_eq!(smt.get_node(index_a).unwrap(), leaf_node_a);
let expected_path = store.get_path(tree_root, index_a).unwrap().path;
assert_eq!(smt.get_path(index_a).unwrap(), expected_path);
assert_eq!(smt.get_value(key_b), val_b);
assert_eq!(smt.get_node(index_b).unwrap(), leaf_node_b);
let expected_path = store.get_path(tree_root, index_b).unwrap().path;
assert_eq!(smt.get_path(index_b).unwrap(), expected_path);
// make sure inner nodes match - the store contains more entries because it keeps track of
// all prior state - so, we don't check that the number of inner nodes is the same in both
let expected_nodes = get_non_empty_nodes(&store);
let actual_nodes = smt.inner_nodes().collect::<Vec<_>>();
actual_nodes.iter().for_each(|node| assert!(expected_nodes.contains(node)));
// make sure leaves are returned correctly
let mut leaves = smt.bottom_leaves();
assert_eq!(leaves.next(), Some((leaf_node_b, vec![(key_b, val_b)])));
assert_eq!(leaves.next(), Some((leaf_node_a, vec![(key_a, val_a)])));
assert_eq!(leaves.next(), None);
}
// ERROR TESTS
// ================================================================================================
#[test]
fn tsmt_node_not_available() {
let mut smt = TieredSmt::default();
let raw = 0b_10101010_10101010_00011111_11111111_10010110_10010011_11100000_00000000_u64;
let key = RpoDigest::from([ONE, ONE, ONE, Felt::new(raw)]);
let value = [ONE; WORD_SIZE];
// build an index which is just below the inserted leaf node
let index = NodeIndex::make(17, raw >> 47);
// since we haven't inserted the node yet, we should be able to get node and path to this index
assert!(smt.get_node(index).is_ok());
assert!(smt.get_path(index).is_ok());
smt.insert(key, value);
// but once the node is inserted, everything under it should be unavailable
assert!(smt.get_node(index).is_err());
assert!(smt.get_path(index).is_err());
let index = NodeIndex::make(32, raw >> 32);
assert!(smt.get_node(index).is_err());
assert!(smt.get_path(index).is_err());
let index = NodeIndex::make(34, raw >> 30);
assert!(smt.get_node(index).is_err());
assert!(smt.get_path(index).is_err());
let index = NodeIndex::make(50, raw >> 14);
assert!(smt.get_node(index).is_err());
assert!(smt.get_path(index).is_err());
let index = NodeIndex::make(64, raw);
assert!(smt.get_node(index).is_err());
assert!(smt.get_path(index).is_err());
}
// HELPER FUNCTIONS
// ================================================================================================
fn get_init_root() -> RpoDigest {
EmptySubtreeRoots::empty_hashes(64)[0]
}
fn build_leaf_node(key: RpoDigest, value: Word, depth: u8) -> RpoDigest {
let remaining_path = get_remaining_path(key, depth as u32);
Rpo256::merge_in_domain(&[remaining_path, value.into()], depth.into())
}
fn build_bottom_leaf_node(keys: &[RpoDigest], values: &[Word]) -> RpoDigest {
assert_eq!(keys.len(), values.len());
let mut elements = Vec::with_capacity(keys.len());
for (key, val) in keys.iter().zip(values.iter()) {
let mut key = Word::from(key);
key[3] = ZERO;
elements.extend_from_slice(&key);
elements.extend_from_slice(val.as_slice());
}
Rpo256::hash_elements(&elements)
}
fn get_non_empty_nodes(store: &MerkleStore) -> Vec<InnerNodeInfo> {
store
.inner_nodes()
.filter(|node| !is_empty_subtree(&node.value))
.collect::<Vec<_>>()
}
fn is_empty_subtree(node: &RpoDigest) -> bool {
EmptySubtreeRoots::empty_hashes(255).contains(node)
}

324
src/utils/kv_map.rs Normal file
View File

@@ -0,0 +1,324 @@
use core::cell::RefCell;
use winter_utils::{
collections::{btree_map::IntoIter, BTreeMap, BTreeSet},
Box,
};
// KEY-VALUE MAP TRAIT
// ================================================================================================
/// A trait that defines the interface for a key-value map.
pub trait KvMap<K: Ord + Clone, V: Clone>:
Extend<(K, V)> + FromIterator<(K, V)> + IntoIterator<Item = (K, V)>
{
fn get(&self, key: &K) -> Option<&V>;
fn contains_key(&self, key: &K) -> bool;
fn len(&self) -> usize;
fn is_empty(&self) -> bool {
self.len() == 0
}
fn insert(&mut self, key: K, value: V) -> Option<V>;
fn iter(&self) -> Box<dyn Iterator<Item = (&K, &V)> + '_>;
}
// BTREE MAP `KvMap` IMPLEMENTATION
// ================================================================================================
impl<K: Ord + Clone, V: Clone> KvMap<K, V> for BTreeMap<K, V> {
fn get(&self, key: &K) -> Option<&V> {
self.get(key)
}
fn contains_key(&self, key: &K) -> bool {
self.contains_key(key)
}
fn len(&self) -> usize {
self.len()
}
fn insert(&mut self, key: K, value: V) -> Option<V> {
self.insert(key, value)
}
fn iter(&self) -> Box<dyn Iterator<Item = (&K, &V)> + '_> {
Box::new(self.iter())
}
}
// RECORDING MAP
// ================================================================================================
/// A [RecordingMap] that records read requests to the underlying key-value map.
///
/// The data recorder is used to generate a proof for read requests.
///
/// The [RecordingMap] is composed of three parts:
/// - `data`: which contains the current set of key-value pairs in the map.
/// - `updates`: which tracks keys for which values have been since the map was instantiated.
/// updates include both insertions and updates of values under existing keys.
/// - `trace`: which contains the key-value pairs from the original data which have been accesses
/// since the map was instantiated.
#[derive(Debug, Default, Clone, Eq, PartialEq)]
pub struct RecordingMap<K, V> {
data: BTreeMap<K, V>,
updates: BTreeSet<K>,
trace: RefCell<BTreeMap<K, V>>,
}
impl<K: Ord + Clone, V: Clone> RecordingMap<K, V> {
// CONSTRUCTOR
// --------------------------------------------------------------------------------------------
/// Returns a new [RecordingMap] instance initialized with the provided key-value pairs.
/// ([BTreeMap]).
pub fn new(init: impl IntoIterator<Item = (K, V)>) -> Self {
RecordingMap {
data: init.into_iter().collect(),
updates: BTreeSet::new(),
trace: RefCell::new(BTreeMap::new()),
}
}
// FINALIZER
// --------------------------------------------------------------------------------------------
/// Consumes the [RecordingMap] and returns a [BTreeMap] containing the key-value pairs from
/// the initial data set that were read during recording.
pub fn into_proof(self) -> BTreeMap<K, V> {
self.trace.take()
}
// TEST HELPERS
// --------------------------------------------------------------------------------------------
#[cfg(test)]
pub fn trace_len(&self) -> usize {
self.trace.borrow().len()
}
#[cfg(test)]
pub fn updates_len(&self) -> usize {
self.updates.len()
}
}
impl<K: Ord + Clone, V: Clone> KvMap<K, V> for RecordingMap<K, V> {
// PUBLIC ACCESSORS
// --------------------------------------------------------------------------------------------
/// Returns a reference to the value associated with the given key if the value exists.
///
/// If the key is part of the initial data set, the key access is recorded.
fn get(&self, key: &K) -> Option<&V> {
self.data.get(key).map(|value| {
if !self.updates.contains(key) {
self.trace.borrow_mut().insert(key.clone(), value.clone());
}
value
})
}
/// Returns a boolean to indicate whether the given key exists in the data set.
///
/// If the key is part of the initial data set, the key access is recorded.
fn contains_key(&self, key: &K) -> bool {
self.get(key).is_some()
}
/// Returns the number of key-value pairs in the data set.
fn len(&self) -> usize {
self.data.len()
}
// MUTATORS
// --------------------------------------------------------------------------------------------
/// Inserts a key-value pair into the data set.
///
/// If the key already exists in the data set, the value is updated and the old value is
/// returned.
fn insert(&mut self, key: K, value: V) -> Option<V> {
let new_update = self.updates.insert(key.clone());
self.data.insert(key.clone(), value).map(|old_value| {
if new_update {
self.trace.borrow_mut().insert(key, old_value.clone());
}
old_value
})
}
// ITERATION
// --------------------------------------------------------------------------------------------
/// Returns an iterator over the key-value pairs in the data set.
fn iter(&self) -> Box<dyn Iterator<Item = (&K, &V)> + '_> {
Box::new(self.data.iter())
}
}
impl<K: Clone + Ord, V: Clone> Extend<(K, V)> for RecordingMap<K, V> {
fn extend<T: IntoIterator<Item = (K, V)>>(&mut self, iter: T) {
iter.into_iter().for_each(move |(k, v)| {
self.insert(k, v);
});
}
}
impl<K: Clone + Ord, V: Clone> FromIterator<(K, V)> for RecordingMap<K, V> {
fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> Self {
Self::new(iter)
}
}
impl<K: Clone + Ord, V: Clone> IntoIterator for RecordingMap<K, V> {
type Item = (K, V);
type IntoIter = IntoIter<K, V>;
fn into_iter(self) -> Self::IntoIter {
self.data.into_iter()
}
}
// TESTS
// ================================================================================================
#[cfg(test)]
mod tests {
use super::*;
const ITEMS: [(u64, u64); 5] = [(0, 0), (1, 1), (2, 2), (3, 3), (4, 4)];
#[test]
fn test_get_item() {
// instantiate a recording map
let map = RecordingMap::new(ITEMS.to_vec());
// get a few items
let get_items = [0, 1, 2];
for key in get_items.iter() {
map.get(key);
}
// convert the map into a proof
let proof = map.into_proof();
// check that the proof contains the expected values
for (key, value) in ITEMS.iter() {
match get_items.contains(key) {
true => assert_eq!(proof.get(key), Some(value)),
false => assert_eq!(proof.get(key), None),
}
}
}
#[test]
fn test_contains_key() {
// instantiate a recording map
let map = RecordingMap::new(ITEMS.to_vec());
// check if the map contains a few items
let get_items = [0, 1, 2];
for key in get_items.iter() {
map.contains_key(key);
}
// convert the map into a proof
let proof = map.into_proof();
// check that the proof contains the expected values
for (key, _) in ITEMS.iter() {
match get_items.contains(key) {
true => assert_eq!(proof.contains_key(key), true),
false => assert_eq!(proof.contains_key(key), false),
}
}
}
#[test]
fn test_len() {
// instantiate a recording map
let mut map = RecordingMap::new(ITEMS.to_vec());
// length of the map should be equal to the number of items
assert_eq!(map.len(), ITEMS.len());
// inserting entry with key that already exists should not change the length, but it does
// add entries to the trace and update sets
map.insert(4, 5);
assert_eq!(map.len(), ITEMS.len());
assert_eq!(map.trace_len(), 1);
assert_eq!(map.updates_len(), 1);
// inserting entry with new key should increase the length; it should also record the key
// as an updated key, but the trace length does not change since old values were not touched
map.insert(5, 5);
assert_eq!(map.len(), ITEMS.len() + 1);
assert_eq!(map.trace_len(), 1);
assert_eq!(map.updates_len(), 2);
// get some items so that they are saved in the trace; this should record original items
// in the trace, but should not affect the set of updates
let get_items = [0, 1, 2];
for key in get_items.iter() {
map.contains_key(key);
}
assert_eq!(map.trace_len(), 4);
assert_eq!(map.updates_len(), 2);
// read the same items again, this should not have any effect on either length, trace, or
// the set of updates
let get_items = [0, 1, 2];
for key in get_items.iter() {
map.contains_key(key);
}
assert_eq!(map.trace_len(), 4);
assert_eq!(map.updates_len(), 2);
// read a newly inserted item; this should not affect either length, trace, or the set of
// updates
let _val = map.get(&5).unwrap();
assert_eq!(map.trace_len(), 4);
assert_eq!(map.updates_len(), 2);
// update a newly inserted item; this should not affect either length, trace, or the set
// of updates
map.insert(5, 11);
assert_eq!(map.trace_len(), 4);
assert_eq!(map.updates_len(), 2);
// Note: The length reported by the proof will be different to the length originally
// reported by the map.
let proof = map.into_proof();
// length of the proof should be equal to get_items + 1. The extra item is the original
// value at key = 4u64
assert_eq!(proof.len(), get_items.len() + 1);
}
#[test]
fn test_iter() {
let mut map = RecordingMap::new(ITEMS.to_vec());
assert!(map.iter().all(|(x, y)| ITEMS.contains(&(*x, *y))));
// when inserting entry with key that already exists the iterator should return the new value
let new_value = 5;
map.insert(4, new_value);
assert_eq!(map.iter().count(), ITEMS.len());
assert!(map.iter().all(|(x, y)| if x == &4 {
y == &new_value
} else {
ITEMS.contains(&(*x, *y))
}));
}
#[test]
fn test_is_empty() {
// instantiate an empty recording map
let empty_map: RecordingMap<u64, u64> = RecordingMap::default();
assert!(empty_map.is_empty());
// instantiate a non-empty recording map
let map = RecordingMap::new(ITEMS.to_vec());
assert!(!map.is_empty());
}
}

36
src/utils/mod.rs Normal file
View File

@@ -0,0 +1,36 @@
use super::{utils::string::String, Word};
use core::fmt::{self, Write};
#[cfg(not(feature = "std"))]
pub use alloc::format;
#[cfg(feature = "std")]
pub use std::format;
mod kv_map;
// RE-EXPORTS
// ================================================================================================
pub use winter_utils::{
string, uninit_vector, Box, ByteReader, ByteWriter, Deserializable, DeserializationError,
Serializable, SliceReader,
};
pub mod collections {
pub use super::kv_map::*;
pub use winter_utils::collections::*;
}
// UTILITY FUNCTIONS
// ================================================================================================
/// Converts a [Word] into hex.
pub fn word_to_hex(w: &Word) -> Result<String, fmt::Error> {
let mut s = String::new();
for byte in w.iter().flat_map(|e| e.to_bytes()) {
write!(s, "{byte:02x}")?;
}
Ok(s)
}