nextjs(reactjs) + bitsocketに決めた話

先に要点

nextjs9.4時点でwebsocketサンプルが少ない。
一旦なんちゃってchat風のプロトタイプは出来た。
nextjs関係ない実装になったけど。

はじめに

昨今のbitcoinエンジニアはnodejsを使います。
ライブラリなどのサンプルもnodejsかpython
Deno(デューノ) v1がでたけどフレームワーク
などもまだなので、Twetchも使っているnextjsを
使うことにした。

プログラミング言語の情勢(特にjavascript)

いくつかの記事をみました。
https://note.com/erukiti/n/na654ad7bd9bb
日本でシェアのあるPHP。これがWebsocket系に弱い。
まぁjsでやればいいんだけど...。それどころか情勢を見ると、
PHPでよくね?第一筆頭のRubyは超急速にシェアを失っている。
フルスタック型?のプログラムがもうオワコンのようだ。

nodejs(nextjs)について

こういう便利ライブラリの知識が皆無。
確かこういうのあるよね?みたいな知識を育まないといけない。

npm install eventsource
npm install classnames
npm install sass
npm install remark remark-html
npm install date-fns
npm install gray-matter

一見遠回りだが、nodejsを用いてブロックチェーンツールを使った方が非常に楽で便利になる。恐らく1か月後には開発スピードが追い付く。サンプルもあるし、質問を投げても当然答えてくれる人が多い...と思う。unwriterもまだ古いライブラリを使っていると度々アラートを流しますがnodejsで構築してないと、どうにもこうにもbitsocketは使えないんですよ。

雑談

そうそうなんでこう舵を切ったかというと、unwtiterのツールが古いものから徐々に障害起こしたり、復旧もしぶしぶやっている感があり、新しいの使ってくれと毎回アナウンスしていたからです。そう、もはや先月使ったものがまともに動かない事態に(笑)。あなたはこの世界に飛び込みますか?mbが作成したbsvライブラリはv2を開発中で、そこも結構なインパクトになると思う。もう学習予定が山積みになっているし、共有したところで来月には動かなくなっているかもしれない。

それにnodejsは序章でbitcoinエンジニアはその先のbitcoin scriptでマイナーというスーパーコンピュータにプログラム処理を極力預けなくてはいけない。そこまでできないと、ワイブロになる。それブロックチェーンでなくてもいいよね?と。セキュリティ確保もここが肝だと思う。学習最終到達地点だと思う。最初にここだけやっても何も作れないので、ある程度作ってから、こここれで置き換えられるんじゃね?というやり方で、皆やっていくんじゃないかと妄想している。

実際世の中にあるビットコインブロックチェーンサービスはブロックチェーン使っているから処理が遅いアプリと、ブロックチェーンはごく一部で、使ってる風だから処理が遅くないアプリがほとんどです。これはマイナーのスペック向上と、コンテンツがマイナーをうまく利用する必要があります。

今回冒頭に記載したチャットもどきのプロトタイプはTwetchの投稿をリアルタイムで取得して表示する感じです。あとはテストで何かしらnextjsでブロックチェーンにデータをアップできたら、やり取りは成立します。

ビジネスで考えると合理的にユーザへのサービスを向上(UX)させるのですが、趣味と思想と学習を兼ねて作りたいので、フルブロックチェーンを目指していきたい。

追記

勉強する前に宣言するからこうなるんだが、nextjsはサーバサイドjsなので、websocketやeventsourceとは相性が当然の事悪い。チャットなのが特にそうで、たまたまチャットで始めてしまったが、幸か不幸か早々壁にぶち当たった。ブログや掲示板程度ならよいが、相互疎通系非同期系はnextjsなしのreactでいくか、場合によってはjqueryの方が良い。

OP_PUSH_TX

ロックスクリプトコントラクトをデータとコードの2つの部分に分割する

OP_PUSH_TXを使用すると...
コードはメソッドであり、データはオブジェクトのメンバー変数...

要はロック解除すればデータは更新できる。
サンプルとしてカウンターが紹介されています(testnetです)。

medium.com

github.com

ロック解除する権限があれば、数字を更新したtxを生み出せる。
が、今のところここだけなのでまだピンと来ない。これから画像をセピアにしたり、辞書を引くみたいに構築できるのか?という話です。bitcoin scriptを用いないなら出来るんですが、bitcoin scriptで出来たらヤバい。

いまのところ例えばjqueryブロックチェーンにアップしてそれを参照するとかはできる。でもプログラムの実行自体は閲覧ブラウザのパソコンや運営サーバで処理しないといけない。もしbitcoin scriptで出来るのであればブロックチェーン上でプログラム処理済みのものが返ってくる。

