This repository has been archived by the owner on Jan 19, 2021. It is now read-only.
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #15 from jbaylina/merkleproof
merkle proof
- Loading branch information
Showing
3 changed files
with
230 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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')) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) | ||
}) | ||
}) | ||
}) |