LayerCake ネットワーク間のコンポーザビリティ保証型プロトコル

2023年7月18日

LayerCake ネットワーク間のコンポーザビリティを保証するプロトコル

概要

仮想通貨の学校

LayerCakeのプログラミングの説明になります。この記事は以下のURLの日本語訳になります。エンジニア寄りの解説がメインです。

https://github.com/flare-labs-ltd/layercake

LayerCakeの特徴

  • LayerCakeの楽観的なモデルの遅延転送とは対照的に即時転送。
  • LayerCakeのすべての送金は、実際の資産によって1対1の裏付けで保証があります。この保険はシステム内で帯域幅と呼ばれ、オペレーターの誤った行動や、例えば6時間といった一定の深さで起こるブロックチェーンの再編成をカバーします。
  • LayerCakeにはカスタムコードエンドポイントがあり、開発者はアセットのブリッジと同じステップで完了する自動アクションをコーディングできる。例えば、どんな開発者でもLayerCakeの上に独自のスワッププロトコルを構築できる。
  • LayerCakeのシステムには、ガバナンスの役割や信頼ある当事者といったものは存在しない。システムへの参加は、ブリッジされたERC20トークン(WETHなど)を所有、それを使って帯域幅プロバイダになって活動するか、ユーザーになってシステムに参加するかによって決まります。
  • システムの帯域幅効率は110%で、12時間ごとに帯域幅の100%を再利用できる。10%のロックアップと12時間の帯域幅期間は、システムの設計に影響を与えることなく、異なるブロックチェーン間やブリッジした異なるアセット間でシステムの異なる展開のために、ゼロ以外の任意の値にカスタマイズができます。
  • このシステムは、誰もが帯域幅の否定メカニズムへの関与を許可しており、信頼できる当事者に依存なく保険システムを完成させることができる。
  • このシステムには、検証済みのセットアップコントラクトのセットがあり、信頼できるパーティを介さずにLayerCakeコントラクトを初期化、加入に関心のある利用可能な帯域幅プロバイダの数だけシステムが開始できるようにする。
  • システムのフットプリントは軽量かつ高速であり、ゼロ知識方式や楽観的ロールアップ方式のアプローチとは異なり、ソースブロックチェーンで発生した計算を宛先ブロックチェーンで再実行や、その逆の利用はない。

LayerCakeのContracts

LayerCake.sol

操作の保存

storeStandardOperations() 関数は LayerCake でユーザが操作する主要な関数です。

function storeStandardOperations(
    Operations memory _operations
)
    external

ユーザーは、LayerCake コントラクトに保管中のトークン合計の depositCap までの量の ERC20 トークンをデポジット、トークンの受取人を指定します。また、オプションのカスタムコード callData とともに、宛先チェーン上でトークンが引き渡されたときに受取人のアドレスで自動的に実行されます。callDataの実行は、保存した操作の一部でユーザーが指定するガス量callDataGasLimitによって制限を受ける。nonce フィールドは、LayerCake のコントラクトストレージでトランザクションに一意な識別子を持たせるために使われ、実際にはユーザがクライアント側で取得した現在のナノ秒のタイムスタンプである。feeフィールドは、このデポジットリクエストをデスティネーションチェーンで履行する帯域幅プロバイダへの配信用トークンの量である。executionTimeの値は、storeOperations()の呼出し内でblock.timestampに設定、_operationsが反対側のチェーンで実行可能な最も早い時刻を表す。

struct Operations {
    uint256 nonce;
    uint256 amount;
    uint256 fee;
    address sender;
    address recipient;
    uint256 executionTime;
    uint256 callDataGasLimit;
    bytes callData;
    bool cancel;
    uint256 cancellationFeeRefund;
    address negatedBandwidthProvider;
    bool initialNegation;
    bytes32 invalidExecutionProofId;
}

storeOperations()を呼出すたびに、以下のように一意のexecutionIdが生成:

