Files
precompile/aivmbridge/module_config_test.go

999 lines
39 KiB
Go

// Copyright (C) 2026, Lux Industries Inc. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
//
// module_config_test.go — the module/config surface, the calldata refusal paths, and
// the PRICE-vs-CHARGE relationship between RequiredGas (what the precompile advertises
// a call will cost) and Run (what it actually consumes).
//
// The gas tests here assert a RELATIONSHIP, never a number: gas constants are the
// operator's dial, and a test that pins them fails on a re-price instead of on a bug.
// The relationship that matters is
//
// RequiredGas(input) >= gas Run consumes for that same input (always)
// RequiredGas(input) == gas Run consumes, for a WELL-FORMED frame (tightness)
//
// Under-reporting is the unsafe direction — a host that deducts the advertised price
// up front would hand out the difference as free work. Over-reporting only wastes a
// caller's budget on a frame that was going to revert anyway.
package aivmbridge
import (
"context"
"encoding/binary"
"errors"
"reflect"
"strconv"
"testing"
"github.com/luxfi/crypto/poi"
"github.com/luxfi/geth/common"
"github.com/luxfi/precompile/modules"
"github.com/luxfi/precompile/precompileconfig"
)
// --- fakes -------------------------------------------------------------------
// countingIntentStore is an IntentStore that COUNTS writes, so a refusal can be shown
// to have written nothing at all — not merely to have returned an error.
type countingIntentStore struct {
status map[[32]byte]OutboxStatus
writes int
}
func newCountingIntentStore() *countingIntentStore {
return &countingIntentStore{status: make(map[[32]byte]OutboxStatus)}
}
func (s *countingIntentStore) IntentStatus(id [32]byte) OutboxStatus { return s.status[id] }
func (s *countingIntentStore) PutPendingIntent(id [32]byte, _ OutboxIntent) {
s.writes++
s.status[id] = OutboxPending
}
// scriptedVerifierState is a ReceiptVerifierState whose outbox record the test sets
// directly, so the verify core can be driven to states the real store never produces
// (an out-of-range status) and its single effect can be counted.
type scriptedVerifierState struct {
roots map[[32]byte]bool
intent OutboxIntent
consumed int
}
func (v *scriptedVerifierState) ReceiptRootCommitted(r [32]byte) bool { return v.roots[r] }
func (v *scriptedVerifierState) LoadIntent([32]byte) OutboxIntent { return v.intent }
func (v *scriptedVerifierState) MarkIntentConsumed([32]byte, uint64) {
v.consumed++
v.intent.Status = OutboxConsumed
}
// foreignConfig is another precompile's config type — a valid precompileconfig.Config
// that is not *aivmbridge.Config.
type foreignConfig struct{}
func (foreignConfig) Key() string { return "someOtherPrecompileConfig" }
func (foreignConfig) Timestamp() *uint64 { return nil }
func (foreignConfig) IsDisabled() bool { return false }
func (foreignConfig) Verify(precompileconfig.ChainConfig) error { return nil }
func (foreignConfig) Equal(precompileconfig.Config) bool { return false }
var (
_ IntentStore = (*countingIntentStore)(nil)
_ ReceiptVerifierState = (*scriptedVerifierState)(nil)
_ precompileconfig.Config = foreignConfig{}
)
// --- shared Pattern-B fixture (no Run layer) ----------------------------------
// committedReceipt returns a receipt, a GENUINE inclusion proof for it, and a factory
// for a verifier state that has the proof's root committed and the bound intent pending.
// It is the smallest fully-consistent Pattern-B input, independent of the Run fixture.
func committedReceipt() (AInferenceReceipt, AInferenceProof, func() *scriptedVerifierState) {
r := AInferenceReceipt{
Version: ReceiptVersion,
IntentID: h32(0x1D),
TaskID: h32(0x7A),
CChainID: h32(0xCC),
AChainID: h32(0xA0),
Requester: common.Address{0x42},
ModelSpecHash: h32(0x5E),
PromptHash: h32(0x9D),
CanonicalOutputHash: h32(0x0F),
Status: StatusCompleted,
N: 3,
Threshold: 2,
}
tree := buildMerkleTree([][32]byte{h32(0x01), ReceiptHash(r), h32(0x03)})
p := tree.proof(1)
newState := func() *scriptedVerifierState {
return &scriptedVerifierState{
roots: map[[32]byte]bool{p.ReceiptRoot: true},
intent: OutboxIntent{
Status: OutboxPending,
Caller: r.Requester,
CChainID: r.CChainID,
AChainID: r.AChainID,
ModelSpecHash: r.ModelSpecHash,
PromptHash: r.PromptHash,
N: r.N,
Threshold: r.Threshold,
BlockNumber: 77,
},
}
}
return r, p, newState
}
// --- calldata builders used only here -----------------------------------------
// proofBytesOfDepth encodes a WELL-FORMED proof frame of the given path depth: the
// header's declared pathLen and the bytes that follow agree.
func proofBytesOfDepth(root [32]byte, depth int) []byte {
p := AInferenceProof{ReceiptRoot: root, Path: make([][32]byte, depth)}
for i := range p.Path {
p.Path[i] = h32(byte(i + 1))
}
return encodeProof(p)
}
// proofBytesClaiming builds a path-FREE (42-byte) proof whose header DECLARES `declared`
// path nodes that are not present. This is the frame whose declared depth and actual
// length disagree.
func proofBytesClaiming(root [32]byte, declared uint16) []byte {
b := proofBytesOfDepth(root, 0)
binary.BigEndian.PutUint16(b[40:42], declared)
return b
}
// verifyFrameDeclaring builds a verify frame whose DECLARED lengths differ from the
// bytes actually carried, so each framing position can be corrupted on its own.
func verifyFrameDeclaring(receipt, proof []byte, declRcpt, declProof uint16) []byte {
out := encodeVerify(receipt, proof)
binary.BigEndian.PutUint16(out[4:6], declRcpt)
binary.BigEndian.PutUint16(out[6:8], declProof)
return out
}
func receiptBytesWithVersion(r AInferenceReceipt, v uint16) []byte {
b := EncodeReceipt(r)
binary.BigEndian.PutUint16(b[0:2], v)
return b
}
func computeFrame(opening []byte) []byte {
in := make([]byte, 4+len(opening))
putU32(in[0:4], SelectorVerifyComputeProof)
copy(in[4:], opening)
return in
}
// --- module registration + configurator ---------------------------------------
// The bridge must occupy its OWN slot in the AI range and must store its state under
// that same identity. A copy-pasted address would silently squat the AI-mining or the
// deterministic-inference precompile.
func TestModule_RegisteredAtItsOwnDisjointAddress(t *testing.T) {
got, ok := modules.GetPrecompileModuleByAddress(ContractAddress)
if !ok {
t.Fatalf("init() left the bridge unregistered at %s", ContractAddress)
}
if got.ConfigKey != ConfigKey {
t.Fatalf("registered under config key %q, want %q", got.ConfigKey, ConfigKey)
}
if got.Contract != BridgePrecompile {
t.Fatal("the registered contract is not the bridge singleton")
}
if got.Configurator == nil {
t.Fatal("the registered module carries no configurator")
}
if bridgeStateAddr != ContractAddress {
t.Fatal("bridge state must live under the precompile's own address")
}
for _, taken := range []common.Address{
common.HexToAddress("0x0300000000000000000000000000000000000000"), // AI mining
common.HexToAddress("0x0300000000000000000000000000000000000003"), // deterministic inference
} {
if ContractAddress == taken {
t.Fatalf("the bridge squats %s, an address another AI precompile owns", taken)
}
}
}
// MakeConfig must hand back a FRESH config each call — the host builds one per network
// upgrade entry and then fills it in; a shared instance would let one chain's upgrade
// timestamp leak into another's. Configure initializes no genesis state.
func TestConfigurator_MakeConfigIsFreshAndConfigureWritesNoState(t *testing.T) {
cfgr := Module.Configurator
a, ok := cfgr.MakeConfig().(*Config)
if !ok || a == nil {
t.Fatal("MakeConfig must return a non-nil *Config")
}
b, ok := cfgr.MakeConfig().(*Config)
if !ok || b == nil {
t.Fatal("MakeConfig must return a non-nil *Config")
}
if a == b {
t.Fatal("MakeConfig handed back the same instance twice; two upgrade entries would alias")
}
if a.Key() != ConfigKey || a.IsDisabled() || a.Timestamp() != nil {
t.Fatalf("a fresh config must be the zero upgrade under %q, got disabled=%v ts=%v",
ConfigKey, a.IsDisabled(), a.Timestamp())
}
ts := uint64(99)
a.Upgrade.BlockTimestamp = &ts
a.Upgrade.Disable = true
if b.Timestamp() != nil || b.IsDisabled() {
t.Fatal("configs from MakeConfig share backing state")
}
db := newMemStateDB()
if err := cfgr.Configure(nil, a, db, nil); err != nil {
t.Fatalf("Configure: %v", err)
}
if n := len(db.slots); n != 0 {
t.Fatalf("Configure wrote %d storage accounts; the bridge has no genesis state", n)
}
}
// The config's read surface is a pass-through onto the Upgrade it holds — a host that
// edits the upgrade sees the change, and Verify accepts every well-formed shape (the
// bridge takes no per-network parameters, so there is nothing to reject).
func TestConfig_SurfaceTracksTheUpgradeItHolds(t *testing.T) {
var c Config
if c.Key() != ConfigKey || c.Key() != Module.ConfigKey {
t.Fatalf("Key() = %q, want %q (and it must match the registered module)", c.Key(), ConfigKey)
}
if c.Timestamp() != nil {
t.Fatal("an unset upgrade must report no activation timestamp (never enabled)")
}
if c.IsDisabled() {
t.Fatal("an unset upgrade is not a disable")
}
if err := c.Verify(nil); err != nil {
t.Fatalf("Verify rejected the zero config: %v", err)
}
for _, want := range []uint64{0, 1, 1 << 40} {
v := want
c.Upgrade.BlockTimestamp = &v
got := c.Timestamp()
if got == nil || *got != want {
t.Fatalf("Timestamp() = %v, want %d", got, want)
}
}
c.Upgrade.Disable = true
if !c.IsDisabled() {
t.Fatal("IsDisabled() must follow Upgrade.Disable")
}
if err := c.Verify(nil); err != nil {
t.Fatalf("Verify must accept a disabling config too: %v", err)
}
}
// Equal must react to EVERY field it is supposed to compare, in both directions. The
// reflection guards make a newly-added field a loud failure here instead of a silently
// ignored one at the comparison site.
func TestConfig_EqualComparesEveryField(t *testing.T) {
if n := reflect.TypeOf(Config{}).NumField(); n != 1 {
t.Fatalf("Config has %d fields but Equal only forwards Upgrade — extend Equal and this test", n)
}
if n := reflect.TypeOf(precompileconfig.Upgrade{}).NumField(); n != 2 {
t.Fatalf("Upgrade has %d fields but this test mutates 2 — extend the mutator list", n)
}
ptr := func(v uint64) *uint64 { return &v }
mutators := []struct {
field string
mutate func(*Config)
}{
{"Upgrade.BlockTimestamp", func(c *Config) { c.Upgrade.BlockTimestamp = ptr(7) }},
{"Upgrade.Disable", func(c *Config) { c.Upgrade.Disable = true }},
}
for _, m := range mutators {
t.Run(m.field, func(t *testing.T) {
a, b := &Config{}, &Config{}
if !a.Equal(b) || !b.Equal(a) {
t.Fatal("two zero configs must compare equal")
}
m.mutate(b)
if a.Equal(b) {
t.Fatalf("Equal ignored %s", m.field)
}
if b.Equal(a) {
t.Fatalf("Equal ignored %s in the reverse direction", m.field)
}
// Applying the SAME mutation to both restores equality — so Equal reacted to
// the difference, not merely to the mutated value.
m.mutate(a)
if !a.Equal(b) || !b.Equal(a) {
t.Fatalf("Equal is not symmetric after both sides took %s", m.field)
}
})
}
t.Run("timestamp compares by value not pointer", func(t *testing.T) {
a := &Config{Upgrade: precompileconfig.Upgrade{BlockTimestamp: ptr(9)}}
b := &Config{Upgrade: precompileconfig.Upgrade{BlockTimestamp: ptr(9)}}
if !a.Equal(b) || !b.Equal(a) {
t.Fatal("distinct pointers holding the same timestamp must compare equal")
}
c := &Config{Upgrade: precompileconfig.Upgrade{BlockTimestamp: ptr(10)}}
if a.Equal(c) || c.Equal(a) {
t.Fatal("different timestamps must not compare equal")
}
// nil and set are distinguishable in both directions.
z := &Config{}
if a.Equal(z) || z.Equal(a) {
t.Fatal("an unset timestamp must not equal a set one")
}
})
t.Run("foreign type and nil", func(t *testing.T) {
a := &Config{}
if a.Equal(foreignConfig{}) {
t.Fatal("Equal accepted another precompile's config type")
}
if a.Equal(&foreignConfig{}) {
t.Fatal("Equal accepted a pointer to another precompile's config type")
}
if a.Equal(nil) {
t.Fatal("Equal accepted a nil config")
}
})
}
// --- RequiredGas vs what Run charges ------------------------------------------
// The price the precompile advertises must never be BELOW what Run consumes for the
// same bytes (that direction hands out free work), and for a frame Run actually
// decodes it must be EXACT (drift either way is an accounting bug).
func TestRequiredGas_NeverBelowWhatRunCharges(t *testing.T) {
seed := newVerifyFixture(t)
root := seed.proof.ReceiptRoot
receipt := EncodeReceipt(seed.receipt)
type frame struct {
name string
in []byte
exact bool // Run decodes this frame, so the advertised price must match exactly
}
frames := []frame{
{"submit", encodeSubmit(h32(0x11), h32(0x22), 3, 2, feeWord(5), [32]byte{}), true},
{"compute proof, honest opening", computeFrame(buildOpening(false)), true},
{"compute proof, undecodable", computeFrame([]byte{1, 2, 3, 4, 5}), true},
// A frame Run refuses before it can meter anything: it returns the whole budget,
// so charged is 0 and only the >= half of the invariant is meaningful.
{"unknown selector", []byte{0xFF, 0xFF, 0xFF, 0xFF}, false},
{"below the selector", []byte{0x11, 0x00}, false},
// A verify frame whose declared proof length is under the 42-byte proof header.
{"proof shorter than its header", encodeVerify(receipt, make([]byte, 10)), true},
// A verify frame whose receipt does not decode: the proof is well formed, so the
// advertised price includes the merkle walk that Run never performs.
{"undecodable receipt", encodeVerify(receiptBytesWithVersion(seed.receipt, ReceiptVersion+1), proofBytesOfDepth(root, 2)), false},
// The frame LIES about its depth: 42 proof bytes, header claiming MaxProofDepth.
{"proof header lies about depth", encodeVerify(receipt, proofBytesClaiming(root, MaxProofDepth)), false},
{"proof header lies by one node", encodeVerify(receipt, proofBytesClaiming(root, 1)), false},
{"declared depth over the cap", encodeVerify(receipt, proofBytesClaiming(root, MaxProofDepth+1)), true},
}
// Every WELL-FORMED depth, including both sides of the cap boundary.
for _, d := range []int{0, 1, 2, 3, 17, MaxProofDepth - 1, MaxProofDepth} {
frames = append(frames, frame{
name: "well-formed proof depth " + strconv.Itoa(d),
in: encodeVerify(receipt, proofBytesOfDepth(root, d)),
exact: true,
})
}
for _, f := range frames {
t.Run(f.name, func(t *testing.T) {
fx := newVerifyFixture(t)
charged, advertised, _ := chargedGas(t, fx.st, fx.caller, f.in)
if advertised < charged {
t.Fatalf("UNDER-PRICED: RequiredGas advertised %d, Run consumed %d — %d gas of work for free",
advertised, charged, charged-advertised)
}
if f.exact && advertised != charged {
t.Fatalf("price drift on a frame Run decodes: advertised %d, charged %d", advertised, charged)
}
if advertised != charged {
t.Logf("over-priced by %d (advertised %d, charged %d) — safe direction", advertised-charged, advertised, charged)
}
})
}
}
// REGRESSION. module.go prices verifyInferenceReceipt from the pathLen the CALLER
// DECLARES in the proof header (module.go:124) without checking it against the proof's
// actual byte length; bridge.go prices from the DERIVED len(p.Path) after DecodeProof
// has enforced len(proof) == 42 + pathLen*32 (proof.go:118-126). A 42-byte proof whose
// header claims MaxProofDepth therefore advertises the deepest possible walk while Run
// bails at decode and charges only the base.
//
// The direction is what matters. Over-pricing (advertised > charged) only wastes the
// caller's own budget on a frame that reverts anyway. UNDER-pricing would be the bug:
// a host that deducts the advertised price up front would execute the difference for
// free. This test pins the direction, not the size of the gap, so it keeps holding if
// the cross-check is added and the gap closes to zero.
func TestRequiredGas_DeclaredDepthIsNotCheckedAgainstProofLength(t *testing.T) {
fx := newVerifyFixture(t)
receipt := EncodeReceipt(fx.receipt)
root := fx.proof.ReceiptRoot
lying := encodeVerify(receipt, proofBytesClaiming(root, MaxProofDepth))
charged, advertised, err := chargedGas(t, fx.st, fx.caller, lying)
if !errors.Is(err, ErrProofDecode) {
t.Fatalf("a proof whose header contradicts its length must be refused, got %v", err)
}
if advertised < charged {
t.Fatalf("UNDER-PRICED: advertised %d < charged %d", advertised, charged)
}
t.Logf("declared-depth frame: advertised %d, charged %d, slack %d", advertised, charged, advertised-charged)
// The honest frame at the same declared depth prices identically to what Run
// charges — so the slack above comes from the missing cross-check, not from the
// depth pricing itself.
honestFx := newVerifyFixture(t)
honest := encodeVerify(receipt, proofBytesOfDepth(root, MaxProofDepth))
honestCharged, honestAdvertised, err := chargedGas(t, honestFx.st, honestFx.caller, honest)
if !errors.Is(err, ErrMerkleVerify) {
t.Fatalf("the honest deep frame must reach the merkle check, got %v", err)
}
if honestAdvertised != honestCharged {
t.Fatalf("a well-formed deep frame must be priced exactly: advertised %d, charged %d",
honestAdvertised, honestCharged)
}
if honestAdvertised != advertised {
t.Fatalf("the lying frame and the honest frame of the same declared depth priced differently (%d vs %d)",
advertised, honestAdvertised)
}
t.Logf("honest frame at the same declared depth: advertised == charged == %d", honestAdvertised)
}
// --- selector routing ----------------------------------------------------------
// The compute-proof selector routes to the native proof-of-inference check, and that
// check is PURE: it writes no C state, so it is legal inside a static call.
func TestRun_RoutesComputeProofAndStaysPureUnderStaticCall(t *testing.T) {
st := newMockState(common.HexToHash("0xC0DE"))
caller := common.Address{0x1}
for _, tc := range []struct {
name string
in []byte
verdict byte
}{
{"honest opening", computeFrame(buildOpening(false)), resultIncluded | resultFreivaldsOK},
{"fabricated output", computeFrame(buildOpening(true)), resultIncluded},
} {
advertised := BridgePrecompile.RequiredGas(tc.in)
if advertised <= GasVerifyComputeProofBase {
t.Fatalf("%s: a real opening must price above the base, got %d", tc.name, advertised)
}
for _, readOnly := range []bool{false, true} {
ret, rem, err := BridgePrecompile.Run(st, caller, ContractAddress, tc.in, advertised, readOnly)
if err != nil {
t.Fatalf("%s readOnly=%v: %v", tc.name, readOnly, err)
}
if len(ret) != 32 {
t.Fatalf("%s: verdict must be one word, got %d bytes", tc.name, len(ret))
}
if ret[31] != tc.verdict {
t.Fatalf("%s: verdict = %d, want %d", tc.name, ret[31], tc.verdict)
}
if rem != 0 {
t.Fatalf("%s: the advertised price must be exactly consumed, %d left", tc.name, rem)
}
}
}
if n := len(st.db.slots); n != 0 {
t.Fatalf("compute-proof verification wrote %d storage accounts; it must touch no state", n)
}
// An undecodable opening prices at the base and reverts there.
garbage := computeFrame([]byte{1, 2, 3})
if g := BridgePrecompile.RequiredGas(garbage); g != GasVerifyComputeProofBase {
t.Fatalf("an undecodable opening must price at the base, got %d", g)
}
if _, _, err := BridgePrecompile.Run(st, caller, ContractAddress, garbage, GasVerifyComputeProofBase, false); err == nil {
t.Fatal("an undecodable opening must revert")
}
}
// --- the fixed-width word decoder ----------------------------------------------
// readUint16Word takes the value from the low two bytes and refuses anything smuggled
// above the declared width. Every high byte is checked one at a time, so a loop that
// stops early is caught.
func TestReadUint16Word_RefusesShortAndDirtyWords(t *testing.T) {
for _, n := range []int{0, 1, 30, 31} {
if _, err := readUint16Word(make([]byte, n)); !errors.Is(err, ErrInputTooShort) {
t.Fatalf("a %d-byte word must be refused, not zero-extended: %v", n, err)
}
}
for _, want := range []uint16{0, 1, 255, 256, 0xFF00, 0xFFFF} {
w := make([]byte, 32)
binary.BigEndian.PutUint16(w[30:32], want)
got, err := readUint16Word(w)
if err != nil || got != want {
t.Fatalf("value %d: got %d, err %v", want, got, err)
}
}
for i := 0; i < 30; i++ {
w := make([]byte, 32)
binary.BigEndian.PutUint16(w[30:32], 0x2A)
w[i] = 0x01
if _, err := readUint16Word(w); !errors.Is(err, ErrDirtyWord) {
t.Fatalf("a non-zero byte at high position %d was not refused: %v", i, err)
}
}
// The decoder is bounded to the first word: bytes past 32 are neither read as value
// nor treated as dirt.
long := make([]byte, 64)
binary.BigEndian.PutUint16(long[30:32], 7)
for i := 32; i < 64; i++ {
long[i] = 0xFF
}
got, err := readUint16Word(long)
if err != nil || got != 7 {
t.Fatalf("a longer slice must read only its first word: got %d, err %v", got, err)
}
}
// The threshold word is hardened exactly like the N word: a caller cannot smuggle bits
// above the declared uint16, and the refusal happens before any state is written.
func TestSubmit_RefusesDirtyThresholdWord(t *testing.T) {
installNative(t, h32(0xA0), nil)
good := encodeSubmit(h32(0x11), h32(0x22), 3, 2, feeWord(5), [32]byte{})
caller := common.Address{0x1}
// The threshold word is args[96:128], i.e. input[100:132]; its high bytes are
// input[100:130].
for _, off := range []int{100, 115, 129} {
in := append([]byte(nil), good...)
in[off] = 0x01
st := newMockState(common.HexToHash("0x02"))
_, _, err := BridgePrecompile.Run(st, caller, ContractAddress, in, BridgePrecompile.RequiredGas(in), false)
if !errors.Is(err, ErrDirtyWord) {
t.Fatalf("dirty byte at input[%d]: err = %v, want %v", off, err, ErrDirtyWord)
}
if n := len(st.db.slots); n != 0 {
t.Fatalf("dirty byte at input[%d]: a refused submit wrote %d storage accounts", off, n)
}
}
// Control: the clean frame still submits, so the refusals above are not vacuous.
st := newMockState(common.HexToHash("0x03"))
if _, _, err := BridgePrecompile.Run(st, caller, ContractAddress, good, BridgePrecompile.RequiredGas(good), false); err != nil {
t.Fatalf("the clean frame must submit: %v", err)
}
}
// --- verify framing refusals ---------------------------------------------------
// Every framing position is exact: a declared length that disagrees with the bytes
// carried is refused, and so is a payload that does not decode. No refusal may consume
// the pending intent.
func TestVerify_RefusesEveryFramingMismatch(t *testing.T) {
// Control first, on its own fixture, so "refused" below is a real distinction.
ctrl := newVerifyFixture(t)
if _, err := ctrl.run(t, ctrl.receipt, ctrl.proof); err != nil {
t.Fatalf("the well-formed frame must verify: %v", err)
}
f := newVerifyFixture(t)
receipt := EncodeReceipt(f.receipt)
proof := encodeProof(f.proof)
rl, pl := uint16(len(receipt)), uint16(len(proof))
root := f.proof.ReceiptRoot
cases := []struct {
name string
in []byte
want error
}{
{"args below the 4-byte header", encodeVerify(receipt, proof)[:4+3], ErrInputTooShort},
{"receiptLen one short", verifyFrameDeclaring(receipt, proof, rl-1, pl), ErrInputOversized},
{"receiptLen one long", verifyFrameDeclaring(receipt, proof, rl+1, pl), ErrInputOversized},
{"receiptLen zero", verifyFrameDeclaring(receipt, proof, 0, pl), ErrInputOversized},
{"proofLen one short", verifyFrameDeclaring(receipt, proof, rl, pl-1), ErrInputOversized},
{"proofLen one long", verifyFrameDeclaring(receipt, proof, rl, pl+1), ErrInputOversized},
{"trailing junk", append(encodeVerify(receipt, proof), 0xAA), ErrInputOversized},
{"receipt truncated", encodeVerify(receipt[:len(receipt)-1], proof), ErrReceiptDecode},
{"receipt one byte long", encodeVerify(append(append([]byte(nil), receipt...), 0x00), proof), ErrReceiptDecode},
{"receipt version unknown", encodeVerify(receiptBytesWithVersion(f.receipt, ReceiptVersion+1), proof), ErrReceiptDecode},
{"proof below its header", encodeVerify(receipt, proof[:41]), ErrProofDecode},
{"proof header lies about depth", encodeVerify(receipt, proofBytesClaiming(root, MaxProofDepth)), ErrProofDecode},
{"proof depth over the cap", encodeVerify(receipt, proofBytesClaiming(root, MaxProofDepth+1)), ErrProofDecode},
{"proof carries an extra node", encodeVerify(receipt, append(append([]byte(nil), proof...), make([]byte, 32)...)), ErrProofDecode},
}
store := newStateKVStore(f.st.db, f.st.blockNumber)
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
gas := BridgePrecompile.RequiredGas(tc.in)
_, rem, err := BridgePrecompile.Run(f.st, f.caller, ContractAddress, tc.in, gas, false)
if !errors.Is(err, tc.want) {
t.Fatalf("err = %v, want %v", err, tc.want)
}
if rem > gas {
t.Fatalf("a refusal returned more gas than it was given (%d > %d)", rem, gas)
}
if got := store.IntentStatus(f.intentID); got != OutboxPending {
t.Fatalf("a refused verify moved the intent to status %d; it must stay pending", got)
}
})
}
// After every refusal the intent still settles on a well-formed frame.
if _, err := f.run(t, f.receipt, f.proof); err != nil {
t.Fatalf("the intent must still be settleable after the refusals: %v", err)
}
}
// --- merkle ---------------------------------------------------------------------
// VerifyMerkle answers false — it never errors and never panics — for a path that does
// not reconstruct the root, and it refuses a path deeper than MaxProofDepth even when
// that path WOULD reconstruct the declared root.
func TestVerifyMerkle_RefusesOverDepthAndNonReconstructingPath(t *testing.T) {
leaves := [][32]byte{h32(0x01), h32(0x02), h32(0x03), h32(0x04), h32(0x05)}
tree := buildMerkleTree(leaves)
leaf := leafHash(h32(0x03))
p := tree.proof(2)
if !VerifyMerkle(leaf, p) {
t.Fatal("a genuine proof must verify")
}
for i := range p.Path {
bad := AInferenceProof{ReceiptRoot: p.ReceiptRoot, Index: p.Index, Path: append([][32]byte(nil), p.Path...)}
bad.Path[i][0] ^= 0xFF
if VerifyMerkle(leaf, bad) {
t.Fatalf("a corrupted sibling at level %d still reconstructed the root", i)
}
}
if VerifyMerkle(leafHash(h32(0x99)), p) {
t.Fatal("a leaf that is not in the tree must not verify under a genuine path")
}
if VerifyMerkle(leaf, AInferenceProof{ReceiptRoot: h32(0xEE), Index: p.Index, Path: p.Path}) {
t.Fatal("a genuine path must not verify against a different root")
}
// fold builds a path of n siblings and the root it reconstructs to, with every index
// bit zero (leaf stays the LEFT child at every level).
fold := func(n int) AInferenceProof {
node := leaf
path := make([][32]byte, n)
for i := range path {
path[i] = h32(byte(i + 1))
node = hashPair(node, path[i])
}
return AInferenceProof{ReceiptRoot: node, Path: path, Index: 0}
}
if !VerifyMerkle(leaf, fold(MaxProofDepth)) {
t.Fatal("a path of exactly MaxProofDepth must be accepted — the cap is inclusive")
}
if VerifyMerkle(leaf, fold(MaxProofDepth+1)) {
t.Fatal("a path past MaxProofDepth must be refused even though it folds to the declared root")
}
}
// --- the A-Chain on-ramp handle -------------------------------------------------
// With no on-ramp wired, Pattern A refuses cleanly and writes nothing, while Pattern B
// keeps working — verifying a committed receipt against committed C state never needs A.
func TestAChainUnavailable_RefusesSubmitButStillVerifies(t *testing.T) {
resetAChainClientForTest()
t.Cleanup(resetAChainClientForTest)
c := currentAChainClient()
if c.Brand() == "" {
t.Fatal("even the closed on-ramp must name itself; InstallAChainClient refuses an empty brand")
}
if !isZero32(c.AChainID()) {
t.Fatal("the closed on-ramp declares no rail peer")
}
store := newCountingIntentStore()
id, err := c.SubmitInferenceIntent(context.Background(), store, InferenceIntent{ModelSpecHash: h32(0x11)})
if !errors.Is(err, ErrAChainUnavailable) {
t.Fatalf("submit with no on-ramp: err = %v, want %v", err, ErrAChainUnavailable)
}
if !isZero32(id) {
t.Fatal("a refused submit must not mint an id")
}
if store.writes != 0 {
t.Fatalf("a refused submit wrote the outbox %d times", store.writes)
}
r, p, newState := committedReceipt()
direct, indirect := newState(), newState()
wantV, wantErr := verifyInferenceReceipt(direct, r, p)
if wantErr != nil {
t.Fatalf("the fixture receipt must verify: %v", wantErr)
}
gotV, gotErr := c.VerifyInferenceReceipt(context.Background(), indirect, r, p)
if gotErr != nil || gotV != wantV {
t.Fatalf("the closed on-ramp's verify diverged from the state core: (%v, %v) vs (%v, %v)",
gotV, gotErr, wantV, wantErr)
}
if direct.consumed != 1 || indirect.consumed != 1 {
t.Fatalf("a verified receipt consumes its intent exactly once (direct %d, via client %d)",
direct.consumed, indirect.consumed)
}
}
// The singleton is never dereferenced blind: an unset pointer resolves to the closed
// on-ramp, so a Run() in flight reverts instead of panicking.
func TestCurrentAChainClient_UnsetPointerYieldsTheClosedOnRamp(t *testing.T) {
resetAChainClientForTest()
t.Cleanup(resetAChainClientForTest)
aChainClient.Store(nil)
c := currentAChainClient()
if c == nil {
t.Fatal("currentAChainClient returned nil")
}
if c.Brand() == "" || !isZero32(c.AChainID()) {
t.Fatal("an unset singleton must resolve to the closed on-ramp")
}
if _, err := c.SubmitInferenceIntent(context.Background(), newCountingIntentStore(), InferenceIntent{}); !errors.Is(err, ErrAChainUnavailable) {
t.Fatalf("an unset singleton must refuse Pattern A: %v", err)
}
st := newMockState(common.HexToHash("0xFEED"))
in := encodeSubmit(h32(0x11), h32(0x22), 1, 1, feeWord(1), [32]byte{})
if _, _, err := BridgePrecompile.Run(st, common.Address{0x1}, ContractAddress, in, BridgePrecompile.RequiredGas(in), false); !errors.Is(err, ErrAChainUnavailable) {
t.Fatalf("Run against an unset singleton: %v", err)
}
if n := len(st.db.slots); n != 0 {
t.Fatalf("a refused submit wrote %d storage accounts", n)
}
}
// --- the staged native client ---------------------------------------------------
// A host that passes no white-label name still gets an INSTALLABLE client, and an
// explicit brand survives verbatim.
func TestNativeClient_BrandDefaultsToSomethingInstallable(t *testing.T) {
resetAChainClientForTest()
t.Cleanup(resetAChainClientForTest)
def := NewNativeAChainClient("", h32(0xA0), nil)
if def.Brand() == "" {
t.Fatal("an empty brand must fall back to a non-empty default")
}
if def.Brand() == (achainUnavailable{}).Brand() {
t.Fatal("a wired client must be distinguishable from the closed on-ramp in a log line")
}
if err := InstallAChainClient(def); err != nil {
t.Fatalf("a default-branded client must be installable: %v", err)
}
named := NewNativeAChainClient("Hanzo Inference", h32(0xA1), nil)
if named.Brand() != "Hanzo Inference" {
t.Fatalf("an explicit brand must survive verbatim, got %q", named.Brand())
}
if named.AChainID() != h32(0xA1) {
t.Fatal("the client must report the rail peer it was constructed with")
}
}
// The client binds its OWN rail peer into the derived id (a caller cannot mint an id
// for another rail), and the outbox is append-once.
func TestNativeClient_BindsItsRailAndWritesOnce(t *testing.T) {
c := NewNativeAChainClient("Lux Inference", h32(0xA0), nil)
store := newCountingIntentStore()
in := InferenceIntent{
CChainID: h32(0xCC),
AChainID: h32(0xBB), // a rail the caller asked for; the client overrides it
CTxHash: h32(0x7A),
CallIndex: 1,
Caller: common.Address{0x9},
ModelSpecHash: h32(0x5E),
ModelPromptHash: h32(0x9D),
N: 3,
Threshold: 2,
Fee: feeWord(5),
}
bound := in
bound.AChainID = h32(0xA0)
id, err := c.SubmitInferenceIntent(context.Background(), store, in)
if err != nil {
t.Fatalf("submit: %v", err)
}
if id != DeriveIntentID(bound) {
t.Fatal("the client must bind its own rail peer into the id, not the caller's")
}
if store.writes != 1 || store.IntentStatus(id) != OutboxPending {
t.Fatalf("one committed pending record expected, got %d writes / status %d", store.writes, store.IntentStatus(id))
}
id2, err := c.SubmitInferenceIntent(context.Background(), store, in)
if !errors.Is(err, ErrIntentReplay) {
t.Fatalf("a second submit of the same intent: err = %v, want %v", err, ErrIntentReplay)
}
if !isZero32(id2) {
t.Fatal("a refused replay must not return an id")
}
if store.writes != 1 {
t.Fatalf("the replay wrote the outbox again (%d writes)", store.writes)
}
// A client on a different rail derives a different id from the same intent, so the
// first rail's record does not block it.
other := NewNativeAChainClient("Lux Inference", h32(0xA2), nil)
id3, err := other.SubmitInferenceIntent(context.Background(), store, in)
if err != nil {
t.Fatalf("submit on a second rail: %v", err)
}
if id3 == id {
t.Fatal("two rails must not share an intent id")
}
if store.writes != 2 {
t.Fatalf("the second rail must write its own record, got %d writes", store.writes)
}
}
// Pattern B on the native client is the package's state core, nothing more — same
// result, same single effect, same double-consume refusal.
func TestNativeClient_VerifyIsTheStateCore(t *testing.T) {
r, p, newState := committedReceipt()
c := NewNativeAChainClient("Lux Inference", h32(0xA0), nil)
direct, viaClient := newState(), newState()
wantV, wantErr := verifyInferenceReceipt(direct, r, p)
if wantErr != nil {
t.Fatalf("the fixture receipt must verify: %v", wantErr)
}
gotV, gotErr := c.VerifyInferenceReceipt(context.Background(), viaClient, r, p)
if gotErr != nil || gotV != wantV {
t.Fatalf("client verify diverged from the core: (%v, %v) vs (%v, %v)", gotV, gotErr, wantV, wantErr)
}
if viaClient.consumed != 1 {
t.Fatalf("verify must consume the intent exactly once, got %d", viaClient.consumed)
}
if _, err := c.VerifyInferenceReceipt(context.Background(), viaClient, r, p); !errors.Is(err, ErrIntentConsumed) {
t.Fatalf("a second verify: err = %v, want %v", err, ErrIntentConsumed)
}
if viaClient.consumed != 1 {
t.Fatalf("a refused second verify consumed again (%d)", viaClient.consumed)
}
}
// --- the verify core's outbox status switch --------------------------------------
// Only a PENDING outbox record settles. Anything else — absent, already consumed, or a
// value the store should never hold — refuses and leaves the record alone.
func TestVerify_OnlyPendingSettles(t *testing.T) {
r, p, newState := committedReceipt()
for _, tc := range []struct {
status OutboxStatus
want error
}{
{OutboxNone, ErrNoMatchingIntent},
{OutboxConsumed, ErrIntentConsumed},
{OutboxStatus(3), ErrNoMatchingIntent},
{OutboxStatus(255), ErrNoMatchingIntent},
} {
s := newState()
s.intent.Status = tc.status
v, err := verifyInferenceReceipt(s, r, p)
if !errors.Is(err, tc.want) {
t.Fatalf("outbox status %d: err = %v, want %v", tc.status, err, tc.want)
}
if v != (VerifiedAInferenceReceipt{}) {
t.Fatalf("outbox status %d: a refusal must return the zero result", tc.status)
}
if s.consumed != 0 {
t.Fatalf("outbox status %d: a refusal consumed the intent", tc.status)
}
}
s := newState()
if _, err := verifyInferenceReceipt(s, r, p); err != nil {
t.Fatalf("a pending record must settle: %v", err)
}
if s.consumed != 1 {
t.Fatalf("a settled record must be consumed once, got %d", s.consumed)
}
}
// --- the A->C receipt-root checkpoint --------------------------------------------
// A zero root is never committed (the empty slot already reads as zero, so accepting it
// would make an unwritten root indistinguishable from a trusted one), and a root
// committed at height ZERO must still read as committed.
func TestCommitReceiptRoot_RefusesZeroAndRecordsHeightZero(t *testing.T) {
db := newMemStateDB()
s := newStateKVStore(db, 5)
CommitReceiptRoot(db, [32]byte{}, 9)
if n := len(db.slots); n != 0 {
t.Fatalf("committing the zero root wrote %d storage accounts", n)
}
if s.ReceiptRootCommitted([32]byte{}) {
t.Fatal("the zero root must never read as committed")
}
root := h32(0x77)
if s.ReceiptRootCommitted(root) {
t.Fatal("an unwritten root must not read as committed")
}
CommitReceiptRoot(db, root, 0)
if !s.ReceiptRootCommitted(root) {
t.Fatal("a root committed at height 0 must read as committed")
}
if s.ReceiptRootCommitted(h32(0x78)) {
t.Fatal("a root that was never committed must not read as committed")
}
// Re-committing at a later height keeps it committed (the seam may see the same root
// twice across an import replay).
CommitReceiptRoot(db, root, 1_000)
if !s.ReceiptRootCommitted(root) {
t.Fatal("re-committing a root must not un-commit it")
}
}
// --- the native proof-of-inference check ------------------------------------------
// An opening that decodes is priced from its decoded dimensions; one gas below that
// price is refused outright rather than half-executed, and an opening whose matrices
// cannot be multiplied is refused instead of yielding a verdict.
func TestComputeProof_RefusesUnderfundedAndUnmultipliableOpenings(t *testing.T) {
honest := buildOpening(false)
cost := computeProofRequiredGas(honest)
if cost <= GasVerifyComputeProofBase {
t.Fatalf("a real opening must price above the base, got %d", cost)
}
if _, rem, err := verifyComputeProof(honest, cost-1); err == nil {
t.Fatal("an opening priced above the supplied gas must be refused")
} else if rem != 0 {
t.Fatalf("an out-of-gas refusal must leave no gas, got %d", rem)
}
if _, rem, err := verifyComputeProof(honest, cost); err != nil || rem != 0 {
t.Fatalf("exactly the priced cost must succeed and consume all of it: rem %d, err %v", rem, err)
}
// A.Cols(3) != B.Rows(4): every matrix is individually well formed, so the frame
// DECODES, but the product is undefined.
mismatched := poi.EncodeOpening([32]byte{0x11}, []byte("beacon"), poi.Opening{
Index: 0,
A: poi.NewMat(2, 3, make([]int64, 6)),
B: poi.NewMat(4, 5, make([]int64, 20)),
C: poi.NewMat(2, 5, make([]int64, 10)),
})
misCost := computeProofRequiredGas(mismatched)
if misCost <= GasVerifyComputeProofBase {
t.Fatalf("a decodable opening must price above the base, got %d", misCost)
}
ret, rem, err := verifyComputeProof(mismatched, misCost)
if err == nil {
t.Fatal("an opening whose matrices cannot be multiplied must error, not return a verdict")
}
if ret != nil {
t.Fatal("a refusal must return no verdict word")
}
if rem != 0 {
t.Fatalf("a decoded-then-refused opening consumes its priced cost, %d left", rem)
}
if g := computeProofRequiredGas([]byte{1, 2, 3}); g != GasVerifyComputeProofBase {
t.Fatalf("an undecodable frame must price at the base, got %d", g)
}
}