技术:如何在 Solidity 中验证签名消息

Posted chinadefi

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了技术:如何在 Solidity 中验证签名消息相关的知识,希望对你有一定的参考价值。

技术:如何在 Solidity 中验证签名消息

介绍

这是一个使用MetaMask签名消息,然后在链上进行验证的实用教程。

这里有一个demo:https://leon-do.github.io/ecrecover/

签名

Sign Message



  async function signMessage() 
    if (!window.ethereum) return alert("Please Install Metamask");

    // connect and get metamask account
    const accounts = await ethereum.request( method: "eth_requestAccounts" );

    // message to sign
    const message = "hello";
    console.log( message );

    // hash message
    const hashedMessage = Web3.utils.sha3(message);
    console.log( hashedMessage );

    // sign hashed message
    const signature = await ethereum.request(
      method: "personal_sign",
      params: [hashedMessage, accounts[0]],
    );
    console.log( signature );

    // split signature
    const r = signature.slice(0, 66);
    const s = "0x" + signature.slice(66, 130);
    const v = parseInt(signature.slice(130, 132), 16);
    console.log( r, s, v );
  

需注意:

  • 这是使用personal_sign方法来防止意外的交易支出。这将与智能合约(以下)中发现的\\x19Ethereum Signed Message:\\n32前缀相关。
  • 这是对散列消息进行签名。

签字

下面是验证签名所需的信息。可在智能合约中使用它。

hashedMessage = 0x1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8
r = 0xb7cf302145348387b9e69fde82d8e634a0f8761e78da3bfa059efced97cbed0d
s = 0x2a66b69167cafe0ccfc726aec6ee393fea3cf0e4f3f9c394705e0f56d9bfe1c9
v = 28

智能合约

创建Solidity合约。

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

contract Verify 

    function VerifyMessage(bytes32 _hashedMessage, uint8 _v, bytes32 _r, bytes32 _s) public pure returns (address) 
        bytes memory prefix = "\\x19Ethereum Signed Message:\\n32";
        bytes32 prefixedHashMessage = keccak256(abi.encodePacked(prefix, _hashedMessage));
        address signer = ecrecover(prefixedHashMessage, _v, _r, _s);
        return signer;
    


需注意:

  • 注意前缀,\\x19Ethereum Signed Message:\\n32是验证签名消息所必需的。
  • 前缀随消息散列。
  • Ecrecover应返回签名者的地址。

在solidity中验证

输入散列消息和签名(r, s, v)来验证地址。

用例:Unity Gaming

  • 游戏开发者创建一个游戏、服务器和智能合约,如果玩家获胜,则该合约会发放奖品。
  • 赢家告诉游戏开发者他们赢了。
  • 游戏服务器为玩家提供一个签名(上面解释过),允许用户认领。把它当作优惠券。
  • 然后,玩家与合约互动,提交签名(优惠券)来领取奖品。

游戏开发者部署智能合约的例子:

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

// game developer depoloys contract
contract Verify 

    // game developer's account
    address public owner = 0xdD4c825203f97984e7867F11eeCc813A036089D1;

    // player claims price
    function claimPrize(bytes32 _hashedMessage, uint8 _v, bytes32 _r, bytes32 _s) public view returns (bool) 
        bytes memory prefix = "\\x19Ethereum Signed Message:\\n32";
        bytes32 prefixedHashMessage = keccak256(abi.encodePacked(prefix, _hashedMessage));
        address signer = ecrecover(prefixedHashMessage, _v, _r, _s);

        // if the signature is signed by the owner
        if (signer == owner) 
            // give player (msg.sender) a prize
            return true;
        

        return false;
    

获胜后,玩家会从游戏服务器获得一张签名(优惠券)来领取奖品。

0 xb7cf302145348387b9e69fde82d8e634a0f8761e78da3bfa059efced97cbed0d2a66b69167cafe0ccfc726aec6ee393fea3cf0e4f3f9c394705e0f56d9bfe1c91c

玩家在Unity中解析签名:

string signature = "0xb7cf302145348387b9e69fde82d8e634a0f8761e78da3bfa059efced97cbed0d2a66b69167cafe0ccfc726aec6ee393fea3cf0e4f3f9c394705e0f56d9bfe1c91c";
string r = signature.Substring(0, 66);
string s = "0x" + signature.Substring(66, 64);
int v = int.Parse(signature.Substring(130, 2), System.Globalization.NumberStyles.HexNumber);

