在ethers中能否嵌套发送不同签名者的已签名消息以实现联合签名?
嵌套联合签名方案实现说明
Ethers.js 完全支持在已签名消息内嵌套其他签名者签署的已签名消息,这种方案可以实现你需要的联合签名能力,同时将大部分编码、拼接逻辑转移到链下执行,有效降低链上gas消耗,具体实现逻辑如下:
链下签名流程
- 签名本身本质是对任意字节内容的哈希运算后再做私钥签名,所以你可以将第一个签名者生成的签名、原始消息、签名地址作为字段,打包成新的待签名内容,再交给第二个签名者做签名即可。
- 更推荐使用 EIP-712 结构化签名 实现,不需要手动拼接字符串,自定义结构体时直接将内层签名相关字段作为结构体成员即可,编码和解码都更安全,也更容易避免哈希不一致的问题。
Ethers 端示例代码(v6版本)
// 第一步:生成内层签名 const innerBusinessData = { orderId: 12345, amount: ethers.parseEther("1") }; // 普通签名直接调用signMessage即可,复杂场景推荐替换为EIP-712签名 const innerSig = await signerA.signMessage(ethers.encodeBytes32String(JSON.stringify(innerBusinessData))); const innerSignerAddr = await signerA.getAddress(); // 第二步:构造外层待签名内容,嵌套内层的所有数据 const outerSignContent = { innerData: innerBusinessData, innerSig: innerSig, innerSigner: innerSignerAddr, deadline: Math.floor(Date.now() / 1000) + 86400 }; const outerSig = await signerB.signMessage(ethers.encodeBytes32String(JSON.stringify(outerSignContent))); // 最终将上述所有字段提交到链上做校验即可
链上校验逻辑
智能合约侧不需要做复杂的编码运算,只需要按照约定的编码规则依次校验各层签名合法性即可:
- 先取出内层原始业务数据,按照约定规则生成哈希,调用
ecrecover(或OpenZeppelin的ECDSA库)还原内层签名地址,和提交的内层签名地址比对 - 内层校验通过后,再将内层所有数据和外层字段拼接生成哈希,同理校验外层签名的合法性
- 所有签名校验通过后即可执行后续业务逻辑
合约侧伪代码示例
// 引入OpenZeppelin的ECDSA工具库 import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; contract NestedSigVerifier { using ECDSA for bytes32; function verifyCoSign( bytes calldata innerData, bytes calldata innerSig, address innerSigner, uint256 deadline, bytes calldata outerSig, address outerSigner ) external view returns (bool) { // 校验有效期 require(deadline > block.timestamp, "signature expired"); // 校验内层签名 bytes32 innerHash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", keccak256(innerData))); if (innerHash.recover(innerSig) != innerSigner) return false; // 校验外层签名 bytes32 outerHash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", keccak256(abi.encode(innerData, innerSig, innerSigner, deadline)))); return outerHash.recover(outerSig) == outerSigner; } }
注意事项
- 必须严格对齐链下和链上的消息编码规则,包括字符串拼接顺序、编码格式、EIP-712的domain参数等,否则会出现签名校验失败的问题
- 该方案可以扩展到任意多签名者的联合签名场景,链上只需要增加对应次数的
ecrecover校验即可,gas消耗增长极低 - 普通
signMessage生成的签名前缀是固定的\x19Ethereum Signed Message:\n,链上校验时记得要加上该前缀再算哈希,避免校验不通过
内容的提问来源于stack exchange,提问作者rami1988
相关产品推荐
相关产品推荐

