github.com/ava-labs/avalanchego@v1.11.11/vms/platformvm/txs/executor/helpers_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  	"math"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/stretchr/testify/require"
    12  
    13  	"github.com/ava-labs/avalanchego/chains"
    14  	"github.com/ava-labs/avalanchego/chains/atomic"
    15  	"github.com/ava-labs/avalanchego/codec"
    16  	"github.com/ava-labs/avalanchego/codec/linearcodec"
    17  	"github.com/ava-labs/avalanchego/database/memdb"
    18  	"github.com/ava-labs/avalanchego/database/versiondb"
    19  	"github.com/ava-labs/avalanchego/ids"
    20  	"github.com/ava-labs/avalanchego/snow"
    21  	"github.com/ava-labs/avalanchego/snow/snowtest"
    22  	"github.com/ava-labs/avalanchego/snow/uptime"
    23  	"github.com/ava-labs/avalanchego/snow/validators"
    24  	"github.com/ava-labs/avalanchego/upgrade/upgradetest"
    25  	"github.com/ava-labs/avalanchego/utils"
    26  	"github.com/ava-labs/avalanchego/utils/constants"
    27  	"github.com/ava-labs/avalanchego/utils/crypto/secp256k1"
    28  	"github.com/ava-labs/avalanchego/utils/logging"
    29  	"github.com/ava-labs/avalanchego/utils/timer/mockable"
    30  	"github.com/ava-labs/avalanchego/utils/units"
    31  	"github.com/ava-labs/avalanchego/vms/platformvm/config"
    32  	"github.com/ava-labs/avalanchego/vms/platformvm/fx"
    33  	"github.com/ava-labs/avalanchego/vms/platformvm/genesis/genesistest"
    34  	"github.com/ava-labs/avalanchego/vms/platformvm/reward"
    35  	"github.com/ava-labs/avalanchego/vms/platformvm/state"
    36  	"github.com/ava-labs/avalanchego/vms/platformvm/state/statetest"
    37  	"github.com/ava-labs/avalanchego/vms/platformvm/status"
    38  	"github.com/ava-labs/avalanchego/vms/platformvm/txs"
    39  	"github.com/ava-labs/avalanchego/vms/platformvm/txs/fee"
    40  	"github.com/ava-labs/avalanchego/vms/platformvm/txs/txstest"
    41  	"github.com/ava-labs/avalanchego/vms/platformvm/utxo"
    42  	"github.com/ava-labs/avalanchego/vms/secp256k1fx"
    43  	"github.com/ava-labs/avalanchego/wallet/chain/p/wallet"
    44  )
    45  
    46  const (
    47  	defaultMinValidatorStake = 5 * units.MilliAvax
    48  
    49  	defaultMinStakingDuration = 24 * time.Hour
    50  	defaultMaxStakingDuration = 365 * 24 * time.Hour
    51  
    52  	defaultTxFee = 100 * units.NanoAvax
    53  )
    54  
    55  var (
    56  	lastAcceptedID = ids.GenerateTestID()
    57  
    58  	testSubnet1 *txs.Tx
    59  )
    60  
    61  type mutableSharedMemory struct {
    62  	atomic.SharedMemory
    63  }
    64  
    65  type environment struct {
    66  	isBootstrapped *utils.Atomic[bool]
    67  	config         *config.Config
    68  	clk            *mockable.Clock
    69  	baseDB         *versiondb.Database
    70  	ctx            *snow.Context
    71  	msm            *mutableSharedMemory
    72  	fx             fx.Fx
    73  	state          state.State
    74  	states         map[ids.ID]state.Chain
    75  	uptimes        uptime.Manager
    76  	utxosHandler   utxo.Verifier
    77  	backend        Backend
    78  }
    79  
    80  func (e *environment) GetState(blkID ids.ID) (state.Chain, bool) {
    81  	if blkID == lastAcceptedID {
    82  		return e.state, true
    83  	}
    84  	chainState, ok := e.states[blkID]
    85  	return chainState, ok
    86  }
    87  
    88  func (e *environment) SetState(blkID ids.ID, chainState state.Chain) {
    89  	e.states[blkID] = chainState
    90  }
    91  
    92  func newEnvironment(t *testing.T, f upgradetest.Fork) *environment {
    93  	var isBootstrapped utils.Atomic[bool]
    94  	isBootstrapped.Set(true)
    95  
    96  	config := defaultConfig(f)
    97  	clk := defaultClock(f)
    98  
    99  	baseDB := versiondb.New(memdb.New())
   100  	ctx := snowtest.Context(t, snowtest.PChainID)
   101  	m := atomic.NewMemory(baseDB)
   102  	msm := &mutableSharedMemory{
   103  		SharedMemory: m.NewSharedMemory(ctx.ChainID),
   104  	}
   105  	ctx.SharedMemory = msm
   106  
   107  	fx := defaultFx(clk, ctx.Log, isBootstrapped.Get())
   108  
   109  	rewards := reward.NewCalculator(config.RewardConfig)
   110  	baseState := statetest.New(t, statetest.Config{
   111  		DB:         baseDB,
   112  		Genesis:    genesistest.NewBytes(t, genesistest.Config{}),
   113  		Validators: config.Validators,
   114  		Upgrades:   config.UpgradeConfig,
   115  		Context:    ctx,
   116  		Rewards:    rewards,
   117  	})
   118  	lastAcceptedID = baseState.GetLastAccepted()
   119  
   120  	uptimes := uptime.NewManager(baseState, clk)
   121  	utxosVerifier := utxo.NewVerifier(ctx, clk, fx)
   122  
   123  	backend := Backend{
   124  		Config:       config,
   125  		Ctx:          ctx,
   126  		Clk:          clk,
   127  		Bootstrapped: &isBootstrapped,
   128  		Fx:           fx,
   129  		FlowChecker:  utxosVerifier,
   130  		Uptimes:      uptimes,
   131  		Rewards:      rewards,
   132  	}
   133  
   134  	env := &environment{
   135  		isBootstrapped: &isBootstrapped,
   136  		config:         config,
   137  		clk:            clk,
   138  		baseDB:         baseDB,
   139  		ctx:            ctx,
   140  		msm:            msm,
   141  		fx:             fx,
   142  		state:          baseState,
   143  		states:         make(map[ids.ID]state.Chain),
   144  		uptimes:        uptimes,
   145  		utxosHandler:   utxosVerifier,
   146  		backend:        backend,
   147  	}
   148  
   149  	addSubnet(t, env)
   150  
   151  	t.Cleanup(func() {
   152  		env.ctx.Lock.Lock()
   153  		defer env.ctx.Lock.Unlock()
   154  
   155  		require := require.New(t)
   156  
   157  		if env.isBootstrapped.Get() {
   158  			validatorIDs := env.config.Validators.GetValidatorIDs(constants.PrimaryNetworkID)
   159  
   160  			require.NoError(env.uptimes.StopTracking(validatorIDs, constants.PrimaryNetworkID))
   161  
   162  			for subnetID := range env.config.TrackedSubnets {
   163  				validatorIDs := env.config.Validators.GetValidatorIDs(subnetID)
   164  
   165  				require.NoError(env.uptimes.StopTracking(validatorIDs, subnetID))
   166  			}
   167  			env.state.SetHeight(math.MaxUint64)
   168  			require.NoError(env.state.Commit())
   169  		}
   170  
   171  		require.NoError(env.state.Close())
   172  		require.NoError(env.baseDB.Close())
   173  	})
   174  
   175  	return env
   176  }
   177  
   178  type walletConfig struct {
   179  	config    *config.Config
   180  	keys      []*secp256k1.PrivateKey
   181  	subnetIDs []ids.ID
   182  	chainIDs  []ids.ID
   183  }
   184  
   185  func newWallet(t testing.TB, e *environment, c walletConfig) wallet.Wallet {
   186  	if c.config == nil {
   187  		c.config = e.config
   188  	}
   189  	if len(c.keys) == 0 {
   190  		c.keys = genesistest.DefaultFundedKeys
   191  	}
   192  	return txstest.NewWallet(
   193  		t,
   194  		e.ctx,
   195  		c.config,
   196  		e.state,
   197  		secp256k1fx.NewKeychain(c.keys...),
   198  		c.subnetIDs,
   199  		c.chainIDs,
   200  	)
   201  }
   202  
   203  func addSubnet(t *testing.T, env *environment) {
   204  	require := require.New(t)
   205  
   206  	wallet := newWallet(t, env, walletConfig{
   207  		keys: genesistest.DefaultFundedKeys[:1],
   208  	})
   209  
   210  	var err error
   211  	testSubnet1, err = wallet.IssueCreateSubnetTx(
   212  		&secp256k1fx.OutputOwners{
   213  			Threshold: 2,
   214  			Addrs: []ids.ShortID{
   215  				genesistest.DefaultFundedKeys[0].Address(),
   216  				genesistest.DefaultFundedKeys[1].Address(),
   217  				genesistest.DefaultFundedKeys[2].Address(),
   218  			},
   219  		},
   220  	)
   221  	require.NoError(err)
   222  
   223  	stateDiff, err := state.NewDiff(lastAcceptedID, env)
   224  	require.NoError(err)
   225  
   226  	feeCalculator := state.PickFeeCalculator(env.config, env.state)
   227  	executor := StandardTxExecutor{
   228  		Backend:       &env.backend,
   229  		FeeCalculator: feeCalculator,
   230  		State:         stateDiff,
   231  		Tx:            testSubnet1,
   232  	}
   233  	require.NoError(testSubnet1.Unsigned.Visit(&executor))
   234  
   235  	stateDiff.AddTx(testSubnet1, status.Committed)
   236  	require.NoError(stateDiff.Apply(env.state))
   237  	require.NoError(env.state.Commit())
   238  }
   239  
   240  func defaultConfig(f upgradetest.Fork) *config.Config {
   241  	upgrades := upgradetest.GetConfigWithUpgradeTime(
   242  		f,
   243  		genesistest.DefaultValidatorStartTime.Add(-2*time.Second),
   244  	)
   245  	upgradetest.SetTimesTo(
   246  		&upgrades,
   247  		min(f, upgradetest.ApricotPhase5),
   248  		genesistest.DefaultValidatorEndTime,
   249  	)
   250  
   251  	return &config.Config{
   252  		Chains:                 chains.TestManager,
   253  		UptimeLockedCalculator: uptime.NewLockedCalculator(),
   254  		Validators:             validators.NewManager(),
   255  		StaticFeeConfig: fee.StaticConfig{
   256  			TxFee:                 defaultTxFee,
   257  			CreateSubnetTxFee:     100 * defaultTxFee,
   258  			CreateBlockchainTxFee: 100 * defaultTxFee,
   259  		},
   260  		MinValidatorStake: 5 * units.MilliAvax,
   261  		MaxValidatorStake: 500 * units.MilliAvax,
   262  		MinDelegatorStake: 1 * units.MilliAvax,
   263  		MinStakeDuration:  defaultMinStakingDuration,
   264  		MaxStakeDuration:  defaultMaxStakingDuration,
   265  		RewardConfig: reward.Config{
   266  			MaxConsumptionRate: .12 * reward.PercentDenominator,
   267  			MinConsumptionRate: .10 * reward.PercentDenominator,
   268  			MintingPeriod:      365 * 24 * time.Hour,
   269  			SupplyCap:          720 * units.MegaAvax,
   270  		},
   271  		UpgradeConfig: upgrades,
   272  	}
   273  }
   274  
   275  func defaultClock(f upgradetest.Fork) *mockable.Clock {
   276  	now := genesistest.DefaultValidatorStartTime
   277  	if f >= upgradetest.Banff {
   278  		// 1 second after active fork
   279  		now = genesistest.DefaultValidatorEndTime.Add(-2 * time.Second)
   280  	}
   281  	clk := &mockable.Clock{}
   282  	clk.Set(now)
   283  	return clk
   284  }
   285  
   286  type fxVMInt struct {
   287  	registry codec.Registry
   288  	clk      *mockable.Clock
   289  	log      logging.Logger
   290  }
   291  
   292  func (fvi *fxVMInt) CodecRegistry() codec.Registry {
   293  	return fvi.registry
   294  }
   295  
   296  func (fvi *fxVMInt) Clock() *mockable.Clock {
   297  	return fvi.clk
   298  }
   299  
   300  func (fvi *fxVMInt) Logger() logging.Logger {
   301  	return fvi.log
   302  }
   303  
   304  func defaultFx(clk *mockable.Clock, log logging.Logger, isBootstrapped bool) fx.Fx {
   305  	fxVMInt := &fxVMInt{
   306  		registry: linearcodec.NewDefault(),
   307  		clk:      clk,
   308  		log:      log,
   309  	}
   310  	res := &secp256k1fx.Fx{}
   311  	if err := res.Initialize(fxVMInt); err != nil {
   312  		panic(err)
   313  	}
   314  	if isBootstrapped {
   315  		if err := res.Bootstrapped(); err != nil {
   316  			panic(err)
   317  		}
   318  	}
   319  	return res
   320  }