bytes32 executionId = getExecutionId(
    departingPathwayId, 
    _operations
);

ここで、departingPathwayIdは以下のように定義:

bytes32 public immutable departingPathwayId = getPathwayId(_thisChainId, _oppositeChainId, _assetId, _contractId);

を表し、現在のチェーンからこの特定のアセットの反対側のチェーンへの一意な経路 ID を表します。contractIdは、特定のソースチェイン、デスティネーションチェイン、アセットに対して、複数のLayerCakeブリッジが並行動作できます。

同様に、arrivingPathwayId は以下のように定義:

bytes32 public immutable arrivingPathwayId = getPathwayId(_oppositeChainId, _thisChainId, _assetId, _contractId);

送信元チェーンの departingPathwayId は送信先チェーンの arrivingPathwayId と等しく、逆も同様であることに注意。これらのパスウェイ ID は、LayerCake コントラクトの特定のペアに一意な ExecutionId を保持に使われます。

executionId の生成だと、まずそれが一意であることをチェック、次にそれを使ってストレージに保存:

storageManager.storeExecutionId(_operations.executionTime, executionId, _operations.sender, _operations.amount);

LayerCake 作戦を実行:

これは、関数bandwidthManager.proveBandwidth(msg.sender, bandwidthUsed)を使って証明できる。executeOperations()の呼出しは競争プロセスであり、executeOperations()への 呼出しの実行に成功した最初の帯域幅プロバイダが、ユーザーが対応するstoreOperations()への呼出しを反対側のチェーン上で実行する権利を獲得できる。

function executeStandardOperations(
    ExecutionProof memory _executionProof
)
    external

executeOperations()への入力フィールドは、反対側のチェーンのstoreOperations()への対応する呼出しと一致し、さらに追加フィールドがあり、これらはすべてExecutionProofメモリ_executionProof構造体に含まれる。ExecutionProof構造体は次のように定義される:

struct ExecutionProof {
    Operations operations;
    uint256 partialAmount;
    uint8 v;
    bytes32 r;
    bytes32 s;
}

vr、およびsは、executeOperations()の呼出しを発行する帯域幅プロバイダに対応の秘密鍵を使った、executionIdに適した署名の要素である。この署名されたexecutionIdは、executeOperations()の実行を成功させるためには、executeOperations()への入力フィールドによって生成されたexecutionId と一致する必要がある。これは、帯域幅プロバイダがこの実行を承認したという簡潔な証明を残すことを意味する。この証明は、実行された操作に対応できる有効な保存操作がなかった場合に、プロバイダに対して使える。

帯域幅プロバイダは、partialAmount < amountの指定で、リクエストを部分的に満たせます。帯域幅プロバイダが、リクエストに充填される残量より大きいpartialAmount を指定した場合、帯域幅プロバイダは、partialAmountの値全体ではなく、リクエストの残量のみをその帯域幅から引き落とせる。部分的な料金は、リクエストの充足に対する各帯域幅プロバイダの貢献度に比例して支払われ、executeOperations()を呼出すたびに、現在のユーザー増加料金に基づいて支払われる。リクエストの合計値は転送され、リクエストが完全に満ちた時点で、 recipientCalldataを使ってて受信契約上でカスタムコードを呼出す。

完全な保管と実行のフローを以下に示す:

オペレーションのキャンセル

ユーザーは、ストアドオペレーションがまだ完全に実行されていない場合、実行のはずだったデスティネーションチェーンでcancelStandardOperations()を呼出すことで、ストアドオペレーションをキャンセルできる:

function cancelStandardOperations(Operations memory _operations) external nonReentrant

これにより、ユーザーが預託金額から、デスティネーション・チェーンで部分的な実行にすでに使われた手数料を差引いた金額、およびexecuteCancelStandardOperations()を呼出してキャンセル処理を実行したオリジン・チェーン上の帯域幅プロバイダへの手数料を差引いた金額を返す、ユーザーが操作を保管したチェーン上での有効な実行が作られる。

