Closed15

話題になりそうな Nostr 触ってみた

Yuta IdeYuta Ide

動機

つい先日 Damus という分散型SNSが出てきました。
どうやら、Nostrという新しいプロトコルを使ってるみたいなので、何かに使えないかととりあえず触ってみました。

Yuta IdeYuta Ide

ここに色々まとまっているみたいです。
もう既にライブラリもたくさんあるみたいですね。すごいなぁ。

Yuta IdeYuta Ide

私は普段 typescript を書いているので nostr-react, nostr-tools あたりがいいかなと。
とりあえずここら辺から触ってみようと思います。

現在では Starが34と少なめなため古参ぶるためにスクショ貼っておきます。

Yuta IdeYuta Ide
  1. yarn create next-app --typescript nostr-test で next プロジェクトの作成
  2. cd nostr-test で移動して
  3. yarn add nostr-react で追加
Yuta IdeYuta Ide

NostrProviderで Wrap する必要があるみたいです。
その際に Relay サーバーを1つ以上指定する必要もあるとのこと。
今回はチュートリアルに書いてあるものをそのまま使用しています。

import "@/styles/globals.css";
import type { AppProps } from "next/app";
import { NostrProvider } from "nostr-react";

const relayUrls = ["wss://nostr-pub.wellorder.net", "wss://relay.nostr.ch"];

export default function App({ Component, pageProps }: AppProps) {
  return (
    <NostrProvider relayUrls={relayUrls} debug={true}>
      <Component {...pageProps} />
    </NostrProvider>
  );
}
Yuta IdeYuta Ide

適当なページを作成して、チュートリアルのコードを写経。
useNostrEventsで Nostr を使って送信されたメッセージ (Event) を取得できるみたいです。
その際にフィルターをかけることができ、今回は

  • 表示時以降に投稿
  • kinds が 1 ???
    のEventを持ってきている様です。
import { useRef } from "react";
import { useNostrEvents, dateToUnix } from "nostr-react";

const Test = () => {
  const now = useRef(new Date()); // Make sure current time isn't re-rendered

  const { events } = useNostrEvents({
    filter: {
      since: dateToUnix(now.current), // all new events from now
      kinds: [1],
    },
  });

  return (
    <>
      {events.map((event) => (
        <p key={event.id}>
          {event.pubkey} posted: {event.content}
        </p>
      ))}
    </>
  );
};

export default Test;
Yuta IdeYuta Ide

kind はどうやら使用しているチャンネル見たいなものっぽいです。多分。
ドキュメントは探し中。。。

Yuta IdeYuta Ide

というかこんな簡単にEventの取得ができちゃうことにびっくりです。

Yuta IdeYuta Ide

Nostr はProfile なるものを設定できます。
といっても,名前とアイコン画像ぐらいではあります。
そのためのIDとして公開鍵が使用されています。
秘密鍵についてはパスワードとして使用されています。
ここら辺はDatum などを触ってみると実感できるかと

Yuta IdeYuta Ide

この公開鍵を使って Profile を取得できるみたいなんですが、どうもうまくいかないですね。
Datum で作成した時の公開鍵を使用するとError: hexToBytes: received invalid unpadded hex63と怒られます。
どうやら文字列だとダメで16進数にしないといけないみたいです。
単純に文字列を16進数に変換して試してみるとErrorは出ませんが、ユーザ名等は表示されません...

Yuta IdeYuta Ide

どうやらその公開鍵のユーザはいないっぽいのですが、僕の公開鍵のはずなんですよね...
うーむ... とりあえず次に進みます。

Yuta IdeYuta Ide

Nostr Implementation Possibilities; NIP と呼ばれる実装上の注意点的なものがあるらしく,
どうやらNIP19にencode 方法について書かれていました。。。お恥ずかしい。

しかし、 Profile はうまく表示されない模様。。。
うむむ...

Yuta IdeYuta Ide

メッセージの送信は滞りなくできました。
ほぼチュートリアルのコピペですが、イベントを作成して Relay に送信するだけでOK。
1点注意なのが、Datum で得られる秘密鍵については、先ほどの NIP19 での decode が必要な点ですね。

import { useNostr, dateToUnix } from "nostr-react";

import {
  type Event as NostrEvent,
  getEventHash,
  getPublicKey,
  signEvent,
  nip19,
} from "nostr-tools";

const PostButton = () => {
  const { publish } = useNostr();

  const onPost = async () => {
    const privKey = prompt("Paste your private key:");

    if (!privKey) {
      alert("no private key provided");
      return;
    }

    const message = prompt("Enter the message you want to send:");

    if (!message) {
      alert("no message provided");
      return;
    }

    const { data: nsec } = nip19.decode(privKey);

    const event: NostrEvent = {
      content: message,
      kind: 1,
      tags: [],
      created_at: dateToUnix(),
      pubkey: getPublicKey(nsec.toString()),
    };

    event.id = getEventHash(event);
    event.sig = signEvent(event, nsec.toString());

    publish(event);
  };

  return <button onClick={onPost}>Post a message!</button>;
};

export default PostButton;
このスクラップは2023/02/06にクローズされました