Solidity学习-数字签名


一、数字签名的作用?

用于签署交易和批准交易。现在流行链下(消息)签名,链上验证。

二、为什么要用数字签名?

  • 证明钱包是本人的,就是本人有这个私钥
  • 确保签名过程中这个信息没有被篡改,因为是hash运算
  • 可以帮项目方省gas费

三、前端生成签名

  ...

四、链上验证签名

  1. 引入openzeppelin ECDSA椭圆曲线签名算法
  2. 对签名信息进行abi编码
  3. 进行keccak256 Hash运算
  4. 添加前缀,如:"\x19Ethereum Signed Message:\n32",可以将计算出的以太坊特定的签名。这样可以防止DApp作恶
  5. 从签名恢复地址

五、合约示例代码

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9;

import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";

contract VerifySig {

    address owner;
    using ECDSA for bytes32; //把库 ECDSA 附加到 bytes32 类型

    constructor (){
        owner = msg.sender;
    }

    function isMessageValid(bytes memory signature, string memory message) public view returns(address, bool){
        //将message进行abi编码
        bytes memory abiEncode = abi.encodePacked(message);
        //进行keccak256 hash运算,生成定长的值
        bytes32 messageHash =  keccak256(abiEncode);
        //添加前缀
        bytes32 ethSignMessageHash = ECDSA.toEthSignedMessageHash(messageHash);
        //从签名中恢复地址
        address singer = ECDSA.recover(ethSignMessageHash, signature);

        if (owner == singer){
            return (singer, true);
        } else {
            return (singer, false);
        }
    }
}

 六、合约的测试

部署合约后,我们开始进行测试。

  • 设置签名内容 

  根据此网页说明,使用metamask插件进行对消息的签名;

  我MetaMask Account1地址: "0x9848Ab0Fc1E0359975AdfEA57a7968058ab76b57";

  "abc"的 hash: "0x4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45";

  • 验证签名是否正确

  调用我们合约的函数 isMessageValid()进行验证:

//test 1:
decoded input    {
"bytes signature": "0xedefd08b275cabb3eecef6b58c6d21ee97de10fb11947b2b28d2af544090e1cc4f7f5c5c1bd6e8a6ffe515233f92ab62d981f7f927b3bfb1ba7330c160ea85b51c",
"string message": "abc"
}
decoded output    {
"0": "address: 0x9848Ab0Fc1E0359975AdfEA57a7968058ab76b57",
"1": "bool: true"
}

//test 2:
decoded input    {
"bytes signature": "0xedefd08b275cabb3eecef6b58c6d21ee97de10fb11947b2b28d2af544090e1cc4f7f5c5c1bd6e8a6ffe515233f92ab62d981f7f927b3bfb1ba7330c160ea85b51c",
"string message": "abcd"
}
decoded output    {
"0": "address: 0x389bcAbEffFB18e9d175757Ad057573FD043F1f0",
"1": "bool: false"
}

//test 3:输入错误的signature
call to VerifySig.isMessageValid
call to VerifySig.isMessageValid errored: execution reverted: ECDSA: invalid signature
{
"originalError": {
"code": 3,
"data": "0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001845434453413a20696e76616c6964207369676e61747572650000000000000000",
"message": "execution reverted: ECDSA: invalid signature"
}
}