Solidity 位元運算優化完整指南:Gas 節省與智能合約效能極致優化

本指南從 EVM 機器碼層級出發,系統性地分析各類位元運算 opcode 的 Gas 消耗模型,提供可直接應用於生產環境的優化策略與程式碼範例。涵蓋定點數學與定標因子運算、位元遮罩與旗標操作、雜湊與簽章驗證優化、壓縮資料結構與位元封裝等進階主題。

Solidity 位元運算優化完整指南:Gas 節省與智能合約效能極致優化

概述

在以太坊智能合約開發中,Gas 費用是影響應用經濟模型的關鍵因素。傳統的 Solidity 程式設計往往忽視位元運算的優化潛力,然而對於高頻交易、DeFi 協議底層、以及需要處理大量資料的應用場景,精確的位元運算優化可以帶來數十%至數百%的 Gas 節省。本指南從 EVM 機器碼層級出發,系統性地分析各類位元運算 opcode 的 Gas 消耗模型,提供可直接應用於生產環境的優化策略與程式碼範例。

截至 2026 年第一季度,以太坊主網的平均交易 Gas 成本約為 30-50 gwei,複雜的 DeFi 操作(如多重交換、清算)在網路繁忙時的 Gas 成本可達數百美元。對於日交易量數百萬美元的去中心化交易所或借貸協議,即使只有 1% 的 Gas 優化,也能帶來可觀的經濟效益。掌握位元運算優化技術,是資深 Solidity 開發者必備的核心能力。

第一章:EVM 位元運算Opcode深入解析

1.1 EVM 指令集架構概覽

EVM 是一款基於堆疊的虛擬機器,支援 256 位元的位元運算。所有資料都以 uint256(253 位元有效數據)形式在堆疊和記憶體中表示。這種寬度設計是為了與橢圓曲線密碼學(如 secp256k1 簽章驗證)完美契合,但也帶來了與傳統 32/64 位元系統截然不同的優化策略。

位元運算 Opcode 完整列表

Opcode名稱輸入輸出Base Gas備註
0x10LT213無符號小於
0x11GT213無符號大於
0x12SLT213有符號小於
0x13SGT213有符號大於
0x14EQ213相等
0x15ISZERO113為零
0x16AND213位元AND
0x17OR213位元OR
0x18XOR213位元XOR
0x19NOT113位元NOT
0x1ABYTE213位元組提取
0x1BSHL213左移(EIP-145)
0x1CSHR213右移(EIP-145)
0x1DSAR213算術右移(EIP-145)

1.2 Gas 消耗的深層機制

EIP-150 之後,Gas 計算採用「漸進式」模型,熱門操作數(memory accessed in current transaction)佔用額外 Gas。理解這個模型對於準確預估 Gas 消耗至關重要。

記憶體 Gas 模型

// EVM 記憶體定址成本
// memory_expansion = (ceil(mem_words ** 2 / 512) + 3) * memory_bytes / 32

// 熱門存取成本(EIP-2929)
WARM_ACCESS_GAS = 100  // 熱門存取(已訪問過的 slot)
COLD_ACCESS_GAS = 2100 // 冷門存取(新訪問的 slot)

// 記憶體擴展計算示例
function memoryGasExample() external pure returns (uint256) {
    uint256[10] memory arr;
    // 首次訪問 slot: 2100 gas (cold)
    arr[0] = 1;
    // 後續訪問: 100 gas (warm)
    arr[1] = 2;  // 100 gas
    arr[2] = 3;  // 100 gas
}

位元運算的 Memory 影響

大多數位元運算 opcode(AND、OR、XOR、NOT、SHL、SHR、SAR)不在記憶體中執行,而是直接在堆疊上操作,因此不受記憶體 Gas 模型的影響。然而,當這些運算結果需要存入記憶體或 storage 時,相關的存取成本仍然適用。

1.3 堆疊操作的隱性成本

EVM 堆疊深度限制為 1024 層。當堆疊深度超過 16 層時,某些操作需要額外的 Gas(Stack Too Deep 錯誤)。理解堆疊操作的成本對於優化複雜函式至關重要。

// 堆疊操作成本分析
// 16 層以內:無額外成本
// 超過 16 層:某些操作需要 DUP 或 SWAP

// 示範:計算 10 個數字的 AND
function naiveAnd(uint256[10] calldata vals) public pure returns (uint256) {
    uint256 result = vals[0];
    for (uint i = 1; i < 10; i++) {
        result = result & vals[i];  // 每輪迴圈產生堆疊操作
    }
    return result;
}

// 優化版本:減少中間變數
function optimizedAnd(uint256[10] calldata vals) public pure returns (uint256) {
    assembly {
        let result := calldataload(0x04)
        for { let i := 1 } lt(i, 10) { i := add(i, 1) } {
            let val := calldataload(add(0x04, mul(i, 0x20)))
            result := and(result, val)
        }
        mstore(0x00, result)
        return(0x00, 0x20)
    }
}

