以太坊交易跟踪器 - 一个高性能的以太坊区块链交易监控和历史数据扫描系统。
graph TB
subgraph "外部服务"
ETH[以太坊节点<br/>RPC服务]
DB[(MySQL数据库)]
end
subgraph "ETH Tracker 系统"
subgraph "API层"
HTTP[HTTP服务器<br/>:8081]
SWAGGER[Swagger文档<br/>/swagger/index.html]
end
subgraph "业务逻辑层"
TH[交易处理器<br/>TransactionHandler]
HH[健康检查处理器<br/>HealthHandler]
end
subgraph "核心服务层"
SYNC[实时同步器<br/>Synchronizer]
SCANNER[历史扫描器<br/>HistoryScanner]
REPO[数据仓库<br/>Repository]
end
subgraph "基础设施层"
CONFIG[配置管理<br/>Config]
LOGGER[日志系统<br/>Zap Logger]
CLIENT[以太坊客户端<br/>go-ethereum]
end
end
subgraph "用户接口"
USER[用户/客户端]
CURL[curl命令]
BROWSER[浏览器]
end
%% 连接关系
USER --> HTTP
CURL --> HTTP
BROWSER --> SWAGGER
HTTP --> TH
HTTP --> HH
SWAGGER --> HTTP
TH --> SYNC
TH --> SCANNER
TH --> REPO
SYNC --> CLIENT
SCANNER --> CLIENT
REPO --> DB
CLIENT --> ETH
CONFIG --> SYNC
CONFIG --> SCANNER
CONFIG --> HTTP
LOGGER --> TH
LOGGER --> SYNC
LOGGER --> SCANNER
%% 样式
classDef external fill:#e1f5fe
classDef api fill:#f3e5f5
classDef business fill:#e8f5e8
classDef core fill:#fff3e0
classDef infra fill:#fce4ec
classDef user fill:#f1f8e9
class ETH,DB external
class HTTP,SWAGGER api
class TH,HH business
class SYNC,SCANNER,REPO core
class CONFIG,LOGGER,CLIENT infra
class USER,CURL,BROWSER user
- Go 1.24+
- MySQL 8.0+
- 以太坊节点访问权限(Infura、Alchemy等)
- 克隆项目
git clone <repository-url>
cd eth-tracker- 安装依赖
go mod download- 配置数据库
# 创建数据库
mysql -u root -p -e "CREATE DATABASE eth_tracker;"
# 运行数据库迁移
go run ./cmd/migrate -config ./configs/config.yaml- 配置文件
# 复制配置模板
cp configs/config.yaml.example configs/config.yaml
# 编辑配置文件,设置数据库连接和RPC端点
vim configs/config.yaml- 启动服务
go run ./cmd/server -config ./configs/config.yaml- 验证服务
# 健康检查
curl http://localhost:8081/api/v1/health
# 访问Swagger文档
open http://localhost:8081/swagger/index.html- 克隆项目
git clone <repository-url>
cd eth-tracker- 配置环境变量
# 复制环境变量模板
cp .env.example .env
# 编辑环境变量
vim .env- 启动服务
# 启动所有服务(包括MySQL)
docker-compose up -d
# 查看日志
docker-compose logs -f eth-tracker- 验证部署
# 健康检查
curl http://localhost:8081/api/v1/health
# 访问Swagger文档
open http://localhost:8081/swagger/index.html# 构建镜像
docker build -t eth-tracker .
# 运行容器
docker run -d \
--name eth-tracker \
-p 8081:8081 \
-e DB_HOST=your-mysql-host \
-e DB_USER=your-mysql-user \
-e DB_PASSWORD=your-mysql-password \
-e MAINNET_RPC_URL=your-mainnet-rpc-url \
eth-tracker| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| 数据库配置 | |||
database.host |
string | localhost |
MySQL主机地址 |
database.port |
int | 3306 |
MySQL端口 |
database.user |
string | root |
MySQL用户名 |
database.password |
string | - | MySQL密码 |
database.name |
string | eth_tracker |
数据库名称 |
database.max_open_conns |
int | 25 |
最大连接数 |
database.max_idle_conns |
int | 5 |
最大空闲连接数 |
database.conn_max_lifetime |
int | 300 |
连接最大生存时间(秒) |
| 服务器配置 | |||
server.host |
string | 0.0.0.0 |
服务器监听地址 |
server.port |
int | 8081 |
服务器端口 |
server.read_timeout |
int | 30 |
读取超时时间(秒) |
server.write_timeout |
int | 30 |
写入超时时间(秒) |
server.cors.enabled |
bool | true |
是否启用CORS |
server.cors.allowed_origins |
[]string | ["*"] |
允许的源 |
server.swagger.enabled |
bool | true |
是否启用Swagger |
| 网络配置 | |||
networks.mainnet.rpc_url |
string | - | 主网RPC地址 |
networks.mainnet.start_block |
int | 0 |
开始同步的区块号 |
networks.mainnet.chain_id |
int | 1 |
链ID |
networks.mainnet.confirmation_blocks |
int | 12 |
确认区块数 |
networks.sepolia.rpc_url |
string | - | Sepolia测试网RPC地址 |
networks.sepolia.start_block |
int | 0 |
开始同步的区块号 |
networks.sepolia.chain_id |
int | 11155111 |
链ID |
| 网络映射配置 | |||
network_mapping.id_to_name.1 |
string | mainnet |
网络ID 1 对应的网络名称 |
network_mapping.id_to_name.2 |
string | sepolia |
网络ID 2 对应的网络名称 |
network_mapping.id_to_name.3 |
string | polygon |
网络ID 3 对应的网络名称 |
network_mapping.id_to_name.4 |
string | mumbai |
网络ID 4 对应的网络名称 |
network_mapping.default_network |
string | mainnet |
无效网络ID时的默认网络 |
| 同步配置 | |||
sync.batch_size |
int | 100 |
批处理大小 |
sync.worker_count |
int | 5 |
工作协程数 |
sync.block_interval |
int | 3 |
区块同步间隔(秒) |
| 日志配置 | |||
log.level |
string | info |
日志级别 |
log.format |
string | json |
日志格式 |
log.output |
string | stdout |
日志输出 |
系统支持通过环境变量覆盖配置文件中的设置:
export DB_HOST=localhost
export DB_PORT=3306
export DB_USER=eth_tracker
export DB_PASSWORD=your_secure_password
export DB_NAME=eth_tracker
export DB_MAX_OPEN_CONNS=25
export DB_MAX_IDLE_CONNS=5
export DB_CONN_MAX_LIFETIME=300export SERVER_HOST=0.0.0.0
export SERVER_PORT=8081
export SERVER_READ_TIMEOUT=30
export SERVER_WRITE_TIMEOUT=30
export CORS_ENABLED=true
export SWAGGER_ENABLED=true# 主网配置
export MAINNET_RPC_URL=https://mainnet.infura.io/v3/your-project-id
export MAINNET_START_BLOCK=18000000
export MAINNET_CHAIN_ID=1
export MAINNET_CONFIRMATION_BLOCKS=12
# Sepolia测试网配置
export SEPOLIA_RPC_URL=https://sepolia.infura.io/v3/your-project-id
export SEPOLIA_START_BLOCK=4000000
export SEPOLIA_CHAIN_ID=11155111
export SEPOLIA_CONFIRMATION_BLOCKS=3
# Polygon配置
export POLYGON_RPC_URL=https://polygon-mainnet.infura.io/v3/your-project-id
export POLYGON_START_BLOCK=45000000
export POLYGON_CHAIN_ID=137
# Mumbai测试网配置
export MUMBAI_RPC_URL=https://polygon-mumbai.infura.io/v3/your-project-id
export MUMBAI_START_BLOCK=35000000
export MUMBAI_CHAIN_ID=80001export SYNC_BATCH_SIZE=100
export SYNC_WORKER_COUNT=5
export SYNC_BLOCK_INTERVAL=3export LOG_LEVEL=info
export LOG_FORMAT=json
export LOG_OUTPUT=stdout网络映射功能允许API同时支持网络名称和网络ID作为参数,提供更灵活的使用方式:
- mainnet (主网) → ID: 1
- sepolia (Sepolia测试网) → ID: 2
- polygon (Polygon主网) → ID: 3
- mumbai (Mumbai测试网) → ID: 4
在API调用中,您可以使用网络名称(如 network=sepolia)或对应的网络ID(如 network=2),两种方式完全等价。
# .env 文件
DB_HOST=mysql
DB_PORT=3306
DB_USER=eth_tracker
DB_PASSWORD=secure_password_123
DB_NAME=eth_tracker
SERVER_PORT=8081
SWAGGER_ENABLED=true
MAINNET_RPC_URL=https://mainnet.infura.io/v3/your-project-id
SEPOLIA_RPC_URL=https://sepolia.infura.io/v3/your-project-id
LOG_LEVEL=info
SYNC_BATCH_SIZE=50
SYNC_WORKER_COUNT=3| HTTP状态码 | 错误代码 | 说明 | 示例 |
|---|---|---|---|
| 400 Bad Request | |||
| 400 | missing_address |
地址参数缺失 | {"error": "missing_address", "message": "地址参数不能为空"} |
| 400 | invalid_address |
地址格式无效 | {"error": "invalid_address", "message": "无效的地址格式"} |
| 400 | missing_scan_id |
扫描ID参数缺失 | {"error": "missing_scan_id", "message": "扫描ID参数不能为空"} |
| 400 | invalid_request |
请求参数无效 | {"error": "invalid_request", "message": "请求参数无效"} |
| 400 | invalid_page_size |
分页参数无效 | {"error": "invalid_page_size", "message": "页面大小必须在1-100之间"} |
| 404 Not Found | |||
| 404 | scan_not_found |
扫描任务未找到 | {"error": "scan_not_found", "message": "未找到扫描任务"} |
| 404 | network_not_found |
网络配置未找到 | {"error": "network_not_found", "message": "网络配置未找到"} |
| 404 | address_not_found |
地址未找到交易记录 | {"error": "address_not_found", "message": "该地址暂无交易记录"} |
| 409 Conflict | |||
| 409 | scan_already_exists |
扫描任务已存在 | {"error": "scan_already_exists", "message": "该地址已有正在进行的扫描任务"} |
| 409 | scan_already_running |
扫描任务正在运行 | {"error": "scan_already_running", "message": "扫描任务正在运行中"} |
| 500 Internal Server Error | |||
| 500 | internal_error |
内部服务器错误 | {"error": "internal_error", "message": "服务器内部错误"} |
| 500 | database_error |
数据库错误 | {"error": "database_error", "message": "数据库操作失败"} |
| 500 | rpc_error |
RPC连接错误 | {"error": "rpc_error", "message": "以太坊节点连接失败"} |
| 500 | scan_start_failed |
扫描启动失败 | {"error": "scan_start_failed", "message": "扫描启动失败"} |
{
"error": "error_code",
"message": "详细错误描述"
}# 检查服务状态
curl -X GET "http://localhost:8081/api/v1/health" \
-H "accept: application/json"
# 响应示例
{
"status": "healthy",
"timestamp": "2024-01-15T10:30:00Z",
"version": "1.0.0",
"uptime": "2h30m15s"
}# 获取最新交易(默认20条)
curl -X GET "http://localhost:8081/api/v1/transactions" \
-H "accept: application/json"
# 分页获取交易
curl -X GET "http://localhost:8081/api/v1/transactions?page=2&size=50" \
-H "accept: application/json"
# 响应示例
{
"data": [
{
"id": 12345,
"hash": "0x1234567890abcdef...",
"block_number": 18500000,
"block_hash": "0xabcdef1234567890...",
"transaction_index": 45,
"from": "0x742d35Cc6634C0532925a3b8D4C9db96C4C4C4C4",
"to": "0x8ba1f109551bD432803012645Hac136c22C501e5",
"value": "1000000000000000000",
"gas": 21000,
"gas_price": "20000000000",
"gas_used": 21000,
"status": 1,
"network": "mainnet",
"created_at": "2024-01-15T10:25:30Z"
}
],
"total": 1500,
"page": 1,
"size": 20,
"total_pages": 75
}# 获取指定地址的交易记录
curl -X GET "http://localhost:8081/api/v1/transactions/0x742d35Cc6634C0532925a3b8D4C9db96C4C4C4C4" \
-H "accept: application/json"
# 分页获取地址交易
curl -X GET "http://localhost:8081/api/v1/transactions/0x742d35Cc6634C0532925a3b8D4C9db96C4C4C4C4?page=1&size=10" \
-H "accept: application/json"
# 指定网络获取地址交易(使用网络名称)
curl -X GET "http://localhost:8081/api/v1/transactions/0x742d35Cc6634C0532925a3b8D4C9db96C4C4C4C4?network=sepolia" \
-H "accept: application/json"
# 指定网络获取地址交易(使用网络ID)
curl -X GET "http://localhost:8081/api/v1/transactions/0x742d35Cc6634C0532925a3b8D4C9db96C4C4C4C4?network=2" \
-H "accept: application/json"
# 响应格式同上# 获取地址统计信息
curl -X GET "http://localhost:8081/api/v1/transactions/0x742d35Cc6634C0532925a3b8D4C9db96C4C4C4C4/stats" \
-H "accept: application/json"
# 指定网络的统计(使用网络名称)
curl -X GET "http://localhost:8081/api/v1/transactions/0x742d35Cc6634C0532925a3b8D4C9db96C4C4C4C4/stats?network=sepolia" \
-H "accept: application/json"
# 指定网络的统计(使用网络ID)
curl -X GET "http://localhost:8081/api/v1/transactions/0x742d35Cc6634C0532925a3b8D4C9db96C4C4C4C4/stats?network=2" \
-H "accept: application/json"
# 响应示例
{
"address": "0x742d35Cc6634C0532925a3b8D4C9db96C4C4C4C4",
"network": "mainnet",
"total_transactions": 156,
"total_sent": 45,
"total_received": 111,
"first_transaction": "2023-06-15T08:30:00Z",
"last_transaction": "2024-01-15T10:25:30Z",
"first_block_number": 17500000,
"last_block_number": 18500000
}# 启动历史扫描任务(使用网络名称)
curl -X POST "http://localhost:8081/api/v1/transactions/scan" \
-H "accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"address": "0x742d35Cc6634C0532925a3b8D4C9db96C4C4C4C4",
"network": "sepolia",
"start_block": 5000000,
"end_block": 5100000,
"batch_size": 1000
}'
# 启动历史扫描任务(使用网络ID)
curl -X POST "http://localhost:8081/api/v1/transactions/scan" \
-H "accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"address": "0x742d35Cc6634C0532925a3b8D4C9db96C4C4C4C4",
"network": "2",
"start_block": 5000000,
"end_block": 5100000,
"batch_size": 1000
}'
#### 使用地址路径启动历史扫描(查询参数)
```bash
# 通过地址路径 + 查询参数启动扫描(支持 network 名称或 ID)
curl -X POST "http://localhost:8081/api/v1/transactions/0x742d35Cc6634C0532925a3b8D4C9db96C4C4C4C4/scan?network=sepolia&start_block=5000000&end_block=5100000&batch_size=100" \
-H "accept: application/json"说明:
batch_size为每批处理的区块数量,默认 100;start_block默认为 0,end_block默认为最新区块。
# 响应示例
{
"address": "0x742d35Cc6634C0532925a3b8D4C9db96C4C4C4C4",
"network": "sepolia",
"start_block": 5000000,
"end_block": 5100000,
"status": "running",
"message": "扫描任务已启动"
}# 获取所有扫描任务进度
curl -X GET "http://localhost:8081/api/v1/transactions/scan-progress" \
-H "accept: application/json"
# 获取特定扫描任务进度
curl -X GET "http://localhost:8081/api/v1/transactions/scan/scan_1728467905_abc123/progress" \
-H "accept: application/json"
# 响应示例
{
"id": "scan_1728467905_abc123",
"address": "0x742d35Cc6634C0532925a3b8D4C9db96C4C4C4C4",
"network": "sepolia",
"start_block": 5000000,
"end_block": 5100000,
"current_block": 5050000,
"total_blocks": 100000,
"scanned_transactions": 1250,
"status": "running",
"progress": 50.0,
"created_at": "2024-01-15T09:45:05Z",
"updated_at": "2024-01-15T09:50:15Z"
}# 停止特定扫描任务
curl -X POST "http://localhost:8081/api/v1/transactions/scan/scan_1728467905_abc123/stop" \
-H "accept: application/json"
# 停止地址相关的扫描任务
curl -X POST "http://localhost:8081/api/v1/transactions/0x742d35Cc6634C0532925a3b8D4C9db96C4C4C4C4/scan-stop" \
-H "accept: application/json"
# 响应示例
{
"success": true,
"message": "扫描任务已停止"
}# 无效地址格式
curl -X GET "http://localhost:8081/api/v1/transactions/invalid-address" \
-H "accept: application/json"
# 错误响应
{
"error": "invalid_address",
"message": "无效的地址格式"
}
# 扫描任务不存在
curl -X GET "http://localhost:8081/api/v1/transactions/scan/nonexistent_scan_id/progress" \
-H "accept: application/json"
# 错误响应
{
"error": "scan_not_found",
"message": "未找到扫描任务"
}- Swagger API 文档: http://localhost:8081/swagger/index.html
- 健康检查端点: http://localhost:8081/api/v1/health
- 支持的网络: Ethereum Mainnet, Sepolia, Polygon, Mumbai
- 数据库: MySQL 8.0+
- Go版本: 1.24+
欢迎提交 Issue 和 Pull Request 来改进这个项目。
本项目采用 MIT 许可证。详情请参阅 LICENSE 文件。