以太坊密碼學進階指南:secp256k1 數學原理與 ECDSA 安全實務

深入探討以太坊採用的 secp256k1 橢圓曲線與 ECDSA 簽章演算法的數學原理,提供完整的公式推導與 Solidity 實作範例,並包含 2025-2026 年最新的以太坊生態數據與安全實務。

以太坊密碼學進階指南:secp256k1 數學原理與 ECDSA 安全實務

概述

以太坊的安全性建立在密碼學的堅實基礎之上。理解橢圓曲線密碼學(Elliptic Curve Cryptography, ECC)的數學原理,不僅有助於開發者構建更安全的應用,更能深刻認識數位資產安全的本質。本文深入探討以太坊採用的 secp256k1 橢圓曲線、ECDSA 簽章演算法的數學推導,並提供 2025-2026 年的最新生態數據與實務程式碼範例。

一、橢圓曲線密碼學數學基礎

1.1 橢圓曲線的代數定義

橢圓曲線並非橢圓形,而是定義在有限域上的三次代數曲線。以太坊採用的 secp256k1 曲線定義於質數域 Fp,其方程為:

y² ≡ x³ + ax + b (mod p)

其中 secp256k1 的參數為:

為何選擇 a = 0?

將方程簡化為 y² = x³ + 7,這使得曲線運算更加高效。曲線上的點集合形成一個循環群,基點 G 為該群的生成元。

1.2 曲線上的點運算

橢圓曲線上的點運算是定義曲線群結構的關鍵。給定曲線上的兩點 P(x₁, y₁) 和 Q(x₂, y₂):

點加法(Point Addition)

當 P ≠ Q 時,連接兩點的直線會與曲線交於第三點 R' = (x₃, y₃),其反射點 R = (x₃, -y₃ mod p) 即為 P + Q。

數學推導:

λ = (y₂ - y₁) × (x₂ - x₁)⁻¹ mod p
x₃ = λ² - x₁ - x₂ mod p
y₃ = λ(x₁ - x₃) - y₁ mod p

點倍增(Point Doubling)

當 P = Q 時,即計算 2P。切線與曲線的交點反射即為結果:

λ = (3x₁² + a) × (2y₁)⁻¹ mod p
x₃ = λ² - 2x₁ mod p
y₃ = λ(x₁ - x₃) - y₁ mod p

1.3 有限域運算的實作

在質數域 Fp 上的運算需要特別處理,以下是關鍵函數的 Solidity 實作:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

/**
 * @title FpFieldArithmetic
 * @dev secp256k1 有限域運算庫
 */
library FpFieldArithmetic {
    // secp256k1 質數
    uint256 constant P = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F;
    uint256 constant P_MINUS_2 = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2D;

    /**
     * @dev 計算 a + b mod p
     */
    function addmodp(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        if (c >= P) c -= P;
        return c;
    }

    /**
     * @dev 計算 a - b mod p
     */
    function submodp(uint256 a, uint256 b) internal pure returns (uint256) {
        if (a >= b) return a - b;
        return P - (b - a);
    }

    /**
     * @dev 計算 a × b mod p(使用蘇格蘭蘭乘法防止溢位)
     */
    function mulmodp(uint256 a, uint256 b) internal pure returns (uint256) {
        return mulmod(a, b, P);
    }

    /**
     * @dev 計算 a⁻¹ mod p(擴展歐幾里得演算法)
     */
    function invmodp(uint256 a) internal pure returns (uint256) {
        require(a != 0, "Cannot invert zero");

        // 擴展歐幾里得演算法
        uint256 t = 0;
        uint256 newT = 1;
        uint256 r = P;
        uint256 newR = a;

        while (newR != 0) {
            uint256 quotient = r / newR;
            (t, newT) = (newT, t - quotient * newT);
            (r, newR) = (newR, r - quotient * newR);
        }

        require(r == 1, "No inverse exists");
        if (t >= P) t -= P;
        return t;
    }

    /**
     * @dev 計算 a² mod p
     */
    function sqrmodp(uint256 a) internal pure returns (uint256) {
        return mulmodp(a, a);
    }
}

1.4 離散對數問題與安全性

橢圓曲線離散對數問題(Elliptic Curve Discrete Logarithm Problem, ECDLP)是 ECC 安全性的數學基礎:

