Я занят, пытаясь разобраться с восстановлением адреса с помощью 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 пришлось немного изменить, чтобы получить правильный хэш для подписи.
Рабочее решение этой функции приведено ниже:
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()
, как описал Исмаэль.
Новая проблема, с которой я столкнулся с приведенным выше кодом 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)
.
Другой вариант — вызвать soliditysha3 из web3 v1.0, который правильно отформатирует данные.
Крейг
Исмаэль
soliditySha3()
.Крейг