アルゴリズムとかオーダーとか

仕事で勉強したことなどをまとめてます

EthereumのContract Creationの仕組み

Solidityをコンパイルして生成されたopcodes=SmartContractが、Contract CreationのときにEthereumのStateDBに保存されるまでの仕組みがどうなっているのか興味が湧いたので調べてみました。

というわけで、今回の記事はContract Creationの仕組みについてまとめたものです。
なおこの記事はgo-ethereumの実装を参考にしています。

go-ethereumでのContract Creationの該当箇所

まずはgo-ethereumでContract Creationが発生した時に実行されるコードを提示します。該当のコードは以下の箇所です。
evm.go#L321

特に、ethereumのstateDBにcontractのコードを保存している箇所は以下になります。
evm.go#L363-L378

Contract Creationの簡単な流れ

Contract Creationの処理の流れを説明すると以下の通りになります。

  1. sender addressとnonceからContractAddressを生成する
  2. evmを起動してtransaction.dataに保持されているopcodeを順に実行する
  3. evmの処理結果のデータ(= return value)を受け取る
  4. 2.で受け取ったreturn valueを1.のcontract addressと一緒にstateDBに保存する

最後にstateDBにaddressとcodeを保存することで、Contractのコードがaddressに紐付いた形で永続化されます。
で、今回特に重要なのが2. のopcodeを順に実行するところです!つまり、Contract Creationの時もopcodeが処理されています。次は、SolidityがContract Creationのためにどのようなopcodeを発行しているのかを見ていきます。

サンプルコントラクト

今回は次のContractのopcodeを例にして説明します。
ethfiddle.com
EthFiddle.comでコードを載せてますが、実際に試す場合はRemixのdebug機能でstep実行するのをおすすめします。

コントラクトのopcode

このサンプルコントラクトをコンパイルして生成されるopcodeは以下の通りです。


0x608060405234801561001057600080fd5b5060dc8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630dbe671f14604e578063cd16ecbf146076575b600080fd5b348015605957600080fd5b50606060a0565b6040518082815260200191505060405180910390f35b348015608157600080fd5b50609e6004803603810190808035906020019092919050505060a6565b005b60005481565b80600081905550505600a165627a7a7230582004ebfbda3bf3d8e99a886700230e70017ab9085657747fee0134c334108fe4010029
上記のopcodeのうち、赤字になっている部分がContract Creationの時に実行されるコードになります。いわゆるコンストラクタのようなものです。

コンストラクタ時に実行されるコード

わかりやすくするために上記で赤字で示したコンストラクタのコードをOPCODE命令に変換すると以下の通りになります。

000 PUSH1 80
002 PUSH1 40
004 MSTORE
005 CALLVALUE
006 DUP1
007 ISZERO
008 PUSH2 0010
011 JUMPI
012 PUSH1 00
014 DUP1
015 REVERT
016 JUMPDEST
017 POP
018 PUSH1 dc
020 DUP1
021 PUSH2 001f
024 PUSH1 00
026 CODECOPY
027 PUSH1 00
029 RETURN
030 STOP

これはコンストラクタのコードですので、Contractにコンストラクタ処理を追加すると長くなります。とりわけ青字で表示した部分が重要で、この処理によって、stateDBに永続化されるContractのコードが決定します。

今回のContractの例では、transactionで渡されたopcodeのうち、0x1f〜0xdcまでのコード(つまり前述したopcode全体のうち黒字の部分)が最終的にstateDBに永続化されます。

緑色で示した部分はContract Creation時にETHが送られるのを拒否するための処理です。payableなコンストラクタを記述することでこのopcodeは消えます。

まとめ

Contract Creationの時はTransactionで渡されたinput dataをすべて保存しているんだろうな。でもそうなるとコンストラクタの処理はどうなるんだろうか?と疑問に思っていた部分が解決しました。
Contract Creationではinput dataのすべてを保存しているのではなく、コンストラクタ処理を除いた部分のみが保存されているということでした。また、コンストラクタ処理は常に、どの部分をContractのコードとしてstateDBに保存するのか?を決定しているということもわかりました。
コンストラクタにassemblyで隠しコードを埋め込むみたいなこともできそうな気がしないでもないですねw