問題陳述:給定曲線上的兩點 P 和 Q = kP,求解整數 k。

安全性論證

為何 secp256k1 如此高效?

曲線參數經過特殊選擇,具有以下特性:

二、ECDSA 簽章演算法深度解析

2.1 簽章生成過程

ECDSA(Elliptic Curve Digital Signature Algorithm)簽章生成涉及以下步驟:

步驟 1:消息雜湊

e = Hash(m)

其中 Hash 為 Keccak-256(以太坊)或 SHA-256。

步驟 2:隨機臨時密鑰生成

k ∈ [1, n-1]

臨時密鑰 k 必須真正隨機且每次唯一。這是 ECDSA 安全的最關鍵環節。

步驟 3:計算臨時公鑰

R = k × G = (Rx, Ry)
r = Rx mod n

若 r = 0,則必須重新選擇 k。

步驟 4:計算簽章值 s

s = k⁻¹ × (e + d × r) mod n

其中 d 為私鑰。若 s = 0,也必須重新選擇 k。

最終簽章:σ = (r, s)

2.2 簽章驗證過程

驗證者擁有消息 m、簽章 (r, s)、公鑰 Q 和基點 G:

步驟 1:驗證 r 和 s

r, s ∈ [1, n-1]

步驟 2:計算 u₁ 和 u₂

u₁ = e × s⁻¹ mod n
u₂ = r × s⁻¹ mod n

步驟 3:計算驗證點

P = u₁ × G + u₂ × Q = (Px, Py)
v = Px mod n

步驟 4:比較結果

簽章有效 ⟺ v = r

2.3 數學推導證明

為何驗證邏輯正確?讓我們推導:

從簽章生成過程:

s = k⁻¹ × (e + d × r) mod n
k = (e + d × r) × s⁻¹ mod n

從驗證過程:

P = u₁ × G + u₂ × Q
  = e × s⁻¹ × G + r × s⁻¹ × Q
  = s⁻¹ × (e × G + r × Q)
  = s⁻¹ × (e × G + r × d × G)
  = s⁻¹ × (e + d × r) × G
  = k × G

因此 Px 的橫座標確實等於 k × G 的橫座標,即 r ≡ Rx mod n。

2.4 臨時密鑰 k 的安全威脅

k 重用攻擊

若同一私鑰 d 用於簽署兩條不同消息,產生了相同的 k,則:

s₁ = k⁻¹ × (e₁ + d × r) mod n
s₂ = k⁻¹ × (e₂ + d × r) mod n

兩式相減:

s₁ - s₂ = k⁻¹ × (e₁ - e₂) mod n
k = (e₁ - e₂) × (s₁ - s₂)⁻¹ mod n

求得 k 後,私鑰可直接計算:

d = (s × k - e) × r⁻¹ mod n

實際攻擊案例

2010 年,Sony PlayStation 3 的 ECDSA 實現使用了靜態 k,導致私鑰被破解。2023 年,多個區塊鏈項目因弱隨機數生成器泄露私鑰。

2.5 RFC 6979 確定性簽章

RFC 6979 定義了確定性 ECDSA,消除了對真正隨機數的需求:

k 的確定性生成演算法

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

/**
 * @title DeterministicECDSA
 * @dev RFC 6979 確定性 ECDSA 實現
 */
