Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Nostrのリレーから漏れなくすべてのイベントを取ってくる技術
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
jiftechnify
March 10, 2023
Technology
730
0
Share
Nostrのリレーから漏れなくすべてのイベントを取ってくる技術
How to fetch all events from Nostr relays
jiftechnify
March 10, 2023
More Decks by jiftechnify
See All by jiftechnify
Functional Programming in Scala 第2版 読書のすゝめ
jiftechnify
1
530
Start Nostr Indie Dev for Great Good
jiftechnify
0
220
Cryptography 101 for Understanding Nostr
jiftechnify
0
410
Thinking about Feasibility of Scheduled Posts on Nostr
jiftechnify
0
300
Other Decks in Technology
See All in Technology
見えない開発現場を、見える投資に変える
rojoudotcom
2
150
GitHub Copilotを極める会 - 開発者のための活用術
findy_eventslides
6
3.8k
第26回FA設備技術勉強会 - Claude/Claude_codeでデータ分析 -
happysamurai294
0
400
NgRx SignalStore: The Power of Extensibility
rainerhahnekamp
0
170
スクラムを支える内部品質の話
iij_pr
0
350
試されDATA SAPPORO [LT]Claude Codeで「ゆっくりデータ分析」
ishikawa_satoru
0
340
仕様通り動くの先へ。Claude Codeで「使える」を検証する
gotalab555
8
3.1k
Hooks, Filters & Now Context: Why MCPs Are the “Hooks” of the AI Era
miriamschwab
0
130
新メンバーのために、シニアエンジニアが環境を作る時代
puku0x
0
460
DIPS2.0データに基づく森林管理における無人航空機の利用状況
naokimuroki
0
170
ZOZOTOWNリプレイスでのSkills導入までの流れとこれから
zozotech
PRO
4
3.2k
Oracle Cloud Infrastructure(OCI):Onboarding Session(はじめてのOCI/Oracle Supportご利⽤ガイド)
oracle4engineer
PRO
2
17k
Featured
See All Featured
16th Malabo Montpellier Forum Presentation
akademiya2063
PRO
0
93
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
38
2.8k
Prompt Engineering for Job Search
mfonobong
0
250
Crafting Experiences
bethany
1
110
We Analyzed 250 Million AI Search Results: Here's What I Found
joshbly
1
1.1k
Skip the Path - Find Your Career Trail
mkilby
1
100
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
141
35k
What's in a price? How to price your products and services
michaelherold
247
13k
Building a A Zero-Code AI SEO Workflow
portentint
PRO
0
430
The Anti-SEO Checklist Checklist. Pubcon Cyber Week
ryanjones
0
110
Collaborative Software Design: How to facilitate domain modelling decisions
baasie
0
190
The browser strikes back
jonoalderson
0
900
Transcript
Nostrのリレーから 漏れなくすべてのイベントを 取ってくる技術 かすてらふぃ @jiftechnify Nostr勉強会 #1
自己紹介 かすてらふぃ npub168ghgug469n4r2tuyw05dmqhqv5jcwm7nxytn67afmz8qkc4a4zqsu2dlc NIP-05:
[email protected]
Twitter: @cstl_fy GitHub: jiftechnify 職業:
フルスタックソフトウェアエンジニア(?) 趣味: 打楽器演奏、音楽ゲーム
TL; DR 「 limit を指定しなければ過去のイベント全部取れる」は大間違い 確実に過去の全イベントを取得するには… リレー実装ごとの細かな挙動の違いを把握した上で 時間を遡るように繰り返しイベントの取得を行う必要がある ↑をいい感じにやるライブラリを作った
事の発端 みんななんか作っとる、何も作ってないのはお前だけ → フォロー整理アプリ作るか〜 → フォロイーがアクティブかどうか知りたい 最終投稿時刻 過去 n 日間の投稿数
→ 特定の人が過去に送った投稿を全部取得した上で集計する必要がある
リレー イベント取得 クライアント Nostrの基本
イベント取得の流れ 購読 (subscription): リレーから特定の条件を満たすイベントを取得する手続き 1. リレーに REQ メッセージを送信 取得したいイベントの条件を表すフィルタを指定 2.
リレーから条件に一致する過去のイベントがまとめて送られてくる 3. リレーから EOSE メッセージが送られてくる End Of Stored Events 過去のイベントはここまでよ、という合図 4. 以降、リレーから条件に一致するイベントがリアルタイムに送られてくる
フィルタ // REQ メッセージの中身 ["REQ", "sub_id", {"authors": [...], "kinds": [...],
...}] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ フィルタ フィールド 条件 ids イベントID authors イベント発行者 kinds イベントの種類(例: 0 = プロフィール, 1 = 投稿) #x 特定のタグの値(例: #p → p タグの値) since / until イベント発行時刻( since 以降 until 以前) limit 過去のイベントの最大取得数
全取得など朝飯前…? limit を指定しなければ、過去イベントの取得数を制限しないことになる → 過去のイベントを全部取得できる? 世の中そんなに甘くない リレーによって挙動が全然違う! 期待通りにすべてのイベントが返ってきたり 明らかに返ってくるイベントの数が少なすぎたり 新しい順に返ってきたり、古い順に返ってきたり
仕様と実装、あるいは理想と現実 仕様: 「Nostrリレーを名乗るからには、こんな風に動いてくれ!」というルール リレーの動作に関係するNIPs: -01, -15( EOSE ) など 実装:
だいたい仕様通りの動きをするソフトウェア Nostrリレー実装の例: nostr-rs-relay, nostream, strfry など 仕様に書かれていない部分については、実装側の都合のいいように作っていい 特に、Nostrプロトコルは全体的に仕様がゆるい → その結果がこのありさまだよ!!!
敵を知る 今のところ、確実にすべてのイベントを取得するには、 リレー実装ごとの細かな挙動の違いを知る必要がある。 とはいえ、すべての実装を調べるのは数が多すぎて困難 → よく使われているリレー実装に絞って調査
三大リレー実装 nostr.watchの統計ページによると、nostr-rs-relay, nostream の2つが圧倒的。 strfry は採用絶対数は少ないものの、利用者が多いリレーでの採用実績が目立つ relay.damus.io nos.lol nostr.wine
三大リレー実装の実装詳細 (1) limit を指定した場合の動作 nostr-rs-relay 指定数のイベントを、新しい順に返す nostream 指定数のイベントを、新しい順に返す limit が5000を超える場合、
REQ を送信した時点でエラー 上限値 5000 は固定 strfry 指定数と「500件」のうち少ない方の数のイベントを、新しい順に返す 「500件」はデフォルト設定の値で、変更可能
三大リレー実装の実装詳細 (2) limit を指定しない場合の動作 nostr-rs-relay すべてのイベントを、古い順に返す nostream 500件のイベントを、古い順に返す 「500件」という数字は固定 strfry
500件のイベントを、新しい順に返す 「500件」はデフォルト設定の値で、変更可能
三大リレー実装の実装詳細 (3) 以上より、過去のイベントの取得に関して信頼できる動作は… limit を指定すれば、最近のイベントが(新しい順に)返ってくる だけ。 実は、NIP-01でさらっと明言されている When limit: n
is present it is assumed that the events returned in the initial query will be the latest n events.
三大リレー実装の実装詳細 (4) since / until に関する動作(取得対象となるイベントの範囲) nostr-rs-relay since < created_at
< until nostream since ≦ created_at ≦ until strfry since ≦ created_at ≦ until
漏れなくすべてのイベントを取得するアルゴリズム 1. フィルタの until を現在時刻、 limit を5000に設定して 購読を行い、 EOSE までのイベントを取得
最新のイベントを最大5000件取得できる 2. 前回新しく取得したイベントのうち、最も古いものの時刻を求める 3. フィルタの until を 2. で求めた時刻 + 1 、 limit を5000に設定して 購読を行い、 EOSE までのイベントを取得 「前回取得したイベント中で最も古いもの」より前のイベントのうち、 最新の最大5000件を取得 4. 2., 3. を新しいイベントが取得できなくなるまで繰り返す
クライアント リレー 保存済みイベント 最古 最新 1回め ["REQ", "sub_id", { "until":
< 現在時刻>, "limit": 5000, } ]
クライアント リレー 保存済みイベント 最古 最新 ["REQ", "sub_id", { "until": <
現在時刻>, "limit": 5000, } ] 1回め 最新‧最⼤5000 件のイベント
クライアント リレー 保存済みイベント 最古 最新 ["REQ", "sub_id", { "until": <1
回めの最古>, "limit": 5000, } ] 2回め
クライアント リレー 保存済みイベント 最古 最新 ["REQ", "sub_id", { "until": <1
回めの最古>, "limit": 5000, } ] 2回め 1 回めより古い中で最新‧ 最⼤5000 件のイベント
クライアント リレー 保存済みイベント 最古 最新 ["REQ", "sub_id", { "until": <(n-1)
回めの最古>, "limit": 5000, } ] n回め
クライアント リレー 保存済みイベント 最古 最新 ["REQ", "sub_id", { "until": <(n-1)
回めの最古>, "limit": 5000, } ] n回め (n-1) 回めより古い中で最新‧ 最⼤5000 件のイベント
クライアント リレー 保存済みイベント 最古 最新 ["REQ", "sub_id", { "until": <n
回めの最古>, "limit": 5000, } ] (n+1)回め 新しいイベントなし → 取得完了
複数リレーの場合 先程のアルゴリズムを各リレーに対して実行し、結果を統合する リレーごとに持っているイベントは違うので、 繰り返しごとに指定する until もリレーごとに異なってくる → 複数のリレーに対し同一のフィルタで購読を行うしくみである nostr-tools の
SimplePool は、今回の用途には全く役に立たない!
nostr-fetch のご紹介 以上のアルゴリズムを1から実装するのはそれなりに大変なので… 「Nostrのリレーから漏れなくすべての過去のイベントを取ってくる」 ことに特化した TypeScript / JavaScript 向けライブラリ nostr-fetch
を作って公開しました(宣伝) npm https://www.npmjs.com/package/nostr-fetch GitHub https://github.com/jiftechnify/nostr-fetch import { NostrEvent, NostrFetcher } from 'nostr-fetch'; const fetcher = new NostrFetcher(); const events: NostrEvent[] = await fetcher.fetchAllEvents(...); const eventsIter = await fetcher.allEventsIterator(...); for await (const e of eventsIter) { // ... }
nostr-fetch の特長 過去の全イベント取得に加え、最新 n 件、最新 1 件のみの取得に対応 全イベントを回す AsyncIterator を返すAPI
( allEventsIterator ) 非同期に返ってくるイベントを for-await-of ループで直感的に処理できる 全イベント取得では、もちろん日時範囲の指定も可能 複数リレーからの取得に対応 (最低限のコネクション管理つき)
Future Works 購読に失敗した場合のリトライ レートリミットに引っかかった場合など 購読失敗時のエラーメッセージの仕様が標準化されてほしい… Developer Experienceの向上 アカウント(公開鍵)ごとの最新イベントを簡単に取れるようにしたい エラー処理、テスト… フォロー管理アプリ、過去のTLを遡るアプリを作る