Longfellow ZK 0290cb32
Loading...
Searching...
No Matches
merkle_tree.h
1// Copyright 2025 Google LLC.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#ifndef PRIVACY_PROOFS_ZK_LIB_MERKLE_MERKLE_TREE_H_
16#define PRIVACY_PROOFS_ZK_LIB_MERKLE_MERKLE_TREE_H_
17
18#include <stddef.h>
19#include <stdint.h>
20
21#include <cstring>
22#include <vector>
23
24#include "util/crypto.h"
25#include "util/panic.h"
26
27namespace proofs {
28
29// This package computes and verifies Merkle Tree inclusion claims.
30// The folklore Merkle tree algorithm has been implemented, with the following
31// constraints:
32// 1. A Merkle tree proof must reveal at least one leaf. We do not define
33// empty proofs.
34// 2. The list of leaves must be a set, i.e., with no duplicates. All usage
35// within this library satisfies this requirement because the FS methods
36// that produce the challenge set of indices includes no duplicates.
37// 3. The generated proof of inclusion for a set of leaves is compressed.
38// That is, if a node in the Merkle tree can be deduced, it is not included
39// in the proof. This makes the proof shorter, but the proof length varies
40// depending on the included leaves.
41
42// A digest of a Merkle tree.
43struct Digest {
44 static constexpr size_t kLength = kSHA256DigestSize;
45 uint8_t data[kLength];
46
47 bool operator==(const Digest& y) const {
48 return memcmp(data, y.data, kLength) == 0;
49 }
50
51 static Digest hash2(const Digest& L, const Digest& R) {
52 SHA256 sha;
53 sha.Update(L.data, kLength);
54 sha.Update(R.data, kLength);
55 Digest output;
56 sha.DigestData(output.data);
57 return output;
58 }
59};
60
61// Return the length of the proof for N leaves.
62// Mimic the code in generate_proof() without actually
63// computing the proof
64inline size_t merkle_tree_len(size_t n) {
65 size_t r = 1;
66 size_t pos = (n - 1); // maximum possible value
67 for (pos += n; pos > 1; pos >>= 1) {
68 ++r;
69 }
70 return r;
71}
72
73// compute the set of all nodes on the path from the
74// root to any leaf in POS.
75inline std::vector<bool> compressed_merkle_proof_tree(size_t n,
76 const size_t pos[/*np*/],
77 size_t np) {
78 check(np > 0, "A Merkle proof with 0 leaves is not defined.");
79 std::vector<bool> tree(2 * n, false);
80
81 // leaves are in TREE
82 for (size_t ip = 0; ip < np; ++ip) {
83 check(pos[ip] < n, "Invalid position for leaf in Merkle tree");
84 check(tree[pos[ip] + n] == false,
85 "duplicate position in merkle tree requested");
86 tree[pos[ip] + n] = true;
87 }
88
89 // If a child of an inner node is in TREE, then the parent is in TREE.
90 for (size_t i = n; i-- > 1;) {
91 tree[i] = (tree[2 * i] || tree[2 * i + 1]);
92 }
93
94 // Assert that the root is in TREE.
95 check(tree[1], "tree[1]");
96
97 return tree;
98}
99
100class MerkleTree {
101 public:
102 explicit MerkleTree(size_t n) : n_(n), layers_(2 * n) {}
103
104 void set_leaf(size_t pos, const Digest& leaf) {
105 check(pos < n_, "Invalid position for leaf in Merkle tree");
106 layers_[pos + n_] = leaf;
107 }
108
109 Digest build_tree() {
110 for (size_t i = n_; i-- > 1;) {
111 layers_[i] = Digest::hash2(layers_[2 * i], layers_[2 * i + 1]);
112 }
113 return layers_[1];
114 }
115
116 // Compressed Merkle proofs over a set POS[NP] of leaves.
117 //
118 // We first compute the set TREE of all nodes that are on the path
119 // from the root to any leaf in POS. Then, for each inner node in
120 // TREE, we include in the proof the child that is not in TREE, if
121 // any. Note, this method requires pos to contain no duplicates.
122 size_t generate_compressed_proof(std::vector<Digest>& proof,
123 const size_t pos[/*np*/], size_t np) {
124 std::vector<bool> tree = compressed_merkle_proof_tree(n_, pos, np);
125
126 // For each TREE node, include in the proof the
127 // child that is not TREE, if any.
128 size_t sz = 0;
129 for (size_t i = n_; i-- > 1;) {
130 if (tree[i]) {
131 size_t child = 2 * i;
132 if (tree[child]) {
133 // try the other child
134 child = 2 * i + 1;
135 }
136 if (!tree[child]) {
137 proof.push_back(layers_[child]);
138 ++sz;
139 }
140 }
141 }
142 return sz;
143 }
144
145 size_t n_;
146 // layers_[n, 2 * n) stores the leaves (nodes at layer 0).
147 // layers_[n/2, n) stores nodes at layer 1.
148 // layers_[n/4, n/2) stores nodes at layer 2, etc.
149 // The root is at layers_[1] where layers_[0] is not used.
150 std::vector<Digest> layers_;
151};
152
153class MerkleTreeVerifier {
154 public:
155 explicit MerkleTreeVerifier(size_t n, const Digest& root)
156 : n_(n), root_(root) {}
157
158 // Verify a compressed Merkle proof.
159 // As mentioned above, this method assumes that pos contains no duplicates.
160 bool verify_compressed_proof(const Digest* proof, size_t proof_len,
161 const Digest leaves[/*np*/],
162 const size_t pos[/*np*/], size_t np) const {
163 // Reconstructed layers_, where only the DEFINED subset is
164 // defined.
165 std::vector<Digest> layers(2 * n_, Digest{});
166 std::vector<bool> defined(2 * n_, false);
167
168 /*scope for TREE */ {
169 std::vector<bool> tree = compressed_merkle_proof_tree(n_, pos, np);
170
171 // read the proof
172 size_t sz = 0;
173 for (size_t i = n_; i-- > 1;) {
174 if (tree[i]) {
175 size_t child = 2 * i;
176 if (tree[child]) {
177 // try the other child
178 child = 2 * i + 1;
179 }
180 if (!tree[child]) {
181 if (sz >= proof_len) {
182 return false;
183 }
184 layers[child] = proof[sz++];
185 defined[child] = true;
186 }
187 }
188 }
189 }
190
191 // set LAYERS at all leaves in POS
192 for (size_t ip = 0; ip < np; ++ip) {
193 size_t l = pos[ip] + n_;
194 layers[l] = leaves[ip];
195 defined[l] = true;
196 }
197
198 // Recompute as many inner nodes as we can
199 for (size_t i = n_; i-- > 1;) {
200 if (defined[2 * i] && defined[2 * i + 1]) {
201 layers[i] = Digest::hash2(layers[2 * i], layers[2 * i + 1]);
202 defined[i] = true;
203 }
204 }
205
206 return (defined[1] && (root_ == layers[1]));
207 }
208
209 private:
210 size_t n_;
211 Digest root_;
212};
213
214} // namespace proofs
215
216#endif // PRIVACY_PROOFS_ZK_LIB_MERKLE_MERKLE_TREE_H_
Definition crypto.h:40
Definition merkle_tree.h:43