预编译合约

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

以上是关于预编译合约的主要内容,如果未能解决你的问题,请参考以下文章

预编译合约

以太坊的ecrecover预编译合约

以太坊智能合约虚拟机(EVM)原理与实现

简单投票DApp

mybatis以及预编译如何防止SQL注入

SQL预编译中order by后为什么不能参数化原因