57 lines
4.7 KiB
JavaScript
57 lines
4.7 KiB
JavaScript
// lume-operation.mjs: the world of the probe and of the vector generator, built by the PRODUCERS (operation.mjs, ../aere-device-identity,
|
|
// ../aere-identity), never typed by hand. Every key is made here and discarded with the world. The chain is not read: the blocks are
|
|
// synthetic (their hash is the sha256 of their number) and the verifier's readings of blocks and of notarized anchors are written here, as
|
|
// the readings a verifier would make; they stand for the chain and are said to.
|
|
import crypto from 'node:crypto';
|
|
import * as ID from '../identity/identity.mjs';
|
|
import * as D from '../device-identity/device.mjs';
|
|
import * as O from './operation.mjs';
|
|
|
|
export const ORA = 3600000;
|
|
export const LANT = 28001;
|
|
export const bloc = (n) => '0x' + crypto.createHash('sha256').update('aere-device-operation probe block ' + n).digest('hex');
|
|
|
|
export function lume({ T0 = Date.UTC(2026, 9, 1) } = {}) {
|
|
const iso = (ms) => new Date(ms).toISOString();
|
|
const [mfr, altMfr, strain] = Array.from({ length: 3 }, () => ID.generateKeys());
|
|
const [dev1, dev2, dev3] = Array.from({ length: 3 }, () => D.generateDeviceKey());
|
|
const LID = 'https://manufacturer.example/status/devices';
|
|
const credential = (o = {}) => D.manufacture({ manufacturer: mfr, deviceKey: dev1.publicKey, model: 'AX-200', serial: 'SN-0001', validFrom: iso(T0 - 30 * 24 * ORA), validUntil: iso(T0 + 3650 * 24 * ORA), status: { list: LID, index: 3 }, now: new Date(T0 - 30 * 24 * ORA), ...o });
|
|
const M1 = credential();
|
|
const M2 = credential({ deviceKey: dev2.publicKey, status: { list: LID, index: 4 } });
|
|
const lista = (revoked = []) => ID.createStatusList({ issuer: mfr, id: LID, revoked, validFrom: iso(T0 - ORA), validUntil: iso(T0 + 30 * 24 * ORA), now: new Date(T0 - ORA) });
|
|
/** a status record of the manufacturer for a device id: the producer of the device component, given the credential of that key */
|
|
const stare = (M, state, at, o = {}) => D.statusRecord({ by: mfr, manufacture: M, state, seq: o.seq ?? 1, validUntil: iso(at + 30 * 24 * ORA), now: new Date(at), ...o });
|
|
const timpul = (i) => T0 + i * ORA + 300000; // record i: five minutes past hour i (its block is read two minutes before, its anchor ten after: inside the hour)
|
|
/**
|
|
* A log rooted in `m1` (the credential of dev1; `m2` certifies dev2) of `ore` records (the hour offsets of each record), rotating to dev2 after record `rotireLa` (null: no rotation), with an anchor
|
|
* envelope at each index of `ancoreLa` (read by the verifier ten minutes after the record) and, when `dupa`, each record naming block
|
|
* 1000 + i (read by the verifier: dated two minutes before the record). Returns { log, readings, beacons }.
|
|
*/
|
|
function jurnal({ m1 = M1, m2 = M2, ore = Array.from({ length: 24 }, (_, i) => i), rotireLa = 12, ancoreLa = [5, 11, 17, 23], dupa = true, stari = (i) => (i % 7 === 3 ? 'idle' : i === 9 ? 'fault' : i === 10 ? 'maintenance' : 'operating') } = {}) {
|
|
let L = O.newLog({ manufacture: m1 }), cheie = dev1;
|
|
const beacons = [], readings = [];
|
|
ore.forEach((h, i) => {
|
|
const at = timpul(h);
|
|
const after = dupa ? { chainId: LANT, block: 1000 + h, hash: bloc(1000 + h) } : null;
|
|
if (dupa) beacons.push({ chainId: LANT, block: 1000 + h, hash: bloc(1000 + h), at: Math.floor((at - 120000) / 1000) });
|
|
const roteste = rotireLa === i;
|
|
const r = O.operationRecord({ device: cheie, log: L, state: stari(i), counter: 100 * (i + 1), measurement: Buffer.from(`measurement ${i}`), after, next: roteste ? dev2 : null, now: new Date(at) });
|
|
L = O.append(L, r, roteste ? m2 : null);
|
|
if (roteste) cheie = dev2;
|
|
});
|
|
for (const j of ancoreLa) {
|
|
if (j >= L.records.length) continue;
|
|
const E = O.anchorEnvelope(L.records[j]);
|
|
L = O.addAnchor(L, { ...E, notarization: { chainId: LANT, note: 'synthetic: this world reads no chain' } });
|
|
readings.push({ statementHash: E.statementHash, at: Math.floor((Date.parse(L.records[j].statement.at) + 600000) / 1000), chainId: LANT });
|
|
}
|
|
return { log: L, readings, beacons };
|
|
}
|
|
/** a record with its statement changed by `f` and signed again with the published formula by `device` (a sibling only a key holder can make) */
|
|
const resemneaza = (r, device, f) => { const st = JSON.parse(JSON.stringify(r.statement)); f(st); return { statement: st, signature: O.signRecord(st, device) }; };
|
|
/** the verifier's options for a log of this world, at `now` (default: one hour after record 23) */
|
|
const optiuni = (j, o = {}) => ({ now: T0 + 25 * ORA, trustedManufacturers: [mfr.id], statusLists: [lista()], statusRecords: [], readings: j.readings, beacons: j.beacons, otherRecords: [], ...o });
|
|
return { T0, iso, timpul, mfr, altMfr, strain, dev1, dev2, dev3, LID, credential, M1, M2, lista, stare, jurnal, resemneaza, optiuni };
|
|
}
|