目 录CONTENT

文章目录

风控测试扫盲

懿曲折扇情
2026-03-14 / 0 评论 / 1 点赞 / 5 阅读 / 6,976 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2026-03-14,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。
广告 广告

交易所风控系统测试方案

版本: v2.0
更新时间: 2026-03-14
适用场景: 加密货币交易所、DeFi平台、区块链钱包


目录

  1. 入金风控测试
  2. 出金风控测试
  3. 异常交易检测
  4. 账户安全测试
  5. 反洗钱(AML)测试
  6. 限额控制测试
  7. 设备指纹测试
  8. 实时风控规则
  9. 压力测试
  10. 自动化测试脚本

1. 入金风控测试

1.1 正常入金场景

测试用例 1.1.1: 标准入金流程

场景描述: 用户从外部钱包转入ETH到交易所充值地址

测试步骤:

  1. 获取用户充值地址
  2. 从外部钱包发送 0.1 ETH
  3. 监听链上交易
  4. 等待确认(1个确认)
  5. 验证入账金额

预期结果:

  • ✅ 交易在1个确认后入账
  • ✅ 金额准确无误
  • ✅ 用户收到入金通知
  • ✅ 订单状态更新为"已完成"

风控检查点:

{
  "txHash": "0x...",
  "from": "外部地址",
  "to": "充值地址",
  "amount": "0.1",
  "confirmations": 1,
  "status": "success",
  "riskLevel": "low",
  "checks": {
    "addressWhitelist": "pass",
    "amountRange": "pass",
    "txPattern": "normal",
    "contractCall": false
  }
}

测试脚本:

// test_normal_deposit.js
const { ethers } = require('ethers');

async function testNormalDeposit() {
  console.log('\n=== 测试1.1.1: 标准入金流程 ===\n');

  const provider = new ethers.JsonRpcProvider(process.env.RPC_URL);
  const wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider);

  // 交易所充值地址
  const depositAddress = '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb';
  const amount = ethers.parseEther('0.1');

  // 发送入金交易
  const tx = await wallet.sendTransaction({
    to: depositAddress,
    value: amount,
    gasLimit: 21000,
  });

  console.log('✅ 交易已发送');
  console.log('交易哈希:', tx.hash);
  console.log('发送方:', tx.from);
  console.log('接收方:', tx.to);
  console.log('金额:', ethers.formatEther(amount), 'ETH');

  // 等待确认
  console.log('\n⏳ 等待确认...');
  const receipt = await tx.wait(1);

  console.log('✅ 交易已确认');
  console.log('区块号:', receipt.blockNumber);
  console.log('Gas使用:', receipt.gasUsed.toString());
  console.log('状态:', receipt.status === 1 ? '成功' : '失败');

  // 风控检查
  const riskCheck = {
    txHash: receipt.hash,
    from: receipt.from,
    to: receipt.to,
    amount: ethers.formatEther(amount),
    blockNumber: receipt.blockNumber,
    confirmations: 1,
    gasPrice: receipt.gasPrice.toString(),

    // 风控标记
    isContract: false,
    isKnownAddress: false,
    amountSuspicious: parseFloat(ethers.formatEther(amount)) > 10,
    timingSuspicious: false,

    riskLevel: 'low',
    autoApprove: true,
  };

  console.log('\n📊 风控检查结果:');
  console.log(JSON.stringify(riskCheck, null, 2));

  return riskCheck;
}

module.exports = testNormalDeposit;

1.2 异常入金场景

测试用例 1.2.1: 小额高频入金(洗钱特征)

场景描述: 短时间内多次小额入金,可能是洗钱行为

测试步骤:

  1. 5分钟内发送10笔 0.01 ETH
  2. 观察风控系统响应
  3. 检查是否触发预警

预期结果:

  • ⚠️ 触发"小额高频"预警
  • ⚠️ 标记为中等风险
  • ⚠️ 需要人工审核
  • ⚠️ 暂时冻结资金

风控规则:

{
  "ruleName": "小额高频入金检测",
  "condition": {
    "timeWindow": "5分钟",
    "txCount": "> 5笔",
    "avgAmount": "< 0.05 ETH",
    "sameSource": true
  },
  "action": {
    "riskLevel": "medium",
    "autoFreeze": true,
    "notifyRiskTeam": true,
    "requireManualReview": true
  }
}

测试脚本:

// test_small_frequent_deposit.js
async function testSmallFrequentDeposit() {
  console.log('\n=== 测试1.2.1: 小额高频入金 ===\n');

  const provider = new ethers.JsonRpcProvider(process.env.RPC_URL);
  const wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider);
  const depositAddress = '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb';

  const transactions = [];
  const startTime = Date.now();

  // 发送10笔小额交易
  for (let i = 0; i < 10; i++) {
    const amount = ethers.parseEther('0.01'); // 小额

    const tx = await wallet.sendTransaction({
      to: depositAddress,
      value: amount,
      gasLimit: 21000,
    });

    transactions.push({
      hash: tx.hash,
      amount: '0.01',
      timestamp: Date.now(),
    });

    console.log(`✅ 交易 ${i + 1}/10 已发送: ${tx.hash}`);

    // 间隔30秒
    if (i < 9) {
      await new Promise(resolve => setTimeout(resolve, 30000));
    }
  }

  const endTime = Date.now();
  const totalTime = (endTime - startTime) / 1000 / 60; // 分钟

  // 风控分析
  const riskAnalysis = {
    pattern: '小额高频入金',
    txCount: transactions.length,
    totalAmount: (transactions.length * 0.01).toFixed(2),
    timeWindow: `${totalTime.toFixed(2)}分钟`,
    avgInterval: `${(totalTime * 60 / transactions.length).toFixed(0)}秒`,

    riskIndicators: {
      smallAmount: true,        // 单笔金额小
      highFrequency: true,      // 频率高
      sameSource: true,         // 同一来源
      unusualPattern: true,     // 异常模式
    },

    riskLevel: 'medium',
    riskScore: 65,

    recommendations: [
      '⚠️ 暂时冻结入金',
      '⚠️ 通知风控团队',
      '⚠️ 要求KYC验证',
      '⚠️ 检查资金来源'
    ]
  };

  console.log('\n🚨 风控预警:');
  console.log(JSON.stringify(riskAnalysis, null, 2));

  return riskAnalysis;
}

