区块链(00):从中心化订单基线开始
一笔订单的“已完成”究竟由谁宣布?先不建链。把合成订单 synthetic-001(面额 70)保存在单台数据库,支付、交付凭证和结算分开记。假设卖家已提交交付凭证;服务收到结算请求,把订单从 DELIVERED 变成 SETTLED,同时给卖家记 70。若数据库提交成功而响应在网络中丢失,客户端会重试。结算次数与收到响应的次数并不相同。
这一篇的先修是字节、哈希表、SQL 事务和 HTTP 重试。不熟悉字节编码可先做下面的自测,不必先掌握密码学。旧文思考区块链和学习区块链的基础资料保留为历史问题入口;这里的目标是把“需要链吗”变成可以检验的工程问题,而不是替旧文重写结论。
模型:谁说了算
单台 SQLite 是唯一排序者和记账权威。服务、数据库与客户端在同一管理域;客户端可超时和重复请求,不允许服务信任客户端宣称“上次一定失败”。安全目标是一个订单只结算一次,活性目标是在数据库可用且请求持续重试时返回持久化后的结果。数据库损坏、管理员篡改、跨机构不共享权威、现实交付真伪不属于这段测试的保证。
sequenceDiagram
participant C as 客户端
participant A as 订单服务
participant D as SQLite
C->>A: settle(req-1, synthetic-001, v1)
A->>D: BEGIN IMMEDIATE + 检查请求键
A->>D: 条件更新订单 + 插入请求记录 + COMMIT
D-->>A: SETTLED,卖家记账 70
A--xC: 响应丢失
C->>A: 原样重试 req-1
A->>D: 读取同键、同意图的持久结果
A-->>C: SETTLED,仍是 70
先运行错误实现:只执行 merchant_credit += amount,不在同一原子操作里检查状态和请求标识。模拟响应丢失后的两次调用,测试记录为 70 → 140。这不是双花链实验,而是常见的“重复外部副作用”反例。正确实现以 BEGIN IMMEDIATE 包住有条件更新和唯一键记录:只有 status='DELIVERED' 的订单能完成一次状态迁移;同一键、同一意图取已有结果,同一键却换了意图立即拒绝。关闭数据库再打开,重试仍返回 70;换一个新键试图再次结算也拒绝。
| 状态 | 可以证明 | 还不能证明 |
|---|---|---|
| API 已收请求 | 服务端看过参数 | 已持久化、卖家入账 |
| SQL 事务已提交 | 本库订单与请求记录原子更新 | 现实交付存在或其他机构认可 |
| 客户端已收到响应 | 这一次读到了提交结果 | 网络上永远不会重发旧请求 |
事务还要考虑失败恰好卡在不同位置:条件更新前崩溃,不应记账;更新后但 COMMIT 前崩溃,订单和请求记录应一起回滚;提交后而回复前崩溃,重试应读取既有结果。测试实际覆盖了最后一种“提交后没有使用返回值、关闭再重开”的路径,另外两种崩溃注入与文件系统断电没有实测,因此不能把“SQLite 原子提交”外推成已完成灾备。幂等键必须与操作的完整意图绑定,单独以 order_id 充当相同请求的判据会把不同金额或收款方误当作重试。
注意 SETTLED 是数据库定义的订单状态,不表示卖家已经收到银行转账,也不表示外部配送确实完成。如果真实付款要经第三方支付,还须分别记录准备发送、已发送、对方确认和最终对账,避免在数据库事务之外的网络调用上空喊原子性。后续 27–28 篇会把这个未知结果和链上交易、索引恢复关联起来。
models/central.py 与 test_models.py 在 examples/blockchain/,运行 python3 -m unittest discover -s examples/blockchain -p 'test_*.py' -v;原始输出、退出码、代码 SHA 和断言记录在 examples/blockchain/evidence/00-05/ 的运行目录。实验只用合成数据,不对真实买卖家或多进程并发作保证。
自测与练习
- 自测:
b"ab" + b"c"与b"a" + b"bc"是否相同?SHA-256 能否辨认原来是哪组字段?如果 SQL 提交后 HTTP 断开,能否据此断言回滚?答案:拼接后的字节相同,任何哈希都不能恢复丢失的字段边界;断开连接只证明响应未知,不证明事务回滚。02 篇会引入字段长度。 - 故障重放:把
req-1改为req-2重试已经结算的订单,会怎样?把req-1保留却换 fingerprint 又怎样?答案:前者无法再次从DELIVERED更新,抛出“不能重复结算”;后者触发相同键不同意图拒绝。代码断言中均未生成第二笔记账。
单一机构足以对账时,先明确事务边界、幂等范围和管理员信任即可;加上区块链并不会自动证实线下交付。下一篇要解决的是:如果任何机构都不被赋予唯一排序权,谁负责阻止两笔互相冲突、却都持有合法签名的支出?
参考资料与核验边界
- SQLite Transactions(原文待联网核验)。本篇的实际 SQL 与行为以本地运行证据为准,系统 SQLite 源码 SHA 尚未冻结。
- Bitcoin: A Peer-to-Peer Electronic Cash System(原文待联网核验),第 2–5 节为后续去中心化排序的原始问题入口。PDF 本环境返回 403,本篇不从中推断当代客户端行为。
系列入口:00 基线 · 区块链(01):两份签名都正确,账本仍会冲突 · 区块链(02):哈希之前,先说清究竟签了哪些字节 · 区块链(03):签名证明授权了哪条消息 · 区块链(04):Merkle 包含证明需要可信根 · 区块链(05):同一笔支付在 UTXO 与账户账本里的两种状态迁移。后续篇章未落盘,不建立空链接。


