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.
462 lines
10 KiB
Go
462 lines
10 KiB
Go
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package executor
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/luxfi/database"
|
|
"github.com/luxfi/metric"
|
|
|
|
"github.com/luxfi/database/memdb"
|
|
|
|
"github.com/luxfi/database/versiondb"
|
|
|
|
"github.com/luxfi/ids"
|
|
|
|
"github.com/luxfi/constants"
|
|
|
|
"github.com/luxfi/crypto/secp256k1"
|
|
|
|
"github.com/luxfi/node/vms/xvm/block"
|
|
|
|
"github.com/luxfi/node/vms/xvm/fxs"
|
|
|
|
"github.com/luxfi/node/vms/xvm/state"
|
|
|
|
"github.com/luxfi/node/vms/xvm/txs"
|
|
|
|
lux "github.com/luxfi/utxo"
|
|
|
|
"github.com/luxfi/node/vms/components/verify"
|
|
|
|
"github.com/luxfi/utxo/secp256k1fx"
|
|
)
|
|
|
|
const trackChecksums = false
|
|
|
|
var (
|
|
chainID = ids.ID{5, 4, 3, 2, 1}
|
|
assetID = ids.ID{1, 2, 3}
|
|
)
|
|
|
|
func TestBaseTxExecutor(t *testing.T) {
|
|
require := require.New(t)
|
|
|
|
secpFx := &secp256k1fx.Fx{}
|
|
parser, err := block.NewParser(
|
|
[]fxs.Fx{secpFx},
|
|
)
|
|
require.NoError(err)
|
|
|
|
db := memdb.New()
|
|
vdb := versiondb.New(db)
|
|
registerer := metric.NewNoOp().Registry()
|
|
state, err := state.New(vdb, parser, registerer, trackChecksums)
|
|
require.NoError(err)
|
|
|
|
utxoID := lux.UTXOID{
|
|
TxID: ids.GenerateTestID(),
|
|
OutputIndex: 1,
|
|
}
|
|
|
|
addr := keys[0].Address()
|
|
utxo := &lux.UTXO{
|
|
UTXOID: utxoID,
|
|
Asset: lux.Asset{ID: assetID},
|
|
Out: &secp256k1fx.TransferOutput{
|
|
Amt: 20 * constants.KiloLux,
|
|
OutputOwners: secp256k1fx.OutputOwners{
|
|
Threshold: 1,
|
|
Addrs: []ids.ShortID{
|
|
addr,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
// Populate the UTXO that we will be consuming
|
|
state.AddUTXO(utxo)
|
|
require.NoError(state.Commit())
|
|
|
|
baseTx := &txs.Tx{Unsigned: &txs.BaseTx{BaseTx: lux.BaseTx{
|
|
NetworkID: constants.UnitTestID,
|
|
BlockchainID: chainID,
|
|
Ins: []*lux.TransferableInput{{
|
|
UTXOID: utxoID,
|
|
Asset: lux.Asset{ID: assetID},
|
|
In: &secp256k1fx.TransferInput{
|
|
Amt: 20 * constants.KiloLux,
|
|
Input: secp256k1fx.Input{
|
|
SigIndices: []uint32{
|
|
0,
|
|
},
|
|
},
|
|
},
|
|
}},
|
|
Outs: []*lux.TransferableOutput{{
|
|
Asset: lux.Asset{ID: assetID},
|
|
Out: &secp256k1fx.TransferOutput{
|
|
Amt: 10 * constants.KiloLux,
|
|
OutputOwners: secp256k1fx.OutputOwners{
|
|
Threshold: 1,
|
|
Addrs: []ids.ShortID{addr},
|
|
},
|
|
},
|
|
}},
|
|
}}}
|
|
require.NoError(baseTx.SignSECP256K1Fx([][]*secp256k1.PrivateKey{{keys[0]}}))
|
|
|
|
executor := &Executor{
|
|
State: state,
|
|
Tx: baseTx,
|
|
}
|
|
|
|
// Execute baseTx
|
|
require.NoError(baseTx.Unsigned.Visit(executor))
|
|
|
|
// Verify the consumed UTXO was removed from the state
|
|
_, err = executor.State.GetUTXO(utxoID.InputID())
|
|
require.ErrorIs(err, database.ErrNotFound)
|
|
|
|
// Verify the produced UTXO was added to the state
|
|
expectedOutputUTXO := &lux.UTXO{
|
|
UTXOID: lux.UTXOID{
|
|
TxID: baseTx.TxID,
|
|
OutputIndex: 0,
|
|
},
|
|
Asset: lux.Asset{
|
|
ID: assetID,
|
|
},
|
|
Out: &secp256k1fx.TransferOutput{
|
|
Amt: 10 * constants.KiloLux,
|
|
OutputOwners: secp256k1fx.OutputOwners{
|
|
Threshold: 1,
|
|
Addrs: []ids.ShortID{addr},
|
|
},
|
|
},
|
|
}
|
|
expectedOutputUTXOID := expectedOutputUTXO.InputID()
|
|
outputUTXO, err := executor.State.GetUTXO(expectedOutputUTXOID)
|
|
require.NoError(err)
|
|
|
|
outputUTXOID := outputUTXO.InputID()
|
|
require.Equal(expectedOutputUTXOID, outputUTXOID)
|
|
require.Equal(expectedOutputUTXO, outputUTXO)
|
|
}
|
|
|
|
func TestCreateAssetTxExecutor(t *testing.T) {
|
|
require := require.New(t)
|
|
|
|
secpFx := &secp256k1fx.Fx{}
|
|
parser, err := block.NewParser(
|
|
[]fxs.Fx{secpFx},
|
|
)
|
|
require.NoError(err)
|
|
|
|
db := memdb.New()
|
|
vdb := versiondb.New(db)
|
|
registerer := metric.NewNoOp().Registry()
|
|
state, err := state.New(vdb, parser, registerer, trackChecksums)
|
|
require.NoError(err)
|
|
|
|
utxoID := lux.UTXOID{
|
|
TxID: ids.GenerateTestID(),
|
|
OutputIndex: 1,
|
|
}
|
|
|
|
addr := keys[0].Address()
|
|
utxo := &lux.UTXO{
|
|
UTXOID: utxoID,
|
|
Asset: lux.Asset{ID: assetID},
|
|
Out: &secp256k1fx.TransferOutput{
|
|
Amt: 20 * constants.KiloLux,
|
|
OutputOwners: secp256k1fx.OutputOwners{
|
|
Threshold: 1,
|
|
Addrs: []ids.ShortID{
|
|
addr,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
// Populate the UTXO that we will be consuming
|
|
state.AddUTXO(utxo)
|
|
require.NoError(state.Commit())
|
|
|
|
createAssetTx := &txs.Tx{Unsigned: &txs.CreateAssetTx{
|
|
BaseTx: txs.BaseTx{BaseTx: lux.BaseTx{
|
|
NetworkID: constants.UnitTestID,
|
|
BlockchainID: chainID,
|
|
Ins: []*lux.TransferableInput{{
|
|
UTXOID: utxoID,
|
|
Asset: lux.Asset{ID: assetID},
|
|
In: &secp256k1fx.TransferInput{
|
|
Amt: 20 * constants.KiloLux,
|
|
Input: secp256k1fx.Input{
|
|
SigIndices: []uint32{
|
|
0,
|
|
},
|
|
},
|
|
},
|
|
}},
|
|
Outs: []*lux.TransferableOutput{{
|
|
Asset: lux.Asset{ID: assetID},
|
|
Out: &secp256k1fx.TransferOutput{
|
|
Amt: 10 * constants.KiloLux,
|
|
OutputOwners: secp256k1fx.OutputOwners{
|
|
Threshold: 1,
|
|
Addrs: []ids.ShortID{addr},
|
|
},
|
|
},
|
|
}},
|
|
}},
|
|
Name: "name",
|
|
Symbol: "symb",
|
|
Denomination: 0,
|
|
States: []*txs.InitialState{
|
|
{
|
|
FxIndex: 0,
|
|
Outs: []verify.State{
|
|
&secp256k1fx.MintOutput{
|
|
OutputOwners: secp256k1fx.OutputOwners{
|
|
Threshold: 1,
|
|
Addrs: []ids.ShortID{addr},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}}
|
|
require.NoError(createAssetTx.SignSECP256K1Fx([][]*secp256k1.PrivateKey{{keys[0]}}))
|
|
|
|
executor := &Executor{
|
|
State: state,
|
|
Tx: createAssetTx,
|
|
}
|
|
|
|
// Execute createAssetTx
|
|
require.NoError(createAssetTx.Unsigned.Visit(executor))
|
|
|
|
// Verify the consumed UTXO was removed from the state
|
|
_, err = executor.State.GetUTXO(utxoID.InputID())
|
|
require.ErrorIs(err, database.ErrNotFound)
|
|
|
|
// Verify the produced UTXOs were added to the state
|
|
txID := createAssetTx.ID()
|
|
expectedOutputUTXOs := []*lux.UTXO{
|
|
{
|
|
UTXOID: lux.UTXOID{
|
|
TxID: txID,
|
|
OutputIndex: 0,
|
|
},
|
|
Asset: lux.Asset{
|
|
ID: assetID,
|
|
},
|
|
Out: &secp256k1fx.TransferOutput{
|
|
Amt: 10 * constants.KiloLux,
|
|
OutputOwners: secp256k1fx.OutputOwners{
|
|
Threshold: 1,
|
|
Addrs: []ids.ShortID{addr},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
UTXOID: lux.UTXOID{
|
|
TxID: txID,
|
|
OutputIndex: 1,
|
|
},
|
|
Asset: lux.Asset{
|
|
ID: txID,
|
|
},
|
|
Out: &secp256k1fx.MintOutput{
|
|
OutputOwners: secp256k1fx.OutputOwners{
|
|
Threshold: 1,
|
|
Addrs: []ids.ShortID{addr},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
for _, expectedOutputUTXO := range expectedOutputUTXOs {
|
|
expectedOutputUTXOID := expectedOutputUTXO.InputID()
|
|
outputUTXO, err := executor.State.GetUTXO(expectedOutputUTXOID)
|
|
require.NoError(err)
|
|
|
|
outputUTXOID := outputUTXO.InputID()
|
|
require.Equal(expectedOutputUTXOID, outputUTXOID)
|
|
require.Equal(expectedOutputUTXO, outputUTXO)
|
|
}
|
|
}
|
|
|
|
func TestOperationTxExecutor(t *testing.T) {
|
|
require := require.New(t)
|
|
|
|
secpFx := &secp256k1fx.Fx{}
|
|
parser, err := block.NewParser(
|
|
[]fxs.Fx{secpFx},
|
|
)
|
|
require.NoError(err)
|
|
|
|
db := memdb.New()
|
|
vdb := versiondb.New(db)
|
|
registerer := metric.NewNoOp().Registry()
|
|
state, err := state.New(vdb, parser, registerer, trackChecksums)
|
|
require.NoError(err)
|
|
|
|
outputOwners := secp256k1fx.OutputOwners{
|
|
Threshold: 1,
|
|
Addrs: []ids.ShortID{
|
|
keys[0].Address(),
|
|
},
|
|
}
|
|
|
|
utxoID := lux.UTXOID{
|
|
TxID: ids.GenerateTestID(),
|
|
OutputIndex: 1,
|
|
}
|
|
utxo := &lux.UTXO{
|
|
UTXOID: utxoID,
|
|
Asset: lux.Asset{ID: assetID},
|
|
Out: &secp256k1fx.TransferOutput{
|
|
Amt: 20 * constants.KiloLux,
|
|
OutputOwners: outputOwners,
|
|
},
|
|
}
|
|
|
|
opUTXOID := lux.UTXOID{
|
|
TxID: ids.GenerateTestID(),
|
|
OutputIndex: 1,
|
|
}
|
|
opUTXO := &lux.UTXO{
|
|
UTXOID: opUTXOID,
|
|
Asset: lux.Asset{ID: assetID},
|
|
Out: &secp256k1fx.MintOutput{
|
|
OutputOwners: outputOwners,
|
|
},
|
|
}
|
|
|
|
// Populate the UTXOs that we will be consuming
|
|
state.AddUTXO(utxo)
|
|
state.AddUTXO(opUTXO)
|
|
require.NoError(state.Commit())
|
|
|
|
operationTx := &txs.Tx{Unsigned: &txs.OperationTx{
|
|
BaseTx: txs.BaseTx{BaseTx: lux.BaseTx{
|
|
NetworkID: constants.UnitTestID,
|
|
BlockchainID: chainID,
|
|
Ins: []*lux.TransferableInput{{
|
|
UTXOID: utxoID,
|
|
Asset: lux.Asset{ID: assetID},
|
|
In: &secp256k1fx.TransferInput{
|
|
Amt: 20 * constants.KiloLux,
|
|
Input: secp256k1fx.Input{
|
|
SigIndices: []uint32{
|
|
0,
|
|
},
|
|
},
|
|
},
|
|
}},
|
|
Outs: []*lux.TransferableOutput{{
|
|
Asset: lux.Asset{ID: assetID},
|
|
Out: &secp256k1fx.TransferOutput{
|
|
Amt: 10 * constants.KiloLux,
|
|
OutputOwners: outputOwners,
|
|
},
|
|
}},
|
|
}},
|
|
Ops: []*txs.Operation{{
|
|
Asset: lux.Asset{ID: assetID},
|
|
UTXOIDs: []*lux.UTXOID{
|
|
&opUTXOID,
|
|
},
|
|
Op: &secp256k1fx.MintOperation{
|
|
MintInput: secp256k1fx.Input{
|
|
SigIndices: []uint32{0},
|
|
},
|
|
MintOutput: secp256k1fx.MintOutput{
|
|
OutputOwners: outputOwners,
|
|
},
|
|
TransferOutput: secp256k1fx.TransferOutput{
|
|
Amt: 12345,
|
|
OutputOwners: outputOwners,
|
|
},
|
|
},
|
|
}},
|
|
}}
|
|
require.NoError(operationTx.SignSECP256K1Fx(
|
|
[][]*secp256k1.PrivateKey{
|
|
{keys[0]},
|
|
{keys[0]},
|
|
},
|
|
))
|
|
|
|
executor := &Executor{
|
|
State: state,
|
|
Tx: operationTx,
|
|
}
|
|
|
|
// Execute operationTx
|
|
require.NoError(operationTx.Unsigned.Visit(executor))
|
|
|
|
// Verify the consumed UTXOs were removed from the state
|
|
_, err = executor.State.GetUTXO(utxo.InputID())
|
|
require.ErrorIs(err, database.ErrNotFound)
|
|
_, err = executor.State.GetUTXO(opUTXO.InputID())
|
|
require.ErrorIs(err, database.ErrNotFound)
|
|
|
|
// Verify the produced UTXOs were added to the state
|
|
txID := operationTx.ID()
|
|
expectedOutputUTXOs := []*lux.UTXO{
|
|
{
|
|
UTXOID: lux.UTXOID{
|
|
TxID: txID,
|
|
OutputIndex: 0,
|
|
},
|
|
Asset: lux.Asset{
|
|
ID: assetID,
|
|
},
|
|
Out: &secp256k1fx.TransferOutput{
|
|
Amt: 10 * constants.KiloLux,
|
|
OutputOwners: outputOwners,
|
|
},
|
|
},
|
|
{
|
|
UTXOID: lux.UTXOID{
|
|
TxID: txID,
|
|
OutputIndex: 1,
|
|
},
|
|
Asset: lux.Asset{
|
|
ID: assetID,
|
|
},
|
|
Out: &secp256k1fx.MintOutput{
|
|
OutputOwners: outputOwners,
|
|
},
|
|
},
|
|
{
|
|
UTXOID: lux.UTXOID{
|
|
TxID: txID,
|
|
OutputIndex: 2,
|
|
},
|
|
Asset: lux.Asset{
|
|
ID: assetID,
|
|
},
|
|
Out: &secp256k1fx.TransferOutput{
|
|
Amt: 12345,
|
|
OutputOwners: outputOwners,
|
|
},
|
|
},
|
|
}
|
|
for _, expectedOutputUTXO := range expectedOutputUTXOs {
|
|
expectedOutputUTXOID := expectedOutputUTXO.InputID()
|
|
outputUTXO, err := executor.State.GetUTXO(expectedOutputUTXOID)
|
|
require.NoError(err)
|
|
|
|
outputUTXOID := outputUTXO.InputID()
|
|
require.Equal(expectedOutputUTXOID, outputUTXOID)
|
|
require.Equal(expectedOutputUTXO, outputUTXO)
|
|
}
|
|
}
|