Noah Bragg's First Stoke Fire Livestream

Michael Lynch

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

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

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

そんな中、Twitterでフォローしているインディー創業者のノア・ブラッグ(Noah Bragg)が、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については聞いたことがないそうです。

マイケルより一言:Solidityには相変わらずうんざりさせられます。正確性と可読性が何より重要な領域なのに、Solidityは不要な落とし穴や罠だらけです。まるでC++とJavaScriptを徹底的に研究して、最悪な部分だけを寄せ集めたかのようです。

Diamond

  • Diamondは、公開後も変更可能なスマートコントラクトをデプロイするためのフレームワークです。
    • 通常、Ethereumのスマートコントラクトは一度デプロイすると変更できません。
    • Diamondは「アップグレード可能」なスマートコントラクトを実現し、デプロイ後でも変更できるようにします。
  • Diamondはスマートコントラクトの保証を弱めますが、Stoke Fireのようなプロジェクトでは素早いイテレーションを可能にします。
  • 「Facets(ファセット)」は(おそらく)スマートコントラクトの中で変更可能な部分を指します。

マイケルより一言:ファセットの管理はとても煩雑に見えましたし、ノアが見せたコードの中で最も壊れやすい部分でした。いくつかの配列を手作業で宣言し、新しいファセットを追加する際にはその都度手動でインデックスを指定しなければならず、配信中も配列サイズとファセット数の整合を取ろうとしてしばらく手こずっていました。チャットのコメントでは、ファセットを自動取得できるヘルパーがあるという声もあったので、もっと簡単な方法があるのかもしれません。

Forge

  • ノアはスマートコントラクトのロジックのテストにForgeを使っています。
  • ノアはForgeに好印象を持っていました。
    • ForgeではテストをSolidityで書けるため、本番のスマートコントラクトと多くのコードを共有できる点を気に入っていました。
    • タイムスタンプやウォレット残高といったさまざまなブロックチェーンの条件を簡単にテストできる点も評価していました。
  • ノアは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で、変数がプライベート/プロテクテッドであることを読み手に示すために使われるのは知っていますが、関数の引数はそもそもプライベートではないのでしょうか? 私自身、Solidityのコードをそれほど多く読んできたわけではありませんが、これまでこの規約を見たことはありません。

原文は Michael Lynch により に公開されました。

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