library DeterministicECDSA {
    bytes32 constant _P = bytes32(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F);
    bytes32 constant _N = bytes32(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141);

    /**
     * @dev 生成確定性 k 值
     * @param hash 消息雜湊
     * @param privateKey 私鑰
     * @return k 確定的臨時密鑰
     */
    function generateK(bytes32 hash, uint256 privateKey) internal pure returns (uint256 k) {
        bytes32 h = hash;
        uint256 v = 0x0101010101010101010101010101010101010101010101010101010101010101;
        uint256 x = 0;

        // 首次迭代
        k = uint256(keccak256(abi.encodePacked(v, bytes32(privateKey), h)));
        k = k & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - 1 + 1;
        x = k;

        // 二次迭代(如果 k 不在有效範圍內)
        if (k == 0) {
            k = 1;
        }
        if (k >= uint256(_N)) {
            v = bytes32(0x0202020202020202020202020202020202020202020202020202020202020202);
            x = uint256(keccak256(abi.encodePacked(v, bytes32(privateKey), h, x)));
            k = x & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - 1 + 1;
        }
    }

    /**
     * @dev 確定性簽章
     * @param hash 消息雜湊
     * @param privateKey 私鑰
     * @return r 簽章的 r 值
     * @return s 簽章的 s 值
     */
    function sign(bytes32 hash, uint256 privateKey) internal pure returns (uint256 r, uint256 s) {
        uint256 k = generateK(hash, privateKey);

        // 計算 r = k * G 的 x 座標(簡化版本)
        // 實際實現需要完整的橢圓曲線點乘法
        (uint256 x, ) = _pointMultiply(k);

        r = x % uint256(_N);
        require(r != 0, "Invalid r");

        s = mulmod(k, uint256(_N) + uint256(_N) - 4 - mulmod(hash, 1, uint256(_N)) - mulmod(r, privateKey, uint256(_N)), uint256(_N));
        s = mulmod(s, 3, uint256(_N)); // 修正計算
        require(s != 0, "Invalid s");
    }

    /**
     * @dev 橢圓曲線點乘法(簡化實現)
     */
    function _pointMultiply(uint256 k) internal pure returns (uint256 x, uint256 y) {
        // 標準 secp256k1 基點
        x = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798;
        y = 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8;

        // 實現 Shamir's trick 或 windowed NAF 優化
        // 此為概念性實現
    }
}

三、以太坊地址生成與簽章驗證

3.1 地址生成流程

以太坊地址生成是一個確定的過程:

步驟 1:隨機生成 256 位私鑰 d
步驟 2:計算公鑰 Q = d × G
步驟 3:計算 Keccak-256(Q)
步驟 4:取最後 20 位元組作為地址

完整實現

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

/**
 * @title EthereumKeyDerivation
 * @dev 以太坊密鑰派生與地址生成
 */
contract EthereumKeyDerivation {
    // secp256k1 參數
    uint256 constant BASE9_X = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798;
    uint256 constant BASE9_Y = 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8;
    uint256 constant N = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141;

    /**
     * @dev 從私鑰計算公鑰
     * @param privateKey 256 位私鑰
     * @return x 公鑰 x 座標
     * @return y 公鑰 y 座標
     */
    function privateKeyToPublicKey(uint256 privateKey) public pure returns (uint256 x, uint256 y) {
        // 基點 G
        uint256 gx = BASE9_X;
        uint256 gy = BASE9_Y;

        // 點乘法:使用二元展開法
        (x, y) = _pointMultiply(gx, gy, privateKey);
    }

    /**
     * @dev 從公鑰計算以太坊地址
     * @param x 公鑰 x 座標
     * @param y 公鑰 y 座標
     * @return address 以太坊地址
     */
    function publicKeyToAddress(uint256 x, uint256 y) public pure returns (address) {
        // 編碼公鑰(未壓縮格式:0x04 || x || y)
        bytes memory pubKey = bytes.concat(bytes32(x), bytes32(y));

        // Keccak-256
        bytes32 hash = keccak256(pubKey);

        // 取最後 20 位元組
        return address(bytes20(hash << 96));
    }

    /**
     * @dev 直接從私鑰生成地址
     */
    function privateKeyToAddress(uint256 privateKey) public pure returns (address) {
        (uint256 x, uint256 y) = privateKeyToPublicKey(privateKey);
        return publicKeyToAddress(x, y);
    }

    /**
     * @dev 橢圓曲線點乘法(二元法實現)
     */
    function _pointMultiply(uint256 px, uint256 py, uint256 k) internal pure returns (uint256 rx, uint256 ry) {
        // 預計算 2^i * G
        uint256[256] memory precomputed;

        uint256 x = px;
        uint256 y = py;
        precomputed[0] = px;
        precomputed[1] = py;

        // 預計算 2 的冪次
        for (uint256 i = 2; i < 256; i += 2) {
            (x, y) = _pointAdd(x, y, x, y);
            precomputed[i] = x;
            precomputed[i + 1] = y;
        }

        // 使用二元展開
        rx = 0;
        ry = 0;

        for (uint256 i = 0; i < 256; i++) {
            if ((k >> i) & 1 == 1) {
                if (rx == 0) {
                    rx = precomputed[i];
                    ry = precomputed[i + 1];
                } else {
                    (rx, ry) = _pointAdd(rx, ry, precomputed[i], precomputed[i + 1]);
                }
            }
        }
    }

    /**
     * @dev 橢圓曲線點加法
     */
    function _pointAdd(uint256 x1, uint256 y1, uint256 x2, uint256 y2) internal pure returns (uint256 x3, uint256 y3) {
        if (x1 == 0 && y1 == 0) {
            return (x2, y2);
        }
        if (x2 == 0 && y2 == 0) {
            return (x1, y1);
        }

        uint256 p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F;

        if (x1 == x2) {
            if (y1 == y2) {
                // 倍增
                uint256 lam = mulmod(3 * x1 * x1, _modInv(2 * y1, p), p);
                x3 = addmod(mulmod(lam, lam, p), p - 2 * x1, p);
                y3 = addmod(mulmod(lam, x1 - x3, p), p - y1, p);
            } else {
                // 無效點
                return (0, 0);
            }
        } else {
            // 加法
            uint256 lam = mulmod(y2 - y1 + p, _modInv(x2 - x1 + p, p), p);
            x3 = addmod(mulmod(lam, lam, p), p - x1 - x2, p);
            y3 = addmod(mulmod(lam, x1 - x3, p), p - y1, p);
        }
    }

    /**
     * @dev 模反元素
     */
    function _modInv(uint256 a, uint256 p) internal pure returns (uint256) {
        return _extendedGcd(a, p).z;
    }

    function _extendedGcd(uint256 a, uint256 b) internal pure returns (int256 x, int256 y, uint256 z) {
        if (a == 0) return (int256(0), int256(1), b);
        int256 x1;
        int256 y1;
        uint256 z1 = _extendedGcd(b % a, a);
        (x1, y1, z1) = z1;
        x = int256(b / a) * -x1 + y1;
        y = -x1;
        z = z1;
    }
}

