全部文章

阅读约 7 分钟

如何验证一条 Label 309 记录

直接从公开的 Cardano 链上,结合记录字节以及可选的内容或接收方密钥来验证一份 Label 309 证明——无需信任 CardanoWall,也无需信任发布它的任何人。

验证一条 Label 309 记录时,你直接从公开的 Cardano 链上着手,起点只需要一项输入:一个 Cardano 交易引用。整个结论不依赖 CardanoWall,不依赖发布这条记录的人,也不依赖任何服务器仍然在线。

验证方会从公开的 Cardano 基础设施中取回原始交易字节,提取元数据 label 309, 重新拼装出记录主体,核对规范 CBOR 与模式,验证任何作者签名,并在内容字节 或加密载荷可用时重新计算哈希。整个核对过程是一连串密码学比对,而不是向某个 受信任的服务发起请求。

发布这条记录的网关并不是权威。记录本身才是。这正是该标准的要义所在:一份 你核对过一次的证明,此后任何人都能用任何符合 Label 309 的工具永久地继续核对。

验证需要哪些东西?

最低限度的输入是一个 Cardano 交易哈希。

仅凭这个交易哈希,验证方就能回答:

  • 这笔交易里是否存在一条 Label 309 记录?
  • 这条记录在结构上是否有效?
  • 这笔交易是否已得到足够深的确认?
  • 如果存在签名,记录层级的签名能否通过验证?
  • 这条记录声明了哪些哈希、存储 URI、Merkle 根和封存信封?

要证明某个具体文件就是哈希背后的那个文件,验证方还需要文件字节,或者记录 所引用的、可归属的存储对象。

要解密一条封存记录,验证方需要接收方的私钥。该密钥仅在本地使用,绝不会回传 给发布者或 CardanoWall——整套构造的设计目标,就是让验证过程永远不会回连到任何 服务器。

验证方到底核对什么?

一个合格的验证方应当分层核对记录。

第一层是结构验证器。它是作用于记录字节的纯函数,不发起任何网络调用,不做解密, 也不做签名验证。它核对规范 CBOR、字段类型、封闭模式、已注册的算法名称、URI 规则,以及跨字段约束,例如「至少必须存在一个 item 或一个 Merkle 承诺」。

第二层是公开验证方。它从验证方自行选定的 Cardano 数据源解析出交易,把取回的 字节绑定到交易哈希,提取 label 309,核对确认深度,并验证记录签名。这一层正是 公开审计、CI 任务或区块浏览器可以运行的部分。

第三层是接收方验证器。它会做公开验证方所做的一切,然后尝试用接收方的本地密钥 解密封存的 item,并在解密后重新计算明文哈希。

这种分层之所以重要,是因为并非每份证明都需要每一项核对。一条仅含哈希的记录 即便没有加密载荷,依然可以有效。公开验证方可以验证一条已签名记录,而无需具备 解密某个封存文件的能力。

为什么要从原始交易字节验证,而不是从 JSON 视图验证?

Label 309 的验证基于原始链上数据,而不是某种方便的 JSON 投影——这一区别并非 只是表面文章。

记录是规范 CBOR。签名覆盖的是逐字节精确的规范 CBOR。Cardano 交易元数据包含 字节串、文本串、数组、映射以及排序规则,而 JSON 无法在不丢失信息的前提下保留 这些内容。

因此,验证方应当取回原始交易 CBOR,重新计算交易 id,把辅助数据绑定到交易主体, 再把 label 309 的分块数组重新拼装回原始的记录主体。

区块浏览器可以是一项有用的基础设施,但它不该成为你信任的对象。验证方应当把区块 浏览器返回的内容当作需要绑定、核对并交叉核对的数据。

一条 label 309 记录里有什么?

一条 Label 309 记录承载于 Cardano 交易元数据 label 309 之下。

链上的值并不是一个松散的 JSON 对象。记录主体先被一次性序列化为规范 CBOR,再以 字节串分块数组的形式传输,因为 Cardano 元数据的字节串和文本串上限为 64 字节。