第二章:定點數學與定標因子運算

2.1 定標因子的位元操作原理

DeFi 協議中廣泛使用定點數學來表示小數。例如,利率通常以 WAD = 10^18 為基礎表示。掌握定標因子的位元操作可以顯著優化乘除法運算。

定標因子快速運算

// 傳統方式:使用不安全庫函式
function traditionalScaleUp(uint256 value, uint256 factor) pure returns (uint256) {
    return value * factor / 1e18;  // 需要完整乘法
}

// 優化方式:利用位移估算
// 當 factor = 10^k 時,可以使用近似或分段計算

// WAD = 10^18 = 2^18 × 5^18 ≈ 2^60.4
// 可以分解為 2^18 × 5^18

// 實際應用:緊急清算折扣
contract LiquidatorOptimized {
    uint256 constant WAD = 10 ** 18;
    uint256 constant LIQUIDATION_BONUS = 1.1e18; // 110%
    
    // 原始實現
    function calculateCollateral(
        uint256 debt,
        uint256 collateralPrice
    ) external pure returns (uint256) {
        return (debt * WAD / collateralPrice) * LIQUIDATION_BONUS / WAD;
    }
    
    // 優化實現:減少除法次數
    function calculateCollateralOptimized(
        uint256 debt,
        uint256 collateralPrice
    ) external pure returns (uint256) {
        // 重組運算順序:先乘後除
        // (debt / collateralPrice) * LIQUIDATION_BONUS
        assembly {
            // 避免中間溢位
            let scaledDebt := div(mul(debt, LIQUIDATION_BONUS), WAD)
            let result := div(scaledDebt, collateralPrice)
            mstore(0x00, result)
            return(0x00, 0x20)
        }
    }
}

2.2 除法與取模的位元優化

除法是 EVM 中最昂貴的運算之一,消耗約 20-500 gas(取決於操作數)。在某些情況下,可以透過位元操作替代除法。

2 的冪次方除法

// 傳統除法
uint256 result = value / 2;  // gas: ~300

// 位元優化
uint256 result = value >> 1;  // gas: ~5

// 推導:
// 對於任意整數 x 和 2^n:
// x / 2^n = x >> n
// x % 2^n = x & (2^n - 1)

// 實例:質押獎勵分配
contract StakingRewards {
    uint256 constant REWARD_RATE = 100 ether; // 每 epoch 獎勵
    uint256 constant DIVISOR = 1000;
    
    // 原始實現
    function distributeOriginal(
        address[] calldata validators,
        uint256 epochRewards
    ) external pure returns (uint256) {
        uint256 perValidator = epochRewards / validators.length;
        return perValidator;
    }
    
    // 當 validators.length 是 2 的冪次方時
    function distributeOptimized(
        uint256 validatorCount,  // 2, 4, 8, 16, ...
        uint256 epochRewards
    ) external pure returns (uint256) {
        require((validatorCount & (validatorCount - 1)) == 0, "Not power of 2");
        return epochRewards >> uint256(log2(validatorCount));
    }
    
    function log2(uint256 x) public pure returns (uint256) {
        uint256 result = 0;
        while (x > 1) {
            x >>= 1;
            result++;
        }
        return result;
    }
}

2.3 模數運算的位元技巧

// 傳統模數
uint256 result = value % 1024;  // gas: ~300

// 位元優化
uint256 result = value & 1023;  // gas: ~5

// 推導:
// 1024 = 2^10
// value % 1024 = value & (1024 - 1) = value & 1023

// 應用:循環分配器
contract RoundRobinAllocator {
    uint256 constant ROUND_SIZE = 16;
    uint256 constant ROUND_MASK = 15; // ROUND_SIZE - 1
    
    address[] public validators;
    
    function selectValidator(uint256 round) public view returns (address) {
        // 原始實現
        uint256 index = round % validators.length;
        
        // 優化實現(當 validators.length 是 2 的冪次方時)
        // 假設固定為 16 個驗證者
        uint256 indexOptimized = round & ROUND_MASK;
        return validators[indexOptimized];
    }
}

第三章:位元遮罩與旗標操作

3.1 權限管理的位元遮罩

傳統的權限管理使用 require 語句逐一檢查每個權限。透過位元遮罩,可以在一個 uint256 中儲存多達 256 個不同的權限標誌,大幅節省存儲和比較成本。

