Solidity RSA 签名详解:在 Gas 效率上击败 ECDSA 与默克尔树

理解用 RSA 做空投/预售白名单的原理、modexp 预编译、与 ECDSA/默克尔树的 Gas 对比

6 分钟阅读
Solidity RSA 签名详解:在 Gas 效率上击败 ECDSA 与默克尔树

Solidity RSA 签名详解:在 Gas 效率上击败 ECDSA 与默克尔树

目录


一、概览

本文把 RSA 签名作为 ECDSA 和默克尔树之外、用于预售白名单和空投的更省 Gas 的替代方案。核心洞察:借助模幂预编译(0x05),RSA 验证在链上出乎意料地便宜。

二、RSA 签名如何工作

RSA 依赖大整数分解的计算困难性

密钥生成

  • 选两个大素数 pq
  • 计算 n = p × q(公共模数);
  • 选小素数 e(可硬编码为 3);
  • 计算 d = e⁻¹ mod t,其中 t = (p-1)(q-1)(私钥);
  • 公钥 (n, e),私钥 d

签名:签名者对消息 m 哈希后计算 s = h(m)^d mod n

验证:验证者检查 s^e mod n == hash(消息)

用于白名单:签名变成 s = buyerAddress^d mod n(项目方对买家地址签名),链上验证 msg.sender == s^e mod n。买家提交 s,合约算 s^e mod n 看是否等于自己的地址——等于就说明项目方授权过这个地址。

三、Gas 成本对比

方案Gas
ECDSA29,293(不含交易发起 8,293)
默克尔树30,517+(随树增大)
RSA-89626,850
RSA-102427,033
RSA-204829,271

RSA-896 比 ECDSA 省约 2,500 gas,比默克尔树省更多。

四、为什么 RSA 能赢 ECDSA

效率来自模幂预编译(0x05)的定价方式

预编译给”把数字升到低次幂”赋予很低的价格,执行成本只有几百 gas。

因为 e 可以是 3 这样的小数,s^3 mod n 极便宜。主要的 Gas 开销其实在 calldata:1024 位密钥下签名占 128 字节,按 16 gas/字节 = 2,048 gas。但即便如此,总量仍低于 ECDSA 的总开销。

五、技术实现:modexp 预编译

以太坊原生只支持 256 位运算,RSA 需要大数运算(1024/2048 位),所以用 EIP-198 引入的 0x05 预编译做模幂:

  1. 把 base、exponent、modulus 按 ABI 编码格式载入内存(先三个长度,再三个值);
  2. 调用地址 0x05;
  3. 从内存取结果。

(具体调用格式见上一篇预编译的 modExp 例子。)

六、公钥存储难题与变形合约

难题:1024 位 RSA 公钥需要 4 个存储槽,用 SLOAD 读取要 8,400 gas——已经超过 ECDSA 的优势了。

解法:把公钥存在合约的字节码里,而不是存储。用 EXTCODECOPY 读取只要 2,600 gas,远低于 8,400。

但白名单需要可作废能力(换一批白名单)。字节码不可变怎么办?用变形合约(metamorphic contract)

  • 用 CREATE2 部署一个确定性地址的合约;
  • 合约字节码含:(a) 自毁机制 + (b) RSA 公钥;
  • 把合约地址存在 immutable 变量;
  • 用 EXTCODECOPY 读公钥(2,600 gas);
  • 作废时:触发自毁,在同一地址重新部署含新公钥的合约。

地址不变是因为 CREATE2 取决于初始化代码而非运行时代码——这就实现了”变形”(同地址换内容)。

注意:EIP-6780 后 selfdestruct 行为受限,变形合约模式在新版以太坊上可能不再可行,需用其他方式(如代理)实现可作废。

七、访问列表优化

EIP-2930 访问列表(Module 9)可再省约 100 gas。因为这个设计要读外部合约(存公钥的那个),可以预声明会访问的外部地址来预热(呼应访问列表篇)。

八、密钥长度与安全

密钥越大,分解难度指数级增长:

  • 已被分解的最大 RSA 密钥:829 位
  • 分解成本:约 940 万美元(云超算);
  • 行业共识:896 位对大多数价值 < $100,000 的代币足够安全。

