From 86e9d946d115e39118a9d4df312c29dd42257ba3 Mon Sep 17 00:00:00 2001 From: Alberto Benegiamo Date: Mon, 19 Feb 2024 09:43:25 +0100 Subject: [PATCH 1/3] X-chain wallet fees UTs --- wallet/chain/x/builder_test.go | 583 +++++++++++++++++++++++++++++++++ 1 file changed, 583 insertions(+) create mode 100644 wallet/chain/x/builder_test.go diff --git a/wallet/chain/x/builder_test.go b/wallet/chain/x/builder_test.go new file mode 100644 index 000000000000..7328fcf25f96 --- /dev/null +++ b/wallet/chain/x/builder_test.go @@ -0,0 +1,583 @@ +// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package x + +import ( + "testing" + + "github.com/stretchr/testify/require" + "golang.org/x/exp/slices" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils/constants" + "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" + "github.com/ava-labs/avalanchego/utils/set" + "github.com/ava-labs/avalanchego/utils/units" + "github.com/ava-labs/avalanchego/vms/components/avax" + "github.com/ava-labs/avalanchego/vms/components/verify" + "github.com/ava-labs/avalanchego/vms/nftfx" + "github.com/ava-labs/avalanchego/vms/propertyfx" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" + "github.com/ava-labs/avalanchego/wallet/subnet/primary/common" + + stdcontext "context" +) + +var ( + testKeys = secp256k1.TestKeys() + + // We hard-code [avaxAssetID] and [subnetAssetID] to make + // ordering of UTXOs generated by [testUTXOsList] is reproducible + avaxAssetID = ids.Empty.Prefix(1789) + xChainID = ids.Empty.Prefix(2021) + nftAssetID = ids.Empty.Prefix(2022) + propertyAssetID = ids.Empty.Prefix(2023) + + testCtx = NewContext( + constants.UnitTestID, + xChainID, + avaxAssetID, + units.MicroAvax, // BaseTxFee + 99*units.MilliAvax, // CreateAssetTxFee + ) +) + +// These tests create and sign a tx, then verify that utxos included +// in the tx are exactly necessary to pay fees for it + +func TestBaseTx(t *testing.T) { + var ( + require = require.New(t) + + // backend + utxosKey = testKeys[1] + utxos = makeTestUTXOs(utxosKey) + genericBackend = newChainUTXOs( + require, + map[ids.ID][]*avax.UTXO{ + xChainID: utxos, + }, + ) + backend = NewBackend(testCtx, genericBackend) + + // builder + utxoAddr = utxosKey.Address() + builder = NewBuilder(set.Of(utxoAddr), backend) + + // data to build the transaction + outputsToMove = []*avax.TransferableOutput{{ + Asset: avax.Asset{ID: avaxAssetID}, + Out: &secp256k1fx.TransferOutput{ + Amt: 7 * units.Avax, + OutputOwners: secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{utxoAddr}, + }, + }, + }} + ) + + utx, err := builder.NewBaseTx( + outputsToMove, + ) + require.NoError(err) + + // check UTXOs selection and fee financing + ins := utx.Ins + outs := utx.Outs + require.Len(ins, 2) + require.Len(outs, 2) + + expectedConsumed := testCtx.BaseTxFee() + consumed := ins[0].In.Amount() + ins[1].In.Amount() - outs[0].Out.Amount() - outs[1].Out.Amount() + require.Equal(expectedConsumed, consumed) + require.Equal(outputsToMove[0], outs[1]) +} + +func TestCreateAssetTx(t *testing.T) { + require := require.New(t) + + var ( + // backend + utxosKey = testKeys[1] + utxos = makeTestUTXOs(utxosKey) + genericBackend = newChainUTXOs( + require, + map[ids.ID][]*avax.UTXO{ + xChainID: utxos, + }, + ) + backend = NewBackend(testCtx, genericBackend) + + // builder + utxoAddr = utxosKey.Address() + builder = NewBuilder(set.Of(utxoAddr), backend) + + // data to build the transaction + assetName = "Team Rocket" + symbol = "TR" + denomination uint8 = 0 + initialState = map[uint32][]verify.State{ + 0: { + &secp256k1fx.MintOutput{ + OutputOwners: secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{testKeys[0].PublicKey().Address()}, + }, + }, &secp256k1fx.MintOutput{ + OutputOwners: secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{testKeys[0].PublicKey().Address()}, + }, + }, + }, + 1: { + &nftfx.MintOutput{ + GroupID: 1, + OutputOwners: secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{testKeys[1].PublicKey().Address()}, + }, + }, + &nftfx.MintOutput{ + GroupID: 2, + OutputOwners: secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{testKeys[1].PublicKey().Address()}, + }, + }, + }, + 2: { + &propertyfx.MintOutput{ + OutputOwners: secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{testKeys[2].PublicKey().Address()}, + }, + }, + &propertyfx.MintOutput{ + OutputOwners: secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{testKeys[2].PublicKey().Address()}, + }, + }, + }, + } + ) + + utx, err := builder.NewCreateAssetTx( + assetName, + symbol, + denomination, + initialState, + ) + require.NoError(err) + + // check UTXOs selection and fee financing + ins := utx.Ins + outs := utx.Outs + require.Len(ins, 2) + require.Len(outs, 1) + + expectedConsumed := testCtx.CreateAssetTxFee() + consumed := ins[0].In.Amount() + ins[1].In.Amount() - outs[0].Out.Amount() + require.Equal(expectedConsumed, consumed) +} + +func TestMintNFTOperation(t *testing.T) { + require := require.New(t) + + var ( + // backend + utxosKey = testKeys[1] + utxos = makeTestUTXOs(utxosKey) + genericBackend = newChainUTXOs( + require, + map[ids.ID][]*avax.UTXO{ + xChainID: utxos, + }, + ) + backend = NewBackend(testCtx, genericBackend) + + // builder + utxoAddr = utxosKey.Address() + builder = NewBuilder(set.Of(utxoAddr), backend) + + // data to build the transaction + payload = []byte{'h', 'e', 'l', 'l', 'o'} + NFTOwner = &secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{utxoAddr}, + } + ) + + utx, err := builder.NewOperationTxMintNFT( + nftAssetID, + payload, + []*secp256k1fx.OutputOwners{NFTOwner}, + ) + require.NoError(err) + + // check UTXOs selection and fee financing + ins := utx.Ins + outs := utx.Outs + require.Len(ins, 1) + require.Len(outs, 1) + + expectedConsumed := testCtx.BaseTxFee() + consumed := ins[0].In.Amount() - outs[0].Out.Amount() + require.Equal(expectedConsumed, consumed) +} + +func TestMintFTOperation(t *testing.T) { + require := require.New(t) + + var ( + // backend + utxosKey = testKeys[1] + utxos = makeTestUTXOs(utxosKey) + genericBackend = newChainUTXOs( + require, + map[ids.ID][]*avax.UTXO{ + xChainID: utxos, + }, + ) + backend = NewBackend(testCtx, genericBackend) + + // builder + utxoAddr = utxosKey.Address() + builder = NewBuilder(set.Of(utxoAddr), backend) + + // data to build the transaction + outputs = map[ids.ID]*secp256k1fx.TransferOutput{ + nftAssetID: { + Amt: 1, + OutputOwners: secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{utxoAddr}, + }, + }, + } + ) + + utx, err := builder.NewOperationTxMintFT( + outputs, + ) + require.NoError(err) + + // check UTXOs selection and fee financing + ins := utx.Ins + outs := utx.Outs + require.Len(ins, 1) + require.Len(outs, 1) + + expectedConsumed := testCtx.BaseTxFee() + consumed := ins[0].In.Amount() - outs[0].Out.Amount() + require.Equal(expectedConsumed, consumed) +} + +func TestMintPropertyOperation(t *testing.T) { + require := require.New(t) + + var ( + // backend + utxosKey = testKeys[1] + utxos = makeTestUTXOs(utxosKey) + genericBackend = newChainUTXOs( + require, + map[ids.ID][]*avax.UTXO{ + xChainID: utxos, + }, + ) + backend = NewBackend(testCtx, genericBackend) + + // builder + utxoAddr = utxosKey.Address() + builder = NewBuilder(set.Of(utxoAddr), backend) + + // data to build the transaction + propertyOwner = &secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{utxoAddr}, + } + ) + + utx, err := builder.NewOperationTxMintProperty( + propertyAssetID, + propertyOwner, + ) + require.NoError(err) + + // check UTXOs selection and fee financing + ins := utx.Ins + outs := utx.Outs + require.Len(ins, 1) + require.Len(outs, 1) + + expectedConsumed := testCtx.BaseTxFee() + consumed := ins[0].In.Amount() - outs[0].Out.Amount() + require.Equal(expectedConsumed, consumed) +} + +func TestBurnPropertyOperation(t *testing.T) { + require := require.New(t) + + var ( + // backend + utxosKey = testKeys[1] + utxos = makeTestUTXOs(utxosKey) + genericBackend = newChainUTXOs( + require, + map[ids.ID][]*avax.UTXO{ + xChainID: utxos, + }, + ) + backend = NewBackend(testCtx, genericBackend) + + // builder + utxoAddr = utxosKey.Address() + builder = NewBuilder(set.Of(utxoAddr), backend) + ) + + utx, err := builder.NewOperationTxBurnProperty( + propertyAssetID, + ) + require.NoError(err) + + // check UTXOs selection and fee financing + ins := utx.Ins + outs := utx.Outs + require.Len(ins, 1) + require.Len(outs, 1) + + expectedConsumed := testCtx.BaseTxFee() + consumed := ins[0].In.Amount() - outs[0].Out.Amount() + require.Equal(expectedConsumed, consumed) +} + +func TestImportTx(t *testing.T) { + var ( + require = require.New(t) + + // backend + utxosKey = testKeys[1] + utxos = makeTestUTXOs(utxosKey) + sourceChainID = ids.GenerateTestID() + importedUTXOs = utxos[:1] + genericBackend = newChainUTXOs( + require, + map[ids.ID][]*avax.UTXO{ + xChainID: utxos, + sourceChainID: importedUTXOs, + }, + ) + + backend = NewBackend(testCtx, genericBackend) + + // builder + utxoAddr = utxosKey.Address() + builder = NewBuilder(set.Of(utxoAddr), backend) + + // data to build the transaction + importKey = testKeys[0] + importTo = &secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{ + importKey.Address(), + }, + } + ) + + utx, err := builder.NewImportTx( + sourceChainID, + importTo, + ) + require.NoError(err) + + // check UTXOs selection and fee financing + ins := utx.Ins + outs := utx.Outs + importedIns := utx.ImportedIns + require.Empty(ins) + require.Len(importedIns, 1) + require.Len(outs, 1) + + expectedConsumed := testCtx.BaseTxFee() + consumed := importedIns[0].In.Amount() - outs[0].Out.Amount() + require.Equal(expectedConsumed, consumed) +} + +func TestExportTx(t *testing.T) { + var ( + require = require.New(t) + + // backend + utxosKey = testKeys[1] + utxos = makeTestUTXOs(utxosKey) + genericBackend = newChainUTXOs( + require, + map[ids.ID][]*avax.UTXO{ + xChainID: utxos, + }, + ) + backend = NewBackend(testCtx, genericBackend) + + // builder + utxoAddr = utxosKey.Address() + builder = NewBuilder(set.Of(utxoAddr), backend) + + // data to build the transaction + subnetID = ids.GenerateTestID() + exportedOutputs = []*avax.TransferableOutput{{ + Asset: avax.Asset{ID: avaxAssetID}, + Out: &secp256k1fx.TransferOutput{ + Amt: 7 * units.Avax, + OutputOwners: secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{utxoAddr}, + }, + }, + }} + ) + + utx, err := builder.NewExportTx( + subnetID, + exportedOutputs, + ) + require.NoError(err) + + // check UTXOs selection and fee financing + ins := utx.Ins + outs := utx.Outs + require.Len(ins, 2) + require.Len(outs, 1) + + expectedConsumed := testCtx.BaseTxFee() + exportedOutputs[0].Out.Amount() + consumed := ins[0].In.Amount() + ins[1].In.Amount() - outs[0].Out.Amount() + require.Equal(expectedConsumed, consumed) + require.Equal(utx.ExportedOuts, exportedOutputs) +} + +func makeTestUTXOs(utxosKey *secp256k1.PrivateKey) []*avax.UTXO { + // Note: we avoid ids.GenerateTestNodeID here to make sure that UTXO IDs won't change + // run by run. This simplifies checking what utxos are included in the built txs. + const utxosOffset uint64 = 2024 + + return []*avax.UTXO{ // currently, the wallet scans UTXOs in the order provided here + { // a small UTXO first, which should not be enough to pay fees + UTXOID: avax.UTXOID{ + TxID: ids.Empty.Prefix(utxosOffset), + OutputIndex: uint32(utxosOffset), + }, + Asset: avax.Asset{ID: avaxAssetID}, + Out: &secp256k1fx.TransferOutput{ + Amt: 2 * units.MilliAvax, + OutputOwners: secp256k1fx.OutputOwners{ + Locktime: 0, + Addrs: []ids.ShortID{utxosKey.PublicKey().Address()}, + Threshold: 1, + }, + }, + }, + { + UTXOID: avax.UTXOID{ + TxID: ids.Empty.Prefix(utxosOffset + 2), + OutputIndex: uint32(utxosOffset + 2), + }, + Asset: avax.Asset{ID: nftAssetID}, + Out: &nftfx.MintOutput{ + GroupID: 1, + OutputOwners: secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{utxosKey.PublicKey().Address()}, + }, + }, + }, + { + UTXOID: avax.UTXOID{ + TxID: ids.Empty.Prefix(utxosOffset + 3), + OutputIndex: uint32(utxosOffset + 3), + }, + Asset: avax.Asset{ID: nftAssetID}, + Out: &secp256k1fx.MintOutput{ + OutputOwners: secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{utxosKey.PublicKey().Address()}, + }, + }, + }, + { + UTXOID: avax.UTXOID{ + TxID: ids.Empty.Prefix(utxosOffset + 4), + OutputIndex: uint32(utxosOffset + 4), + }, + Asset: avax.Asset{ID: propertyAssetID}, + Out: &propertyfx.MintOutput{ + OutputOwners: secp256k1fx.OutputOwners{ + Locktime: 0, + Addrs: []ids.ShortID{utxosKey.PublicKey().Address()}, + Threshold: 1, + }, + }, + }, + { + UTXOID: avax.UTXOID{ + TxID: ids.Empty.Prefix(utxosOffset + 5), + OutputIndex: uint32(utxosOffset + 5), + }, + Asset: avax.Asset{ID: propertyAssetID}, + Out: &propertyfx.OwnedOutput{ + OutputOwners: secp256k1fx.OutputOwners{ + Locktime: 0, + Addrs: []ids.ShortID{utxosKey.PublicKey().Address()}, + Threshold: 1, + }, + }, + }, + { // a large UTXO last, which should be enough to pay any fee by itself + UTXOID: avax.UTXOID{ + TxID: ids.Empty.Prefix(utxosOffset + 6), + OutputIndex: uint32(utxosOffset + 6), + }, + Asset: avax.Asset{ID: avaxAssetID}, + Out: &secp256k1fx.TransferOutput{ + Amt: 9 * units.Avax, + OutputOwners: secp256k1fx.OutputOwners{ + Locktime: 0, + Addrs: []ids.ShortID{utxosKey.PublicKey().Address()}, + Threshold: 1, + }, + }, + }, + } +} + +func newChainUTXOs(require *require.Assertions, utxoSets map[ids.ID][]*avax.UTXO) common.ChainUTXOs { + globalUTXOs := common.NewUTXOs() + for subnetID, utxos := range utxoSets { + for _, utxo := range utxos { + require.NoError( + globalUTXOs.AddUTXO(stdcontext.Background(), subnetID, constants.PlatformChainID, utxo), + ) + } + } + return &deterministicChainUTXOs{ + ChainUTXOs: common.NewChainUTXOs(constants.PlatformChainID, globalUTXOs), + } +} + +type deterministicChainUTXOs struct { + common.ChainUTXOs +} + +func (c *deterministicChainUTXOs) UTXOs(ctx stdcontext.Context, sourceChainID ids.ID) ([]*avax.UTXO, error) { + utxos, err := c.ChainUTXOs.UTXOs(ctx, sourceChainID) + if err != nil { + return nil, err + } + + slices.SortFunc(utxos, func(a, b *avax.UTXO) int { + return a.Compare(&b.UTXOID) + }) + return utxos, nil +} From 80bd32fd8f2c14f874216167f1a91e7bfde40b16 Mon Sep 17 00:00:00 2001 From: Alberto Benegiamo Date: Mon, 19 Feb 2024 11:44:25 +0100 Subject: [PATCH 2/3] replaced experimental with std lib package --- wallet/chain/x/builder_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wallet/chain/x/builder_test.go b/wallet/chain/x/builder_test.go index 7328fcf25f96..f52f1344b001 100644 --- a/wallet/chain/x/builder_test.go +++ b/wallet/chain/x/builder_test.go @@ -4,10 +4,10 @@ package x import ( + "slices" "testing" "github.com/stretchr/testify/require" - "golang.org/x/exp/slices" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/constants" From 730810aef419d4aeea2e4dd0fb645ba5a5249e0a Mon Sep 17 00:00:00 2001 From: Alberto Benegiamo Date: Mon, 19 Feb 2024 16:58:34 +0100 Subject: [PATCH 3/3] consolidated deterministic utxos --- wallet/chain/p/builder_test.go | 55 +++++----------------- wallet/chain/x/builder_test.go | 49 ++++--------------- wallet/subnet/primary/common/test_utxos.go | 45 ++++++++++++++++++ 3 files changed, 64 insertions(+), 85 deletions(-) create mode 100644 wallet/subnet/primary/common/test_utxos.go diff --git a/wallet/chain/p/builder_test.go b/wallet/chain/p/builder_test.go index 9e880f27f6d4..473103147549 100644 --- a/wallet/chain/p/builder_test.go +++ b/wallet/chain/p/builder_test.go @@ -4,7 +4,6 @@ package p import ( - "slices" "testing" "time" @@ -23,8 +22,6 @@ import ( "github.com/ava-labs/avalanchego/vms/platformvm/txs" "github.com/ava-labs/avalanchego/vms/secp256k1fx" "github.com/ava-labs/avalanchego/wallet/subnet/primary/common" - - stdcontext "context" ) var ( @@ -59,7 +56,7 @@ func TestBaseTx(t *testing.T) { // backend utxosKey = testKeys[1] utxos = makeTestUTXOs(utxosKey) - chainUTXOs = newChainUTXOs(require, map[ids.ID][]*avax.UTXO{ + chainUTXOs = common.NewDeterministicChainUTXOs(require, map[ids.ID][]*avax.UTXO{ constants.PlatformChainID: utxos, }) backend = NewBackend(testCtx, chainUTXOs, nil) @@ -103,7 +100,7 @@ func TestAddSubnetValidatorTx(t *testing.T) { // backend utxosKey = testKeys[1] utxos = makeTestUTXOs(utxosKey) - chainUTXOs = newChainUTXOs(require, map[ids.ID][]*avax.UTXO{ + chainUTXOs = common.NewDeterministicChainUTXOs(require, map[ids.ID][]*avax.UTXO{ constants.PlatformChainID: utxos, }) @@ -160,7 +157,7 @@ func TestRemoveSubnetValidatorTx(t *testing.T) { // backend utxosKey = testKeys[1] utxos = makeTestUTXOs(utxosKey) - chainUTXOs = newChainUTXOs(require, map[ids.ID][]*avax.UTXO{ + chainUTXOs = common.NewDeterministicChainUTXOs(require, map[ids.ID][]*avax.UTXO{ constants.PlatformChainID: utxos, }) @@ -211,7 +208,7 @@ func TestCreateChainTx(t *testing.T) { // backend utxosKey = testKeys[1] utxos = makeTestUTXOs(utxosKey) - chainUTXOs = newChainUTXOs(require, map[ids.ID][]*avax.UTXO{ + chainUTXOs = common.NewDeterministicChainUTXOs(require, map[ids.ID][]*avax.UTXO{ constants.PlatformChainID: utxos, }) @@ -270,7 +267,7 @@ func TestCreateSubnetTx(t *testing.T) { // backend utxosKey = testKeys[1] utxos = makeTestUTXOs(utxosKey) - chainUTXOs = newChainUTXOs(require, map[ids.ID][]*avax.UTXO{ + chainUTXOs = common.NewDeterministicChainUTXOs(require, map[ids.ID][]*avax.UTXO{ constants.PlatformChainID: utxos, }) @@ -318,7 +315,7 @@ func TestTransferSubnetOwnershipTx(t *testing.T) { // backend utxosKey = testKeys[1] utxos = makeTestUTXOs(utxosKey) - chainUTXOs = newChainUTXOs(require, map[ids.ID][]*avax.UTXO{ + chainUTXOs = common.NewDeterministicChainUTXOs(require, map[ids.ID][]*avax.UTXO{ constants.PlatformChainID: utxos, }) @@ -371,7 +368,7 @@ func TestImportTx(t *testing.T) { utxos = makeTestUTXOs(utxosKey) sourceChainID = ids.GenerateTestID() importedUTXOs = utxos[:1] - chainUTXOs = newChainUTXOs(require, map[ids.ID][]*avax.UTXO{ + chainUTXOs = common.NewDeterministicChainUTXOs(require, map[ids.ID][]*avax.UTXO{ constants.PlatformChainID: utxos, sourceChainID: importedUTXOs, }) @@ -419,7 +416,7 @@ func TestExportTx(t *testing.T) { // backend utxosKey = testKeys[1] utxos = makeTestUTXOs(utxosKey) - chainUTXOs = newChainUTXOs(require, map[ids.ID][]*avax.UTXO{ + chainUTXOs = common.NewDeterministicChainUTXOs(require, map[ids.ID][]*avax.UTXO{ constants.PlatformChainID: utxos, }) backend = NewBackend(testCtx, chainUTXOs, nil) @@ -468,7 +465,7 @@ func TestTransformSubnetTx(t *testing.T) { // backend utxosKey = testKeys[1] utxos = makeTestUTXOs(utxosKey) - chainUTXOs = newChainUTXOs(require, map[ids.ID][]*avax.UTXO{ + chainUTXOs = common.NewDeterministicChainUTXOs(require, map[ids.ID][]*avax.UTXO{ constants.PlatformChainID: utxos, }) @@ -538,7 +535,7 @@ func TestAddPermissionlessValidatorTx(t *testing.T) { // backend utxosKey = testKeys[1] utxos = makeTestUTXOs(utxosKey) - chainUTXOs = newChainUTXOs(require, map[ids.ID][]*avax.UTXO{ + chainUTXOs = common.NewDeterministicChainUTXOs(require, map[ids.ID][]*avax.UTXO{ constants.PlatformChainID: utxos, }) backend = NewBackend(testCtx, chainUTXOs, nil) @@ -608,7 +605,7 @@ func TestAddPermissionlessDelegatorTx(t *testing.T) { // backend utxosKey = testKeys[1] utxos = makeTestUTXOs(utxosKey) - chainUTXOs = newChainUTXOs(require, map[ids.ID][]*avax.UTXO{ + chainUTXOs = common.NewDeterministicChainUTXOs(require, map[ids.ID][]*avax.UTXO{ constants.PlatformChainID: utxos, }) backend = NewBackend(testCtx, chainUTXOs, nil) @@ -747,33 +744,3 @@ func makeTestUTXOs(utxosKey *secp256k1.PrivateKey) []*avax.UTXO { }, } } - -func newChainUTXOs(require *require.Assertions, utxoSets map[ids.ID][]*avax.UTXO) common.ChainUTXOs { - globalUTXOs := common.NewUTXOs() - for subnetID, utxos := range utxoSets { - for _, utxo := range utxos { - require.NoError( - globalUTXOs.AddUTXO(stdcontext.Background(), subnetID, constants.PlatformChainID, utxo), - ) - } - } - return &deterministicChainUTXOs{ - ChainUTXOs: common.NewChainUTXOs(constants.PlatformChainID, globalUTXOs), - } -} - -type deterministicChainUTXOs struct { - common.ChainUTXOs -} - -func (c *deterministicChainUTXOs) UTXOs(ctx stdcontext.Context, sourceChainID ids.ID) ([]*avax.UTXO, error) { - utxos, err := c.ChainUTXOs.UTXOs(ctx, sourceChainID) - if err != nil { - return nil, err - } - - slices.SortFunc(utxos, func(a, b *avax.UTXO) int { - return a.Compare(&b.UTXOID) - }) - return utxos, nil -} diff --git a/wallet/chain/x/builder_test.go b/wallet/chain/x/builder_test.go index f52f1344b001..f4eb916b693c 100644 --- a/wallet/chain/x/builder_test.go +++ b/wallet/chain/x/builder_test.go @@ -4,7 +4,6 @@ package x import ( - "slices" "testing" "github.com/stretchr/testify/require" @@ -20,8 +19,6 @@ import ( "github.com/ava-labs/avalanchego/vms/propertyfx" "github.com/ava-labs/avalanchego/vms/secp256k1fx" "github.com/ava-labs/avalanchego/wallet/subnet/primary/common" - - stdcontext "context" ) var ( @@ -53,7 +50,7 @@ func TestBaseTx(t *testing.T) { // backend utxosKey = testKeys[1] utxos = makeTestUTXOs(utxosKey) - genericBackend = newChainUTXOs( + genericBackend = common.NewDeterministicChainUTXOs( require, map[ids.ID][]*avax.UTXO{ xChainID: utxos, @@ -102,7 +99,7 @@ func TestCreateAssetTx(t *testing.T) { // backend utxosKey = testKeys[1] utxos = makeTestUTXOs(utxosKey) - genericBackend = newChainUTXOs( + genericBackend = common.NewDeterministicChainUTXOs( require, map[ids.ID][]*avax.UTXO{ xChainID: utxos, @@ -191,7 +188,7 @@ func TestMintNFTOperation(t *testing.T) { // backend utxosKey = testKeys[1] utxos = makeTestUTXOs(utxosKey) - genericBackend = newChainUTXOs( + genericBackend = common.NewDeterministicChainUTXOs( require, map[ids.ID][]*avax.UTXO{ xChainID: utxos, @@ -236,7 +233,7 @@ func TestMintFTOperation(t *testing.T) { // backend utxosKey = testKeys[1] utxos = makeTestUTXOs(utxosKey) - genericBackend = newChainUTXOs( + genericBackend = common.NewDeterministicChainUTXOs( require, map[ids.ID][]*avax.UTXO{ xChainID: utxos, @@ -283,7 +280,7 @@ func TestMintPropertyOperation(t *testing.T) { // backend utxosKey = testKeys[1] utxos = makeTestUTXOs(utxosKey) - genericBackend = newChainUTXOs( + genericBackend = common.NewDeterministicChainUTXOs( require, map[ids.ID][]*avax.UTXO{ xChainID: utxos, @@ -326,7 +323,7 @@ func TestBurnPropertyOperation(t *testing.T) { // backend utxosKey = testKeys[1] utxos = makeTestUTXOs(utxosKey) - genericBackend = newChainUTXOs( + genericBackend = common.NewDeterministicChainUTXOs( require, map[ids.ID][]*avax.UTXO{ xChainID: utxos, @@ -364,7 +361,7 @@ func TestImportTx(t *testing.T) { utxos = makeTestUTXOs(utxosKey) sourceChainID = ids.GenerateTestID() importedUTXOs = utxos[:1] - genericBackend = newChainUTXOs( + genericBackend = common.NewDeterministicChainUTXOs( require, map[ids.ID][]*avax.UTXO{ xChainID: utxos, @@ -414,7 +411,7 @@ func TestExportTx(t *testing.T) { // backend utxosKey = testKeys[1] utxos = makeTestUTXOs(utxosKey) - genericBackend = newChainUTXOs( + genericBackend = common.NewDeterministicChainUTXOs( require, map[ids.ID][]*avax.UTXO{ xChainID: utxos, @@ -551,33 +548,3 @@ func makeTestUTXOs(utxosKey *secp256k1.PrivateKey) []*avax.UTXO { }, } } - -func newChainUTXOs(require *require.Assertions, utxoSets map[ids.ID][]*avax.UTXO) common.ChainUTXOs { - globalUTXOs := common.NewUTXOs() - for subnetID, utxos := range utxoSets { - for _, utxo := range utxos { - require.NoError( - globalUTXOs.AddUTXO(stdcontext.Background(), subnetID, constants.PlatformChainID, utxo), - ) - } - } - return &deterministicChainUTXOs{ - ChainUTXOs: common.NewChainUTXOs(constants.PlatformChainID, globalUTXOs), - } -} - -type deterministicChainUTXOs struct { - common.ChainUTXOs -} - -func (c *deterministicChainUTXOs) UTXOs(ctx stdcontext.Context, sourceChainID ids.ID) ([]*avax.UTXO, error) { - utxos, err := c.ChainUTXOs.UTXOs(ctx, sourceChainID) - if err != nil { - return nil, err - } - - slices.SortFunc(utxos, func(a, b *avax.UTXO) int { - return a.Compare(&b.UTXOID) - }) - return utxos, nil -} diff --git a/wallet/subnet/primary/common/test_utxos.go b/wallet/subnet/primary/common/test_utxos.go new file mode 100644 index 000000000000..094c57d53705 --- /dev/null +++ b/wallet/subnet/primary/common/test_utxos.go @@ -0,0 +1,45 @@ +// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package common + +import ( + "context" + "slices" + + "github.com/stretchr/testify/require" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils/constants" + "github.com/ava-labs/avalanchego/vms/components/avax" +) + +func NewDeterministicChainUTXOs(require *require.Assertions, utxoSets map[ids.ID][]*avax.UTXO) *DeterministicChainUTXOs { + globalUTXOs := NewUTXOs() + for subnetID, utxos := range utxoSets { + for _, utxo := range utxos { + require.NoError( + globalUTXOs.AddUTXO(context.Background(), subnetID, constants.PlatformChainID, utxo), + ) + } + } + return &DeterministicChainUTXOs{ + ChainUTXOs: NewChainUTXOs(constants.PlatformChainID, globalUTXOs), + } +} + +type DeterministicChainUTXOs struct { + ChainUTXOs +} + +func (c *DeterministicChainUTXOs) UTXOs(ctx context.Context, sourceChainID ids.ID) ([]*avax.UTXO, error) { + utxos, err := c.ChainUTXOs.UTXOs(ctx, sourceChainID) + if err != nil { + return nil, err + } + + slices.SortFunc(utxos, func(a, b *avax.UTXO) int { + return a.Compare(&b.UTXOID) + }) + return utxos, nil +}