首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场

ecrecover()
EN

Ethereum用户
提问于 2018-09-13 09:09:53
回答 2查看 940关注 0票数 1

我有一个测试例程在特弗莱,它的哈希数据,签名,并将签名数据传递给一个合同,以核实。第一个测试对单个字符串进行散列,其签名由契约成功验证。第二个散列是数字和地址。第二个测试失败,因为ecrecover()不返回msg.sender

我正在测试的合同是:

代码语言:javascript
运行
复制
pragma solidity ^0.4.24;

contract ContractAuth {
    function getPrefixedHash(bytes32 messageHash) internal pure returns (bytes32) {
        bytes memory hashPrefix = "\x19Ethereum Signed Message:\n32";
        return keccak256(abi.encodePacked(hashPrefix, messageHash));
    } 

    // https://ethereum.stackexchange.com/a/15911
    function verifyMessageHash(bytes32 messageHash, uint8 v, bytes32 r, bytes32 s) internal view returns (bool) {
        bytes32 prefixedHash = getPrefixedHash(messageHash);
        return ecrecover(prefixedHash, v, r, s) == msg.sender;
    }
}

为了将上述功能保持在内部,我创建了一个测试包装契约:

代码语言:javascript
运行
复制
pragma solidity ^0.4.24;

import "./ContractAuth .sol";

// TestContractAuth acts as a wrapper contract, allowing internal and
// private functions to be accessed without modifying the scope of the actual functions.
contract TestContractAuth is ContractAuth {

    // Test access to ContractAuth::getPrefixedHash()
    function getPrefixedHashTest(bytes32 messageHash) public pure returns (bytes32) {
        return getPrefixedHash(messageHash);
    }

    // Test access to ContractAuth::verifyMessageHash()
    function verifyMessageHashTest(bytes32 messageHash, uint8 v, bytes32 r, bytes32 s) public view returns (bool) {
        return verifyMessageHash(messageHash, v, r, s);
    }

    function verifyMultipleInputs(uint256 inputNumber, address inputAddress, uint8 v, bytes32 r, bytes32 s) public view returns (bool) {
        bytes32 messageHash = keccak256(abi.encodePacked(inputNumber, inputAddress));
        return verifyMessageHash(messageHash, v, r, s);
    }
}

验证签名数据的测试例程如下:

代码语言:javascript
运行
复制
it("verify signed data", async () => {
    // getInstance() deploys the test contract above and returns
    // an instance to it for testing
    const contractAuth = getInstance("TestContractAuth");
    const testAddr = await web3.eth.getCoinbase();
    const msgPrefix = "\x19Ethereum Signed Message:\n32";

    {
        let hashedMessage = web3.utils.soliditySha3("Hello, World!");
        const prefixedHash = web3.utils.soliditySha3(msgPrefix, hashedMessage);

        let rawSig = await web3.eth.sign(prefixedHash, testAddr);
        const sig = parseSignature(rawSig);

        let validSig =
            await contractAuth.methods.verifyMessageHashTest(prefixedHash, sig.v, sig.r, sig.s).call();

        assert.equal(validSig, true, "Expected valid signature returned by verifyMessageHashTest()");
    }
    {   // Test currently fails...
        // TODO: ask question on StackExchange

        let price = 100000000;
        let contractAddr = "0x856F6BD97c2e74F1089a9e7827586a8E3447400b";

        let hashedMessage = web3.utils.soliditySha3(price, contractAddr);
        const prefixedHash = web3.utils.soliditySha3(msgPrefix, hashedMessage);

        const rawSig = await web3.eth.sign(prefixedHash, testAddr);
        const sig = parseSignature(rawSig);

        let validSig =
            await contractAuth.methods.verifyMultipleInputs(price, contractAddr, sig.v, sig.r, sig.s).call();

        assert.equal(validSig, true, "expected valid sig from verifyMultipleInputs()");
    }
});

parseSignature()是一个辅助函数,用于对web3.eth.sign()返回的原始签名进行切片。

第一个assert.equal()调用是在契约上成功验证签名时传递的。第二个失败了,目前我不知道原因。

我相信,当我在客户机中散列时,inputNumberinputAddress的格式存在问题。在对数据进行散列之前,是否应该对其进行格式化?

Edit0:

为了验证散列,我在我的测试合同中添加了以下功能:

代码语言:javascript
运行
复制
function hashMultipleValues(uint256 inputNumber, address inputAddress) public pure returns (bytes32) {
    return keccak256(abi.encodePacked(inputNumber, inputAddress));
}

它只返回两个输入的散列组合。

为了同时验证散列,我在测试中添加了以下内容:

代码语言:javascript
运行
复制
let hashedMessage = web3.utils.soliditySha3(price, contractAddr);
let solHashedMessage =
    await contractAuth.methods.hashMultipleValues(price, contractAddr).call();
console.log("Solidity:\t" + solHashedMessage);
console.log("Web3:\t\t" + hashedMessage);

在测试期间,通过给定的输入值,我在控制台中获得了以下输出:

代码语言:javascript
运行
复制
Solidity:       0xdce71017994de76c5339d7b083bcbe948e6e75786de1faeb971c2cb369913535
Web3:           0xdce71017994de76c5339d7b083bcbe948e6e75786de1faeb971c2cb369913535

这证明了我正在使用的散列方法之间是一致的。

EN

回答 2

Ethereum用户

回答已采纳

发布于 2018-09-13 23:36:04

好吧,所以我似乎无意中发现了答案。我遇到的问题是,我签署的是前缀消息,而不仅仅是没有前缀的散列。

修改后的测试在我的测试合同上调用了verifyMultipleInputs(),如下所示:

代码语言:javascript
运行
复制
const testAddr = await web3.eth.getCoinbase();
let price = 100000000;
let contractAddr = "0x856F6BD97c2e74F1089a9e7827586a8E3447400b";

let hashedMessage = web3.utils.soliditySha3(price, contractAddr);

// Sign the hashedMessage, not a prefixedHash
const rawSig = await web3.eth.sign(hashedMessage, testAddr);
const sig = parseSignature(rawSig);

let validSig =
    await CentraDEX.methods.verifyMultipleInputs(price, contractAddr, sig.v, sig.r, sig.s).call({from: testAddr});

assert.equal(validSig, true, "expected valid sig from verifyMultipleInputs()");

合同上的函数现在返回true,并且测试通过了。有人能解释一下为什么在打电话给合同之前我不应该签前缀数据吗?

Edit0:

当调用符号时,geth在内部为Ethereum Signed Message加前面。

https://github.com/ethereum/go-ethereum/commit/b59c8399fbe42390a3d41e945d03b1f21c1a9b8d

票数 2
EN

Ethereum用户

发布于 2018-09-13 09:20:41

您是否可以尝试这样做,以确保在调用智能契约函数时需要使用msg.sender

改变:

代码语言:javascript
运行
复制
let validSig =
    await contractAuth.methods.verifyMultipleInputs(price, contractAddr, sig.v, sig.r, sig.s).call();

至:

代码语言:javascript
运行
复制
let validSig =
    await contractAuth.methods.verifyMultipleInputs(price, contractAddr, sig.v, sig.r, sig.s).call({from: testAddr});

问题可能来自于abi.encodePacked,所以下面这两个问题是不同的

keccak256(abi.encodePacked(inputNumber, inputAddress)

web3.utils.soliditySha3(price, contractAddr);

它可能会发出警告,但您可以尝试使用keccak256(inputNumber, inputAddress)来确认问题。

希望这能帮上忙!

票数 0
EN
页面原文内容由Ethereum提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://ethereum.stackexchange.com/questions/58653

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档