理由:每多 1 位,数字规模翻倍。攻击者的成本收益分析使大多数 NFT/代币预售成为不划算的攻击目标。但高价值场景应用 2048 位。

九、要点与局限

要和现有方案竞争,方案必须:

  1. 能作废(像 ECDSA 换签名地址、默克尔树换根);
  2. 最小化卖方开销(避免基于 mapping 白名单的百万级 gas);
  3. 成本低于 8,200 gas(含存储读取);
  4. 安全参数

局限:文章明确这是概念验证(PoC),建议生产前谨慎。它未处理 PKCS 填充等标准化问题——真实 RSA 签名需要正确的填充方案防止伪造,这是 PoC 省略但生产必须补的。

十、总结

  • RSA 用 s^e mod n == hash(地址) 验证白名单,e 取小值(如 3)让模幂极便宜;
  • RSA-896 约 26,850 gas,省过 ECDSA(29,293)和默克尔树(30,517+);
  • 主要开销在 calldata(签名 128 字节),计算靠 0x05 modexp 预编译;
  • 公钥存字节码(EXTCODECOPY 2,600)而非存储(SLOAD 8,400);
  • 可作废靠变形合约(CREATE2 + selfdestruct 重部署,注意 EIP-6780 限制);
  • 是 PoC,未处理 PKCS 填充,生产需谨慎。

十一、动手练习项目:RSA 白名单空投 RsaAllowlist

项目目标

实现一个用 RSA 签名做白名单的空投合约,链下用 RSA 私钥对地址签名、链上用 modexp 预编译验证,并与 ECDSA 版本对比 Gas。部署到 Sepolia。

合约要求

1. 链下密钥与签名脚本(Python/JS)

  • 生成 RSA 密钥对(e=3 或 65537,n 取 1024 位);
  • 对白名单里每个地址签名:s = address^d mod n(或对 hash(address) 签,更安全);
  • 输出每个地址的签名 s(128 字节)。

2. RsaAllowlist.sol

  • bytes public modulus;(n,存字节码或 immutable 引用——简化版可先存储,进阶再优化);
  • uint256 public constant E = 3;(或 65537);
  • claim(bytes calldata signature) external
    • 用 modexp 预编译算 recovered = signature^E mod n
    • require recovered 对应的地址 == msg.sender(或 == hash(msg.sender));
    • mapping claimed 防重复,发空投;
  • verify(address account, bytes calldata sig) public view returns (bool):暴露验证逻辑便于测试。

3. EcdsaAllowlist.sol(对照)

  • 用 ecrecover + 项目方签名地址做同样的白名单,便于 Gas 对比。

测试要求(Foundry)

  1. test_ValidSignatureClaims:用链下生成的合法 RSA 签名,对应地址能领取;
  2. test_InvalidSignatureReverts:错误签名 verify 返回 false、claim revert;
  3. test_WrongAccountReverts:A 的签名给 B 用,revert(签名绑定地址);
  4. test_NoDoubleClaim:重复领取 revert;
  5. test_ModExpCorrect:单测 signature^E mod n 计算正确(用已知小数验证 modexp 逻辑);
  6. test_GasVsEcdsa:对比 RSA claim 和 ECDSA claim 的 Gas,记录差异(RSA-1024 应在 27,000 量级);
  7. test_DifferentKeySizes:用 896/1024/2048 位密钥各测一次,对比 calldata 增大带来的 Gas 变化。

Sepolia 部署与验证步骤

  1. 链下生成密钥、对你的测试地址签名;
  2. 部署 RsaAllowlist(传入 modulus n)和 EcdsaAllowlist;
  3. 用 RSA 签名调 claim,Etherscan 上观察 Gas;
  4. 用 ECDSA 版本领同样的空投,对比 Gas;
  5. 故意用别人的签名尝试,观察 revert。

进阶挑战(可选)

  • 把公钥从存储改到字节码(部署一个只含公钥的合约,用 EXTCODECOPY 读),实测 SLOAD(8400) vs EXTCODECOPY(2600) 的 Gas 差;
  • 加上正确的 PKCS#1 v1.5 填充验证,理解为什么裸 RSA(无填充)不安全;
  • 写注释回答:为什么 e 取小值(3)能让验证便宜?为什么 calldata 是 RSA 方案的主要 Gas 开销?

💬 评论