File size: 3,752 Bytes
c404bdc | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | // @snapkitty/ledge β JavaScript/TypeScript interface
// Crypto: Rust (ledge-open/src/lib.rs) compiled to WASM via wasm-pack
// This file is a typed wrapper. Zero crypto runs here.
//
// Build WASM first:
// cd ledge-open && wasm-pack build --features wasm --target bundler --out-dir pkg
export interface SealedEvent {
index: number
seal: string
previousSeal: string
payload: Record<string, unknown>
timestampMs: number
}
export interface VerifyResult {
valid: boolean
failures: number[]
merkleRoot: string
eventCount: number
error?: string
}
// ββ WASM module interface βββββββββββββββββββββββββββββββββββββββββββββββββββββ
interface LedgeWasm {
ledge_genesis(): string
ledge_seal(prev: string, payload: string, ts: bigint, idx: bigint): string
ledge_verify(events_json: string): string
ledge_merkle_root(seals_json: string): string
}
// ββ In-memory chain (state lives in JS, crypto lives in Rust) βββββββββββββββββ
export class LedgeChain {
private events: SealedEvent[] = []
private head: string
private genesis: string
private wasm: LedgeWasm
constructor(wasm: LedgeWasm) {
this.wasm = wasm
this.genesis = wasm.ledge_genesis()
this.head = this.genesis
}
seal(payload: Record<string, unknown>, timestampMs = Date.now()): SealedEvent {
const index = this.events.length
const payloadJson = JSON.stringify(payload)
const seal = this.wasm.ledge_seal(
this.head,
payloadJson,
BigInt(timestampMs),
BigInt(index),
)
const event: SealedEvent = {
index,
seal,
previousSeal: this.head,
payload,
timestampMs,
}
this.head = seal
this.events.push(event)
return event
}
verify(): VerifyResult {
const result = JSON.parse(this.wasm.ledge_verify(JSON.stringify(this.events)))
return result as VerifyResult
}
merkleRoot(): string {
const seals = this.events.map(e => e.seal)
return this.wasm.ledge_merkle_root(JSON.stringify(seals))
}
getEvents(): SealedEvent[] {
return [...this.events]
}
getGenesis(): string {
return this.genesis
}
}
// ββ Factory ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
/**
* Load the WASM module and return a factory for creating chains.
*
* Browser / bundler:
* import { initLedge } from '@snapkitty/ledge'
* const { createChain } = await initLedge()
* const chain = createChain()
*
* Node.js (after wasm-pack --target nodejs):
* const wasm = require('../pkg/ledge')
* const { initLedge } = require('@snapkitty/ledge')
* const { createChain } = await initLedge(wasm)
*/
export async function initLedge(wasmModule?: LedgeWasm): Promise<{
createChain: () => LedgeChain
genesis: () => string
}> {
let wasm: LedgeWasm
if (wasmModule) {
wasm = wasmModule
} else {
// Dynamic import β bundler resolves to pkg/ledge_bg.wasm
const mod = await import('../pkg/ledge.js' as string) as LedgeWasm & { default?: () => Promise<void> }
if (mod.default) await mod.default()
wasm = mod
}
return {
createChain: () => new LedgeChain(wasm),
genesis: () => wasm.ledge_genesis(),
}
}
|