Solidity
無法驗證簽名
我正在嘗試為即將到來的項目掌握 Web3 和 Solidity 的竅門。所以現在我只是在擺弄一些不同的東西,而且我一直堅持驗證簽名。我可以簽名,但我無法驗證。在過去的兩個晚上,我每晚都在嘗試 3-4 個小時,但我根本無法弄清楚問題是什麼,所以現在我爬到這裡尋求幫助。
我在這裡包含了我的客戶端和契約程式碼。我希望有人能指出我正確的方向。我確實從
verify
對契約的呼叫中得到了結果,但結果始終是false
.契約程式碼或多或少是從https://solidity-by-example.org/signature/複製/粘貼的,我知道這有點過時了。
以下範例中的所有地址和密鑰均來自測試網。
客戶端,使用 MetaMask:
const contractAddress = '0x94F9f9d16f2cb8542BE31D6fA99471Ea8251D7eC'; const sign = async (address) => { const nonce = Math.floor(Math.random() * 10000000000) + 1; console.log(nonce); const data = [{"address": address}, {"uint256": 1000000}, {"uint256": nonce}, {"address": contractAddress}]; console.log(window.web3.eth.accounts.sign(generateHash(data), '61323f1a2911e70573be49819e68a7dbffa10216135bb06171e596a4f89bfd79')); } const verify = async (address, nonce, signature) => { const contractInstance = new window.web3.eth.Contract( ABI[0].abi, contractAddress, { from: address, } ); const result = contractInstance.methods.verify(contractAddress, address, 1000000, nonce, signature).call(function (error, result) { console.log(result); }); } const generateHash = (data) => { const keys = data.map((item) => { return Object.keys(item)[0]; }); const values = data.map((item) => { return Object.values(item)[0]; }); return "0x" + ethereumjs.soliditySHA3(keys, values).toString("hex"); } //sign('0x93E16885EE732BFF281285efe2f2F46Dc92590C5'); verify('0x93E16885EE732BFF281285efe2f2F46Dc92590C5', 3421724946, '0x7eba658b197add3604c7d25c9305507cc67dd7a9298862fb4fcf2a2bcfd274d6039681008fa7a6201194258dee17eed4d709ea704427135cb80731a44908d29a1b');
//SPDX-License-Identifier: Unlicense pragma solidity ^0.8.0; import "hardhat/console.sol"; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import '@openzeppelin/contracts/access/Ownable.sol'; contract Moonboi is ERC20, Ownable { constructor() ERC20("Moonboi Token", "MBT") {} function getMessageHash( address _to, uint _amount, uint _nonce ) public view returns (bytes32) { return keccak256(abi.encodePacked(_to, _amount, _nonce)); } function getEthSignedMessageHash(bytes32 _messageHash) public pure returns (bytes32) { return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", _messageHash)); } function verify( address _signer, address _to, uint256 _amount, uint256 _nonce, bytes memory signature ) public view returns (bool) { bytes32 messageHash = getMessageHash(_to, _amount, _nonce); bytes32 ethSignedMessageHash = getEthSignedMessageHash(messageHash); return recoverSigner(ethSignedMessageHash, signature) == _signer; } function recoverSigner(bytes32 _ethSignedMessageHash, bytes memory _signature) public pure returns (address) { (bytes32 r, bytes32 s, uint8 v) = splitSignature(_signature); return ecrecover(_ethSignedMessageHash, v, r, s); } function splitSignature(bytes memory sig) public pure returns (bytes32 r, bytes32 s, uint8 v) { require(sig.length == 65, "invalid signature length"); assembly { r := mload(add(sig, 32)) s := mload(add(sig, 64)) v := byte(0, mload(add(sig, 96))) } return (r, s, v); } }
我弄清楚我做錯了什麼,所以我會在這裡添加我的發現作為答案,以防其他人犯了同樣的錯誤。一旦我意識到發生了什麼,問題就很明顯了。
在呼叫智能合約方法時
verify
,我將智能合約地址作為簽名者傳入,而實際上簽名者是智能合約的所有者。所以在這個方法中:
const verify = async (address, nonce, signature) => { const contractInstance = new window.web3.eth.Contract( ABI[0].abi, contractAddress, { from: address, } ); contractInstance.methods.verify(contractAddress, address, 1000000, nonce, signature).call(function (error, result) { console.log(error); console.log(result); }); }
..
verify
方法中的第一個參數應該是契約所有者,如下所示:const contractOwner = '0xa914E79148447A9affe1bCC4A72859e26c0f75AB'; contractInstance.methods.verify(contractOwner, address, 1000000, nonce, signature).call(function (error, result) { console.log(error); console.log(result); });
我犯的另一個錯誤是私鑰需要以 為前綴
0x
,所以:window.web3.eth.accounts.sign(generateHash(data), '61323f1a2911e70573be49819e68a7dbffa10216135bb06171e596a4f89bfd79')
不會奏效的。我應該進去
0x61323f1a2911e70573be49819e68a7dbffa10216135bb06171e596a4f89bfd79
。