Files
node/vms/platformvm/txs/base_tx_test.go
T
Hanzo AI 8de1b78ed8 refactor: migrate UTXO components to standalone github.com/luxfi/utxo
One place, one way to define UTXO primitives. Delete the in-tree duplicate
at node/vms/components/lux/ and import the standalone luxfi/utxo package
with a 'lux' alias so existing call-sites (lux.UTXO, lux.TransferableInput,
etc.) remain unchanged in consumers.

Files changed: 167 imports rewritten, 2 directories deleted.

Build still green on the whole tree except pre-existing examples/multi-network
QChainMainnetID issue (unrelated).

Next: rename luxfi/utxo/luxmock → utxomock for full brand-neutrality; that
is a separate PR since it requires touching all consumer alias names.
2026-04-13 06:47:35 -07:00

459 lines
13 KiB
Go

// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package txs
import (
"github.com/luxfi/runtime"
"encoding/json"
"testing"
"github.com/stretchr/testify/require"
"github.com/luxfi/constants"
"github.com/luxfi/ids"
lux "github.com/luxfi/utxo"
"github.com/luxfi/node/vms/platformvm/stakeable"
"github.com/luxfi/utils"
"github.com/luxfi/utxo/secp256k1fx"
"github.com/luxfi/vm/types"
)
func TestBaseTxSerialization(t *testing.T) {
require := require.New(t)
addr := ids.ShortID{
0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
0x44, 0x55, 0x66, 0x77,
}
xAssetID, err := ids.FromString("d1Rdokz7Vq8H5aczkwgkiPCCa6JME7yT2xpqgWTfFKWYVsGbG")
require.NoError(err)
customAssetID := ids.ID{
0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
}
txID := ids.ID{
0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
}
simpleBaseTx := &BaseTx{
BaseTx: lux.BaseTx{
NetworkID: constants.MainnetID,
BlockchainID: ids.Empty, // Use empty for serialization test
Outs: []*lux.TransferableOutput{},
Ins: []*lux.TransferableInput{
{
UTXOID: lux.UTXOID{
TxID: txID,
OutputIndex: 1,
},
Asset: lux.Asset{
ID: xAssetID,
},
In: &secp256k1fx.TransferInput{
Amt: constants.MilliLux,
Input: secp256k1fx.Input{
SigIndices: []uint32{5},
},
},
},
},
Memo: types.JSONByteSlice{},
},
}
testChainID := ids.Empty // Use empty for serialization test
rt := &runtime.Runtime{
NetworkID: constants.MainnetID, // Must match tx.NetworkID
ChainID: testChainID,
XAssetID: xAssetID,
}
require.NoError(simpleBaseTx.SyntacticVerify(rt))
expectedUnsignedSimpleBaseTxBytes := []byte{
// Codec version
0x00, 0x00,
// BaseTx Type ID
0x00, 0x00, 0x00, 0x22,
// Mainnet network ID
0x00, 0x00, 0x00, 0x01,
// P-chain blockchain ID (31 zeros + 'P')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Number of outputs
0x00, 0x00, 0x00, 0x00,
// Number of inputs
0x00, 0x00, 0x00, 0x01,
// Inputs[0]
// TxID
0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
// Tx output index
0x00, 0x00, 0x00, 0x01,
// Mainnet LUX assetID
0x51, 0xc2, 0x4f, 0xe7, 0xee, 0x02, 0x01, 0xff,
0x0f, 0x33, 0x5f, 0x51, 0x99, 0x28, 0xdb, 0x6e,
0xef, 0x62, 0x24, 0x25, 0x45, 0x52, 0xc9, 0x6b,
0x6b, 0x42, 0x5f, 0xbc, 0x18, 0xfa, 0x24, 0x3b,
// secp256k1fx transfer input type ID
0x00, 0x00, 0x00, 0x05,
// input amount = 1 MilliLux
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xe8,
// number of signatures needed in input
0x00, 0x00, 0x00, 0x01,
// index of signer
0x00, 0x00, 0x00, 0x05,
// length of memo
0x00, 0x00, 0x00, 0x00,
}
var unsignedSimpleBaseTx UnsignedTx = simpleBaseTx
unsignedSimpleBaseTxBytes, err := Codec.Marshal(CodecVersion, &unsignedSimpleBaseTx)
require.NoError(err)
require.Equal(expectedUnsignedSimpleBaseTxBytes, unsignedSimpleBaseTxBytes)
complexBaseTx := &BaseTx{
BaseTx: lux.BaseTx{
NetworkID: constants.MainnetID,
BlockchainID: ids.Empty, // Use empty for serialization test
Outs: []*lux.TransferableOutput{
{
Asset: lux.Asset{
ID: xAssetID,
},
Out: &stakeable.LockOut{
Locktime: 87654321,
TransferableOut: &secp256k1fx.TransferOutput{
Amt: 1,
OutputOwners: secp256k1fx.OutputOwners{
Locktime: 12345678,
Threshold: 0,
Addrs: []ids.ShortID{},
},
},
},
},
{
Asset: lux.Asset{
ID: customAssetID,
},
Out: &stakeable.LockOut{
Locktime: 876543210,
TransferableOut: &secp256k1fx.TransferOutput{
Amt: 0xffffffffffffffff,
OutputOwners: secp256k1fx.OutputOwners{
Locktime: 0,
Threshold: 1,
Addrs: []ids.ShortID{
addr,
},
},
},
},
},
},
Ins: []*lux.TransferableInput{
{
UTXOID: lux.UTXOID{
TxID: txID,
OutputIndex: 1,
},
Asset: lux.Asset{
ID: xAssetID,
},
In: &secp256k1fx.TransferInput{
Amt: constants.Lux,
Input: secp256k1fx.Input{
SigIndices: []uint32{2, 5},
},
},
},
{
UTXOID: lux.UTXOID{
TxID: txID,
OutputIndex: 2,
},
Asset: lux.Asset{
ID: customAssetID,
},
In: &stakeable.LockIn{
Locktime: 876543210,
TransferableIn: &secp256k1fx.TransferInput{
Amt: 0xefffffffffffffff,
Input: secp256k1fx.Input{
SigIndices: []uint32{0},
},
},
},
},
{
UTXOID: lux.UTXOID{
TxID: txID,
OutputIndex: 3,
},
Asset: lux.Asset{
ID: customAssetID,
},
In: &secp256k1fx.TransferInput{
Amt: 0x1000000000000000,
Input: secp256k1fx.Input{
SigIndices: []uint32{},
},
},
},
},
Memo: types.JSONByteSlice("😅\nwell that's\x01\x23\x45!"),
},
}
lux.SortTransferableOutputs(complexBaseTx.Outs, Codec)
utils.Sort(complexBaseTx.Ins)
rt2 := &runtime.Runtime{
NetworkID: constants.MainnetID, // Must match tx.NetworkID
ChainID: testChainID,
XAssetID: xAssetID,
}
require.NoError(complexBaseTx.SyntacticVerify(rt2))
expectedUnsignedComplexBaseTxBytes := []byte{
// Codec version
0x00, 0x00,
// BaseTx Type ID
0x00, 0x00, 0x00, 0x22,
// Mainnet network ID
0x00, 0x00, 0x00, 0x01,
// P-chain blockchain ID (31 zeros + 'P')
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Number of outputs
0x00, 0x00, 0x00, 0x02,
// Outputs[0]
// Mainnet LUX assetID
0x51, 0xc2, 0x4f, 0xe7, 0xee, 0x02, 0x01, 0xff,
0x0f, 0x33, 0x5f, 0x51, 0x99, 0x28, 0xdb, 0x6e,
0xef, 0x62, 0x24, 0x25, 0x45, 0x52, 0xc9, 0x6b,
0x6b, 0x42, 0x5f, 0xbc, 0x18, 0xfa, 0x24, 0x3b,
// Stakeable locked output type ID
0x00, 0x00, 0x00, 0x16,
// Locktime
0x00, 0x00, 0x00, 0x00, 0x05, 0x39, 0x7f, 0xb1,
// secp256k1fx transfer output type ID
0x00, 0x00, 0x00, 0x07,
// amount
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
// secp256k1fx output locktime
0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x61, 0x4e,
// threshold
0x00, 0x00, 0x00, 0x00,
// number of addresses
0x00, 0x00, 0x00, 0x00,
// Outputs[1]
// custom asset ID
0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
// Stakeable locked output type ID
0x00, 0x00, 0x00, 0x16,
// Locktime
0x00, 0x00, 0x00, 0x00, 0x34, 0x3e, 0xfc, 0xea,
// secp256k1fx transfer output type ID
0x00, 0x00, 0x00, 0x07,
// amount
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
// secp256k1fx output locktime
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// threshold
0x00, 0x00, 0x00, 0x01,
// number of addresses
0x00, 0x00, 0x00, 0x01,
// address[0]
0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
0x44, 0x55, 0x66, 0x77,
// number of inputs
0x00, 0x00, 0x00, 0x03,
// inputs[0]
// TxID
0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
// Tx output index
0x00, 0x00, 0x00, 0x01,
// Mainnet LUX assetID
0x51, 0xc2, 0x4f, 0xe7, 0xee, 0x02, 0x01, 0xff,
0x0f, 0x33, 0x5f, 0x51, 0x99, 0x28, 0xdb, 0x6e,
0xef, 0x62, 0x24, 0x25, 0x45, 0x52, 0xc9, 0x6b,
0x6b, 0x42, 0x5f, 0xbc, 0x18, 0xfa, 0x24, 0x3b,
// secp256k1fx transfer input type ID
0x00, 0x00, 0x00, 0x05,
// input amount = 1 Lux
0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x42, 0x40,
// number of signatures needed in input
0x00, 0x00, 0x00, 0x02,
// index of first signer
0x00, 0x00, 0x00, 0x02,
// index of second signer
0x00, 0x00, 0x00, 0x05,
// inputs[1]
// TxID
0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
// Tx output index
0x00, 0x00, 0x00, 0x02,
// Custom asset ID
0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
// Stakeable locked input type ID
0x00, 0x00, 0x00, 0x15,
// Locktime
0x00, 0x00, 0x00, 0x00, 0x34, 0x3e, 0xfc, 0xea,
// secp256k1fx transfer input type ID
0x00, 0x00, 0x00, 0x05,
// input amount
0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
// number of signatures needed in input
0x00, 0x00, 0x00, 0x01,
// index of signer
0x00, 0x00, 0x00, 0x00,
// inputs[2]
// TxID
0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
// Tx output index
0x00, 0x00, 0x00, 0x03,
// custom asset ID
0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
// secp256k1fx transfer input type ID
0x00, 0x00, 0x00, 0x05,
// input amount
0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// number of signatures needed in input
0x00, 0x00, 0x00, 0x00,
// length of memo
0x00, 0x00, 0x00, 0x14,
// memo
0xf0, 0x9f, 0x98, 0x85, 0x0a, 0x77, 0x65, 0x6c,
0x6c, 0x20, 0x74, 0x68, 0x61, 0x74, 0x27, 0x73,
0x01, 0x23, 0x45, 0x21,
}
var unsignedComplexBaseTx UnsignedTx = complexBaseTx
unsignedComplexBaseTxBytes, err := Codec.Marshal(CodecVersion, &unsignedComplexBaseTx)
require.NoError(err)
require.Equal(expectedUnsignedComplexBaseTxBytes, unsignedComplexBaseTxBytes)
// Remove aliaser as BCLookup field doesn't exist in runtime.Runtime
// This functionality is now handled differently
rt3 := &runtime.Runtime{
NetworkID: constants.MainnetID, // Must match tx.NetworkID
ChainID: testChainID,
XAssetID: xAssetID,
}
unsignedComplexBaseTx.InitRuntime(rt3)
unsignedComplexBaseTxJSONBytes, err := json.MarshalIndent(unsignedComplexBaseTx, "", "\t")
require.NoError(err)
require.JSONEq(`{
"networkID": 1,
"blockchainID": "11111111111111111111111111111111LpoYY",
"outputs": [
{
"assetID": "d1Rdokz7Vq8H5aczkwgkiPCCa6JME7yT2xpqgWTfFKWYVsGbG",
"fxID": "spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ",
"output": {
"locktime": 87654321,
"output": {
"addresses": [],
"amount": 1,
"locktime": 12345678,
"threshold": 0
}
}
},
{
"assetID": "2Ab62uWwJw1T6VvmKD36ufsiuGZuX1pGykXAvPX1LtjTRHxwcc",
"fxID": "spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ",
"output": {
"locktime": 876543210,
"output": {
"addresses": [
"7EKFm18KvWqcxMCNgpBSN51pJnEr1cVUb"
],
"amount": 18446744073709551615,
"locktime": 0,
"threshold": 1
}
}
}
],
"inputs": [
{
"txID": "2wiU5PnFTjTmoAXGZutHAsPF36qGGyLHYHj9G1Aucfmb3JFFGN",
"outputIndex": 1,
"assetID": "d1Rdokz7Vq8H5aczkwgkiPCCa6JME7yT2xpqgWTfFKWYVsGbG",
"fxID": "spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ",
"input": {
"amount": 1000000,
"signatureIndices": [
2,
5
]
}
},
{
"txID": "2wiU5PnFTjTmoAXGZutHAsPF36qGGyLHYHj9G1Aucfmb3JFFGN",
"outputIndex": 2,
"assetID": "2Ab62uWwJw1T6VvmKD36ufsiuGZuX1pGykXAvPX1LtjTRHxwcc",
"fxID": "spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ",
"input": {
"locktime": 876543210,
"input": {
"amount": 17293822569102704639,
"signatureIndices": [
0
]
}
}
},
{
"txID": "2wiU5PnFTjTmoAXGZutHAsPF36qGGyLHYHj9G1Aucfmb3JFFGN",
"outputIndex": 3,
"assetID": "2Ab62uWwJw1T6VvmKD36ufsiuGZuX1pGykXAvPX1LtjTRHxwcc",
"fxID": "spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ",
"input": {
"amount": 1152921504606846976,
"signatureIndices": []
}
}
],
"memo": "0xf09f98850a77656c6c2074686174277301234521"
}`, string(unsignedComplexBaseTxJSONBytes))
}