Added splitSignature and beginning of better error messages.

This commit is contained in:
Richard Moore 2018-04-12 15:18:11 -04:00
parent fdb7114511
commit efb7dce524
No known key found for this signature in database
GPG Key ID: 525F70A6FCABC295
7 changed files with 148 additions and 12 deletions

@ -1,6 +1,6 @@
{
"name": "ethers",
"version": "3.0.9",
"version": "3.0.10",
"description": "Ethereum wallet library.",
"main": "index.js",
"scripts": {

@ -183,3 +183,31 @@ describe('Test Hash Functions', function() {
});
});
});
describe('Test Solidity splitSignature', function() {
var convert = require('../utils/convert');
it('splits a canonical signature', function() {
var r = '0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef';
var s = '0xcafe1a7ecafe1a7ecafe1a7ecafe1a7ecafe1a7ecafe1a7ecafe1a7ecafe1a7e';
for (var v = 27; v <= 28; v++) {
var signature = convert.concat([ r, s, [ v ] ]);
var sig = convert.splitSignature(signature);
assert.equal(sig.r, r, 'split r correctly');
assert.equal(sig.s, s, 'split s correctly');
assert.equal(sig.v, v, 'split v correctly');
}
});
it('splits a legacy signature', function() {
var r = '0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef';
var s = '0xcafe1a7ecafe1a7ecafe1a7ecafe1a7ecafe1a7ecafe1a7ecafe1a7ecafe1a7e';
for (var v = 27; v <= 28; v++) {
var signature = convert.concat([ r, s, [ v - 27 ] ]);
var sig = convert.splitSignature(signature);
assert.equal(sig.r, r, 'split r correctly');
assert.equal(sig.s, s, 'split s correctly');
assert.equal(sig.v, v, 'split v correctly');
}
});
});

