github.com/MetalBlockchain/metalgo@v1.11.9/wallet/chain/p/builder_test.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package p
     5  
     6  import (
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/stretchr/testify/require"
    11  
    12  	"github.com/MetalBlockchain/metalgo/ids"
    13  	"github.com/MetalBlockchain/metalgo/utils/constants"
    14  	"github.com/MetalBlockchain/metalgo/utils/crypto/bls"
    15  	"github.com/MetalBlockchain/metalgo/utils/crypto/secp256k1"
    16  	"github.com/MetalBlockchain/metalgo/utils/set"
    17  	"github.com/MetalBlockchain/metalgo/utils/units"
    18  	"github.com/MetalBlockchain/metalgo/vms/components/avax"
    19  	"github.com/MetalBlockchain/metalgo/vms/platformvm/reward"
    20  	"github.com/MetalBlockchain/metalgo/vms/platformvm/signer"
    21  	"github.com/MetalBlockchain/metalgo/vms/platformvm/stakeable"
    22  	"github.com/MetalBlockchain/metalgo/vms/platformvm/txs"
    23  	"github.com/MetalBlockchain/metalgo/vms/secp256k1fx"
    24  	"github.com/MetalBlockchain/metalgo/wallet/chain/p/builder"
    25  	"github.com/MetalBlockchain/metalgo/wallet/subnet/primary/common"
    26  )
    27  
    28  var (
    29  	testKeys = secp256k1.TestKeys()
    30  
    31  	// We hard-code [avaxAssetID] and [subnetAssetID] to make
    32  	// ordering of UTXOs generated by [testUTXOsList] is reproducible
    33  	avaxAssetID   = ids.Empty.Prefix(1789)
    34  	subnetAssetID = ids.Empty.Prefix(2024)
    35  
    36  	testContext = &builder.Context{
    37  		NetworkID:                     constants.UnitTestID,
    38  		AVAXAssetID:                   avaxAssetID,
    39  		BaseTxFee:                     units.MicroAvax,
    40  		CreateSubnetTxFee:             19 * units.MicroAvax,
    41  		TransformSubnetTxFee:          789 * units.MicroAvax,
    42  		CreateBlockchainTxFee:         1234 * units.MicroAvax,
    43  		AddPrimaryNetworkValidatorFee: 19 * units.MilliAvax,
    44  		AddPrimaryNetworkDelegatorFee: 765 * units.MilliAvax,
    45  		AddSubnetValidatorFee:         1010 * units.MilliAvax,
    46  		AddSubnetDelegatorFee:         9 * units.Avax,
    47  	}
    48  )
    49  
    50  // These tests create a tx, then verify that utxos included in the tx are
    51  // exactly necessary to pay fees for it.
    52  
    53  func TestBaseTx(t *testing.T) {
    54  	var (
    55  		require = require.New(t)
    56  
    57  		// backend
    58  		utxosKey   = testKeys[1]
    59  		utxos      = makeTestUTXOs(utxosKey)
    60  		chainUTXOs = common.NewDeterministicChainUTXOs(require, map[ids.ID][]*avax.UTXO{
    61  			constants.PlatformChainID: utxos,
    62  		})
    63  		backend = NewBackend(testContext, chainUTXOs, nil)
    64  
    65  		// builder
    66  		utxoAddr = utxosKey.Address()
    67  		builder  = builder.New(set.Of(utxoAddr), testContext, backend)
    68  
    69  		// data to build the transaction
    70  		outputsToMove = []*avax.TransferableOutput{{
    71  			Asset: avax.Asset{ID: avaxAssetID},
    72  			Out: &secp256k1fx.TransferOutput{
    73  				Amt: 7 * units.Avax,
    74  				OutputOwners: secp256k1fx.OutputOwners{
    75  					Threshold: 1,
    76  					Addrs:     []ids.ShortID{utxoAddr},
    77  				},
    78  			},
    79  		}}
    80  	)
    81  
    82  	utx, err := builder.NewBaseTx(outputsToMove)
    83  	require.NoError(err)
    84  
    85  	// check UTXOs selection and fee financing
    86  	ins := utx.Ins
    87  	outs := utx.Outs
    88  	require.Len(ins, 2)
    89  	require.Len(outs, 2)
    90  
    91  	expectedConsumed := testContext.BaseTxFee + outputsToMove[0].Out.Amount()
    92  	consumed := ins[0].In.Amount() + ins[1].In.Amount() - outs[0].Out.Amount()
    93  	require.Equal(expectedConsumed, consumed)
    94  	require.Equal(outputsToMove[0], outs[1])
    95  }
    96  
    97  func TestAddSubnetValidatorTx(t *testing.T) {
    98  	var (
    99  		require = require.New(t)
   100  
   101  		// backend
   102  		utxosKey   = testKeys[1]
   103  		utxos      = makeTestUTXOs(utxosKey)
   104  		chainUTXOs = common.NewDeterministicChainUTXOs(require, map[ids.ID][]*avax.UTXO{
   105  			constants.PlatformChainID: utxos,
   106  		})
   107  
   108  		subnetID       = ids.GenerateTestID()
   109  		subnetAuthKey  = testKeys[0]
   110  		subnetAuthAddr = subnetAuthKey.Address()
   111  		subnetOwner    = &secp256k1fx.OutputOwners{
   112  			Threshold: 1,
   113  			Addrs:     []ids.ShortID{subnetAuthAddr},
   114  		}
   115  		subnets = map[ids.ID]*txs.Tx{
   116  			subnetID: {
   117  				Unsigned: &txs.CreateSubnetTx{
   118  					Owner: subnetOwner,
   119  				},
   120  			},
   121  		}
   122  
   123  		backend = NewBackend(testContext, chainUTXOs, subnets)
   124  
   125  		// builder
   126  		utxoAddr = utxosKey.Address()
   127  		builder  = builder.New(set.Of(utxoAddr, subnetAuthAddr), testContext, backend)
   128  
   129  		// data to build the transaction
   130  		subnetValidator = &txs.SubnetValidator{
   131  			Validator: txs.Validator{
   132  				NodeID: ids.GenerateTestNodeID(),
   133  				End:    uint64(time.Now().Add(time.Hour).Unix()),
   134  			},
   135  			Subnet: subnetID,
   136  		}
   137  	)
   138  
   139  	// build the transaction
   140  	utx, err := builder.NewAddSubnetValidatorTx(subnetValidator)
   141  	require.NoError(err)
   142  
   143  	// check UTXOs selection and fee financing
   144  	ins := utx.Ins
   145  	outs := utx.Outs
   146  	require.Len(ins, 2)
   147  	require.Len(outs, 1)
   148  
   149  	expectedConsumed := testContext.AddSubnetValidatorFee
   150  	consumed := ins[0].In.Amount() + ins[1].In.Amount() - outs[0].Out.Amount()
   151  	require.Equal(expectedConsumed, consumed)
   152  }
   153  
   154  func TestRemoveSubnetValidatorTx(t *testing.T) {
   155  	var (
   156  		require = require.New(t)
   157  
   158  		// backend
   159  		utxosKey   = testKeys[1]
   160  		utxos      = makeTestUTXOs(utxosKey)
   161  		chainUTXOs = common.NewDeterministicChainUTXOs(require, map[ids.ID][]*avax.UTXO{
   162  			constants.PlatformChainID: utxos,
   163  		})
   164  
   165  		subnetID       = ids.GenerateTestID()
   166  		subnetAuthKey  = testKeys[0]
   167  		subnetAuthAddr = subnetAuthKey.Address()
   168  		subnetOwner    = &secp256k1fx.OutputOwners{
   169  			Threshold: 1,
   170  			Addrs:     []ids.ShortID{subnetAuthAddr},
   171  		}
   172  		subnets = map[ids.ID]*txs.Tx{
   173  			subnetID: {
   174  				Unsigned: &txs.CreateSubnetTx{
   175  					Owner: subnetOwner,
   176  				},
   177  			},
   178  		}
   179  
   180  		backend = NewBackend(testContext, chainUTXOs, subnets)
   181  
   182  		// builder
   183  		utxoAddr = utxosKey.Address()
   184  		builder  = builder.New(set.Of(utxoAddr, subnetAuthAddr), testContext, backend)
   185  	)
   186  
   187  	// build the transaction
   188  	utx, err := builder.NewRemoveSubnetValidatorTx(
   189  		ids.GenerateTestNodeID(),
   190  		subnetID,
   191  	)
   192  	require.NoError(err)
   193  
   194  	// check UTXOs selection and fee financing
   195  	ins := utx.Ins
   196  	outs := utx.Outs
   197  	require.Len(ins, 1)
   198  	require.Len(outs, 1)
   199  
   200  	expectedConsumed := testContext.BaseTxFee
   201  	consumed := ins[0].In.Amount() - outs[0].Out.Amount()
   202  	require.Equal(expectedConsumed, consumed)
   203  }
   204  
   205  func TestCreateChainTx(t *testing.T) {
   206  	var (
   207  		require = require.New(t)
   208  
   209  		// backend
   210  		utxosKey   = testKeys[1]
   211  		utxos      = makeTestUTXOs(utxosKey)
   212  		chainUTXOs = common.NewDeterministicChainUTXOs(require, map[ids.ID][]*avax.UTXO{
   213  			constants.PlatformChainID: utxos,
   214  		})
   215  
   216  		subnetID       = ids.GenerateTestID()
   217  		subnetAuthKey  = testKeys[0]
   218  		subnetAuthAddr = subnetAuthKey.Address()
   219  		subnetOwner    = &secp256k1fx.OutputOwners{
   220  			Threshold: 1,
   221  			Addrs:     []ids.ShortID{subnetAuthAddr},
   222  		}
   223  		subnets = map[ids.ID]*txs.Tx{
   224  			subnetID: {
   225  				Unsigned: &txs.CreateSubnetTx{
   226  					Owner: subnetOwner,
   227  				},
   228  			},
   229  		}
   230  
   231  		backend = NewBackend(testContext, chainUTXOs, subnets)
   232  
   233  		utxoAddr = utxosKey.Address()
   234  		builder  = builder.New(set.Of(utxoAddr, subnetAuthAddr), testContext, backend)
   235  
   236  		// data to build the transaction
   237  		genesisBytes = []byte{'a', 'b', 'c'}
   238  		vmID         = ids.GenerateTestID()
   239  		fxIDs        = []ids.ID{ids.GenerateTestID()}
   240  		chainName    = "dummyChain"
   241  	)
   242  
   243  	// build the transaction
   244  	utx, err := builder.NewCreateChainTx(
   245  		subnetID,
   246  		genesisBytes,
   247  		vmID,
   248  		fxIDs,
   249  		chainName,
   250  	)
   251  	require.NoError(err)
   252  
   253  	// check UTXOs selection and fee financing
   254  	ins := utx.Ins
   255  	outs := utx.Outs
   256  	require.Len(ins, 1)
   257  	require.Len(outs, 1)
   258  
   259  	expectedConsumed := testContext.CreateBlockchainTxFee
   260  	consumed := ins[0].In.Amount() - outs[0].Out.Amount()
   261  	require.Equal(expectedConsumed, consumed)
   262  }
   263  
   264  func TestCreateSubnetTx(t *testing.T) {
   265  	var (
   266  		require = require.New(t)
   267  
   268  		// backend
   269  		utxosKey   = testKeys[1]
   270  		utxos      = makeTestUTXOs(utxosKey)
   271  		chainUTXOs = common.NewDeterministicChainUTXOs(require, map[ids.ID][]*avax.UTXO{
   272  			constants.PlatformChainID: utxos,
   273  		})
   274  
   275  		subnetID       = ids.GenerateTestID()
   276  		subnetAuthKey  = testKeys[0]
   277  		subnetAuthAddr = subnetAuthKey.Address()
   278  		subnetOwner    = &secp256k1fx.OutputOwners{
   279  			Threshold: 1,
   280  			Addrs:     []ids.ShortID{subnetAuthAddr},
   281  		}
   282  		subnets = map[ids.ID]*txs.Tx{
   283  			subnetID: {
   284  				Unsigned: &txs.CreateSubnetTx{
   285  					Owner: subnetOwner,
   286  				},
   287  			},
   288  		}
   289  
   290  		backend = NewBackend(testContext, chainUTXOs, subnets)
   291  
   292  		// builder
   293  		utxoAddr = utxosKey.Address()
   294  		builder  = builder.New(set.Of(utxoAddr, subnetAuthAddr), testContext, backend)
   295  	)
   296  
   297  	// build the transaction
   298  	utx, err := builder.NewCreateSubnetTx(subnetOwner)
   299  	require.NoError(err)
   300  
   301  	// check UTXOs selection and fee financing
   302  	ins := utx.Ins
   303  	outs := utx.Outs
   304  	require.Len(ins, 1)
   305  	require.Len(outs, 1)
   306  
   307  	expectedConsumed := testContext.CreateSubnetTxFee
   308  	consumed := ins[0].In.Amount() - outs[0].Out.Amount()
   309  	require.Equal(expectedConsumed, consumed)
   310  }
   311  
   312  func TestTransferSubnetOwnershipTx(t *testing.T) {
   313  	var (
   314  		require = require.New(t)
   315  
   316  		// backend
   317  		utxosKey   = testKeys[1]
   318  		utxos      = makeTestUTXOs(utxosKey)
   319  		chainUTXOs = common.NewDeterministicChainUTXOs(require, map[ids.ID][]*avax.UTXO{
   320  			constants.PlatformChainID: utxos,
   321  		})
   322  
   323  		subnetID       = ids.GenerateTestID()
   324  		subnetAuthKey  = testKeys[0]
   325  		subnetAuthAddr = subnetAuthKey.Address()
   326  		subnetOwner    = &secp256k1fx.OutputOwners{
   327  			Threshold: 1,
   328  			Addrs:     []ids.ShortID{subnetAuthAddr},
   329  		}
   330  		subnets = map[ids.ID]*txs.Tx{
   331  			subnetID: {
   332  				Unsigned: &txs.CreateSubnetTx{
   333  					Owner: subnetOwner,
   334  				},
   335  			},
   336  		}
   337  
   338  		backend = NewBackend(testContext, chainUTXOs, subnets)
   339  
   340  		// builder
   341  		utxoAddr = utxosKey.Address()
   342  		builder  = builder.New(set.Of(utxoAddr, subnetAuthAddr), testContext, backend)
   343  	)
   344  
   345  	// build the transaction
   346  	utx, err := builder.NewTransferSubnetOwnershipTx(
   347  		subnetID,
   348  		subnetOwner,
   349  	)
   350  	require.NoError(err)
   351  
   352  	// check UTXOs selection and fee financing
   353  	ins := utx.Ins
   354  	outs := utx.Outs
   355  	require.Len(ins, 1)
   356  	require.Len(outs, 1)
   357  
   358  	expectedConsumed := testContext.BaseTxFee
   359  	consumed := ins[0].In.Amount() - outs[0].Out.Amount()
   360  	require.Equal(expectedConsumed, consumed)
   361  }
   362  
   363  func TestImportTx(t *testing.T) {
   364  	var (
   365  		require = require.New(t)
   366  
   367  		// backend
   368  		utxosKey      = testKeys[1]
   369  		utxos         = makeTestUTXOs(utxosKey)
   370  		sourceChainID = ids.GenerateTestID()
   371  		importedUTXOs = utxos[:1]
   372  		chainUTXOs    = common.NewDeterministicChainUTXOs(require, map[ids.ID][]*avax.UTXO{
   373  			constants.PlatformChainID: utxos,
   374  			sourceChainID:             importedUTXOs,
   375  		})
   376  
   377  		backend = NewBackend(testContext, chainUTXOs, nil)
   378  
   379  		// builder
   380  		utxoAddr = utxosKey.Address()
   381  		builder  = builder.New(set.Of(utxoAddr), testContext, backend)
   382  
   383  		// data to build the transaction
   384  		importKey = testKeys[0]
   385  		importTo  = &secp256k1fx.OutputOwners{
   386  			Threshold: 1,
   387  			Addrs: []ids.ShortID{
   388  				importKey.Address(),
   389  			},
   390  		}
   391  	)
   392  
   393  	// build the transaction
   394  	utx, err := builder.NewImportTx(
   395  		sourceChainID,
   396  		importTo,
   397  	)
   398  	require.NoError(err)
   399  
   400  	// check UTXOs selection and fee financing
   401  	ins := utx.Ins
   402  	outs := utx.Outs
   403  	importedIns := utx.ImportedInputs
   404  	require.Empty(ins) // we spend the imported input (at least partially)
   405  	require.Len(importedIns, 1)
   406  	require.Len(outs, 1)
   407  
   408  	expectedConsumed := testContext.BaseTxFee
   409  	consumed := importedIns[0].In.Amount() - outs[0].Out.Amount()
   410  	require.Equal(expectedConsumed, consumed)
   411  }
   412  
   413  func TestExportTx(t *testing.T) {
   414  	var (
   415  		require = require.New(t)
   416  
   417  		// backend
   418  		utxosKey   = testKeys[1]
   419  		utxos      = makeTestUTXOs(utxosKey)
   420  		chainUTXOs = common.NewDeterministicChainUTXOs(require, map[ids.ID][]*avax.UTXO{
   421  			constants.PlatformChainID: utxos,
   422  		})
   423  		backend = NewBackend(testContext, chainUTXOs, nil)
   424  
   425  		// builder
   426  		utxoAddr = utxosKey.Address()
   427  		builder  = builder.New(set.Of(utxoAddr), testContext, backend)
   428  
   429  		// data to build the transaction
   430  		subnetID        = ids.GenerateTestID()
   431  		exportedOutputs = []*avax.TransferableOutput{{
   432  			Asset: avax.Asset{ID: avaxAssetID},
   433  			Out: &secp256k1fx.TransferOutput{
   434  				Amt: 7 * units.Avax,
   435  				OutputOwners: secp256k1fx.OutputOwners{
   436  					Threshold: 1,
   437  					Addrs:     []ids.ShortID{utxoAddr},
   438  				},
   439  			},
   440  		}}
   441  	)
   442  
   443  	// build the transaction
   444  	utx, err := builder.NewExportTx(
   445  		subnetID,
   446  		exportedOutputs,
   447  	)
   448  	require.NoError(err)
   449  
   450  	// check UTXOs selection and fee financing
   451  	ins := utx.Ins
   452  	outs := utx.Outs
   453  	require.Len(ins, 2)
   454  	require.Len(outs, 1)
   455  
   456  	expectedConsumed := testContext.BaseTxFee + exportedOutputs[0].Out.Amount()
   457  	consumed := ins[0].In.Amount() + ins[1].In.Amount() - outs[0].Out.Amount()
   458  	require.Equal(expectedConsumed, consumed)
   459  	require.Equal(utx.ExportedOutputs, exportedOutputs)
   460  }
   461  
   462  func TestTransformSubnetTx(t *testing.T) {
   463  	var (
   464  		require = require.New(t)
   465  
   466  		// backend
   467  		utxosKey   = testKeys[1]
   468  		utxos      = makeTestUTXOs(utxosKey)
   469  		chainUTXOs = common.NewDeterministicChainUTXOs(require, map[ids.ID][]*avax.UTXO{
   470  			constants.PlatformChainID: utxos,
   471  		})
   472  
   473  		subnetID       = ids.GenerateTestID()
   474  		subnetAuthKey  = testKeys[0]
   475  		subnetAuthAddr = subnetAuthKey.Address()
   476  		subnetOwner    = &secp256k1fx.OutputOwners{
   477  			Threshold: 1,
   478  			Addrs:     []ids.ShortID{subnetAuthAddr},
   479  		}
   480  		subnets = map[ids.ID]*txs.Tx{
   481  			subnetID: {
   482  				Unsigned: &txs.CreateSubnetTx{
   483  					Owner: subnetOwner,
   484  				},
   485  			},
   486  		}
   487  
   488  		backend = NewBackend(testContext, chainUTXOs, subnets)
   489  
   490  		// builder
   491  		utxoAddr = utxosKey.Address()
   492  		builder  = builder.New(set.Of(utxoAddr, subnetAuthAddr), testContext, backend)
   493  
   494  		// data to build the transaction
   495  		initialSupply = 40 * units.MegaAvax
   496  		maxSupply     = 100 * units.MegaAvax
   497  	)
   498  
   499  	// build the transaction
   500  	utx, err := builder.NewTransformSubnetTx(
   501  		subnetID,
   502  		subnetAssetID,
   503  		initialSupply,                 // initial supply
   504  		maxSupply,                     // max supply
   505  		reward.PercentDenominator,     // min consumption rate
   506  		reward.PercentDenominator,     // max consumption rate
   507  		1,                             // min validator stake
   508  		100*units.MegaAvax,            // max validator stake
   509  		time.Second,                   // min stake duration
   510  		365*24*time.Hour,              // max stake duration
   511  		0,                             // min delegation fee
   512  		1,                             // min delegator stake
   513  		5,                             // max validator weight factor
   514  		.80*reward.PercentDenominator, // uptime requirement
   515  	)
   516  	require.NoError(err)
   517  
   518  	// check UTXOs selection and fee financing
   519  	ins := utx.Ins
   520  	outs := utx.Outs
   521  	require.Len(ins, 2)
   522  	require.Len(outs, 2)
   523  
   524  	expectedConsumedSubnetAsset := maxSupply - initialSupply
   525  	consumedSubnetAsset := ins[0].In.Amount() - outs[1].Out.Amount()
   526  	require.Equal(expectedConsumedSubnetAsset, consumedSubnetAsset)
   527  	expectedConsumed := testContext.TransformSubnetTxFee
   528  	consumed := ins[1].In.Amount() - outs[0].Out.Amount()
   529  	require.Equal(expectedConsumed, consumed)
   530  }
   531  
   532  func TestAddPermissionlessValidatorTx(t *testing.T) {
   533  	var (
   534  		require = require.New(t)
   535  
   536  		// backend
   537  		utxosKey   = testKeys[1]
   538  		utxos      = makeTestUTXOs(utxosKey)
   539  		chainUTXOs = common.NewDeterministicChainUTXOs(require, map[ids.ID][]*avax.UTXO{
   540  			constants.PlatformChainID: utxos,
   541  		})
   542  		backend = NewBackend(testContext, chainUTXOs, nil)
   543  
   544  		// builder
   545  		utxoAddr   = utxosKey.Address()
   546  		rewardKey  = testKeys[0]
   547  		rewardAddr = rewardKey.Address()
   548  		builder    = builder.New(set.Of(utxoAddr, rewardAddr), testContext, backend)
   549  
   550  		// data to build the transaction
   551  		validationRewardsOwner = &secp256k1fx.OutputOwners{
   552  			Threshold: 1,
   553  			Addrs: []ids.ShortID{
   554  				rewardAddr,
   555  			},
   556  		}
   557  		delegationRewardsOwner = &secp256k1fx.OutputOwners{
   558  			Threshold: 1,
   559  			Addrs: []ids.ShortID{
   560  				rewardAddr,
   561  			},
   562  		}
   563  	)
   564  
   565  	sk, err := bls.NewSecretKey()
   566  	require.NoError(err)
   567  
   568  	// build the transaction
   569  	utx, err := builder.NewAddPermissionlessValidatorTx(
   570  		&txs.SubnetValidator{
   571  			Validator: txs.Validator{
   572  				NodeID: ids.GenerateTestNodeID(),
   573  				End:    uint64(time.Now().Add(time.Hour).Unix()),
   574  				Wght:   2 * units.Avax,
   575  			},
   576  			Subnet: constants.PrimaryNetworkID,
   577  		},
   578  		signer.NewProofOfPossession(sk),
   579  		avaxAssetID,
   580  		validationRewardsOwner,
   581  		delegationRewardsOwner,
   582  		reward.PercentDenominator,
   583  	)
   584  	require.NoError(err)
   585  
   586  	// check UTXOs selection and fee financing
   587  	ins := utx.Ins
   588  	staked := utx.StakeOuts
   589  	outs := utx.Outs
   590  	require.Len(ins, 4)
   591  	require.Len(staked, 2)
   592  	require.Len(outs, 2)
   593  
   594  	expectedConsumedSubnetAsset := utx.Validator.Weight()
   595  	consumedSubnetAsset := staked[0].Out.Amount() + staked[1].Out.Amount()
   596  	require.Equal(expectedConsumedSubnetAsset, consumedSubnetAsset)
   597  	expectedConsumed := testContext.AddPrimaryNetworkValidatorFee
   598  	consumed := ins[1].In.Amount() + ins[3].In.Amount() - outs[0].Out.Amount()
   599  	require.Equal(expectedConsumed, consumed)
   600  }
   601  
   602  func TestAddPermissionlessDelegatorTx(t *testing.T) {
   603  	var (
   604  		require = require.New(t)
   605  
   606  		// backend
   607  		utxosKey   = testKeys[1]
   608  		utxos      = makeTestUTXOs(utxosKey)
   609  		chainUTXOs = common.NewDeterministicChainUTXOs(require, map[ids.ID][]*avax.UTXO{
   610  			constants.PlatformChainID: utxos,
   611  		})
   612  		backend = NewBackend(testContext, chainUTXOs, nil)
   613  
   614  		// builder
   615  		utxoAddr   = utxosKey.Address()
   616  		rewardKey  = testKeys[0]
   617  		rewardAddr = rewardKey.Address()
   618  		builder    = builder.New(set.Of(utxoAddr, rewardAddr), testContext, backend)
   619  
   620  		// data to build the transaction
   621  		rewardsOwner = &secp256k1fx.OutputOwners{
   622  			Threshold: 1,
   623  			Addrs: []ids.ShortID{
   624  				rewardAddr,
   625  			},
   626  		}
   627  	)
   628  
   629  	// build the transaction
   630  	utx, err := builder.NewAddPermissionlessDelegatorTx(
   631  		&txs.SubnetValidator{
   632  			Validator: txs.Validator{
   633  				NodeID: ids.GenerateTestNodeID(),
   634  				End:    uint64(time.Now().Add(time.Hour).Unix()),
   635  				Wght:   2 * units.Avax,
   636  			},
   637  			Subnet: constants.PrimaryNetworkID,
   638  		},
   639  		avaxAssetID,
   640  		rewardsOwner,
   641  	)
   642  	require.NoError(err)
   643  
   644  	// check UTXOs selection and fee financing
   645  	ins := utx.Ins
   646  	staked := utx.StakeOuts
   647  	outs := utx.Outs
   648  	require.Len(ins, 4)
   649  	require.Len(staked, 2)
   650  	require.Len(outs, 2)
   651  
   652  	expectedConsumedSubnetAsset := utx.Validator.Weight()
   653  	consumedSubnetAsset := staked[0].Out.Amount() + staked[1].Out.Amount()
   654  	require.Equal(expectedConsumedSubnetAsset, consumedSubnetAsset)
   655  	expectedConsumed := testContext.AddPrimaryNetworkDelegatorFee
   656  	consumed := ins[1].In.Amount() + ins[3].In.Amount() - outs[0].Out.Amount()
   657  	require.Equal(expectedConsumed, consumed)
   658  }
   659  
   660  func makeTestUTXOs(utxosKey *secp256k1.PrivateKey) []*avax.UTXO {
   661  	// Note: we avoid ids.GenerateTestNodeID here to make sure that UTXO IDs won't change
   662  	// run by run. This simplifies checking what utxos are included in the built txs.
   663  	const utxosOffset uint64 = 2024
   664  
   665  	utxosAddr := utxosKey.Address()
   666  	return []*avax.UTXO{
   667  		{ // a small UTXO first, which should not be enough to pay fees
   668  			UTXOID: avax.UTXOID{
   669  				TxID:        ids.Empty.Prefix(utxosOffset),
   670  				OutputIndex: uint32(utxosOffset),
   671  			},
   672  			Asset: avax.Asset{ID: avaxAssetID},
   673  			Out: &secp256k1fx.TransferOutput{
   674  				Amt: 2 * units.MilliAvax,
   675  				OutputOwners: secp256k1fx.OutputOwners{
   676  					Locktime:  0,
   677  					Addrs:     []ids.ShortID{utxosAddr},
   678  					Threshold: 1,
   679  				},
   680  			},
   681  		},
   682  		{ // a locked, small UTXO
   683  			UTXOID: avax.UTXOID{
   684  				TxID:        ids.Empty.Prefix(utxosOffset + 1),
   685  				OutputIndex: uint32(utxosOffset + 1),
   686  			},
   687  			Asset: avax.Asset{ID: avaxAssetID},
   688  			Out: &stakeable.LockOut{
   689  				Locktime: uint64(time.Now().Add(time.Hour).Unix()),
   690  				TransferableOut: &secp256k1fx.TransferOutput{
   691  					Amt: 3 * units.MilliAvax,
   692  					OutputOwners: secp256k1fx.OutputOwners{
   693  						Threshold: 1,
   694  						Addrs:     []ids.ShortID{utxosAddr},
   695  					},
   696  				},
   697  			},
   698  		},
   699  		{ // a subnetAssetID denominated UTXO
   700  			UTXOID: avax.UTXOID{
   701  				TxID:        ids.Empty.Prefix(utxosOffset + 2),
   702  				OutputIndex: uint32(utxosOffset + 2),
   703  			},
   704  			Asset: avax.Asset{ID: subnetAssetID},
   705  			Out: &secp256k1fx.TransferOutput{
   706  				Amt: 99 * units.MegaAvax,
   707  				OutputOwners: secp256k1fx.OutputOwners{
   708  					Locktime:  0,
   709  					Addrs:     []ids.ShortID{utxosAddr},
   710  					Threshold: 1,
   711  				},
   712  			},
   713  		},
   714  		{ // a locked, large UTXO
   715  			UTXOID: avax.UTXOID{
   716  				TxID:        ids.Empty.Prefix(utxosOffset + 3),
   717  				OutputIndex: uint32(utxosOffset + 3),
   718  			},
   719  			Asset: avax.Asset{ID: avaxAssetID},
   720  			Out: &stakeable.LockOut{
   721  				Locktime: uint64(time.Now().Add(time.Hour).Unix()),
   722  				TransferableOut: &secp256k1fx.TransferOutput{
   723  					Amt: 88 * units.Avax,
   724  					OutputOwners: secp256k1fx.OutputOwners{
   725  						Threshold: 1,
   726  						Addrs:     []ids.ShortID{utxosAddr},
   727  					},
   728  				},
   729  			},
   730  		},
   731  		{ // a large UTXO last, which should be enough to pay any fee by itself
   732  			UTXOID: avax.UTXOID{
   733  				TxID:        ids.Empty.Prefix(utxosOffset + 4),
   734  				OutputIndex: uint32(utxosOffset + 4),
   735  			},
   736  			Asset: avax.Asset{ID: avaxAssetID},
   737  			Out: &secp256k1fx.TransferOutput{
   738  				Amt: 9 * units.Avax,
   739  				OutputOwners: secp256k1fx.OutputOwners{
   740  					Locktime:  0,
   741  					Addrs:     []ids.ShortID{utxosAddr},
   742  					Threshold: 1,
   743  				},
   744  			},
   745  		},
   746  	}
   747  }