Akihito Ikeda

RemixでEVMバイトコードをデプロイして動かす

posts/2021-11-20diary

自作言語をEVM LLVMでコンパイルして得られたEVMバイトコードを実際に動かしてみる方法としてRemixが使えないか調べたメモ。

Remix - Ethereum IDE

Remixには多分そのままではバイトコードをデプロイする機能はなさそうだったけど、ちょっと探したらズバリのものが見つかったのでこれを使う。

Sekin/ethereum-bytecode-deployment: Deploy any Ethereum smart contract using its bytecode.

流れとしては、まずこのリポジトリにあるDeployBytecode.solをRemixでデプロイして、コントラクトのdeployBytecode関数に目的のバイトコードを渡してRemixからコールしてやればOK。 DeployBytecode.solではSolidityのインラインアセンブリでmstore, createを実行してバイトコードを新しいコントラクトとしてデプロイしてそのアドレスを返すようになっている。

以下、Remixの使い方自体は所々端折って書く。

まずはDeployBytecode.solをRemixでコンパイルしてデプロイする。

deploy_deployer.png

これでバイトコードを直接デプロイする準備ができた。

今回はこういうSolidityコードを用意してバイトコードのデプロイを試してみる。

pragma solidity >=0.7.0 <0.9.0;

contract MyContract {
    function hoge(int256 a, int256 b) public pure returns (int256) {
        return a + b;
   }
}

solcでコンパイル。

❯ solc --bin MyContract.sol
Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: <SPDX-License>" to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information.
--> MyContract.sol


======= MyContract.sol:MyContract =======
Binary:
608060405234801561001057600080fd5b50610214806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063f146765514610030575b600080fd5b61004a600480360381019061004591906100b1565b610060565b6040516100579190610100565b60405180910390f35b6000818361006e919061014a565b905092915050565b600080fd5b6000819050919050565b61008e8161007b565b811461009957600080fd5b50565b6000813590506100ab81610085565b92915050565b600080604083850312156100c8576100c7610076565b5b60006100d68582860161009c565b92505060206100e78582860161009c565b9150509250929050565b6100fa8161007b565b82525050565b600060208201905061011560008301846100f1565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006101558261007b565b91506101608361007b565b9250817f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0383136000831215161561019b5761019a61011b565b5b817f80000000000000000000000000000000000000000000000000000000000000000383126000831216156101d3576101d261011b565b5b82820190509291505056fea2646970667358221220ab2ae6fa80744fcf4182074ec2946fd86a0b7bc15fd84ab4cff6761e3e5e5fd564736f6c63430008090033

さっきデプロイしておいたDeployBytecodeのdeployBytecode関数にこのバイトコードを引数として渡してコールする。

deploy_bytecode.png

deploy_bytecode_log.png

コンソールみたいなところにログが表示されていて、バイトコードから生成したコントラクトのアドレス0x6BedB3D6F39CAffEfa3Fd6D0bc804659B4D5210Aが返ってきてる。

次はこれをRemixで実行する(つまりhoge関数を実行する)。

バイトコードだけでは実行できなくて、コントラクトのインターフェースが必要(ABIでもいけるっぽい?)。

実行したいバイトコードと同じインターフェースを定義したファイルを作る。

pragma solidity >=0.7.0 <0.9.0;

interface MyContract {
    function hoge(int256 a, int256 b) external view returns (int256);
}

インターフェースなのでhoge関数の実装はなくていい。ちなみにsolファイルを作るとこういうjsonファイルが生成されて、ここにABIも含まれている。

{
	"deploy": {
		"VM:-": {
			"linkReferences": {},
			"autoDeployLib": true
		},
		"main:1": {
			"linkReferences": {},
			"autoDeployLib": true
		},
		"ropsten:3": {
			"linkReferences": {},
			"autoDeployLib": true
		},
		"rinkeby:4": {
			"linkReferences": {},
			"autoDeployLib": true
		},
		"kovan:42": {
			"linkReferences": {},
			"autoDeployLib": true
		},
		"görli:5": {
			"linkReferences": {},
			"autoDeployLib": true
		},
		"Custom": {
			"linkReferences": {},
			"autoDeployLib": true
		}
	},
	"data": {
		"bytecode": {
			"functionDebugData": {},
			"generatedSources": [],
			"linkReferences": {},
			"object": "",
			"opcodes": "",
			"sourceMap": ""
		},
		"deployedBytecode": {
			"functionDebugData": {},
			"generatedSources": [],
			"immutableReferences": {},
			"linkReferences": {},
			"object": "",
			"opcodes": "",
			"sourceMap": ""
		},
		"gasEstimates": null,
		"methodIdentifiers": {
			"hoge(int256,int256)": "f1467655"
		}
	},
	"abi": [
		{
			"inputs": [
				{
					"internalType": "int256",
					"name": "a",
					"type": "int256"
				},
				{
					"internalType": "int256",
					"name": "b",
					"type": "int256"
				}
			],
			"name": "hoge",
			"outputs": [
				{
					"internalType": "int256",
					"name": "",
					"type": "int256"
				}
			],
			"stateMutability": "view",
			"type": "function"
		}
	]
}

いよいよバイトコードを実行してみる。 先ほどのインターフェースの.solファイルをエディターで開いてる状態で(これが重要)、At Addressに目的のバイトコードを入力してクリックする。

at_address.png

そうするとこのような感じでhoge関数を実行するための入力フォームが出てくる。

deployed_function.png

引数を渡してコールするとちゃんと計算されて結果が返ってくる。

deep_thought.png (Deep Thoughtと同等の計算能力を持つ)

ということでRemixでEVMバイトコードをデプロイして動かすことができた。

ここで書いてるようにEVM LLVMでバイトコードを吐けたので、それをRemixから実行してみる。

中身はdef hoge(a b) a + b;なのでコールしたい関数はこの記事で試したコントラクトと同等のもの。バイトコードは0x5b608060405260208035600080356100458084836000518060600152600051608001806000526020526004580192565b6000516020900351600052915092509150908152f35b019056

さっそく関数をコールしてみると…。

failure.png

にゃーん。なんか計算結果が0になっててちゃんと動いてなさそう。デバッガをみてみるとオペコードへのデコード結果がおかしい。

opcode.png

Etherscanのツールだと一応オペコードがちゃんと並んでるんだけどなあ〜。

etherscan_opcode.png

とりあえず目的は達成できたのでこのあたりはまた改めて詳しく調べてみようかな…。しかしRemixは便利ですごい。とはいえ実際に動かすまで手作業が多くてちょっと面倒なので、他のツールでも(例えばGanacheとか)で似たようなことができないかは調べてみたほうがよさそうだった。

© Akihito Ikeda - Last update 03.12.2021 01:11.