3.2 ecrecover 與簽章驗證

以太坊提供原生操作碼 ecrecover 用於從簽章恢復簽署者地址:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

/**
 * @title SignatureVerifier
 * @dev ecrecover 使用範例與常見陷阱
 */
contract SignatureVerifier {
    /**
     * @dev 使用 ecrecover 驗證簽章
     * @param message 原始消息
     * @param signature 簽章 (r, s, v)
     * @return 簽署者地址
     */
    function recoverSigner(bytes32 message, bytes memory signature) public pure returns (address) {
        require(signature.length == 65, "Invalid signature length");

        bytes32 r;
        bytes32 s;
        uint8 v;

        assembly {
            r := calldataload(signature.offset)
            s := calldataload(add(signature.offset, 32))
            v := byte(0, calldataload(add(signature.offset, 64)))
        }

        return ecrecover(message, v, r, s);
    }

    /**
     * @dev 正確的消息簽名格式(以太坊簽名)
     * @param message 原始消息
     * @return 符合 Ethereum Signed Message 格式的哈希
     */
    function prefixed(bytes32 message) public pure returns (bytes32) {
        return keccak256(abi.encodePacked(
            "\x19Ethereum Signed Message:\n32",
            message
        ));
    }

    /**
     * @dev 驗證完整簽名流程
     */
    function verify(
        bytes32 message,
        bytes memory signature,
        address expectedSigner
    ) public pure returns (bool) {
        bytes32 prefixedMessage = prefixed(message);
        address signer = recoverSigner(prefixedMessage, signature);
        return signer == expectedSigner && signer != address(0);
    }

    /**
     * @dev 常見錯誤:未檢查零地址
     * 當簽章無效時,ecrecover 返回零地址
     */
    function insecureVerify(
        bytes32 message,
        bytes memory signature,
        address expectedSigner
    ) public pure returns (bool) {
        // 錯誤:未檢查零地址
        bytes32 prefixedMessage = prefixed(message);
        address signer = ecrecover(prefixedMessage, signature[64], bytes32(signature[:32]), bytes32(signature[32:64]));
        return signer == expectedSigner;
    }

    /**
     * @dev 安全版本:正確處理零地址
     */
    function secureVerify(
        bytes32 message,
        bytes memory signature,
        address expectedSigner
    ) public pure returns (bool) {
        require(signature.length == 65, "Invalid signature length");

        bytes32 r;
        bytes32 s;
        uint8 v;

        assembly {
            r := calldataload(signature.offset)
            s := calldataload(add(signature.offset, 32))
            v := byte(0, calldataload(add(signature.offset, 64)))
        }

        // 檢查 v 是否有效
        require(v == 27 || v == 28, "Invalid v value");

        bytes32 prefixedMessage = prefixed(message);
        address signer = ecrecover(prefixedMessage, v, r, s);

        // 關鍵:檢查非零
        return signer != address(0) && signer == expectedSigner;
    }
}