@ -4,7 +4,8 @@
*/
var defineProperty = require('./properties.js').defineProperty;
var throwError = require('./throw-error');
var errors = require('./errors');
function addSlice(array) {
if (array.slice) { return array; }
@ -32,7 +33,10 @@ function isArrayish(value) {
return true;
}
function arrayify(value, name) {
function arrayify(value) {
if (value == null) {
errors.throwError('cannot convert null value to array', errors.INVALID_ARGUMENT, { arg: 'value', value: value });
}
if (value && value.toHexString) {
value = value.toHexString();
@ -48,13 +52,19 @@ function arrayify(value, name) {
}
return addSlice(new Uint8Array(result));
} else if (typeof(value) === 'string') {
if (value.match(/^[0-9a-fA-F]*$/)) {
errors.throwError('hex string must have 0x prefix', errors.INVALID_ARGUMENT, { arg: 'value', value: value });
}
errors.throwError('invalid hexidecimal string', errors.INVALID_ARGUMENT, { arg: 'value', value: value });
}
if (isArrayish(value)) {
return addSlice(new Uint8Array(value));
}
throwError('invalid arrayify value', { name: name, input: value });
errors.throwError('invalid arrayify value', { arg: 'value', value: value, type: typeof(value) });
}
function concat(objects) {
@ -113,7 +123,7 @@ function isHexString(value, length) {
var HexCharacters = '0123456789abcdef';
function hexlify(value, name) {
function hexlify(value) {
if (value && value.toHexString) {
return value.toHexString();
@ -121,7 +131,7 @@ function hexlify(value, name) {
if (typeof(value) === 'number') {
if (value < 0) {
throwError('cannot hexlify negative value', { name: name, input: value });
errors.throwError('cannot hexlify negative value', errors.INVALID_ARG, { arg: 'value', value: value });
}
var hex = '';
@ -154,7 +164,7 @@ function hexlify(value, name) {
return '0x' + result.join('');
}
throwError('invalid hexlify value', { name: name, input: value });
errors.throwError('invalid hexlify value', { arg: 'value', value: value });
}
function hexStripZeros(value) {
@ -171,6 +181,31 @@ function hexZeroPad(value, length) {
return value;
}
/* @TODO: Add something like this to make slicing code easier to understand
function hexSlice(hex, start, end) {
hex = hexlify(hex);
return '0x' + hex.substring(2 + start * 2, 2 + end * 2);
}
*/
function splitSignature(signature) {
signature = arrayify(signature);
if (signature.length !== 65) {
throw new Error('invalid signature');
}
var v = signature[64];
if (v !== 27 && v !== 28) {
v = 27 + (v % 2);
}
return {
r: hexlify(signature.slice(0, 32)),
s: hexlify(signature.slice(32, 64)),
v: v
}
}
module.exports = {
arrayify: arrayify,
isArrayish: isArrayish,
@ -180,6 +215,8 @@ module.exports = {
padZeros: padZeros,
stripZeros: stripZeros,
splitSignature: splitSignature,
hexlify: hexlify,
isHexString: isHexString,
hexStripZeros: hexStripZeros,

54
utils/errors.js Normal file

@ -0,0 +1,54 @@
'use strict';
var defineProperty = require('./properties').defineProperty;
var codes = { };
[
// Unknown Error
'UNKNOWN_ERROR',
// Missing new operator to an object
// - name: The name of the class
'MISSING_NEW',
// Invalid argument to a function:
// - arg: The argument name that was invalid
'INVALID_ARGUMENT'
].forEach(function(code) {
defineProperty(codes, code, code);
});
defineProperty(codes, 'throwError', function(message, code, params) {
if (!code) { code = codes.UNKNOWN_ERROR; }
if (!params) { params = {}; }
var messageDetails = [];
Object.keys(params).forEach(function(key) {
messageDetails.push(key + '=' + JSON.stringify(params[key]));
});
var reason = message;
if (messageDetails.length) {
message += ' (' + messageDetails.join(', ') + ')';
}
var error = new Error(message);
error.reason = reason;
error.code = code
Object.keys(params).forEach(function(key) {
error[key] = params[key];
});
throw error;
});
defineProperty(codes, 'checkNew', function(self, kind) {
if (!(self instanceof kind)) {
codes.throwError('missing new', codes.MISSING_NEW, { name: kind.name });
}
});
module.exports = codes;

@ -67,4 +67,6 @@ module.exports = {
solidityPack: solidity.pack,
solidityKeccak256: solidity.keccak256,
soliditySha256: solidity.sha256,
splitSignature: convert.splitSignature,
}

@ -21,15 +21,28 @@ var utils = (function() {
};
})();
var errors = require('../utils/errors');
function SigningKey(privateKey) {
if (!(this instanceof SigningKey)) { throw new Error('missing new'); }
errors.checkNew(this, SigningKey);
privateKey = utils.arrayify(privateKey);
if (privateKey.length !== 32) {
throw new Error('invalid private key');
try {
privateKey = utils.arrayify(privateKey);
if (privateKey.length !== 32) {
errors.throwError('exactly 32 bytes required', errors.INVALID_ARGUMENT, { value: privateKey });
}
} catch(error) {
var params = { reason: error.reason, value: '[REDACTED]' }
if (error.value) {
if(typeof(error.value.length) === 'number') {
params.length = error.value.length;
}
params.type = typeof(error.value);
}
errors.throwError('invalid private key', error.code, params);
}
utils.defineProperty(this, 'privateKey', utils.hexlify(privateKey))
var keyPair = secp256k1.keyFromPrivate(privateKey);

@ -28,6 +28,8 @@ var utils = (function() {
};
})();
var errors = require('../utils/errors');
var HDNode = require('./hdnode');
var secretStorage = require('./secret-storage');
@ -49,7 +51,7 @@ var transactionFields = [
];
function Wallet(privateKey, provider) {
if (!(this instanceof Wallet)) { throw new Error('missing new'); }
errors.checkNew(this, Wallet);
// Make sure we have a valid signing key
var signingKey = privateKey;