// 權限位元遮罩系統
library Permissions {
    uint256 constant NONE = 0;
    uint256 constant ADMIN = 1 << 0;      // 1
    uint256 constant MANAGER = 1 << 1;     // 2
    uint256 constant MINTER = 1 << 2;      // 4
    uint256 constant BURNER = 1 << 3;     // 8
    uint256 constant PAUSER = 1 << 4;     // 16
    uint256 constant LIQUIDATOR = 1 << 5;  // 32
    uint256 constant UPGRADER = 1 << 6;    // 64
    
    function hasPermission(uint256 permissions, uint256 flag) 
        internal pure returns (bool) {
        return (permissions & flag) == flag;
    }
    
    function hasAnyPermission(uint256 permissions, uint256 flags)
        internal pure returns (bool) {
        return (permissions & flags) != 0;
    }
    
    function addPermission(uint256 permissions, uint256 flag)
        internal pure returns (uint256) {
        return permissions | flag;
    }
    
    function removePermission(uint256 permissions, uint256 flag)
        internal pure returns (uint256) {
        return permissions & ~flag;
    }
}

contract PermissionedToken {
    using Permissions for uint256;
    
    // 儲存結構
    struct Role {
        uint256 permissions;
        uint256 expiration;
    }
    
    mapping(address => uint256) public balances;
    mapping(address => Role) public roles;
    
    // 傳統實現:多個 mapping
    mapping(address => bool) public isAdmin;
    mapping(address => bool) public isMinter;
    mapping(address => bool) public isPauser;
    
    // 優化實現:單一 uint256
    mapping(address => uint256) public permissions;
    
    // Gas 比較:
    // 傳統:檢查 3 個 mapping = 3 SLOAD (3 × 2100 cold) = 6300 gas
    // 優化:檢查 1 個 uint256 = 1 SLOAD + 2 AND = ~2150 gas
    modifier onlyAdmin() {
        require(roles[msg.sender].permissions & Permissions.ADMIN != 0, "Unauthorized");
        _;
    }
    
    modifier onlyMinter() {
        require(roles[msg.sender].permissions & Permissions.MINTER != 0, "Unauthorized");
        _;
    }
    
    function mint(address to, uint256 amount) external onlyMinter {
        balances[to] += amount;
    }
    
    // 批量權限檢查:節省更多 gas
    function batchTransfer(
        address[] calldata recipients,
        uint256[] calldata amounts
    ) external {
        uint256 senderBalance = balances[msg.sender];
        
        // 預計算總量
        uint256 total;
        for (uint256 i = 0; i < recipients.length; i++) {
            total += amounts[i];
        }
        require(senderBalance >= total, "Insufficient balance");
        
        // 批量轉帳
        balances[msg.sender] = senderBalance - total;
        for (uint256 i = 0; i < recipients.length; i++) {
            balances[recipients[i]] += amounts[i];
        }
    }
}

3.2 旗標狀態機

// 狀態旗標位元表示
contract StateMachine {
    enum State { Active, Paused, Upgrading, Migrating, Deprecated }
    
    // 狀態編碼
    uint256 constant STATE_ACTIVE = 1 << 0;      // 1
    uint256 constant STATE_PAUSED = 1 << 1;      // 2
    uint256 constant STATE_UPGRADING = 1 << 2;   // 4
    uint256 constant STATE_MIGRATING = 1 << 3;   // 8
    uint256 constant STATE_DEPRECATED = 1 << 4;   // 16
    
    uint256 public stateFlags;
    
    // 單一狀態檢查
    function isActive() public view returns (bool) {
        return stateFlags & STATE_ACTIVE != 0;
    }
    
    function isPaused() public view returns (bool) {
        return stateFlags & STATE_PAUSED != 0;
    }
    
    // 多狀態組合檢查
    function canOperate() public view returns (bool) {
        // 只有 Active 狀態可以操作
        return (stateFlags & (STATE_PAUSED | STATE_DEPRECATED)) == 0;
    }
    
    // 狀態轉換驗證
    function transitionToPaused() external {
        require(isActive(), "Must be active");
        stateFlags = (stateFlags & ~STATE_ACTIVE) | STATE_PAUSED;
    }
    
    function transitionToActive() external {
        require(stateFlags & STATE_PAUSED != 0, "Must be paused");
        stateFlags = (stateFlags & ~STATE_PAUSED) | STATE_ACTIVE;
    }
    
    function deprecate() external {
        // 合併操作:清除 Active 和 Paused,設定 Deprecated
        stateFlags = (stateFlags & ~(STATE_ACTIVE | STATE_PAUSED)) | STATE_DEPRECATED;
    }
}

第四章:雜湊與簽章驗證優化

4.1 Keccak256 批次處理

Keccak256 是以太坊中最昂貴的操作之一,計算一個 32 位元組雜湊約消耗 30-60 gas(含記憶體成本)。當需要計算多個雜湊時,批次處理可以節省重複的記憶體初始化成本。

