如何写回交易哈希?
解读
国内面试官问“如何写回交易哈希”,并不是想听区块链的挖矿逻辑,而是考察候选人能否把 CouchDB 当成“可信追加账本”来用:
- 业务端产生一条交易记录,需要生成不可逆、可审计的哈希指纹;
- 该哈希必须原子地写回同一份文档,保证后续任何字段修改都会让哈希失效,从而防篡改;
- 整个过程要适配 CouchDB 的多主复制与离线优先特性,避免“先写库再回写”造成的双写冲突。
一句话:在 CouchDB 的 JSON 文档里,怎样安全、原子、可追溯地把“交易哈希”字段落盘,并保证后续任何篡改都能被发现。
知识点
- _rev 机制:CouchDB 用 MVCC,每次更新产生新 _rev,旧版本物理保留,天然适合“追加式账本”。
- update 函数:CouchDB 提供服务器端钩子,可原子改写文档并插入计算字段,避免“读-改-写”竞争。
- validate_doc_update:同一钩子链上的校验函数,可强制要求哈希字段必须存在且符合算法长度,拒绝非法提交。
- Etag 与 412 Precondition Failed:国内金融项目常把 _rev 作为 Etag,先比对再回写,防止离线合并时覆盖他人哈希。
- 附件(_attachments):若哈希值需要与原始 PDF、票据二进制绑定,可一起塞进附件,用
content_digest做二次校验。 - 区块链侧链思路:部分央企项目把 CouchDB 当“链下账本”,哈希写回后定时批量上链,只存根哈希,节省 Gas。
- 国密合规:国内银行要求 SM3 而非 SHA-256,需在 Erlang 层或 Node 层预置国密算法库,并在 update 函数里调用。
答案
分四步落地,全部在 CouchDB 原生能力内完成,无需外部锁:
第一步:生成交易哈希
业务服务在应用层按固定规则序列化(字段 key 升序、UTF-8 无 BOM、浮点转字符串),使用国密 SM3计算得 64 位 hex,记为 txHash。
第二步:原子写回
不直接 PUT /db/docid,而是POST /db/_design/ledger/_update/addHash/docid,触发如下 update 函数:
function(doc, req) { if (!doc) return [null, {code: 404, body: 'Doc not found'}]; var sm3 = require('views/lib/sm3'); // 预置国密 var payload = {}; Object.keys(doc).sort().forEach(function(k){ if (k !== 'txHash') payload[k] = doc[k]; }); var calc = sm3(JSON.stringify(payload)); if (calc !== req.body) return [null, {code: 400, body: 'Hash mismatch'}]; doc.txHash = calc; return [doc, {body: JSON.stringify({_id: doc._id, _rev: doc._rev, txHash: calc})}]; }
该函数在同一 Erlang 进程内完成校验+写回,保证原子性;返回的新 _rev 即刻成为后续版本的父版本。
第三步:强制校验
在同一 design 文档里再加 validate_doc_update:
function(newDoc, oldDoc, userCtx) { if (newDoc.type === 'trade' && !newDoc.txHash) throw({forbidden: '交易文档必须含 txHash'}); if (oldDoc && oldDoc.txHash && newDoc.txHash !== oldDoc.txHash) throw({forbidden: '交易哈希一旦写入不可修改'}); }
由此拒绝任何绕过 update 函数的直接写库,包括某些后台脚本。
第四步:复制与审计
下游数据中心通过_filtered replication只同步含 txHash 的文档;
审计端调用 GET /db/docid?revs=true&open_revs=all 可遍历所有历史 _rev,配合 txHash 字段即可秒级发现哪一版被篡改,满足国内等保 2.0 对数据完整性的要求。
拓展思考
- 如果交易哈希需要多机构会签,可在 CouchDB 里再建一个“会签子文档”,利用双向复制让各机构本地追加签名哈希,最后由 update 函数聚合出聚合哈希写回父文档,实现“链下多签”。
- 当数据量达到千亿级,可对历史版本做分层存储:热数据留在 CouchDB,冷版本按 _rev 范围导出到对象存储 OSS,只在 CouchDB 保留
txHash + _rev索引,实现哈希锚定冷数据,兼顾合规与成本。 - 国内央行数字货币试点项目,要求秒级对账,可在 update 函数里同步写一条哈希通知到 Kafka,供监管节点实时拉取;CouchDB 本身不丢数据,Kafka 只负责流式对账,两者通过
txHash做幂等,保证最终一致。