...聞いてみようかな。将来的にできるものなのか...限界があるのか...大事よね...

bitbus2.0を使う

_unwriter氏が直近推している。というか他が手に回らないので、最新のツールを使ってくれという感じでいつも言っている。
性能もいいみたいだから使おう。なによりクエリーで$gtとか仕様上は使えるはずなのにgenecisで今は使えないので、bitbusでいけるか試す必要がある。まぁ使えなくても何とかなる気がするからいいんだけども。

docs.bitbus.network

これまで通りトークンを取得して、APIなのでどんなプラットフォームでもいけますよという超便利なビットコインクローラーです。
ドキュメントでいきなり"blk.i": { "$gt": 609000 }とあって動いてるgifがあるのでまぁーいけるでしょう!!

というかいままで

https://genesis.bitdb.network/q/1FnauZ9aUH2Bex6JzdcV4eNX7oLSSEbxtN/...

だったのを

https://txo.bitbus.network/block

にすればいいくさい?

GET形式からPOSTになったので地味に取得コーディングを変えんといけん。でも以前やった。curlを使えばいい。
何でやったかと思えばmoneybuttonのoauthだ。サンプルを公開したいがライブラリを使用しているので、ライブラリ毎公開する気はないので、今回もやりましたよー記事です。

しかしできねぇ!{"error":"invalid query"}になる!

・・・
・・・
・・・

おかしいぞ!って思って、nodejsのサンプル実行したら動いたので、何かしらcurlの送り方がおかしいっぽい。クッソ!!!!
でもnodejsのサンプルで「$gt」は動いた。小さな目的達成。そもそもドキュメントにaggregate以外使えるとある。まぁバグとかこれまでのはあったんすよ。
とりまPHPでもいけるように頑張る。世界はPHP眼中にない(笑)

・・・
・・・
・・・

ぎゃー、わかった。javascriptのサンプルqとかfindがなぜか囲いがねぇ。「"」で囲ったら余裕だった。ざけんな!

それ以外にもPOSTデータって大抵複数あるので、大方キーバリューの配列構成なんだけど、jsonのクエリー一つで完結しているのでPOSTデータを配列でいれてはいけない。
PHPのhttp_build_queryでだいたいエンコードするもんだけど、それもやるとおかしくなるのでやらない。無駄を極限まで省くためかは謎。まぁ動けばいい。

取れたと思ったら、大きなデータはBitFSに託されたみたいで一遍にとれない。うーむ、仕方ない。
bitfsやってなかったのでよくわからんけど、バイナリデータを持ってこなくても取得したアドレスを指定すれば描画されるっぽい。
これはこれで画像とかなら自前のサーバに負荷が掛からなくていいかも?暗号化とかしたら取得して復号化しないといけないけどね。

bitfs.network


そんな感じで、bitbus2.0 + bitfsでございました。最近外部jsとかチェーンにあげてそれを読むとかしてる人とかいてサーバレス近づいてきてます。しかし進展早いからキツイ。かといって遅れて入れば入るほど過程が飛びまくって分からんかもしれんね。

1度の決済で別々のtxでOP_RETURNが送れるかテスト

表題の件、よーく考えたら分かる事なんですが、1取引が1トランザクションなので、表向き偽装しない限りできません。ただこれを何故できないか言い出した理由は、データを別々に登録したかったから。Dプロトコルなどで上書きにしても全項目入れ直しは非常にしんどい。このため1回の決済で個別のtxハッシュが欲しかったの。


で、テストした意味はありました。

<div class='money-button'
	data-outputs = '[
		{
			"script": "OP_0 OP_RETURN hex_text1",
			"amount": "0",
			"currency": "BSV"
		},
		{
			"script": "OP_0 OP_RETURN hex_text2",
			"amount": "0",
			"currency": "BSV"
		}
	]'
	data-on-payment = 'myCustomCallback'
></div>

outputsはもともと以前手数料をいただきつつ~の記事で紹介した変則バージョンになりますね。そもそも送り先は複数が可能です。実際ブロックチェーンは仕組み上そういうものです。
online106.hatenablog.jp

で、このパターンで実行するとどうなるかというとout.iで分けられて記録されます。

{
  "v": 3,
  "q": {
    "find": {"tx.h": "tx hash"}
  },
  "r": {
    "f": "[.[] | { text1: .out[0].s2, text2: .out[1].s2} ]"
  }
}

こういうことですわ。これどうやるのかいままでわかってなかったのでテストしてよかった。軽く見た感じではDプロトコルでこのレコード?までは指定できないっぽい。これ識別子かインデックスかスコープと言われるのか分からないけど...何の役に立つのだろうか。データ分割してもビックブロックだし1tx1GB制限の範囲だしなぁ。

