みなさん、こんにちは!私はかなり長い間バックエンド開発を行ってきましたが、ここ数年はさまざまなブロックチェーン プロジェクト (EVM 上の Solidity) をどんどん書いてきました。ブロックチェーンに飛び込むのは私にとって簡単なことではなく、バックエンドの脳が何度も故障したため、ブロックチェーン開発への切り替えに関する私の見解を共有することにしました。 免責事項: 以下に記載されている内容はすべて私の意見です。間違っている可能性もありますし、頻繁に間違っています:)) は、世界を前進させることができる非常に優れた技術です。しかし、今のところ、多くの人がそれを売買、詐欺、不正行為、ブーストに使用しています。私は暗号通貨を資産として考えるつもりはありませんし、今後も考えるつもりはありません。 ブロックチェーン はい、さまざまな動画や投稿で、「ここでは、これこれの暗号通貨は今成長する可能性があり、これこれの暗号通貨は下落しています。皆さん、投資しましょう、購入しましょう...」と書かれていますが、私はそれについては何も言いません。 一つだけ言いたいのは、お金を稼ぐために暗号通貨を使うのは避けるべきだということです。開発者であれば、開発者として稼ぐことに集中し、投資には関わらない方が良いでしょう。そして、本当に投資したいのであれば、暗号通貨には手を出さないでください。 ブロックチェーンについて、そしてそれがなぜ興味深いのか まず重要な点から始めましょう。ブロックチェーンは暗号通貨ではありません。ブロックチェーンは暗号通貨の基盤となる技術であり、暗号通貨をブロックチェーンと呼ぶことは、開発業界全体を 呼ぶようなものです。確かに、JavaScript は特別な開発ケースのようですが、開発について議論する場合、JavaScript を意味するわけではありません。JavaScript のみを意味する人もいますが... JavaScript と お金のためにブロックチェーンへ 最初に思い浮かぶのはお金です。ブロックチェーン開発者の給料はかなり良いです。私は個人的にそのような求人を募集しました。私は個人的にそのような求人に応募しました。そこでは、1 日の勤務時間が同じでも、同じバックエンド開発者よりも多くの報酬を得ることができます。ブロックチェーン ベースのスタートアップでは、ブロックチェーン開発者は金と同等の価値があります。特に優秀なブロックチェーン開発者は! 優れたバックエンド開発者とは誰でしょうか? 製品を落としたり修正したりせずに、優れたバックエンド開発者になることは不可能です。私はただの負け犬で、ネガティブな経験を通してしか学ばないのかもしれません。でも、それが私の理論です。 バックエンド開発者が経験豊富であれば、本番環境でソリューションを実行したことがあるはずです。 Prod で実行したことがある場合は、それがどのように実行されるかを知っており、負荷がかかった状態で製品に何が起こるかを知っています。 サービスに負荷をかけ続けていれば、ダウンタイムやサービスのクラッシュを捉えることができます。 そして、サービスクラッシュが発生した場合、おそらく彼はそれを拾い上げたでしょう。 経験を失敗と交換することはできませんが、失敗を経験することで、自分が経験を積んでいることに気づくことができます。お金を失わずに優れたブロックチェーン開発者になるのは難しいです。 私の契約からまだお金が盗まれていないということは、実際のお金(少なくとも契約上の 1,000 ドル)を使って何かを開始していないことを意味します。 実際のお金を使って何かを立ち上げたり、テストネットワーク(偽のお金)でのみ何かを立ち上げたりしたことがなければ、ブロックチェーンの有害な世界で何が待ち受けているのか全くわかりません。 自分に何が起こるか分からなければ、最初のリリースで失敗することになります。それは、私のミスに対する最初の支払い者になるリスクを負う意思のあるチーム/会社の問題です。 ブロックチェーン技術に参入 上の画像は です。飛行はかなり下手でした。しかし、飛行機は飛びました。当時の平均的な人々の飛行機に対する態度は、次のようなものでした。 ライト兄弟が作った世界初の飛行機 高い 不便 不便、理解不能 そして今、航空業界は私たちの生活の素晴らしい一部となり、地球上の人々を数時間以内に結びつけています。物流は今やライト兄弟が夢にも思わなかったレベルに達しています。飛行機のおかげで、世界全体の生活が変わりました。 ブロックチェーンについても、今同じことを言いたいです。高価で不便で、その理由も不明です。ブロックチェーン開発に没頭するまでは、詐欺的な投資家(=ハムスター)にとって役に立たないもののように思えました。しかし、別の観点から見ると、改ざんの可能性なしに、あらゆる事実を分散的に保存することが可能です。「改ざんの可能性なしに」というのが重要な詳細です。 しかし残念なことに、「ブロックチェーン」という言葉に関連するイメージはむしろ退屈で単調です。 ビットコイン 投資 詐欺 ETH、リップル、{コイン名を挿入} はじけそうなバブル 紙幣の束をストーブに投げ込むと、ストーブはよく燃え、お金はしばらくの間暖かささえ与えます。しかし、それは不合理です。 ブロックチェーンも同じです。お金と暗号通貨だけに使うのはよくありませんが、他のものはまだ定着していません... あらゆる製品開発の主な原動力の 1 つはお金です。 強力で優れたお金を稼ぐための何かがあれば、この「何か」は積極的に開発されます。 そのため、これまでのところ、ブロックチェーンに基づく金融プロジェクトは、誰かがお金を稼ぐためにお金を稼ぎ、誰かがお金を失うことに基づいています。 ブロックチェーン開発理論 分散化 集中型サービスと分散型サービスの違いは何でしょうか? 集中型システムから始めましょう。私と他の人がいて、送金を容易にするために銀行や他のプロバイダーなどのサービスを私たちの間で利用することにしました。 中央集権型のサービスである銀行があると想像してください。私はこの銀行に「この人に 100 ドルを振り込んでください」と命令します。銀行は、私の残高が 100 ドル少なく、他の誰かが 100 ドル多く持っていると記録します。 しかし、集中型サービスの何が問題なのでしょうか? この集中型サービスの背後には所有者がいますよね? 通常、大企業やホールディングスなどは関係ありませんが、私たちの場合は 1 人の人物です。この 1 人の人物は、「こうしましょう。アレックスに 100 ドル送らせましょう。しかし、この 100 ドルを受け取る人はいません。もっと必要です」と言うことができます。 中央集権型サービスには所有者がいます。問題は、所有者がマイナスの決定を下し、お金を奪い取ることができることです。そして、それは「自分のためにお金を取る」ことだけではありません。たとえば、「銀行に 100,500 ドルあります」と書いて、預金者全員がそのお金を追いかけないことを願うことができます...SVB や他の倒産した銀行で起こったように。 ビットコインは資金管理を分散化するために発明されました。 分散型サービスは、各ノードが情報を保存および送信するノードのネットワーク上に構築されます。簡単に言えば、ノードはどの情報が正しいとみなされ、どの情報がそうでないか、そしてどのようにそれを保存するかについて合意しています。 部屋の例えで考えてみましょう。一人が他の人に残しておいて欲しい情報を叫びます。すると、部屋にいる全員が叫んだ後に記憶した情報を使って作業します。 たとえば、ブロックチェーンはメッセージや送金に関する情報を保存および送信できます。ネットワークの参加者は、情報を記録する前に検証します。 部屋の例えで言うと、私は「サムに 100 ドルを送金します」と叫びます。全員が、私の残高が 100 ドル少なく、サムの残高が 100 ドル多いと記録します。送金前に突然、私の残高が 100 ドル未満になった場合、誰もその取引を記録しません。 スマートコントラクト ブロックチェーンでは、Solidity 言語でスマート コントラクトを作成できます (EVM 上のブロックチェーンの場合)。スマート コントラクトは、ブロックチェーン ネットワーク上で実行されるプログラムです。検証メカニズム、エラー処理、その他の機能が含まれる場合があります。 繰り返しになりますが、コマンドを叫ぶ部屋の場合、スマート コントラクトとは、私が部屋の各参加者に、私のコマンドにどのように反応するか、何をチェックするか、何を保存するかなどのプログラム コードを事前に渡すことです。そして、「これらのパラメーターを使用してプログラムを実行してください」と叫びます。すると、全員が指示に従います。シンプルなスマート コントラクトの例としては、データの追加と受信の機能を備えた情報ストレージがあります。コントラクト コードはバイト コードにコンパイルされ、ブロックチェーンの参加者に渡されて実行されます。 このスマート コントラクトに送信される特定のリクエストをどのように処理するのでしょうか。バックエンドに例えると、POST および GET リクエストを処理できるサービスです。POST は情報を格納します。ここで GET は格納した情報を送り返します。通常、バックエンドはこのように構成されます。 バックエンドの開発中に、API、データベース、およびデータの保存と処理に関連するすべてのものが自分の側で行われるという配置に非常に慣れました。そして、まるで壁の後ろのように、事前に準備されたシナリオに従ってユーザーがこのデータを操作するためのインターフェイスをすでに提供しています。 たとえば、ユーザー 1 が POST メソッドを使用してコンテンツ (投稿など) を保存します。次に、ユーザー 2 が GET メソッドを使用してこのコンテンツを取得します。ユーザーにはコンテンツがどこにどのように保存されているかわかりません。バックエンドはユーザーにとってブラック ボックスです。 ここで、ブロックチェーンの非常に重要な部分に触れます。部屋の中に立っているノードや人々の例に戻りましょう。バックエンドとの類推で、毎回次のようなことが起こります。ブロックチェーンに「ADD」メソッドを投入すると、全員がローカルでそのメソッドを呼び出し、ブロックチェーンのコピーから情報を取得できます。 つまり、ネットワークにはノードが情報を取得するさまざまなコピーが多数存在します。ブロックチェーンの問題は、書き込み操作ごとに実際のお金を支払わなければならないことです。これはネットワーク通貨によって支払われ、実際のお金で購入 (またはマイニング、ただしこれは今日の話題ではありません) できます。 ブロックチェーンとバックエンドを比較すると、次のようになります。 従来のサービスでは、無料で書き、無料で読み、依存的に ブロックチェーンでは、私たちは料金を支払って書き込み、無料で独立して読み取る。 たとえば、Telegram には集中型データベースがあります。いつでも無料でアクセスして、メッセージ、写真、ビデオなどをダウンロードできます。ただし、Telegram のサーバーが突然ダウンすると、アクセスできなくなります。 ブロックチェーンへの情報の書き込みを含む、いくつかのスマート コントラクト コマンドを実行するには、EVM 仮想マシンに料金を支払う必要があります。EVM 仮想マシンはいくつかの計算を実行し、何かを加算し、乗算、乗算、乗算を繰り返し、最終的にブロックチェーン ストレージに新しいアーティファクトが表示され、ブロックチェーンに参加しているすべてのノードで更新されます。 ネットワーク参加者は誰でも、数百ギガバイトのブロックチェーン データを持つフルノードを実行し、ローカルで操作できます。また、ノードの軽量バージョンを使用することもできます。このバージョンではブロックチェーン全体が保存されるわけではありませんが、ネットワーク内のフルノードにアクセスし、それを介して必要な情報を取得できます。 ブロックチェーンの各エントリは、ブロックチェーンの状態の変化が発生する一連のトランザクションを含むブロックであるという考え方です。後続の各ブロックは、ハッシュ アルゴリズムに基づくチェーン内の前のブロックに依存します。 一般的には基本的なことですが、覚えておく価値はあります。データが変更されると、くしゃみをするたびに料金を支払う必要があるのです。ちなみに、コントラクトのデプロイメントもブロックチェーンの記録であり、安くはありません。 スマートコントラクトの展開 バックエンドの世界では、私はおおよそ次のような機能開発ライフサイクルに慣れています。 コードを書いた Gitlabでリリースしました GitLab CIはテストを実行し、すべてをチェックアウトします すべてが正常であれば、CIはアプリケーションの新しいバージョンをサーバーにデプロイし始めます。 つまり、私たちはこの方法で作業することに慣れており、無料で行われています。ただし、サーバーに料金を支払っているため、条件付きで無料です。ブロックチェーンはどうでしょうか? ブロックチェーンの場合、「アプリケーション」(スマート コントラクト)の新しいコードをブロックチェーンに書き込む必要があります。上で書いたように、レコードごとに料金を支払う必要があります。スマート コントラクトにトランザクションを行う前に、スマート コントラクトを配置してトランザクションを行う必要があります。 次に、クライアント/サービス サーバーはいずれかのノードに接続して、契約内の情報を受信または保存します。 膨大な数のノードに通知する必要があります。「みんな、これが私のトランザクションでアルゴリズムを実行する必要がある契約のバイトコードです」。ブロックチェーンを学習するすべてのノードに同じコードが表示され、誰が呼び出すか、どのように呼び出されるかに関係なく、同じ方法で実行されることを確認することが不可欠です。メカニズムは同じで、変更されません。さらに、スマート コントラクトを何らかの方法で変更して、いずれかのノードで異なる動作をするようにすることはできません。 以下は、かなり前に ETH ネットワークに契約を預けたトランザクションの例です。 これは実際には一度も使われなかったテスト コントラクトでした。デプロイのために ETH で 200 ドルを支払いました。つまり、このコントラクトではまだ何もしていませんでした。リクエストは 1 つもありませんでした。しかし、すでに 200 ドルが費やされていました。間違ったコントラクトを誤ってデプロイしてしまったことを思い出すと、今でも悲しくなります... データストレージ データストレージについてお話ししましょう。私たちは、バックエンドで 、 、 、 などのサービスを使用して、データを便利に操作することに慣れています。ブロックチェーンの場合、それに近いものはまったくありません。 PostgreSQL MySQL MongoDB Redis ブロックチェーンでは、ストレージは他の言語のクラスの変数のように実装されます。つまり、キー値または配列のみです。便利なリンクを持つリレーショナル テーブルなどはありません。変数に書き込むだけで、満足できます。 現時点では、ブロックチェーンでストレージを整理する他の方法は知りません。状況はすでに変わっているかもしれません。この記事を読んでいるときには、そのような方法があるかもしれません。コメントに書いてください。 たとえば、配列だけではなくキーごとに情報を保存したい場合、そのためのマッピングがあります。 ドル記号が描かれているのには理由があります。ネットワーク手数料はセットごとに徴収されるからです。 痛みや苦痛 このブロックでは、私が驚いたり怒ったりしたことについてお話しします。この文書に書かれていること以外にもたくさんありますが、私が実践している中で最初に衝撃を受けたことをお話しします。 「苦痛」のほとんどは、何らかの論理的理由により理解できるものであることに注意することが重要です。しかし、それがバックエンドの脳の苦痛を打ち消すわけではありません。 たとえば、私はあらゆるものの要素を簡単に調べられることに慣れています。配列でもオブジェクトでもマップでも関係ありません。Solidity では、この目的のために、すべてのキーの配列を個別に保存し、必要に応じてそれらすべてを調べて、各キーのマップから要素を取得する必要があります。また、このキーの配列への追加の書き込みとその初期化にもガスを消費します。 便利なキーソート機能もすべて利用できません。 ログ記録 ログ記録の状況も不快です。開発環境ではデバッガーを介してデバッグすることに慣れていますが、ここでは通常のログ記録さえ忘れるべきです。 Typescript では、 と書くだけで、すぐにコンソールに出力が表示されるのが普通です。Solidity には がありますが、これは で実行しているときだけ機能します。そして、素晴らしいのは、必要なものを分割した後、コントラクトをデプロイする前にこれらのログをすべて削除する必要があることです。そうしないと、コントラクトの重量が増え、デプロイにかかるコストが高くなり、本番環境ではまったく機能しなくなります。 console.log(a) console.log ローカルの hardhat 開発環境 結局、すでに戦闘中のプロジェクトを実行すると、何が間違っているのかを確認したいのですが、何が間違っていたのかはわかりません。しかし、何がうまくいったのかは確認できます。スマート コントラクト内にはイベント システムがあります。例を挙げます。このインデックスの下にこの値で新しいアイテムが追加されたというイベントを発生させたいとします。 このイベントは メソッド内で呼び出され、正常に実行された場合にのみログが表示されます。何か問題が発生した場合、コントラクトへの呼び出しが複数回あった場合、またはトランザクションがクラッシュした場合、ブロックチェーンの情報がロールバックされるため、ログも保存されません。 set 複数のスマート コントラクトのチェーンを使用するとします。最初のコントラクトでいくつかのイベントが呼び出され、次に 2 番目のコントラクトが呼び出され、これが他のイベントを呼び出します。その後、2 番目のコントラクト内で呼び出されたすべてのものが削除されます。すべてが完全に削除されます。 ブロックチェーン内で何が起こっているかをログに記録するときは非常に注意する必要があり、私たちが慣れている通常のログ記録はここでは利用できないことを念頭に置く必要があります。 もう 1 つの厄介な点は、書き込み関数でトランザクションから情報を取得できないことです。ブロックチェーンに何かを書き込むトランザクション (つまり、有料トランザクション) を実行すると、スマート コントラクトと統合されているサービスに何も返されません。この は、スマート コントラクト自体または (無料) 関数内でのみ機能します。 return view たとえば、ブロックチェーンに新しい値を追加したときに、保存後のストレージ サイズを確認したい場合があります (上のスクリーンショット)。つまり、何が追加されたかを正確に確認できるのはイベントを通じてのみです。そのためには、そのトランザクション内でトリガーされたイベントを取得する必要があります。 文字列の操作 ここで私は驚きました。文字列を通常どおりに操作することは不可能です。ブロックチェーンは文字列用に作成されたものではありません。例に移りましょう。 以下のコードは問題なく動作します。 そして、このコードはもう機能しません: 私は長い間、文字列を普通に操作し、文字列内の文字を変更したり、文字列をカットしたり、文字列を連結したりすることに慣れてきましたが、これらはすべてすぐには利用できません。文字列の長さを表示することもできません。つまり、次のコードはコンパイルされません。 文字列の長さが本当に必要な場合は、文字列をバイトに変換してからバイト数をカウントすることができます。しかし、問題は、一部の特殊文字が 1 対 1 でバイトに変換されないことです。また、一部の特殊文字は単に変換されず、トランザクションがクラッシュする可能性があります。 最終的には、文字列を処理し、通常の文字列をテストするスマート コントラクトを作成することになります。すると、処理されない文字列が到着し、すべてがクラッシュしたり、特殊文字が原因で文字列の長さが誤ってカウントされたりします。 文字列に関する結論は簡単です。コントラクト内で文字列を操作したり、文字列に依存したりしないでください。文字列を保存することが重要である場合は、バイトを保存してバイトに依存し、サービス自体で文字列をバイトに変換します。 外部通話の問題 ブロックチェーンの主な特徴の延長である次の複雑さは、分離です。ブロックチェーン上に存在するすべてのデータは、ブロックチェーン内で生成されるか、外部からブロックチェーンに送信されます。ただし、ブロックチェーン自体は外部の世界にアクセスできるわけではなく、他のスマート コントラクトにのみアクセスできます。 問題は、すべてのスマート コントラクト コマンドがネットワークの各参加者で実行されることです。また、すべてのノードで同じ情報が受信されるかどうかはわからないため、外部ソースを信頼することはできません。各ノードが異なるデータを持つ異なるバージョンのブロックチェーンを持つことになり、ブロックチェーンが崩壊することになります。 そして、「現在の外気温を取得する」という些細なタスクが不可能になります。天気は常に必要というわけではありませんが、一部のデータ (為替レートや外部システムの現在の状態など) は不可欠です。解決策は次のアプローチにあります。 私たちにはオペレーター コントラクトがあり、サービスでは「このサーバーにこのようなパラメータでリクエストを送信する」などのタスクを送信します。 コントラクトは を発行する イベント 別のバックエンドがこのイベントをサブスクライブし、イベントから「どこにどのようなパラメータでリクエストを送信するか」と「それをこのコントラクトのここに配置する」という回答を示す情報を抽出します。 サーバーは適切なパラメータでリクエストを送信し、応答を取得します サーバーは要求された契約に応答を送信します。 次に何が起こるかは、このデータで何が起こるかです。 非常に長いチェーンであることがわかりました。悲しいことに、お金は「このようなリクエストをしてください」という私の最初のリクエストと、リクエストを実行したサーバーによってすでに行われている 2 番目のリクエストで引き落とされます。 たとえば、ステップごとに 50,000 GAS が必要です。トランザクションを開始し、50,000 GAS LIMIT を設定すれば、問題ないと考えます。しかし、たとえば、新しい天気を保存する仕組みが変わります。気温が 10 度を超えると、参加者の 1 人に送金する必要があります。ロジックが拡張され、たとえば、トランザクションごとに 80,000 GAS が必要になります。 結局、2 回目のトランザクションで、トランザクションのガス不足によりチェーン全体が崩壊します。外部呼び出しの周りのこのような「野菜畑」は、このようなプロジェクトをさらに複雑にします。おそらく、外部世界とのハード接続がある場合は、プロジェクトにブロックチェーンを選択すべきではありません。 事前に決定できない通常のランダム性もありません。このランダム性も、さまざまなプロバイダーによって「そのまま」提供されます。つまり、ランダムな値がスマート コントラクトに定期的に書き込まれるだけです。しかし、実際の金融プロジェクトでそのようなものを信頼するのは危険です。 変数の値がブロックマイナーによって設定されるという事実は、特に注意を払う価値があります。もちろん、マイナーがブロックをマイニングするのは自分であることを事前に知っていて、時間を代用できるとは考えにくいです。それでも、仮説上の可能性はあります。この危険は 15 秒という状況に関係しており、分単位や長い時間間隔に頼る場合は、このような問題は発生しません。 block.timestamp セキュリティ問題 セキュリティについてはあまり話すつもりはありません。しかし、重要な点を強調しておきます。ブロックチェーン内のすべては誰でも見ることができます。他人がアクセスできないのは、あなたの秘密鍵だけです。スマート コントラクト コードは、監査に合格し、スマート コントラクト ユーザーが信頼できるように、公開されています。 監査手順とは、スマート コントラクト コードを確認し、この特定のコントラクトがこのアドレスに投稿されていることを確認するために会社を雇うことです。コントラクトのセキュリティの問題がチェックされ、開発者の宣言どおりに実行されます。次に、監査会社は「このコントラクトは当社によって検証済みです。信頼できます」などの情報を自社の Web サイトに投稿します。 不変変数 しかし、コントラクト コードが提供されていなくても、簡単に逆コンパイルできます。たとえば、次のコードには不変の変数がありますが、これはコード内のすべての場所で定数に置き換えられるだけです。 このコントラクトをデプロイし、デコンパイラーで開くと、次のようになります。 つまり、変数のこの値がすぐに取得されます。 プライベート変数 バックエンドでは落ち着いていられることに慣れているので、メモリアクセスなしで読み取るプライベート変数の値は問題になります。ここでも同じで、誰もが「メモリ」にアクセスできるというだけです。 変数 private と呼びます。スマート コントラクトをデプロイし、簡単なコード スニペットでその値を取得します。 amount そうすることで、結局何でも引き出せることになります。したがって、スマート コントラクトに機密情報を保存しようとは考えないでください。 スマートコントラクトの展開 変更をロールバックすることは基本的に不可能です。スマート コントラクトは一度割り当て解除されると、何も変更できなくなります。それは、永遠にブロックチェーン上に残ります。 アップグレード可能なスマートコントラクト そのため、一度にすべてを正しく適切に記述する必要があります。私はそれができないので、興味深い回避策である をすぐに思いつきました。その仕組みは次のように機能します。 アップグレード可能な契約 契約書の最初のバージョン(契約書V1)が公開されました プロキシ コントラクトが投稿され、次のタスクが実行されます: すべてのリクエスト 1v1 をコントラクト V1 に転送するか、独自のストレージを使用してターゲット コントラクトのロジックのみを使用します。 さらに、ユーザーはメイン契約と同じ方法でプロキシ契約と通信します。 契約を更新する必要がある場合、管理者は契約 V2 をデプロイし、admin-contract を介して、実装が現在契約 V2 のアドレスにあることを proxy-contract に通知します。 次に、ユーザーもプロキシと通信し、Contract V2 のメカニズムがすでに実行されます。 次に、ユーザーもプロキシと通信し、Contract V2 のメカニズムがすでに実行されます。 このメカニズムには、いくつかの制限とトリックがあります。たとえば、以前のバージョンの変数は、新しいバージョンのコントラクトでは変更できません。変数が不要になった場合は、そのまま残して新しいコントラクトに取り込む必要があります。 もちろん、このソリューションや他の多くのソリューションには、すでに既成の回避策があります。これらの開発の主なサプライヤーは です。したがって、幸いなことに、車輪の再発明は必要ありません。 OpenZeppelin アップグレード可能な契約: アップグレード可能なスマート コントラクトは、監査を受けない大きな理由です。ブロックチェーンの世界は信頼の上に成り立っています。現時点では、スマート コントラクトは正直でオープンな仕組みを持っているかもしれませんが、後になって、スマート コントラクトの所有者は、自分がすべてのお金を受け取るような実装に切り替えるでしょう。