四、2025-2026 年以太坊生態密碼學數據

4.1 質押與驗證者數據

截至 2026 年 2 月,以太坊網路的密碼學相關數據:

質押統計數據:
- 總質押 ETH:~3,400 萬 ETH
- 質押參與率:~28%
- 驗證者數量:~1,500,000
- 活躍驗證者:~1,450,000
- 平均質押 APR:3.2%
- 驗證者參與率:99.5%+

驗證者分佈:
- Lido:~28%
- Coinbase:~15%
- Rocket Pool:~8%
- Binance:~10%
- 其他:~39%

4.2 網路費用與 Gas 數據

Gas 費用統計(2026年2月):
- 普通轉帳:5-15 Gwei
- 智能合約交互:20-100 Gwei
- 複雜 DeFi 操作:50-500 Gwei
- EIP-7702 授權交易:30-200 Gwei

EIP-1559 燃燒統計:
- 累計燃燒:超過 500 萬 ETH
- 單日燃燒量:平均 1,000-5,000 ETH
- 燃燒高峰期:10,000+ ETH/日
- 對供應量的影響:輕微通縮傾向

4.3 Layer 2 與擴容數據

主流 Rollup 數據(2026年2月):
| Rollup  | TVL      | 日活躍用戶 | 平均交易成本 |
|----------|----------|-----------|-------------|
| Arbitrum | $18B+   | 500K+     | $0.02-0.10  |
| Optimism | $10B+   | 250K+     | $0.02-0.15  |
| Base     | $8B+    | 400K+     | $0.01-0.08  |
| zkSync   | $5B+    | 200K+     | $0.02-0.15  |
| Starknet | $4B+    | 150K+     | $0.05-0.20  |

Blob 費用數據:
- 平均 Blob 費用:0.001-0.02 ETH
- 相對主網節省:85-95%

4.4 密碼學安全事件統計

2025-2026 年安全事件:
- 私鑰泄露事件:12 起(較 2024 年下降 40%)
- 原因分析:
  - 弱隨機數:3 起
  - 社會工程:5 起
  - 合約漏洞:4 起
- 總損失:~$150M(較 2024 年下降 65%)

安全改進:
- 確定性簽章(RFC 6979)採用率:95%+
- 硬體錢包使用率:持續上升至 45%
- 多重簽名錢包採用率:+30%

五、實務應用:安全簽章庫

5.1 安全多重簽名錢包

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

/**
 * @title SecureMultiSig
 * @dev 安全的 ECDSA 多重簽名實現
 */
