github.com/ava-labs/avalanchego@v1.11.11/vms/platformvm/txs/executor/create_chain_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 executor
     5  
     6  import (
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/stretchr/testify/require"
    11  
    12  	"github.com/ava-labs/avalanchego/database"
    13  	"github.com/ava-labs/avalanchego/ids"
    14  	"github.com/ava-labs/avalanchego/upgrade/upgradetest"
    15  	"github.com/ava-labs/avalanchego/utils/constants"
    16  	"github.com/ava-labs/avalanchego/utils/crypto/secp256k1"
    17  	"github.com/ava-labs/avalanchego/utils/hashing"
    18  	"github.com/ava-labs/avalanchego/utils/set"
    19  	"github.com/ava-labs/avalanchego/utils/units"
    20  	"github.com/ava-labs/avalanchego/vms/platformvm/genesis/genesistest"
    21  	"github.com/ava-labs/avalanchego/vms/platformvm/state"
    22  	"github.com/ava-labs/avalanchego/vms/platformvm/txs"
    23  	"github.com/ava-labs/avalanchego/vms/platformvm/utxo"
    24  	"github.com/ava-labs/avalanchego/vms/secp256k1fx"
    25  )
    26  
    27  // Ensure Execute fails when there are not enough control sigs
    28  func TestCreateChainTxInsufficientControlSigs(t *testing.T) {
    29  	require := require.New(t)
    30  	env := newEnvironment(t, upgradetest.Banff)
    31  	env.ctx.Lock.Lock()
    32  	defer env.ctx.Lock.Unlock()
    33  
    34  	subnetID := testSubnet1.ID()
    35  	wallet := newWallet(t, env, walletConfig{
    36  		subnetIDs: []ids.ID{subnetID},
    37  	})
    38  
    39  	tx, err := wallet.IssueCreateChainTx(
    40  		subnetID,
    41  		nil,
    42  		constants.AVMID,
    43  		nil,
    44  		"chain name",
    45  	)
    46  	require.NoError(err)
    47  
    48  	// Remove a signature
    49  	tx.Creds[0].(*secp256k1fx.Credential).Sigs = tx.Creds[0].(*secp256k1fx.Credential).Sigs[1:]
    50  
    51  	stateDiff, err := state.NewDiff(lastAcceptedID, env)
    52  	require.NoError(err)
    53  
    54  	feeCalculator := state.PickFeeCalculator(env.config, stateDiff)
    55  	executor := StandardTxExecutor{
    56  		Backend:       &env.backend,
    57  		FeeCalculator: feeCalculator,
    58  		State:         stateDiff,
    59  		Tx:            tx,
    60  	}
    61  	err = tx.Unsigned.Visit(&executor)
    62  	require.ErrorIs(err, errUnauthorizedSubnetModification)
    63  }
    64  
    65  // Ensure Execute fails when an incorrect control signature is given
    66  func TestCreateChainTxWrongControlSig(t *testing.T) {
    67  	require := require.New(t)
    68  	env := newEnvironment(t, upgradetest.Banff)
    69  	env.ctx.Lock.Lock()
    70  	defer env.ctx.Lock.Unlock()
    71  
    72  	subnetID := testSubnet1.ID()
    73  	wallet := newWallet(t, env, walletConfig{
    74  		subnetIDs: []ids.ID{subnetID},
    75  	})
    76  
    77  	tx, err := wallet.IssueCreateChainTx(
    78  		subnetID,
    79  		nil,
    80  		constants.AVMID,
    81  		nil,
    82  		"chain name",
    83  	)
    84  	require.NoError(err)
    85  
    86  	// Generate new, random key to sign tx with
    87  	key, err := secp256k1.NewPrivateKey()
    88  	require.NoError(err)
    89  
    90  	// Replace a valid signature with one from another key
    91  	sig, err := key.SignHash(hashing.ComputeHash256(tx.Unsigned.Bytes()))
    92  	require.NoError(err)
    93  	copy(tx.Creds[0].(*secp256k1fx.Credential).Sigs[0][:], sig)
    94  
    95  	stateDiff, err := state.NewDiff(lastAcceptedID, env)
    96  	require.NoError(err)
    97  
    98  	feeCalculator := state.PickFeeCalculator(env.config, stateDiff)
    99  	executor := StandardTxExecutor{
   100  		Backend:       &env.backend,
   101  		FeeCalculator: feeCalculator,
   102  		State:         stateDiff,
   103  		Tx:            tx,
   104  	}
   105  	err = tx.Unsigned.Visit(&executor)
   106  	require.ErrorIs(err, errUnauthorizedSubnetModification)
   107  }
   108  
   109  // Ensure Execute fails when the Subnet the blockchain specifies as
   110  // its validator set doesn't exist
   111  func TestCreateChainTxNoSuchSubnet(t *testing.T) {
   112  	require := require.New(t)
   113  	env := newEnvironment(t, upgradetest.Banff)
   114  	env.ctx.Lock.Lock()
   115  	defer env.ctx.Lock.Unlock()
   116  
   117  	subnetID := testSubnet1.ID()
   118  	wallet := newWallet(t, env, walletConfig{
   119  		subnetIDs: []ids.ID{subnetID},
   120  	})
   121  
   122  	tx, err := wallet.IssueCreateChainTx(
   123  		subnetID,
   124  		nil,
   125  		constants.AVMID,
   126  		nil,
   127  		"chain name",
   128  	)
   129  	require.NoError(err)
   130  
   131  	tx.Unsigned.(*txs.CreateChainTx).SubnetID = ids.GenerateTestID()
   132  
   133  	stateDiff, err := state.NewDiff(lastAcceptedID, env)
   134  	require.NoError(err)
   135  
   136  	builderDiff, err := state.NewDiffOn(stateDiff)
   137  	require.NoError(err)
   138  
   139  	feeCalculator := state.PickFeeCalculator(env.config, builderDiff)
   140  	executor := StandardTxExecutor{
   141  		Backend:       &env.backend,
   142  		FeeCalculator: feeCalculator,
   143  		State:         stateDiff,
   144  		Tx:            tx,
   145  	}
   146  	err = tx.Unsigned.Visit(&executor)
   147  	require.ErrorIs(err, database.ErrNotFound)
   148  }
   149  
   150  // Ensure valid tx passes semanticVerify
   151  func TestCreateChainTxValid(t *testing.T) {
   152  	require := require.New(t)
   153  	env := newEnvironment(t, upgradetest.Banff)
   154  	env.ctx.Lock.Lock()
   155  	defer env.ctx.Lock.Unlock()
   156  
   157  	subnetID := testSubnet1.ID()
   158  	wallet := newWallet(t, env, walletConfig{
   159  		subnetIDs: []ids.ID{subnetID},
   160  	})
   161  
   162  	tx, err := wallet.IssueCreateChainTx(
   163  		subnetID,
   164  		nil,
   165  		constants.AVMID,
   166  		nil,
   167  		"chain name",
   168  	)
   169  	require.NoError(err)
   170  
   171  	stateDiff, err := state.NewDiff(lastAcceptedID, env)
   172  	require.NoError(err)
   173  
   174  	builderDiff, err := state.NewDiffOn(stateDiff)
   175  	require.NoError(err)
   176  
   177  	feeCalculator := state.PickFeeCalculator(env.config, builderDiff)
   178  	executor := StandardTxExecutor{
   179  		Backend:       &env.backend,
   180  		FeeCalculator: feeCalculator,
   181  		State:         stateDiff,
   182  		Tx:            tx,
   183  	}
   184  	require.NoError(tx.Unsigned.Visit(&executor))
   185  }
   186  
   187  func TestCreateChainTxAP3FeeChange(t *testing.T) {
   188  	ap3Time := genesistest.DefaultValidatorStartTime.Add(time.Hour)
   189  	tests := []struct {
   190  		name          string
   191  		time          time.Time
   192  		fee           uint64
   193  		expectedError error
   194  	}{
   195  		{
   196  			name:          "pre-fork - correctly priced",
   197  			time:          genesistest.DefaultValidatorStartTime,
   198  			fee:           0,
   199  			expectedError: nil,
   200  		},
   201  		{
   202  			name:          "post-fork - incorrectly priced",
   203  			time:          ap3Time,
   204  			fee:           100*defaultTxFee - 1*units.NanoAvax,
   205  			expectedError: utxo.ErrInsufficientUnlockedFunds,
   206  		},
   207  		{
   208  			name:          "post-fork - correctly priced",
   209  			time:          ap3Time,
   210  			fee:           100 * defaultTxFee,
   211  			expectedError: nil,
   212  		},
   213  	}
   214  	for _, test := range tests {
   215  		t.Run(test.name, func(t *testing.T) {
   216  			require := require.New(t)
   217  
   218  			env := newEnvironment(t, upgradetest.Banff)
   219  			env.config.UpgradeConfig.ApricotPhase3Time = ap3Time
   220  
   221  			addrs := set.NewSet[ids.ShortID](len(genesistest.DefaultFundedKeys))
   222  			for _, key := range genesistest.DefaultFundedKeys {
   223  				addrs.Add(key.Address())
   224  			}
   225  
   226  			env.state.SetTimestamp(test.time) // to duly set fee
   227  
   228  			config := *env.config
   229  			config.StaticFeeConfig.CreateBlockchainTxFee = test.fee
   230  			subnetID := testSubnet1.ID()
   231  			wallet := newWallet(t, env, walletConfig{
   232  				config:    &config,
   233  				subnetIDs: []ids.ID{subnetID},
   234  			})
   235  
   236  			tx, err := wallet.IssueCreateChainTx(
   237  				subnetID,
   238  				nil,
   239  				ids.GenerateTestID(),
   240  				nil,
   241  				"",
   242  			)
   243  			require.NoError(err)
   244  
   245  			stateDiff, err := state.NewDiff(lastAcceptedID, env)
   246  			require.NoError(err)
   247  
   248  			stateDiff.SetTimestamp(test.time)
   249  
   250  			feeCalculator := state.PickFeeCalculator(env.config, stateDiff)
   251  			executor := StandardTxExecutor{
   252  				Backend:       &env.backend,
   253  				FeeCalculator: feeCalculator,
   254  				State:         stateDiff,
   255  				Tx:            tx,
   256  			}
   257  			err = tx.Unsigned.Visit(&executor)
   258  			require.ErrorIs(err, test.expectedError)
   259  		})
   260  	}
   261  }
   262  
   263  func TestEtnaCreateChainTxInvalidWithManagedSubnet(t *testing.T) {
   264  	require := require.New(t)
   265  	env := newEnvironment(t, upgradetest.Etna)
   266  	env.ctx.Lock.Lock()
   267  	defer env.ctx.Lock.Unlock()
   268  
   269  	subnetID := testSubnet1.ID()
   270  	wallet := newWallet(t, env, walletConfig{
   271  		subnetIDs: []ids.ID{subnetID},
   272  	})
   273  
   274  	tx, err := wallet.IssueCreateChainTx(
   275  		subnetID,
   276  		nil,
   277  		constants.AVMID,
   278  		nil,
   279  		"chain name",
   280  	)
   281  	require.NoError(err)
   282  
   283  	stateDiff, err := state.NewDiff(lastAcceptedID, env)
   284  	require.NoError(err)
   285  
   286  	builderDiff, err := state.NewDiffOn(stateDiff)
   287  	require.NoError(err)
   288  
   289  	stateDiff.SetSubnetManager(subnetID, ids.GenerateTestID(), []byte{'a', 'd', 'd', 'r', 'e', 's', 's'})
   290  
   291  	feeCalculator := state.PickFeeCalculator(env.config, builderDiff)
   292  	executor := StandardTxExecutor{
   293  		Backend:       &env.backend,
   294  		FeeCalculator: feeCalculator,
   295  		State:         stateDiff,
   296  		Tx:            tx,
   297  	}
   298  	err = tx.Unsigned.Visit(&executor)
   299  	require.ErrorIs(err, errIsImmutable)
   300  }