mirror of
https://github.com/luxfi/consensus.git
synced 2026-09-24 00:26:58 +00:00
THE TOTAL. `Register` in `validator/registration.go` runs seven clauses, and this port had six. The seventh is `math.Add64` on the running weight: a set whose weights sum past what a `uint64` holds is refused entire, before any of it is admitted. Without it `total_stake` folded with `saturating_add` and `voted_stake` summed with `saturating_add`, so a set of four validators at 2^63 reported `u64::MAX` staked AND `u64::MAX` voted — and `u64::MAX > floor(2·u64::MAX/3)`. A certificate carrying HALF the stake cleared the two-thirds export floor, which is the rung a bridge reads. Clamping is what makes the two sides agree on a number neither can represent, so neither clamps any more. The set carries its total as a fact established at admission with `checked_add` — one source of truth, decided at the one place that still has a registration to refuse — and `voted_stake` is checked too: the voters are a subset of the members, so against a set this crate admitted the overflow is unreachable, and reaching it is evidence about the stake source rather than about the votes. `weighted_quasar` takes the same treatment: a vector with no representable total leaves by the same door as a zero total, since a plain sum WRAPS to a small total and sizes the count gate below the stake quorum it exists to anticipate. THE SET. `insert` admits one validator and reports on that one, which is a property no set can be built out of: a caller that inserts in a loop and logs what failed is left holding a set missing a member, with `n` short by one and the total short by that member's stake — the two numbers every threshold is read against, wrong, with nothing to say so. `ValidatorSet::register` is the port of Go's whole-set door: sorted by node id so which duplicate a set is refused on cannot depend on the order a caller built its input, and all-or-nothing because the set under construction is a local. `insert` stays for a membership change to a set already live, and now says so. THE BINDING. `pkg/typescript` path-depends on `pkg/rust` and could not compile: narrowing the node id from 32 bytes to 20 left two `NodeID::from([u8; 32])` calls with no impl. It is a 20-byte API now, and the 20 bytes come from 20-byte input — `arr[..20]` would reintroduce exactly the collision the narrowing exists to prevent, so a hex string of the wrong length is refused, never trimmed. The width is `size_of::<cert::NodeId>()`, taken from the core rather than restated, so it cannot drift again. `addValidator` also stopped discarding the core's answer, which reported a validator as added when the set had refused it. And the two crates are one workspace, because that is what they already were. Built separately they can disagree and did — nothing built both, so nothing noticed. `cargo test` at the root now builds both, which turns this class of break from a review finding into a compile error. It also surfaced two tests in the binding that had been failing unnoticed: they voted with unregistered ids, from before only members were counted. No signed byte moves. `canonical_vote_message` is untouched, the node id enters no signed message, and the Go corpus and the frozen `pop` vectors pass unchanged. Verified: 97 tests green (92 rust, 5 napi), clippy clean at the workspace root, the binding's 29 JS tests green against a linux build of it. Red's PoC — four validators of 2^63, two voting, read at QUASAR — returned `Ok(())` before and now dies at registration with `WeightOverflow`. Co-authored-by: Hanzo Dev <[email protected]>
163 lines
5.2 KiB
TypeScript
163 lines
5.2 KiB
TypeScript
/* tslint:disable */
|
||
/* eslint-disable */
|
||
|
||
/* auto-generated by NAPI-RS */
|
||
|
||
/** Block status */
|
||
export const enum BlockStatus {
|
||
Unknown = 0,
|
||
Processing = 1,
|
||
Rejected = 2,
|
||
Accepted = 3
|
||
}
|
||
/** Vote type */
|
||
export const enum VoteType {
|
||
/** Preference vote during sampling */
|
||
Preference = 0,
|
||
/** Commit vote for finalization */
|
||
Commit = 1,
|
||
/** Cancel/reject vote */
|
||
Cancel = 2
|
||
}
|
||
/** Complete Quasar consensus configuration */
|
||
export interface QuasarConfig {
|
||
/** Sample/committee size (default: 20) */
|
||
k: number
|
||
/** Fixed threshold ratio 0.5-0.8 (default: 0.69 = 69%) */
|
||
alpha: number
|
||
/** Consecutive rounds for finality (default: 20) */
|
||
beta: number
|
||
/** Round timeout in milliseconds (default: 100) */
|
||
roundTimeoutMs: number
|
||
/** Enable FPC adaptive thresholds (default: true) */
|
||
enableFpc: boolean
|
||
/** Minimum FPC threshold (default: 0.5) */
|
||
thetaMin: number
|
||
/** Maximum FPC threshold (default: 0.8) */
|
||
thetaMax: number
|
||
/** PRF seed for FPC (32-byte hex, optional) */
|
||
fpcSeed?: string
|
||
/** Base luminance in lux (default: 100.0) */
|
||
baseLuminance: number
|
||
/** Maximum luminance (default: 1000.0) */
|
||
maxLuminance: number
|
||
/** Minimum luminance (default: 10.0) */
|
||
minLuminance: number
|
||
/** Success multiplier (default: 1.1) */
|
||
successMultiplier: number
|
||
/** Failure multiplier (default: 0.9) */
|
||
failureMultiplier: number
|
||
/** Network timeout in milliseconds (default: 5000) */
|
||
networkTimeoutMs: number
|
||
/** Max message size in bytes (default: 2MB) */
|
||
maxMessageSize: number
|
||
/** Max outstanding polls (default: 10) */
|
||
maxOutstanding: number
|
||
/** Security level: 2=Low, 3=Medium, 5=High (default: 3) */
|
||
securityLevel: number
|
||
/** Enable quantum-resistant signatures (default: true) */
|
||
quantumResistant: boolean
|
||
/** Enable GPU acceleration (default: true) */
|
||
gpuAcceleration: boolean
|
||
}
|
||
/** Block data for consensus */
|
||
export interface Block {
|
||
/** Block ID (32-byte hex string) */
|
||
id: string
|
||
/** Parent block ID (32-byte hex string) */
|
||
parentId: string
|
||
/** Block height */
|
||
height: number
|
||
/** Block payload (hex-encoded) */
|
||
payload: string
|
||
/** Block timestamp (Unix milliseconds) */
|
||
timestamp: number
|
||
}
|
||
/** Vote on a block */
|
||
export interface Vote {
|
||
/** Block ID being voted on (hex string) */
|
||
blockId: string
|
||
/** Vote type (0=Preference, 1=Commit, 2=Cancel) */
|
||
voteType: number
|
||
/** Voter node ID (20-byte hex string) */
|
||
voter: string
|
||
/** Optional signature (hex string) */
|
||
signature?: string
|
||
}
|
||
/** Generate a random 32-byte block ID */
|
||
export declare function generateBlockId(): string
|
||
/** Create default Quasar configuration (production) */
|
||
export declare function defaultConfig(): QuasarConfig
|
||
/** Create testnet configuration (lower thresholds for testing) */
|
||
export declare function testnetConfig(): QuasarConfig
|
||
/** Create mainnet configuration (production settings) */
|
||
export declare function mainnetConfig(): QuasarConfig
|
||
/** Get SDK version */
|
||
export declare function version(): string
|
||
/** Compute FPC threshold for given parameters */
|
||
export declare function fpcThreshold(phase: number, k: number, thetaMin: number, thetaMax: number): number
|
||
/** Block identifier (32-byte hex string) */
|
||
export declare class BlockId {
|
||
constructor(hexString: string)
|
||
/** Create zero ID (genesis parent) */
|
||
static zero(): BlockId
|
||
toHex(): string
|
||
toBytes(): Array<number>
|
||
isZero(): boolean
|
||
}
|
||
/** Node identifier (20-byte hex string) */
|
||
export declare class NodeId {
|
||
constructor(hexString: string)
|
||
toHex(): string
|
||
toBytes(): Array<number>
|
||
}
|
||
/**
|
||
* FPC (Fast Probabilistic Consensus) threshold selector
|
||
*
|
||
* Computes phase-dependent thresholds using PRF-based selection.
|
||
* Formula: α(phase, k) = ⌈θ(phase) · k⌉
|
||
*/
|
||
export declare class FpcSelector {
|
||
/** Create a new FPC selector with given theta range */
|
||
constructor(thetaMin: number, thetaMax: number, seedHex?: string | undefined | null)
|
||
/** Get the threshold for a given phase and k */
|
||
threshold(phase: number, k: number): number
|
||
/** Get theta value for a phase (between theta_min and theta_max) */
|
||
theta(phase: number): number
|
||
}
|
||
/**
|
||
* Lux Quasar Consensus Engine for TypeScript/Node.js
|
||
*
|
||
* Thread-safe consensus engine with native performance.
|
||
* Implements complete Quasar consensus with:
|
||
* - Wave: Threshold voting with FPC support
|
||
* - FPC: Fast Probabilistic Consensus
|
||
* - Photon: Light-based validator sampling
|
||
* - Focus: Confidence accumulation
|
||
* - Quasar: Post-quantum finality
|
||
*/
|
||
export declare class ConsensusEngine {
|
||
/** Create a new consensus engine with configuration */
|
||
constructor(config: QuasarConfig)
|
||
/** Start the consensus engine */
|
||
start(): void
|
||
/** Stop the consensus engine */
|
||
stop(): void
|
||
/** Add a block to the consensus */
|
||
addBlock(block: Block): void
|
||
/** Record a vote on a block */
|
||
recordVote(vote: Vote): void
|
||
/** Check if a block has been accepted */
|
||
isAccepted(blockId: string): boolean
|
||
/** Get the status of a block */
|
||
getStatus(blockId: string): BlockStatus
|
||
/** Process multiple votes in batch (optimized) */
|
||
recordVotesBatch(votes: Array<Vote>): number
|
||
/** Add a validator to the engine, by its 20-byte node id */
|
||
addValidator(nodeId: string, weight: number): void
|
||
/** Get the current configuration */
|
||
getConfig(): QuasarConfig
|
||
/** Get current block height */
|
||
getHeight(): number
|
||
}
|