zcash-primitives-js/test/incremental_merkle_tree.js

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'))
})
})
})
})