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
jiftechnify
March 10, 2023
Technology
0
540
Nostrのリレーから漏れなくすべてのイベントを取ってくる技術
How to fetch all events from Nostr relays
jiftechnify
March 10, 2023
Tweet
Share
More Decks by jiftechnify
See All by jiftechnify
Start Nostr Indie Dev for Great Good
jiftechnify
0
150
Cryptography 101 for Understanding Nostr
jiftechnify
0
300
Thinking about Feasibility of Scheduled Posts on Nostr
jiftechnify
0
240
Other Decks in Technology
See All in Technology
データの信頼性を支える仕組みと技術
chanyou0311
6
1.7k
テストコード品質を高めるためにMutation Testingライブラリ・Strykerを実戦導入してみた話
ysknsid25
1
120
今、始める、第一歩。 / Your first step
yahonda
2
720
障害対応指揮の意思決定と情報共有における価値観 / Waroom Meetup #2
arthur1
4
390
福岡新卒エンジニアの会
teba_eleven
1
190
マイベストのデータ基盤の現在と未来 / mybest-data-infra-asis-tobe
mybestinc
2
2k
Deno+JSRでパッケージを作って公開する
askua
0
120
株式会社ログラス − エンジニア向け会社説明資料 / Loglass Comapany Deck for Engineer
loglass2019
3
28k
Lambdaと地方とコミュニティ
miu_crescent
2
280
利きプロセススケジューラ
sat
PRO
4
2.7k
Microsoft Intune アプリのトラブルシューティング
sophiakunii
1
420
信頼性に挑む中で拡張できる・得られる1人のスキルセットとは?
ken5scal
2
450
Featured
See All Featured
ReactJS: Keep Simple. Everything can be a component!
pedronauck
665
120k
Designing Experiences People Love
moore
138
23k
YesSQL, Process and Tooling at Scale
rocio
168
14k
Typedesign – Prime Four
hannesfritz
40
2.4k
Designing Dashboards & Data Visualisations in Web Apps
destraynor
229
52k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
31
2.7k
Learning to Love Humans: Emotional Interface Design
aarron
273
40k
Being A Developer After 40
akosma
86
590k
Building Better People: How to give real-time feedback that sticks.
wjessup
364
19k
How STYLIGHT went responsive
nonsquared
95
5.2k
Building Flexible Design Systems
yeseniaperezcruz
327
38k
Code Reviewing Like a Champion
maltzj
520
39k
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を遡るアプリを作る