Files
zeekay ca0bf6b303 precompile: seven packages to 96-99%, from lanes killed before they could commit
slhdsa 97.9 · mlkem 97.4 · mldsa 96.8 · xwing 96.2 · bls12381 97.2 ·
kzg4844 97.8 · v3 98.7. All build, vet and gofmt clean, all green under
-count=1, and preserved here rather than lost to a session limit.

v3's lane did report before it died: 78.1% to 98.7%, 70 of 70 mutations caught,
four of which survived a first pass and needed tests written. The two that
matter are rounding: burn rounding principal UP, and fee growth rounding UP,
neither of which any behaviour test could see -- a Q128.128 off-by-one is
2^-128 of a token and floors to zero through every payout path, so it took an
exact-value assertion. It also found that a full drain of a pool with
overlapping positions REVERTS rather than paying out, because deposits round
down once per position while payouts round down once per step over combined
liquidity, and floor(a+b) >= floor(a)+floor(b). Custody holds; not one unbacked
wei leaves. Liveness, not theft.

The other six carry no report -- their lanes were killed mid-run. Build, vet,
gofmt and the suite are all I can vouch for.
2026-08-28 13:27:00 -07:00

157 lines
4.7 KiB
Go

// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// Package xwing implements the X-Wing hybrid KEM precompile (X25519 + ML-KEM-768).
// Address: 0x0000000000000000000000000000000000002221 (PQ Hybrid KEM, LP-2221)
//
// X-Wing combines classical X25519 with post-quantum ML-KEM-768 for hybrid
// key encapsulation, as specified in IETF draft-connolly-cfrg-xwing-kem.
//
// Operations:
// - 0x02: Encapsulate -> pk -> (ct, ss) ciphertext + shared secret
//
// KeyGen and Decapsulate are intentionally excluded: KeyGen returns secret
// key material in EVM return data (visible to all validators), and
// Decapsulate requires the secret key in calldata (public on-chain).
// Both operations MUST be performed off-chain.
package xwing
import (
"crypto/sha256"
"errors"
"github.com/cloudflare/circl/kem"
"github.com/cloudflare/circl/kem/xwing"
"github.com/luxfi/geth/common"
"github.com/luxfi/precompile/contract"
)
// SeedSize is the caller-provided seed length. X-Wing's internal
// EncapsulationSeedSize is 64; we accept 32 from the caller and expand
// via SHA-256("XWING_ENCAP_v1" || caller || raw_seed) to 64 bytes.
// Consensus safety: identical caller + raw seed + pk = identical output
// on every validator, eliminating the crypto/rand chain split.
const SeedSize = 32
// Ensure circl KEM interface is satisfied (compile-time check)
var _ kem.Scheme = xwing.Scheme()
var (
// LP-2221: C-Chain hybrid KEM (X25519+ML-KEM)
ContractAddress = common.HexToAddress("0x0000000000000000000000000000000000002221")
XWingPrecompile = &xwingPrecompile{}
_ contract.StatefulPrecompiledContract = &xwingPrecompile{}
ErrInvalidInput = contract.ErrInvalidInput
ErrInvalidOp = errors.New("invalid xwing operation")
)
const (
OpEncapsulate = 0x02
GasEncapsulate = 40000
// GasRefuse is charged for input this precompile refuses: empty calldata
// and every op byte other than OpEncapsulate. Refusing still costs the
// node a dispatch, a bounds check and an error allocation, so it cannot
// be free -- a zero-gas branch is an unmetered path an attacker can
// hammer. Every sibling in the PQCrypto block charges its base gas for
// unparseable input; this is that same floor.
GasRefuse = 1000
)
type xwingPrecompile struct{}
func (p *xwingPrecompile) RequiredGas(input []byte) uint64 {
if len(input) < 1 {
return GasRefuse
}
switch input[0] {
case OpEncapsulate:
return GasEncapsulate
default:
return GasRefuse
}
}
func (p *xwingPrecompile) Run(
accessibleState contract.AccessibleState,
caller common.Address,
addr common.Address,
input []byte,
suppliedGas uint64,
readOnly bool,
) ([]byte, uint64, error) {
gasCost := p.RequiredGas(input)
gas, err := contract.DeductGas(suppliedGas, gasCost)
if err != nil {
return nil, 0, err
}
if len(input) < 1 {
return nil, gas, ErrInvalidInput
}
scheme := xwing.Scheme()
switch input[0] {
case OpEncapsulate:
pkSize := scheme.PublicKeySize()
// Input: [op(1)][seed(32)][pk(pkSize)] -- exactly, so one
// encapsulation has exactly one calldata spelling.
if len(input) != 1+SeedSize+pkSize {
return nil, gas, ErrInvalidInput
}
rawSeed := input[1 : 1+SeedSize]
pkBytes := input[1+SeedSize : 1+SeedSize+pkSize]
pk, err := scheme.UnmarshalBinaryPublicKey(pkBytes)
if err != nil {
return nil, gas, err
}
// Domain-separate by caller + expand 32-byte raw seed to X-Wing's
// required 64-byte seed via iterated SHA-256.
seed := expandSeed(caller, rawSeed)
ct, ss, err := scheme.EncapsulateDeterministically(pk, seed)
if err != nil {
return nil, gas, err
}
// Output: [2 bytes ct_len][ct][ss]
result := make([]byte, 2+len(ct)+len(ss))
result[0] = byte(len(ct) >> 8)
result[1] = byte(len(ct))
copy(result[2:], ct)
copy(result[2+len(ct):], ss)
return result, gas, nil
default:
return nil, gas, ErrInvalidOp
}
}
// expandSeed combines a caller-provided 32-byte seed with the caller
// address into a 64-byte seed suitable for X-Wing's
// EncapsulateDeterministically. Identical inputs on every validator
// produce identical output (consensus-safe). Two different callers
// cannot collide on the derived seed even if they pick the same raw
// seed bytes.
func expandSeed(caller common.Address, raw []byte) []byte {
// First half: H("XWING_ENCAP_v1" || caller || raw || 0x00)
// Second half: H("XWING_ENCAP_v1" || caller || raw || 0x01)
// This is the standard HKDF-lite extract technique for a 64-byte output.
out := make([]byte, 64)
for i, tag := range []byte{0x00, 0x01} {
h := sha256.New()
h.Write([]byte("XWING_ENCAP_v1"))
h.Write(caller.Bytes())
h.Write(raw)
h.Write([]byte{tag})
copy(out[i*32:(i+1)*32], h.Sum(nil))
}
return out
}