外部所有アカウントとコントラクトアカウント、そしてマークルツリー

EthereumにはExternally owned accounts(外部所有アカウント)とcontract accounts(コントラクトアカウント)という二つのアカウントがあります。各アカウントはアドレスと、トランザクションによって偏移する状態(ステート)を持っています。そしてそれぞれのアカウントの状態が、全体として一致するようにネットワーク全体に共有されています。このあたりのことは以下の記事でも述べました。

ブロックチェーンとはそもそも何なのか?Ethereum Yellow Paperのアブストラクトに書かれていること

イーサリアムガイドの他の記事に関してはこちらをご覧下さい。

Ethereum(イーサリアム)ガイド

外部所有アカウントとコントラクトアカウント

外部所有アカウントやコントラクトアカウントというと仰々しい感じがしますが、簡潔に表現するとそれぞれ以下のような性質を持っています。

  • 外部所有アカウント
    我々が普段Etherを送信する時に使うアカウントです。プライベートキーによって管理され、スマートコントラクトのコードは含まれません。
  • コントラクトアカウント
    コントラクトコードによってコントロールされるアカウントです。

そして、それぞれのアカウントには以下の要素が含まれています。

  • nonce
    当該アカウントが送信した過去のトランザクション数合計
  • ether balance
    アドレス内のEther残高。
  • contract code
    スマートコントラクトのコード本体。外部所有アカウントの場合は、このコードは含まれない。
  • storage
    ストレージ。初期状態は空。

外部所有アカウントはトランザクションを生成することにより、他の外部所有アカウントやコントラクトアカウントに対して”メッセージ”を送ることができます。外部所有アカウントから外部所有アカウントへのメッセージ送信は”一般的な送金”のようなものです。一方、外部所有アカウントからコントラクトアカウントにメッセージを送った場合、コントラクトアカウントに紐付けられているコードを走らせることができます。コードの実行によってEthereum上でトークンの発行や送信、ストレージへの書き込み等が実行可能です。

このようにあるトランザクションを発動条件とし、自動で執行されるコードのことをスマートコントラクトと読んだりしますが、Nicolas Dorier氏が主張するようにスマートコントラクトより自動執行契約と呼んだほうが理解しやすいかと思います。コントラクトアカウントは、他のアカウント(コントラクトアカウントも含む)からの働きかけがない限り、自身で勝手に発動することはありません。

コントラクトアカウントに予め設定されたコードが、発動条件に従って、自動的に執行されるので、コード自体にバグが含まれていると深刻な状況を引き起こすことがあります。例えば、そのコードがEtherやトークンを扱っている場合、予期せぬ移動や凍結が起こります。

話は若干逸れますが、開発者のVlad氏がTwitterで発言しているようにEthereum自体がプラットフォームとして実験段階にあります。実験段階なので安全なコードを用意できない確率は高まります。またEthereum上にアプリケーションを展開しようとする多くのプロジェクトは開発力が不足しています。そして現在のICOバブルでは、”実験段階にあるプラットフォーム”と”開発力に不安のあるチーム”という不安定の2階建ての上に何百億円という資金が集まっています。ICOの法的な根拠はともかくとして、分散型プラットフォームとして盤石な地位を築いているようにみえるEthereumでさえ、様々な課題を内包していることは頭に入れていて損はないでしょう。

状態共有の仕組み

Ethereumには二つのアカウントが存在し、外部所有アカウントとコントラクトアカウントは、それぞれ異なる機能を有していることが分かりました。ではそれぞれのアカウントの状態はどのようにしてネットワーク全体で共有されているのでしょうか?膨大な数のアカウントの状態を、ネットワーク全体でリアルタイムに共有することは本当に可能なのでしょうか?以下ではマークルツリーの仕組みを簡単に紹介します。

マークルツリーの簡単な説明

以下の図はSatoshi Nakamotoのビットコイン論文から引用したものです。Ethereumのホワイトペーパーにも同様の図がありますが、ビットコイン論文の図の方が分かりやすいため、こちらを利用します。

最下部にTx0~Tx3が並んでいます(Txはトランザクション)。Tx0とTx1に目を向けると、まずそれぞれのハッシュ値が取られています(Hash0とHash1)。そのHash0とHash1を足し合わせ、さらにハッシュ値を取ったものがHash01です。ハッシュ値はインプットされたデータを一定の長さの文字列に置き換えるものなので、Hash0もHash1もHash01も全て同じ長さの文字列であることに注目して下さい(※下図参照)。つまりTx0やTx1のハッシュ値であるHash0やHash1と、Tx0とTx1の両方のデータから導かれたHash01は、同じサイズのデータなのです。これをハッシュ値が一つになるまで繰り返していきます。最後に残った一つのハッシュ値をルートハッシュと呼びます。

Merkling in Ethereum – Ethereum Blog

このルートハッシュのみをブロックのヘッダーに格納します。なぜルートハッシュのみを格納するだけで事足りるかというと、ハッシュ値はインプットが少しでも変わると全く別の値になってしまうため、誰かがトランザクションを改竄した場合、トランザクション(のハッシュ値)をインプットとして用いるルートハッシュ自体も別の値になるからです。故にルートハッシュさえ堅牢性のあるシステムの中に保管することができれば、トランザクションの改竄を防げるというわけです。

また全てのトランザクションデータを、そのままの形でブロックチェーン上に保存する必要がないため、膨大な量のデータを扱わなくて済みます。

あるトランザクション(例えばTx2)がそのブロックに含まれているか確認したい場合、Tx2のデータに加えて、Hash3とHash01を用意し、それらのハッシュ値がルートハッシュに一致するか確認します。最下部に位置している全てのトランザクションのデータから、Tx2に一致するデータがあるかどうかを調べる必要がないので、計算量を節約することが可能です。

イーサリアムでは、このマークルツリーの仕組みが「状態」「トランザクション」「レシート」の3つで使われており、それぞれにマークルツリーが存在します。さらに状態の中には、上述の通りStorageがあり、Storageもマークルツリーを用いています。

  • 状態ツリー
    • ストレージツリー
  • トランザクションツリー
  • レシートツリー

トランザクションやレシートに関しては別記事で紹介したいと思います。


White Paper · ethereum/wiki Wiki · GitHub
Patricia Tree · ethereum/wiki Wiki · GitHub
Merkling in Ethereum – Ethereum Blog