Ethereum Virtual Machine のバイトコードで1から10までの合計を計算する

概要

最近、仮想通貨の市場も落ち着いてきて、技術的な話がしやすい環境が整ってきたように思う。最近、改めて「ブロックチェーンとは何か?」という疑問を持ち、技術的な側面を探究したくなった。

仮想通貨といえば、ビットコインが一番有名だが、イーサリアムのほうが後発だけあって、技術的な整理がされていて、ブロックチェーンの技術的な側面を学ぶには便利に感じた。イーサリアムでは、スマートコントラクトと言って、一定のプログラムをマイニングの過程で実行できる機能がある。そのプログラムは安全性を確保するため、EVM(Ethereum Virtual Machine)という仮想マシン上で実行される。

通常は、Solidity という専用の高級言語でコントラクト上のプログラムは記述されることが多いのだが、最終的には、EVM上のバイトコードにコンパイルされて実行される。

これらのプログラムは、マイニングの過程で実行され、ブロックチェーンに記録されるという特殊な要件があるために、おそらく厳しい制約が課されることが想像される。より低水準の動きを知りたくなったので、EVMのバイトコードそのものを少し調べてみた。

調査結果

EVMのバイトコードの仕様は、yellow paperと呼ばれる論文に規定されている。だが厳密すぎて読みにくい。私の場合、ざっくり仕様がつかめれば十分だったので、Notes on the EVMがとても役にたった。

準備

Ethereum では Go 言語の実装が有名なようである。これを利用したシステムをインストールする。

私は Go 言語には慣れていないのでちょっと手こずった。私が実際に行った手順を下にしるす。

  1. Go 言語のインストール(説明省略)
  2. GOPATH 環境変数の設定
    • 例 .bashrc 等に記述
      • export GOPATH=~/go
      • export PATH=\$PATH:$GOPATH/bin
    • source ~/.bashrc などして反映
  3. go get github.com/Masterminds/glide
    • Go 言語のパッケージ管理ツールである glide がインストールされる
  4. \$GOPATH/src で
    • git clone https://github.com/CoinCulture/evm-tools.git
    • cd evm-tools
    • glide install # もろもろのソースコードがダウンロードされる
    • make # $GOPATH/bin に disasm, evm, evm-deploy の各コマンドがインストールされる

バイトコードについて

Notes on the EVMの解説がたいへんわかりやすいので、読んで欲しい。基本的にスタックマシーンである。

1から10の合計を計算する

アセンブリ言語

push 10
push 0
push 0x0
MSTORE

JUMPDEST

DUP1
push 0x0
MLOAD
ADD
push 0x0
MSTORE

push 1
SWAP1
SUB
DUP1
push 7
JUMPI

という内容を sum.esam という名前で保存。

本当は push 1 ではなく PUSH1 1 と書きたいのだが、evm compile コマンドは受け付けてくれない。オペランドの数字の大きさで自動的に push1, push2... 等にアセンブル仕分けているようだ。

evm compile コマンドは大文字小文字の区別をするらしく、大文字で書くと yellow paperのオリジナルのオペコードとして取り扱われ、小文字で書くと、このコマンドの独自拡張となり振る舞いが変わるようである。

evm compile がアセンブルする対象のソースコードの仕様を見つけることができない。だれがご存じの方がいれば教えて下さい。

アセンブル

$ evm compile sum.easm 
600a60006000525b80600051016000526001900380600757

バイトコードがそっけない16進数で表示される。

ディスアセンブル

正しくアセンブルされたか確認する。

$ evm compile sum.easm | disasm
600a60006000525b80600051016000526001900380600757
0      PUSH1  => 0a
2      PUSH1  => 00
4      PUSH1  => 00
6      MSTORE
7      JUMPDEST
8      DUP1
9      PUSH1  => 00
11     MLOAD
12     ADD
13     PUSH1  => 00
15     MSTORE
16     PUSH1  => 01
18     SWAP1
19     SUB
20     DUP1
21     PUSH1  => 07
23     JUMPI

大丈夫そうだ。

実行

実行といっても当然イーサリアムの本番環境で行われるわけではなく、ローカルPCの EVM で実行されるだけ。

$ evm --debug --code  $(evm compile sum.easm) run
#### TRACE ####
PUSH1           pc=00000000 gas=10000000000 cost=3

PUSH1           pc=00000002 gas=9999999997 cost=3
Stack:
00000000  000000000000000000000000000000000000000000000000000000000000000a

PUSH1           pc=00000004 gas=9999999994 cost=3
Stack:
00000000  0000000000000000000000000000000000000000000000000000000000000000
00000001  000000000000000000000000000000000000000000000000000000000000000a

MSTORE          pc=00000006 gas=9999999991 cost=6
Stack:
00000000  0000000000000000000000000000000000000000000000000000000000000000
00000001  0000000000000000000000000000000000000000000000000000000000000000
00000002  000000000000000000000000000000000000000000000000000000000000000a
Memory:
00000000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
(中略)
STOP            pc=00000024 gas=9999999545 cost=0
Stack:
00000000  0000000000000000000000000000000000000000000000000000000000000000
Memory:
00000000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 37  |...............7|
#### LOGS ####

Notes on the EVMとは出力が少し異なっている。バージョンの違いだろうか。また、コマンドの最後に run を付けること。これもチュートリアルとは異なっている部分。

いちばん出力の最後に 37 (0x37 = 55) という部分が見えるだろう。これが1から10までを足しあわせた計算結果であり、正しく計算されていることがわかる。

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.