network,node: complete strict-PQ activation (schemeGate + pre-dedup handshake + classical-compat)#137
Merged
Conversation
…andshake + classical-compat) main carries the strict-PQ peer-identity fix (NewLocalIdentityFromStakingKey + adoptVerifiedPQIdentity) but is missing three further layers that are each required for strict-PQ consensus and chain creation to actually work. All three are proven on a live devnet running the equivalent fix (node v1.10.18-strictpq): a full ML-DSA validator mesh forms and the EVM/DEX/FHE chains are created on the sovereign L1. 1. schemeGate nil-gate (network/network.go) Under strict-PQ the TLS layer is transport-only: peer leaf certs are ephemeral ECDSA, which schemeFromCert classifies as classical, so the SchemeGate refuses every peer at the upgrade. Pass a nil gate to the TLS upgraders when PQHandshakeConfig is active; identity is enforced by the PQ handshake instead. Non-PQ paths keep the real gate. 2. pre-dedup PQ handshake (network/network.go + network/peer/peer.go) The PQ handshake ran inside peer.Start, under peersLock, with init/responder roles fixed by dial direction. On simultaneous mutual dials every node acts as a lone initiator (keeps its outbound, drops the peer's inbound at the connecting-dedup) and deadlocks -> 0 peers. Run the handshake per-conn in network.upgrade, before the dedup and without the lock, so each TCP conn completes independently and the dedup keys on the resulting stable ML-DSA NodeID. RunPQHandshakeConn shares main's binding via a new verifyPQIdentityBinding helper, so the pre-dedup and in-Start paths enforce byte-identical identity semantics. 3. classical-compat allow-list (node/node.go) The ClassicalCompatRegistry was nil, so strict-PQ refused every classical secp256k1 P-chain credential and the bootstrap control key could not issue CreateNetwork/CreateChainTx. Seed it (strict-PQ only) from the genesis P-chain allocation owners plus ids.ShortEmpty (the mempool's current originator). This unblocks creating the EVM/DEX/FHE chains. Builds clean with CGO_ENABLED=0; network/peer, network, vms/txs/auth and platformvm genesis/mempool tests pass. main's identity fix is unchanged. Supersedes #136 (which carried these layers on a branch that had diverged 331 commits behind main). Follow-up, tracked separately: config.pemBytesOrFile short-circuits on a blank *-content flag and silently degrades a strict-PQ validator to an ECDSA NodeID.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
mainalready carries the strict-PQ peer-identity fix (NewLocalIdentityFromStakingKey+adoptVerifiedPQIdentity). This PR adds the three further layers that are each required for strict-PQ consensus and chain creation to actually work end-to-end. All three are proven on a live devnet running the equivalent fix (node v1.10.18-strictpq): a full ML-DSA validator mesh forms and the EVM/DEX/FHE chains are created on the sovereign L1.This is a clean additive change off current
main— it keeps main's identity fix untouched and only fills the gaps.1. schemeGate nil-gate —
network/network.goUnder strict-PQ the TLS layer is transport-only: peer leaf certs are ephemeral ECDSA, which
schemeFromCertclassifies as classical, so theSchemeGaterefuses every peer at the upgrade. WhenPQHandshakeConfigis active we now pass a nil gate to the TLS upgraders; identity is enforced by the PQ handshake instead. Non-PQ paths keep the real gate. Without this, no strict-PQ peer survives the TLS upgrade.2. pre-dedup PQ handshake —
network/network.go+network/peer/peer.goThe PQ handshake ran inside
peer.Start, underpeersLock, with initiator/responder roles fixed by dial direction. On simultaneous mutual dials every node acts as a lone initiator (keeps its outbound, drops the peer's inbound at the connecting-dedup) and deadlocks → 0 peers. We now run the handshake per-conn innetwork.upgrade, before the dedup and without the lock, so each TCP conn completes independently and the dedup keys on the resulting stable ML-DSA NodeID.RunPQHandshakeConnshares main's binding via a newverifyPQIdentityBindinghelper, so the pre-dedup path and the original in-Startpath enforce byte-identical identity semantics (re-derive NodeID from the peer's ML-DSA key underids.Empty, require== claimed).handshake.gois untouched. A PQ handshake failure now tears the conn down fromupgrade(before dedup) instead of fromStart— same outcome, just earlier and lock-free.3. classical-compat allow-list —
node/node.goClassicalCompatRegistrywasnilat both factory sites, so strict-PQ refused every classical secp256k1 P-chain credential and the bootstrap control key could not issueCreateNetwork/CreateChainTx. We seed it (strict-PQ only — gated onRequireTypedTxAuth) from the genesis P-chain allocation owners plusids.ShortEmpty(the mempool's current originator,vms/platformvm/txs/mempool/mempool.go). This unblocks creating the EVM/DEX/FHE chains.Testing
CGO_ENABLED=0 go build ./...— clean.CGO_ENABLED=0 go test ./network/peer/— pass (handshake round-trips, classical KEM/ECDSA refusal, the 3AdoptVerifiedPQIdentitybinding tests, fullSchemeGatesuite, both upgrader nil-gate tests).CGO_ENABLED=0 go test ./network/ ./vms/txs/auth/ ./vms/platformvm/genesis/ ./vms/platformvm/txs/mempool/— all pass.v1.10.18-strictpq): ML-DSA validator mesh + EVM chain servingeth_chainId.Notes
main; this is the clean re-land).config.pemBytesOrFileshort-circuits on a blank*-contentflag and never falls through to the*-filepath — silently degrading a strict-PQ validator to an ECDSA NodeID. Out of scope here (config, not these three consensus/chain-creation layers).