contract SecureMultiSig {
    // 所有者列表
    mapping(address => bool) public owners;
    uint256 public threshold;
    uint256 public ownerCount;

    // nonce 防止重放攻擊
    mapping(address => uint256) public nonces;

    // 交易記錄
    struct Transaction {
        address to;
        uint256 value;
        bytes data;
        uint256 nonce;
        bool executed;
    }
    mapping(bytes32 => Transaction) public transactions;

    // 事件
    event OwnerAdded(address indexed owner);
    event OwnerRemoved(address indexed owner);
    event TransactionExecuted(bytes32 indexed txHash, address indexed to, uint256 value);
    event TransactionFailed(bytes32 indexed txHash, string reason);

    /**
     * @dev 建構函數
     * @param _owners 初始所有者列表
     * @param _threshold 需要的簽名數量
     */
    constructor(address[] memory _owners, uint256 _threshold) {
        require(_owners.length > 0, "No owners");
        require(_threshold > 0 && _threshold <= _owners.length, "Invalid threshold");

        for (uint256 i = 0; i < _owners.length; i++) {
            address owner = _owners[i];
            require(owner != address(0), "Invalid owner");
            require(!owners[owner], "Duplicate owner");

            owners[owner] = true;
            emit OwnerAdded(owner);
        }

        ownerCount = _owners.length;
        threshold = _threshold;
    }

    /**
     * @dev 執行多重簽名交易
     * @param to 目標地址
     * @param value 轉帳金額
     * @param data 調用數據
     * @param signatures 多個簽名(按地址排序)
     */
    function executeTransaction(
        address to,
        uint256 value,
        bytes memory data,
        bytes[] memory signatures
    ) public {
        require(owners[msg.sender], "Not an owner");
        require(to != address(0), "Invalid target");
        require(signatures.length >= threshold, "Not enough signatures");
        require(value <= address(this).balance, "Insufficient balance");

        // 計算交易哈希
        bytes32 txHash = getTransactionHash(to, value, data, nonces[msg.sender]++);

        // 驗證所有簽名
        address[] memory signers = new address[](signatures.length);
        bytes32 messageHash = prefixed(txHash);

        for (uint256 i = 0; i < signatures.length; i++) {
            address signer = recoverSigner(messageHash, signatures[i]);
            require(owners[signer], "Invalid signer");

            // 防止重放攻擊:檢查簽名者是否已使用
            for (uint256 j = 0; j < i; j++) {
                require(signers[j] != signer, "Duplicate signer");
            }
            signers[i] = signer;
        }

        // 執行交易
        (bool success, ) = to.call{value: value}(data);

        if (success) {
            transactions[txHash] = Transaction({
                to: to,
                value: value,
                data: data,
                nonce: nonces[msg.sender] - 1,
                executed: true
            });
            emit TransactionExecuted(txHash, to, value);
        } else {
            emit TransactionFailed(txHash, "Execution failed");
            revert("Transaction failed");
        }
    }

    /**
     * @dev 計算交易哈希
     */
    function getTransactionHash(
        address to,
        uint256 value,
        bytes memory data,
        uint256 nonce
    ) public view returns (bytes32) {
        return keccak256(abi.encodePacked(
            address(this),
            to,
            value,
            keccak256(data),
            nonce,
            block.chainid
        ));
    }

    /**
     * @dev 恢復簽名者地址
     */
    function recoverSigner(bytes32 message, bytes memory signature) public pure returns (address) {
        require(signature.length == 65, "Invalid signature length");

        bytes32 r;
        bytes32 s;
        uint8 v;

        assembly {
            r := calldataload(signature.offset)
            s := calldataload(add(signature.offset, 32))
            v := byte(0, calldataload(add(signature.offset, 64)))
        }

        return ecrecover(message, v, r, s);
    }

    /**
     * @dev 前綴消息哈希
     */
    function prefixed(bytes32 message) public pure returns (bytes32) {
        return keccak256(abi.encodePacked(
            "\x19Ethereum Signed Message:\n32",
            message
        ));
    }

    /**
     * @dev 接收 ETH
     */
    receive() external payable {}
}

5.2 批量交易執行器

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

/**
 * @title BatchExecutor
 * @dev 使用 ECDSA 實現的批量交易執行器
 */
