Skip to content
This repository has been archived by the owner on Jan 19, 2021. It is now read-only.

Commit

Permalink
Support for proofs of null/absence. Dried up prove/verify.
Browse files Browse the repository at this point in the history
  • Loading branch information
zmitton committed Feb 14, 2019
1 parent c315566 commit fe5687a
Show file tree
Hide file tree
Showing 9 changed files with 114 additions and 132 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,7 @@ secure.js
scratchReadStream.js
trieNode.js
util.js
checkpointTrie.js
db.js
scratch.js
util/
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
"karma-firefox-launcher": "^1.0.1",
"karma-tap": "^1.0.3",
"standard": "^5.3.1",
"tape": "^4.4.0"
"tape": "^4.9.2"
},
"standard": {
"ignore": [
Expand Down
49 changes: 39 additions & 10 deletions src/baseTrie.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,35 @@ module.exports = class Trie {
this.root = root
}

static fromProof(proofNodes, cb){
let proofTrie = new Trie()
let opStack = proofNodes.map((nodeValue) => {
return {type: 'put', key: ethUtil.keccak(nodeValue), value: ethUtil.toBuffer(nodeValue)}
})
if(opStack[0]){ proofTrie.root = opStack[0].key}

proofTrie.db.batch(opStack, (e) => {
if(e) throw Error('Invalid proof nodes given')
cb(e, proofTrie)
})
}

static prove (trie, key, cb) {
trie.findPath(key, function (err, node, remaining, stack) {
if (err) return cb(err)
let p = stack.map((stackElem)=>{ return stackElem.serialize() })
cb(null, p)
})
}

static verifyProof (rootHash, key, proofNodes, cb) {
Trie.fromProof(proofNodes, (error,proofTrie)=>{
proofTrie.get(key, (e,r)=>{
return cb(e,r)
})
})
}

/**
* Gets a value given a `key`
* @method get
Expand Down Expand Up @@ -132,18 +161,17 @@ module.exports = class Trie {
// retrieves a node from dbs by hash
_lookupNode (node, cb) {
if (TrieNode.isRawNode(node)) {
cb(new TrieNode(node))
cb(null, new TrieNode(node))
} else {
this.db.get(node, (err, value) => {
if (err) {
throw err
}

if (value) {
value = new TrieNode(rlp.decode(value))
}else{
err = "Missing node in DB" || err
}

cb(value)
cb(err, value)
})
}
}
Expand Down Expand Up @@ -375,7 +403,7 @@ module.exports = class Trie {
return onDone()
}

this._lookupNode(root, node => {
this._lookupNode(root, (e, node) => {
processNode(root, node, null, err => {
if (err) {
return onDone(err)
Expand Down Expand Up @@ -421,7 +449,7 @@ module.exports = class Trie {
const childKey = key.concat(keyExtension)
const priority = childKey.length
taskExecutor.execute(priority, taskCallback => {
self._lookupNode(childRef, childNode => {
self._lookupNode(childRef, (e, childNode) => {
taskCallback()
processNode(childRef, childNode, childKey, cb)
})
Expand All @@ -434,7 +462,8 @@ module.exports = class Trie {
childKey.push(childIndex)
const priority = childKey.length
taskExecutor.execute(priority, taskCallback => {
self._lookupNode(childRef, childNode => {
self._lookupNode(childRef, (e, childNode) => {
if(e){ return cb(e, null)}
taskCallback()
processNode(childRef, childNode, childKey, cb)
})
Expand Down Expand Up @@ -585,7 +614,7 @@ module.exports = class Trie {
const branchNodeKey = branchNodes[0][0]

// look up node
this._lookupNode(branchNode, foundNode => {
this._lookupNode(branchNode, (e, foundNode) => {
key = processBranchNode(key, branchNodeKey, foundNode, parentNode, stack, opStack)
this._saveStack(key, stack, opStack, cb)
})
Expand Down Expand Up @@ -694,7 +723,7 @@ module.exports = class Trie {
*/
checkRoot (root, cb) {
root = ethUtil.toBuffer(root)
this._lookupNode(root, value => {
this._lookupNode(root, (e, value) => {
cb(null, !!value)
})
}
Expand Down
10 changes: 1 addition & 9 deletions src/checkpointTrie.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
const async = require('async')
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)
Expand All @@ -17,14 +17,6 @@ module.exports = class CheckpointTrie extends BaseTrie {
this._checkpoints = []
}

static prove (...args) {
return proof.prove(...args)
}

static verifyProof (...args) {
return proof.verifyProof(...args)
}

/**
* Is the trie during a checkpoint phase?
*/
Expand Down
100 changes: 0 additions & 100 deletions src/proof.js

This file was deleted.

10 changes: 10 additions & 0 deletions src/secure.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,16 @@ module.exports = class SecureTrie extends CheckpointTrie {
super(...args)
}

static prove (trie, key, cb) {
const hash = ethUtil.keccak256(key)
super.prove(trie, hash, cb)
}

static verifyProof (rootHash, key, proof, cb) {
const hash = ethUtil.keccak256(key)
super.verifyProof(rootHash, hash, proof, cb)
}

copy () {
const db = this.db.copy()
return new SecureTrie(db, this.root)
Expand Down
2 changes: 1 addition & 1 deletion test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const ethUtil = require('ethereumjs-util')

tape('simple save and retrive', function (tester) {
var it = tester.test

it('should not crash if given a non-existant root', function (t) {
var root = new Buffer('3f4399b08efe68945c1cf90ffe85bbe3ce978959da753f9e649f034015b8817d', 'hex')
var trie = new Trie(null, root)
Expand All @@ -15,7 +16,6 @@ tape('simple save and retrive', function (tester) {
t.end(err)
})
})

var trie = new Trie()

it('save a value', function (t) {
Expand Down
45 changes: 34 additions & 11 deletions test/proof.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,34 +37,57 @@ tape('simple merkle proofs generation and verification', function (tester) {
})
})
},
function (cb) {
function (cb) { //should create a valid proof of null
Trie.prove(trie, 'key2bb', function (err, prove) {
t.equal(err, null, 'Path to key2 should create valid proof of absence')
if (err) return cb(err)
Trie.verifyProof(trie.root, 'randomkey', prove, function (err, val) {
t.notEqual(err, null, 'Expected error: ' + err.message)
Trie.verifyProof(trie.root, 'key2', prove, function (err, val) {
t.equal(val, null, 'Expected value at a random key to be null')
t.equal(err, null, 'Path to key2 should show a null value')
cb()
})
})
},
function (cb) {
Trie.prove(trie, 'key2bb', function (err, prove) {
let myKey = "anyrandomkey"
Trie.prove(trie, myKey, function (err, prove) {
if (err) return cb(err)
Trie.verifyProof(trie.root, 'key2b', prove, function (err, val) {
t.notEqual(err, null, 'Expected error: ' + err.message)
Trie.verifyProof(trie.root, myKey, prove, function (err, val) {
t.equal(val, null, 'Expected value to be null')
t.equal(err, null, err)
cb()
})
})
},
function (cb) {
Trie.prove(trie, 'key2bb', function (err, prove) {
let myKey = "anothergarbagekey" //should generate a valid proof of null
Trie.prove(trie, myKey, function (err, prove) {
if (err) return cb(err)
prove.push(Buffer.from('123456'))
Trie.verifyProof(trie.root, 'key2b', prove, function (err, val) {
t.notEqual(err, null, 'Expected error: ' + err.message)
prove.push(Buffer.from('123456'))//extra nodes are just ignored
Trie.verifyProof(trie.root, myKey, prove, function (err, val) {
t.equal(val, null, 'Expected value to be null')
t.equal(err, null, err)
cb()
})
})
}
},
function (cb) {
trie.put('another', '3498h4riuhgwe', cb)
},
function (cb) {
// to throw an error we need to request proof for one key
Trie.prove(trie, 'another', function (err, prove) {
if (err) return cb(err)
// and try to use that proof on another key
Trie.verifyProof(trie.root, 'key1aa', prove, function (err, val) {
t.equal(val, null, 'Expected value: to be null ')
// this proof would be insignificant to prove `key1aa`
t.notEqual(err, null, 'Expected error: Missing node in DB')
t.notEqual(err, undefined, 'Expected error: Missing node in DB')
cb()
})
})
},
], function (err) {
t.end(err)
})
Expand Down
24 changes: 24 additions & 0 deletions test/secure.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,30 @@ tape('SecureTrie', function (t) {
})
})

tape('SecureTrie proof', function (t) {
t.test('create a merkle proof and verify it with a single short key', function (st) {
const trie = new Trie()

async.series([
function (cb) {
trie.put('key1aa', '01234', cb)
},
function (cb) {
Trie.prove(trie, 'key1aa', function (err, prove) {
if (err) return cb(err)
Trie.verifyProof(trie.root, 'key1aa', prove, function (err, val) {
if (err) return cb(err)
st.equal(val.toString('utf8'), '01234')
cb()
})
})
}
], function (err) {
st.end(err)
})
})
})

tape('secure tests', function (it) {
let trie = new Trie()
const jsonTests = require('./fixture/trietest_secureTrie.json').tests
Expand Down

0 comments on commit fe5687a

Please sign in to comment.