1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Rust の WASM はクソデカ? Symbol の虚栄心剥き出しアドレス生成アプリをブラウザで作って測ってみた

1
Posted at

Rust で WebAssembly を書くと、ファイルがでかい。いつものよく聞く話である。

本当にそうなのか、自作の heika(Symbol の Rust のライブラリ)を使って、好きな文字で始まるアドレスを探すページを作って測ってみた。ついでに、見た目は Claude に丸投げした。シンギュラリティってすばらしい✨。

web-vanitygen の画面

ソースはここにある。

作ったもの

NA や NARUTO のような先頭の文字を入れて「探す」を押すと、鍵を作ってはアドレスにして比べるのを、見つかるまで繰り返す。

  • 鍵はページの中で作り、どこにも送らない。保存もしない
  • CPU のコアの数だけ Web Worker を立てて、並列に探す
  • 試した数、速さ、見つかるまでの見込み、いま見つかっているはずの確率を出す

スマホでも、NARUTO で始まるアドレスが1分もかからずに見つかった。いいじゃなぁい🎉

大きさ

先に結果から。公開しているもの(trunk build --release、wasm-opt 済み)の大きさである。

ファイル そのまま gzip
画面の WASM(Leptos 込み) 313KB 126KB
画面の JS 38KB 7KB
Worker の WASM(heika で探すところ) 98KB 57KB
Worker の JS 16KB 4KB
CSS 5KB 2KB

全部で gzip して約 200KB。そのうち、heika で鍵を作ってアドレスにするところは約 60KB で、残りのほとんどは画面を作る Leptos である。

画像1枚くらいなので、さほど「でかい」とは言えないと思う。ケータイ小説の時代じゃあるまいに。

段階ごとの大きさ

heika で探すところだけを、段階ごとに測るとこうなる。

opt-level = 3 opt-level = "s" opt-level = "z"
cargo のビルドそのまま 130KB 126KB 144KB
wasm-bindgen のあと 110KB 109KB 125KB
wasm-opt のあと 96KB 84KB 89KB
gzip 56KB 53KB 55KB

"s" や "z" にしても、wasm-opt と gzip のあとでは数 KB しか変わらなかった。鍵を作る速さのほうが大事なので、opt-level = 3 のままにしている。

なかなか効果あったのは、リリースの設定と wasm-opt である。

Cargo.toml
[profile.release]
lto = "fat"
codegen-units = 1
panic = "abort"
opt-level = 3

中身を見ると、約 34KB はコードではなくデータだった。たぶんだけどどうせ、Curve25519 の計算を速くするための表である。コードそのものは 60KB くらいになる。

速さ

Symbol のアドレスは、秘密鍵 → 公開鍵 → SHA3-256 → RIPEMD-160 → Base32 で作る。一番重いのは、秘密鍵から公開鍵を作る楕円曲線の計算である。

ほかのチェーンの vanitygen には、「前の鍵に1足して、公開鍵にも点を1つ足す」という近道を使うものがある。楕円曲線の掛け算を毎回しなくて済むので、ずっと速い。ところが Symbol(Ed25519)の秘密鍵は、使う前に SHA-512 でハッシュする決まりになっている。秘密鍵に1足すと、ハッシュしたあとの値はまったく別物になるので、この近道は使えない。毎回まじめに掛け算をするしかない。

なので、速くするには数で押すしかない。Web Worker をコアの数(navigator.hardwareConcurrency)だけ立てて、それぞれに探させている。Worker どうしでやりとりすることはないので、メモリは共有しない。共有しなければ、GitHub Pages では設定できない COOP/COEP のヘッダーもいらない。

各 Worker は、500 個試すごとに画面に数を送り、見つかったら結果を送る。止めるときは、画面が Worker ごと terminate() する。

ハマったところ

「探す」を押しても始まらない

最初の版は、画面が Worker を作ってすぐに先頭の文字を送っていた。ところが Worker は、WASM を非同期で読み終えてから、ようやくメッセージを受け取る準備をする。準備ができる前に届いたメッセージは捨てられるので、どの Worker も何も受け取らずに待ち続けていた。カウンタが動かないので「遅いなー」と思っていたら、そもそも始まっていなかった。