function executeCancelStandardOperations(ExecutionProof memory _executionProof) external nonReentrant

帯域幅の加算と減算

addBandwidth()を呼出せば、いつでも誰でも新たな帯域幅をシステムに追加でき る:

function addBandwidth(
    uint256 _bandwidthAmount
)
    external

これにより、_bandwidthAmount + _bandwidthAmount/bandwidthDepositDenominatorトークンがデポジットされ、送信者に_bandwidthAmountの新たな帯域幅が付与する。bandwidthAmount/bandwidthDepositDenominatorはトークンの預託であり、subtractBandwidth()関数を使 用して正確な方法でシステムから離脱するインセンティブで保有。帯域幅プロバイダが、有効なstoreOperations()を使用せずにexecuteOperations()を使うなど、他の方法でシステムから離脱した場合、そのプロバイダはこの預託金を失う。

function subtractBandwidth(
    uint256 _bandwidthAmount
)
    external
    nonReentrant

帯域幅プロバイダがsubtractBandwidth()を量_bandwidthAmountで呼出した場合、その帯域幅は利用可能性が証明され、次にexecuteOperations()の呼出時と同じproveBandwidthPrivate()関数を使って使われる。その後、帯域幅プロバイダのcurrentTotalBandwidth_bandwidthAmountだけ減少し、_bandwidthAmount + _bandwidthAmount/bandwidthDepositDenominator トークンが送信される。

bandwidthPeriod時間ごとに_bandwidthAmountトークンへのアクセスのみが許可されている間に、_bandwidthAmount + _bandwidthAmount/bandwidthDepositDenominatorトークンの保存は、帯域幅の否定に報いるためである、

これは、システムから欠陥のある帯域幅プロバイダの削除方法である。

LayerCakeの証明機能

executeOperations()への呼出しの有効性は、帯域幅プロバイダが実行呼出しの実行に署名しなければならなかったであろう署名済みの_executionProof を使ってgetExecutionValidity()を呼出しで、反対側のチェーンで確認できる。

function getExecutionValidity(
    address _bandwidthProvider,
    bytes32 _executionId,
    ExecutionProof memory _executionProof
)
    public
    view
    returns (bool)

この呼出しは、ユーザーがstoreOperations()を呼出すべきチェーン上での実行が有効なストアドオペレーションセットに対応しているかどうかをチェック。この呼出しの成功には

storageManager.getExecutionIdStored(_executionProof.operations.executionTime, _executionId)

falseを返さなければならない。

recoverSigner(_executionId, _executionProof) == _bandwidthProvider

trueを返さなければならない。

帯域幅の否定

保険証明システムを完成させるために、否定という概念が導入された。帯域幅プロバイダがチェーン上で無効な実行を発行した場合、誰でも競って、そのチェーン上でstoreNegationOperations()を呼出しができる。これにより、帯域幅プロバイダの帯域幅量に-1が乗算され、事実上、負のままシステム内で利用可能な帯域幅がなくなる。

function storeNegationOperations(
    Operations memory _operations
)
    external
    nonReentrant

上記の_operations構造体内では、negatedBandwidthProviderが無効な実行を実行した帯域幅プロバイダであり、initialNegationがtrueに設定、invalidExecutionProofIdが帯域幅プロバイダによって実行された無効な実行のハッシュであり、amountdefaultNegationCostの値に設定される。

storeNegationOperations()へのこの呼出しは、送信者からdefaultNegationCostトークンを預かるが、_operations.amountの値をdefaultNegationCost + bp.currentTotalBandwidth/negationRewardDenominatorに等しくなるように更新してから、このチェーンに操作を格納。これは事実上、送信者にdefaultNegationCostトークンを課金するが、その送信者に対して、反対側のチェーンでのより高い値の実行を許可。これは、対応ネゲーションの実行が行われる反対側のチェーンで、ネゲーション操作を格納したアカウントへのdefaultNegationCost + bp.currentTotalBandwidth/negationRewardDenominatorの転送の許可前に、参照の実行証明が無効を要求するためである。

