Files
precompile/modules/registerer.go
T
zeekay 15e5648035 registry, modules: one record of where a precompile lives
registry kept ~400 address constants beside the addresses the precompiles
actually claim in their init(). Two records of one fact, and they had already
drifted: the prose mapped chain nibble 3 to D-Chain while the constants used it
for Q-Chain. The package now derives InFamily from modules.RegisteredModules(),
so a question asked here and an address dispatched by the EVM cannot disagree.
PrecompileAddress, ChainSlot and FamilyPage compute what the table enumerated.

RegisteredModules returned the package slice itself. The host walks it to build
the per-block precompile set, so its order is consensus-visible, and any caller
that sorted or truncated it reordered the registry for every subsequent block.
It returns a copy. The duplicate 0x9000-0x9FFF allowlist range is one entry.
2026-08-28 15:39:13 -07:00

325 lines
12 KiB
Go

// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package modules
import (
"bytes"
"fmt"
"slices"
"github.com/luxfi/geth/common"
)
// AddressRange represents a continuous range of addresses
type AddressRange struct {
Start common.Address
End common.Address
}
// Contains returns true iff [addr] is contained within the (inclusive)
// range of addresses defined by [a].
func (a *AddressRange) Contains(addr common.Address) bool {
addrBytes := addr.Bytes()
return bytes.Compare(addrBytes, a.Start[:]) >= 0 && bytes.Compare(addrBytes, a.End[:]) <= 0
}
// BlackholeAddr is the address where assets are burned
var BlackholeAddr = common.Address{
1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
}
var (
// registeredModules is a list of Module to preserve order
// for deterministic iteration
registeredModules = make([]Module, 0)
// Reserved address ranges for stateful precompiles
//
// HIGH-BYTE RANGES (legacy format: 0xXX00...0000):
// 0x0100-0x01FF: Warp/Teleport messaging
// 0x0200-0x02FF: Chain config (AllowLists, FeeManager, etc.)
// 0x0300-0x03FF: AI Mining (sole owner; pre-LP-4200 quasar stubs retired)
// 0x0400-0x04FF: DEX (Uniswap v4-style)
// 0x0500-0x05FF: Graph/Query layer
// 0x0600-0x06FF: Post-quantum crypto
// 0x0700-0x07FF: Privacy/Encryption
// 0x0800-0x08FF: Threshold signatures
// 0x0900-0x09FF: ZK proofs
// 0x0A00-0x0AFF: Curves (secp256r1, etc.)
//
// LOW-BYTE RANGES (EIP-collision-free: 0x0000...XXXX):
// 0x8000-0x8FFF: Lux Core System (AI Mining at 0x8100)
// 0x9000-0x9FFF: Lux Crypto Privacy (HPKE, ECIES, FHE)
// 0xA000-0xAFFF: Lux Hashing & ZK (Poseidon2, Blake3, STARK)
// 0xB000-0xBFFF: Lux KZG Extensions
reservedRanges = []AddressRange{
// Warp/Teleport (0x0100-0x01FF)
{
Start: common.HexToAddress("0x0100000000000000000000000000000000000000"),
End: common.HexToAddress("0x01000000000000000000000000000000000000ff"),
},
// Chain Config (0x0200-0x02FF)
{
Start: common.HexToAddress("0x0200000000000000000000000000000000000000"),
End: common.HexToAddress("0x02000000000000000000000000000000000000ff"),
},
// AI Mining (0x0300-0x03FF). Sole owner of this range —
// the pre-LP-4200 quasar precompiles at 0x0300..20-25
// (forgeable Verkle stub + classical BLS/Hybrid) were
// retired in favor of the LP-4200 0x012200 block.
{
Start: common.HexToAddress("0x0300000000000000000000000000000000000000"),
End: common.HexToAddress("0x03000000000000000000000000000000000000ff"),
},
// DEX - Uniswap v4-style (0x0400-0x04FF)
{
Start: common.HexToAddress("0x0400000000000000000000000000000000000000"),
End: common.HexToAddress("0x04000000000000000000000000000000000000ff"),
},
// Graph/Query Layer (0x0500-0x05FF)
{
Start: common.HexToAddress("0x0500000000000000000000000000000000000000"),
End: common.HexToAddress("0x05000000000000000000000000000000000000ff"),
},
// Post-Quantum Crypto (0x0600-0x06FF)
{
Start: common.HexToAddress("0x0600000000000000000000000000000000000000"),
End: common.HexToAddress("0x06000000000000000000000000000000000000ff"),
},
// Privacy/Encryption (0x0700-0x07FF)
{
Start: common.HexToAddress("0x0700000000000000000000000000000000000000"),
End: common.HexToAddress("0x07000000000000000000000000000000000000ff"),
},
// Threshold Signatures (0x0800-0x08FF)
{
Start: common.HexToAddress("0x0800000000000000000000000000000000000000"),
End: common.HexToAddress("0x08000000000000000000000000000000000000ff"),
},
// ZK Proofs (0x0900-0x09FF)
{
Start: common.HexToAddress("0x0900000000000000000000000000000000000000"),
End: common.HexToAddress("0x09000000000000000000000000000000000000ff"),
},
// Curves - secp256r1, etc. (0x0A00-0x0AFF)
{
Start: common.HexToAddress("0x0A00000000000000000000000000000000000000"),
End: common.HexToAddress("0x0A000000000000000000000000000000000000ff"),
},
// =====================================================================
// LP-ALIGNED RANGES (Low-byte format: 0x0000...LPNUM)
// Address = LP number directly, e.g., LP-9010 = 0x...9010
// See precompile/registry/registry.go for full scheme documentation
// =====================================================================
// LP-2xxx: PQ Identity (0x0..2000 - 0x0..2FFF)
{
Start: common.HexToAddress("0x0000000000000000000000000000000000002000"),
End: common.HexToAddress("0x0000000000000000000000000000000000002fff"),
},
// LP-3xxx: EVM/Crypto (0x0..3000 - 0x0..3FFF)
{
Start: common.HexToAddress("0x0000000000000000000000000000000000003000"),
End: common.HexToAddress("0x0000000000000000000000000000000000003fff"),
},
// LP-4xxx: Privacy/ZK (0x0..4000 - 0x0..4FFF)
{
Start: common.HexToAddress("0x0000000000000000000000000000000000004000"),
End: common.HexToAddress("0x0000000000000000000000000000000000004fff"),
},
// LP-5xxx: Threshold/MPC (0x0..5000 - 0x0..5FFF)
{
Start: common.HexToAddress("0x0000000000000000000000000000000000005000"),
End: common.HexToAddress("0x0000000000000000000000000000000000005fff"),
},
// LP-6xxx: Bridges (0x0..6000 - 0x0..6FFF)
{
Start: common.HexToAddress("0x0000000000000000000000000000000000006000"),
End: common.HexToAddress("0x0000000000000000000000000000000000006fff"),
},
// LP-7xxx: AI (0x0..7000 - 0x0..7FFF)
{
Start: common.HexToAddress("0x0000000000000000000000000000000000007000"),
End: common.HexToAddress("0x0000000000000000000000000000000000007fff"),
},
// LP-9xxx: DEX/Markets and Lux Crypto Privacy — HPKE, ECIES, FHE
// (0x0..9000 - 0x0..9FFF). One page, two families; the allowlist is a
// range check, so a second identical entry could never change an answer.
{
Start: common.HexToAddress("0x0000000000000000000000000000000000009000"),
End: common.HexToAddress("0x0000000000000000000000000000000000009fff"),
},
// LP-12xxx: Unified PQCrypto block (0x0..12000 - 0x0..12fff)
// LP-3500/LP-3501/LP-4200:
// 0x12201 = ML-KEM (Module-LWE KEM, FIPS 203)
// 0x12202 = ML-DSA (Module-LWE single-sig, FIPS 204)
// 0x12203 = SLH-DSA (hash-based signature, FIPS 205)
// 0x12204 = Pulsar (Module-LWE threshold, FIPS 204 byte-equal)
// 0x12205 = P3Q (rollup-commit PQ verifier; kind-byte dispatch
// to Pulsar/Corona/Magnetar — LP-218)
// 0x12206 = Corona (Module-LWE threshold)
// 0x12207 = Magnetar (hash-based threshold, FIPS 205 byte-equal)
// 0x12208 = HQC (code-based KEM, family-disjoint backup)
// 0x12220 = STARK-FRI (strict-PQ STARK / FRI verifier — LP-221)
{
Start: common.HexToAddress("0x0000000000000000000000000000000000012000"),
End: common.HexToAddress("0x0000000000000000000000000000000000012fff"),
},
// =====================================================================
// LOW-BYTE RANGES (EIP-collision-free addresses)
// =====================================================================
// Lux Core System (0x8000-0x8FFF) - AI Mining, etc.
{
Start: common.HexToAddress("0x0000000000000000000000000000000000008000"),
End: common.HexToAddress("0x0000000000000000000000000000000000008fff"),
},
// Lux Hashing & ZK (0xA000-0xAFFF) - Poseidon2, Blake3, STARK, etc.
{
Start: common.HexToAddress("0x000000000000000000000000000000000000a000"),
End: common.HexToAddress("0x000000000000000000000000000000000000afff"),
},
// Lux KZG Extensions (0xB000-0xBFFF)
{
Start: common.HexToAddress("0x000000000000000000000000000000000000b000"),
End: common.HexToAddress("0x000000000000000000000000000000000000bfff"),
},
// Standard EVM Precompiles (0x01-0x11)
// Includes: ECRECOVER, SHA256, RIPEMD160, IDENTITY, MODEXP, BN254,
// BLAKE2F, KZG, BLS12-381 (EIP-2537)
{
Start: common.HexToAddress("0x0000000000000000000000000000000000000001"),
End: common.HexToAddress("0x00000000000000000000000000000000000000ff"),
},
// EIP-7212 secp256r1 / P256 verify (0x100)
// Reserved post-EVM-standard band 0x100-0x1FF for upstream-EIP
// precompiles that EVM clients converge on (RIP-7212 picked 0x100
// for P256 verify). Keeping the range tight (one /248 of the 20-
// byte space) means future EIPs slot in here without colliding
// with anything Lux uses.
{
Start: common.HexToAddress("0x0000000000000000000000000000000000000100"),
End: common.HexToAddress("0x00000000000000000000000000000000000001ff"),
},
// High-byte EVM/Crypto page (LP-3xxx classical signatures)
// Per precompile/registry/registry.go PCII scheme: P=3 (EVM/Crypto)
// at high-byte format 0x3PCC0000...0000. Reserved for the classical-
// signature sub-page so ed25519 (LP-3211 at 0x3211...0000), ECDSA
// extensions (LP-3210), BLS12-381 (LP-3212), VRF (LP-3213), etc.
// can register without each subsystem extending the table.
{
Start: common.HexToAddress("0x3200000000000000000000000000000000000000"),
End: common.HexToAddress("0x32ff000000000000000000000000000000000000"),
},
// Dead/Burn Addresses (LP-0150)
// 0x0000...0000 - Zero address
{
Start: common.HexToAddress("0x0000000000000000000000000000000000000000"),
End: common.HexToAddress("0x0000000000000000000000000000000000000000"),
},
// 0x0000...dEaD - Common dead address
{
Start: common.HexToAddress("0x000000000000000000000000000000000000dEaD"),
End: common.HexToAddress("0x000000000000000000000000000000000000dEaD"),
},
// 0xdEaD...0000 - Full dead address prefix
{
Start: common.HexToAddress("0xdEaD000000000000000000000000000000000000"),
End: common.HexToAddress("0xdEaD000000000000000000000000000000000000"),
},
}
)
// ReservedAddress returns true if [addr] is in a reserved range for custom precompiles
func ReservedAddress(addr common.Address) bool {
for _, reservedRange := range reservedRanges {
if reservedRange.Contains(addr) {
return true
}
}
return false
}
// RegisterModule registers a stateful precompile module
func RegisterModule(stm Module) error {
address := stm.Address
key := stm.ConfigKey
if address == BlackholeAddr {
return fmt.Errorf("address %s overlaps with blackhole address", address)
}
if !ReservedAddress(address) {
return fmt.Errorf("address %s not in a reserved range", address)
}
for _, registeredModule := range registeredModules {
if registeredModule.ConfigKey == key {
return fmt.Errorf(
"precompile config key collision: %q already registered by %s — "+
"each precompile must have a globally unique ConfigKey",
key, registeredModule.Address)
}
if registeredModule.Address == address {
return fmt.Errorf(
"precompile address collision: %s already used by %q — "+
"trying to register %q at the same address (import-order-independent fail-fast)",
address, registeredModule.ConfigKey, key)
}
}
// sort by address to ensure deterministic iteration
registeredModules = insertSortedByAddress(registeredModules, stm)
return nil
}
func GetPrecompileModuleByAddress(address common.Address) (Module, bool) {
for _, stm := range registeredModules {
if stm.Address == address {
return stm, true
}
}
return Module{}, false
}
func GetPrecompileModule(key string) (Module, bool) {
for _, stm := range registeredModules {
if stm.ConfigKey == key {
return stm, true
}
}
return Module{}, false
}
// RegisteredModules returns every registered module in ascending address order.
// The host walks this slice to build the per-block precompile set, so the order
// is consensus-visible; the returned slice is a copy so a caller that sorts or
// truncates it cannot reorder the registry for every subsequent block.
func RegisteredModules() []Module {
out := make([]Module, len(registeredModules))
copy(out, registeredModules)
return out
}
// AlwaysOnModules returns the registered modules marked AlwaysOn — precompiles that
// are active on every chain from genesis with NO config entry. The host activates
// these unconditionally (EXTCODESIZE marker at genesis + Run dispatch on every block),
// independent of genesisPrecompiles / precompileUpgrades. Returned in the same
// deterministic address order as RegisteredModules.
func AlwaysOnModules() []Module {
out := make([]Module, 0, len(registeredModules))
for _, m := range registeredModules {
if m.AlwaysOn {
out = append(out, m)
}
}
return out
}
func insertSortedByAddress(data []Module, stm Module) []Module {
data = append(data, stm)
slices.SortFunc(data, func(a, b Module) int {
return bytes.Compare(a.Address.Bytes(), b.Address.Bytes())
})
return data
}