Noah Bragg's First Stoke Fire Livestream

Michael Lynch

ノア・ブラッグの初回Stoke Fireライブ配信

原文は Michael Lynch により に公開されました。 このブログを購読する

ここ1年ほどEthereum、特にBaseエコシステムに興味を持っています。困ったことに、Baseについて何時間も調べても、結局Baseが何なのかいまだによくわかりません。

数カ月おきにBaseのウェブサイトの開発者向けセクションをのぞいて、初心者がBase上で開発を始めるための道筋がないか確認していますが、いつも返ってくるのは「特定のことに特化したバラバラのチュートリアルがここにあります。質問があればDiscordで聞いてください」といった内容です。

そんな中、Twitterでフォローしているインディー創業者のノア・ブラッグが、Baseエコシステム上でシンプルなゲームを作る過程をライブ配信し始めたのを見て、わくわくしました。

インディー創業者のノア・ブラッグが、Ethereumブロックチェーン上でゲームを構築する過程をライブ配信している。

ノアの初回配信を見たので、そこから得た気づきを以下にまとめます。

ゲーム:Stoke Fire

  • Stoke Fireは、村で木を切り、燃え続ける火を絶やさないようにして村を暖かく保つ、リソース管理ゲームです。
  • 影響を受けた作品
    • ノアが子供の頃に遊んだAge of Empires II。
    • テキストベースのウェブゲームであるA Dark Room
    • 個人開発者が6年かけて作り上げたトータルウォー系シミュレーション、Manor Lords。
  • 参加を促すため、基本プレイ無料になる予定です。
  • ゲームの状態はNFTとして保存され、ウォレット間の移動がしやすくなります。ただし、アートNFTのようにゲーム状態のNFTが盛んに取引されることは、ノア自身は想定していません。
  • Coinbaseの投資によってコンシューマーが集まると見込んでいるため、Baseブロックチェーン上で構築しています。

アーリーアクセス

  • Stoke Fireのアーリーアクセスは、Hypersub上のノアのI Must Buildサブスクリプションから利用できます。
    • Hypersubはオンチェーン版のPatreonのようなものです。

配信で起きたこと

  • ノアはゲーム内で、木材を使って小屋を建てられる機能を追加しました。
  • 村にある資源に応じて村人を引き寄せる機能の実装にも着手しましたが、精神的に疲れてしまい、途中で作業を中断しました。

Solidity

  • ノアはスマートコントラクト開発にSolidityを使っています。
    • Vyperのことは聞いたことがあるそうです。ただ、Solidity周辺のエコシステムが他の何よりもはるかに成熟しているため、使っていないとのことです。
    • Huffについては聞いたことがないそうです。

Michaelより補足:Solidityには相変わらずうんざりさせられます。正確性と可読性が極めて重要な分野であるにもかかわらず、Solidityは不要な落とし穴やハマりどころを大量に持ち込んでいます。まるでC++とJavaScriptを徹底的に研究して、最悪な部分だけを寄せ集めたかのようです。

Diamond

  • Diamondは、デプロイ後も変更可能なスマートコントラクトを展開するためのフレームワークです。
    • 通常、Ethereumのスマートコントラクトは一度デプロイされると変更できません(イミュータブルです)。
    • Diamondは「アップグレード可能」なスマートコントラクトを実現し、デプロイ後でも変更できるようにします。
  • Diamondはスマートコントラクトの保証を弱めますが、Stoke Fireのようなプロジェクトではイテレーションを容易にします。
  • 「Facet」は(たぶん)スマートコントラクトの中で変更可能な部分のことです。

Michaelより補足:Facetの管理は手間がかかりそうで、ノアが見せたコードの中で最も脆い部分でした。いくつもの異なる配列を手動で宣言し、新しいFacetを追加する際に手動でインデックスを指定しなければならず、あるときは埋もれた宣言の中でFacet数と配列サイズを一致させるのにしばらく手こずっていました。チャットのコメントでは、Facetを自動的に取得できるヘルパーがあるとのことなので、もっと簡単な方法があるのかもしれません。

Forge

  • ノアはスマートコントラクトのロジックのテストにForgeを使っています。
  • ノアはForgeを好意的に評価していました。
    • ForgeではテストをSolidityで書けるため、本番のスマートコントラクトと多くのコードを共有できる点を気に入っています。
    • Forgeはタイムスタンプやウォレット残高といったさまざまなブロックチェーンの条件を簡単にテストできます。
  • ノアはForgeの出力の読み解きに時々苦労していましたが、私自身も出力が非常にノイジーで読みづらいと感じました。

    Forgeのテスト出力は非常にノイジーだと感じました

    • Forgeではアサーションが失敗しても、失敗した行番号が出力されません。配信中のある場面では、Forgeが出力した失敗内容は単に5 != 4だけで、どのアサーションが失敗したのかは開発者がソースコードから探し出す必要があります。これまで、失敗したアサーションの行番号を表示しないテストフレームワークは見たことがありません。
  • ノアの本番コードが特定のエラーをスローしたことを検証するために、テストコード側でそのエラーを再定義しなければならず、これは奇妙だと感じました。

Warpcastにはウェブアプリがある

  • Farcasterは、Ethereum版のTwitterやMastodonのようなものです。
  • FarcasterはWarpcastがモバイル専用であるかのように見せていますが、ノアの配信を見てWarpcastにウェブアプリがあることに気づきました。
    • アカウント作成には今でもモバイルアプリが必要だと思います。

