From c3155665c19b5106991cd708137d7d8272e5d13f Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi <1591639+s1na@users.noreply.github.com> Date: Tue, 22 Jan 2019 14:17:12 +0100 Subject: [PATCH] Extract db-related methods from baseTrie (#74) * Mv raw methods to DB class * Add ScratchDB which CheckpointTrie uses * Rename checkpoint-trie to checkpointTrie * Add comments to scratch * Fix linting errors * Regenerate docs * Rm raw methods and their tests * Rename DB._db to DB._leveldb * Make createScratchReadStream private * Fix linting error and jsdoc comments * Regenerate docs * Add tests for db, scratch and more cases for checkpoint * Fix checkpoint copy, add tests for checkpoint and secure copy Signed-off-by: Sina Mahmoodi --- docs/index.md | 338 +++++++++++------- src/baseTrie.js | 104 +----- src/{checkpoint-trie.js => checkpointTrie.js} | 88 ++--- src/db.js | 83 +++++ src/index.js | 2 +- src/scratch.js | 42 +++ src/secure.js | 5 +- test/checkpoint.js | 128 +++++++ test/db.js | 55 +++ test/index.js | 68 ---- test/rawOPs.js | 71 ---- test/scratch.js | 57 +++ test/secure.js | 28 +- test/streams.js | 2 +- 14 files changed, 654 insertions(+), 417 deletions(-) rename src/{checkpoint-trie.js => checkpointTrie.js} (70%) create mode 100644 src/db.js create mode 100644 src/scratch.js create mode 100644 test/checkpoint.js create mode 100644 test/db.js delete mode 100644 test/rawOPs.js create mode 100644 test/scratch.js diff --git a/docs/index.md b/docs/index.md index 1ef081a..0f8e75d 100644 --- a/docs/index.md +++ b/docs/index.md @@ -2,7 +2,7 @@ ## SecureTrie -[src/secure.js:12-43][1] +[src/secure.js:12-44][1] **Extends Trie** @@ -12,100 +12,68 @@ It has the same methods and constructor as `Trie`. ## Trie -[src/baseTrie.js:23-779][2] +[src/baseTrie.js:25-701][2] -Use `require('merkel-patricia-tree')` for the base interface. In Ethereum applications stick with the Secure Trie Overlay `require('merkel-patricia-tree/secure')`. The API for the raw and the secure interface are about the same +Use `require('merkel-patricia-tree')` for the base interface. In Ethereum applications +stick with the Secure Trie Overlay `require('merkel-patricia-tree/secure')`. +The API for the raw and the secure interface are about the same ### Parameters -- `db` **[Object][3]?** An instance of [levelup][4] or a compatible API. If the db is `null` or left undefined, then the trie will be stored in memory via [memdown][5] -- `root` **([Buffer][6] \| [String][7])?** A hex `String` or `Buffer` for the root of a previously stored trie +- `db` **[Object][3]?** An instance of `DB`. + If the db is `null` or left undefined, then the trie will be stored in memory via [memdown][4] +- `root` **([Buffer][5] \| [String][6])?** A hex `String` or `Buffer` for the root of a previously stored trie ### Properties -- `root` **[Buffer][6]** The current root of the `trie` -- `isCheckpoint` **[Boolean][8]** determines if you are saving to a checkpoint or directly to the db -- `EMPTY_TRIE_ROOT` **[Buffer][6]** the Root for an empty trie +- `root` **[Buffer][5]** The current root of the `trie` +- `EMPTY_TRIE_ROOT` **[Buffer][5]** the Root for an empty trie ### get -[src/baseTrie.js:63-75][9] +[src/baseTrie.js:59-71][7] Gets a value given a `key` #### Parameters -- `key` **([Buffer][6] \| [String][7])** the key to search for -- `cb` **[Function][10]** A callback `Function` which is given the arguments `err` - for errors that may have occured and `value` - the found value in a `Buffer` or if no value was found `null` +- `key` **([Buffer][5] \| [String][6])** the key to search for +- `cb` **[Function][8]** A callback `Function` which is given the arguments `err` - for errors that may have occured and `value` - the found value in a `Buffer` or if no value was found `null` ### put -[src/baseTrie.js:85-109][11] +[src/baseTrie.js:81-105][9] Stores a given `value` at the given `key` #### Parameters -- `key` **([Buffer][6] \| [String][7])** -- `Value` **([Buffer][6] \| [String][7])** -- `cb` **[Function][10]** A callback `Function` which is given the argument `err` - for errors that may have occured +- `key` **([Buffer][5] \| [String][6])** +- `Value` **([Buffer][5] \| [String][6])** +- `cb` **[Function][8]** A callback `Function` which is given the argument `err` - for errors that may have occured ### del -[src/baseTrie.js:118-134][12] +[src/baseTrie.js:114-130][10] deletes a value given a `key` #### Parameters -- `key` **([Buffer][6] \| [String][7])** -- `callback` **[Function][10]** the callback `Function` - -### getRaw - -[src/baseTrie.js:143-160][13] - -Retrieves a raw value in the underlying db - -#### Parameters - -- `key` **[Buffer][6]** -- `callback` **[Function][10]** A callback `Function`, which is given the arguments `err` - for errors that may have occured and `value` - the found value in a `Buffer` or if no value was found `null`. - -### putRaw - -[src/baseTrie.js:190-199][14] - -Writes a value directly to the underlining db - -#### Parameters - -- `key` **([Buffer][6] \| [String][7])** The key as a `Buffer` or `String` -- `value` **[Buffer][6]** The value to be stored -- `callback` **[Function][10]** A callback `Function`, which is given the argument `err` - for errors that may have occured - -### delRaw - -[src/baseTrie.js:208-216][15] - -Removes a raw value in the underlying db - -#### Parameters - -- `key` **([Buffer][6] \| [String][7])** -- `callback` **[Function][10]** A callback `Function`, which is given the argument `err` - for errors that may have occured +- `key` **([Buffer][5] \| [String][6])** +- `callback` **[Function][8]** the callback `Function` ### findPath -[src/baseTrie.js:250-296][16] +[src/baseTrie.js:171-217][11] Tries to find a path to the node for the given key It returns a `stack` of nodes to the closet node #### Parameters -- `null-null` **([String][7] \| [Buffer][6])** key - the search key -- `null-null` **[Function][10]** cb - the callback function. Its is given the following +- `null-null` **([String][6] \| [Buffer][5])** key - the search key +- `null-null` **[Function][8]** cb - the callback function. Its is given the following arguments- err - any errors encontered - node - the last node found - keyRemainder - the remaining key nibbles not accounted for @@ -113,22 +81,22 @@ It returns a `stack` of nodes to the closet node ### createReadStream -[src/baseTrie.js:728-730][17] +[src/baseTrie.js:649-651][12] The `data` event is given an `Object` hat has two properties; the `key` and the `value`. Both should be Buffers. -Returns **[stream.Readable][18]** Returns a [stream][19] of the contents of the `trie` +Returns **[stream.Readable][13]** Returns a [stream][14] of the contents of the `trie` ### batch -[src/baseTrie.js:754-764][20] +[src/baseTrie.js:676-686][15] The given hash of operations (key additions or deletions) are executed on the DB #### Parameters -- `ops` **[Array][21]** -- `cb` **[Function][10]** +- `ops` **[Array][16]** +- `cb` **[Function][8]** #### Examples @@ -145,14 +113,14 @@ trie.batch(ops) ### checkRoot -[src/baseTrie.js:773-778][22] +[src/baseTrie.js:695-700][17] Checks if a given root exists #### Parameters -- `root` **[Buffer][6]** -- `cb` **[Function][10]** +- `root` **[Buffer][5]** +- `cb` **[Function][8]** ## Merkle Proof @@ -161,28 +129,28 @@ Static functions for creating/verifying a merkle proof. ### prove -[src/proof.js:12-29][23] +[src/proof.js:12-29][18] Returns a merkle proof for a given key #### Parameters -- `trie` **[Trie][24]** -- `key` **[String][7]** -- `cb` **[Function][10]** A callback `Function` (arguments {Error} `err`, {Array.} `proof`) +- `trie` **[Trie][19]** +- `key` **[String][6]** +- `cb` **[Function][8]** A callback `Function` (arguments {Error} `err`, {Array.} `proof`) ### verifyProof -[src/proof.js:39-100][25] +[src/proof.js:39-100][20] Verifies a merkle proof for a given key #### Parameters -- `rootHash` **[Buffer][6]** -- `key` **[String][7]** -- `proof` **[Array][21]<TrieNode>** -- `cb` **[Function][10]** A callback `Function` (arguments {Error} `err`, {String} `val`) +- `rootHash` **[Buffer][5]** +- `key` **[String][6]** +- `proof` **[Array][16]<TrieNode>** +- `cb` **[Function][8]** A callback `Function` (arguments {Error} `err`, {String} `val`) ## Internal Util Functions @@ -191,20 +159,20 @@ These are not exposed. ### addHexPrefix -[src/util/hex.js:7-22][26] +[src/util/hex.js:7-22][21] Prepends hex prefix to an array of nibbles. #### Parameters -- `Array` **[Array][21]** of nibbles +- `Array` **[Array][16]** of nibbles -Returns **[Array][21]** returns buffer of encoded data +Returns **[Array][16]** returns buffer of encoded data \* ### asyncFirstSeries -[src/util/async.js:38-54][27] +[src/util/async.js:38-54][22] Take a collection of async fns, call the cb on the first to return a truthy value. If all run without a truthy result, return undefined @@ -217,31 +185,138 @@ If all run without a truthy result, return undefined ### doKeysMatch -[src/util/nibbles.js:56-59][28] +[src/util/nibbles.js:56-59][23] Compare two nibble array keys. #### Parameters -- `keyA` **[Array][21]** -- `keyB` **[Array][21]** +- `keyA` **[Array][16]** +- `keyB` **[Array][16]** ## -[src/util/async.js:3-6][29] +[src/util/async.js:3-6][24] Take two or more functions and returns a function that will execute all of the given functions +## + +[src/db.js:3-3][25] + +DB is a thin wrapper around the underlying levelup db, +which validates inputs and sets encoding type. + +## + +[src/scratch.js:4-4][26] + +An in-memory wrap over `DB` with an upstream DB +which will be queried when a key is not found +in the in-memory scratch. This class is used to implement +checkpointing functionality in CheckpointTrie. + +## decodeNode + +[src/node/index.js:12-31][27] + +Returns node instance given encoded RLP value. + +### Parameters + +- `v` + +## constructor + +[src/db.js:15-17][28] + +Initialize a DB instance. If `leveldb` is not provided, DB +defaults to an [in-memory store][4]. + +### Parameters + +- `leveldb` **[Object][3]?** An abstract-leveldown compliant store + +## get + +[src/db.js:26-36][29] + +Retrieves a raw value from leveldb. + +### Parameters + +- `key` **[Buffer][5]** +- `cb` **[Function][8]** A callback `Function`, which is given the arguments + `err` - for errors that may have occured + and `value` - the found value in a `Buffer` or if no value was found `null`. + +## put + +[src/db.js:45-50][30] + +Writes a value directly to leveldb. + +### Parameters + +- `key` **[Buffer][5]** The key as a `Buffer` or `String` +- `val` +- `cb` **[Function][8]** A callback `Function`, which is given the argument + `err` - for errors that may have occured +- `value` **[Buffer][5]** The value to be stored + +## del + +[src/db.js:58-62][31] + +Removes a raw value in the underlying leveldb. + +### Parameters + +- `key` **[Buffer][5]** +- `cb` **[Function][8]** A callback `Function`, which is given the argument + `err` - for errors that may have occured + +## batch + +[src/db.js:70-74][32] + +Performs a batch operation on db. + +### Parameters + +- `opStack` **[Array][16]** A stack of levelup operations +- `cb` **[Function][8]** A callback `Function`, which is given the argument + `err` - for errors that may have occured + +## copy + +[src/db.js:80-82][33] + +Returns a copy of the DB instance, with a reference +to the **same** underlying leveldb instance. + +## get + +[src/scratch.js:22-35][34] + +Similar to `DB.get`, but first searches in-memory +scratch DB, if key not found, searches upstream DB. + +### Parameters + +- `key` +- `cb` + ## isCheckpoint -[src/checkpoint-trie.js:27-29][30] +[src/checkpointTrie.js:31-33][35] Is the trie during a checkpoint phase? ## put -[src/secure.js:30-37][31] +[src/secure.js:31-38][36] For a falsey value, use the original key to avoid double hashing the key. @@ -254,7 +329,7 @@ to avoid double hashing the key. ## checkpoint -[src/checkpoint-trie.js:38-46][32] +[src/checkpointTrie.js:42-50][37] Creates a checkpoint that can later be reverted to or committed. After this is called, no changes to the trie will be permanently saved @@ -263,111 +338,114 @@ mechanism and would directly write to db. ## commit -[src/checkpoint-trie.js:55-70][33] +[src/checkpointTrie.js:59-74][38] Commits a checkpoint to disk, if current checkpoint is not nested. If nested, only sets the parent checkpoint as current checkpoint. ### Parameters -- `cb` **[Function][10]** the callback +- `cb` **[Function][8]** the callback - Throws **any** If not during a checkpoint phase ## revert -[src/checkpoint-trie.js:78-92][34] +[src/checkpointTrie.js:83-97][39] Reverts the trie to the state it was at when `checkpoint` was first called. -If during a nested checkpoint, only sets parent as current checkpoint. +If during a nested checkpoint, sets root to most recent checkpoint, and sets +parent checkpoint as current. ### Parameters -- `cb` **[Function][10]** the callback +- `cb` **[Function][8]** the callback ## copy -[src/checkpoint-trie.js:99-104][35] +[src/checkpointTrie.js:105-114][40] Returns a copy of the underlying trie with the interface -of CheckpointTrie. +of CheckpointTrie. If during a checkpoint, the copy will +contain the checkpointing metadata (incl. reference to the same scratch). + +[1]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/a4c7f5423da6a9d23ff0aa886c2fd560dac05b7d/src/secure.js#L12-L44 "Source code on GitHub" -## createScratchReadStream +[2]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/a4c7f5423da6a9d23ff0aa886c2fd560dac05b7d/src/baseTrie.js#L25-L701 "Source code on GitHub" -[src/checkpoint-trie.js:111-118][36] +[3]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object -Returns a `ScratchReadStream` based on the state updates -since checkpoint. +[4]: https://github.com/Level/memdown -[1]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/4194fd7855a3b9bd85742ea3c20ebfc405182742/src/secure.js#L12-L43 "Source code on GitHub" +[5]: https://nodejs.org/api/buffer.html -[2]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/4194fd7855a3b9bd85742ea3c20ebfc405182742/src/baseTrie.js#L23-L779 "Source code on GitHub" +[6]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String -[3]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object +[7]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/a4c7f5423da6a9d23ff0aa886c2fd560dac05b7d/src/baseTrie.js#L59-L71 "Source code on GitHub" -[4]: https://github.com/rvagg/node-levelup/ +[8]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function -[5]: https://github.com/rvagg/memdown +[9]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/a4c7f5423da6a9d23ff0aa886c2fd560dac05b7d/src/baseTrie.js#L81-L105 "Source code on GitHub" -[6]: https://nodejs.org/api/buffer.html +[10]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/a4c7f5423da6a9d23ff0aa886c2fd560dac05b7d/src/baseTrie.js#L114-L130 "Source code on GitHub" -[7]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String +[11]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/a4c7f5423da6a9d23ff0aa886c2fd560dac05b7d/src/baseTrie.js#L171-L217 "Source code on GitHub" -[8]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean +[12]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/a4c7f5423da6a9d23ff0aa886c2fd560dac05b7d/src/baseTrie.js#L649-L651 "Source code on GitHub" -[9]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/4194fd7855a3b9bd85742ea3c20ebfc405182742/src/baseTrie.js#L63-L75 "Source code on GitHub" +[13]: https://nodejs.org/api/stream.html#stream_class_stream_readable -[10]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function +[14]: https://nodejs.org/dist/latest-v5.x/docs/api/stream.html#stream_class_stream_readable -[11]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/4194fd7855a3b9bd85742ea3c20ebfc405182742/src/baseTrie.js#L85-L109 "Source code on GitHub" +[15]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/a4c7f5423da6a9d23ff0aa886c2fd560dac05b7d/src/baseTrie.js#L676-L686 "Source code on GitHub" -[12]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/4194fd7855a3b9bd85742ea3c20ebfc405182742/src/baseTrie.js#L118-L134 "Source code on GitHub" +[16]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array -[13]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/4194fd7855a3b9bd85742ea3c20ebfc405182742/src/baseTrie.js#L143-L160 "Source code on GitHub" +[17]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/a4c7f5423da6a9d23ff0aa886c2fd560dac05b7d/src/baseTrie.js#L695-L700 "Source code on GitHub" -[14]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/4194fd7855a3b9bd85742ea3c20ebfc405182742/src/baseTrie.js#L190-L199 "Source code on GitHub" +[18]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/a4c7f5423da6a9d23ff0aa886c2fd560dac05b7d/src/proof.js#L12-L29 "Source code on GitHub" -[15]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/4194fd7855a3b9bd85742ea3c20ebfc405182742/src/baseTrie.js#L208-L216 "Source code on GitHub" +[19]: #trie -[16]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/4194fd7855a3b9bd85742ea3c20ebfc405182742/src/baseTrie.js#L250-L296 "Source code on GitHub" +[20]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/a4c7f5423da6a9d23ff0aa886c2fd560dac05b7d/src/proof.js#L39-L100 "Source code on GitHub" -[17]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/4194fd7855a3b9bd85742ea3c20ebfc405182742/src/baseTrie.js#L728-L730 "Source code on GitHub" +[21]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/a4c7f5423da6a9d23ff0aa886c2fd560dac05b7d/src/util/hex.js#L7-L22 "Source code on GitHub" -[18]: https://nodejs.org/api/stream.html#stream_class_stream_readable +[22]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/a4c7f5423da6a9d23ff0aa886c2fd560dac05b7d/src/util/async.js#L38-L54 "Source code on GitHub" -[19]: https://nodejs.org/dist/latest-v5.x/docs/api/stream.html#stream_class_stream_readable +[23]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/a4c7f5423da6a9d23ff0aa886c2fd560dac05b7d/src/util/nibbles.js#L56-L59 "Source code on GitHub" -[20]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/4194fd7855a3b9bd85742ea3c20ebfc405182742/src/baseTrie.js#L754-L764 "Source code on GitHub" +[24]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/a4c7f5423da6a9d23ff0aa886c2fd560dac05b7d/src/util/async.js#L3-L6 "Source code on GitHub" -[21]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array +[25]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/a4c7f5423da6a9d23ff0aa886c2fd560dac05b7d/src/db.js#L3-L3 "Source code on GitHub" -[22]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/4194fd7855a3b9bd85742ea3c20ebfc405182742/src/baseTrie.js#L773-L778 "Source code on GitHub" +[26]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/a4c7f5423da6a9d23ff0aa886c2fd560dac05b7d/src/scratch.js#L4-L4 "Source code on GitHub" -[23]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/4194fd7855a3b9bd85742ea3c20ebfc405182742/src/proof.js#L12-L29 "Source code on GitHub" +[27]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/a4c7f5423da6a9d23ff0aa886c2fd560dac05b7d/src/node/index.js#L12-L31 "Source code on GitHub" -[24]: #trie +[28]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/a4c7f5423da6a9d23ff0aa886c2fd560dac05b7d/src/db.js#L15-L17 "Source code on GitHub" -[25]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/4194fd7855a3b9bd85742ea3c20ebfc405182742/src/proof.js#L39-L100 "Source code on GitHub" +[29]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/a4c7f5423da6a9d23ff0aa886c2fd560dac05b7d/src/db.js#L26-L36 "Source code on GitHub" -[26]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/4194fd7855a3b9bd85742ea3c20ebfc405182742/src/util/hex.js#L7-L22 "Source code on GitHub" +[30]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/a4c7f5423da6a9d23ff0aa886c2fd560dac05b7d/src/db.js#L45-L50 "Source code on GitHub" -[27]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/4194fd7855a3b9bd85742ea3c20ebfc405182742/src/util/async.js#L38-L54 "Source code on GitHub" +[31]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/a4c7f5423da6a9d23ff0aa886c2fd560dac05b7d/src/db.js#L58-L62 "Source code on GitHub" -[28]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/4194fd7855a3b9bd85742ea3c20ebfc405182742/src/util/nibbles.js#L56-L59 "Source code on GitHub" +[32]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/a4c7f5423da6a9d23ff0aa886c2fd560dac05b7d/src/db.js#L70-L74 "Source code on GitHub" -[29]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/4194fd7855a3b9bd85742ea3c20ebfc405182742/src/util/async.js#L3-L6 "Source code on GitHub" +[33]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/a4c7f5423da6a9d23ff0aa886c2fd560dac05b7d/src/db.js#L80-L82 "Source code on GitHub" -[30]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/4194fd7855a3b9bd85742ea3c20ebfc405182742/src/checkpoint-trie.js#L27-L29 "Source code on GitHub" +[34]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/a4c7f5423da6a9d23ff0aa886c2fd560dac05b7d/src/scratch.js#L22-L35 "Source code on GitHub" -[31]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/4194fd7855a3b9bd85742ea3c20ebfc405182742/src/secure.js#L30-L37 "Source code on GitHub" +[35]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/a4c7f5423da6a9d23ff0aa886c2fd560dac05b7d/src/checkpointTrie.js#L31-L33 "Source code on GitHub" -[32]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/4194fd7855a3b9bd85742ea3c20ebfc405182742/src/checkpoint-trie.js#L38-L46 "Source code on GitHub" +[36]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/a4c7f5423da6a9d23ff0aa886c2fd560dac05b7d/src/secure.js#L31-L38 "Source code on GitHub" -[33]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/4194fd7855a3b9bd85742ea3c20ebfc405182742/src/checkpoint-trie.js#L55-L70 "Source code on GitHub" +[37]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/a4c7f5423da6a9d23ff0aa886c2fd560dac05b7d/src/checkpointTrie.js#L42-L50 "Source code on GitHub" -[34]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/4194fd7855a3b9bd85742ea3c20ebfc405182742/src/checkpoint-trie.js#L78-L92 "Source code on GitHub" +[38]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/a4c7f5423da6a9d23ff0aa886c2fd560dac05b7d/src/checkpointTrie.js#L59-L74 "Source code on GitHub" -[35]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/4194fd7855a3b9bd85742ea3c20ebfc405182742/src/checkpoint-trie.js#L99-L104 "Source code on GitHub" +[39]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/a4c7f5423da6a9d23ff0aa886c2fd560dac05b7d/src/checkpointTrie.js#L83-L97 "Source code on GitHub" -[36]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/4194fd7855a3b9bd85742ea3c20ebfc405182742/src/checkpoint-trie.js#L111-L118 "Source code on GitHub" +[40]: https://git@github.com/:ethereumjs/merkle-patricia-tree/blob/a4c7f5423da6a9d23ff0aa886c2fd560dac05b7d/src/checkpointTrie.js#L105-L114 "Source code on GitHub" diff --git a/src/baseTrie.js b/src/baseTrie.js index d668e7f..b7a3796 100644 --- a/src/baseTrie.js +++ b/src/baseTrie.js @@ -1,23 +1,25 @@ const assert = require('assert') -const level = require('level-mem') const async = require('async') const rlp = require('rlp') const ethUtil = require('ethereumjs-util') const semaphore = require('semaphore') +const DB = require('./db') const TrieNode = require('./trieNode') const ReadStream = require('./readStream') const PrioritizedTaskExecutor = require('./prioritizedTaskExecutor') -const { callTogether, asyncFirstSeries } = require('./util/async') +const { callTogether } = require('./util/async') const { stringToNibbles, matchingNibbleLength, doKeysMatch } = require('./util/nibbles') /** - * Use `require('merkel-patricia-tree')` for the base interface. In Ethereum applications stick with the Secure Trie Overlay `require('merkel-patricia-tree/secure')`. The API for the raw and the secure interface are about the same + * Use `require('merkel-patricia-tree')` for the base interface. In Ethereum applications + * stick with the Secure Trie Overlay `require('merkel-patricia-tree/secure')`. + * The API for the raw and the secure interface are about the same * @class Trie * @public - * @param {Object} [db] An instance of [levelup](https://github.com/rvagg/node-levelup/) or a compatible API. If the db is `null` or left undefined, then the trie will be stored in memory via [memdown](https://github.com/rvagg/memdown) + * @param {Object} [db] An instance of `DB`. + * If the db is `null` or left undefined, then the trie will be stored in memory via [memdown](https://github.com/Level/memdown) * @param {Buffer|String} [root] A hex `String` or `Buffer` for the root of a previously stored trie * @prop {Buffer} root The current root of the `trie` - * @prop {Boolean} isCheckpoint determines if you are saving to a checkpoint or directly to the db * @prop {Buffer} EMPTY_TRIE_ROOT the Root for an empty trie */ module.exports = class Trie { @@ -26,11 +28,7 @@ module.exports = class Trie { this.EMPTY_TRIE_ROOT = ethUtil.SHA3_RLP this.sem = semaphore(1) - // setup dbs - this.db = db || level() - - this._getDBs = [this.db] - this._putDBs = [this.db] + this.db = db || new DB() Object.defineProperty(this, 'root', { set(value) { @@ -49,8 +47,6 @@ module.exports = class Trie { }) this.root = root - - this.putRaw = this._putRaw } /** @@ -133,38 +129,12 @@ module.exports = class Trie { }) } - /** - * Retrieves a raw value in the underlying db - * @method getRaw - * @memberof Trie - * @param {Buffer} key - * @param {Function} callback A callback `Function`, which is given the arguments `err` - for errors that may have occured and `value` - the found value in a `Buffer` or if no value was found `null`. - */ - getRaw (key, cb) { - key = ethUtil.toBuffer(key) - - function dbGet (db, cb2) { - db.get(key, { - keyEncoding: 'binary', - valueEncoding: 'binary' - }, (err, foundNode) => { - if (err || !foundNode) { - cb2(null, null) - } else { - cb2(null, foundNode) - } - }) - } - - asyncFirstSeries(this._getDBs, dbGet, cb) - } - // retrieves a node from dbs by hash _lookupNode (node, cb) { if (TrieNode.isRawNode(node)) { cb(new TrieNode(node)) } else { - this.getRaw(node, (err, value) => { + this.db.get(node, (err, value) => { if (err) { throw err } @@ -178,60 +148,11 @@ module.exports = class Trie { } } - /** - * Writes a value directly to the underlining db - * @method putRaw - * @memberof Trie - * @param {Buffer|String} key The key as a `Buffer` or `String` - * @param {Buffer} value The value to be stored - * @param {Function} callback A callback `Function`, which is given the argument `err` - for errors that may have occured - */ - // TODO: remove the proxy method when changing the caching - _putRaw (key, val, cb) { - function dbPut (db, cb2) { - db.put(key, val, { - keyEncoding: 'binary', - valueEncoding: 'binary' - }, cb2) - } - - async.each(this._putDBs, dbPut, cb) - } - - /** - * Removes a raw value in the underlying db - * @method delRaw - * @memberof Trie - * @param {Buffer|String} key - * @param {Function} callback A callback `Function`, which is given the argument `err` - for errors that may have occured - */ - delRaw (key, cb) { - function del (db, cb2) { - db.del(key, { - keyEncoding: 'binary' - }, cb2) - } - - async.each(this._putDBs, del, cb) - } - // writes a single node to dbs _putNode (node, cb) { const hash = node.hash() const serialized = node.serialize() - this._putRaw(hash, serialized, cb) - } - - // writes many nodes to db - _batchNodes (opStack, cb) { - function dbBatch (db, cb) { - db.batch(opStack, { - keyEncoding: 'binary', - valueEncoding: 'binary' - }, cb) - } - - async.each(this._putDBs, dbBatch, cb) + this.db.put(hash, serialized, cb) } /** @@ -560,7 +481,7 @@ module.exports = class Trie { this.root = lastRoot } - this._batchNodes(opStack, cb) + this.db.batch(opStack, cb) } _deleteNode (key, stack, cb) { @@ -732,7 +653,8 @@ module.exports = class Trie { // creates a new trie backed by the same db // and starting at the same root copy () { - return new Trie(this.db, this.root) + const db = this.db.copy() + return new Trie(db, this.root) } /** diff --git a/src/checkpoint-trie.js b/src/checkpointTrie.js similarity index 70% rename from src/checkpoint-trie.js rename to src/checkpointTrie.js index 5beb3b1..f9998b1 100644 --- a/src/checkpoint-trie.js +++ b/src/checkpointTrie.js @@ -1,15 +1,19 @@ const async = require('async') -const level = require('level-mem') const WriteStream = require('level-ws') const BaseTrie = require('./baseTrie') const proof = require('./proof.js') const ScratchReadStream = require('./scratchReadStream') +const ScratchDB = require('./scratch') const { callTogether } = require('./util/async') module.exports = class CheckpointTrie extends BaseTrie { constructor (...args) { super(...args) + // Reference to main DB instance + this._mainDB = this.db + // DB instance used for checkpoints this._scratch = null + // Roots of trie at the moment of checkpoint this._checkpoints = [] } @@ -71,7 +75,8 @@ module.exports = class CheckpointTrie extends BaseTrie { /** * Reverts the trie to the state it was at when `checkpoint` was first called. - * If during a nested checkpoint, only sets parent as current checkpoint. + * If during a nested checkpoint, sets root to most recent checkpoint, and sets + * parent checkpoint as current. * @method revert * @param {Function} cb the callback */ @@ -93,42 +98,19 @@ module.exports = class CheckpointTrie extends BaseTrie { /** * Returns a copy of the underlying trie with the interface - * of CheckpointTrie. + * of CheckpointTrie. If during a checkpoint, the copy will + * contain the checkpointing metadata (incl. reference to the same scratch). * @method copy */ copy () { - const trie = new CheckpointTrie(this.db, this.root) - trie._scratch = this._scratch - // trie._checkpoints = this._checkpoints.slice() - return trie - } - - /** - * Returns a `ScratchReadStream` based on the state updates - * since checkpoint. - * @method createScratchReadStream - */ - createScratchReadStream (scratch) { - const trie = this.copy() - scratch = scratch || this._scratch - // Only read from the scratch - trie._getDBs = [scratch] - trie._scratch = scratch - return new ScratchReadStream(trie) - } - - /** - * Puts kv-pair directly to db, ignoring checkpoints. - * @private - */ - _overridePutRaw (key, val, cb) { - const dbPut = (db, cb2) => { - db.put(key, val, { - keyEncoding: 'binary', - valueEncoding: 'binary' - }, cb2) + const db = this._mainDB.copy() + const trie = new CheckpointTrie(db, this.root) + if (this.isCheckpoint) { + trie._checkpoints = this._checkpoints.slice() + trie._scratch = this._scratch.copy() + trie.db = trie._scratch } - async.each(this.__putDBs, dbPut, cb) + return trie } /** @@ -136,12 +118,8 @@ module.exports = class CheckpointTrie extends BaseTrie { * @private */ _enterCpMode () { - this._scratch = level() - this._getDBs = [this._scratch].concat(this._getDBs) - this.__putDBs = this._putDBs - this._putDBs = [this._scratch] - this._putRaw = this.putRaw - this.putRaw = this._overridePutRaw + this._scratch = new ScratchDB(this._mainDB) + this.db = this._scratch } /** @@ -149,22 +127,28 @@ module.exports = class CheckpointTrie extends BaseTrie { * @private */ _exitCpMode (commitState, cb) { - var scratch = this._scratch + const scratch = this._scratch this._scratch = null - this._getDBs = this._getDBs.slice(1) - this._putDBs = this.__putDBs - this.putRaw = this._putRaw - - const flushScratch = (db, cb) => { - this.createScratchReadStream(scratch) - .pipe(WriteStream(db)) - .on('close', cb) - } + this.db = this._mainDB if (commitState) { - async.map(this._putDBs, flushScratch, cb) + this._createScratchReadStream(scratch) + .pipe(WriteStream(this.db)) + .on('close', cb) } else { - cb() + async.nextTick(cb) } } + + /** + * Returns a `ScratchReadStream` based on the state updates + * since checkpoint. + * @method createScratchReadStream + * @private + */ + _createScratchReadStream (scratch) { + scratch = scratch || this._scratch + const trie = new BaseTrie(scratch, this.root) + return new ScratchReadStream(trie) + } } diff --git a/src/db.js b/src/db.js new file mode 100644 index 0000000..f51ab5d --- /dev/null +++ b/src/db.js @@ -0,0 +1,83 @@ +const level = require('level-mem') + +const ENCODING_OPTS = { keyEncoding: 'binary', valueEncoding: 'binary' } + +/** + * DB is a thin wrapper around the underlying levelup db, + * which validates inputs and sets encoding type. + */ +module.exports = class DB { + /** + * Initialize a DB instance. If `leveldb` is not provided, DB + * defaults to an [in-memory store](https://github.com/Level/memdown). + * @param {Object} [leveldb] - An abstract-leveldown compliant store + */ + constructor (leveldb) { + this._leveldb = leveldb || level() + } + + /** + * Retrieves a raw value from leveldb. + * @param {Buffer} key + * @param {Function} cb A callback `Function`, which is given the arguments + * `err` - for errors that may have occured + * and `value` - the found value in a `Buffer` or if no value was found `null`. + */ + get (key, cb) { + if (!Buffer.isBuffer(key)) throw new Error('Invalid input: expected buffer') + + this._leveldb.get(key, ENCODING_OPTS, (err, v) => { + if (err || !v) { + cb(null, null) + } else { + cb(null, v) + } + }) + } + + /** + * Writes a value directly to leveldb. + * @param {Buffer} key The key as a `Buffer` or `String` + * @param {Buffer} value The value to be stored + * @param {Function} cb A callback `Function`, which is given the argument + * `err` - for errors that may have occured + */ + put (key, val, cb) { + if (!Buffer.isBuffer(key)) throw new Error('Invalid input: expected buffer') + if (!Buffer.isBuffer(val)) throw new Error('Invalid input: expected buffer') + + this._leveldb.put(key, val, ENCODING_OPTS, cb) + } + + /** + * Removes a raw value in the underlying leveldb. + * @param {Buffer} key + * @param {Function} cb A callback `Function`, which is given the argument + * `err` - for errors that may have occured + */ + del (key, cb) { + if (!Buffer.isBuffer(key)) throw new Error('Invalid input: expected buffer') + + this._leveldb.del(key, ENCODING_OPTS, cb) + } + + /** + * Performs a batch operation on db. + * @param {Array} opStack A stack of levelup operations + * @param {Function} cb A callback `Function`, which is given the argument + * `err` - for errors that may have occured + */ + batch (opStack, cb) { + if (!Array.isArray(opStack)) throw new Error('Invalid input: expected buffer') + + this._leveldb.batch(opStack, ENCODING_OPTS, cb) + } + + /** + * Returns a copy of the DB instance, with a reference + * to the **same** underlying leveldb instance. + */ + copy () { + return new DB(this._leveldb) + } +} diff --git a/src/index.js b/src/index.js index bcbc04b..dd7bc96 100644 --- a/src/index.js +++ b/src/index.js @@ -1 +1 @@ -module.exports = require('./checkpoint-trie') +module.exports = require('./checkpointTrie') diff --git a/src/scratch.js b/src/scratch.js new file mode 100644 index 0000000..d6ec9b2 --- /dev/null +++ b/src/scratch.js @@ -0,0 +1,42 @@ +const DB = require('./db') +const { asyncFirstSeries } = require('./util/async') + +const ENCODING_OPTS = { keyEncoding: 'binary', valueEncoding: 'binary' } + +/** + * An in-memory wrap over `DB` with an upstream DB + * which will be queried when a key is not found + * in the in-memory scratch. This class is used to implement + * checkpointing functionality in CheckpointTrie. + */ +module.exports = class ScratchDB extends DB { + constructor (upstreamDB) { + super() + this._upstream = upstreamDB + } + + /** + * Similar to `DB.get`, but first searches in-memory + * scratch DB, if key not found, searches upstream DB. + */ + get (key, cb) { + const getDBs = this._upstream._leveldb ? [this._leveldb, this._upstream._leveldb] : [this._leveldb] + const dbGet = (db, cb2) => { + db.get(key, ENCODING_OPTS, (err, v) => { + if (err || !v) { + cb2(null, null) + } else { + cb2(null, v) + } + }) + } + + asyncFirstSeries(getDBs, dbGet, cb) + } + + copy () { + const scratch = new ScratchDB(this._upstream) + scratch._leveldb = this._leveldb + return scratch + } +} diff --git a/src/secure.js b/src/secure.js index 78f60aa..9a62f63 100644 --- a/src/secure.js +++ b/src/secure.js @@ -1,5 +1,5 @@ const ethUtil = require('ethereumjs-util') -const CheckpointTrie = require('./checkpoint-trie') +const CheckpointTrie = require('./checkpointTrie') /** * You can create a secure Trie where the keys are automatically hashed @@ -15,7 +15,8 @@ module.exports = class SecureTrie extends CheckpointTrie { } copy () { - return new SecureTrie(this.db, this.root) + const db = this.db.copy() + return new SecureTrie(db, this.root) } get (key, cb) { diff --git a/test/checkpoint.js b/test/checkpoint.js new file mode 100644 index 0000000..21aba13 --- /dev/null +++ b/test/checkpoint.js @@ -0,0 +1,128 @@ +const tape = require('tape') +const Trie = require('../src/checkpointTrie') + +tape('testing checkpoints', function (tester) { + let trie, preRoot, postRoot, trieCopy + const it = tester.test + + it('setup', function (t) { + trie = new Trie() + trie.put('do', 'verb', function () { + trie.put('doge', 'coin', function () { + preRoot = trie.root.toString('hex') + t.end() + }) + }) + }) + + it('should copy trie and get value before checkpoint', function (t) { + trieCopy = trie.copy() + t.equal(trieCopy.root.toString('hex'), preRoot) + trieCopy.get('do', function (err, res) { + t.error(err) + t.ok(Buffer.from('verb').equals(res)) + t.end() + }) + }) + + it('should create a checkpoint', function (t) { + trie.checkpoint() + t.end() + }) + + it('should save to the cache', function (t) { + trie.put('test', 'something', function () { + trie.put('love', 'emotion', function () { + postRoot = trie.root.toString('hex') + t.end() + }) + }) + }) + + it('should get values from before checkpoint', function (t) { + trie.get('doge', function (err, res) { + t.error(err) + t.ok(Buffer.from('coin').equals(res)) + t.end() + }) + }) + + it('should get values from cache', function (t) { + trie.get('love', function (err, res) { + t.error(err) + t.ok(Buffer.from('emotion').equals(res)) + t.end() + }) + }) + + it('should copy trie and get upstream and cache values after checkpoint', function (t) { + trieCopy = trie.copy() + t.equal(trieCopy.root.toString('hex'), postRoot) + t.equal(trieCopy._checkpoints.length, 1) + t.ok(trieCopy.isCheckpoint) + trieCopy.get('do', function (err, res) { + t.error(err) + t.ok(Buffer.from('verb').equals(res)) + trieCopy.get('love', function (err, res) { + t.error(err) + t.ok(Buffer.from('emotion').equals(res)) + t.end() + }) + }) + }) + + it('should revert to the orginal root', function (t) { + t.equal(trie.isCheckpoint, true) + trie.revert(function () { + t.equal(trie.root.toString('hex'), preRoot) + t.equal(trie.isCheckpoint, false) + t.end() + }) + }) + + it('should not get values from cache after revert', function (t) { + trie.get('love', function (err, res) { + t.error(err) + t.notOk(res) + t.end() + }) + }) + + it('should commit a checkpoint', function (t) { + trie.checkpoint() + trie.put('test', 'something', function () { + trie.put('love', 'emotion', function () { + trie.commit(function () { + t.equal(trie.isCheckpoint, false) + t.equal(trie.root.toString('hex'), postRoot) + t.end() + }) + }) + }) + }) + + it('should get new values after commit', function (t) { + trie.get('love', function (err, res) { + t.error(err) + t.ok(Buffer.from('emotion').equals(res)) + t.end() + }) + }) + + it('should commit a nested checkpoint', function (t) { + trie.checkpoint() + var root + trie.put('test', 'something else', function () { + root = trie.root + trie.checkpoint() + trie.put('the feels', 'emotion', function () { + trie.revert() + trie.commit(function () { + t.equal(trie.isCheckpoint, false) + t.equal(trie.root.toString('hex'), root.toString('hex')) + t.end() + }) + }) + }) + }) +}) diff --git a/test/db.js b/test/db.js new file mode 100644 index 0000000..2f69128 --- /dev/null +++ b/test/db.js @@ -0,0 +1,55 @@ +const tape = require('tape') +const DB = require('../src/db') + +tape('DB basic functionality', (t) => { + const db = new DB() + + const k = Buffer.from('foo') + const v = Buffer.from('bar') + + t.test('puts and gets value', (st) => { + db.put(k, v, () => { + db.get(k, (err, res) => { + st.error(err) + st.ok(v.equals(res)) + st.end() + }) + }) + }) + + t.test('dels value', (st) => { + db.del(k, () => { + db.get(k, (err, res) => { + st.error(err) + st.notOk(res) + st.end() + }) + }) + }) + + t.test('batch ops', (st) => { + const k2 = Buffer.from('bar') + const v2 = Buffer.from('baz') + const ops = [{ type: 'put', key: k, value: v }, { type: 'put', key: k2, value: v2 }] + db.batch(ops, (err) => { + st.error(err) + db.get(k2, (err, res) => { + st.error(err) + st.ok(v2.equals(res)) + st.end() + }) + }) + }) +}) + +tape('DB input types', (t) => { + const db = new DB() + + t.test('fails for invalid input', (st) => { + try { + db.get('test') + } catch (e) { + st.end() + } + }) +}) diff --git a/test/index.js b/test/index.js index b0bf9bc..c5256f3 100644 --- a/test/index.js +++ b/test/index.js @@ -229,74 +229,6 @@ tape('testing deletions cases', function (tester) { }) }) -tape('testing checkpoints', function (tester) { - var trie, preRoot, postRoot - var it = tester.test - - it('setup', function (t) { - trie = new Trie() - trie.put('do', 'verb', function () { - trie.put('doge', 'coin', function () { - preRoot = trie.root.toString('hex') - t.end() - }) - }) - }) - - it('should create a checkpoint', function (t) { - trie.checkpoint() - t.end() - }) - - it('should save to the cache', function (t) { - trie.put('test', 'something', function () { - trie.put('love', 'emotion', function () { - postRoot = trie.root.toString('hex') - t.end() - }) - }) - }) - - it('should revert to the orginal root', function (t) { - t.equal(trie.isCheckpoint, true) - trie.revert(function () { - t.equal(trie.root.toString('hex'), preRoot) - t.equal(trie.isCheckpoint, false) - t.end() - }) - }) - - it('should commit a checkpoint', function (t) { - trie.checkpoint() - trie.put('test', 'something', function () { - trie.put('love', 'emotion', function () { - trie.commit(function () { - t.equal(trie.isCheckpoint, false) - t.equal(trie.root.toString('hex'), postRoot) - t.end() - }) - }) - }) - }) - - it('should commit a nested checkpoint', function (t) { - trie.checkpoint() - var root - trie.put('test', 'something else', function () { - root = trie.root - trie.checkpoint() - trie.put('the feels', 'emotion', function () { - trie.revert() - trie.commit(function () { - t.equal(trie.isCheckpoint, false) - t.equal(trie.root.toString('hex'), root.toString('hex')) - t.end() - }) - }) - }) - }) -}) - tape('it should create the genesis state root from ethereum', function (tester) { var it = tester.test var trie4 = new Trie() diff --git a/test/rawOPs.js b/test/rawOPs.js deleted file mode 100644 index 26b2e12..0000000 --- a/test/rawOPs.js +++ /dev/null @@ -1,71 +0,0 @@ -const Trie = require('../src/index.js') -const tape = require('tape') -const crypto = require('crypto') - -tape('put & get raw functions', function (it) { - var trie = new Trie() - var key = crypto.randomBytes(32) - var val = crypto.randomBytes(32) - - it.test('putRaw', function (t) { - trie.putRaw(key, val, t.end) - }) - - it.test('getRaw', function (t) { - trie.getRaw(key, function (err, rVal) { - t.equal(val.toString('hex'), rVal.toString('hex')) - t.end(err) - }) - }) - - it.test('should checkpoint and get the rawVal', function (t) { - trie.checkpoint() - trie.getRaw(key, function (err, rVal) { - t.equal(val.toString('hex'), rVal.toString('hex')) - t.end(err) - }) - }) - - var key2 = crypto.randomBytes(32) - var val2 = crypto.randomBytes(32) - it.test('should store while in a checkpoint', function (t) { - trie.putRaw(key2, val2, t.end) - }) - - it.test('should retrieve from a checkpoint', function (t) { - trie.getRaw(key2, function (err, rVal) { - t.equal(val2.toString('hex'), rVal.toString('hex')) - t.end(err) - }) - }) - - it.test('should not retiev after revert', function (t) { - trie.revert(t.end) - }) - - it.test('should delete raw', function (t) { - trie.delRaw(val2, t.end) - }) - - it.test('should not get val after delete ', function (t) { - trie.getRaw(val2, function (err, val) { - t.notok(val) - t.end(err) - }) - }) - - var key3 = crypto.randomBytes(32) - var val3 = crypto.randomBytes(32) - - it.test('test commit behavoir', function (t) { - trie.checkpoint() - trie.putRaw(key3, val3, function () { - trie.commit(function () { - trie.getRaw(key3, function (err, val) { - t.equal(val.toString('hex'), val3.toString('hex')) - t.end(err) - }) - }) - }) - }) -}) diff --git a/test/scratch.js b/test/scratch.js new file mode 100644 index 0000000..5be8aa6 --- /dev/null +++ b/test/scratch.js @@ -0,0 +1,57 @@ +const tape = require('tape') +const ScratchDB = require('../src/scratch') +const DB = require('../src/db') + +tape('ScratchDB', (t) => { + const upstream = new DB() + const scratch = new ScratchDB(upstream) + + const k = Buffer.from('foo') + const v = Buffer.from('bar') + const k2 = Buffer.from('bar') + const v2 = Buffer.from('baz') + + t.test('should fail to get non-existent value', (st) => { + scratch.get(k, (err, res) => { + st.error(err) + st.notOk(res) + st.end() + }) + }) + + t.test('puts value to scratch', (st) => { + scratch.put(k, v, () => { + scratch.get(k, (err, res) => { + st.error(err) + st.ok(v.equals(res)) + st.end() + }) + }) + }) + + t.test('should not have put value to upstream', (st) => { + upstream.get(k, (err, res) => { + st.error(err) + st.notOk(res) + st.end() + }) + }) + + t.test('should put value directly to upstream', (st) => { + upstream.put(k2, v2, () => { + upstream.get(k2, (err, res) => { + st.error(err) + st.ok(v2.equals(res)) + st.end() + }) + }) + }) + + t.test('scratch should get value from upstream', (st) => { + scratch.get(k2, (err, res) => { + st.error(err) + st.ok(v2.equals(res)) + st.end() + }) + }) +}) diff --git a/test/secure.js b/test/secure.js index aa62c91..f251df4 100644 --- a/test/secure.js +++ b/test/secure.js @@ -2,10 +2,36 @@ const Trie = require('../src/secure.js') const async = require('async') const tape = require('tape') -var trie = new Trie() + +tape('SecureTrie', function (t) { + const trie = new Trie() + const k = Buffer.from('foo') + const v = Buffer.from('bar') + + t.test('put and get value', function (st) { + trie.put(k, v, function () { + trie.get(k, function (err, res) { + st.error(err) + st.ok(v.equals(res)) + st.end() + }) + }) + }) + + t.test('copy trie', function (st) { + const t = trie.copy() + t.get(k, function (err, res) { + st.error(err) + st.ok(v.equals(res)) + st.end() + }) + }) +}) tape('secure tests', function (it) { + let trie = new Trie() const jsonTests = require('./fixture/trietest_secureTrie.json').tests + it.test('empty values', function (t) { async.eachSeries(jsonTests.emptyValues.in, function (row, cb) { trie.put(new Buffer(row[0]), row[1], cb) diff --git a/test/streams.js b/test/streams.js index e6eaa83..bf1d5f7 100644 --- a/test/streams.js +++ b/test/streams.js @@ -141,7 +141,7 @@ describe('db stream test', function (tester) { }) it('should only fetch nodes in the current trie', function (t) { - var stream = trie.createScratchReadStream() + var stream = trie._createScratchReadStream() stream.on('data', function (d) { var key = d.key.toString('hex') t.ok(!!expectedNodes[key])