nostr-tools
用于开发 Nostr 客户端的工具。
仅依赖 @scure 和 @noble 包。
本包仅提供低级功能。如果你需要更高级的功能,可以查看 Nostrify,或者如果你想要一个易于使用的全面解决方案,能够抽象出 Nostr 的复杂部分并为你做出决策,可以查看 NDK 和 @snort/system。
安装
npm install nostr-tools # 或 yarn add nostr-tools
如果使用 TypeScript,本包要求 TypeScript 版本 >= 5.0。
使用方法
生成私钥和公钥
import { generateSecretKey, getPublicKey } from 'nostr-tools/pure'
let sk = generateSecretKey() // `sk` 是一个 Uint8Array
let pk = getPublicKey(sk) // `pk` 是一个十六进制字符串
要获取十六进制格式的私钥,可以使用
import { bytesToHex, hexToBytes } from '@noble/hashes/utils' // 已经是安装的依赖
let skHex = bytesToHex(sk)
let backToBytes = hexToBytes(skHex)
创建、签名和验证事件
import { finalizeEvent, verifyEvent } from 'nostr-tools/pure'
let event = finalizeEvent({
kind: 1,
created_at: Math.floor(Date.now() / 1000),
tags: [],
content: 'hello',
}, sk)
let isGood = verifyEvent(event)
与中继交互
import { finalizeEvent, generateSecretKey, getPublicKey } from 'nostr-tools/pure'
import { Relay } from 'nostr-tools/relay'
const relay = await Relay.connect('wss://relay.example.com')
console.log(`已连接到 ${relay.url}`)
// 让我们查询一个存在的事件
const sub = relay.subscribe([
{
ids: ['d7dd5eb3ab747e16f8d0212d53032ea2a7cadef53837e5a6c66d42849fcb9027'],
},
], {
onevent(event) {
console.log('我们得到了想要的事件:', event)
},
oneose() {
sub.close()
}
})
// 让我们发布一个新事件,同时监控中继以获取它
let sk = generateSecretKey()
let pk = getPublicKey(sk)
relay.subscribe([
{
kinds: [1],
authors: [pk],
},
], {
onevent(event) {
console.log('收到事件:', event)
}
})
let eventTemplate = {
kind: 1,
created_at: Math.floor(Date.now() / 1000),
tags: [],
content: 'hello world',
}
// 这会在一个步骤中分配公钥、计算事件 ID 并签名事件
const signedEvent = finalizeEvent(eventTemplate, sk)
await relay.publish(signedEvent)
relay.close()
要在 Node.js 上使用此功能,你首先必须安装 ws
并调用类似以下的代码:
import { useWebSocketImplementation } from 'nostr-tools/pool'
// 或者如果你直接使用 Relay,可以使用 import { useWebSocketImplementation } from 'nostr-tools/relay'
import WebSocket from 'ws'
useWebSocketImplementation(WebSocket)
与多个中继交互
import { SimplePool } from 'nostr-tools/pool'
const pool = new SimplePool()
let relays = ['wss://relay.example.com', 'wss://relay.example2.com']
let h = pool.subscribeMany(
[...relays, 'wss://relay.example3.com'],
[
{
authors: ['32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245'],
},
],
{
onevent(event) {
// 这只会在第一次接收到事件时被调用一次
// ...
},
oneose() {
h.close()
}
}
)
await Promise.any(pool.publish(relays, newEvent))
console.log('至少发布到了一个中继!')
let events = await pool.querySync(relays, { kinds: [0, 1] })
let event = await pool.get(relays, {
ids: ['44e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245'],
})
使用 NIP-10 和 NIP-27 从内容中解析引用(提及)
import { parseReferences } from 'nostr-tools/references'
let references = parseReferences(event)
let simpleAugmentedContent = event.content
for (let i = 0; i < references.length; i++) {
let { text, profile, event, address } = references[i]
let augmentedReference = profile
? `<strong>@${profilesCache[profile.pubkey].name}</strong>`
: event
? `<em>${eventsCache[event.id].content.slice(0, 5)}</em>`
: address
? `<a href="${text}">[链接]</a>`
: text
simpleAugmentedContent.replaceAll(text, augmentedReference)
}
从 NIP-05 地址查询配置文件数据
import { queryProfile } from 'nostr-tools/nip05'
let profile = await queryProfile('jb55.com')
console.log(profile.pubkey)
// 输出:32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245
console.log(profile.relays)
// 输出:[wss://relay.damus.io]
要在 Node.js < v18 上使用此功能,你首先必须安装 node-fetch@2
并调用类似以下的代码:
import { useFetchImplementation } from 'nostr-tools/nip05'
useFetchImplementation(require('node-fetch'))
包含 NIP-07 类型
import type { WindowNostr } from 'nostr-tools/nip07'
declare global {
interface Window {
nostr?: WindowNostr;
}
}
生成 NIP-06 密钥
import {
privateKeyFromSeedWords,
accountFromSeedWords,
extendedKeysFromSeedWords,
accountFromExtendedKey
} from 'nostr-tools/nip06'
const mnemonic = 'zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong'
const passphrase = '123' // 可选
const accountIndex = 0
const sk0 = privateKeyFromSeedWords(mnemonic, passphrase, accountIndex)
const { privateKey: sk1, publicKey: pk1 } = accountFromSeedWords(mnemonic, passphrase, accountIndex)
const extendedAccountIndex = 0
const { privateExtendedKey, publicExtendedKey } = extendedKeysFromSeedWords(mnemonic, passphrase, extendedAccountIndex)
const { privateKey: sk2, publicKey: pk2 } = accountFromExtendedKey(privateExtendedKey)
const { publicKey: pk3 } = accountFromExtendedKey(publicExtendedKey)
编码和解码 NIP-19 代码
import { generateSecretKey, getPublicKey } from 'nostr-tools/pure'
import * as nip19 from 'nostr-tools/nip19'
让 sk = generateSecretKey()
让 nsec = nip19.nsecEncode(sk)
让 { type, data } = nip19.decode(nsec)
断言(type === 'nsec')
断言(data === sk)
让 pk = getPublicKey(generateSecretKey())
让 npub = nip19.npubEncode(pk)
让 { type, data } = nip19.decode(npub)
断言(type === 'npub')
断言(data === pk)
让 pk = getPublicKey(generateSecretKey())
让 relays = ['wss://relay.nostr.example.mydomain.example.com', 'wss://nostr.banana.com']
让 nprofile = nip19.nprofileEncode({ pubkey: pk, relays })
让 { type, data } = nip19.decode(nprofile)
断言(type === 'nprofile')
断言(data.pubkey === pk)
断言(data.relays.length === 2)
与 nostr-wasm
一起使用
nostr-wasm
是一个薄包装器,包装了编译为 WASM 的 libsecp256k1,仅用于哈希、签名和验证 Nostr 事件。
import { setNostrWasm, generateSecretKey, finalizeEvent, verifyEvent } from 'nostr-tools/wasm'
import { initNostrWasm } from 'nostr-wasm'
// 确保在应用开始调用 finalizeEvent 或 verifyEvent 之前这个 promise 已解决
initNostrWasm().then(setNostrWasm)
// 或使用 'nostr-wasm/gzipped' 甚至 'nostr-wasm/headless',
// 查看 https://www.npmjs.com/package/nostr-wasm 了解选项
如果你要使用 Relay
和 SimplePool
,你还必须导入 nostr-tools/abstract-relay
和/或 nostr-tools/abstract-pool
而不是默认值,然后通过传递 verifyEvent
来实例化它们:
import { setNostrWasm, verifyEvent } from 'nostr-tools/wasm'
import { AbstractRelay } from 'nostr-tools/abstract-relay'
import { AbstractSimplePool } from 'nostr-tools/abstract-pool'
import { initNostrWasm } from 'nostr-wasm'
initNostrWasm().then(setNostrWasm)
const relay = AbstractRelay.connect('wss://relayable.org', { verifyEvent })
const pool = new AbstractSimplePool({ verifyEvent })
这可能比默认使用的纯 JS noble 库和 nostr-tools/pure
更快。基准测试:
benchmark time (avg) (min … max) p75 p99 p995
------------------------------------------------- -----------------------------
• relay read message and verify event (many events)
------------------------------------------------- -----------------------------
wasm 34.94 ms/iter (34.61 ms … 35.73 ms) 35.07 ms 35.73 ms 35.73 ms
pure js 239.7 ms/iter (235.41 ms … 243.69 ms) 240.51 ms 243.69 ms 243.69 ms
trusted 402.71 µs/iter (344.57 µs … 2.98 ms) 407.39 µs 745.62 µs 812.59 µs
summary for relay read message and verify event
wasm
86.77x slower than trusted
6.86x faster than pure js
从浏览器使用(如果你不想使用打包工具)
<script src="https://unpkg.com/nostr-tools/lib/nostr.bundle.js"></script>
<script>
window.NostrTools.generateSecretKey('...') // 等等
</script>
管道
要开发 nostr-tools
,安装 just
并运行 just -l
查看可用的命令。
许可证
这是免费且不受限制的软件,发布到公共领域。通过向本项目提交补丁,你同意将此软件的任何和所有版权利益贡献给公共领域。
为此存储库做贡献
使用 NIP-34 将你的补丁发送到:
naddr1qq9kummnw3ez6ar0dak8xqg5waehxw309aex2mrp0yhxummnw3ezucn8qyt8wumn8ghj7un9d3shjtnwdaehgu3wvfskueqpzemhxue69uhhyetvv9ujuurjd9kkzmpwdejhgq3q80cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsxpqqqpmejdv00jq