negationRewardDenominator2 * bandwidthDepositDenominatorに等しい。つまり、帯域幅の預け入れ割合が追加された総帯域幅の10%である場合、ネゲーションの報酬割合はネゲートされた総帯域幅の5%である。

トークンの量 bp. ここで報酬に使われるトークン量bp.currentTotalBandwidth/negationRewardDenominatorは、前述の預託された_bandwidthAmount + _bandwidthAmount/bandwidthDepositDenominatorから引き出せる。この場合、帯域幅プロバイダは、_bandwidthAmountトークンに各 bandwidthPeriod時間分しかアクセスできず、_bandwidthAmountトークンの残量がネゲーション報酬に使われるように、_bandwidthAmountトークンを誤って使ったと想定できる。

function executeNegationOperations(
    ExecutionProof memory _negationExecutionProof,
    ExecutionProof memory _invalidExecutionProof
) 
    external
    nonReentrant

上記のexecuteNegationOperations()の呼出しでは、_negationExecutionProofは、 storeNegationOperations()の呼出しで格納された操作のセットであり、_invalidExecutionProofは、否定された帯域幅プロバイダが署名した完全な無効実行証明である。

bytes32 invalidExecutionProofId = getInvalidExecutionProofId(_invalidExecutionProof);
require(invalidExecutionProofId == _negationExecutionProof.operations.invalidExecutionProofId);
bytes32 invalidExecutionId = getExecutionId(
    departingPathwayId, 
    _invalidExecutionProof.operations
);
bool executionValidity = getExecutionValidity(
                                _negationExecutionProof.operations.negatedBandwidthProvider,
                                invalidExecutionId,
                                _invalidExecutionProof);
require(_negationExecutionProof.operations.initialNegation != executionValidity);

executeNegationOperations()関数の上記の一連のコードでは、まず、_invalidExecutionProofが_negationExecutionProofで参照されていることが、一致するinvalidExecutionProofIdの要求でチェックされる。次に、_negationExecutionProof.operations.initialNegationがtrueに等しい場合、_invalidExecutionProofの有効性がfalseを要求される。つまり、帯域幅プロバイダがその帯域幅を負にした場合、この証明は、そのプロバイダが実際に無効な実行を要求する。

storeNegationOperations()呼出しの送信者は、defaultNegationCostトークンのコストに対して、defaultNegationCost + bp.currentTotalBandwidth/negationRewardDenominatorを得る。

完全なネゲーションのフローを以下に示す:

帯域幅プロバイダは実際には無効な実行を行わなかったが、送信者が storeNegationOperations()を呼出しによって、無効な実行が否定の場合、これは、_negationExecutionProof.operations.initialNegation==executionValidity、つまりinitialNegationがtrueであり、参照された_invalidExecutionProofが実際には有効である。

つまり、帯域幅プロバイダは、帯域幅を再度ネゲートするような設定(正になるように設定)を指示し、現在ネゲートの合計帯域幅と同じ量をシステムに預ける。これにより、executeNegationOperations()への有効な呼出しが作られ、帯域幅プロバイダは、帯域幅プロバイダを最初にネゲートした元の送信者が預託のdefaultNegationCostだけでなく、総帯域幅も取り戻す。帯域幅プロバイダの帯域幅は、1帯域幅期間後に動作を再開できるように、再びプラスになった。

帯域幅を負にする否定を最初に格納、次に正の呼出しのペアは、同じinvalidExecutionProofIdの参照でリンクの必要があることに注意。これは、有効な呼が常に1つだけである必要があります。そうでない場合、否定を反転させる2回目の呼では、その前の最初の否定で提示された実行証明ではなく、任意の有効な実行証明の参照可能性がある。

