# NIP01

## 基本协议流程描述

`draft` `mandatory` `author:fiatjaf` `author:distbit` `author:scsibug` `author:kukks` `author:jb55`

这个 NIP 定义了每个人都应该执行的基本协议。新的 NIP 可以向此处描述的结构和流程添加新的可选（或强制）字段、消息和功能。

## 事件和签名

每个用户都有一个密钥对。根据 \[Schnorr Signatures Standard for the Curve `secp256k1`]（https：//bips.xyz/340）完成签名、公钥和编码。

唯一存在的对象类型是 `event`，它在网络上具有以下格式：

```json
{
  "id": <32-bytes lowercase hex-encoded sha256 of the serialized event data>
  "pubkey": <32-bytes lowercase hex-encoded public key of the event creator>,
  "created_at": <unix timestamp in seconds>,
  "kind": <integer>,
  "tags": [
    ["e", <32-bytes hex of the id of another event>, <recommended relay URL>],
    ["p", <32-bytes hex of a pubkey>, <recommended relay URL>],
    ... // other kinds of tags may be included later
  ],
  "content": <arbitrary string>,
  "sig": <64-bytes hex of the signature of the sha256 hash of the serialized event data, which is the same as the "id" field>
}
```

为了获得 `event.id`，我们 `sha256` 将事件序列化。序列化是在以下结构的 UTF-8 JSON 序列化字符串（没有空格或换行符）上完成的：

```json
[
  0,
  <pubkey, as a (lowercase) hex string>,
  <created_at, as a number>,
  <kind, as a number>,
  <tags, as an array of arrays of non-null strings>,
  <content, as a string>
]
```

## 客户端与中继之间的通信

中继公开客户端可以连接到的 WebSocket 端点。

### 从客户端到中继：发送事件和创建订阅

客户端可以根据以下模式发送 3 种类型的消息，这些消息必须是 JSON 数组：

* `["EVENT", <event JSON as defined above>]`，用于发布事件。
* `["REQ", <subscription_id>, <filters JSON>...]`，用于请求事件和订阅新的更新。
* `["CLOSE", <subscription_id>]`，用于停止以前的订阅。

`<subscription_id>` 是任意的非空字符串，最大长度为 64 个字符，用于表示订阅。

`<filters>` 是一个 JSON 对象，用于确定将在该订阅中发送哪些事件，它可以具有以下属性：

```json
{
  "ids": <a list of event ids or prefixes>,
  "authors": <a list of pubkeys or prefixes, the pubkey of an event must be one of these>,
  "kinds": <a list of a kind numbers>,
  "#e": <a list of event ids that are referenced in an "e" tag>,
  "#p": <a list of pubkeys that are referenced in a "p" tag>,
  "since": <an integer unix timestamp, events must be newer than this to pass>,
  "until": <an integer unix timestamp, events must be older than this to pass>,
  "limit": <maximum number of events to be returned in the initial query>
}
```

在接收 `REQ` 消息时，中继应查询其内部数据库并返回与过滤器匹配的事件，然后存储该过滤器并再次将其接收的所有未来事件发送到同一 WebSocket，直到 WebSocket 关闭。 `CLOSE` 接收相同 `<subscription_id>` 的事件或使用相同 `<subscription_id>` 的事件发送新 `REQ` 事件，在这种情况下，它应覆盖以前的订阅。

包含列表的筛选器属性（如 `ids`、 `kinds` 或 `#e`）是具有一个或多个值的 JSON 数组。数组的值必须至少有一个与事件中的相关字段匹配，才能将条件本身视为匹配。对于标量事件属性（如） `kind`，事件中的属性必须包含在过滤器列表中。对于标记属性（例如 `#e`，其中一个事件可能有多个值），事件和筛选条件值必须至少有一个公共项。

`ids` 和 `authors` 列表包含小写的十六进制字符串，可以是 64 个字符的精确匹配，也可以是事件值的前缀。前缀匹配是指筛选器字符串是事件值的精确字符串前缀。前缀的使用允许在查询大量值的情况下使用更紧凑的过滤器，并且可以为可能不想公开其正在搜索的确切作者或事件的客户端提供一些隐私。

指定的过滤器的所有条件必须与事件匹配，事件才能通过过滤器，即多个条件被解释为 `&&` 条件。

一 `REQ` 封邮件可能包含多个筛选器。在这种情况下，将返回与任何过滤器匹配的事件，即，将多个过滤器解释为 `||` 条件。

筛选器的 `limit` 属性仅对初始查询有效，以后可以忽略。当 `limit: n` 存在时，假设在初始查询中返回的事件将是最新 `n` 事件。返回少于指定数量的事件 `limit` 是安全的，但是期望中继不会返回（太多）多于请求数量的事件，这样客户端就不会不必要地被数据淹没。

### 从中继到客户端：发送事件和通知

根据以下模式，中继可以发送 2 种类型的消息，这些消息也必须是 JSON 数组：

* `["EVENT", <subscription_id>, <event JSON as defined above>]`，用于发送客户端请求的事件。
* `["NOTICE", <message>]`，用于向客户端发送人类可读的错误消息或其他内容。

此 NIP 未定义应如何 `NOTICE` 发送或处理消息的规则。

只能使用与客户端先前启动的订阅相关的订阅 ID 发送 `EVENT` 消息（使用上面的 `REQ` 消息）。

## 基本事件种类

* `0`： `set_metadata`： `content` 设置为描述创建事件的用户的字符串化 JSON 对象 `{name: <username>, about: <string>, picture: <url, string>}`。一旦中继获得相同公钥的新事件，它就可以删除过去 `set_metadata` 的事件。
* `1`： `text_note`： `content` 设置为便笺的明文内容（用户想说的任何内容）。Markdown 链接（ `[]()` 内容）不是明文。
* `2`： `recommend_server`： `content` 设置为事件创建者希望向其追随者推荐的中继的 URL（例如 `wss://somerelay.com`）。

中继可以选择以不同的方式处理不同的消息类型，并且它可以选择也可以不选择使用默认方式来处理它不知道的类型。

## 其他说明：

* 客户端不应为每个中继打开多个 WebSocket.一个频道可以支持无限数量的订阅，所以客户端应该这样做。
* `tags` 数组可以将标签标识符存储为每个子数组的第一个元素，然后再加上任意信息（总是以字符串的形式）。此 NIP 定义了 `"p"` " pubkey "（指向事件中引用的某人的 pubkey）和 `"e"` " event "（指向该事件引用、回复或以某种方式引用的事件的 ID）。
* 和 `"p"` 标记上 `"e"` 的 `<recommended relay URL>` 项目是可选的（可以设置为 `""`）中继的 URL，客户端可以尝试连接以从标记的配置文件中获取标记的事件或其他事件。它可能会被忽略，但它的存在是为了增加审查阻力，并使中继地址在客户端之间的传播更加无缝。


---

# 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://sherry-pang.gitbook.io/nostr-cn/fu-lu-1-nip-xiang-jie/01.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.
