@crpdo/merkle
Version:
A dynamic, in-memory merkle tree implementation in js
238 lines (205 loc) • 7.5 kB
JavaScript
const Merkle = require('../lib/merkle')
describe('Merkle', () => {
let merkle
const testValue1 = 'Hello'
const testValue2 = 'World'
const hashLength = 32
before(() => merkle = new Merkle([testValue1, testValue2]))
describe('constructor()', () => {
it('should construct properly', () => {
expect(merkle).to.be.instanceOf(Merkle)
})
it('should compute root correctly', () => {
const root = merkle.getRoot()
expect(root).to.not.be.null
})
it('should append values correctly', () => {
const newTestValue = 'Test'
merkle.append(newTestValue)
expect(merkle.getLeafCount()).to.equal(3)
})
it('should retrieve leaf correctly', () => {
const leaf = merkle.getLeaf(0)
expect(leaf).to.equal(testValue1)
})
it('should generate proof correctly', () => {
const proof = merkle.getProof(0)
expect(proof).to.not.be.null
expect(proof).to.have.property('proof')
expect(proof.proof).to.be.an('array')
expect(proof).to.have.property('target')
expect(proof.target).to.be.a('string')
expect(proof).to.have.property('index')
expect(proof.index).to.be.finite
expect(proof).to.have.property('root')
expect(proof.root).to.be.a('string')
})
it('should verify proof correctly', () => {
const proof = merkle.getProof(0)
const isVerified = merkle.verifyProof(proof)
expect(isVerified).to.be.true
})
it('should commit changes correctly', () => {
merkle.append('Test')
const { root, stack } = merkle.commit()
expect(root).to.not.be.null
expect(stack).to.not.be.null
})
it('should throw an error when committing without a checkpoint', () => {
merkle = new Merkle([testValue1, testValue2], false)
expect(() => merkle.commit()).to.throw('No checkpoint to commit')
})
it('should revert changes correctly', () => {
merkle.append('Test')
merkle.checkpoint()
merkle.append('Another Test')
const root = merkle.revert()
expect(root).to.equal(merkle.getRoot())
})
it('should throw an error when reverting without a checkpoint', () => {
merkle = new Merkle([testValue1, testValue2], false)
expect(() => merkle.revert()).to.throw('No checkpoint to revert to')
})
it('should create Merkle instance correctly', () => {
expect(merkle).to.be.instanceOf(Merkle)
expect(merkle.levels[0].length).to.equal(2)
expect(merkle.levels[0][0]).to.equal(testValue1)
expect(merkle.levels[0][1]).to.equal(testValue2)
})
})
describe('static getRoot()', () => {
it('should return the root of the levels', () => {
const root = Merkle.getRoot(merkle.levels)
expect(root).to.be.a('string')
})
it('should mutate the levels when mutate is true', () => {
const levelsCopy = merkle.levels.slice()
const root = Merkle.getRoot(levelsCopy, true)
expect(root).to.be.a('string')
expect(levelsCopy.length).to.equal(merkle.levels.length - 1)
})
})
describe('getRoot()', () => {
it('should return the root of the Merkle tree instance', () => {
const root = merkle.getRoot()
expect(root).to.be.a('string')
})
})
describe('genRoot()', () => {
it('should generate and set the root of the Merkle tree instance', () => {
const root = merkle.genRoot()
expect(root).to.be.a('string')
expect(root).to.equal(merkle.root)
})
})
describe('derive()', () => {
it('should generate new levels and root for the Merkle tree', () => {
const prevMerkle = new Merkle([testValue1])
const nextMerkle = new Merkle([testValue1, testValue2])
const prevRoot = prevMerkle.getRoot()
const nextRoot = nextMerkle.getRoot()
expect(prevRoot).to.not.equal(nextRoot)
})
})
describe('static derive()', () => {
it('should return new levels for the Merkle tree', () => {
const levels = [testValue1, testValue2]
const undos = []
const newLevels = Merkle.derive(levels, undos, hashLength)
expect(newLevels).to.be.an('array')
expect(newLevels[0]).to.include(testValue1)
expect(newLevels[0]).to.include(testValue2)
})
})
describe('static _derive()', () => {
it('should return new levels for the Merkle tree', () => {
const levels = [testValue1, testValue2]
const undos = []
const newLevels = Merkle._derive([levels], undos, hashLength)
expect(newLevels).to.be.an('array')
expect(newLevels[0]).to.include(testValue1)
expect(newLevels[0]).to.include(testValue2)
})
})
describe('static append()', () => {
it('should append a value and return the new levels', () => {
const levelsCopy = merkle.levels.slice()
const newLevels = Merkle.append('Test', levelsCopy)
expect(newLevels).to.be.an('array')
expect(newLevels[0]).to.include('Test')
})
})
describe('append()', () => {
it('should append a value and generate a new root', () => {
const oldRoot = merkle.genRoot()
merkle.append('Test')
const newRoot = merkle.getRoot()
expect(oldRoot).to.not.equal(newRoot)
})
})
describe('getLeaf()', () => {
it('should retrieve leaf correctly', () => {
const leaf = merkle.getLeaf(0)
expect(leaf).to.equal(testValue1)
})
})
describe('getLeafCount()', () => {
it('should get the correct leaf count', () => {
const count = merkle.getLeafCount()
expect(count).to.equal(2)
})
})
describe('getIndex()', () => {
it('should get the correct index of a leaf', () => {
const index = merkle.getIndex(testValue1)
expect(index).to.equal(0)
})
it('should return -1 if leaf not found', () => {
const index = merkle.getIndex('NotALeaf')
expect(index).to.equal(-1)
})
})
describe('depth', () => {
it('should return the correct depth', () => {
const depth = merkle.depth
expect(depth).to.equal(2) // the depth should be 2 for a Merkle tree with two leaves
})
})
describe('peek()', () => {
before(() => merkle = new Merkle([testValue1, testValue2]))
it('should return the last key when no parameter is given', () => {
const key = merkle.peek()
const firstKey = Object.keys(merkle.stacks)[0]
expect(key).to.not.be.empty
expect(key).to.equal(firstKey)
})
it('should return the correct key when a parameter is given', () => {
merkle.checkpoint()
const keys = Object.keys(merkle.stacks)
const key = merkle.peek(0)
expect(key).to.equal(keys[0]) // the key should be the first key when the parameter is 0
})
})
describe('checkpoint()', () => {
it('should create a new checkpoint correctly', () => {
const checkpoint = merkle.checkpoint()
expect(checkpoint).to.not.be.empty
expect(checkpoint.root).to.not.be.empty
expect(checkpoint.stack).to.be.an('array')
expect(checkpoint.stack).to.be.empty
expect(merkle.stacks).to.have.property(checkpoint.root)
})
})
describe('hash()', () => {
it('should return hashed value for given input', () => {
const hash = merkle.hash(testValue1)
expect(hash).to.be.a('string')
})
})
describe('static hash()', () => {
it('should return hashed value for given input', () => {
const hash = Merkle.hash(testValue1)
expect(hash).to.be.a('string')
})
})
})