241 lines
6.6 KiB
Go
241 lines
6.6 KiB
Go
// 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)
|
||
}
|