# BNSv1（旧版）

{% hint style="info" %}
本页内容与旧版 BNS v1 有关。
{% endhint %}

比特币名称系统（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.podcast` 和 `muneeb.id`。只要包含它的命名空间已经存在，任何人都可以创建一个 BNS 名称。
* **BNS 子域。** 这些是其记录存储在链下、但整体锚定到区块链上的名称。这些名称的所有权和状态存在于 P2P 网络数据中。虽然 BNS 子域由独立的私钥拥有，但 BNS 名称所有者必须广播其子域状态。示例子域包括 `jude.personal.id` 和 `podsaveamerica.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 文件的哈希：

```bash
$TTL 3600
1yeardaily TXT "owner=1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH" "seqn=0" "parts=1" "zf0=JE9SSUdJTiAxeWVhcmRhaWx5CiRUVEwgMzYwMApfaHR0cC5fdGNwIFVSSSAxMCAxICJodHRwczovL3BoLmRvdHBvZGNhc3QuY28vMXllYXJkYWlseS9oZWFkLmpzb24iCg=="
2dopequeens TXT "owner=1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH" "seqn=0" "parts=1" "zf0=JE9SSUdJTiAyZG9wZXF1ZWVucwokVFRMIDM2MDAKX2h0dHAuX3RjcCBVUkkgMTAgMSAiaHR0cHM6Ly9waC5kb3Rwb2RjYXN0LmNvLzJkb3BlcXVlZW5zL2hlYWQuanNvbiIK"
10happier TXT "owner=1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH" "seqn=0" "parts=1" "zf0=JE9SSUdJTiAxMGhhcHBpZXIKJFRUTCAzNjAwCl9odHRwLl90Y3AgVVJJIDEwIDEgImh0dHBzOi8vcGguZG90cG9kY2FzdC5jby8xMGhhcHBpZXIvaGVhZC5qc29uIgo="
31thoughts TXT "owner=1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH" "seqn=0" "parts=1" "zf0=JE9SSUdJTiAzMXRob3VnaHRzCiRUVEwgMzYwMApfaHR0cC5fdGNwIFVSSSAxMCAxICJodHRwczovL3BoLmRvdHBvZGNhc3QuY28vMzF0aG91Z2h0cy9oZWFkLmpzb24iCg=="
359 TXT "owner=1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH" "seqn=0" "parts=1" "zf0=JE9SSUdJTiAzNTkKJFRUTCAzNjAwCl9odHRwLl90Y3AgVVJJIDEwIDEgImh0dHBzOi8vcGguZG90cG9kY2FzdC5jby8zNTkvaGVhZC5qc29uIgo="
30for30 TXT "owner=1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH" "seqn=0" "parts=1" "zf0=JE9SSUdJTiAzMGZvcjMwCiRUVEwgMzYwMApfaHR0cC5fdGNwIFVSSSAxMCAxICJodHRwczovL3BoLmRvdHBvZGNhc3QuY28vMzBmb3IzMC9oZWFkLmpzb24iCg=="
onea TXT "owner=1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH" "seqn=0" "parts=1" "zf0=JE9SSUdJTiBvbmVhCiRUVEwgMzYwMApfaHR0cC5fdGNwIFVSSSAxMCAxICJodHRwczovL3BoLmRvdHBvZGNhc3QuY28vb25lYS9oZWFkLmpzb24iCg=="
10minuteteacher TXT "owner=1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH" "seqn=0" "parts=1" "zf0=JE9SSUdJTiAxMG1pbnV0ZXRlYWNoZXIKJFRUTCAzNjAwCl9odHRwLl90Y3AgVVJJIDEwIDEgImh0dHBzOi8vcGguZG90cG9kY2FzdC5jby8xMG1pbnV0ZXRlYWNoZXIvaGVhZC5qc29uIgo="
36questionsthepodcastmusical TXT "owner=1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH" "seqn=0" "parts=1" "zf0=JE9SSUdJTiAzNnF1ZXN0aW9uc3RoZXBvZGNhc3RtdXNpY2FsCiRUVEwgMzYwMApfaHR0cC5fdGNwIFVSSSAxMCAxICJodHRwczovL3BoLmRvdHBvZGNhc3QuY28vMzZxdWVzdGlvbnN0aGVwb2RjYXN0bXVzaWNhbC9oZWFkLmpzb24iCg=="
_http._tcp URI 10 1 "https://dotpodcast.co/"
```

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

```json
{
  "address": "1MwPD6dH4fE3gQ9mCov81L1DEQWT7E85qH",
  "blockchain": "bitcoin",
  "last_txid": "d87a22ebab3455b7399bfef8a41791935f94bc97aee55967edd5a87f22cce339",
  "status": "registered_subdomain",
  "zonefile_hash": "e7acc97fd42c48ed94fd4d41f674eddbee5557e3",
  "zonefile_txt": "$ORIGIN 1yeardaily\n$TTL 3600\n_http._tcp URI 10 1 \"https://ph.dotpodcast.co/1yeardaily/head.json\"\n"
}
```

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

子域生命周期

{% stepper %}
{% step %}
**创建**

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

{% step %}
**更新**

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

{% step %}
**转移**

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

序列和验证规则

* 子域操作按序列号排序，从 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 格式为：

```bash
did:stack:v0:{address}-{index}
```

其中：

* `{address}` 是链上的公钥哈希（例如比特币地址）。
* `{index}` 指的是 `第 n 个` 由该地址创建的名称。

示例：

* `personal.id` → `did:stack:v0:1dARRtzHPAFRNE7Yup2Md9w18XEQAtLiV-0` （由该地址创建的第一个名称）
* `jude.id` → `did: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 的地址（以 `1` 或 `3`开头的地址，分别表示 `p2pkh` 和 `p2sh` 地址）。
* 对于链下 BNS 子域， `{address}` 对于由单个私钥拥有的子域，其版本字节为 63；对于由 m-of-n 私钥集合拥有的子域，其版本字节为 50。也就是说，子域 DID 地址分别以 `S` 或 `M`开头。

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

* 字段，即使同一地址创建了名称和子域也是如此。示例： `abcdefgh123456.id` → `did:stack:v0:16EMaNw3pkn3v6f2BgnSSs53zAKH4Q8YJg-0` （由该地址创建的第一个名称）
* 子域 `jude.statism.id` 由同一地址创建 → `did:stack:v0:SSXMcDiCZ7yFSQSUj7mWzmDcdwYhq97p2i-0`

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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.stacks.co/learn/zh/network-fundamentals/bitcoin-name-system/bnsv1-legacy.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
