Nostr NIP-26 委託機制與以太坊錢包登入完整指南

Nostr(Notes and Other Stuff Transmitted by Relays)是一種去中心化的社交媒體協議,其設計理念是創建一個抗審查、簡潔的社交網絡。NIP-26 是 Nostr 協議中定義委託機制的核心規範,它允許用戶將帳號控制權委託給其他公鑰,同時保持撤銷權限。

Nostr NIP-26 委託機制與以太坊錢包登入完整指南

概述

Nostr(Notes and Other Stuff Transmitted by Relays)是一種去中心化的社交媒體協議,其設計理念是創建一個抗審查、簡潔的社交網絡。NIP-26 是 Nostr 協議中定義委託機制的核心規範,它允許用戶將帳號控制權委託給其他公鑰,同時保持撤銷權限。

這種委託機制與以太坊錢包系統的整合正在開創新的 Web3 身份認證範式——用戶可以使用以太坊錢包直接控制 Nostr 帳號,或者將社交帳號委託給可信的第三方運營商,而無需暴露私鑰。

本文深入解析 NIP-26 委託機制的技術原理、實現方式,以及與以太坊錢包登入系統的整合實踐,幫助開發者和用戶理解這一正在興起的去中心化身份解決方案。

一、Nostr 協議基礎

1.1 Nostr 核心概念

Nostr 是一種極簡的去中心化社交協議,其設計遵循以下核心原則:

簡潔性

抗審查

互通性

1.2 Nostr 事件結構

Nostr 的核心數據單元是「事件」(Event):

{
  "id": "<32 字節的事件 ID>",
  "pubkey": "<32 字節的公鑰>",
  "created_at": "<Unix 時間戳>",
  "kind": "<事件類型>",
  "tags": "<標籤數組>",
  "content": "<訊息內容>",
  "sig": "<事件的 Ed25519 簽名>"
}

常見事件類型(kind)

kind 值說明
0用戶元數據(個人資料)
1短訊息(類似 Twitter 推文)
2推薦中繼器
3關注列表
4直接訊息(加密)
5刪除事件
6轉發
7點擊
8報價
30023長文內容

1.3 Nostr 密碼學基礎

Nostr 使用 Ed25519 橢圓曲線密碼學:

密鑰對

簽名

// 使用 nostr-tools 生成密鑰對
const { generatePrivateKey, getPublicKey, signEvent, verifySignature } = require('nostr-tools');

// 生成私鑰
const privateKey = generatePrivateKey();

// 派生公鑰
const publicKey = getPublicKey(privateKey);

// 創建事件
const event = {
  pubkey: publicKey,
  created_at: Math.floor(Date.now() / 1000),
  kind: 1,
  tags: [],
  content: 'Hello Nostr!',
  id: '',
  sig: ''
};

// 計算事件 ID
event.id = sha256(JSON.stringify([0, event.pubkey, event.created_at, event.kind, event.tags, event.content]));

// 簽名
event.sig = signEvent(event, privateKey);

// 驗證
const isValid = verifySignature(event);

二、NIP-26 委託機制詳解

2.1 NIP-26 核心概念

NIP-26 定義了 Nostr 中的委託(delegation)機制,允許用戶將其帳號的控制權委託給另一個密鑰,同時保持隨時撤銷的能力。

委託的典型應用場景

  1. 多設備同步
  1. 帳號運營
  1. 服務集成
  1. 安全隔離

2.2 委託結構

NIP-26 委託通過特殊的事件類型實現:

{
  "kind": 0,
  "created_at": 1234567890,
  "tags": [
    ["delegation", "<delegator_pubkey>", "<conditions>", "<delegator_signature>"]
  ],
  "content": "{\"name\": \"delegated_account\"}",
  "pubkey": "<delegate_pubkey>",
  "sig": "<delegate_signature>"
}

委託標籤結構

["delegation", <委派人公鑰>, <條件>, <委派人簽名>]

條件(conditions)

條件決定了被委託人可以執行哪些操作:

kind=1
或
created_at>1672531200
或
kind=1&created_at>1672531200

2.3 委託實現原理

委派人(Delegator)

  1. 生成條件表達式
  2. 對條件表達式簽名
  3. 將簽名提供給被委託人
// 委派人創建委託
const delegationCondition = 'kind=1';
const delegationSig = signEvent(
  { content: delegationCondition, pubkey: delegatorPubKey },
  delegatorPrivateKey
);

