Firme el mensaje con web3 y verifique con openzeppelin-solidity ECDSA.sol

Estoy tratando de obtener un pequeño ejemplo trabajando con ECDSA.sol aquí: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/cryptography/ECDSA.sol

Este contrato:

  1. Genere bytes32 aleatorios (ish) (stub para un resumen de mensaje futuro).
  2. Transformarlo en un EthSignedHash conkeccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
  3. Utilice ecrecover para averiguar quién firmó un mensaje.
pragma solidity 0.5.8;

import "../../node_modules/openzeppelin-solidity/contracts/cryptography/ECDSA.sol";

contract Sigs {

    using ECDSA for bytes32;

    function rndHash() public view returns(bytes32) {
        return keccak256(abi.encodePacked(block.number));
    }

    function ethSignedHash(bytes32 messageHash) public pure returns(bytes32) {
        return messageHash.toEthSignedMessageHash();
    }

    function recover(bytes32 hash, bytes memory signature) public pure returns(address) {
        return hash.recover(signature);
    }
}

Del lado del cliente:

  1. Evoca un mensaje aleatorio (ish).
  2. Haz un ethHash del mensaje.
  3. Firmarlo.
  4. Envíe ethHash y la firma a ecRecover y obtenga la dirección del firmante.

No estoy seguro de lo que me estoy perdiendo. Creo que el hash del mensaje original debe estar firmado y EIP-712 realiza la "Firma de Ethereum", por lo que debo enviar el "Ethereum Hash" a ecrecover (como se implementa en ECDSA.sol). En cualquier caso, ninguna combinación trae alegría.

var Sigs = artifacts.require("./Junk/Sigs");

contract("Sigs", accounts => {

  var signer;

  beforeEach(async () => {
    signer = accounts[0];
    sigs = await Sigs.new({from: signer}); 
  });

  it("should recover signer address.", async () =>  {

    console.log("SIGNER: ", signer);

    var random  = await sigs.rndHash({from: signer});
    var ethHash = await sigs.ethSignedHash(random);

    // four possible combinations tried for the next step :/
    var signature = await web3.eth.sign(random, signer);    // sign the random hash or the ethHash
    var recovered = await sigs.recover(ethHash, signature); // recover from the random hash or the ethHash

    console.log("random1: ", random);
    console.log("signature: ", signature);
    console.log("recovered: ", recovered);

    assert.strictEqual(recovered, signer, "The recovered signature does not match the signer.");

  });    
});

Agradecería mucho si alguien me puede solucionar.

Truffle v5.0.41 (core: 5.0.41)
Solidity - 0.5.8 (solc-js)
Node v8.10.0
Web3.js v1.2.1

¡Gracias!

Actualizar

Para cualquier otra persona que se encuentre con esto, esta es la prueba aprobada revisada.

const Sigs = artifacts.require("./Junk/Sigs");
const EthCrypto = require("eth-crypto");

contract("Sigs", accounts => {

  var signer;

  beforeEach(async () => {
    signer = accounts[0];
    sigs = await Sigs.new({from: signer}); 
  });

  it("should recover signer address.", async () =>  {

    console.log("SIGNER: ", signer);

    var message = "0x1234";
    console.log("message: ", message);
    var msgHash    = await sigs.messageHash(message);
    var ethHash = await sigs.ethSignedHash(msgHash);

    var signature = await web3.eth.sign(msgHash, signer);    // sign the mesage hash
    signature = signature.substr(0, 130) + (signature.substr(130) == "00" ? "1b" : "1c"); // v: 0,1 => 27,28
    var recovered = await sigs.recover(ethHash, signature); // recover from the ethHash

    console.log("msgHash: ", msgHash);
    console.log("ethHash: ", ethHash);
    console.log("signature: ", signature);
    console.log("recovered: ", recovered);

    assert.strictEqual(recovered, signer, "The recovered signature does not match the signer.");

  });    
});
¿Ha intentado publicar una pregunta (un problema) en su GitHub o en la comunidad de OpenZeppelin ? Por lo general, son muy receptivos (mucho más que esta comunidad) y muy útiles.
Rob, openzeppelin-solidityes ahora @openzeppelin/contracts. También en entornos como la trufa, puede escribir su importación como: import "@openzeppelin/contracts/cryptography/ECDSA.sol";No necesita especificarnode_modules
Gracias @goodvibration, soy el administrador de la comunidad en OpenZeppelin. Me registro aquí, pero paso la mayor parte de mi tiempo en el foro de la comunidad de OpenZeppelin

Respuestas (2)

El problema es que eth.signdevuelve una firma donde ves 0 o 1 y ecrecoverespera que sea 27 o 28.

Una nota en la documentación de web3 v0.20 es clara:

Tenga en cuenta que si está utilizando ecrecover, vserá "00"o "01". Como resultado, para usar este valor, tendrá que analizarlo en un número entero y luego agregar 27. Esto dará como resultado un 27o un 28.

tienes que hacer algo como esto

var signature = await web3.eth.sign(random, signer);    // sign the random hash or the ethHash
signature = signature.substr(0, 130) + (signature.substr(130) == "00" ? "1b" : "1c");
var recovered = await sigs.recover(ethHash, signature); // recover from the random hash or the ethHash
Este problema también se informa en el paquete 'ethereumjs-util'/función 'fromRpcSig' , donde la documentación indica que "todo debido a un error en geth: github.com/ethereum/go-ethereum/issues/2053 " (Acabo de pasar tropezar con eso yo mismo hace un par de días).
Como puede ver, la función agrega 27 al último byte en el búfer de 65 bytes (los dos últimos caracteres en la cadena de 130 caracteres)
Gracias a ambos. No puedo acercarme a una computadora hoy, pero tengo muchas ganas de probar esto.
No podía dejarlo pasar, jajaja. Funciona de maravilla. Muchas gracias.

Rob, veo que ya lo resolviste (lo cual es genial).

Para futuros lectores:

Para Contratos OpenZeppelin puede consultar la documentación para más información, en este caso la sección Criptografía: https://docs.openzeppelin.com/contracts/2.x/utilities#cryptography

Las pruebas para los contratos pueden ser útiles, en este caso ECDSA.test.js: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/test/cryptography/ECDSA.test.js

Hay un tutorial sobre cómo iniciar sesión en el foro: https://forum.openzeppelin.com/t/sign-it-like-you-mean-it-creating-and-verifying-ethereum-signatures/697

Si tiene preguntas sobre el uso de OpenZeppelin, también puede preguntar en el Foro de la comunidad: https://forum.openzeppelin.com/

Divulgación: soy el administrador de la comunidad en OpenZeppelin