mirror of
https://github.com/luxfi/utxo.git
synced 2026-10-30 15:13:57 +00:00
118 lines
4.8 KiB
Go
118 lines
4.8 KiB
Go
// Copyright (C) 2026, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
// Package wire is the cross-VM ZAP-native wire format for the fxs
|
|
// (feature-extension) primitives shared by P-chain (platformvm) and
|
|
// X-chain (xvm). It replaces the legacy luxfi/codec.Manager registry
|
|
// with a (TypeKind, ShapeKind) discriminator pair on every primitive's
|
|
// wire envelope.
|
|
//
|
|
// Activation: 2025-12-25T16:20:00-08:00 (LP-023). New final Lux network.
|
|
// No backwards compatibility — legacy codec-encoded fxs primitives on the
|
|
// wire are a protocol-violation reject after activation.
|
|
//
|
|
// Wire envelope per primitive: [TypeKind:1][ShapeKind:1][ZAP message: N].
|
|
//
|
|
// The TypeKind byte names the fx family (secp256k1fx, mldsafx, ...) and
|
|
// the ShapeKind byte names the primitive shape within that family
|
|
// (TransferOutput, TransferInput, MintOutput, MintOperation, Credential,
|
|
// AttestationOutput, AttestationInput, OutputOwners). The remaining
|
|
// bytes are a ZAP message describing the shape's fields.
|
|
//
|
|
// This is decomposed from the legacy codec.Manager slot map (which
|
|
// braided "is this an Output? is this an Input? which fx?" into a
|
|
// single dense uint32 slot id). TypeKind + ShapeKind separates the two
|
|
// values cleanly — composition over inheritance.
|
|
package wire
|
|
|
|
import "errors"
|
|
|
|
// TypeKind names the fx family that owns the primitive. Dense values,
|
|
// 0x00 reserved (rejected by every Wrap*).
|
|
type TypeKind uint8
|
|
|
|
const (
|
|
TypeKindReserved TypeKind = 0x00
|
|
TypeKindSecp256k1 TypeKind = 0x01
|
|
TypeKindMLDSA TypeKind = 0x02
|
|
TypeKindSLHDSA TypeKind = 0x03
|
|
TypeKindEd25519 TypeKind = 0x04
|
|
TypeKindSecp256r1 TypeKind = 0x05
|
|
TypeKindSchnorr TypeKind = 0x06
|
|
TypeKindBLS12381 TypeKind = 0x07
|
|
)
|
|
|
|
// ShapeKind names the primitive shape within a fx family. Dense values,
|
|
// 0x00 reserved.
|
|
type ShapeKind uint8
|
|
|
|
const (
|
|
ShapeKindReserved ShapeKind = 0x00
|
|
ShapeKindTransferOutput ShapeKind = 0x01
|
|
ShapeKindTransferInput ShapeKind = 0x02
|
|
ShapeKindMintOutput ShapeKind = 0x03
|
|
ShapeKindMintInput ShapeKind = 0x04
|
|
ShapeKindMintOperation ShapeKind = 0x05
|
|
ShapeKindCredential ShapeKind = 0x06
|
|
ShapeKindAttestationOut ShapeKind = 0x07
|
|
ShapeKindAttestationIn ShapeKind = 0x08
|
|
ShapeKindOutputOwners ShapeKind = 0x09
|
|
ShapeKindUTXO ShapeKind = 0x0A
|
|
ShapeKindTransferableOut ShapeKind = 0x0B
|
|
ShapeKindTransferableIn ShapeKind = 0x0C
|
|
ShapeKindPChainOwner ShapeKind = 0x0D
|
|
ShapeKindSignedTx ShapeKind = 0x0E
|
|
ShapeKindLockedOutput ShapeKind = 0x0F
|
|
ShapeKindXVMBaseTx ShapeKind = 0x12
|
|
)
|
|
|
|
// Errors returned by every Wrap*Primitive accessor when the discriminator
|
|
// pair on the wire does not match the expected (TypeKind, ShapeKind) for
|
|
// the function being called. This closes the cross-type confusion surface
|
|
// where a TransferInput buffer could be Wrap'd as a TransferOutput and
|
|
// return garbage-but-deterministic field reads.
|
|
var (
|
|
ErrWrongTypeKind = errors.New("wire: TypeKind discriminator does not match expected fx family")
|
|
ErrWrongShapeKind = errors.New("wire: ShapeKind discriminator does not match expected primitive shape")
|
|
ErrShortEnvelope = errors.New("wire: envelope shorter than 2-byte discriminator prefix")
|
|
)
|
|
|
|
// EnvelopePrefix is the 2-byte discriminator prefix that every fxs
|
|
// primitive's wire envelope begins with. The remainder of the envelope
|
|
// is a ZAP message describing the shape's fields.
|
|
const EnvelopePrefix = 2
|
|
|
|
// readEnvelopePrefix parses the (TypeKind, ShapeKind) prefix from a wire
|
|
// buffer and returns the post-prefix ZAP slice. Returns ErrShortEnvelope
|
|
// when the buffer is shorter than 2 bytes.
|
|
func readEnvelopePrefix(b []byte) (TypeKind, ShapeKind, []byte, error) {
|
|
if len(b) < EnvelopePrefix {
|
|
return 0, 0, nil, ErrShortEnvelope
|
|
}
|
|
return TypeKind(b[0]), ShapeKind(b[1]), b[EnvelopePrefix:], nil
|
|
}
|
|
|
|
// PeekDiscriminator returns the (TypeKind, ShapeKind) of a wire envelope
|
|
// without consuming the ZAP body. Used by composite-shape dispatchers
|
|
// (e.g. LockedOutput) that need to recurse on an inner envelope's
|
|
// discriminator without committing to a Wrap*. Returns ErrShortEnvelope
|
|
// when the buffer is shorter than the 2-byte prefix.
|
|
func PeekDiscriminator(b []byte) (TypeKind, ShapeKind, error) {
|
|
tk, sk, _, err := readEnvelopePrefix(b)
|
|
return tk, sk, err
|
|
}
|
|
|
|
// writeEnvelopePrefix prepends a (TypeKind, ShapeKind) discriminator to a
|
|
// ZAP message and returns the concatenated wire envelope.
|
|
//
|
|
// Allocates a single fresh buffer of size 2 + len(zapBytes). Callers
|
|
// that hold the ZAP bytes by reference must not retain ownership of the
|
|
// input slice — the returned slice is the canonical wire envelope.
|
|
func writeEnvelopePrefix(tk TypeKind, sk ShapeKind, zapBytes []byte) []byte {
|
|
out := make([]byte, EnvelopePrefix+len(zapBytes))
|
|
out[0] = byte(tk)
|
|
out[1] = byte(sk)
|
|
copy(out[EnvelopePrefix:], zapBytes)
|
|
return out
|
|
}
|