# solidity关于ecrecover函数在签名场景的应用

By [yunxu](https://paragraph.com/@02af61) · 2022-11-01

---

在solidity中有一个函数,这个函数主要使用签名的`v,r,s`值通过`hash`值还原地址信息，通常应用场景用于验证签名及内容是否为该地址签名。

    ecrecover(bytes32 hash,uint8 v,bytes32 r,bytes32 s)  returns (address)
    

比如以太坊智能合约中，多签钱包构造交易需要几个重要的参数如下：

**参数:**

*   `to` 发送地址;
    
*   `value` 发送数额;
    
*   `data` data参数;
    
*   `nonce` 交易nonce;
    
*   `chainid` 当前所属链的ID，防止重放;
    

通常钱包会将这几个重要参数通过`keccak256`计算唯一摘要hash后再让签名人使用私钥签名。

**交易参数摘要hash计算:**

    function encodeTransactionData(address to,uint256 value,bytes memory data,uint256 _nonce,uint256 chainid) internal pure returns(bytes32 safeTxhash){
        safeTxhash=keccak256(
                 abi.encode(to,
                 value,
                 keccak256(data),
                 _nonce,
                 chainid)
        );
    }
    

通过上面的参数组成再计算`keccak256`的方式可以得到最终的交易`hash`，这个hash还需要签名人签名才可以生效。

> eip191 要求对消息签名需要字符串 "\\x19Ethereum Signed Message:\\n32" + message 的格式再通过keccak256计算摘要计算出最终签名内容。

**python-web3 签名代码:**

    def msg_signed(self,to,value,data,nonce,chain):
        from eth_account.messages import encode_defunct
        from eth_abi import encode_abi
        # bytes通过keccak加密后会变成固定字节组bytes32
        data = self.w3.solidityKeccak(['bytes'],[data]) 
              abi_codec_params=encode_abi(("address","uint256","bytes32","uint256","uint256"),(
            self.w3.toChecksumAddress(to),
            value,
            data,
            nonce,
            chain
        ))
    
        msg = self.w3.keccak(abi_codec_params)
        msg_hash=encode_defunct(msg)
        print('msg:',msg_hash)
        signed_msg=self.w3.eth.account.sign_message(msg_hash,private_key)
        print('sign:',signed_msg)
        print('r:',hex(signed_msg.r))
        print('s:',hex(signed_msg.s))
        print('v:',hex(signed_msg.v))
    

签名消息构造. "\\x19Ethereum Signed Message:\\n32" + message

    msg: SignableMessage(version=b'E', header=b'thereum Signed Message:\n32', body=HexBytes('0xa5fecbdb4eb5e3309c2459598d9a1db2089dde1cc879a9fc3c2f892ab420aaac'))
    

签名完成后的计算结果,这里也包含了`v,r,s`,signature的属性值其实就是`hex(r)+hex(s)+hex(v)`

    sign: SignedMessage(messageHash=HexBytes('0xa96b0deea3209cba1da6f5bba951472cf43832198063f8b2f366a785f9ee9b9c'), r=10925239203571165987254287811719770579579300206557368452150218380525237128433, s=19985186572457285387066880993246285566249382386279146193757115445100796650173, v=28, signature=HexBytes('0x1827775c84240dcdac526def5a3c22338fe9db89ff38692e50c2ff931fb74cf12c2f36f9926acf352701e2adae5c7aed93d894777765c82fc3b827b71b5f22bd1c'))
    

r,s,v值

    r: 0x1827775c84240dcdac526def5a3c22338fe9db89ff38692e50c2ff931fb74cf1
    s: 0x2c2f36f9926acf352701e2adae5c7aed93d894777765c82fc3b827b71b5f22bd
    v: 
    0x1c
    

**智能合约中ecrecover验证签名信息**

通过上面两步骤分别生成了签名的内容并对这个内容进行签名得到签名后的摘要hash，那么接下来就要在智能合约中完成对签名的有效性做验证。

还是回到多签钱包案例中，我们需要对python签名的内容完成验证。

    # 构造相同的签名内容,再尝试使用签名还原,还原成功后可以得到签名人地址。
    currentOwner = 
    ecrecover(
    keccak256(
    abi.encodePacked("\x19Ethereum Signed Message:\n32",dataHash)
    ),v,r,s);
    require(currentOwner==msg.sender,"err");

---

*Originally published on [yunxu](https://paragraph.com/@02af61/solidity-ecrecover)*
