ECRecovery library added with test and docs

pull/265/merge
Augusto 8 years ago committed by Francisco Giordano
parent b05fb9c200
commit 4201d50dcf
  1. 68
      contracts/ECRecovery.sol
  2. 14
      docs/source/ecrecovery.rst
  3. 51
      test/ECRecovery.js
  4. 20
      test/helpers/ECRecoveryMock.sol

@ -0,0 +1,68 @@
pragma solidity ^0.4.11;
/**
* Eliptic curve signature operations
* Based on https://gist.github.com/axic/5b33912c6f61ae6fd96d6c4a47afde6d
*/
library ECRecovery {
// Duplicate Solidity's ecrecover, but catching the CALL return value
function safeRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal returns (bool, address) {
// We do our own memory management here. Solidity uses memory offset
// 0x40 to store the current end of memory. We write past it (as
// writes are memory extensions), but don't update the offset so
// Solidity will reuse it. The memory used here is only needed for
// this context.
bool ret;
address addr;
assembly {
let size := mload(0x40)
mstore(size, hash)
mstore(add(size, 32), v)
mstore(add(size, 64), r)
mstore(add(size, 96), s)
// NOTE: we can reuse the request memory because we deal with
// the return code
ret := call(3000, 1, 0, size, 128, size, 32)
addr := mload(size)
}
return (ret, addr);
}
function recover(bytes32 hash, bytes sig) internal returns (address) {
bytes32 r;
bytes32 s;
uint8 v;
if (sig.length != 65)
return (address(0));
assembly {
r := mload(add(sig, 32))
s := mload(add(sig, 64))
v := byte(0, mload(add(sig, 96)))
}
// albeit non-transactional signatures are not specified by the YP, one would expect it
// to match the YP range of [27, 28]
//
// geth uses [0, 1] and some clients have followed. This might change, see:
// https://github.com/ethereum/go-ethereum/issues/2053
if (v < 27)
v += 27;
if (v != 27 && v != 28)
return (address(0));
bool ret;
address addr;
(ret, addr) = safeRecover(hash, v, r, s);
return addr;
}
}

@ -0,0 +1,14 @@
ECReovery
=============================================
Recover the signer address of messages using elliptic curve signatures.
safeRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal returns (bool, address)
"""""""""""""""""""""""""""""""""""""""""""""""""
Returns the signer of the the hash using the signature divided in v, r, and s values.
recover(bytes32 hash, bytes sig) internal returns (address)
"""""""""""""""""""""""""""""""""""""""""""""""""
Returns the signer of the the hash using the signature that provides the web3.sign() method.

@ -0,0 +1,51 @@
var ECRecoveryMock = artifacts.require("./helpers/ECRecoveryMock.sol");
contract('ECRecovery', function(accounts) {
let ecrecovery;
before(async function() {
ecrecovery = await ECRecoveryMock.new();
});
it("recover v0", async function() {
let signer = '0x2cc1166f6212628a0deef2b33befb2187d35b86c';
let message = '0x7dbaf558b0a1a5dc7a67202117ab143c1d8605a983e4a743bc06fcc03162dc0d'; // web3.sha3('OpenZeppelin')
let signature = '0x5d99b6f7f6d1f73d1a26497f2b1c89b24c0993913f86e9a2d02cd69887d9c94f3c880358579d811b21dd1b7fd9bb01c1d81d10e69f0384e675c32b39643be89200';
await ecrecovery.recover(message, signature);
assert.equal(signer, await ecrecovery.signer());
});
it("recover v1", async function() {
let signer = '0x1e318623ab09fe6de3c9b8672098464aeda9100e';
let message = '0x7dbaf558b0a1a5dc7a67202117ab143c1d8605a983e4a743bc06fcc03162dc0d'; // web3.sha3('OpenZeppelin')
let signature = '0x331fe75a821c982f9127538858900d87d3ec1f9f737338ad67cad133fa48feff48e6fa0c18abc62e42820f05943e47af3e9fbe306ce74d64094bdf1691ee53e001';
await ecrecovery.recover(message, signature);
assert.equal(signer, await ecrecovery.signer());
});
it("safeRecover v0", async function() {
let signer = '0x58d5f9f841bcf9e502b438cc81d1ea3ba3f8f7f3';
let message = '0x7dbaf558b0a1a5dc7a67202117ab143c1d8605a983e4a743bc06fcc03162dc0d'; // web3.sha3('OpenZeppelin')
let signature = '3690f285f30200dfacd35b9ee9af4beaf2c2f4b7880d93dd9bdf776e8fdbec6a095d00c80e20e95a68c8effc038707dd740aabf94a6ca37c09733874f772d6e000';
let v = (signature.substring(128,130) == '01') ? 28 : 27;
let r = '0x'+signature.substring(0,64);
let s = '0x'+signature.substring(64,128);
await ecrecovery.safeRecover(message, v, r, s);
assert.equal(signer, await ecrecovery.signer());
assert.equal(true, await ecrecovery.result());
});
it("safeRecover v1", async function() {
let signer = '0x0b8124c2429c44e8ca31e7db6f85845abf146415';
let message = '0x7dbaf558b0a1a5dc7a67202117ab143c1d8605a983e4a743bc06fcc03162dc0d'; // web3.sha3('OpenZeppelin')
let signature = '7696f87b3f14e2f1c408c552c0005479bfe35df3a9efb493a2ad2bdf25d95c8c605b6f83699faca9bcbc3c665b434ed8d9c717aa71a1916f054fc41671dd38ad01';
let v = (signature.substring(128,130) == '01') ? 28 : 27;
let r = '0x'+signature.substring(0,64);
let s = '0x'+signature.substring(64,128);
await ecrecovery.safeRecover(message, v, r, s);
assert.equal(signer, await ecrecovery.signer());
assert.equal(true, await ecrecovery.result());
});
});

@ -0,0 +1,20 @@
pragma solidity ^0.4.11;
import '../../contracts/ECRecovery.sol';
contract ECRecoveryMock {
bool public result;
address public signer;
function safeRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) {
(result, signer) = ECRecovery.safeRecover(hash, v, r, s);
}
function recover(bytes32 hash, bytes sig) {
signer = ECRecovery.recover(hash, sig);
}
}
Loading…
Cancel
Save