EVM的深入研究和分析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/http188188/article/details/87883330

最终目标是能够完整地理解已编译的Solidity合同
1、执行
evm-tools 安装
https://github.com/CoinCulture/evm-tools/blob/master/INSTALL.md
/home/xue/go/bin/evm --debug --code 366020036101000a600035045b6001900380600c57 --input 05

不同字节码编译成不同EVM指令
1、基本:disam显示指定地址后面的汇编代码
echo 60056004016000526001601ff3  | disasm
evm --debug --code 60056004016000526001601ff3
2、输入
echo 60003560203501 | disasm
evm --debug --code 60003560203501 --input 00000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000004

2、合约

字节码在evm部署后编译为EVM指令集
$ echo 6005600401 | evm-deploy | disasm 
600580600b6000396000f36005600401
0      PUSH1  => 05
$ evm --debug --code 600580600b6000396000f36005600401

3、安全性

同一个合约每次在evm部署后生成的 合约地址不同
1、生成合约地址
// Contract Address: 1F2A98889594024BFFDA3311CBE69728D392C06D
$ evm --code $(echo "60056004016000526001601ff3" | evm-deploy)  --datadir evm-data
// Contract Address: 14F6D12ECEBB7606C528880AD8B97C25AB7D4AD9
$ evm --code $(echo "60056004016000526001601ff3" | evm-deploy)  --datadir evm-data
2、向合约发送交易
$ evm --to 14F6D12ECEBB7606C528880AD8B97C25AB7D4AD9 --datadir evm-data
Datadir already exists
Loading database
Loading root hash 60209E93FEFD3DD5CF1D6B3FBDC33DA1B020C5B880A51E8306A3F5FDF269122A
Loaded account for receiver 14F6D12ECEBB7606C528880AD8B97C25AB7D4AD9
CODE: 60056004016000526001601FF3
VM STAT 0 OPs
OUT: 0x09

4、异常

//  5f 不是合理操作码
evm --debug --code 5f
// JUMP (0x56)  要求堆栈中至少有一个参数
evm --debug --code 56
// 0x0 不是一个JUMPDEST
evm --debug --code 600056
// gas 超出异常
evm --debug --gas 1 --code 6000

5、内存和存储

EVM的编译器实际上不会为字节码的大小、速度或内存高效性进行优化。相反,它会为gas的使用进行优化,这间接鼓励了计算的排序,让以太坊区块链可以更高效一点
一笔交易的每个零字节的数据或代码费用为 4 gas
一笔交易的每个非零字节的数据或代码的费用为 68 gas

0x200000000000000000000000000000000比exp(0x2, 0x81)更便宜
0x200000000000000000000000000000000字节码包含了很多的0,更加的便宜。
(1 * 68) + (32 * 4) = 196

608160020a字节码更短,但是没有0。
5 * 68 = 340

6、合约
一个帐户最多只能有一个与之关联的程序 - 无论何时对合同进行交易,或者它是执行CALL操作码的另一个合同的目标,该合同的代码都将执行。部署后,合同代码可能不会更改。
合约创建时,通过将合同代码作为数据发送到空地址。通过创建新帐户,运行调用数据中指定的程序,以及将EVM返回的任何内容设置为新合同的代码,以太坊状态转换功能将此事件解释为合同创建事件。也就是说,在创建期间发送的代码与将存储在合同中的代码不同 - 它是所谓的“部署代码”,它包含在某些操作中包含的实际合同代码,这些操作将其复制到内存中并返回它。

合约编译的字节码通过evm部署转化为部署代码,再编译执行
$ echo 6005600401 | evm-deploy | disasm 
600580600b6000396000f36005600401
0      PUSH1  => 05

7、solidity
Solidity可编译为EVM。它也符合以太坊ABI,这是一个关于如何在调用数据中编码参数和函数调用的规范。 总之,调用数据的前四个字节是函数标识符,对应于函数签名的规范版本的sha3散列的前四个字节。 其余参数以填充方式传递给32字节。
无返回值:

