Files
node/vms/xvm/vm_security_profile_test.go
T
Hanzo AI c3b398bc7b json: migrate every encoding/json import to json/v2 (go-json-experiment)
External (HTTP / JSON-RPC) is the only place JSON is legitimate. Every
existing encoding/json import in node/ moves to github.com/go-json-experiment/json
(v2 root, not the v1 sub-package). NewEncoder/NewDecoder rewrite to
MarshalWrite/UnmarshalRead. MarshalIndent rewrites to Marshal with
jsontext.WithIndent. json.RawMessage rewrites to jsontext.Value.
*json.SyntaxError rewrites to *jsontext.SyntacticError.

81 files migrated. LLM.md captures the rule + v1->v2 delta table.

Known v2 semantic deltas surfaced by existing tests (followups, not regressions):
- [N]byte fields with no MarshalJSON now marshal as base64 string (v1 marshalled
  as JSON array of byte numbers). Affects vms/platformvm/txs/*_test.go fixtures
  with embedded BLS proofOfPossession.
- time.Duration has no v2 default representation; configs that wire-format
  Duration as nanoseconds (vms/{xvm,platformvm}/config, config/spec) need to
  switch to string-form Duration or carry an explicit option. v2 root does not
  re-export FormatDurationAsNano.
- v2 enforces strict UTF-8 (vms/chainadapter/messaging fixture has non-UTF-8).
- json.MarshalWrite does not append a trailing '\n' (v1 NewEncoder.Encode did);
  service/auth/auth_test.go expectation updated.
- nil []byte round-trips to empty (not nil); config_test deep-equal fixtures
  surface this.

All affected sites are at the API boundary; ZAP wire envelope already covers
the internal data paths (state, P2P, consensus, MPC, threshold). Internal
JSON sites that should move to ZAP next (separate work):
- vms/da/store.go            (DA blob/cert storage as JSON)
- vms/platformvm/airdrop     (airdrop claims as JSON in db)
- vms/chainadapter/appchain  (SQLite materializer schema/data blobs)
- vms/chainadapter/messaging (conversation codec)
- staking/kms.go             (KMS HTTP client — external technically, leave)
- utils/{bimap,ips}          (small marshaler shims — low priority)
2026-06-06 22:26:02 -07:00

123 lines
3.7 KiB
Go

// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package xvm
import (
"context"
"github.com/go-json-experiment/json"
"errors"
"sync"
"testing"
"github.com/stretchr/testify/require"
consensusconfig "github.com/luxfi/consensus/config"
"github.com/luxfi/constants"
"github.com/luxfi/database/memdb"
"github.com/luxfi/ids"
"github.com/luxfi/node/vms/txs/auth"
xvmfx "github.com/luxfi/node/vms/xvm/fxs"
"github.com/luxfi/node/vms/xvm/txs"
"github.com/luxfi/runtime"
"github.com/luxfi/utxo/nftfx"
"github.com/luxfi/utxo/propertyfx"
"github.com/luxfi/utxo/secp256k1fx"
"github.com/luxfi/vm"
"github.com/luxfi/vm/chains/atomic"
)
// TestXVMInitialize_WiresSecurityProfileIntoMempool proves the F102
// close-out wiring on the X-chain: the SecurityProfile carried on
// xvm.Factory propagates through vm.Initialize → mempool.SetAuthPolicy.
//
// Strict-PQ chains refuse classical secp256k1 credentials at gossip
// time; the X-chain test exercises the unwrap path because X-chain
// credentials are wrapped in fxs.FxCredential before being checked by
// the auth policy gate.
//
// The test issues the tx via IssueTxFromRPCWithoutVerification, which
// skips block-state verification but still runs the mempool's
// SetAuthPolicy gate — that gate is the F102 close-out being verified
// here.
func TestXVMInitialize_WiresSecurityProfileIntoMempool(t *testing.T) {
require := require.New(t)
strictPQ := consensusconfig.StrictPQ()
vmImpl := &VM{
securityProfile: strictPQ,
classicalCompatRegistry: nil,
}
chainID := ids.GenerateTestID()
rt := &runtime.Runtime{
NetworkID: constants.UnitTestID,
ChainID: chainID,
XChainID: ids.GenerateTestID(),
CChainID: ids.GenerateTestID(),
NodeID: ids.GenerateTestNodeID(),
ValidatorState: &mockValidatorState{chainID: chainID},
}
baseDB := memdb.New()
sharedMemory := atomic.NewMemory(memdb.New())
vmImpl.SharedMemory = &testSharedMemory{mem: sharedMemory.NewSharedMemory(rt.ChainID)}
testLock := &sync.Mutex{}
testLock.Lock()
genesisBytes := newGenesisBytesTest(t)
fxs := []interface{}{
&vm.Fx{ID: secp256k1fx.ID, Fx: &secp256k1fx.Fx{}},
&vm.Fx{ID: nftfx.ID, Fx: &nftfx.Fx{}},
&vm.Fx{ID: propertyfx.ID, Fx: &propertyfx.Fx{}},
}
configBytes, err := json.Marshal(DefaultConfig)
require.NoError(err)
require.NoError(vmImpl.Initialize(
context.Background(),
vm.Init{
Runtime: rt,
DB: baseDB,
Genesis: genesisBytes,
Upgrade: nil,
Config: configBytes,
ToEngine: make(chan vm.Message, 1),
Fx: fxs,
Sender: &noOpSender{},
},
))
t.Cleanup(func() { _ = vmImpl.Shutdown() })
// Linearize so the mempool builder is constructed and SetAuthPolicy
// has fired with the strict-PQ profile.
stopVertexID := getCreateTxFromGenesisTest(t, genesisBytes, "LUX").ID()
require.NoError(vmImpl.Linearize(
context.Background(),
stopVertexID,
make(chan vm.Message, 1),
))
// Construct an X-chain tx whose Creds carry one FxCredential
// wrapping a classical *secp256k1fx.Credential. The mempool gate
// unwraps the FxCredential before consulting the auth policy.
tx := &txs.Tx{
Unsigned: &txs.BaseTx{},
Creds: []*xvmfx.FxCredential{
{Credential: &secp256k1fx.Credential{}},
},
}
// IssueTxFromRPCWithoutVerification reaches the mempool's
// AuthPolicy gate without going through block-state verification.
// The network is unexported on *VM; tests in-package access it
// directly to assert end-to-end wiring.
err = vmImpl.network.IssueTxFromRPCWithoutVerification(tx)
require.True(
errors.Is(err, auth.ErrLegacyCredentialUnderStrictPQ),
"IssueTxFromRPCWithoutVerification: got %v, want wrap of ErrLegacyCredentialUnderStrictPQ", err,
)
}