// 批次 Keccak256 優化
contract BatchHasher {
    // 原始實現:逐一雜湊
    function hashArrayOriginal(bytes32[] calldata data) 
        external pure returns (bytes32[] memory) {
        bytes32[] memory results = new bytes32[](data.length);
        for (uint256 i = 0; i < data.length; i++) {
            results[i] = keccak256(abi.encodePacked(data[i]));
        }
        return results;
    }
    
    // 優化實現:使用內嵌assembly批量處理
    function hashArrayOptimized(bytes32[] calldata data) 
        external pure returns (bytes32[] memory) {
        bytes32[] memory results = new bytes32[](data.length);
        
        assembly {
            // 載入資料指標
            let dataPtr := data.offset
            let resultsPtr := results.offset
            
            for { let i := 0 } lt(i, data.length) { i := add(i, 1) } {
                // 讀取當前資料
                let item := calldataload(add(dataPtr, mul(i, 0x20)))
                // 計算雜湊
                mstore(0x00, item)
                let hash := keccak256(0x00, 0x20)
                // 儲存結果
                mstore(add(resultsPtr, mul(i, 0x20)), hash)
            }
        }
        
        return results;
    }
    
    // Merkle 樹葉節點批量雜湊
    function hashLeaves(bytes32[] calldata leaves) 
        external pure returns (bytes32[] memory) {
        require(leaves.length > 0, "Empty array");
        
        uint256 nextLevel = leaves.length;
        uint256 currentLevel = nextLevel;
        
        // 計算層數
        uint256 levels = 0;
        while (currentLevel > 1) {
            currentLevel = (currentLevel + 1) / 2;
            levels++;
        }
        
        bytes32[] memory results = new bytes32[](levels);
        bytes32[] memory current = leaves;
        
        for (uint256 l = 0; l < levels; l++) {
            uint256 parentCount = (current.length + 1) / 2;
            bytes32[] memory parents = new bytes32[](parentCount);
            
            for (uint256 i = 0; i < parentCount; i++) {
                bytes32 left = current[i * 2];
                bytes32 right = i * 2 + 1 < current.length 
                    ? current[i * 2 + 1] 
                    : current[i * 2]; // 複製最後一個如果是奇數
                
                parents[i] = keccak256(abi.encodePacked(left, right));
            }
            
            results[l] = parents[0];
            current = parents;
        }
        
        return results;
    }
}

4.2 ECDSA 簽章驗證批量處理

// 批量簽章驗證
contract BatchSignatureVerifier {
    // 單一簽章驗證(使用原生 ecrecover)
    function verifySingle(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s,
        address signer
    ) external pure returns (bool) {
        bytes32 expected = keccak256(abi.encodePacked(
            "\x19Ethereum Signed Message:\n32",
            hash
        ));
        return ecrecover(expected, v, r, s) == signer;
    }
    
    // 批量驗證(枚舉實現,Gas 較高但簡單)
    function verifyBatch(
        bytes32[] calldata hashes,
        uint8[] calldata vs,
        bytes32[] calldata rs,
        bytes32[] calldata ss,
        address[] calldata signers
    ) external pure returns (bool) {
        require(
            hashes.length == vs.length && 
            vs.length == rs.length && 
            rs.length == ss.length && 
            ss.length == signers.length,
            "Length mismatch"
        );
        
        // 如果任一簽章無效,整個批次失敗
        for (uint256 i = 0; i < hashes.length; i++) {
            if (!verifySingle(hashes[i], vs[i], rs[i], ss[i], signers[i])) {
                return false;
            }
        }
        return true;
    }
    
    // 批量驗證(枚舉實現, Gas 較高但簡單)
    // 當需要部分通過時使用
    function verifyBatchAny(
        bytes32[] calldata hashes,
        uint8[] calldata vs,
        bytes32[] calldata rs,
        bytes32[] calldata ss,
        address[] calldata signers,
        uint256 requiredValid
    ) external pure returns (bool) {
        uint256 validCount = 0;
        
        for (uint256 i = 0; i < hashes.length; i++) {
            if (verifySingle(hashes[i], vs[i], rs[i], ss[i], signers[i])) {
                validCount++;
                if (validCount >= requiredValid) {
                    return true;
                }
            }
        }
        return false;
    }
}

第五章:壓縮資料結構與位元封裝

5.1 小數值位元封裝

當需要儲存多個小範圍值時,可以將它們封裝到單一 uint256 中,節省 storage slot。

