2026.03.10

Transient Storage における call frame ロールバック

はじめに

Dencun で導入された EIP-1153 は、EVM に TSTORE / TLOAD を追加し、トランザクション中のみ存在する一時ストレージ(Transient Storage)を扱えるようにした。Gas コストは 100 で固定、refund なしという扱いやすい設計から、reentrancy guard などの用途で注目されている。

しかし、Transient Storage を正しく理解するうえで本質的なのは「トランザクション終了時に消える」という点ではない。重要なのは、revert と call frame による巻き戻りの挙動である。本稿ではその点に絞って整理し、実際に動かせる最小コードで検証する。

Transient Storage と call frame

Transient Storage はトランザクション単位で初期化されるが、更新の適用・巻き戻しは call frame 単位で管理される。

挙動は次の通りである。

  • あるフレーム内で TSTORE した値は、そのフレームが REVERT した場合に巻き戻る
  • 内側 call が revert しても、外側フレームで行った更新は保持される

  • 外側フレームが revert した場合、内側で成功していた更新もまとめて巻き戻る

これは永続ストレージ (SSTORE) と同じスナップショット & ロールバックモデルであり、Transient Storage が単なるメモリではないことを示している。

最小検証コード

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

contract TStoreTarget {
    function write(uint256 v) external {
        assembly {
            tstore(0, v)
        }
    }

    function writeThenRevert(uint256 v) external {
        assembly {
            tstore(0, v)
        }
        revert("inner revert");
    }

    function read() external view returns (uint256 v) {
        assembly {
            v := tload(0)
        }
    }
}

contract TStoreCaller {
    function innerRevertCase(TStoreTarget t) external returns (uint256) {
        // 外側フレームで 1 を保存
        t.write(1);

        // 内側で 2 を保存して revert
        try t.writeThenRevert(2) {
        } catch {}

        // 内側の変更は巻き戻るため 1 が残る
        return t.read();
    }

    function outerRevertCase(TStoreTarget t) external returns (uint256) {
        try this._outerThatReverts(t) {
        } catch {}

        // 外側フレームごと revert したので 0 に戻る
        return t.read();
    }

    function _outerThatReverts(TStoreTarget t) external {
        t.write(123);
        revert("outer revert");
    }
}

検証結果

ケース1: 内側のみ revert

  • 外側で TSTORE(0,1)

  • 内側で TSTORE(0,2) → revert

  • 内側フレーム分のみ巻き戻り

  • 結果は 1

ケース2: 外側が revert

  • 外側フレーム内で TSTORE(0,123)

  • 外側が revert

  • フレーム開始時点(= 0)へ戻る

この結果から、Transient Storage は「トランザクション単位で存在」しつつも、「call frame 単位でロールバックされる」ことが確認できる。

実装上の注意点

Transient Storage はメモリとは異なり、関数終了時に自動的には消えない。同一トランザクション内で同じコントラクトが再度呼ばれれば、前のフレームで成功した値は残っている。

そのため、reentrancy guard を TSTORE で実装する場合でも、明示的にロック解除を行わなければ、同一トランザクション内の後続 call に影響が残る。

Gas が安いからといって「メモリの代わり」と考えると、複数 call を組み合わせたトランザクションで思わぬ副作用を生む可能性がある。

おわりに

EIP-1153 の Transient Storage は、「トランザクションで消える一時領域」という説明だけでは不十分である。本質は、EVM の call frame ロールバック規則に統合された一時ストレージであることにある。

revert 伝播と同じ境界で巻き戻るという設計は、EVM が依然として call tree を単位に状態管理を行う VM であることを改めて示している。Transient Storage を安全に使うためには、「トランザクションスコープ」だけでなく「フレーム境界」を意識する必要がある。

次世代システム研究室では、グループ全体のインテグレーションを支援してくれるアーキテクトを募集しています。インフラ設計、構築経験者の方、次世代システム研究室にご興味を持って頂ける方がいらっしゃいましたら、ぜひ募集職種一覧からご応募をお願いします。

皆さんのご応募をお待ちしています。

  • Twitter
  • Facebook
  • はてなブックマークに追加

グループ研究開発本部の最新情報をTwitterで配信中です。ぜひフォローください。

 
  • AI研究開発室
  • 大阪研究開発グループ

関連記事