blockchainjapan’s blog

旬のブロックチェーンを記事を厳選して提供!

技術シリーズ#10: Viteの非同期アーキテクチャを例を挙げて説明する


技術シリーズ#10: Viteの非同期アーキテクチャを例を挙げて説明する

著:Allen Liu

この記事では、Viteの最も重要なイノベーションの一つである、非同期アーキテクチャについて解説します。

同期 v.s. 非同期

同期アーキテクチャでは、プロセスがリクエストを実行すると、実行が完了するまで待ってから結果を返す。その間、プロセスは一時停止します。リクエストが送信された後、非同期プロセス(通常はスレッド)が裏で動き、メインプロセスは他の実行を処理するために先に進みます。非同期プロセスが完了すると、メインプロセスは結果を返した通知を受け取ります。

非同期アーキテクチャの利点は明らかです。処理のバッチ実行がはるかに高速になります。 ViteはDAGベースの元帳を採用しており、各アカウントには個別のチェーンがあります。 トランザクションは1つのアカウントチェーンの状態を変更するだけなので、複数のアカウントのトランザクションを並行して実行して、より高いスループットを実現できます。

Viteでの非同期

Viteの非同期設計は主に3つの側面から成り立っています。

ここでは、3点目の「スマートコントラクト間の非同期通信」に焦点を当てます。

以下は、Solidity++の非同期構文とVVM(Vite仮想マシン)の実行処理の両方を紹介する簡単な例です。

VoteContractはその名の通り、投票コントラクトです。フィールドを見てみましょう。

voteMap は投票記録を保存するために使用されます。invalidAddrs は無効な投票アドレスをすべて保存するためのマップです。checkAddr は投票アドレスを検証する別のコントラクトのアドレスです。

CheckContract は投票アドレスの検証を行います。検証を行い checkResultを返すcheck 関数を持っています。

Solidity++での非同期構文

Ethereumとは異なり、Vite上のスマートコントラクトは非同期です。スマートコントラクトのメッセージリスナーは、他のコントラクトからの着信メッセージをリッスンし、ビジネスロジックを実行します。

上記の例では、VoteContractは voteisValid という2つのメッセージリスナーを定義しています。投票プロセスでは、ユーザーはメッセージリスナーの vote をトリガーして、アドレス addr に投票します。vote は支払い可能です。ユーザーは呼び出し時にスマートコントラクトにvoteNum トークンを転送する必要があります。

CheckContract には checkVaild という1つのメッセージリスナーがあり、このリスナーがアドレス確認を行い、結果を isValid に送り返します。こちらも支払い可能です。

Solidity++ では、メッセージリスナーを介してスマートコントラクト間でメッセージを渡すことができます。したがって、スマートコントラクトを書き始めるには、最初にメッセージを宣言しなければなりません。

The message must have the same name with a corresponding message listener. In our example, VoteContract declares a message checkValid, which will be sent to the checkValid CheckContract for processing. CheckContract also declares a message isValid to send the verification result to VoteContract. メッセージは、対応するメッセージリスナーと同じ名前である必要があります。例では、VoteContractはメッセージ checkValid を宣言します。このメッセージは checkValid CheckContract に送信されて処理されます。 CheckContract はまた、検証結果を VoteContract に送信するためのメッセージ isValid も宣言します。

メッセージを送信するための構文は send(address addr, message msg, tokenId token, uint amount)です。ここで addrコントラクトアドレス、msg は送信するメッセージ、tokenamount はメッセージ内で転送されるトークンの量を表します。tokenamount はオプションであり、デフォルトでは転送は行われないことに注意してください。addr.transfer(tokenId token, uint amount) を使用して、スマートコントラクトの別のアドレスにトークンを送ることもできます。

メッセージリスナーには戻り値がありません。呼び出し元のコントラクトがメッセージ呼び出しの結果を待つことはありません。上記の例のように、VoteContract はメッセージリスナーの vote にあるアドレスを確認するために、CheckContract にメッセージ checkValid を送信します。メッセージが送信されると、残りの処理が継続して実行されます。ここで、メッセージリスナーは戻り値のない Ethereum の関数とは異なるという点を指摘しておく必要があります。Ethereum では、関数呼び出しに戻り値がなくても、処理が成功したかどうかを示す結果が返されます。そのため、Ethereum での関数呼び出しは同期的に行われます。

非同期のVite仮想マシン

ここでは、Vite 仮想マシンでの非同期コントラクト実行の実装を見てみましょう。

