git ssb

0+

wanderer🌟 / js-dfinity-radix-tree



Commit 11be8699369866363129c6162babf55dfaf9bf10

updated to new binary format

Signed-off-by: wanderer <mjbecze@gmail.com>
wanderer committed on 10/2/2017, 6:20:14 PM
Parent: 8063e81512342ad27a3e38e5e3fbbda334b3669b

Files changed

dag.jschanged
docs/spec.mdchanged
index.jschanged
tests/index.jschanged
treeNode.jschanged
dag.jsView
@@ -4,12 +4,12 @@
44 const HASH_LEN = 20
55
66 module.exports = class TreeDAG extends DAG {
77 put (val, options) {
8- const encoded = treeNode.encode(val).toString('hex')
8 + const encoded = treeNode.encode(val)
99 const key = crypto.createHash('sha256').update(encoded).digest().slice(0, HASH_LEN)
1010 return new Promise((resolve, reject) => {
11- this._dag.put(key, encoded, () => {
11 + this._dag.put(key, encoded.toString('hex'), () => {
1212 resolve(key)
1313 })
1414 })
1515 }
docs/spec.mdView
@@ -1,56 +1,94 @@
1-This is a simple Binary [Radix Tree](https://ipfs.io/ipns/QmdJiuMWp2FxyaerfLrtdLF6Nr1EWpL7dPAxA9oKSPYYgV/wiki/Radix_tree.html).
2-The default encodeing used is [cbor](http://cbor.io/) and the default hashing
3-algorithim used is sha2-256.
1 +This is a simple Merkle Binary [Radix Tree](https://ipfs.io/ipns/QmdJiuMWp2FxyaerfLrtdLF6Nr1EWpL7dPAxA9oKSPYYgV/wiki/Radix_tree.html).
42
53
64 ## Node
7-Each node is an variable length array containing at the most four elements
8-"left branch", "right branch" "extension", "value"
5 +Each node contains at the most four elements
6 +"extension", "left branch", "right branch" and "value".
97
108 ```
11-node : = [LBRANCH, RBRANCH, EXTENSION, VALUE]
9 +node : = EXTENSION | LBRANCH | RBRANCH | VALUE
1210 ```
1311
14-If no value exists at a given node the array is truncated to
12 +A prefix byte is used a bit field to signify which elements a node contains.
13 +The bit field is defined a the following. The first nibble is reserved for
14 +merkle proofs.
1515
1616 ```
17-node : = [LBRANCH, RBRANCH, EXTENSION]
17 +PREFIX := reserved | reserved | reserved | reserved | EXTENSION | LBRANCH | RBRANCH | VALUE
1818 ```
1919
20-If no value and no extension exists at a given node the array is truncated to
20 +For example a node that contained a left branch and a value would have a prefix byte
21 +of 00000101 or 0x07
2122
22-```
23-node : = [LBRANCH, RBRANCH]
24-```
23 +The full encoded node would then look something like. `0x07<20_bytes_for_lbranch><remaing_bytes_for_value>`
2524
26-All empty values in the array are encoded as "null".
27-An emty tree is defined as `[null, null]`
2825
2926 ## Branches
3027
31-Each link points to the next node in the tree.
28 +The branch elements point to the next node in the tree using a merkle link.
29 +A merkle link is defined by the first 20 bytes of the result SHA2-256 of an encoded node.
30 +
31 +
3232 ```
3333 branch : = <merkle link>
3434 ```
35-Where the link is a [CID](https://github.com/ipld/cid) is encoded as a byte string.
36-Using CIDs allow for the flexablity to update the hashing algorithim and encoding
37-at a later date while being backwards compatiable.
3835
36 +```
37 +link := SHA2(encoded_node)[0..20]
38 +```
39 +
40 +
3941 ## Extensions
4042 Extensions encode shared paths. Extensions are defined as
43 +
4144 ```
42-extension := [length, extension]
45 +extension := length | extension
4346
4447 ```
45-Where the length is an interger and the extension is a byte string padded with
46-0's
48 +Where the length is the number of bits that extension repesents. This varuint32
49 +encoded with a leb128. And the extension is bit array padded with 0's to the nearst byte.
4750
4851 For example if the binary keys [0, 0, 1, 1] and
49-[0, 0, 1, 0] have a shared path of [0, 0]. If they where the only two values in
50-the tree the root node would have an extension of [0, 0]
52 +[0, 0, 1, 0] have a shared path of [0, 0, 1]. The extension node would therefor be
5153
54 +`0x03, 0x04`
55 +
56 +where 3 is the the shared path length and the `0x04` is the shared path encoded
57 +as a little endian byte array.
58 +
59 +# Examples
60 +
61 +An empty tree is hash a merkle link of sha256(0x00) or `6e340b9cffb37a989ca544e6bb780a2c78901d3f`
62 +
63 +
64 +A tree with a single key 'binary' and value of 'tree' is encoded as
65 +
5266 ```
53-root := [null, <link>, [2, 0x00]]
67 +0x093062696e61727974726565
68 +
69 +0x09 this node has an extention and value
70 +0x30 the extention has a length of 48 bits or 6 bytes
71 +0x62696e617279 the key 'binary'
72 +0x4726565 the value 'tree'
5473 ```
5574
75 +If an other key "bin" with the value "number" is add the tree will have two nodes
5676
77 +The root node will be
78 +`0x0b1862696eaf39aa98eb0350611f230cbeb2e68dbe95ab5ecc6e756d626572`
79 +
80 +0xb this node has an extention, a right branch and a value
81 +0x18 this the extention has a length of 24 bits
82 +0x62696e the value extention "bin"
83 +0xaf39aa98eb0350611f230cbeb2e68dbe95ab5ecc a merkle link to another node
84 +0x6e756d626572 the value "number"
85 +
86 +the link 0xaf39aa98eb0350611f230cbeb2e68dbe95ab5ecc points to the node
87 +
88 +0x091730b93c74726565
89 +
90 +0x09 this node has an extention and value
91 +0x17 this the extention has a length of 23 bits
92 +0x30b93c the extention 'ary' = 0x617279 that is shift left one bit. 'a' is 0x61 which
93 +is read 100001100 (little endian) The was first bit is was used to choose the branch (right)
94 +turns into 0x30 (110000)
index.jsView
@@ -47,15 +47,8 @@
4747 const exNode = await this.graph.get(root, treeNode.EXTENSION, true)
4848 if (exNode) {
4949 let subKey = key.subarray(index)
5050
51- // let extensionIndex = 0
52- // const extension = getExtension(root)
53- // const extensionLen = extension.length
54- // // checks the extension against the key
55- // while (extensionIndex < extensionLen && extension[extensionIndex] === subKey[extensionIndex]) {
56- // extensionIndex++
57- // }
5851 const {extensionIndex, extensionLen, extension} = findMatchBits(subKey, root)
5952 index += extensionIndex
6053 // check if we compelete travered the extension
6154 if (extensionIndex !== extensionLen) {
@@ -89,14 +82,10 @@
8982
9083 index++
9184 }
9285
93- let value = treeNode.getValue(root)
86 + const value = treeNode.getValue(root)
9487
95- if (value && value['/']) {
96- value = await this.graph.get(root, treeNode.VALUE, true)
97- }
98-
9988 return {
10089 value: value,
10190 root: root,
10291 parent: parent,
tests/index.jsView
@@ -44,9 +44,8 @@
4444
4545 val = await tree.get('test')
4646 t.equals(val.toString(), 'cat111')
4747 // console.log(JSON.stringify(tree.root, null, 2))
48-
4948 t.end()
5049 })
5150
5251 tape('branch nodes', async t => {
@@ -118,8 +117,23 @@
118117 t.equals(value.toString(), saved.toString())
119118 t.end()
120119 })
121120
121 +tape('errors', async t => {
122 + const tree = new RadixTree({
123 + db: db,
124 + root: {
125 + '/': Buffer.alloc(20)
126 + }
127 + })
128 +
129 + try {
130 + await tree.get('test')
131 + } catch (e) {
132 + t.end()
133 + }
134 +})
135 +
122136 tape('random', async t => {
123137 const tree = new RadixTree({
124138 db: db
125139 })