// 壓縮資料結構
contract PackedData {
    // 傳統方式:多個 storage slot
    struct TraditionalTokenData {
        uint128 balance;      // slot 1
        uint128 locked;       // slot 2
        uint64 lastUpdate;    // slot 3
        uint32 rewardPerShare;// slot 4
        uint16 level;         // slot 5
    }
    
    // 優化方式:單一 uint256
    // 結構:balance(128) | locked(64) | lastUpdate(32) | level(16) | flags(16)
    uint256 public packedData;
    
    uint256 constant BALANCE_BITS = 128;
    uint256 constant BALANCE_MASK = (1 << BALANCE_BITS) - 1;
    
    uint256 constant LOCKED_SHIFT = 128;
    uint256 constant LOCKED_BITS = 64;
    uint256 constant LOCKED_MASK = ((1 << LOCKED_BITS) - 1) << LOCKED_SHIFT;
    
    uint256 constant UPDATE_SHIFT = 192;
    uint256 constant UPDATE_BITS = 32;
    uint256 constant UPDATE_MASK = ((1 << UPDATE_BITS) - 1) << UPDATE_SHIFT;
    
    uint256 constant LEVEL_SHIFT = 224;
    uint256 constant LEVEL_BITS = 16;
    uint256 constant LEVEL_MASK = ((1 << LEVEL_BITS) - 1) << LEVEL_SHIFT;
    
    uint256 constant FLAGS_SHIFT = 240;
    uint256 constant FLAGS_MASK = ~((1 << FLAGS_SHIFT) - 1);
    
    // Gas 比較:
    // 傳統:5 SLOAD = 5 × 2100 = 10500 gas
    // 優化:1 SLOAD = 2100 gas (減少約 80%)
    
    function getBalance() public view returns (uint128) {
        return uint128(packedData & BALANCE_MASK);
    }
    
    function getLocked() public view returns (uint64) {
        return uint64((packedData & LOCKED_MASK) >> LOCKED_SHIFT);
    }
    
    function getLastUpdate() public view returns (uint32) {
        return uint32((packedData & UPDATE_MASK) >> UPDATE_SHIFT);
    }
    
    function getLevel() public view returns (uint16) {
        return uint16((packedData & LEVEL_MASK) >> LEVEL_SHIFT);
    }
    
    function updateBalance(uint128 newBalance) external {
        packedData = (packedData & ~BALANCE_MASK) | uint256(newBalance);
    }
    
    function updateAll(
        uint128 balance,
        uint64 locked,
        uint32 lastUpdate,
        uint16 level
    ) external {
        packedData = 
            uint256(balance) |
            (uint256(locked) << LOCKED_SHIFT) |
            (uint256(lastUpdate) << UPDATE_SHIFT) |
            (uint256(level) << LEVEL_SHIFT);
    }
}

5.2 位元組偏移精確控制

// 精確位元組操作
contract ByteOperations {
    // 提取特定位元組
    function extractByte(bytes32 data, uint8 index) 
        external pure returns (uint8) {
        require(index < 32, "Index out of bounds");
        return uint8(data >> (31 - index) * 8);
    }
    
    // 替換特定位元組
    function replaceByte(bytes32 data, uint8 index, uint8 newByte)
        external pure returns (bytes32) {
        require(index < 32, "Index out of bounds");
        uint256 shift = (31 - index) * 8;
        return bytes32(
            (uint256(data) & ~(0xFF << shift)) |
            (uint256(newByte) << shift)
        );
    }
    
    // 批量位元組提取(使用 assembly)
    function extractMultiple(bytes32 data, uint8[] calldata indices)
        external pure returns (uint8[] memory) {
        uint8[] memory results = new uint8[](indices.length);
        
        assembly {
            for (uint256 i = 0; i < indices.length; i++) {
                let shift := mul(sub(31, calldataload(add(indices.offset, mul(i, 0x01)))), 8)
                let byte := and(div(data, exp(2, shift)), 0xFF)
                mstore(add(results.offset, mul(add(i, 1), 0x20)), byte)
            }
        }
        
        return results;
    }
    
    // 32位元組到 8×uint32 的解包
    function unpack32(bytes32 data) 
        external pure returns (uint32[8] memory) {
        uint32[8] memory result;
        
        assembly {
            for { let i := 0 } lt(i, 8) { i := add(i, 1) } {
                let value := and(div(data, exp(2, mul(sub(7, i), 32))), 0xFFFFFFFF)
                mstore(add(result.offset, mul(i, 0x20)), value)
            }
        }
        
        return result;
    }
}

第六章:位元運算在密碼學中的應用

6.1 橢圓曲線點座標轉換

