// 错误示例 - 缺少onlyOwner检查
function withdrawAll() public {
payable(owner()).transfer(address(this).balance);
}
// 正确示例
function withdrawAll() public onlyOwner {
payable(owner()).transfer(address(this).balance);
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.24;
contract AssemblyExample {
function addWithAssembly(uint256 a, uint256 b) public pure returns (uint256 result) {
assembly {
result := add(a, b)
}
}
}
// 单槽存储多个小值
contract CompactStorage {
function packValues(uint128 a, uint128 b) public {
assembly {
// 将两个128位值打包到一个槽中
sstore(0, add(a, shl(128, b)))
}
}
function unpackValues() public view returns (uint128 a, uint128 b) {
assembly {
let packed := sload(0)
a := and(packed, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
b := shr(128, packed)
}
}
}
contract ArraySum {
function sumSolidity(uint256[] memory data) public pure returns (uint256 sum) {
for (uint256 i = 0; i < data.length; i++) {
sum += data[i];
}
}
function sumAssembly(uint256[] memory data) public pure returns (uint256 sum) {
assembly {
let len := mload(data)
let ptr := add(data, 0x20)
let end := add(ptr, mul(len, 0x20))
for { } lt(ptr, end) { ptr := add(ptr, 0x20) } {
sum := add(sum, mload(ptr))
}
}
}
}
在生产环境中使用优化版本时,务必确保调用者传入的数组是有效的,且代码逻辑保证不会越界访问。
位运算加速
某些数学运算可以用位运算替代,从而大幅提升性能:
solidity
contract BitOptimizations {
// 用位移替代乘法
function multiplyByPowerOfTwo(uint256 x, uint8 power) public pure returns (uint256 result) {
assembly {
result := shl(power, x)
}
}
// 用位移替代除法
function divideByPowerOfTwo(uint256 x, uint8 power) public pure returns (uint256 result) {
assembly {
result := shr(power, x)
}
}
// 快速取模(当divisor是2的幂时)
function modPowerOfTwo(uint256 x, uint256 divisor) public pure returns (uint256 result) {
require(divisor > 0 && (divisor & (divisor - 1)) == 0, "Not power of two");
assembly {
result := and(x, sub(divisor, 1))
}
}
}
零值检查优化
Solidity的零值检查在某些场景下可能不够高效。通过汇编可以实现更激进的检查模式:
solidity
contract ZeroChecks {
// 高效的非零检查
function requireNonZero(uint256 x) public pure {
assembly {
if iszero(x) {
mstore(0x00, 0x20)
mstore(0x20, 0x736d617274636f6e7472616374000000000000000000000000000000000000)
revert(0x1c, 0x04)
}
}
}
// 使用舍入方式计算最小值
function min(uint256 a, uint256 b) public pure returns (uint256 result) {
assembly {
result := sub(a, sub(a, b))
if slt(result, 0) {
result := b
}
}
}
}
合约代码获取
内联汇编的一个常见用途是检查其他合约的字节码。这在实现某些白名单机制或者合约验证时非常有用:
solidity
library CodeReader {
function getCodeSize(address target) internal view returns (uint256 size) {
assembly {
size := extcodesize(target)
}
}
function getCodeHash(address target) internal view returns (bytes32 hash) {
assembly {
hash := extcodehash(target)
}
}
}
contract CodeChecker {
using CodeReader for address;
function isContract(address target) public view returns (bool) {
return target.getCodeSize() > 0;
}
function verifyContractCode(address target, bytes32 expectedHash)
public
view
returns (bool)
{
return target.getCodeHash() == expectedHash;
}
}
实战:构建高效的数据验证器
让我们用一个完整例子来整合所有技术:实现一个高效的数据哈希验证器:
solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
contract EfficientHasher {
// 使用内联汇编优化哈希计算
function hashData(bytes memory data) public pure returns (bytes32) {
bytes32 result;
assembly {
// 从内存加载数据并计算Keccak256
result := keccak256(add(data, 0x20), mload(data))
}
return result;
}
// 批量验证 - 优化版
function verifyBatch(
bytes[] memory dataArray,
bytes32[] memory expectedHashes
) public pure returns (bool) {
require(dataArray.length == expectedHashes.length, "Length mismatch");
assembly {
let len := dataArray.length
// 指向两个数组的数据区域
let dataPtr := add(dataArray, 0x20)
let hashPtr := add(expectedHashes, 0x20)
for { let i := 0 } lt(i, len) { i := add(i, 1) } {
// 计算当前数据的哈希
let dataLen := mload(dataPtr)
let actualHash := keccak256(add(dataPtr, 0x20), dataLen)
// 与预期值比较
if iszero(eq(actualHash, mload(hashPtr))) {
// 找到不匹配项,返回失败
mstore(0, 0)
return(0, 0x20)
}
// 移动到下一个元素
dataPtr := add(dataPtr, 0x20)
hashPtr := add(hashPtr, 0x20)
}
}
// 所有验证通过
return true;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import "forge-std/Test.sol";
contract AssemblyBenchmark is Test {
uint256[] testData;
function setUp() public {
testData = new uint256[](1000);
for (uint256 i = 0; i < 1000; i++) {
testData[i] = i;
}
}
function testSoliditySum() public view {
uint256 sum;
for (uint256 i = 0; i < testData.length; i++) {
sum += testData[i];
}
}
function testAssemblySum() public pure {
uint256 sum;
assembly {
let len := calldataload(0x04)
let ptr := add(calldataload(0x24), 0x20)
let end := add(ptr, mul(len, 0x20))
for { } lt(ptr, end) { ptr := add(ptr, 0x20) } {
sum := add(sum, calldataload(sub(ptr, 0x20)))
}
}
}
}
contract UnrolledLoop {
uint256[8] public coefficients;
// 普通循环
function calculateNormal(uint256 x) public view returns (uint256) {
uint256 result = 0;
for (uint256 i = 0; i < coefficients.length; i++) {
result += coefficients[i] * x;
}
return result;
}
// 循环展开(适合固定迭代次数)
function calculateUnrolled(uint256 x) public view returns (uint256) {
uint256[8] memory c = coefficients; // 缓存到内存
return c[0] * x + c[1] * x + c[2] * x + c[3] * x +
c[4] * x + c[5] * x + c[6] * x + c[7] * x;
}
}