cic-staff-client/src/assets/js/moolb.js

166 lines
4.7 KiB
JavaScript

let crypto = undefined;
(function() {
if (typeof module !== 'undefined' && typeof exports !== 'undefined') {
const nodeCrypto = require('crypto');
function hashWrapper(nodeCrypto, alg) {
this.alg = alg;
this.crypto = nodeCrypto.createHash(alg);
}
hashWrapper.prototype.update = function(d) {
this.crypto.update(d);
}
hashWrapper.prototype.digest = async function() {
const z = this.crypto.digest(this.data);
return new Uint8Array(z);
}
function cryptoWrapper(nodeCrypto) {
this.crypto = nodeCrypto
}
cryptoWrapper.prototype.createHash = function(alg) {
return new hashWrapper(this.crypto, alg);
}
module.exports = {
Bloom: Bloom,
fromBytes: fromBytes,
};
crypto = new cryptoWrapper(nodeCrypto);
} else {
function hashWrapper(webCrypto, alg) {
this.alg = alg;
this.crypto = webCrypto;
this.data = undefined;
}
hashWrapper.prototype.update = function(d) {
if (this.data != undefined) {
throw "cannot append";
}
this.data = d;
}
hashWrapper.prototype.digest = async function() {
const z = await this.crypto.subtle.digest('SHA-256', this.data);
return new Uint8Array(z);
}
function cryptoWrapper(webCrypto) {
this.crypto = webCrypto
}
cryptoWrapper.prototype.createHash = function(alg) {
return new hashWrapper(this.crypto, alg);
}
crypto = new cryptoWrapper(window.crypto);
window.Bloom = Bloom;
window.bloomFromBytes = fromBytes;
}
})()
// block numbers 6000000
// false positive probability 2%
//
// m = ceil((n * log(p)) / log(1 / pow(2, log(2))));
// m = ceil((6000000 * log(0.1)) / log(1 / pow(2, log(2))))
// = 3917675
// Creates a new bloom object.
// \param size of filter in bits, aligned to byte boundary
// \param number of rounds to hash
// \param hasher function, which must take two Uint8Array parameters 'data' and 'salt'. If not sef, hashBloomDefault will be used.
function Bloom(bits, rounds, hasher) {
this.bits = bits;
this.bytes = parseInt(bits / 8, 10);
this.rounds = rounds;
if (this.hasher === undefined) {
this.hasher = hashBloomDefault;
}
if (this.bytes * 8 != this.bits) {
console.error('number of bits must be on byte boundary');
return false;
} else {
this.filter = new Uint8Array(this.bytes);
}
}
// add entry to bloom filter
// \param value to add
Bloom.prototype.add = async function(v) {
let a = new ArrayBuffer(v.byteLength + 4);
let iw = new DataView(a);
for (let i = 0; i < v.byteLength; i++) {
iw.setUint8(i, v[i]);
}
//console.debug(iw, v);
for (var i = 0; i < this.rounds; i++) {
iw.setInt32(v.byteLength, i);
let result = await this.hasher(iw);
let resultHex = Array.prototype.map.call(new Uint8Array(result), x => ('00' + x.toString(16)).slice(-2)).join('');
let resultInt = parseInt(BigInt('0x'+resultHex) % BigInt(this.bits), 10);
let bytepos = parseInt(resultInt / 8, 10);
let bitpos = parseInt(resultInt, 10) % 8;
this.filter[bytepos] |= 1 << bitpos;
//console.log("setpos ", bytepos, bitpos);
}
};
// checks if the block number has been added to the bloom filter
// \param value to check for
// \return false if not found in filter
Bloom.prototype.check = async function(v) {
let a = new ArrayBuffer(v.byteLength + 4);
let iw = new DataView(a);
for (let i = 0; i < v.byteLength; i++) {
iw.setUint8(i, v[i]);
}
for (let j = 0; j < this.filter.byteLength; j++) {
if (v[3] == 20 && this.filter[j] > 0) {
// console.log('filter ', j, this.filter[j]);
}
}
for (let i = 0; i < this.rounds; i++) {
iw.setInt32(v.byteLength, i);
let result = await this.hasher(iw);
//console.log('result', result);
let resultHex = Array.prototype.map.call(new Uint8Array(result), x => ('00' + x.toString(16)).slice(-2)).join('');
let resultInt = parseInt(BigInt('0x'+resultHex) % BigInt(this.bits), 10);
let bytepos = parseInt(resultInt / 8, 10);
//console.log("setpos ", v, bytepos, resultInt % 8);
if (this.filter[bytepos] === undefined) {
// console.error('byte pos ' + bytepos + ' is undefined (filter length ' + this.filter.byteLength + ')');
return false;
} else {
let test = 1 << (0xff & (resultInt % 8));
if ((this.filter[bytepos] & test) == 0) {
return false;
}
}
}
return true;
};
// return the verbatim filter
// \return Uint8Array filter
Bloom.prototype.bytes = function() {
return this.filter;
};
// Default hashing function used in Bloom.add() - sha256
// \param data to insert in filter
// \param salt, typically a sequence number
// \return Uint8Array digest
async function hashBloomDefault(data, salt) {
const h = crypto.createHash('sha256');
h.update(data);
const z = await h.digest();
return Uint8Array.from(z);
}
function fromBytes(bytes, rounds, hasher) {
const bits = bytes.byteLength * 8;
let b = new Bloom(bits, rounds, hasher);
b.filter = bytes;
return b;
}