Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
fix: refactor sign.js and related tests
  • Loading branch information
shrugs committed Aug 25, 2018
commit 6db5d6ea64eca7cd668f7c48ad9236afa3b595ae
6 changes: 2 additions & 4 deletions test/AutoIncrementing.test.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
const { hashMessage } = require('./helpers/sign');

const AutoIncrementing = artifacts.require('AutoIncrementingImpl');

require('chai')
.use(require('chai-bignumber')(web3.BigNumber))
.should();

const EXPECTED = [1, 2, 3, 4];
const KEY1 = hashMessage('key1');
const KEY2 = hashMessage('key2');
const KEY1 = web3.sha3('key1');
const KEY2 = web3.sha3('key2');

contract('AutoIncrementing', function ([_, owner]) {
beforeEach(async function () {
Expand Down
35 changes: 15 additions & 20 deletions test/helpers/sign.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,21 @@
const utils = require('ethereumjs-util');
const { soliditySha3 } = require('web3-utils');
const { sha3, soliditySha3 } = require('web3-utils');

const REAL_SIGNATURE_SIZE = 2 * 65; // 65 bytes in hexadecimal string legnth
const PADDED_SIGNATURE_SIZE = 2 * 96; // 96 bytes in hexadecimal string length

const DUMMY_SIGNATURE = `0x${web3.padLeft('', REAL_SIGNATURE_SIZE)}`;

/**
* Hash and add same prefix to the hash that ganache use.
* @param {string} message the plaintext/ascii/original message
* @return {string} the hash of the message, prefixed, and then hashed again
*/
function hashMessage (message) {
const messageHex = Buffer.from(utils.sha3(message).toString('hex'), 'hex');
const prefix = utils.toBuffer('\u0019Ethereum Signed Message:\n' + messageHex.length.toString());
return utils.bufferToHex(utils.sha3(Buffer.concat([prefix, messageHex])));
// messageHex = '0xdeadbeef'
function toEthSignedMessageHash (messageHex) {
const messageBuffer = Buffer.from(messageHex.substring(2), 'hex');
const prefix = Buffer.from(`\u0019Ethereum Signed Message:\n${messageBuffer.length}`);
return sha3(Buffer.concat([prefix, messageBuffer]));
}

// signs message in node (auto-applies prefix)
// message must be in hex already! will not be autoconverted!
const signMessage = (signer, message = '') => {
return web3.eth.sign(signer, message);
// signs message in node (ganache auto-applies "Ethereum Signed Message" prefix)
// messageHex = '0xdeadbeef'
const signMessage = (signer, messageHex = '0x') => {
return web3.eth.sign(signer, messageHex); // actually personal_sign
};

// @TODO - remove this when we migrate to web3-1.0.0
Expand Down Expand Up @@ -62,18 +57,18 @@ const getBouncerSigner = (contract, signer) => (redeemer, methodName, methodArgs
} else {
const abi = contract.abi.find(abi => abi.name === methodName);
const name = transformToFullName(abi);
const signature = web3.sha3(name).slice(0, 10);
const signature = sha3(name).slice(0, 10);
parts.push(signature);
}
}

// ^ substr to remove `0x` because in solidity the address is a set of byes, not a string `0xabcd`
const hashOfMessage = soliditySha3(...parts);
return signMessage(signer, hashOfMessage);
// return the signature of the "Ethereum Signed Message" hash of the hash of `parts`
const messageHex = soliditySha3(...parts);
return signMessage(signer, messageHex);
};

module.exports = {
hashMessage,
signMessage,
toEthSignedMessageHash,
getBouncerSigner,
};
48 changes: 21 additions & 27 deletions test/library/ECRecovery.test.js
Original file line number Diff line number Diff line change
@@ -1,72 +1,66 @@
const { hashMessage, signMessage } = require('../helpers/sign');
const { signMessage, toEthSignedMessageHash } = require('../helpers/sign');
const { expectThrow } = require('../helpers/expectThrow');

const ECRecoveryMock = artifacts.require('ECRecoveryMock');

require('chai')
.should();

contract('ECRecovery', function ([_, anyone]) {
let ecrecovery;
const TEST_MESSAGE = 'OpenZeppelin';
const TEST_MESSAGE = web3.sha3('OpenZeppelin');
const WRONG_MESSAGE = web3.sha3('Nope');

contract('ECRecovery', function ([_, anyone]) {
beforeEach(async function () {
ecrecovery = await ECRecoveryMock.new();
this.mock = await ECRecoveryMock.new();
});

it('recover v0', async function () {
// Signature generated outside ganache with method web3.eth.sign(signer, message)
const signer = '0x2cc1166f6212628a0deef2b33befb2187d35b86c';
const message = web3.sha3(TEST_MESSAGE);
// eslint-disable-next-line max-len
const signature = '0x5d99b6f7f6d1f73d1a26497f2b1c89b24c0993913f86e9a2d02cd69887d9c94f3c880358579d811b21dd1b7fd9bb01c1d81d10e69f0384e675c32b39643be89200';
(await ecrecovery.recover(message, signature)).should.equal(signer);
(await this.mock.recover(TEST_MESSAGE, signature)).should.equal(signer);
});

it('recover v1', async function () {
// Signature generated outside ganache with method web3.eth.sign(signer, message)
const signer = '0x1e318623ab09fe6de3c9b8672098464aeda9100e';
const message = web3.sha3(TEST_MESSAGE);
// eslint-disable-next-line max-len
const signature = '0x331fe75a821c982f9127538858900d87d3ec1f9f737338ad67cad133fa48feff48e6fa0c18abc62e42820f05943e47af3e9fbe306ce74d64094bdf1691ee53e001';
(await ecrecovery.recover(message, signature)).should.equal(signer);
(await this.mock.recover(TEST_MESSAGE, signature)).should.equal(signer);
});

it('recover using web3.eth.sign()', async function () {
// Create the signature using account[0]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Were these account[0] comments inaccurate?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah they were using accounts[1] after the [_, anyone] change

const signature = signMessage(anyone, web3.sha3(TEST_MESSAGE));
// Create the signature
const signature = signMessage(anyone, TEST_MESSAGE);

// Recover the signer address from the generated message and signature.
(await ecrecovery.recover(
hashMessage(TEST_MESSAGE),
(await this.mock.recover(
toEthSignedMessageHash(TEST_MESSAGE),
signature
)).should.equal(anyone);
});

it('recover using web3.eth.sign() should return wrong signer', async function () {
// Create the signature using account[0]
const signature = signMessage(anyone, web3.sha3(TEST_MESSAGE));
// Create the signature
const signature = signMessage(anyone, TEST_MESSAGE);

// Recover the signer address from the generated message and wrong signature.
(await ecrecovery.recover(hashMessage('Nope'), signature)).should.not.equal(anyone);
(await this.mock.recover(WRONG_MESSAGE, signature)).should.not.equal(anyone);
});

it('recover should revert when a small hash is sent', async function () {
// Create the signature using account[0]
// @TODO - remove `skip` once we upgrade to solc^0.5
it.skip('recover should revert when a small hash is sent', async function () {
// Create the signature
const signature = signMessage(anyone, TEST_MESSAGE);
try {
await expectThrow(
ecrecovery.recover(hashMessage(TEST_MESSAGE).substring(2), signature)
);
} catch (error) {
// @TODO(shrugs) - remove this once we upgrade to solc^0.5
}
await expectThrow(
this.mock.recover(TEST_MESSAGE.substring(2), signature)
);
});

context('toEthSignedMessage', function () {
it('should prefix hashes correctly', async function () {
const hashedMessage = web3.sha3(TEST_MESSAGE);
(await ecrecovery.toEthSignedMessageHash(hashedMessage)).should.equal(hashMessage(TEST_MESSAGE));
(await this.mock.toEthSignedMessageHash(TEST_MESSAGE)).should.equal(toEthSignedMessageHash(TEST_MESSAGE));
});
});
});