Solidity

無法使用 ethers.js 驗證經過身份驗證的消息

  • October 15, 2021

我需要能夠從契約中檢索文件,讓使用者錢包對其進行簽名,然後將其發送回契約並驗證簽名。

這是我讓 address0 在客戶端簽名的方法:

  let message : string = "check this message signed by account0";
  let messageHash = keccak256(utils.toUtf8Bytes(message));
  let signature = await address0.signMessage(messageHash);
  await hm.connect(address0).verifyMessage(message, signature);

這是我契約中的驗證器:

  function verifyMessage(string memory message, 
     bytes memory signature) 
     public view  returns(bool) {
     
       //hash the plain text message
       bytes32 messagehash =  keccak256(bytes(message));
       //hash the prefix and messagehash together   
       bytes32 messagehash2 = keccak256(abi.encodePacked("x19Ethereum Signed Messsage:\\n32", messagehash));
       //extract the signing contract address
       address signeraddress = ECDSA.recover( messagehash2, signature);
       if (msg.sender==signeraddress) {
           //The message is authentic
           return true;
       } else {
           //msg.sender didnt sign this message.
           return false;
       }
   }

遺憾的是,ECDSA.recover 為 signeraddress 返回的值不是 account0 的地址,儘管經過大量實驗,我仍無法得出消息發送者的正確地址。

將不勝感激任何指針。

我能夠從 Openzeppelin 上的人們那裡得到答案。

萬一任何其他機構遇到這種情況,一個問題是計算雜湊和在客戶端計算簽名的方式

代替

  let messageHash = keccak256(utils.toUtf8Bytes(message));

採用

   let messageHash = ethers.utils.solidityKeccak256(['string'], [message]);

而不是

   let signature = await address0.signMessage(messageHash);

採用

    let signature = await address0.signMessage(ethers.utils.arrayify(messageHash));

在伺服器端,可以使用 `ECDSA.toEthSignedMessageHash() 更簡單地添加前綴,如下面的解決方案所示:

using ECDSA for bytes32; 

function verifyMessage(string memory message, bytes memory signature) public view  returns(address, bool) {
       //hash the plain text message
       bytes32 messagehash =  keccak256(bytes(message));
      
       address signeraddress = messagehash.toEthSignedMessageHash().recover(signature);
             
       if (msg.sender==signeraddress) {
           //The message is authentic
           return (signeraddress, true);
       } else {
           //msg.sender didnt sign this message.
           return (signeraddress, false);
       }
   }
 

   

引用自:https://ethereum.stackexchange.com/questions/111549