// 點座標壓縮與解壓
contract PointCompression {
    // 壓縮點表示:prefix(1) + x(32) = 33 bytes
    // 解壓需要恢復 y 座標
    
    function compressPoint(uint256 x, uint256 y) 
        external pure returns (bytes32) {
        // y 是偶數則 prefix = 0x02,否則 0x03
        uint8 prefix = uint8(y & 1) + 0x02;
        return bytes32((x << 8) | uint256(prefix));
    }
    
    function decompressPoint(bytes32 compressed)
        external view returns (uint256 x, uint256 y) {
        x = uint256(compressed >> 8);
        uint8 prefix = uint8(compressed);
        
        require(prefix == 0x02 || prefix == 0x03, "Invalid prefix");
        
        // 計算 y^2 = x^3 + 7 (secp256k1)
        uint256 ySquared = addmod(
            mulmod(x, x, type(uint256).max - 1), // 延後取模
            7,
            type(uint256).max - 1
        );
        ySquared = addmod(ySquared, 7, type(uint256).max - 1);
        
        // 實際取模
        uint256 prime = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F;
        ySquared = mulmod(ySquared, 1, prime);
        
        // 計算 y = y^2^((p+1)/4)
        uint256 exponent = (prime + 1) / 4;
        y = powmod(ySquared, exponent, prime);
        
        // 確認奇偶性
        bool yBit = (y & 1) == 1;
        bool expectedBit = (prefix == 0x03);
        
        if (yBit != expectedBit) {
            y = prime - y;
        }
        
        // 驗證點在曲線上
        require(
            mulmod(y, y, prime) == ySquared,
            "Invalid point"
        );
    }
    
    // 安全的模指數(防止 MIMC 等攻擊)
    function powmod(
        uint256 base, 
        uint256 exponent, 
        uint256 modulus
    ) public pure returns (uint256) {
        if (modulus == 1) return 0;
        
        uint256 result = 1;
        base = base % modulus;
        
        while (exponent > 0) {
            if (exponent & 1 == 1) {
                result = mulmod(result, base, modulus);
            }
            exponent >>= 1;
            base = mulmod(base, base, modulus);
        }
        
        return result;
    }
}

6.2 多簽名閾值位元運算

// 多簽名閾值運算
contract MultisigThreshold {
    // 閾值簽名:n 個簽名者中需要 k 個同意
    
    uint256 public n;
    uint256 public k;
    mapping(address => uint256) public signers;
    
    constructor(uint256 _n, uint256 _k) {
        require(_k <= _n && _n <= 32, "Invalid parameters");
        n = _n;
        k = _k;
    }
    
    // 添加簽名者
    function addSigner(address signer) external {
        require(signers[signer] == 0, "Already a signer");
        uint256 index;
        for (uint256 i = 0; i < n; i++) {
            if (signers[address(0)] == 0) {
                index = i;
                break;
            }
        }
        signers[signer] = 1 << index;
    }
    
    // 驗證簽名數量是否達到閾值
    function verifyThreshold(
        uint256[] calldata signatureBits
    ) external pure returns (bool) {
        uint256 totalSigs = 0;
        for (uint256 i = 0; i < signatureBits.length; i++) {
            totalSigs += _popcount(signatureBits[i]);
        }
        return totalSigs >= k;
    }
    
    // 內聯 popcount(計算 set bits 數量)
    function _popcount(uint256 x) internal pure returns (uint256 count) {
        assembly {
            for {} gt(x, 0) {} {
                x := and(x, sub(x, 1))
                count := add(count, 1)
            }
        }
    }
    
    // 批量驗證閾值(使用 assembly 優化)
    function verifyThresholdOptimized(uint256 signatureBitmap)
        external pure returns (bool) {
        uint256 count;
        assembly {
            for {} gt(signatureBitmap, 0) {} {
                signatureBitmap := and(signatureBitmap, sub(signatureBitmap, 1))
                count := add(count, 1)
            }
        }
        return count >= k;
    }
}

第七章:Gas 優化實戰案例

7.1 AMM 流動性池位元運算優化

