ecrecover от Geth и web3.eth.sign

Я пытался использовать ecrecover()для проверки подписи сообщения.

Я просмотрел множество ссылок здесь и в других местах, например:

и другие.

Но я все еще не могу ecrecover()вернуть адрес подписи. Поэтому я надеюсь, что кто-то может указать на какую-то глупую ошибку, которую я совершаю.

Вот мой код:

pragma solidity ^0.4.0;

contract test {

  function test() {
  }

  function verify(bytes32 _message, uint8 _v, bytes32 _r, bytes32 _s) constant returns (address) {
   address signer = ecrecover(_message, _v, _r, _s);
   return signer;
  }
}

Затем в geth я делаю:

> var msg = web3.sha3("hello")
"0x1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8"

> eth.accounts[0] -->
"0x7156526fbd7a3c72969b54f64e42c10fbb768c8a"

> var sig = eth.sign(eth.accounts[0], msg)
"0x9242685bf161793cc25603c231bc2f568eb630ea16aa137d2664ac80388256084f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada1c"

> var r = sig.substr(0,66)
"0x9242685bf161793cc25603c231bc2f568eb630ea16aa137d2664ac8038825608"

> var s = "0x" + sig.substr(66,64)
"0x4f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada"

> var v = 28
28

> test.verify(msg,v,r,s)
"0x33692ee5cbf7ecdb8ca43ec9e815c47f3db8cd11"

...что, конечно, НЕeth.accounts[0]

Я совершенно озадачен. Есть ли кто-нибудь, кто может увидеть, что я делаю неправильно?

