Skip to content
[OPEN_POKER]
流程图显示了完整的 Open Poker 手牌变成了规范的 JSON、SHA-256 哈希、Constellation 指纹和公开证明。

Open Poker 证据层背后的工程选择

JJoão Carvalho||已更新 |15 min read

Open Poker 的数字证据集成围绕一个工程原则设计:证据应增强手牌记录的可信度,却不能进入扑克对局的关键路径。我们希望通过 Constellation Digital Evidence 验证已完成的手牌,但绝不能让证明 API、网络延迟或确认等待决定一手牌能否结束、底池能否派奖。

披露:我是 openpoker.ai 的创始人。这是两篇系列文章的第 2 篇。第 1 篇解释产品层面的原因;本文介绍工程取舍,而不是逐行代码教程。

第 1 篇:产品Open Poker 为什么使用 Constellation Digital Evidence

第 2 篇:工程:证据层背后的设计选择,包括证据何时创建、哪些数据参与哈希、哪些信息保持私密,以及如何实现独立验证。

核心要点

  • Open Poker 仅在牌局结果提交至数据库后才开始证据工作。
  • 公开手牌载荷会生成确定性 JSON,再以 SHA-256 计算文档哈希。
  • 本地证据状态明确:待定、已提交、已完成或错误。
  • 手牌页面公开状态、文档哈希、指纹、浏览器链接,以及验证所需的规范化载荷。

设计目标

证据层必须同时满足两个要求:一方面要足以暴露对手牌历史的篡改,另一方面又要足够朴素,绝不影响牌局运行方式。

由此形成了一条清晰边界。Open Poker 负责运行游戏、保存手牌并发布公开手牌页面。数据库事务提交后,后台证据服务才会创建指纹并发送至 Constellation Digital Evidence。证明延迟或失败都不影响手牌记录;证明最终确认后,这手牌便多了一层更有力的公开凭证。

该边界是集成中最重要的工程决策。

证明流程

管道故意缩短。

开放扑克数字证据流程:完整手牌、规范 JSON、SHA-256 文档哈希、Constellation 指纹、公共验证页面。

一手牌结束后,Open Poker 先保存结果,再根据已保存数据构建公开手牌载荷,包括公共牌、摊牌时亮出的底牌、赢家、行动、筹码变动、手牌编号、牌桌 ID 和时间戳。

接下来,载荷会被转换为键已排序且不含多余空格的确定性 JSON。只有同一份手牌数据始终生成完全相同的字节,证明才有意义。这与 IETF 的 JSON 规范化方案所处理的是同一类问题(RFC 8785),但 Open Poker 的本地文档哈希并未声称完全兼容 RFC 8785。随后,系统使用 SHA-256 对这些字节计算哈希;SHA-256 属于 NIST 定义的安全哈希标准系列(FIPS 180-4)。用户之后可以自行重新计算所得文档哈希。

然后,系统按照 Constellation 记录的提交与验证模型,通过 Constellation Digital Evidence 提交指纹(开发者文档查找并验证文档)。Open Poker 会把证明状态和浏览器链接与手牌一同存储,因此手牌页面可以显示记录处于待处理、已提交、已确认或出错状态。

完整流程就是:保存手牌、生成公开载荷、计算确定性哈希、提交外部指纹、展示可验证证据。

为什么我们对公开的手数据进行哈希处理

扑克存在一个常被“全部上链”方案忽视的隐私前提:隐藏信息本就是游戏的一部分。如果验证系统泄露玩家弃牌时未公开的底牌,反而会破坏产品。

Open Poker 哈希的是公开记录,而不是隐藏游戏状态。证据载荷只包含可安全展示在手牌复盘中的信息,例如公开行动、公共牌、赢家和筹码变化;弃牌时未亮出的底牌、API 密钥、私有代理 ID 与内部所有者状态均不包含在内。

这正是我们需要的能力:开发者可以验证公开手牌记录未被暗中修改,同时 Open Poker 仍能保护本应隐藏的信息。

为什么它在保存手牌后运行

证据层在持久化之后开始,因为证据应该引用真实的记录。如果一手牌未能保存,则没有稳定的公共牌局历史可以证明。

这一顺序也保护了游戏循环。手牌无需等待证明服务即可结束;Open Poker 先记录结果,再在后台处理证据。

这使得集成在正常生产故障模式下具有弹性:

  • 外部 API 可能会很慢。
  • 最终确认可能需要时间。
  • 非生产环境中可能会丢失凭证。
  • 可以在不改变牌局结果的情况下进行重试。

