BNSv1(旧版)

circle-info

本页内容与旧版 BNS v1 有关。

比特币名称系统(BNS)是一种网络系统,可将 Stacks 用户名绑定到链下状态,而无需依赖任何中心化控制点。

Stacks V1 区块链通过一阶名称操作实现 BNS。在 Stacks V2 中,BNS 则通过创世区块期间加载的智能合约来实现。

BNS 中的名称具有三个属性:

  • 名称在全局范围内唯一。 该协议不允许名称冲突,所有行为正常的节点都会将给定名称解析为相同状态。

  • 名称对人类有意义。 每个名称都由其创建者选择。

  • 名称具有强所有权。 只有名称的所有者才能更改其解析到的状态。具体而言,一个名称由一个或多个 ECDSA 私钥拥有。

Stacks 区块链确保每个节点的 BNS 视图与世界上所有其他节点保持同步,因此在一个节点上的查询结果会与其他节点相同。Stacks 区块链节点允许名称所有者将最多 40Kb 的链下状态绑定到其名称上,这些状态将通过 P2P 网络复制到所有其他 Stacks 区块链节点。

对开发者来说,最大的影响是,在 BNS 中,读取名称状态快速且便宜,但写入名称状态缓慢且昂贵。这是因为注册和修改名称需要向底层区块链发送一个或多个交易,而 BNS 节点在这些交易获得足够确认之前不会处理它们。用户和开发者需要获取并花费所需的加密货币(STX)来发送 BNS 交易。

名称系统背后的动机

我们在日常生活中依赖名称系统,而且它们在许多不同的应用中发挥关键作用。例如,当你在社交媒体上查找朋友时,你是在使用该平台的名称系统将他们的名字解析为其个人资料。当你查找一个网站时,你是在使用域名服务将主机名解析为其主机的 IP 地址。当你检出一个 Git 分支时,你是在使用 Git 客户端将分支名解析为提交哈希。当你在密钥服务器上查找某人的 PGP 密钥时,你是在将其密钥 ID 解析为其公钥。

我们希望名称具备哪些属性?在 BNS 中,名称是全局唯一的、对人类有意义的,并且具有强所有权。然而,如果你看看这些例子,就会发现它们每一个只保证其中两个属性。这限制了它们的实用性。

  • 在 DNS 和社交媒体中,名称是全局唯一且人类可读的,但并不具有强所有权。系统运营者对每个名称最终解析到什么拥有最终决定权。

    • 问题:客户端必须信任系统会对给定名称应解析到什么做出正确选择。这包括相信除了系统管理员之外,没有人能够做出这些更改。

  • 在 Git 中,分支名称对人类有意义且具有强所有权,但并非全局唯一。两个不同的 Git 节点可能会将同一个分支名称解析为不同且互不相关的仓库状态。

    • 问题:由于名称可能指向相互冲突的状态,开发者必须找出其他机制来消除歧义。

  • 在 PGP 中,名称就是密钥 ID。它们是全局唯一且由密码学方式拥有的,但并非人类可读。PGP 密钥 ID 源自它们所引用的密钥。

    • 问题:这些名称对大多数用户来说很难记住,因为它们不携带与其在系统中用途相关的语义信息。

BNS 名称同时具备这三个属性,并且不存在这些问题。这使其成为构建各种网络应用程序的强大工具。借助 BNS,我们可以做到以下这些以及更多:

  • 构建主机名无法被劫持的域名服务。

  • 构建用户名无法被钓鱼者窃取的社交媒体平台。

  • 构建仓库分支不会冲突的版本控制系统。

  • 构建用户可以轻松发现并记住彼此密钥的公钥基础设施。

BNS 的组织结构

BNS 名称被组织在一个全局名称层级结构中。这个层级中与命名相关的有三种不同层次:

  • 命名空间。 这些是层级中的顶级名称。BNS 命名空间可类比为 DNS 顶级域。现有的 BNS 命名空间包括 .id, .podcast,以及 .helloworld。所有其他名称都且仅属于一个命名空间。任何人都可以创建命名空间,但为了让该命名空间被持久保存,它必须被 启动 ,以便任何人都能在其中注册名称。命名空间不归其创建者所有。

  • BNS 名称。 这些是其记录直接存储在区块链上的名称。这些名称的所有权和状态通过发送区块链交易来控制。示例名称包括 verified.podcastmuneeb.id。只要包含它的命名空间已经存在,任何人都可以创建一个 BNS 名称。

  • BNS 子域。 这些是其记录存储在链下、但整体锚定到区块链上的名称。这些名称的所有权和状态存在于 P2P 网络数据中。虽然 BNS 子域由独立的私钥拥有,但 BNS 名称所有者必须广播其子域状态。示例子域包括 jude.personal.idpodsaveamerica.verified.podcast。与 BNS 命名空间和名称不同,BNS 子域的状态是 不会 区块链共识规则的一部分。

