ecrecover de Geth y web3.eth.sign

He intentado utilizar ecrecover()para verificar la firma de un mensaje.

He mirado muchas referencias aquí y en otros lugares, como:

y otros.

Pero todavía no puedo ecrecover()devolver la dirección de firma. Así que espero que alguien pueda señalar algún error estúpido que estoy cometiendo.

Aquí está mi código:

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;
  }
}

Luego en geth, hago:

> 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"

...que por supuesto, NO eseth.accounts[0]

Estoy totalmente perplejo. ¿Hay alguien que pueda ver lo que estoy haciendo mal?

Tu primer enlace ha muerto... :-(
La solución dada no funciona en la versión 0.5.8 del compilador remix. Por favor ayuda.
Esto realmente no responde la pregunta. Si tiene una pregunta diferente, puede hacerla haciendo clic en Preguntar . También puede agregar una recompensa para llamar más la atención sobre esta pregunta una vez que tenga suficiente reputación . - De la revisión
Esto no proporciona una respuesta a la pregunta. Una vez que tenga suficiente reputación , podrá comentar cualquier publicación ; en su lugar, proporcione respuestas que no requieran aclaración por parte del autor de la pregunta . - De la revisión

Respuestas (6)

También estuve atascado en este tema durante mucho tiempo.

Entonces, la solución es: agregue esta cadena de prefijo a su contrato inteligente de 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);
}
amigo, lo lograste! ¡Gracias! No entiendo totalmente lo que está pasando allí o por qué funciona así, pero definitivamente funciona... Aquí hay una pregunta sobre si puede pasar el preámbulo ya cifrado, en lugar de hacerlo en Solidity... ¿Tienes una opinión sobre eso? De todos modos, ¡buen uno! ¡Gracias de nuevo!
omg, en caso de que alguien más esté adivinando al azar como yo: ¡ 32al final del prefixcorresponde a la longitud del mensaje! github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign
@AdilHaris Add this prefix string to your Solidity smart contracty en caso de que no controle el contrato inteligente ?
Artículo pequeño pero claro - medium.com/@angellopozo/…

Según el número 3731 :

Geth antepone la cadena \x19Ethereum Signed Message:\n<length of message>a todos los datos antes de firmarlos ( https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign ). Si desea verificar dicha firma de Solidity, deberá anteponer la misma cadena en solidity antes de realizar la ecrecovery.

Aquí hay un ejemplo de trabajo que probé usando truffle :

Ejemplo.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;
    }
}

ejemplo.js (prueba)

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)
  })
})

Prueba de carrera:

$ truffle test

Using network 'development'.

Compiling ./contracts/Example.sol...


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


  1 passing (147ms)

Probablemente sea mejor hacer el prefijo en el nivel de la aplicación en lugar de en el contrato de solidez, ya que será más económico.

Relacionado

¿Tiene algún significado el valor '0x8CbaC5e4d803bE2A3A5cd3DbE7174504c6DD0c1C' que asignó a la variable msg? ¿O son solo datos ficticios para la llamada? En otras palabras, ¿es solo algo para que la función sha3() haga hash? Además, ¿por qué necesita usar la función slice() en el hash con un desplazamiento de 2 bytes? ¿Es para deshacerse del identificador de formato en el hash?
@RobertOschler excelentes preguntas. 1. Es una buena práctica codificar los datos del mensaje para firmar porque siempre tiene el mismo tamaño. El valor de msg en el ejemplo es un ejemplo de un hash, por ejemplo, sha3 ("algunos datos para firmar"). 2. Sí, es para eliminar el prefijo 0x que no son bytes importantes que se requieren para el cálculo

Entonces, gracias a Adil (ver arriba), aquí está el código terminado que utilicé que definitivamente funciona con el proceso como lo describí anteriormente:

solidez de pragma ^0.4.0;

prueba de contrato {

  prueba de funcionamiento() {
  }

  función verificar(bytes32 _mensaje, uint8 _v, bytes32 _r, bytes32 _s) devoluciones constantes (dirección) {
    prefijo de memoria de bytes = "\x19Ethereum Signed Message:\n32";
    bytes32 prefixedHash = sha3(prefijo, _mensaje);
    firmante de la dirección = ecrecover(prefixedHash, _v, _r, _s);
    firmante de retorno;
  }
}
Estoy totalmente confundido por esto. En su contrato de ejemplo, _message es el hash en el que realmente firma, ¿verdad? Quiero decir que el _message es el mensaje en el que inicia sesión y genera el _v, _r y _s. Luego, en el contrato, lo hash la segunda vez con el prefijo y lo arrojas a la función ecrecover, ¿verdad? ¿Por qué necesitamos hacer esto y me parece que no funciona?
sí, así es exactamente como funciona... hash un mensaje y lo pasas al contrato. Luego lo hash nuevamente con el prefijo. Entonces funciona. Pero no sé por qué funciona. No puedo ayudarlo más que eso, excepto para decir que si sigue los pasos anteriores, debería obtener la respuesta correcta. Si alguien pudiera explicar por qué funciona esto, o por qué incluso necesita ese prefijo aleatorio, ¡sería genial!
con go-ethereum necesitarás v+27en su lugar. github.com/ethereum/go-ethereum/issues/2053
v+27falla donde lo he visto, pero alguien lo usa por alguna razón

¿Está seguro de que está utilizando los tipos de datos correctos? Creé una aplicación de validación simple usando ecrecover hace unas semanas.

Puede ver el código aquí: https://github.com/Shultzi/validator/blob/master/client/main.js

Como puede ver, pasé el h, ry sno como bufferssino un hex. Además, asegúrese de que en su contrato inteligente el vvalor seauint8

Por cierto, acabo de verificar sus argumentos en mi código y también recibí la dirección "0x3369...". Estoy seguro de que tiene algo que ver con la cuenta que está firmando. ¿Qué cliente estás usando para firmar?
hola, gracias por tu respuesta... Estoy firmando a través de la consola geth. Usando la versión 1.6.1-stable-021c3c28 de geth y haciendo web3.eth.sign() . ¿Crees que hay un problema ahí?

El siguiente artículo puede explicar la parte "\x19EthereumSignedMessage"

https://blog.ricmoo.com/verificando-mensajes-en-solidez-50a94f82b2ca

La forma en que ECDSA de OpenZeppelin lo hace es requerir que hash tu mensaje en un bytes32, en lugar de usar el ensamblado evm para generar una cadena base10 que represente la longitud arbitraria de la cadena del mensaje.

ECDSA de OpenZeppelin tiene una herramienta para generar estos mensajes envueltos, así como eliminar casos de esquina adicionales en ECDSA.

Escribí un artículo describiendo lo que creo que es la forma correcta de usar hashMessage() y sign() de web3, ecrecover() de Solidity y ECDSA lib de 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