Ich habe eine Testroutine in Truffle, die Daten hasht, signiert und die Signaturdaten zur Überprüfung an einen Vertrag übergibt. Der erste Test hasht eine einzelne Zeichenfolge und ihre Signatur wird erfolgreich durch den Vertrag verifiziert. Der zweite hasht eine Nummer und eine Adresse. Dieser zweite Test schlägt fehl, da ecrecover()
nicht zurückgegeben wird msg.sender
.
Der Vertrag, den ich teste, lautet:
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;
}
}
Um die oben genannten Funktionen intern zu halten, habe ich einen Test-Wrapper-Vertrag erstellt:
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);
}
}
Die Testroutine zur Überprüfung signierter Daten sieht wie folgt aus:
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()
ist eine Hilfsfunktion, um das Slicing der von zurückgegebenen Rohsignatur durchzuführen web3.eth.sign()
.
Der erste assert.equal()
Anruf wird bestanden, da die Unterschrift auf dem Vertrag erfolgreich verifiziert wurde. Der zweite schlägt fehl und derzeit weiß ich nicht warum.
Ich glaube, dass es ein Problem im Format gibt inputNumber
und inputAddress
wann ich es im Client hash. Gibt es eine Möglichkeit, diese Daten zu formatieren, bevor sie gehasht werden?
Bearbeiten0:
Um das Hashing zu verifizieren, habe ich meinem Testvertrag folgende Funktion hinzugefügt:
function hashMultipleValues(uint256 inputNumber, address inputAddress) public pure returns (bytes32) {
return keccak256(abi.encodePacked(inputNumber, inputAddress));
}
Was einfach die gehashte Kombination der beiden Eingaben zurückgibt.
Ich habe einem Test Folgendes hinzugefügt, um Hashes nebeneinander zu überprüfen:
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);
Mit den oben angegebenen Eingabewerten erhalte ich während des Tests folgende Ausgabe in der Konsole:
Solidity: 0xdce71017994de76c5339d7b083bcbe948e6e75786de1faeb971c2cb369913535
Web3: 0xdce71017994de76c5339d7b083bcbe948e6e75786de1faeb971c2cb369913535
Dies beweist, dass es Parität zwischen den von mir verwendeten Hashing-Methoden gibt.
OK, also scheine ich über die Antwort gestolpert zu sein. Das Problem, das ich hatte, war, dass ich die Nachricht mit Präfix signierte und nicht nur den Hash ohne Präfix.
Der modifizierte Test, der verifyMultipleInputs()
meinen Testvertrag aufruft, sieht wie folgt aus:
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()");
Die Funktion für den Vertrag gibt jetzt wahr zurück und der Test wird bestanden. Kann jemand erklären, warum ich keine vorangestellten Daten unterschreiben sollte, bevor ich den Vertrag anrufe?
Bearbeiten0:
geth
stellt das Ethereum Signed Message
intern voran, wenn sign aufgerufen wird.
https://github.com/ethereum/go-ethereum/commit/b59c8399fbe42390a3d41e945d03b1f21c1a9b8d
Können Sie dies versuchen, um sicherzustellen, dass die msg.sender
Smart-Contract-Funktion beim Aufrufen erwartet wird
ändern:
let validSig =
await contractAuth.methods.verifyMultipleInputs(price, contractAddr, sig.v, sig.r, sig.s).call();
Zu:
let validSig =
await contractAuth.methods.verifyMultipleInputs(price, contractAddr, sig.v, sig.r, sig.s).call({from: testAddr});
Und das Problem kann kommen von abi.encodePacked
; also sind diese beiden unten unterschied
keccak256(abi.encodePacked(inputNumber, inputAddress)
Und
web3.utils.soliditySha3(price, contractAddr);
Es kann eine Warnung geben, aber Sie können versuchen, keccak256(inputNumber, inputAddress)
das Problem mit zu bestätigen.
Hoffe, das wird helfen!
keccak256
und sha3
nicht derselbe Hash-Algorithmus istsoliditySha3()
das Verhalten von nachzuahmen keccak256()
?abi.encodePacked
Ha ĐANG