下表总结了这些名称对象之间相似性与差异性的功能对比矩阵:

功能

命名空间

BNS 名称

BNS 子域

全局唯一

X

X

X

对人类有意义

X

X

X

由私钥拥有

X

X

任何人都可创建

X

X

[1]

所有者可更新

X

[1]

状态托管在链上

X

X

状态托管在链下

X

X

行为由共识规则控制

X

X

可能有过期日期

X

[1] 需要 BNS 名称所有者配合广播其交易

命名空间

命名空间是 BNS 中的顶级名称对象。它们控制其中名称的一些属性:

  • 注册成本有多高

  • 在需要续期之前可持续多久

  • 由谁(如果有的话)接收名称注册费用

  • 谁被允许用初始名称为该命名空间预置内容

在撰写本文时,目前规模最大的 BNS 命名空间是 .id 命名空间。 .id 命名空间中的名称旨在用于解析用户身份。 .id 中的短名称比长名称更贵,并且必须由其所有者每两年续期一次。名称注册费用不会支付给任何特定对象——而是被发送到一个“黑洞”中,使其无法再被花费(目的是阻止 ID 囤积者)。

与 DNS 不同,任何人都可以创建命名空间并设置其属性。命名空间按先到先得原则创建,一旦创建,将永久存在。

然而,创建命名空间并不是免费的。命名空间创建者必须销毁加密货币才能这样做。命名空间越短,必须销毁的加密货币就越多(也就是说,短命名空间比长命名空间更有价值)。例如,Blockstack PBC 在 2015 年创建该 .id 命名空间时花费了 40 BTC(在交易 5f00b8e609821edd6f3369ee4ee86e03ea34b890e242236cdb66ef6c9c6a1b281).

命名空间长度可以在 1 到 19 个字符之间,并由以下字符组成 a-z, 0-9, -,以及 _.

子域

BNS 名称之所以具有强所有权,是因为其私钥的持有者可以生成有效交易来更新其 zone 文件哈希和所有者。不过,这样做的代价是名称所有者必须为区块链中的底层交易付费。此外,这种方法将 BNS 名称注册和操作的速率限制在底层区块链的交易带宽之内。

BNS 通过子域克服了这一点。一个 BNS 子域 是一种 BNS 名称,其状态和所有者存储在区块链之外,但其存在和操作历史锚定在区块链上。与其链上对应物一样,子域是全局唯一的、具有强所有权且人类可读。BNS 为它们提供各自独立的名称状态和公钥。与链上名称不同,子域可以低成本地创建和管理,因为它们是按批次广播到 BNS 网络中的。单笔区块链交易最多可以发送 120 个子域操作。

这是通过将子域记录存储在 BNS 名称的 zone 文件中实现的。链上名称所有者通过将子域操作编码为 DNS zone 文件中的 TXT 记录来进行广播。为了广播该 zone 文件,名称所有者使用一笔 NAME_UPDATE 交易设置新的 zone 文件哈希并复制该 zone 文件。反过来,这会复制其中包含的所有子域操作,并将这组子域操作锚定到一笔链上交易。BNS 节点的共识规则确保只有来自有效 NAME_UPDATE 交易的有效子域操作才会被存储。

例如,名称 verified.podcast 曾写入 zone 文件哈希 247121450ca0e9af45e85a82e61cd525cd7ba023,这是以下 zone 文件的哈希:

每条 TXT 该 zone 文件中的记录都编码了一个子域创建操作。例如, 1yeardaily.verified.podcast 解析为:

这些信息是从 1yeardaily TXT 的 zone 文件中的资源记录提取出来的,用于 verified.podcast.

子域生命周期

1

创建

子域创建操作由子域所有者创建,并编码到链上名称所有者 zone 文件中的一条 TXT 记录中。链上名称所有者通过发出一笔 NAME_UPDATE 交易来广播该 zone 文件,从而将该子域创建锚定到链上。

2

更新

子域更新通过使用子域所有者私钥创建已签名操作在链下完成。任何链上名称所有者都可以将这些已签名操作包含到其 zone 文件中,并通过 NAME_UPDATE进行广播。操作按序列号排序,并要求具有有效签名,该签名关联到前一个操作的公钥。

3

转移

