RemixでEVMバイトコードをデプロイして動かす
自作言語をEVM LLVMでコンパイルして得られたEVMバイトコードを実際に動かしてみる方法としてRemixが使えないか調べたメモ。
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でコンパイルしてデプロイする。
これでバイトコードを直接デプロイする準備ができた。
今回はこういう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
関数にこのバイトコードを引数として渡してコールする。
コンソールみたいなところにログが表示されていて、バイトコードから生成したコントラクトのアドレス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
に目的のバイトコードを入力してクリックする。
そうするとこのような感じでhoge関数を実行するための入力フォームが出てくる。
引数を渡してコールするとちゃんと計算されて結果が返ってくる。
ということでRemixでEVMバイトコードをデプロイして動かすことができた。
ここで書いてるようにEVM LLVMでバイトコードを吐けたので、それをRemixから実行してみる。
中身はdef hoge(a b) a + b;
なのでコールしたい関数はこの記事で試したコントラクトと同等のもの。バイトコードは0x5b608060405260208035600080356100458084836000518060600152600051608001806000526020526004580192565b6000516020900351600052915092509150908152f35b019056
。
さっそく関数をコールしてみると…。
にゃーん。なんか計算結果が0になっててちゃんと動いてなさそう。デバッガをみてみるとオペコードへのデコード結果がおかしい。
Etherscanのツールだと一応オペコードがちゃんと並んでるんだけどなあ〜。
とりあえず目的は達成できたのでこのあたりはまた改めて詳しく調べてみようかな…。しかしRemixは便利ですごい。とはいえ実際に動かすまで手作業が多くてちょっと面倒なので、他のツールでも(例えばGanacheとか)で似たようなことができないかは調べてみたほうがよさそうだった。