重新拼装之后,记录主体就是一个 CBOR 映射。它的顶层键包括:

  • v,模式版本(当前为 1);
  • items,一个可选数组,包含按内容划分的承诺——每个 item 都带有一个必需的 hashes 映射,并可选地携带自己的 uris 列表(用于内容寻址存储 ar://ipfs://)以及一个用于封存内容的 enc 信封;
  • merkle,可选的列表承诺,把一个链上根绑定到一份链下叶子列表——大批量数据 正是以此方式锚定的;
  • sigs,可选的记录层级作者签名;
  • supersedes,一个可选的仅追加指针,指向更早的一条记录;
  • crit 以及带命名空间的扩展键,用于面向未来兼容的增补。

一条符合规范的记录必须至少承诺一个 items 条目或一个 merkle 承诺。存储 URI 和封存信封位于某个 item 内部,而不在顶层——当你自己解析记录时,这是个值得弄对 的细节。

核心主张始终以内容为先:这些哈希,或这个 Merkle 根,已在这笔交易中被锚定,时间 不晚于区块时间。

自动化应当预期哪些判定结果?

验证不应把每一个问题都简化为「有效」或「无效」。

Label 309 的验证方模型采用四种机器判定结果:

  • valid:每一项必需的核对都已通过。
  • failed:记录本身未能通过结构、签名或可归属完整性的某项核对。
  • unverifiable:某项必需核对因为不可归咎于记录的原因而无法完成,例如基础设施 不可用,或内容无法取回。
  • pending:交易已在链上,但低于验证方的确认阈值。

这种区分在真实系统里很重要。

如果某个存储网关宕机了,记录不该因此被判负。如果取回的字节可归属于某个已承诺的 URI,而它们的哈希对不上,记录就应当判定为 failed。如果交易只有一个确认,验证方 就该说它处于 pending,而不是假装最终性已经发生。

开源的 cardanowall CLI 把这些状态直接映射到退出码,于是判定结果能进入 shell 脚本而无需解析任何 JSON:

cardanowall verify <tx-hash> --json
退出码判定含义
0valid每一项必需的核对都已通过
1failed一项可归属于记录的核对未通过(完整性、结构或签名)
2unverifiable记录无过错,但某项必需核对无法运行(网络或策略原因)
3pending确认数尚不足——pending 记录给出的结果都不是最终的
4CLI 输入错误(参数有误或缺少必需输入)

这种区分正是你在持续集成里想要的:脚本能够区分「证明是坏的」(退出码 1,且 行为异常的区块浏览器永远无法伪造它)与「稍后再试」(退出码 2)。同一个验证器也随 TypeScript、Python 和 Rust SDK 一并发布,因此应用程序可以读取完整的结构化报告, 而不只是退出码。关于把这套流程接入流水线的相关模式,参见 在自动化中使用 CLI

你要如何验证一个文件?

对于一份简单的文件证明,工作流程是:

  1. 拿到交易哈希。
  2. 取回并验证这条 Label 309 记录。
  3. 确定所承诺的哈希算法与摘要。
  4. 在本地对文件字节计算哈希。
  5. 把你的摘要与记录中的摘要比对。
  6. 核对确认深度与区块时间。
  7. 如果存在记录签名,则核对它们。

只要文件有一个字节不同,哈希就会不同。

这正是内容哈希证明的力量。它不证明文件是真实、合法、原创或有价值的。它证明的是: 与该哈希匹配的那一组确切字节,存在的时间不晚于被锚定的区块时间,并受验证方的 最终性策略约束。

你要如何验证一条封存记录?

封存记录会加上加密内容。

公开记录仍然承诺明文哈希。密文可以存放在链下,例如通过 Arweave 或 IPFS。链上 信封中包含的信息,足以让目标接收方在本地恢复出内容密钥。

接收方验证器应当:

  1. 先走一遍公开验证路径。
  2. 用提供的私钥尝试封存信封中的各个密钥槽。
  3. 如果某个槽被打开,就解密密文。
  4. 重新计算明文哈希。
  5. 把它与链上的哈希承诺比对。

最后这一步哈希核对,正是「我解开了某个东西」与「这就是被打上时间戳的那份确切 内容」之间的桥梁。仅仅把字节解密出来还不够;重新计算出的明文哈希,必须与记录在 任何加密发生之前所做出的承诺相匹配。

接收方密钥始终留在本地。验证既不需要受信任的 CardanoWall 账户,不需要发行方 服务器,也不需要向发布记录的那个人回调。一条封存记录会让明文对持有密钥者保持 机密——但要注意它的边界:它不保证匿名,而且一旦接收方解密了内容,他们就可以 对明文为所欲为。

你要如何验证一个 Merkle 承诺?

Merkle 承诺是为批量数据而设的。

与其在一条记录里直接发布成千上万个哈希,生产者可以发布一个 Merkle 根,并把 有序的叶子列表和包含证明保留在链下。

要验证批次中的一个 item:

  1. 对该 item 计算哈希,或获取所承诺的叶子摘要。
  2. 对照 Merkle 根验证包含证明。
  3. 核对根和 leaf_count 确实在这条 Label 309 记录里。
  4. 验证 Cardano 交易与确认深度。

仅有根本身还不够。生产者或归档方必须保留叶子列表和包含证明。Label 309 为批次 提供了一个公开锚点;围绕这批数据的配套证据仍然需要妥善留存。 一条记录证明数千个文件 正是以这种方式、以固定的链上成本实现的。

哪些东西不该被信任?

不要把发布者的网站当作真相来源来信任。

不要信任一笔交易的截图。

不要把 JSON 区块浏览器视图当作密码学输入来信任。

不要仅仅因为某个存储网关返回了字节就信任它。

不要信任那些不说明核对了什么的「已验证」徽章。

验证方应当能够产出一份报告:交易哈希、所用的 Cardano 数据源、确认深度、区块 时间、记录摘要、签名结果、内容哈希结果、存储取回结果、Merkle 核对,以及任何被 跳过的核对。

正是这份报告,让证明能够用于审计与自动化。

验证不能证明什么?

验证是精确的。不该夸大其作用。

一条有效的 Label 309 记录,仅凭自身并不能证明:

  • 对内容的合法所有权,或对它的排他性权利;
  • 内容是真实的;
  • 发布者拥有发布它的许可;
  • 现实世界中有某个人掌控着某个签名密钥;
  • 某个法院、监管机构或客户会在没有佐证的情况下接受这份证明——可采性取决于 司法管辖区,证明可以支撑一桩案件,但不能替代律师;
  • 链下存储会永远保持可用,除非另行维护存储的持久性。

它证明的是记录实际做出的那个主张:一个哈希或 Merkle 根已锚定在 Cardano 上, 覆盖记录的任何签名都已通过验证,任何内容或解密核对都与承诺相匹配。换句话说, 它确立的是时间与完整性——而不是真实性、所有权或权利。

正是这个更窄的主张,让它真正有用。关于时间戳能做什么、不能做什么的完整边界,参见 一份证明不能证明什么

延伸阅读

  • Label 309 标准,包含完整的验证流程、判定状态以及类型化错误目录: label309.org
  • 开源验证器——cardanowall CLI 以及 TypeScript、Python 和 Rust SDK,它们 全都运行同一套核对:github.com/cardanowall
  • Label 309 已提交至 Cardano CIP 流程,并作为一项 Metadata 类别提案处于 CIP 编辑的审议之中:公开的 pull request

verificationdeveloperslabel-309