¿Cómo se usa correctamente ecrecover para verificar las firmas de Ethereum?

He estado buscando para descubrir qué estoy haciendo mal aquí, pero aún no he tenido suerte. Por lo que puedo decir, estoy usando ecrecover correctamente, pero parece que no puedo recuperar la dirección de firma de Ethereum.

Usando una sesión geth, creé y firmé un hash, recuperando los valores r, v y s según la API de Javascript :

> var foobar = web3.sha3('foobar')
undefined
> foobar
"38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e"
> var foo = eth.sign('0x803c84d8b64be30554e2edb9c61b50bc78a7231f', foobar).slice(2)
undefined
> var r = foo.slice(0, 64); var s = foo.slice(64, 128); var v = foo.slice(128);
> v
"00"
> r
"723841761d213b60ac1cbf063207cbeba6c2725bcaf7c189e63f13d93fc1dc07"
> s
"789d1dd423d25f0772d2748d60f7e4b81bb14d086eba8e8e8efb6dcff8a4ae02"

Luego traduje esos valores en una prueba de solidez:

import 'dapple/test.sol';

contract ECRecoverTest is Test {
  function testRecovery() {
    bytes32 foobar = 0x38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e;
    uint8 v = 0x00;
    bytes32 r = 0x723841761d213b60ac1cbf063207cbeba6c2725bcaf7c189e63f13d93fc1dc07;
    bytes32 s = 0x789d1dd423d25f0772d2748d60f7e4b81bb14d086eba8e8e8efb6dcff8a4ae02;

    log_address(ecrecover(foobar, v, r, s));
  }
}

Espero ver mi dirección de firma ( 0x803c84d8b64be30554e2edb9c61b50bc78a7231f) registrada, pero todo lo que veo es:

$ dapple test
Testing...
Using local solc installation...

ECRecoverTest
  test recovery
  LOG:  log_address
  LOG:    val: 0xb62f18e17054f66a817bd4295423adf9ed98873e
  Passed!

También intenté convertir el hash a ASCII antes de firmarlo y también prefijar el hexadecimal con "0x" en caso de que el problema se deba a que la eth.signfunción no entendió que los datos debían tratarse como un valor hexadecimal.

¿Algunas ideas? Ya miré el caso de prueba en pyethereum para asegurarme de que lo estoy usando correctamente, y vi la otra pregunta en este StackExchange . Sin embargo, no parece estar teniendo mucho éxito.

Respuestas (2)

No estoy seguro de si este es su único problema, pero al menos, la v que está obteniendo es incorrecta: debería ser 27 o 28. Desafortunadamente, muchas funciones de firma no le darán, pero puede simplemente pruebe ambos y vea cuál le da la dirección correcta. No es necesario usar hexadecimal para "v", simplemente aliméntelo literalmente 27 o 28 como un int.

El otro truco que me confundió por un tiempo es que si vas a codificar bytes cuando haces que el hash pase a ecrecover, debes asegurarte de que también estabas codificando bytes cuando firmaste. No estoy seguro de si lo estás haciendo bien o no.

Hay un ejemplo aquí si eso ayuda: https://github.com/edmundedgar/realitykeys-examples-ethereum/tree/master/sponsor

Solo usé hexadecimal para v aquí para mayor claridad. Quería demostrar que efectivamente venía directamente de la firma. Todavía no estoy seguro de cuál es el problema, así que estoy actualizando mi copia de geth en caso de que mis problemas se deban a un error en la copia anterior, construida a partir de una confirmación de Github que estoy ejecutando.
La falta de una "v" probablemente sea solo la forma en que funciona; por ejemplo, la codificación DER estándar parece codificar solo los puntos r y s, y desechar la "v". Como digo, prueba los dos.

Esta esencia es un buen ejemplo de cómo usar ecrecover. Utiliza ensamblador para filtrar las partes r, s, v de una firma y lo completa en la ecrecoverfunción.

assembly {
            r := mload(add(sig, 32))
            s := mload(add(sig, 64))
            // Here we are loading the last 32 bytes, including 31 bytes
            // of 's'. There is no 'mload8' to do this.
            //
            // 'byte' is not working due to the Solidity parser, so lets
            // use the second best option, 'and'
            v := and(mload(add(sig, 65)), 255)
        }

Note también la parte v 27, 28 que Edmund mencionó