/** * @fileoverview Ledger — append-only blockchain ledger persisted as * RawDataBlock instances within any IBlockStore implementation. * * Each entry is serialized, padded to BlockSize, and stored as a RawDataBlock. * A metadata block tracks the chain head, length, ledger ID, and the full * sequenceNumber → blockChecksum index for cold-start reconstruction. * * The metadata block is stored at a deterministic Checksum derived from the * ledgerId so that `Ledger.load()` can locate it without external state. * Because the metadata content changes on each append while the storage key * stays fixed, the metadata block uses a validation-exempt RawDataBlock * subclass that allows a caller-specified checksum to differ from the * content hash. * * Governance support: the Ledger enforces role-based access control via an * AuthorizedSignerSet. The genesis entry must be a governance genesis payload * containing the initial signer set and quorum policy. Subsequent governance * entries require admin role and quorum satisfaction. * * @see Design: Block Chain Ledger — Ledger * @see Requirements 5.1–5.6, 6.1–6.6, 7.1–7.5, 10.1–10.4, 11.2–11.4, * 12.2–12.8, 13.1–13.9, 14.1–14.7, 15.1–15.5, 17.6–17.9, 18.5–18.7 */ import { SignatureUint8Array } from '@digitaldefiance/ecies-lib'; import { BlockSize } from '../enumerations/blockSize'; import { IAuthorizedSigner } from '../interfaces/ledger/authorizedSigner'; import { IBrightTrustPolicy } from '../interfaces/ledger/brightTrustPolicy'; import { IConsistencyProof } from '../interfaces/ledger/consistencyProof'; import { IGovernanceAction } from '../interfaces/ledger/governanceAction'; import { ILedgerEntry } from '../interfaces/ledger/ledgerEntry'; import { ILedgerSigner } from '../interfaces/ledger/ledgerSigner'; import { IMerkleProof } from '../interfaces/ledger/merkleProof'; import { IProofVerificationResult } from '../interfaces/ledger/proofVerificationResult'; import { IBlockStore } from '../interfaces/storage/blockStore'; import { Checksum } from '../types/checksum'; import { GovernancePayloadSerializer } from './governancePayloadSerializer'; import { LedgerEntrySerializer } from './ledgerEntrySerializer'; /** * Append-only blockchain ledger backed by an IBlockStore. * * Maintains an in-memory index (sequenceNumber → block Checksum) for O(1) * lookups and persists a metadata block after each append for cold-start * reconstruction via `Ledger.load()`. * * Enforces role-based access control via an AuthorizedSignerSet when * governance is enabled (i.e., when a GovernancePayloadSerializer is provided). */ export declare class Ledger { private readonly store; private readonly blockSize; private readonly serializer; private readonly ledgerId; private readonly governanceSerializer?; private readonly index; private readonly checksumService; private merkleTree; private _length; private _head; private _headEntryHash; private _authorizedSignerSet; constructor(store: IBlockStore, blockSize: BlockSize, serializer: LedgerEntrySerializer, ledgerId: string, governanceSerializer?: GovernancePayloadSerializer | undefined); /** Current chain length. */ get length(): number; /** Head block Checksum (block storage key), or null if empty. */ get head(): Checksum | null; /** Current quorum policy, or undefined if governance not initialized. */ get brightTrustPolicy(): IBrightTrustPolicy | undefined; /** * Current Merkle root, or null if the ledger is empty. * @see Requirements 9.1, 7.1 */ get merkleRoot(): Checksum | null; /** * Append a new entry to the ledger. * Returns the Checksum of the stored block. * * If governance is enabled: * - Genesis entry (sequenceNumber 0) must be a governance genesis payload. * - Subsequent entries require the signer to be authorized (active admin or writer). */ append(payload: Uint8Array, signer: ILedgerSigner): Promise; /** * Append a governance entry. Requires admin role and quorum satisfaction. * * @param actions - Governance actions to apply * @param primarySigner - The primary signer (must be active admin) * @param cosigners - Additional signers for quorum (each provides their own signature over the actions) */ appendGovernance(actions: IGovernanceAction[], primarySigner: ILedgerSigner, cosigners?: { signer: ILedgerSigner; signature: SignatureUint8Array; }[]): Promise; /** Get info about a specific signer. */ getSignerInfo(publicKey: Uint8Array): IAuthorizedSigner | undefined; /** Get the full current authorized signer set. */ getAuthorizedSigners(): IAuthorizedSigner[]; /** * Get entry by sequence number. * @throws LedgerError if not found */ getEntry(sequenceNumber: number): Promise; /** * Get entries in range [start, end] inclusive. * @throws LedgerError for invalid range */ getEntries(start: number, end: number): Promise; /** * Get the most recent entry, or null if empty. */ getLatestEntry(): Promise; /** * Get an inclusion proof for the entry at the given sequence number. * * @param sequenceNumber - Zero-based sequence number of the entry * @returns An IMerkleProof with the authentication path from leaf to root * @throws LedgerError(MerkleProofFailed) if sequenceNumber is out of range * * @see Requirements 9.2, 18.1 */ getInclusionProof(sequenceNumber: number): IMerkleProof; /** * Get a consistency proof between an earlier length and the current length. * * @param earlierLength - The earlier ledger length M * @returns An IConsistencyProof with the intermediate hashes * @throws LedgerError(ConsistencyProofFailed) if earlierLength > current length * * @see Requirements 9.3 */ getConsistencyProof(earlierLength: number): IConsistencyProof; /** * Handle the genesis entry for a governance-enabled ledger. * The payload must be a governance genesis payload containing the * initial signer set and quorum policy. */ private appendGenesisEntry; /** * Internal append logic shared by regular and governance appends. * Does NOT check authorization — callers must do that first. */ private appendInternal; /** * Compute the deterministic storage key for this ledger's metadata block. * The key is SHA3-512("ledger-meta:" + ledgerId). */ private computeMetadataKey; /** * Serialize the current metadata into binary. * * Format (version 0x0001): * magic 4 bytes 0x4C4D4554 ("LMET") * version 2 bytes 0x0001 * ledgerIdLength 4 bytes uint32 BE * ledgerId var UTF-8 * length 4 bytes uint32 BE * hasHead 1 byte 0x00 or 0x01 * headChecksum 0|64 SHA3-512 bytes (block checksum of head) * hasMerkleRoot 1 byte 0x00 or 0x01 * merkleRoot 0|64 SHA3-512 Merkle root * frontierCount 2 bytes uint16 BE — number of frontier hashes * frontier var frontierCount × 64 bytes SHA3-512 hashes * index entries var seqNum (uint32 BE) + blockChecksum (64 bytes) * * @see Requirements 7.1, 7.3, 8.1, 8.4 */ private serializeMetadata; /** * Persist the metadata block to the store under a deterministic key. */ private persistMetadata; /** * Verify an inclusion proof against a Merkle root. * Static — no Ledger instance needed. Suitable for light clients. * * Recomputes hashes from the leaf to the root using the proof path, * then compares the result to the provided Merkle root. * * @param proof - The inclusion proof to verify * @param merkleRoot - The expected Merkle root * @returns An IProofVerificationResult indicating success or failure * * @see Requirements 9.4, 4.1, 4.2, 4.3, 4.4, 15.3, 18.3 */ static verifyInclusionProof(proof: IMerkleProof, merkleRoot: Checksum): IProofVerificationResult; /** * Verify a consistency proof between two Merkle roots. * Static — no Ledger instance needed. * * Implements the RFC 6962 Section 2.1.4 consistency proof verification * algorithm. The proof hashes are consumed in order to reconstruct both * the earlier and later roots. * * @param proof - The consistency proof to verify * @param earlierRoot - The Merkle root at the earlier length * @param laterRoot - The Merkle root at the later length * @param earlierLength - The earlier ledger length M * @param laterLength - The later ledger length N * @returns An IProofVerificationResult indicating success or failure * * @see Requirements 9.5, 6.1, 6.2, 6.3 */ static verifyConsistencyProof(proof: IConsistencyProof, earlierRoot: Checksum, laterRoot: Checksum, earlierLength: number, laterLength: number): IProofVerificationResult; /** * Hash two child nodes: SHA3-512(left || right). * Helper for static verification methods. */ private static hashChildren; /** * Load ledger state from the block store. * * 1. Compute metadata block key from ledgerId * 2. Try to retrieve metadata block from store * 3. If not found, return empty ledger * 4. Parse metadata to get head checksum, length, and index * 5. Restore headEntryHash by reading the head entry * 6. Replay governance entries to reconstruct AuthorizedSignerSet * 7. Return populated ledger */ static load(store: IBlockStore, blockSize: BlockSize, serializer: LedgerEntrySerializer, ledgerId: string, governanceSerializer?: GovernancePayloadSerializer): Promise; /** * Replay all entries from genesis to head to reconstruct the * AuthorizedSignerSet from governance entries. */ private replayGovernance; /** * Restore the Merkle tree during `Ledger.load()`. * * Strategy: * 1. If a Merkle root and frontier are persisted, attempt fast restoration * via `IncrementalMerkleTree.fromFrontier()`. Verify the restored root * matches the persisted root. * 2. On mismatch, fall back to full reconstruction from entry hashes. * 3. If no Merkle root in metadata, rebuild from all entry hashes. * * @see Requirements 7.2, 8.2, 8.3 */ private restoreMerkleTree; /** * Parse a metadata block's binary content. * * Returns the parsed fields including Merkle root and frontier * so that `Ledger.load()` can restore the Merkle tree state. * * @see Requirements 7.2, 7.3, 8.1, 8.4 */ private static parseMetadata; } //# sourceMappingURL=ledger.d.ts.map