// 將委託授予被委託人
const delegationTag = [
  'delegation',
  delegatorPubKey,
  delegationCondition,
  delegationSig
];

被委託人(Delegate)

  1. 使用自己的私鑰對事件簽名
  2. 在事件中包含委託標籤
  3. 接收方驗證委託有效性
// 被委託人發布事件
const delegatedEvent = {
  pubkey: delegatePubKey,
  created_at: Math.floor(Date.now() / 1000),
  kind: 1,
  tags: [
    ['delegation', delegatorPubKey, 'kind=1', delegationSig]
  ],
  content: 'Hello from delegated account!',
  id: '',
  sig: ''
};

// 使用被委託人的私鑰簽名
delegatedEvent.sig = signEvent(delegatedEvent, delegatePrivateKey);

2.4 委託驗證邏輯

客戶端驗證委託事件的流程:

function verifyDelegatedEvent(event) {
  // 1. 查找委託標籤
  const delegationTag = event.tags.find(t => t[0] === 'delegation');
  if (!delegationTag) {
    return { valid: false, reason: 'No delegation tag' };
  }

  const [_, delegatorPubkey, condition, delegatorSig] = delegationTag;

  // 2. 驗證委派人簽名
  const conditionMessage = { content: condition, pubkey: delegatorPubkey };
  if (!verifySignature(conditionMessage, delegatorSig)) {
    return { valid: false, reason: 'Invalid delegator signature' };
  }

  // 3. 解析並驗證條件
  const eventConditions = parseConditions(event.kind, event.created_at);
  if (!evaluateCondition(condition, eventConditions)) {
    return { valid: false, reason: 'Conditions not met' };
  }

  // 4. 驗證被委託人簽名
  if (!verifySignature(event, event.sig)) {
    return { valid: false, reason: 'Invalid delegate signature' };
  }

  // 5. 確認被委託人公鑰有效
  // (可選) 檢查被委託人是否在黑名單中

  return { valid: true, delegator: delegatorPubkey };
}

function parseConditions(kind, created_at) {
  return {
    kind: kind,
    created_at: created_at
  };
}

function evaluateCondition(condition, eventConditions) {
  // 簡化的條件評估
  // 實際實現需要更複雜的解析器

  const parts = condition.split('&');
  for (const part of parts) {
    if (part.startsWith('kind=')) {
      const requiredKind = parseInt(part.split('=')[1]);
      if (eventConditions.kind !== requiredKind) {
        return false;
      }
    }
    if (part.startsWith('created_at>')) {
      const minTime = parseInt(part.split('>')[1]);
      if (eventConditions.created_at <= minTime) {
        return false;
      }
    }
  }

  return true;
}

2.5 委託的權限控制

時間限制

// 設置時間限制的委託條件
const timeLimitedCondition = 'created_at>1672531200&created_at<1704067200';
// 只能在 2023 年內有效的委託

類型限制

// 只允許發布短訊息
const kind1Only = 'kind=1';

// 只允許發布元數據
const metadataOnly = 'kind=0';

// 允許多種操作
const multiCondition = 'kind=0|kind=1|kind=30023';

組合限制

// 複雜條件示例
const complexCondition = `
  (kind=1 & created_at>1672531200)
  | (kind=0 & created_at>1680307200)
  | kind=30023
`;

三、以太坊錢包整合

3.1 整合架構概述

將以太坊錢包與 Nostr NIP-26 委託整合,可以實現:

  1. 以太坊登入 Nostr
  1. 帳號委託
  1. 跨平台身份

3.2 以太坊簽名驗證 Nostr

挑戰-回應機制

Nostr 客戶端可以使用以太坊錢包進行身份驗證:

// 1. 客戶端生成挑戰
const challenge = generateRandomChallenge(); // "Please sign this message to authenticate: <random>"

// 2. 用戶使用以太坊錢包簽名
async function signInWithEthereum() {
  const message = `Nostr Authentication\n\nChallenge: ${challenge}\n\nWallet: ${window.ethereum.selectedAddress}`;

  const signature = await window.ethereum.request({
    method: 'personal_sign',
    params: [message, window.ethereum.selectedAddress]
  });

  // 3. 發送到服務器驗證
  const response = await fetch('/api/auth/verify', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      address: window.ethereum.selectedAddress,
      signature,
      challenge
    })
  });

  return response.json();
}