配信について

  • ノアの配信はピーク時に250人の視聴者を集めました。
    • ただ最後になって、この数字が同時視聴者数なのか、配信中に一度でも視聴した人数の累計なのか、本人もよくわかっていないことが判明しました。
  • 視聴者の大半はTwitterから来ていました。
  • 本人も認めているように、ライブ配信は久しぶりで、配信中は沈黙が目立ちました。

改善の余地

便利な開発スクリプト

ノアはあるとき、テストスイート全体ではなく単一のテストだけを実行する方法を思い出せず、数分間詰まってしまいました。最終的にはチャットの視聴者の助けを借りて、正しい構文を見つけました。

forge test --match-contract BuildFacetTest -vvvv

覚えにくいコマンドライン構文については、便利スクリプトを書いてリポジトリに置いておくことをおすすめします。扱う技術スタックごとに単一のテストを実行する正しい構文を覚える代わりに、例えば次のようなコマンドを実行すれば済むようにします。

./dev-scripts/run-single-test BuildFacetTest

私自身、いくつかのリポジトリでこの方法を実践しています。

ノアはすでにmakeを使っているので、これらをmakeコマンドとして用意することもできます。

テストを保守しやすくする

小屋を建てる機能の最終的な統合テストは、次のようになっていました。

function testBuildHut() public {
  vm.prank(USER);
  ResourceFacet(address(diamond)).chopWood(1, someWhatRandNum);
  vm.warp (1719981068 + 1 days); //set the block time to the future so I can chop wood again

  vm.prank(USER);
  ResourceFacet(address(diamond)).chopWood(1, someWhatRandNum);

  vm.prank(USER);
  BuildFacet(address(diamond)).buildHut(1);

  Village memory village = VillageFacet(address(diamond)).getVillage(1);
  assertEq(village.timeLastChoppedWood, block.timestamp);
  assertGe(village.wood, 4); //at minimum 2 wood per chop.
  assertLe(village.wood, 12); //at maximun 6 wood per chop.
  assertEq(village.huts, 1);
}

このテストには、いくつか改善の余地があると感じました。

まず、このテストは擬似乱数生成器(PRNG)に依存しており、ロジックが非常にわかりにくくなっています。テスト用にPRNGには固定のシード値が設定されているため、テスト間で乱数列は再現されますが、それでもテストのロジックを追うのは困難です。

配信中、ノアは何度もやみくもに木こりの回数を増やしてテストが通るか試していました。最終的には乱数のシード値を別の値に変えることで、2回の伐採だけでテストが通るようにしていました。だからこそ、このテストでは残りの木材の正確な量を検証できず、ある範囲内にあることを推測するだけのアサーションになっているのです。このテストの正しさは、実際に実行してみないことには検証できません

もし自分がこのコードを担当するなら、木こり機能をモック化して、1回の伐採で得られる木材の量を正確に指定できるテスト用の実装を用意するでしょう。あるいは、本物の木材生成コードをテストしたいのであれば、シード値をハードコードするのではなく、特定の数列を返すフェイクの乱数生成器を使うと思います。

もう一つの問題は、呼び出し元での関数パラメータが読み取りにくいことです。例えばbuildHut(1)chopWood(1, ...)のように、1が何を表しているのか、テスト内に登場する複数のマジックナンバー1が同じ値を指しているのか、たまたまどれも1になっているだけなのかが不明瞭です。

未解決の疑問

なぜブロックチェーンなのか?

配信を見終えて一番大きく残った疑問は、なぜブロックチェーンなのか?ということでした。

ノアはブロックチェーン上でよりオリジナルなことに挑戦したいと語っており、そこに私も興味を惹かれたのですが、ブロックチェーンがどう役立つのかは依然としてよくわかりません。

現時点では、ブロックチェーンによってすべてが10倍複雑になっているだけで、すべてを単一のSQLiteデータベースに入れておく場合と比べて何のメリットもないように見えます。

同様に、当初答えを探していた「Baseっていったい何なんだ?」という問いにも、まだ答えが見つかっていません。なぜノアがEthereumや他のチェーンではなくBase上で構築しているのか、依然としてよくわかりません。Coinbaseの投資について触れていましたが、それがノアのような開発者にとって何を意味するのかが理解できていません。

なんて大きな整数なんだ!

ノアが、タイムスタンプや木材の数のような、明らかに過剰に思える箇所で頻繁にuint256を使っていることに気づきました。現状、1回の「伐採」アクションで得られる木材は2〜4個なのに、その結果をuint256で保存する必要があるとは到底思えません。

こんなに大きな整数型はガス代を上げないのだろうか?

自分自身のEVM実装に取り組んだ経験から、Ethereumネットワークでは処理しなければならないデータに対して課金されることを知っています。なので、256ビットの値をスタックにプッシュするのは32ビットのワードをプッシュするより8倍高くつくはずです。(追記:実際には256ビットワードと32ビットワードのプッシュコストは同じでした。訂正してくれたa14uに感謝します。)

これが単なる見落としなのか、それともガス代が私の想像よりも実際は安いのかはわかりません。

なぜアンダースコア?

ノアは、すべての関数パラメータ名の先頭にアンダースコアを付けるという命名規則を使っています。

関数パラメータ名の先頭にアンダースコアを付けるという規約は見たことがなく、なぜノアがそうしているのかわかりません。

この規約はPythonやJavaScriptで、変数がprivate/protectedであることを読み手に示すために使われるのは知っていますが、関数の引数はそもそもプライベートなのではないでしょうか。Solidityのコードを読む経験はまだ浅いとはいえ、この規約はこれまで見たことがありません。

この記事は「muse-spark-1.2-contributor」を使用して翻訳されました。

コメント