测试用例 1.2.2: 大额单笔入金

场景描述: 单笔入金金额超过阈值

测试参数:

  • 金额: 100 ETH (假设阈值是50 ETH)
  • 来源: 未知地址

预期结果:

  • 🚨 触发"大额入金"预警
  • 🚨 标记为高风险
  • 🚨 自动冻结资金
  • 🚨 立即通知风控团队
  • 🚨 要求提供资金来源证明

风控规则:

{
  "ruleName": "大额入金检测",
  "threshold": {
    "ETH": 50,
    "USDT": 100000,
    "BTC": 2
  },
  "action": {
    "riskLevel": "high",
    "autoFreeze": true,
    "immediatAlert": true,
    "requireProofOfFunds": true,
    "manualApprovalRequired": true
  }
}

测试用例 1.2.3: 黑名单地址入金

场景描述: 从已知黑名单地址转入资金

测试步骤:

  1. 维护黑名单地址列表
  2. 从黑名单地址发送交易
  3. 验证系统是否拦截

预期结果:

  • 🔴 立即拒绝入账
  • 🔴 标记为极高风险
  • 🔴 冻结相关账户
  • 🔴 触发安全警报
  • 🔴 自动生成风险报告

黑名单来源:

const BLACKLIST_SOURCES = [
  'Chainalysis Sanctions',
  'OFAC SDN List',
  'Tornado Cash addresses',
  'Known hacker addresses',
  'Phishing addresses',
  'Exchange hack addresses',
];

// 黑名单检查
async function checkBlacklist(address) {
  const blacklist = [
    '0x8576acc5c05d6ce88f4e49bf65bdf0c62f91353c', // Tornado Cash
    '0x722122df12d4e14e13ac3b6895a86e84145b6967', // Ronin Bridge Hacker
    // ... 更多地址
  ];

  const isBlacklisted = blacklist.includes(address.toLowerCase());

  if (isBlacklisted) {
    return {
      isBlacklisted: true,
      riskLevel: 'critical',
      action: 'reject',
      reason: '黑名单地址',
      alert: {
        priority: 'critical',
        notifyList: ['security-team@exchange.com', 'compliance@exchange.com'],
        freezeAccount: true,
        reportToAuthorities: true,
      }
    };
  }

  return { isBlacklisted: false };
}

测试用例 1.2.4: 合约转账入金

场景描述: 从智能合约地址转入资金(可能是套利、闪电贷)

测试步骤:

  1. 部署测试合约
  2. 从合约调用transfer
  3. 观察风控响应

预期结果:

  • ⚠️ 标记为"合约转账"
  • ⚠️ 延迟入账(增加确认数)
  • ⚠️ 中等风险
  • ⚠️ 记录合约地址

检测脚本:

// 检测是否是合约地址
async function isContractAddress(address, provider) {
  const code = await provider.getCode(address);
  return code !== '0x';
}

// 合约转账检测
async function checkContractDeposit(txHash, provider) {
  const tx = await provider.getTransaction(txHash);
  const receipt = await provider.getTransactionReceipt(txHash);

  const fromIsContract = await isContractAddress(tx.from, provider);

  if (fromIsContract) {
    return {
      isContractDeposit: true,
      riskLevel: 'medium',
      action: {
        increaseConfirmations: 12,  // 从1增加到12
        delayCredit: true,
        flagForReview: true,
      },
      contractInfo: {
        address: tx.from,
        verified: false,  // 是否经过验证
        knownContract: false,  // 是否已知合约
      }
    };
  }

  return { isContractDeposit: false };
}

测试用例 1.2.5: 重放攻击检测

场景描述: 攻击者尝试重复提交同一笔入金交易

测试步骤:

  1. 提交正常入金交易
  2. 尝试重复提交相同txHash
  3. 验证系统是否拦截

预期结果:

  • 🔴 检测到重复交易
  • 🔴 拒绝重复入账
  • 🔴 记录异常尝试
  • 🔴 可能冻结账户

防护代码:

// 交易去重检查
const processedTxs = new Set();

async function checkDuplicateTx(txHash) {
  if (processedTxs.has(txHash)) {
    return {
      isDuplicate: true,
      action: 'reject',
      reason: '重复交易',
      alert: {
        priority: 'high',
        message: '检测到重放攻击尝试',
        suspiciousActivity: true,
      }
    };
  }

  processedTxs.add(txHash);
  return { isDuplicate: false };
}

2. 出金风控测试

2.1 正常提现场景

测试用例 2.1.1: 标准提现流程

场景描述: 用户正常提现ETH到外部地址

测试步骤:

  1. 用户提交提现请求
  2. 系统风控检查
  3. 审批通过
  4. 执行链上转账
  5. 确认到账

预期结果:

  • ✅ 风控检查通过
  • ✅ 自动审批
  • ✅ 成功发送交易
  • ✅ 用户收到提现通知

