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