Skip to content

Commit 7288854

Browse files
ernestognwAmxx
andauthored
Add SimpleMerkleTree docs (#42)
Co-authored-by: Hadrien Croubois <[email protected]>
1 parent 952fd9f commit 7288854

File tree

1 file changed

+100
-33
lines changed

1 file changed

+100
-33
lines changed

README.md

Lines changed: 100 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
# `@openzeppelin/merkle-tree`
22

3-
43
**A JavaScript library to generate merkle trees and merkle proofs.**
54

65
Well suited for airdrops and similar mechanisms in combination with OpenZeppelin Contracts [`MerkleProof`] utilities.
@@ -120,15 +119,39 @@ This library works on "standard" merkle trees designed for Ethereum smart contra
120119

121120
[second preimage attacks]: https://flawed.net.nz/2018/02/21/attacking-merkle-trees-with-a-second-preimage-attack/
122121

122+
## Simple Merkle Trees
123+
124+
The library also supports "simple" merkle trees, which are a simplified version of the standard ones. They are designed to be more flexible and accept arbitrary `bytes32` data as leaves. It keeps the same tree shape and internal pair hashing algorithm.
125+
126+
As opposed to standard trees, leaves are not double-hashed. Instead they are ABI encoded to `bytes32` and hashed in pairs inside the tree. This is useful to override the leaf hashing algorithm and use a different one prior to building the tree.
127+
128+
Users of tooling that produced trees without double leaf hashing can use this feature to build a representation of the tree in JavaScript. We recommend this approach exclusively for trees that are already built on-chain. Otherwise the standard tree may be a better fit.
129+
130+
```typescript
131+
import { SimpleMerkleTree } from '@openzeppelin/merkle-tree';
132+
import keccak256 from '@ethersproject/keccak256';
133+
134+
// (1)
135+
const tree = SimpleMerkleTree.of([keccak256('Value 1'), keccak256('Value 2')]);
136+
137+
// (2)
138+
// ...
139+
```
140+
141+
1. Use a custom leaf hashing algorithm to produce `bytes32` values for the tree.
142+
2. The Simple Merkle Tree share the same API as Standard Merkle Tree.
143+
144+
## Advanced usage
145+
123146
### Leaf Hash
124147

125-
From the last three points we get that the hash of a leaf in the tree with value `[addr, amount]` can be computed in Solidity as follows:
148+
The Standard Merkle Tree uses an opinionated double leaf hashing algorithm. For example, a leaf in the tree with value `[addr, amount]` can be computed in Solidity as follows:
126149

127150
```solidity
128151
bytes32 leaf = keccak256(bytes.concat(keccak256(abi.encode(addr, amount))));
129152
```
130153

131-
This is an opinionated design that we believe will offer the best out of the box experience for most users. We may introduce options for customization in the future based on user requests.
154+
This is an opinionated design that we believe will offer the best out of the box experience for most users. However, there are advanced use case where a different leaf hashing algorithm may be needed. For those, the `SimpleMerkleTree` can be used to build a tree with custom leaf hashing.
132155

133156
### Leaf ordering
134157

@@ -144,73 +167,117 @@ However, some trees are constructed iteratively from unsorted data, causing the
144167

145168
## API & Examples
146169

170+
> **Note**
171+
> Consider reading the array of elements from a CSV file for easy interoperability with spreadsheets or other data processing pipelines.
172+
173+
> **Note**
174+
> By default, leaves are sorted according to their hash. This is done so that multiproof generated by the library can more easily be verified onchain. This can be disabled using the optional third argument. See the [Leaf ordering](#leaf-ordering) section for more details.
175+
147176
### `StandardMerkleTree`
148177

149178
```typescript
150179
import { StandardMerkleTree } from "@openzeppelin/merkle-tree";
151180
```
152181

153-
### `StandardMerkleTree.of`
182+
#### `StandardMerkleTree.of`
154183

155184
```typescript
156185
const tree = StandardMerkleTree.of([[alice, '100'], [bob, '200']], ['address', 'uint'], options);
157186
```
158187

159188
Creates a standard merkle tree out of an array of the elements in the tree, along with their types for ABI encoding. For documentation on the syntax of the types, including how to encode structs, refer to the documentation for Ethers.js's [`AbiCoder`](https://docs.ethers.org/v5/api/utils/abi/coder/#AbiCoder-encode).
160189

161-
> **Note**
162-
> Consider reading the array of elements from a CSV file for easy interoperability with spreadsheets or other data processing pipelines.
163-
164-
> **Note**
165-
> By default, leaves are sorted according to their hash. This is done so that multiproof generated by the library can more easily be verified onchain. This can be disabled using the optional third argument. See the [Leaf ordering](#leaf-ordering) section for more details.
190+
#### `StandardMerkleTree.load`
166191

167-
#### Options
192+
```typescript
193+
StandardMerkleTree.load(JSON.parse(fs.readFileSync('tree.json', 'utf8')));
194+
```
168195

169-
| Option | Description | Default |
170-
| ------------ | ----------------------------------------------------------------------------------- | ------- |
171-
| `sortLeaves` | Enable or disable sorted leaves. Sorting is strongly recommended for multiproofs. | `true` |
196+
Loads the tree from a description previously returned by `tree.dump`.
172197

173-
### `StandardMerkleTree.verify`
198+
#### `StandardMerkleTree.verify`
174199

175200
```typescript
176201
const verified = StandardMerkleTree.verify(root, ['address', 'uint'], [alice, '100'], proof);
177202
```
178203

179204
Returns a boolean that is `true` when the proof verifies that the value is contained in the tree given only the proof, merkle root, and encoding.
180205

181-
### `StandardMerkleTree.verifyMultiProof`
206+
#### `StandardMerkleTree.verifyMultiProof`
182207

183208
```typescript
184209
const isValid = StandardMerkleTree.verifyMultiProof(root, leafEncoding, multiproof);
185210
```
186211

187212
Returns a boolean that is `true` when the multiproof verifies that all the values are contained in the tree given only the multiproof, merkle root, and leaf encoding.
188213

189-
### `StandardMerkleTree.load`
214+
### `SimpleMerkleTree`
190215

191216
```typescript
192-
StandardMerkleTree.load(JSON.parse(fs.readFileSync('tree.json', 'utf8')));
217+
import { SimpleMerkleTree } from '@openzeppelin/merkle-tree';
193218
```
194219

195-
Loads the tree from a description previously returned by `tree.dump`.
220+
#### `SimpleMerkleTree.of`
221+
222+
```typescript
223+
const tree = SimpleMerkleTree.of([hashFn('Value 1'), hashFn('Value 2')]);
224+
```
225+
226+
The `hashFn` is a custom cryptographic leaf hashing algorithm that returns `bytes32` values. The tree will be built using these values as leaves. The function should be different to the internal hashing pair algorithm used by the tree.
227+
228+
#### `SimpleMerkleTree.load`
229+
230+
```typescript
231+
SimpleMerkleTree.load(JSON.parse(fs.readFileSync('tree.json', 'utf8')));
232+
```
233+
234+
Same as `StandardMerkleTree.load`.
235+
236+
#### `SimpleMerkleTree.verify`
237+
238+
```typescript
239+
const verified = SimpleMerkleTree.verify(root, hashFn('Value 1'), proof);
240+
```
241+
242+
Same as `StandardMerkleTree.verify`, but using raw `bytes32` values.
243+
244+
#### `SimpleMerkleTree.verifyMultiProof`
245+
246+
```typescript
247+
const isValid = SimpleMerkleTree.verifyMultiProof(root, multiproof);
248+
```
249+
250+
Same as `StandardMerkleTree.verifyMultiProof`.
251+
252+
### Shared API
253+
254+
Both `StandardMerkleTree` and `SimpleMerkleTree` share the same API, defined below.
255+
256+
#### Options
257+
258+
Allows to configure the behavior of the tree. The following options are available:
259+
260+
| Option | Description | Default |
261+
| ------------ | --------------------------------------------------------------------------------- | ------- |
262+
| `sortLeaves` | Enable or disable sorted leaves. Sorting is strongly recommended for multiproofs. | `true` |
196263

197-
### `tree.root`
264+
#### `tree.root`
198265

199266
```typescript
200267
console.log(tree.root);
201268
```
202269

203270
The root of the tree is a commitment on the values of the tree. It can be published (e.g., in a smart contract) to later prove that its values are part of the tree.
204271

205-
### `tree.dump`
272+
#### `tree.dump`
206273

207274
```typescript
208275
fs.writeFileSync('tree.json', JSON.stringify(tree.dump()));
209276
```
210277

211278
Returns a description of the merkle tree for distribution. It contains all the necessary information to reproduce the tree, find the relevant leaves, and generate proofs. You should distribute this to users in a web application or command line interface so they can generate proofs for their leaves of interest.
212279

213-
### `tree.getProof`
280+
#### `tree.getProof`
214281

215282
```typescript
216283
const proof = tree.getProof(i);
@@ -221,10 +288,10 @@ Returns a proof for the `i`th value in the tree. Indices refer to the position o
221288
Also accepts a value instead of an index, but this will be less efficient. It will fail if the value is not found in the tree.
222289

223290
```typescript
224-
const proof = tree.getProof([alice, '100']);
291+
const proof = tree.getProof(value); // e.g. [alice, '100']
225292
```
226293

227-
### `tree.getMultiProof`
294+
#### `tree.getMultiProof`
228295

229296
```typescript
230297
const { proof, proofFlags, leaves } = tree.getMultiProof([i0, i1, ...]);
@@ -237,27 +304,27 @@ The multiproof returned contains an array with the leaves that are being proven.
237304
Also accepts values instead of indices, but this will be less efficient. It will fail if any of the values is not found in the tree.
238305

239306
```typescript
240-
const proof = tree.getProof([[alice, '100'], [bob, '200']]);
307+
const proof = tree.getMultiProof([value1, value2]); // e.g. [[alice, '100'], [bob, '200']]
241308
```
242309

243-
### `tree.verify`
310+
#### `tree.verify`
244311

245312
```typescript
246313
tree.verify(i, proof);
247-
tree.verify([alice, '100'], proof);
314+
tree.verify(value, proof); // e.g. [alice, '100']
248315
```
249316

250317
Returns a boolean that is `true` when the proof verifies that the value is contained in the tree.
251318

252-
### `tree.verifyMultiProof`
319+
#### `tree.verifyMultiProof`
253320

254321
```typescript
255322
tree.verifyMultiProof({ proof, proofFlags, leaves });
256323
```
257324

258325
Returns a boolean that is `true` when the multi-proof verifies that the values are contained in the tree.
259326

260-
### `tree.entries`
327+
#### `tree.entries`
261328

262329
```typescript
263330
for (const [i, v] of tree.entries()) {
@@ -268,23 +335,23 @@ for (const [i, v] of tree.entries()) {
268335

269336
Lists the values in the tree along with their indices, which can be used to obtain proofs.
270337

271-
### `tree.render`
338+
#### `tree.render`
272339

273340
```typescript
274341
console.log(tree.render());
275342
```
276343

277344
Returns a visual representation of the tree that can be useful for debugging.
278345

279-
### `tree.leafHash`
346+
#### `tree.leafHash`
280347

281348
```typescript
282-
const leaf = tree.leafHash([alice, '100']);
349+
const leaf = tree.leafHash(value); // e.g. [alice, '100']
283350
```
284351

285-
Returns the leaf hash of the value, as defined in [Standard Merkle Trees](#standard-merkle-trees).
352+
Returns the leaf hash of the value, defined per tree type.
286353

287-
Corresponds to the following expression in Solidity:
354+
In case of the `StandardMerkleTree`, it corresponds to the following expression in Solidity:
288355

289356
```solidity
290357
bytes32 leaf = keccak256(bytes.concat(keccak256(abi.encode(alice, 100))));

0 commit comments

Comments
 (0)