Solidity

驗證簽名數據

  • September 11, 2018

我正忙於嘗試使用 ecrecover() 恢復地址。在向契約提供簽名數據時,我無法恢復正確的地址。

我正在測試的契約如下所示:

pragma solidity ^0.4.24;

contract VerifyTest {
   // https://ethereum.stackexchange.com/a/15911
   function verifyMessage(bytes32 messageHash, uint8 v, bytes32 r, bytes32 s) private view returns (bool) {
       bytes memory hashPrefix = "\x19Ethereum Signed Message:\n32";
       bytes32 prefixedHash = keccak256(abi.encodePacked(hashPrefix, messageHash));
       return ecrecover(prefixedHash, v, r, s) == msg.sender;
   }

   function testBuyOrder(uint256 orderTotal, address tokenContract, uint8 v, bytes32 r, bytes32 s) public view returns (bool) {
       bytes32 messageHash = keccak256(abi.encodePacked(orderTotal, tokenContract));
       return verifyMessage(messageHash, v, r, s);
   }
}

編譯後,將生成以下 ABI 對象:

[
   {
       "constant": true,
       "inputs": [
           {
               "name": "orderTotal",
               "type": "uint256"
           },
           {
               "name": "tokenContract",
               "type": "address"
           },
           {
               "name": "v",
               "type": "uint8"
           },
           {
               "name": "r",
               "type": "bytes32"
           },
           {
               "name": "s",
               "type": "bytes32"
           }
       ],
       "name": "testBuyOrder",
       "outputs": [
           {
               "name": "",
               "type": "bool"
           }
       ],
       "payable": false,
       "stateMutability": "view",
       "type": "function"
   }
]

我可以testBuyOrder()使用下面的一小段 Javascript 成功呼叫該函式:

// Trying to keep it concise; assume we have a functioning web3 instance...

const signTestAddr = "[ADDRESS_OF_DEPLOYED_CONTRACT]";
const signTestABI = [...]; // ABI quoted above...

testSignMessage = () => {
   console.log(web3.version.api);

   let tokenAddr = "0x128Df2a07Dc41E034bD9a3CEaddDc0341250a6C8";
   let verifyTest = web3.eth.contract(signTestABI).at(signTestAddr);
   let orderTotal = web3.fromDecimal(100000000);
   console.log(orderTotal);

   let testHash = web3.sha3(orderTotal + tokenAddr);

   signHashedMessage(testHash, (error, signature) => {
       if(error === null) {
           signature = signature.substring(2);
           let r = '0x' + signature.substring(0, 64);
           let s = '0x' + signature.substring(64, 128);
           let v = '0x' + signature.slice(128, 130);
           let vDec = parseInt(v, 16);

           // Call the function on our contract here...
           verifyTest.testBuyOrder(100000000, tokenAddr, vDec, r, s, (testErr, result) => {
                   if(testErr === null) {
                       console.log("Success: " + result);
                   } else {
                       console.log("Error: " + testErr);
                   }
           });

       } else {
           console.log("error: " + error);
       }
   })
};

上面 javascript 的輸出是:

0.20.3
0x5f5e100
Success: false

上面的程式碼成功呼叫了合約,但是我不明白為什麼呼叫testBuyOrder() 總是返回false。

**Edit0:**此外,我為看似重複的問題道歉,但我試圖應用其他人的答案的智慧無濟於事。


編輯1:

Ismael 的第一個建議是在合約本身中包含散列函式,這將確保數據在散列之前被正確連接和格式化。Ismael 的函式必須稍作修改才能獲得要簽名的正確雜湊值。

該功能的工作解決方案如下:

function getMessageHash(uint256 orderTotal, address tokenContract) public pure returns (bytes32) {
   bytes memory hashPrefix = "\x19Ethereum Signed Message:\n32";
   bytes32 messageHash = keccak256(abi.encodePacked(orderTotal, tokenContract));
   return keccak256(abi.encodePacked(hashPrefix, messageHash));
}

編輯2:

在探索了 Web3 v1.0 的使用之後,我仍然在努力恢復合約中的發件人地址。

我更新的客戶端程式碼如下所示:

signHashedMessage = (messageHash, callback) => {
   web3.eth.getCoinbase().then((coinbase) => {
       web3.eth.sign(prefixHashedData(messageHash), coinbase, callback);
   });
};

prefixHashedData = (messageHash) => {
   let msgPrefix = "\x19Ethereum Signed Message:\n32";
   return web3.utils.soliditySha3(msgPrefix, messageHash);
};

testSignMessage = () => {

   let verifyTest = new web3.eth.Contract(signTestABI, signTestAddr);

   let tokenAddr = "0x128Df2a07Dc41E034bD9a3CEaddDc0341250a6C8";
   let orderTotal = 100000000;

   let testHash = web3.utils.soliditySha3(orderTotal, tokenAddr);

   signHashedMessage(testHash, (error, signature) => {
       if(error === null) {
           signature = signature.substring(2);
           let r = '0x' + signature.substring(0, 64);
           let s = '0x' + signature.substring(64, 128);
           let v = '0x' + signature.slice(128, 130);
           let vDec = parseInt(v, 16);

           // Call the function on our contract here...
           let testBuyOrderRes = verifyTest.methods.testBuyOrder(orderTotal, tokenAddr, vDec, r, s);
           testBuyOrderRes.call((callError, callResult) => {
               if (callError === null) {
                   console.log("Verified: " + callResult);
               } else {
                   console.log("Error: " + callError);
               }
           });
       } else {
           console.log("error: " + error);
       }
   });
};

為了完整展示我的工作,我添加了signHashedMessage()和。prefixHashedMessage()

為了驗證prefixHashedData(),我getMessageHash()在我的契約中添加瞭如上所述的 。呼叫getMessageHash()返回的雜湊值與使用web.utils.soliditySha3()Ismael 所描述的返回值相同。

我在上面的 Web3 v1.0 程式碼中遇到的新問題;在驗證合約上的簽名時,通過呼叫testBuyOrder(),函式返回false.

誰能看到我犯的錯誤?

您發送給合約的消息與您在 javascript 中編碼的消息orderTotal + tokenAddr不同,與abi.encodePacked(orderTotal, tokenContract).

要麼使用您在契約中使用的相同功能。

function getMessageHash(uint256 orderTotal, address tokenContract) public pure returns (bytes32) {
   bytes32 messageHash = keccak256(abi.encodePacked(orderTotal, tokenContract));
   returnmessageHash;
}

並從 javascript 呼叫它verifyTest.getMessageHash(orderTotal, tokenAddr)

另一種選擇是從 web3 v1.0 呼叫soliditysha3,它將正確格式化數據。

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