Files
zeekayandHanzo Dev 4abf37c952 config: K is a number of draws, not a headcount
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]>
2026-08-16 11:42:58 -07:00

74 lines
2.9 KiB
Go

// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package config
import "fmt"
// Profile is the user-facing chain security profile — a single string
// knob set in chain config (YAML) or env (CONSENSUS_PROFILE). One
// config selects one Profile; the wire byte that gets pinned into the
// cert envelope is derived from the string at config-load time.
//
// Three canonical bundles. PQ mode is binary: a chain is strict (or
// fips, the FIPS-aligned superset of strict) or it is permissive. There
// is no "hybrid" / "bls" / "classical" pseudo-profile — chains that
// genuinely need classical primitives pin
// ForkClassicalCompatUnsafeProfile (0x80) at the byte layer and own the
// audit consequence.
//
// strict — refuses every classical primitive at every layer:
// ecrecover, sha256, ripemd, blake2F, alt_bn128,
// BLS12-381 pairings, KZG, X25519. Production PQ chains.
// permissive — testnet / devnet. Strict-PQ schemes still pinned, but
// dev backends + fallbacks permitted. Never mainnet.
// fips — strict + every primitive restricted to FIPS-203 / 204
// / 205 with the canonical P3Q backend. FIPS-targeted
// deployments.
//
// Operators write the string in config files; protocol layers that
// need the wire byte call WireByte() (or read ProfileID off the
// resolved ChainSecurityProfile struct).
type Profile string
const (
profileStrict Profile = "strict"
profilePermissive Profile = "permissive"
profileFIPS Profile = "fips"
)
// String implements fmt.Stringer.
func (p Profile) String() string { return string(p) }
// IsStrict reports whether this profile enforces strict crypto policy
// — i.e. refuses every classical primitive at every layer. Single
// canonical entry point for "should this chain install AllForbidden()
// at the EVM precompile boundary?".
//
// Strict and FIPS return true; Permissive returns false.
func (p Profile) IsStrict() bool {
return p == profileStrict || p == profileFIPS
}
// Resolve returns the canonical ChainSecurityProfile for this Profile.
// The returned profile passes Validate() by construction; the caller
// receives a fresh pointer copy and may retain it without affecting
// other callers. Canonical immutable values live in profiles.go.
//
// Returns a typed error for unknown / empty profiles so chain-config
// loaders fail loudly at boot.
func (p Profile) Resolve() (*ChainSecurityProfile, error) {
switch p {
case profileStrict:
return StrictPQ(), nil
case profilePermissive:
return Permissive(), nil
case profileFIPS:
return FIPS(), nil
case "":
return nil, fmt.Errorf("consensus profile is empty; must be one of: permissive, strict, fips")
default:
return nil, fmt.Errorf("unknown consensus profile %q; must be one of: permissive, strict, fips", string(p))
}
}