zcash-primitives-js/src/incremental_witness.js

166 lines
3.6 KiB
JavaScript

'use strict'
var varuint = require('varuint-bitcoin')
var ZCIncrementalMerkleTree = require('./incremental_merkle_tree')
function ZCIncrementalWitness () {
this.tree = new ZCIncrementalMerkleTree()
this.filled = []
this.cursor = null
this.cursor_depth = 0
}
ZCIncrementalWitness.fromTree = function (tree) {
var witness = new ZCIncrementalWitness()
witness.tree = tree.clone()
return witness
}
ZCIncrementalWitness.fromBuffer = function (buffer, __noStrict, __depth) {
var offset = 0
function readSlice (n) {
offset += n
return buffer.slice(offset - n, offset)
}
function readUInt8 () {
var i = buffer.readUInt8(offset)
offset += 1
return i
}
function readVarInt () {
var vi = varuint.decode(buffer, offset)
offset += varuint.decode.bytes
return vi
}
function readOptional (func) {
var i = readUInt8()
if (i === 1) {
return func()
} else if (i === 0) {
return null
} else {
throw new Error('Invalid optional')
}
}
function readZCIncrementalMerkleTree () {
var tree = ZCIncrementalMerkleTree.fromBuffer(buffer.slice(offset), true, __depth)
offset += tree.byteLength()
return tree
}
var witness = new ZCIncrementalWitness()
witness.tree = readZCIncrementalMerkleTree()
var filledLen = readVarInt()
for (var i = 0; i < filledLen; ++i) {
witness.filled.push(readSlice(32))
}
witness.cursor = readOptional(readZCIncrementalMerkleTree)
if (__noStrict && offset <= buffer.length) return witness
if (offset !== buffer.length) throw new Error('ZCIncrementalWitness has unexpected data')
return witness
}
ZCIncrementalWitness.prototype.byteLength = function () {
return (
this.tree.byteLength() +
varuint.encodingLength(this.filled.length) +
this.filled.length * 32 +
(this.cursor ? this.cursor.byteLength() + 1 : 1)
)
}
ZCIncrementalWitness.prototype.toBuffer = function () {
var buffer = Buffer.alloc(this.byteLength())
var offset = 0
function writeSlice (slice) {
slice.copy(buffer, offset)
offset += slice.length
}
function writeUInt8 (i) {
buffer.writeUInt8(i, offset)
offset += 1
}
function writeVarInt (i) {
varuint.encode(i, buffer, offset)
offset += varuint.encode.bytes
}
function writeOptional (val, func) {
if (val) {
writeUInt8(1)
func(val)
} else {
writeUInt8(0)
}
}
function writeZCIncrementalMerkleTree (tree) {
writeSlice(tree.toBuffer())
}
writeSlice(this.tree.toBuffer())
writeVarInt(this.filled.length)
this.filled.forEach(function (hash) {
writeSlice(hash)
})
writeOptional(this.cursor, writeZCIncrementalMerkleTree)
return buffer
}
ZCIncrementalWitness.prototype.root = function () {
return this.tree.root(this.tree.depth, this.partial_path())
}
ZCIncrementalWitness.prototype.partial_path = function () {
var uncles = this.filled.map(function (entry) {
return entry
})
if (this.cursor) {
uncles.push(this.cursor.root(this.cursor_depth))
}
return uncles
}
ZCIncrementalWitness.prototype.append = function (obj) {
if (this.cursor) {
this.cursor.append(obj)
if (this.cursor.is_complete(this.cursor_depth)) {
this.filled.push(this.cursor.root(this.cursor_depth))
this.cursor = null
}
} else {
this.cursor_depth = this.tree.next_depth(this.filled.length)
if (this.cursor_depth >= this.tree.depth) {
throw new Error('tree is full')
}
if (this.cursor_depth === 0) {
this.filled.push(obj)
} else {
this.cursor = new ZCIncrementalMerkleTree(this.tree.depth)
this.cursor.append(obj)
}
}
}
module.exports = ZCIncrementalWitness