mirror of
https://github.com/luxfi/node.git
synced 2026-10-30 15:24:05 +00:00
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.
191 lines
5.6 KiB
Go
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
|
|
}
|