这些都不会影响底池是否被正确授予。

为什么我们保留本地证据状态

Open Poker 保留本地证据记录,因为用户需要清晰的状态,而不是黑匣子。记录可以是“待处理”、“已提交”、“已完成”或“错误”。如果证据在手牌完成时被禁用,则可能根本没有证据记录,这与假装证据存在不同。

这种状态让 UI 变得诚实。待定证据应注明待定。最终的证明应显示浏览器链接。缺失的证据不应该假装存在。

本地记录还让 Open Poker 能够保存自主验证所需的文档哈希与规范化载荷。外部证明固然重要,用户仍需要准确的载荷字节来重新计算哈希。

它也让重试逻辑更加明确。待提交任务可以在不修改已保存手牌的前提下重试;已提交指纹可以持续轮询到最终确认;重试次数耗尽后,则转为 UI 能如实展示的错误状态。

为什么用户界面很重要

大多数用户不会直接读取 API 响应或检查指纹。证据必须出现在问题出现的地方:手页上。

手牌详情页会显示状态、指纹、文档哈希、确认时间、Constellation 浏览器链接,以及简短的验证步骤。页面还可以公开计算文档哈希所用的规范化载荷。这样,后端管道中的证据层才真正成为用户可用的产品功能。

用户界面只有一个任务:让验证过程具体可见。用户应能查看一手牌,确认其证据存在,打开浏览器记录,并理解哈希值代表什么。

独立验证在这里意味着什么

独立验证并不表示 Open Poker 已经完全免信任,而是说用户可以依据数据库之外的加密承诺,核验公开载荷。

验证思路是:

  1. 获取公开手牌载荷。
  2. 对规范化载荷计算哈希。
  3. 将其与 Open Poker 显示的文档哈希进行比较。
  4. 检查 Constellation Digital Evidence 上的指纹。

如果公开手牌载荷在作出承诺后被修改,哈希也会随之改变,因此暗中编辑可以被发现。

这是一个狭隘的主张,但它很有用,因为它是可测试的。

我们刻意避免的

我们避免将数字证据变成游戏玩法依赖。证明层不应该决定一手牌是否可以结束,机器人是否可以行动,或者是否发出河牌。

我们还避免发布私人游戏状态。更多数据并不总是更好的证据。在扑克中,错误的数据可能会损害未来牌局的完整性。

我们避免让该功能需要加密知识。产品表面是哈希值、状态和浏览器链接。这足以让构建者理解信任模型,而无需关心每个内部实现细节。

建设者的经验教训

如果你要为应用加入证据或公证功能,这套模式可以复用:

  1. 选择用户真正需要信任的记录。
  2. 仔细定义公共负载。
  3. 在计算哈希前将载荷确定性序列化。
  4. 在主数据库外部提交指纹。
  5. 使证明层远离关键运行时路径。
  6. 为用户提供一种可见的方式来验证结果。

关键是克制。当主张范围狭窄、边界清晰且验证路径易于解释时,证据系统效果最佳。

对 Open Poker 而言,这项主张很明确:一手牌完成并生成指纹后,任何对公开手牌记录的暗中修改都会使验证失败。

这就是工程故事。代码的存在是为了支持这一主张,而不是让该功能听起来比实际更复杂。

工程参考

上述工程选择基于一些外部标准和产品文档:

常见问题解答

为什么不在交手过程中进行公证?

因为证明不应减慢或中断游戏玩法。 Open Poker 首先保存手牌,然后在后台创建证据。这使得证明层与实时游戏循环分开。

到底正在验证什么?

公开手牌载荷,包括行动、公共牌、赢家、筹码变动、手牌 ID 和时间戳。弃牌时未公开的底牌与内部机密不属于证据范围。

最终证明是否意味着扑克引擎没有错误?

不会。这意味着提交的公开手记录可以被检查以防止以后被篡改。引擎的正确性仍然取决于测试、监控、重放和审查。

如果证明服务宕机了会发生什么?

这手牌仍然完成并且仍然可以审查。证据提交可以在后台重试,如果重试次数耗尽,证据记录可以转至错误状态,而不是阻止游戏玩法。

为什么使用 Constellation 数字证据?

它为 Open Poker 提供了外部指纹和完整牌局记录的公共查找路径。这正是我们需要的信任原语:无需将整个游戏转移到链上即可验证的证据。

其他构建者应该复制什么?

复制这条边界:先保存作为事实来源的记录,再对确定性公开载荷计算哈希,异步提交证明,并在用户原本查看记录的位置展示验证结果。

继续阅读