package reorg import ( "context" "fmt" "log" "math/big" "github.com/ethereum/go-ethereum/ethclient" "github.com/jackc/pgx/v5/pgxpool" ) // ReorgHandler handles blockchain reorganizations type ReorgHandler struct { db *pgxpool.Pool client *ethclient.Client chainID int } // NewReorgHandler creates a new reorg handler func NewReorgHandler(db *pgxpool.Pool, client *ethclient.Client, chainID int) *ReorgHandler { return &ReorgHandler{ db: db, client: client, chainID: chainID, } } // DetectReorg detects if a reorg has occurred func (rh *ReorgHandler) DetectReorg(ctx context.Context, blockNumber int64) (bool, int64, error) { // Get stored block hash var storedHash string query := `SELECT hash FROM blocks WHERE chain_id = $1 AND number = $2` err := rh.db.QueryRow(ctx, query, rh.chainID, blockNumber).Scan(&storedHash) if err != nil { return false, 0, err } // Get current block hash from chain block, err := rh.client.BlockByNumber(ctx, big.NewInt(blockNumber)) if err != nil { return false, 0, err } currentHash := block.Hash().Hex() // Compare hashes if storedHash != currentHash { // Reorg detected, find common ancestor commonAncestor, err := rh.findCommonAncestor(ctx, blockNumber) if err != nil { return false, 0, err } return true, commonAncestor, nil } return false, 0, nil } // findCommonAncestor finds the common ancestor block func (rh *ReorgHandler) findCommonAncestor(ctx context.Context, startBlock int64) (int64, error) { // Binary search to find common ancestor low := int64(0) high := startBlock for low <= high { mid := (low + high) / 2 var storedHash string err := rh.db.QueryRow(ctx, `SELECT hash FROM blocks WHERE chain_id = $1 AND number = $2`, rh.chainID, mid, ).Scan(&storedHash) if err != nil { high = mid - 1 continue } block, err := rh.client.BlockByNumber(ctx, big.NewInt(mid)) if err != nil { high = mid - 1 continue } if storedHash == block.Hash().Hex() { low = mid + 1 } else { high = mid - 1 } } return high, nil } // HandleReorg handles a detected reorg func (rh *ReorgHandler) HandleReorg(ctx context.Context, commonAncestor int64) error { log.Printf("Handling reorg: common ancestor at block %d", commonAncestor) tx, err := rh.db.Begin(ctx) if err != nil { return fmt.Errorf("failed to begin transaction: %w", err) } defer tx.Rollback(ctx) // Mark blocks as orphaned _, err = tx.Exec(ctx, ` UPDATE blocks SET orphaned = true, orphaned_at = NOW() WHERE chain_id = $1 AND number > $2 AND orphaned = false `, rh.chainID, commonAncestor) if err != nil { return fmt.Errorf("failed to mark blocks as orphaned: %w", err) } // Delete orphaned data (cascade will handle related records) _, err = tx.Exec(ctx, ` DELETE FROM blocks WHERE chain_id = $1 AND number > $2 AND orphaned = true `, rh.chainID, commonAncestor) if err != nil { return fmt.Errorf("failed to delete orphaned blocks: %w", err) } return tx.Commit(ctx) }