UNPKG

signify-ts

Version:

Signing at the edge for KERI, ACDC, and KERIA

194 lines (193 loc) 5.85 kB
import { BexDex, Matter, NumDex } from "./matter.js"; import { CesrNumber } from "./number.js"; import { format, sum, fraction } from 'mathjs'; export class Tholder { // private _bexter: any constructor(kargs) { this._weighted = false; this._thold = undefined; this._size = 0; this._number = undefined; this._satisfy = undefined; if (kargs.thold !== undefined) { this._processThold(kargs.thold); } else if (kargs.limen != undefined) { this._processLimen(kargs.limen); } else if (kargs.sith !== undefined) { this._processSith(kargs.sith); } else { throw new Error('Missing threshold expression'); } } get weighted() { return this._weighted; } get thold() { return this._thold; } get size() { return this._size; } get limen() { var _a; return (_a = this._number) === null || _a === void 0 ? void 0 : _a.qb64b; } get sith() { if (this.weighted) { let sith = this.thold.map((clause) => { return clause.map((c) => { if (0 < Number(c) && Number(c) < 1) { return format(c, { fraction: 'ratio' }); } else { return format(c, { fraction: 'decimal' }); } }); }); if (sith.length == 1) { sith = sith[0]; } return sith; } else { return this.thold.toString(16); } } get json() { return JSON.stringify(this.sith); } get num() { return this._weighted ? undefined : this._thold; } _processThold(thold) { if (typeof thold === 'number') { this._processUnweighted(thold); } else { this._processWeighted(thold); } } _processLimen(limen) { const matter = new Matter({ qb64: limen }); if (NumDex.has(matter.code)) { const number = new CesrNumber({ raw: matter.raw, code: matter.code, }); this._processUnweighted(number.num); } else if (BexDex.has(matter.code)) { // TODO: Implement Bexter } else { throw new Error('Invalid code for limen=' + matter.code); } } _processSith(sith) { if (typeof sith == 'number') { this._processUnweighted(sith); } else if (typeof sith == 'string' && sith.indexOf('[') == -1) { this._processUnweighted(parseInt(sith, 16)); } else { let _sith = sith; if (typeof sith == 'string') { _sith = JSON.parse(sith); } if (_sith.length == 0) { throw new Error('Empty weight list'); } const mask = _sith.map((x) => { return typeof x !== 'string'; }); if (mask.length > 0 && !mask.every((x) => x)) { _sith = [_sith]; } for (const c of _sith) { const mask = c.map((x) => { return typeof x === 'string'; }); if (mask.length > 0 && !mask.every((x) => x)) { throw new Error('Invalid sith, some weights in clause ' + mask + ' are non string'); } } const thold = this._processClauses(_sith); this._processWeighted(thold); } } _processClauses(sith) { const thold = new Array(); sith.forEach((clause) => { thold.push(clause.map((w) => { return this.weight(w); })); }); return thold; } _processUnweighted(thold) { if (thold < 0) { throw new Error('Non-positive int threshold = {thold}.'); } this._thold = thold; this._weighted = false; this._size = this._thold; // used to verify that keys list size is at least size this._satisfy = this._satisfy_numeric; this._number = new CesrNumber({}, thold); // this._bexter = undefined } _processWeighted(thold) { for (const clause of thold) { if (Number(sum(clause)) < 1) { throw new Error('Invalid sith clause: ' + thold + 'all clause weight sums must be >= 1'); } } this._thold = thold; this._weighted = true; this._size = thold.reduce((acc, currentValue) => { return acc + currentValue.length; }, 0); this._satisfy = this._satisfy_weighted; //TODO: created Bexter if needed } weight(w) { return fraction(w); } _satisfy_numeric(indices) { return this.thold > 0 && indices.length >= this.thold; // at least one } _satisfy_weighted(indices) { if (indices.length === 0) { return false; } const indexes = new Set(indices.sort()); const sats = new Array(indices.length).fill(false); for (const idx of indexes) { sats[idx] = true; } let wio = 0; for (const clause of this.thold) { let cw = 0; for (const w of clause) { if (sats[wio]) { cw += Number(w); } wio += 1; } if (cw < 1) { return false; } } return true; } satisfy(indices) { return this._satisfy(indices); } }