mirror of
https://github.com/luxfi/consensus.git
synced 2026-09-24 00:26:58 +00:00
The live guard refused to start a chain whose K was below the number of validators that existed. But sampling is stake-weighted: a validator is drawn in proportion to what it staked, however many validators there are, and K only sets how many draws a poll makes. Tying K to the headcount gave a validator holding a billionth of the stake the same standing as one holding a fifth, and refused to boot a producing chain because a small staker had joined. The guard keeps what makes a committee safe — the overlap bound and at least single-fault tolerance — and stops counting heads. The tier floor stays a reported target: refusing to start does not add validators, it pins every node on whatever it last booted. Co-authored-by: Hanzo Dev <[email protected]>
284 lines
12 KiB
Go
284 lines
12 KiB
Go
// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
// profiles.go — canonical ChainSecurityProfile registry.
|
|
//
|
|
// One locked profile per chain. The constants below are the canonical,
|
|
// audit-signed-off-on profile values; their ProfileHash is computed at
|
|
// package init via MustComputeHash and pinned into genesis.
|
|
//
|
|
// To register a new profile:
|
|
//
|
|
// 1. Claim a free ProfileID byte in security_profile.go's numbering table.
|
|
// 2. Add a var <Name>Profile here with every field set explicitly (no
|
|
// zero-value defaults — Validate refuses zero-init).
|
|
// 3. Add the profile to init()'s "compute and pin hash" list.
|
|
// 4. Add a TestProfilesValidate sub-case in security_profile_test.go.
|
|
//
|
|
// Profiles are sorted by ProfileID. New profiles append at the bottom.
|
|
|
|
package config
|
|
|
|
// strictPQProfile is the canonical Lux mainnet locked profile. NIST-
|
|
// aligned PQ across every axis: SHA3-NIST + STARK_FRI_SHA3_PQ identified
|
|
// proofs, raw ML-DSA-65 identity, Pulsar-M-65 finality with M-87 reserved
|
|
// for high-value roots. Five production STARK backends; no dev backends;
|
|
// no classical SNARK wrappers; no fallbacks.
|
|
//
|
|
// This is the only profile a Lux mainnet validator may accept. Operators
|
|
// cannot override; the profile is pinned into genesis by ProfileHash and
|
|
// every cert envelope binds the same hash.
|
|
//
|
|
// ProfileHash is computed in init() — see the bottom of this file.
|
|
var strictPQProfile = ChainSecurityProfile{
|
|
ProfileID: uint32(ProfileStrictPQ),
|
|
ProfileName: ProfileNameStrictPQ,
|
|
HashSuiteID: HashSuiteSHA3NIST,
|
|
IdentitySchemeID: SigSchemeMLDSA65,
|
|
FinalitySchemeID: SigSchemePulsar65,
|
|
HighValueSchemeID: SigSchemePulsar87,
|
|
ProofPolicyID: ProofPolicySTARKFRISHA3PQ,
|
|
AllowedProofBackends: []ProofBackendID{
|
|
ProofBackendSP1CompressedSTARK,
|
|
ProofBackendRISC0SuccinctSTARK,
|
|
ProofBackendP3QSTARKFRISHA3,
|
|
ProofBackendStoneCairoSTARK,
|
|
ProofBackendStwoCircleSTARK,
|
|
},
|
|
AllowedProofFormats: []ProofFormatID{
|
|
ProofFormatSTARKFRIBinaryV1,
|
|
ProofFormatSP1BinaryV1,
|
|
ProofFormatRISC0BinaryV1,
|
|
ProofFormatP3QBinaryV1,
|
|
ProofFormatStoneCairoBinaryV1,
|
|
ProofFormatStwoCircleBinaryV1,
|
|
},
|
|
MinSoundnessBits: 128,
|
|
MinHashOutputBits: 384,
|
|
RequireTransparent: true,
|
|
ForbidPairings: true,
|
|
ForbidKZG: true,
|
|
ForbidTrustedSetup: true,
|
|
ForbidClassicalSNARKs: true,
|
|
ForbidDevProofs: true,
|
|
ForbidFallbacks: true,
|
|
|
|
// E2E PQ axes. Wallet / tx / contract-auth pinned at ML-DSA-65 (FIPS
|
|
// 204 Cat 3), KEM at ML-KEM-768 with ML-KEM-1024 reserved for
|
|
// high-value, recovery at SLH-DSA-192 (FIPS 205 Cat 3 stateless
|
|
// backstop). Every classical 0x90 primitive is refused on every axis.
|
|
WalletSchemeID: WalletSchemeMLDSA65,
|
|
TxSchemeID: TxSchemeMLDSA65,
|
|
ContractAuthID: ContractAuthMLDSA65,
|
|
KeyExchangeID: KeyExchangeMLKEM768,
|
|
HighValueKEM: KeyExchangeMLKEM1024,
|
|
RecoverySchemeID: RecoverySchemeSLHDSA192,
|
|
ForbidECDSAWallets: true,
|
|
ForbidECDSAContractAuth: true,
|
|
ForbidBLSContractAuth: true,
|
|
ForbidClassicalKEM: true,
|
|
RequireTypedTxAuth: true,
|
|
}
|
|
|
|
// permissiveProfile is the testnet/devnet profile. Accepts the strict-PQ
|
|
// production backends plus the dev backends (RISC0_RAW_STARK_DEV,
|
|
// SP1_CORE_STARK_DEV) so iteration is possible without lying about
|
|
// security level. Still refuses classical wrappers and trusted-setup
|
|
// systems. MinSoundnessBits relaxed to 96 — never advertised on mainnet.
|
|
//
|
|
// NOT a Lux strict-PQ profile. Marketing as such is forbidden by
|
|
// `ForbidDevProofs == false`.
|
|
var permissiveProfile = ChainSecurityProfile{
|
|
ProfileID: uint32(ProfilePermissive),
|
|
ProfileName: ProfileNamePermissive,
|
|
HashSuiteID: HashSuiteSHA3NIST,
|
|
IdentitySchemeID: SigSchemeMLDSA65,
|
|
FinalitySchemeID: SigSchemePulsar65,
|
|
HighValueSchemeID: SigSchemePulsar65,
|
|
ProofPolicyID: ProofPolicySTARKFRISHA3PQ,
|
|
AllowedProofBackends: []ProofBackendID{
|
|
ProofBackendSP1CompressedSTARK,
|
|
ProofBackendRISC0SuccinctSTARK,
|
|
ProofBackendP3QSTARKFRISHA3,
|
|
ProofBackendStoneCairoSTARK,
|
|
ProofBackendStwoCircleSTARK,
|
|
},
|
|
AllowedProofFormats: []ProofFormatID{
|
|
ProofFormatSTARKFRIBinaryV1,
|
|
ProofFormatSP1BinaryV1,
|
|
ProofFormatRISC0BinaryV1,
|
|
ProofFormatP3QBinaryV1,
|
|
ProofFormatStoneCairoBinaryV1,
|
|
ProofFormatStwoCircleBinaryV1,
|
|
},
|
|
// Permissive uses the same primitive floors as strict-PQ; the
|
|
// distinction is the *backend allowlist* (above) and the absence
|
|
// of ForbidDevProofs / ForbidFallbacks below.
|
|
MinSoundnessBits: 128,
|
|
MinHashOutputBits: 384,
|
|
RequireTransparent: true,
|
|
ForbidPairings: true,
|
|
ForbidKZG: true,
|
|
ForbidTrustedSetup: true,
|
|
ForbidClassicalSNARKs: true,
|
|
ForbidDevProofs: false, // dev backends OK on testnet/devnet
|
|
ForbidFallbacks: false, // fallbacks OK on testnet/devnet
|
|
|
|
// E2E PQ axes — same lattice scheme defaults as strict-PQ but the
|
|
// Forbid* bits are left false so experimental classical primitives
|
|
// can ride alongside the lattice path on a permissive testnet.
|
|
// RequireTypedTxAuth stays false so legacy testnet clients without
|
|
// the typed-auth byte still round-trip.
|
|
WalletSchemeID: WalletSchemeMLDSA65,
|
|
TxSchemeID: TxSchemeMLDSA65,
|
|
ContractAuthID: ContractAuthMLDSA65,
|
|
KeyExchangeID: KeyExchangeMLKEM768,
|
|
HighValueKEM: KeyExchangeMLKEM1024,
|
|
RecoverySchemeID: RecoverySchemeSLHDSA192,
|
|
ForbidECDSAWallets: false,
|
|
ForbidECDSAContractAuth: false,
|
|
ForbidBLSContractAuth: false,
|
|
ForbidClassicalKEM: false,
|
|
RequireTypedTxAuth: false,
|
|
}
|
|
|
|
// fipsProfile is the FIPS-204-only profile. Drops Pulsar-M (production
|
|
// fork of Corona; not yet FIPS-approved) — but the profile still has to
|
|
// satisfy `FinalitySchemeID.IsPulsarM()`, so for FIPS deployments the
|
|
// chain DOES use Pulsar-M (FIPS 204-compatible output) at M-65 and M-87.
|
|
// Only the canonical P3Q STARK/FRI/SHA3 backend is admitted.
|
|
//
|
|
// Note: this profile is named FIPS but the actual FIPS-only protocol
|
|
// stance the FIPS deployment ships is documented per HIP-0077 §"FIPS
|
|
// profile". This struct is the consensus-layer allow-list; the larger
|
|
// FIPS posture lives in the operator manifest.
|
|
var fipsProfile = ChainSecurityProfile{
|
|
ProfileID: uint32(ProfileFIPS),
|
|
ProfileName: ProfileNameFIPS,
|
|
HashSuiteID: HashSuiteSHA3NIST,
|
|
IdentitySchemeID: SigSchemeMLDSA65,
|
|
FinalitySchemeID: SigSchemePulsar65,
|
|
HighValueSchemeID: SigSchemePulsar87,
|
|
ProofPolicyID: ProofPolicySTARKFRISHA3PQ,
|
|
AllowedProofBackends: []ProofBackendID{
|
|
ProofBackendP3QSTARKFRISHA3,
|
|
},
|
|
AllowedProofFormats: []ProofFormatID{
|
|
ProofFormatP3QBinaryV1,
|
|
},
|
|
MinSoundnessBits: 128,
|
|
MinHashOutputBits: 384,
|
|
RequireTransparent: true,
|
|
ForbidPairings: true,
|
|
ForbidKZG: true,
|
|
ForbidTrustedSetup: true,
|
|
ForbidClassicalSNARKs: true,
|
|
ForbidDevProofs: true,
|
|
ForbidFallbacks: true,
|
|
|
|
// E2E PQ axes — identical to StrictPQ. FIPS deployments demand
|
|
// every E2E layer sit inside FIPS 203/204/205; the canonical FIPS
|
|
// scheme set is the same as the strict-PQ set.
|
|
WalletSchemeID: WalletSchemeMLDSA65,
|
|
TxSchemeID: TxSchemeMLDSA65,
|
|
ContractAuthID: ContractAuthMLDSA65,
|
|
KeyExchangeID: KeyExchangeMLKEM768,
|
|
HighValueKEM: KeyExchangeMLKEM1024,
|
|
RecoverySchemeID: RecoverySchemeSLHDSA192,
|
|
ForbidECDSAWallets: true,
|
|
ForbidECDSAContractAuth: true,
|
|
ForbidBLSContractAuth: true,
|
|
ForbidClassicalKEM: true,
|
|
RequireTypedTxAuth: true,
|
|
}
|
|
|
|
// ForkClassicalCompatUnsafeProfileID is the wire byte for the
|
|
// classical-compat fork profile. Reserved in the 0x80+ downstream block.
|
|
// Any chain that pins this profile MUST NOT be marketed as Lux strict-PQ
|
|
// — Validate accepts it (it satisfies all soundness/forbid invariants)
|
|
// but its ProofPolicyID is one of the explicit classical-PQ STARK
|
|
// policies, and the ChainSecurityProfile.Validate check that refuses a
|
|
// classical ProofPolicyID does NOT apply here because we accept the
|
|
// keccak-merkle STARK policy (PQ-positive but non-NIST-canonical).
|
|
//
|
|
// ForkClassicalCompatUnsafeProfile is provided so audit tooling has a
|
|
// concrete fork-stance to point at in error messages: "this chain
|
|
// pinned the COMPAT_UNSAFE fork profile; the consensus layer is not
|
|
// auditing it as Lux strict-PQ."
|
|
const ForkClassicalCompatUnsafeProfileID uint32 = 0x80
|
|
|
|
// ForkClassicalCompatUnsafeProfile is the locked profile for downstream
|
|
// forks that have an external requirement to maintain compatibility with
|
|
// classical-compat verifiers (e.g. forks tied to an L1 that requires a
|
|
// Groth16 wrapper for cheap EVM verification). The profile uses the
|
|
// non-NIST-canonical STARK_FRI_Keccak policy and allows backends that
|
|
// emit STARKs verifiable under Keccak Merkle trees.
|
|
//
|
|
// CRITICAL: This profile MUST NOT be marketed as "Lux strict-PQ." The
|
|
// ProfileName explicitly says "FORK_CLASSICAL_COMPAT_UNSAFE" so an
|
|
// operator who deploys it cannot accidentally claim the strict-PQ
|
|
// posture; audit tooling matches on ProfileName and refuses to issue
|
|
// "strict-PQ" attestations to a chain on this profile.
|
|
var ForkClassicalCompatUnsafeProfile = ChainSecurityProfile{
|
|
ProfileID: ForkClassicalCompatUnsafeProfileID,
|
|
ProfileName: "FORK_CLASSICAL_COMPAT_UNSAFE",
|
|
HashSuiteID: HashSuiteSHA3NIST,
|
|
IdentitySchemeID: SigSchemeMLDSA65,
|
|
FinalitySchemeID: SigSchemePulsar65,
|
|
HighValueSchemeID: SigSchemePulsar87,
|
|
ProofPolicyID: ProofPolicySTARKFRIKeccak,
|
|
AllowedProofBackends: []ProofBackendID{
|
|
ProofBackendP3QSTARKFRISHA3,
|
|
ProofBackendSP1CompressedSTARK,
|
|
ProofBackendRISC0SuccinctSTARK,
|
|
},
|
|
AllowedProofFormats: []ProofFormatID{
|
|
ProofFormatSTARKFRIBinaryV1,
|
|
ProofFormatSP1BinaryV1,
|
|
ProofFormatRISC0BinaryV1,
|
|
ProofFormatP3QBinaryV1,
|
|
},
|
|
MinSoundnessBits: 128,
|
|
MinHashOutputBits: 384,
|
|
RequireTransparent: true,
|
|
ForbidPairings: true,
|
|
ForbidKZG: true,
|
|
ForbidTrustedSetup: true,
|
|
ForbidClassicalSNARKs: true, // even the fork refuses Groth16/PLONK wrappers
|
|
ForbidDevProofs: true,
|
|
ForbidFallbacks: false, // forks may fall back; strict mainnet may not
|
|
|
|
// E2E PQ axes — the fork explicitly opts INTO classical primitives
|
|
// on every axis so existing EVM-classical wallets / contracts /
|
|
// session keys keep working. RecoverySchemeNone is permitted here
|
|
// because the high-value scheme is Pulsar-M-87 (Cat 5). Every
|
|
// Forbid* bit is false; RequireTypedTxAuth is false. A chain that
|
|
// pins this profile MUST NOT be marketed as Lux strict-PQ.
|
|
WalletSchemeID: WalletSchemeECDSAUnsafe,
|
|
TxSchemeID: TxSchemeECDSAUnsafe,
|
|
ContractAuthID: ContractAuthECDSAUnsafe,
|
|
KeyExchangeID: KeyExchangeX25519Unsafe,
|
|
HighValueKEM: KeyExchangeX25519Unsafe,
|
|
RecoverySchemeID: RecoverySchemeNone,
|
|
ForbidECDSAWallets: false,
|
|
ForbidECDSAContractAuth: false,
|
|
ForbidBLSContractAuth: false,
|
|
ForbidClassicalKEM: false,
|
|
RequireTypedTxAuth: false,
|
|
}
|
|
|
|
// init computes and pins ProfileHash for every canonical profile. Runs
|
|
// at package load; a build whose canonical profiles fail Validate cannot
|
|
// initialise and therefore cannot ship. This is the genesis-pinning
|
|
// guarantee at the binary level.
|
|
//
|
|
// MustComputeHash panics on validation failure; the panic message names
|
|
// the failing profile so a misconfiguration is immediate and visible in
|
|
// the boot log.
|
|
func init() {
|
|
strictPQProfile.ProfileHash = strictPQProfile.MustComputeHash()
|
|
permissiveProfile.ProfileHash = permissiveProfile.MustComputeHash()
|
|
fipsProfile.ProfileHash = fipsProfile.MustComputeHash()
|
|
ForkClassicalCompatUnsafeProfile.ProfileHash = ForkClassicalCompatUnsafeProfile.MustComputeHash()
|
|
}
|