gcx/blockchain/bridge-monitor/internal/monitor/reconciler.go

134 lines
3.2 KiB
Go

package monitor
import (
"context"
"math"
"sync"
"time"
"github.com/gogenex/bridge-monitor/internal/eth"
"github.com/gogenex/bridge-monitor/internal/genex"
log "github.com/sirupsen/logrus"
)
// ReconciliationResult 对账结果
type ReconciliationResult struct {
EthLocked float64 `json:"ethLocked"`
GenexMinted float64 `json:"genexMinted"`
Discrepancy float64 `json:"discrepancy"`
Healthy bool `json:"healthy"`
Timestamp time.Time `json:"timestamp"`
BlockEth int64 `json:"blockEth"`
BlockGenex int64 `json:"blockGenex"`
}
// BridgeMonitor 跨链桥监控器
type BridgeMonitor struct {
ethClient *eth.Client
genexClient *genex.Client
alerter *Alerter
metrics *Metrics
threshold float64
mu sync.RWMutex
latest *ReconciliationResult
history []ReconciliationResult
}
func NewBridgeMonitor(
ethClient *eth.Client,
genexClient *genex.Client,
alerter *Alerter,
metrics *Metrics,
threshold float64,
) *BridgeMonitor {
return &BridgeMonitor{
ethClient: ethClient,
genexClient: genexClient,
alerter: alerter,
metrics: metrics,
threshold: threshold,
history: make([]ReconciliationResult, 0, 1000),
}
}
// Reconcile 执行对账:两侧资产必须一致
func (bm *BridgeMonitor) Reconcile(ctx context.Context) (*ReconciliationResult, error) {
// Ethereum 侧:查询 Axelar Gateway 合约锁定的 USDC
ethLocked, ethBlock, err := bm.ethClient.GetLockedAmount("USDC")
if err != nil {
return nil, err
}
// Genex Chain 侧:查询桥铸造的 wrapped USDC 总量
genexMinted, genexBlock, err := bm.genexClient.GetBridgeTokenSupply("USDC")
if err != nil {
return nil, err
}
discrepancy := math.Abs(ethLocked - genexMinted)
result := ReconciliationResult{
EthLocked: ethLocked,
GenexMinted: genexMinted,
Discrepancy: discrepancy,
Healthy: true,
Timestamp: time.Now(),
BlockEth: ethBlock,
BlockGenex: genexBlock,
}
// 更新 Prometheus 指标
bm.metrics.SetEthLocked(ethLocked)
bm.metrics.SetGenexMinted(genexMinted)
bm.metrics.SetDiscrepancy(discrepancy)
bm.metrics.SetTVL(ethLocked)
bm.metrics.IncReconciliation()
// 偏差检查
if ethLocked > 0 && discrepancy > ethLocked*bm.threshold {
result.Healthy = false
log.WithField("ethLocked", ethLocked).
WithField("genexMinted", genexMinted).
WithField("discrepancy", discrepancy).
Error("Bridge asset discrepancy detected!")
bm.alerter.Critical("Bridge asset discrepancy detected", map[string]interface{}{
"ethLocked": ethLocked,
"genexMinted": genexMinted,
"discrepancy": discrepancy,
})
bm.alerter.EmergencyPause()
bm.metrics.IncEmergencyPause()
}
// 保存结果
bm.mu.Lock()
bm.latest = &result
bm.history = append(bm.history, result)
if len(bm.history) > 1000 {
bm.history = bm.history[len(bm.history)-1000:]
}
bm.mu.Unlock()
return &result, nil
}
// GetLatestResult 获取最新对账结果
func (bm *BridgeMonitor) GetLatestResult() *ReconciliationResult {
bm.mu.RLock()
defer bm.mu.RUnlock()
return bm.latest
}
// GetHistory 获取最近 N 条对账记录
func (bm *BridgeMonitor) GetHistory(n int) []ReconciliationResult {
bm.mu.RLock()
defer bm.mu.RUnlock()
if n > len(bm.history) {
n = len(bm.history)
}
return bm.history[len(bm.history)-n:]
}