如何在 Solidity 中验证签名消息
公众号:元壤
元壤:一分钟搭建您专属的 NFT 数字藏品平台
如何使用钱包签署消息,并使用 ecrecover
验证链上签名。
介绍
这是一个实用教程,介绍如何使用 MetaMask 签署消息,然后在链上验证。
这是一个Demo:https ://leon-do.github.io/ecrecover/
签名过程
<button onClick="signMessage()">Sign Message</button>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/web3/1.7.0-rc.0/web3.min.js"
integrity="sha512-/PTXSvaFzmO4So7Ghyq+DEZOz0sNLU4v1DP4gMOfY3kFu9L/IKoqSHZ6lNl3ZoZ7wT20io3vu/U4IchGcGIhfw=="
crossorigin="anonymous"
referrerpolicy="no-referrer"
></script>
<script>
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 });
}
</script>
注意:
- 这是使用
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 游戏
- 游戏开发者创建一个游戏、服务器和智能合约,如果玩家获胜,则该合约会发放奖品。
- 玩家获胜并告诉游戏开发者他们获胜了。
- 游戏服务器向玩家提供一个签名(上面解释过),允许用户进行认领。将此视为优惠券。
- 然后玩家与合约交互并提交签名(优惠券)以领取他们的奖品。
游戏开发者部署智能合约的示例
// 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;
}
}
获胜后,玩家从游戏服务器获得签名(优惠券)以领取奖品
0xb7cf302145348387b9e69fde82d8e634a0f8761e78da3bfa059efced97cbed0d2a66b69167cafe0ccfc726aec6ee393fea3cf0e4f3f9c394705e0f56d9bfe1c91c
玩家在 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);
然后玩家可以与智能合约交互以领取他们的奖品。更多信息在这里。
true 表示玩家收到了他们的奖品
结论
还有其他问题吗?可以在评论区留言,我们继续探讨。
原文链接:https://blog.chainsafe.io/how-to-verify-a-signed-message-in-solidity-6b3100277424