miniledger 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,702 @@
1
+ import { EventEmitter } from 'node:events';
2
+ import pino from 'pino';
3
+ import Database from 'better-sqlite3';
4
+ import WebSocket from 'ws';
5
+
6
+ type HexString = string;
7
+ declare enum TxType {
8
+ StateSet = "state:set",
9
+ StateDelete = "state:delete",
10
+ ContractDeploy = "contract:deploy",
11
+ ContractInvoke = "contract:invoke",
12
+ GovernancePropose = "governance:propose",
13
+ GovernanceVote = "governance:vote"
14
+ }
15
+ interface StateSetPayload {
16
+ kind: "state:set";
17
+ key: string;
18
+ value: unknown;
19
+ }
20
+ interface StateDeletePayload {
21
+ kind: "state:delete";
22
+ key: string;
23
+ }
24
+ interface ContractDeployPayload {
25
+ kind: "contract:deploy";
26
+ name: string;
27
+ version: string;
28
+ code: string;
29
+ }
30
+ interface ContractInvokePayload {
31
+ kind: "contract:invoke";
32
+ contract: string;
33
+ method: string;
34
+ args: unknown[];
35
+ }
36
+ interface GovernanceProposePayload {
37
+ kind: "governance:propose";
38
+ title: string;
39
+ description: string;
40
+ action: Record<string, unknown>;
41
+ }
42
+ interface GovernanceVotePayload {
43
+ kind: "governance:vote";
44
+ proposalId: string;
45
+ vote: boolean;
46
+ }
47
+ type TxPayload = StateSetPayload | StateDeletePayload | ContractDeployPayload | ContractInvokePayload | GovernanceProposePayload | GovernanceVotePayload;
48
+ interface Transaction {
49
+ hash: HexString;
50
+ type: TxType;
51
+ sender: HexString;
52
+ nonce: number;
53
+ timestamp: number;
54
+ payload: TxPayload;
55
+ signature: HexString;
56
+ }
57
+ interface Block {
58
+ height: number;
59
+ hash: HexString;
60
+ previousHash: HexString;
61
+ timestamp: number;
62
+ merkleRoot: HexString;
63
+ stateRoot: HexString;
64
+ proposer: HexString;
65
+ signature: HexString;
66
+ transactions: Transaction[];
67
+ }
68
+ interface StateEntry {
69
+ key: string;
70
+ value: unknown;
71
+ version: number;
72
+ updatedAt: number;
73
+ updatedBy: HexString;
74
+ blockHeight: number;
75
+ }
76
+ interface PeerInfo {
77
+ id: string;
78
+ publicKey: HexString;
79
+ address: string;
80
+ orgId: string;
81
+ role: "validator" | "observer";
82
+ status: "connected" | "disconnected" | "syncing";
83
+ lastSeen: number;
84
+ chainHeight: number;
85
+ }
86
+ interface NodeIdentity {
87
+ publicKey: HexString;
88
+ nodeId: string;
89
+ orgId: string;
90
+ name: string;
91
+ role: "admin" | "member" | "observer";
92
+ createdAt: number;
93
+ }
94
+ interface NodeStatus {
95
+ nodeId: string;
96
+ publicKey: HexString;
97
+ chainHeight: number;
98
+ latestBlockHash: HexString;
99
+ peerCount: number;
100
+ txPoolSize: number;
101
+ uptime: number;
102
+ version: number;
103
+ }
104
+
105
+ declare class MiniLedgerDB {
106
+ private db;
107
+ constructor(dbPath: string);
108
+ /** Run all pending migrations. */
109
+ migrate(): void;
110
+ /** Get the underlying better-sqlite3 Database instance. */
111
+ raw(): Database.Database;
112
+ close(): void;
113
+ }
114
+
115
+ declare class BlockStore {
116
+ private q;
117
+ constructor(db: Database.Database);
118
+ insert(block: Block): void;
119
+ getByHeight(height: number): Block | null;
120
+ getByHash(hash: string): Block | null;
121
+ getLatest(): Block | null;
122
+ getRange(fromHeight: number, toHeight: number): Block[];
123
+ getHeight(): number;
124
+ }
125
+
126
+ declare class StateStore {
127
+ private q;
128
+ private db;
129
+ constructor(db: Database.Database);
130
+ get(key: string): StateEntry | null;
131
+ set(key: string, value: unknown, updatedBy: string, blockHeight: number): void;
132
+ delete(key: string): boolean;
133
+ /** Execute a read-only SQL query against the world_state table. */
134
+ query(sql: string, params?: unknown[]): Record<string, unknown>[];
135
+ /** Compute a hash of the entire world state (for stateRoot). */
136
+ computeStateRoot(): string;
137
+ count(): number;
138
+ }
139
+
140
+ declare class TxStore {
141
+ private q;
142
+ constructor(db: Database.Database);
143
+ getByHash(hash: string): Transaction | null;
144
+ getBySender(sender: string, limit?: number): Transaction[];
145
+ /** Add a transaction to the pending pool. */
146
+ addToPending(tx: Transaction): void;
147
+ /** Get all pending transactions, ordered by received time. */
148
+ getPending(limit?: number): Transaction[];
149
+ /** Remove transactions from the pending pool (after inclusion in a block). */
150
+ removePending(hashes: string[]): void;
151
+ pendingCount(): number;
152
+ /** Get the next nonce for a sender. */
153
+ getNextNonce(sender: string): number;
154
+ /** Update the nonce tracker for a sender. */
155
+ updateNonce(sender: string, nonce: number): void;
156
+ }
157
+
158
+ interface MiniLedgerConfig {
159
+ dataDir: string;
160
+ node: {
161
+ name: string;
162
+ orgId: string;
163
+ role: "validator" | "observer";
164
+ };
165
+ network: {
166
+ listenAddress: string;
167
+ p2pPort: number;
168
+ apiPort: number;
169
+ peers: string[];
170
+ maxPeers: number;
171
+ };
172
+ consensus: {
173
+ algorithm: "raft" | "solo";
174
+ blockTimeMs: number;
175
+ maxTxPerBlock: number;
176
+ };
177
+ api: {
178
+ enabled: boolean;
179
+ cors: boolean;
180
+ };
181
+ logging: {
182
+ level: "debug" | "info" | "warn" | "error";
183
+ };
184
+ }
185
+
186
+ declare enum MessageType {
187
+ Handshake = "HANDSHAKE",
188
+ HandshakeAck = "HANDSHAKE_ACK",
189
+ BlockAnnounce = "BLOCK_ANNOUNCE",
190
+ BlockRequest = "BLOCK_REQUEST",
191
+ BlockResponse = "BLOCK_RESPONSE",
192
+ TxBroadcast = "TX_BROADCAST",
193
+ TxForward = "TX_FORWARD",// Forward tx to leader
194
+ ConsensusRequestVote = "CONSENSUS_REQUEST_VOTE",
195
+ ConsensusRequestVoteReply = "CONSENSUS_REQUEST_VOTE_REPLY",
196
+ ConsensusAppendEntries = "CONSENSUS_APPEND_ENTRIES",
197
+ ConsensusAppendEntriesReply = "CONSENSUS_APPEND_ENTRIES_REPLY",
198
+ SyncRequest = "SYNC_REQUEST",
199
+ SyncResponse = "SYNC_RESPONSE",
200
+ PeerList = "PEER_LIST",
201
+ Ping = "PING",
202
+ Pong = "PONG"
203
+ }
204
+ interface MessageEnvelope<T = unknown> {
205
+ version: number;
206
+ type: MessageType;
207
+ from: string;
208
+ timestamp: number;
209
+ payload: T;
210
+ }
211
+
212
+ interface PeerState {
213
+ nodeId: string;
214
+ publicKey: HexString;
215
+ orgId: string;
216
+ address: string;
217
+ chainHeight: number;
218
+ status: "connecting" | "connected" | "disconnected";
219
+ lastSeen: number;
220
+ isInbound: boolean;
221
+ }
222
+ declare class Peer {
223
+ readonly nodeId: string;
224
+ publicKey: HexString;
225
+ orgId: string;
226
+ address: string;
227
+ chainHeight: number;
228
+ status: "connecting" | "connected" | "disconnected";
229
+ lastSeen: number;
230
+ isInbound: boolean;
231
+ private ws;
232
+ constructor(ws: WebSocket, info: Partial<PeerState> & {
233
+ nodeId: string;
234
+ isInbound: boolean;
235
+ });
236
+ send(msg: MessageEnvelope): void;
237
+ close(): void;
238
+ getSocket(): WebSocket;
239
+ updateSeen(): void;
240
+ toInfo(): PeerState;
241
+ }
242
+
243
+ type MessageHandler = (msg: MessageEnvelope, peer: Peer) => void | Promise<void>;
244
+ /**
245
+ * Routes incoming messages to registered handlers by message type.
246
+ */
247
+ declare class MessageRouter {
248
+ private handlers;
249
+ on(type: MessageType, handler: MessageHandler): void;
250
+ off(type: MessageType, handler: MessageHandler): void;
251
+ dispatch(msg: MessageEnvelope, peer: Peer): Promise<void>;
252
+ }
253
+
254
+ interface PeerManagerOptions {
255
+ nodeId: string;
256
+ publicKey: HexString;
257
+ orgId: string;
258
+ p2pPort: number;
259
+ listenAddress: string;
260
+ log: pino.Logger;
261
+ getChainHeight: () => number;
262
+ }
263
+ declare class PeerManager extends EventEmitter {
264
+ readonly router: MessageRouter;
265
+ private server;
266
+ private client;
267
+ private peers;
268
+ private opts;
269
+ private healthTimer;
270
+ constructor(opts: PeerManagerOptions);
271
+ start(): Promise<void>;
272
+ stop(): void;
273
+ /** Connect to a peer by address (ws://host:port). */
274
+ connectTo(address: string): void;
275
+ /** Broadcast a message to all connected peers. */
276
+ broadcast(msg: MessageEnvelope): void;
277
+ /** Send a message to a specific peer by nodeId. */
278
+ sendTo(nodeId: string, msg: MessageEnvelope): void;
279
+ /** Send to leader or a specific peer. */
280
+ sendToPeer(peer: Peer, msg: MessageEnvelope): void;
281
+ getPeer(nodeId: string): Peer | undefined;
282
+ getConnectedPeers(): Peer[];
283
+ getPeerStates(): PeerState[];
284
+ getPeerCount(): number;
285
+ /** Get all known node IDs (for Raft voter list). */
286
+ getKnownNodeIds(): string[];
287
+ private onRawPeerConnected;
288
+ private onRawPeerDisconnected;
289
+ private sendHandshake;
290
+ private handleHandshake;
291
+ private handleHandshakeAck;
292
+ private handlePing;
293
+ private handlePong;
294
+ private handlePeerList;
295
+ private sharePeerList;
296
+ private pingAll;
297
+ }
298
+
299
+ declare enum RaftRole {
300
+ Follower = "follower",
301
+ Candidate = "candidate",
302
+ Leader = "leader"
303
+ }
304
+ interface ConsensusState {
305
+ role: RaftRole;
306
+ term: number;
307
+ leaderId: string | null;
308
+ votedFor: string | null;
309
+ commitIndex: number;
310
+ }
311
+ /**
312
+ * Interface that consensus implementations must satisfy.
313
+ * The node orchestrator interacts with consensus through this interface.
314
+ */
315
+ interface IConsensus {
316
+ /** Start the consensus engine. */
317
+ start(): void;
318
+ /** Stop the consensus engine. */
319
+ stop(): void;
320
+ /** Check if this node is the current leader. */
321
+ isLeader(): boolean;
322
+ /** Get the current leader's node ID (if known). */
323
+ getLeaderId(): string | null;
324
+ /** Get current consensus state for status reporting. */
325
+ getState(): ConsensusState;
326
+ /** Propose a block with pending transactions. Called by the node when it's time to produce a block. */
327
+ proposeBlock(block: Block): void;
328
+ /** Handle a received block from consensus (called when a committed entry needs to be applied). */
329
+ onBlockCommitted: ((block: Block) => void) | null;
330
+ /** Handle transaction received — if leader, include in next block; if follower, forward to leader. */
331
+ onTransactionReceived: ((tx: Transaction) => void) | null;
332
+ }
333
+
334
+ interface RaftNodeOptions {
335
+ nodeId: string;
336
+ peerManager: PeerManager;
337
+ log: pino.Logger;
338
+ blockTimeMs: number;
339
+ }
340
+ /**
341
+ * Raft consensus implementation.
342
+ *
343
+ * Key simplification: Raft log entries ARE block proposals.
344
+ * A committed entry directly becomes a finalized block.
345
+ */
346
+ declare class RaftNode extends EventEmitter implements IConsensus {
347
+ private opts;
348
+ private role;
349
+ private currentTerm;
350
+ private votedFor;
351
+ private leaderId;
352
+ private raftLog;
353
+ private commitIndex;
354
+ private lastApplied;
355
+ private nextIndex;
356
+ private matchIndex;
357
+ private timer;
358
+ private blockProposalTimer;
359
+ onBlockCommitted: ((block: Block) => void) | null;
360
+ onTransactionReceived: ((tx: Transaction) => void) | null;
361
+ constructor(opts: RaftNodeOptions);
362
+ start(): void;
363
+ stop(): void;
364
+ isLeader(): boolean;
365
+ getLeaderId(): string | null;
366
+ getState(): ConsensusState;
367
+ /** Called by the node to propose a block (only works when leader). */
368
+ proposeBlock(block: Block): void;
369
+ /** Forward a transaction to the current leader. */
370
+ forwardToLeader(tx: Transaction): void;
371
+ private startElection;
372
+ private _voteCallback;
373
+ private becomeLeader;
374
+ private stepDown;
375
+ private handleRequestVote;
376
+ private handleRequestVoteReply;
377
+ private isLogUpToDate;
378
+ private sendHeartbeats;
379
+ private sendAppendEntries;
380
+ private handleAppendEntries;
381
+ private sendAppendEntriesReply;
382
+ private handleAppendEntriesReply;
383
+ private advanceCommitIndex;
384
+ private applyCommitted;
385
+ }
386
+
387
+ interface ContractInstance {
388
+ name: string;
389
+ version: string;
390
+ deployedAt: number;
391
+ deployedBy: string;
392
+ }
393
+ /**
394
+ * The context object passed to contract functions.
395
+ * Contracts interact with state exclusively through this interface.
396
+ */
397
+ interface ContractContext {
398
+ /** Read a value from world state. */
399
+ get(key: string): unknown | null;
400
+ /** Write a value to world state. */
401
+ set(key: string, value: unknown): void;
402
+ /** Delete a key from world state. */
403
+ del(key: string): void;
404
+ /** Get the sender (public key) of the current transaction. */
405
+ readonly sender: string;
406
+ /** Get the current block height. */
407
+ readonly blockHeight: number;
408
+ /** Get current timestamp. */
409
+ readonly timestamp: number;
410
+ /** Emit a log entry (stored in tx receipt). */
411
+ log(message: string): void;
412
+ }
413
+ type ContractFunction = (ctx: ContractContext, ...args: unknown[]) => unknown;
414
+ interface ContractModule {
415
+ [methodName: string]: ContractFunction;
416
+ }
417
+
418
+ interface ContextOptions {
419
+ stateStore: StateStore;
420
+ sender: string;
421
+ blockHeight: number;
422
+ timestamp: number;
423
+ }
424
+ /**
425
+ * Creates a ContractContext that contracts use to interact with the ledger.
426
+ * All state mutations go through the StateStore.
427
+ */
428
+ declare function createContractContext(opts: ContextOptions): ContractContext & {
429
+ getLogs(): string[];
430
+ };
431
+
432
+ /**
433
+ * Compile contract source code into a callable module.
434
+ *
435
+ * Contracts are JavaScript/TypeScript functions. The source code should export
436
+ * methods as properties of a returned object. Example:
437
+ *
438
+ * ```
439
+ * return {
440
+ * init(ctx) { ctx.set("counter", 0); },
441
+ * increment(ctx, amount) {
442
+ * const val = ctx.get("counter") || 0;
443
+ * ctx.set("counter", val + amount);
444
+ * return val + amount;
445
+ * }
446
+ * }
447
+ * ```
448
+ */
449
+ declare function compileContract(source: string): ContractModule;
450
+ /**
451
+ * Execute a contract method with a context and arguments.
452
+ * Enforces a timeout to prevent infinite loops.
453
+ */
454
+ declare function executeContract(mod: ContractModule, method: string, ctx: ContractContext, args?: unknown[]): unknown;
455
+
456
+ /**
457
+ * Manages deployed contracts. Contract metadata and code are stored in world state
458
+ * with reserved key prefixes.
459
+ */
460
+ declare class ContractRegistry {
461
+ private compiled;
462
+ private stateStore;
463
+ constructor(stateStore: StateStore);
464
+ /** Deploy a new contract or upgrade an existing one. */
465
+ deploy(name: string, version: string, code: string, deployedBy: string, blockHeight: number): void;
466
+ /** Get a compiled contract module, loading from state if needed. */
467
+ getModule(name: string): ContractModule | null;
468
+ /** Get contract metadata. */
469
+ getInstance(name: string): ContractInstance | null;
470
+ /** Invoke a method on a deployed contract. */
471
+ invoke(contractName: string, method: string, ctx: ContractContext, args?: unknown[]): unknown;
472
+ /** List all deployed contracts. */
473
+ listContracts(): ContractInstance[];
474
+ /** Clear compiled cache (useful after chain reorg). */
475
+ clearCache(): void;
476
+ }
477
+
478
+ /**
479
+ * Built-in contract: token transfer.
480
+ *
481
+ * State keys: "balance:<address>" -> number
482
+ */
483
+ declare const TRANSFER_CONTRACT = "\nreturn {\n init(ctx, initialBalance) {\n ctx.set(\"balance:\" + ctx.sender, initialBalance || 0);\n ctx.log(\"Account initialized with balance: \" + (initialBalance || 0));\n },\n\n mint(ctx, amount) {\n if (typeof amount !== \"number\" || amount <= 0) throw new Error(\"Invalid amount\");\n const key = \"balance:\" + ctx.sender;\n const current = ctx.get(key) || 0;\n ctx.set(key, current + amount);\n ctx.log(\"Minted \" + amount + \" to \" + ctx.sender);\n return current + amount;\n },\n\n transfer(ctx, to, amount) {\n if (typeof amount !== \"number\" || amount <= 0) throw new Error(\"Invalid amount\");\n if (!to) throw new Error(\"Recipient required\");\n\n const fromKey = \"balance:\" + ctx.sender;\n const toKey = \"balance:\" + to;\n\n const fromBalance = ctx.get(fromKey) || 0;\n if (fromBalance < amount) throw new Error(\"Insufficient balance\");\n\n const toBalance = ctx.get(toKey) || 0;\n ctx.set(fromKey, fromBalance - amount);\n ctx.set(toKey, toBalance + amount);\n ctx.log(\"Transfer \" + amount + \" from \" + ctx.sender + \" to \" + to);\n },\n\n balance(ctx, address) {\n const key = \"balance:\" + (address || ctx.sender);\n return ctx.get(key) || 0;\n }\n}\n";
484
+ /**
485
+ * Built-in contract: simple key-value store with ownership.
486
+ */
487
+ declare const KV_STORE_CONTRACT = "\nreturn {\n set(ctx, key, value) {\n const fullKey = \"kv:\" + key;\n const existing = ctx.get(fullKey + \":owner\");\n if (existing && existing !== ctx.sender) throw new Error(\"Not owner of key: \" + key);\n ctx.set(fullKey, value);\n ctx.set(fullKey + \":owner\", ctx.sender);\n },\n\n get(ctx, key) {\n return ctx.get(\"kv:\" + key);\n },\n\n del(ctx, key) {\n const owner = ctx.get(\"kv:\" + key + \":owner\");\n if (owner && owner !== ctx.sender) throw new Error(\"Not owner of key: \" + key);\n ctx.del(\"kv:\" + key);\n ctx.del(\"kv:\" + key + \":owner\");\n }\n}\n";
488
+
489
+ declare enum ProposalType {
490
+ AddPeer = "add-peer",
491
+ RemovePeer = "remove-peer",
492
+ UpdateConfig = "update-config",
493
+ UpgradeContract = "upgrade-contract",
494
+ Custom = "custom"
495
+ }
496
+ declare enum ProposalStatus {
497
+ Active = "active",
498
+ Approved = "approved",
499
+ Rejected = "rejected",
500
+ Expired = "expired"
501
+ }
502
+ interface Proposal {
503
+ id: string;
504
+ type: ProposalType;
505
+ title: string;
506
+ description: string;
507
+ proposer: string;
508
+ action: Record<string, unknown>;
509
+ status: ProposalStatus;
510
+ createdAt: number;
511
+ expiresAt: number;
512
+ votes: Record<string, boolean>;
513
+ }
514
+
515
+ /**
516
+ * Governor: manages the lifecycle of governance proposals.
517
+ * Proposals are stored in world state with a reserved key prefix.
518
+ */
519
+ declare class Governor {
520
+ private stateStore;
521
+ constructor(stateStore: StateStore);
522
+ /** Create and store a new proposal. */
523
+ propose(opts: {
524
+ type: ProposalType;
525
+ title: string;
526
+ description: string;
527
+ proposer: string;
528
+ action: Record<string, unknown>;
529
+ blockHeight: number;
530
+ votingPeriodMs?: number;
531
+ }): Proposal;
532
+ /** Cast a vote on a proposal. */
533
+ vote(proposalId: string, voter: string, approve: boolean, blockHeight: number): Proposal;
534
+ /**
535
+ * Evaluate a proposal: check if it should be approved, rejected, or expired.
536
+ * Returns the action to execute if approved, null otherwise.
537
+ */
538
+ evaluate(proposalId: string, totalVoters: number, blockHeight: number): {
539
+ status: ProposalStatus;
540
+ action: Record<string, unknown> | null;
541
+ };
542
+ /** Get a proposal by ID. */
543
+ getProposal(proposalId: string): Proposal | null;
544
+ /** List all proposals. */
545
+ listProposals(): Proposal[];
546
+ }
547
+
548
+ interface CreateNodeOptions {
549
+ dataDir?: string;
550
+ config?: Partial<MiniLedgerConfig>;
551
+ }
552
+ declare class MiniLedgerNode extends EventEmitter {
553
+ readonly config: MiniLedgerConfig;
554
+ readonly log: pino.Logger;
555
+ private db;
556
+ private blockStore;
557
+ private stateStore;
558
+ private txStore;
559
+ private chain;
560
+ private keyPair;
561
+ private nodeId;
562
+ private blockTimer;
563
+ private peerManager;
564
+ private blockSync;
565
+ private raft;
566
+ private raftBlockTimer;
567
+ private contractRegistry;
568
+ private governor;
569
+ private startedAt;
570
+ private running;
571
+ constructor(config: MiniLedgerConfig);
572
+ /** Create a new MiniLedgerNode with optional overrides. */
573
+ static create(options?: CreateNodeOptions): Promise<MiniLedgerNode>;
574
+ /** Initialize the node: open DB, load or create identity, restore chain. */
575
+ init(): Promise<void>;
576
+ /** Start the node: begin producing blocks and serving API. */
577
+ start(): Promise<void>;
578
+ /** Stop the node. */
579
+ stop(): Promise<void>;
580
+ private startNetworking;
581
+ private startRaftConsensus;
582
+ private handleBlockAnnounce;
583
+ private handleTxBroadcast;
584
+ /** Apply a block received from network/consensus. */
585
+ private applyReceivedBlock;
586
+ /** Submit a transaction to the node. */
587
+ submit(params: {
588
+ type?: TxType;
589
+ key?: string;
590
+ value?: unknown;
591
+ payload?: Transaction["payload"];
592
+ }): Promise<Transaction>;
593
+ /** Produce a block in solo mode. */
594
+ private produceBlock;
595
+ /** Produce a block as Raft leader — propose it through consensus. */
596
+ private produceRaftBlock;
597
+ /** Apply a single transaction to the world state. */
598
+ private applyTransaction;
599
+ query(sql: string, params?: unknown[]): Promise<Record<string, unknown>[]>;
600
+ getState(key: string): Promise<StateEntry | null>;
601
+ getBlock(height: number): Promise<Block | null>;
602
+ getLatestBlock(): Promise<Block | null>;
603
+ getTransaction(hash: string): Promise<Transaction | null>;
604
+ getStatus(): NodeStatus;
605
+ getPublicKey(): string;
606
+ getNodeId(): string;
607
+ getStores(): {
608
+ blocks: BlockStore;
609
+ state: StateStore;
610
+ txs: TxStore;
611
+ };
612
+ getDatabase(): MiniLedgerDB;
613
+ getPeerManager(): PeerManager | null;
614
+ getRaft(): RaftNode | null;
615
+ getContractRegistry(): ContractRegistry;
616
+ getGovernor(): Governor;
617
+ isRunning(): boolean;
618
+ }
619
+
620
+ interface KeyPair {
621
+ publicKey: string;
622
+ privateKey: string;
623
+ }
624
+ /** Generate a new Ed25519 keypair. */
625
+ declare function generateKeyPair(): KeyPair;
626
+
627
+ /** Sign a message (hex string or raw string) with a private key. Returns hex signature. */
628
+ declare function sign(message: string, privateKeyHex: string): string;
629
+ /** Verify a signature against a message and public key. */
630
+ declare function verify(message: string, signatureHex: string, publicKeyHex: string): boolean;
631
+
632
+ /**
633
+ * In-memory chain manager. Tracks the current chain tip and validates new blocks.
634
+ * Backed by a storage layer for persistence.
635
+ */
636
+ declare class Chain {
637
+ private tip;
638
+ private height;
639
+ /** Initialize chain with genesis block or restore from latest block. */
640
+ init(latestBlock?: Block): void;
641
+ /** Create and return a genesis block. */
642
+ createGenesis(proposer: HexString): Block;
643
+ /** Propose a new block with the given transactions. Returns unsigned block. */
644
+ proposeBlock(transactions: Transaction[], proposer: HexString, stateRoot: HexString): Block;
645
+ /** Validate and append a block to the chain. */
646
+ appendBlock(block: Block): void;
647
+ getTip(): Block | null;
648
+ getHeight(): number;
649
+ }
650
+
651
+ interface ACLPolicy {
652
+ /** Public key of the record owner. */
653
+ owner: string;
654
+ /** Public keys that can read this record. Empty = owner only. */
655
+ readers: string[];
656
+ /** Public keys that can write this record. Empty = owner only. */
657
+ writers: string[];
658
+ /** If true, anyone can read (value is unencrypted). */
659
+ public: boolean;
660
+ }
661
+ declare function createACL(owner: string, opts?: {
662
+ readers?: string[];
663
+ writers?: string[];
664
+ public?: boolean;
665
+ }): ACLPolicy;
666
+ /** Check if an identity can read a record. */
667
+ declare function canRead(acl: ACLPolicy, publicKey: string): boolean;
668
+ /** Check if an identity can write/update a record. */
669
+ declare function canWrite(acl: ACLPolicy, publicKey: string): boolean;
670
+
671
+ interface EncryptedRecord {
672
+ /** The encrypted value (hex). */
673
+ ciphertext: string;
674
+ /** Per-recipient encrypted AES keys: publicKey -> encryptedAESKey (hex). */
675
+ encryptedKeys: Record<string, string>;
676
+ /** The ACL policy (stored in plaintext for access control). */
677
+ acl: ACLPolicy;
678
+ /** The sender's public key (needed for decryption key derivation). */
679
+ senderPublicKey: string;
680
+ }
681
+ /**
682
+ * Encrypt a value for a set of authorized readers.
683
+ * Each reader gets a copy of the AES key encrypted with a pairwise secret
684
+ * derived from the sender's and reader's public keys.
685
+ */
686
+ declare function encryptForACL(value: string, acl: ACLPolicy, _senderPrivateKey: string, senderPublicKey?: string): EncryptedRecord;
687
+ /**
688
+ * Decrypt a record if the reader has access.
689
+ * Uses the pairwise secret between the sender and reader's public keys.
690
+ */
691
+ declare function decryptRecord(record: EncryptedRecord, _readerPrivateKey: string, readerPublicKey: string): string | null;
692
+
693
+ interface GovernancePolicy {
694
+ /** Minimum quorum percentage (0-100) for proposals to pass. */
695
+ quorumPercent: number;
696
+ /** Default voting period in milliseconds. */
697
+ votingPeriodMs: number;
698
+ /** Proposal types that any member can create. */
699
+ allowedProposalTypes: ProposalType[];
700
+ }
701
+
702
+ export { type ACLPolicy, type Block, Chain, type ContractContext, type ContractInstance, type ContractModule, ContractRegistry, type CreateNodeOptions, type EncryptedRecord, type GovernancePolicy, Governor, type HexString, KV_STORE_CONTRACT, type KeyPair, MiniLedgerNode as MiniLedger, MiniLedgerNode, type NodeIdentity, type NodeStatus, type PeerInfo, type Proposal, ProposalStatus, ProposalType, type StateEntry, TRANSFER_CONTRACT, type Transaction, type TxPayload, TxType, canRead, canWrite, compileContract, createACL, createContractContext, decryptRecord, encryptForACL, executeContract, generateKeyPair, sign, verify };