M2Pool Payment System v2
基于以太坊区块链的分布式支付系统
支持 充值、提现、支付 三大核心功能,实时监听链上交易,自动确认到账。
基于 Go 1.24 + Ethereum + RabbitMQ + MySQL 技术栈构建的企业级支付解决方案。
📋 目录
项目简介
M2Pool Payment System v2 是一个基于以太坊区块链的分布式支付解决方案,提供完整的数字货币充值、提现、支付功能。
核心能力
- 🔍 实时监听:订阅链上事件,实时检测 USDT 转账
- ⚡ 快速确认:20 个区块确认,约 4-5 分钟到账
- 🔒 安全可靠:私钥加密存储,签名验证机制
- 📊 高并发:支持少量并发(50-200 TPS)
- 🔄 自动重连:WebSocket 断开自动重连
- 📨 消息队列:基于 RabbitMQ 的异步通信
技术栈
| 组件 | 技术 | 版本 |
|---|---|---|
| 语言 | Go | 1.24+ |
| 区块链 | Ethereum | go-ethereum v1.16.4 |
| 消息队列 | RabbitMQ | amqp091-go v1.10.0 |
| 数据库 | MySQL | 8.0+ |
| 网络协议 | WebSocket + RPC | - |
功能特性
1. 充值功能 💰
用户转账 → 实时检测 → 待确认通知 → 区块确认 → 最终通知
特点:
- ✅ 实时检测到账
- ✅ 发送两次通知:待确认 + 最终确认
- ✅ 支持多币种(当前支持 USDT)
- ✅ 自动地址监听管理
消息流:
- 业务系统发送充值请求 → RabbitMQ
- 系统添加地址监听
- 用户转账 → 立即通知(status=2 待确认)
- 等待 20 个区块 → 最终通知(status=1 成功 / 0 失败)
2. 提现功能 💸
提现请求 → 验证余额 → 发送交易 → 等待确认 → 返回结果
特点:
- ✅ 自动余额检查
- ✅ 余额不足时使用归集钱包
- ✅ 发送一次通知:最终确认
- ✅ Gas 费用检查
消息流:
- 业务系统发送提现请求 → RabbitMQ
- 系统验证余额并发送交易
- 等待 20 个区块确认
- 返回结果(status=1 成功 / 0 失败)
3. 支付功能 💳
支付请求 → 验证余额 → 发送交易 → 等待确认 → 返回结果
特点:
- ✅ 订单关联
- ✅ 自动余额检查
- ✅ 发送一次通知:最终确认
- ✅ 支持商户收款
消息流:
- 业务系统发送支付请求(含订单ID)→ RabbitMQ
- 系统验证余额并发送交易
- 等待 20 个区块确认
- 返回结果(status=1 成功 / 0 失败)
架构设计
系统架构图
┌─────────────────────────────────────────────────────────────┐
│ 业务系统 │
│ (Web/App/API Server) │
└────────────┬────────────────────────────┬───────────────────┘
│ │
│ 请求 │ 响应
↓ ↑
┌─────────────────────────────────────────────────────────────┐
│ RabbitMQ │
│ ┌─────────┐ ┌──────────┐ ┌────────┐ │
│ │ topup │ │ withdraw │ │ pay │ 请求队列 │
│ └─────────┘ └──────────┘ └────────┘ │
│ ┌─────────┐ ┌──────────┐ ┌────────┐ │
│ │topup_ │ │withdraw_ │ │ pay_ │ 响应队列 │
│ │ resp │ │ resp │ │ resp │ │
│ └─────────┘ └──────────┘ └────────┘ │
└────────────┬────────────────────────────┬───────────────────┘
│ │
↓ ↑
┌─────────────────────────────────────────────────────────────┐
│ M2Pool Payment System v2 │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ RabbitMQ │ │ Blockchain │ │ Database │ │
│ │ Consumer │─>│ Manager │─>│ (MySQL) │ │
│ └──────────────┘ └──────┬───────┘ └──────────────┘ │
│ │ │
│ │ WebSocket + RPC │
└────────────────────────────┼─────────────────────────────────┘
│
↓
┌─────────────────────────────────────────────────────────────┐
│ 以太坊区块链网络 │
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ 新区块 │ │ USDT Transfer │ │
│ │ (NewHead) │ │ 事件 │ │
│ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────┘
核心模块
1. Blockchain Manager (internal/blockchain/)
- blockchain.go:统一的区块链接口定义
- eth/eth.go:以太坊节点实现
- 监听 USDT Transfer 事件(实时检测充值)
- 监听新区块产生(触发交易确认)
- 管理待确认交易池(UnConfirmTxs)
- 执行 ERC20 转账(提现/支付)
- 自动重连机制
- 地址统一小写处理
2. Message Queue (internal/queue/)
- rabbitmq.go:RabbitMQ 消息队列服务
- 消费 3 个请求队列(充值/提现/支付)
- 发布 3 个响应队列(交易确认结果)
- 自动重连和错误重试
- 消息持久化
3. Database (internal/db/)
- db.go:MySQL 数据库连接池
- 存储钱包私钥(加密)
- 连接池管理
- 事务支持
4. Message (internal/msg/)
- msg.go:消息结构定义
- 请求消息(TopupMsg_req, WithdrawMsg_req, PayMsg_req)
- 响应消息(TopupMsg_resp, WithdrawMsg_resp, PayMsg_resp)
- 配置结构(Config, RMQConfig, ETHConfig)
5. Utils (internal/utils/)
- utils.go:工具函数
- 数值转换(BigInt ↔ Float64)
- 哈希计算
- 加密解密
6. Crypto (internal/crypto/)
- crypto.go:加密工具
- SHA256 哈希
- 签名验证
7. Server (internal/server.go)
- 服务启动和管理
- 消息路由和处理
- 优雅关闭
快速开始
前置条件
- ✅ Go 1.24 或更高版本
- ✅ MySQL 8.0+
- ✅ RabbitMQ 3.x+
- ✅ 以太坊节点(支持 WebSocket 和 RPC)
安装步骤
# 1. 克隆项目
git clone <repository-url>
cd m2pool-payment-v2
# 2. 安装依赖
go mod download
# 3. 创建数据库
mysql -u root -p
CREATE DATABASE payment CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE payment;
-- 创建钱包余额表
CREATE TABLE `eth_balance` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`address` VARCHAR(42) NOT NULL UNIQUE,
`private_key` VARCHAR(255) NOT NULL COMMENT '加密后的私钥',
`balance` DECIMAL(20, 8) DEFAULT 0,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX `idx_address` (`address`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='ETH钱包表';
# 4. 配置文件
cd test
cp config.json config.json.backup
# 编辑 config.json,填入实际配置
# 5. 编译主程序
cd ..
go build -o m2pool-payment cmd/main.go
# 6. 运行(指定通信密钥)
./m2pool-payment -key=your_secret_key
# 或者运行测试程序
cd test
go run test.go
配置文件示例
创建 test/config.json:
{
"rmq_config": {
"sub_addr": "amqp://m2pool:m2pool@localhost:5672"
// ... 其他配置见下文
},
"eth_config": {
"rpcUrl": "http://localhost:8545",
"wsUrl": "ws://localhost:8546",
"confirmHeight": 20,
"dbConfig": {
"user": "root",
"password": "your_password",
"host": "127.0.0.1",
"port": 3306,
"database": "payment",
"maxOpenConns": 20,
"maxIdleCoons": 20,
"connMaxLife": 120
}
}
}
配置说明
配置文件结构 (config.json)
{
"rmq_config": {
"sub_addr": "amqp://username:password@localhost:5672",
"pay": {
"queue": "pay.auto.queue",
"exchange": "pay.exchange",
"routing": ["pay.auto.routing.key"]
},
"topup": {
"queue": "pay.recharge.queue",
"exchange": "pay.exchange",
"routing": ["pay.recharge.routing.key"]
},
"withdraw": {
"queue": "pay.withdraw.queue",
"exchange": "pay.exchange",
"routing": ["pay.withdraw.routing.key"]
},
"pay_resp": {
"queue": "pay.auto.return.queue",
"exchange": "pay.exchange",
"routing": ["pay.auto.return.routing.key"]
},
"topup_resp": {
"queue": "pay.recharge.return.queue",
"exchange": "pay.exchange",
"routing": ["pay.recharge.return.routing.key"]
},
"withdraw_resp": {
"queue": "pay.withdraw.return.queue",
"exchange": "pay.exchange",
"routing": ["pay.withdraw.return.routing.key"]
}
},
"eth_config": {
"rpcUrl": "http://localhost:8545",
"wsUrl": "ws://localhost:8546",
"confirmHeight": 20,
"dbConfig": {
"user": "root",
"password": "your_password",
"host": "127.0.0.1",
"port": 3306,
"database": "payment",
"maxOpenConns": 20,
"maxIdleCoons": 20,
"connMaxLife": 120
}
}
}
配置项说明
| 配置项 | 说明 | 默认值 | 必填 |
|---|---|---|---|
rmq_config.sub_addr |
RabbitMQ 连接地址 | - | ✅ |
eth_config.rpcUrl |
以太坊 RPC 地址 | - | ✅ |
eth_config.wsUrl |
以太坊 WebSocket 地址 | - | ✅ |
eth_config.confirmHeight |
确认区块数 | 20 | ✅ |
dbConfig.user |
数据库用户名 | root | ✅ |
dbConfig.password |
数据库密码 | - | ✅ |
dbConfig.database |
数据库名称 | payment | ✅ |
使用示例
1. 充值流程
步骤 1:业务系统发送充值请求
发送到 RabbitMQ 队列:pay.recharge.queue
{
"chain": "ETH",
"symbol": "USDT",
"address": "0x4e5b2e1dc63f6b91cb6cd759936495434c7e972f",
"timestamp": 1758610297,
"sign": "signature_hash"
}
步骤 2:用户转账
用户向指定地址转账 USDT
步骤 3:接收通知
从 RabbitMQ 队列:pay.recharge.return.queue 接收两次消息:
第一次(待确认):
{
"address": "0x4e5b2e1dc63f6b91cb6cd759936495434c7e972f",
"status": 2,
"chain": "ETH",
"symbol": "USDT",
"amount": 100.5,
"tx_hash": "0xabc..."
}
第二次(最终确认):
{
"address": "0x4e5b2e1dc63f6b91cb6cd759936495434c7e972f",
"status": 1,
"chain": "ETH",
"symbol": "USDT",
"amount": 100.5,
"tx_hash": "0xabc..."
}
2. 提现流程
步骤 1:业务系统发送提现请求
发送到 RabbitMQ 队列:pay.withdraw.queue
{
"queue_id": "withdraw_123",
"from_address": "0x1111...",
"to_address": "0x2222...",
"amount": 50.0,
"chain": "ETH",
"symbol": "USDT",
"timestamp": 1758610297,
"sign": "signature_hash"
}
步骤 2:系统处理
- 验证签名
- 检查余额
- 发送链上交易
- 等待确认
步骤 3:接收通知
从 RabbitMQ 队列:pay.withdraw.return.queue 接收一次消息:
{
"queue_id": "withdraw_123",
"status": 1,
"amount": 50.0,
"chain": "ETH",
"symbol": "USDT",
"tx_hash": "0xdef..."
}
3. 支付流程
步骤 1:业务系统发送支付请求
发送到 RabbitMQ 队列:pay.auto.queue
{
"queue_id": "pay_456",
"from_address": "0x1111...",
"to_address": "0x3333...",
"amount": 200.0,
"chain": "ETH",
"symbol": "USDT",
"order_id": "order_789",
"timestamp": 1758610297,
"sign": "signature_hash"
}
步骤 2:系统处理
- 验证签名
- 检查余额
- 发送链上交易
- 等待确认
步骤 3:接收通知
从 RabbitMQ 队列:pay.auto.return.queue 接收一次消息:
{
"queue_id": "pay_456",
"status": 1,
"amount": 200.0,
"chain": "ETH",
"symbol": "USDT",
"order_id": "order_789",
"tx_hash": "0xghi..."
}
API 文档
状态码说明
| 状态码 | 常量名 | 说明 | 适用场景 |
|---|---|---|---|
0 |
STATUS_FAILED | 交易失败 | 交易被回退或执行失败 |
1 |
STATUS_SUCCESS | 交易成功 | 交易成功并已确认 |
2 |
STATUS_PENDING | 待确认 | 交易已检测到,等待区块确认 |
3 |
STATUS_VERIFY_FAILED | 验证失败 | 签名验证失败 |
消息结构
充值请求 (TopupMsg_req)
| 字段 | 类型 | 说明 | 必填 |
|---|---|---|---|
| chain | string | 链名称 (ETH) | ✅ |
| symbol | string | 币种 (USDT) | ✅ |
| address | string | 充值地址 | ✅ |
| timestamp | uint64 | 时间戳 | ✅ |
| sign | string | 签名 | ✅ |
充值响应 (TopupMsg_resp)
| 字段 | 类型 | 说明 |
|---|---|---|
| address | string | 充值地址 |
| status | int | 状态码 (0/1/2/3) |
| chain | string | 链名称 |
| symbol | string | 币种 |
| amount | float64 | 金额 |
| tx_hash | string | 交易哈希 |
提现请求 (WithdrawMsg_req)
| 字段 | 类型 | 说明 | 必填 |
|---|---|---|---|
| queue_id | string | 队列ID | ✅ |
| from_address | string | 转出地址 | ✅ |
| to_address | string | 转入地址 | ✅ |
| amount | float64 | 金额 | ✅ |
| chain | string | 链名称 | ✅ |
| symbol | string | 币种 | ✅ |
| timestamp | uint64 | 时间戳 | ✅ |
| sign | string | 签名 | ✅ |
提现响应 (WithdrawMsg_resp)
| 字段 | 类型 | 说明 |
|---|---|---|
| queue_id | string | 队列ID |
| status | int | 状态码 |
| amount | float64 | 金额 |
| chain | string | 链名称 |
| symbol | string | 币种 |
| tx_hash | string | 交易哈希 |
支付请求 (PayMsg_req)
| 字段 | 类型 | 说明 | 必填 |
|---|---|---|---|
| queue_id | string | 队列ID | ✅ |
| from_address | string | 付款地址 | ✅ |
| to_address | string | 收款地址(商户) | ✅ |
| amount | float64 | 金额 | ✅ |
| chain | string | 链名称 | ✅ |
| symbol | string | 币种 | ✅ |
| order_id | string | 订单ID | ✅ |
| timestamp | uint64 | 时间戳 | ✅ |
| sign | string | 签名 | ✅ |
支付响应 (PayMsg_resp)
| 字段 | 类型 | 说明 |
|---|---|---|
| queue_id | string | 队列ID |
| status | int | 状态码 |
| amount | float64 | 金额 |
| chain | string | 链名称 |
| symbol | string | 币种 |
| order_id | string | 订单ID |
| tx_hash | string | 交易哈希 |
开发指南
项目结构
m2pool-payment-v2/
├── cmd/ # 主程序入口
│ └── main.go # 程序入口,解析命令行参数
├── internal/ # 内部包(不对外暴露)
│ ├── server.go # 服务启动和管理
│ ├── blockchain/ # 区块链交互模块
│ │ ├── blockchain.go # 统一的区块链接口定义
│ │ ├── eth/ # 以太坊实现
│ │ │ └── eth.go # USDT 监听、转账、确认
│ │ └── tron/ # TRON 实现(待开发)
│ ├── crypto/ # 加密工具
│ │ └── crypto.go # SHA256、签名验证
│ ├── db/ # 数据库
│ │ └── db.go # MySQL 连接池管理
│ ├── msg/ # 消息定义
│ │ └── msg.go # 请求/响应结构体定义
│ ├── queue/ # 消息队列
│ │ ├── rabbitmq.go # RabbitMQ 客户端封装
│ │ └── README.md # RabbitMQ 使用文档
│ └── utils/ # 工具函数
│ └── utils.go # 类型转换、格式化
├── test/ # 测试和示例
│ ├── test.go # 测试程序(独立运行)
│ └── config.json # 配置文件
├── go.mod # Go 模块定义
├── go.sum # 依赖版本锁定
└── README.md # 项目文档(本文件)
代码统计
| 模块 | 文件 | 代码行数 | 说明 |
|---|---|---|---|
| eth | eth.go | ~700 | 以太坊核心逻辑 |
| queue | rabbitmq.go | ~350 | RabbitMQ 封装 |
| server | server.go | ~300 | 服务管理 |
| msg | msg.go | ~130 | 消息定义 |
| blockchain | blockchain.go | ~70 | 接口定义 |
| 其他 | - | ~200 | 工具、数据库等 |
| 总计 | - | ~1750 | - |
开发环境设置
方式一:Docker 快速启动(推荐)
# 1. 启动 MySQL
docker run -d \
--name m2pool-mysql \
-p 3306:3306 \
-e MYSQL_ROOT_PASSWORD=Lzx2021@! \
-e MYSQL_DATABASE=payment \
-v mysql_data:/var/lib/mysql \
mysql:8.0
# 2. 启动 RabbitMQ
docker run -d \
--name m2pool-rabbitmq \
-p 5672:5672 \
-p 15672:15672 \
-e RABBITMQ_DEFAULT_USER=m2pool \
-e RABBITMQ_DEFAULT_PASS=m2pool \
-v rabbitmq_data:/var/lib/rabbitmq \
rabbitmq:3-management
# 3. 访问 RabbitMQ 管理界面
# http://localhost:15672
# 用户名: m2pool
# 密码: m2pool
方式二:本地安装
# Ubuntu/Debian
sudo apt update
sudo apt install golang-1.24 mysql-server rabbitmq-server
# CentOS/RHEL
sudo yum install golang mysql-server rabbitmq-server
# macOS
brew install go mysql rabbitmq
以太坊节点选择
选项 1:使用公共节点服务(推荐)
# Infura(免费层每天 100,000 请求)
RPC: https://mainnet.infura.io/v3/YOUR_API_KEY
WS: wss://mainnet.infura.io/ws/v3/YOUR_API_KEY
# Alchemy(免费层每秒 25 请求)
RPC: https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY
WS: wss://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY
选项 2:自建节点
# 使用 Geth
geth --http --http.addr 0.0.0.0 --http.port 8545 \
--ws --ws.addr 0.0.0.0 --ws.port 8546 \
--http.api eth,net,web3 --ws.api eth,net,web3
# 或使用 Erigon(更轻量)
erigon --http --ws
选项 3:测试网络
# Goerli 测试网(免费)
RPC: https://goerli.infura.io/v3/YOUR_API_KEY
WS: wss://goerli.infura.io/ws/v3/YOUR_API_KEY
# Sepolia 测试网(推荐)
RPC: https://sepolia.infura.io/v3/YOUR_API_KEY
WS: wss://sepolia.infura.io/ws/v3/YOUR_API_KEY
数据库表结构
-- 钱包余额表(必须)
CREATE TABLE `eth_balance` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`address` VARCHAR(42) NOT NULL UNIQUE COMMENT '钱包地址(小写)',
`private_key` VARCHAR(255) NOT NULL COMMENT '加密后的私钥',
`balance` DECIMAL(20, 8) DEFAULT 0 COMMENT 'USDT余额',
`eth_balance` DECIMAL(20, 18) DEFAULT 0 COMMENT 'ETH余额(用于Gas)',
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX `idx_address` (`address`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='ETH钱包表';
-- 插入归集钱包示例
INSERT INTO `eth_balance` (`address`, `private_key`, `balance`, `eth_balance`)
VALUES ('归集钱包', 'encrypted_private_key_here', 10000.00, 1.0);
-- 交易记录表(可选,用于审计)
CREATE TABLE `transactions` (
`id` BIGINT AUTO_INCREMENT PRIMARY KEY,
`tx_hash` VARCHAR(66) NOT NULL UNIQUE COMMENT '交易哈希',
`from_address` VARCHAR(42) NOT NULL COMMENT '发送地址',
`to_address` VARCHAR(42) NOT NULL COMMENT '接收地址',
`amount` DECIMAL(20, 8) NOT NULL COMMENT '金额',
`symbol` VARCHAR(10) NOT NULL COMMENT '币种',
`chain` VARCHAR(10) NOT NULL COMMENT '链名称',
`tx_type` TINYINT NOT NULL COMMENT '0=充值,1=提现,2=支付',
`status` TINYINT NOT NULL COMMENT '0=失败,1=成功,2=待确认',
`block_height` BIGINT COMMENT '区块高度',
`queue_id` VARCHAR(50) COMMENT '队列ID',
`order_id` VARCHAR(50) COMMENT '订单ID',
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`confirmed_at` TIMESTAMP NULL COMMENT '确认时间',
INDEX `idx_tx_hash` (`tx_hash`),
INDEX `idx_from` (`from_address`),
INDEX `idx_to` (`to_address`),
INDEX `idx_status` (`status`),
INDEX `idx_order_id` (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='交易记录表';
重要说明
⚠️ 私钥安全:
- 数据库中存储的是加密后的私钥
- 需要实现真实的加密/解密逻辑
- 当前代码中的解密逻辑是占位代码,需要替换为实际的加密算法(如 AES-256)
⚠️ 归集钱包:
- 必须在数据库中配置归集钱包
- 地址字段填写
"归集钱包"字符串 - 保持足够的 USDT 和 ETH 余额
部署指南
Docker 部署(推荐)
# Dockerfile
FROM golang:1.24-alpine AS builder
WORKDIR /app
COPY . .
RUN go mod download
RUN CGO_ENABLED=0 GOOS=linux go build -o m2pool-payment cmd/main.go
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /app/m2pool-payment .
COPY --from=builder /app/test/config.json .
CMD ["./m2pool-payment"]
# docker-compose.yml
version: '3.8'
services:
payment:
build: .
depends_on:
- mysql
- rabbitmq
environment:
- CONFIG_PATH=/root/config.json
volumes:
- ./config.json:/root/config.json
restart: unless-stopped
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_PASSWORD}
MYSQL_DATABASE: payment
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
rabbitmq:
image: rabbitmq:3-management
environment:
RABBITMQ_DEFAULT_USER: m2pool
RABBITMQ_DEFAULT_PASS: m2pool
ports:
- "5672:5672"
- "15672:15672"
volumes:
- rabbitmq_data:/var/lib/rabbitmq
volumes:
mysql_data:
rabbitmq_data:
# 启动
docker-compose up -d
# 查看日志
docker-compose logs -f payment
# 停止
docker-compose down
系统服务部署
# 1. 创建系统用户
sudo useradd -r -s /bin/false m2pool
# 2. 创建服务文件
sudo nano /etc/systemd/system/m2pool-payment.service
[Unit]
Description=M2Pool Payment System
After=network.target mysql.service rabbitmq-server.service
[Service]
Type=simple
User=m2pool
WorkingDirectory=/opt/m2pool-payment
ExecStart=/opt/m2pool-payment/m2pool-payment
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
# 3. 启动服务
sudo systemctl daemon-reload
sudo systemctl enable m2pool-payment
sudo systemctl start m2pool-payment
# 4. 查看状态
sudo systemctl status m2pool-payment
# 5. 查看日志
sudo journalctl -u m2pool-payment -f
性能指标
处理能力
| 指标 | 数值 | 说明 |
|---|---|---|
| 充值检测 TPS | 500-1000 | 每秒处理交易数 |
| 提现/支付 TPS | 200-500 | 包含数据库查询 |
| 消息发送 TPS | 5000-10000 | RabbitMQ 发送速率 |
| 并发地址监听 | 10000+ | 同时监听的地址数量 |
响应时间
| 操作 | 响应时间 | 说明 |
|---|---|---|
| 充值待确认通知 | < 3 秒 | 检测到交易后 |
| 充值最终确认 | 4-5 分钟 | 20 个区块 |
| 提现执行 | < 5 秒 | 发送交易 |
| 提现最终确认 | 4-5 分钟 | 20 个区块 |
资源占用(4核8G环境)
| 资源 | 使用量 | 峰值 |
|---|---|---|
| CPU | 5-15% | 30% |
| 内存 | 100-300 MB | 500 MB |
| 网络带宽 | 1-5 MB/s | 10 MB/s |
| 数据库连接 | 5-10 | 20 |
常见问题
Q1: 为什么充值会收到两次通知?
A: 这是设计特性!
- 第一次(status=2):检测到交易,提醒用户"正在确认"
- 第二次(status=1/0):交易确认,通知最终结果
业务系统应该:
- status=2:显示进度,不增加余额
- status=1:增加余额
Q2: 提现/支付为什么只有一次通知?
A: 因为是系统主动发起的交易,用户已经知道在处理中,不需要额外的待确认通知。
Q3: 如何处理交易失败?
A: 系统会返回 status=0 的消息,业务系统应该:
- 充值失败:不增加余额,提示用户联系客服
- 提现失败:退回用户余额
- 支付失败:恢复订单状态,退回余额
Q4: 确认需要多长时间?
A: 配置为 20 个区块确认,以太坊约 12 秒/块:
- 理论时间:20 × 12 = 240 秒(4 分钟)
- 实际时间:4-5 分钟(包括网络延迟)
Q5: 如何保证私钥安全?
A:
- 私钥加密存储:数据库中存储加密后的私钥
- 临时解密:仅在转账时临时解密,用完立即释放
- 访问控制:数据库限制访问权限
- 建议方案:
- 使用 AES-256 加密私钥
- 使用 HSM(硬件安全模块)
- 使用云服务商的 KMS(密钥管理服务)
- 使用环境变量传递解密密钥
⚠️ 重要:当前代码中的解密逻辑(eth.go 第 467 行)是占位代码,生产环境必须替换为真实的加密算法!
// ❌ 当前代码(占位)
privateKey := encryptedKey + address + ""
// ✅ 应该改为(示例)
privateKey := AES256Decrypt(encryptedKey, decodeKey)
Q6: 余额不足时如何处理?
A: 系统会自动使用归集钱包转账。归集钱包应该:
- 保持足够的余额
- 定期从各个钱包归集资金
- 设置余额告警
Q7: 支持哪些网络?
A:
- ✅ 以太坊主网(Mainnet)
- ✅ 以太坊测试网(Goerli, Sepolia)
- ✅ 私有链
- ⚠️ 需要修改 USDT 合约地址
Q8: Gas 费用谁承担?
A:
- 充值:用户承担(用户自己发送交易)
- 提现:平台承担(系统发送交易)
- 支付:平台承担(系统发送交易)
建议:提现/支付时从用户金额中扣除 Gas 费
Q9: 如何监控系统状态?
A: 建议监控:
- 待确认交易数量:
len(UnConfirmTxs) - Channel 使用率:
len(chainEventCh)/cap(chainEventCh) - RabbitMQ 连接状态
- WebSocket 连接状态
- 数据库连接池状态
Q10: 如何处理重复消息?
A: RabbitMQ 可能重复投递消息,业务系统应该:
- 使用
tx_hash作为唯一标识 - 实现幂等性处理
- 数据库添加唯一索引
Q11: 支持的 USDT 合约地址是什么?
A: 当前配置的合约地址:
- 以太坊主网:
0xdAC17F958D2ee523a2206206994597C13D831ec7 - 测试网:需要部署测试 ERC20 合约
- 私有链:需要部署自己的 ERC20 合约
修改合约地址位置:internal/blockchain/eth/eth.go 第 99 行
Q12: 新区块监听的作用是什么?
A: 新区块监听确保交易及时确认:
- 问题:如果只依赖 USDT Transfer 事件,在长时间无 USDT 转账时,待确认交易无法被确认
- 解决:监听新区块产生,每个新区块都检查待确认交易
- 效果:交易在达到第 20 个区块后,下一个区块就会被确认
Q13: Channel 缓冲区设置多大?
A: 当前设置为 1000:
chainEventCh := make(chan any, 1000)
- 轻量负载(<50 TPS):100 足够
- 中等负载(50-200 TPS):500-1000 推荐
- 高负载(>200 TPS):2000+ 或优化架构
监控 Channel 使用率,避免满载丢消息。
安全建议
🔒 安全检查清单
- 私钥加密存储
- 使用 HTTPS/WSS 连接
- 签名验证所有请求
- 限制 API 访问频率
- 定期备份数据库
- 监控异常交易
- 设置余额告警
- 使用防火墙限制访问
- 定期更新依赖包
- 日志脱敏处理
⚠️ 重要提示
-
私钥管理
- ❌ 不要在代码中硬编码私钥
- ❌ 不要在日志中打印私钥
- ✅ 使用环境变量或密钥管理服务
-
网络安全
- ✅ 使用 VPN 或专线连接区块链节点
- ✅ RabbitMQ 启用 TLS
- ✅ MySQL 限制远程访问
-
资金安全
- ✅ 设置单笔交易限额
- ✅ 异常交易人工审核
- ✅ 多签钱包(建议)
- ✅ 冷热钱包分离
监控告警
推荐监控指标
// 添加监控指标
type Metrics struct {
TotalTransactions int64 // 总交易数
PendingTransactions int // 待确认交易数
FailedTransactions int64 // 失败交易数
ChannelUsage int // Channel使用率
LastBlockHeight uint64 // 最新区块高度
}
// 定期上报
go func() {
ticker := time.NewTicker(1 * time.Minute)
for range ticker.C {
log.Printf("📊 待确认交易: %d", len(e.UnConfirmTxs))
log.Printf("📊 监听地址数: %d", countAddresses())
log.Printf("📊 Channel使用率: %d%%", len(ch)*100/cap(ch))
}
}()
告警规则建议
| 指标 | 阈值 | 告警级别 |
|---|---|---|
| 待确认交易数 | > 100 | ⚠️ 警告 |
| 待确认交易数 | > 500 | 🔴 严重 |
| Channel 使用率 | > 80% | ⚠️ 警告 |
| Channel 使用率 | > 95% | 🔴 严重 |
| 交易失败率 | > 5% | ⚠️ 警告 |
| WebSocket 断线 | 重连 > 3次/小时 | ⚠️ 警告 |
故障排查
问题:充值检测不到
可能原因:
- 地址未加入监听列表
- WebSocket 连接断开
- 合约地址配置错误
- 网络 ID 不匹配
排查步骤:
# 检查日志
grep "新增钱包监听消息" logs/payment.log
# 检查订阅状态
grep "订阅成功" logs/payment.log
# 测试节点连接
curl -X POST -H "Content-Type: application/json" \
--data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' \
http://localhost:8545
问题:提现/支付失败
可能原因:
- 余额不足
- Gas 费不足
- 私钥错误
- nonce 冲突
排查步骤:
# 检查错误日志
grep "转账失败" logs/payment.log
# 检查余额
grep "余额" logs/payment.log
# 检查私钥
grep "查询私钥" logs/payment.log
问题:消息未返回
可能原因:
- RabbitMQ 连接断开
- Channel 阻塞
- 交易未确认
排查步骤:
# 检查 RabbitMQ 连接
rabbitmqctl list_connections
# 检查队列状态
rabbitmqctl list_queues
# 检查待确认交易
# 添加 HTTP 接口查询 UnConfirmTxs
注意事项
⚠️ 生产环境部署前必须修改
1. 私钥加密实现(重要!)
位置:internal/blockchain/eth/eth.go 第 467 行
当前代码(占位):
privateKey := encryptedKey + address + ""
必须改为(示例):
// 使用 AES-256-GCM 加密
import "crypto/aes"
import "crypto/cipher"
func (e *ETHNode) decodePrivatekey(address string) string {
// 从数据库查询加密密钥
encryptedKey := queryFromDB(address)
// 使用 AES 解密
privateKey := AESDecrypt(encryptedKey, e.decodeKey)
return privateKey
}
2. USDT 合约地址验证
确认你的以太坊网络与合约地址匹配:
| 网络 | Chain ID | USDT 合约地址 |
|---|---|---|
| 主网 | 1 | 0xdAC17F958D2ee523a2206206994597C13D831ec7 ✅ |
| Goerli | 5 | 需要部署测试合约 |
| Sepolia | 11155111 | 需要部署测试合约 |
| 私有链 | 自定义 | 需要部署自己的合约 |
修改位置:internal/blockchain/eth/eth.go 第 99 行
3. 消息签名密钥
当前密钥:9f3c7a12(测试用)
生产环境:
# 生成强密钥
openssl rand -hex 32
# 启动时传入
./m2pool-payment -key=your_production_secret_key
4. 数据库密码安全
- ❌ 不要在代码中硬编码密码
- ✅ 使用环境变量
- ✅ 使用配置管理工具(如 Vault)
贡献指南
欢迎贡献代码!请遵循以下步骤:
- Fork 本项目
- 创建功能分支 (
git checkout -b feature/AmazingFeature) - 提交更改 (
git commit -m 'Add some AmazingFeature') - 推送到分支 (
git push origin feature/AmazingFeature) - 开启 Pull Request
代码规范
- ✅ 使用
gofmt格式化代码 - ✅ 遵循 Go 命名规范
- ✅ 添加必要的注释
- ✅ 所有地址统一小写处理
- ✅ 使用状态码常量(不要硬编码数字)
- ✅ 添加错误处理和日志
- ✅ 更新相关文档
提交规范
# 功能
feat: 添加 BTC 网络支持
# 修复
fix: 修复充值消息重复发送问题
# 文档
docs: 更新 API 文档
# 性能
perf: 优化交易确认性能
# 重构
refactor: 重构数据库连接池
命令行参数
# 启动程序
./m2pool-payment -key=your_secret_key
# 参数说明
-key string
通信密钥,用于消息签名验证 (默认: "m2pool")
签名验证算法
// 生成签名
hash := SHA256(hex(timestamp) + secret_key)
sign := hex.EncodeToString(hash)
// 示例
timestamp = 1758610297
secret_key = "9f3c7a12"
hash = SHA256("696a2d6929" + "9f3c7a12")
sign = "219b3b3935f3d56db7eacd32aae84fa06df95806373d6fc4ed6e9b35ffb17f2d"
运行日志示例
启动日志
========================================
🚀 M2Pool Payment System Starting...
========================================
✅ 配置加载成功: RPC=http://10.168.3.236:18545, WS=ws://10.168.3.236:18546
✅ 区块链服务初始化完成
✅ RabbitMQ服务初始化完成: amqp://m2pool:m2pool@localhost:5672
✅ RabbitMQ 监听启动完成
========================================
🎉 所有服务启动完成!
========================================
🔍 ETH 开始监听 USDT Transfer 事件...
🔍 开始监听新区块...
✅ 订阅成功
✅ 新区块订阅成功
充值日志
📥 [RMQ] 收到充值请求: Chain=ETH, Symbol=USDT, Address=0x123...
📨 [链上] 充值待确认: Address=0x123..., Amount=100.50, TxHash=0xabc...
📤 [RMQ] 发送充值响应: Address=0x123..., Status=2, TxHash=0xabc...
✅ [链上] 充值确认: Address=0x123..., Amount=100.50, TxHash=0xabc..., Status=1
📤 [RMQ] 发送充值响应: Address=0x123..., Status=1, TxHash=0xabc...
提现日志
📥 [RMQ] 收到提现请求: QueueId=w123, From=0x111..., To=0x222..., Amount=50.00 USDT
✅ [链上] 提现确认: QueueId=w123, Amount=50.00, TxHash=0xdef..., Status=1
📤 [RMQ] 发送提现响应: QueueId=w123, Status=1, TxHash=0xdef...
路线图
✅ v2.0 (已完成)
- 以太坊 USDT 支持
- 充值/提现/支付功能
- RabbitMQ 集成
- 双重监听机制
- 自动交易确认
- 地址统一规范
- Gas 费用检查
- Panic 恢复机制
🚧 v2.1 (开发中)
- 支持更多 ERC20 代币(USDC, DAI)
- 交易记录持久化
- HTTP API 接口
- 私钥真实加密实现
- 性能优化(读写锁、缓存)
📋 v2.2 (计划中)
- 支持 TRON 网络(TRC20-USDT)
- 支持 BTC 网络
- 多签钱包支持
- 管理后台界面
- 实时监控面板
- 告警系统
🔮 v3.0 (规划中)
- 微服务架构拆分
- 水平扩展支持
- 分布式事务
- 高可用集群部署
- Kubernetes 支持
核心特性详解
1. 双重监听机制 🎯
系统同时监听两种链上事件:
① USDT Transfer 事件监听
// 检测 USDT 转账,用于充值检测和交易确认触发
e.WsClient.SubscribeFilterLogs(query, e.USDT.LogsChan)
② 新区块头监听
// 每个新区块触发交易确认检查,确保及时确认
e.WsClient.SubscribeNewHead(e.Ctx, headers)
2. 智能交易确认 ⚡
事件驱动 + 区块驱动:
- Transfer 事件到达时立即检查
- 每个新区块产生时也检查
- 确保交易在第 20 个区块后立即确认
3. 地址统一规范 🔡
所有以太坊地址统一转换为小写:
- 存储时转换
- 比较时转换
- 查询时转换
避免大小写不一致导致的匹配失败。
4. 并发安全设计 🔒
sync.Map用于高并发地址监听sync.Mutex保护共享数据结构- Channel 缓冲区防止阻塞
- Goroutine panic 恢复机制
5. 余额智能管理 💰
自动归集钱包切换:
用户钱包余额 < 转账金额
↓
自动使用归集钱包
↓
确保交易成功
6. Gas 费用检查 ⛽
转账前自动检查:
- USDT 余额是否足够
- ETH 余额是否足够支付 Gas
- 预估 Gas 价格
相关文档
贡献指南
欢迎贡献代码!请遵循以下步骤:
提交流程
- Fork 本项目
- 创建功能分支 (
git checkout -b feature/AmazingFeature) - 提交更改 (
git commit -m 'feat: Add some AmazingFeature') - 推送到分支 (
git push origin feature/AmazingFeature) - 开启 Pull Request
代码规范
- ✅ 使用
gofmt格式化代码 - ✅ 遵循 Go 命名规范
- ✅ 添加必要的注释
- ✅ 所有地址统一小写处理
- ✅ 使用状态码常量(不要硬编码数字)
- ✅ 添加错误处理和日志
- ✅ 更新相关文档
Commit 规范
# 新功能
feat: 添加 BTC 网络支持
# Bug 修复
fix: 修复充值消息重复发送问题
# 文档更新
docs: 更新 API 文档
# 性能优化
perf: 优化交易确认性能
# 代码重构
refactor: 重构数据库连接池
# 测试
test: 添加单元测试
技术支持
如有问题,欢迎通过以下方式联系:
- 📧 Email: support@example.com
- 💬 Issues: GitHub Issues
- 📖 文档: 项目 Wiki
许可证
MIT License
Copyright (c) 2025 M2Pool Team
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
⭐ 如果这个项目对你有帮助,请给一个 Star!⭐
Made with ❤️ by M2Pool Team