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