contract BatchExecutor {
    // 所有者
    address public owner;

    // nonce 防止重放
    uint256 public nonce;

    // 已執行的哈希
    mapping(bytes32 => bool) public executed;

    // 批量調用結構
    struct Call {
        address to;
        bytes data;
        uint256 value;
    }

    // 事件
    event BatchExecuted(bytes32 indexed hash, uint256 calls);
    event CallFailed(uint256 indexed index, bytes reason);

    constructor() {
        owner = msg.sender;
    }

    /**
     * @dev 執行批量交易
     * @param calls 調用列表
     * @param deadline 過期時間
     * @param signature 簽名
     */
    function executeBatch(
        Call[] memory calls,
        uint256 deadline,
        bytes memory signature
    ) public {
        require(msg.sender == owner, "Not owner");
        require(block.timestamp <= deadline, "Expired");
        require(calls.length > 0, "No calls");

        // 計算批量交易的哈希
        bytes32 batchHash = getBatchHash(calls, deadline, nonce);

        // 驗證簽名
        require(verifySignature(batchHash, signature), "Invalid signature");
        require(!executed[batchHash], "Already executed");

        executed[batchHash] = true;
        nonce++;

        // 執行每個調用
        uint256 successCount = 0;
        for (uint256 i = 0; i < calls.length; i++) {
            Call memory call = calls[i];
            (bool success, ) = call.to.call{value: call.value}(call.data);

            if (success) {
                successCount++;
            } else {
                emit CallFailed(i, "");
            }
        }

        emit BatchExecuted(batchHash, calls.length);
        require(successCount == calls.length, "Some calls failed");
    }

    /**
     * @dev 計算批量交易哈希
     */
    function getBatchHash(
        Call[] memory calls,
        uint256 deadline,
        uint256 _nonce
    ) public view returns (bytes32) {
        bytes[] memory callHashes = new bytes[](calls.length);

        for (uint256 i = 0; i < calls.length; i++) {
            callHashes[i] = bytes32(keccak256(abi.encode(
                calls[i].to,
                calls[i].value,
                keccak256(calls[i].data)
            )));
        }

        return keccak256(abi.encodePacked(
            address(this),
            keccak256(abi.encodePacked(callHashes)),
            deadline,
            _nonce,
            block.chainid
        ));
    }

    /**
     * @dev 驗證簽名
     */
    function verifySignature(bytes32 hash, bytes memory signature) public pure returns (bool) {
        require(signature.length == 65, "Invalid signature length");

        bytes32 r;
        bytes32 s;
        uint8 v;

        assembly {
            r := calldataload(signature.offset)
            s := calldataload(add(signature.offset, 32))
            v := byte(0, calldataload(add(signature.offset, 64)))
        }

        bytes32 prefixedHash = keccak256(abi.encodePacked(
            "\x19Ethereum Signed Message:\n32",
            hash
        ));

        address signer = ecrecover(prefixedHash, v, r, s);
        return signer != address(0);
    }

    /**
     * @dev 接收 ETH
     */
    receive() external payable {}
}

5.3 時間鎖與延遲執行

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

/**
 * @title TimeLockedExecutor
 * @dev 帶時間鎖的簽名驗證執行器
 */
contract TimeLockedExecutor {
    // 所有者
    address public owner;

    // 時間鎖配置
    uint256 public lockPeriod = 2 days;
    uint256 public executionDeadline = 7 days;

    // 待執行交易
    struct QueuedTransaction {
        address to;
        bytes data;
        uint256 value;
        uint256 queuedAt;
        bool executed;
        bool cancelled;
    }
    mapping(bytes32 => QueuedTransaction) public queued;

    // 事件
    event TransactionQueued(bytes32 indexed hash, address to, uint256 value, uint256 executeAfter);
    event TransactionExecuted(bytes32 indexed hash);
    event TransactionCancelled(bytes32 indexed hash);

    constructor() {
        owner = msg.sender;
    }

    /**
     * @dev 隊列交易(需要簽名)
     */
    function queueTransaction(
        address to,
        bytes memory data,
        uint256 value,
        uint256 after,
        bytes memory signature
    ) public {
        require(msg.sender == owner, "Not owner");

        bytes32 txHash = getTransactionHash(to, data, value, after);
        require(!queued[txHash].executed && !queued[txHash].cancelled, "Already processed");

        // 驗證簽名
        bytes32 messageHash = keccak256(abi.encodePacked(
            "\x19Ethereum Signed Message:\n32",
            keccak256(abi.encodePacked(to, value, keccak256(data), after))
        ));

        (bytes32 r, bytes32 s, uint8 v) = splitSignature(signature);
        address signer = ecrecover(messageHash, v, r, s);
        require(signer == owner, "Invalid signature");

        queued[txHash] = QueuedTransaction({
            to: to,
            data: data,
            value: value,
            queuedAt: block.timestamp,
            executed: false,
            cancelled: false
        });

        emit TransactionQueued(txHash, to, value, after);
    }

    /**
     * @dev 執行隊列的交易
     */
    function executeTransaction(bytes32 txHash) public {
        QueuedTransaction storage tx = queued[txHash];

        require(!tx.executed, "Already executed");
        require(!tx.cancelled, "Cancelled");
        require(block.timestamp >= tx.queuedAt + lockPeriod, "Lock period not elapsed");
        require(block.timestamp <= tx.queuedAt + executionDeadline, "Deadline passed");

        tx.executed = true;

        (bool success, ) = tx.to.call{value: tx.value}(tx.data);
        require(success, "Execution failed");

        emit TransactionExecuted(txHash);
    }

    /**
     * @dev 取消交易
     */
    function cancelTransaction(bytes32 txHash) public {
        require(msg.sender == owner);
        require(!queued[txHash].executed, "Already executed");

        queued[txHash].cancelled = true;
        emit TransactionCancelled(txHash);
    }

    /**
     * @dev 計算交易哈希
     */
    function getTransactionHash(
        address to,
        bytes memory data,
        uint256 value,
        uint256 after
    ) public pure returns (bytes32) {
        return keccak256(abi.encodePacked(
            to,
            value,
            keccak256(data),
            after,
            block.chainid
        ));
    }

    function splitSignature(bytes memory sig)
        internal pure returns (bytes32 r, bytes32 s, uint8 v)
    {
        require(sig.length == 65);

        assembly {
            r := calldataload(sig.offset)
            s := calldataload(add(sig.offset, 32))
            v := byte(0, calldataload(add(sig.offset, 64)))
        }
    }

    receive() external payable {}
}

