Files
node/vms/platformvm/txs/operation.go
T
Hanzo AI b4f240d285 vms/platformvm: rip luxfi/codec via pcodecs (wave 2D — final)
Thirty platformvm files migrated. Every codec.Manager / codec.Registry /
linearcodec / wrappers / zapcodec reference inside vms/platformvm now
flows through node/vms/pcodecs, completing wave 2D of the codec rip
(#101). zero direct luxfi/codec imports remain anywhere in node/vms
outside vms/pcodecs/{pcodecs.go,pcodecsmock/manager.go}.

Codec construction sites (singletons preserved for external API
compatibility — wallet, indexer, network — pcodecs is the construction
layer only):
  platformvm/txs/codec.go       — V0+V1 linearcodec + V2 zapcodec
                                   via pcodecs.NewLinearCodec /
                                   NewZAPCodec / NewDefaultManager /
                                   NewMaxInt32Manager helpers
  platformvm/block/codec.go     — v1 + v0 read-only + GenesisCodec
                                   via pcodecs helpers
  platformvm/warp/codec.go      — Codec via pcodecs.NewMaxIntManager
  platformvm/warp/message/codec.go — Codec via pcodecs.NewMaxIntManager
  platformvm/warp/payload/codec.go — Codec via pcodecs.NewManager
  platformvm/state/metadata_codec.go — MetadataCodec via pcodecs

Production code typed through pcodecs:
  platformvm/txs/{tx,initial_state,operation}.go — codec.Manager →
                                                   pcodecs.Manager
  platformvm/txs/fee/complexity.go — wrappers.{LongLen,IntLen,ShortLen,
                                     ByteLen} + codec.VersionSize →
                                     pcodecs.{LongLen,IntLen,ShortLen,
                                     ByteLen,VersionSize}
  platformvm/state/{state,codec_helpers,metadata_validator}.go —
                                   pcodecs.Manager / pcodecs.Registry /
                                   pcodecs.{VersionSize,LongLen,IntLen,
                                   BoolLen}
  platformvm/block/{parse,v0/block}.go — pcodecs.Manager + LinearCodec
                                          + Errs
  platformvm/metrics/metrics.go — wrappers.Errs → pcodecs.Errs
  platformvm/vm.go — codec.Registry / linearcodec.NewDefault →
                     pcodecs.Registry / pcodecs.NewLinearCodec

Tests:
  platformvm/block/codec_multiversion_test.go — codec.ErrUnknownVersion
                                                 → pcodecs
  platformvm/state/{codec_helpers,metadata_validator,
                    metadata_delegator,state_v0_codec}_test.go —
                                   codec sentinel errors → pcodecs;
                                   linearcodec.NewDefault /
                                   codec.NewDefaultManager → pcodecs
                                   helpers
  platformvm/txs/{fee/complexity,tx_fuzz}_test.go — pcodecs.VersionSize
                                                    / NewLinearCodec /
                                                    Packer
  platformvm/warp/{message,unsigned_message}_test.go — pcodecs.ErrUnknownVersion
  platformvm/warp/message/payload_test.go — pcodecs.ErrUnknownVersion
  platformvm/warp/payload/{addressed_call,hash,payload}_test.go —
                                   pcodecs.ErrUnknownVersion

Build: go build ./vms/...
Tests: go test ./vms/platformvm/... — all green.

Pre-existing failures in vms/xvm (TestTxAcceptAfterParseTx,
TestIssueImportTx, TestIssueNFT, TestIssueProperty, TestVerifyFxUsage)
are unrelated to wave 2D — they fail on the baseline branch without any
change to xvm. Independent fix.

Wave 2D complete:
  - Components:  8 files migrated
  - Proposervm:  9 files migrated
  - EVM:         5 files migrated
  - XSVM:        2 files migrated
  - RPCchainvm:  1 file migrated
  - XVM:        18 files migrated
  - Platformvm: 30 files migrated
  --
  Total:        73 files migrated to pcodecs.

vms/pcodecs is the single canonical construction site.
2026-06-06 02:55:23 -07:00

131 lines
4.0 KiB
Go

// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package txs
import (
"bytes"
"errors"
"sort"
"github.com/luxfi/crypto/secp256k1"
"github.com/luxfi/node/vms/components/verify"
"github.com/luxfi/node/vms/pcodecs"
"github.com/luxfi/utils"
lux "github.com/luxfi/utxo"
"github.com/luxfi/utxo/secp256k1fx"
)
// Operation expresses an action against a previously-minted asset's UTXOs
// (transfer, mint, burn, NFT-style state op). PlatformVM exposes only the
// secp256k1fx Fx so [Op] is a secp256k1fx-compatible verifiable input.
type Operation struct {
lux.Asset `serialize:"true"`
// UTXOIDs of UTXOs this operation consumes.
UTXOIDs []*lux.UTXOID `serialize:"true" json:"inputIDs"`
// Op is the action to perform. On P-Chain this is always a
// secp256k1fx-flavoured operation; the codec keeps the door open for
// additional Fx flavours by storing a verify.Verifiable.
Op verify.Verifiable `serialize:"true" json:"operation"`
}
var (
ErrNilOperation = errors.New("nil operation is not valid")
ErrNilFxOperation = errors.New("nil fx operation is not valid")
ErrNotSortedAndUniqueUTXOIDs = errors.New("utxo ids on operation are not sorted and unique")
// ErrUnsupportedOpType pins [Op] to the single Fx PlatformVM ships
// (secp256k1fx). Anything else — propertyfx, nftfx, a future Fx an
// attacker tries to slip in — is rejected at syntactic verification
// time before any state-modifying executor sees it.
ErrUnsupportedOpType = errors.New("operation op type is not supported on PlatformVM (secp256k1fx only)")
)
// Verify returns nil iff this Operation is well formed.
func (op *Operation) Verify() error {
switch {
case op == nil:
return ErrNilOperation
case op.Op == nil:
return ErrNilFxOperation
case !utils.IsSortedAndUnique(op.UTXOIDs):
return ErrNotSortedAndUniqueUTXOIDs
}
if _, ok := op.Op.(*secp256k1fx.MintOperation); !ok {
return ErrUnsupportedOpType
}
return verify.All(&op.Asset, op.Op)
}
// secp256k1fx is the only Fx on PlatformVM. The compile-time check guards
// against drift if a developer tries to wire a non-secp256k1fx operation in.
var _ = secp256k1fx.ID
// SortOperations sorts the given operations by their codec-marshalled bytes.
type operationAndCodec struct {
op *Operation
codec pcodecs.Manager
}
func (o *operationAndCodec) Compare(other *operationAndCodec) int {
oBytes, err := o.codec.Marshal(CodecVersion, o.op)
if err != nil {
return 0
}
otherBytes, err := o.codec.Marshal(CodecVersion, other.op)
if err != nil {
return 0
}
return bytes.Compare(oBytes, otherBytes)
}
func SortOperations(ops []*Operation, c pcodecs.Manager) {
wrapped := make([]*operationAndCodec, len(ops))
for i, op := range ops {
wrapped[i] = &operationAndCodec{op: op, codec: c}
}
utils.Sort(wrapped)
for i, w := range wrapped {
ops[i] = w.op
}
}
// IsSortedAndUniqueOperations reports whether [ops] is sorted by codec bytes
// and contains no duplicates.
func IsSortedAndUniqueOperations(ops []*Operation, c pcodecs.Manager) bool {
wrapped := make([]*operationAndCodec, len(ops))
for i, op := range ops {
wrapped[i] = &operationAndCodec{op: op, codec: c}
}
return utils.IsSortedAndUnique(wrapped)
}
type innerSortOperationsWithSigners struct {
ops []*Operation
signers [][]*secp256k1.PrivateKey
codec pcodecs.Manager
}
func (s *innerSortOperationsWithSigners) Less(i, j int) bool {
iBytes, err := s.codec.Marshal(CodecVersion, s.ops[i])
if err != nil {
return false
}
jBytes, err := s.codec.Marshal(CodecVersion, s.ops[j])
if err != nil {
return false
}
return bytes.Compare(iBytes, jBytes) == -1
}
func (s *innerSortOperationsWithSigners) Len() int { return len(s.ops) }
func (s *innerSortOperationsWithSigners) Swap(i, j int) {
s.ops[j], s.ops[i] = s.ops[i], s.ops[j]
s.signers[j], s.signers[i] = s.signers[i], s.signers[j]
}
func SortOperationsWithSigners(ops []*Operation, signers [][]*secp256k1.PrivateKey, c pcodecs.Manager) {
sort.Sort(&innerSortOperationsWithSigners{ops: ops, signers: signers, codec: c})
}