帯域幅プロバイダが上記のように否定を取りやめたが、実際には無効なinvalidExecutionProofIdを参照した場合、反対側のチェーンの対応するexecuteNegationOperatios()の呼出失敗で、否定を取り消すために送信したamount=negatedBandwidthProvider.currentTotalBandwidthを失う。このため、negatedBandwidthProvider.currentTotalBandwidthは、 有効な対応する実行がないままシステムに預けられるため、預けられたnegatedBandwidthProvider.currentTotalBandwidth/negationRewardDenominatorは、この帯域幅プロバイダに対して始まる次のネゲー ションの報酬となって、システムに残る。

storeNegationOperations()の連続呼出は、常に、invalidExecutionProofが有効または無効であるという主張が実際に正確な送信者に報酬をもたらす。そのため、帯域幅プロバイダが無効なアクションを実行していないのにネゲートされ続けた場合、そのプロバイダは繰り返しdefaultNegationCostの獲得になる。

一般に、最初の否定実行のコストは低く、否定の作成は参入障壁が低い。ただ、帯域幅プロバイダが何度もnegationCounterResetを負になり、negationCostResetPeriodの長さのあいだ帯域幅を使っていない場合、最初のネゲーションの実行コストは、最後のネゲーションの取消しの発生から2*bandwidthPeriodの時間の合計帯域幅になる。その後、bp.negationCounterの値はゼロにリセット、以降のネゲーションのコストは再びdefaultNegationCostとなる。この設定の目的は、一般的にネゲーションの作成コストを低く保つことであるが、システムを停止させるためにネゲーションを無限に呼出しの抑制に、ネゲーションコストを高くする。

negationCounterResetの値は、自動的にbandwidthDepositDenominatorに等しくなるように設定される。つまり、bandwidthDepositDenominatorが高い場合、ネゲーションの報酬は低くなるため、その後のネゲーションのコストは、negationCostResetPeriod = negationCounterReset * bandwidthPeriodとなる。

追加機能

LayerCakeの利用者は、increaseFee()の呼出しで、操作の実行を想定中チェーンの料金を増やせる。

function increaseFee(
    bytes32 _executionId,
    uint256 _executionTime,
    uint256 _addedFee
) 
    external 
    nonReentrant 

ユーザーは、updateStandardOperations()の呼出しで、まだ未実行の、保存している操作の送信者、受信者、callDataの更新もできる。

function updateStandardOperations(
    Operations memory _operations,
    address _updatedSender,
    address _updatedRecipient,
    bytes memory _updatedCallData
)
    external
    nonReentrant

この関数は、ユーザーが操作の実行を期待しているチェーン上でも呼出せる、この機能は、ユーザーが保存のオペレーションをサードパーティとの交換や、オペレーションが実行先のチェーンで完全に準備されるまでに時間がかかる場合に、より有利な実行ができるようにオペレーションを更新等の機能を提供。

LayerCakeBandwidthManager.sol

LayerCakeの帯域幅は、12時間など、bandwidthPeriod時間Tごとにリサイクルされる。これは、帯域幅Bを持つ帯域幅プロバイダが、時間TあたりトークンBの量まで executeOperations()を呼出すことを意味する。

  • reorgAssumption: これは、オリジンチェーンまたはデスティネーションチェーンが、デポジットが消失ブロック再編成に遭遇しないと安全に想定される経過時間である。この時間までは、オリジンチェーンでユーザー預金を消滅させるような再整理が発生した場合、帯域幅プロバイダがデスティネーションチェーンでexecuteOperations() の無効な呼出しを実行シナリオと完全に一致。帯域幅プロバイダは、再編成のリスクを引き受けるエンティティであり、これはプロバイダが料金の根拠部分の一部である。帯域幅プロバイダが、蓄積された操作のセットで十分な確認を要求しないというリスクの高い戦略を実行する場合、蓄積された操作がネットワークの再編成によって意図的に消滅させられた場合に、その操作が否定だというインセンティブシナリオの発生可能性の存在に注意。
  • bandwidthPeriod: 帯域幅プロバイダの帯域幅がリフレッシュされて最大量に戻るまでの時間。2*reorgAssumptionに等しくなるように設定される。

