github.com/ava-labs/avalanchego@v1.11.11/vms/platformvm/service_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 platformvm
     5  
     6  import (
     7  	"context"
     8  	"encoding/json"
     9  	"errors"
    10  	"fmt"
    11  	"math"
    12  	"math/rand"
    13  	"testing"
    14  	"time"
    15  
    16  	"github.com/stretchr/testify/require"
    17  	"go.uber.org/mock/gomock"
    18  
    19  	"github.com/ava-labs/avalanchego/api"
    20  	"github.com/ava-labs/avalanchego/api/keystore"
    21  	"github.com/ava-labs/avalanchego/cache"
    22  	"github.com/ava-labs/avalanchego/chains/atomic"
    23  	"github.com/ava-labs/avalanchego/database"
    24  	"github.com/ava-labs/avalanchego/database/memdb"
    25  	"github.com/ava-labs/avalanchego/database/prefixdb"
    26  	"github.com/ava-labs/avalanchego/ids"
    27  	"github.com/ava-labs/avalanchego/snow"
    28  	"github.com/ava-labs/avalanchego/snow/consensus/snowman"
    29  	"github.com/ava-labs/avalanchego/snow/validators"
    30  	"github.com/ava-labs/avalanchego/upgrade/upgradetest"
    31  	"github.com/ava-labs/avalanchego/utils/constants"
    32  	"github.com/ava-labs/avalanchego/utils/crypto/bls"
    33  	"github.com/ava-labs/avalanchego/utils/crypto/secp256k1"
    34  	"github.com/ava-labs/avalanchego/utils/formatting"
    35  	"github.com/ava-labs/avalanchego/utils/formatting/address"
    36  	"github.com/ava-labs/avalanchego/utils/logging"
    37  	"github.com/ava-labs/avalanchego/vms/components/avax"
    38  	"github.com/ava-labs/avalanchego/vms/components/gas"
    39  	"github.com/ava-labs/avalanchego/vms/platformvm/block"
    40  	"github.com/ava-labs/avalanchego/vms/platformvm/block/executor/executormock"
    41  	"github.com/ava-labs/avalanchego/vms/platformvm/genesis/genesistest"
    42  	"github.com/ava-labs/avalanchego/vms/platformvm/signer"
    43  	"github.com/ava-labs/avalanchego/vms/platformvm/state"
    44  	"github.com/ava-labs/avalanchego/vms/platformvm/status"
    45  	"github.com/ava-labs/avalanchego/vms/platformvm/txs"
    46  	"github.com/ava-labs/avalanchego/vms/secp256k1fx"
    47  	"github.com/ava-labs/avalanchego/wallet/subnet/primary/common"
    48  
    49  	avajson "github.com/ava-labs/avalanchego/utils/json"
    50  	vmkeystore "github.com/ava-labs/avalanchego/vms/components/keystore"
    51  	pchainapi "github.com/ava-labs/avalanchego/vms/platformvm/api"
    52  	blockbuilder "github.com/ava-labs/avalanchego/vms/platformvm/block/builder"
    53  	blockexecutor "github.com/ava-labs/avalanchego/vms/platformvm/block/executor"
    54  	txexecutor "github.com/ava-labs/avalanchego/vms/platformvm/txs/executor"
    55  )
    56  
    57  var (
    58  	// Test user username
    59  	testUsername = "ScoobyUser"
    60  
    61  	// Test user password, must meet minimum complexity/length requirements
    62  	testPassword = "ShaggyPassword1Zoinks!"
    63  
    64  	// Bytes decoded from CB58 "ewoqjP7PxY4yr3iLTpLisriqt94hdyDFNgchSxGGztUrTXtNN"
    65  	testPrivateKey = []byte{
    66  		0x56, 0x28, 0x9e, 0x99, 0xc9, 0x4b, 0x69, 0x12,
    67  		0xbf, 0xc1, 0x2a, 0xdc, 0x09, 0x3c, 0x9b, 0x51,
    68  		0x12, 0x4f, 0x0d, 0xc5, 0x4a, 0xc7, 0xa7, 0x66,
    69  		0xb2, 0xbc, 0x5c, 0xcf, 0x55, 0x8d, 0x80, 0x27,
    70  	}
    71  
    72  	// 3cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c
    73  	// Platform address resulting from the above private key
    74  	testAddress = "P-testing18jma8ppw3nhx5r4ap8clazz0dps7rv5umpc36y"
    75  
    76  	encodings = []formatting.Encoding{
    77  		formatting.JSON, formatting.Hex,
    78  	}
    79  )
    80  
    81  func defaultService(t *testing.T, fork upgradetest.Fork) (*Service, *mutableSharedMemory) {
    82  	vm, _, mutableSharedMemory := defaultVM(t, fork)
    83  	return &Service{
    84  		vm:          vm,
    85  		addrManager: avax.NewAddressManager(vm.ctx),
    86  		stakerAttributesCache: &cache.LRU[ids.ID, *stakerAttributes]{
    87  			Size: stakerAttributesCacheSize,
    88  		},
    89  	}, mutableSharedMemory
    90  }
    91  
    92  func TestExportKey(t *testing.T) {
    93  	require := require.New(t)
    94  
    95  	service, _ := defaultService(t, upgradetest.Latest)
    96  	service.vm.ctx.Lock.Lock()
    97  
    98  	ks := keystore.New(logging.NoLog{}, memdb.New())
    99  	require.NoError(ks.CreateUser(testUsername, testPassword))
   100  	service.vm.ctx.Keystore = ks.NewBlockchainKeyStore(service.vm.ctx.ChainID)
   101  
   102  	user, err := vmkeystore.NewUserFromKeystore(service.vm.ctx.Keystore, testUsername, testPassword)
   103  	require.NoError(err)
   104  
   105  	pk, err := secp256k1.ToPrivateKey(testPrivateKey)
   106  	require.NoError(err)
   107  
   108  	require.NoError(user.PutKeys(pk, genesistest.DefaultFundedKeys[0]))
   109  
   110  	service.vm.ctx.Lock.Unlock()
   111  
   112  	jsonString := `{"username":"` + testUsername + `","password":"` + testPassword + `","address":"` + testAddress + `"}`
   113  	args := ExportKeyArgs{}
   114  	require.NoError(json.Unmarshal([]byte(jsonString), &args))
   115  
   116  	reply := ExportKeyReply{}
   117  	require.NoError(service.ExportKey(nil, &args, &reply))
   118  
   119  	require.Equal(testPrivateKey, reply.PrivateKey.Bytes())
   120  }
   121  
   122  // Test issuing a tx and accepted
   123  func TestGetTxStatus(t *testing.T) {
   124  	require := require.New(t)
   125  	service, mutableSharedMemory := defaultService(t, upgradetest.Latest)
   126  	service.vm.ctx.Lock.Lock()
   127  
   128  	recipientKey, err := secp256k1.NewPrivateKey()
   129  	require.NoError(err)
   130  
   131  	m := atomic.NewMemory(prefixdb.New([]byte{}, service.vm.db))
   132  
   133  	sm := m.NewSharedMemory(service.vm.ctx.ChainID)
   134  	peerSharedMemory := m.NewSharedMemory(service.vm.ctx.XChainID)
   135  
   136  	randSrc := rand.NewSource(0)
   137  
   138  	utxo := &avax.UTXO{
   139  		UTXOID: avax.UTXOID{
   140  			TxID:        ids.GenerateTestID(),
   141  			OutputIndex: uint32(randSrc.Int63()),
   142  		},
   143  		Asset: avax.Asset{ID: service.vm.ctx.AVAXAssetID},
   144  		Out: &secp256k1fx.TransferOutput{
   145  			Amt: 1234567,
   146  			OutputOwners: secp256k1fx.OutputOwners{
   147  				Locktime:  0,
   148  				Addrs:     []ids.ShortID{recipientKey.Address()},
   149  				Threshold: 1,
   150  			},
   151  		},
   152  	}
   153  	utxoBytes, err := txs.Codec.Marshal(txs.CodecVersion, utxo)
   154  	require.NoError(err)
   155  
   156  	inputID := utxo.InputID()
   157  	require.NoError(peerSharedMemory.Apply(map[ids.ID]*atomic.Requests{
   158  		service.vm.ctx.ChainID: {
   159  			PutRequests: []*atomic.Element{
   160  				{
   161  					Key:   inputID[:],
   162  					Value: utxoBytes,
   163  					Traits: [][]byte{
   164  						recipientKey.Address().Bytes(),
   165  					},
   166  				},
   167  			},
   168  		},
   169  	}))
   170  
   171  	mutableSharedMemory.SharedMemory = sm
   172  
   173  	wallet := newWallet(t, service.vm, walletConfig{
   174  		keys: []*secp256k1.PrivateKey{recipientKey},
   175  	})
   176  	tx, err := wallet.IssueImportTx(
   177  		service.vm.ctx.XChainID,
   178  		&secp256k1fx.OutputOwners{
   179  			Threshold: 1,
   180  			Addrs:     []ids.ShortID{ids.ShortEmpty},
   181  		},
   182  	)
   183  	require.NoError(err)
   184  
   185  	service.vm.ctx.Lock.Unlock()
   186  
   187  	var (
   188  		arg  = &GetTxStatusArgs{TxID: tx.ID()}
   189  		resp GetTxStatusResponse
   190  	)
   191  	require.NoError(service.GetTxStatus(nil, arg, &resp))
   192  	require.Equal(status.Unknown, resp.Status)
   193  	require.Zero(resp.Reason)
   194  
   195  	// put the chain in existing chain list
   196  	require.NoError(service.vm.Network.IssueTxFromRPC(tx))
   197  	service.vm.ctx.Lock.Lock()
   198  
   199  	block, err := service.vm.BuildBlock(context.Background())
   200  	require.NoError(err)
   201  
   202  	blk := block.(*blockexecutor.Block)
   203  	require.NoError(blk.Verify(context.Background()))
   204  
   205  	require.NoError(blk.Accept(context.Background()))
   206  
   207  	service.vm.ctx.Lock.Unlock()
   208  
   209  	resp = GetTxStatusResponse{} // reset
   210  	require.NoError(service.GetTxStatus(nil, arg, &resp))
   211  	require.Equal(status.Committed, resp.Status)
   212  	require.Zero(resp.Reason)
   213  }
   214  
   215  // Test issuing and then retrieving a transaction
   216  func TestGetTx(t *testing.T) {
   217  	type test struct {
   218  		description string
   219  		createTx    func(t testing.TB, s *Service) *txs.Tx
   220  	}
   221  
   222  	tests := []test{
   223  		{
   224  			"standard block",
   225  			func(t testing.TB, s *Service) *txs.Tx {
   226  				subnetID := testSubnet1.ID()
   227  				wallet := newWallet(t, s.vm, walletConfig{
   228  					subnetIDs: []ids.ID{subnetID},
   229  				})
   230  
   231  				tx, err := wallet.IssueCreateChainTx(
   232  					subnetID,
   233  					[]byte{},
   234  					constants.AVMID,
   235  					[]ids.ID{},
   236  					"chain name",
   237  					common.WithMemo([]byte{}),
   238  				)
   239  				require.NoError(t, err)
   240  				return tx
   241  			},
   242  		},
   243  		{
   244  			"proposal block",
   245  			func(t testing.TB, s *Service) *txs.Tx {
   246  				wallet := newWallet(t, s.vm, walletConfig{})
   247  
   248  				sk, err := bls.NewSecretKey()
   249  				require.NoError(t, err)
   250  
   251  				rewardsOwner := &secp256k1fx.OutputOwners{
   252  					Threshold: 1,
   253  					Addrs:     []ids.ShortID{ids.GenerateTestShortID()},
   254  				}
   255  				tx, err := wallet.IssueAddPermissionlessValidatorTx(
   256  					&txs.SubnetValidator{
   257  						Validator: txs.Validator{
   258  							NodeID: ids.GenerateTestNodeID(),
   259  							Start:  uint64(s.vm.clock.Time().Add(txexecutor.SyncBound).Unix()),
   260  							End:    uint64(s.vm.clock.Time().Add(txexecutor.SyncBound).Add(defaultMinStakingDuration).Unix()),
   261  							Wght:   s.vm.MinValidatorStake,
   262  						},
   263  						Subnet: constants.PrimaryNetworkID,
   264  					},
   265  					signer.NewProofOfPossession(sk),
   266  					s.vm.ctx.AVAXAssetID,
   267  					rewardsOwner,
   268  					rewardsOwner,
   269  					0,
   270  					common.WithMemo([]byte{}),
   271  				)
   272  				require.NoError(t, err)
   273  				return tx
   274  			},
   275  		},
   276  		{
   277  			"atomic block",
   278  			func(t testing.TB, s *Service) *txs.Tx {
   279  				wallet := newWallet(t, s.vm, walletConfig{})
   280  
   281  				tx, err := wallet.IssueExportTx(
   282  					s.vm.ctx.XChainID,
   283  					[]*avax.TransferableOutput{{
   284  						Asset: avax.Asset{ID: s.vm.ctx.AVAXAssetID},
   285  						Out: &secp256k1fx.TransferOutput{
   286  							Amt: 100,
   287  							OutputOwners: secp256k1fx.OutputOwners{
   288  								Locktime:  0,
   289  								Threshold: 1,
   290  								Addrs:     []ids.ShortID{ids.GenerateTestShortID()},
   291  							},
   292  						},
   293  					}},
   294  					common.WithMemo([]byte{}),
   295  				)
   296  				require.NoError(t, err)
   297  				return tx
   298  			},
   299  		},
   300  	}
   301  
   302  	for _, test := range tests {
   303  		for _, encoding := range encodings {
   304  			testName := fmt.Sprintf("test '%s - %s'",
   305  				test.description,
   306  				encoding.String(),
   307  			)
   308  			t.Run(testName, func(t *testing.T) {
   309  				require := require.New(t)
   310  				service, _ := defaultService(t, upgradetest.Latest)
   311  
   312  				service.vm.ctx.Lock.Lock()
   313  				tx := test.createTx(t, service)
   314  				service.vm.ctx.Lock.Unlock()
   315  
   316  				arg := &api.GetTxArgs{
   317  					TxID:     tx.ID(),
   318  					Encoding: encoding,
   319  				}
   320  				var response api.GetTxReply
   321  				err := service.GetTx(nil, arg, &response)
   322  				require.ErrorIs(err, database.ErrNotFound) // We haven't issued the tx yet
   323  
   324  				require.NoError(service.vm.Network.IssueTxFromRPC(tx))
   325  				service.vm.ctx.Lock.Lock()
   326  
   327  				blk, err := service.vm.BuildBlock(context.Background())
   328  				require.NoError(err)
   329  
   330  				require.NoError(blk.Verify(context.Background()))
   331  
   332  				require.NoError(blk.Accept(context.Background()))
   333  
   334  				if blk, ok := blk.(snowman.OracleBlock); ok { // For proposal blocks, commit them
   335  					options, err := blk.Options(context.Background())
   336  					if !errors.Is(err, snowman.ErrNotOracle) {
   337  						require.NoError(err)
   338  
   339  						commit := options[0].(*blockexecutor.Block)
   340  						require.IsType(&block.BanffCommitBlock{}, commit.Block)
   341  						require.NoError(commit.Verify(context.Background()))
   342  						require.NoError(commit.Accept(context.Background()))
   343  					}
   344  				}
   345  
   346  				service.vm.ctx.Lock.Unlock()
   347  
   348  				require.NoError(service.GetTx(nil, arg, &response))
   349  
   350  				switch encoding {
   351  				case formatting.Hex:
   352  					// we're always guaranteed a string for hex encodings.
   353  					var txStr string
   354  					require.NoError(json.Unmarshal(response.Tx, &txStr))
   355  					responseTxBytes, err := formatting.Decode(response.Encoding, txStr)
   356  					require.NoError(err)
   357  					require.Equal(tx.Bytes(), responseTxBytes)
   358  
   359  				case formatting.JSON:
   360  					tx.Unsigned.InitCtx(service.vm.ctx)
   361  					expectedTxJSON, err := json.Marshal(tx)
   362  					require.NoError(err)
   363  					require.Equal(expectedTxJSON, []byte(response.Tx))
   364  				}
   365  			})
   366  		}
   367  	}
   368  }
   369  
   370  func TestGetBalance(t *testing.T) {
   371  	require := require.New(t)
   372  	service, _ := defaultService(t, upgradetest.Durango)
   373  
   374  	feeCalculator := state.PickFeeCalculator(&service.vm.Config, service.vm.state)
   375  	createSubnetFee, err := feeCalculator.CalculateFee(testSubnet1.Unsigned)
   376  	require.NoError(err)
   377  
   378  	// Ensure GetStake is correct for each of the genesis validators
   379  	genesis := genesistest.New(t, genesistest.Config{})
   380  	for idx, utxo := range genesis.UTXOs {
   381  		out := utxo.Out.(*secp256k1fx.TransferOutput)
   382  		require.Len(out.Addrs, 1)
   383  
   384  		addr := out.Addrs[0]
   385  		addrStr, err := address.Format("P", constants.UnitTestHRP, addr.Bytes())
   386  		require.NoError(err)
   387  
   388  		request := GetBalanceRequest{
   389  			Addresses: []string{
   390  				addrStr,
   391  			},
   392  		}
   393  		reply := GetBalanceResponse{}
   394  
   395  		require.NoError(service.GetBalance(nil, &request, &reply))
   396  		balance := genesistest.DefaultInitialBalance
   397  		if idx == 0 {
   398  			// we use the first key to fund a subnet creation in [defaultGenesis].
   399  			// As such we need to account for the subnet creation fee
   400  			balance = genesistest.DefaultInitialBalance - createSubnetFee
   401  		}
   402  		require.Equal(avajson.Uint64(balance), reply.Balance)
   403  		require.Equal(avajson.Uint64(balance), reply.Unlocked)
   404  		require.Equal(avajson.Uint64(0), reply.LockedStakeable)
   405  		require.Equal(avajson.Uint64(0), reply.LockedNotStakeable)
   406  	}
   407  }
   408  
   409  func TestGetStake(t *testing.T) {
   410  	require := require.New(t)
   411  	service, _ := defaultService(t, upgradetest.Latest)
   412  
   413  	// Ensure GetStake is correct for each of the genesis validators
   414  	genesis := genesistest.New(t, genesistest.Config{})
   415  	addrsStrs := []string{}
   416  	for _, validatorTx := range genesis.Validators {
   417  		validator := validatorTx.Unsigned.(*txs.AddValidatorTx)
   418  		require.Len(validator.StakeOuts, 1)
   419  		stakeOut := validator.StakeOuts[0].Out.(*secp256k1fx.TransferOutput)
   420  		require.Len(stakeOut.Addrs, 1)
   421  		addr := stakeOut.Addrs[0]
   422  
   423  		addrStr, err := address.Format("P", constants.UnitTestHRP, addr.Bytes())
   424  		require.NoError(err)
   425  
   426  		addrsStrs = append(addrsStrs, addrStr)
   427  
   428  		args := GetStakeArgs{
   429  			JSONAddresses: api.JSONAddresses{
   430  				Addresses: []string{addrStr},
   431  			},
   432  			Encoding: formatting.Hex,
   433  		}
   434  		response := GetStakeReply{}
   435  		require.NoError(service.GetStake(nil, &args, &response))
   436  		require.Equal(genesistest.DefaultValidatorWeight, uint64(response.Staked))
   437  		require.Len(response.Outputs, 1)
   438  
   439  		// Unmarshal into an output
   440  		outputBytes, err := formatting.Decode(args.Encoding, response.Outputs[0])
   441  		require.NoError(err)
   442  
   443  		var output avax.TransferableOutput
   444  		_, err = txs.Codec.Unmarshal(outputBytes, &output)
   445  		require.NoError(err)
   446  
   447  		require.Equal(
   448  			avax.TransferableOutput{
   449  				Asset: avax.Asset{
   450  					ID: service.vm.ctx.AVAXAssetID,
   451  				},
   452  				Out: &secp256k1fx.TransferOutput{
   453  					Amt: genesistest.DefaultValidatorWeight,
   454  					OutputOwners: secp256k1fx.OutputOwners{
   455  						Threshold: 1,
   456  						Addrs: []ids.ShortID{
   457  							addr,
   458  						},
   459  					},
   460  				},
   461  			},
   462  			output,
   463  		)
   464  	}
   465  
   466  	// Make sure this works for multiple addresses
   467  	args := GetStakeArgs{
   468  		JSONAddresses: api.JSONAddresses{
   469  			Addresses: addrsStrs,
   470  		},
   471  		Encoding: formatting.Hex,
   472  	}
   473  	response := GetStakeReply{}
   474  	require.NoError(service.GetStake(nil, &args, &response))
   475  	require.Equal(len(genesis.Validators)*int(genesistest.DefaultValidatorWeight), int(response.Staked))
   476  	require.Len(response.Outputs, len(genesis.Validators))
   477  
   478  	for _, outputStr := range response.Outputs {
   479  		outputBytes, err := formatting.Decode(args.Encoding, outputStr)
   480  		require.NoError(err)
   481  
   482  		var output avax.TransferableOutput
   483  		_, err = txs.Codec.Unmarshal(outputBytes, &output)
   484  		require.NoError(err)
   485  
   486  		out := output.Out.(*secp256k1fx.TransferOutput)
   487  		require.Equal(genesistest.DefaultValidatorWeight, out.Amt)
   488  		require.Equal(uint32(1), out.Threshold)
   489  		require.Zero(out.Locktime)
   490  		require.Len(out.Addrs, 1)
   491  	}
   492  
   493  	oldStake := genesistest.DefaultValidatorWeight
   494  
   495  	service.vm.ctx.Lock.Lock()
   496  
   497  	wallet := newWallet(t, service.vm, walletConfig{})
   498  
   499  	// Add a delegator
   500  	stakeAmount := service.vm.MinDelegatorStake + 12345
   501  	delegatorNodeID := genesistest.DefaultNodeIDs[0]
   502  	delegatorEndTime := genesistest.DefaultValidatorStartTime.Add(defaultMinStakingDuration)
   503  	rewardsOwner := &secp256k1fx.OutputOwners{
   504  		Threshold: 1,
   505  		Addrs:     []ids.ShortID{ids.GenerateTestShortID()},
   506  	}
   507  	withChangeOwner := common.WithChangeOwner(&secp256k1fx.OutputOwners{
   508  		Threshold: 1,
   509  		Addrs:     []ids.ShortID{genesistest.DefaultFundedKeys[0].Address()},
   510  	})
   511  	tx, err := wallet.IssueAddDelegatorTx(
   512  		&txs.Validator{
   513  			NodeID: delegatorNodeID,
   514  			Start:  genesistest.DefaultValidatorStartTimeUnix,
   515  			End:    uint64(delegatorEndTime.Unix()),
   516  			Wght:   stakeAmount,
   517  		},
   518  		rewardsOwner,
   519  		withChangeOwner,
   520  	)
   521  	require.NoError(err)
   522  
   523  	addDelTx := tx.Unsigned.(*txs.AddDelegatorTx)
   524  	staker, err := state.NewCurrentStaker(
   525  		tx.ID(),
   526  		addDelTx,
   527  		genesistest.DefaultValidatorStartTime,
   528  		0,
   529  	)
   530  	require.NoError(err)
   531  
   532  	service.vm.state.PutCurrentDelegator(staker)
   533  	service.vm.state.AddTx(tx, status.Committed)
   534  	require.NoError(service.vm.state.Commit())
   535  
   536  	service.vm.ctx.Lock.Unlock()
   537  
   538  	// Make sure the delegator addr has the right stake (old stake + stakeAmount)
   539  	addr, _ := service.addrManager.FormatLocalAddress(genesistest.DefaultFundedKeys[0].Address())
   540  	args.Addresses = []string{addr}
   541  	require.NoError(service.GetStake(nil, &args, &response))
   542  	require.Equal(oldStake+stakeAmount, uint64(response.Staked))
   543  	require.Len(response.Outputs, 2)
   544  
   545  	// Unmarshal into transferable outputs
   546  	outputs := make([]avax.TransferableOutput, 2)
   547  	for i := range outputs {
   548  		outputBytes, err := formatting.Decode(args.Encoding, response.Outputs[i])
   549  		require.NoError(err)
   550  		_, err = txs.Codec.Unmarshal(outputBytes, &outputs[i])
   551  		require.NoError(err)
   552  	}
   553  
   554  	// Make sure the stake amount is as expected
   555  	require.Equal(stakeAmount+oldStake, outputs[0].Out.Amount()+outputs[1].Out.Amount())
   556  
   557  	oldStake = uint64(response.Staked)
   558  
   559  	service.vm.ctx.Lock.Lock()
   560  
   561  	// Make sure this works for pending stakers
   562  	// Add a pending staker
   563  	stakeAmount = service.vm.MinValidatorStake + 54321
   564  	pendingStakerNodeID := ids.GenerateTestNodeID()
   565  	pendingStakerEndTime := uint64(genesistest.DefaultValidatorStartTime.Add(defaultMinStakingDuration).Unix())
   566  	tx, err = wallet.IssueAddValidatorTx(
   567  		&txs.Validator{
   568  			NodeID: pendingStakerNodeID,
   569  			Start:  uint64(genesistest.DefaultValidatorStartTime.Unix()),
   570  			End:    pendingStakerEndTime,
   571  			Wght:   stakeAmount,
   572  		},
   573  		rewardsOwner,
   574  		0,
   575  		withChangeOwner,
   576  	)
   577  	require.NoError(err)
   578  
   579  	staker, err = state.NewPendingStaker(
   580  		tx.ID(),
   581  		tx.Unsigned.(*txs.AddValidatorTx),
   582  	)
   583  	require.NoError(err)
   584  
   585  	require.NoError(service.vm.state.PutPendingValidator(staker))
   586  	service.vm.state.AddTx(tx, status.Committed)
   587  	require.NoError(service.vm.state.Commit())
   588  
   589  	service.vm.ctx.Lock.Unlock()
   590  
   591  	// Make sure the delegator has the right stake (old stake + stakeAmount)
   592  	require.NoError(service.GetStake(nil, &args, &response))
   593  	require.Equal(oldStake+stakeAmount, uint64(response.Staked))
   594  	require.Len(response.Outputs, 3)
   595  
   596  	// Unmarshal
   597  	outputs = make([]avax.TransferableOutput, 3)
   598  	for i := range outputs {
   599  		outputBytes, err := formatting.Decode(args.Encoding, response.Outputs[i])
   600  		require.NoError(err)
   601  		_, err = txs.Codec.Unmarshal(outputBytes, &outputs[i])
   602  		require.NoError(err)
   603  	}
   604  
   605  	// Make sure the stake amount is as expected
   606  	require.Equal(stakeAmount+oldStake, outputs[0].Out.Amount()+outputs[1].Out.Amount()+outputs[2].Out.Amount())
   607  }
   608  
   609  func TestGetCurrentValidators(t *testing.T) {
   610  	require := require.New(t)
   611  	service, _ := defaultService(t, upgradetest.Latest)
   612  
   613  	genesis := genesistest.New(t, genesistest.Config{})
   614  
   615  	// Call getValidators
   616  	args := GetCurrentValidatorsArgs{SubnetID: constants.PrimaryNetworkID}
   617  	response := GetCurrentValidatorsReply{}
   618  
   619  	require.NoError(service.GetCurrentValidators(nil, &args, &response))
   620  	require.Len(response.Validators, len(genesis.Validators))
   621  
   622  	for _, validatorTx := range genesis.Validators {
   623  		validator := validatorTx.Unsigned.(*txs.AddValidatorTx)
   624  		nodeID := validator.NodeID()
   625  
   626  		found := false
   627  		for i := 0; i < len(response.Validators); i++ {
   628  			gotVdr := response.Validators[i].(pchainapi.PermissionlessValidator)
   629  			if gotVdr.NodeID != nodeID {
   630  				continue
   631  			}
   632  
   633  			require.Equal(validator.EndTime().Unix(), int64(gotVdr.EndTime))
   634  			require.Equal(validator.StartTime().Unix(), int64(gotVdr.StartTime))
   635  			found = true
   636  			break
   637  		}
   638  		require.True(found, "expected validators to contain %s but didn't", nodeID)
   639  	}
   640  
   641  	// Add a delegator
   642  	stakeAmount := service.vm.MinDelegatorStake + 12345
   643  	validatorNodeID := genesistest.DefaultNodeIDs[1]
   644  	delegatorEndTime := genesistest.DefaultValidatorStartTime.Add(defaultMinStakingDuration)
   645  
   646  	service.vm.ctx.Lock.Lock()
   647  
   648  	wallet := newWallet(t, service.vm, walletConfig{})
   649  	delTx, err := wallet.IssueAddDelegatorTx(
   650  		&txs.Validator{
   651  			NodeID: validatorNodeID,
   652  			Start:  genesistest.DefaultValidatorStartTimeUnix,
   653  			End:    uint64(delegatorEndTime.Unix()),
   654  			Wght:   stakeAmount,
   655  		},
   656  		&secp256k1fx.OutputOwners{
   657  			Threshold: 1,
   658  			Addrs:     []ids.ShortID{ids.GenerateTestShortID()},
   659  		},
   660  		common.WithChangeOwner(&secp256k1fx.OutputOwners{
   661  			Threshold: 1,
   662  			Addrs:     []ids.ShortID{genesistest.DefaultFundedKeys[0].Address()},
   663  		}),
   664  	)
   665  	require.NoError(err)
   666  
   667  	addDelTx := delTx.Unsigned.(*txs.AddDelegatorTx)
   668  	staker, err := state.NewCurrentStaker(
   669  		delTx.ID(),
   670  		addDelTx,
   671  		genesistest.DefaultValidatorStartTime,
   672  		0,
   673  	)
   674  	require.NoError(err)
   675  
   676  	service.vm.state.PutCurrentDelegator(staker)
   677  	service.vm.state.AddTx(delTx, status.Committed)
   678  	require.NoError(service.vm.state.Commit())
   679  
   680  	service.vm.ctx.Lock.Unlock()
   681  
   682  	// Call getCurrentValidators
   683  	args = GetCurrentValidatorsArgs{SubnetID: constants.PrimaryNetworkID}
   684  	require.NoError(service.GetCurrentValidators(nil, &args, &response))
   685  	require.Len(response.Validators, len(genesis.Validators))
   686  
   687  	// Make sure the delegator is there
   688  	found := false
   689  	for i := 0; i < len(response.Validators) && !found; i++ {
   690  		vdr := response.Validators[i].(pchainapi.PermissionlessValidator)
   691  		if vdr.NodeID != validatorNodeID {
   692  			continue
   693  		}
   694  		found = true
   695  
   696  		require.Nil(vdr.Delegators)
   697  
   698  		innerArgs := GetCurrentValidatorsArgs{
   699  			SubnetID: constants.PrimaryNetworkID,
   700  			NodeIDs:  []ids.NodeID{vdr.NodeID},
   701  		}
   702  		innerResponse := GetCurrentValidatorsReply{}
   703  		require.NoError(service.GetCurrentValidators(nil, &innerArgs, &innerResponse))
   704  		require.Len(innerResponse.Validators, 1)
   705  
   706  		innerVdr := innerResponse.Validators[0].(pchainapi.PermissionlessValidator)
   707  		require.Equal(vdr.NodeID, innerVdr.NodeID)
   708  
   709  		require.NotNil(innerVdr.Delegators)
   710  		require.Len(*innerVdr.Delegators, 1)
   711  		delegator := (*innerVdr.Delegators)[0]
   712  		require.Equal(delegator.NodeID, innerVdr.NodeID)
   713  		require.Equal(uint64(delegator.StartTime), genesistest.DefaultValidatorStartTimeUnix)
   714  		require.Equal(int64(delegator.EndTime), delegatorEndTime.Unix())
   715  		require.Equal(uint64(delegator.Weight), stakeAmount)
   716  	}
   717  	require.True(found)
   718  
   719  	service.vm.ctx.Lock.Lock()
   720  
   721  	// Reward the delegator
   722  	tx, err := blockbuilder.NewRewardValidatorTx(service.vm.ctx, delTx.ID())
   723  	require.NoError(err)
   724  	service.vm.state.AddTx(tx, status.Committed)
   725  	service.vm.state.DeleteCurrentDelegator(staker)
   726  	require.NoError(service.vm.state.SetDelegateeReward(staker.SubnetID, staker.NodeID, 100000))
   727  	require.NoError(service.vm.state.Commit())
   728  
   729  	service.vm.ctx.Lock.Unlock()
   730  
   731  	// Call getValidators
   732  	response = GetCurrentValidatorsReply{}
   733  	require.NoError(service.GetCurrentValidators(nil, &args, &response))
   734  	require.Len(response.Validators, len(genesis.Validators))
   735  
   736  	for _, vdr := range response.Validators {
   737  		castVdr := vdr.(pchainapi.PermissionlessValidator)
   738  		if castVdr.NodeID != validatorNodeID {
   739  			continue
   740  		}
   741  		require.Equal(uint64(100000), uint64(*castVdr.AccruedDelegateeReward))
   742  	}
   743  }
   744  
   745  func TestGetTimestamp(t *testing.T) {
   746  	require := require.New(t)
   747  	service, _ := defaultService(t, upgradetest.Latest)
   748  
   749  	reply := GetTimestampReply{}
   750  	require.NoError(service.GetTimestamp(nil, nil, &reply))
   751  
   752  	service.vm.ctx.Lock.Lock()
   753  
   754  	require.Equal(service.vm.state.GetTimestamp(), reply.Timestamp)
   755  
   756  	newTimestamp := reply.Timestamp.Add(time.Second)
   757  	service.vm.state.SetTimestamp(newTimestamp)
   758  
   759  	service.vm.ctx.Lock.Unlock()
   760  
   761  	require.NoError(service.GetTimestamp(nil, nil, &reply))
   762  	require.Equal(newTimestamp, reply.Timestamp)
   763  }
   764  
   765  func TestGetBlock(t *testing.T) {
   766  	tests := []struct {
   767  		name     string
   768  		encoding formatting.Encoding
   769  	}{
   770  		{
   771  			name:     "json",
   772  			encoding: formatting.JSON,
   773  		},
   774  		{
   775  			name:     "hex",
   776  			encoding: formatting.Hex,
   777  		},
   778  	}
   779  
   780  	for _, test := range tests {
   781  		t.Run(test.name, func(t *testing.T) {
   782  			require := require.New(t)
   783  			service, _ := defaultService(t, upgradetest.Latest)
   784  			service.vm.ctx.Lock.Lock()
   785  
   786  			service.vm.Config.CreateAssetTxFee = 100 * defaultTxFee
   787  
   788  			subnetID := testSubnet1.ID()
   789  			wallet := newWallet(t, service.vm, walletConfig{
   790  				subnetIDs: []ids.ID{subnetID},
   791  			})
   792  			tx, err := wallet.IssueCreateChainTx(
   793  				subnetID,
   794  				[]byte{},
   795  				constants.AVMID,
   796  				[]ids.ID{},
   797  				"chain name",
   798  				common.WithMemo([]byte{}),
   799  			)
   800  			require.NoError(err)
   801  
   802  			preferredID := service.vm.manager.Preferred()
   803  			preferred, err := service.vm.manager.GetBlock(preferredID)
   804  			require.NoError(err)
   805  
   806  			statelessBlock, err := block.NewBanffStandardBlock(
   807  				preferred.Timestamp(),
   808  				preferred.ID(),
   809  				preferred.Height()+1,
   810  				[]*txs.Tx{tx},
   811  			)
   812  			require.NoError(err)
   813  
   814  			blk := service.vm.manager.NewBlock(statelessBlock)
   815  
   816  			require.NoError(blk.Verify(context.Background()))
   817  			require.NoError(blk.Accept(context.Background()))
   818  
   819  			service.vm.ctx.Lock.Unlock()
   820  
   821  			args := api.GetBlockArgs{
   822  				BlockID:  blk.ID(),
   823  				Encoding: test.encoding,
   824  			}
   825  			response := api.GetBlockResponse{}
   826  			require.NoError(service.GetBlock(nil, &args, &response))
   827  
   828  			switch {
   829  			case test.encoding == formatting.JSON:
   830  				statelessBlock.InitCtx(service.vm.ctx)
   831  				expectedBlockJSON, err := json.Marshal(statelessBlock)
   832  				require.NoError(err)
   833  				require.Equal(expectedBlockJSON, []byte(response.Block))
   834  			default:
   835  				var blockStr string
   836  				require.NoError(json.Unmarshal(response.Block, &blockStr))
   837  				responseBlockBytes, err := formatting.Decode(response.Encoding, blockStr)
   838  				require.NoError(err)
   839  				require.Equal(blk.Bytes(), responseBlockBytes)
   840  			}
   841  
   842  			require.Equal(test.encoding, response.Encoding)
   843  		})
   844  	}
   845  }
   846  
   847  func TestGetValidatorsAtReplyMarshalling(t *testing.T) {
   848  	require := require.New(t)
   849  
   850  	reply := &GetValidatorsAtReply{
   851  		Validators: make(map[ids.NodeID]*validators.GetValidatorOutput),
   852  	}
   853  
   854  	{
   855  		reply.Validators[ids.EmptyNodeID] = &validators.GetValidatorOutput{
   856  			NodeID:    ids.EmptyNodeID,
   857  			PublicKey: nil,
   858  			Weight:    0,
   859  		}
   860  	}
   861  	{
   862  		nodeID := ids.GenerateTestNodeID()
   863  		sk, err := bls.NewSecretKey()
   864  		require.NoError(err)
   865  		reply.Validators[nodeID] = &validators.GetValidatorOutput{
   866  			NodeID:    nodeID,
   867  			PublicKey: bls.PublicFromSecretKey(sk),
   868  			Weight:    math.MaxUint64,
   869  		}
   870  	}
   871  
   872  	replyJSON, err := reply.MarshalJSON()
   873  	require.NoError(err)
   874  
   875  	var parsedReply GetValidatorsAtReply
   876  	require.NoError(parsedReply.UnmarshalJSON(replyJSON))
   877  	require.Equal(reply, &parsedReply)
   878  }
   879  
   880  func TestServiceGetBlockByHeight(t *testing.T) {
   881  	ctrl := gomock.NewController(t)
   882  
   883  	blockID := ids.GenerateTestID()
   884  	blockHeight := uint64(1337)
   885  
   886  	type test struct {
   887  		name                        string
   888  		serviceAndExpectedBlockFunc func(t *testing.T, ctrl *gomock.Controller) (*Service, interface{})
   889  		encoding                    formatting.Encoding
   890  		expectedErr                 error
   891  	}
   892  
   893  	tests := []test{
   894  		{
   895  			name: "block height not found",
   896  			serviceAndExpectedBlockFunc: func(_ *testing.T, ctrl *gomock.Controller) (*Service, interface{}) {
   897  				state := state.NewMockState(ctrl)
   898  				state.EXPECT().GetBlockIDAtHeight(blockHeight).Return(ids.Empty, database.ErrNotFound)
   899  
   900  				manager := executormock.NewManager(ctrl)
   901  				return &Service{
   902  					vm: &VM{
   903  						state:   state,
   904  						manager: manager,
   905  						ctx: &snow.Context{
   906  							Log: logging.NoLog{},
   907  						},
   908  					},
   909  				}, nil
   910  			},
   911  			encoding:    formatting.Hex,
   912  			expectedErr: database.ErrNotFound,
   913  		},
   914  		{
   915  			name: "block not found",
   916  			serviceAndExpectedBlockFunc: func(_ *testing.T, ctrl *gomock.Controller) (*Service, interface{}) {
   917  				state := state.NewMockState(ctrl)
   918  				state.EXPECT().GetBlockIDAtHeight(blockHeight).Return(blockID, nil)
   919  
   920  				manager := executormock.NewManager(ctrl)
   921  				manager.EXPECT().GetStatelessBlock(blockID).Return(nil, database.ErrNotFound)
   922  				return &Service{
   923  					vm: &VM{
   924  						state:   state,
   925  						manager: manager,
   926  						ctx: &snow.Context{
   927  							Log: logging.NoLog{},
   928  						},
   929  					},
   930  				}, nil
   931  			},
   932  			encoding:    formatting.Hex,
   933  			expectedErr: database.ErrNotFound,
   934  		},
   935  		{
   936  			name: "JSON format",
   937  			serviceAndExpectedBlockFunc: func(_ *testing.T, ctrl *gomock.Controller) (*Service, interface{}) {
   938  				block := block.NewMockBlock(ctrl)
   939  				block.EXPECT().InitCtx(gomock.Any())
   940  
   941  				state := state.NewMockState(ctrl)
   942  				state.EXPECT().GetBlockIDAtHeight(blockHeight).Return(blockID, nil)
   943  
   944  				manager := executormock.NewManager(ctrl)
   945  				manager.EXPECT().GetStatelessBlock(blockID).Return(block, nil)
   946  				return &Service{
   947  					vm: &VM{
   948  						state:   state,
   949  						manager: manager,
   950  						ctx: &snow.Context{
   951  							Log: logging.NoLog{},
   952  						},
   953  					},
   954  				}, block
   955  			},
   956  			encoding:    formatting.JSON,
   957  			expectedErr: nil,
   958  		},
   959  		{
   960  			name: "hex format",
   961  			serviceAndExpectedBlockFunc: func(t *testing.T, ctrl *gomock.Controller) (*Service, interface{}) {
   962  				block := block.NewMockBlock(ctrl)
   963  				blockBytes := []byte("hi mom")
   964  				block.EXPECT().Bytes().Return(blockBytes)
   965  
   966  				state := state.NewMockState(ctrl)
   967  				state.EXPECT().GetBlockIDAtHeight(blockHeight).Return(blockID, nil)
   968  
   969  				expected, err := formatting.Encode(formatting.Hex, blockBytes)
   970  				require.NoError(t, err)
   971  
   972  				manager := executormock.NewManager(ctrl)
   973  				manager.EXPECT().GetStatelessBlock(blockID).Return(block, nil)
   974  				return &Service{
   975  					vm: &VM{
   976  						state:   state,
   977  						manager: manager,
   978  						ctx: &snow.Context{
   979  							Log: logging.NoLog{},
   980  						},
   981  					},
   982  				}, expected
   983  			},
   984  			encoding:    formatting.Hex,
   985  			expectedErr: nil,
   986  		},
   987  		{
   988  			name: "hexc format",
   989  			serviceAndExpectedBlockFunc: func(t *testing.T, ctrl *gomock.Controller) (*Service, interface{}) {
   990  				block := block.NewMockBlock(ctrl)
   991  				blockBytes := []byte("hi mom")
   992  				block.EXPECT().Bytes().Return(blockBytes)
   993  
   994  				state := state.NewMockState(ctrl)
   995  				state.EXPECT().GetBlockIDAtHeight(blockHeight).Return(blockID, nil)
   996  
   997  				expected, err := formatting.Encode(formatting.HexC, blockBytes)
   998  				require.NoError(t, err)
   999  
  1000  				manager := executormock.NewManager(ctrl)
  1001  				manager.EXPECT().GetStatelessBlock(blockID).Return(block, nil)
  1002  				return &Service{
  1003  					vm: &VM{
  1004  						state:   state,
  1005  						manager: manager,
  1006  						ctx: &snow.Context{
  1007  							Log: logging.NoLog{},
  1008  						},
  1009  					},
  1010  				}, expected
  1011  			},
  1012  			encoding:    formatting.HexC,
  1013  			expectedErr: nil,
  1014  		},
  1015  		{
  1016  			name: "hexnc format",
  1017  			serviceAndExpectedBlockFunc: func(t *testing.T, ctrl *gomock.Controller) (*Service, interface{}) {
  1018  				block := block.NewMockBlock(ctrl)
  1019  				blockBytes := []byte("hi mom")
  1020  				block.EXPECT().Bytes().Return(blockBytes)
  1021  
  1022  				state := state.NewMockState(ctrl)
  1023  				state.EXPECT().GetBlockIDAtHeight(blockHeight).Return(blockID, nil)
  1024  
  1025  				expected, err := formatting.Encode(formatting.HexNC, blockBytes)
  1026  				require.NoError(t, err)
  1027  
  1028  				manager := executormock.NewManager(ctrl)
  1029  				manager.EXPECT().GetStatelessBlock(blockID).Return(block, nil)
  1030  				return &Service{
  1031  					vm: &VM{
  1032  						state:   state,
  1033  						manager: manager,
  1034  						ctx: &snow.Context{
  1035  							Log: logging.NoLog{},
  1036  						},
  1037  					},
  1038  				}, expected
  1039  			},
  1040  			encoding:    formatting.HexNC,
  1041  			expectedErr: nil,
  1042  		},
  1043  	}
  1044  
  1045  	for _, tt := range tests {
  1046  		t.Run(tt.name, func(t *testing.T) {
  1047  			require := require.New(t)
  1048  
  1049  			service, expected := tt.serviceAndExpectedBlockFunc(t, ctrl)
  1050  
  1051  			args := &api.GetBlockByHeightArgs{
  1052  				Height:   avajson.Uint64(blockHeight),
  1053  				Encoding: tt.encoding,
  1054  			}
  1055  			reply := &api.GetBlockResponse{}
  1056  			err := service.GetBlockByHeight(nil, args, reply)
  1057  			require.ErrorIs(err, tt.expectedErr)
  1058  			if tt.expectedErr != nil {
  1059  				return
  1060  			}
  1061  			require.Equal(tt.encoding, reply.Encoding)
  1062  
  1063  			expectedJSON, err := json.Marshal(expected)
  1064  			require.NoError(err)
  1065  
  1066  			require.Equal(json.RawMessage(expectedJSON), reply.Block)
  1067  		})
  1068  	}
  1069  }
  1070  
  1071  func TestServiceGetSubnets(t *testing.T) {
  1072  	require := require.New(t)
  1073  	service, _ := defaultService(t, upgradetest.Latest)
  1074  
  1075  	testSubnet1ID := testSubnet1.ID()
  1076  
  1077  	var response GetSubnetsResponse
  1078  	require.NoError(service.GetSubnets(nil, &GetSubnetsArgs{}, &response))
  1079  	require.Equal([]APISubnet{
  1080  		{
  1081  			ID: testSubnet1ID,
  1082  			ControlKeys: []string{
  1083  				"P-testing1d6kkj0qh4wcmus3tk59npwt3rluc6en72ngurd",
  1084  				"P-testing17fpqs358de5lgu7a5ftpw2t8axf0pm33983krk",
  1085  				"P-testing1lnk637g0edwnqc2tn8tel39652fswa3xk4r65e",
  1086  			},
  1087  			Threshold: 2,
  1088  		},
  1089  		{
  1090  			ID:          constants.PrimaryNetworkID,
  1091  			ControlKeys: []string{},
  1092  			Threshold:   0,
  1093  		},
  1094  	}, response.Subnets)
  1095  
  1096  	newOwnerIDStr := "P-testing1t73fa4p4dypa4s3kgufuvr6hmprjclw66mgqgm"
  1097  	newOwnerID, err := service.addrManager.ParseLocalAddress(newOwnerIDStr)
  1098  	require.NoError(err)
  1099  
  1100  	service.vm.ctx.Lock.Lock()
  1101  	service.vm.state.SetSubnetOwner(testSubnet1ID, &secp256k1fx.OutputOwners{
  1102  		Addrs:     []ids.ShortID{newOwnerID},
  1103  		Threshold: 1,
  1104  	})
  1105  	service.vm.ctx.Lock.Unlock()
  1106  
  1107  	require.NoError(service.GetSubnets(nil, &GetSubnetsArgs{}, &response))
  1108  	require.Equal([]APISubnet{
  1109  		{
  1110  			ID: testSubnet1ID,
  1111  			ControlKeys: []string{
  1112  				newOwnerIDStr,
  1113  			},
  1114  			Threshold: 1,
  1115  		},
  1116  		{
  1117  			ID:          constants.PrimaryNetworkID,
  1118  			ControlKeys: []string{},
  1119  			Threshold:   0,
  1120  		},
  1121  	}, response.Subnets)
  1122  }
  1123  
  1124  func TestGetFeeConfig(t *testing.T) {
  1125  	tests := []struct {
  1126  		name     string
  1127  		etnaTime time.Time
  1128  		expected gas.Config
  1129  	}{
  1130  		{
  1131  			name:     "pre-etna",
  1132  			etnaTime: time.Now().Add(time.Hour),
  1133  			expected: gas.Config{},
  1134  		},
  1135  		{
  1136  			name:     "post-etna",
  1137  			etnaTime: time.Now().Add(-time.Hour),
  1138  			expected: defaultDynamicFeeConfig,
  1139  		},
  1140  	}
  1141  	for _, test := range tests {
  1142  		t.Run(test.name, func(t *testing.T) {
  1143  			require := require.New(t)
  1144  
  1145  			service, _ := defaultService(t, upgradetest.Latest)
  1146  			service.vm.Config.UpgradeConfig.EtnaTime = test.etnaTime
  1147  
  1148  			var reply gas.Config
  1149  			require.NoError(service.GetFeeConfig(nil, nil, &reply))
  1150  			require.Equal(test.expected, reply)
  1151  		})
  1152  	}
  1153  }
  1154  
  1155  func FuzzGetFeeState(f *testing.F) {
  1156  	f.Fuzz(func(t *testing.T, capacity, excess uint64) {
  1157  		require := require.New(t)
  1158  
  1159  		service, _ := defaultService(t, upgradetest.Latest)
  1160  
  1161  		var (
  1162  			expectedState = gas.State{
  1163  				Capacity: gas.Gas(capacity),
  1164  				Excess:   gas.Gas(excess),
  1165  			}
  1166  			expectedTime  = time.Now()
  1167  			expectedReply = GetFeeStateReply{
  1168  				State: expectedState,
  1169  				Price: gas.CalculatePrice(
  1170  					defaultDynamicFeeConfig.MinPrice,
  1171  					expectedState.Excess,
  1172  					defaultDynamicFeeConfig.ExcessConversionConstant,
  1173  				),
  1174  				Time: expectedTime,
  1175  			}
  1176  		)
  1177  
  1178  		service.vm.ctx.Lock.Lock()
  1179  		service.vm.state.SetFeeState(expectedState)
  1180  		service.vm.state.SetTimestamp(expectedTime)
  1181  		service.vm.ctx.Lock.Unlock()
  1182  
  1183  		var reply GetFeeStateReply
  1184  		require.NoError(service.GetFeeState(nil, nil, &reply))
  1185  		require.Equal(expectedReply, reply)
  1186  	})
  1187  }