风控检查项:

const withdrawalRiskCheck = {
  // 用户维度
  user: {
    accountAge: '> 30天',
    kycLevel: 'L2',
    tradingHistory: '正常',
    previousWithdrawals: '正常',
    riskScore: 20,
  },

  // 提现维度
  withdrawal: {
    amount: '0.5 ETH',
    destination: '0x...',
    isNewAddress: false,
    addressWhitelisted: true,
    frequency: '正常',
  },

  // 行为维度
  behavior: {
    loginLocation: '常用地区',
    deviceFingerprint: '已识别',
    ipReputation: '正常',
    timePattern: '正常营业时间',
  },

  // 综合评分
  overallRisk: {
    level: 'low',
    score: 15,
    autoApprove: true,
    requiresManualReview: false,
  }
};

2.2 异常提现场景

测试用例 2.2.1: 新地址大额提现

场景描述: 首次向新地址提现大额资金

测试参数:

  • 金额: 50 ETH
  • 目标地址: 从未使用过的新地址
  • 用户账户年龄: 7天

预期结果:

  • 🚨 触发"新地址大额提现"预警
  • 🚨 高风险评分
  • 🚨 要求额外验证(2FA、邮件确认、SMS)
  • 🚨 人工审核
  • 🚨 延迟24小时执行

风控规则:

{
  "ruleName": "新地址大额提现",
  "conditions": {
    "isNewAddress": true,
    "amount": "> 10 ETH",
    "accountAge": "< 30天"
  },
  "actions": {
    "riskLevel": "high",
    "requireAdditionalAuth": true,
    "authMethods": ["2FA", "email", "sms"],
    "manualReview": true,
    "delayExecution": "24小时",
    "notifyUser": true,
    "notifyRiskTeam": true
  }
}

测试脚本:

// test_new_address_large_withdrawal.js
async function testNewAddressLargeWithdrawal() {
  console.log('\n=== 测试2.2.1: 新地址大额提现 ===\n');

  const withdrawalRequest = {
    userId: 'user_12345',
    amount: '50',
    currency: 'ETH',
    destinationAddress: '0x1234567890123456789012345678901234567890', // 新地址
    requestTime: Date.now(),
  };

  // 风控检查
  const riskCheck = await performWithdrawalRiskCheck(withdrawalRequest);

  console.log('📊 风控检查结果:');
  console.log(JSON.stringify(riskCheck, null, 2));

  return riskCheck;
}

async function performWithdrawalRiskCheck(request) {
  // 1. 检查地址是否首次使用
  const isNewAddress = await checkIfNewAddress(request.destinationAddress);

  // 2. 检查金额是否超过阈值
  const isLargeAmount = parseFloat(request.amount) > 10;

  // 3. 检查用户账户年龄
  const accountAge = await getAccountAge(request.userId);
  const isNewAccount = accountAge < 30; // 天

  // 4. 计算风险评分
  let riskScore = 0;
  const riskFactors = [];

  if (isNewAddress) {
    riskScore += 30;
    riskFactors.push('新提现地址');
  }

  if (isLargeAmount) {
    riskScore += 40;
    riskFactors.push('大额提现');
  }

  if (isNewAccount) {
    riskScore += 20;
    riskFactors.push('新用户账户');
  }

  // 5. 决策
  let decision = 'approve';
  let actions = [];

  if (riskScore >= 70) {
    decision = 'manual_review';
    actions = [
      '要求额外身份验证',
      '24小时延迟执行',
      '人工审核',
      '通知风控团队',
    ];
  } else if (riskScore >= 40) {
    decision = 'additional_verification';
    actions = [
      '要求2FA验证',
      '发送邮件确认链接',
      '短信验证码',
    ];
  }

  return {
    requestId: `WD_${Date.now()}`,
    decision,
    riskLevel: riskScore >= 70 ? 'high' : riskScore >= 40 ? 'medium' : 'low',
    riskScore,
    riskFactors,
    actions,
    estimatedProcessTime: decision === 'manual_review' ? '24-48小时' : '10分钟',
  };
}

// 辅助函数
async function checkIfNewAddress(address) {
  // 查询数据库:该地址是否曾被该用户或其他用户使用过
  // 模拟实现
  return true; // 假设是新地址
}

async function getAccountAge(userId) {
  // 查询用户注册天数
  // 模拟实现
  return 7; // 7天
}

测试用例 2.2.2: 高频提现(可能被盗)

场景描述: 短时间内多次提现,可能账户被盗

测试参数:

  • 1小时内提现3次
  • 每次金额递增
  • 提现地址不同

预期结果:

  • 🔴 触发"异常高频提现"
  • 🔴 立即冻结账户
  • 🔴 暂停所有提现
  • 🔴 联系用户确认
  • 🔴 极高风险

风控规则:

{
  "ruleName": "高频提现检测",
  "timeWindow": "1小时",
  "conditions": {
    "withdrawalCount": ">= 3",
    "differentAddresses": true,
    "increasingAmounts": true
  },
  "riskIndicators": {
    "accountCompromise": "very_high",
    "possibleTheft": true
  },
  "actions": {
    "freezeAccount": true,
    "cancelPendingWithdrawals": true,
    "notifyUser": {
      "email": true,
      "sms": true,
      "phone": true
    },
    "requireIdentityVerification": true,
    "escalateToSecurity": true
  }
}

测试用例 2.2.3: 异常IP/设备提现

场景描述: 从未使用过的IP/设备发起提现

