YM 中心记账系统开发文档
版本:2.0 | 修订日期:2026-07-01 | 状态:正式发布
1. 引言
1.1 项目背景
传统中心化账务系统常将用户私钥托管于服务器,存在被攻击或内部作恶的风险。YM 系统采用区块链式去信任设计,私钥完全由客户端生成并保管,服务器仅存储公钥与交易账本,所有转账必须通过数字签名验证,确保资产安全与交易不可篡改。
1.2 项目目标
- 提供一个轻量级、高性能的中心记账服务器,支持 YM 代币的发行、转账与查询。
- 实现用户自治:用户自主生成私钥/公钥/地址,自主签名交易。
- 实现服务器无感验证:仅验证签名有效性、公钥与地址匹配、余额充足及 Nonce 连续性。
- 提供管理后台,供运营方进行代币增发、地址冻结、数据审计与重建。
1.3 术语定义
| 术语 | 解释 |
|---|---|
| 私钥 (Private Key) | 256 位随机数,用于签名交易,永不传输 |
| 公钥 (Public Key) | 由私钥通过椭圆曲线算法推导,可公开,用于验证签名 |
| 地址 (Address) | 公钥经 SHA‑256 和 RIPEMD‑160 双重哈希后的 40 位十六进制串 |
| Nonce | 从 0 开始递增的序号,防止交易重放攻击 |
| 交易哈希 (TxHash) | SHA256(from + to + amount + nonce) 计算得出 |
| 账本 (Ledger) | 所有交易记录的不可变集合 |
| 余额缓存 (Balance Cache) | 由账本实时维护的地址余额快照 |
2. 系统架构
2.1 整体架构图(概念层)
┌─────────────────────────────────────────────────────────────────┐
│ 钱包客户端(用户端) │
│ ┌─────────────┐ ┌─────────────┐ ┌───────────────────────┐ │
│ │ 生成私钥/公钥 │ │ 生成地址 │ │ 签名交易(离线) │ │
│ └─────────────┘ └─────────────┘ └───────────────────────┘ │
│ │ │ │ │
│ └───────────────┼─────────────────────┘ │
│ │ HTTPS / JSON │
└──────────────────────────┼──────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────────┐
│ 服务器端(PHP 8.0) │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ API 网关 (路由) │ │
│ │ - /api/register - /api/transfer │ │
│ │ - /api/transaction - /api/ledger │ │
│ │ - /api/wallets - /api/transactions │ │
│ │ - /api/ping - /api/frozen/{addr} │ │
│ │ - /api/exists/{hash} │ │
│ └───────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌──────────────────────────▼──────────────────────────────┐ │
│ │ 业务逻辑层 │ │
│ │ - 地址/公钥验证 - 签名验证 - Nonce 检查 │ │
│ │ - 余额查询/更新 - 交易写入 - 冻结状态检测 │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌──────────────────────────▼──────────────────────────────┐ │
│ │ 数据访问层 │ │
│ │ - PDO 数据库连接 - 事务控制 - SQL 执行 │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌──────────────────────────▼──────────────────────────────┐ │
│ │ MySQL 数据库(5.7+) │ │
│ │ wallets | transactions | wallet_balance | admin_config │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ 管理员后台(/admin/{admin_key}/) │ │
│ │ 登录认证 | 钱包列表 | 交易列表 | 增发 | 冻结 │ │
│ │ 余额重建 | 登出 │ │
│ └───────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
2.2 模块职责
| 模块 | 职责 |
|---|---|
| API 网关 | 解析请求 URL,分发至对应处理函数,统一 JSON 输出 |
| 验证器 | 地址格式、公钥匹配地址、签名有效性、Nonce 连续性、冻结状态 |
| 账本引擎 | 插入交易记录,更新余额缓存,确保原子性 |
| 查询服务 | 交易哈希/地址交易检索,钱包列表,余额查询 |
| 管理模块 | 后台认证、代币增发(SYSTEM 交易)、冻结/解冻、余额重建 |
| 工具库 | 椭圆曲线适配、哈希计算、地址生成、签名接口 |
3. 技术选型
| 层次 | 技术 | 版本 | 原因 |
|---|---|---|---|
| 后端语言 | PHP | 8.0+ | 性能优异(JIT)、成熟生态、完美支持 OpenSSL 及 PDO |
| 数据库 | MySQL | 5.7+ | 稳定、支持事务与行锁,满足并发转账需求 |
| 加密算法 | ECDSA (secp256k1 / prime256v1) | - | 公钥/私钥体系,secp256k1 为比特币标准 |
| 哈希算法 | SHA‑256, RIPEMD‑160 | - | 用于地址生成与交易哈希 |
| 通信协议 | HTTPS / RESTful JSON | - | 简单、跨平台 |
| 会话管理 | PHP 内置 Session | - | 轻量级 |
| 密码存储 | bcrypt (password_hash) | - | 抗暴力破解 |
4. 数据库设计
4.1 实体关系(逻辑描述)
- wallets 与 transactions 通过
address字段关联(from_address / to_address)。 - wallets 与 wallet_balance 通过
address一对一关联(缓存)。 - admin_config 为独立配置表,仅一条记录。
4.2 表结构明细
wallets(钱包主表)
CREATE TABLE `wallets` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`address` varchar(40) NOT NULL UNIQUE COMMENT '40位十六进制地址',
`public_key` text NOT NULL COMMENT 'PEM格式公钥',
`last_nonce` int(11) NOT NULL DEFAULT -1 COMMENT '已使用最大nonce',
`frozen` tinyint(1) NOT NULL DEFAULT 0 COMMENT '0正常 1冻结',
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `address` (`address`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
transactions(交易账本)
CREATE TABLE `transactions` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`tx_hash` varchar(64) NOT NULL UNIQUE COMMENT 'SHA256哈希',
`from_address` varchar(40) NOT NULL COMMENT '付款方,SYSTEM表示增发',
`to_address` varchar(40) NOT NULL,
`amount` decimal(20,8) NOT NULL,
`nonce` int(11) NOT NULL COMMENT '付款方的nonce',
`signature` text NOT NULL COMMENT 'Base64签名(增发为空)',
`public_key` text NOT NULL COMMENT 'PEM公钥(增发为空)',
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `tx_hash` (`tx_hash`),
KEY `from_address` (`from_address`),
KEY `to_address` (`to_address`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
wallet_balance(余额缓存)
CREATE TABLE `wallet_balance` (
`address` varchar(40) NOT NULL PRIMARY KEY,
`balance` decimal(20,8) NOT NULL DEFAULT 0,
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
admin_config(管理员配置)
CREATE TABLE `admin_config` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`admin_key` varchar(50) NOT NULL COMMENT '后台URL后缀',
`password_hash` varchar(255) NOT NULL,
`ip_whitelist` text COMMENT '逗号分隔IP,空则允许所有',
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
4.3 索引优化建议
transactions表的from_address和to_address已建立普通索引。tx_hash唯一索引用于交易查询与防重放。wallets.address唯一索引,保证地址全局唯一。
5. API 接口规范
5.1 通用约定
- Base URL:
https://your-domain.com/api - 请求方式:GET(查询)或 POST(写操作)
- Content-Type:
application/json(POST 请求) - 响应格式:
application/json,包含success/error字段或直接返回数据。 - 错误 HTTP 状态码:400(参数错误),404(资源不存在),405(方法不允许),500(服务器内部错误)。
5.2 接口列表
5.2.1 钱包备案 POST /register
请求体:
{
"address": "a1b2c3d4e5f67890a1b2c3d4e5f67890a1b2c3d4",
"public_key": "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC...\n-----END PUBLIC KEY-----"
}
响应(成功):
{ "success": true, "address": "a1b2c3d4..." }
可能的错误:
"Invalid address format":地址非40位十六进制"Address does not match public key":公钥不能推导出该地址"Address already registered":重复备案
5.2.2 发起转账 POST /transfer
请求体:
{
"from": "付款方地址",
"to": "收款方地址",
"amount": 10.5,
"nonce": 0,
"signature": "MEUCIQD...",
"public_key": "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----"
}
响应(成功):
{ "success": true, "tx_hash": "abc123..." }
错误(部分):
"From address not registered"/"To address not registered""From address is frozen""Invalid nonce, expected X""Insufficient balance""Invalid signature""Transaction already exists (replay)"
5.2.3 查询交易详情 GET /transaction/{tx_hash}
返回完整交易记录(若无则返回 404)。
5.2.4 查询账本 GET /ledger?address={addr} 或 ?tx_hash={hash}
- 按地址:返回该地址所有相关交易(按
id DESC)。 - 按哈希:返回单笔交易(数组形式以保持统一)。
5.2.5 获取所有钱包 GET /wallets
[
{"address":"...", "balance":"100.00000000", "frozen":0},
...
]
5.2.6 获取所有交易(分页)GET /transactions?page=1&limit=100
默认 page=1, limit=100。
5.2.7 服务器心跳 GET /ping
响应:{"status":"ok"}
5.2.8 检查冻结状态 GET /frozen/{address}
响应:{"frozen": true|false}
5.2.9 检查交易是否存在 GET /exists/{tx_hash}
响应:{"exists": true|false}
6. 管理员后台
6.1 首次配置
访问 /admin/,系统自动检测 admin_config 表是否为空。若为空,显示配置表单,要求填写:
- Admin Key(用于 URL 后缀,建议复杂)
- 登录密码(至少 8 位)
- IP 白名单(可选,多个 IP 用逗号分隔)
提交后,密码经 bcrypt 哈希保存,Admin Key 明文保存(建议保密)。
6.2 登录与认证
后续访问 /admin/{admin_key}/ 会要求输入密码,验证通过后建立 Session。同时检查请求 IP 是否在白名单内(若设置),否则返回 403。
6.3 后台功能菜单
| 功能 | 说明 |
|---|---|
| 钱包列表 | 展示所有地址、余额、冻结状态 |
| 交易列表 | 显示最近 200 笔交易,可按时间排序 |
| 增发 YM 币 | 表单输入目标地址和金额,生成 from='SYSTEM' 的交易 |
| 冻结/解冻 | 输入地址,选择操作,更新 wallets.frozen |
| 重建余额 | 清空 wallet_balance,根据 transactions 全量重算并插入 |
| 登出 | 销毁 Session,返回登录页 |
7. 核心业务流程
7.1 地址生成与备案(客户端主导)
- 客户端:生成 256 位随机数作为私钥。
- 客户端:使用
secp256k1曲线通过私钥推导公钥(PEM 格式)。 - 客户端:对公钥 DER 编码进行
SHA‑256,再RIPEMD‑160,得到 20 字节 → 转 40 位十六进制地址。 - 客户端:调用
/api/register将{address, public_key}发送至服务器。 - 服务器:验证地址格式、公钥与地址匹配性,若通过则存入
wallets表,并初始化余额缓存(余额为 0)。 - 服务器:返回成功。
7.2 转账交易(客户端与服务器交互)
客户端 → 客户端:查询本地 last_nonce
客户端 → 客户端:构造数据 from+to+amount+nonce
客户端 → 客户端:用私钥签名得到 signature
客户端 → 服务器:POST /api/transfer (from,to,amount,nonce,signature,public_key)
服务器 → 服务器:验证公钥推导地址 == from
服务器 → 服务器:检查 from 已注册、未冻结
服务器 → 服务器:检查 nonce == last_nonce+1
服务器 → 服务器:查询余额缓存,判断 >= amount
服务器 → 服务器:用公钥验证签名
服务器 → 服务器:检查 to 是否已注册
服务器 → 服务器:计算 tx_hash 并检查唯一性
服务器 → 服务器:事务开始:插入 transactions,更新 from/to 余额缓存,更新 wallets.last_nonce
服务器 → 客户端:返回 tx_hash
7.3 余额缓存重建
- 管理员触发(后台操作)或脚本调用。
- 服务器:执行
TRUNCATE wallet_balance。 - 服务器:按
id ASC顺序读取所有交易记录。 - 对每条交易:
- 若
from_address != 'SYSTEM',则对应地址余额 减去 amount; - 若
to_address != 'SYSTEM',则对应地址余额 加上 amount。
- 若
- 使用
INSERT ... ON DUPLICATE KEY UPDATE将最终余额写入wallet_balance。 - 建议低峰期操作。
8. 安全机制
- 私钥隔离:私钥永不传输至服务器。
- 签名验证:所有交易必须附带签名,服务器使用公钥验证。
- Nonce 防重放:每个地址维护
last_nonce,新交易必须递增。 - 管理员后台安全:密码 bcrypt 哈希,Admin Key 隐藏入口,IP 白名单。
- 数据传输:强制 HTTPS。
- 数据库安全:使用 PDO 预处理语句,事务保证原子性。
9. 部署指南
9.1 环境准备
- 操作系统:Linux(推荐 Ubuntu 20.04+)或 Windows
- PHP:8.0 或更高版本,需安装扩展:
openssl,pdo_mysql,session - 数据库:MySQL 5.7+ 或 MariaDB 10.2+
- Web 服务器:Apache 2.4+ 或 Nginx 1.18+
9.2 安装步骤
- 下载代码:将项目文件(
config.php,functions.php,index.php)放置于网站根目录。 - 修改配置:编辑
config.php,填写正确的数据库连接信息。 - 创建数据库:执行 SQL 脚本创建库及表。
- 设置 URL 重写:
- Apache:在根目录创建
.htaccess,内容见文档。 - Nginx:在站点配置中添加
try_files $uri $uri/ /index.php?$args;。
- Apache:在根目录创建
- 权限设置:确保
session.save_path目录可写。 - 访问配置:浏览器访问
https://yourdomain/admin/进行首次配置。 - 测试接口:使用
curl测试/api/ping。
9.3 性能调优
- 启用 PHP OpCache。
- 根据并发量调整 MySQL 连接数。
- 可考虑将
wallet_balance迁移至 Redis(扩展开发)。
10. 测试指南
10.1 单元测试
| 测试类 | 覆盖功能 |
|---|---|
| AddressTest | 地址生成、格式验证、公钥匹配 |
| SignatureTest | 签名与验证流程 |
| BalanceTest | 余额计算与缓存更新 |
| TransactionTest | 转账完整流程、错误场景 |
10.2 集成测试
| 场景 | 预期结果 |
|---|---|
| 注册新钱包 | 成功,数据库中新增记录 |
| 重复注册 | 返回错误 |
| 转账到未注册地址 | 返回错误(若强制要求) |
| 转账金额超过余额 | 返回余额不足 |
| 转账 nonce 错误 | 返回 nonce 错误 |
| 转账签名错误 | 返回签名无效 |
| 增发代币 | 目标地址余额增加,交易记录生成 |
| 冻结地址 | 该地址无法发起转账 |
| 查询账本 | 返回正确的交易列表 |
| 重建余额 | 所有地址余额与账本计算一致 |
10.3 压力测试
使用 Apache Bench 或 JMeter 模拟并发转账,验证事务锁和数据库性能。
11. 扩展性考虑
- 多币种支持:在
transactions和wallet_balance中增加currency字段。 - 交易手续费:增加
fee字段,从付款方扣除。 - 更完善的权限分级:细分为超级管理员、审计员、发行员等。
- 事件通知(Webhook):交易入账后触发 HTTP 回调。
- API 限流:使用 Redis 存储访问计数。
12. 附录
12.1 错误码速查
| 错误码 | 含义 |
|---|---|
| 1001 | 地址格式无效 |
| 1002 | 公钥不匹配地址 |
| 1003 | 地址已存在 |
| 2001 | 非法的 nonce |
| 2002 | 余额不足 |
| 2003 | 无效签名 |
| 2004 | 交易哈希重复 |
| 2005 | 地址被冻结 |
| 3001 | 目标地址未注册 |
| 4001 | 管理员密码错误 |
| 4002 | IP 不在白名单 |
12.2 椭圆曲线参数说明
- 首选曲线:
secp256k1(a=0, b=7, p=2^256-2^32-977)。 - 备选曲线:
prime256v1(NIST P-256),当环境不支持 secp256k1 时使用。 - 客户端与服务器必须使用相同曲线,否则签名验证失败。
12.3 常见问题(FAQ)
- Q:如何获取当前地址的 nonce?
- A:目前设计由客户端本地维护,服务器在
wallets表中记录last_nonce,可添加接口/api/nonce/{address}返回。 - Q:增发交易是否需要签名?
- A:不需要,增发由管理员后台操作,信任管理员身份。
- Q:如果余额缓存损坏,如何处理?
- A:使用后台“重建余额”功能,系统会根据交易表全量重新计算。
- Q:是否支持跨链资产?
- A:当前为单一 YM 代币,扩展多币种需修改数据库与逻辑。
— 文档结束 —
↑ 返回顶部