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.
This commit is contained in:
2026-08-28 15:39:13 -07:00
parent 96ba923c2f
commit 15e5648035
5 changed files with 1103 additions and 1264 deletions
+10 -7
View File
@@ -143,7 +143,9 @@ var (
Start: common.HexToAddress("0x0000000000000000000000000000000000007000"),
End: common.HexToAddress("0x0000000000000000000000000000000000007fff"),
},
// LP-9xxx: DEX/Markets (0x0..9000 - 0x0..9FFF)
// 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"),
@@ -172,11 +174,6 @@ var (
Start: common.HexToAddress("0x0000000000000000000000000000000000008000"),
End: common.HexToAddress("0x0000000000000000000000000000000000008fff"),
},
// Lux Crypto Privacy (0x9000-0x9FFF) - HPKE, ECIES, FHE
{
Start: common.HexToAddress("0x0000000000000000000000000000000000009000"),
End: common.HexToAddress("0x0000000000000000000000000000000000009fff"),
},
// Lux Hashing & ZK (0xA000-0xAFFF) - Poseidon2, Blake3, STARK, etc.
{
Start: common.HexToAddress("0x000000000000000000000000000000000000a000"),
@@ -293,8 +290,14 @@ func GetPrecompileModule(key string) (Module, bool) {
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 {
return registeredModules
out := make([]Module, len(registeredModules))
copy(out, registeredModules)
return out
}
// AlwaysOnModules returns the registered modules marked AlwaysOn — precompiles that
+499
View File
@@ -0,0 +1,499 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package modules
import (
"bytes"
"fmt"
"math/big"
"testing"
"github.com/luxfi/geth/common"
"github.com/luxfi/precompile/contract"
"github.com/stretchr/testify/require"
)
// ---------------------------------------------------------------------------
// Fixtures
//
// registeredModules is process-global consensus-critical state. Every test that
// registers must restore it, or a later test observes a registry the production
// binary would never have.
// ---------------------------------------------------------------------------
// isolate replaces the global registry with an empty one for the duration of
// the test and restores the original afterwards.
func isolate(t *testing.T) {
t.Helper()
saved := registeredModules
registeredModules = make([]Module, 0)
t.Cleanup(func() { registeredModules = saved })
}
// stub is a do-nothing StatefulPrecompiledContract. Registration must not care
// what the contract does, only where it lives.
type stub struct{}
func (stub) Run(contract.AccessibleState, common.Address, common.Address, []byte, uint64, bool) ([]byte, uint64, error) {
return nil, 0, nil
}
func mod(key string, addr common.Address) Module {
return Module{ConfigKey: key, Address: addr, Contract: stub{}}
}
// addr builds a 20-byte address from a big.Int, so tests can walk a range
// boundary by ±1 in the full 160-bit address space.
func addr(n *big.Int) common.Address {
var a common.Address
b := n.Bytes()
if len(b) > common.AddressLength {
b = b[len(b)-common.AddressLength:]
}
copy(a[common.AddressLength-len(b):], b)
return a
}
func toInt(a common.Address) *big.Int { return new(big.Int).SetBytes(a[:]) }
// unionContains is an independent reimplementation of the allowlist predicate:
// it answers "is addr in ANY reserved range" without calling ReservedAddress,
// so a boundary test compares two implementations instead of one against itself.
func unionContains(a common.Address) bool {
n := toInt(a)
for _, r := range reservedRanges {
if n.Cmp(toInt(r.Start)) >= 0 && n.Cmp(toInt(r.End)) <= 0 {
return true
}
}
return false
}
// maxAddr is 0xff..ff, above every reserved range's End.
var maxAddr = common.HexToAddress("0xffffffffffffffffffffffffffffffffffffffff")
// ---------------------------------------------------------------------------
// AddressRange.Contains
// ---------------------------------------------------------------------------
func TestAddressRangeContainsBoundaries(t *testing.T) {
r := AddressRange{
Start: common.HexToAddress("0x0000000000000000000000000000000000001000"),
End: common.HexToAddress("0x0000000000000000000000000000000000001fff"),
}
// Inclusive on BOTH ends. `>` instead of `>=` (or `<` instead of `<=`) in
// Contains is the classic off-by-one and is caught here.
require.True(t, r.Contains(r.Start), "Start must be inside its own range")
require.True(t, r.Contains(r.End), "End must be inside its own range")
require.True(t, r.Contains(common.HexToAddress("0x0000000000000000000000000000000000001800")))
// One below Start, one above End.
require.False(t, r.Contains(addr(new(big.Int).Sub(toInt(r.Start), big.NewInt(1)))))
require.False(t, r.Contains(addr(new(big.Int).Add(toInt(r.End), big.NewInt(1)))))
}
func TestAddressRangeContainsIsUnsignedByteOrder(t *testing.T) {
// A signed-comparison bug shows up at the 0x7f→0x80 high-bit crossing: a
// signed compare would place 0x80.. BELOW 0x00.. and the range would invert.
r := AddressRange{
Start: common.HexToAddress("0x7f00000000000000000000000000000000000000"),
End: common.HexToAddress("0x8100000000000000000000000000000000000000"),
}
require.True(t, r.Contains(common.HexToAddress("0x8000000000000000000000000000000000000000")))
require.False(t, r.Contains(common.HexToAddress("0x7effffffffffffffffffffffffffffffffffffff")))
require.False(t, r.Contains(common.HexToAddress("0x8100000000000000000000000000000000000001")))
}
func TestAddressRangeSingleAddress(t *testing.T) {
only := common.HexToAddress("0x000000000000000000000000000000000000dEaD")
r := AddressRange{Start: only, End: only}
require.True(t, r.Contains(only))
require.False(t, r.Contains(addr(new(big.Int).Sub(toInt(only), big.NewInt(1)))))
require.False(t, r.Contains(addr(new(big.Int).Add(toInt(only), big.NewInt(1)))))
}
// ---------------------------------------------------------------------------
// ReservedAddress — every declared range, at both edges
// ---------------------------------------------------------------------------
func TestReservedAddressEveryRangeBoundary(t *testing.T) {
require.NotEmpty(t, reservedRanges)
for i, r := range reservedRanges {
t.Run(fmt.Sprintf("range%02d_%s", i, r.Start.Hex()), func(t *testing.T) {
require.True(t, ReservedAddress(r.Start), "first address of range must be reserved")
require.True(t, ReservedAddress(r.End), "last address of range must be reserved")
// A midpoint is reserved too.
mid := addr(new(big.Int).Rsh(new(big.Int).Add(toInt(r.Start), toInt(r.End)), 1))
require.True(t, ReservedAddress(mid), "midpoint %s must be reserved", mid.Hex())
// One outside each edge is reserved only if a DIFFERENT range covers
// it. Cross-checked against the independent union predicate so an
// inverted comparison in Contains cannot hide.
below := addr(new(big.Int).Sub(toInt(r.Start), big.NewInt(1)))
if toInt(r.Start).Sign() > 0 {
require.Equal(t, unionContains(below), ReservedAddress(below),
"below-Start %s disagrees with the union", below.Hex())
}
above := addr(new(big.Int).Add(toInt(r.End), big.NewInt(1)))
require.Equal(t, unionContains(above), ReservedAddress(above),
"above-End %s disagrees with the union", above.Hex())
})
}
}
func TestReservedAddressAgreesWithUnionOnProbeGrid(t *testing.T) {
// Walk a grid across the whole 160-bit space plus every range edge ±1 and
// require ReservedAddress to agree with the independent union predicate.
var probes []common.Address
for _, r := range reservedRanges {
for _, d := range []int64{-1, 0, 1} {
probes = append(probes,
addr(new(big.Int).Add(toInt(r.Start), big.NewInt(d))),
addr(new(big.Int).Add(toInt(r.End), big.NewInt(d))),
)
}
}
for i := 0; i < 256; i++ {
var a common.Address
a[0] = byte(i)
a[19] = byte(i)
probes = append(probes, a)
}
for _, p := range probes {
require.Equal(t, unionContains(p), ReservedAddress(p), "disagreement at %s", p.Hex())
}
}
func TestReservedAddressRejectsOutside(t *testing.T) {
// Addresses that no range covers. Each is asserted against the union too, so
// this test cannot rot if a range is widened.
for _, a := range []common.Address{
maxAddr,
common.HexToAddress("0x1000000000000000000000000000000000000000"),
common.HexToAddress("0x0c00000000000000000000000000000000000000"),
common.HexToAddress("0x0000000000000000000000000000000000000200"),
common.HexToAddress("0x000000000000000000000000000000000000c000"),
} {
require.False(t, unionContains(a), "fixture %s is no longer outside every range", a.Hex())
require.False(t, ReservedAddress(a), "%s must not be reserved", a.Hex())
}
}
func TestReservedRangesAreWellFormed(t *testing.T) {
seen := make(map[string]int, len(reservedRanges))
for i, r := range reservedRanges {
require.LessOrEqual(t, bytes.Compare(r.Start[:], r.End[:]), 0,
"range %d is inverted: Start %s > End %s", i, r.Start.Hex(), r.End.Hex())
// An exact duplicate range is dead weight: the first entry always wins
// the linear scan, so the second can never change an answer.
k := r.Start.Hex() + "-" + r.End.Hex()
if prev, dup := seen[k]; dup {
t.Fatalf("range %d duplicates range %d (%s); one of them is dead", i, prev, k)
}
seen[k] = i
}
}
// ---------------------------------------------------------------------------
// RegisterModule — the collision gate
// ---------------------------------------------------------------------------
func TestRegisterModuleAcceptsReservedAddress(t *testing.T) {
isolate(t)
a := common.HexToAddress("0x0000000000000000000000000000000000002000")
require.NoError(t, RegisterModule(mod("k", a)))
got, ok := GetPrecompileModuleByAddress(a)
require.True(t, ok)
require.Equal(t, "k", got.ConfigKey)
}
func TestRegisterModuleRejectsBlackhole(t *testing.T) {
isolate(t)
err := RegisterModule(mod("blackhole", BlackholeAddr))
require.Error(t, err)
require.Contains(t, err.Error(), "blackhole")
// And nothing was recorded — a refused registration must not half-commit.
_, ok := GetPrecompileModuleByAddress(BlackholeAddr)
require.False(t, ok)
require.Empty(t, RegisteredModules())
}
func TestBlackholeIsTheWarpRangeStart(t *testing.T) {
// BlackholeAddr sits on the first address of the Warp/Teleport range, so the
// blackhole check is the ONLY thing keeping a precompile off it. If the
// blackhole guard is removed, ReservedAddress would happily admit it.
require.True(t, ReservedAddress(BlackholeAddr),
"blackhole is inside a reserved range — the explicit guard is load-bearing")
}
func TestRegisterModuleRejectsUnreservedAddress(t *testing.T) {
isolate(t)
err := RegisterModule(mod("stray", maxAddr))
require.Error(t, err)
require.Contains(t, err.Error(), "not in a reserved range")
require.Empty(t, RegisteredModules())
}
func TestRegisterModuleRejectsZeroAddressIsNotRejected(t *testing.T) {
isolate(t)
// The zero address IS a declared reserved range (LP-0150 dead/burn), so the
// gate admits it. Pinned deliberately: if the dead-address ranges are ever
// removed this flips, and that is a consensus-visible change that must be
// made on purpose.
require.True(t, ReservedAddress(common.Address{}))
require.NoError(t, RegisterModule(mod("zero", common.Address{})))
}
func TestRegisterModuleRejectsDuplicateAddress(t *testing.T) {
isolate(t)
a := common.HexToAddress("0x0000000000000000000000000000000000003001")
require.NoError(t, RegisterModule(mod("first", a)))
err := RegisterModule(mod("second", a))
require.Error(t, err)
require.Contains(t, err.Error(), "address collision")
// The winner is untouched: a rejected duplicate must not overwrite.
got, ok := GetPrecompileModuleByAddress(a)
require.True(t, ok)
require.Equal(t, "first", got.ConfigKey)
require.Len(t, RegisteredModules(), 1)
}
func TestRegisterModuleRejectsDuplicateConfigKey(t *testing.T) {
isolate(t)
a := common.HexToAddress("0x0000000000000000000000000000000000003002")
b := common.HexToAddress("0x0000000000000000000000000000000000003003")
require.NoError(t, RegisterModule(mod("shared", a)))
err := RegisterModule(mod("shared", b))
require.Error(t, err)
require.Contains(t, err.Error(), "config key collision")
_, ok := GetPrecompileModuleByAddress(b)
require.False(t, ok, "the rejected module must not be reachable by address")
require.Len(t, RegisteredModules(), 1)
}
func TestRegisterModuleCollisionIsOrderIndependent(t *testing.T) {
// Whichever import order the linker picks, a colliding pair must fail. Not
// "the second one wins" and not "it depends".
a := common.HexToAddress("0x0000000000000000000000000000000000003004")
for _, order := range [][2]Module{
{mod("x", a), mod("y", a)},
{mod("y", a), mod("x", a)},
} {
func() {
isolate(t)
require.NoError(t, RegisterModule(order[0]))
require.Error(t, RegisterModule(order[1]))
}()
}
}
func TestRegisterModuleAtEveryRangeEdge(t *testing.T) {
// Registering exactly on the first and last address of every reserved range
// must be accepted. This is the gate-level restatement of the boundary test:
// an off-by-one in Contains would reject a legitimate precompile address.
for i, r := range reservedRanges {
for name, a := range map[string]common.Address{"start": r.Start, "end": r.End} {
t.Run(fmt.Sprintf("range%02d_%s", i, name), func(t *testing.T) {
isolate(t)
err := RegisterModule(mod("edge", a))
if a == BlackholeAddr {
require.Error(t, err, "blackhole must stay refused even on a range edge")
return
}
require.NoError(t, err, "edge address %s must be registrable", a.Hex())
})
}
}
}
// ---------------------------------------------------------------------------
// Lookups
// ---------------------------------------------------------------------------
func TestGetPrecompileModuleByAddress(t *testing.T) {
isolate(t)
a := common.HexToAddress("0x0000000000000000000000000000000000004001")
require.NoError(t, RegisterModule(mod("hit", a)))
got, ok := GetPrecompileModuleByAddress(a)
require.True(t, ok)
require.Equal(t, a, got.Address)
miss, ok := GetPrecompileModuleByAddress(common.HexToAddress("0x0000000000000000000000000000000000004002"))
require.False(t, ok)
require.Equal(t, Module{}, miss, "a miss must return the zero Module, never a neighbour")
}
func TestGetPrecompileModuleByKey(t *testing.T) {
isolate(t)
a := common.HexToAddress("0x0000000000000000000000000000000000004003")
require.NoError(t, RegisterModule(mod("theKey", a)))
got, ok := GetPrecompileModule("theKey")
require.True(t, ok)
require.Equal(t, a, got.Address)
_, ok = GetPrecompileModule("nope")
require.False(t, ok)
// Key lookup is exact, not prefix or case-insensitive.
_, ok = GetPrecompileModule("theKe")
require.False(t, ok)
_, ok = GetPrecompileModule("THEKEY")
require.False(t, ok)
}
// ---------------------------------------------------------------------------
// Determinism — nothing consensus-reachable may depend on registration order
// ---------------------------------------------------------------------------
func TestRegisteredModulesSortedByAddress(t *testing.T) {
isolate(t)
addrs := []common.Address{
common.HexToAddress("0x0000000000000000000000000000000000005003"),
common.HexToAddress("0x0000000000000000000000000000000000005001"),
common.HexToAddress("0x0a00000000000000000000000000000000000000"),
common.HexToAddress("0x0000000000000000000000000000000000005002"),
common.HexToAddress("0x0200000000000000000000000000000000000000"),
}
for i, a := range addrs {
require.NoError(t, RegisterModule(mod(fmt.Sprintf("k%d", i), a)))
}
got := RegisteredModules()
require.Len(t, got, len(addrs))
for i := 1; i < len(got); i++ {
require.Negative(t, bytes.Compare(got[i-1].Address[:], got[i].Address[:]),
"registry must be strictly ascending by address")
}
}
func TestRegisteredModulesOrderIsIndependentOfRegistrationOrder(t *testing.T) {
// The host iterates RegisteredModules() to build the per-block precompile
// set. If that order tracked import order (or map iteration), two nodes with
// the same code could disagree. Every permutation must produce one order.
addrs := []common.Address{
common.HexToAddress("0x0000000000000000000000000000000000006001"),
common.HexToAddress("0x0000000000000000000000000000000000006002"),
common.HexToAddress("0x0000000000000000000000000000000000006003"),
common.HexToAddress("0x0000000000000000000000000000000000006004"),
}
var want []common.Address
for _, perm := range permutations(len(addrs)) {
var got []common.Address
func() {
isolate(t)
for _, i := range perm {
require.NoError(t, RegisterModule(mod(fmt.Sprintf("k%d", i), addrs[i])))
}
for _, m := range RegisteredModules() {
got = append(got, m.Address)
}
}()
if want == nil {
want = got
continue
}
require.Equal(t, want, got, "permutation %v produced a different order", perm)
}
}
func permutations(n int) [][]int {
if n == 0 {
return [][]int{{}}
}
var out [][]int
for _, sub := range permutations(n - 1) {
for pos := 0; pos <= len(sub); pos++ {
p := make([]int, 0, n)
p = append(p, sub[:pos]...)
p = append(p, n-1)
p = append(p, sub[pos:]...)
out = append(out, p)
}
}
return out
}
func TestRegisteredModulesCannotBeMutatedByCallers(t *testing.T) {
// The returned slice must not alias the registry. A consumer that sorts or
// truncates its copy would otherwise permanently reorder the consensus set
// for every later block — silently, with no error anywhere.
isolate(t)
lo := common.HexToAddress("0x0000000000000000000000000000000000007001")
hi := common.HexToAddress("0x0000000000000000000000000000000000007002")
require.NoError(t, RegisterModule(mod("lo", lo)))
require.NoError(t, RegisterModule(mod("hi", hi)))
caller := RegisteredModules()
caller[0], caller[1] = caller[1], caller[0]
caller[0].ConfigKey = "clobbered"
fresh := RegisteredModules()
require.Equal(t, lo, fresh[0].Address, "caller mutation leaked into the registry")
require.Equal(t, "lo", fresh[0].ConfigKey)
require.Equal(t, hi, fresh[1].Address)
}
// ---------------------------------------------------------------------------
// AlwaysOnModules
// ---------------------------------------------------------------------------
func TestAlwaysOnModulesFiltersAndKeepsOrder(t *testing.T) {
isolate(t)
type spec struct {
addr common.Address
alwaysOn bool
}
specs := []spec{
{common.HexToAddress("0x0000000000000000000000000000000000009003"), true},
{common.HexToAddress("0x0000000000000000000000000000000000009001"), false},
{common.HexToAddress("0x0000000000000000000000000000000000009002"), true},
{common.HexToAddress("0x0000000000000000000000000000000000009004"), false},
}
for i, s := range specs {
m := mod(fmt.Sprintf("k%d", i), s.addr)
m.AlwaysOn = s.alwaysOn
require.NoError(t, RegisterModule(m))
}
on := AlwaysOnModules()
require.Len(t, on, 2)
require.Equal(t, common.HexToAddress("0x0000000000000000000000000000000000009002"), on[0].Address)
require.Equal(t, common.HexToAddress("0x0000000000000000000000000000000000009003"), on[1].Address)
for _, m := range on {
require.True(t, m.AlwaysOn)
}
}
func TestAlwaysOnModulesEmptyWhenNoneMarked(t *testing.T) {
isolate(t)
require.NoError(t, RegisterModule(mod("plain", common.HexToAddress("0x0000000000000000000000000000000000009005"))))
require.Empty(t, AlwaysOnModules())
}
func TestAlwaysOnIsASubsetOfRegistered(t *testing.T) {
// Holds for the REAL registry too, whatever packages the test binary links.
all := make(map[common.Address]Module, len(RegisteredModules()))
for _, m := range RegisteredModules() {
all[m.Address] = m
}
for _, m := range AlwaysOnModules() {
got, ok := all[m.Address]
require.True(t, ok, "%s is always-on but not registered", m.Address.Hex())
require.Equal(t, got.ConfigKey, m.ConfigKey)
}
}
+180
View File
@@ -0,0 +1,180 @@
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// The invariants that keep this package honest: it must actually link the
// precompiles it claims to link, and every answer it gives must be derived from
// the addresses those precompiles registered — never from a second table.
package registry_test
import (
"fmt"
"sort"
"strings"
"testing"
"github.com/luxfi/geth/common"
"github.com/luxfi/precompile/modules"
"github.com/luxfi/precompile/registry"
"github.com/stretchr/testify/require"
)
// TestImportingRegistryRegistersPrecompiles is the reason a host writes
// `import _ "github.com/luxfi/precompile/registry"`. This test package imports
// registry and nothing else that registers, so if the blank imports inside
// registry.go were dropped the count would collapse.
func TestImportingRegistryRegistersPrecompiles(t *testing.T) {
got := modules.RegisteredModules()
require.Greater(t, len(got), 40,
"importing registry must pull in the precompile set; got %d", len(got))
}
// TestEveryRegisteredAddressIsReserved — the module gate refuses an address
// outside reservedRanges, so a registration that got through proves the address
// is admissible. Restated here over the full linked set so a range narrowed in
// modules/ cannot orphan a live precompile.
func TestEveryRegisteredAddressIsReserved(t *testing.T) {
var bad []string
for _, m := range modules.RegisteredModules() {
if !modules.ReservedAddress(m.Address) {
bad = append(bad, fmt.Sprintf("%s at %s", m.ConfigKey, m.Address.Hex()))
}
}
sort.Strings(bad)
require.Empty(t, bad, "registered outside every reserved range:\n %s", strings.Join(bad, "\n "))
}
// TestNoAddressOrKeyCollisionsAcrossTheWholeLinkedSet is the property
// RegisterModule enforces one call at a time, restated over the whole set that
// a production binary actually links. Two precompiles at one address is a
// consensus split; two at one config key silently drops one from upgrade.json.
func TestNoAddressOrKeyCollisionsAcrossTheWholeLinkedSet(t *testing.T) {
byAddr := map[common.Address]string{}
byKey := map[string]common.Address{}
for _, m := range modules.RegisteredModules() {
if prev, dup := byAddr[m.Address]; dup {
t.Fatalf("address %s claimed by %q and %q", m.Address.Hex(), prev, m.ConfigKey)
}
byAddr[m.Address] = m.ConfigKey
if prev, dup := byKey[m.ConfigKey]; dup {
t.Fatalf("config key %q claimed by %s and %s", m.ConfigKey, prev.Hex(), m.Address.Hex())
}
byKey[m.ConfigKey] = m.Address
}
require.Equal(t, len(byAddr), len(byKey))
}
// TestRegisteredModulesIsSortedAcrossTheWholeLinkedSet — the host relies on
// this ordering per block. Checking it on synthetic modules in the modules
// package is necessary; checking it on the real linked set is what matters.
func TestRegisteredModulesIsSortedAcrossTheWholeLinkedSet(t *testing.T) {
got := modules.RegisteredModules()
require.NotEmpty(t, got)
for i := 1; i < len(got); i++ {
require.Negative(t, bytesCompare(got[i-1].Address, got[i].Address),
"registry out of order at %d: %s then %s", i, got[i-1].Address.Hex(), got[i].Address.Hex())
}
}
func bytesCompare(a, b common.Address) int {
for i := range a {
switch {
case a[i] < b[i]:
return -1
case a[i] > b[i]:
return 1
}
}
return 0
}
// TestEveryRegisteredModuleIsUsable — a registered module with a nil Contract
// is an address the EVM will dispatch to and then nil-panic on. A nil
// Configurator on a module that is not AlwaysOn cannot be activated by config.
func TestEveryRegisteredModuleIsUsable(t *testing.T) {
for _, m := range modules.RegisteredModules() {
require.NotEmpty(t, m.ConfigKey, "%s registered with no config key", m.Address.Hex())
require.NotNil(t, m.Contract, "%s (%s) registered with a nil Contract", m.ConfigKey, m.Address.Hex())
if !m.AlwaysOn {
require.NotNil(t, m.Configurator,
"%s (%s) has no Configurator and is not AlwaysOn, so nothing can activate it",
m.ConfigKey, m.Address.Hex())
}
}
}
// TestConfiguratorsMakeTheirOwnConfig — MakeConfig is how the host unmarshals
// upgrade.json into a precompile's config. Returning nil, or a config whose
// Key() disagrees with the module's ConfigKey, routes an operator's upgrade
// entry to the wrong precompile or to none.
func TestConfiguratorsMakeTheirOwnConfig(t *testing.T) {
var bad []string
for _, m := range modules.RegisteredModules() {
if m.Configurator == nil {
continue
}
cfg := m.MakeConfig()
if cfg == nil {
bad = append(bad, fmt.Sprintf("%s: MakeConfig returned nil", m.ConfigKey))
continue
}
if got := cfg.Key(); got != m.ConfigKey {
bad = append(bad, fmt.Sprintf(
"%s at %s: MakeConfig().Key() is %q", m.ConfigKey, m.Address.Hex(), got))
}
}
sort.Strings(bad)
require.Empty(t, bad, "%d config-key mismatches:\n %s", len(bad), strings.Join(bad, "\n "))
}
// TestInFamilyAgreesWithTheRegistry — the derivation must not invent or drop
// members. Every family member is registered, and every registered PCII address
// whose page names a known family is in that family.
func TestInFamilyAgreesWithTheRegistry(t *testing.T) {
families := []string{"PQ", "EVM", "Privacy", "Threshold", "Bridge", "AI", "DEX"}
inSomeFamily := map[common.Address]bool{}
for _, f := range families {
members, ok := registry.InFamily(f)
require.True(t, ok)
for _, m := range members {
got, found := modules.GetPrecompileModuleByAddress(m.Address)
require.True(t, found, "%s is in family %s but not registered", m.Address.Hex(), f)
require.Equal(t, got.ConfigKey, m.ConfigKey)
inSomeFamily[m.Address] = true
}
}
pageToFamily := map[uint8]string{}
for _, f := range families {
p, ok := registry.FamilyPage(f)
require.True(t, ok)
pageToFamily[p] = f
}
var missed []string
for _, m := range modules.RegisteredModules() {
a := m.Address
// PCII addresses have 18 leading zero bytes.
pcii := true
for _, b := range a[:common.AddressLength-2] {
if b != 0 {
pcii = false
break
}
}
if !pcii {
continue
}
page := a[common.AddressLength-2] >> 4
if _, known := pageToFamily[page]; !known {
continue
}
if !inSomeFamily[a] {
missed = append(missed, fmt.Sprintf("%s at %s (page %d)", m.ConfigKey, a.Hex(), page))
}
}
sort.Strings(missed)
require.Empty(t, missed, "%d registered precompiles missing from their family:\n %s",
len(missed), strings.Join(missed, "\n "))
}
+152 -570
View File
@@ -1,620 +1,202 @@
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// Package registry links every precompile into the binary and answers questions
// about the resulting set.
//
// Importing this package — even blank — runs each precompile's init(), which is
// where it claims its address through modules.RegisterModule. A host that wants
// the whole Lux precompile surface imports registry and nothing else:
//
// import _ "github.com/luxfi/precompile/registry"
//
// There is exactly one record of where a precompile lives: the address it
// registers at init time. This package derives its answers from
// modules.RegisteredModules() rather than keeping a parallel table, so a
// question asked here and an address dispatched by the EVM cannot disagree.
//
// # Address scheme (LP-0099)
//
// LP-numbered precompiles use trailing-significant 20-byte addresses:
//
// 0x0000000000000000000000000000000000PCII
// │ │ └┴─ Item (8 bits, 256 per family×chain)
// │ └──── Chain slot (4 bits)
// └────── Family page (4 bits, the LP-Pxxx digit)
//
// Family page (P), aligned with the LP range's first digit:
//
// P=2 → LP-2xxx PQ identity P=3 → LP-3xxx EVM/crypto
// P=4 → LP-4xxx privacy/ZK P=5 → LP-5xxx threshold/MPC
// P=6 → LP-6xxx bridges P=7 → LP-7xxx AI
// P=9 → LP-9xxx DEX/markets
//
// Chain slot (C) is a 4-bit routing index over the chains that host C-nibble-
// routed precompiles, plus the sovereign-L1 EVM targets that reuse the C-Chain
// precompile surface. It is the mapping ChainSlot implements:
//
// C=0 P-Chain (platform) C=1 X-Chain (avm) C=2 C-Chain (evm)
// C=3 Q-Chain (quantumvm) C=4 A-Chain (aivm) C=5 B-Chain (bridgevm)
// C=6 Z-Chain (zkvm) C=7 M-Chain (mpcvm) C=8 Zoo (sovereign L1)
// C=9 Hanzo (sovereign L1) C=A SPC (sovereign L1)
//
// Chains outside this set address precompiles by full chain ID, not by nibble.
//
// Example: FROST on C-Chain is P=5, C=2, II=0x00 → LP-5200 →
// 0x0000000000000000000000000000000000005200.
package registry
import (
"fmt"
"github.com/luxfi/geth/common"
"github.com/luxfi/precompile/modules"
// Every package below claims an address in its init(). The blank imports
// are the entire reason this package exists as a link target.
_ "github.com/luxfi/precompile/ai"
_ "github.com/luxfi/precompile/aivmbridge"
_ "github.com/luxfi/precompile/anchor"
_ "github.com/luxfi/precompile/attestation"
_ "github.com/luxfi/precompile/babyjubjub"
_ "github.com/luxfi/precompile/blake3"
_ "github.com/luxfi/precompile/bls12381"
_ "github.com/luxfi/precompile/bridge"
_ "github.com/luxfi/precompile/cggmp21"
_ "github.com/luxfi/precompile/compute"
_ "github.com/luxfi/precompile/corona"
_ "github.com/luxfi/precompile/curve25519"
_ "github.com/luxfi/precompile/dead"
_ "github.com/luxfi/precompile/dex"
_ "github.com/luxfi/precompile/ed25519"
_ "github.com/luxfi/precompile/fhe"
_ "github.com/luxfi/precompile/frost"
_ "github.com/luxfi/precompile/graph"
_ "github.com/luxfi/precompile/hpke"
_ "github.com/luxfi/precompile/hqc"
_ "github.com/luxfi/precompile/inference"
_ "github.com/luxfi/precompile/kzg4844"
_ "github.com/luxfi/precompile/magnetar"
_ "github.com/luxfi/precompile/math"
_ "github.com/luxfi/precompile/mldsa"
_ "github.com/luxfi/precompile/mlkem"
_ "github.com/luxfi/precompile/modelregistry"
_ "github.com/luxfi/precompile/p3q"
_ "github.com/luxfi/precompile/pasta"
_ "github.com/luxfi/precompile/pedersen"
_ "github.com/luxfi/precompile/poseidon"
_ "github.com/luxfi/precompile/pulsar"
_ "github.com/luxfi/precompile/ring"
_ "github.com/luxfi/precompile/secp256r1"
_ "github.com/luxfi/precompile/slhdsa"
_ "github.com/luxfi/precompile/sr25519"
_ "github.com/luxfi/precompile/stableswap"
_ "github.com/luxfi/precompile/starkfri"
_ "github.com/luxfi/precompile/swap"
_ "github.com/luxfi/precompile/v3"
_ "github.com/luxfi/precompile/vrf"
_ "github.com/luxfi/precompile/x25519"
_ "github.com/luxfi/precompile/xwing"
_ "github.com/luxfi/precompile/zk"
)
// ============================================================================
// PRECOMPILE ADDRESS SCHEME - Aligned with LP Numbering (LP-0099)
// ============================================================================
//
// LP-numbered precompiles (Pages 2-9) use trailing-significant 20-byte addresses:
// Format: 0x0000000000000000000000000000000000PCII
//
// Legacy module ranges (math 0x0400..0050, stableswap 0x0400..0060) use
// leading-significant addresses and are NOT part of the PCII scheme.
//
// The address ends with the 16-bit LP number (PCII) for easy identification.
// The selector encodes:
// 0x 0000...0000 P C II
// │ │ └┴─ Item/function (8 bits, 256 items per family×chain)
// │ └──── Chain slot (4 bits, 16 chains max, 11 assigned)
// └────── Family page (4 bits, aligned with LP-Pxxx)
//
// P nibble = LP range first digit:
// P=2 → LP-2xxx (PQ Identity)
// P=3 → LP-3xxx (EVM/Crypto)
// P=4 → LP-4xxx (Privacy/ZK)
// P=5 → LP-5xxx (Threshold/MPC)
// P=6 → LP-6xxx (Bridges)
// P=7 → LP-7xxx (AI)
// P=9 → LP-9xxx (DEX/Markets)
//
// C nibble = Chain slot (4-bit address-routing index). The primary-
// network chain letters are the authoritative set in
// node/genesis/builder/registry.go (P/X/C/D/Q/A/B/T/Z/G/K/I/O/R).
// This nibble can only address 16 slots, so it enumerates the chains
// that host C-nibble-routed precompiles plus the sovereign-L1 EVM
// targets that reuse the C-Chain EVM precompile surface:
// C=0 → P-Chain (platform)
// C=1 → X-Chain (avm)
// C=2 → C-Chain (evm, main EVM)
// C=3 → D-Chain (dexvm — DEX/CLOB)
// C=4 → A-Chain (aivm — AI / attestation)
// C=5 → B-Chain (bridgevm — bridge)
// C=6 → Z-Chain (zkvm — zk-rollup / privacy)
// C=7 → Q-Chain (quantumvm — PQ consensus signing)
// C=8 → Zoo (sovereign L1 EVM, white-label)
// C=9 → Hanzo (sovereign L1 EVM, white-label)
// C=A → SPC (sovereign L1 EVM, white-label)
// T/G/K/I/O/R primary-network chains do not register C-nibble-routed
// precompiles; they address by full chain ID, not by this nibble.
//
// Example: FROST on C-Chain = P=5 (Threshold), C=2 (C-Chain), II=00
// Address = 0x0000000000000000000000000000000000005200 (LP-5200)
// nibbleMax is the largest value a 4-bit address nibble can hold.
const nibbleMax = 15
const (
// =========================================================================
// STANDARD EVM (0x01-0x11) - Native to EVM, not in our range
// =========================================================================
// 0x01 = ECRECOVER
// 0x02 = SHA256
// 0x03 = RIPEMD160
// 0x04 = IDENTITY
// 0x05 = MODEXP
// 0x06 = ECADD (BN254)
// 0x07 = ECMUL (BN254)
// 0x08 = ECPAIRING (BN254)
// 0x09 = BLAKE2F
// 0x0A = KZG Point Evaluation (EIP-4844)
// 0x0B-0x11 = BLS12-381 (EIP-2537)
// BLS12-381 (0x0B-0x11) - EIP-2537 (standard EVM addresses)
BLS12381G1AddAddress = "0x000000000000000000000000000000000000000b"
BLS12381G1MulAddress = "0x000000000000000000000000000000000000000c"
BLS12381G1MSMAddress = "0x000000000000000000000000000000000000000d"
BLS12381G2AddAddress = "0x000000000000000000000000000000000000000e"
BLS12381G2MulAddress = "0x000000000000000000000000000000000000000f"
BLS12381G2MSMAddress = "0x0000000000000000000000000000000000000010"
BLS12381PairingAddress = "0x0000000000000000000000000000000000000011"
// secp256r1 (P-256) - EIP-7212 (passkeys/WebAuthn)
P256VerifyAddress = "0x0000000000000000000000000000000000000100"
// =========================================================================
// PAGE 2: PQ IDENTITY (0x2CII) → LP-2xxx
// =========================================================================
// Post-Quantum Signatures (II = 0x00-0x0F)
MLDSACChain = "0x0000000000000000000000000000000000002200" // C-Chain ML-DSA (LP-2200)
MLDSAQChain = "0x0000000000000000000000000000000000002300" // Q-Chain ML-DSA (LP-2300)
MLKEMCChain = "0x0000000000000000000000000000000000002201" // C-Chain ML-KEM (LP-2201)
MLKEMQChain = "0x0000000000000000000000000000000000002301" // Q-Chain ML-KEM (LP-2301)
SLHDSACChain = "0x0000000000000000000000000000000000002202" // C-Chain SLH-DSA (LP-2202)
SLHDSAQChain = "0x0000000000000000000000000000000000002302" // Q-Chain SLH-DSA (LP-2302)
FalconCChain = "0x0000000000000000000000000000000000002203" // C-Chain Falcon (LP-2203)
FalconQChain = "0x0000000000000000000000000000000000002303" // Q-Chain Falcon (LP-2303)
// PQ Key Exchange (II = 0x10-0x1F)
KyberCChain = "0x0000000000000000000000000000000000002210" // C-Chain Kyber (LP-2210)
KyberQChain = "0x0000000000000000000000000000000000002310" // Q-Chain Kyber (LP-2310)
NTRUCChain = "0x0000000000000000000000000000000000002211" // C-Chain NTRU (LP-2211)
NTRUQChain = "0x0000000000000000000000000000000000002311" // Q-Chain NTRU (LP-2311)
// Hybrid Modes (II = 0x20-0x2F)
HybridSignCChain = "0x0000000000000000000000000000000000002220" // C-Chain ECDSA+ML-DSA (LP-2220)
HybridSignQChain = "0x0000000000000000000000000000000000002320" // Q-Chain ECDSA+ML-DSA (LP-2320)
HybridKEMCChain = "0x0000000000000000000000000000000000002221" // C-Chain X25519+Kyber (LP-2221)
HybridKEMQChain = "0x0000000000000000000000000000000000002321" // Q-Chain X25519+Kyber (LP-2321)
// =========================================================================
// PAGE 3: EVM/CRYPTO (0x3CII) → LP-3xxx
// =========================================================================
// Hashing (II = 0x00-0x0F)
Poseidon2CChain = "0x3200000000000000000000000000000000000000" // C-Chain Poseidon2
Poseidon2ZChain = "0x3600000000000000000000000000000000000000" // Z-Chain Poseidon2
Poseidon2SpongeCCh = "0x3201000000000000000000000000000000000000" // C-Chain Poseidon2Sponge
Blake3CChain = "0x3202000000000000000000000000000000000000" // C-Chain Blake3
Blake3ZChain = "0x3602000000000000000000000000000000000000" // Z-Chain Blake3
PedersenCChain = "0x3203000000000000000000000000000000000000" // C-Chain Pedersen
PedersenZChain = "0x3603000000000000000000000000000000000000" // Z-Chain Pedersen
MiMCCChain = "0x3204000000000000000000000000000000000000" // C-Chain MiMC
RescueCChain = "0x3205000000000000000000000000000000000000" // C-Chain Rescue
// Classical Signatures (II = 0x10-0x1F)
ECDSACChain = "0x3210000000000000000000000000000000000000" // Extended ECDSA
Ed25519CChain = "0x3211000000000000000000000000000000000000" // Ed25519
BLS381CChain = "0x3212000000000000000000000000000000000000" // BLS12-381
VRFCChain = "0x0000000000000000000000000000000000003213" // ECVRF (RFC 9381)
// Encryption (II = 0x20-0x2F)
AESGCMCChain = "0x3220000000000000000000000000000000000000" // AES-GCM
ChaCha20CChain = "0x3221000000000000000000000000000000000000" // ChaCha20-Poly1305
HPKECChain = "0x3222000000000000000000000000000000000000" // HPKE
ECIESCChain = "0x3223000000000000000000000000000000000000" // ECIES
// =========================================================================
// PAGE 4: PRIVACY/ZK (0x4CII) → LP-4xxx
// =========================================================================
// SNARKs (II = 0x00-0x0F)
Groth16CChain = "0x4200000000000000000000000000000000000000" // C-Chain Groth16
Groth16ZChain = "0x4600000000000000000000000000000000000000" // Z-Chain Groth16
PLONKCChain = "0x4201000000000000000000000000000000000000" // C-Chain PLONK
PLONKZChain = "0x4601000000000000000000000000000000000000" // Z-Chain PLONK
fflonkCChain = "0x4202000000000000000000000000000000000000" // C-Chain fflonk
fflonkZChain = "0x4602000000000000000000000000000000000000" // Z-Chain fflonk
Halo2CChain = "0x4203000000000000000000000000000000000000" // C-Chain Halo2
Halo2ZChain = "0x4603000000000000000000000000000000000000" // Z-Chain Halo2
NovaCChain = "0x4204000000000000000000000000000000000000" // C-Chain Nova
NovaZChain = "0x4604000000000000000000000000000000000000" // Z-Chain Nova
// STARKs (II = 0x10-0x1F)
STARKCChain = "0x4210000000000000000000000000000000000000" // C-Chain STARK
STARKZChain = "0x4610000000000000000000000000000000000000" // Z-Chain STARK
STARKRecursiveCCh = "0x4211000000000000000000000000000000000000" // C-Chain STARKRecursive
STARKRecursiveZCh = "0x4611000000000000000000000000000000000000" // Z-Chain STARKRecursive
STARKBatchCChain = "0x4212000000000000000000000000000000000000" // C-Chain STARKBatch
STARKBatchZChain = "0x4612000000000000000000000000000000000000" // Z-Chain STARKBatch
STARKReceiptsCCh = "0x421F000000000000000000000000000000000000" // C-Chain STARKReceipts
STARKReceiptsZCh = "0x461F000000000000000000000000000000000000" // Z-Chain STARKReceipts
// Commitments (II = 0x20-0x2F)
KZGCChain = "0x4220000000000000000000000000000000000000" // C-Chain KZG
KZGZChain = "0x4620000000000000000000000000000000000000" // Z-Chain KZG
IPACChain = "0x4221000000000000000000000000000000000000" // C-Chain IPA
IPAZChain = "0x4621000000000000000000000000000000000000" // Z-Chain IPA
FRICChain = "0x4222000000000000000000000000000000000000" // C-Chain FRI
FRIZChain = "0x4622000000000000000000000000000000000000" // Z-Chain FRI
// Privacy Primitives (II = 0x30-0x3F)
RangeProofCChain = "0x4230000000000000000000000000000000000000" // C-Chain Bulletproofs
RangeProofZChain = "0x4630000000000000000000000000000000000000" // Z-Chain Bulletproofs
NullifierCChain = "0x4231000000000000000000000000000000000000" // C-Chain Nullifier
NullifierZChain = "0x4631000000000000000000000000000000000000" // Z-Chain Nullifier
CommitmentCChain = "0x4232000000000000000000000000000000000000" // C-Chain Commitment
CommitmentZChain = "0x4632000000000000000000000000000000000000" // Z-Chain Commitment
MerkleProofCChain = "0x4233000000000000000000000000000000000000" // C-Chain MerkleProof
MerkleProofZChain = "0x4633000000000000000000000000000000000000" // Z-Chain MerkleProof
// FHE (II = 0x40-0x4F)
FHECChain = "0x4240000000000000000000000000000000000000" // C-Chain FHE
FHEZChain = "0x4640000000000000000000000000000000000000" // Z-Chain FHE
TFHECChain = "0x4241000000000000000000000000000000000000" // C-Chain TFHE
TFHEZChain = "0x4641000000000000000000000000000000000000" // Z-Chain TFHE
CKKSCChain = "0x4242000000000000000000000000000000000000" // C-Chain CKKS
CKKSZChain = "0x4642000000000000000000000000000000000000" // Z-Chain CKKS
BGVCChain = "0x4243000000000000000000000000000000000000" // C-Chain BGV
BGVZChain = "0x4643000000000000000000000000000000000000" // Z-Chain BGV
GatewayCChain = "0x4244000000000000000000000000000000000000" // C-Chain Gateway
GatewayZChain = "0x4644000000000000000000000000000000000000" // Z-Chain Gateway
TaskManagerCChain = "0x4245000000000000000000000000000000000000" // C-Chain TaskManager
TaskManagerZChain = "0x4645000000000000000000000000000000000000" // Z-Chain TaskManager
// =========================================================================
// PAGE 5: THRESHOLD/MPC (0x5CII) → LP-5xxx
// =========================================================================
// Threshold Signatures (II = 0x00-0x0F)
FROSTCChain = "0x5200000000000000000000000000000000000000" // C-Chain FROST
FROSTQChain = "0x5300000000000000000000000000000000000000" // Q-Chain FROST
CGGMP21CChain = "0x5201000000000000000000000000000000000000" // C-Chain CGGMP21
CGGMP21QChain = "0x5301000000000000000000000000000000000000" // Q-Chain CGGMP21
CoronaCChain = "0x5202000000000000000000000000000000000000" // C-Chain Corona
CoronaQChain = "0x5302000000000000000000000000000000000000" // Q-Chain Corona
DoernerCChain = "0x5203000000000000000000000000000000000000" // C-Chain Doerner
DoernerQChain = "0x5303000000000000000000000000000000000000" // Q-Chain Doerner
BLSThreshCChain = "0x5204000000000000000000000000000000000000" // C-Chain BLS Threshold
BLSThreshQChain = "0x5304000000000000000000000000000000000000" // Q-Chain BLS Threshold
// Secret Sharing (II = 0x10-0x1F)
LSSCChain = "0x5210000000000000000000000000000000000000" // C-Chain LSS
LSSQChain = "0x5310000000000000000000000000000000000000" // Q-Chain LSS
ShamirCChain = "0x5211000000000000000000000000000000000000" // C-Chain Shamir
ShamirQChain = "0x5311000000000000000000000000000000000000" // Q-Chain Shamir
FeldmanCChain = "0x5212000000000000000000000000000000000000" // C-Chain Feldman
FeldmanQChain = "0x5312000000000000000000000000000000000000" // Q-Chain Feldman
// DKG/Custody (II = 0x20-0x2F)
DKGCChain = "0x5220000000000000000000000000000000000000" // C-Chain DKG
DKGQChain = "0x5320000000000000000000000000000000000000" // Q-Chain DKG
RefreshCChain = "0x5221000000000000000000000000000000000000" // C-Chain Key Refresh
RefreshQChain = "0x5321000000000000000000000000000000000000" // Q-Chain Key Refresh
RecoveryCChain = "0x5222000000000000000000000000000000000000" // C-Chain Recovery
RecoveryQChain = "0x5322000000000000000000000000000000000000" // Q-Chain Recovery
// =========================================================================
// PAGE 6: BRIDGES (0x6CII) → LP-6xxx
// =========================================================================
// Warp Messaging (II = 0x00-0x0F)
WarpSendCChain = "0x6200000000000000000000000000000000000000" // C-Chain WarpSend
WarpSendBChain = "0x6500000000000000000000000000000000000000" // B-Chain WarpSend
WarpReceiveCChain = "0x6201000000000000000000000000000000000000" // C-Chain WarpReceive
WarpReceiveBChain = "0x6501000000000000000000000000000000000000" // B-Chain WarpReceive
WarpReceiptsCChain = "0x6202000000000000000000000000000000000000" // C-Chain WarpReceipts
WarpReceiptsBChain = "0x6502000000000000000000000000000000000000" // B-Chain WarpReceipts
// Token Bridges (II = 0x10-0x1F)
BridgeCChain = "0x6210000000000000000000000000000000000000" // C-Chain Bridge
BridgeBChain = "0x6510000000000000000000000000000000000000" // B-Chain Bridge
TeleportCChain = "0x6211000000000000000000000000000000000000" // C-Chain Teleport
TeleportBChain = "0x6511000000000000000000000000000000000000" // B-Chain Teleport
BridgeRouterCChain = "0x6212000000000000000000000000000000000000" // C-Chain BridgeRouter
BridgeRouterBChain = "0x6512000000000000000000000000000000000000" // B-Chain BridgeRouter
// Fee Collection (II = 0x20-0x2F)
FeeCollectCChain = "0x6220000000000000000000000000000000000000" // C-Chain FeeCollect
FeeCollectBChain = "0x6520000000000000000000000000000000000000" // B-Chain FeeCollect
FeeGovCChain = "0x6221000000000000000000000000000000000000" // C-Chain FeeGov
FeeGovBChain = "0x6521000000000000000000000000000000000000" // B-Chain FeeGov
// =========================================================================
// PAGE 7: AI (0x7CII) → LP-7xxx
// =========================================================================
// Attestation (II = 0x00-0x0F)
GPUAttestCChain = "0x7200000000000000000000000000000000000000" // C-Chain GPU Attestation
GPUAttestAChain = "0x7400000000000000000000000000000000000000" // A-Chain GPU Attestation
GPUAttestHanzo = "0x7900000000000000000000000000000000000000" // Hanzo GPU Attestation
TEEVerifyCChain = "0x7201000000000000000000000000000000000000" // C-Chain TEE Verify
TEEVerifyAChain = "0x7401000000000000000000000000000000000000" // A-Chain TEE Verify
NVTrustCChain = "0x7202000000000000000000000000000000000000" // C-Chain NVTrust
NVTrustAChain = "0x7402000000000000000000000000000000000000" // A-Chain NVTrust
SGXAttestCChain = "0x7203000000000000000000000000000000000000" // C-Chain SGX Attestation
SGXAttestAChain = "0x7403000000000000000000000000000000000000" // A-Chain SGX Attestation
TDXAttestCChain = "0x7204000000000000000000000000000000000000" // C-Chain TDX Attestation
TDXAttestAChain = "0x7404000000000000000000000000000000000000" // A-Chain TDX Attestation
// Inference (II = 0x10-0x1F)
InferenceCChain = "0x7210000000000000000000000000000000000000" // C-Chain Inference
InferenceAChain = "0x7410000000000000000000000000000000000000" // A-Chain Inference
InferenceHanzo = "0x7910000000000000000000000000000000000000" // Hanzo Inference
ProvenanceCChain = "0x7211000000000000000000000000000000000000" // C-Chain Provenance
ProvenanceAChain = "0x7411000000000000000000000000000000000000" // A-Chain Provenance
ModelHashCChain = "0x7212000000000000000000000000000000000000" // C-Chain ModelHash
ModelHashAChain = "0x7412000000000000000000000000000000000000" // A-Chain ModelHash
// Mining (II = 0x20-0x2F)
SessionCChain = "0x7220000000000000000000000000000000000000" // C-Chain Session
SessionAChain = "0x7420000000000000000000000000000000000000" // A-Chain Session
SessionHanzo = "0x7920000000000000000000000000000000000000" // Hanzo Session
HeartbeatCChain = "0x7221000000000000000000000000000000000000" // C-Chain Heartbeat
HeartbeatAChain = "0x7421000000000000000000000000000000000000" // A-Chain Heartbeat
RewardCChain = "0x7222000000000000000000000000000000000000" // C-Chain Reward
RewardAChain = "0x7422000000000000000000000000000000000000" // A-Chain Reward
// =========================================================================
// DATA ANCHORING (0x0700) - Privacy/Encryption page
// =========================================================================
// State checkpoint anchoring for CRDT/off-chain state durability
AnchorCChain = "0x0700000000000000000000000000000000000000" // Anchor (checkpoint root storage)
// =========================================================================
// PAGE 9: DEX/MARKETS → LP-9xxx (addresses match LP numbers directly)
// =========================================================================
// LP-9000: DEX Core Trading Protocol (spec, not precompile)
// LP-9001: DEX Trading Engine (spec, not precompile)
// LP-9010: DEX Precompile - Native HFT Order Book (PoolManager, Uniswap v4 style)
// LP-9011: Oracle Precompile - Multi-Source Price Aggregation
// LP-9015: Precompile Registry - DeFi Precompile Addresses
// Core DEX (LP-9010 series - Uniswap v4 style singleton PoolManager)
LXPool = "0x0000000000000000000000000000000000009010" // LP-9010 LXPool (singleton AMM)
LXOracle = "0x0000000000000000000000000000000000009011" // LP-9011 LXOracle (price aggregation)
LXRouter = "0x0000000000000000000000000000000000009012" // LP-9012 LXRouter (swap routing)
LXHooks = "0x0000000000000000000000000000000000009013" // LP-9013 LXHooks (hook registry)
LXFlash = "0x0000000000000000000000000000000000009014" // LP-9014 LXFlash (flash loans)
// Trading & DeFi Extensions (LP-90xx)
LXBook = "0x0000000000000000000000000000000000009020" // LP-9020 LXBook (orderbook + matching)
LXVault = "0x0000000000000000000000000000000000009030" // LP-9030 LXVault (custody + margin)
LXFeed = "0x0000000000000000000000000000000000009040" // LP-9040 LXFeed (computed prices)
LXLend = "0x0000000000000000000000000000000000009050" // LP-9050 LXLend (lending pool)
LXLiquid = "0x0000000000000000000000000000000000009060" // LP-9060 LXLiquid (self-repaying loans)
Liquidator = "0x0000000000000000000000000000000000009070" // LP-9070 Liquidator (position liquidation)
LiquidFX = "0x0000000000000000000000000000000000009080" // LP-9080 LiquidFX (transmuter)
LXSwap = "0x00000000000000000000000000000000000090A0" // LP-90A0 LXSwap (cross-chain HTLC atomic swap)
)
// PrecompileAddress calculates address from (P, C, II) nibbles
// P = Family page (aligned with LP-Pxxx), C = Chain slot, II = Item
// Returns PCII address: 0x0000000000000000000000000000000000PCII
// The address ends with the LP number (e.g., 9200 for LP-9200 PoolManager)
func PrecompileAddress(p, c, ii uint8) common.Address {
if p > 15 || c > 15 {
return common.Address{}
// PrecompileAddress builds the PCII address for a family page, chain slot and
// item. ok is false when p or c does not fit in a nibble; the address is zero
// in that case, which is why the boolean exists — the zero address is also the
// legitimate answer for (0, 0, 0).
func PrecompileAddress(p, c, ii uint8) (addr common.Address, ok bool) {
if p > nibbleMax || c > nibbleMax {
return common.Address{}, false
}
// Build the 4-character selector: PCII (hex)
selector := fmt.Sprintf("%x%x%02x", p, c, ii)
// Pad with leading zeros to 40 hex chars (20 bytes)
addr := "0000000000000000000000000000000000" + selector
return common.HexToAddress("0x" + addr)
addr[common.AddressLength-2] = p<<4 | c
addr[common.AddressLength-1] = ii
return addr, true
}
// ChainSlot returns the C-nibble for a chain name
func ChainSlot(chain string) uint8 {
// ChainSlot returns the C-nibble for a chain name. ok is false for a chain that
// does not route precompiles by nibble.
func ChainSlot(chain string) (slot uint8, ok bool) {
switch chain {
case "P", "p":
return 0
return 0, true
case "X", "x":
return 1
return 1, true
case "C", "c":
return 2
return 2, true
case "Q", "q":
return 3
return 3, true
case "A", "a":
return 4
return 4, true
case "B", "b":
return 5
return 5, true
case "Z", "z":
return 6
return 6, true
case "M", "m":
return 7
return 7, true
case "Zoo", "zoo":
return 8
return 8, true
case "Hanzo", "hanzo":
return 9
return 9, true
case "SPC", "spc":
return 0xA
return 0xA, true
default:
return 0xFF
return 0, false
}
}
// FamilyPage returns the P-nibble for a family name (aligned with LP-Pxxx)
func FamilyPage(family string) uint8 {
// FamilyPage returns the P-nibble for a family name, aligned with LP-Pxxx.
// ok is false for a name that names no family.
func FamilyPage(family string) (page uint8, ok bool) {
switch family {
case "PQ", "pq":
return 2 // LP-2xxx
return 2, true
case "EVM", "evm", "Crypto", "crypto":
return 3 // LP-3xxx
return 3, true
case "Privacy", "privacy", "ZK", "zk":
return 4 // LP-4xxx
return 4, true
case "Threshold", "threshold", "MPC", "mpc":
return 5 // LP-5xxx
return 5, true
case "Bridge", "bridge":
return 6 // LP-6xxx
return 6, true
case "AI", "ai":
return 7 // LP-7xxx
return 7, true
case "DEX", "dex", "Markets", "markets":
return 9 // LP-9xxx
return 9, true
default:
return 0xFF
return 0, false
}
}
// ChainPrecompiles defines which precompiles are enabled for each chain
var ChainPrecompiles = map[string][]string{
// C-Chain (main EVM) - all families enabled
"C": {
// BLS12-381 (standard EVM)
BLS12381G1AddAddress, BLS12381G1MulAddress, BLS12381G1MSMAddress,
BLS12381G2AddAddress, BLS12381G2MulAddress, BLS12381G2MSMAddress,
BLS12381PairingAddress,
// P-256
P256VerifyAddress,
// PQ (P=2)
MLDSACChain, MLKEMCChain, SLHDSACChain, HybridSignCChain,
// Crypto (P=3)
ECDSACChain, Ed25519CChain, BLS381CChain, VRFCChain,
Poseidon2CChain, Blake3CChain, PedersenCChain, ECIESCChain,
// Privacy/ZK (P=4)
Groth16CChain, PLONKCChain, STARKCChain, KZGCChain, FHECChain, RangeProofCChain,
// Threshold (P=5)
FROSTCChain, CGGMP21CChain, CoronaCChain, LSSCChain, DKGCChain,
// Bridges (P=6)
WarpSendCChain, WarpReceiveCChain, BridgeCChain, TeleportCChain,
// AI (P=7)
GPUAttestCChain, TEEVerifyCChain, InferenceCChain, SessionCChain,
// DEX (LP-9xxx)
LXPool, LXRouter, LXHooks, LXFlash, LXOracle, LXBook, LXVault, LXFeed, LXLend, LXLiquid, Liquidator, LiquidFX, LXSwap,
},
// Q-Chain (Quantum) - PQ and Threshold focused
"Q": {
// PQ (P=2)
MLDSAQChain, MLKEMQChain, SLHDSAQChain, FalconQChain, KyberQChain, HybridSignQChain,
// Threshold (P=5)
FROSTQChain, CGGMP21QChain, CoronaQChain, LSSQChain, DKGQChain,
},
// A-Chain (AI) - AI focused
"A": {
// AI (P=7)
GPUAttestAChain, TEEVerifyAChain, NVTrustAChain, SGXAttestAChain, TDXAttestAChain,
InferenceAChain, ProvenanceAChain, ModelHashAChain,
SessionAChain, HeartbeatAChain, RewardAChain,
// Bridges (P=6) - for cross-chain AI
WarpSendCChain, WarpReceiveCChain,
},
// B-Chain (Bridge) - Bridge focused
"B": {
// Bridges (P=6)
WarpSendBChain, WarpReceiveBChain, WarpReceiptsBChain,
BridgeBChain, TeleportBChain, BridgeRouterBChain,
FeeCollectBChain, FeeGovBChain,
},
// Z-Chain (Privacy) - ZK/Privacy focused
"Z": {
// Crypto (P=3)
Poseidon2ZChain, Blake3ZChain, PedersenZChain,
// Privacy/ZK (P=4)
Groth16ZChain, PLONKZChain, fflonkZChain, Halo2ZChain, NovaZChain,
STARKZChain, STARKRecursiveZCh, STARKBatchZChain,
KZGZChain, IPAZChain, FRIZChain,
RangeProofZChain, NullifierZChain, CommitmentZChain, MerkleProofZChain,
FHEZChain, TFHEZChain, CKKSZChain, GatewayZChain,
},
// Zoo - DEX focused (same precompile addresses)
"Zoo": {
// DEX (LP-9xxx) - same addresses as C-Chain
LXPool, LXRouter, LXHooks, LXFlash, LXOracle, LXBook, LXVault, LXFeed, LXLend, LXLiquid, Liquidator, LiquidFX, LXSwap,
// Bridges for cross-chain trading
WarpSendCChain, WarpReceiveCChain,
},
// Hanzo - AI focused
"Hanzo": {
// AI (P=7)
GPUAttestHanzo, InferenceHanzo, SessionHanzo,
// Bridges for cross-chain AI
WarpSendCChain, WarpReceiveCChain,
},
// P-Chain (Platform) - Minimal
"P": {
WarpSendCChain, WarpReceiveCChain,
},
// X-Chain (Exchange) - UTXO
"X": {
WarpSendCChain, WarpReceiveCChain,
},
}
// PrecompileInfo contains metadata about a precompile
type PrecompileInfo struct {
Address string
Name string
Description string
GasBase uint64
Chains []string
LPRange string // LP-Pxxx range alignment
}
// AllPrecompiles lists all available precompiles with their metadata
var AllPrecompiles = []PrecompileInfo{
// BLS12-381 (standard EVM, EIP-2537)
{BLS12381G1AddAddress, "BLS12381_G1ADD", "BLS12-381 G1 point addition", 500, []string{"C"}, "EIP-2537"},
{BLS12381G1MulAddress, "BLS12381_G1MUL", "BLS12-381 G1 scalar multiplication", 12000, []string{"C"}, "EIP-2537"},
{BLS12381G1MSMAddress, "BLS12381_G1MSM", "BLS12-381 G1 multi-scalar multiplication", 12000, []string{"C"}, "EIP-2537"},
{BLS12381G2AddAddress, "BLS12381_G2ADD", "BLS12-381 G2 point addition", 800, []string{"C"}, "EIP-2537"},
{BLS12381G2MulAddress, "BLS12381_G2MUL", "BLS12-381 G2 scalar multiplication", 45000, []string{"C"}, "EIP-2537"},
{BLS12381G2MSMAddress, "BLS12381_G2MSM", "BLS12-381 G2 multi-scalar multiplication", 45000, []string{"C"}, "EIP-2537"},
{BLS12381PairingAddress, "BLS12381_PAIRING", "BLS12-381 pairing check (65000 + 43000*n)", 108000, []string{"C"}, "EIP-2537"},
// P-256
{P256VerifyAddress, "P256_VERIFY", "secp256r1/P-256 signature verification", 3450, []string{"C"}, "EIP-7212"},
// PQ Identity (P=2) → LP-2xxx
{MLDSACChain, "ML_DSA", "NIST ML-DSA post-quantum signatures", 50000, []string{"C", "Q"}, "LP-2xxx"},
{MLKEMCChain, "ML_KEM", "NIST ML-KEM key encapsulation", 25000, []string{"C", "Q"}, "LP-2xxx"},
{SLHDSACChain, "SLH_DSA", "NIST SLH-DSA hash-based signatures", 75000, []string{"C", "Q"}, "LP-2xxx"},
{HybridSignCChain, "HYBRID_SIGN", "ECDSA+ML-DSA hybrid signatures", 75000, []string{"C", "Q"}, "LP-2xxx"},
// EVM/Crypto (P=3) → LP-3xxx
{Poseidon2CChain, "POSEIDON2", "ZK-friendly Poseidon2 hash", 20000, []string{"C", "Z"}, "LP-3xxx"},
{Blake3CChain, "BLAKE3", "High-performance Blake3 hash", 5000, []string{"C", "Z"}, "LP-3xxx"},
{PedersenCChain, "PEDERSEN", "Pedersen commitment", 15000, []string{"C", "Z"}, "LP-3xxx"},
{ECDSACChain, "ECDSA_EXT", "Extended ECDSA verification (multi-curve)", 5000, []string{"C"}, "LP-3xxx"},
{Ed25519CChain, "ED25519_VERIFY", "Ed25519 signature verification (Solana/TON/XRP)", 3000, []string{"C"}, "LP-3xxx"},
{BLS381CChain, "BLS381", "BLS12-381 aggregate signatures", 15000, []string{"C"}, "LP-3xxx"},
{VRFCChain, "VRF", "ECVRF-EDWARDS25519-SHA512-ELL2 (RFC 9381)", 20000, []string{"C"}, "LP-3xxx"},
{ECIESCChain, "ECIES", "Elliptic Curve Integrated Encryption", 25000, []string{"C"}, "LP-3xxx"},
// Privacy/ZK (P=4) → LP-4xxx
{Groth16CChain, "GROTH16", "Groth16 ZK proof verification", 150000, []string{"C", "Z"}, "LP-4xxx"},
{PLONKCChain, "PLONK", "PLONK ZK proof verification", 175000, []string{"C", "Z"}, "LP-4xxx"},
{STARKCChain, "STARK", "STARK proof verification", 200000, []string{"C", "Z"}, "LP-4xxx"},
{KZGCChain, "KZG", "KZG polynomial commitments", 50000, []string{"C", "Z"}, "LP-4xxx"},
{FHECChain, "FHE", "Fully Homomorphic Encryption", 500000, []string{"C", "Z"}, "LP-4xxx"},
{RangeProofCChain, "RANGE_PROOF", "Bulletproof range proofs", 100000, []string{"C", "Z"}, "LP-4xxx"},
// Threshold/MPC (P=5) → LP-5xxx
{FROSTCChain, "FROST", "Schnorr threshold signatures", 25000, []string{"C", "Q"}, "LP-5xxx"},
{CGGMP21CChain, "CGGMP21", "ECDSA threshold signatures", 50000, []string{"C", "Q"}, "LP-5xxx"},
{CoronaCChain, "CORONA", "Threshold Module-LWE signatures (PQ)", 75000, []string{"C", "Q"}, "LP-5xxx"},
{LSSCChain, "LSS", "Lux Secret Sharing", 10000, []string{"C", "Q"}, "LP-5xxx"},
{DKGCChain, "DKG", "Distributed Key Generation", 100000, []string{"C", "Q"}, "LP-5xxx"},
// Bridges (P=6) → LP-6xxx
{WarpSendCChain, "WARP_SEND", "Cross-chain message send", 50000, []string{"C", "B", "A", "Zoo", "Hanzo", "P", "X"}, "LP-6xxx"},
{WarpReceiveCChain, "WARP_RECEIVE", "Cross-chain message receive", 50000, []string{"C", "B", "A", "Zoo", "Hanzo", "P", "X"}, "LP-6xxx"},
{BridgeCChain, "BRIDGE", "Token bridge operations", 75000, []string{"C", "B"}, "LP-6xxx"},
{TeleportCChain, "TELEPORT", "Instant token teleport", 100000, []string{"C", "B"}, "LP-6xxx"},
// AI (P=7) → LP-7xxx
{GPUAttestCChain, "GPU_ATTEST", "GPU compute attestation", 100000, []string{"C", "A", "Hanzo"}, "LP-7xxx"},
{TEEVerifyCChain, "TEE_VERIFY", "TEE attestation verification", 75000, []string{"C", "A"}, "LP-7xxx"},
{NVTrustCChain, "NVTRUST", "NVIDIA trust attestation", 100000, []string{"C", "A"}, "LP-7xxx"},
{InferenceCChain, "INFERENCE", "AI inference verification", 150000, []string{"C", "A", "Hanzo"}, "LP-7xxx"},
{SessionCChain, "SESSION", "AI mining session management", 50000, []string{"C", "A", "Hanzo"}, "LP-7xxx"},
// DEX/Markets → LP-9xxx (addresses end with LP number)
{LXPool, "LX_POOL", "Uniswap v4-style singleton AMM", 50000, []string{"C", "Zoo"}, "LP-9010"},
{LXOracle, "LX_ORACLE", "Price oracle aggregation", 15000, []string{"C", "Zoo"}, "LP-9011"},
{LXRouter, "LX_ROUTER", "Optimized swap routing", 10000, []string{"C", "Zoo"}, "LP-9012"},
{LXHooks, "LX_HOOKS", "Hook contract registry", 10000, []string{"C", "Zoo"}, "LP-9013"},
{LXFlash, "LX_FLASH", "Flash loan facility", 50000, []string{"C", "Zoo"}, "LP-9014"},
{LXBook, "LX_BOOK", "Central limit order book", 25000, []string{"C", "Zoo"}, "LP-9020"},
{LXVault, "LX_VAULT", "Custody, margin, positions", 50000, []string{"C", "Zoo"}, "LP-9030"},
{LXFeed, "LX_FEED", "Computed price feeds (mark/index)", 10000, []string{"C", "Zoo"}, "LP-9040"},
{LXLend, "LX_LEND", "Lending pool (Aave-style)", 25000, []string{"C", "Zoo"}, "LP-9050"},
{LXLiquid, "LX_LIQUID", "Self-repaying loans (Alchemix-style)", 30000, []string{"C", "Zoo"}, "LP-9060"},
{Liquidator, "LIQUIDATOR", "Position liquidation engine", 50000, []string{"C", "Zoo"}, "LP-9070"},
{LiquidFX, "LIQUID_FX", "Transmuter (liquid token conversion)", 25000, []string{"C", "Zoo"}, "LP-9080"},
{LXSwap, "LX_SWAP", "Cross-chain HTLC atomic swap (non-custodial settlement)", 60000, []string{"C", "Zoo"}, "LP-90A0"},
}
// GetPrecompileAddress returns the address for a precompile by name
func GetPrecompileAddress(name string) common.Address {
for _, p := range AllPrecompiles {
if p.Name == name {
return common.HexToAddress(p.Address)
}
}
return common.Address{}
}
// GetChainPrecompiles returns all precompile addresses for a chain
func GetChainPrecompiles(chainLetter string) []common.Address {
addrs, ok := ChainPrecompiles[chainLetter]
// InFamily returns the registered precompiles whose address carries the given
// family's P-nibble, in the same deterministic address order the host
// dispatches them in. ok is false for an unknown family, which is distinct from
// a known family with no precompiles registered in this binary.
//
// Only PCII-scheme addresses can answer: an address is in the family when its
// leading 18 bytes are zero and byte 18's high nibble is the family page.
// Legacy leading-significant addresses (0x0400..0050 math, 0x0400..0060
// stableswap) carry no P-nibble and are in no family.
func InFamily(family string) (found []modules.Module, ok bool) {
page, ok := FamilyPage(family)
if !ok {
return nil
return nil, false
}
result := make([]common.Address, len(addrs))
for i, addr := range addrs {
result[i] = common.HexToAddress(addr)
}
return result
}
// IsPrecompileEnabled checks if a precompile is enabled for a chain
func IsPrecompileEnabled(chainLetter string, precompileAddr common.Address) bool {
addrs := ChainPrecompiles[chainLetter]
for _, addr := range addrs {
if common.HexToAddress(addr) == precompileAddr {
return true
for _, m := range modules.RegisteredModules() {
if p, inScheme := familyPageOf(m.Address); inScheme && p == page {
found = append(found, m)
}
}
return false
return found, true
}
// GetPrecompilesByFamily returns all precompiles for a family page
func GetPrecompilesByFamily(family string) []PrecompileInfo {
page := FamilyPage(family)
if page == 0xFF {
return nil
}
lpRange := "LP-" + string('0'+page) + "xxx"
var result []PrecompileInfo
for _, p := range AllPrecompiles {
if p.LPRange == lpRange {
result = append(result, p)
// familyPageOf returns the P-nibble of a PCII address. inScheme is false for an
// address outside the PCII scheme, which therefore belongs to no family.
func familyPageOf(addr common.Address) (page uint8, inScheme bool) {
for _, b := range addr[:common.AddressLength-2] {
if b != 0 {
return 0, false
}
}
return result
return addr[common.AddressLength-2] >> 4, true
}
+262 -687
View File
File diff suppressed because it is too large Load Diff