Files
zeekay 7cf87b230b node-unified is the node
The module already called itself github.com/luxfi/node while containing no node
in Go: cmd/luxd was twenty-one lines that printed the size of a few types, and
the Go column of the matrix measured a VM plugin. A repo that fetches the thing
it claims to be has not replaced it, so the Go sources move in — 1515 files,
the daemon and everything under it — and the build reaches outside this module
for nothing.

Rust and C++ sit beside it unchanged and are still measured against it by the
differential. That is the point of one repo: the reference implementation and
the two ports are the same checkout, so parity is a thing you can run rather
than a thing you assert.

Verified: builds, and the closure carries no lux-private — checked at build time
rather than believed. The daemon starts twelve chains on a local network
(P X C Q Z A B G K M F, plus D through its plugin) and reports healthy.
2026-09-07 16:15:25 -07:00

249 lines
6.9 KiB
Go

// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package lux
import (
"bytes"
"errors"
"sort"
"github.com/luxfi/crypto/secp256k1"
"github.com/luxfi/ids"
"github.com/luxfi/node/vms/components/verify"
"github.com/luxfi/runtime"
"github.com/luxfi/util"
)
var (
ErrNilTransferableOutput = errors.New("nil transferable output is not valid")
ErrNilTransferableFxOutput = errors.New("nil transferable feature extension output is not valid")
ErrOutputsNotSorted = errors.New("outputs not sorted")
ErrNilTransferableInput = errors.New("nil transferable input is not valid")
ErrNilTransferableFxInput = errors.New("nil transferable feature extension input is not valid")
ErrInputsNotSortedUnique = errors.New("inputs not sorted and unique")
_ verify.Verifiable = (*TransferableOutput)(nil)
_ verify.Verifiable = (*TransferableInput)(nil)
_ utils.Sortable[*TransferableInput] = (*TransferableInput)(nil)
)
// Amounter is a data structure that has an amount of something associated with it
type Amounter interface {
// Amount returns how much value this element represents of the asset in its
// transaction.
Amount() uint64
}
// Coster is a data structure that has a cost associated with it
type Coster interface {
// Cost returns how much this element costs to be included in its
// transaction.
Cost() (uint64, error)
}
// TransferableIn is the interface a feature extension must provide to transfer
// value between features extensions.
type TransferableIn interface {
verify.Verifiable
Amounter
Coster
}
// TransferableOut is the interface a feature extension must provide to transfer
// value between features extensions.
type TransferableOut interface {
verify.State
Amounter
InitRuntime(*runtime.Runtime)
}
type TransferableOutput struct {
Asset `serialize:"true"`
// FxID has serialize false because we don't want this to be encoded in bytes
FxID ids.ID `serialize:"false" json:"fxID"`
Out TransferableOut `serialize:"true" json:"output"`
}
func (out *TransferableOutput) InitRuntime(rt *runtime.Runtime) {
out.Out.InitRuntime(rt)
}
// Output returns the feature extension output that this Output is using.
func (out *TransferableOutput) Output() TransferableOut {
return out.Out
}
func (out *TransferableOutput) Verify() error {
switch {
case out == nil:
return ErrNilTransferableOutput
case out.Out == nil:
return ErrNilTransferableFxOutput
default:
return verify.All(&out.Asset, out.Out)
}
}
// wireBytesOrNil returns the native ZAP wire envelope of a TransferableOut
// when the inner fx primitive carries a Bytes() adapter (every production
// fx does). This is the single source of truth for canonical ordering —
// the same bytes that hit disk and the wire, no separate codec marshal.
func wireBytesOrNil(out TransferableOut) []byte {
if ws, ok := out.(interface{ Bytes() []byte }); ok {
return ws.Bytes()
}
return nil
}
type innerSortTransferableOutputs struct {
outs []*TransferableOutput
}
func (outs *innerSortTransferableOutputs) Less(i, j int) bool {
iOut := outs.outs[i]
jOut := outs.outs[j]
iAssetID := iOut.AssetID()
jAssetID := jOut.AssetID()
switch bytes.Compare(iAssetID[:], jAssetID[:]) {
case -1:
return true
case 1:
return false
}
return bytes.Compare(wireBytesOrNil(iOut.Out), wireBytesOrNil(jOut.Out)) == -1
}
func (outs *innerSortTransferableOutputs) Len() int {
return len(outs.outs)
}
func (outs *innerSortTransferableOutputs) Swap(i, j int) {
o := outs.outs
o[j], o[i] = o[i], o[j]
}
// 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) bool {
return sort.IsSorted(&innerSortTransferableOutputs{outs: outs})
}
type TransferableInput struct {
UTXOID `serialize:"true"`
Asset `serialize:"true"`
// FxID has serialize false because we don't want this to be encoded in bytes
FxID ids.ID `serialize:"false" json:"fxID"`
In TransferableIn `serialize:"true" json:"input"`
}
// Input returns the feature extension input that this Input is using.
func (in *TransferableInput) Input() TransferableIn {
return in.In
}
func (in *TransferableInput) Verify() error {
switch {
case in == nil:
return ErrNilTransferableInput
case in.In == nil:
return ErrNilTransferableFxInput
default:
return verify.All(&in.UTXOID, &in.Asset, in.In)
}
}
func (in *TransferableInput) InitRuntime(rt *runtime.Runtime) {
if contextInput, ok := in.In.(interface{ InitRuntime(*runtime.Runtime) }); ok {
contextInput.InitRuntime(rt)
}
}
func (in *TransferableInput) Compare(other *TransferableInput) int {
return in.UTXOID.Compare(&other.UTXOID)
}
type innerSortTransferableInputsWithSigners struct {
ins []*TransferableInput
signers [][]*secp256k1.PrivateKey
}
func (ins *innerSortTransferableInputsWithSigners) Less(i, j int) bool {
iID, iIndex := ins.ins[i].InputSource()
jID, jIndex := ins.ins[j].InputSource()
switch bytes.Compare(iID[:], jID[:]) {
case -1:
return true
case 0:
return iIndex < jIndex
default:
return false
}
}
func (ins *innerSortTransferableInputsWithSigners) Len() int {
return len(ins.ins)
}
func (ins *innerSortTransferableInputsWithSigners) Swap(i, j int) {
ins.ins[j], ins.ins[i] = ins.ins[i], ins.ins[j]
ins.signers[j], ins.signers[i] = ins.signers[i], ins.signers[j]
}
// SortTransferableInputsWithSigners sorts the inputs and signers based on the
// input's utxo ID
func SortTransferableInputsWithSigners(ins []*TransferableInput, signers [][]*secp256k1.PrivateKey) {
sort.Sort(&innerSortTransferableInputsWithSigners{ins: ins, signers: signers})
}
// VerifyTx verifies that the inputs and outputs flowcheck, including a fee.
// Additionally, this verifies that the inputs and outputs are sorted.
func VerifyTx(
feeAmount uint64,
feeAssetID ids.ID,
allIns [][]*TransferableInput,
allOuts [][]*TransferableOutput,
) error {
fc := NewFlowChecker()
fc.Produce(feeAssetID, feeAmount) // The txFee must be burned
// Add all the outputs to the flow checker and make sure they are sorted
for _, outs := range allOuts {
for _, out := range outs {
if err := out.Verify(); err != nil {
return err
}
fc.Produce(out.AssetID(), out.Output().Amount())
}
if !IsSortedTransferableOutputs(outs) {
return ErrOutputsNotSorted
}
}
// Add all the inputs to the flow checker and make sure they are sorted
for _, ins := range allIns {
for _, in := range ins {
if err := in.Verify(); err != nil {
return err
}
fc.Consume(in.AssetID(), in.Input().Amount())
}
if !utils.IsSortedAndUnique(ins) {
return ErrInputsNotSortedUnique
}
}
return fc.Verify()
}