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

Commit

Permalink
Merge pull request #15 from jbaylina/merkleproof
Browse files Browse the repository at this point in the history
merkle proof
  • Loading branch information
holgerd77 committed Nov 30, 2017
2 parents bbba056 + 5e97166 commit df14ad1
Show file tree
Hide file tree
Showing 3 changed files with 230 additions and 0 deletions.
5 changes: 5 additions & 0 deletions index.js
@@ -1,6 +1,7 @@
const BaseTrie = require('./baseTrie')
const checkpointInterface = require('./checkpoint-interface')
const inherits = require('util').inherits
const proof = require('./proof.js')

module.exports = CheckpointTrie

Expand All @@ -10,3 +11,7 @@ function CheckpointTrie () {
BaseTrie.apply(this, arguments)
checkpointInterface(this)
}

CheckpointTrie.prove = proof.prove
CheckpointTrie.verifyProof = proof.verifyProof

80 changes: 80 additions & 0 deletions proof.js
@@ -0,0 +1,80 @@
const TrieNode = require('./trieNode')
const ethUtil = require('ethereumjs-util')
const matchingNibbleLength = require('./util').matchingNibbleLength

exports.prove = function (trie, key, cb) {
var nodes

trie.findPath(key, function (err, node, remaining, stack) {
if (err) return cb(err)
if (remaining.length > 0) return cb(new Error('Node does not contain the key'))
nodes = stack
var p = []
for (var i = 0; i < nodes.length; i++) {
var rlpNode = nodes[i].serialize()

if ((rlpNode.length >= 32) || (i === 0)) {
p.push(rlpNode)
}
}
cb(null, p)
})
}

exports.verifyProof = function (rootHash, key, proof, cb) {
key = TrieNode.stringToNibbles(key)
var wantHash = ethUtil.toBuffer(rootHash)
for (var i = 0; i < proof.length; i++) {
var p = ethUtil.toBuffer(proof[i])
var hash = ethUtil.sha3(proof[i])
if (Buffer.compare(hash, wantHash)) {
return cb(new Error('bad proof node ' + i + ': hash mismatch'))
}
var node = new TrieNode(ethUtil.rlp.decode(p))
var cld
if (node.type === 'branch') {
if (key.length === 0) {
if (i !== proof.length - 1) {
return cb(new Error('additional nodes at end of proof'))
}
return cb(null, node.value)
}
cld = node.raw[key[0]]
key = key.slice(1)
if (cld.length === 2) {
var embeddedNode = new TrieNode(cld)
if (i !== proof.length - 1) {
return cb(new Error('Key does not match with the proof one'))
}

if (matchingNibbleLength(embeddedNode.key, key) !== embeddedNode.key.length) {
return cb(new Error('Key does not match with the proof one'))
}
key = key.slice(embeddedNode.key.length)
if (key.length !== 0) {
return cb(new Error('Key does not match with the proof one'))
}
return cb(null, embeddedNode.value)
} else {
wantHash = cld
}
} else if ((node.type === 'extention') || (node.type === 'leaf')) {
if (matchingNibbleLength(node.key, key) !== node.key.length) {
return cb(new Error('Key does not match with the proof one'))
}
cld = node.value
key = key.slice(node.key.length)
if (key.length === 0) {
if (i !== proof.length - 1) {
return cb(new Error('Key does not match with the proof one'))
}
return cb(null, cld)
} else {
wantHash = cld
}
} else {
return cb(new Error('Invalid node type'))
}
}
cb(new Error('unexpected end of proof'))
}
145 changes: 145 additions & 0 deletions test/proof.js
@@ -0,0 +1,145 @@
const Trie = require('../index.js')
const async = require('async')
const tape = require('tape')

tape('simple merkle proofs generation and verification', function (tester) {
var it = tester.test
it('create a merkle proof and verify it', function (t) {
var trie = new Trie()

async.series([
function (cb) {
trie.put('key1aa', '0123456789012345678901234567890123456789xx', cb)
},
function (cb) {
trie.put('key2bb', 'aval2', cb)
},
function (cb) {
trie.put('key3cc', 'aval3', cb)
},
function (cb) {
Trie.prove(trie, 'key2bb', function (err, prove) {
if (err) return cb(err)
Trie.verifyProof(trie.root, 'key2bb', prove, function (err, val) {
if (err) return cb(err)
t.equal(val.toString('utf8'), 'aval2')
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)
t.equal(val.toString('utf8'), '0123456789012345678901234567890123456789xx')
cb()
})
})
}
], function (err) {
t.end(err)
})
})

it('create a merkle proof and verify it with a single long key', function (t) {
var trie = new Trie()

async.series([
function (cb) {
trie.put('key1aa', '0123456789012345678901234567890123456789xx', 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)
t.equal(val.toString('utf8'), '0123456789012345678901234567890123456789xx')
cb()
})
})
}
], function (err) {
t.end(err)
})
})

it('create a merkle proof and verify it with a single short key', function (t) {
var 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)
t.equal(val.toString('utf8'), '01234')
cb()
})
})
}
], function (err) {
t.end(err)
})
})

it('create a merkle proof and verify it whit keys in the midle', function (t) {
var trie = new Trie()

async.series([
function (cb) {
trie.put('key1aa', '0123456789012345678901234567890123456789xxx', cb)
},
function (cb) {
trie.put('key1', '0123456789012345678901234567890123456789Very_Long', cb)
},
function (cb) {
trie.put('key2bb', 'aval3', cb)
},
function (cb) {
trie.put('key2', 'short', cb)
},
function (cb) {
trie.put('key3cc', 'aval3', cb)
},
function (cb) {
trie.put('key3', '1234567890123456789012345678901', cb)
},
function (cb) {
Trie.prove(trie, 'key1', function (err, prove) {
if (err) return cb(err)
Trie.verifyProof(trie.root, 'key1', prove, function (err, val) {
if (err) return cb(err)
t.equal(val.toString('utf8'), '0123456789012345678901234567890123456789Very_Long')
cb()
})
})
},
function (cb) {
Trie.prove(trie, 'key2', function (err, prove) {
if (err) return cb(err)
Trie.verifyProof(trie.root, 'key2', prove, function (err, val) {
if (err) return cb(err)
t.equal(val.toString('utf8'), 'short')
cb()
})
})
},
function (cb) {
Trie.prove(trie, 'key3', function (err, prove) {
if (err) return cb(err)
Trie.verifyProof(trie.root, 'key3', prove, function (err, val) {
if (err) return cb(err)
t.equal(val.toString('utf8'), '1234567890123456789012345678901')
cb()
})
})
}
], function (err) {
t.end(err)
})
})
})

0 comments on commit df14ad1

Please sign in to comment.