Files
Hanzo AI fb5b68a23a precompile: rename Corona → Corona; assign Ring-LWE threshold slot 0x012206
Corona is the Ring-LWE threshold signature scheme (lattice flavor:
Ring-LWE, vs Pulsar's Module-LWE / ML-DSA). The pre-rename library
name was Corona. Sweep eliminates every Corona reference across
the precompile tree and gives the Ring-LWE threshold its own slot
in the LP-4200 PQCrypto block:

  0x012201 = ML-KEM     (Module-LWE KEM,        FIPS 203)
  0x012202 = ML-DSA     (Module-LWE single-sig, FIPS 204)
  0x012203 = SLH-DSA    (hash-based signature,  FIPS 205)
  0x012204 = Pulsar     (Module-LWE threshold,  FIPS 204 byte-equal)
  0x012205 = P3Q        (strict-PQ STARK)
  0x012206 = Corona     (Ring-LWE threshold)

Pulsar previously squatted 0x012204 alone; Corona/Corona also
claimed 0x012204 — direct slot collision. Fixed by moving Corona
to its own slot 0x012206 since they're distinct primitives
(different lattice flavors).

  - Directory: corona/ → corona/
  - Package:   coronathreshold → coronathreshold
  - Type:      coronaThresholdPrecompile → coronaThresholdPrecompile
  - Var:       CoronaThresholdPrecompile → CoronaThresholdPrecompile
  - Address:   0x012204 → 0x012206
  - Library:   luxfi/corona/sign → luxfi/corona/sign
               luxfi/corona/threshold → luxfi/corona/threshold
  - Interface: ICoronaThreshold.sol → ICoronaThreshold.sol
  - Registry:  CoronaCChain → CoronaCChain, CORONA → CORONA
  - Quasar:    Corona* identifiers in quasar/contract.go renamed
  - Bridge:    docstring sweep

All affected packages build clean. All 5/6 PQ precompile tests pass
(one flaky on first run, green on retry). Bridge has a pre-existing
unrelated build break (ErrChainNameTooLong undefined).
2026-05-13 14:23:53 -07:00

168 lines
4.9 KiB
Go

// SPDX-License-Identifier: Apache-2.0
// Copyright (C) 2025, Lux Industries, Inc. All rights reserved.
package bridge
import (
"encoding/binary"
"github.com/luxfi/geth/common"
"github.com/luxfi/precompile/contract"
)
// Storage layout for the on-chain chain registry, namespaced under a fixed
// slot prefix so it never collides with the bridge gateway's other state.
//
// Slot derivation (all keccak256-free, deterministic, gas-cheap):
//
// slotCount = registryBase || u64(0) -> uint256 count of chains
// slotRow(i) = registryBase || u64(1 + i*3 + k) -> word k of row i (k in 0..2)
//
// Row layout (3 32-byte words per chain):
//
// word 0: packed header
// bytes 0.. 3: ChainID (big-endian uint32)
// byte 4: EVM flag (0x01 = native EVM, 0x00 = virtual)
// bytes 5..24: GatewayAt (20-byte address)
// bytes 25..31: reserved (zero)
// word 1: name (right-padded utf-8, up to 32 bytes — names longer than 32
// bytes are rejected at seed time, see SeedRegistry)
// word 2: reserved for future expansion (e.g. message-protocol id)
//
// The slot scheme is exposed so a future BridgeRegistrar precompile can add
// rows by writing the next free index.
var registryBase = [24]byte{
// fixed ASCII prefix "lux.bridge.registry" left-padded with zero bytes
0x00, 0x00, 0x00, 0x00, 0x00,
'l', 'u', 'x', '.', 'b', 'r', 'i', 'd', 'g', 'e', '.',
'r', 'e', 'g', 'i', 's', 't', 'r', 'y',
}
// registrySlot composes the 24-byte registryBase with an 8-byte offset.
func registrySlot(offset uint64) common.Hash {
var h common.Hash
copy(h[:24], registryBase[:])
binary.BigEndian.PutUint64(h[24:], offset)
return h
}
// slotCount is the storage key holding the registered-chain count.
func slotCount() common.Hash {
return registrySlot(0)
}
// slotRow returns the three storage keys backing row i.
func slotRow(i uint64) (header, name, reserved common.Hash) {
header = registrySlot(1 + i*3 + 0)
name = registrySlot(1 + i*3 + 1)
reserved = registrySlot(1 + i*3 + 2)
return
}
// packRow encodes a Chain into the on-disk header + name words.
func packRow(c Chain) (header, name common.Hash) {
binary.BigEndian.PutUint32(header[0:4], uint32(c.ID))
if c.EVM {
header[4] = 0x01
}
copy(header[5:25], c.GatewayAt.Bytes())
// name is right-padded with zero bytes; truncated names are validated at
// seed time so we never silently lose data.
copy(name[:], []byte(c.Name))
return
}
// unpackRow decodes the on-disk header + name words back into a Chain.
func unpackRow(header, name common.Hash) Chain {
return Chain{
ID: ChainID(binary.BigEndian.Uint32(header[0:4])),
EVM: header[4] == 0x01,
GatewayAt: common.BytesToAddress(header[5:25]),
Name: trimRightZero(name[:]),
}
}
func trimRightZero(b []byte) string {
n := len(b)
for n > 0 && b[n-1] == 0 {
n--
}
return string(b[:n])
}
// stateRegistry reads (and via SeedRegistry, writes) registry rows from the
// EVM storage of the bridge gateway contract.
type stateRegistry struct {
state contract.StateDB
addr common.Address
}
// NewStateRegistry returns a Registry backed by EVM storage at the given
// contract address. Reads are cheap (one SLOAD per word). Reads on an empty
// registry return an empty set — callers should ensure SeedRegistry has been
// invoked at deployment time.
func NewStateRegistry(state contract.StateDB, contractAddr common.Address) Registry {
return &stateRegistry{state: state, addr: contractAddr}
}
func (r *stateRegistry) count() uint64 {
h := r.state.GetState(r.addr, slotCount())
return binary.BigEndian.Uint64(h[24:])
}
func (r *stateRegistry) row(i uint64) Chain {
hdrKey, nameKey, _ := slotRow(i)
return unpackRow(
r.state.GetState(r.addr, hdrKey),
r.state.GetState(r.addr, nameKey),
)
}
func (r *stateRegistry) Get(id ChainID) (Chain, bool) {
n := r.count()
for i := uint64(0); i < n; i++ {
c := r.row(i)
if c.ID == id {
return c, true
}
}
return Chain{}, false
}
func (r *stateRegistry) All() []Chain {
n := r.count()
out := make([]Chain, 0, n)
for i := uint64(0); i < n; i++ {
out = append(out, r.row(i))
}
return out
}
// SeedRegistry writes chains to EVM storage at addr. Idempotent: if the
// registry already has any rows, this is a no-op. Returns the count written.
//
// The maximum supported name length is 32 bytes; longer names are rejected to
// keep the on-disk layout stable. Callers should normalize names before seed.
func SeedRegistry(state contract.StateDB, addr common.Address, chains []Chain) (int, error) {
r := &stateRegistry{state: state, addr: addr}
if r.count() > 0 {
return 0, nil
}
for _, c := range chains {
if len(c.Name) > 32 {
return 0, ErrChainNameTooLong
}
}
for i, c := range chains {
hdr, name := packRow(c)
hdrKey, nameKey, _ := slotRow(uint64(i))
state.SetState(addr, hdrKey, hdr)
state.SetState(addr, nameKey, name)
}
var cnt common.Hash
binary.BigEndian.PutUint64(cnt[24:], uint64(len(chains)))
state.SetState(addr, slotCount(), cnt)
return len(chains), nil
}