// 優化的流動性池常數乘積函式
contract OptimizedConstantProductAMM {
    uint256 constant PRECISION = 10 ** 18;
    uint256 constant FEE_PRECISION = 10 ** 10;
    
    uint256 public reserve0;
    uint256 public reserve1;
    uint256 public feeBps; // basis points, e.g., 30 = 0.3%
    
    function getAmountOut(
        uint256 amountIn,
        uint256 reserveIn,
        uint256 reserveOut
    ) external pure returns (uint256) {
        require(amountIn > 0, "INSUFFICIENT_INPUT_AMOUNT");
        require(reserveIn > 0 && reserveOut > 0, "INSUFFICIENT_LIQUIDITY");
        
        uint256 amountInWithFee = amountIn * (FEE_PRECISION - feeBps);
        
        // 優化:避免中間溢位
        // 分子: amountInWithFee * reserveOut
        // 分母: reserveIn * FEE_PRECISION + amountInWithFee
        uint256 numerator = amountInWithFee * reserveOut;
        uint256 denominator = reserveIn * FEE_PRECISION + amountInWithFee;
        
        return numerator / denominator;
    }
    
    // 內嵌 assembly 版本(最大優化)
    function getAmountOutOptimized(
        uint256 amountIn,
        uint256 reserveIn,
        uint256 reserveOut
    ) external pure returns (uint256) {
        assembly {
            // 輸入驗證
            if iszero(amountIn) { revert(0, 0) }
            if or(iszero(reserveIn), iszero(reserveOut)) { revert(0, 0) }
            
            let feePrecision := FEE_PRECISION
            
            // amountInWithFee = amountIn * (FEE_PRECISION - feeBps)
            let fee := sub(feePrecision, feeBps)
            let amountInWithFee := mul(amountIn, fee)
            
            // numerator = amountInWithFee * reserveOut
            let numerator := mul(amountInWithFee, reserveOut)
            
            // denominator = reserveIn * FEE_PRECISION + amountInWithFee
            let denominator := add(mul(reserveIn, feePrecision), amountInWithFee)
            
            // result = numerator / denominator
            mstore(0x00, div(numerator, denominator))
            return(0x00, 0x20)
        }
    }
    
    // 使用二分搜尋優化的利率計算
    function getAmountOutBinarySearch(
        uint256 amountIn,
        uint256 reserveIn,
        uint256 reserveOut,
        uint256 iterations
    ) external pure returns (uint256) {
        uint256 low = 1;
        uint256 high = reserveOut;
        uint256 mid;
        uint256 y;
        
        while (iterations > 0 && low < high) {
            mid = (low + high + 1) / 2;
            y = get_y(mid, reserveIn, reserveOut);
            
            if (y < amountIn) {
                low = mid + 1;
            } else {
                high = mid - 1;
            }
            iterations--;
        }
        
        return low;
    }
    
    function get_y(
        uint256 x,
        uint256 reserveIn,
        uint256 reserveOut
    ) internal pure returns (uint256) {
        uint256 FEE_PRECISION = 10 ** 10;
        uint256 feeBps_local = feeBps;
        
        uint256 amountInWithFee = x * (FEE_PRECISION - feeBps_local);
        uint256 numerator = amountInWithFee * reserveOut;
        uint256 denominator = reserveIn * FEE_PRECISION + amountInWithFee;
        
        return numerator / denominator;
    }
}

7.2 批量轉帳位元運算

// 批量轉帳優化
contract BatchTransfer {
    // 標準批量轉帳(未優化)
    function batchTransferOriginal(
        address[] calldata recipients,
        uint256[] calldata amounts
    ) external {
        for (uint256 i = 0; i < recipients.length; i++) {
            payable(recipients[i]).transfer(amounts[i]);
        }
    }
    
    // assembly 優化版本
    function batchTransferOptimized(
        address[] calldata recipients,
        uint256[] calldata amounts
    ) external {
        uint256 total;
        for (uint256 i = 0; i < amounts.length; i++) {
            total += amounts[i];
        }
        
        require(address(this).balance >= total, "Insufficient balance");
        
        assembly {
            let ptr := mload(0x40)
            let recipientsPtr := recipients.offset
            let amountsPtr := amounts.offset
            
            for { let i := 0 } lt(i, amounts.length) { i := add(i, 1) } {
                let recipient := calldataload(add(recipientsPtr, mul(i, 0x20)))
                let amount := calldataload(add(amountsPtr, mul(i, 0x20)))
                
                // gasleft() 檢查防止 out of gas
                if iszero(gt(gas(), 2300)) { revert(0, 0) }
                
                // 內部轉帳
                let success := call(gas(), recipient, amount, 0, 0, 0, 0)
                if iszero(success) { revert(0, 0) }
            }
        }
    }
    
    // ERC-20 批量轉帳
    function batchERC20Transfer(
        IERC20 token,
        address[] calldata recipients,
        uint256[] calldata amounts
    ) external {
        uint256 total;
        for (uint256 i = 0; i < amounts.length; i++) {
            total += amounts[i];
        }
        
        require(token.transferFrom(msg.sender, address(this), total), "Transfer failed");
        
        for (uint256 i = 0; i < recipients.length; i++) {
            require(token.transfer(recipients[i], amounts[i]), "Transfer failed");
        }
    }
    
    // 完全 assembly 版本
    function batchERC20TransferOptimized(
        address token,
        address[] calldata recipients,
        uint256[] calldata amounts
    ) external {
        assembly {
            let ptr := mload(0x40)
            let recipientsPtr := recipients.offset
            let amountsPtr := amounts.offset
            
            // 計算總量
            let total := 0
            for { let i := 0 } lt(i, amounts.length) { i := add(i, 1) } {
                total := add(total, calldataload(add(amountsPtr, mul(i, 0x20))))
            }
            
            // transferFrom
            mstore(0x00, 0x23b872dd) // transferFrom selector
            mstore(0x04, caller())
            mstore(0x24, address())
            mstore(0x44, total)
            let success := call(sub(gas(), 5000), token, 0, 0x00, 0x64, 0x00, 0x20)
            if iszero(success) { revert(0, 0) }
            
            // 逐一轉帳
            for { let i := 0 } lt(i, recipients.length) { i := add(i, 1) } {
                let recipient := calldataload(add(recipientsPtr, mul(i, 0x20)))
                let amount := calldataload(add(amountsPtr, mul(i, 0x20)))
                
                mstore(0x00, 0xa9059cbb) // transfer selector
                mstore(0x04, recipient)
                mstore(0x24, amount)
                success := call(sub(gas(), 5000), token, 0, 0x00, 0x44, 0x00, 0x20)
                if iszero(success) { revert(0, 0) }
            }
        }
    }
}

