mirror of
https://github.com/luxfi/precompile.git
synced 2026-09-24 00:26:21 +00:00
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:
+10
-7
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user