mirror of
https://github.com/luxfi/node.git
synced 2026-07-27 03:39:39 +00:00
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.
459 lines
13 KiB
Go
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))
|
|
}
|