Precompiled

预编译合约提供一种使用C++编写合约的方法,合约逻辑与数据分离,相比于solidity合约具有更好的性能,可以通过修改底层代码实现合约升级。

预编译合约与Solidity合约对比

表名预编译合约Solidity合约
地址固定地址,代码中定义部署时确定
合约代码数据存储在表中,与合约分离,可升级合约逻辑合约变量和数据存储在MPT树中
执行C++底层执行,性能更高,可实现并行EVM虚拟机,串行执行

模块架构

Precompiled的架构如下图所示:

  • 区块验证器在执行交易的时候会根据被调用合约的地址来判断类型。地址1-4表示以太坊预编译合约,地址0x1000-0x10000是C++预编译合约,其他地址是EVM合约。

../../../_images/architecture5.png

关键流程

  • 执行预编译合约时首先需要根据合约地址获取到预编译合约的对象。
  • 每个预编译合约对象都会实现call接口,预编译合约的具体逻辑在该接口中实现。
  • call根据交易的abi编码,获取到Function Selector和参数,然后执行对应的逻辑。

graph TB Start(开始) —> branch1{预编译合约} branch1 —> |是|op1[根据地址获取合约对象] branch1 —> |否|op2[EVM] op1 —> op3[解析调用函数及参数] op3 —> End(返回执行结果) op2 —> End(返回执行结果)

接口定义

每个预编译合约都必须实现自己的call接口,接口接受三个参数,分别是ExecutiveContext执行上下文、bytesConstRef参数的abi编码和外部账户地址,其中外部账户地址用于判断是否具有写权限。Precompiled源码

接口名参数说明接口说明
virtual bytes call(std::shared_ptr<ExecutiveContext> context, bytesConstRef param, Address const& origin = Address())context为区块执行上下文,param为abi编码的参数,origin为调用的外部账户地址具体合约接口的实现
virtual uint32_t getParamFunc(bytesConstRef param)param为abi编码的参数获取调用的函数的Function Select(函数名的sha3的前四个大端字节)
virtual uint32_t getFuncSelector(std::string const& _functionName)_functionName为函数名根据函数名计算Function Select
virtual bytesConstRef getParamData(bytesConstRef param)param为abi编码的参数获取调用函数的具体参数的abi编码