github.com/MetalBlockchain/metalgo@v1.11.9/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/MetalBlockchain/metalgo/chains" 15 "github.com/MetalBlockchain/metalgo/chains/atomic" 16 "github.com/MetalBlockchain/metalgo/codec" 17 "github.com/MetalBlockchain/metalgo/codec/linearcodec" 18 "github.com/MetalBlockchain/metalgo/database" 19 "github.com/MetalBlockchain/metalgo/database/memdb" 20 "github.com/MetalBlockchain/metalgo/database/prefixdb" 21 "github.com/MetalBlockchain/metalgo/database/versiondb" 22 "github.com/MetalBlockchain/metalgo/ids" 23 "github.com/MetalBlockchain/metalgo/snow" 24 "github.com/MetalBlockchain/metalgo/snow/engine/common" 25 "github.com/MetalBlockchain/metalgo/snow/snowtest" 26 "github.com/MetalBlockchain/metalgo/snow/uptime" 27 "github.com/MetalBlockchain/metalgo/snow/validators" 28 "github.com/MetalBlockchain/metalgo/utils" 29 "github.com/MetalBlockchain/metalgo/utils/constants" 30 "github.com/MetalBlockchain/metalgo/utils/crypto/secp256k1" 31 "github.com/MetalBlockchain/metalgo/utils/formatting" 32 "github.com/MetalBlockchain/metalgo/utils/formatting/address" 33 "github.com/MetalBlockchain/metalgo/utils/json" 34 "github.com/MetalBlockchain/metalgo/utils/logging" 35 "github.com/MetalBlockchain/metalgo/utils/timer/mockable" 36 "github.com/MetalBlockchain/metalgo/utils/units" 37 "github.com/MetalBlockchain/metalgo/vms/platformvm/api" 38 "github.com/MetalBlockchain/metalgo/vms/platformvm/config" 39 "github.com/MetalBlockchain/metalgo/vms/platformvm/fx" 40 "github.com/MetalBlockchain/metalgo/vms/platformvm/metrics" 41 "github.com/MetalBlockchain/metalgo/vms/platformvm/network" 42 "github.com/MetalBlockchain/metalgo/vms/platformvm/reward" 43 "github.com/MetalBlockchain/metalgo/vms/platformvm/state" 44 "github.com/MetalBlockchain/metalgo/vms/platformvm/status" 45 "github.com/MetalBlockchain/metalgo/vms/platformvm/txs" 46 "github.com/MetalBlockchain/metalgo/vms/platformvm/txs/fee" 47 "github.com/MetalBlockchain/metalgo/vms/platformvm/txs/mempool" 48 "github.com/MetalBlockchain/metalgo/vms/platformvm/txs/txstest" 49 "github.com/MetalBlockchain/metalgo/vms/platformvm/upgrade" 50 "github.com/MetalBlockchain/metalgo/vms/platformvm/utxo" 51 "github.com/MetalBlockchain/metalgo/vms/secp256k1fx" 52 53 blockexecutor "github.com/MetalBlockchain/metalgo/vms/platformvm/block/executor" 54 txexecutor "github.com/MetalBlockchain/metalgo/vms/platformvm/txs/executor" 55 pvalidators "github.com/MetalBlockchain/metalgo/vms/platformvm/validators" 56 walletsigner "github.com/MetalBlockchain/metalgo/wallet/chain/p/signer" 57 walletcommon "github.com/MetalBlockchain/metalgo/wallet/subnet/primary/common" 58 ) 59 60 const ( 61 defaultWeight = 10000 62 trackChecksum = false 63 64 apricotPhase3 fork = iota 65 apricotPhase5 66 banff 67 cortina 68 durango 69 eUpgrade 70 71 latestFork = durango 72 ) 73 74 var ( 75 defaultMinStakingDuration = 24 * time.Hour 76 defaultMaxStakingDuration = 365 * 24 * time.Hour 77 defaultGenesisTime = time.Date(1997, 1, 1, 0, 0, 0, 0, time.UTC) 78 defaultValidateStartTime = defaultGenesisTime 79 defaultValidateEndTime = defaultValidateStartTime.Add(10 * defaultMinStakingDuration) 80 defaultMinValidatorStake = 5 * units.MilliAvax 81 defaultBalance = 100 * defaultMinValidatorStake 82 preFundedKeys = secp256k1.TestKeys() 83 defaultTxFee = uint64(100) 84 85 testSubnet1 *txs.Tx 86 testSubnet1ControlKeys = preFundedKeys[0:3] 87 88 // Node IDs of genesis validators. Initialized in init function 89 genesisNodeIDs []ids.NodeID 90 ) 91 92 func init() { 93 genesisNodeIDs = make([]ids.NodeID, len(preFundedKeys)) 94 for i := range preFundedKeys { 95 genesisNodeIDs[i] = ids.GenerateTestNodeID() 96 } 97 } 98 99 type fork uint8 100 101 type mutableSharedMemory struct { 102 atomic.SharedMemory 103 } 104 105 type environment struct { 106 Builder 107 blkManager blockexecutor.Manager 108 mempool mempool.Mempool 109 network *network.Network 110 sender *common.SenderTest 111 112 isBootstrapped *utils.Atomic[bool] 113 config *config.Config 114 clk *mockable.Clock 115 baseDB *versiondb.Database 116 ctx *snow.Context 117 msm *mutableSharedMemory 118 fx fx.Fx 119 state state.State 120 uptimes uptime.Manager 121 utxosVerifier utxo.Verifier 122 factory *txstest.WalletFactory 123 backend txexecutor.Backend 124 } 125 126 func newEnvironment(t *testing.T, f fork) *environment { //nolint:unparam 127 require := require.New(t) 128 129 res := &environment{ 130 isBootstrapped: &utils.Atomic[bool]{}, 131 config: defaultConfig(t, f), 132 clk: defaultClock(), 133 } 134 res.isBootstrapped.Set(true) 135 136 res.baseDB = versiondb.New(memdb.New()) 137 atomicDB := prefixdb.New([]byte{1}, res.baseDB) 138 m := atomic.NewMemory(atomicDB) 139 140 res.ctx = snowtest.Context(t, snowtest.PChainID) 141 res.msm = &mutableSharedMemory{ 142 SharedMemory: m.NewSharedMemory(res.ctx.ChainID), 143 } 144 res.ctx.SharedMemory = res.msm 145 146 res.ctx.Lock.Lock() 147 defer res.ctx.Lock.Unlock() 148 149 res.fx = defaultFx(t, res.clk, res.ctx.Log, res.isBootstrapped.Get()) 150 151 rewardsCalc := reward.NewCalculator(res.config.RewardConfig) 152 res.state = defaultState(t, res.config, res.ctx, res.baseDB, rewardsCalc) 153 154 res.uptimes = uptime.NewManager(res.state, res.clk) 155 res.utxosVerifier = utxo.NewVerifier(res.ctx, res.clk, res.fx) 156 res.factory = txstest.NewWalletFactory(res.ctx, res.config, res.state) 157 158 genesisID := res.state.GetLastAccepted() 159 res.backend = txexecutor.Backend{ 160 Config: res.config, 161 Ctx: res.ctx, 162 Clk: res.clk, 163 Bootstrapped: res.isBootstrapped, 164 Fx: res.fx, 165 FlowChecker: res.utxosVerifier, 166 Uptimes: res.uptimes, 167 Rewards: rewardsCalc, 168 } 169 170 registerer := prometheus.NewRegistry() 171 res.sender = &common.SenderTest{T: t} 172 res.sender.SendAppGossipF = func(context.Context, common.SendConfig, []byte) error { 173 return nil 174 } 175 176 metrics, err := metrics.New(registerer) 177 require.NoError(err) 178 179 res.mempool, err = mempool.New("mempool", registerer, nil) 180 require.NoError(err) 181 182 res.blkManager = blockexecutor.NewManager( 183 res.mempool, 184 metrics, 185 res.state, 186 &res.backend, 187 pvalidators.TestManager, 188 ) 189 190 txVerifier := network.NewLockedTxVerifier(&res.ctx.Lock, res.blkManager) 191 res.network, err = network.New( 192 res.backend.Ctx.Log, 193 res.backend.Ctx.NodeID, 194 res.backend.Ctx.SubnetID, 195 res.backend.Ctx.ValidatorState, 196 txVerifier, 197 res.mempool, 198 res.backend.Config.PartialSyncPrimaryNetwork, 199 res.sender, 200 registerer, 201 network.DefaultConfig, 202 ) 203 require.NoError(err) 204 205 res.Builder = New( 206 res.mempool, 207 &res.backend, 208 res.blkManager, 209 ) 210 res.Builder.StartBlockTimer() 211 212 res.blkManager.SetPreference(genesisID) 213 addSubnet(t, res) 214 215 t.Cleanup(func() { 216 res.ctx.Lock.Lock() 217 defer res.ctx.Lock.Unlock() 218 219 res.Builder.ShutdownBlockTimer() 220 221 if res.isBootstrapped.Get() { 222 validatorIDs := res.config.Validators.GetValidatorIDs(constants.PrimaryNetworkID) 223 224 require.NoError(res.uptimes.StopTracking(validatorIDs, constants.PrimaryNetworkID)) 225 226 require.NoError(res.state.Commit()) 227 } 228 229 require.NoError(res.state.Close()) 230 require.NoError(res.baseDB.Close()) 231 }) 232 233 return res 234 } 235 236 func addSubnet(t *testing.T, env *environment) { 237 require := require.New(t) 238 239 builder, signer := env.factory.NewWallet(preFundedKeys[0]) 240 utx, err := builder.NewCreateSubnetTx( 241 &secp256k1fx.OutputOwners{ 242 Threshold: 2, 243 Addrs: []ids.ShortID{ 244 preFundedKeys[0].PublicKey().Address(), 245 preFundedKeys[1].PublicKey().Address(), 246 preFundedKeys[2].PublicKey().Address(), 247 }, 248 }, 249 walletcommon.WithChangeOwner(&secp256k1fx.OutputOwners{ 250 Threshold: 1, 251 Addrs: []ids.ShortID{preFundedKeys[0].PublicKey().Address()}, 252 }), 253 ) 254 require.NoError(err) 255 testSubnet1, err = walletsigner.SignUnsigned(context.Background(), signer, utx) 256 require.NoError(err) 257 258 genesisID := env.state.GetLastAccepted() 259 stateDiff, err := state.NewDiff(genesisID, env.blkManager) 260 require.NoError(err) 261 262 executor := txexecutor.StandardTxExecutor{ 263 Backend: &env.backend, 264 State: stateDiff, 265 Tx: testSubnet1, 266 } 267 require.NoError(testSubnet1.Unsigned.Visit(&executor)) 268 269 stateDiff.AddTx(testSubnet1, status.Committed) 270 require.NoError(stateDiff.Apply(env.state)) 271 } 272 273 func defaultState( 274 t *testing.T, 275 cfg *config.Config, 276 ctx *snow.Context, 277 db database.Database, 278 rewards reward.Calculator, 279 ) state.State { 280 require := require.New(t) 281 282 execCfg, _ := config.GetExecutionConfig([]byte(`{}`)) 283 genesisBytes := buildGenesisTest(t, ctx) 284 state, err := state.New( 285 db, 286 genesisBytes, 287 prometheus.NewRegistry(), 288 cfg, 289 execCfg, 290 ctx, 291 metrics.Noop, 292 rewards, 293 ) 294 require.NoError(err) 295 296 // persist and reload to init a bunch of in-memory stuff 297 state.SetHeight(0) 298 require.NoError(state.Commit()) 299 return state 300 } 301 302 func defaultConfig(t *testing.T, f fork) *config.Config { 303 c := &config.Config{ 304 Chains: chains.TestManager, 305 UptimeLockedCalculator: uptime.NewLockedCalculator(), 306 Validators: validators.NewManager(), 307 StaticFeeConfig: fee.StaticConfig{ 308 TxFee: defaultTxFee, 309 CreateSubnetTxFee: 100 * defaultTxFee, 310 CreateBlockchainTxFee: 100 * defaultTxFee, 311 }, 312 MinValidatorStake: 5 * units.MilliAvax, 313 MaxValidatorStake: 500 * units.MilliAvax, 314 MinDelegatorStake: 1 * units.MilliAvax, 315 MinStakeDuration: defaultMinStakingDuration, 316 MaxStakeDuration: defaultMaxStakingDuration, 317 RewardConfig: reward.Config{ 318 MaxConsumptionRate: .12 * reward.PercentDenominator, 319 MinConsumptionRate: .10 * reward.PercentDenominator, 320 MintingPeriod: 365 * 24 * time.Hour, 321 SupplyCap: 720 * units.MegaAvax, 322 }, 323 UpgradeConfig: upgrade.Config{ 324 ApricotPhase3Time: mockable.MaxTime, 325 ApricotPhase5Time: mockable.MaxTime, 326 BanffTime: mockable.MaxTime, 327 CortinaTime: mockable.MaxTime, 328 DurangoTime: mockable.MaxTime, 329 EUpgradeTime: mockable.MaxTime, 330 }, 331 } 332 333 switch f { 334 case eUpgrade: 335 c.UpgradeConfig.EUpgradeTime = time.Time{} // neglecting fork ordering this for package tests 336 fallthrough 337 case durango: 338 c.UpgradeConfig.DurangoTime = time.Time{} // neglecting fork ordering for this package's tests 339 fallthrough 340 case cortina: 341 c.UpgradeConfig.CortinaTime = time.Time{} // neglecting fork ordering for this package's tests 342 fallthrough 343 case banff: 344 c.UpgradeConfig.BanffTime = time.Time{} // neglecting fork ordering for this package's tests 345 fallthrough 346 case apricotPhase5: 347 c.UpgradeConfig.ApricotPhase5Time = defaultValidateEndTime 348 fallthrough 349 case apricotPhase3: 350 c.UpgradeConfig.ApricotPhase3Time = defaultValidateEndTime 351 default: 352 require.FailNow(t, "unhandled fork", f) 353 } 354 355 return c 356 } 357 358 func defaultClock() *mockable.Clock { 359 // set time after Banff fork (and before default nextStakerTime) 360 clk := &mockable.Clock{} 361 clk.Set(defaultGenesisTime) 362 return clk 363 } 364 365 type fxVMInt struct { 366 registry codec.Registry 367 clk *mockable.Clock 368 log logging.Logger 369 } 370 371 func (fvi *fxVMInt) CodecRegistry() codec.Registry { 372 return fvi.registry 373 } 374 375 func (fvi *fxVMInt) Clock() *mockable.Clock { 376 return fvi.clk 377 } 378 379 func (fvi *fxVMInt) Logger() logging.Logger { 380 return fvi.log 381 } 382 383 func defaultFx(t *testing.T, clk *mockable.Clock, log logging.Logger, isBootstrapped bool) fx.Fx { 384 require := require.New(t) 385 386 fxVMInt := &fxVMInt{ 387 registry: linearcodec.NewDefault(), 388 clk: clk, 389 log: log, 390 } 391 res := &secp256k1fx.Fx{} 392 require.NoError(res.Initialize(fxVMInt)) 393 if isBootstrapped { 394 require.NoError(res.Bootstrapped()) 395 } 396 return res 397 } 398 399 func buildGenesisTest(t *testing.T, ctx *snow.Context) []byte { 400 require := require.New(t) 401 402 genesisUTXOs := make([]api.UTXO, len(preFundedKeys)) 403 for i, key := range preFundedKeys { 404 id := key.PublicKey().Address() 405 addr, err := address.FormatBech32(constants.UnitTestHRP, id.Bytes()) 406 require.NoError(err) 407 genesisUTXOs[i] = api.UTXO{ 408 Amount: json.Uint64(defaultBalance), 409 Address: addr, 410 } 411 } 412 413 genesisValidators := make([]api.GenesisPermissionlessValidator, len(genesisNodeIDs)) 414 for i, nodeID := range genesisNodeIDs { 415 addr, err := address.FormatBech32(constants.UnitTestHRP, nodeID.Bytes()) 416 require.NoError(err) 417 genesisValidators[i] = api.GenesisPermissionlessValidator{ 418 GenesisValidator: api.GenesisValidator{ 419 StartTime: json.Uint64(defaultValidateStartTime.Unix()), 420 EndTime: json.Uint64(defaultValidateEndTime.Unix()), 421 NodeID: nodeID, 422 }, 423 RewardOwner: &api.Owner{ 424 Threshold: 1, 425 Addresses: []string{addr}, 426 }, 427 Staked: []api.UTXO{{ 428 Amount: json.Uint64(defaultWeight), 429 Address: addr, 430 }}, 431 DelegationFee: reward.PercentDenominator, 432 } 433 } 434 435 buildGenesisArgs := api.BuildGenesisArgs{ 436 NetworkID: json.Uint32(constants.UnitTestID), 437 AvaxAssetID: ctx.AVAXAssetID, 438 UTXOs: genesisUTXOs, 439 Validators: genesisValidators, 440 Chains: nil, 441 Time: json.Uint64(defaultGenesisTime.Unix()), 442 InitialSupply: json.Uint64(360 * units.MegaAvax), 443 Encoding: formatting.Hex, 444 } 445 446 buildGenesisResponse := api.BuildGenesisReply{} 447 platformvmSS := api.StaticService{} 448 require.NoError(platformvmSS.BuildGenesis(nil, &buildGenesisArgs, &buildGenesisResponse)) 449 450 genesisBytes, err := formatting.Decode(buildGenesisResponse.Encoding, buildGenesisResponse.Bytes) 451 require.NoError(err) 452 453 return genesisBytes 454 }