要更改拥有子域的地址(公钥哈希),子域所有者需签署一个子域转移操作,并请求一位链上名称所有者(通常是创建该子域的人)通过 NAME_UPDATE进行广播。进行广播的链上名称所有者的 zone 文件必须存在于 Atlas 网络中,以证明不存在冲突操作。

序列和验证规则

  • 子域操作按序列号排序,从 0 开始。每个新操作都必须包含:

    • 下一个序列号

    • 其哈希值等于前一笔子域交易地址的公钥

    • 对应私钥对整个子域操作的签名

  • 如果两个签名正确但相互冲突的操作具有相同序列号,则区块链历史中更早的那个会被接受。无效操作会被忽略。

子域创建和管理规则

  • 子域创建交易只能由与其后缀共享同一链上名称的所有者处理(例如,只有 res_publica.id 的所有者才能为 *.res_publica.id).

  • 广播创建操作。

  • 要发送子域创建或子域转移,链上名称所有者的所有 zone 文件都必须存在于 Atlas 网络中。这使得证明不存在冲突操作成为可能。

  • 子域更新可以由任何链上名称所有者广播,但子域所有者需要找到愿意合作的链上名称所有者来包含并广播它。

要创建子域,子域所有者生成创建操作并将其交给链上名称所有者。创建完成后,子域所有者可以通过提供打包进 zone 文件中的已签名操作,使用任何链上名称所有者来广播更新。

子域注册器

由于子域名称成本低廉,开发者可以为其应用运行子域注册器。例如,名称 personal.id 被用于注册用户名,而无需用户花费比特币。

有一个参考实现可用:https://github.com/stacks-network/subdomain-registrar。用户仍然拥有其子域名称;注册器帮助开发者广播子域操作。

BNS 与 DID 标准

BNS 名称符合新兴的去中心化身份基金会(DIF)关于去中心化标识符(DID)的协议规范:http://identity.foundation

BNS 中的每个名称都有一个关联的 DID。BNS 的 DID 格式为:

其中:

  • {address} 是链上的公钥哈希(例如比特币地址)。

  • {index} 指的是 第 n 个 由该地址创建的名称。

示例:

  • personal.iddid:stack:v0:1dARRtzHPAFRNE7Yup2Md9w18XEQAtLiV-0 (由该地址创建的第一个名称)

  • jude.iddid:stack:v0:16EMaNw3pkn3v6f2BgnSSs53zAKH4Q8YJg-1 (该地址在此之前已经创建过一个名称)

用途:DID 为公钥提供一个永久标识符。公钥可以变化,但 DID 不会。

要让 DID 可被解析,对于某个名称,以下条件必须全部成立:

  • 该名称必须存在

  • 该名称的 zone 文件哈希必须是格式良好的 DNS zone 文件的哈希

  • 该 DNS zone 文件必须存在于 Stacks 节点的数据中

  • 该 DNS zone 文件必须包含一个 URI 资源记录,其指向一个已签名的 JSON Web Token

  • 为该 JSON Web Token 签名的公钥(并且随其一起包含)必须哈希到拥有该名称的地址

并非所有名称都会有可解析为公钥的 DID。通过标准工具创建的名称会有这样的 DID。

一个 RESTful API 正在开发中。

子域的 DID 编码

BNS 中的每个名称和子域都有一个 DID。编码方式不同,以便软件能够确定应采用哪条代码路径。

  • 对于链上 BNS 名称, {address} 与拥有该名称的比特币地址相同。目前,同时支持版本字节 0 和版本字节 5 的地址(以 13开头的地址,分别表示 p2pkhp2sh 地址)。

  • 对于链下 BNS 子域, {address} 对于由单个私钥拥有的子域,其版本字节为 63;对于由 m-of-n 私钥集合拥有的子域,其版本字节为 50。也就是说,子域 DID 地址分别以 SM开头。

这个 {index} 子域 DID 的 {index} 字段不同于 BNS 名称 DID 的

  • 字段,即使同一地址创建了名称和子域也是如此。示例: abcdefgh123456.iddid:stack:v0:16EMaNw3pkn3v6f2BgnSSs53zAKH4Q8YJg-0 (由该地址创建的第一个名称)

  • 子域 jude.statism.id 由同一地址创建 → did:stack:v0:SSXMcDiCZ7yFSQSUj7mWzmDcdwYhq97p2i-0

注意:地址 SSXMcDiCZ7yFSQSUj7mWzmDcdwYhq97p2i 编码的是与 16EMaNw3pkn3v6f2BgnSSs53zAKH4Q8YJg相同的公钥哈希——区别在于 base58check 版本字节(63 对 0)。

最后更新于

这有帮助吗?