Nostr NIP 協議詳解

Nostr 改進提案協議詳解

Nostr NIP 協議深度解析

什麼是 NIP

NIP(Nostr Implementation Possibilities)是 Nostr 協議的擴展規範,定義了客戶端和 relays之間交互的標準。每個 NIP 處理特定的用例或功能,確保不同客戶端的互操作性。

NIP 體系結構

協議層級

┌─────────────────────────────────────────┐
│           Application Layer             │
│        (Clients: Damus, Iris, etc.)     │
├─────────────────────────────────────────┤
│              NIP Layer                  │
│   (NIP-01, NIP-02, NIP-26, etc.)        │
├─────────────────────────────────────────┤
│          Base Protocol                  │
│            (NIP-01 核心)                │
├─────────────────────────────────────────┤
│          Transport Layer                │
│           WebSocket                     │
└─────────────────────────────────────────┘

核心 NIP 與擴展 NIP

類別NIP 編號描述
核心NIP-01基本協議
核心NIP-02聯繫人名單
核心NIP-04加密私信
擴展NIP-26委託登錄
擴展NIP-57閃電網絡 zaps
擴展NIP-65公告列表

核心 NIP 詳解

NIP-01:基礎協議

定義 Nostr 的基本結構:

{
  "kind": 1,
  "created_at": 167890,
  "content": "Hello Nostr!",
  "tags": [],
  "pubkey": "npub1...",
  "sig": "schnorr签名"
}

事件類型

Kind描述
0元數據(用戶資料)
1文字筆記
3關注列表
4加密私信
5刪除事件
6轉發
7點讚
8閒置

NIP-02:聯繫人名單

管理用戶的社交圖譜:

class ContactList:
    def __init__(self):
        self.contacts = []  # [(pubkey, relay_url, petname)]

    def add_contact(self, pubkey, relay_url=None, petname=None):
        """添加聯繫人"""
        self.contacts.append((pubkey, relay_url, petname))

    def to_event(self, kind=3):
        """轉換為 Nostr 事件"""
        content = json.dumps([
            [pubkey, relay_url, petname]
            for pubkey, relay_url, petname in self.contacts
        ])

        return Event(
            kind=kind,
            content=content,
            created_at=time.time()
        )

NIP-04:加密私信

端到端加密的私人訊息:

from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
import json

class DirectMessage:
    def __init__(self, sender_privkey, recipient_pubkey):
        self.sender_privkey = sender_privkey
        self.recipient_pubkey = recipient_pubkey

    def encrypt(self, message):
        """加密訊息"""
        # 生成臨時密鑰對
        ephemeral_key = ec.generate_private_key(ec.SECP256K1())

        # 計算共享密鑰
        shared_key = self.derive_shared_key(
            ephemeral_key,
            self.recipient_pubkey
        )

        # AES-GCM 加密
        ciphertext = encrypt_aes_gcm(shared_key, message)

        return {
            'epk': ephemeral_key.public_key().serialize(),
            'ciphertext': ciphertext
        }

    def decrypt(self, encrypted_data):
        """解密訊息"""
        # 恢復共享密鑰
        shared_key = self.derive_shared_key(
            self.sender_privkey,
            encrypted_data['epk']
        )

        # 解密
        return decrypt_aes_gcm(shared_key, encrypted_data['ciphertext'])

重要擴展 NIP

NIP-26:委託登錄

允許用戶委託其他密鑰發布事件:

class Delegation:
    def __init__(self, delegator_privkey):
        self.delegator_privkey = delegator_privkey
        self.delegator_pubkey = privkey_to_pubkey(delegator_privkey)

    def create_delegation(self, delegate_pubkey, conditions):
        """
        創建委託權限
        conditions: 如 {"kind": 1}
        """
        # 委託 token
        token = json.dumps({
            'delegate': delegate_pubkey,
            'conditions': conditions,
            'created_at': time.time()
        })

        # 委託者簽名
        signature = sign(self.delegator_privkey, token)

        return {
            'token': token,
            'sig': signature
        }

    def sign_as_delegate(self, delegate_privkey, delegation, event):
        """使用委託權限簽名"""
        # 驗證委託有效性
        verify_delegation(delegation)

        # 委託者公鑰作為發布者
        event.pubkey = delegation.delegate_pubkey
        event.delegation = delegation

        # 使用委託者密鑰簽名(不是 delegate)
        event.sig = sign(self.delegator_privkey, event.serialize())

NIP-57:閃電網絡 Zaps

支持比特幣閃電網絡打賞:

