Skip to content

Commit

Permalink
Merge pull request #2587 from OffchainLabs/fix-hotreloading-staker
Browse files Browse the repository at this point in the history
Fix live reloading of staker config
  • Loading branch information
ganeshvanahalli committed Aug 20, 2024
2 parents 7b0b640 + 4c0603f commit 40601f7
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 44 deletions.
2 changes: 1 addition & 1 deletion arbnode/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
59 changes: 33 additions & 26 deletions staker/staker.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ func (c *L1ValidatorConfig) Validate() error {
return nil
}

type L1ValidatorConfigFetcher func() *L1ValidatorConfig

var DefaultL1ValidatorConfig = L1ValidatorConfig{
Enable: true,
Strategy: "Watchtower",
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -295,7 +297,7 @@ func NewStaker(
l1Reader *headerreader.HeaderReader,
wallet ValidatorWalletInterface,
callOpts bind.CallOpts,
config L1ValidatorConfig,
config L1ValidatorConfigFetcher,
blockValidator *BlockValidator,
statelessBlockValidator *StatelessBlockValidator,
stakedNotifiers []LatestStakedNotifier,
Expand All @@ -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()
Expand All @@ -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 {
Expand All @@ -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,
Expand All @@ -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
Expand All @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -587,7 +591,7 @@ func (s *Staker) Start(ctxIn context.Context) {
notifier.UpdateLatestConfirmed(confirmedMsgCount, *confirmedGlobalState)
}
}
return s.config.StakerInterval
return s.config().StakerInterval
})
}

Expand All @@ -608,14 +612,15 @@ 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)
if err != nil {
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
}
}
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
}
}
}
Expand Down Expand Up @@ -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)
}
}

Expand Down Expand Up @@ -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 {
Expand All @@ -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)
Expand All @@ -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)
}
Expand All @@ -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
Expand Down Expand Up @@ -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 {
Expand Down
18 changes: 10 additions & 8 deletions system_tests/fast_confirm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ func TestFastConfirmation(t *testing.T) {
l2node.L1Reader,
valWallet,
bind.CallOpts{},
valConfig,
func() *staker.L1ValidatorConfig { return &valConfig },
nil,
stateless,
nil,
Expand Down Expand Up @@ -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 {
Expand All @@ -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
Expand All @@ -361,7 +361,7 @@ func TestFastConfirmationWithSafe(t *testing.T) {
l2nodeA.L1Reader,
valWalletA,
bind.CallOpts{},
valConfig,
func() *staker.L1ValidatorConfig { return &valConfigA },
nil,
statelessA,
nil,
Expand Down Expand Up @@ -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,
Expand All @@ -411,7 +413,7 @@ func TestFastConfirmationWithSafe(t *testing.T) {
l2nodeB.L1Reader,
valWalletB,
bind.CallOpts{},
valConfig,
func() *staker.L1ValidatorConfig { return &valConfigB },
nil,
statelessB,
nil,
Expand Down
20 changes: 11 additions & 9 deletions system_tests/staker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand Down

0 comments on commit 40601f7

Please sign in to comment.