gcx/blockchain/genex-chain/x/evm/ante/compliance_ante.go

241 lines
6.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Package ante — 验证节点级合规交易拦截
//
// ComplianceAnteHandler 在验证节点打包交易前执行合规检查:
// 1. OFAC 地址拦截 — 制裁名单地址的交易直接拒绝
// 2. Structuring 检测 — 拆分交易规避 $3,000 Travel Rule 阈值
// 3. Travel Rule 预检查 — ≥$3,000 转移必须携带身份哈希
//
// 参考: FinCEN Travel Rule, OFAC SDN List, FATF Recommendation 16
package ante
import (
"fmt"
"math/big"
"sync"
"time"
)
// ComplianceAnteHandler 验证节点级合规检查处理器
type ComplianceAnteHandler struct {
mu sync.RWMutex
// OFAC 制裁名单(地址 → 是否制裁)
ofacList map[string]bool
// Travel Rule 记录sender+receiver hash → 是否已记录)
travelRuleRecords map[string]bool
// Structuring 检测:地址 → 24h 内累计转移金额
recentTransfers map[string]*TransferWindow
// 配置
travelRuleThreshold *big.Int // $3,000 USDC (3000 * 1e6)
structuringWindow time.Duration
}
// TransferWindow 滑动窗口内的转移记录
type TransferWindow struct {
TotalAmount *big.Int
Transfers []TransferRecord
}
// TransferRecord 单笔转移记录
type TransferRecord struct {
Amount *big.Int
Timestamp time.Time
}
// ComplianceResult 合规检查结果
type ComplianceResult struct {
Allowed bool
Reason string
Suspicious bool // 标记为可疑但不拒绝
SuspReason string // 可疑原因
}
// NewComplianceAnteHandler 创建合规检查处理器
func NewComplianceAnteHandler() *ComplianceAnteHandler {
threshold := new(big.Int).Mul(big.NewInt(3000), big.NewInt(1e6)) // $3,000 USDC
return &ComplianceAnteHandler{
ofacList: make(map[string]bool),
travelRuleRecords: make(map[string]bool),
recentTransfers: make(map[string]*TransferWindow),
travelRuleThreshold: threshold,
structuringWindow: 24 * time.Hour,
}
}
// AnteHandle 执行合规检查(在交易打包前调用)
//
// 返回:
// - ComplianceResult.Allowed = false: 交易被拒绝,不会被打包
// - ComplianceResult.Suspicious = true: 标记为可疑,仍可打包但上报
func (h *ComplianceAnteHandler) AnteHandle(from, to string, value *big.Int) ComplianceResult {
h.mu.RLock()
defer h.mu.RUnlock()
// 1. OFAC 地址拦截(链级强制)
if h.ofacList[from] {
return ComplianceResult{
Allowed: false,
Reason: fmt.Sprintf("OFAC sanctioned address (sender: %s), transaction rejected at validator level", from),
}
}
if h.ofacList[to] {
return ComplianceResult{
Allowed: false,
Reason: fmt.Sprintf("OFAC sanctioned address (receiver: %s), transaction rejected at validator level", to),
}
}
result := ComplianceResult{Allowed: true}
// 2. Structuring 检测(拆分交易规避 $3,000 阈值)
if h.isStructuringPattern(from, value) {
result.Suspicious = true
result.SuspReason = fmt.Sprintf(
"Potential structuring detected for %s: cumulative transfers approaching Travel Rule threshold",
from,
)
}
// 3. Travel Rule 预打包检查(≥$3,000
if value.Cmp(h.travelRuleThreshold) >= 0 {
recordKey := fmt.Sprintf("%s:%s", from, to)
if !h.travelRuleRecords[recordKey] {
return ComplianceResult{
Allowed: false,
Reason: fmt.Sprintf("Travel Rule: identity data required for transfers >= $3,000 (from: %s, to: %s)", from, to),
}
}
}
return result
}
// isStructuringPattern 检测是否存在拆分交易模式
// 规则24h 内同一地址的累计小额转移接近或超过 $3,000
func (h *ComplianceAnteHandler) isStructuringPattern(from string, currentValue *big.Int) bool {
window, exists := h.recentTransfers[from]
if !exists {
return false
}
// 清理过期记录
now := time.Now()
var validTransfers []TransferRecord
totalAmount := new(big.Int)
for _, t := range window.Transfers {
if now.Sub(t.Timestamp) <= h.structuringWindow {
validTransfers = append(validTransfers, t)
totalAmount.Add(totalAmount, t.Amount)
}
}
// 加上当前交易
totalAmount.Add(totalAmount, currentValue)
// 如果每笔都低于阈值,但累计超过阈值 → 可疑
allBelowThreshold := true
for _, t := range validTransfers {
if t.Amount.Cmp(h.travelRuleThreshold) >= 0 {
allBelowThreshold = false
break
}
}
if currentValue.Cmp(h.travelRuleThreshold) >= 0 {
allBelowThreshold = false
}
return allBelowThreshold && totalAmount.Cmp(h.travelRuleThreshold) >= 0
}
// ========================
// OFAC 名单管理
// ========================
// UpdateOFACList 更新 OFAC 制裁名单(由链下服务定期调用)
func (h *ComplianceAnteHandler) UpdateOFACList(addresses []string) {
h.mu.Lock()
defer h.mu.Unlock()
h.ofacList = make(map[string]bool, len(addresses))
for _, addr := range addresses {
h.ofacList[addr] = true
}
}
// AddToOFACList 添加地址到 OFAC 名单
func (h *ComplianceAnteHandler) AddToOFACList(address string) {
h.mu.Lock()
defer h.mu.Unlock()
h.ofacList[address] = true
}
// RemoveFromOFACList 从 OFAC 名单移除
func (h *ComplianceAnteHandler) RemoveFromOFACList(address string) {
h.mu.Lock()
defer h.mu.Unlock()
delete(h.ofacList, address)
}
// IsOFACSanctioned 查询地址是否在 OFAC 名单中
func (h *ComplianceAnteHandler) IsOFACSanctioned(address string) bool {
h.mu.RLock()
defer h.mu.RUnlock()
return h.ofacList[address]
}
// ========================
// Travel Rule 管理
// ========================
// RecordTravelRule 记录 Travel Rule 数据
func (h *ComplianceAnteHandler) RecordTravelRule(from, to string) {
h.mu.Lock()
defer h.mu.Unlock()
key := fmt.Sprintf("%s:%s", from, to)
h.travelRuleRecords[key] = true
}
// HasTravelRuleRecord 查询是否已记录 Travel Rule
func (h *ComplianceAnteHandler) HasTravelRuleRecord(from, to string) bool {
h.mu.RLock()
defer h.mu.RUnlock()
key := fmt.Sprintf("%s:%s", from, to)
return h.travelRuleRecords[key]
}
// ========================
// 转移记录Structuring 检测用)
// ========================
// RecordTransfer 记录一笔转移(用于 Structuring 检测)
func (h *ComplianceAnteHandler) RecordTransfer(from string, amount *big.Int) {
h.mu.Lock()
defer h.mu.Unlock()
window, exists := h.recentTransfers[from]
if !exists {
window = &TransferWindow{
TotalAmount: new(big.Int),
Transfers: make([]TransferRecord, 0),
}
h.recentTransfers[from] = window
}
window.Transfers = append(window.Transfers, TransferRecord{
Amount: new(big.Int).Set(amount),
Timestamp: time.Now(),
})
window.TotalAmount.Add(window.TotalAmount, amount)
}
// GetOFACListSize 获取当前 OFAC 名单大小
func (h *ComplianceAnteHandler) GetOFACListSize() int {
h.mu.RLock()
defer h.mu.RUnlock()
return len(h.ofacList)
}