199 lines
6.7 KiB
JavaScript
199 lines
6.7 KiB
JavaScript
/* global describe, it */
|
|
|
|
var assert = require('assert')
|
|
|
|
var ZCIncrementalMerkleTree = require('../src/incremental_merkle_tree')
|
|
var ZCIncrementalWitness = require('../src/incremental_witness')
|
|
|
|
var merkleCommitments = require('./fixtures/zcash/merkle_commitments')
|
|
var merkleRoots = require('./fixtures/zcash/merkle_roots')
|
|
var merkleRootsEmpty = require('./fixtures/zcash/merkle_roots_empty')
|
|
var merkleSerialization = require('./fixtures/zcash/merkle_serialization')
|
|
var merkleWitnessSerialization = require('./fixtures/zcash/merkle_witness_serialization')
|
|
|
|
var INCREMENTAL_MERKLE_TREE_DEPTH_TESTING = 4
|
|
|
|
describe('ZCIncrementalMerkleTree', function () {
|
|
it('correctly constructs a tree', function () {
|
|
var witnessSerIndex = 0
|
|
var tree = new ZCIncrementalMerkleTree(INCREMENTAL_MERKLE_TREE_DEPTH_TESTING)
|
|
|
|
// The root of the tree at this point is expected to be the root of the
|
|
// empty tree.
|
|
assert.strictEqual(
|
|
tree.root().toString('hex'),
|
|
tree.empty_root().toString('hex'),
|
|
'Tree root inconsistent for empty tree'
|
|
)
|
|
|
|
// The tree doesn't have a 'last' element added since it's blank.
|
|
assert.throws(function () {
|
|
tree.last()
|
|
}, new RegExp('tree has no cursor'))
|
|
|
|
// The tree is empty.
|
|
assert.strictEqual(tree.size(), 0)
|
|
|
|
// We need to witness at every single point in the tree, so
|
|
// that the consistency of the tree and the merkle paths can
|
|
// be checked.
|
|
var witnesses = []
|
|
|
|
merkleCommitments.forEach(function (commitment, i) {
|
|
var testCommitment = [].reverse.call(Buffer.from(commitment, 'hex'))
|
|
|
|
// Witness here
|
|
witnesses.push(ZCIncrementalWitness.fromTree(tree))
|
|
|
|
// Now append a commitment to the tree
|
|
tree.append(testCommitment)
|
|
|
|
// Size incremented by one
|
|
assert.strictEqual(
|
|
tree.size(), i + 1,
|
|
'Invalid tree size for commitment ' + i
|
|
)
|
|
|
|
// Last element added to the tree was `testCommitment`
|
|
assert.strictEqual(
|
|
tree.last().toString('hex'), testCommitment.toString('hex'),
|
|
'Invalid last element for commitment ' + i
|
|
)
|
|
|
|
// Check tree root consistency
|
|
assert.strictEqual(
|
|
tree.root().toString('hex'), merkleRoots[i],
|
|
'Tree root inconsistent for commitment ' + i
|
|
)
|
|
|
|
// Check serialization of tree
|
|
assert.strictEqual(
|
|
tree.toBuffer().toString('hex'), merkleSerialization[i],
|
|
'Tree serialization inconsistent for commitment ' + i
|
|
)
|
|
|
|
witnesses.forEach(function (witness, j) {
|
|
// Append the same commitment to all the witnesses
|
|
witness.append(testCommitment)
|
|
|
|
// Can't do path checks without libsnark
|
|
|
|
// Check witness serialization
|
|
assert.strictEqual(
|
|
witness.toBuffer().toString('hex'), merkleWitnessSerialization[witnessSerIndex++],
|
|
'Witness ' + j + ' serialization inconsistent for commitment ' + i
|
|
)
|
|
|
|
// Check witness root is same as tree root
|
|
assert.strictEqual(
|
|
witness.root().toString('hex'), tree.root().toString('hex'),
|
|
'Witness ' + j + ' root inconsistent for commitment ' + i
|
|
)
|
|
})
|
|
})
|
|
|
|
// Tree should be full now
|
|
assert.throws(function () {
|
|
tree.append(Buffer.alloc(32))
|
|
})
|
|
|
|
witnesses.forEach(function (witness) {
|
|
assert.throws(function () {
|
|
witness.append(Buffer.alloc(32))
|
|
})
|
|
})
|
|
})
|
|
|
|
it('has the correct empty root', function () {
|
|
assert.strictEqual(
|
|
new ZCIncrementalMerkleTree().empty_root().toString('hex'),
|
|
'd7c612c817793191a1e68652121876d6b3bde40f4fa52bc314145ce6e5cdd259',
|
|
'Invalid empty root'
|
|
)
|
|
})
|
|
|
|
describe('_generateEmptyRoots', function () {
|
|
var emptyRoots = ZCIncrementalMerkleTree._generateEmptyRoots(64)
|
|
|
|
emptyRoots.forEach(function (root, d) {
|
|
it('generates the correct empty root for depth ' + d, function () {
|
|
assert.strictEqual(root.toString('hex'), merkleRootsEmpty[d])
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('fromBuffer', function () {
|
|
it('throws when the most ancestral parent is empty', function () {
|
|
assert.throws(function () {
|
|
ZCIncrementalMerkleTree.fromBuffer(
|
|
Buffer.from('0155b852781b9995a44c939b64e441ae2724b96f99c8f4fb9a141cfc9842c4b0e3000100', 'hex')
|
|
)
|
|
}, new RegExp('tree has non-canonical representation of parent'))
|
|
})
|
|
|
|
it('throws when left doesn\'t exists but right does', function () {
|
|
assert.throws(function () {
|
|
ZCIncrementalMerkleTree.fromBuffer(
|
|
Buffer.from('000155b852781b9995a44c939b64e441ae2724b96f99c8f4fb9a141cfc9842c4b0e300', 'hex')
|
|
)
|
|
}, new RegExp('tree has non-canonical representation; right should not exist'))
|
|
})
|
|
|
|
it('throws when left doesn\'t exists but a parent does', function () {
|
|
assert.throws(function () {
|
|
ZCIncrementalMerkleTree.fromBuffer(
|
|
Buffer.from('000001018695873d63ec0bceeadb5bf4ccc6723ac803c1826fc7cfb34fc76180305ae27d', 'hex')
|
|
)
|
|
}, new RegExp('tree has non-canonical representation; parents should not be unempty'))
|
|
})
|
|
|
|
describe('invalid optional', function () {
|
|
var data = Buffer.from('0262fdad9bfbf17c38ea626a9c9b8af8a748e6b4367c8494caf0ca592999e8b6ba0000', 'hex')
|
|
|
|
it('throws', function () {
|
|
assert.throws(function () {
|
|
ZCIncrementalMerkleTree.fromBuffer(data)
|
|
}, new RegExp('Invalid optional'))
|
|
})
|
|
|
|
it('throws with __noStrict = true', function () {
|
|
assert.throws(function () {
|
|
ZCIncrementalMerkleTree.fromBuffer(data, true)
|
|
}, new RegExp('Invalid optional'))
|
|
})
|
|
})
|
|
|
|
describe('insufficient data', function () {
|
|
var data = Buffer.from('0162fdad9bfbf17c38ea626a9c9b8af8a748e6b4367c8494caf0ca592999e8b6ba00', 'hex')
|
|
|
|
it('throws', function () {
|
|
assert.throws(function () {
|
|
ZCIncrementalMerkleTree.fromBuffer(data)
|
|
}, new RegExp('Index out of range'))
|
|
})
|
|
|
|
it('throws with __noStrict = true', function () {
|
|
assert.throws(function () {
|
|
ZCIncrementalMerkleTree.fromBuffer(data, true)
|
|
}, new RegExp('Index out of range'))
|
|
})
|
|
})
|
|
|
|
describe('excess data', function () {
|
|
var data = Buffer.from('0162fdad9bfbf17c38ea626a9c9b8af8a748e6b4367c8494caf0ca592999e8b6ba0000ff', 'hex')
|
|
|
|
it('throws', function () {
|
|
assert.throws(function () {
|
|
ZCIncrementalMerkleTree.fromBuffer(data)
|
|
}, new RegExp('ZCIncrementalMerkleTree has unexpected data'))
|
|
})
|
|
|
|
it('passes with __noStrict = true', function () {
|
|
assert.doesNotThrow(function () {
|
|
ZCIncrementalMerkleTree.fromBuffer(data, true)
|
|
}, new RegExp('ZCIncrementalMerkleTree has unexpected data'))
|
|
})
|
|
})
|
|
})
|
|
})
|