github.com/ava-labs/avalanchego@v1.11.11/vms/avm/txs/executor/executor_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  
     9  	"github.com/prometheus/client_golang/prometheus"
    10  	"github.com/stretchr/testify/require"
    11  
    12  	"github.com/ava-labs/avalanchego/database"
    13  	"github.com/ava-labs/avalanchego/database/memdb"
    14  	"github.com/ava-labs/avalanchego/database/versiondb"
    15  	"github.com/ava-labs/avalanchego/ids"
    16  	"github.com/ava-labs/avalanchego/utils/constants"
    17  	"github.com/ava-labs/avalanchego/utils/crypto/secp256k1"
    18  	"github.com/ava-labs/avalanchego/utils/units"
    19  	"github.com/ava-labs/avalanchego/vms/avm/block"
    20  	"github.com/ava-labs/avalanchego/vms/avm/fxs"
    21  	"github.com/ava-labs/avalanchego/vms/avm/state"
    22  	"github.com/ava-labs/avalanchego/vms/avm/txs"
    23  	"github.com/ava-labs/avalanchego/vms/components/avax"
    24  	"github.com/ava-labs/avalanchego/vms/components/verify"
    25  	"github.com/ava-labs/avalanchego/vms/secp256k1fx"
    26  )
    27  
    28  const trackChecksums = false
    29  
    30  var (
    31  	chainID = ids.ID{5, 4, 3, 2, 1}
    32  	assetID = ids.ID{1, 2, 3}
    33  )
    34  
    35  func TestBaseTxExecutor(t *testing.T) {
    36  	require := require.New(t)
    37  
    38  	secpFx := &secp256k1fx.Fx{}
    39  	parser, err := block.NewParser(
    40  		[]fxs.Fx{secpFx},
    41  	)
    42  	require.NoError(err)
    43  	codec := parser.Codec()
    44  
    45  	db := memdb.New()
    46  	vdb := versiondb.New(db)
    47  	registerer := prometheus.NewRegistry()
    48  	state, err := state.New(vdb, parser, registerer, trackChecksums)
    49  	require.NoError(err)
    50  
    51  	utxoID := avax.UTXOID{
    52  		TxID:        ids.GenerateTestID(),
    53  		OutputIndex: 1,
    54  	}
    55  
    56  	addr := keys[0].Address()
    57  	utxo := &avax.UTXO{
    58  		UTXOID: utxoID,
    59  		Asset:  avax.Asset{ID: assetID},
    60  		Out: &secp256k1fx.TransferOutput{
    61  			Amt: 20 * units.KiloAvax,
    62  			OutputOwners: secp256k1fx.OutputOwners{
    63  				Threshold: 1,
    64  				Addrs: []ids.ShortID{
    65  					addr,
    66  				},
    67  			},
    68  		},
    69  	}
    70  
    71  	// Populate the UTXO that we will be consuming
    72  	state.AddUTXO(utxo)
    73  	require.NoError(state.Commit())
    74  
    75  	baseTx := &txs.Tx{Unsigned: &txs.BaseTx{BaseTx: avax.BaseTx{
    76  		NetworkID:    constants.UnitTestID,
    77  		BlockchainID: chainID,
    78  		Ins: []*avax.TransferableInput{{
    79  			UTXOID: utxoID,
    80  			Asset:  avax.Asset{ID: assetID},
    81  			In: &secp256k1fx.TransferInput{
    82  				Amt: 20 * units.KiloAvax,
    83  				Input: secp256k1fx.Input{
    84  					SigIndices: []uint32{
    85  						0,
    86  					},
    87  				},
    88  			},
    89  		}},
    90  		Outs: []*avax.TransferableOutput{{
    91  			Asset: avax.Asset{ID: assetID},
    92  			Out: &secp256k1fx.TransferOutput{
    93  				Amt: 10 * units.KiloAvax,
    94  				OutputOwners: secp256k1fx.OutputOwners{
    95  					Threshold: 1,
    96  					Addrs:     []ids.ShortID{addr},
    97  				},
    98  			},
    99  		}},
   100  	}}}
   101  	require.NoError(baseTx.SignSECP256K1Fx(codec, [][]*secp256k1.PrivateKey{{keys[0]}}))
   102  
   103  	executor := &Executor{
   104  		Codec: codec,
   105  		State: state,
   106  		Tx:    baseTx,
   107  	}
   108  
   109  	// Execute baseTx
   110  	require.NoError(baseTx.Unsigned.Visit(executor))
   111  
   112  	// Verify the consumed UTXO was removed from the state
   113  	_, err = executor.State.GetUTXO(utxoID.InputID())
   114  	require.ErrorIs(err, database.ErrNotFound)
   115  
   116  	// Verify the produced UTXO was added to the state
   117  	expectedOutputUTXO := &avax.UTXO{
   118  		UTXOID: avax.UTXOID{
   119  			TxID:        baseTx.TxID,
   120  			OutputIndex: 0,
   121  		},
   122  		Asset: avax.Asset{
   123  			ID: assetID,
   124  		},
   125  		Out: &secp256k1fx.TransferOutput{
   126  			Amt: 10 * units.KiloAvax,
   127  			OutputOwners: secp256k1fx.OutputOwners{
   128  				Threshold: 1,
   129  				Addrs:     []ids.ShortID{addr},
   130  			},
   131  		},
   132  	}
   133  	expectedOutputUTXOID := expectedOutputUTXO.InputID()
   134  	outputUTXO, err := executor.State.GetUTXO(expectedOutputUTXOID)
   135  	require.NoError(err)
   136  
   137  	outputUTXOID := outputUTXO.InputID()
   138  	require.Equal(expectedOutputUTXOID, outputUTXOID)
   139  	require.Equal(expectedOutputUTXO, outputUTXO)
   140  }
   141  
   142  func TestCreateAssetTxExecutor(t *testing.T) {
   143  	require := require.New(t)
   144  
   145  	secpFx := &secp256k1fx.Fx{}
   146  	parser, err := block.NewParser(
   147  		[]fxs.Fx{secpFx},
   148  	)
   149  	require.NoError(err)
   150  	codec := parser.Codec()
   151  
   152  	db := memdb.New()
   153  	vdb := versiondb.New(db)
   154  	registerer := prometheus.NewRegistry()
   155  	state, err := state.New(vdb, parser, registerer, trackChecksums)
   156  	require.NoError(err)
   157  
   158  	utxoID := avax.UTXOID{
   159  		TxID:        ids.GenerateTestID(),
   160  		OutputIndex: 1,
   161  	}
   162  
   163  	addr := keys[0].Address()
   164  	utxo := &avax.UTXO{
   165  		UTXOID: utxoID,
   166  		Asset:  avax.Asset{ID: assetID},
   167  		Out: &secp256k1fx.TransferOutput{
   168  			Amt: 20 * units.KiloAvax,
   169  			OutputOwners: secp256k1fx.OutputOwners{
   170  				Threshold: 1,
   171  				Addrs: []ids.ShortID{
   172  					addr,
   173  				},
   174  			},
   175  		},
   176  	}
   177  
   178  	// Populate the UTXO that we will be consuming
   179  	state.AddUTXO(utxo)
   180  	require.NoError(state.Commit())
   181  
   182  	createAssetTx := &txs.Tx{Unsigned: &txs.CreateAssetTx{
   183  		BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{
   184  			NetworkID:    constants.UnitTestID,
   185  			BlockchainID: chainID,
   186  			Ins: []*avax.TransferableInput{{
   187  				UTXOID: utxoID,
   188  				Asset:  avax.Asset{ID: assetID},
   189  				In: &secp256k1fx.TransferInput{
   190  					Amt: 20 * units.KiloAvax,
   191  					Input: secp256k1fx.Input{
   192  						SigIndices: []uint32{
   193  							0,
   194  						},
   195  					},
   196  				},
   197  			}},
   198  			Outs: []*avax.TransferableOutput{{
   199  				Asset: avax.Asset{ID: assetID},
   200  				Out: &secp256k1fx.TransferOutput{
   201  					Amt: 10 * units.KiloAvax,
   202  					OutputOwners: secp256k1fx.OutputOwners{
   203  						Threshold: 1,
   204  						Addrs:     []ids.ShortID{addr},
   205  					},
   206  				},
   207  			}},
   208  		}},
   209  		Name:         "name",
   210  		Symbol:       "symb",
   211  		Denomination: 0,
   212  		States: []*txs.InitialState{
   213  			{
   214  				FxIndex: 0,
   215  				Outs: []verify.State{
   216  					&secp256k1fx.MintOutput{
   217  						OutputOwners: secp256k1fx.OutputOwners{
   218  							Threshold: 1,
   219  							Addrs:     []ids.ShortID{addr},
   220  						},
   221  					},
   222  				},
   223  			},
   224  		},
   225  	}}
   226  	require.NoError(createAssetTx.SignSECP256K1Fx(codec, [][]*secp256k1.PrivateKey{{keys[0]}}))
   227  
   228  	executor := &Executor{
   229  		Codec: codec,
   230  		State: state,
   231  		Tx:    createAssetTx,
   232  	}
   233  
   234  	// Execute createAssetTx
   235  	require.NoError(createAssetTx.Unsigned.Visit(executor))
   236  
   237  	// Verify the consumed UTXO was removed from the state
   238  	_, err = executor.State.GetUTXO(utxoID.InputID())
   239  	require.ErrorIs(err, database.ErrNotFound)
   240  
   241  	// Verify the produced UTXOs were added to the state
   242  	txID := createAssetTx.ID()
   243  	expectedOutputUTXOs := []*avax.UTXO{
   244  		{
   245  			UTXOID: avax.UTXOID{
   246  				TxID:        txID,
   247  				OutputIndex: 0,
   248  			},
   249  			Asset: avax.Asset{
   250  				ID: assetID,
   251  			},
   252  			Out: &secp256k1fx.TransferOutput{
   253  				Amt: 10 * units.KiloAvax,
   254  				OutputOwners: secp256k1fx.OutputOwners{
   255  					Threshold: 1,
   256  					Addrs:     []ids.ShortID{addr},
   257  				},
   258  			},
   259  		},
   260  		{
   261  			UTXOID: avax.UTXOID{
   262  				TxID:        txID,
   263  				OutputIndex: 1,
   264  			},
   265  			Asset: avax.Asset{
   266  				ID: txID,
   267  			},
   268  			Out: &secp256k1fx.MintOutput{
   269  				OutputOwners: secp256k1fx.OutputOwners{
   270  					Threshold: 1,
   271  					Addrs:     []ids.ShortID{addr},
   272  				},
   273  			},
   274  		},
   275  	}
   276  	for _, expectedOutputUTXO := range expectedOutputUTXOs {
   277  		expectedOutputUTXOID := expectedOutputUTXO.InputID()
   278  		outputUTXO, err := executor.State.GetUTXO(expectedOutputUTXOID)
   279  		require.NoError(err)
   280  
   281  		outputUTXOID := outputUTXO.InputID()
   282  		require.Equal(expectedOutputUTXOID, outputUTXOID)
   283  		require.Equal(expectedOutputUTXO, outputUTXO)
   284  	}
   285  }
   286  
   287  func TestOperationTxExecutor(t *testing.T) {
   288  	require := require.New(t)
   289  
   290  	secpFx := &secp256k1fx.Fx{}
   291  	parser, err := block.NewParser(
   292  		[]fxs.Fx{secpFx},
   293  	)
   294  	require.NoError(err)
   295  	codec := parser.Codec()
   296  
   297  	db := memdb.New()
   298  	vdb := versiondb.New(db)
   299  	registerer := prometheus.NewRegistry()
   300  	state, err := state.New(vdb, parser, registerer, trackChecksums)
   301  	require.NoError(err)
   302  
   303  	outputOwners := secp256k1fx.OutputOwners{
   304  		Threshold: 1,
   305  		Addrs: []ids.ShortID{
   306  			keys[0].Address(),
   307  		},
   308  	}
   309  
   310  	utxoID := avax.UTXOID{
   311  		TxID:        ids.GenerateTestID(),
   312  		OutputIndex: 1,
   313  	}
   314  	utxo := &avax.UTXO{
   315  		UTXOID: utxoID,
   316  		Asset:  avax.Asset{ID: assetID},
   317  		Out: &secp256k1fx.TransferOutput{
   318  			Amt:          20 * units.KiloAvax,
   319  			OutputOwners: outputOwners,
   320  		},
   321  	}
   322  
   323  	opUTXOID := avax.UTXOID{
   324  		TxID:        ids.GenerateTestID(),
   325  		OutputIndex: 1,
   326  	}
   327  	opUTXO := &avax.UTXO{
   328  		UTXOID: opUTXOID,
   329  		Asset:  avax.Asset{ID: assetID},
   330  		Out: &secp256k1fx.MintOutput{
   331  			OutputOwners: outputOwners,
   332  		},
   333  	}
   334  
   335  	// Populate the UTXOs that we will be consuming
   336  	state.AddUTXO(utxo)
   337  	state.AddUTXO(opUTXO)
   338  	require.NoError(state.Commit())
   339  
   340  	operationTx := &txs.Tx{Unsigned: &txs.OperationTx{
   341  		BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{
   342  			NetworkID:    constants.UnitTestID,
   343  			BlockchainID: chainID,
   344  			Ins: []*avax.TransferableInput{{
   345  				UTXOID: utxoID,
   346  				Asset:  avax.Asset{ID: assetID},
   347  				In: &secp256k1fx.TransferInput{
   348  					Amt: 20 * units.KiloAvax,
   349  					Input: secp256k1fx.Input{
   350  						SigIndices: []uint32{
   351  							0,
   352  						},
   353  					},
   354  				},
   355  			}},
   356  			Outs: []*avax.TransferableOutput{{
   357  				Asset: avax.Asset{ID: assetID},
   358  				Out: &secp256k1fx.TransferOutput{
   359  					Amt:          10 * units.KiloAvax,
   360  					OutputOwners: outputOwners,
   361  				},
   362  			}},
   363  		}},
   364  		Ops: []*txs.Operation{{
   365  			Asset: avax.Asset{ID: assetID},
   366  			UTXOIDs: []*avax.UTXOID{
   367  				&opUTXOID,
   368  			},
   369  			Op: &secp256k1fx.MintOperation{
   370  				MintInput: secp256k1fx.Input{
   371  					SigIndices: []uint32{0},
   372  				},
   373  				MintOutput: secp256k1fx.MintOutput{
   374  					OutputOwners: outputOwners,
   375  				},
   376  				TransferOutput: secp256k1fx.TransferOutput{
   377  					Amt:          12345,
   378  					OutputOwners: outputOwners,
   379  				},
   380  			},
   381  		}},
   382  	}}
   383  	require.NoError(operationTx.SignSECP256K1Fx(
   384  		codec,
   385  		[][]*secp256k1.PrivateKey{
   386  			{keys[0]},
   387  			{keys[0]},
   388  		},
   389  	))
   390  
   391  	executor := &Executor{
   392  		Codec: codec,
   393  		State: state,
   394  		Tx:    operationTx,
   395  	}
   396  
   397  	// Execute operationTx
   398  	require.NoError(operationTx.Unsigned.Visit(executor))
   399  
   400  	// Verify the consumed UTXOs were removed from the state
   401  	_, err = executor.State.GetUTXO(utxo.InputID())
   402  	require.ErrorIs(err, database.ErrNotFound)
   403  	_, err = executor.State.GetUTXO(opUTXO.InputID())
   404  	require.ErrorIs(err, database.ErrNotFound)
   405  
   406  	// Verify the produced UTXOs were added to the state
   407  	txID := operationTx.ID()
   408  	expectedOutputUTXOs := []*avax.UTXO{
   409  		{
   410  			UTXOID: avax.UTXOID{
   411  				TxID:        txID,
   412  				OutputIndex: 0,
   413  			},
   414  			Asset: avax.Asset{
   415  				ID: assetID,
   416  			},
   417  			Out: &secp256k1fx.TransferOutput{
   418  				Amt:          10 * units.KiloAvax,
   419  				OutputOwners: outputOwners,
   420  			},
   421  		},
   422  		{
   423  			UTXOID: avax.UTXOID{
   424  				TxID:        txID,
   425  				OutputIndex: 1,
   426  			},
   427  			Asset: avax.Asset{
   428  				ID: assetID,
   429  			},
   430  			Out: &secp256k1fx.MintOutput{
   431  				OutputOwners: outputOwners,
   432  			},
   433  		},
   434  		{
   435  			UTXOID: avax.UTXOID{
   436  				TxID:        txID,
   437  				OutputIndex: 2,
   438  			},
   439  			Asset: avax.Asset{
   440  				ID: assetID,
   441  			},
   442  			Out: &secp256k1fx.TransferOutput{
   443  				Amt:          12345,
   444  				OutputOwners: outputOwners,
   445  			},
   446  		},
   447  	}
   448  	for _, expectedOutputUTXO := range expectedOutputUTXOs {
   449  		expectedOutputUTXOID := expectedOutputUTXO.InputID()
   450  		outputUTXO, err := executor.State.GetUTXO(expectedOutputUTXOID)
   451  		require.NoError(err)
   452  
   453  		outputUTXOID := outputUTXO.InputID()
   454  		require.Equal(expectedOutputUTXOID, outputUTXOID)
   455  		require.Equal(expectedOutputUTXO, outputUTXO)
   456  	}
   457  }