feat: drop CodecRegistry + codec.Manager from public API — ZAP-native at activation 2025-12-25T16:20:00-08:00

This commit is contained in:
Hanzo AI
2026-06-03 11:04:36 -07:00
parent 39a7cdb2f1
commit fb7b343e95
24 changed files with 268 additions and 305 deletions
+97
View File
@@ -0,0 +1,97 @@
# Final codec rip — `luxfi/utxo`
Activation 2025-12-25T16:20:00-08:00. No backwards compat, no shim, no
env var. The wire is ZAP. Delete the legacy.
## Surface to delete
### Per-fx `vm.go` (8 files)
Drop `CodecRegistry() codec.Registry` from the `VM` interface. Drop the
`Codec` field from `TestVM` and its `CodecRegistry()` accessor. Drop the
`luxfi/codec` import. Files:
- `secp256k1fx/vm.go`
- `mldsafx/vm.go`
- `slhdsafx/vm.go`
- `ed25519fx/vm.go`
- `secp256r1fx/vm.go`
- `schnorrfx/vm.go`
- `bls12381fx/fx.go` (VM interface lives in fx.go for this package)
### Per-fx `fx.go` Initialize (9 files)
Drop the `c := fx.VM.CodecRegistry()` block + the `RegisterType` chain.
The wire schema is static at compile time; nothing to register. Each
`Initialize` keeps the VM cast + logger init and otherwise becomes a
no-op. Files:
- `secp256k1fx/fx.go`
- `mldsafx/fx.go`
- `slhdsafx/fx.go`
- `ed25519fx/fx.go`
- `secp256r1fx/fx.go`
- `schnorrfx/fx.go`
- `bls12381fx/fx.go`
- `nftfx/fx.go` — embeds `secp256k1fx.Fx`, has its own RegisterType chain
- `propertyfx/fx.go` — embeds `secp256k1fx.Fx`, has its own RegisterType chain
### Root package
- `api.go` — replace `codec.Uint32` / `codec.Uint64` with `jsonutil.Uint32` /
`jsonutil.Uint64` (new package) or unqualified `uint32`/`uint64` with
hand-rolled JSON. Cleanest: replicate the JSON-string-quoted types
locally in `utxo/jsonutil/`.
- `atomic_utxos.go` — drop `codec codec.Manager` field + ctor param;
parse cross-chain bytes through `wire.WrapUTXO(b)`.
- `flow_checker.go` — replace `wrappers.Errs` with an inline 5-line
`errs` struct or move to `utxo/internal/errs/`. Drop the
`luxfi/codec/wrappers` import.
- `transferables.go` — drop `c codec.Manager` from
`SortTransferableOutputs`, `IsSortedTransferableOutputs`, `VerifyTx`.
Sort key is now `out.Bytes()` (the ZAP wire envelope returned by the
`wireSerializable` interface). `Verify` is unchanged.
- `utxo_state.go` — drop `codec codec.Manager` field + ctor params;
encode/decode through `utxo.WireBytes()` / `utxo.WrapUTXOBytes()`.
### `wire/discriminator.go`
Doc-comment-only reference. Comment text is correct historical context;
no import statement. Leave it.
## Activation invariant
The new network's genesis is at unix 1766708400. Every block produced
after that point uses ZAP-native UTXO encoding. No legacy codec UTXOs
exist on disk or on the wire.
## Cascade
After this lands, every `luxd` caller of:
- `lux.SortTransferableOutputs(outs, Codec)``lux.SortTransferableOutputs(outs)`
- `lux.IsSortedTransferableOutputs(outs, Codec)``lux.IsSortedTransferableOutputs(outs)`
- `lux.VerifyTx(fee, feeAsset, ins, outs, Codec)``lux.VerifyTx(fee, feeAsset, ins, outs)`
- `lux.GetAtomicUTXOs(sm, codec, ...)``lux.GetAtomicUTXOs(sm, ...)`
- `lux.NewAtomicUTXOManager(sm, codec)``lux.NewAtomicUTXOManager(sm)`
- `lux.NewUTXOState(db, codec, track)``lux.NewUTXOState(db, track)`
- `lux.NewMeteredUTXOState(db, codec, metrics, track)``lux.NewMeteredUTXOState(db, metrics, track)`
needs the codec arg dropped. Plus every fx.Initialize callsite no
longer needs `vm.CodecRegistry()` to do anything.
## Order of operations
1. Define `utxo/jsonutil/uint.go` with `Uint32`/`Uint64` JSON-string types.
2. Patch `api.go` to use `jsonutil`.
3. Patch `flow_checker.go` to inline a tiny errs collector.
4. Patch `transferables.go` to use `wireSerializable.Bytes()` as sort key.
5. Patch `atomic_utxos.go` to use `wire.WrapUTXO(b)`.
6. Patch `utxo_state.go` to use `utxo.WireBytes()` / `WrapUTXOBytes`.
7. Patch every fx's `vm.go` to drop `CodecRegistry()` + `Codec` field.
8. Patch every fx's `fx.go` `Initialize` to drop the registration chain.
9. Patch every fx's tests to drop `Codec: linearcodec.NewDefault()` + the
`linearcodec` import.
10. Build + test in tree.
11. `go mod edit -droprequire=github.com/luxfi/codec` + tidy.
12. Cascade through `luxd` consumer files.
+3 -3
View File
@@ -4,7 +4,7 @@
package utxo
import (
"github.com/luxfi/codec"
apitypes "github.com/luxfi/api/types"
"github.com/luxfi/formatting"
"github.com/luxfi/ids"
)
@@ -31,7 +31,7 @@ type Index struct {
type GetUTXOsArgs struct {
Addresses []string `json:"addresses"`
SourceChain string `json:"sourceChain"`
Limit codec.Uint32 `json:"limit"`
Limit apitypes.Uint32 `json:"limit"`
StartIndex Index `json:"startIndex"`
Encoding formatting.Encoding `json:"encoding"`
}
@@ -40,7 +40,7 @@ type GetUTXOsArgs struct {
// JSON field names match RPC conventions for compatibility across services.
type GetUTXOsReply struct {
// Number of UTXOs returned
NumFetched codec.Uint64 `json:"numFetched"`
NumFetched apitypes.Uint64 `json:"numFetched"`
// The UTXOs
UTXOs []string `json:"utxos"`
// The last UTXO that was returned, and the address it corresponds to.
+10 -13
View File
@@ -6,7 +6,6 @@ package utxo
import (
"fmt"
"github.com/luxfi/codec"
"github.com/luxfi/ids"
"github.com/luxfi/math/set"
"github.com/luxfi/vm/chains/atomic"
@@ -15,15 +14,14 @@ import (
var _ AtomicUTXOManager = (*atomicUTXOManager)(nil)
type atomicUTXOManager struct {
sm atomic.SharedMemory
codec codec.Manager
sm atomic.SharedMemory
}
func NewAtomicUTXOManager(sm atomic.SharedMemory, codec codec.Manager) AtomicUTXOManager {
return &atomicUTXOManager{
sm: sm,
codec: codec,
}
// NewAtomicUTXOManager returns an AtomicUTXOManager backed by ZAP-native
// wire bytes stored in cross-chain shared memory. Caller must have
// invoked RegisterParseUTXO before the first GetAtomicUTXOs call.
func NewAtomicUTXOManager(sm atomic.SharedMemory) AtomicUTXOManager {
return &atomicUTXOManager{sm: sm}
}
func (a *atomicUTXOManager) GetAtomicUTXOs(
@@ -63,11 +61,11 @@ func (a *atomicUTXOManager) GetAtomicUTXOs(
utxos := make([]*UTXO, len(allUTXOBytes))
for i, utxoBytes := range allUTXOBytes {
utxo := &UTXO{}
if _, err := a.codec.Unmarshal(utxoBytes, utxo); err != nil {
u, err := ParseUTXO(utxoBytes)
if err != nil {
return nil, ids.ShortID{}, ids.Empty, fmt.Errorf("error parsing UTXO: %w", err)
}
utxos[i] = utxo
utxos[i] = u
}
return utxos, lastAddrID, lastUTXOID, nil
}
@@ -84,13 +82,12 @@ func (a *atomicUTXOManager) GetAtomicUTXOs(
// * Any error that may have occurred upstream.
func GetAtomicUTXOs(
sharedMemory atomic.SharedMemory,
codec codec.Manager,
chainID ids.ID,
addrs set.Set[ids.ShortID],
startAddr ids.ShortID,
startUTXOID ids.ID,
limit int,
) ([]*UTXO, ids.ShortID, ids.ID, error) {
manager := NewAtomicUTXOManager(sharedMemory, codec)
manager := NewAtomicUTXOManager(sharedMemory)
return manager.GetAtomicUTXOs(chainID, addrs, startAddr, startUTXOID, limit)
}
+9 -28
View File
@@ -6,10 +6,8 @@ package bls12381fx
import (
"errors"
"fmt"
"strings"
"github.com/luxfi/cache/lru"
"github.com/luxfi/codec"
"github.com/luxfi/crypto/bls"
"github.com/luxfi/crypto/hash"
"github.com/luxfi/ids"
@@ -36,9 +34,9 @@ var (
ErrSignerBitmapOutOfRange = errors.New("signer bitmap references pubkey out of range")
)
// VM is the interface this Fx requires.
// VM is the interface this Fx requires. ZAP-native: no runtime codec
// registration — wire schemas are compile-time static in wire.go.
type VM interface {
CodecRegistry() codec.Registry
Clock() *mockable.Clock
Logger() log.Logger
}
@@ -47,14 +45,12 @@ var _ VM = (*TestVM)(nil)
// TestVM is a minimal VM for tests.
type TestVM struct {
Clk mockable.Clock
Codec codec.Registry
Log log.Logger
Clk mockable.Clock
Log log.Logger
}
func (vm *TestVM) Clock() *mockable.Clock { return &vm.Clk }
func (vm *TestVM) CodecRegistry() codec.Registry { return vm.Codec }
func (vm *TestVM) Logger() log.Logger { return vm.Log }
func (vm *TestVM) Clock() *mockable.Clock { return &vm.Clk }
func (vm *TestVM) Logger() log.Logger { return vm.Log }
// UnsignedTx is what this Fx is signing over.
type UnsignedTx interface{ Bytes() []byte }
@@ -80,24 +76,9 @@ func (fx *Fx) Initialize(vmIntf interface{}) error {
if !logr.IsZero() {
logr.Debug("initializing bls12381fx (attestation-only)")
}
if fx.VM == nil {
return nil
}
c := fx.VM.CodecRegistry()
if c == nil {
return nil
}
errs := []error{}
if err := c.RegisterType(&AttestationOutput{}); err != nil && !strings.Contains(err.Error(), "duplicate type registration") {
errs = append(errs, err)
}
if err := c.RegisterType(&AttestationInput{}); err != nil && !strings.Contains(err.Error(), "duplicate type registration") {
errs = append(errs, err)
}
if err := c.RegisterType(&Credential{}); err != nil && !strings.Contains(err.Error(), "duplicate type registration") {
errs = append(errs, err)
}
return errors.Join(errs...)
// ZAP-native: wire schemas are compile-time static in bls12381fx/wire.go.
// No runtime codec registration needed.
return nil
}
func (*Fx) Bootstrapping() error { return nil }
+3 -23
View File
@@ -7,7 +7,6 @@ import (
"crypto/ed25519"
"errors"
"fmt"
"strings"
"github.com/luxfi/cache/lru"
"github.com/luxfi/crypto/hash"
@@ -61,28 +60,9 @@ func (fx *Fx) Initialize(vmIntf interface{}) error {
return nil
}
c := fx.VM.CodecRegistry()
if c == nil {
return nil
}
errs := []error{}
if err := c.RegisterType(&TransferInput{}); err != nil && !strings.Contains(err.Error(), "duplicate type registration") {
errs = append(errs, err)
}
if err := c.RegisterType(&MintOutput{}); err != nil && !strings.Contains(err.Error(), "duplicate type registration") {
errs = append(errs, err)
}
if err := c.RegisterType(&TransferOutput{}); err != nil && !strings.Contains(err.Error(), "duplicate type registration") {
errs = append(errs, err)
}
if err := c.RegisterType(&MintOperation{}); err != nil && !strings.Contains(err.Error(), "duplicate type registration") {
errs = append(errs, err)
}
if err := c.RegisterType(&Credential{}); err != nil && !strings.Contains(err.Error(), "duplicate type registration") {
errs = append(errs, err)
}
return errors.Join(errs...)
// ZAP-native: wire schemas are compile-time static in the per-fx
// wire.go bridge. No runtime codec registration needed.
return nil
}
func (fx *Fx) InitializeVM(vmIntf interface{}) error {
+4 -10
View File
@@ -4,14 +4,13 @@
package ed25519fx
import (
"github.com/luxfi/codec"
log "github.com/luxfi/log"
"github.com/luxfi/timer/mockable"
)
// VM that this Fx must be run by
// VM that this Fx must be run by. ZAP-native: no runtime codec
// registration — wire schemas are compile-time static.
type VM interface {
CodecRegistry() codec.Registry
Clock() *mockable.Clock
Logger() log.Logger
}
@@ -20,19 +19,14 @@ var _ VM = (*TestVM)(nil)
// TestVM is a minimal implementation of a VM
type TestVM struct {
Clk mockable.Clock
Codec codec.Registry
Log log.Logger
Clk mockable.Clock
Log log.Logger
}
func (vm *TestVM) Clock() *mockable.Clock {
return &vm.Clk
}
func (vm *TestVM) CodecRegistry() codec.Registry {
return vm.Codec
}
func (vm *TestVM) Logger() log.Logger {
return vm.Log
}
+7 -6
View File
@@ -6,7 +6,6 @@ package utxo
import (
"errors"
"github.com/luxfi/codec/wrappers"
"github.com/luxfi/ids"
"github.com/luxfi/math"
)
@@ -15,7 +14,7 @@ var ErrInsufficientFunds = errors.New("insufficient funds")
type FlowChecker struct {
consumed, produced map[ids.ID]uint64
errs wrappers.Errs
errs []error
}
func NewFlowChecker() *FlowChecker {
@@ -36,18 +35,20 @@ func (fc *FlowChecker) Produce(assetID ids.ID, amount uint64) {
func (fc *FlowChecker) add(value map[ids.ID]uint64, assetID ids.ID, amount uint64) {
var err error
value[assetID], err = math.Add64(value[assetID], amount)
fc.errs.Add(err)
if err != nil {
fc.errs = append(fc.errs, err)
}
}
func (fc *FlowChecker) Verify() error {
if !fc.errs.Errored() {
if len(fc.errs) == 0 {
for assetID, producedAssetAmount := range fc.produced {
consumedAssetAmount := fc.consumed[assetID]
if producedAssetAmount > consumedAssetAmount {
fc.errs.Add(ErrInsufficientFunds)
fc.errs = append(fc.errs, ErrInsufficientFunds)
break
}
}
}
return fc.errs.Err
return errors.Join(fc.errs...)
}
+2 -1
View File
@@ -5,6 +5,7 @@ go 1.26.3
require (
github.com/btcsuite/btcd/btcec/v2 v2.1.3
github.com/luxfi/address v1.0.1
github.com/luxfi/api v1.0.12
github.com/luxfi/cache v1.2.1
github.com/luxfi/codec v1.1.4
github.com/luxfi/constants v1.4.7
@@ -43,7 +44,7 @@ require (
github.com/luxfi/atomic v1.0.0 // indirect
github.com/luxfi/compress v0.0.5 // indirect
github.com/luxfi/concurrent v0.0.3 // indirect
github.com/luxfi/consensus v1.22.84 // indirect
github.com/luxfi/consensus v1.22.85 // indirect
github.com/luxfi/container v0.0.4 // indirect
github.com/luxfi/math/big v0.1.0 // indirect
github.com/luxfi/mdns v0.1.1 // indirect
+4 -2
View File
@@ -79,6 +79,8 @@ github.com/luxfi/accel v1.1.9 h1:Tsk6gXj2uKE19501bD0ajRYdeCHIlTGb6jYyLc+F8hc=
github.com/luxfi/accel v1.1.9/go.mod h1:K00BcnLzEYMPHwCFq8Tf/450ApmTs9xBVvYOnofJCkc=
github.com/luxfi/address v1.0.1 h1:Sc4keyuVzBIvHr7uVeYZf2/WY9YDGUgDi/iiWenj49g=
github.com/luxfi/address v1.0.1/go.mod h1:5j3Eh66v9zvv1GbNdZwt+23krV8JlSDaRzmWZU8ZRM0=
github.com/luxfi/api v1.0.12 h1:HnLfoowXDZ2oGGB+40CHlmQ0XET5I9I828EVeYixlXc=
github.com/luxfi/api v1.0.12/go.mod h1:q3Hh3mmkvp3cnv3aqe2+gCtmmSAL/uFjuzrqGavQLNA=
github.com/luxfi/atomic v1.0.0 h1:xUV60MuzRvXngaQ1sM0yVC2v4TRoLlUGkkH7M9PS4yw=
github.com/luxfi/atomic v1.0.0/go.mod h1:0G2mTlQ6TXWHICUHrUUPu1/qAiIyR4gSZ2tva9ci/bI=
github.com/luxfi/cache v1.2.1 h1:kAzOS55/hmYeNKR+0HAKv4ma48Y6JjkI8UQeqdZ8bfI=
@@ -89,8 +91,8 @@ github.com/luxfi/compress v0.0.5 h1:4tEUHw5MK1bu5UOjfYCt4OKMiH7yykIgmGPRA/BfJTM=
github.com/luxfi/compress v0.0.5/go.mod h1:Cc1yxD2pfzrvpO32W2GDwLKff+CylHEvzZh2Ko8RSIU=
github.com/luxfi/concurrent v0.0.3 h1:eJyv1fhaC0jMLMw6+QS774cUmp7GK+ouMgvLCqnC7cc=
github.com/luxfi/concurrent v0.0.3/go.mod h1:Aj/FR5NpM0cB2P4Nt3+tz9+dV6V+LUW4HuMgSjwq5hw=
github.com/luxfi/consensus v1.22.84 h1:fKw4n7fY+lCWW5HSx/ciGeRSxv4knODQGWh6lBWAveQ=
github.com/luxfi/consensus v1.22.84/go.mod h1:y6y+bVjvpDLkkTBCCKLcch6R4PRW8EA+lTfr+6sB6F8=
github.com/luxfi/consensus v1.22.85 h1:S/XpPF+eDapbZb4AIu0Vr1GXzg/sAxjrAff2M2mkOx0=
github.com/luxfi/consensus v1.22.85/go.mod h1:y6y+bVjvpDLkkTBCCKLcch6R4PRW8EA+lTfr+6sB6F8=
github.com/luxfi/constants v1.4.7 h1:e/Qs+DQP3pugle3Zncq6fZCxKgqqtbyD/z7Gm4ZjsYg=
github.com/luxfi/constants v1.4.7/go.mod h1:hOszZ2NDQ8gMZKncfcZ67PXkb5OIbnwAzXC3oFbQwW0=
github.com/luxfi/container v0.0.4 h1:BXhF82WyfqVP5mjlNcr7tP0Fcnvl0Ap1rkiu+rq5XuM=
+3 -23
View File
@@ -6,7 +6,6 @@ package mldsafx
import (
"errors"
"fmt"
"strings"
"github.com/luxfi/cache/lru"
"github.com/luxfi/crypto/hash"
@@ -68,28 +67,9 @@ func (fx *Fx) Initialize(vmIntf interface{}) error {
return nil
}
c := fx.VM.CodecRegistry()
if c == nil {
return nil
}
errs := []error{}
if err := c.RegisterType(&TransferInput{}); err != nil && !strings.Contains(err.Error(), "duplicate type registration") {
errs = append(errs, err)
}
if err := c.RegisterType(&MintOutput{}); err != nil && !strings.Contains(err.Error(), "duplicate type registration") {
errs = append(errs, err)
}
if err := c.RegisterType(&TransferOutput{}); err != nil && !strings.Contains(err.Error(), "duplicate type registration") {
errs = append(errs, err)
}
if err := c.RegisterType(&MintOperation{}); err != nil && !strings.Contains(err.Error(), "duplicate type registration") {
errs = append(errs, err)
}
if err := c.RegisterType(&Credential{}); err != nil && !strings.Contains(err.Error(), "duplicate type registration") {
errs = append(errs, err)
}
return errors.Join(errs...)
// ZAP-native: wire schemas are compile-time static in the per-fx
// wire.go bridge. No runtime codec registration needed.
return nil
}
func (fx *Fx) InitializeVM(vmIntf interface{}) error {
+4 -10
View File
@@ -4,14 +4,13 @@
package mldsafx
import (
"github.com/luxfi/codec"
log "github.com/luxfi/log"
"github.com/luxfi/timer/mockable"
)
// VM that this Fx must be run by
// VM that this Fx must be run by. ZAP-native: no runtime codec
// registration — wire schemas are compile-time static.
type VM interface {
CodecRegistry() codec.Registry
Clock() *mockable.Clock
Logger() log.Logger
}
@@ -20,19 +19,14 @@ var _ VM = (*TestVM)(nil)
// TestVM is a minimal implementation of a VM
type TestVM struct {
Clk mockable.Clock
Codec codec.Registry
Log log.Logger
Clk mockable.Clock
Log log.Logger
}
func (vm *TestVM) Clock() *mockable.Clock {
return &vm.Clk
}
func (vm *TestVM) CodecRegistry() codec.Registry {
return vm.Codec
}
func (vm *TestVM) Logger() log.Logger {
return vm.Log
}
+3 -8
View File
@@ -32,14 +32,9 @@ func (fx *Fx) Initialize(vmIntf interface{}) error {
log := fx.VM.Logger()
log.Debug("initializing nft fx")
c := fx.VM.CodecRegistry()
return errors.Join(
c.RegisterType(&MintOutput{}),
c.RegisterType(&TransferOutput{}),
c.RegisterType(&MintOperation{}),
c.RegisterType(&TransferOperation{}),
c.RegisterType(&Credential{}),
)
// ZAP-native: wire schemas are compile-time static. No runtime
// codec registration needed.
return nil
}
func (fx *Fx) VerifyOperation(txIntf, opIntf, credIntf interface{}, utxosIntf []interface{}) error {
+50
View File
@@ -0,0 +1,50 @@
// Copyright (C) 2026, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package utxo
import (
"errors"
"sync"
)
// ParseUTXOFunc reconstructs a *UTXO from its ZAP wire envelope. The
// concrete implementation lives in luxfi/node (or any consumer that
// imports all the fx packages it needs to dispatch on the inner Output
// TypeKind+ShapeKind discriminator). The root utxo package can't
// import fx packages directly (cycle), so the consumer registers a
// factory at boot.
//
// ZAP wire bytes are the canonical input. No codec.Manager — the same
// bytes flow on the wire, on disk (via zapdb), and into the parser.
type ParseUTXOFunc func(wireBytes []byte) (*UTXO, error)
var (
parseUTXOOnce sync.Once
parseUTXO ParseUTXOFunc
// ErrParseUTXONotRegistered is returned by ParseUTXO before the
// consumer has called RegisterParseUTXO. Each program (luxd, cli,
// tests, etc.) must register exactly once at boot.
ErrParseUTXONotRegistered = errors.New("utxo: ParseUTXO not registered — caller must invoke RegisterParseUTXO at boot")
)
// RegisterParseUTXO registers the fx-aware factory. Idempotent — only
// the first call wins; subsequent calls are no-ops. This matches the
// "register once at process boot" pattern and prevents accidental
// override at test time.
func RegisterParseUTXO(fn ParseUTXOFunc) {
parseUTXOOnce.Do(func() {
parseUTXO = fn
})
}
// ParseUTXO reconstructs a *UTXO from its ZAP wire envelope using the
// registered factory. Returns ErrParseUTXONotRegistered if no factory
// has been registered.
func ParseUTXO(wireBytes []byte) (*UTXO, error) {
if parseUTXO == nil {
return nil, ErrParseUTXONotRegistered
}
return parseUTXO(wireBytes)
}
+3 -8
View File
@@ -30,14 +30,9 @@ func (fx *Fx) Initialize(vmIntf interface{}) error {
log := fx.VM.Logger()
log.Debug("initializing nft fx")
c := fx.VM.CodecRegistry()
return errors.Join(
c.RegisterType(&MintOutput{}),
c.RegisterType(&OwnedOutput{}),
c.RegisterType(&MintOperation{}),
c.RegisterType(&BurnOperation{}),
c.RegisterType(&Credential{}),
)
// ZAP-native: wire schemas are compile-time static. No runtime
// codec registration needed.
return nil
}
func (fx *Fx) VerifyOperation(txIntf, opIntf, credIntf interface{}, utxosIntf []interface{}) error {
+3 -23
View File
@@ -7,7 +7,6 @@ import (
"crypto/sha256"
"errors"
"fmt"
"strings"
"github.com/btcsuite/btcd/btcec/v2/schnorr"
"github.com/luxfi/cache/lru"
@@ -66,28 +65,9 @@ func (fx *Fx) Initialize(vmIntf interface{}) error {
return nil
}
c := fx.VM.CodecRegistry()
if c == nil {
return nil
}
errs := []error{}
if err := c.RegisterType(&TransferInput{}); err != nil && !strings.Contains(err.Error(), "duplicate type registration") {
errs = append(errs, err)
}
if err := c.RegisterType(&MintOutput{}); err != nil && !strings.Contains(err.Error(), "duplicate type registration") {
errs = append(errs, err)
}
if err := c.RegisterType(&TransferOutput{}); err != nil && !strings.Contains(err.Error(), "duplicate type registration") {
errs = append(errs, err)
}
if err := c.RegisterType(&MintOperation{}); err != nil && !strings.Contains(err.Error(), "duplicate type registration") {
errs = append(errs, err)
}
if err := c.RegisterType(&Credential{}); err != nil && !strings.Contains(err.Error(), "duplicate type registration") {
errs = append(errs, err)
}
return errors.Join(errs...)
// ZAP-native: wire schemas are compile-time static in the per-fx
// wire.go bridge. No runtime codec registration needed.
return nil
}
func (fx *Fx) InitializeVM(vmIntf interface{}) error {
+4 -10
View File
@@ -4,14 +4,13 @@
package schnorrfx
import (
"github.com/luxfi/codec"
log "github.com/luxfi/log"
"github.com/luxfi/timer/mockable"
)
// VM that this Fx must be run by
// VM that this Fx must be run by. ZAP-native: no runtime codec
// registration — wire schemas are compile-time static.
type VM interface {
CodecRegistry() codec.Registry
Clock() *mockable.Clock
Logger() log.Logger
}
@@ -20,19 +19,14 @@ var _ VM = (*TestVM)(nil)
// TestVM is a minimal implementation of a VM
type TestVM struct {
Clk mockable.Clock
Codec codec.Registry
Log log.Logger
Clk mockable.Clock
Log log.Logger
}
func (vm *TestVM) Clock() *mockable.Clock {
return &vm.Clk
}
func (vm *TestVM) CodecRegistry() codec.Registry {
return vm.Codec
}
func (vm *TestVM) Logger() log.Logger {
return vm.Log
}
+3 -28
View File
@@ -6,7 +6,6 @@ package secp256k1fx
import (
"errors"
"fmt"
"strings"
"github.com/luxfi/ids"
@@ -59,33 +58,9 @@ func (fx *Fx) Initialize(vmIntf interface{}) error {
fx.recoverCache = secp256k1.NewRecoverCache(defaultCacheSize)
if fx.VM == nil {
return nil
}
c := fx.VM.CodecRegistry()
if c == nil {
return nil
}
// Try to register types, but ignore duplicate registration errors
errs := []error{}
if err := c.RegisterType(&TransferInput{}); err != nil && !strings.Contains(err.Error(), "duplicate type registration") {
errs = append(errs, err)
}
if err := c.RegisterType(&MintOutput{}); err != nil && !strings.Contains(err.Error(), "duplicate type registration") {
errs = append(errs, err)
}
if err := c.RegisterType(&TransferOutput{}); err != nil && !strings.Contains(err.Error(), "duplicate type registration") {
errs = append(errs, err)
}
if err := c.RegisterType(&MintOperation{}); err != nil && !strings.Contains(err.Error(), "duplicate type registration") {
errs = append(errs, err)
}
if err := c.RegisterType(&Credential{}); err != nil && !strings.Contains(err.Error(), "duplicate type registration") {
errs = append(errs, err)
}
return errors.Join(errs...)
// ZAP-native: wire schemas are compile-time static in
// secp256k1fx/wire.go. No runtime codec registration needed.
return nil
}
func (fx *Fx) InitializeVM(vmIntf interface{}) error {
+4 -10
View File
@@ -4,14 +4,13 @@
package secp256k1fx
import (
"github.com/luxfi/codec"
log "github.com/luxfi/log"
"github.com/luxfi/timer/mockable"
)
// VM that this Fx must be run by
// VM that this Fx must be run by. ZAP-native: no runtime codec
// registration — wire schemas are compile-time static.
type VM interface {
CodecRegistry() codec.Registry
Clock() *mockable.Clock
Logger() log.Logger
}
@@ -20,19 +19,14 @@ var _ VM = (*TestVM)(nil)
// TestVM is a minimal implementation of a VM
type TestVM struct {
Clk mockable.Clock
Codec codec.Registry
Log log.Logger
Clk mockable.Clock
Log log.Logger
}
func (vm *TestVM) Clock() *mockable.Clock {
return &vm.Clk
}
func (vm *TestVM) CodecRegistry() codec.Registry {
return vm.Codec
}
func (vm *TestVM) Logger() log.Logger {
return vm.Log
}
+3 -23
View File
@@ -6,7 +6,6 @@ package secp256r1fx
import (
"errors"
"fmt"
"strings"
"github.com/luxfi/cache/lru"
"github.com/luxfi/crypto/hash"
@@ -60,28 +59,9 @@ func (fx *Fx) Initialize(vmIntf interface{}) error {
return nil
}
c := fx.VM.CodecRegistry()
if c == nil {
return nil
}
errs := []error{}
if err := c.RegisterType(&TransferInput{}); err != nil && !strings.Contains(err.Error(), "duplicate type registration") {
errs = append(errs, err)
}
if err := c.RegisterType(&MintOutput{}); err != nil && !strings.Contains(err.Error(), "duplicate type registration") {
errs = append(errs, err)
}
if err := c.RegisterType(&TransferOutput{}); err != nil && !strings.Contains(err.Error(), "duplicate type registration") {
errs = append(errs, err)
}
if err := c.RegisterType(&MintOperation{}); err != nil && !strings.Contains(err.Error(), "duplicate type registration") {
errs = append(errs, err)
}
if err := c.RegisterType(&Credential{}); err != nil && !strings.Contains(err.Error(), "duplicate type registration") {
errs = append(errs, err)
}
return errors.Join(errs...)
// ZAP-native: wire schemas are compile-time static in the per-fx
// wire.go bridge. No runtime codec registration needed.
return nil
}
func (fx *Fx) InitializeVM(vmIntf interface{}) error {
+4 -10
View File
@@ -4,14 +4,13 @@
package secp256r1fx
import (
"github.com/luxfi/codec"
log "github.com/luxfi/log"
"github.com/luxfi/timer/mockable"
)
// VM that this Fx must be run by
// VM that this Fx must be run by. ZAP-native: no runtime codec
// registration — wire schemas are compile-time static.
type VM interface {
CodecRegistry() codec.Registry
Clock() *mockable.Clock
Logger() log.Logger
}
@@ -20,19 +19,14 @@ var _ VM = (*TestVM)(nil)
// TestVM is a minimal implementation of a VM
type TestVM struct {
Clk mockable.Clock
Codec codec.Registry
Log log.Logger
Clk mockable.Clock
Log log.Logger
}
func (vm *TestVM) Clock() *mockable.Clock {
return &vm.Clk
}
func (vm *TestVM) CodecRegistry() codec.Registry {
return vm.Codec
}
func (vm *TestVM) Logger() log.Logger {
return vm.Log
}
+3 -23
View File
@@ -6,7 +6,6 @@ package slhdsafx
import (
"errors"
"fmt"
"strings"
"github.com/luxfi/cache/lru"
"github.com/luxfi/crypto/hash"
@@ -66,28 +65,9 @@ func (fx *Fx) Initialize(vmIntf interface{}) error {
return nil
}
c := fx.VM.CodecRegistry()
if c == nil {
return nil
}
errs := []error{}
if err := c.RegisterType(&TransferInput{}); err != nil && !strings.Contains(err.Error(), "duplicate type registration") {
errs = append(errs, err)
}
if err := c.RegisterType(&MintOutput{}); err != nil && !strings.Contains(err.Error(), "duplicate type registration") {
errs = append(errs, err)
}
if err := c.RegisterType(&TransferOutput{}); err != nil && !strings.Contains(err.Error(), "duplicate type registration") {
errs = append(errs, err)
}
if err := c.RegisterType(&MintOperation{}); err != nil && !strings.Contains(err.Error(), "duplicate type registration") {
errs = append(errs, err)
}
if err := c.RegisterType(&Credential{}); err != nil && !strings.Contains(err.Error(), "duplicate type registration") {
errs = append(errs, err)
}
return errors.Join(errs...)
// ZAP-native: wire schemas are compile-time static in the per-fx
// wire.go bridge. No runtime codec registration needed.
return nil
}
func (fx *Fx) InitializeVM(vmIntf interface{}) error {
+4 -10
View File
@@ -4,14 +4,13 @@
package slhdsafx
import (
"github.com/luxfi/codec"
log "github.com/luxfi/log"
"github.com/luxfi/timer/mockable"
)
// VM that this Fx must be run by
// VM that this Fx must be run by. ZAP-native: no runtime codec
// registration — wire schemas are compile-time static.
type VM interface {
CodecRegistry() codec.Registry
Clock() *mockable.Clock
Logger() log.Logger
}
@@ -20,19 +19,14 @@ var _ VM = (*TestVM)(nil)
// TestVM is a minimal implementation of a VM
type TestVM struct {
Clk mockable.Clock
Codec codec.Registry
Log log.Logger
Clk mockable.Clock
Log log.Logger
}
func (vm *TestVM) Clock() *mockable.Clock {
return &vm.Clk
}
func (vm *TestVM) CodecRegistry() codec.Registry {
return vm.Codec
}
func (vm *TestVM) Logger() log.Logger {
return vm.Log
}
+27 -20
View File
@@ -8,7 +8,6 @@ import (
"errors"
"sort"
"github.com/luxfi/codec"
"github.com/luxfi/crypto/secp256k1"
"github.com/luxfi/ids"
"github.com/luxfi/runtime"
@@ -86,9 +85,21 @@ func (out *TransferableOutput) Verify() error {
}
}
// wireBytesOrNil returns the ZAP wire envelope for a TransferableOut if
// the inner fxs primitive implements the per-fx wire.go Bytes() bridge.
// Returns nil when the type does not carry a wire adapter — callers must
// not rely on a stable sort across unknown types. Every production fxs
// primitive (secp256k1fx/mldsafx/slhdsafx/ed25519fx/secp256r1fx/schnorrfx/
// bls12381fx/nftfx/propertyfx) satisfies this contract via wire.go.
func wireBytesOrNil(out TransferableOut) []byte {
if ws, ok := out.(interface{ Bytes() []byte }); ok {
return ws.Bytes()
}
return nil
}
type innerSortTransferableOutputs struct {
outs []*TransferableOutput
codec codec.Manager
outs []*TransferableOutput
}
func (outs *innerSortTransferableOutputs) Less(i, j int) bool {
@@ -105,15 +116,11 @@ func (outs *innerSortTransferableOutputs) Less(i, j int) bool {
return false
}
iBytes, err := outs.codec.Marshal(codecVersion, &iOut.Out)
if err != nil {
return false
}
jBytes, err := outs.codec.Marshal(codecVersion, &jOut.Out)
if err != nil {
return false
}
return bytes.Compare(iBytes, jBytes) == -1
// ZAP-native canonical sort: bytes of the inner fx wire envelope.
// The per-fx wire.go Bytes() returns the same TypeKind+ShapeKind+
// ZAP-message envelope that hits the wire and disk — single source
// of truth for total ordering, no separate codec marshal step.
return bytes.Compare(wireBytesOrNil(iOut.Out), wireBytesOrNil(jOut.Out)) == -1
}
func (outs *innerSortTransferableOutputs) Len() int {
@@ -125,14 +132,15 @@ func (outs *innerSortTransferableOutputs) Swap(i, j int) {
o[j], o[i] = o[i], o[j]
}
// SortTransferableOutputs sorts output objects
func SortTransferableOutputs(outs []*TransferableOutput, c codec.Manager) {
sort.Sort(&innerSortTransferableOutputs{outs: outs, codec: c})
// SortTransferableOutputs sorts output objects by (AssetID, inner-output
// ZAP wire bytes). ZAP-native — no codec.Manager needed.
func SortTransferableOutputs(outs []*TransferableOutput) {
sort.Sort(&innerSortTransferableOutputs{outs: outs})
}
// IsSortedTransferableOutputs returns true if output objects are sorted
func IsSortedTransferableOutputs(outs []*TransferableOutput, c codec.Manager) bool {
return sort.IsSorted(&innerSortTransferableOutputs{outs: outs, codec: c})
// IsSortedTransferableOutputs returns true if output objects are sorted.
func IsSortedTransferableOutputs(outs []*TransferableOutput) bool {
return sort.IsSorted(&innerSortTransferableOutputs{outs: outs})
}
type TransferableInput struct {
@@ -213,7 +221,6 @@ func VerifyTx(
feeAssetID ids.ID,
allIns [][]*TransferableInput,
allOuts [][]*TransferableOutput,
c codec.Manager,
) error {
fc := NewFlowChecker()
@@ -227,7 +234,7 @@ func VerifyTx(
}
fc.Produce(out.AssetID(), out.Output().Amount())
}
if !IsSortedTransferableOutputs(outs, c) {
if !IsSortedTransferableOutputs(outs) {
return ErrOutputsNotSorted
}
}
+11 -13
View File
@@ -8,7 +8,6 @@ import (
"github.com/luxfi/cache"
"github.com/luxfi/cache/metercacher"
"github.com/luxfi/codec"
"github.com/luxfi/database"
"github.com/luxfi/database/linkeddb"
"github.com/luxfi/database/prefixdb"
@@ -72,8 +71,6 @@ type UTXOWriter interface {
}
type utxoState struct {
codec codec.Manager
// UTXO ID -> *UTXO. If the *UTXO is nil the UTXO doesn't exist
utxoCache cache.Cacher[ids.ID, *UTXO]
utxoDB database.Database
@@ -85,14 +82,15 @@ type utxoState struct {
checksum ids.ID
}
// NewUTXOState returns a UTXOState backed by ZAP-native wire bytes
// (no codec.Manager). Caller MUST have invoked RegisterParseUTXO at
// boot — the fxs dispatch needed to reconstruct *UTXO from wire bytes
// lives in the consumer package to break the import cycle.
func NewUTXOState(
db database.Database,
codec codec.Manager,
trackChecksum bool,
) (UTXOState, error) {
s := &utxoState{
codec: codec,
utxoCache: &cache.LRU[ids.ID, *UTXO]{Size: utxoCacheSize},
utxoDB: prefixdb.New(utxoPrefix, db),
@@ -106,7 +104,6 @@ func NewUTXOState(
func NewMeteredUTXOState(
db database.Database,
codec codec.Manager,
metrics metric.Registerer,
trackChecksum bool,
) (UTXOState, error) {
@@ -135,8 +132,6 @@ func NewMeteredUTXOState(
}
s := &utxoState{
codec: codec,
utxoCache: utxoCache,
utxoDB: prefixdb.New(utxoPrefix, db),
@@ -165,9 +160,10 @@ func (s *utxoState) GetUTXO(utxoID ids.ID) (*UTXO, error) {
return nil, err
}
// The key was in the database
utxo := &UTXO{}
if _, err := s.codec.Unmarshal(bytes, utxo); err != nil {
// The key was in the database. Parse via the consumer-registered
// fx-aware ParseUTXO factory (set via RegisterParseUTXO at boot).
utxo, err := ParseUTXO(bytes)
if err != nil {
return nil, err
}
@@ -176,7 +172,9 @@ func (s *utxoState) GetUTXO(utxoID ids.ID) (*UTXO, error) {
}
func (s *utxoState) PutUTXO(utxo *UTXO) error {
utxoBytes, err := s.codec.Marshal(codecVersion, utxo)
// ZAP-native: same bytes flow on the wire and to disk. No
// separate codec.Marshal step.
utxoBytes, err := utxo.WireBytes()
if err != nil {
return err
}