Files
zeekay d4474142f1 dex, e2e, LLM.md: pin the hook and market surfaces, and point e2e at current deps
The D-Chain, hook and market surfaces had behaviour no test observed. e2e/clob
moves to the module versions this repo builds against. LLM.md records the two
orthogonal calldata bounds — Cursor for how many bytes, Unsigned/Signed for
which values — and why hand-writing either is the bug they exist to close.
2026-08-28 15:39:28 -07:00

206 lines
7.6 KiB
Go

// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package precompileconfig
import (
"encoding/json"
"testing"
"github.com/stretchr/testify/require"
)
func ptr(v uint64) *uint64 { return &v }
// ---------------------------------------------------------------------------
// Timestamp / IsDisabled
// ---------------------------------------------------------------------------
func TestUpgradeTimestamp(t *testing.T) {
// nil means "never enabled" — distinct from 0, which means "from genesis".
require.Nil(t, (&Upgrade{}).Timestamp())
var zero Upgrade
zero.BlockTimestamp = ptr(0)
require.NotNil(t, zero.Timestamp())
require.Equal(t, uint64(0), *zero.Timestamp())
at := Upgrade{BlockTimestamp: ptr(1_700_000_000)}
require.Equal(t, uint64(1_700_000_000), *at.Timestamp())
}
func TestUpgradeIsDisabled(t *testing.T) {
require.False(t, (&Upgrade{}).IsDisabled())
require.True(t, (&Upgrade{Disable: true}).IsDisabled())
// Disable is independent of the timestamp: a disable at a future block is a
// legitimate upgrade, not a contradiction.
require.True(t, (&Upgrade{BlockTimestamp: ptr(99), Disable: true}).IsDisabled())
}
// ---------------------------------------------------------------------------
// Equal
// ---------------------------------------------------------------------------
func TestUpgradeEqual(t *testing.T) {
cases := []struct {
name string
a, b *Upgrade
want bool
}{
{"both zero", &Upgrade{}, &Upgrade{}, true},
{"nil other", &Upgrade{}, nil, false},
{"same timestamp", &Upgrade{BlockTimestamp: ptr(7)}, &Upgrade{BlockTimestamp: ptr(7)}, true},
{"different timestamp", &Upgrade{BlockTimestamp: ptr(7)}, &Upgrade{BlockTimestamp: ptr(8)}, false},
{"nil vs set", &Upgrade{}, &Upgrade{BlockTimestamp: ptr(0)}, false},
{"set vs nil", &Upgrade{BlockTimestamp: ptr(0)}, &Upgrade{}, false},
{"disable differs", &Upgrade{Disable: true}, &Upgrade{}, false},
{"disable matches", &Upgrade{Disable: true}, &Upgrade{Disable: true}, true},
{
"same timestamp, disable differs",
&Upgrade{BlockTimestamp: ptr(7), Disable: true},
&Upgrade{BlockTimestamp: ptr(7)},
false,
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
require.Equal(t, c.want, c.a.Equal(c.b))
if c.b != nil {
require.Equal(t, c.want, c.b.Equal(c.a), "Equal must be symmetric")
}
})
}
}
func TestUpgradeEqualComparesValuesNotPointers(t *testing.T) {
// Two distinct *uint64 holding the same value must compare equal. Pointer
// identity would make an upgrade config never equal to its own reload from
// JSON, and the host uses Equal to detect an upgrade re-declaration.
a := &Upgrade{BlockTimestamp: ptr(1234)}
b := &Upgrade{BlockTimestamp: ptr(1234)}
require.NotSame(t, a.BlockTimestamp, b.BlockTimestamp)
require.True(t, a.Equal(b))
}
func TestUpgradeEqualIsReflexive(t *testing.T) {
for _, u := range []*Upgrade{
{},
{BlockTimestamp: ptr(0)},
{BlockTimestamp: ptr(1), Disable: true},
} {
require.True(t, u.Equal(u))
}
}
// ---------------------------------------------------------------------------
// JSON — the on-disk upgrade.json shape is a compatibility surface
// ---------------------------------------------------------------------------
func TestUpgradeJSONRoundTrip(t *testing.T) {
for _, src := range []Upgrade{
{},
{BlockTimestamp: ptr(0)},
{BlockTimestamp: ptr(1_700_000_000)},
{BlockTimestamp: ptr(42), Disable: true},
{Disable: true},
} {
b, err := json.Marshal(src)
require.NoError(t, err)
var got Upgrade
require.NoError(t, json.Unmarshal(b, &got))
require.True(t, src.Equal(&got), "round trip lost information: %s", b)
}
}
func TestUpgradeJSONFieldNames(t *testing.T) {
// The key names ARE the file format. A rename silently ignores every
// operator's existing upgrade.json — it unmarshals clean into a nil
// timestamp, which means "never enabled".
b, err := json.Marshal(Upgrade{BlockTimestamp: ptr(9), Disable: true})
require.NoError(t, err)
require.JSONEq(t, `{"blockTimestamp":9,"disable":true}`, string(b))
// disable is omitempty; blockTimestamp is not, so "never enabled" is
// explicit on the wire rather than an absent key.
b, err = json.Marshal(Upgrade{})
require.NoError(t, err)
require.JSONEq(t, `{"blockTimestamp":null}`, string(b))
}
func TestUpgradeJSONUnknownKeyIsIgnoredButTimestampIsNot(t *testing.T) {
var u Upgrade
require.NoError(t, json.Unmarshal([]byte(`{"blockTimestamp":5,"disable":false,"bogus":1}`), &u))
require.Equal(t, uint64(5), *u.Timestamp())
require.False(t, u.IsDisabled())
// Key matching is case-insensitive, so a capitalisation slip still lands.
var caseSlip Upgrade
require.NoError(t, json.Unmarshal([]byte(`{"blockTimeStamp":5}`), &caseSlip))
require.Equal(t, uint64(5), *caseSlip.Timestamp())
// A genuinely misspelled key leaves the field nil — "never enabled" — with
// no error at all. Pinned so the silence is a documented property rather
// than a surprise on an operator's upgrade.json.
var typo Upgrade
require.NoError(t, json.Unmarshal([]byte(`{"blockTime":5}`), &typo))
require.Nil(t, typo.Timestamp())
}
// ---------------------------------------------------------------------------
// Config interface shape
// ---------------------------------------------------------------------------
// upgradeOnly embeds Upgrade and supplies the rest of Config, proving that
// Upgrade's Timestamp/IsDisabled signatures actually satisfy the interface a
// precompile config must implement. A signature drift breaks the build here
// rather than in twenty precompile packages. Note the pointer receiver:
// Upgrade's methods are declared on *Upgrade, so only *upgradeOnly is a Config.
type upgradeOnly struct{ Upgrade }
func (*upgradeOnly) Key() string { return "test" }
func (*upgradeOnly) Verify(ChainConfig) error { return nil }
func (u *upgradeOnly) Equal(other Config) bool {
o, ok := other.(*upgradeOnly)
return ok && u.Upgrade.Equal(&o.Upgrade)
}
var _ Config = (*upgradeOnly)(nil)
func TestConfigInterfaceSatisfiedByUpgrade(t *testing.T) {
var c Config = &upgradeOnly{Upgrade{BlockTimestamp: ptr(3)}}
require.Equal(t, "test", c.Key())
require.Equal(t, uint64(3), *c.Timestamp())
require.False(t, c.IsDisabled())
require.NoError(t, c.Verify(nil))
require.True(t, c.Equal(&upgradeOnly{Upgrade{BlockTimestamp: ptr(3)}}))
require.False(t, c.Equal(&upgradeOnly{Upgrade{BlockTimestamp: ptr(4)}}))
// Equal against a different concrete Config type is false, never a panic.
require.False(t, c.Equal(otherConfig{}))
}
// otherConfig is a second, unrelated Config implementation. Equal must reject
// it by type rather than by comparing fields it does not have.
type otherConfig struct{}
func (otherConfig) Key() string { return "other" }
func (otherConfig) Timestamp() *uint64 { return nil }
func (otherConfig) IsDisabled() bool { return false }
func (otherConfig) Equal(Config) bool { return false }
func (otherConfig) Verify(ChainConfig) error { return nil }
func TestChainConfigAdmitsAnythingIncludingNil(t *testing.T) {
// ChainConfig is a bare interface{} on purpose: under activate-all-implicitly
// there are no upgrade gates to ask about, and hosts that DO have something
// to say implement a narrower interface that a precompile feature-detects
// (see contract.FeeConfigReporter). The consequence is that Verify's
// parameter is unchecked — nil and a wrong-typed value are both accepted.
// Pinned so the looseness is a decision, not an accident.
var c Config = &upgradeOnly{}
require.NoError(t, c.Verify(nil))
require.NoError(t, c.Verify(struct{ Unrelated int }{1}))
require.NoError(t, c.Verify("not a chain config"))
}