测试参数:

  • 登录IP: 来自高风险国家
  • 设备指纹: 首次见到
  • 与历史登录地点不符

预期结果:

  • 🚨 触发"异常设备访问"
  • 🚨 要求强制重新登录
  • 🚨 要求身份验证
  • 🚨 人工审核提现请求

检测脚本:

// 设备指纹检测
async function checkDeviceAnomaly(request) {
  const userProfile = await getUserProfile(request.userId);

  const checks = {
    // IP检查
    ipAnomaly: {
      currentIP: request.ipAddress,
      historicalIPs: userProfile.knownIPs,
      isNewIP: !userProfile.knownIPs.includes(request.ipAddress),
      ipReputation: await getIPReputation(request.ipAddress),
      countryCode: await getIPCountry(request.ipAddress),
      isHighRiskCountry: ['XX', 'YY'].includes(await getIPCountry(request.ipAddress)),
    },

    // 设备检查
    deviceAnomaly: {
      deviceId: request.deviceFingerprint,
      isKnownDevice: userProfile.knownDevices.includes(request.deviceFingerprint),
      deviceType: request.deviceType,
      browserFingerprint: request.browserFingerprint,
    },

    // 时间检查
    timeAnomaly: {
      currentTime: new Date(request.timestamp),
      usualActiveHours: userProfile.usualActiveHours,
      isUnusualTime: checkUnusualTime(request.timestamp, userProfile.usualActiveHours),
    },
  };

  // 计算异常评分
  let anomalyScore = 0;

  if (checks.ipAnomaly.isNewIP) anomalyScore += 20;
  if (checks.ipAnomaly.isHighRiskCountry) anomalyScore += 30;
  if (!checks.deviceAnomaly.isKnownDevice) anomalyScore += 25;
  if (checks.timeAnomaly.isUnusualTime) anomalyScore += 15;

  return {
    checks,
    anomalyScore,
    riskLevel: anomalyScore >= 50 ? 'high' : anomalyScore >= 30 ? 'medium' : 'low',
    recommendation: anomalyScore >= 50 ? 'reject' : anomalyScore >= 30 ? 'additional_verification' : 'approve',
  };
}

测试用例 2.2.4: 混币器地址提现

场景描述: 提现到已知混币器地址(隐私币混合服务)

已知混币器:

  • Tornado Cash
  • Blender
  • ChipMixer
  • CoinJoin

预期结果:

  • 🔴 立即拦截
  • 🔴 标记账户
  • 🔴 可能涉及洗钱
  • 🔴 报告合规部门

检测脚本:

// 混币器地址库
const MIXER_ADDRESSES = {
  tornadoCash: [
    '0x8576acc5c05d6ce88f4e49bf65bdf0c62f91353c',
    '0xd90e2f925DA726b50C4Ed8D0Fb90Ad053324F31b',
    // ... 更多地址
  ],
  other: [
    // 其他混币器
  ]
};

async function checkMixerAddress(destinationAddress) {
  const allMixers = [
    ...MIXER_ADDRESSES.tornadoCash,
    ...MIXER_ADDRESSES.other,
  ];

  const isMixer = allMixers.includes(destinationAddress.toLowerCase());

  if (isMixer) {
    return {
      isMixerAddress: true,
      mixerType: 'Tornado Cash', // 实际需要识别具体类型
      riskLevel: 'critical',
      action: 'reject',
      reasons: [
        '目标地址为混币器',
        '可能涉及洗钱',
        '违反AML政策',
      ],
      requiredActions: [
        '立即冻结账户',
        '报告合规部门',
        '记录详细日志',
        '可能需要报告监管机构',
      ]
    };
  }

  return { isMixerAddress: false };
}

3. 异常交易检测

3.1 自交易检测

场景描述: 用户在自己控制的账户间频繁交易(刷量)

检测方法:

async function detectSelfTrading(userId, trades) {
  // 分析交易对手
  const counterparties = trades.map(t => t.counterpartyId);
  const uniqueCounterparties = new Set(counterparties);

  // 检查是否存在循环交易
  const tradingGraph = buildTradingGraph(trades);
  const cycles = detectCycles(tradingGraph);

  if (cycles.length > 0) {
    return {
      isSelfTrading: true,
      pattern: 'circular_trading',
      cycleCount: cycles.length,
      involvedAccounts: cycles.flat(),
      riskLevel: 'high',
      possibleWashTrading: true,
    };
  }

  return { isSelfTrading: false };
}

3.2 价格操纵检测

场景描述: 异常大单导致价格剧烈波动

检测指标:

  • 单笔订单占成交量比例 > 30%
  • 价格波动 > 5% in 1分钟
  • 订单簿失衡

预期结果:

  • 🚨 触发"价格操纵"预警
  • 🚨 暂停交易
  • 🚨 回滚异常交易
  • 🚨 调查相关账户

3.3 抢先交易检测

场景描述: 检测到内幕交易或抢先交易行为

检测方法:

async function detectFrontRunning(trades) {
  for (let i = 0; i < trades.length - 1; i++) {
    const trade1 = trades[i];
    const trade2 = trades[i + 1];

    // 检查时间间隔
    const timeDiff = trade2.timestamp - trade1.timestamp;

    // 检查交易方向和金额
    if (
      timeDiff < 1000 && // 1秒内
      trade1.side !== trade2.side && // 相反方向
      trade1.amount * 0.8 < trade2.amount // 金额相近
    ) {
      return {
        isFrontRunning: true,
        suspiciousTrades: [trade1.id, trade2.id],
        timeDiff: `${timeDiff}ms`,
        riskLevel: 'high',
      };
    }
  }

  return { isFrontRunning: false };
}