Ваша первая ссылка умерла... :-(
Данное решение не работает в компиляторе ремиксов версии 0.5.8. Пожалуйста помоги.
Это на самом деле не отвечает на вопрос. Если у вас есть другой вопрос, вы можете задать его, нажав Задать вопрос . Вы также можете добавить вознаграждение , чтобы привлечь больше внимания к этому вопросу, когда у вас будет достаточно репутации . - Из обзора
Это не дает ответа на вопрос. Когда у вас будет достаточно репутации , вы сможете комментировать любой пост ; вместо этого предоставьте ответы, которые не требуют разъяснений от спрашивающего . - Из обзора

Ответы (6)

Я тоже очень долго застревал на этом вопросе.

Итак, решение таково: добавьте эту строку префикса в свой смарт-контракт Solidity.

function verify(bytes32 hash, uint8 v, bytes32 r, bytes32 s) constant returns(bool) {

    bytes memory prefix = "\x19Ethereum Signed Message:\n32";
    bytes32 prefixedHash = keccak256(prefix, hash);
    return ecrecover(prefixedHash, v, r, s) == (Your Address);
}
чувак, ты попал! Спасибо! Я не совсем понимаю, что там происходит или почему это работает именно так, но это определенно работает... Здесь возникает вопрос о том, можно ли передать уже хешированную преамбулу, а не делать это в Solidity... иметь представление об этом? В любом случае, хорошенький! Спасибо еще раз!
omg, на случай, если кто-то еще, как я, случайно угадает: 32в конце prefixсоответствует длина сообщения! github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign
@AdilHaris, Add this prefix string to your Solidity smart contractа в случае, если я не контролирую смарт-контракт ?
Небольшая, но понятная статья - medium.com/@angellopozo/…

Согласно вопросу № 3731 :

Geth добавляет строку \x19Ethereum Signed Message:\n<length of message>ко всем данным перед их подписанием ( https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign ). Если вы хотите проверить такую ​​подпись от Solidity, вам нужно добавить ту же строку в Solidity перед выполнением ecrecovery.

Вот рабочий пример, который я тестировал с помощью truffle :

Пример.sol

pragma solidity ^0.4.0;

contract Example {
    function testRecovery(bytes32 h, uint8 v, bytes32 r, bytes32 s) returns (address) {
        bytes memory prefix = "\x19Ethereum Signed Message:\n32";
        bytes32 prefixedHash = sha3(prefix, h);
        address addr = ecrecover(prefixedHash, v, r, s);

        return addr;
    }
}

пример.js (тест)

var Example = artifacts.require('./Example.sol')

var Web3 = require('web3')
var web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545'))

contract('Example', (accounts) => {
  var address = accounts[0]

  it('ecrecover result matches address', async function() {
    var instance = await Example.deployed()
    var msg = '0x8CbaC5e4d803bE2A3A5cd3DbE7174504c6DD0c1C'

    var h = web3.sha3(msg)
    var sig = web3.eth.sign(address, h).slice(2)
    var r = `0x${sig.slice(0, 64)}`
    var s = `0x${sig.slice(64, 128)}`
    var v = web3.toDecimal(sig.slice(128, 130)) + 27

    var result = await instance.testRecovery.call(h, v, r, s)
    assert.equal(result, address)
  })
})

Текущий тест:

$ truffle test

Using network 'development'.

Compiling ./contracts/Example.sol...


  Contract: Example
    ✓ ecrecover result matches address (132ms)


  1 passing (147ms)

Вероятно, лучше сделать префикс на уровне приложения, а не в контракте солидности, так как это будет дешевле.

Связанный

Имеет ли какое-либо значение значение «0x8CbaC5e4d803bE2A3A5cd3DbE7174504c6DD0c1C», которое вы присвоили переменной msg? Или это просто фиктивные данные для звонка? Другими словами, функция sha3() просто что-то хэширует? Кроме того, зачем вам использовать функцию slice() для хэша со смещением в 2 байта? Это чтобы избавиться от идентификатора формата в хэше?
@RobertOschler отличные вопросы. 1. Лучше всего хэшировать данные сообщения для подписи, поскольку они всегда имеют одинаковый размер. Значение msg в примере является примером хэша, например, sha3("некоторые данные для подписи"). 2. Да, нужно удалить префикс 0x, который не является важным байтом, необходимым для расчета.

Итак, спасибо Адилу (см. выше), вот готовый код, который я использовал, который определенно работает с процессом, описанным мной выше:

прочность прагмы ^0,4,0;

тест контракта {

  функциональный тест () {
  }

  Функция проверки (bytes32 _message, uint8 _v, bytes32 _r, bytes32 _s) возвращает константу (адрес) {
    префикс памяти в байтах = "\x19Ethereum Signed Message:\n32";
    bytes32 prefixedHash = sha3 (префикс, _сообщение);
    подписывающий адрес = ecrecover (prefixedHash, _v, _r, _s);
    обратно подписывающий;
  }
}
Я совершенно смущен этим. В вашем примере контракта _message — это хэш, который вы действительно подписываете, верно? Я имею в виду, что _message — это сообщение, на которое вы подписываетесь и генерируете _v, _r и _s. Потом в контракте ты его второй раз хэшируешь с префиксом и кидаешь в функцию ecrecover, да? Почему нам нужно сделать так, и мне кажется, что это не работает. (Я генерирую _r, _s, _v в Truffle и тестирую контракт в Remix) Можете ли вы сказать мне, как это решить?
да, именно так это и работает... вы хэшируете сообщение и передаете его в контракт. Затем вы снова хешируете его с префиксом. Тогда это работает. Но я не знаю, почему это работает. Я не могу помочь вам дальше этого, кроме как сказать, что если вы выполните указанные выше шаги, вы должны получить правильный ответ. Если бы кто-нибудь мог объяснить, почему это работает или зачем вообще нужен этот случайный префикс, было бы здорово!
вместо этого вам понадобится go-ethereum v+27. github.com/ethereum/go-ethereum/issues/2053
v+27не работает там, где я видел, но кто-то использует его по какой-то причине

Вы уверены, что используете правильные типы данных? Несколько недель назад я создал простое приложение для проверки с помощью ecrecover.

Код можно посмотреть здесь: https://github.com/Shultzi/validator/blob/master/client/main.js .

Как видите, я прошел h, rи sне как , buffersа hex. Также убедитесь, что в вашем смарт-контракте vзначение равноuint8

Кстати, я только что проверил ваши аргументы в своем коде и также получил адрес "0x3369...". Я уверен, что это как-то связано с учетной записью, которую вы подписываете. Какой клиент вы используете для подписи?
привет, спасибо за ваш ответ... Я подписываюсь через консоль geth. Используя версию 1.6.1-stable-021c3c28 geth и выполняя web3.eth.sign() . Думаете, там проблема?

В следующей статье может быть объяснена часть «\x19EthereumSignedMessage».

https://blog.ricmoo.com/verifying-messages-in-solidity-50a94f82b2ca

ECDSA от OpenZeppelin требует, чтобы вы хэшировали ваше сообщение в bytes32, а не использовали сборку evm для генерации строки base10, представляющей произвольную длину строки сообщения.

В ECDSA компании OpenZeppelin есть инструмент для создания таких упакованных сообщений, а также для устранения дополнительных нестандартных ситуаций в ECDSA.

Я написал статью, описывающую, как я считаю, правильный способ использования hashMessage() и sign() web3, ecrecover() Solidity и библиотеки ECDSA Open Zeppelin.

https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/cryptography/ECDSA.sol

https://medium.com/@yaoshiang/ethereums-ecrecover-openzeppelin-s-ecdsa-and-web3-s-sign-8ff8d16595e1