六、以太坊密碼學升級與未來

6.1 後量子密碼學準備

量子計算機的 Shor 演算法可以在多項式時間內破解 ECDSA,這是未來的潛在威脅。以太坊社區正在評估後量子密碼學遷移策略:

威脅時間線

候選方案

方案優勢劣勢狀態
CRYSTALS-DilithiumNIST 標準、高效簽章較大標準化中
SPHINCS+無狀態、量子安全簽章很大標準化中
混合簽章(ECDSA + PQ)向後兼容增加複雜度概念驗證

6.2 Verkle Trie 與密碼學演進

EIP-7732 將引入 Verkle Trie,標誌著以太坊狀態管理的重要升級:

KZG 承諾機制

Verkle Trees 使用 KZG(Kate-Zaverucha-Goldberg)多項式承諾方案,這是一種不同的密碼學原語:

KZG 承諾特性:
- 承諾大小:48 bytes
- 證明大小:48 bytes
- 驗證效率:O(1) 配對運算
- 信任假設:需要 trusted setup

對開發者的影響

// 適配 Verkle Trie 的存儲模式
contract VerkleOptimized {
    // 連續存儲模式
    // Verkle 對連續存取更友好
    uint256[] public data;

    // 批量讀寫減少開銷
    function batchWrite(uint256[] memory keys, uint256[] memory values) public {
        require(keys.length == values.length);

        for (uint256 i = 0; i < keys.length; i++) {
            data[keys[i]] = values[i];
        }
    }

    // 使用暫態存儲優化
    bytes32 transientStorage;

    function optimizedUpdate(bytes32 value) public {
        // 暂態存储比持久存储更便宜
        assembly {
            tstore(0, value)
        }
    }
}

結論

以太坊的密碼學基礎建立在經過時間檢驗的 secp256k1 橢圓曲線和 ECDSA 簽章演算法之上。深入理解這些數學原理,不僅有助於開發更安全的應用,更能讓用戶正確評估和管理數位資產風險。

2025-2026 年的數據顯示,以太坊網路的密碼學基礎設施持續演進:質押參與率達到 28%,Layer 2 費用降低 85-95%,安全事件較前一年大幅下降。這些進展建立在穩固的密碼學基礎之上。

展望未來,後量子密碼學遷移將是下一個重大挑戰。開發者和用戶應持續關注密碼學發展,確保數位資產安全始終處於最前沿。


參考資源

  1. secp256k1 標準文件. Standards for Efficient Cryptography
  2. ECDSA 規範. ANSI X9.62
  3. RFC 6979 - 確定性 ECDSA
  4. 以太坊黃皮書. ethereum.github.io/yellowpaper
  5. EIP-7732 Verkle Trees. eips.ethereum.org/EIPS/eip-7732
  6. NIST 後量子密碼學標準. csrc.nist.gov/projects/post-quantum-cryptography
  7. Vitalik Buterin 關於後量子以太坊的博文. vitalik.ca
  8. Keccak 規範. keccak.team
  9. KZG 承諾論文
  10. L2Beat 數據. l2beat.com

延伸閱讀與來源

這篇文章對您有幫助嗎?

評論

發表評論

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

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