新たな帯域幅期間が開始し、帯域幅プロバイダが、前の帯域幅期間に使った帯域幅と合計するとBを超える量の帯域幅を予約している場合、そのプロバイダの最終アクティブ時間がreorgAssumption時間R (たとえば6時間)よりも長いことが必要である。

// New bandwidth period
if (_amount > bp.currentTotalBandwidth.sub(bp.currentUsedBandwidth)) {
    require(block.timestamp.sub(bp.timeLastActive) > REORG_ASSUMPTION);
}

これにより、帯域幅プロバイダが、帯域幅期間の終了時に、Bまでの量に対して executeOperations()の誤った呼出しを防げる。次の帯域幅期間の開始時に、別の量Bに対して再度 executeOperations()を呼出す。つまり、帯域幅がBしかないのに2Bを実行

帯域幅の証明

function proveBandwidth(
    address _bandwidthProvider,
    uint256 _amount
)
    external
    layerCakeOnly
    nonReentrant

要約は、executeOperations()の呼出中にproveBandwidth()の呼出しで実行されるチェックの総セットは以下のとおりである:

  • 帯域幅プロバイダが現在肯定中で、過去のbandwidthPeriod時間内に正でない必要である。
  • 帯域幅プロバイダは、利用可能な帯域幅がpartialAmount以上でなければならない。

LayerCakeStorageManager.sol

単純な方法で実装した場合、かなりのストレージ容量を使うストレージ変数が3つある:

mapping(bytes32=>bool) internal openedExecutionIds;
mapping(bytes32=>address) internal preparedExecutionIds;
mapping(bytes32=>uint256) internal increasedFees;

storeOperations()executeOperations() を呼出すたびに、これらのハッシュマッピングはステートサイズが大きくなり、LayerCake コントラクトとのインタラクションが時間とともに遅くなります。この軽減用のシナリオでは、新たなストレージの値が古いものを置き換えるときに自動的に上書きされる一定サイズの配列を持つというアプローチがあります。ただ、LayerCakeにとってここでの障害は、古いストレージ値に正確な時間だけアクセスの必要がある。これは、LayerCakeコントラクトの1秒あたりの実行量を制限し、1回あたりのアレイを固定サイズで実現できますが、アンバインドストレージがコントラクトとのインタラクションを遅くするという本来の問題を避けるためにスループットを制限するのは悪いトレードオフです。

LayerCakeの効率的なストレージ設計

LayerCakeのストレージ設計ソリューションは、ストレージスロットを活用します。ストレージスロットは、継続的にデプロイされ、期限が切れると使われなくなる個別のストレージ契約です。ストレージスロットは制限のない量のストレージを含みますが、固定した時間しかアクティブにならないため、ストレージの量は現在運用されているネットワークのスループットによって上限が決まります。これは、各ネットワークの特定のスループット特性に合わせて設定しなければならない契約レベルでのスループット・パラメータのチューニングよりも優れたソリューションである。

function getStorageSlot(uint256 _timestamp)
    private
    returns (uint256 storageSlot)

getStorageSlot() に渡せる _timestamp の値は、executionId 識別子ハッシュの executionTime フィールドである。N 個のストレージ・スロットが任意の時点でアクティブであり、例えば 100 個のスロットがあり、各スロットは同じ期間、例えば 365 日存続する。

getStorageSlot()の呼出しが新たなストレージスロットの時間帯に発生した場合、新たなスロットが自動的に配置され、Nスロット前のストレージスロット参照が上書かれる

LayerCakeCalldataInterface.sol

calldataInterface.execute()を使ったexecuteStandardOperations()の呼出時に、_recipientアドレスのカスタムコードを呼出すために、storeStandardOperations()の呼出しの一部で、ユーザー定義の_callDataフィールドが使われる。