然后,玩家可以与智能合约进行交互,领取奖品。

Source:https://www.web3.university/article/how-to-verify-a-signed-message-in-solidity

关于

ChinaDeFi - ChinaDeFi.com 是一个研究驱动的DeFi创新组织,同时我们也是区块链开发团队。每天从全球超过500个优质信息源的近900篇内容中,寻找思考更具深度、梳理更为系统的内容,以最快的速度同步到中国市场提供决策辅助材料。

Layer 2道友 - 欢迎对Layer 2感兴趣的区块链技术爱好者、研究分析人与Gavin(微信: chinadefi)联系,共同探讨Layer 2带来的落地机遇。敬请关注我们的微信公众号 “去中心化金融社区”

Web3 签名验证失败 - ethers.js

【中文标题】Web3 签名验证失败 - ethers.js【英文标题】:Web3 signature verification is failing - ethers.js 【发布时间】:2020-01-22 10:37:25 【问题描述】:

我正在尝试使用ethers.js 在链外创建签名消息,并使用ecrecover 在链上验证该消息。我正在从我的元掩码钱包中签署正确的消息,并将该签名中的 r、s 和 v 传递到 ecrecover,但没有与我的元掩码钱包匹配。

我的 Solidity 代码应该适用于带前缀或不带前缀的签名。

这是我用来验证签名的合同:

pragma solidity ^0.5.0;
contract SignatureVerifier 
    /// @dev Signature verifier
    function isSigned(address _address, bytes32 messageHash, uint8 v, bytes32 r, bytes32 s) public pure returns (bool) 
        return _isSigned(_address, messageHash, v, r, s) || _isSignedPrefixed(_address, messageHash, v, r, s);
    

    /// @dev Checks unprefixed signatures.
    function _isSigned(address _address, bytes32 messageHash, uint8 v, bytes32 r, bytes32 s)
        internal pure returns (bool)
    
        return ecrecover(messageHash, v, r, s) == _address;
    

    /// @dev Checks prefixed signatures.
    function _isSignedPrefixed(address _address, bytes32 messageHash, uint8 v, bytes32 r, bytes32 s)
        internal pure returns (bool)
    
        bytes memory prefix = "\x19Ethereum Signed Message:\n32";
        return _isSigned(_address, keccak256(abi.encodePacked(prefix, messageHash)), v, r, s);
    
 

来自 ethers,这是我用来生成签名的代码(简化版本),我将其用作 _isSigned 函数调用的参数。

    let provider = new ethers.providers.Web3Provider(window.ethereum)
    let signer = provider.getSigner()
    let dataHash = '0x952d17582514a6a434234b10b8e6b681b6006c8ed225d479fa3db70828b9cd60'
    let signature = await signer.signMessage(dataHash)
    let sigBreakdown = ethers.utils.splitSignature(signature)
    console.log(sigBreakdown)

这提示我在 matamask 中签名,我在其中签署了正确的 dataHash。然后它记录一个 r、s 和 v 值。

在混音中,我调用 isSigned,传递我的元掩码地址、dataHash (0x952...d60) 以及 r、s 和 v 值,期望结果为 true,但它返回 false .我对这里的solidity 代码和javascript 代码相当有信心,但显然我遗漏了一些东西。非常感谢您的帮助!

【问题讨论】:

【参考方案1】:

想通了!原来,我签署的是string dataHash 而不是bytes dataHash 的值。我能够通过添加来获得验证:

let bytesDataHash = ethers.utils.arrayify(dataHash)

并签名bytesDataHash 而不是dataHash :) 像往常一样,Richard Moore 在 github 上回答了一个类似的问题,因为那个人太不可思议了: https://github.com/ethers-io/ethers.js/issues/245#issuecomment-408706606

【讨论】:

以上是关于技术:如何在 Solidity 中验证签名消息的主要内容,如果未能解决你的问题,请参考以下文章

图解密码技术笔记数字签名——消息到底是谁写的

图解密码技术笔记数字签名——消息到底是谁写的

第153篇 Solidity 中支付通道的实现

第153篇 Solidity 中支付通道的实现

无法验证由 sol-wallet-adapter 签名的消息

接收方无法验证签名的 XML 消息