diff --git a/nodemgr/daemon.go b/nodemgr/daemon.go index 1599f83c..d52e72b0 100644 --- a/nodemgr/daemon.go +++ b/nodemgr/daemon.go @@ -535,8 +535,9 @@ func (d *Daemon) EpochUpdater(ctx context.Context) { } curRound := status.LastRound epochRoundLength := uint64(App.retiClient.Info().Config.EpochRoundLength) - // let's not worry about each pool and its last payout - just get next immediate block number for epoch - stopAtRound := nextEpoch(curRound, epochRoundLength) + // First we need to see if we MISSED an epoch in ANY of our pools - across all of our pools determine which + // we need to stop at first (could be in past - which will be instant fallthrough in waitUntilBlock) + stopAtRound := d.getFirstEligibleEpochRound(curRound, epochRoundLength) misc.Infof(d.logger, "at round:%d, with epoch length:%d, first epoch check at %d", curRound, epochRoundLength, stopAtRound) @@ -557,7 +558,6 @@ func (d *Daemon) EpochUpdater(ctx context.Context) { info = App.retiClient.Info() ) for i, pool := range info.Pools { - appid := pool.PoolAppId if _, found := info.LocalPools[uint64(i+1)]; !found { continue } @@ -577,7 +577,7 @@ func (d *Daemon) EpochUpdater(ctx context.Context) { misc.Infof(d.logger, "already ran epoch update for this epoch on pool:%d, round:%d", i+1, blockWaitResult.atRound) return nil } - err = App.retiClient.EpochBalanceUpdate(i+1, appid, signerAddr) + err = App.retiClient.EpochBalanceUpdate(i+1, pool.PoolAppId, signerAddr) if err != nil { // Assume epoch update failed because it's just 'slightly' too early? return repeat.HintTemporary(fmt.Errorf("epoch balance update failed for pool app id:%d, err:%w", i+1, err)) @@ -650,6 +650,24 @@ func (d *Daemon) waitUntilBlock(ctx context.Context, round uint64) chan BlockOrE return chReturn } +func (d *Daemon) getFirstEligibleEpochRound(curRound uint64, epochRoundLength uint64) uint64 { + var ( + info = App.retiClient.Info() + curRoundEpochStart = curRound - (curRound % epochRoundLength) + earliestEpochToUse = curRoundEpochStart + ) + for i, pool := range info.Pools { + if _, found := info.LocalPools[uint64(i+1)]; !found { + continue + } + lastPayout, err := App.retiClient.GetLastPayout(pool.PoolAppId) + if err == nil { + earliestEpochToUse = min(earliestEpochToUse, nextEpoch(lastPayout, epochRoundLength)) + } + } + return earliestEpochToUse +} + // accountHasAtLeast checks if an account has at least a certain amount of microAlgos (spendable) // Errors are just treated as failures func accountHasAtLeast(ctx context.Context, algoClient *algod.Client, accountAddr string, microAlgos uint64) bool { diff --git a/nodemgr/internal/lib/reti/stakingpool.go b/nodemgr/internal/lib/reti/stakingpool.go index 289f9448..d63552a2 100644 --- a/nodemgr/internal/lib/reti/stakingpool.go +++ b/nodemgr/internal/lib/reti/stakingpool.go @@ -127,8 +127,14 @@ func (r *Reti) EpochBalanceUpdate(poolID int, poolAppID uint64, caller types.Add if err != nil { return fmt.Errorf("failed to get algod status at start: %w", err) } - - misc.Infof(r.Logger, "[EpochBalanceUpdate] pool:%d epoch update at round:%d for app id:%d, avail rewards:%s", poolID, status.LastRound, poolAppID, algo.FormattedAlgoAmount(rewardAvail)) + var epochStr string + epochStart := status.LastRound - status.LastRound%uint64(info.Config.EpochRoundLength) + if epochStart != status.LastRound { + epochStr = fmt.Sprintf("round:%d [EpochStart:%d]", status.LastRound, epochStart) + } else { + epochStr = fmt.Sprintf("EpochStart:%d", epochStart) + } + misc.Infof(r.Logger, "[EpochBalanceUpdate] pool:%d epoch update at %s for app id:%d, avail rewards:%s", poolID, epochStr, poolAppID, algo.FormattedAlgoAmount(rewardAvail)) params, err := r.algoClient.SuggestedParams().Do(context.Background()) if err != nil {