预编译合约是以太坊内置的特殊合约,在客户端节点本地运行而非 EVM 上执行,提供高效的加密和数学运算功能。
- 本地执行:不在 EVM 上运行,避免了 EVM 的执行开销
- 优化实现:使用高度优化的本地代码实现
- 固定成本:大多数预编译合约有固定或线性的 gas 成本
- 复杂运算:提供椭圆曲线、模幂运算等复杂数学操作
- 加密算法:支持多种哈希和签名算法
- 内存操作:高效的大量数据复制
| 地址 | 名称 | 功能 | Gas 成本 | 主要用途 |
|---|---|---|---|---|
| 0x01 | ECRECOVER | 椭圆曲线签名恢复 | 3000 | 签名验证 |
| 0x02 | SHA256 | SHA-256 哈希 | 60 + 12/word | 哈希计算 |
| 0x03 | RIPEMD160 | RIPEMD-160 哈希 | 600 + 120/word | 比特币兼容 |
| 0x04 | IDENTITY | 数据复制 | 15 + 3/word | 内存复制 |
| 0x05 | MODEXP | 模幂运算 | 动态计算 | RSA, DH 密钥交换 |
| 0x06 | ECADD | 椭圆曲线加法 | 150 | zk-SNARKs |
| 0x07 | ECMUL | 椭圆曲线乘法 | 6000 | zk-SNARKs |
| 0x08 | ECPAIRING | 椭圆曲线配对 | 45000 + 34000/pair | zk-SNARKs |
| 0x09 | BLAKE2F | BLAKE2b F 函数 | 动态计算 | Zcash 兼容 |
// ❌ 低效:无法在 Solidity 中实现椭圆曲线运算
function verifySignatureManual(bytes32 hash, uint8 v, bytes32 r, bytes32 s)
public pure returns (address) {
// 这在 Solidity 中几乎不可能高效实现
// 需要大量复杂的椭圆曲线数学运算
}
// ✅ 高效:使用 ECRECOVER 预编译合约
function verifySignature(bytes32 hash, uint8 v, bytes32 r, bytes32 s)
public pure returns (address) {
return ecrecover(hash, v, r, s); // 仅消耗 3000 gas
}// ❌ 低效:循环复制大量数据
function copyDataSlow(bytes memory data) public pure returns (bytes memory) {
bytes memory result = new bytes(data.length);
for (uint256 i = 0; i < data.length; i++) {
result[i] = data[i]; // 每次循环消耗额外 gas
}
return result;
}
// ✅ 高效:使用 IDENTITY 预编译合约
function copyDataFast(bytes memory data) public view returns (bytes memory) {
(bool success, bytes memory result) = address(0x04).staticcall(data);
require(success);
return result; // 线性 gas 成本:15 + 3 * words
}contract HashComparison {
// SHA256 预编译合约:60 + 12 * words
function hashWithSHA256(bytes memory data) public view returns (bytes32) {
(bool success, bytes memory result) = address(0x02).staticcall(data);
require(success);
return bytes32(result);
}
// Keccak256 内置函数:30 + 6 * words
function hashWithKeccak256(bytes memory data) public pure returns (bytes32) {
return keccak256(data);
}
// 对于小数据:Keccak256 更便宜
// 对于大数据:差异不大,但 SHA256 提供不同的安全属性
}// RSA 验证示例
contract RSAVerification {
function verifyRSASignature(
bytes memory message,
bytes memory signature,
bytes memory publicKey,
bytes memory modulus
) public view returns (bool) {
// 使用 MODEXP 预编译合约进行 signature^publicKey mod modulus
bytes memory input = abi.encodePacked(
uint256(signature.length),
uint256(publicKey.length),
uint256(modulus.length),
signature,
publicKey,
modulus
);
(bool success, bytes memory result) = address(0x05).staticcall(input);
require(success);
// 比较结果与消息哈希
return keccak256(result) == keccak256(message);
}
}contract PerformanceTest {
function testHashingPerformance() public view returns (uint256, uint256) {
bytes memory data = "Large amount of data to hash for performance testing";
// SHA256 预编译合约
uint256 gasBefore = gasleft();
(bool success,) = address(0x02).staticcall(data);
require(success);
uint256 sha256Gas = gasBefore - gasleft();
// Keccak256 内置函数
gasBefore = gasleft();
keccak256(data);
uint256 keccakGas = gasBefore - gasleft();
return (sha256Gas, keccakGas);
// 典型结果:SHA256 ≈ 100-200 gas, Keccak256 ≈ 50-100 gas
}
}contract CopyPerformanceTest {
function testCopyPerformance(uint256 dataSize) public view returns (uint256, uint256) {
bytes memory data = new bytes(dataSize);
// IDENTITY 预编译合约
uint256 gasBefore = gasleft();
(bool success,) = address(0x04).staticcall(data);
require(success);
uint256 identityGas = gasBefore - gasleft();
// Solidity 循环复制(仅测试前100字节避免gas limit)
gasBefore = gasleft();
bytes memory copy = new bytes(dataSize > 100 ? 100 : dataSize);
for (uint256 i = 0; i < copy.length; i++) {
copy[i] = data[i];
}
uint256 loopGas = gasBefore - gasleft();
return (identityGas, loopGas);
// 对于大数据:IDENTITY 显著更便宜
}
}contract L2CompatibilityCheck {
function checkPrecompileSupport() public view returns (bool[] memory) {
bool[] memory supported = new bool[](9);
// 检查各个预编译合约是否可用
for (uint256 i = 1; i <= 9; i++) {
try this.testPrecompile(address(uint160(i))) {
supported[i-1] = true;
} catch {
supported[i-1] = false;
}
}
return supported;
}
function testPrecompile(address precompile) external view {
// 尝试调用预编译合约
(bool success,) = precompile.staticcall("");
require(success || precompile == address(0x05)); // MODEXP 需要特殊输入
}
}contract SafePrecompileUsage {
function safeECRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s)
public pure returns (address) {
// ECRECOVER 可能返回 address(0) 表示失败
address recovered = ecrecover(hash, v, r, s);
require(recovered != address(0), "Invalid signature");
return recovered;
}
function safeSHA256(bytes memory data) public view returns (bytes32) {
(bool success, bytes memory result) = address(0x02).staticcall(data);
require(success, "SHA256 precompile failed");
require(result.length == 32, "Invalid hash length");
return bytes32(result);
}
}contract GasCostCalculator {
function calculateSHA256Cost(uint256 dataLength) public pure returns (uint256) {
// SHA256: 60 + 12 * ceil(dataLength / 32)
uint256 words = (dataLength + 31) / 32;
return 60 + 12 * words;
}
function calculateIdentityCost(uint256 dataLength) public pure returns (uint256) {
// IDENTITY: 15 + 3 * ceil(dataLength / 32)
uint256 words = (dataLength + 31) / 32;
return 15 + 3 * words;
}
}- 小数据哈希:优先使用 Keccak256
- 大数据复制:使用 IDENTITY 预编译合约
- 签名验证:始终使用 ECRECOVER
- 复杂数学运算:使用相应的椭圆曲线预编译合约
- 始终检查预编译合约调用的返回值
- 验证返回数据的长度和格式
- 提供适当的错误消息
- 在多链部署前测试预编译合约可用性
- 为不支持的链提供备选方案
- 考虑使用条件编译或工厂模式
- 对于批量操作,考虑预编译合约的固定成本
- 测量实际 gas 消耗而非理论计算
- 考虑数据大小对 gas 成本的影响
| 操作类型 | 传统方法 | 预编译合约 | 性能提升 |
|---|---|---|---|
| 签名恢复 | 不可行 | 3000 gas | 无限大 |
| 大数据复制 | O(n) 循环 | 线性成本 | 2-10x |
| SHA256 哈希 | 不可用 | 高效实现 | 专用算法 |
| 模幂运算 | 不可行 | 优化实现 | 无限大 |
| 椭圆曲线运算 | 不可行 | 固定成本 | 无限大 |
预编译合约是以太坊生态系统中强大的 Gas 优化工具,特别适用于加密操作和大数据处理场景。正确使用可以显著降低 Gas 消耗并提高应用性能。