mirror of
https://github.com/luxfi/evm.git
synced 2026-09-24 00:27:44 +00:00
params/extras: StrictPQTimestamp + per-chain PQ posture wiring (CR-2)
Closes CR-2 from the cross-stack adversarial audit: PQ posture on
multi-chain hosts moves from a process-global to a per-chain
ChainConfig setting. A strict-PQ subnet and a permissive L1 in the
same node process now refuse / admit classical primitives correctly,
independent of which subnet last initialised.
Surface:
- NetworkUpgrades.StrictPQTimestamp *uint64 (json:"strictPQTimestamp")
nil = chain is classical-permissive (default for non-Lux integrators)
0 = strict-PQ from genesis (set by plugin/evm when config.PQ=true)
- NetworkUpgrades.IsStrictPQ(time uint64) bool — fork rule
- chainConfigAdapter.IsStrictPQ — bridges params/extras into the
contract.StrictPQReporter contract that stateful precompiles call.
- extChainConfigBridge.IsStrictPQ — type-asserts via a local
interface so non-Lux chains integrating Lux precompiles remain
classical-permissive (no contract widening forced on them).
- plugin/evm: when config.PQ=true, vm.chainConfig.PQ is installed
(covers 0x01..0x0a standard precompiles via runPrecompile gate) AND
extras.StrictPQTimestamp is set to genesis (covers Lux stateful
precompiles — KZG, Groth16, PLONK, fflonk, Halo2, BN254-Pedersen,
BabyJubJub, Pallas/Vesta — via contract.RefuseUnderStrictPQ).
Module bumps:
- github.com/luxfi/geth v1.16.91
(bundles MLDSATxType (0x42) + per-chain PQ profile gate)
- github.com/luxfi/pq v1.0.1-0.20260512064747-3d4c9414e15e
(RefuseUnder promoted to method on *Profile)
No semantic change for chains that do not opt into PQ:
StrictPQTimestamp is nil, IsStrictPQ returns false, every classical
primitive admits, gas accounting unchanged.
This commit is contained in:
@@ -234,3 +234,13 @@ func (c *chainConfigAdapter) IsDurango(time uint64) bool {
|
||||
extra := params.GetExtra(c.config)
|
||||
return extra.IsDurango(time)
|
||||
}
|
||||
|
||||
// IsStrictPQ satisfies the contract.StrictPQReporter interface so that
|
||||
// classical precompiles (KZG, Groth16, PLONK, fflonk, Halo2, BN254-Pedersen,
|
||||
// BabyJubJub, Pallas/Vesta) refuse to run on chains that pin a strict
|
||||
// post-quantum profile. Returns false when StrictPQTimestamp is unset
|
||||
// (the default for non-Lux chains and Lux chains without strict-PQ pinned).
|
||||
func (c *chainConfigAdapter) IsStrictPQ(time uint64) bool {
|
||||
extra := params.GetExtra(c.config)
|
||||
return extra.IsStrictPQ(time)
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ require (
|
||||
github.com/luxfi/crypto v1.18.1
|
||||
github.com/luxfi/database v1.18.1
|
||||
github.com/luxfi/filesystem v0.0.1
|
||||
github.com/luxfi/geth v1.16.90
|
||||
github.com/luxfi/geth v1.16.91
|
||||
github.com/luxfi/go-bip39 v1.1.2
|
||||
github.com/luxfi/gpu v1.0.1
|
||||
github.com/luxfi/ids v1.2.9
|
||||
@@ -92,7 +92,7 @@ require (
|
||||
github.com/luxfi/fhe v1.8.0 // indirect
|
||||
github.com/luxfi/lattice/v7 v7.0.0 // indirect
|
||||
github.com/luxfi/node v1.23.4 // indirect
|
||||
github.com/luxfi/pq v0.0.0-20260511232911-de15d6ce2f0f // indirect
|
||||
github.com/luxfi/pq v1.0.1-0.20260512064747-3d4c9414e15e // indirect
|
||||
github.com/luxfi/protocol v0.0.2 // indirect
|
||||
github.com/luxfi/resource v0.0.1 // indirect
|
||||
github.com/luxfi/threshold v1.5.5 // indirect
|
||||
|
||||
@@ -228,6 +228,8 @@ github.com/luxfi/formatting v1.0.1 h1:ZnE1rAdEUds9yAegdVdGDOBGN6hLMPOv6E03Fp8IEY
|
||||
github.com/luxfi/formatting v1.0.1/go.mod h1:mYzNf5DJOiqSSKUPzNj5dKy4tstFbN3pZlkI5716eKc=
|
||||
github.com/luxfi/geth v1.16.90 h1:LLvhUggNyK2bZSN1gtH3yknVB8YPXcS9sHrqTZwWpfg=
|
||||
github.com/luxfi/geth v1.16.90/go.mod h1:ynzwEXqLboNr1R1wdht/SqYnGINtaU4lYajR779JxgE=
|
||||
github.com/luxfi/geth v1.16.91 h1:1XjCX29Ry6+wrn1Jmu/Ri05QYfgrIRo50Xmn+BP1PSk=
|
||||
github.com/luxfi/geth v1.16.91/go.mod h1:7qZotINLz5Zq3WD2CK2gm6mngscR9x/eSXtfzBzmDjA=
|
||||
github.com/luxfi/go-bip39 v1.1.2 h1:p+wLMPGs6MLQh7q0YIsmy2EhHL7LHiELEGTJko6t/Jg=
|
||||
github.com/luxfi/go-bip39 v1.1.2/go.mod h1:96de9VkR2kY/ASAnhMtvt3TSh+PZkAFAngNj0GjRGDo=
|
||||
github.com/luxfi/gpu v1.0.1 h1:7cDJTwTZ8Wk5KmyzsiGPe1nekyTgsSG6LJ+4rgKim8E=
|
||||
@@ -254,6 +256,8 @@ github.com/luxfi/p2p v1.19.2 h1:uqZq7ofmEDbXlTkv1QThtci01Q+dmDkNmAPeORIyP8E=
|
||||
github.com/luxfi/p2p v1.19.2/go.mod h1:tI9Bt1R0ouvVtJvXG4e20GlGeV4AR230k4mFF9Vglzk=
|
||||
github.com/luxfi/pq v0.0.0-20260511232911-de15d6ce2f0f h1:heVZJMlU6RtYZGjgmqFbtcO9RCMl+6Sfchz/n0U/6+U=
|
||||
github.com/luxfi/pq v0.0.0-20260511232911-de15d6ce2f0f/go.mod h1:8bppZcRElfrVt0n3nYCZW3iX1TvhvzNbdjNdK1irgIE=
|
||||
github.com/luxfi/pq v1.0.1-0.20260512064747-3d4c9414e15e h1:eXbRZiu08vO0f4JaJd/XVGnDn25x5W4TS4Jf2nHRTVM=
|
||||
github.com/luxfi/pq v1.0.1-0.20260512064747-3d4c9414e15e/go.mod h1:8bppZcRElfrVt0n3nYCZW3iX1TvhvzNbdjNdK1irgIE=
|
||||
github.com/luxfi/precompile v0.5.16 h1:tIY7/yK856lggOvv/5vVC0Cr7gRUs6Wi7MhibIszsng=
|
||||
github.com/luxfi/precompile v0.5.16/go.mod h1:r2nImBshkpIfl3n0+Zas0uxfJ3c7JB0E0qpJkBtSV/s=
|
||||
github.com/luxfi/protocol v0.0.2 h1:TAuXMm1K4VDpG3B3brq1EE9Lb7XlbhQmVBObskgNhmc=
|
||||
|
||||
@@ -54,6 +54,12 @@ type NetworkUpgrades struct {
|
||||
FortunaTimestamp *uint64 `json:"fortunaTimestamp,omitempty"`
|
||||
// Granite is a placeholder for the next upgrade.
|
||||
GraniteTimestamp *uint64 `json:"graniteTimestamp,omitempty"`
|
||||
// StrictPQTimestamp pins the chain to a strict post-quantum profile from
|
||||
// the given timestamp onward. When active, classical pairing-based and
|
||||
// discrete-log precompiles refuse to execute via contract.RefuseUnderStrictPQ.
|
||||
// nil = never activates (classical-permissive, the default for non-Lux
|
||||
// chains that integrate Lux precompiles). 0 = active from genesis.
|
||||
StrictPQTimestamp *uint64 `json:"strictPQTimestamp,omitempty"`
|
||||
}
|
||||
|
||||
func (n *NetworkUpgrades) Equal(other *NetworkUpgrades) bool {
|
||||
@@ -201,6 +207,9 @@ func (n *NetworkUpgrades) Override(o *NetworkUpgrades) {
|
||||
if o.GraniteTimestamp != nil {
|
||||
n.GraniteTimestamp = o.GraniteTimestamp
|
||||
}
|
||||
if o.StrictPQTimestamp != nil {
|
||||
n.StrictPQTimestamp = o.StrictPQTimestamp
|
||||
}
|
||||
}
|
||||
|
||||
// IsEVM returns whether [time] represents a block
|
||||
@@ -233,6 +242,14 @@ func (n *NetworkUpgrades) IsGranite(time uint64) bool {
|
||||
return isTimestampForked(n.GraniteTimestamp, time)
|
||||
}
|
||||
|
||||
// IsStrictPQ returns whether [time] represents a block at or after the
|
||||
// strict-PQ activation timestamp. nil StrictPQTimestamp means the chain
|
||||
// is classical-permissive (the default for non-Lux chains that integrate
|
||||
// Lux precompiles).
|
||||
func (n *NetworkUpgrades) IsStrictPQ(time uint64) bool {
|
||||
return isTimestampForked(n.StrictPQTimestamp, time)
|
||||
}
|
||||
|
||||
func (n *NetworkUpgrades) Description() string {
|
||||
var banner string
|
||||
banner += fmt.Sprintf(" - EVM Timestamp: @%-10v (https://github.com/luxfi/node/releases/tag/v1.10.0)\n", ptrToString(n.EVMTimestamp))
|
||||
|
||||
+25
-12
@@ -345,20 +345,22 @@ func (vm *VM) Initialize(ctx context.Context, init block.Init) error {
|
||||
|
||||
log.Info("Initializing Chain EVM VM", "Version", Version, "geth version", params.VersionWithMeta, "Config", vm.config)
|
||||
|
||||
// F102 close-out — install the chain-wide PQ posture into the EVM
|
||||
// precompile layer. PQ mode is binary: a chain is PQ or it isn't.
|
||||
// Strict-PQ refuses every classical primitive at the precompile
|
||||
// boundary: ecrecover/p256verify, sha256/ripemd160/blake2F,
|
||||
// alt_bn128/BLS12-381 pairings, and EIP-4844 KZG point evaluation.
|
||||
// Gas is still charged per EIP-150 so the refusal isn't observable
|
||||
// via gas timing.
|
||||
// F102 close-out — install the chain-scoped PQ posture on the chain
|
||||
// config. PQ mode is binary: a chain is PQ or it isn't. Strict-PQ
|
||||
// refuses every classical primitive at the precompile boundary:
|
||||
// ecrecover/p256verify, sha256/ripemd160/blake2F, alt_bn128/BLS12-381
|
||||
// pairings, and EIP-4844 KZG point evaluation. Gas is still charged
|
||||
// per EIP-150 so the refusal isn't observable via gas timing.
|
||||
//
|
||||
// One concept, one way: SetPQProfile installs the projection; refuse()
|
||||
// is the gate every classical precompile calls. nil projection means
|
||||
// classical EVM semantics. config.PQ=false (default) preserves classical
|
||||
// semantics for legacy chains and the permissive profile.
|
||||
// One concept, one way: ChainConfig.PQ holds the profile;
|
||||
// (*EVM).runPrecompile reads it via chainConfig.PQ.RefuseUnder(op)
|
||||
// before dispatching each precompile. nil profile (the default)
|
||||
// preserves classical EVM semantics — every legacy chain runs
|
||||
// unchanged. Multi-chain hosts (strict-PQ + permissive in one
|
||||
// process) get the right gate per EVM instance because the profile
|
||||
// lives on each chain's config, not on a process-global atomic.
|
||||
if vm.config.PQ {
|
||||
gethvm.SetPQProfile(gethvm.AllForbidden())
|
||||
vm.chainConfig.PQ = gethvm.AllForbidden()
|
||||
log.Info("EVM PQ mode active: classical precompiles (ecrecover, sha256/ripemd/blake2F, alt_bn128, BLS12-381, KZG) refuse")
|
||||
}
|
||||
|
||||
@@ -403,6 +405,17 @@ func (vm *VM) Initialize(ctx context.Context, init block.Init) error {
|
||||
}
|
||||
debugLog("parseGenesis succeeded: chainID=%v alloc=%d accounts", g.Config.ChainID, len(g.Alloc))
|
||||
|
||||
// When config.PQ is set, pin strict-PQ from genesis on the extras config so
|
||||
// stateful precompiles that call contract.RefuseUnderStrictPQ refuse
|
||||
// classical primitives. The geth-layer gate above (SetPQProfile) covers
|
||||
// 0x01..0x09 standard precompiles; this covers Lux stateful precompiles
|
||||
// (KZG, Groth16, PLONK, fflonk, Halo2, BN254-Pedersen, BabyJubJub,
|
||||
// Pallas/Vesta, BLS12-381 modules, etc.) via the same posture.
|
||||
if vm.config.PQ {
|
||||
zero := uint64(0)
|
||||
params.GetExtra(g.Config).StrictPQTimestamp = &zero
|
||||
}
|
||||
|
||||
vm.syntacticBlockValidator = NewBlockValidator()
|
||||
debugLog("Creating ethConfig")
|
||||
|
||||
|
||||
@@ -277,6 +277,22 @@ func (c *extChainConfigBridge) IsDurango(time uint64) bool {
|
||||
return c.internal.IsDurango(time)
|
||||
}
|
||||
|
||||
// IsStrictPQ satisfies the external contract.StrictPQReporter interface so
|
||||
// classical precompiles in github.com/luxfi/precompile/* refuse to execute
|
||||
// when the chain pins a strict post-quantum profile. The internal config
|
||||
// (chainConfigAdapter) delegates to extras.IsStrictPQ. We type-assert
|
||||
// rather than widen precompileconfig.ChainConfig so non-Lux chains that
|
||||
// integrate Lux precompiles remain classical-permissive by default.
|
||||
func (c *extChainConfigBridge) IsStrictPQ(time uint64) bool {
|
||||
type strictPQ interface {
|
||||
IsStrictPQ(time uint64) bool
|
||||
}
|
||||
if r, ok := c.internal.(strictPQ); ok {
|
||||
return r.IsStrictPQ(time)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Configurator Bridge (external → internal)
|
||||
// =============================================================================
|
||||
|
||||
Reference in New Issue
Block a user