预编译合约
Posted mutourend
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了预编译合约相关的知识,希望对你有一定的参考价值。
1. 引言
ethereum virtual machine (EVM) 提供了丰富的图灵完备环境,用于运行智能合约。可以运行所有程序并不代表可高效运行。不论是公链、联盟链、私链,都需要 可将EVM中的特定操作扩展为可高效运行。
EVM的扩展方式有2种:
- 增加opcodes:类似于传统CPU的方式,向EVM增加额外的指令来支持新的操作——如addition cryptographic primitives。
与向传统CPU添加指令类似,相应的不足为:为了充分利用新操作的优势,需修改编译器以使用这些指令(否则必须使用汇编)。
此外,指令编码具有有限的指令空间(对于EVM,其指令空间为1个字节,对应只有256个指令)。同时,对于公链和私链,可能会对相同的opcode赋予不同的操作,使得相同的bytecode在不同的网络上表现完全不同。 - 预编译合约:采用Ethereum client的native language来编写合约(而不是EVM bytecode)的方式来添加操作。这些操作以合约的方式实现,不需要对编译器进行更新。预编译合约也有与opcodes类似的地址空间限制,但是预编译合约的地址空间要更大,可支持更多的操作。
这种方式仍然有捕获合约地址的开销,如:unpacking parameters、calling the native code以及repacking the results into the EVM。同时,由于预编译合约是共识过程的一部分,要求预编译合约必须为deterministic的。
借助预编译合约,可解决:
allow complex cryptographic computations to be used in the EVM without having to deal with EVM overhead。
2. 以太坊的预编译合约
根据 https://github.com/ethereum/go-ethereum/blob/master/core/vm/contracts.go,以太坊中支持的预编译合约主要有:
// PrecompiledContractsBerlin contains the default set of pre-compiled Ethereum
// contracts used in the Berlin release.
var PrecompiledContractsBerlin = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{1}): &ecrecover{},
common.BytesToAddress([]byte{2}): &sha256hash{},
common.BytesToAddress([]byte{3}): &ripemd160hash{},
common.BytesToAddress([]byte{4}): &dataCopy{},
common.BytesToAddress([]byte{5}): &bigModExp{eip2565: true},
common.BytesToAddress([]byte{6}): &bn256AddIstanbul{},
common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{},
common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{},
common.BytesToAddress([]byte{9}): &blake2F{},
}
// PrecompiledContractsBLS contains the set of pre-compiled Ethereum
// contracts specified in EIP-2537. These are exported for testing purposes.
var PrecompiledContractsBLS = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{10}): &bls12381G1Add{},
common.BytesToAddress([]byte{11}): &bls12381G1Mul{},
common.BytesToAddress([]byte{12}): &bls12381G1MultiExp{},
common.BytesToAddress([]byte{13}): &bls12381G2Add{},
common.BytesToAddress([]byte{14}): &bls12381G2Mul{},
common.BytesToAddress([]byte{15}): &bls12381G2MultiExp{},
common.BytesToAddress([]byte{16}): &bls12381Pairing{},
common.BytesToAddress([]byte{17}): &bls12381MapG1{},
common.BytesToAddress([]byte{18}): &bls12381MapG2{},
}
根据How to add custom precompiled contract?可知,以太坊的预编译合约中仅需实现2个接口:
- RequiredGas(): to define how many gas user need to pay when calling the pre-compiled contract。
- Run(): write your code to do the thing that you want the pre-compiled contract will do。
3. Tron的预编译合约
而对于Tron,根据https://github.com/tronprotocol/java-tron/blob/develop/actuator/src/main/java/org/tron/core/vm/PrecompiledContracts.java,其支持的预编译有:
private static final DataWord ecRecoverAddr = new DataWord(
"0000000000000000000000000000000000000000000000000000000000000001");
private static final DataWord sha256Addr = new DataWord(
"0000000000000000000000000000000000000000000000000000000000000002");
private static final DataWord ripempd160Addr = new DataWord(
"0000000000000000000000000000000000000000000000000000000000000003");
private static final DataWord identityAddr = new DataWord(
"0000000000000000000000000000000000000000000000000000000000000004");
private static final DataWord modExpAddr = new DataWord(
"0000000000000000000000000000000000000000000000000000000000000005");
private static final DataWord altBN128AddAddr = new DataWord(
"0000000000000000000000000000000000000000000000000000000000000006");
private static final DataWord altBN128MulAddr = new DataWord(
"0000000000000000000000000000000000000000000000000000000000000007");
private static final DataWord altBN128PairingAddr = new DataWord(
"0000000000000000000000000000000000000000000000000000000000000008");
private static final DataWord batchValidateSignAddr = new DataWord(
"0000000000000000000000000000000000000000000000000000000000000009");
private static final DataWord validateMultiSignAddr = new DataWord(
"000000000000000000000000000000000000000000000000000000000000000a");
private static final DataWord verifyMintProofAddr = new DataWord(
"0000000000000000000000000000000000000000000000000000000001000001");
private static final DataWord verifyTransferProofAddr = new DataWord(
"0000000000000000000000000000000000000000000000000000000001000002");
private static final DataWord verifyBurnProofAddr = new DataWord(
"0000000000000000000000000000000000000000000000000000000001000003");
private static final DataWord merkleHashAddr = new DataWord(
"0000000000000000000000000000000000000000000000000000000001000004");
Tron的预编译合约中仅需实现2个接口:
- getEnergyForData(): 定义调用该预编译合约所需的能量值。
- execute(): write your code to do the thing that you want the pre-compiled contract will do。
合约与地址绑定关系在Solidity中设定:
case FunctionType::Kind::ECRecover:
case FunctionType::Kind::SHA256:
case FunctionType::Kind::RIPEMD160:
case FunctionType::Kind::ValidateMultiSign:
case FunctionType::Kind::BatchValidateSign:
case FunctionType::Kind::verifyBurnProof:
case FunctionType::Kind::verifyTransferProof:
case FunctionType::Kind::verifyMintProof:
case FunctionType::Kind::pedersenHash:
{
_functionCall.expression().accept(*this);
static map<FunctionType::Kind, u256> const contractAddresses{
{FunctionType::Kind::ECRecover, 1},
{FunctionType::Kind::SHA256, 2},
{FunctionType::Kind::RIPEMD160, 3},
{FunctionType::Kind::BatchValidateSign, 9},
{FunctionType::Kind::ValidateMultiSign, 10},
{FunctionType::Kind::verifyMintProof, 16777217}, //1000001
{FunctionType::Kind::verifyTransferProof, 16777218}, //1000002
{FunctionType::Kind::verifyBurnProof, 16777219}, //1000003
{FunctionType::Kind::pedersenHash, 16777220} //1000004
};
m_context << contractAddresses.at(function.kind());
for (unsigned i = function.sizeOnStack(); i > 0; --i)
m_context << swapInstruction(i);
appendExternalFunctionCall(function, arguments);
break;
}
参考资料
[1] What’s a precompiled contract and how are they different from native opcodes?
[2] A Prehistory of the Ethereum Protocol
[3] 2018年4月博客 Extending the EVM
[4] 2020年9月博客 Edgeware EVM Precompiles: A deeper dive
以上是关于预编译合约的主要内容,如果未能解决你的问题,请参考以下文章