From e41aec3d04d51c65d586421528d7f0f40e73943b Mon Sep 17 00:00:00 2001 From: Calin Martinconi Date: Fri, 9 Jan 2026 20:03:20 +0200 Subject: [PATCH 1/2] fix: more proper way to update height on staking contract --- pkg/node/node.go | 58 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 47 insertions(+), 11 deletions(-) diff --git a/pkg/node/node.go b/pkg/node/node.go index f20468df338..34164e77b34 100644 --- a/pkg/node/node.go +++ b/pkg/node/node.go @@ -1116,19 +1116,55 @@ func NewBee( logger.Info("overlay address changed in staking contract", "transaction", tx) } - // make sure that the staking contract has the up to date height - tx, updated, err := stakingContract.UpdateHeight(ctx) - if err != nil { - return nil, fmt.Errorf("update height in staking contract: %w", err) - } - if updated { - logger.Info("updated new reserve capacity doubling height in the staking contract", "transaction", tx, "new_height", o.ReserveCapacityDoubling) - } - // Check if the staked amount is sufficient to cover the additional neighborhoods. // The staked amount must be at least 2^h * MinimumStake. - if o.ReserveCapacityDoubling > 0 && stake.Cmp(big.NewInt(0).Mul(big.NewInt(1< 0 && stake.Cmp(minStake) < 0 { + logger.Warning("staked amount does not sufficiently cover the additional reserve capacity. On-chain height update will be skipped. Node will start, but storage incentives may not function for this capacity.", "missing_stake", new(big.Int).Sub(minStake, stake)) + + // Start a background worker to monitor the stake and update the height when the stake is sufficient. + go func() { + // Check every 10 minutes + ticker := time.NewTicker(10 * time.Minute) + defer ticker.Stop() + + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + currentStake, err := stakingContract.GetPotentialStake(ctx) + if err != nil { + logger.Error(err, "runtime staking monitor: get potential stake") + continue + } + + if currentStake.Cmp(minStake) >= 0 { + tx, updated, err := stakingContract.UpdateHeight(ctx) + if err != nil { + logger.Error(err, "runtime staking monitor: update height") + continue + } + if updated { + logger.Warning("Runtime stake top-up detected. Reserve capacity doubling updated. Node will be FROZEN for ~2 rounds.", "transaction", tx) + } else { + logger.Info("Runtime stake top-up detected. Reserve capacity doubling already updated.") + } + return + } + } + } + }() + + } else { + // make sure that the staking contract has the up to date height + tx, updated, err := stakingContract.UpdateHeight(ctx) + if err != nil { + return nil, fmt.Errorf("update height in staking contract: %w", err) + } + if updated { + logger.Info("updated new reserve capacity doubling height in the staking contract", "transaction", tx, "new_height", o.ReserveCapacityDoubling) + } } } } From c5705ba7bc46659d4ee520524e7d6fed7d748b90 Mon Sep 17 00:00:00 2001 From: Calin Martinconi Date: Tue, 10 Feb 2026 20:38:02 +0200 Subject: [PATCH 2/2] feat: add automatic height update hook to staking deposit api handler --- pkg/api/staking.go | 16 ++++++++++++++++ pkg/node/node.go | 34 ---------------------------------- 2 files changed, 16 insertions(+), 34 deletions(-) diff --git a/pkg/api/staking.go b/pkg/api/staking.go index 2f957e4532a..14a824b4900 100644 --- a/pkg/api/staking.go +++ b/pkg/api/staking.go @@ -5,6 +5,7 @@ package api import ( + "context" "errors" "math/big" "net/http" @@ -77,6 +78,21 @@ func (s *Service) stakingDepositHandler(w http.ResponseWriter, r *http.Request) jsonhttp.InternalServerError(w, "cannot stake") return } + + // if the deposit is successful, we should update the height of the node in the staking contract + // this is done to make sure that the node is participating in the redistribution game with the correct height + // if the node has started with insufficient stake + go func() { + tx, updated, err := s.stakingContract.UpdateHeight(context.Background()) + if err != nil { + logger.Error(err, "update height failed") + return + } + if updated { + logger.Warning("reserve capacity doubling updated after stake deposit. Node will be FROZEN for ~2 rounds.", "transaction", tx) + } + }() + jsonhttp.OK(w, stakeTransactionReponse{ TxHash: txHash.String(), }) diff --git a/pkg/node/node.go b/pkg/node/node.go index 34164e77b34..b828983b5de 100644 --- a/pkg/node/node.go +++ b/pkg/node/node.go @@ -1122,40 +1122,6 @@ func NewBee( if o.ReserveCapacityDoubling > 0 && stake.Cmp(minStake) < 0 { logger.Warning("staked amount does not sufficiently cover the additional reserve capacity. On-chain height update will be skipped. Node will start, but storage incentives may not function for this capacity.", "missing_stake", new(big.Int).Sub(minStake, stake)) - // Start a background worker to monitor the stake and update the height when the stake is sufficient. - go func() { - // Check every 10 minutes - ticker := time.NewTicker(10 * time.Minute) - defer ticker.Stop() - - for { - select { - case <-ctx.Done(): - return - case <-ticker.C: - currentStake, err := stakingContract.GetPotentialStake(ctx) - if err != nil { - logger.Error(err, "runtime staking monitor: get potential stake") - continue - } - - if currentStake.Cmp(minStake) >= 0 { - tx, updated, err := stakingContract.UpdateHeight(ctx) - if err != nil { - logger.Error(err, "runtime staking monitor: update height") - continue - } - if updated { - logger.Warning("Runtime stake top-up detected. Reserve capacity doubling updated. Node will be FROZEN for ~2 rounds.", "transaction", tx) - } else { - logger.Info("Runtime stake top-up detected. Reserve capacity doubling already updated.") - } - return - } - } - } - }() - } else { // make sure that the staking contract has the up to date height tx, updated, err := stakingContract.UpdateHeight(ctx)