第八章:位元運算安全注意事項

8.1 溢位與下溢防護

// 安全位元運算包裝
library SafeBitOps {
    // 安全加法(檢查溢位)
    function addSafe(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");
        return c;
    }
    
    // 安全乘法
    function mulSafe(uint256 a, uint256 b) internal pure returns (uint256) {
        if (a == 0) return 0;
        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");
        return c;
    }
    
    // 安全的 2^n 計算
    function pow2(uint256 n) internal pure returns (uint256) {
        require(n < 256, "Exponent too large");
        return 1 << n;
    }
    
    // 安全的位移(處理邊界情況)
    function shiftLeft(uint256 value, uint256 bits) internal pure returns (uint256) {
        require(bits < 256, "Shift too large");
        return value << bits;
    }
    
    function shiftRight(uint256 value, uint256 bits) internal pure returns (uint256) {
        require(bits < 256, "Shift too large");
        return value >> bits;
    }
    
    // 安全的位元組提取
    function extractByteSafe(bytes32 data, uint256 index) 
        internal pure returns (uint8) {
        require(index < 32, "Index out of bounds");
        return uint8(data >> (31 - index) * 8);
    }
}

8.2 攻擊向量防護

// 防護常見攻擊向量的位元運算
contract SecureBitOperations {
    // 防範灰天鵝攻擊(惡意操縱質押率)
    uint256 public constant DECAY_RATE = 1 << 96; // 每日衰減因子
    
    function calculateDecay(uint256 value, uint256 days) 
        external pure returns (uint256) {
        // 使用位移實現指數衰減
        // value * (DECAY_RATE / 2^96)^days
        uint256 decayFactor = DECAY_RATE;
        uint256 result = value;
        
        while (days > 0) {
            if (days & 1 == 1) {
                result = (result * decayFactor) >> 96;
            }
            decayFactor = (decayFactor * decayFactor) >> 96;
            days >>= 1;
        }
        
        return result;
    }
    
    // 防範搶跑攻擊的時間鎖
    bytes32 public constant TIME_LOCK_MASK = 0x0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
    
    function setTimelock(uint256 timestamp) external pure returns (uint256) {
        // 限制時間戳在合理範圍內
        require(timestamp < 1 << 48, "Timestamp too large");
        
        // 使用高位保留作為標誌位
        return timestamp | (1 << 159);
    }
    
    function verifyTimelock(uint256 packed) external view returns (bool) {
        // 驗證標誌位
        if ((packed >> 159) & 1 == 0) return false;
        
        uint256 timestamp = packed & TIME_LOCK_MASK;
        return block.timestamp >= timestamp;
    }
}

結論

位元運算優化是以太坊智能合約開發中的進階技術,它要求開發者深入理解 EVM 指令集架構和 Gas 消耗模型。本指南涵蓋的優化策略可以總結為以下幾點核心原則:

第一,優先使用位移替代除法。當除數是 2 的冪次方時,右移運算比除法節省約 60 倍的 Gas。這一原則同樣適用於取模運算,當模數是 2 的冪次方時,可以使用 AND 運算替代。

第二,批次處理減少重複開銷。對於 Keccak256、簽章驗證等昂貴操作,批次處理可以分攤記憶體初始化成本,節省約 20-30% 的 Gas。

第三,壓縮資料結構節省存儲。一個 uint256 可以儲存多達 8 個 uint32 或 4 個 uint64,這種封裝技術可以將存儲 Gas 降低 80% 以上。

第四,合理使用 assembly 優化關鍵路徑。對於高頻調用的函式,assembly 可以消除 Solidity 編譯器產生的冗餘代碼,但同時增加了代碼複雜度和安全風險。

第五,始終驗證位元運算的安全性。位元運算中的溢位和移位錯誤可能導致嚴重的安全漏洞,必須配合嚴格的邊界檢查。

這些優化技術的實際效益取決於具體應用場景。對於 Gas 敏感的 DeFi 協議,即使是 5-10% 的 Gas 節省,也能帶來可觀的經濟價值。建議開發者在追求效能優化的同時,平衡代碼可讀性和安全風險。

延伸閱讀與來源

這篇文章對您有幫助嗎?

評論

發表評論

注意:由於這是靜態網站,您的評論將儲存在本地瀏覽器中,不會公開顯示。

目前尚無評論,成為第一個發表評論的人吧!