github.com/MetalBlockchain/metalgo@v1.11.9/vms/platformvm/txs/transform_subnet_tx_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 txs
     5  
     6  import (
     7  	"encoding/json"
     8  	"testing"
     9  
    10  	"github.com/stretchr/testify/require"
    11  	"go.uber.org/mock/gomock"
    12  
    13  	"github.com/MetalBlockchain/metalgo/ids"
    14  	"github.com/MetalBlockchain/metalgo/snow"
    15  	"github.com/MetalBlockchain/metalgo/utils"
    16  	"github.com/MetalBlockchain/metalgo/utils/constants"
    17  	"github.com/MetalBlockchain/metalgo/utils/units"
    18  	"github.com/MetalBlockchain/metalgo/vms/components/avax"
    19  	"github.com/MetalBlockchain/metalgo/vms/components/verify"
    20  	"github.com/MetalBlockchain/metalgo/vms/platformvm/reward"
    21  	"github.com/MetalBlockchain/metalgo/vms/platformvm/stakeable"
    22  	"github.com/MetalBlockchain/metalgo/vms/secp256k1fx"
    23  	"github.com/MetalBlockchain/metalgo/vms/types"
    24  )
    25  
    26  func TestTransformSubnetTxSerialization(t *testing.T) {
    27  	require := require.New(t)
    28  
    29  	addr := ids.ShortID{
    30  		0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
    31  		0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
    32  		0x44, 0x55, 0x66, 0x77,
    33  	}
    34  
    35  	avaxAssetID, err := ids.FromString("FvwEAhmxKfeiG8SnEvq42hc6whRyY3EFYAvebMqDNDGCgxN5Z")
    36  	require.NoError(err)
    37  
    38  	customAssetID := ids.ID{
    39  		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
    40  		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
    41  		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
    42  		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
    43  	}
    44  
    45  	txID := ids.ID{
    46  		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
    47  		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
    48  		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
    49  		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
    50  	}
    51  	subnetID := ids.ID{
    52  		0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
    53  		0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
    54  		0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
    55  		0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
    56  	}
    57  
    58  	simpleTransformTx := &TransformSubnetTx{
    59  		BaseTx: BaseTx{
    60  			BaseTx: avax.BaseTx{
    61  				NetworkID:    constants.MainnetID,
    62  				BlockchainID: constants.PlatformChainID,
    63  				Outs:         []*avax.TransferableOutput{},
    64  				Ins: []*avax.TransferableInput{
    65  					{
    66  						UTXOID: avax.UTXOID{
    67  							TxID:        txID,
    68  							OutputIndex: 1,
    69  						},
    70  						Asset: avax.Asset{
    71  							ID: avaxAssetID,
    72  						},
    73  						In: &secp256k1fx.TransferInput{
    74  							Amt: 10 * units.Avax,
    75  							Input: secp256k1fx.Input{
    76  								SigIndices: []uint32{5},
    77  							},
    78  						},
    79  					},
    80  					{
    81  						UTXOID: avax.UTXOID{
    82  							TxID:        txID,
    83  							OutputIndex: 2,
    84  						},
    85  						Asset: avax.Asset{
    86  							ID: customAssetID,
    87  						},
    88  						In: &secp256k1fx.TransferInput{
    89  							Amt: 0xefffffffffffffff,
    90  							Input: secp256k1fx.Input{
    91  								SigIndices: []uint32{0},
    92  							},
    93  						},
    94  					},
    95  				},
    96  				Memo: types.JSONByteSlice{},
    97  			},
    98  		},
    99  		Subnet:                   subnetID,
   100  		AssetID:                  customAssetID,
   101  		InitialSupply:            0x1000000000000000,
   102  		MaximumSupply:            0xffffffffffffffff,
   103  		MinConsumptionRate:       1_000,
   104  		MaxConsumptionRate:       1_000_000,
   105  		MinValidatorStake:        1,
   106  		MaxValidatorStake:        0xffffffffffffffff,
   107  		MinStakeDuration:         1,
   108  		MaxStakeDuration:         365 * 24 * 60 * 60,
   109  		MinDelegationFee:         reward.PercentDenominator,
   110  		MinDelegatorStake:        1,
   111  		MaxValidatorWeightFactor: 1,
   112  		UptimeRequirement:        .95 * reward.PercentDenominator,
   113  		SubnetAuth: &secp256k1fx.Input{
   114  			SigIndices: []uint32{3},
   115  		},
   116  	}
   117  	require.NoError(simpleTransformTx.SyntacticVerify(&snow.Context{
   118  		NetworkID:   1,
   119  		ChainID:     constants.PlatformChainID,
   120  		AVAXAssetID: avaxAssetID,
   121  	}))
   122  
   123  	expectedUnsignedSimpleTransformTxBytes := []byte{
   124  		// Codec version
   125  		0x00, 0x00,
   126  		// TransformSubnetTx type ID
   127  		0x00, 0x00, 0x00, 0x18,
   128  		// Mainnet network ID
   129  		0x00, 0x00, 0x00, 0x01,
   130  		// P-chain blockchain ID
   131  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   132  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   133  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   134  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   135  		// number of outputs
   136  		0x00, 0x00, 0x00, 0x00,
   137  		// number of inputs
   138  		0x00, 0x00, 0x00, 0x02,
   139  		// inputs[0]
   140  		// TxID
   141  		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
   142  		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
   143  		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
   144  		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
   145  		// Tx output index
   146  		0x00, 0x00, 0x00, 0x01,
   147  		// Mainnet AVAX assetID
   148  		0x21, 0xe6, 0x73, 0x17, 0xcb, 0xc4, 0xbe, 0x2a,
   149  		0xeb, 0x00, 0x67, 0x7a, 0xd6, 0x46, 0x27, 0x78,
   150  		0xa8, 0xf5, 0x22, 0x74, 0xb9, 0xd6, 0x05, 0xdf,
   151  		0x25, 0x91, 0xb2, 0x30, 0x27, 0xa8, 0x7d, 0xff,
   152  		// secp256k1fx transfer input type ID
   153  		0x00, 0x00, 0x00, 0x05,
   154  		// input amount = 10 AVAX
   155  		0x00, 0x00, 0x00, 0x02, 0x54, 0x0b, 0xe4, 0x00,
   156  		// number of signatures needed in input
   157  		0x00, 0x00, 0x00, 0x01,
   158  		// index of signer
   159  		0x00, 0x00, 0x00, 0x05,
   160  		// inputs[1]
   161  		// TxID
   162  		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
   163  		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
   164  		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
   165  		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
   166  		// Tx output index
   167  		0x00, 0x00, 0x00, 0x02,
   168  		// custom asset ID
   169  		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
   170  		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
   171  		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
   172  		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
   173  		// secp256k1fx transfer input type ID
   174  		0x00, 0x00, 0x00, 0x05,
   175  		// input amount
   176  		0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
   177  		// number of signatures needed in input
   178  		0x00, 0x00, 0x00, 0x01,
   179  		// index of signer
   180  		0x00, 0x00, 0x00, 0x00,
   181  		// length of memo
   182  		0x00, 0x00, 0x00, 0x00,
   183  		// subnetID being transformed
   184  		0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
   185  		0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
   186  		0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
   187  		0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
   188  		// staking asset ID
   189  		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
   190  		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
   191  		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
   192  		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
   193  		// initial supply
   194  		0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   195  		// maximum supply
   196  		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
   197  		// minimum consumption rate
   198  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xe8,
   199  		// maximum consumption rate
   200  		0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x42, 0x40,
   201  		// minimum staking amount
   202  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
   203  		// maximum staking amount
   204  		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
   205  		// minimum staking duration
   206  		0x00, 0x00, 0x00, 0x01,
   207  		// maximum staking duration
   208  		0x01, 0xe1, 0x33, 0x80,
   209  		// minimum delegation fee
   210  		0x00, 0x0f, 0x42, 0x40,
   211  		// minimum delegation amount
   212  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
   213  		// maximum validator weight factor
   214  		0x01,
   215  		// uptime requirement
   216  		0x00, 0x0e, 0x7e, 0xf0,
   217  		// secp256k1fx authorization type ID
   218  		0x00, 0x00, 0x00, 0x0a,
   219  		// number of signatures needed in authorization
   220  		0x00, 0x00, 0x00, 0x01,
   221  		// authorization signfature index
   222  		0x00, 0x00, 0x00, 0x03,
   223  	}
   224  	var unsignedSimpleTransformTx UnsignedTx = simpleTransformTx
   225  	unsignedSimpleTransformTxBytes, err := Codec.Marshal(CodecVersion, &unsignedSimpleTransformTx)
   226  	require.NoError(err)
   227  	require.Equal(expectedUnsignedSimpleTransformTxBytes, unsignedSimpleTransformTxBytes)
   228  
   229  	complexTransformTx := &TransformSubnetTx{
   230  		BaseTx: BaseTx{
   231  			BaseTx: avax.BaseTx{
   232  				NetworkID:    constants.MainnetID,
   233  				BlockchainID: constants.PlatformChainID,
   234  				Outs: []*avax.TransferableOutput{
   235  					{
   236  						Asset: avax.Asset{
   237  							ID: avaxAssetID,
   238  						},
   239  						Out: &stakeable.LockOut{
   240  							Locktime: 87654321,
   241  							TransferableOut: &secp256k1fx.TransferOutput{
   242  								Amt: 1,
   243  								OutputOwners: secp256k1fx.OutputOwners{
   244  									Locktime:  12345678,
   245  									Threshold: 0,
   246  									Addrs:     []ids.ShortID{},
   247  								},
   248  							},
   249  						},
   250  					},
   251  					{
   252  						Asset: avax.Asset{
   253  							ID: customAssetID,
   254  						},
   255  						Out: &stakeable.LockOut{
   256  							Locktime: 876543210,
   257  							TransferableOut: &secp256k1fx.TransferOutput{
   258  								Amt: 0xffffffffffffffff,
   259  								OutputOwners: secp256k1fx.OutputOwners{
   260  									Locktime:  0,
   261  									Threshold: 1,
   262  									Addrs: []ids.ShortID{
   263  										addr,
   264  									},
   265  								},
   266  							},
   267  						},
   268  					},
   269  				},
   270  				Ins: []*avax.TransferableInput{
   271  					{
   272  						UTXOID: avax.UTXOID{
   273  							TxID:        txID,
   274  							OutputIndex: 1,
   275  						},
   276  						Asset: avax.Asset{
   277  							ID: avaxAssetID,
   278  						},
   279  						In: &secp256k1fx.TransferInput{
   280  							Amt: units.KiloAvax,
   281  							Input: secp256k1fx.Input{
   282  								SigIndices: []uint32{2, 5},
   283  							},
   284  						},
   285  					},
   286  					{
   287  						UTXOID: avax.UTXOID{
   288  							TxID:        txID,
   289  							OutputIndex: 2,
   290  						},
   291  						Asset: avax.Asset{
   292  							ID: customAssetID,
   293  						},
   294  						In: &stakeable.LockIn{
   295  							Locktime: 876543210,
   296  							TransferableIn: &secp256k1fx.TransferInput{
   297  								Amt: 0xefffffffffffffff,
   298  								Input: secp256k1fx.Input{
   299  									SigIndices: []uint32{0},
   300  								},
   301  							},
   302  						},
   303  					},
   304  					{
   305  						UTXOID: avax.UTXOID{
   306  							TxID:        txID,
   307  							OutputIndex: 3,
   308  						},
   309  						Asset: avax.Asset{
   310  							ID: customAssetID,
   311  						},
   312  						In: &secp256k1fx.TransferInput{
   313  							Amt: 0x1000000000000000,
   314  							Input: secp256k1fx.Input{
   315  								SigIndices: []uint32{},
   316  							},
   317  						},
   318  					},
   319  				},
   320  				Memo: types.JSONByteSlice("😅\nwell that's\x01\x23\x45!"),
   321  			},
   322  		},
   323  		Subnet:                   subnetID,
   324  		AssetID:                  customAssetID,
   325  		InitialSupply:            0x1000000000000000,
   326  		MaximumSupply:            0x1000000000000000,
   327  		MinConsumptionRate:       0,
   328  		MaxConsumptionRate:       0,
   329  		MinValidatorStake:        1,
   330  		MaxValidatorStake:        0x1000000000000000,
   331  		MinStakeDuration:         1,
   332  		MaxStakeDuration:         1,
   333  		MinDelegationFee:         0,
   334  		MinDelegatorStake:        0xffffffffffffffff,
   335  		MaxValidatorWeightFactor: 255,
   336  		UptimeRequirement:        0,
   337  		SubnetAuth: &secp256k1fx.Input{
   338  			SigIndices: []uint32{},
   339  		},
   340  	}
   341  	avax.SortTransferableOutputs(complexTransformTx.Outs, Codec)
   342  	utils.Sort(complexTransformTx.Ins)
   343  	require.NoError(complexTransformTx.SyntacticVerify(&snow.Context{
   344  		NetworkID:   1,
   345  		ChainID:     constants.PlatformChainID,
   346  		AVAXAssetID: avaxAssetID,
   347  	}))
   348  
   349  	expectedUnsignedComplexTransformTxBytes := []byte{
   350  		// Codec version
   351  		0x00, 0x00,
   352  		// TransformSubnetTx type ID
   353  		0x00, 0x00, 0x00, 0x18,
   354  		// Mainnet network ID
   355  		0x00, 0x00, 0x00, 0x01,
   356  		// P-chain blockchain ID
   357  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   358  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   359  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   360  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   361  		// number of outputs
   362  		0x00, 0x00, 0x00, 0x02,
   363  		// outputs[0]
   364  		// Mainnet AVAX asset ID
   365  		0x21, 0xe6, 0x73, 0x17, 0xcb, 0xc4, 0xbe, 0x2a,
   366  		0xeb, 0x00, 0x67, 0x7a, 0xd6, 0x46, 0x27, 0x78,
   367  		0xa8, 0xf5, 0x22, 0x74, 0xb9, 0xd6, 0x05, 0xdf,
   368  		0x25, 0x91, 0xb2, 0x30, 0x27, 0xa8, 0x7d, 0xff,
   369  		// Stakeable locked output type ID
   370  		0x00, 0x00, 0x00, 0x16,
   371  		// Locktime
   372  		0x00, 0x00, 0x00, 0x00, 0x05, 0x39, 0x7f, 0xb1,
   373  		// seck256k1fx tranfer output type ID
   374  		0x00, 0x00, 0x00, 0x07,
   375  		// amount
   376  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
   377  		// secp256k1fx locktime
   378  		0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x61, 0x4e,
   379  		// threshold
   380  		0x00, 0x00, 0x00, 0x00,
   381  		// number of addresses
   382  		0x00, 0x00, 0x00, 0x00,
   383  		// outputs[1]
   384  		// custom assest ID
   385  		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
   386  		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
   387  		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
   388  		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
   389  		// Stakeable locked output type ID
   390  		0x00, 0x00, 0x00, 0x16,
   391  		// Locktime
   392  		0x00, 0x00, 0x00, 0x00, 0x34, 0x3e, 0xfc, 0xea,
   393  		// seck256k1fx tranfer output type ID
   394  		0x00, 0x00, 0x00, 0x07,
   395  		// amount
   396  		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
   397  		// secp256k1fx locktime
   398  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   399  		// threshold
   400  		0x00, 0x00, 0x00, 0x01,
   401  		// number of addresses
   402  		0x00, 0x00, 0x00, 0x01,
   403  		// address[0]
   404  		0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
   405  		0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
   406  		0x44, 0x55, 0x66, 0x77,
   407  		// number of inputs
   408  		0x00, 0x00, 0x00, 0x03,
   409  		// inputs[0]
   410  		// TxID
   411  		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
   412  		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
   413  		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
   414  		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
   415  		// Tx output index
   416  		0x00, 0x00, 0x00, 0x01,
   417  		// Mainnet AVAX asset ID
   418  		0x21, 0xe6, 0x73, 0x17, 0xcb, 0xc4, 0xbe, 0x2a,
   419  		0xeb, 0x00, 0x67, 0x7a, 0xd6, 0x46, 0x27, 0x78,
   420  		0xa8, 0xf5, 0x22, 0x74, 0xb9, 0xd6, 0x05, 0xdf,
   421  		0x25, 0x91, 0xb2, 0x30, 0x27, 0xa8, 0x7d, 0xff,
   422  		// secp256k1fx transfer input type ID
   423  		0x00, 0x00, 0x00, 0x05,
   424  		// amount = 1,000 AVAX
   425  		0x00, 0x00, 0x00, 0xe8, 0xd4, 0xa5, 0x10, 0x00,
   426  		// number of signatures indices
   427  		0x00, 0x00, 0x00, 0x02,
   428  		// first signature index
   429  		0x00, 0x00, 0x00, 0x02,
   430  		// second signature index
   431  		0x00, 0x00, 0x00, 0x05,
   432  		// inputs[1]
   433  		// TxID
   434  		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
   435  		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
   436  		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
   437  		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
   438  		// Tx output index
   439  		0x00, 0x00, 0x00, 0x02,
   440  		// custom asset ID
   441  		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
   442  		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
   443  		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
   444  		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
   445  		// stakeable locked input type ID
   446  		0x00, 0x00, 0x00, 0x15,
   447  		// locktime
   448  		0x00, 0x00, 0x00, 0x00, 0x34, 0x3e, 0xfc, 0xea,
   449  		// secp256k1fx transfer input type ID
   450  		0x00, 0x00, 0x00, 0x05,
   451  		// input amount
   452  		0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
   453  		// number of signatures needed in input
   454  		0x00, 0x00, 0x00, 0x01,
   455  		// index of signer
   456  		0x00, 0x00, 0x00, 0x00,
   457  		// inputs[2]
   458  		// TxID
   459  		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
   460  		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
   461  		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
   462  		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
   463  		// Tx output index
   464  		0x00, 0x00, 0x00, 0x03,
   465  		// custom asset ID
   466  		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
   467  		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
   468  		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
   469  		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
   470  		// secp256k1fx transfer input type ID
   471  		0x00, 0x00, 0x00, 0x05,
   472  		// input amount
   473  		0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   474  		// number of signatures needed in input
   475  		0x00, 0x00, 0x00, 0x00,
   476  		// memo length
   477  		0x00, 0x00, 0x00, 0x14,
   478  		// memo
   479  		0xf0, 0x9f, 0x98, 0x85, 0x0a, 0x77, 0x65, 0x6c,
   480  		0x6c, 0x20, 0x74, 0x68, 0x61, 0x74, 0x27, 0x73,
   481  		0x01, 0x23, 0x45, 0x21,
   482  		// subnetID being transformed
   483  		0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
   484  		0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
   485  		0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
   486  		0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
   487  		// staking asset ID
   488  		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
   489  		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
   490  		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
   491  		0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31,
   492  		// initial supply
   493  		0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   494  		// maximum supply
   495  		0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   496  		// minimum consumption rate
   497  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   498  		// maximum consumption rate
   499  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   500  		// minimum staking amount
   501  		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
   502  		// maximum staking amount
   503  		0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   504  		// minimum staking duration
   505  		0x00, 0x00, 0x00, 0x01,
   506  		// maximum staking duration
   507  		0x00, 0x00, 0x00, 0x01,
   508  		// minimum delegation fee
   509  		0x00, 0x00, 0x00, 0x00,
   510  		// minimum delegation amount
   511  		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
   512  		// maximum validator weight factor
   513  		0xff,
   514  		// uptime requirement
   515  		0x00, 0x00, 0x00, 0x00,
   516  		// secp256k1fx authorization type ID
   517  		0x00, 0x00, 0x00, 0x0a,
   518  		// number of signatures needed in authorization
   519  		0x00, 0x00, 0x00, 0x00,
   520  	}
   521  	var unsignedComplexTransformTx UnsignedTx = complexTransformTx
   522  	unsignedComplexTransformTxBytes, err := Codec.Marshal(CodecVersion, &unsignedComplexTransformTx)
   523  	require.NoError(err)
   524  	require.Equal(expectedUnsignedComplexTransformTxBytes, unsignedComplexTransformTxBytes)
   525  
   526  	aliaser := ids.NewAliaser()
   527  	require.NoError(aliaser.Alias(constants.PlatformChainID, "P"))
   528  
   529  	unsignedComplexTransformTx.InitCtx(&snow.Context{
   530  		NetworkID:   1,
   531  		ChainID:     constants.PlatformChainID,
   532  		AVAXAssetID: avaxAssetID,
   533  		BCLookup:    aliaser,
   534  	})
   535  
   536  	unsignedComplexTransformTxJSONBytes, err := json.MarshalIndent(unsignedComplexTransformTx, "", "\t")
   537  	require.NoError(err)
   538  	require.Equal(`{
   539  	"networkID": 1,
   540  	"blockchainID": "11111111111111111111111111111111LpoYY",
   541  	"outputs": [
   542  		{
   543  			"assetID": "FvwEAhmxKfeiG8SnEvq42hc6whRyY3EFYAvebMqDNDGCgxN5Z",
   544  			"fxID": "spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ",
   545  			"output": {
   546  				"locktime": 87654321,
   547  				"output": {
   548  					"addresses": [],
   549  					"amount": 1,
   550  					"locktime": 12345678,
   551  					"threshold": 0
   552  				}
   553  			}
   554  		},
   555  		{
   556  			"assetID": "2Ab62uWwJw1T6VvmKD36ufsiuGZuX1pGykXAvPX1LtjTRHxwcc",
   557  			"fxID": "spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ",
   558  			"output": {
   559  				"locktime": 876543210,
   560  				"output": {
   561  					"addresses": [
   562  						"P-metal1g32kvaugnx4tk3z4vemc3xd2hdz92enhqaj6ex"
   563  					],
   564  					"amount": 18446744073709551615,
   565  					"locktime": 0,
   566  					"threshold": 1
   567  				}
   568  			}
   569  		}
   570  	],
   571  	"inputs": [
   572  		{
   573  			"txID": "2wiU5PnFTjTmoAXGZutHAsPF36qGGyLHYHj9G1Aucfmb3JFFGN",
   574  			"outputIndex": 1,
   575  			"assetID": "FvwEAhmxKfeiG8SnEvq42hc6whRyY3EFYAvebMqDNDGCgxN5Z",
   576  			"fxID": "spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ",
   577  			"input": {
   578  				"amount": 1000000000000,
   579  				"signatureIndices": [
   580  					2,
   581  					5
   582  				]
   583  			}
   584  		},
   585  		{
   586  			"txID": "2wiU5PnFTjTmoAXGZutHAsPF36qGGyLHYHj9G1Aucfmb3JFFGN",
   587  			"outputIndex": 2,
   588  			"assetID": "2Ab62uWwJw1T6VvmKD36ufsiuGZuX1pGykXAvPX1LtjTRHxwcc",
   589  			"fxID": "spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ",
   590  			"input": {
   591  				"locktime": 876543210,
   592  				"input": {
   593  					"amount": 17293822569102704639,
   594  					"signatureIndices": [
   595  						0
   596  					]
   597  				}
   598  			}
   599  		},
   600  		{
   601  			"txID": "2wiU5PnFTjTmoAXGZutHAsPF36qGGyLHYHj9G1Aucfmb3JFFGN",
   602  			"outputIndex": 3,
   603  			"assetID": "2Ab62uWwJw1T6VvmKD36ufsiuGZuX1pGykXAvPX1LtjTRHxwcc",
   604  			"fxID": "spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ",
   605  			"input": {
   606  				"amount": 1152921504606846976,
   607  				"signatureIndices": []
   608  			}
   609  		}
   610  	],
   611  	"memo": "0xf09f98850a77656c6c2074686174277301234521",
   612  	"subnetID": "SkB92YpWm4UpburLz9tEKZw2i67H3FF6YkjaU4BkFUDTG9Xm",
   613  	"assetID": "2Ab62uWwJw1T6VvmKD36ufsiuGZuX1pGykXAvPX1LtjTRHxwcc",
   614  	"initialSupply": 1152921504606846976,
   615  	"maximumSupply": 1152921504606846976,
   616  	"minConsumptionRate": 0,
   617  	"maxConsumptionRate": 0,
   618  	"minValidatorStake": 1,
   619  	"maxValidatorStake": 1152921504606846976,
   620  	"minStakeDuration": 1,
   621  	"maxStakeDuration": 1,
   622  	"minDelegationFee": 0,
   623  	"minDelegatorStake": 18446744073709551615,
   624  	"maxValidatorWeightFactor": 255,
   625  	"uptimeRequirement": 0,
   626  	"subnetAuthorization": {
   627  		"signatureIndices": []
   628  	}
   629  }`, string(unsignedComplexTransformTxJSONBytes))
   630  }
   631  
   632  func TestTransformSubnetTxSyntacticVerify(t *testing.T) {
   633  	type test struct {
   634  		name   string
   635  		txFunc func(*gomock.Controller) *TransformSubnetTx
   636  		err    error
   637  	}
   638  
   639  	var (
   640  		networkID = uint32(1337)
   641  		chainID   = ids.GenerateTestID()
   642  	)
   643  
   644  	ctx := &snow.Context{
   645  		ChainID:     chainID,
   646  		NetworkID:   networkID,
   647  		AVAXAssetID: ids.GenerateTestID(),
   648  	}
   649  
   650  	// A BaseTx that already passed syntactic verification.
   651  	verifiedBaseTx := BaseTx{
   652  		SyntacticallyVerified: true,
   653  	}
   654  
   655  	// A BaseTx that passes syntactic verification.
   656  	validBaseTx := BaseTx{
   657  		BaseTx: avax.BaseTx{
   658  			NetworkID:    networkID,
   659  			BlockchainID: chainID,
   660  		},
   661  	}
   662  
   663  	// A BaseTx that fails syntactic verification.
   664  	invalidBaseTx := BaseTx{}
   665  
   666  	tests := []test{
   667  		{
   668  			name: "nil tx",
   669  			txFunc: func(*gomock.Controller) *TransformSubnetTx {
   670  				return nil
   671  			},
   672  			err: ErrNilTx,
   673  		},
   674  		{
   675  			name: "already verified",
   676  			txFunc: func(*gomock.Controller) *TransformSubnetTx {
   677  				return &TransformSubnetTx{
   678  					BaseTx: verifiedBaseTx,
   679  				}
   680  			},
   681  			err: nil,
   682  		},
   683  		{
   684  			name: "invalid subnetID",
   685  			txFunc: func(*gomock.Controller) *TransformSubnetTx {
   686  				return &TransformSubnetTx{
   687  					BaseTx: validBaseTx,
   688  					Subnet: constants.PrimaryNetworkID,
   689  				}
   690  			},
   691  			err: errCantTransformPrimaryNetwork,
   692  		},
   693  		{
   694  			name: "empty assetID",
   695  			txFunc: func(*gomock.Controller) *TransformSubnetTx {
   696  				return &TransformSubnetTx{
   697  					BaseTx:  validBaseTx,
   698  					Subnet:  ids.GenerateTestID(),
   699  					AssetID: ids.Empty,
   700  				}
   701  			},
   702  			err: errEmptyAssetID,
   703  		},
   704  		{
   705  			name: "AVAX assetID",
   706  			txFunc: func(*gomock.Controller) *TransformSubnetTx {
   707  				return &TransformSubnetTx{
   708  					BaseTx:  validBaseTx,
   709  					Subnet:  ids.GenerateTestID(),
   710  					AssetID: ctx.AVAXAssetID,
   711  				}
   712  			},
   713  			err: errAssetIDCantBeAVAX,
   714  		},
   715  		{
   716  			name: "initialSupply == 0",
   717  			txFunc: func(*gomock.Controller) *TransformSubnetTx {
   718  				return &TransformSubnetTx{
   719  					BaseTx:        validBaseTx,
   720  					Subnet:        ids.GenerateTestID(),
   721  					AssetID:       ids.GenerateTestID(),
   722  					InitialSupply: 0,
   723  				}
   724  			},
   725  			err: errInitialSupplyZero,
   726  		},
   727  		{
   728  			name: "initialSupply > maximumSupply",
   729  			txFunc: func(*gomock.Controller) *TransformSubnetTx {
   730  				return &TransformSubnetTx{
   731  					BaseTx:        validBaseTx,
   732  					Subnet:        ids.GenerateTestID(),
   733  					AssetID:       ids.GenerateTestID(),
   734  					InitialSupply: 2,
   735  					MaximumSupply: 1,
   736  				}
   737  			},
   738  			err: errInitialSupplyGreaterThanMaxSupply,
   739  		},
   740  		{
   741  			name: "minConsumptionRate > maxConsumptionRate",
   742  			txFunc: func(*gomock.Controller) *TransformSubnetTx {
   743  				return &TransformSubnetTx{
   744  					BaseTx:             validBaseTx,
   745  					Subnet:             ids.GenerateTestID(),
   746  					AssetID:            ids.GenerateTestID(),
   747  					InitialSupply:      1,
   748  					MaximumSupply:      1,
   749  					MinConsumptionRate: 2,
   750  					MaxConsumptionRate: 1,
   751  				}
   752  			},
   753  			err: errMinConsumptionRateTooLarge,
   754  		},
   755  		{
   756  			name: "maxConsumptionRate > 100%",
   757  			txFunc: func(*gomock.Controller) *TransformSubnetTx {
   758  				return &TransformSubnetTx{
   759  					BaseTx:             validBaseTx,
   760  					Subnet:             ids.GenerateTestID(),
   761  					AssetID:            ids.GenerateTestID(),
   762  					InitialSupply:      1,
   763  					MaximumSupply:      1,
   764  					MinConsumptionRate: 0,
   765  					MaxConsumptionRate: reward.PercentDenominator + 1,
   766  				}
   767  			},
   768  			err: errMaxConsumptionRateTooLarge,
   769  		},
   770  		{
   771  			name: "minValidatorStake == 0",
   772  			txFunc: func(*gomock.Controller) *TransformSubnetTx {
   773  				return &TransformSubnetTx{
   774  					BaseTx:             validBaseTx,
   775  					Subnet:             ids.GenerateTestID(),
   776  					AssetID:            ids.GenerateTestID(),
   777  					InitialSupply:      1,
   778  					MaximumSupply:      1,
   779  					MinConsumptionRate: 0,
   780  					MaxConsumptionRate: reward.PercentDenominator,
   781  					MinValidatorStake:  0,
   782  				}
   783  			},
   784  			err: errMinValidatorStakeZero,
   785  		},
   786  		{
   787  			name: "minValidatorStake > initialSupply",
   788  			txFunc: func(*gomock.Controller) *TransformSubnetTx {
   789  				return &TransformSubnetTx{
   790  					BaseTx:             validBaseTx,
   791  					Subnet:             ids.GenerateTestID(),
   792  					AssetID:            ids.GenerateTestID(),
   793  					InitialSupply:      1,
   794  					MaximumSupply:      1,
   795  					MinConsumptionRate: 0,
   796  					MaxConsumptionRate: reward.PercentDenominator,
   797  					MinValidatorStake:  2,
   798  				}
   799  			},
   800  			err: errMinValidatorStakeAboveSupply,
   801  		},
   802  		{
   803  			name: "minValidatorStake > maxValidatorStake",
   804  			txFunc: func(*gomock.Controller) *TransformSubnetTx {
   805  				return &TransformSubnetTx{
   806  					BaseTx:             validBaseTx,
   807  					Subnet:             ids.GenerateTestID(),
   808  					AssetID:            ids.GenerateTestID(),
   809  					InitialSupply:      10,
   810  					MaximumSupply:      10,
   811  					MinConsumptionRate: 0,
   812  					MaxConsumptionRate: reward.PercentDenominator,
   813  					MinValidatorStake:  2,
   814  					MaxValidatorStake:  1,
   815  				}
   816  			},
   817  			err: errMinValidatorStakeAboveMax,
   818  		},
   819  		{
   820  			name: "maxValidatorStake > maximumSupply",
   821  			txFunc: func(*gomock.Controller) *TransformSubnetTx {
   822  				return &TransformSubnetTx{
   823  					BaseTx:             validBaseTx,
   824  					Subnet:             ids.GenerateTestID(),
   825  					AssetID:            ids.GenerateTestID(),
   826  					InitialSupply:      10,
   827  					MaximumSupply:      10,
   828  					MinConsumptionRate: 0,
   829  					MaxConsumptionRate: reward.PercentDenominator,
   830  					MinValidatorStake:  2,
   831  					MaxValidatorStake:  11,
   832  				}
   833  			},
   834  			err: errMaxValidatorStakeTooLarge,
   835  		},
   836  		{
   837  			name: "minStakeDuration == 0",
   838  			txFunc: func(*gomock.Controller) *TransformSubnetTx {
   839  				return &TransformSubnetTx{
   840  					BaseTx:             validBaseTx,
   841  					Subnet:             ids.GenerateTestID(),
   842  					AssetID:            ids.GenerateTestID(),
   843  					InitialSupply:      10,
   844  					MaximumSupply:      10,
   845  					MinConsumptionRate: 0,
   846  					MaxConsumptionRate: reward.PercentDenominator,
   847  					MinValidatorStake:  2,
   848  					MaxValidatorStake:  10,
   849  					MinStakeDuration:   0,
   850  				}
   851  			},
   852  			err: errMinStakeDurationZero,
   853  		},
   854  		{
   855  			name: "minStakeDuration > maxStakeDuration",
   856  			txFunc: func(*gomock.Controller) *TransformSubnetTx {
   857  				return &TransformSubnetTx{
   858  					BaseTx:             validBaseTx,
   859  					Subnet:             ids.GenerateTestID(),
   860  					AssetID:            ids.GenerateTestID(),
   861  					InitialSupply:      10,
   862  					MaximumSupply:      10,
   863  					MinConsumptionRate: 0,
   864  					MaxConsumptionRate: reward.PercentDenominator,
   865  					MinValidatorStake:  2,
   866  					MaxValidatorStake:  10,
   867  					MinStakeDuration:   2,
   868  					MaxStakeDuration:   1,
   869  				}
   870  			},
   871  			err: errMinStakeDurationTooLarge,
   872  		},
   873  		{
   874  			name: "minDelegationFee > 100%",
   875  			txFunc: func(*gomock.Controller) *TransformSubnetTx {
   876  				return &TransformSubnetTx{
   877  					BaseTx:             validBaseTx,
   878  					Subnet:             ids.GenerateTestID(),
   879  					AssetID:            ids.GenerateTestID(),
   880  					InitialSupply:      10,
   881  					MaximumSupply:      10,
   882  					MinConsumptionRate: 0,
   883  					MaxConsumptionRate: reward.PercentDenominator,
   884  					MinValidatorStake:  2,
   885  					MaxValidatorStake:  10,
   886  					MinStakeDuration:   1,
   887  					MaxStakeDuration:   2,
   888  					MinDelegationFee:   reward.PercentDenominator + 1,
   889  				}
   890  			},
   891  			err: errMinDelegationFeeTooLarge,
   892  		},
   893  		{
   894  			name: "minDelegatorStake == 0",
   895  			txFunc: func(*gomock.Controller) *TransformSubnetTx {
   896  				return &TransformSubnetTx{
   897  					BaseTx:             validBaseTx,
   898  					Subnet:             ids.GenerateTestID(),
   899  					AssetID:            ids.GenerateTestID(),
   900  					InitialSupply:      10,
   901  					MaximumSupply:      10,
   902  					MinConsumptionRate: 0,
   903  					MaxConsumptionRate: reward.PercentDenominator,
   904  					MinValidatorStake:  2,
   905  					MaxValidatorStake:  10,
   906  					MinStakeDuration:   1,
   907  					MaxStakeDuration:   2,
   908  					MinDelegationFee:   reward.PercentDenominator,
   909  					MinDelegatorStake:  0,
   910  				}
   911  			},
   912  			err: errMinDelegatorStakeZero,
   913  		},
   914  		{
   915  			name: "maxValidatorWeightFactor == 0",
   916  			txFunc: func(*gomock.Controller) *TransformSubnetTx {
   917  				return &TransformSubnetTx{
   918  					BaseTx:                   validBaseTx,
   919  					Subnet:                   ids.GenerateTestID(),
   920  					AssetID:                  ids.GenerateTestID(),
   921  					InitialSupply:            10,
   922  					MaximumSupply:            10,
   923  					MinConsumptionRate:       0,
   924  					MaxConsumptionRate:       reward.PercentDenominator,
   925  					MinValidatorStake:        2,
   926  					MaxValidatorStake:        10,
   927  					MinStakeDuration:         1,
   928  					MaxStakeDuration:         2,
   929  					MinDelegationFee:         reward.PercentDenominator,
   930  					MinDelegatorStake:        1,
   931  					MaxValidatorWeightFactor: 0,
   932  				}
   933  			},
   934  			err: errMaxValidatorWeightFactorZero,
   935  		},
   936  		{
   937  			name: "uptimeRequirement > 100%",
   938  			txFunc: func(*gomock.Controller) *TransformSubnetTx {
   939  				return &TransformSubnetTx{
   940  					BaseTx:                   validBaseTx,
   941  					Subnet:                   ids.GenerateTestID(),
   942  					AssetID:                  ids.GenerateTestID(),
   943  					InitialSupply:            10,
   944  					MaximumSupply:            10,
   945  					MinConsumptionRate:       0,
   946  					MaxConsumptionRate:       reward.PercentDenominator,
   947  					MinValidatorStake:        2,
   948  					MaxValidatorStake:        10,
   949  					MinStakeDuration:         1,
   950  					MaxStakeDuration:         2,
   951  					MinDelegationFee:         reward.PercentDenominator,
   952  					MinDelegatorStake:        1,
   953  					MaxValidatorWeightFactor: 1,
   954  					UptimeRequirement:        reward.PercentDenominator + 1,
   955  				}
   956  			},
   957  			err: errUptimeRequirementTooLarge,
   958  		},
   959  		{
   960  			name: "invalid subnetAuth",
   961  			txFunc: func(ctrl *gomock.Controller) *TransformSubnetTx {
   962  				// This SubnetAuth fails verification.
   963  				invalidSubnetAuth := verify.NewMockVerifiable(ctrl)
   964  				invalidSubnetAuth.EXPECT().Verify().Return(errInvalidSubnetAuth)
   965  				return &TransformSubnetTx{
   966  					BaseTx:                   validBaseTx,
   967  					Subnet:                   ids.GenerateTestID(),
   968  					AssetID:                  ids.GenerateTestID(),
   969  					InitialSupply:            10,
   970  					MaximumSupply:            10,
   971  					MinConsumptionRate:       0,
   972  					MaxConsumptionRate:       reward.PercentDenominator,
   973  					MinValidatorStake:        2,
   974  					MaxValidatorStake:        10,
   975  					MinStakeDuration:         1,
   976  					MaxStakeDuration:         2,
   977  					MinDelegationFee:         reward.PercentDenominator,
   978  					MinDelegatorStake:        1,
   979  					MaxValidatorWeightFactor: 1,
   980  					UptimeRequirement:        reward.PercentDenominator,
   981  					SubnetAuth:               invalidSubnetAuth,
   982  				}
   983  			},
   984  			err: errInvalidSubnetAuth,
   985  		},
   986  		{
   987  			name: "invalid BaseTx",
   988  			txFunc: func(*gomock.Controller) *TransformSubnetTx {
   989  				return &TransformSubnetTx{
   990  					BaseTx:                   invalidBaseTx,
   991  					Subnet:                   ids.GenerateTestID(),
   992  					AssetID:                  ids.GenerateTestID(),
   993  					InitialSupply:            10,
   994  					MaximumSupply:            10,
   995  					MinConsumptionRate:       0,
   996  					MaxConsumptionRate:       reward.PercentDenominator,
   997  					MinValidatorStake:        2,
   998  					MaxValidatorStake:        10,
   999  					MinStakeDuration:         1,
  1000  					MaxStakeDuration:         2,
  1001  					MinDelegationFee:         reward.PercentDenominator,
  1002  					MinDelegatorStake:        1,
  1003  					MaxValidatorWeightFactor: 1,
  1004  					UptimeRequirement:        reward.PercentDenominator,
  1005  				}
  1006  			},
  1007  			err: avax.ErrWrongNetworkID,
  1008  		},
  1009  		{
  1010  			name: "passes verification",
  1011  			txFunc: func(ctrl *gomock.Controller) *TransformSubnetTx {
  1012  				// This SubnetAuth passes verification.
  1013  				validSubnetAuth := verify.NewMockVerifiable(ctrl)
  1014  				validSubnetAuth.EXPECT().Verify().Return(nil)
  1015  				return &TransformSubnetTx{
  1016  					BaseTx:                   validBaseTx,
  1017  					Subnet:                   ids.GenerateTestID(),
  1018  					AssetID:                  ids.GenerateTestID(),
  1019  					InitialSupply:            10,
  1020  					MaximumSupply:            10,
  1021  					MinConsumptionRate:       0,
  1022  					MaxConsumptionRate:       reward.PercentDenominator,
  1023  					MinValidatorStake:        2,
  1024  					MaxValidatorStake:        10,
  1025  					MinStakeDuration:         1,
  1026  					MaxStakeDuration:         2,
  1027  					MinDelegationFee:         reward.PercentDenominator,
  1028  					MinDelegatorStake:        1,
  1029  					MaxValidatorWeightFactor: 1,
  1030  					UptimeRequirement:        reward.PercentDenominator,
  1031  					SubnetAuth:               validSubnetAuth,
  1032  				}
  1033  			},
  1034  			err: nil,
  1035  		},
  1036  	}
  1037  
  1038  	for _, tt := range tests {
  1039  		t.Run(tt.name, func(t *testing.T) {
  1040  			ctrl := gomock.NewController(t)
  1041  
  1042  			tx := tt.txFunc(ctrl)
  1043  			err := tx.SyntacticVerify(ctx)
  1044  			require.ErrorIs(t, err, tt.err)
  1045  		})
  1046  	}
  1047  }