SNAPKITTYWEST's picture
push from SNAPKITTYWEST/rowm-polymorphic-notebook
1d3f990 verified
Raw
History Blame Contribute Delete
6.84 kB
/**
* WORM Receipt Chain
* Generates deterministic, cryptographically sealed execution receipts
*/
class WORMReceiptChain {
constructor() {
this.receipts = [];
this.chain = [];
this.previousHash = this.genesisHash();
}
/**
* Genesis hash (all zeros for first receipt)
*/
genesisHash() {
return '0'.repeat(128); // SHA-512 equivalent (128 hex chars)
}
/**
* Generate deterministic canonical form (excludes timestamp)
*/
canonicalForm(receiptData) {
const {
sequenceNumber,
agentId,
capabilityId,
instructionHash,
action,
inputHash,
outputHash,
keyVersion,
signature,
status,
} = receiptData;
// Deterministic field order (excludes timestamp)
return [
`seq:${sequenceNumber}`,
`agent:${agentId}`,
`cap:${capabilityId}`,
`instr:${instructionHash}`,
`action:${action}`,
`input:${inputHash}`,
`output:${outputHash}`,
`keyver:${keyVersion}`,
`sig:${signature}`,
`status:${status}`,
].join('|');
}
/**
* Compute SHA-512 hash (using Web Crypto)
*/
async computeSHA512(data) {
const encoder = new TextEncoder();
const dataBuffer = encoder.encode(data);
const hashBuffer = await crypto.subtle.digest('SHA-512', dataBuffer);
const hashArray = Array.from(new Uint8Array(hashBuffer));
return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
}
/**
* Create a new receipt
*/
async createReceipt(receiptData) {
const sequenceNumber = this.receipts.length + 1;
const canonical = this.canonicalForm({
sequenceNumber,
...receiptData,
});
const receiptHash = await this.computeSHA512(canonical);
const receipt = {
sequenceNumber,
receiptId: `rcpt-${sequenceNumber.toString().padStart(10, '0')}`,
receiptHash,
previousHash: this.previousHash,
canonical,
timestamp: Math.floor(Date.now() / 1000),
...receiptData,
};
this.receipts.push(receipt);
this.previousHash = receiptHash;
this.chain.push({
sequence: sequenceNumber,
hash: receiptHash,
previousHash: receipt.previousHash,
});
return receipt;
}
/**
* Verify chain integrity
*/
verifyChainIntegrity() {
for (let i = 0; i < this.receipts.length; i++) {
const receipt = this.receipts[i];
// Verify hash is deterministic
const canonical = this.canonicalForm({
sequenceNumber: receipt.sequenceNumber,
agentId: receipt.agentId,
capabilityId: receipt.capabilityId,
instructionHash: receipt.instructionHash,
action: receipt.action,
inputHash: receipt.inputHash,
outputHash: receipt.outputHash,
keyVersion: receipt.keyVersion,
signature: receipt.signature,
status: receipt.status,
});
if (canonical !== receipt.canonical) {
return {
valid: false,
error: `Receipt ${i}: canonical form mismatch`,
receipt: receipt,
};
}
// Verify chain linkage
if (i > 0) {
const prevReceipt = this.receipts[i - 1];
if (receipt.previousHash !== prevReceipt.receiptHash) {
return {
valid: false,
error: `Receipt ${i}: previous hash mismatch`,
receipt: receipt,
};
}
} else {
// First receipt should link to genesis
if (receipt.previousHash !== this.genesisHash()) {
return {
valid: false,
error: `Receipt 0: should link to genesis hash`,
receipt: receipt,
};
}
}
}
return { valid: true, receiptsVerified: this.receipts.length };
}
/**
* Export receipt as JSON
*/
exportReceipt(index) {
if (index < 0 || index >= this.receipts.length) {
throw new Error('Receipt index out of bounds');
}
const receipt = this.receipts[index];
return {
receiptId: receipt.receiptId,
sequenceNumber: receipt.sequenceNumber,
receiptHash: receipt.receiptHash,
previousHash: receipt.previousHash,
agentId: receipt.agentId,
capabilityId: receipt.capabilityId,
action: receipt.action,
status: receipt.status,
canonical: receipt.canonical,
timestamp: receipt.timestamp,
};
}
/**
* Export entire chain as JSON
*/
exportChain() {
return {
totalReceipts: this.receipts.length,
genesisHash: this.genesisHash(),
receipts: this.receipts.map((r, i) => this.exportReceipt(i)),
chainValid: this.verifyChainIntegrity().valid,
};
}
/**
* Get receipt by sequence number
*/
getReceipt(sequenceNumber) {
return this.receipts.find(r => r.sequenceNumber === sequenceNumber);
}
/**
* Get last receipt
*/
getLastReceipt() {
return this.receipts[this.receipts.length - 1];
}
/**
* Clear all receipts
*/
clear() {
this.receipts = [];
this.chain = [];
this.previousHash = this.genesisHash();
}
/**
* Get chain statistics
*/
getStats() {
return {
totalReceipts: this.receipts.length,
chainValid: this.verifyChainIntegrity().valid,
genesisHash: this.genesisHash(),
currentHeadHash: this.previousHash,
receipts: this.receipts.map(r => ({
sequence: r.sequenceNumber,
receiptId: r.receiptId,
hash: r.receiptHash,
})),
};
}
}
// Export for use in other modules
if (typeof module !== 'undefined' && module.exports) {
module.exports = WORMReceiptChain;
}