function execute(
    address _recipient,
    bytes calldata _callData
)
    external 
    nonReentrant

アクションの例は、この calldataInterface.execute() の実行から LayerCake に新たな預金の開始である。これは、全ての LayerCake の預け入れが最初の経由から目的地に向かう流動性ハブネットワークを構築する基礎となる。流動性ハブの設計は、流動性を単一のネットワークに集中させ、必要な LayerCake のネットワーク間の接続の総数を減らす。

calldataInterface.execute()の使い方は完全に柔軟であり、ユーザーによる単一の初期取引の一部で、接続されたネットワーク上のDeFiコントラクトなどと同時のやり取りに使用できる。

Verified Setup of LayerCake Contracts

検証された方法でLayerCakeコントラクトを展開する方法は以下の通り:

Origin-Side

  1. LayerCakeOriginDeploy 契約は LayerCake のデプロイパラメータ _deployParams と共にデプロイされます。
  2. ある期間 _depositWindow (例えば 7 日間)、誰でも deposit() 関数を使ってオリジン側のトークンを LayerCakeOriginDeploy 契約に預けるれます。
    1. 入金者はこの間にトークンを引き出せます。
    2. この変数は、このコントラクトで行われた残高変更トランザクションの最終セットの識別に使われる。
  3. deployLayerCake() はオリジン側に新たな LayerCake コントラクトをデプロイし、オリジン側のトークンの残高をそのコントラクトに送る。

Destination-Side

  1. LayerCakeDestinationDeploy コントラクトは、LayerCakeOriginDeploy で使われた LayerCake デプロイメントパラメータ _deployParams と一致するセットで誰でもデプロイできます。LayerCakeDestinationDeploy はまた、オリジン側の verificationHash_depositedAmount の対応する値で構築されます。
    1. 構築の一部として、デスティネーション側の LayerCake と LayerCakeTransportedToken コントラクトが自動的にデプロイされ、LayerCake コントラクトは LayerCakeTransportedToken の depositCap - _depositedAmount 量を受け取る。
  2. その後、デプロイヤーは setBalanceChange()を呼出すことで、LayerCakeOriginDeploy で発生した全ての残高変更トランザクションを再生し、これにより computedVerificationHash の値が継続的に更新される。
  3. computedVerificationHash == verificationHash となった時点で、デプロイ側は setBalanceChange() を呼出すことができなくなり、LayerCakeDestinationDeploy 契約は deployed == true とマークされる。
  4. 誰でも、オリジン側とデスティネーション側の LayerCake コントラクトの _deployParamsverificationHash の値の比較で、ペアの LayerCake コントラクトの構築を検証できます。
  5. deployed == true となったことで、オリジン側のデポジット者は withdraw() を呼出して LayerCakeTransportedToken の残高を引き出せます。

このセットアップにより、LayerCakeTransportedTokenトークン保持者の初期セットを作り、宛先側でaddBandwidth()を呼出して新たな帯域幅プロバイダを作成できる。同様に、オリジン側でaddBandwidth()を呼出して、オリジン側の帯域幅プロバイダの新規作成もできる。

Unit Tests

まず、マシンにFoundry開発ツールチェーンをインストールします: https://book.getfoundry.sh/getting-started/installation

このリポジトリとそのgitmoduleをクローン:

git clone --recurse-submodules -j8 https://github.com/flare-labs-ltd/layercake.git

layercake/ディレクトリでプロジェクトをビルドする:

forge build

テストの実行

forge test --revert-strings=debug -vvv

LayerCake Indexer

以下の手順はLinuxマシン用で、推奨インスタンスは以下の通りです:

  • Ubuntu 22.10 x64
  • 8GB of RAM
  • 4 CPUs
  • 160 GB storage
  • 5TB transfer

layercake/ ディレクトリから、依存関係をインストール、LayerCake のコントラクトを Coston1 と Coston2 のテストネットにデプロイします:

bash -i script/deps/linux/base.sh
source $HOME/.bashrc

オプション: 帯域幅プロバイダの例を実行

テスト用帯域幅プロバイダの例をインデクサの一部で実行する場合は、.env.deploy ファイルで INDEXER_TEST_BANDWIDTH_PROVIDER=true を設定、.env.secret ファイルで秘密鍵変数を両方のネットワークで資金提供されているアカウントに設定します。この展開例では、プロフェッショナルな帯域幅プロバイダでサーバと秘密変数をセキュリティ保護用に実行すべきすべてを網羅できるわけではないことに注意。

オプション LayerCakeコントラクトをデプロイ

.env.secretファイルに秘密鍵変数を設定、両方のネットワーク上のアカウントに資金提供後、LayerCakeコントラクトの新たなセットを実行してデプロイをテストができます:

./script/test_deploy.sh

Set the .env file

上記のtest_deploy.shスクリプトを実行の場合は、このステップは省略できます。そうでなければ、先に進む前に、.envファイルと.env.exampleファイルに示している例のフィールドがすべて設定している状態をご確認ください。

Run the Indexer

LayerCake インデクサーをオリジンネットワークとデスティネーションネットワークの両方で実行する:

nohup cargo run --bin indexer ORIGIN > indexerOrigin.log 2>&1 &
nohup cargo run --bin indexer DESTINATION > indexerDestination.log 2>&1 &

LayerCake Grafanaダッシュボードを実行

grafanaをインストールください:

bash -i script/deps/linux/grafana.sh
source $HOME/.bashrc

次に、ウェブブラウザで localhost:3000 に移動、初回ログイン {username: admin, password: admin} を使う。次に、LayerCake grafana ダッシュボードをインポートします:

  1. Add your first data source "をクリック。PostgreSQL」を検索、フィールドに入力:
    • ホスト:localhost:5432
    • データベース:lcdb
    • ユーザ:postgres
    • パスワード:postgresのインストール時に上記のセクションで設定したパスワード
    • TLS/SSLモード:無効
  2. Save & test "をクリックしてください。
  3. 前のステップで接続テストがうまくいった場合は、左上の3つのダッシュのナビゲーションボタンをクリック、"Dashboards "に移動します。
  4. New" > "Import" > "Upload dashboard JSON file "をクリック、このリポジトリのlayercake/src/grafanaフォルダにあるgrafana jsonファイルを選択。PostgreSQLドロップダウンをクリック、上記で作ったデータソースを選択。

これで LayerCake grafana ダッシュボードが表示され、storeStandardOpertions 時系列、executeStandardOpertations 時系列、および発信地と宛先のネットワークの帯域幅統計が表示されます。時系列の各TXのエクスプローラリンクは、各データポイントの上部をクリック、"エクスプローラで表示 "を選択で移動できます。

オプション テストStoreStandardOperationsトランザクションの送信

.envファイルで秘密鍵変数をfundedアカウントに設定、出発地または目的地のネットワーク上で以下のバイナリの実行で、テスト操作を一度だけ保存ができる:

cargo run --bin store_operations ORIGIN
cargo run --bin store_operations DESTINATION

バイナリは実行で繰り返し実行できる:

nohup watch -n 2 cargo run --bin store_operations ORIGIN > storeOperationsOrigin.log 2>&1 &
nohup watch -n 2 cargo run --bin store_operations DESTINATION > storeOperationsDestination.log 2>&1 &

LayerCake インデクサーは、保存されたオペレーションを自動的にピックアップ、対向ネットワーク上の帯域幅プロバイダとして実行する。

Copyright (c) 2023, Flare Mainnet Holdings Ltd. All rights reserved.

まとめ

FLRトークンの利用のF-AssetsとLayerCakeは機能の要です。機能を理解してサードパーティのアプリケーション開発すると効率的です。

-フレアネットワークス, EVM