No se puede usar ecrecover en solidity para recuperar la dirección utilizada para firmar un mensaje, simplemente devuelve la dirección 0x0

Hago lo siguiente en geth:

var msgHash = web3.sha3("hello")
var signature = eth.sign(eth.accounts[0], msgHash)
var r = signature.slice(0,64)
var s = "0x" +signature.slice(64,128)
var v = signature.slice(128,130)

r = signature.slice(0,64) "0x6d49c891d29b33c292232f690c9972e17e0dbead7d4fc446bb4ce5892f0e55" s = signature.slice(64,128) "a22c9f3c20a7f0bc90666d1d1c6f269658a0ccd56b1db1812671e23331d8ad2c" v = signature.slice(128,130) "52"

Entonces llamo al siguiente código de solidez

function verify(bytes32 msgHash, uint8 v, bytes32 r, bytes32 s) public {
    bytes memory prefix = "\x19Ethereum Signed Message:\n32";
    bytes32 prefixedHash = keccak256(prefix, msgHash);
    a = ecrecover(prefixedHash, v, r, s);
}

Lo cual devuelve lo siguiente:0x0000000000000000000000000000000000000000

Absolutamente perplejo por esto, he echado un vistazo a los siguientes hilos sin éxito:

ecrecover de Geth y web3.eth.sign

Respuestas (3)

Tienes que sacar el 0x antes de hacer el corte; es solo para mostrar que la cadena es hexadecimal, no parte del valor r.

Debe deshacerse del 0xprefijo antes de cortar.

ethereumjs-utiltiene una función muy útil fromRpcSig, puedes usarla como

const eutil = require('ethereumjs-util')

const hash = web3.sha3('hello world')
const rpcSig = web3.eth.sign(web3.eth.coinbase, eutil.bufferToHex(hash))
const rsv = eutil.fromRpcSig(rpcSig)
// then call your ecrecover with 
// hash, eutil.bufferToHex(rsv.r), eutil.bufferToHex(rsv.s), rsv.v
¡Gracias! Aunque no soy muy bueno en javascript, eché un vistazo a la biblioteca y pude encontrar una versión de python adecuada.

Tanto flygoing como Liberty tenían razón en que necesitaba eliminar el 0xprefijo antes de cortar, pero necesitaba volver a agregar el prefijo después del corte, lo que me llevó un poco descifrar. Pude usar web3py para prefijar automáticamente y hacer el segmento, que aún no pudo recuperar la dirección adecuada. No fue hasta que volví a agregar el 0xprefijo ry sfuncionó. Como resultado, pude generar el siguiente código web3py:

decryptedPrivateKey = w3.eth.account.decrypt(encrypted, password)
attribDict = w3.eth.account.sign(message_text="testmessage", private_key=decryptedPrivateKey)
msgHash = Web3.toHex(attribDict['messageHash'])
v = attribDict['v']
r = attribDict['r']
s = attribDict['s']
r = Web3.toHex(r)
s = Web3.toHex(s)
vrs = (v,r,s)
contract.functions.testRecovery(msgHash,v,r,s).transact({'from': w3.eth.accounts[0], 'gasPrice': 91000000000})
print("v:\t",v,"\nr:\t",r,"\ns:\t",s,"\nmsgHsh:\t",msgHash)
recoveredAddress = w3.eth.account.recover(msgHash,vrs=vrs)

La testRecoveryfunción de solidez que utilicé:

function testRecovery(
    bytes32 _msgHash, 
    uint8 _v, 
    bytes32 _r,
    bytes32 _s)
    public
    returns (bool)
{
    returned = ecrecover(_msgHash, _v, _r, _s);
    if (returned == expected) {
        return true;
    } else {
        return false;
    }
}
Aquí está firmando usando web3.eth.account.sign(msg, private_key) pero en su pregunta estaba firmando usando web3.eth.sign(msg, public_key). Supongo que ambos funcionan de diferentes maneras. web3.eth.account.sign() antepone la cadena "\x19Ethereum Signed Message:\n32" antes de firmar el mensaje, por otro lado, web3.eth.account.sign() no antepone esta cadena. así que al decodificar supongo que la firma web3.eth.sign() no funcionará. Este es el caso que me está pasando ahora mismo.