github.com/MetalBlockchain/metalgo@v1.11.9/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  	"context"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/stretchr/testify/require"
    12  
    13  	"github.com/MetalBlockchain/metalgo/database"
    14  	"github.com/MetalBlockchain/metalgo/ids"
    15  	"github.com/MetalBlockchain/metalgo/utils/constants"
    16  	"github.com/MetalBlockchain/metalgo/utils/crypto/secp256k1"
    17  	"github.com/MetalBlockchain/metalgo/utils/hashing"
    18  	"github.com/MetalBlockchain/metalgo/utils/set"
    19  	"github.com/MetalBlockchain/metalgo/utils/units"
    20  	"github.com/MetalBlockchain/metalgo/vms/platformvm/state"
    21  	"github.com/MetalBlockchain/metalgo/vms/platformvm/txs"
    22  	"github.com/MetalBlockchain/metalgo/vms/platformvm/txs/txstest"
    23  	"github.com/MetalBlockchain/metalgo/vms/platformvm/utxo"
    24  	"github.com/MetalBlockchain/metalgo/vms/secp256k1fx"
    25  
    26  	walletsigner "github.com/MetalBlockchain/metalgo/wallet/chain/p/signer"
    27  )
    28  
    29  // Ensure Execute fails when there are not enough control sigs
    30  func TestCreateChainTxInsufficientControlSigs(t *testing.T) {
    31  	require := require.New(t)
    32  	env := newEnvironment(t, banff)
    33  	env.ctx.Lock.Lock()
    34  	defer env.ctx.Lock.Unlock()
    35  
    36  	builder, signer := env.factory.NewWallet(preFundedKeys[0], preFundedKeys[1])
    37  	utx, err := builder.NewCreateChainTx(
    38  		testSubnet1.ID(),
    39  		nil,
    40  		constants.AVMID,
    41  		nil,
    42  		"chain name",
    43  	)
    44  	require.NoError(err)
    45  	tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx)
    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  	executor := StandardTxExecutor{
    55  		Backend: &env.backend,
    56  		State:   stateDiff,
    57  		Tx:      tx,
    58  	}
    59  	err = tx.Unsigned.Visit(&executor)
    60  	require.ErrorIs(err, errUnauthorizedSubnetModification)
    61  }
    62  
    63  // Ensure Execute fails when an incorrect control signature is given
    64  func TestCreateChainTxWrongControlSig(t *testing.T) {
    65  	require := require.New(t)
    66  	env := newEnvironment(t, banff)
    67  	env.ctx.Lock.Lock()
    68  	defer env.ctx.Lock.Unlock()
    69  
    70  	builder, signer := env.factory.NewWallet(testSubnet1ControlKeys[0], testSubnet1ControlKeys[1])
    71  	utx, err := builder.NewCreateChainTx(
    72  		testSubnet1.ID(),
    73  		nil,
    74  		constants.AVMID,
    75  		nil,
    76  		"chain name",
    77  	)
    78  	require.NoError(err)
    79  	tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx)
    80  	require.NoError(err)
    81  
    82  	// Generate new, random key to sign tx with
    83  	key, err := secp256k1.NewPrivateKey()
    84  	require.NoError(err)
    85  
    86  	// Replace a valid signature with one from another key
    87  	sig, err := key.SignHash(hashing.ComputeHash256(tx.Unsigned.Bytes()))
    88  	require.NoError(err)
    89  	copy(tx.Creds[0].(*secp256k1fx.Credential).Sigs[0][:], sig)
    90  
    91  	stateDiff, err := state.NewDiff(lastAcceptedID, env)
    92  	require.NoError(err)
    93  
    94  	executor := StandardTxExecutor{
    95  		Backend: &env.backend,
    96  		State:   stateDiff,
    97  		Tx:      tx,
    98  	}
    99  	err = tx.Unsigned.Visit(&executor)
   100  	require.ErrorIs(err, errUnauthorizedSubnetModification)
   101  }
   102  
   103  // Ensure Execute fails when the Subnet the blockchain specifies as
   104  // its validator set doesn't exist
   105  func TestCreateChainTxNoSuchSubnet(t *testing.T) {
   106  	require := require.New(t)
   107  	env := newEnvironment(t, banff)
   108  	env.ctx.Lock.Lock()
   109  	defer env.ctx.Lock.Unlock()
   110  
   111  	builder, signer := env.factory.NewWallet(testSubnet1ControlKeys[0], testSubnet1ControlKeys[1])
   112  	utx, err := builder.NewCreateChainTx(
   113  		testSubnet1.ID(),
   114  		nil,
   115  		constants.AVMID,
   116  		nil,
   117  		"chain name",
   118  	)
   119  	require.NoError(err)
   120  	tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx)
   121  	require.NoError(err)
   122  
   123  	tx.Unsigned.(*txs.CreateChainTx).SubnetID = ids.GenerateTestID()
   124  
   125  	stateDiff, err := state.NewDiff(lastAcceptedID, env)
   126  	require.NoError(err)
   127  
   128  	executor := StandardTxExecutor{
   129  		Backend: &env.backend,
   130  		State:   stateDiff,
   131  		Tx:      tx,
   132  	}
   133  	err = tx.Unsigned.Visit(&executor)
   134  	require.ErrorIs(err, database.ErrNotFound)
   135  }
   136  
   137  // Ensure valid tx passes semanticVerify
   138  func TestCreateChainTxValid(t *testing.T) {
   139  	require := require.New(t)
   140  	env := newEnvironment(t, banff)
   141  	env.ctx.Lock.Lock()
   142  	defer env.ctx.Lock.Unlock()
   143  
   144  	builder, signer := env.factory.NewWallet(testSubnet1ControlKeys[0], testSubnet1ControlKeys[1])
   145  	utx, err := builder.NewCreateChainTx(
   146  		testSubnet1.ID(),
   147  		nil,
   148  		constants.AVMID,
   149  		nil,
   150  		"chain name",
   151  	)
   152  	require.NoError(err)
   153  	tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx)
   154  	require.NoError(err)
   155  
   156  	stateDiff, err := state.NewDiff(lastAcceptedID, env)
   157  	require.NoError(err)
   158  
   159  	executor := StandardTxExecutor{
   160  		Backend: &env.backend,
   161  		State:   stateDiff,
   162  		Tx:      tx,
   163  	}
   164  	require.NoError(tx.Unsigned.Visit(&executor))
   165  }
   166  
   167  func TestCreateChainTxAP3FeeChange(t *testing.T) {
   168  	ap3Time := defaultGenesisTime.Add(time.Hour)
   169  	tests := []struct {
   170  		name          string
   171  		time          time.Time
   172  		fee           uint64
   173  		expectedError error
   174  	}{
   175  		{
   176  			name:          "pre-fork - correctly priced",
   177  			time:          defaultGenesisTime,
   178  			fee:           0,
   179  			expectedError: nil,
   180  		},
   181  		{
   182  			name:          "post-fork - incorrectly priced",
   183  			time:          ap3Time,
   184  			fee:           100*defaultTxFee - 1*units.NanoAvax,
   185  			expectedError: utxo.ErrInsufficientUnlockedFunds,
   186  		},
   187  		{
   188  			name:          "post-fork - correctly priced",
   189  			time:          ap3Time,
   190  			fee:           100 * defaultTxFee,
   191  			expectedError: nil,
   192  		},
   193  	}
   194  	for _, test := range tests {
   195  		t.Run(test.name, func(t *testing.T) {
   196  			require := require.New(t)
   197  
   198  			env := newEnvironment(t, banff)
   199  			env.config.UpgradeConfig.ApricotPhase3Time = ap3Time
   200  
   201  			addrs := set.NewSet[ids.ShortID](len(preFundedKeys))
   202  			for _, key := range preFundedKeys {
   203  				addrs.Add(key.Address())
   204  			}
   205  
   206  			env.state.SetTimestamp(test.time) // to duly set fee
   207  
   208  			cfg := *env.config
   209  
   210  			cfg.StaticFeeConfig.CreateBlockchainTxFee = test.fee
   211  			factory := txstest.NewWalletFactory(env.ctx, &cfg, env.state)
   212  			builder, signer := factory.NewWallet(preFundedKeys...)
   213  			utx, err := builder.NewCreateChainTx(
   214  				testSubnet1.ID(),
   215  				nil,
   216  				ids.GenerateTestID(),
   217  				nil,
   218  				"",
   219  			)
   220  			require.NoError(err)
   221  			tx, err := walletsigner.SignUnsigned(context.Background(), signer, utx)
   222  			require.NoError(err)
   223  
   224  			stateDiff, err := state.NewDiff(lastAcceptedID, env)
   225  			require.NoError(err)
   226  
   227  			stateDiff.SetTimestamp(test.time)
   228  
   229  			executor := StandardTxExecutor{
   230  				Backend: &env.backend,
   231  				State:   stateDiff,
   232  				Tx:      tx,
   233  			}
   234  			err = tx.Unsigned.Visit(&executor)
   235  			require.ErrorIs(err, test.expectedError)
   236  		})
   237  	}
   238  }