Worker の準備ができたら "ready" を送り、画面はそれを待ってから先頭の文字を渡すようにして直った。

Worker の初期化の警告

trunk の data-loader-shim が作る Worker の読み込み用スクリプトは、wasm-bindgen の古い呼び方で初期化するので、警告が出る。ゆーて3行なのでと Claude がぱぱっと書いてくれた。

worker_loader.js
importScripts("./worker.js");
wasm_bindgen({ module_or_path: "./worker_bg.wasm" });

どこに置いても動くように

GitHub Pages では /heika/examples/web-vanitygen/ の下に置くので、Trunk.toml でパスを相対にしている。

Trunk.toml
[build]
public_url = "./"

見た目は Claude に丸投げした

今どきデザインはもはや身だしなみみたいなものなので、見た目は Claude に丸投げした。

伝えたのは、だいたいこれだけである。

  • すっごい薄みずいろとふつうに薄みずいろの、細い縦のしましま
  • おしゃれで落ち着きのある、かわいい感じ
  • ダークモードには合わせなくていい

出てきたのは、しましまの背景に、少し透けた白い角丸のカード、丸ゴシック、押すと沈むボタン、見つかったときにぽんと出る点線の枠、という見た目だった。スマホの幅では、数字のカードが縦に並ぶ。頼んでいないのに、見つかっているはずの確率をしましまのバーで出すところまで作ってくれた。

口を出したのは、しまの太さくらいである(4px から 6px にした)。

それくらいしか突っ込みどころが無くてくやしい……私いらない子になっちゃう……捨てないでくれClaude Code……

TypeScript の SDK と比べて

Symbol には公式の JavaScript/TypeScript の SDK(symbol-sdk v3)がある。実は v3 も、署名などの暗号の計算は WASM(symbol-crypto-wasm)でやっている。WASM を使うこと自体は同じである。

違いは組み立て方にある。symbol-sdk は Node.js でもブラウザでも動くように、WASM を読み込むところが Node 用(symbol-crypto-wasm-node)とブラウザ用(symbol-crypto-wasm-web)に分かれている。バンドラーにそのまま任せると Node 用が選ばれるので、ブラウザ用に差し替える設定が要る。

2023 年の symbol-sdk 3.1.0 のころの記事では、Next.js(webpack)で NormalModuleReplacementPlugin を使って Node 用をブラウザ用に差し替え、experiments で asyncWebAssembly などを有効にして動かしている。同じ記事には、Vue や Nuxt ではうまくいかず、Next.js なら動くと教わって試した、という経緯も書かれている。

Turbopack になった今の Next.js では、そのまま使うと Module not found: Can't resolve 'fs' で止まる。next.config の turbopack.resolveAlias で Node 用をブラウザ用に差し替え、依存の bitcore 系をサーバー側で external にすれば動く、という記事がある。

バンドラーが変わるたびに、差し替えの書き方も変わっている。

どちらも、いろいろな環境で動かすための作りなので、悪いというわけではない。一度設定してしまえば、ちゃんと動く。

一方で、今回のように全部 Rust で書くと、そもそもこの手の設定がいらない。trunk が Rust のビルド、wasm-bindgen、wasm-opt までやってくれて、JS のバンドラーの設定は1行も書いていない。画面も Worker も鍵の計算も、全部 Rust の型でつながっているので、もうすんごい楽。

そのかわり、画面も Rust(Leptos)で書くことになるし、npm のパッケージをそのまま使うこともできない。既存の React や Next.js のアプリに組み込みたいなら、symbol-sdk のほうが素直だと思う。素直だとは思うけど、ああいう試行錯誤を自力でやりたいかは別なので、その折は Claude に一任もとい丸投げしちゃいたいな〜👀✨チラッ。

おわりに

Rust の WASM は、案外気をつければでかくない。heika の部分は gzip で約 60KB だった。

Leptos は SPA でもないページには大げさかと思ったが、Worker から数が届くたびにいくつもの表示を書き換えるので、シグナルで済むのは楽だった。その分の大きさは、上の表のとおりである。

公開は、タグを打つと GitHub Actions がビルドして GitHub Pages に出すようにした。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?