4. 账户安全测试

4.1 暴力破解检测

场景描述: 多次密码尝试失败

测试步骤:

  1. 连续5次输入错误密码
  2. 观察系统响应

预期结果:

  • ⚠️ 第3次失败后显示验证码
  • 🔴 第5次失败后锁定账户15分钟
  • 🔴 通知用户
  • 🔴 记录IP地址

防护代码:

const loginAttempts = new Map();

async function checkLoginAttempts(username, ip) {
  const key = `${username}_${ip}`;
  const attempts = loginAttempts.get(key) || { count: 0, firstAttempt: Date.now() };

  attempts.count++;
  loginAttempts.set(key, attempts);

  if (attempts.count >= 5) {
    return {
      allowed: false,
      reason: '登录尝试次数过多',
      lockDuration: 15 * 60 * 1000, // 15分钟
      action: 'account_locked',
    };
  }

  if (attempts.count >= 3) {
    return {
      allowed: true,
      requireCaptcha: true,
    };
  }

  return { allowed: true };
}

4.2 会话劫持检测

场景描述: 同一账户从多个地点同时登录

检测方法:

async function detectSessionHijacking(userId, newSession) {
  const activeSessions = await getActiveSessions(userId);

  for (const session of activeSessions) {
    // 检查地理位置差异
    const distance = calculateDistance(
      session.location,
      newSession.location
    );

    // 检查时间差异
    const timeDiff = newSession.timestamp - session.lastActivity;

    // 如果距离太远但时间太短(不可能的旅行)
    if (distance > 1000 && timeDiff < 3600000) { // 1000km, 1小时
      return {
        isHijacking: true,
        reason: '不可能的旅行',
        distance: `${distance}km`,
        timeDiff: `${timeDiff / 60000}分钟`,
        action: 'terminate_all_sessions',
        requireReauth: true,
      };
    }
  }

  return { isHijacking: false };
}

5. 反洗钱(AML)测试

5.1 资金链分析

场景描述: 追踪资金来源和去向

测试步骤:

  1. 获取入金交易
  2. 追溯链上历史(向前5跳)
  3. 分析资金来源
  4. 评估风险

风险来源:

  • 🔴 来自黑客地址
  • 🔴 来自暗网交易
  • 🔴 来自勒索软件
  • 🔴 经过混币器
  • ⚠️ 来自高风险交易所

分析脚本:

// 资金链追踪
async function traceFundSource(txHash, depth = 5) {
  const provider = new ethers.JsonRpcProvider(process.env.RPC_URL);
  const visited = new Set();
  const path = [];

  async function trace(hash, currentDepth) {
    if (currentDepth >= depth || visited.has(hash)) {
      return;
    }

    visited.add(hash);

    const tx = await provider.getTransaction(hash);
    if (!tx) return;

    path.push({
      depth: currentDepth,
      hash: tx.hash,
      from: tx.from,
      to: tx.to,
      value: ethers.formatEther(tx.value),
      blockNumber: tx.blockNumber,
    });

    // 检查来源地址的风险
    const fromRisk = await checkAddressRisk(tx.from);
    if (fromRisk.isHighRisk) {
      path[path.length - 1].risk = fromRisk;
    }

    // 继续追溯from地址的历史交易
    const fromTxs = await getAddressTransactions(tx.from, 'received');
    if (fromTxs.length > 0) {
      await trace(fromTxs[0].hash, currentDepth + 1);
    }
  }

  await trace(txHash, 0);

  // 分析路径
  const analysis = {
    totalHops: path.length,
    path: path,
    riskAssessment: assessPathRisk(path),
    fundSource: path[path.length - 1]?.from || 'unknown',
  };

  return analysis;
}

function assessPathRisk(path) {
  let riskScore = 0;
  const riskFactors = [];

  for (const hop of path) {
    if (hop.risk) {
      riskScore += hop.risk.score;
      riskFactors.push({
        address: hop.from,
        reason: hop.risk.reason,
      });
    }
  }

  return {
    score: riskScore,
    level: riskScore > 70 ? 'high' : riskScore > 40 ? 'medium' : 'low',
    factors: riskFactors,
  };
}

async function checkAddressRisk(address) {
  // 集成多个数据源
  const checks = await Promise.all([
    checkChainalysis(address),
    checkElliptic(address),
    checkInternalBlacklist(address),
  ]);

  const highRiskSources = checks.filter(c => c.isHighRisk);

  if (highRiskSources.length > 0) {
    return {
      isHighRisk: true,
      score: 100,
      reason: highRiskSources.map(s => s.reason).join(', '),
      sources: highRiskSources,
    };
  }

  return { isHighRisk: false, score: 0 };
}

// 模拟外部API调用
async function checkChainalysis(address) {
  // 实际应调用 Chainalysis API
  return { isHighRisk: false };
}

async function checkElliptic(address) {
  // 实际应调用 Elliptic API
  return { isHighRisk: false };
}

async function checkInternalBlacklist(address) {
  // 检查内部黑名单
  return { isHighRisk: false };
}

async function getAddressTransactions(address, type) {
  // 获取地址交易记录
  // 实际应该调用区块链浏览器API或自建索引
  return [];
}

5.2 分层交易检测

场景描述: 资金通过多层账户转移(典型洗钱手法)

特征:

A → B → C → D → E → F
每层金额略有变化
每层间隔一定时间
最终回到A或转到外部

检测逻辑:

async function detectLayering(transactions) {
  // 构建资金流图
  const graph = {};

  for (const tx of transactions) {
    if (!graph[tx.from]) graph[tx.from] = [];
    graph[tx.from].push({ to: tx.to, amount: tx.amount, time: tx.timestamp });
  }

  // 检测链式转账
  function findChains(start, path = [], visited = new Set()) {
    if (path.length >= 5) {
      // 发现5层以上的链
      return [path];
    }

    if (!graph[start] || visited.has(start)) {
      return [];
    }

    visited.add(start);
    const chains = [];

    for (const next of graph[start]) {
      const newPath = [...path, { from: start, to: next.to, amount: next.amount }];
      chains.push(...findChains(next.to, newPath, new Set(visited)));
    }

    return chains;
  }

  const allChains = [];
  for (const address in graph) {
    allChains.push(...findChains(address));
  }

  // 分析可疑链
  const suspiciousChains = allChains.filter(chain => {
    // 检查金额衰减(扣除手续费)
    const amounts = chain.map(c => parseFloat(c.amount));
    const avgDecay = (amounts[0] - amounts[amounts.length - 1]) / amounts.length;

    // 检查时间间隔
    const times = chain.map(c => c.time);
    const intervals = times.slice(1).map((t, i) => t - times[i]);
    const avgInterval = intervals.reduce((a, b) => a + b, 0) / intervals.length;

    return (
      chain.length >= 5 &&
      avgDecay < 0.05 && // 每层损失小于5%
      avgInterval < 3600000 && // 平均间隔小于1小时
      avgInterval > 60000 // 但大于1分钟
    );
  });

  return {
    totalChains: allChains.length,
    suspiciousChains: suspiciousChains.length,
    details: suspiciousChains.map(chain => ({
      layers: chain.length,
      startAddress: chain[0].from,
      endAddress: chain[chain.length - 1].to,
      totalAmount: chain[0].amount,
      lossPercentage: ((chain[0].amount - chain[chain.length - 1].amount) / chain[0].amount * 100).toFixed(2) + '%',
      riskLevel: 'high',
      possibleLayering: true,
    })),
  };
}

6. 限额控制测试

6.1 单笔限额测试

测试用例:

场景 限额 测试金额 预期结果
未KYC用户提现 1 ETH/天 1.5 ETH ❌ 拒绝
L1 KYC用户提现 10 ETH/天 5 ETH ✅ 通过
L2 KYC用户提现 100 ETH/天 50 ETH ✅ 通过
VIP用户提现 无限制 500 ETH ✅ 通过(需审核)

实现代码:

// 限额配置
const WITHDRAWAL_LIMITS = {
  'unverified': { daily: 1, monthly: 10 },
  'kyc_l1': { daily: 10, monthly: 100 },
  'kyc_l2': { daily: 100, monthly: 1000 },
  'vip': { daily: Infinity, monthly: Infinity },
};

async function checkWithdrawalLimit(userId, amount) {
  const user = await getUser(userId);
  const limits = WITHDRAWAL_LIMITS[user.kycLevel];

  // 查询今日已提现金额
  const todayWithdrawals = await getTodayWithdrawals(userId);
  const todayTotal = todayWithdrawals.reduce((sum, w) => sum + parseFloat(w.amount), 0);

  // 查询本月已提现金额
  const monthWithdrawals = await getMonthWithdrawals(userId);
  const monthTotal = monthWithdrawals.reduce((sum, w) => sum + parseFloat(w.amount), 0);

  // 检查限额
  const afterToday = todayTotal + amount;
  const afterMonth = monthTotal + amount;

  if (afterToday > limits.daily) {
    return {
      allowed: false,
      reason: '超过每日提现限额',
      limit: limits.daily,
      current: todayTotal,
      requested: amount,
      remaining: Math.max(0, limits.daily - todayTotal),
    };
  }

  if (afterMonth > limits.monthly) {
    return {
      allowed: false,
      reason: '超过每月提现限额',
      limit: limits.monthly,
      current: monthTotal,
      requested: amount,
      remaining: Math.max(0, limits.monthly - monthTotal),
    };
  }

  return {
    allowed: true,
    remainingDaily: limits.daily - afterToday,
    remainingMonthly: limits.monthly - afterMonth,
  };
}

7. 设备指纹测试

7.1 设备指纹采集

采集项目:

const deviceFingerprint = {
  // 基础信息
  userAgent: navigator.userAgent,
  platform: navigator.platform,
  language: navigator.language,

  // 屏幕信息
  screenResolution: `${screen.width}x${screen.height}`,
  colorDepth: screen.colorDepth,
  pixelRatio: window.devicePixelRatio,

  // 浏览器特征
  timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
  timezoneOffset: new Date().getTimezoneOffset(),
  cookieEnabled: navigator.cookieEnabled,
  doNotTrack: navigator.doNotTrack,

  // Canvas指纹
  canvasFingerprint: getCanvasFingerprint(),

  // WebGL指纹
  webglVendor: getWebGLVendor(),
  webglRenderer: getWebGLRenderer(),

  // 音频指纹
  audioFingerprint: getAudioFingerprint(),

  // 字体检测
  installedFonts: detectFonts(),

  // 插件
  plugins: Array.from(navigator.plugins).map(p => p.name),

  // 硬件信息
  hardwareConcurrency: navigator.hardwareConcurrency,
  deviceMemory: navigator.deviceMemory,

  // 网络信息
  connection: {
    effectiveType: navigator.connection?.effectiveType,
    downlink: navigator.connection?.downlink,
  },
};

// 生成指纹哈希
const fingerprintHash = sha256(JSON.stringify(deviceFingerprint));

7.2 设备指纹验证