MAPでDELETEしてみる。SETで上書きできる?

はい、ということでDELETEとUPDATEを試してみます。filenameはいらない気がしてきました。URLの見た目は良くなりますが、txにすれば一意だしチェーンに刻む分ちょっとお金かかるし...前回同じアドレスで同じfilenameにしたら動き変になった気がするし。課題としています。

DELETEはキーをピンポイントで削れるようです。

1PuQa7K62MiKCtssSLKy1kh56WWU7MtUR5 (MAP)
'DELETE'
'profile.name'

github.com


つまり前回の記事で、投稿したうちの経度緯度4番目を削るとすると

OP_RETURN 
19HxigV4QyBv3tHpQVcUEQyq1pzZVdoAut
"<html><body><h1>なんでもいい?</h1></body></html>"
text/html
utf8
map_test001.html
| 
1PuQa7K62MiKCtssSLKy1kh56WWU7MtUR5
DELETE
4

で、いいのかな...ここでこそfilenameが重要になってくるのではないだろうか。


できない!なんかゴミレコードができてしまった。誰も使ってないし使わなくてもいいけどDプロトコルの方が最適解になるやも?勘違いだろうけど。
※とりあえず問い合わせてみている最中。回答来るか分からないけど。
※あがいてapp指定してDELETEテスト。READMEにはないけど、普通app指定するはずだから...。だめだわからん。

ではUPDATEできるのか?あるレコードにSETで上書きしてみる奴。

"19HxigV4QyBv3tHpQVcUEQyq1pzZVdoAut", 
'<html><body><h1 style="font-size:72px;">もす!</h1></body></html>', 
"text/html", 
"UTF-8", 
"map_test001.html", 
"|", 
"1PuQa7K62MiKCtssSLKy1kh56WWU7MtUR5", 
"SET", 
"app",
"test.app",
"5",
"34.6731630^135.5193140^e17.8",
"6",
"34.6731630^135.5193140^e17.8"

5は更新で、6は追加という感じ。

・・・だめだな。

MAPのテスト結果。

SETする分にはいいかも?twetchのように1回きりで流れていく短文にはいいけど、更新するならB->Dでシーケンス管理の方がいまのところちゃんと動く。私がまったく使い方違ってるだけかもしれんが。

そう思てB|Dのテストを見に行ったらDのエクスプローラーオフラインになってた。安定稼働には徐々に普及が必要そうだなぁ


確認しました。v2策定中なので現在初版を導入する必要はなさそうです。
github.com

とりあえずMAPを試す。

B |(パイプ) D と違ってMAPプロトコルはREST感!CRUD感だしてます!横文字カッコイイ!
MAGIC ATTRIBUTE PROTOCOLいうらしいです。
プレフィックスは1PuQa7K62MiKCtssSLKy1kh56WWU7MtUR5です。

<OP_RETURN | <input>>
MAP
<SET | DELETE>
<key>
<value>

以下サンプル

OP_RETURN
19HxigV4QyBv3tHpQVcUEQyq1pzZVdoAut
text/html
UTF-8
<html><body><h1>とりあえず何かいれておく</h1></body></html>
|
1PuQa7K62MiKCtssSLKy1kh56WWU7MtUR5
SET
app
test_app
type
gps
1
34.6731630^135.5193140^e17.8
2
34.6912530^135.5348380^e0.4
3
34.7415840^135.5266780^e-1.4
4
34.7651220^135.5210800^e13.8
5
34.8244010^135.5088550^e41.4

とりあえずです。何度か上手くいかず書き直しました。エクスプローラー側のバグかもしれんし、仕様かもしれん。とりあえずデータはAPIで取得したい身としてはバグだろうと仕様だろうと従うしかない。

MAPにパイプしてからはキーバリューの形になります。ルールとしてappでアプリ名を書く。そしてtypeで自前のコンテンツの用途を書く。今回は5つだけのGPS風にしてみました。この登録の仕方がベストかは不明です。twetchだとAIPにパイプしています。これによって誤データを弾いているのかもしれない。

BMAPのエクスプローラーで登録内容を確認できます。
https://b.map.sv/

{
  "v": 3,
  "q": {
    "find": {
      "MAP.app": "tonicpow"
    },
    "limit": 10
  }
}
{
  "v": 3,
  "q": {
    "find": {
      "MAP.app": "twetch"
    },
    "limit": 10
  }
}