contract Addition{
	int x;
        function add(int a, int b){
		x = a + b;
    	}
}
(1)编译合约
solc --bin-runtime --optimize -o . add.sol
(2)反汇编:将内存中的机器码程序以指令助记符的形式显示出来
$ echo $(cat MyContract.bin-runtime)  | disasm 
606060405260e060020a6000350463a5f3c23b8114601a575b005b60243560043501600055601856
(3)验证函数标识符:通过运行solc --hashes add.sol,或通过散列规范签名
>>> import sha3
>>> sha3.sha3_256("add(int256,int256)").hexdigest()[:8]
'a5f3c23b`
(4)正确调用函数
 evm --debug --code $(cat Addition.bin-runtime) --input **a5f3c23b**00000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000004

带返回值:

contract Addition{
	int x;
	function add(int a, int b){
		x = a + b;
	}

	function get() returns (int){
		return x;
	}

}
(1)编译:solc --bin --optimize -o . add.sol
(2)部署:
$ evm --code $(cat Addition.bin) --datadir evm-data
Loading database
Loading root hash 0000000000000000000000000000000000000000000000000000000000000000
Contract Address: 1F2A98889594024BFFDA3311CBE69728D392C06D
VM STAT 0 OPs
OUT: 0x606060405260e060020a60003504636d4ce63c81146024578063a5f3c23b146031575b005b600054606090815260
(3)调用 add方法 :
evm --datadir evm-data --to 0x1F2A98889594024BFFDA3311CBE69728D392C06D --input a5f3c23b00000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000004
(4)调用get方法
$ solc --hashes add.sol
======= Addition =======
Function signatures: 
6d4ce63c: get()
a5f3c23b: add(int256,int256)
(5)调用合约
Now we can call the contract:

$ evm --datadir evm-data --to 0x1F2A98889594024BFFDA3311CBE69728D392C06D --input 6d4ce63c
Datadir already exists
Loading database
Loading root hash F3C30A7CD9769C45590C236816F2714E96198DBD7FEC33AE892E861816F548B2
Loaded account for receiver 1F2A98889594024BFFDA3311CBE69728D392C06D
CODE: 606060405260E060020A60003504636D4CE63C81146024578063A5F3C23B146031575B005B6000546060908152602090F35B60243560043501600055602256
VM STAT 0 OPs
OUT: 0x0000000000000000000000000000000000000000000000000000000000000009


1、EVM架构
EVM是一种简单的基于堆栈的架构
2、EVM的机器空间
EVM存储
2.1 堆栈
所有操作都在堆栈上执行。如PUSH / POP / COPY / SWAP,…
在这里插入图片描述
2.2 内存
内存是线性的,可以在字节级别进行寻址。使用MSTORE / MSTORE8 / MLOAD指令访问。内存中的所有位置最初定义为零。
内存
2.3 账户存储
存储是一个键值存储,可将256位字映射到256位字。使用SSTORE / SLOAD指令访问。存储中的所有位置最初都定义为零。
账户存储
2.4 EVM代码
EVM代码是EVM可以本机执行的字节代码。
2.5 执行模型
执行模型
3、信息调用
EVM可以向其他帐户发送消息。消息调用的深度限制为小于1024。
在这里插入图片描述
3.2 消息调用说明
消息调用由CALL指令触发。使用内存传递参数和返回值。
在这里插入图片描述
4、异常
在这里插入图片描述
5、Gas 和 fee
外部账户:Externally owned account (EOA)
RLP(递归长度前缀)的目的是编码任意嵌套的二进制数据数组,RLP是用于序列化以太坊中对象的主要编码方法。
以太坊中的所有可编程计算都需要收取费用(以gas计价)

在这里插入图片描述
6、输入和输出
EVM可以从消息调用中输入外部数据。EVM可以输出日志。 EVM还可以将值返回给Caller EVM。
输入数据说明图
7、字节顺序
内存为字节图(网络字节顺序)
MSB代表最高有效位,而LSB代表最低有效位。
字节指令和签名扩展指令的字节顺序图。
8、指令集
MUL:乘法 DIV:无符号整除运算 SDIV:有符号整除运算
合约账户CA
EVM code在EVM上运行,在创建期间代码evm code与存储在合约中的代码storage code不同 ,storage code作为“部署代码”,它包含嵌套在某些操作中的实际合约代码,这些操作将其复制到内存中并返回它。

猜你喜欢

转载自blog.csdn.net/http188188/article/details/87883330