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 | - |
项目架构
系统架构图
┌─────────────────────────────────────────────────────────────┐
│ 业务系统 │
│ (Web/App/API Server) │
└────────────┬────────────────────────────┬───────────────────┘
│ │
│ 请求 │ 响应
↓ ↑
┌─────────────────────────────────────────────────────────────┐
│ RabbitMQ │
│ ┌─────────┐ ┌──────────┐ ┌────────┐ ┌────────┐ │
│ │ topup │ │ withdraw │ │ pay │ │ remove │ 请求队列 │
│ └─────────┘ └──────────┘ └────────┘ └────────┘ │
│ ┌─────────┐ ┌──────────┐ ┌────────┐ ┌────────┐ │
│ │topup_ │ │withdraw_ │ │ pay_ │ │remove_ │ 响应队列 │
│ │ resp │ │ resp │ │ resp │ │ resp │ │
│ └─────────┘ └──────────┘ └────────┘ └────────┘ │
└────────────┬────────────────────────────┬───────────────────┘
│ │
↓ ↑
┌─────────────────────────────────────────────────────────────┐
│ M2Pool Payment System v2 │
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Server │ │ ListenServer │ │
│ │ (路由层) │────────>│ (消息层) │ │
│ └──────────────┘ └──────┬───────┘ │
│ │ │
│ ┌────────────┴────────────┐ │
│ ↓ ↓ │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ Blockchain │ │ Database │ │
│ │ Manager │ │ (MySQL/SQLite) │ │
│ │ (ETH Node) │ │ │ │
│ └────────┬─────────┘ └──────────────────┘ │
│ │ │
│ │ WebSocket + RPC │
└───────────────────┼─────────────────────────────────────────┘
│
↓
┌─────────────────────────────────────────────────────────────┐
│ 以太坊区块链网络 │
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ 新区块 │ │ ETH/USDT │ │
│ │ (NewHead) │ │ Transfer事件 │ │
│ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────┘
消息流转架构
业务系统 → RabbitMQ → Server → ListenServer → Blockchain Node
↑ ↓
│ (记录到数据库)
│ │
│ (监听链上事件)
│ │
│ ←── 返回响应 ───
│ │
└─── 发送响应 ───────────┘
核心组件说明
-
Server (server.go): 服务入口和消息路由
- 接收 RabbitMQ 消息
- 验证消息签名
- 路由消息到 ListenServer
-
ListenServer (listen/): 消息中间层
- 管理消息状态(TopupMsgs, WithdrawMsgs, PayMsgs, RemoveMsgs)
- 与区块链节点双向通信
- 持久化消息到 SQLite
- 转发响应到 RabbitMQ
-
Blockchain Node (blockchain/eth/): 区块链交互层
- 监听链上事件(ETH 和 USDT 转账)
- 执行转账交易
- 管理待确认交易(UnConfirmTxs)
- 确认交易状态
核心模块
1. Server (internal/server.go)
- 服务入口和启动管理
- 消息签名验证
- 消息路由到 ListenServer
- RabbitMQ 消息处理回调
- 响应消息转发到 RabbitMQ
2. ListenServer (internal/listen/)
- listen.go:消息中间层,连接 RabbitMQ 和区块链节点
- RmqMsgIn():处理来自 RabbitMQ 的消息
- 充值请求 → 写入数据库 → 添加到 TopupMsgs → 转发到区块链节点
- 提现请求 → 写入数据库 → 添加到 WithdrawMsgs → 转发到区块链节点
- 支付请求 → 写入数据库 → 添加到 PayMsgs → 转发到区块链节点
- 移除监听 → 写入数据库 → 添加到 RemoveMsgs → 转发到区块链节点
- NetMsgIn():处理来自区块链节点的响应
- 充值响应 → 更新数据库 → 转发到 RabbitMQ
- 提现响应 → 更新数据库 → 转发到 RabbitMQ
- 支付响应 → 更新数据库 → 转发到 RabbitMQ
- 消息状态管理(TopupMsgs, WithdrawMsgs, PayMsgs, RemoveMsgs)
- 消息查找(根据 address 或 queue_id)
- RmqMsgIn():处理来自 RabbitMQ 的消息
3. Blockchain Manager (internal/blockchain/)
- blockchain.go:统一的区块链接口定义和路由
- eth/eth.go:以太坊节点实现
- Listen():监听链上事件
- 监听 ETH Transfer 事件
- 监听 USDT Transfer 事件
- 监听新区块产生(用于交易确认)
- ListenMsg():监听来自 ListenServer 的消息
- 充值请求 → 添加到钱包监听
- 提现请求 → 执行转账
- 支付请求 → 执行转账
- 移除监听 → 移除钱包监听
- Transfer():执行转账交易
- 管理待确认交易池(UnConfirmTxs)
- 交易确认逻辑
- Listen():监听链上事件
4. Message Queue (internal/queue/)
- rabbitmq.go:RabbitMQ 消息队列服务
- 消费 4 个请求队列(充值/提现/支付/移除监听)
- 发布 4 个响应队列(交易确认结果)
- 自动重连和错误重试
- 消息持久化
5. Database (internal/db/)
- mysql.go:MySQL 数据库连接池
- 存储钱包私钥(加密)
- 连接池管理
- 事务支持
- sqlite.go:SQLite 本地存储
- 存储消息请求和响应记录
- 存储未确认交易信息
- 提供事务支持
6. Message (internal/msg/)
- msg.go:消息结构定义
- 请求消息(TopupMsg_req, WithdrawMsg_req, PayMsg_req, RemoveListenMsg_req)
- 响应消息(TopupMsg_resp, WithdrawMsg_resp, PayMsg_resp, RemoveListenMsg_resp)
- config.go:配置结构
- Config, RmqConfig, ETHConfig, MysqlConfig
7. Utils (internal/utils/)
- utils.go:工具函数
- 数值转换(BigInt ↔ Float64)
- 数组/切片操作
- 字符串处理
8. Crypto (internal/crypto/)
- crypto.go:加密工具
- SHA256 哈希
- 签名验证
9. Logger (internal/logger/)
- transaction_logger.go:交易日志记录
- 记录所有交易操作的详细日志
- 记录 RMQ 与 Listen 之间的所有通信消息
- 按地址分文件存储,自动日志轮转和压缩
日志分类
① 交易日志(按地址分文件)
- 充值日志:按
address命名文件 - 提现日志:按
fromAddress命名文件 - 支付日志:按
fromAddress命名文件 - ETH 节点日志:
ethnode.log
② RMQ ↔ Listen 通信日志(按地址分文件)
- 充值请求/响应:按
address命名文件 - 提现请求/响应:按
fromAddress命名文件 - 支付请求/响应:按
fromAddress命名文件 - 移除监听请求/响应:按
address命名文件
日志格式
RMQ ↔ Listen 通信日志格式:[msg-Type]: time-fromaddress-toaddress-chain-symbol-amount
- TopupReq:
[TopupReq]: 2006-01-02 15:04:05--0xabc...-ETH-USDT-0 - WithdrawReq:
[WithdrawReq]: 2006-01-02 15:04:05-0x123...-0xabc...-ETH-USDT-100.500000 - PayReq:
[PayReq]: 2006-01-02 15:04:05-0x123...-0xabc...-ETH-USDT-50.250000 - TopupResp:
[TopupResp]: 2006-01-02 15:04:05-0xdef...-0xabc...-ETH-USDT-200.000000 - WithdrawResp:
[WithdrawResp]: 2006-01-02 15:04:05-0x123...-0xabc...-ETH-USDT-100.500000 - PayResp:
[PayResp]: 2006-01-02 15:04:05-0x123...-0xabc...-ETH-USDT-50.250000
日志轮转机制
- 单文件超过 1MB 自动压缩为
.gz格式 - 后台异步压缩,不影响性能
- 压缩后的文件名格式:
{address}_{timestamp}.log.gz - 自动创建新的日志文件继续写入
日志存储位置
所有日志文件存储在 logs/ 目录下:
logs/
├── 0x123...abc.log # 地址 0x123...abc 的日志
├── 0x456...def.log # 地址 0x456...def 的日志
├── 0x123...abc_20240102_120000.log.gz # 压缩的历史日志
├── ethnode.log # ETH 节点日志
└── ...
项目结构
m2pool-payment-v2/
├── cmd/ # 主程序入口
│ └── main.go # 程序入口,解析命令行参数 (-key)
├── internal/ # 内部包(不对外暴露)
│ ├── server.go # 服务启动和管理、消息路由
│ ├── blockchain/ # 区块链交互模块
│ │ ├── blockchain.go # 统一的区块链接口定义和路由
│ │ └── eth/ # 以太坊实现
│ │ ├── eth.go # ETH节点接口定义
│ │ └── eth_prv.go # ETH节点实现(监听、转账、确认)
│ ├── listen/ # 消息中间层
│ │ ├── listen.go # ListenServer 定义和消息查找
│ │ └── listen_prv.go # ListenServer 实现(消息处理)
│ ├── queue/ # 消息队列
│ │ └── rabbitmq.go # RabbitMQ 客户端封装
│ ├── db/ # 数据库
│ │ ├── mysql.go # MySQL 连接池管理
│ │ └── sqlite.go # SQLite 本地存储
│ ├── msg/ # 消息定义
│ │ ├── msg.go # 请求/响应结构体定义
│ │ └── config.go # 配置结构定义
│ ├── crypto/ # 加密工具
│ │ └── crypto.go # SHA256、签名验证
│ ├── logger/ # 日志记录
│ │ └── transaction_logger.go # 交易日志
│ ├── constant/ # 常量定义
│ │ └── constant.go # 状态码等常量
│ └── utils/ # 工具函数
│ └── utils.go # 类型转换、格式化
├── public/ # 公共资源
│ ├── eth.sql # ETH钱包表结构(MySQL)
│ ├── msg.sql # 消息表结构(SQLite)
│ └── eth_mysql.sql # MySQL 完整建表脚本
├── bin/ # 构建和启动脚本
│ ├── build.sh # 编译脚本
│ └── start.sh # 启动脚本
├── test/ # 测试和示例
│ ├── test.go # 测试程序(独立运行)
│ └── config.json # 配置文件示例
├── 流程.txt # 业务流程说明文档
├── go.mod # Go 模块定义
├── go.sum # 依赖版本锁定
└── README.md # 项目文档(本文件)
核心特性详解
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 价格
功能特性
1. 充值功能 💰
完整流程:
业务系统 → RabbitMQ → Server → ListenServer → 写数据库 → 添加到TopupMsgs
↓
Blockchain Node → 添加到钱包 → 记录到钱包数据库
↓
新区块产生 → 监听链上事件 → 对比交易
↓
消息中的to = 区块交易中的to(充值)
↓
返回消息 → ListenServer → 修改数据库
↓
记录到UnConfirmTxs → 返回RabbitMQ → 发送待确认消息(status=2)
↓
新区块产生 → 读取UnConfirmTxs → 对比交易高度
↓
符合确认条件(当前高度 >= 交易高度 + 确认高度)
↓
修改钱包数据 → 返回消息 → ListenServer → 修改数据库
↓
返回RabbitMQ → 发送最终确认消息(status=1/0)
特点:
- ✅ 实时检测到账
- ✅ 发送两次通知:待确认(status=2) + 最终确认(status=1/0)
- ✅ 支持 ETH 和 USDT
- ✅ 自动地址监听管理
- ✅ 钱包余额自动更新
消息流:
- 业务系统发送充值请求 → RabbitMQ
topup.queue - Server 验证签名 → 转发到 ListenServer
- ListenServer → 写入 SQLite 数据库 → 添加到 TopupMsgs → 转发到 Blockchain Node
- Blockchain Node → 添加地址到钱包监听 → 写入 MySQL 钱包数据库
- 监听链上事件 → 检测到充值交易 → 匹配地址 → 返回 TopupMsg_resp(status=2)→ 记录到 UnConfirmTxs
- ListenServer → 更新数据库 → 发送到 RabbitMQ
topup_resp.queue(第一次通知) - 新区块确认 → 达到确认高度 → 更新钱包余额 → 返回 TopupMsg_resp(status=1/0)
- ListenServer → 更新数据库 → 发送到 RabbitMQ
topup_resp.queue(第二次通知)
2. 提现功能 💸
完整流程:
业务系统 → RabbitMQ → Server → ListenServer → 写数据库 → 添加到WithdrawMsgs
↓
Blockchain Node → 记录到数据库
↓
开始转账 → 校验余额 → 发送交易
↓
转账结果返回 → ListenServer → 修改相关状态
↓
新区块产生 → 监听链上事件 → 对比交易
↓
消息中的from、to、amount = 区块交易中的from、to、amount
↓
返回消息 → ListenServer → 修改数据库状态
↓
记录到UnConfirmTxs → 返回RabbitMQ → 发送消息(status=2)
↓
新区块产生 → 读取UnConfirmTxs → 对比交易高度
↓
符合确认条件(当前高度 >= 交易高度 + 确认高度)
↓
修改钱包数据 → 返回消息 → ListenServer → 修改数据库
↓
返回RabbitMQ → 发送最终确认消息(status=1/0)
特点:
- ✅ 自动余额检查
- ✅ 余额不足时使用归集钱包
- ✅ 发送两次通知:待确认(status=2) + 最终确认(status=1/0)
- ✅ Gas 费用检查
- ✅ 支持 ETH 和 USDT
消息流:
- 业务系统发送提现请求 → RabbitMQ
withdraw.queue - Server 验证签名 → 转发到 ListenServer
- ListenServer → 写入 SQLite 数据库 → 添加到 WithdrawMsgs → 转发到 Blockchain Node
- Blockchain Node → 验证余额 → 发送交易 → 记录到数据库 → 返回 WithdrawMsg_resp(status=2)
- ListenServer → 更新数据库 → 发送到 RabbitMQ
withdraw_resp.queue(第一次通知) - 监听链上事件 → 检测到提现交易 → 匹配交易 → 记录到 UnConfirmTxs
- 新区块确认 → 达到确认高度 → 更新钱包余额 → 返回 WithdrawMsg_resp(status=1/0)
- ListenServer → 更新数据库 → 发送到 RabbitMQ
withdraw_resp.queue(第二次通知)
3. 支付功能 💳
完整流程:
业务系统 → RabbitMQ → Server → ListenServer → 写数据库 → 添加到PayMsgs
↓
Blockchain Node → 记录到数据库
↓
开始转账 → 校验余额 → 发送交易
↓
转账结果返回 → ListenServer → 修改相关状态
↓
新区块产生 → 监听链上事件 → 对比交易
↓
消息中的from、to、amount = 区块交易中的from、to、amount
↓
返回消息 → ListenServer → 修改数据库状态
↓
记录到UnConfirmTxs → 返回RabbitMQ → 发送消息(status=2)
↓
新区块产生 → 读取UnConfirmTxs → 对比交易高度
↓
符合确认条件(当前高度 >= 交易高度 + 确认高度)
↓
修改钱包数据 → 返回消息 → ListenServer → 修改数据库
↓
返回RabbitMQ → 发送最终确认消息(status=1/0)
特点:
- ✅ 支持批量支付(一次请求多笔转账)
- ✅ 自动余额检查
- ✅ 发送两次通知:待确认(status=2) + 最终确认(status=1/0)
- ✅ Gas 费用检查
- ✅ 支持 ETH 和 USDT
消息流:
- 业务系统发送支付请求 → RabbitMQ
pay.queue(可包含多笔交易) - Server 验证签名 → 转发到 ListenServer
- ListenServer → 写入 SQLite 数据库 → 添加到 PayMsgs → 转发到 Blockchain Node
- Blockchain Node → 验证余额 → 逐个发送交易 → 记录到数据库 → 返回 PayData(status=2)
- ListenServer → 更新数据库 → 发送到 RabbitMQ
pay_resp.queue(第一次通知) - 监听链上事件 → 检测到支付交易 → 匹配交易 → 记录到 UnConfirmTxs
- 新区块确认 → 达到确认高度 → 更新钱包余额 → 返回 PayData(status=1/0)
- ListenServer → 更新数据库 → 发送到 RabbitMQ
pay_resp.queue(第二次通知)
4. 移除监听功能 🗑️
完整流程:
业务系统 → RabbitMQ → Server → ListenServer → 写数据库 → 添加到RemoveMsgs
↓
Blockchain Node → 移除钱包监听
↓
返回消息 → ListenServer → 修改数据库
↓
返回RabbitMQ → 发送响应消息(status=1/0)
特点:
- ✅ 支持移除充值监听
- ✅ 支持移除提现/支付监听
- ✅ 实时生效
快速开始
前置条件
- ✅ 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. 准备配置文件
cp bin/config.json ./config.json
# 编辑 config.json,填入实际配置
# 6. 初始化数据库
# MySQL: 执行 public/eth_mysql.sql
# SQLite: 程序会自动创建(如果不存在)
# 7. 编译主程序
go build -o m2pool-payment cmd/main.go
# 8. 运行(指定通信密钥)
./m2pool-payment -key=your_secret_key
# 或者使用启动脚本
cd bin
chmod +x build.sh start.sh
./build.sh
./start.sh your_secret_key
配置文件示例
创建 test/config.json:
{
"net": ["ETH"],
"rmq_config": {
"sub_addr": "amqp://m2pool:m2pool@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"]
},
"remove": {
"queue": "pay.remove.queue",
"exchange": "pay.exchange",
"routing": ["pay.remove.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"]
},
"remove_resp": {
"queue": "pay.remove.return.queue",
"exchange": "pay.exchange",
"routing": ["pay.remove.return.routing.key"]
}
},
"msg_config": {
"sqlite_path": "./msg.db"
},
"mysql_config": {
"wallet": {
"user": "root",
"password": "your_password",
"host": "127.0.0.1",
"port": 3306,
"database": "payment",
"maxOpenConns": 20,
"maxIdleConns": 10,
"connMaxLife": 120000000000
}
},
"eth_config": {
"rpc_url": "http://localhost:8545",
"ws_url": "ws://localhost:8546",
"confirm_height": 20,
"sqlite_path": "./eth.db"
}
}
配置说明
配置项说明
| 配置项 | 说明 | 默认值 | 必填 |
|---|---|---|---|
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..."
}
常见问题
Q1: 为什么所有功能都会收到两次通知?
A: 这是设计特性!
- 第一次(status=2):检测到交易/开始处理,提醒用户"正在确认"
- 第二次(status=1/0):交易确认,通知最终结果
业务系统应该:
- status=2:显示进度,不修改余额或订单状态
- status=1:增加/减少余额,更新订单状态
- status=0:交易失败,不修改余额,提示错误
Q2: 为什么需要两次通知?
A:
- 实时反馈:用户能立即知道交易已被检测到
- 状态追踪:业务系统可以追踪交易从待确认到最终确认的完整流程
- 用户体验:提供更好的用户交互体验
Q3: 如何处理交易失败?
A: 系统会返回 status=0 的消息,业务系统应该:
- 充值失败:不增加余额,提示用户联系客服
- 提现失败:退回用户余额
- 支付失败:恢复订单状态,退回余额
Q4: 确认需要多长时间?
A: 配置为 20 个区块确认,以太坊约 12 秒/块:
- 理论时间:20 × 12 = 240 秒(4 分钟)
- 实际时间:4-5 分钟(包括网络延迟)
Q5: 如何保证私钥安全?
A:
- 私钥加密存储:数据库中存储加密后的私钥
- 临时解密:仅在转账时临时解密,用完立即释放
- 访问控制:数据库限制访问权限
- 建议方案:
- 使用 AES-256 加密私钥
- 使用 HSM(硬件安全模块)
- 使用云服务商的 KMS(密钥管理服务)
- 使用环境变量传递解密密钥
⚠️ 重要:当前代码中的解密逻辑是占位代码,生产环境必须替换为真实的加密算法!
Q6: 余额不足时如何处理?
A: 系统会自动使用归集钱包转账。归集钱包应该:
- 保持足够的余额
- 定期从各个钱包归集资金
- 设置余额告警
Q7: 支持哪些网络?
A:
- ✅ 以太坊主网(Mainnet)
- ✅ 以太坊测试网(Goerli, Sepolia)
- ✅ 私有链
- ⚠️ 需要修改 USDT 合约地址
Q8: Gas 费用谁承担?
A:
- 充值:用户承担(用户自己发送交易)
- 提现:平台承担(系统发送交易)
- 支付:平台承担(系统发送交易)
建议:提现/支付时从用户金额中扣除 Gas 费
部署指南
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
性能测试
压测环境配置
| 配置项 | 规格 | 说明 |
|---|---|---|
| CPU | 4核 2.4GHz | Intel/AMD x64 |
| 内存 | 8GB DDR4 | 系统 + 缓存 |
| 网络 | 100Mbps | 公网带宽 |
| 数据库 | MySQL 8.0 | 本地部署 |
| 消息队列 | RabbitMQ 3.x | 本地部署 |
| 区块链节点 | Infura/Alchemy | 云端服务 |
预估压测结果
1. 充值功能压测
| 并发用户数 | TPS | 响应时间 | 成功率 | 说明 |
|---|---|---|---|---|
| 10 | 50-80 | < 2秒 | 99.9% | 轻量负载,性能优秀 |
| 50 | 200-300 | 2-5秒 | 99.5% | 中等负载,性能良好 |
| 100 | 300-500 | 5-10秒 | 99.0% | 高负载,性能稳定 |
| 200 | 400-600 | 10-20秒 | 98.5% | 极限负载,性能下降 |
| 500+ | 500-800 | > 20秒 | < 98% | 超负载,不建议 |
特点:
- ✅ 充值检测性能优秀(纯监听,无链上操作)
- ✅ 内存占用低(仅地址监听)
- ✅ 网络带宽消耗小
2. 提现功能压测
| 并发用户数 | TPS | 响应时间 | 成功率 | 说明 |
|---|---|---|---|---|
| 5 | 20-30 | 3-8秒 | 99.8% | 轻量负载,性能优秀 |
| 20 | 50-80 | 8-15秒 | 99.0% | 中等负载,性能良好 |
| 50 | 80-120 | 15-30秒 | 98.5% | 高负载,性能稳定 |
| 100 | 100-150 | 30-60秒 | 97.0% | 极限负载,性能下降 |
| 200+ | 120-200 | > 60秒 | < 95% | 超负载,不建议 |
特点:
- ⚠️ 受链上交易确认时间影响(4-5分钟)
- ⚠️ 数据库查询和私钥解密开销
- ⚠️ Gas费用波动影响成功率
3. 支付功能压测
| 并发用户数 | TPS | 响应时间 | 成功率 | 说明 |
|---|---|---|---|---|
| 5 | 20-30 | 3-8秒 | 99.8% | 轻量负载,性能优秀 |
| 20 | 50-80 | 8-15秒 | 99.0% | 中等负载,性能良好 |
| 50 | 80-120 | 15-30秒 | 98.5% | 高负载,性能稳定 |
| 100 | 100-150 | 30-60秒 | 97.0% | 极限负载,性能下降 |
| 200+ | 120-200 | > 60秒 | < 95% | 超负载,不建议 |
特点:
- ⚠️ 与提现功能性能相近
- ⚠️ 订单ID处理增加少量开销
- ⚠️ 商户地址验证开销
系统资源占用
内存使用情况
| 并发数 | 基础内存 | 峰值内存 | 说明 |
|---|---|---|---|
| 10 | 100MB | 150MB | 轻量运行 |
| 50 | 200MB | 350MB | 中等负载 |
| 100 | 300MB | 500MB | 高负载 |
| 200 | 500MB | 800MB | 极限负载 |
CPU使用情况
| 并发数 | 基础CPU | 峰值CPU | 说明 |
|---|---|---|---|
| 10 | 5% | 15% | 轻量运行 |
| 50 | 15% | 35% | 中等负载 |
| 100 | 25% | 50% | 高负载 |
| 200 | 40% | 70% | 极限负载 |
网络带宽使用
| 并发数 | 上行带宽 | 下行带宽 | 说明 |
|---|---|---|---|
| 10 | 1Mbps | 2Mbps | 轻量运行 |
| 50 | 3Mbps | 5Mbps | 中等负载 |
| 100 | 5Mbps | 8Mbps | 高负载 |
| 200 | 8Mbps | 12Mbps | 极限负载 |
性能瓶颈分析
1. 主要瓶颈
| 瓶颈类型 | 影响程度 | 解决方案 |
|---|---|---|
| 区块链确认时间 | 🔴 高 | 使用Layer2或侧链 |
| 数据库查询 | 🟡 中 | 添加索引,使用缓存 |
| 私钥解密 | 🟡 中 | 优化加密算法 |
| 网络延迟 | 🟡 中 | 使用CDN,就近部署 |
| Gas费用波动 | 🟡 中 | 动态Gas价格调整 |
2. 优化建议
短期优化(1-2周):
- ✅ 添加数据库索引
- ✅ 优化SQL查询语句
- ✅ 增加Channel缓冲区大小
- ✅ 使用连接池复用
中期优化(1-2月):
- 🔄 实现Redis缓存
- 🔄 优化私钥加密算法
- 🔄 添加负载均衡
- 🔄 实现读写分离
长期优化(3-6月):
- 🚀 支持Layer2网络
- 🚀 实现微服务架构
- 🚀 添加水平扩展
- 🚀 使用分布式数据库
压测工具推荐
1. 消息队列压测
# 使用 RabbitMQ 压测工具
docker run --rm -it \
-e RABBITMQ_HOST=localhost \
-e RABBITMQ_PORT=5672 \
-e RABBITMQ_USER=m2pool \
-e RABBITMQ_PASS=m2pool \
rabbitmq-perf-test:latest \
--rate 100 --time 60 --queue pay.withdraw.queue
2. 数据库压测
# 使用 sysbench 压测 MySQL
sysbench mysql \
--mysql-host=localhost \
--mysql-port=3306 \
--mysql-user=root \
--mysql-password=password \
--mysql-db=payment \
--tables=1 \
--table-size=10000 \
--threads=10 \
--time=60 \
run
3. 系统监控
# 监控系统资源
htop # CPU和内存监控
iotop # 磁盘IO监控
nethogs # 网络带宽监控
生产环境建议
1. 推荐配置
| 环境 | CPU | 内存 | 并发数 | 说明 |
|---|---|---|---|---|
| 开发环境 | 2核 | 4GB | < 10 | 功能测试 |
| 测试环境 | 4核 | 8GB | < 50 | 压力测试 |
| 生产环境 | 8核 | 16GB | < 100 | 稳定运行 |
| 高可用环境 | 16核 | 32GB | < 200 | 负载均衡 |
2. 监控指标
// 关键监控指标
type Metrics struct {
TotalTransactions int64 // 总交易数
PendingTransactions int // 待确认交易数
FailedTransactions int64 // 失败交易数
ChannelUsage int // Channel使用率
LastBlockHeight uint64 // 最新区块高度
MemoryUsage int64 // 内存使用量
CPUUsage float64 // CPU使用率
}
3. 告警阈值
| 指标 | 警告阈值 | 严重阈值 | 说明 |
|---|---|---|---|
| 待确认交易数 | > 100 | > 500 | 交易积压 |
| Channel使用率 | > 80% | > 95% | 消息积压 |
| 交易失败率 | > 5% | > 10% | 系统异常 |
| 内存使用率 | > 80% | > 95% | 内存不足 |
| CPU使用率 | > 70% | > 90% | CPU过载 |
业务流程详解
根据 流程.txt 文档,系统的完整业务流程如下:
充值流程
-
RabbitMQ → ListenServer
- 接收充值请求
- 写入 SQLite 数据库(
topup_req_msg表) - 添加到 TopupMsgs 内存映射
-
ListenServer → Blockchain Node
- 转发充值请求到区块链节点
- 区块链节点接收后:
- 添加地址到钱包监听
- 记录到 MySQL 钱包数据库(
ETH_wallets表)
-
Blockchain Node → ListenServer(监听阶段)
- 新区块产生时,监听链上交易
- 对比消息中的
to地址和区块交易中的to地址 - 如果匹配(充值检测):
- 返回 TopupMsg_resp(status=2 待确认)
- 记录到 UnConfirmTxs(待确认交易池)
- ListenServer 接收后:
- 修改数据库状态
- 返回 RabbitMQ → 发送第一次通知(status=2)
-
Blockchain Node → ListenServer(确认阶段)
- 新区块产生时,读取 UnConfirmTxs 数据
- 对比每个交易的高度:
当前高度 >= 交易高度 + 确认高度 - 符合确认条件后:
- 修改钱包数据(更新余额)
- 返回 TopupMsg_resp(status=1 成功 / 0 失败)
- ListenServer 接收后:
- 修改数据库状态
- 返回 RabbitMQ → 发送第二次通知(status=1/0)
提现/支付流程
-
RabbitMQ → ListenServer
- 接收提现/支付请求
- 写入 SQLite 数据库(
withdraw_req_msg或pay_req_msg表) - 添加到 WithdrawMsgs 或 PayMsgs 内存映射
-
ListenServer → Blockchain Node
- 转发提现/支付请求到区块链节点
- 区块链节点接收后:
- 记录到数据库
- 开始转账流程:
- 校验余额
- 发送交易
- 返回响应(status=2 待确认)
- ListenServer 接收后:
- 修改相关状态
- 返回 RabbitMQ → 发送第一次通知(status=2)
-
Blockchain Node → ListenServer(监听阶段)
- 新区块产生时,监听链上交易
- 对比消息中的
from、to、amount和区块交易中的对应字段 - 如果匹配(提现/支付检测):
- 返回响应(status=2 待确认)
- 记录到 UnConfirmTxs
- ListenServer 接收后:
- 修改数据库状态
- 返回 RabbitMQ → 发送第一次通知(status=2)
-
Blockchain Node → ListenServer(确认阶段)
- 新区块产生时,读取 UnConfirmTxs 数据
- 对比每个交易的高度:
当前高度 >= 交易高度 + 确认高度 - 符合确认条件后:
- 修改钱包数据(更新余额)
- 返回响应(status=1 成功 / 0 失败)
- ListenServer 接收后:
- 修改数据库状态
- 返回 RabbitMQ → 发送第二次通知(status=1/0)
重要修复说明
🔧 已修复的问题
1. 服务启动问题
问题:部分关键服务未启动
修复:
- 添加
rmqServer.Start()启动 RabbitMQ 服务 - 添加
messageServer.RmqMsgIn()启动消息接收监听 - 添加
messageServer.NetMsgIn()启动网络消息监听 - 添加
blockChainServer.Listen()启动区块链监听 - 添加
blockChainServer.ListenMsg()启动区块链消息监听
2. 错误处理优化
问题:使用 log.Fatalf 导致程序非正常退出
修复:
- 将非关键错误的
log.Fatalf改为log.Printf - 添加错误恢复机制,确保程序稳定运行
3. 变量和字段名称修复
问题:部分变量和字段名称存在拼写错误
修复:
- 修复
Heihgt→Height - 修复数据库字段名称不匹配问题
- 修复 mutex 解锁错误
4. 逻辑错误修复
问题:交易确认高度判断逻辑错误
修复:
- 修复确认高度判断条件:
now_height >= tx.Height+e.ConfirmHeight - 修复变量重复声明问题
贡献指南
欢迎贡献代码!请遵循以下步骤:
提交流程
- 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: 添加单元测试
许可证
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