测试场景:

  1. 正常登录:设备指纹匹配 → ✅ 通过
  2. 新设备登录:指纹不匹配 → ⚠️ 要求额外验证
  3. 虚拟机/模拟器:检测到异常 → 🔴 高风险

8. 实时风控规则

8.1 规则引擎

规则配置示例:

const riskRules = [
  {
    id: 'R001',
    name: '大额单笔提现',
    condition: (tx) => tx.type === 'withdrawal' && tx.amount > 50,
    action: { requireManualReview: true, priority: 'high' },
  },
  {
    id: 'R002',
    name: '新用户快速提现',
    condition: (tx, user) => user.accountAge < 7 && tx.type === 'withdrawal',
    action: { delay: 24 * 3600, notifyRiskTeam: true },
  },
  {
    id: 'R003',
    name: '异地登录',
    condition: (session, user) => !user.knownLocations.includes(session.location),
    action: { requireReauth: true, send2FA: true },
  },
  {
    id: 'R004',
    name: '黑名单地址',
    condition: (tx) => isBlacklisted(tx.destinationAddress),
    action: { reject: true, freezeAccount: true, alertCompliance: true },
  },
];

// 规则执行引擎
async function executeRiskRules(transaction, user) {
  const triggeredRules = [];
  const actions = [];

  for (const rule of riskRules) {
    try {
      if (rule.condition(transaction, user)) {
        triggeredRules.push(rule.id);
        actions.push(rule.action);
      }
    } catch (error) {
      console.error(`规则 ${rule.id} 执行失败:`, error);
    }
  }

  // 合并所有动作
  const finalAction = mergeActions(actions);

  return {
    triggeredRules,
    finalAction,
    riskScore: calculateRiskScore(triggeredRules),
  };
}

function mergeActions(actions) {
  const merged = {};

  for (const action of actions) {
    if (action.reject) merged.reject = true;
    if (action.freezeAccount) merged.freezeAccount = true;
    if (action.requireManualReview) merged.requireManualReview = true;
    // ... 合并其他动作
  }

  return merged;
}

9. 压力测试

9.1 风控系统性能测试

测试目标:

  • 并发处理能力: 10,000 TPS
  • 响应时间: < 100ms (P99)
  • 误报率: < 1%
  • 漏报率: < 0.1%

测试脚本:

// 压力测试脚本
const { performance } = require('perf_hooks');

async function stressTestRiskSystem() {
  console.log('\n=== 风控系统压力测试 ===\n');

  const concurrency = 1000;
  const totalRequests = 100000;
  const batchSize = Math.floor(totalRequests / concurrency);

  const startTime = performance.now();
  const results = {
    total: 0,
    success: 0,
    failed: 0,
    responseTimes: [],
  };

  // 并发发送请求
  const batches = [];
  for (let i = 0; i < concurrency; i++) {
    batches.push(runBatch(batchSize, results));
  }

  await Promise.all(batches);

  const endTime = performance.now();
  const duration = (endTime - startTime) / 1000;

  // 统计结果
  const sortedTimes = results.responseTimes.sort((a, b) => a - b);
  const p50 = sortedTimes[Math.floor(sortedTimes.length * 0.5)];
  const p95 = sortedTimes[Math.floor(sortedTimes.length * 0.95)];
  const p99 = sortedTimes[Math.floor(sortedTimes.length * 0.99)];

  console.log('📊 压力测试结果:');
  console.log(`  总请求数: ${results.total}`);
  console.log(`  成功: ${results.success}`);
  console.log(`  失败: ${results.failed}`);
  console.log(`  总耗时: ${duration.toFixed(2)}秒`);
  console.log(`  TPS: ${(results.total / duration).toFixed(0)}`);
  console.log(`  响应时间 P50: ${p50.toFixed(2)}ms`);
  console.log(`  响应时间 P95: ${p95.toFixed(2)}ms`);
  console.log(`  响应时间 P99: ${p99.toFixed(2)}ms`);
}

async function runBatch(count, results) {
  for (let i = 0; i < count; i++) {
    const start = performance.now();

    try {
      // 模拟风控检查
      await performRiskCheck({
        type: 'withdrawal',
        amount: Math.random() * 100,
        userId: `user_${Math.floor(Math.random() * 10000)}`,
      });

      results.success++;
    } catch (error) {
      results.failed++;
    }

    const end = performance.now();
    results.responseTimes.push(end - start);
    results.total++;
  }
}

async function performRiskCheck(transaction) {
  // 模拟风控检查逻辑
  await new Promise(resolve => setTimeout(resolve, Math.random() * 50));
  return { riskScore: Math.random() * 100 };
}

10. 自动化测试脚本

10.1 完整测试套件

// full_risk_test_suite.js
const tests = require('./tests');