// 4. 服務器驗證
async function verifyEthereumSignature(address, signature, challenge) {
  const message = `Nostr Authentication\n\nChallenge: ${challenge}\n\nWallet: ${address}`;
  const recoveredAddress = await ethers.utils.verifyMessage(message, signature);

  return recoveredAddress.toLowerCase() === address.toLowerCase();
}

3.3 從以太坊密鑰派生 Nostr 公鑰

雖然以太坊使用 secp256k1 曲線,而 Nostr 使用 Ed25519,但可以通過以下方式實現整合:

方案一:消息派生

// 從以太坊私鑰派生 Nostr 私鑰
const { utils } = require('ethers');

// 以太坊私鑰
const ethPrivateKey = "0x...";

// 使用確定性派生
const nostrPrivateKey = utils.keccak256(
  utils.solidityKeccak256(
    ['string', 'bytes'],
    ['Nostr Key Derivation', ethPrivateKey]
  )
);

// Nostr 公鑰
const nostrPublicKey = getPublicKey(nostrPrivateKey);

方案二:委託模式(推薦):

使用 NIP-26 委託,以太坊錢包作為委派人:

// 1. 生成 Nostr 設備密鑰(用於日常操作)
const devicePrivateKey = generatePrivateKey();
const devicePublicKey = getPublicKey(devicePrivateKey);

// 2. 以太坊錢包簽署委託條件
const delegationMessage = `Nostr Delegation\n\nDelegate: ${devicePublicKey}\nConditions: kind=1|kind=6|kind=7\nExpiry: 2027-01-01`;
const delegationSignature = await window.ethereum.request({
  method: 'personal_sign',
  params: [delegationMessage, window.ethereum.selectedAddress]
});

// 3. 設備密鑰可以使用委託發布內容
const delegatedEvent = {
  pubkey: devicePublicKey,
  created_at: Math.floor(Date.now() / 1000),
  kind: 1,
  tags: [
    ['delegation', ethAddress, 'kind=1|kind=6|kind=7', delegationSignature]
  ],
  content: 'Posted from device with Ethereum delegation!',
  id: '',
  sig: ''
};

delegatedEvent.sig = signEvent(delegatedEvent, devicePrivateKey);

3.4 NIP-98 HTTP 授權

NIP-98 定義了通過 HTTP 頭進行 Nostr 身份驗證的標準:

