From e8ad7c21a3d5340f24f82298b606db400e88b4b3 Mon Sep 17 00:00:00 2001 From: Ganesh Vanahalli Date: Mon, 19 Aug 2024 15:55:15 +0530 Subject: [PATCH] Fix live reloading of staker config --- arbnode/node.go | 2 +- staker/staker.go | 59 +++++++++++++++++-------------- system_tests/fast_confirm_test.go | 18 +++++----- system_tests/staker_test.go | 20 ++++++----- 4 files changed, 55 insertions(+), 44 deletions(-) diff --git a/arbnode/node.go b/arbnode/node.go index 0676d6393c..c66598618f 100644 --- a/arbnode/node.go +++ b/arbnode/node.go @@ -660,7 +660,7 @@ func createNodeImpl( confirmedNotifiers = append(confirmedNotifiers, messagePruner) } - stakerObj, err = staker.NewStaker(l1Reader, wallet, bind.CallOpts{}, config.Staker, blockValidator, statelessBlockValidator, nil, confirmedNotifiers, deployInfo.ValidatorUtils, fatalErrChan) + stakerObj, err = staker.NewStaker(l1Reader, wallet, bind.CallOpts{}, func() *staker.L1ValidatorConfig { return &configFetcher.Get().Staker }, blockValidator, statelessBlockValidator, nil, confirmedNotifiers, deployInfo.ValidatorUtils, fatalErrChan) if err != nil { return nil, err } diff --git a/staker/staker.go b/staker/staker.go index 9d917d1853..c54e74be37 100644 --- a/staker/staker.go +++ b/staker/staker.go @@ -142,6 +142,8 @@ func (c *L1ValidatorConfig) Validate() error { return nil } +type L1ValidatorConfigFetcher func() *L1ValidatorConfig + var DefaultL1ValidatorConfig = L1ValidatorConfig{ Enable: true, Strategy: "Watchtower", @@ -257,7 +259,7 @@ type Staker struct { confirmedNotifiers []LatestConfirmedNotifier activeChallenge *ChallengeManager baseCallOpts bind.CallOpts - config L1ValidatorConfig + config L1ValidatorConfigFetcher highGasBlocksBuffer *big.Int lastActCalledBlock *big.Int inactiveLastCheckedNode *nodeAndHash @@ -295,7 +297,7 @@ func NewStaker( l1Reader *headerreader.HeaderReader, wallet ValidatorWalletInterface, callOpts bind.CallOpts, - config L1ValidatorConfig, + config L1ValidatorConfigFetcher, blockValidator *BlockValidator, statelessBlockValidator *StatelessBlockValidator, stakedNotifiers []LatestStakedNotifier, @@ -304,7 +306,7 @@ func NewStaker( fatalErr chan<- error, ) (*Staker, error) { - if err := config.Validate(); err != nil { + if err := config().Validate(); err != nil { return nil, err } client := l1Reader.Client() @@ -314,7 +316,7 @@ func NewStaker( return nil, err } stakerLastSuccessfulActionGauge.Update(time.Now().Unix()) - if config.StartValidationFromStaked && blockValidator != nil { + if config().StartValidationFromStaked && blockValidator != nil { stakedNotifiers = append(stakedNotifiers, blockValidator) } inactiveValidatedNodes := btree.NewG(2, func(a, b validatedNode) bool { @@ -327,7 +329,7 @@ func NewStaker( confirmedNotifiers: confirmedNotifiers, baseCallOpts: callOpts, config: config, - highGasBlocksBuffer: big.NewInt(config.PostingStrategy.HighGasDelayBlocks), + highGasBlocksBuffer: big.NewInt(config().PostingStrategy.HighGasDelayBlocks), lastActCalledBlock: nil, inboxReader: statelessBlockValidator.inboxReader, statelessBlockValidator: statelessBlockValidator, @@ -345,7 +347,7 @@ func (s *Staker) Initialize(ctx context.Context) error { if walletAddressOrZero != (common.Address{}) { s.updateStakerBalanceMetric(ctx) } - if s.blockValidator != nil && s.config.StartValidationFromStaked { + if s.blockValidator != nil && s.config().StartValidationFromStaked { latestStaked, _, err := s.validatorUtils.LatestStaked(&s.baseCallOpts, s.rollupAddress, walletAddressOrZero) if err != nil { return err @@ -369,7 +371,8 @@ func (s *Staker) Initialize(ctx context.Context) error { // based on the config, the wallet address, and the on-chain rollup designated fast confirmer. // Before this function, both variables should be their default (i.e. fast confirmation is disabled). func (s *Staker) setupFastConfirmation(ctx context.Context) error { - if !s.config.EnableFastConfirmation { + cfg := s.config() + if !cfg.EnableFastConfirmation { return nil } if s.wallet.Address() == nil { @@ -400,7 +403,7 @@ func (s *Staker) setupFastConfirmation(ctx context.Context) error { fastConfirmer, s.builder, s.wallet, - s.config.gasRefunder, + cfg.gasRefunder, s.l1Reader, ) if err != nil { @@ -516,8 +519,9 @@ func (s *Staker) Start(ctxIn context.Context) { } }() var err error - if common.HexToAddress(s.config.GasRefunderAddress) != (common.Address{}) { - gasRefunderBalance, err := s.client.BalanceAt(ctx, common.HexToAddress(s.config.GasRefunderAddress), nil) + cfg := s.config() + if common.HexToAddress(cfg.GasRefunderAddress) != (common.Address{}) { + gasRefunderBalance, err := s.client.BalanceAt(ctx, common.HexToAddress(cfg.GasRefunderAddress), nil) if err != nil { log.Warn("error fetching validator gas refunder balance", "err", err) } else { @@ -546,7 +550,7 @@ func (s *Staker) Start(ctxIn context.Context) { // Try to create another tx return 0 } - return s.config.StakerInterval + return cfg.StakerInterval } stakerActionFailureCounter.Inc(1) backoff *= 2 @@ -587,7 +591,7 @@ func (s *Staker) Start(ctxIn context.Context) { notifier.UpdateLatestConfirmed(confirmedMsgCount, *confirmedGlobalState) } } - return s.config.StakerInterval + return s.config().StakerInterval }) } @@ -608,6 +612,7 @@ func (s *Staker) IsWhitelisted(ctx context.Context) (bool, error) { } func (s *Staker) shouldAct(ctx context.Context) bool { + cfg := s.config() var gasPriceHigh = false var gasPriceFloat float64 gasPrice, err := s.client.SuggestGasPrice(ctx) @@ -615,7 +620,7 @@ func (s *Staker) shouldAct(ctx context.Context) bool { log.Warn("error getting gas price", "err", err) } else { gasPriceFloat = float64(gasPrice.Int64()) / 1e9 - if gasPriceFloat >= s.config.PostingStrategy.HighGasThreshold { + if gasPriceFloat >= cfg.PostingStrategy.HighGasThreshold { gasPriceHigh = true } } @@ -640,14 +645,14 @@ func (s *Staker) shouldAct(ctx context.Context) bool { // Clamp `s.highGasBlocksBuffer` to between 0 and HighGasDelayBlocks if s.highGasBlocksBuffer.Sign() < 0 { s.highGasBlocksBuffer.SetInt64(0) - } else if s.highGasBlocksBuffer.Cmp(big.NewInt(s.config.PostingStrategy.HighGasDelayBlocks)) > 0 { - s.highGasBlocksBuffer.SetInt64(s.config.PostingStrategy.HighGasDelayBlocks) + } else if s.highGasBlocksBuffer.Cmp(big.NewInt(cfg.PostingStrategy.HighGasDelayBlocks)) > 0 { + s.highGasBlocksBuffer.SetInt64(cfg.PostingStrategy.HighGasDelayBlocks) } if gasPriceHigh && s.highGasBlocksBuffer.Sign() > 0 { log.Warn( "not acting yet as gas price is high", "gasPrice", gasPriceFloat, - "highGasPriceConfig", s.config.PostingStrategy.HighGasThreshold, + "highGasPriceConfig", cfg.PostingStrategy.HighGasThreshold, "highGasBuffer", s.highGasBlocksBuffer, ) return false @@ -678,7 +683,8 @@ func (s *Staker) confirmDataPosterIsReady(ctx context.Context) error { } func (s *Staker) Act(ctx context.Context) (*types.Transaction, error) { - if s.config.strategy != WatchtowerStrategy { + cfg := s.config() + if cfg.strategy != WatchtowerStrategy { err := s.confirmDataPosterIsReady(ctx) if err != nil { return nil, err @@ -732,7 +738,7 @@ func (s *Staker) Act(ctx context.Context) (*types.Transaction, error) { StakeExists: rawInfo != nil, } - effectiveStrategy := s.config.strategy + effectiveStrategy := cfg.strategy nodesLinear, err := s.validatorUtils.AreUnresolvedNodesLinear(callOpts, s.rollupAddress) if err != nil { return nil, fmt.Errorf("error checking for rollup assertion fork: %w", err) @@ -760,7 +766,7 @@ func (s *Staker) Act(ctx context.Context) (*types.Transaction, error) { info.LatestStakedNodeHash = s.inactiveLastCheckedNode.hash } - if s.config.EnableFastConfirmation { + if cfg.EnableFastConfirmation { firstUnresolvedNode, err := s.rollup.FirstUnresolvedNode(callOpts) if err != nil { return nil, err @@ -799,7 +805,7 @@ func (s *Staker) Act(ctx context.Context) (*types.Transaction, error) { if s.builder.BuildingTransactionCount() > 0 { // Try to fast confirm previous nodes before working on new ones log.Info("fast confirming previous node", "node", firstUnresolvedNode) - return s.wallet.ExecuteTransactions(ctx, s.builder, s.config.gasRefunder) + return s.wallet.ExecuteTransactions(ctx, s.builder, cfg.gasRefunder) } } } @@ -886,7 +892,7 @@ func (s *Staker) Act(ctx context.Context) (*types.Transaction, error) { return nil, fmt.Errorf("error withdrawing staker funds from our staker %v: %w", walletAddressOrZero, err) } log.Info("removing old stake and withdrawing funds") - return s.wallet.ExecuteTransactions(ctx, s.builder, s.config.gasRefunder) + return s.wallet.ExecuteTransactions(ctx, s.builder, cfg.gasRefunder) } } @@ -940,7 +946,7 @@ func (s *Staker) Act(ctx context.Context) (*types.Transaction, error) { if info.StakerInfo == nil && info.StakeExists { log.Info("staking to execute transactions") } - return s.wallet.ExecuteTransactions(ctx, s.builder, s.config.gasRefunder) + return s.wallet.ExecuteTransactions(ctx, s.builder, cfg.gasRefunder) } func (s *Staker) handleConflict(ctx context.Context, info *StakerInfo) error { @@ -966,7 +972,7 @@ func (s *Staker) handleConflict(ctx context.Context, info *StakerInfo) error { *info.CurrentChallenge, s.statelessBlockValidator, latestConfirmedCreated, - s.config.ConfirmationBlocks, + s.config().ConfirmationBlocks, ) if err != nil { return fmt.Errorf("error creating challenge manager: %w", err) @@ -980,8 +986,9 @@ func (s *Staker) handleConflict(ctx context.Context, info *StakerInfo) error { } func (s *Staker) advanceStake(ctx context.Context, info *OurStakerInfo, effectiveStrategy StakerStrategy) error { + cfg := s.config() active := effectiveStrategy >= StakeLatestStrategy - action, wrongNodesExist, err := s.generateNodeAction(ctx, info, effectiveStrategy, &s.config) + action, wrongNodesExist, err := s.generateNodeAction(ctx, info, effectiveStrategy, cfg) if err != nil { return fmt.Errorf("error generating node action: %w", err) } @@ -995,7 +1002,7 @@ func (s *Staker) advanceStake(ctx context.Context, info *OurStakerInfo, effectiv switch action := action.(type) { case createNodeAction: - if wrongNodesExist && s.config.DisableChallenge { + if wrongNodesExist && cfg.DisableChallenge { log.Error("refusing to challenge assertion as config disables challenges") info.CanProgress = false return nil @@ -1192,7 +1199,7 @@ func (s *Staker) createConflict(ctx context.Context, info *StakerInfo) error { } func (s *Staker) Strategy() StakerStrategy { - return s.config.strategy + return s.config().strategy } func (s *Staker) Rollup() *RollupWatcher { diff --git a/system_tests/fast_confirm_test.go b/system_tests/fast_confirm_test.go index f05219d994..4a679e5077 100644 --- a/system_tests/fast_confirm_test.go +++ b/system_tests/fast_confirm_test.go @@ -160,7 +160,7 @@ func TestFastConfirmation(t *testing.T) { l2node.L1Reader, valWallet, bind.CallOpts{}, - valConfig, + func() *staker.L1ValidatorConfig { return &valConfig }, nil, stateless, nil, @@ -317,8 +317,8 @@ func TestFastConfirmationWithSafe(t *testing.T) { _, err = builder.L1.EnsureTxSucceeded(tx) Require(t, err) - valConfig := staker.TestL1ValidatorConfig - valConfig.EnableFastConfirmation = true + valConfigA := staker.TestL1ValidatorConfig + valConfigA.EnableFastConfirmation = true parentChainID, err := builder.L1.Client.ChainID(ctx) if err != nil { @@ -335,9 +335,9 @@ func TestFastConfirmationWithSafe(t *testing.T) { if err != nil { t.Fatalf("Error creating validator dataposter: %v", err) } - valWalletA, err := validatorwallet.NewContract(dpA, nil, l2nodeA.DeployInfo.ValidatorWalletCreator, l2nodeA.DeployInfo.Rollup, l2nodeA.L1Reader, &l1authA, 0, func(common.Address) {}, func() uint64 { return valConfig.ExtraGas }) + valWalletA, err := validatorwallet.NewContract(dpA, nil, l2nodeA.DeployInfo.ValidatorWalletCreator, l2nodeA.DeployInfo.Rollup, l2nodeA.L1Reader, &l1authA, 0, func(common.Address) {}, func() uint64 { return valConfigA.ExtraGas }) Require(t, err) - valConfig.Strategy = "MakeNodes" + valConfigA.Strategy = "MakeNodes" _, valStack := createTestValidationNode(t, ctx, &valnode.TestValidationConfig) blockValidatorConfig := staker.TestBlockValidatorConfig @@ -361,7 +361,7 @@ func TestFastConfirmationWithSafe(t *testing.T) { l2nodeA.L1Reader, valWalletA, bind.CallOpts{}, - valConfig, + func() *staker.L1ValidatorConfig { return &valConfigA }, nil, statelessA, nil, @@ -391,7 +391,9 @@ func TestFastConfirmationWithSafe(t *testing.T) { } valWalletB, err := validatorwallet.NewEOA(dpB, l2nodeB.DeployInfo.Rollup, l2nodeB.L1Reader.Client(), func() uint64 { return 0 }) Require(t, err) - valConfig.Strategy = "watchtower" + valConfigB := staker.TestL1ValidatorConfig + valConfigB.EnableFastConfirmation = true + valConfigB.Strategy = "watchtower" statelessB, err := staker.NewStatelessBlockValidator( l2nodeB.InboxReader, l2nodeB.InboxTracker, @@ -411,7 +413,7 @@ func TestFastConfirmationWithSafe(t *testing.T) { l2nodeB.L1Reader, valWalletB, bind.CallOpts{}, - valConfig, + func() *staker.L1ValidatorConfig { return &valConfigB }, nil, statelessB, nil, diff --git a/system_tests/staker_test.go b/system_tests/staker_test.go index f57b68ad8a..03c9fd3628 100644 --- a/system_tests/staker_test.go +++ b/system_tests/staker_test.go @@ -166,7 +166,7 @@ func stakerTestImpl(t *testing.T, faultyStaker bool, honestStakerInactive bool) validatorUtils, err := rollupgen.NewValidatorUtils(l2nodeA.DeployInfo.ValidatorUtils, builder.L1.Client) Require(t, err) - valConfig := staker.TestL1ValidatorConfig + valConfigA := staker.TestL1ValidatorConfig parentChainID, err := builder.L1.Client.ChainID(ctx) if err != nil { t.Fatalf("Failed to get parent chain id: %v", err) @@ -182,12 +182,12 @@ func stakerTestImpl(t *testing.T, faultyStaker bool, honestStakerInactive bool) if err != nil { t.Fatalf("Error creating validator dataposter: %v", err) } - valWalletA, err := validatorwallet.NewContract(dpA, nil, l2nodeA.DeployInfo.ValidatorWalletCreator, l2nodeA.DeployInfo.Rollup, l2nodeA.L1Reader, &l1authA, 0, func(common.Address) {}, func() uint64 { return valConfig.ExtraGas }) + valWalletA, err := validatorwallet.NewContract(dpA, nil, l2nodeA.DeployInfo.ValidatorWalletCreator, l2nodeA.DeployInfo.Rollup, l2nodeA.L1Reader, &l1authA, 0, func(common.Address) {}, func() uint64 { return valConfigA.ExtraGas }) Require(t, err) if honestStakerInactive { - valConfig.Strategy = "Defensive" + valConfigA.Strategy = "Defensive" } else { - valConfig.Strategy = "MakeNodes" + valConfigA.Strategy = "MakeNodes" } _, valStack := createTestValidationNode(t, ctx, &valnode.TestValidationConfig) @@ -210,7 +210,7 @@ func stakerTestImpl(t *testing.T, faultyStaker bool, honestStakerInactive bool) l2nodeA.L1Reader, valWalletA, bind.CallOpts{}, - valConfig, + func() *staker.L1ValidatorConfig { return &valConfigA }, nil, statelessA, nil, @@ -244,7 +244,8 @@ func stakerTestImpl(t *testing.T, faultyStaker bool, honestStakerInactive bool) } valWalletB, err := validatorwallet.NewEOA(dpB, l2nodeB.DeployInfo.Rollup, l2nodeB.L1Reader.Client(), func() uint64 { return 0 }) Require(t, err) - valConfig.Strategy = "MakeNodes" + valConfigB := staker.TestL1ValidatorConfig + valConfigB.Strategy = "MakeNodes" statelessB, err := staker.NewStatelessBlockValidator( l2nodeB.InboxReader, l2nodeB.InboxTracker, @@ -262,7 +263,7 @@ func stakerTestImpl(t *testing.T, faultyStaker bool, honestStakerInactive bool) l2nodeB.L1Reader, valWalletB, bind.CallOpts{}, - valConfig, + func() *staker.L1ValidatorConfig { return &valConfigB }, nil, statelessB, nil, @@ -278,12 +279,13 @@ func stakerTestImpl(t *testing.T, faultyStaker bool, honestStakerInactive bool) Require(t, err) } valWalletC := validatorwallet.NewNoOp(builder.L1.Client, l2nodeA.DeployInfo.Rollup) - valConfig.Strategy = "Watchtower" + valConfigC := staker.TestL1ValidatorConfig + valConfigC.Strategy = "Watchtower" stakerC, err := staker.NewStaker( l2nodeA.L1Reader, valWalletC, bind.CallOpts{}, - valConfig, + func() *staker.L1ValidatorConfig { return &valConfigC }, nil, statelessA, nil,