UNPKG

8.88 kBMarkdownView Raw
1# merkle-patricia-tree
2
3[![NPM Package][trie-npm-badge]][trie-npm-link]
4[![GitHub Issues][trie-issues-badge]][trie-issues-link]
5[![Actions Status][trie-actions-badge]][trie-actions-link]
6[![Code Coverage][trie-coverage-badge]][trie-coverage-link]
7[![Discord][discord-badge]][discord-link]
8
9This is an implementation of the modified merkle patricia tree as specified in the [Ethereum Yellow Paper](http://gavwood.com/Paper.pdf):
10
11> The modified Merkle Patricia tree (trie) provides a persistent data structure to map between arbitrary-length binary data (byte arrays). It is defined in terms of a mutable data structure to map between 256-bit binary fragments and arbitrary-length binary data. The core of the trie, and its sole requirement in terms of the protocol specification is to provide a single 32-byte value that identifies a given set of key-value pairs.
12
13The only backing store supported is LevelDB through the `levelup` module.
14
15# INSTALL
16
17`npm install merkle-patricia-tree`
18
19# USAGE
20
21There are 3 variants of the tree implemented in this library, namely: `BaseTrie`, `CheckpointTrie` and `SecureTrie`. `CheckpointTrie` adds checkpointing functionality to the `BaseTrie` with the methods `checkpoint`, `commit` and `revert`. `SecureTrie` extends `CheckpointTrie` and is the most suitable variant for Ethereum applications. It stores values under the `keccak256` hash of their keys.
22
23By default, trie nodes are not deleted from the underlying DB to not corrupt older trie states (as of `v4.2.0`). If you are only interested in the latest state of a trie, you can switch to a delete behavior (e.g. if you want to save disk space) by using the `deleteFromDB` constructor option (see related release notes in the changelog for more details).
24
25## Initialization and Basic Usage
26
27```typescript
28import level from 'level'
29import { BaseTrie as Trie } from 'merkle-patricia-tree'
30
31const db = level('./testdb')
32const trie = new Trie(db)
33
34async function test() {
35 await trie.put(Buffer.from('test'), Buffer.from('one'))
36 const value = await trie.get(Buffer.from('test'))
37 console.log(value.toString()) // 'one'
38}
39
40test()
41```
42
43## Merkle Proofs
44
45The `createProof` and `verifyProof` functions allow you to verify that a certain value does or does not exist within a Merkle-Patricia trie with a given root.
46
47### Proof of existence
48
49The below code demonstrates how to construct and then verify a proof that proves that the key `test` that corresponds to the value `one` does exist in the given trie, so a proof of existence.
50
51```typescript
52const trie = new Trie()
53
54async function test() {
55 await trie.put(Buffer.from('test'), Buffer.from('one'))
56 const proof = await Trie.createProof(trie, Buffer.from('test'))
57 const value = await Trie.verifyProof(trie.root, Buffer.from('test'), proof)
58 console.log(value.toString()) // 'one'
59}
60
61test()
62```
63
64### Proof of non-existence
65
66The below code demonstrates how to construct and then verify a proof that proves that the key `test3` does not exist in the given trie, so a proof of non-existence.
67
68```typescript
69const trie = new Trie()
70
71async function test() {
72 await trie.put(Buffer.from('test'), Buffer.from('one'))
73 await trie.put(Buffer.from('test2'), Buffer.from('two'))
74 const proof = await Trie.createProof(trie, Buffer.from('test3'))
75 const value = await Trie.verifyProof(trie.root, Buffer.from('test3'), proof)
76 console.log(value.toString()) // null
77}
78
79test()
80```
81
82### Invalid proofs
83
84Note, if `verifyProof` detects an invalid proof, it throws an error. While contrived, the below example demonstrates the error condition that would result if a prover tampers with the data in a merkle proof.
85
86```typescript
87const trie = new Trie()
88
89async function test() {
90 await trie.put(Buffer.from('test'), Buffer.from('one'))
91 await trie.put(Buffer.from('test2'), Buffer.from('two'))
92 const proof = await Trie.createProof(trie, Buffer.from('test2'))
93 proof[1].reverse()
94 try {
95 const value = await Trie.verifyProof(trie.root, Buffer.from('test2'), proof)
96 console.log(value.toString()) // results in error
97 } catch (err) {
98 console.log(err) // Missing node in DB
99 }
100}
101
102test()
103```
104
105## Read stream on Geth DB
106
107```typescript
108import level from 'level'
109import { SecureTrie as Trie } from 'merkle-patricia-tree'
110
111const db = level('YOUR_PATH_TO_THE_GETH_CHAIN_DB')
112// Set stateRoot to block #222
113const stateRoot = '0xd7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544'
114// Convert the state root to a Buffer (strip the 0x prefix)
115const stateRootBuffer = Buffer.from(stateRoot.slice(2), 'hex')
116// Initialize trie
117const trie = new Trie(db, stateRootBuffer)
118
119trie
120 .createReadStream()
121 .on('data', console.log)
122 .on('end', () => {
123 console.log('End.')
124 })
125```
126
127## Read Account State including Storage from Geth DB
128
129```typescript
130import level from 'level'
131import rlp from 'rlp'
132import { BN, bufferToHex } from 'ethereumjs-util'
133import Account from 'ethereumjs-account'
134import { SecureTrie as Trie } from 'merkle-patricia-tree'
135
136const stateRoot = 'STATE_ROOT_OF_A_BLOCK'
137
138const db = level('YOUR_PATH_TO_THE_GETH_CHAINDATA_FOLDER')
139const trie = new Trie(db, stateRoot)
140
141const address = 'AN_ETHEREUM_ACCOUNT_ADDRESS'
142
143async function test() {
144 const data = await trie.get(address)
145 const acc = new Account(data)
146
147 console.log('-------State-------')
148 console.log(`nonce: ${new BN(acc.nonce)}`)
149 console.log(`balance in wei: ${new BN(acc.balance)}`)
150 console.log(`storageRoot: ${bufferToHex(acc.stateRoot)}`)
151 console.log(`codeHash: ${bufferToHex(acc.codeHash)}`)
152
153 let storageTrie = trie.copy()
154 storageTrie.root = acc.stateRoot
155
156 console.log('------Storage------')
157 const stream = storageTrie.createReadStream()
158 stream
159 .on('data', (data) => {
160 console.log(`key: ${bufferToHex(data.key)}`)
161 console.log(`Value: ${bufferToHex(rlp.decode(data.value))}`)
162 })
163 .on('end', () => {
164 console.log('Finished reading storage.')
165 })
166}
167
168test()
169```
170
171Additional examples with detailed explanations are available [here](https://github.com/gabrocheleau/merkle-patricia-trees-examples).
172
173# API
174
175[Documentation](./docs/README.md)
176
177# TESTING
178
179`npm test`
180
181# BENCHMARKS
182
183There are two simple **benchmarks** in the `benchmarks` folder:
184
185- `random.ts` runs random `PUT` operations on the tree.
186- `checkpointing.ts` runs checkpoints and commits between `PUT` operations.
187
188A third benchmark using mainnet data to simulate real load is also under consideration.
189
190Benchmarks can be run with:
191
192```shell
193npm run benchmarks
194```
195
196To run a **profiler** on the `random.ts` benchmark and generate a flamegraph with [0x](https://github.com/davidmarkclements/0x) you can use:
197
198```shell
199npm run profiling
200```
201
2020x processes the stacks and generates a profile folder (`<pid>.0x`) containing [`flamegraph.html`](https://github.com/davidmarkclements/0x/blob/master/docs/ui.md).
203
204# REFERENCES
205
206- Wiki
207 - [Ethereum Trie Specification](https://github.com/ethereum/wiki/wiki/Patricia-Tree)
208- Blog posts
209 - [Ethereum's Merkle Patricia Trees - An Interactive JavaScript Tutorial](https://rockwaterweb.com/ethereum-merkle-patricia-trees-javascript-tutorial/)
210 - [Merkling in Ethereum](https://blog.ethereum.org/2015/11/15/merkling-in-ethereum/)
211 - [Understanding the Ethereum Trie](https://easythereentropy.wordpress.com/2014/06/04/understanding-the-ethereum-trie/). Worth a read, but the Python libraries are outdated.
212- Videos
213 - [Trie and Patricia Trie Overview](https://www.youtube.com/watch?v=jXAHLqQthKw&t=26s)
214
215# EthereumJS
216
217See our organizational [documentation](https://ethereumjs.readthedocs.io) for an introduction to `EthereumJS` as well as information on current standards and best practices.
218
219If you want to join for work or do improvements on the libraries have a look at our [contribution guidelines](https://ethereumjs.readthedocs.io/en/latest/contributing.html).
220
221# LICENSE
222
223[MPL-2.0](<https://tldrlegal.com/license/mozilla-public-license-2.0-(mpl-2)>)
224
225[discord-badge]: https://img.shields.io/static/v1?logo=discord&label=discord&message=Join&color=blue
226[discord-link]: https://discord.gg/TNwARpR
227[trie-npm-badge]: https://img.shields.io/npm/v/merkle-patricia-tree.svg
228[trie-npm-link]: https://www.npmjs.com/package/merkle-patricia-tree
229[trie-issues-badge]: https://img.shields.io/github/issues/ethereumjs/ethereumjs-monorepo/package:%20trie?label=issues
230[trie-issues-link]: https://github.com/ethereumjs/ethereumjs-monorepo/issues?q=is%3Aopen+is%3Aissue+label%3A"package%3A+trie"
231[trie-actions-badge]: https://github.com/ethereumjs/ethereumjs-monorepo/workflows/Trie/badge.svg
232[trie-actions-link]: https://github.com/ethereumjs/ethereumjs-monorepo/actions?query=workflow%3A%22Trie%22
233[trie-coverage-badge]: https://codecov.io/gh/ethereumjs/ethereumjs-monorepo/branch/master/graph/badge.svg?flag=trie
234[trie-coverage-link]: https://codecov.io/gh/ethereumjs/ethereumjs-monorepo/tree/master/packages/trie