私が「Dusk」の取引詳細で引っかかってずっと困っていたのは、取引自体が消えることなしに、コントラクト実行が取り消し(リバート)され得るという点です。

最初の直感では、それらをひとつの結果にまとめてしまおうと思いました。

callが成功 = 取引が起きた。
callがリバート = 取引は起きなかった。

でもMoonlightは、そんなふうにきれいには整理できません。

受理される前に、ネットワークは送信者が支払えることを次の範囲で確認します。値(value)、コントラクトの預け入れ、そして最大ガス(最大ガス量は gas_limit × gas_price)。

それから署名を検証し、さらにnonceがアカウントの現在のnonceの「1つ上」であることを確認します。

なるほど。

面白いのは、実行の「後」にあります。

ホワイトペーパーによれば、スマートコントラクトの実行が取り消されるなら、対応する金額が返金されます。未使用のガスも返金されます。

しかし、nonceは受理時にインクリメントされます。

つまり「コントラクト側の状態変更が取り消された」と「この取引試行がネットワークによって消費された」は、同じ状態ではありません。

リバートによってコントラクト側の効果は元に戻せても、アカウント側の順序付けメカニズムは前に進み続けます。

gasにはまた別の境界があります。

ユーザーは実行前に最大ガスを負担できる必要があります。最終的には未使用ガスが戻ってくるので、実際に支払う額は少なくなる可能性がありますが。

関わってくる金額は3つあります。

・負担できる必要がある金額
・実行が消費する金額
・その後に返ってくる金額

gas_limitが「手数料」ではなく「支出上限」に見えていると、見落としやすいです。

では、アプリがいくつかのMoonlightトランザクションを順番に準備している場面を想像してください。

トランザクションNはコントラクトを呼び出してリバートします。
コントラクトの効果は消えます。

しかしNが受理されているなら、送信者のnonceは進んでいます。

トランザクションN+1は、「前回の呼び出しが失敗したから何も起きていない」かのようには推論できません。

失敗によって、コントラクトの外側で何かが変わったのです。

ここで私が好きなのは、この区別です。

実行ロールバックはローカルです。
トランザクション履歴はそうではありません。

そして、それが統合(integration)の問いを変えます。

アプリがユーザーに「このトランザクションは失敗しました」と伝えるとき、何が正確に失敗したのでしょう?

意図した状態遷移でしょうか?
それともトランザクションそのものですか?

Duskでは、それが2つの異なる答えになり得ます。

@Dusk_Foundation #Dusk $DUSK $GPS $TUT