が、このMAP.appで検索なんですが、何度かやって失敗したのは別の理由がありそうです。
先ほどまでtest_appでヒットしていたのが、ゼロ件になってしまいました...うーん。
今度は最初にうまく行かなかったtest.appがヒットしました。エクスプローラーの黄色時の「c」「u」がポイントの様です。
試しにもう一度登録するとcはblkありで、uはblkがありません。つまるところこれはあれですね。
confirmed: 確認済み
unconfirmed: 未確認
気にしてなかったけど...しかも今回は気にしなくていいかも
4回やって、3回目だけ出てたと思ったら、3回目だけでなくなってしまいました。
もしかするとfilenameが2回目と3回目が同じだからでしょうか...うーん。

ちなみにテスト中同じアドレスだと思うので、以下の方が追いかけやすいです。

{
  "v": 3,
  "q": {
    "find": {
      "in.e.a": "address"
    },
    "limit": 10
  }
}

とりあえずもう少しいじって安定感確認したい。


確認しました。v2策定中なので現在初版を導入する必要はなさそうです。
github.com

Dプロトコル(D://)を試しました。

Bitcoin dynamic content protocolらしいです。

型はこれなんですが、パイプもできます。

OP_RETURN
19iG3WTYSsbyos3uJ733yK4zEioi1FesNU
[key]
[value]
[type]
[sequence]

一旦前回の試したBをDれるかテストします。
....
で、これ幾度かミスって、bico.mediaやD用のエクスプローラーで確認していくうちに分かりました。

D用のエクスプローラ
https://d.onchain.ch/query/1G3BpTyEK6xF4LaQTHqdFBBaVxYHZzts4M/ewogICJ2IjogMywKICAicSI6IHsKICAgICJmaW5kIjoge30sCiAgICAibGltaXQiOiAxMDAwCiAgfSwKICAiciI6IHsKICAgICJmIjogIlsuW10gfCB7IHByb3RvY29sczogLnByb3RvY29scywgdHJhbnNhY3Rpb246IC50eGlkLCBibG9jazogLmJsaywgc2VuZGVyOiAuc2VuZGVyICwga2V5OiAua2V5LCB2YWx1ZTogLnZhbHVlLCB0eXBlOiAudHlwZSwgc2VxOiAuc2VxLCBcIlVSSSBvdmVyIGh0dHBzXCI6IChpZiAudHlwZSA9PSBcImNcIiB0aGVuIFwiaHR0cHM6Ly9kYXRhLmJpdGRiLm5ldHdvcmsvMUt1VXIycFNKRGFvOTdYTThKc3E4endMUzZXMVd0RmZMZy9jL1xcKC52YWx1ZSlcIiBlbGlmIC50eXBlID09IFwidHh0XCIgdGhlbiBudWxsIGVsc2UgXCJodHRwczovL2IuYml0ZGIubmV0d29yayNcXCgudmFsdWUpXCIgZW5kKX1dIgogIH0KfQ==

まずBプロトコルではひたすら一意のtxでデータが入り続けます。
このBが一体何なのかをDで指定します。

Dプロトコルのtypeでtxを指定します。
で、そのtxをvalueにいれます。
Bプロトコルで出力されたtxをvalueに突っ込む感じです。
するとDプロトコルでtypeがtxだからvalueにtxはいってるなって判断されて、

https://bico.media/ tx生んだアドレス / キーで指定したパス

と言ったことができます。このtypeが以下のマニュアルにあります
planaria.network

tx生んだアドレスはmoneybuttonで今回決済しているので、moneybuttonのtxを調べると自分の今回のアドレスが分かります。
マニュアルに話を戻すと、c,b,tx,txtなどが指定できるようです。
ざーっと他人のDのtxみましたがほとんどがBでたまにtxでした。なのでみんなパイプしていると思われる。

OP_RETURN
19HxigV4QyBv3tHpQVcUEQyq1pzZVdoAut
[DATA]
[MEDIA TYPE]
[ENCODING] or NULL
[FILENAME] or NULL
|
19iG3WTYSsbyos3uJ733yK4zEioi1FesNU
[key]
NULL
b
[sequence]

Dのプレフィックスは「19iG3WTYSsbyos3uJ733yK4zEioi1FesNU」です。もうBの流れで分かると思います。

"B | (パイプ) D" だとtypeにB指定してvalueはnullでいいっぽい。

無事いけた。

リダイレクトが掛からないのはシーケンスか、Bでnullにしたからかも?
それはどれでもいいかな。

お、そうそうそしてさらに上書きテストをします。

シーケンス2にして...


いけました。


キャッシュがきついですが、ちゃんとシーケンスの大きい数字が表に出ます。
BSVUP使うまでもないな。これなら個人のmoneybutton使わせて、ブログ記事の投稿更新とかいけますね。
ドメインサブドメインを選ばせて、暗号復号のプロトコルが出来るかなと思ったけどパブリックだと難しいかも。自前でやりたければやればいい。