まず最初に紹介したいコンセプトを示します。

  1. Vite上のトランザクションは、リクエストランザクションとレスポンストランザクションに分類されます。転送を送信するか、コントラクトを呼び出すかのいずれかで、ブロックチェーン上に2つの別々のトランザクションが作成されます。リクエストランザクションが完了すると、レスポンストランザクションの完了を待たずにすぐに戻ります。
  2. Ethereum上では取引手数料としてGASが請求されます。EVMはGASの消費量を計算し、取引の実行時に差し引きます。Ethereumとは異なり、ViteではGASは請求されません。GASはQuotaと呼ばれる別のリソースに置き換えられます。Viteではトランザクションがリクエストランザクションとレスポンストランザクションに分かれるため、リクエストランザクションではレスポンスで消費されるクオータがわかりません。クオータはリクエストランザクションとレスポンストランザクションで別々に計算されて消費されます。

Vite 仮想マシンでは、リクエストランザクションとレスポンストランザクションの実行は分離されています。リクエストランザクションは通常、転送またはコントラクトコールのいずれかの形式で、ユーザによって送信されるメッセージです。例のように、ユーザが VoteContract に送信した vote メッセージはリクエストランザクションです。リクエストランザクションを開始するための受動的な方法もありますが、これは後ほど紹介します。

最初のステージでは、Vite 仮想マシンはリクエストランザクションの処理を開始します。実行中に例外がないと仮定して、仮想マシンは最初にリクエストランザクションで消費されるクォータを計算し、次に送信者のアカウントから対応するクォータの量を差し引き、最後にリクエストランザクションブロックを更新して戻ります。

2番目のステージでは、VoteContract の 委任コンセンサスグループのブロック生成ノードがリクエストランザクションを受信すると、レスポンスブロックの構成を開始し、仮想マシンは、深度検査、クォータ計算、バランスインクリメントなどのいくつかの操作を実行した後、レスポンストランザクション内のメッセージリスナーのコードを実行します。例ではcheckValid メッセージが CheckContract へ送信されていますが、この場合、仮想マシンvoteビジネスロジックがすべて完了した後、CheckContract に別のリクエストランザクションを開始する必要があります。では、詳しく説明していきます。

  1. まず、リクエストランザクション(CheckContractへの)はVoteContract のレスポンストランザクションで生成されます。この状況を処理するために、Vite仮想マシンは、レスポンス内のリクエストランザクションのリストを保持し、レスポンストランザクションが完了するとそれらを送信します。ここでの例では、checkValid メッセージがリスナーメソッド vote から 送信され、後者は CheckContract がレスポンスを送り返すかどうかに関係なく実行を続けます。
  2. 第二に、VoteContract はコントラクト内でリクエストランザクションを開始します。この内部リクエストランザクションはクォータを必要としないことに注意してください。また、トークンの転送を行うため、対応するトークンの量を残高から差し引く必要があります。この例では、複数のユーザーが投票した場合、VoteContract は CheckContractへの複数の転送を開始し、変更を反映させるために残高を差し引くことになります。
  3. 第三に、これらのリクエストメッセージがいつ、どのような順序でCheckContract によって受信されたかに関係なく、CheckContract によって受信された最終的な金額は必ず等しくなります。

第3段階では、CheckContract は、レスポンストランザクションでVoteContract によって送信されたリクエストに応答します。 より正確には、checkValid メッセージリスナーで。 実行プロセスは第2段階と同様であり、isValid メッセージが新しいリクエストで VoteContract に送信されます。

第4段階では、CheckContract によってレスポンストランザクションが開始され、isValid のロジックが実行されます。これは記事の冒頭で述べたもので、リクエストが結果を返すとプロセスは通知を受け取り、処理を開始します。

上記の実行プロセス全体で、リクエストで例外が発生した場合、リクエストランザクションは元に戻されます。 リクエストランザクションが成功し、レスポンスで例外が発生した場合、レスポンストランザクションは元に戻され、リクエストランザクションが残高を変更した場合、トークンを送り返すために別のリクエストランザクションコントラクトによって開始されます。 例えば、VoteContract の残高は、checkValid が開始された後に差し引かれます。 CheckContract のレスポンスが失敗した場合、の別のリクエストで VoteContract へ払い戻しが開始されます。

ゲッター

非同期メッセージリスナーには戻り値がありません。 そこで、コントラクト状態を取得するために、getterメソッドを導入します。

この例では、getterメソッド getVoteNum が提供されており、コントラクトのコントラクト状態を取得します。 これには戻り値があります。 ただし、getterメソッドを使用する場合は、次の2つのポイントを覚えておく必要があります。

  1. 一つは、 getterメソッドはコントラクトの状態を取得するためだけに用い、状態を変更することはできないということです
  2. 二つ目は、getterメソッドはブロックチェーン上のデータにはアクセスできないということです

したがって、getterメソッドはオフチェーンクエリメソッドとも呼ばれます。 実際、それらはイーサリアムのオフチェーンクエリのパブリックメソッドに似ています。

サマリー

この記事では、Viteにおける非同期設計と関連する実装を実例を用いて簡単に紹介しました。他にも質問があれば、遠慮なくViteのTelegramグループやDiscordで質問してください。喜んでお答えいたします。