class Zap:
    def __init__(self, client):
        self.client = client
        self.relays = get_zap_enabled_relays()

    def create_zap_request(self, recipient_pubkey, amount, content, lnurl):
        """
        創建 Zap 請求
        """
        # 生成隨機 bolt11 請求
        invoice = self.request_invoice(
            amount,
            recipient_pubkey,
            content
        )

        # 創建 NIP-57 事件 (kind 9734)
        zap_request = {
            'kind': 9734,
            'created_at': time.time(),
            'content': content,
            'tags': [
                ['p', recipient_pubkey],
                ['amount', str(amount)],  # millisats
                ['lnurl', lnurl],
                [' Bolt11 發票']
            ]
        }

        # 客戶端簽名
        zap_request['sig'] = self.client.sign(zap_request)

        return zap_request

    def process_zap(self, zap_event):
        """
        處理收到的 Zap
        """
        # 驗證事件有效性
        verify_event(zap_event)

        # 獲取付款證明
        payment_preimage = self.wait_for_payment(zap_event['id'])

        # 發布確認事件 (kind 9735)
        zap_receipt = {
            'kind': 9735,
            'created_at': time.time(),
            'tags': [
                ['e', zap_event['id']],
                ['p', zap_event['pubkey']],
                ['relays', self.relays]
            ],
            'content': payment_preimage  # 付款原像
        }

        self.client.publish(zap_receipt)

NIP-65:公告列表

管理用戶首選的 relays:

class RelayList:
    def __init__(self):
        self.read_relays = []
        self.write_relays = []

    def add_read_relay(self, url):
        """添加讀取 relay"""
        if url not in self.read_relays:
            self.read_relays.append(url)

    def add_write_relay(self, url):
        """添加寫入 relay"""
        if url not in self.write_relays:
            self.write_relays.append(url)

    def to_event(self):
        """轉換為 NIP-65 事件"""
        content = json.dumps({
            'read': self.read_relays,
            'write': self.write_relays
        })

        return Event(
            kind=10002,
            content=content,
            created_at=time.time()
        )

其他重要 NIP

NIP-09:刪除事件

{
  "kind": 5,
  "created_at": 167890,
  "content": "Reason for deletion",
  "tags": [
    ["e", "event_id_to_delete"]
  ]
}

NIP-10:對事件的回覆

在 ["e"] 標籤中使用 "reply" 標記:
["e", "parent_event_id", "relay_url", "reply"]

NIP-12:通用標籤查詢

允許 relays 支援更靈活的過濾:

{
  "kinds": [1],
  "#t": ["bitcoin", "nostr"]
}

NIP-15:結束事件

定義事件傳播的結束:

{
  "kind": 7,
  "created_at": 0,
  "content": "EOSE",
  "tags": []
}

NIP-20:事件結果

{
  "kind": 2000,
  "content": "duplicate",
  "tags": [
    ["e", "event_id"]
  ]
}

客戶端實現指南

1. 基本客戶端結構

class NostrClient:
    def __init__(self, private_key):
        self.private_key = private_key
        self.public_key = private_to_public(private_key)
        self.relays = {}

    def connect(self, relay_url):
        """連接到 relay"""
        ws = websocket.WebSocketApp(
            relay_url,
            on_message=self.on_message,
            on_error=self.on_error,
            on_close=self.on_close
        )
        self.relays[relay_url] = ws

    def publish(self, event):
        """發布事件"""
        for relay in self.relays.values():
            relay.send(json.dumps(event))

    def subscribe(self, subscription_id, filters):
        """訂閱事件"""
        msg = ["REQ", subscription_id, filters]
        for relay in self.relays.values():
            relay.send(json.dumps(msg))

2. 事件處理

def on_message(self, ws, message):
    """處理收到的消息"""
    msg = json.loads(message)

    msg_type = msg[0]

    if msg_type == 'EVENT':
        # 新事件
        subscription_id = msg[1]
        event = msg[2]
        self.handle_event(event)

    elif msg_type == 'EOSE':
        # 訂閱結束
        subscription_id = msg[1]
        self.on_subscription_complete(subscription_id)

    elif msg_type == 'OK':
        # 事件已接受
        event_id = msg[1]
        success = msg[2]

NIP 開發最佳實踐

1. 兼容性

2. 安全性

3. 性能

未來 NIP 提案

正在討論

實驗性

總結

NIP 協議是 Nostr 生態系統的支柱,為去中心化社交網絡提供了標準化的互操作性。從基礎的發布/訂閱到複雜的閃電網絡打賞,每個 NIP 都擴展了協議的功能。開發者應熟悉核心 NIP,並根據應用需求選擇實現相應的擴展。隨著生態系統的發展,更多 NIP 將被提出和標準化,推動去中心化社交網絡的持續創新。

風險提示

延伸閱讀與來源

這篇文章對您有幫助嗎?

評論

發表評論

注意:由於這是靜態網站,您的評論將儲存在本地瀏覽器中,不會公開顯示。

目前尚無評論,成為第一個發表評論的人吧!