Solidity——Bytecode文件结构

SolidityBytecode文件结构

智能合约Demo

pragma solidity ^0.4.25;
contract Demo{
	uint public value1 = 0;
	uint public value2 = 0;
	
	function A(uint a) public returns(uint){
	    value1 += a;
	    return value1;
	}
	function B(uint b) public{
	    value2 += A(b);
	}
}

上面是一个智能合约DEMO,用REMIX部署DEMO合约,获得Bytecode文件。
Remix界面

点击右侧Details ,如下图:
字节码

Bytecode文件

Bytecode由两部分构成。第一部分的.code包含了一些smart contract初始化的代码,比如构造函数,state variable(全局变量)的赋值等操作。区块链上,这些都是EOA在部署合约时就执行完成的,在区块链浏览器,是无法看到这部分的代码的。从.data开始,是smart contract的runtime bytecode,也就是在区块链上保存的合约的bytecode。想要获得该部分的bytecode,可以安装solidity,通过命令 solc --bin-runtime demo.sol获得。
Remix的结构有点不太一样,是由若干个tag组成的,每个tag由若干个基本块组成。以JUMPDEST或者结束指令(RETURN,REVERT,STOP)划分。.code部分是Bytecode的入口,这部分的指令包含了所有能够被外部调用的函数的函数签名和跳转pc值。
.code
上面的5个框分别是该合约的5个跳转函数。可能会奇怪合约就2个函数,为何会有5个可跳转函数。这5个跳转函数分别是:1. fallback(回退函数),2个public全局变量,2个public函数。
首先解释一下fallback函数,在EVM中,fallback函数是唯一一个未命名的函数,可以发现其他4个框前面都有一个函数签名,如第二个框的3033413B,只有fallback function没有函数签名。因此如果我们调用了一个合约中没有的函数,没有一个函数签名能满足,接下来的四个框都不会满足跳转条件,因此会通过fall to的形式执行tag1,tag1也就是fallback函数的开始位置。
接下来说一下什么是函数签名。函数签名是一个4byte的hash值,用来唯一标识smart contract中的函数。它是通过sha3(“functionName(type1, type2)”) 或者 keccak256(“functionName(type1, type2)”),取前4bytes得到的。也就是说该函数签名只与函数名,函数类型有关,与函数内容无关
总结一下.code部分,该部分包含了合约能调用的所有函数的跳转地址,从上图中提现就是tag1-5。 tag 1-5分别是5个函数的起始位置。

Bytecode执行流程

下面用函数B为例,解释一下EVM的bytecode是如何跳转的:
在这里插入图片描述
在这里插入图片描述

  1. 要调用函数B,首先EVM会接受到函数签名(DAC0EB07),在.code部分中,跳转到tag 5
  2. tag 5是函数B的开始部分,tag 5中有一个JUMPI,假设跳转条件满足,EVM会跳转到tag 15,如果不满足条件,则会执行PUSH, DUP1, REVERT。 REVERT是终止指令。tag 15上通常是用来判断一个函数是否是payable的。比如CALLVALUE指令会得到transacation是否发了Ether,如果发了ether,ISZERO的结果就会是false,因此不会执行跳转
  3. 执行tag 15, 执行到最后有一个JUMP指令,会从EVM stack读出一个值, 上一个push到stack的值是tag 17,因此跳转到tag 17
  4. 执行tag 17,同tag 15,tag17最后的tag 15会使pc跳转到tag14(tag 14也就是函数A的函数体部分)
  5. 执行tag 14,执行到最后有一个JUMP指令,这时JUMP指令读到的是tag 17中push的tag 20
  6. 执行tag 20, tag20最后的JUMP指令,执行的是tag15中的push tag 16, 因此会跳转到tag 16。
  7. 执行tag 16,执行到stop指令,程序终止。

猜你喜欢

转载自blog.csdn.net/weixin_43405220/article/details/100174389