我有一个测试例程在特弗莱,它的哈希数据,签名,并将签名数据传递给一个合同,以核实。第一个测试对单个字符串进行散列,其签名由契约成功验证。第二个散列是数字和地址。第二个测试失败,因为ecrecover()
不返回msg.sender
。
我正在测试的合同是:
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;
}
}
为了将上述功能保持在内部,我创建了一个测试包装契约:
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);
}
}
验证签名数据的测试例程如下:
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()
调用是在契约上成功验证签名时传递的。第二个失败了,目前我不知道原因。
我相信,当我在客户机中散列时,inputNumber
和inputAddress
的格式存在问题。在对数据进行散列之前,是否应该对其进行格式化?
为了验证散列,我在我的测试合同中添加了以下功能:
function hashMultipleValues(uint256 inputNumber, address inputAddress) public pure returns (bytes32) {
return keccak256(abi.encodePacked(inputNumber, inputAddress));
}
它只返回两个输入的散列组合。
为了同时验证散列,我在测试中添加了以下内容:
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);
在测试期间,通过给定的输入值,我在控制台中获得了以下输出:
Solidity: 0xdce71017994de76c5339d7b083bcbe948e6e75786de1faeb971c2cb369913535
Web3: 0xdce71017994de76c5339d7b083bcbe948e6e75786de1faeb971c2cb369913535
这证明了我正在使用的散列方法之间是一致的。
发布于 2018-09-13 23:36:04
好吧,所以我似乎无意中发现了答案。我遇到的问题是,我签署的是前缀消息,而不仅仅是没有前缀的散列。
修改后的测试在我的测试合同上调用了verifyMultipleInputs()
,如下所示:
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,并且测试通过了。有人能解释一下为什么在打电话给合同之前我不应该签前缀数据吗?
当调用符号时,geth
在内部为Ethereum Signed Message
加前面。
https://github.com/ethereum/go-ethereum/commit/b59c8399fbe42390a3d41e945d03b1f21c1a9b8d
发布于 2018-09-13 09:20:41
您是否可以尝试这样做,以确保在调用智能契约函数时需要使用msg.sender
?
改变:
let validSig =
await contractAuth.methods.verifyMultipleInputs(price, contractAddr, sig.v, sig.r, sig.s).call();
至:
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)
来确认问题。
希望这能帮上忙!
https://ethereum.stackexchange.com/questions/58653
复制相似问题