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 |
|
9 | This 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 |
|
13 | The only backing store supported is LevelDB through the `levelup` module.
|
14 |
|
15 | # INSTALL
|
16 |
|
17 | `npm install merkle-patricia-tree`
|
18 |
|
19 | # USAGE
|
20 |
|
21 | There 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 |
|
23 | By 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
|
28 | import level from 'level'
|
29 | import { BaseTrie as Trie } from 'merkle-patricia-tree'
|
30 |
|
31 | const db = level('./testdb')
|
32 | const trie = new Trie(db)
|
33 |
|
34 | async 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 |
|
40 | test()
|
41 | ```
|
42 |
|
43 | ## Merkle Proofs
|
44 |
|
45 | The `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 |
|
49 | The 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
|
52 | const trie = new Trie()
|
53 |
|
54 | async 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 |
|
61 | test()
|
62 | ```
|
63 |
|
64 | ### Proof of non-existence
|
65 |
|
66 | The 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
|
69 | const trie = new Trie()
|
70 |
|
71 | async 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 |
|
79 | test()
|
80 | ```
|
81 |
|
82 | ### Invalid proofs
|
83 |
|
84 | Note, 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
|
87 | const trie = new Trie()
|
88 |
|
89 | async 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 |
|
102 | test()
|
103 | ```
|
104 |
|
105 | ## Read stream on Geth DB
|
106 |
|
107 | ```typescript
|
108 | import level from 'level'
|
109 | import { SecureTrie as Trie } from 'merkle-patricia-tree'
|
110 |
|
111 | const db = level('YOUR_PATH_TO_THE_GETH_CHAIN_DB')
|
112 | // Set stateRoot to block #222
|
113 | const stateRoot = '0xd7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544'
|
114 | // Convert the state root to a Buffer (strip the 0x prefix)
|
115 | const stateRootBuffer = Buffer.from(stateRoot.slice(2), 'hex')
|
116 | // Initialize trie
|
117 | const trie = new Trie(db, stateRootBuffer)
|
118 |
|
119 | trie
|
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
|
130 | import level from 'level'
|
131 | import rlp from 'rlp'
|
132 | import { BN, bufferToHex } from 'ethereumjs-util'
|
133 | import Account from 'ethereumjs-account'
|
134 | import { SecureTrie as Trie } from 'merkle-patricia-tree'
|
135 |
|
136 | const stateRoot = 'STATE_ROOT_OF_A_BLOCK'
|
137 |
|
138 | const db = level('YOUR_PATH_TO_THE_GETH_CHAINDATA_FOLDER')
|
139 | const trie = new Trie(db, stateRoot)
|
140 |
|
141 | const address = 'AN_ETHEREUM_ACCOUNT_ADDRESS'
|
142 |
|
143 | async 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 |
|
168 | test()
|
169 | ```
|
170 |
|
171 | Additional 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 |
|
183 | There 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 |
|
188 | A third benchmark using mainnet data to simulate real load is also under consideration.
|
189 |
|
190 | Benchmarks can be run with:
|
191 |
|
192 | ```shell
|
193 | npm run benchmarks
|
194 | ```
|
195 |
|
196 | To 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
|
199 | npm run profiling
|
200 | ```
|
201 |
|
202 | 0x 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 |
|
217 | See our organizational [documentation](https://ethereumjs.readthedocs.io) for an introduction to `EthereumJS` as well as information on current standards and best practices.
|
218 |
|
219 | If 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
|