// 使用 NIP-98 HTTP 授權發布事件
async function publishWithNIP98(event, privateKey) {
  const eventJson = JSON.stringify(event);
  const signature = signEvent(event, privateKey);

  // 創建 HTTP 授權頭
  const authHeader = btoa(JSON.stringify({
    sig: signature,
    key: event.pubkey
  }));

  const response = await fetch('https://nostr.example.com/notes', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Nostr ${authHeader}`
    },
    body: eventJson
  });

  return response.json();
}

3.5 完整整合示例

以下是一個完整的以太坊錢包登入 Nostr 的實現架構:

// nostr-ethereum-integration.js

class NostrEthereumIntegrator {
  constructor() {
    this.ethereum = window.ethereum;
    this.signer = null;
    this.nostrPrivateKey = null;
    this.nostrPublicKey = null;
    this.delegationCondition = 'kind=1|kind=6';
    this.delegationExpiry = Math.floor(new Date('2027-01-01').getTime() / 1000);
  }

  // 初始化
  async initialize() {
    if (!this.ethereum) {
      throw new Error('Ethereum wallet not found');
    }

    // 連接錢包
    const accounts = await this.ethereum.request({
      method: 'eth_requestAccounts'
    });

    this.signer = accounts[0];
    console.log('Connected:', this.signer);
  }

  // 登入流程
  async login() {
    // 1. 生成 Nostr 臨時密鑰
    this.nostrPrivateKey = generatePrivateKey();
    this.nostrPublicKey = getPublicKey(this.nostrPrivateKey);

    // 2. 創建委託
    const delegation = await this.createDelegation();

    // 3. 存儲委託信息
    await this.storeDelegation(delegation);

    return {
      nostrPublicKey: this.nostrPublicKey,
      delegator: this.signer,
      delegation
    };
  }

  // 創建委託
  async createDelegation() {
    const condition = `${this.delegationCondition}&created_at<${this.delegationExpiry}`;
    const message = `Nostr Delegation\n\nDelegator: ${this.signer}\nDelegate: ${this.nostrPublicKey}\nConditions: ${condition}`;

    const signature = await this.ethereum.request({
      method: 'personal_sign',
      params: [message, this.signer]
    });

    return {
      delegator: this.signer,
      delegate: this.nostrPublicKey,
      condition,
      signature,
      created_at: Math.floor(Date.now() / 1000)
    };
  }

  // 發布事件(使用委託)
  async publishEvent(kind, content, tags = []) {
    const event = {
      pubkey: this.nostrPublicKey,
      created_at: Math.floor(Date.now() / 1000),
      kind,
      tags: [
        ['delegation',
          this.signer,
          this.delegationCondition,
          (await this.createDelegation()).signature
        ],
        ...tags
      ],
      content,
      id: '',
      sig: ''
    };

    event.id = sha256(JSON.stringify([
      0, event.pubkey, event.created_at,
      event.kind, event.tags, event.content
    ]));

    event.sig = signEvent(event, this.nostrPrivateKey);

    return event;
  }

  // 發布短訊息
  async publishNote(content) {
    return this.publishEvent(1, content);
  }

  // 發布元數據
  async publishMetadata(metadata) {
    return this.publishEvent(0, JSON.stringify(metadata));
  }
}

// 使用示例
const integrator = new NostrEthereumIntegrator();
await integrator.initialize();
await integrator.login();

// 發布訊息
const note = await integrator.publishNote('Hello Nostr from Ethereum!');
console.log('Published:', note.id);

四、安全考量

4.1 委託安全風險

條件繞過

過度授權

委託撤銷

4.2 以太坊錢包安全

簽名風險

私鑰保護

4.3 最佳實踐

委託管理

安全委託清單:
- [ ] 設定明確的條件範圍
- [ ] 設定合理的過期時間
- [ ] 定期審查和更新委託
- [ ] 準備緊急撤銷機制
- [ ] 監控帳號活動

錢包安全

錢包安全清單:
- [ ] 使用硬件錢包進行重要操作
- [ ] 啟用錢包的多重認證
- [ ] 定期備份錢包
- [ ] 驗證每個簽名請求
- [ ] 避免在公共場所使用錢包

五、主要應用與實現

5.1 Nostr 客戶端

支持 NIP-26 的主要 Nostr 客戶端:

客戶端平台NIP-26 支持以太坊登入
DamusiOS有限
AmethystAndroid有限
SnortWeb
NostrgramWeb
CoracleWeb

5.2 身份認證服務

提供以太坊-Nostr 整合的服務:

NIP-07 兼容錢包

登入服務

5.3 開發工具

// 開發相關的 JavaScript 庫
const libraries = {
  'nostr-tools': '核心 Nostr 功能',
  'nostr-hooks': 'React Hooks',
  '@nostr/	client': '客戶端庫',
  'ether-signer-nostr': '以太坊簽名轉換'
};

六、未來發展趨勢

6.1 標準化進展

NIP 擴展

跨平台身份

6.2 採用趨勢

增長驅動因素

挑戰

6.3 創新方向

ZK 證明整合

社交恢復

七、結論

NIP-26 委託機制為 Nostr 帶來了靈活的權限控制能力,而以太坊錢包的整合則開創了 Web3 身份的新範式。通過理解這些技術的原理和實現方式,開發者可以構建更加安全、用戶友好的去中心化應用。

對於普通用戶而言,這些技術意味著:

隨著標準的成熟和工具的完善,基於 Nostr 和以太坊的去中心化身份將在 Web3 生態中發揮越來越重要的作用。

參考資源

  1. NIP-26 Specification. github.com/nostr-protocol/nips/blob/master/nip-0026.md
  2. NIP-07 Specification. github.com/nostr-protocol/nips/blob/master/nip-0007.md
  3. NIP-98 Specification. github.com/nostr-protocol/nips/blob/master/nip-0098.md
  4. Nostr Protocol Documentation. nostr.how
  5. nostr-tools Library. github.com/nostr-protocol/nostr-tools

延伸閱讀

去中心化身份

DeSoc 協議

錢包安全

延伸閱讀與來源

這篇文章對您有幫助嗎?

評論

發表評論

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

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