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

191 lines
5.6 KiB
Go

// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package xvm
import (
"cmp"
"fmt"
"github.com/luxfi/address"
"github.com/luxfi/ids"
"github.com/luxfi/node/vms/xvm/txs"
"github.com/luxfi/util"
lux "github.com/luxfi/utxo"
"github.com/luxfi/utxo/secp256k1fx"
)
// Genesis represents the genesis state of the XVM
type Genesis struct {
Txs []*GenesisAsset `serialize:"true"`
}
// GenesisAsset represents an asset in the genesis block
type GenesisAsset struct {
Alias string `serialize:"true"`
txs.CreateAssetTx `serialize:"true"`
}
// Compare implements utils.Sortable for GenesisAsset
func (g *GenesisAsset) Compare(other *GenesisAsset) int {
return cmp.Compare(g.Alias, other.Alias)
}
// AssetInitialState describes the initial state of an asset
type AssetInitialState struct {
FixedCap []GenesisHolder
VariableCap []GenesisOwners
}
// GenesisAssetDefinition describes a genesis asset and its initial state
type GenesisAssetDefinition struct {
Name string
Symbol string
Denomination byte
InitialState AssetInitialState
Memo []byte
}
// GenesisHolder describes how much asset is owned by an address
type GenesisHolder struct {
Amount uint64
Address string
}
// GenesisOwners describes who can perform an action
type GenesisOwners struct {
Threshold uint32
Minters []string
}
// NewGenesis creates a new Genesis from genesis data
func NewGenesis(
networkID uint32,
genesisData map[string]GenesisAssetDefinition,
) (*Genesis, error) {
g := &Genesis{}
for assetAlias, assetDefinition := range genesisData {
asset := GenesisAsset{
Alias: assetAlias,
CreateAssetTx: txs.CreateAssetTx{
BaseTx: txs.BaseTx{BaseTx: lux.BaseTx{
NetworkID: networkID,
BlockchainID: ids.Empty,
Memo: assetDefinition.Memo,
}},
Name: assetDefinition.Name,
Symbol: assetDefinition.Symbol,
Denomination: assetDefinition.Denomination,
},
}
initialState := &txs.InitialState{
FxIndex: 0, // secp256k1fx
}
for _, holder := range assetDefinition.InitialState.FixedCap {
_, addrbuff, err := address.ParseBech32(holder.Address)
if err != nil {
return nil, fmt.Errorf("problem parsing holder address: %w", err)
}
addr, err := ids.ToShortID(addrbuff)
if err != nil {
return nil, fmt.Errorf("problem parsing holder address: %w", err)
}
initialState.Outs = append(initialState.Outs, &secp256k1fx.TransferOutput{
Amt: holder.Amount,
OutputOwners: secp256k1fx.OutputOwners{
Threshold: 1,
Addrs: []ids.ShortID{addr},
},
})
}
for _, owners := range assetDefinition.InitialState.VariableCap {
out := &secp256k1fx.MintOutput{
OutputOwners: secp256k1fx.OutputOwners{
Threshold: owners.Threshold,
},
}
for _, addrStr := range owners.Minters {
_, addrBytes, err := address.ParseBech32(addrStr)
if err != nil {
return nil, fmt.Errorf("problem parsing minters address: %w", err)
}
addr, err := ids.ToShortID(addrBytes)
if err != nil {
return nil, fmt.Errorf("problem parsing minters address: %w", err)
}
out.Addrs = append(out.Addrs, addr)
}
out.Sort()
initialState.Outs = append(initialState.Outs, out)
}
if len(initialState.Outs) > 0 {
initialState.Sort()
asset.States = append(asset.States, initialState)
}
utils.Sort(asset.States)
g.Txs = append(g.Txs, &asset)
}
utils.Sort(g.Txs)
return g, nil
}
// Bytes serializes the Genesis to its canonical native-ZAP bytes.
func (g *Genesis) Bytes() ([]byte, error) {
return marshalGenesis(g)
}
// ParseGenesisBytes decodes the canonical XVM genesis bytes produced by
// (*Genesis).Bytes() back into a *Genesis with each GenesisAsset's
// embedded CreateAssetTx initialised against the genesis codec. After
// Initialize, each tx's deterministic ID (tx.ID()) is the runtime asset
// ID of that genesis-minted asset — i.e. the same value vm.initGenesis
// computes when bootstrapping the X-Chain.
//
// Callers (genesis/builder, config/getGenesisData) use this to derive
// the X-Chain native asset ID from genesis content rather than the
// network-id-keyed constants.UTXOAssetIDFor(networkID). On sovereign
// L1s those two values DIFFER — the wallet
// builder context's UTXOAssetID must be the genesis-derived one or every
// fee-paying tx fails with "insufficient funds, needs N more µLUX".
func ParseGenesisBytes(genesisBytes []byte) (*Genesis, error) {
g, err := parseGenesis(genesisBytes)
if err != nil {
return nil, err
}
for i := range g.Txs {
tx := &txs.Tx{Unsigned: &g.Txs[i].CreateAssetTx}
if err := tx.Initialize(); err != nil {
return nil, fmt.Errorf("initialize genesis asset %d (%s): %w", i, g.Txs[i].Alias, err)
}
}
return g, nil
}
// AssetIDFromGenesisBytes returns the first genesis asset's runtime
// asset ID — the ID vm.initGenesis assigns to genesis.Txs[0]. This is
// the X-Chain native fee asset by convention (the same asset the
// platform-vm reports via platform.getStakingAssetID and the wallet
// builder context's UTXOAssetID).
//
// Returns an error when genesisBytes is malformed or contains zero
// assets — both are unrecoverable on a primary-network bootstrap.
func AssetIDFromGenesisBytes(genesisBytes []byte) (ids.ID, error) {
g, err := ParseGenesisBytes(genesisBytes)
if err != nil {
return ids.Empty, err
}
if len(g.Txs) == 0 {
return ids.Empty, fmt.Errorf("xvm genesis has zero asset txs")
}
tx := &txs.Tx{Unsigned: &g.Txs[0].CreateAssetTx}
if err := tx.Initialize(); err != nil {
return ids.Empty, fmt.Errorf("initialize first genesis asset (%s): %w", g.Txs[0].Alias, err)
}
return tx.ID(), nil
}