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

128 lines
4.0 KiB
Go

// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package stakeable
import (
"errors"
"fmt"
luxcomp "github.com/luxfi/node/vms/components/lux"
"github.com/luxfi/runtime"
lux "github.com/luxfi/utxo"
"github.com/luxfi/utxo/wire"
"github.com/luxfi/vm/components/verify"
)
var (
errInvalidLocktime = errors.New("invalid locktime")
errNestedStakeableLocks = errors.New("shouldn't nest stakeable locks")
)
// init registers the LockedOutput wire handler with the cross-fx
// dispatcher in node/vms/components/lux. Without this registration the
// dispatcher returns "unknown (TypeKind=0x00, ShapeKind=0x0F)" for every
// vesting-allocation UTXO in genesis — observed live as
// `platform.getBalance = 0` for every funded P-chain address on
// mainnet/testnet/devnet despite the genesis allocations being
// well-formed and the supply tally matching the sum of allocations.
//
// The cycle that forced this registration pattern: stakeable.LockOut
// embeds utxo.TransferableOut so node/vms/components/lux cannot import
// stakeable directly (would import the embed source through the embed,
// breaking the dispatcher package's "no fx-specific deps" property).
// The registration lets stakeable own the *LockOut reconstruction
// without flipping the dependency graph.
func init() {
luxcomp.RegisterLockedOutputHandler(func(b []byte) (verify.State, error) {
lo, err := wire.WrapLockedOutput(b)
if err != nil {
return nil, fmt.Errorf("wire.WrapLockedOutput: %w", err)
}
innerState, err := luxcomp.WrapOutputBytes(lo.TransferOutBytes())
if err != nil {
return nil, fmt.Errorf("inner output dispatch: %w", err)
}
inner, ok := innerState.(lux.TransferableOut)
if !ok {
return nil, fmt.Errorf("inner state is %T, not lux.TransferableOut — LockOut envelope corrupted", innerState)
}
return &LockOut{
Locktime: lo.Locktime(),
TransferableOut: inner,
}, nil
})
}
type LockOut struct {
Locktime uint64 `json:"locktime"`
lux.TransferableOut `json:"output"`
}
func (s *LockOut) InitRuntime(rt *runtime.Runtime) {
// Initialize the context for the underlying output if it supports it
if contextOutput, ok := s.TransferableOut.(interface{ InitRuntime(*runtime.Runtime) }); ok {
contextOutput.InitRuntime(rt)
}
}
func (s *LockOut) Addresses() [][]byte {
if addressable, ok := s.TransferableOut.(lux.Addressable); ok {
return addressable.Addresses()
}
return nil
}
func (s *LockOut) Verify() error {
if s.Locktime == 0 {
return errInvalidLocktime
}
if _, nested := s.TransferableOut.(*LockOut); nested {
return errNestedStakeableLocks
}
return s.TransferableOut.Verify()
}
// Bytes returns the ZAP-native wire envelope for this LockOut.
// Envelope = (TypeKindReserved, ShapeKindLockedOutput, ZAP message)
// where the ZAP message carries Locktime and the inner TransferableOut's
// own wire envelope (which carries its own TypeKind+ShapeKind+message).
//
// The inner TransferableOut MUST satisfy the wireSerializable contract
// (Bytes() []byte) — every fx package's wire.go adapter does. Nested
// LockOut is forbidden by Verify() and panics here to surface the bug.
func (s *LockOut) Bytes() []byte {
type wireSerializable interface {
Bytes() []byte
}
inner, ok := s.TransferableOut.(wireSerializable)
if !ok {
panic("stakeable.LockOut: inner TransferableOut does not implement wire-serializable (Bytes() []byte)")
}
return wire.NewLockedOutput(wire.LockedOutputInput{
Locktime: s.Locktime,
TransferOutBytes: inner.Bytes(),
})
}
type LockIn struct {
Locktime uint64 `json:"locktime"`
lux.TransferableIn `json:"input"`
}
func (s *LockIn) Verify() error {
if s.Locktime == 0 {
return errInvalidLocktime
}
if _, nested := s.TransferableIn.(*LockIn); nested {
return errNestedStakeableLocks
}
return s.TransferableIn.Verify()
}
func (s *LockIn) InitRuntime(rt *runtime.Runtime) {
if contextInput, ok := s.TransferableIn.(interface{ InitRuntime(*runtime.Runtime) }); ok {
contextInput.InitRuntime(rt)
}
}