async function runFullRiskTestSuite() {
  console.log('\n' + '='.repeat(80));
  console.log('交易所风控系统 - 完整测试套件');
  console.log('='.repeat(80) + '\n');

  const results = [];

  // 1. 入金测试
  console.log('📥 [第1部分] 入金风控测试');
  results.push(await tests.deposit.testNormalDeposit());
  results.push(await tests.deposit.testSmallFrequentDeposit());
  results.push(await tests.deposit.testLargeDeposit());
  results.push(await tests.deposit.testBlacklistDeposit());
  results.push(await tests.deposit.testContractDeposit());

  // 2. 出金测试
  console.log('\n📤 [第2部分] 出金风控测试');
  results.push(await tests.withdrawal.testNormalWithdrawal());
  results.push(await tests.withdrawal.testNewAddressLargeWithdrawal());
  results.push(await tests.withdrawal.testHighFrequencyWithdrawal());
  results.push(await tests.withdrawal.testAnomalousIPWithdrawal());
  results.push(await tests.withdrawal.testMixerWithdrawal());

  // 3. 交易检测
  console.log('\n💱 [第3部分] 异常交易检测');
  results.push(await tests.trading.testSelfTrading());
  results.push(await tests.trading.testPriceManipulation());
  results.push(await tests.trading.testFrontRunning());

  // 4. 账户安全
  console.log('\n🔐 [第4部分] 账户安全测试');
  results.push(await tests.security.testBruteForce());
  results.push(await tests.security.testSessionHijacking());

  // 5. AML测试
  console.log('\n🕵️ [第5部分] 反洗钱测试');
  results.push(await tests.aml.testFundTracing());
  results.push(await tests.aml.testLayeringDetection());

  // 6. 限额测试
  console.log('\n📊 [第6部分] 限额控制测试');
  results.push(await tests.limits.testDailyLimit());
  results.push(await tests.limits.testMonthlyLimit());

  // 7. 设备指纹
  console.log('\n📱 [第7部分] 设备指纹测试');
  results.push(await tests.device.testFingerprintMatching());
  results.push(await tests.device.testNewDeviceDetection());

  // 8. 规则引擎
  console.log('\n⚙️ [第8部分] 规则引擎测试');
  results.push(await tests.rules.testRuleExecution());

  // 9. 压力测试
  console.log('\n⚡ [第9部分] 性能压力测试');
  results.push(await tests.performance.testConcurrency());

  // 生成报告
  console.log('\n' + '='.repeat(80));
  console.log('测试完成 - 生成报告');
  console.log('='.repeat(80));

  generateReport(results);
}

function generateReport(results) {
  const passed = results.filter(r => r.status === 'pass').length;
  const failed = results.filter(r => r.status === 'fail').length;
  const warnings = results.filter(r => r.status === 'warning').length;

  console.log('\n📊 测试结果统计:');
  console.log(`  总测试数: ${results.length}`);
  console.log(`  ✅ 通过: ${passed}`);
  console.log(`  ❌ 失败: ${failed}`);
  console.log(`  ⚠️  警告: ${warnings}`);
  console.log(`  成功率: ${(passed / results.length * 100).toFixed(2)}%`);

  // 高风险发现
  const highRisks = results.filter(r => r.riskLevel === 'high' || r.riskLevel === 'critical');
  if (highRisks.length > 0) {
    console.log('\n🚨 高风险发现:');
    highRisks.forEach(r => {
      console.log(`  - ${r.testName}: ${r.issue}`);
    });
  }

  // 导出报告
  const report = {
    timestamp: new Date().toISOString(),
    summary: { total: results.length, passed, failed, warnings },
    details: results,
    recommendations: generateRecommendations(results),
  };

  require('fs').writeFileSync(
    `risk_test_report_${Date.now()}.json`,
    JSON.stringify(report, null, 2)
  );

  console.log('\n✅ 报告已生成');
}

function generateRecommendations(results) {
  const recommendations = [];

  // 根据测试结果生成建议
  const failedTests = results.filter(r => r.status === 'fail');

  if (failedTests.some(t => t.category === 'blacklist')) {
    recommendations.push({
      priority: 'critical',
      category: 'compliance',
      issue: '黑名单检测失败',
      recommendation: '立即更新黑名单数据库,加强与Chainalysis等服务的集成',
    });
  }

  if (failedTests.some(t => t.category === 'withdrawal')) {
    recommendations.push({
      priority: 'high',
      category: 'withdrawal',
      issue: '出金风控存在漏洞',
      recommendation: '加强出金审核流程,增加人工审核环节',
    });
  }

  // ... 更多建议

  return recommendations;
}

// 执行测试
if (require.main === module) {
  runFullRiskTestSuite()
    .then(() => {
      console.log('\n✅ 所有测试完成');
      process.exit(0);
    })
    .catch(error => {
      console.error('\n❌ 测试失败:', error);
      process.exit(1);
    });
}

module.exports = { runFullRiskTestSuite };

总结

风控测试核心要点

  1. 入金风控

    • ✅ 黑名单地址检测
    • ✅ 小额高频检测
    • ✅ 大额入金审核
    • ✅ 合约转账识别
    • ✅ 重放攻击防护
  2. 出金风控

    • ✅ 新地址验证
    • ✅ 大额人工审核
    • ✅ 高频提现拦截
    • ✅ 异常IP/设备检测
    • ✅ 混币器地址拦截
  3. 交易监控

    • ✅ 自交易检测
    • ✅ 价格操纵识别
    • ✅ 抢先交易检测
  4. 账户安全

    • ✅ 暴力破解防护
    • ✅ 会话劫持检测
    • ✅ 设备指纹验证
  5. 反洗钱

    • ✅ 资金链追踪
    • ✅ 分层交易识别
    • ✅ 异常模式分析
  6. 性能指标

    • ✅ 处理能力: 10,000+ TPS
    • ✅ 响应时间: < 100ms
    • ✅ 误报率: < 1%
    • ✅ 可用性: 99.9%

测试环境要求:

  • 测试网络: Sepolia/Goerli
  • 主网测试: 小额自转账
  • 监控工具: Prometheus + Grafana
  • 日志系统: ELK Stack

持续优化:

  • 定期更新黑名单
  • 调整风控阈值
  • 机器学习优化
  • A/B测试新规则

最后更新: 2026-03-14
维护团队: Risk & Compliance Team

1

评论区