diff --git a/internal/constants/blockchains.go b/internal/constants/blockchains.go index 29fe6f3..3aa7dd7 100644 --- a/internal/constants/blockchains.go +++ b/internal/constants/blockchains.go @@ -42,6 +42,45 @@ func BlockchainTypeToPB(bt wconstants.BlockchainType) commonv1.Blockchain { } } +// rollbackSafeDepth is the number of blocks to roll back when no recent incident is found. +// Used as a fallback when the last rollback incident is older than rollbackIncidentMaxAge. +const rollbackIncidentMaxAge = 2 * time.Hour + +var rollbackSafeDepth = map[wconstants.BlockchainType]int64{ + // Tron + wconstants.BlockchainTypeTron: 20, + + // EVM + wconstants.BlockchainTypeEthereum: 12, + wconstants.BlockchainTypeBinanceSmartChain: 20, + wconstants.BlockchainTypePolygon: 128, + wconstants.BlockchainTypeArbitrum: 20, + wconstants.BlockchainTypeOptimism: 20, + wconstants.BlockchainTypeLinea: 20, + + // BTC Like + wconstants.BlockchainTypeBitcoin: 6, + wconstants.BlockchainTypeLitecoin: 6, + wconstants.BlockchainTypeBitcoinCash: 6, + wconstants.BlockchainTypeDogecoin: 6, +} + +// RollbackFallbackBlock returns the safe block to revert to when no recent incident is found. +func RollbackFallbackBlock(blockchain wconstants.BlockchainType, currentBlock int64) int64 { + depth, ok := rollbackSafeDepth[blockchain] + if !ok { + depth = 20 + } + safe := currentBlock - depth + if safe < 0 { + return 0 + } + return safe +} + +// RollbackIncidentMaxAge is the maximum age of a rollback incident to be considered relevant. +func RollbackIncidentMaxAge() time.Duration { return rollbackIncidentMaxAge } + var minConfirmations = map[wconstants.BlockchainType]uint64{ // Tron wconstants.BlockchainTypeTron: 19, diff --git a/internal/eproxy/service.go b/internal/eproxy/service.go index a77c20d..0d2f9d0 100644 --- a/internal/eproxy/service.go +++ b/internal/eproxy/service.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "strings" + "time" "github.com/dv-net/dv-processing/internal/interceptors" @@ -256,23 +257,29 @@ func (s *Service) GetIncidents(ctx context.Context, blockchain wconstants.Blockc return incidents.Msg.GetItems(), nil } -// GetRollbackStartingBlock returns the block height to start parsing from after a rollback incident -func (s *Service) GetRollbackStartingBlock(ctx context.Context, blockchain wconstants.BlockchainType) (uint64, error) { - incidents, err := s.GetIncidents(ctx, blockchain, 1) - if err != nil { - return 0, fmt.Errorf("get last incident for rollback recovery: %w", err) - } +// GetRollbackStartingBlock returns the block height to start parsing from after a rollback. +// It looks for a recent rollback incident (within rollbackIncidentMaxAge). If none is found, +// it falls back to currentBlock - safeDepth, where safeDepth is blockchain-specific. +func (s *Service) GetRollbackStartingBlock(ctx context.Context, blockchain wconstants.BlockchainType, currentBlock int64) (uint64, error) { + incidents, _ := s.GetIncidents(ctx, blockchain, 10) - if len(incidents) < 1 { - return 0, fmt.Errorf("no incidents found for blockchain %s", blockchain.String()) - } + maxAge := constants.RollbackIncidentMaxAge() + + for _, incident := range incidents { + if incident.GetType() != incidentsv2.IncidentType_INCIDENT_TYPE_ROLLBACK { + continue + } + + if incident.GetCreatedAt() != nil && time.Since(incident.GetCreatedAt().AsTime()) > maxAge { + break // incidents are ordered newest-first; all further are older + } - incident := incidents[0] - if incident.GetType() != incidentsv2.IncidentType_INCIDENT_TYPE_ROLLBACK { - return 0, fmt.Errorf("last incident is not a rollback incident for blockchain %s", blockchain.String()) + return incident.GetDataRollback().GetRevertToBlockHeight(), nil } - return incident.GetDataRollback().GetRevertToBlockHeight(), nil + // No recent incident or API unavailable — fall back to safe depth per blockchain. + safeBlock := constants.RollbackFallbackBlock(blockchain, currentBlock) + return uint64(safeBlock), nil //nolint:gosec } type FindTransactionsParams struct { diff --git a/internal/escanner/scanner.go b/internal/escanner/scanner.go index baa8601..d868d90 100644 --- a/internal/escanner/scanner.go +++ b/internal/escanner/scanner.go @@ -591,7 +591,7 @@ func (s *scanner) checksForEvent(event *transactionsv2.Event) []eventCheck { func (s *scanner) handleRollback(ctx context.Context) error { s.logger.Infof("Handling rollback incident for blockchain %s", s.blockchain.String()) - newStartingBlock, err := s.bs.EProxy().GetRollbackStartingBlock(ctx, s.blockchain) + newStartingBlock, err := s.bs.EProxy().GetRollbackStartingBlock(ctx, s.blockchain, s.lastParsedBlockHeight.Load()) if err != nil { return fmt.Errorf("get rollback starting block: %w", err) }