Throw an error when Amount or Fee contains a decimal (Fix #31)

Thanks to @jwbusch for review
This commit is contained in:
Elliot Lee
2019-07-26 16:53:25 -07:00
parent b775a6f3c8
commit da5edb0b3b
15 changed files with 413 additions and 162 deletions

View File

@@ -23,7 +23,7 @@
"devDependencies": { "devDependencies": {
"babel-cli": "^6.8.0", "babel-cli": "^6.8.0",
"babel-core": "^6.8.0", "babel-core": "^6.8.0",
"babel-eslint": "^6.0.4", "babel-eslint": "^10.0.2",
"babel-loader": "^6.2.4", "babel-loader": "^6.2.4",
"babel-preset-es2015": "^6.6.0", "babel-preset-es2015": "^6.6.0",
"babel-register": "^6.8.0", "babel-register": "^6.8.0",

View File

@@ -83,7 +83,7 @@ const BinaryParser = makeClass({
const value = kls.fromParser(this, sizeHint); const value = kls.fromParser(this, sizeHint);
if (value === undefined) { if (value === undefined) {
throw new Error( throw new Error(
`fromParser for (${field.name}, ${field.type.name}) -> undefined `); `fromParser for (${field.name}, ${field.type.name}) -> undefined `);
} }
return value; return value;
}, },

View File

@@ -10,8 +10,8 @@ const AccountID = makeClass({
statics: { statics: {
from(value) { from(value) {
return value instanceof this ? value : return value instanceof this ? value :
/^r/.test(value) ? this.fromBase58(value) : /^r/.test(value) ? this.fromBase58(value) :
new this(value); new this(value);
}, },
cache: {}, cache: {},
fromCache(base58) { fromCache(base58) {

View File

@@ -57,7 +57,8 @@ function raiseIllegalAmountError(value) {
const parsers = { const parsers = {
string(str) { string(str) {
if (!str.match(/\d+/)) { // Using /^\d+$/ here fixes #31
if (!str.match(/^\d+$/)) {
raiseIllegalAmountError(str); raiseIllegalAmountError(str);
} }
return [new Decimal(str).dividedBy(DROPS_PER_XRP), Currency.XRP]; return [new Decimal(str).dividedBy(DROPS_PER_XRP), Currency.XRP];
@@ -66,8 +67,8 @@ const parsers = {
assert(isDefined(object.currency), 'currency must be defined'); assert(isDefined(object.currency), 'currency must be defined');
assert(isDefined(object.issuer), 'issuer must be defined'); assert(isDefined(object.issuer), 'issuer must be defined');
return [new Decimal(object.value), return [new Decimal(object.value),
Currency.from(object.currency), Currency.from(object.currency),
AccountID.from(object.issuer)]; AccountID.from(object.issuer)];
} }
}; };
@@ -109,7 +110,7 @@ const Amount = makeClass({
mantissa[1] &= 0x3F; mantissa[1] &= 0x3F;
// decimal.js won't accept e notation with hex // decimal.js won't accept e notation with hex
const value = new Decimal(`${sign}0x${bytesToHex(mantissa)}`) const value = new Decimal(`${sign}0x${bytesToHex(mantissa)}`)
.times('1e' + exponent); .times('1e' + exponent);
return new this(value, currency, issuer, false); return new this(value, currency, issuer, false);
} }
@@ -128,6 +129,7 @@ const Amount = makeClass({
// value is in XRP scale, but show the value in canonical json form // value is in XRP scale, but show the value in canonical json form
raiseIllegalAmountError(this.value.times(DROPS_PER_XRP)) raiseIllegalAmountError(this.value.times(DROPS_PER_XRP))
} }
this.verifyNoDecimal(this.value); // This is a secondary fix for #31
} else { } else {
const p = this.value.precision(); const p = this.value.precision();
const e = this.exponent(); const e = this.exponent();
@@ -143,8 +145,24 @@ const Amount = makeClass({
return this.currency.isNative(); return this.currency.isNative();
}, },
mantissa() { mantissa() {
// This is a tertiary fix for #31
const integerNumberString = this.verifyNoDecimal();
return new UInt64( return new UInt64(
new BN(this.value.times('1e' + -this.exponent()).abs().toString())); new BN(integerNumberString));
},
verifyNoDecimal() {
const integerNumberString = this.value
.times('1e' + -this.exponent()).abs().toString();
// Ensure that the value (after being multiplied by the exponent)
// does not contain a decimal. From the bn.js README:
// "decimals are not supported in this library."
// eslint-disable-next-line max-len
// https://github.com/indutny/bn.js/blob/9cb459f044853b46615464eea1a3ddfc7006463b/README.md
if (integerNumberString.indexOf('.') !== -1) {
raiseIllegalAmountError(integerNumberString);
}
return integerNumberString;
}, },
isZero() { isZero() {
return this.value.isZero(); return this.value.isZero();
@@ -154,7 +172,7 @@ const Amount = makeClass({
}, },
valueString() { valueString() {
return (this.isNative() ? this.value.times(DROPS_PER_XRP) : this.value) return (this.isNative() ? this.value.times(DROPS_PER_XRP) : this.value)
.toString(); .toString();
}, },
toBytesSink(sink) { toBytesSink(sink) {
const isNative = this.isNative(); const isNative = this.isNative();

View File

@@ -7,7 +7,7 @@ const Hash = makeClass({
Hash(bytes) { Hash(bytes) {
const width = this.constructor.width; const width = this.constructor.width;
this._bytes = bytes ? parseBytes(bytes, Uint8Array) : this._bytes = bytes ? parseBytes(bytes, Uint8Array) :
new Uint8Array(width); new Uint8Array(width);
assert.equal(this._bytes.length, width); assert.equal(this._bytes.length, width);
}, },
mixins: [Comparable, SerializedType], mixins: [Comparable, SerializedType],

View File

@@ -22,7 +22,7 @@ const UInt = makeClass({
width: 0, width: 0,
fromParser(parser) { fromParser(parser) {
const val = this.width > 4 ? parser.read(this.width) : const val = this.width > 4 ? parser.read(this.width) :
parser.readUIntN(this.width); parser.readUIntN(this.width);
return new this(val); return new this(val);
}, },
from(val) { from(val) {
@@ -40,8 +40,8 @@ const UInt = makeClass({
const otherValue = other.valueOf(); const otherValue = other.valueOf();
if (thisValue instanceof BN) { if (thisValue instanceof BN) {
return otherValue instanceof BN ? return otherValue instanceof BN ?
thisValue.cmp(otherValue) : thisValue.cmp(otherValue) :
thisValue.cmpn(otherValue); thisValue.cmpn(otherValue);
} else if (otherValue instanceof BN) { } else if (otherValue instanceof BN) {
return -other.compareTo(this); return -other.compareTo(this);
} }

View File

@@ -16,15 +16,15 @@ describe('ripple-binary-codec', function() {
entries.forEach((t, test_n) => { entries.forEach((t, test_n) => {
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
it(`${name}[${test_n}] can encode ${truncateForDisplay(json(t.json))} to ${truncateForDisplay(t.binary)}`, it(`${name}[${test_n}] can encode ${truncateForDisplay(json(t.json))} to ${truncateForDisplay(t.binary)}`,
() => { () => {
assert.equal(t.binary, encode(t.json)); assert.equal(t.binary, encode(t.json));
}); });
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
it(`${name}[${test_n}] can decode ${truncateForDisplay(t.binary)} to ${truncateForDisplay(json(t.json))}`, it(`${name}[${test_n}] can decode ${truncateForDisplay(t.binary)} to ${truncateForDisplay(json(t.json))}`,
() => { () => {
const decoded = decode(t.binary); const decoded = decode(t.binary);
assert.deepEqual(t.json, decoded); assert.deepEqual(t.json, decoded);
}); });
}); });
}); });
} }
@@ -34,10 +34,10 @@ describe('ripple-binary-codec', function() {
describe('ledgerData', function() { describe('ledgerData', function() {
fixtures.ledgerData.forEach((t, test_n) => { fixtures.ledgerData.forEach((t, test_n) => {
it(`ledgerData[${test_n}] can decode ${t.binary} to ${json(t.json)}`, it(`ledgerData[${test_n}] can decode ${t.binary} to ${json(t.json)}`,
() => { () => {
const decoded = decodeLedgerData(t.binary); const decoded = decodeLedgerData(t.binary);
assert.deepEqual(t.json, decoded); assert.deepEqual(t.json, decoded);
}); });
}); });
}) })
}); });

View File

@@ -48,8 +48,8 @@ function transactionParsingTests() {
'SigningPubKey': 'SigningPubKey':
'028472865AF4CB32AA285834B57576B7290AA8C31B459047DB27E16F418D6A7166', '028472865AF4CB32AA285834B57576B7290AA8C31B459047DB27E16F418D6A7166',
'TakerGets': {'currency': 'ILS', 'TakerGets': {'currency': 'ILS',
'issuer': 'rNPRNzBB92BVpAhhZr4iXDTveCgV5Pofm9', 'issuer': 'rNPRNzBB92BVpAhhZr4iXDTveCgV5Pofm9',
'value': '1694.768'}, 'value': '1694.768'},
'TakerPays': '98957503520', 'TakerPays': '98957503520',
'TransactionType': 'OfferCreate', 'TransactionType': 'OfferCreate',
'TxnSignature': __(` 'TxnSignature': __(`
@@ -88,7 +88,7 @@ function transactionParsingTests() {
// amount currency // amount currency
assert(Hash160.fromParser(parser)); assert(Hash160.fromParser(parser));
assert.equal(encodeAccountID(parser.read(20)), assert.equal(encodeAccountID(parser.read(20)),
tx_json.TakerGets.issuer); tx_json.TakerGets.issuer);
assert.equal(parser.readField(), Field.Fee); assert.equal(parser.readField(), Field.Fee);
assert(parser.read(8)); assert(parser.read(8));
assert.equal(parser.readField(), Field.SigningPubKey); assert.equal(parser.readField(), Field.SigningPubKey);
@@ -209,8 +209,8 @@ function assertRecyclable(json, forField) {
const sink = new BytesList(); const sink = new BytesList();
Type.from(recycled).toBytesSink(sink); Type.from(recycled).toBytesSink(sink);
const recycledAgain = makeParser(sink.toHex()) const recycledAgain = makeParser(sink.toHex())
.readType(Type) .readType(Type)
.toJSON(); .toJSON();
assert.deepEqual(recycledAgain, json); assert.deepEqual(recycledAgain, json);
} }
@@ -285,36 +285,36 @@ function pathSetBinaryTests() {
const expectedJSON = const expectedJSON =
[[{account: 'r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K', [[{account: 'r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K',
currency: 'BTC', currency: 'BTC',
issuer: 'r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K'}, issuer: 'r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K'},
{account: 'rM1oqKtfh1zgjdAgbFmaRm3btfGBX25xVo', {account: 'rM1oqKtfh1zgjdAgbFmaRm3btfGBX25xVo',
currency: 'BTC', currency: 'BTC',
issuer: 'rM1oqKtfh1zgjdAgbFmaRm3btfGBX25xVo'}, issuer: 'rM1oqKtfh1zgjdAgbFmaRm3btfGBX25xVo'},
{account: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B', {account: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
currency: 'BTC', currency: 'BTC',
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'}, issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'},
{currency: 'USD', {currency: 'USD',
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'}], issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'}],
[{account: 'r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K', [{account: 'r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K',
currency: 'BTC', currency: 'BTC',
issuer: 'r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K'}, issuer: 'r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K'},
{account: 'rM1oqKtfh1zgjdAgbFmaRm3btfGBX25xVo', {account: 'rM1oqKtfh1zgjdAgbFmaRm3btfGBX25xVo',
currency: 'BTC', currency: 'BTC',
issuer: 'rM1oqKtfh1zgjdAgbFmaRm3btfGBX25xVo'}, issuer: 'rM1oqKtfh1zgjdAgbFmaRm3btfGBX25xVo'},
{account: 'rpvfJ4mR6QQAeogpXEKnuyGBx8mYCSnYZi', {account: 'rpvfJ4mR6QQAeogpXEKnuyGBx8mYCSnYZi',
currency: 'BTC', currency: 'BTC',
issuer: 'rpvfJ4mR6QQAeogpXEKnuyGBx8mYCSnYZi'}, issuer: 'rpvfJ4mR6QQAeogpXEKnuyGBx8mYCSnYZi'},
{currency: 'USD', {currency: 'USD',
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'}], issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'}],
[{account: 'r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K', [{account: 'r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K',
currency: 'BTC', currency: 'BTC',
issuer: 'r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K'}, issuer: 'r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K'},
{account: 'r3AWbdp2jQLXLywJypdoNwVSvr81xs3uhn', {account: 'r3AWbdp2jQLXLywJypdoNwVSvr81xs3uhn',
currency: 'BTC', currency: 'BTC',
issuer: 'r3AWbdp2jQLXLywJypdoNwVSvr81xs3uhn'}, issuer: 'r3AWbdp2jQLXLywJypdoNwVSvr81xs3uhn'},
{currency: '0000000000000000000000005852500000000000'}, {currency: '0000000000000000000000005852500000000000'},
{currency: 'USD', {currency: 'USD',
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'}]]; issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'}]];
it('works with long paths', () => { it('works with long paths', () => {
const parser = makeParser(bytes); const parser = makeParser(bytes);

View File

@@ -80,8 +80,8 @@ function nestedObjectTests() {
fixtures.whole_objects.forEach((f, i) => { fixtures.whole_objects.forEach((f, i) => {
it(`whole_objects[${i}]: can parse blob and dump out same blob`, it(`whole_objects[${i}]: can parse blob and dump out same blob`,
/* */ () => { /* */ () => {
assertRecycles(f.blob_with_no_signing); assertRecycles(f.blob_with_no_signing);
}); });
}); });
} }

View File

@@ -29,7 +29,7 @@ describe('Hash256', function() {
}); });
it('supports getting the nibblet values at given positions', function() { it('supports getting the nibblet values at given positions', function() {
const h = Hash256.from( const h = Hash256.from(
'1359BD0000000000000000000000000000000000000000000000000000000000'); '1359BD0000000000000000000000000000000000000000000000000000000000');
assert.equal(h.nibblet(0), 0x1); assert.equal(h.nibblet(0), 0x1);
assert.equal(h.nibblet(1), 0x3); assert.equal(h.nibblet(1), 0x3);
assert.equal(h.nibblet(2), 0x5); assert.equal(h.nibblet(2), 0x5);
@@ -53,6 +53,6 @@ describe('Currency', function() {
assert.throws(() => Currency.from(new Uint8Array(19))); assert.throws(() => Currency.from(new Uint8Array(19)));
assert.throws(() => Currency.from(1)); assert.throws(() => Currency.from(1));
assert.throws(() => Currency.from( assert.throws(() => Currency.from(
'00000000000000000000000000000000000000m')); '00000000000000000000000000000000000000m'));
}); });
}); });

View File

@@ -9,11 +9,11 @@ describe('Ledger Hashes', function() {
const ledger = loadFixture(ledgerFixture); const ledger = loadFixture(ledgerFixture);
it('computes correct account state hash', function() { it('computes correct account state hash', function() {
assert.equal(accountStateHash(ledger.accountState).toHex(), assert.equal(accountStateHash(ledger.accountState).toHex(),
ledger.account_hash); ledger.account_hash);
}); });
it('computes correct transaction tree hash', function() { it('computes correct transaction tree hash', function() {
assert.equal(transactionTreeHash(ledger.transactions).toHex(), assert.equal(transactionTreeHash(ledger.transactions).toHex(),
ledger.transaction_hash); ledger.transaction_hash);
}); });
it('computes correct ledger header hash', function() { it('computes correct ledger header hash', function() {
assert.equal(ledgerHash(ledger).toHex(), ledger.hash); assert.equal(ledgerHash(ledger).toHex(), ledger.hash);

View File

@@ -25,82 +25,82 @@ describe('Signing data', function() {
it('can create single signing blobs', function() { it('can create single signing blobs', function() {
const actual = encodeForSigning(tx_json); const actual = encodeForSigning(tx_json);
assert.equal(actual, assert.equal(actual,
['53545800', // signingPrefix ['53545800', // signingPrefix
// TransactionType // TransactionType
'12', '12',
'0000', '0000',
// Flags // Flags
'22', '22',
'80000000', '80000000',
// Sequence // Sequence
'24', '24',
'00000001', '00000001',
// Amount // Amount
'61', '61',
// native amount // native amount
'40000000000003E8', '40000000000003E8',
// Fee // Fee
'68', '68',
// native amount // native amount
'400000000000000A', '400000000000000A',
// SigningPubKey // SigningPubKey
'73', '73',
// VLLength // VLLength
'21', '21',
'ED5F5AC8B98974A3CA843326D9B88CEBD0560177B973EE0B149F782CFAA06DC66A', 'ED5F5AC8B98974A3CA843326D9B88CEBD0560177B973EE0B149F782CFAA06DC66A',
// Account // Account
'81', '81',
// VLLength // VLLength
'14', '14',
'5B812C9D57731E27A2DA8B1830195F88EF32A3B6', '5B812C9D57731E27A2DA8B1830195F88EF32A3B6',
// Destination // Destination
'83', '83',
// VLLength // VLLength
'14', '14',
'B5F762798A53D543A014CAF8B297CFF8F2F937E8'].join('') 'B5F762798A53D543A014CAF8B297CFF8F2F937E8'].join('')
); );
}); });
it('can create multi signing blobs', function() { it('can create multi signing blobs', function() {
const signingAccount = 'rJZdUusLDtY9NEsGea7ijqhVrXv98rYBYN'; const signingAccount = 'rJZdUusLDtY9NEsGea7ijqhVrXv98rYBYN';
const signingJson = _.assign({}, tx_json, {SigningPubKey: ''}); const signingJson = _.assign({}, tx_json, {SigningPubKey: ''});
const actual = encodeForMultisigning(signingJson, signingAccount); const actual = encodeForMultisigning(signingJson, signingAccount);
assert.equal(actual, assert.equal(actual,
['534D5400', // signingPrefix ['534D5400', // signingPrefix
// TransactionType // TransactionType
'12', '12',
'0000', '0000',
// Flags // Flags
'22', '22',
'80000000', '80000000',
// Sequence // Sequence
'24', '24',
'00000001', '00000001',
// Amount // Amount
'61', '61',
// native amount // native amount
'40000000000003E8', '40000000000003E8',
// Fee // Fee
'68', '68',
// native amount // native amount
'400000000000000A', '400000000000000A',
// SigningPubKey // SigningPubKey
'73', '73',
// VLLength // VLLength
'00', '00',
// '', // '',
// Account // Account
'81', '81',
// VLLength // VLLength
'14', '14',
'5B812C9D57731E27A2DA8B1830195F88EF32A3B6', '5B812C9D57731E27A2DA8B1830195F88EF32A3B6',
// Destination // Destination
'83', '83',
// VLLength // VLLength
'14', '14',
'B5F762798A53D543A014CAF8B297CFF8F2F937E8', 'B5F762798A53D543A014CAF8B297CFF8F2F937E8',
// signingAccount suffix // signingAccount suffix
'C0A5ABEF242802EFED4B041E8F2D4A8CC86AE3D1'].join('') 'C0A5ABEF242802EFED4B041E8F2D4A8CC86AE3D1'].join('')
); );
}); });
it('can create claim blob', function() { it('can create claim blob', function() {
const channel = const channel =

View File

@@ -0,0 +1,124 @@
const assert = require('assert');
const {
encode,
decode
} = require('../src')
// Notice: no Amount or Fee
const tx_json = {
Account: 'r9LqNeG6qHxjeUocjvVki2XR35weJ9mZgQ',
// Amount: '1000',
Destination: 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh',
// Fee: '10',
// JavaScript converts operands to 32-bit signed ints after doing bitwise
// operations. We need to convert it back to an unsigned int with >>> 0.
Flags: ((1 << 31) >>> 0), // tfFullyCanonicalSig
Sequence: 1,
TransactionType: 'Payment'
// TxnSignature,
// Signature,
// SigningPubKey
};
const amount_parameters_message = input => {
// disables the ESLint rule on the whole rest of the file
/* eslint-disable max-len */
return `${input} is an illegal amount
Native values must be described in drops, a million of which equal one XRP.
This must be an integer number, with the absolute value not exceeding 100000000000000000
IOU values must have a maximum precision of 16 significant digits. They are serialized as
a canonicalised mantissa and exponent.
The valid range for a mantissa is between 1000000000000000 and 9999999999999999
The exponent must be >= -96 and <= 80
Thus the largest serializable IOU value is:
999999999999999900000000000000000000000000000000000000000000000000000000000000000000000000000000
And the smallest:
0.000000000000000000000000000000000000000000000000000000000000000000000000000000001
`;
};
describe('encoding and decoding tx_json', function() {
it('can encode tx_json without Amount or Fee', function() {
const encoded = encode(tx_json);
const decoded = decode(encoded);
assert.deepStrictEqual(tx_json, decoded);
});
it('can encode tx_json with Amount and Fee', function() {
const my_tx = Object.assign({}, tx_json, {
Amount: '1000',
Fee: '10'
});
const encoded = encode(my_tx);
const decoded = decode(encoded);
assert.deepStrictEqual(my_tx, decoded);
});
it('throws when Amount is invalid', function() {
const my_tx = Object.assign({}, tx_json, {
Amount: '1000.001',
Fee: '10'
});
assert.throws(() => {
encode(my_tx);
}, {
name: 'Error',
message: amount_parameters_message('1000.001')
});
});
it('throws when Fee is invalid', function() {
const my_tx = Object.assign({}, tx_json, {
Amount: '1000',
Fee: '10.123'
});
assert.throws(() => {
encode(my_tx);
}, {
name: 'Error',
message: amount_parameters_message('10.123')
});
});
it('throws when Amount and Fee are invalid', function() {
const my_tx = Object.assign({}, tx_json, {
Amount: '1000.789',
Fee: '10.123'
});
assert.throws(() => {
encode(my_tx);
}, {
name: 'Error',
message: amount_parameters_message('1000.789')
});
});
it('throws when Amount is a number instead of a string-encoded integer',
function() {
const my_tx = Object.assign({}, tx_json, {
Amount: 1000.789
});
assert.throws(() => {
encode(my_tx);
},
{
name: 'Error',
message: 'unsupported value: 1000.789'
});
});
it('throws when Fee is a number instead of a string-encoded integer',
function() {
const my_tx = Object.assign({}, tx_json, {
Amount: 1234.56
});
assert.throws(() => {
encode(my_tx);
},
{
name: 'Error',
message: 'unsupported value: 1234.56'
});
});
});

View File

@@ -77,7 +77,7 @@ function assertEqualAmountJSON(actual, expected) {
assert.equal(actual.issuer, expected.issuer); assert.equal(actual.issuer, expected.issuer);
assert(actual.value === expected.value || assert(actual.value === expected.value ||
new Decimal(actual.value).equals( new Decimal(actual.value).equals(
new Decimal(expected.value))); new Decimal(expected.value)));
} }
module.exports = { module.exports = {

View File

@@ -2,6 +2,94 @@
# yarn lockfile v1 # yarn lockfile v1
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.5.5":
version "7.5.5"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.5.5.tgz#bc0782f6d69f7b7d49531219699b988f669a8f9d"
integrity sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==
dependencies:
"@babel/highlight" "^7.0.0"
"@babel/generator@^7.5.5":
version "7.5.5"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.5.5.tgz#873a7f936a3c89491b43536d12245b626664e3cf"
integrity sha512-ETI/4vyTSxTzGnU2c49XHv2zhExkv9JHLTwDAFz85kmcwuShvYG2H08FwgIguQf4JC75CBnXAUM5PqeF4fj0nQ==
dependencies:
"@babel/types" "^7.5.5"
jsesc "^2.5.1"
lodash "^4.17.13"
source-map "^0.5.0"
trim-right "^1.0.1"
"@babel/helper-function-name@^7.1.0":
version "7.1.0"
resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz#a0ceb01685f73355d4360c1247f582bfafc8ff53"
integrity sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==
dependencies:
"@babel/helper-get-function-arity" "^7.0.0"
"@babel/template" "^7.1.0"
"@babel/types" "^7.0.0"
"@babel/helper-get-function-arity@^7.0.0":
version "7.0.0"
resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz#83572d4320e2a4657263734113c42868b64e49c3"
integrity sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==
dependencies:
"@babel/types" "^7.0.0"
"@babel/helper-split-export-declaration@^7.4.4":
version "7.4.4"
resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz#ff94894a340be78f53f06af038b205c49d993677"
integrity sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==
dependencies:
"@babel/types" "^7.4.4"
"@babel/highlight@^7.0.0":
version "7.5.0"
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.5.0.tgz#56d11312bd9248fa619591d02472be6e8cb32540"
integrity sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==
dependencies:
chalk "^2.0.0"
esutils "^2.0.2"
js-tokens "^4.0.0"
"@babel/parser@^7.0.0", "@babel/parser@^7.4.4", "@babel/parser@^7.5.5":
version "7.5.5"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.5.5.tgz#02f077ac8817d3df4a832ef59de67565e71cca4b"
integrity sha512-E5BN68cqR7dhKan1SfqgPGhQ178bkVKpXTPEXnFJBrEt8/DKRZlybmy+IgYLTeN7tp1R5Ccmbm2rBk17sHYU3g==
"@babel/template@^7.1.0":
version "7.4.4"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.4.4.tgz#f4b88d1225689a08f5bc3a17483545be9e4ed237"
integrity sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw==
dependencies:
"@babel/code-frame" "^7.0.0"
"@babel/parser" "^7.4.4"
"@babel/types" "^7.4.4"
"@babel/traverse@^7.0.0":
version "7.5.5"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.5.5.tgz#f664f8f368ed32988cd648da9f72d5ca70f165bb"
integrity sha512-MqB0782whsfffYfSjH4TM+LMjrJnhCNEDMDIjeTpl+ASaUvxcjoiVCo/sM1GhS1pHOXYfWVCYneLjMckuUxDaQ==
dependencies:
"@babel/code-frame" "^7.5.5"
"@babel/generator" "^7.5.5"
"@babel/helper-function-name" "^7.1.0"
"@babel/helper-split-export-declaration" "^7.4.4"
"@babel/parser" "^7.5.5"
"@babel/types" "^7.5.5"
debug "^4.1.0"
globals "^11.1.0"
lodash "^4.17.13"
"@babel/types@^7.0.0", "@babel/types@^7.4.4", "@babel/types@^7.5.5":
version "7.5.5"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.5.5.tgz#97b9f728e182785909aa4ab56264f090a028d18a"
integrity sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==
dependencies:
esutils "^2.0.2"
lodash "^4.17.13"
to-fast-properties "^2.0.0"
abbrev@1: abbrev@1:
version "1.1.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
@@ -232,16 +320,17 @@ babel-core@^6.26.0, babel-core@^6.8.0:
slash "^1.0.0" slash "^1.0.0"
source-map "^0.5.7" source-map "^0.5.7"
babel-eslint@^6.0.4: babel-eslint@^10.0.2:
version "6.1.2" version "10.0.2"
resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-6.1.2.tgz#5293419fe3672d66598d327da9694567ba6a5f2f" resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.0.2.tgz#182d5ac204579ff0881684b040560fdcc1558456"
integrity sha1-UpNBn+NnLWZZjTJ9qWlFZ7pqXy8= integrity sha512-UdsurWPtgiPgpJ06ryUnuaSXC2s0WoSZnQmEpbAH65XZSdwowgN5MvyP7e88nW07FYXv72erVtpBkxyDVKhH1Q==
dependencies: dependencies:
babel-traverse "^6.0.20" "@babel/code-frame" "^7.0.0"
babel-types "^6.0.19" "@babel/parser" "^7.0.0"
babylon "^6.0.18" "@babel/traverse" "^7.0.0"
lodash.assign "^4.0.0" "@babel/types" "^7.0.0"
lodash.pickby "^4.0.0" eslint-scope "3.7.1"
eslint-visitor-keys "^1.0.0"
babel-generator@^6.26.0: babel-generator@^6.26.0:
version "6.26.1" version "6.26.1"
@@ -641,7 +730,7 @@ babel-template@^6.24.1, babel-template@^6.26.0:
babylon "^6.18.0" babylon "^6.18.0"
lodash "^4.17.4" lodash "^4.17.4"
babel-traverse@^6.0.20, babel-traverse@^6.24.1, babel-traverse@^6.26.0: babel-traverse@^6.24.1, babel-traverse@^6.26.0:
version "6.26.0" version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee" resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee"
integrity sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4= integrity sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=
@@ -656,7 +745,7 @@ babel-traverse@^6.0.20, babel-traverse@^6.24.1, babel-traverse@^6.26.0:
invariant "^2.2.2" invariant "^2.2.2"
lodash "^4.17.4" lodash "^4.17.4"
babel-types@^6.0.19, babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.26.0: babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.26.0:
version "6.26.0" version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497" resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497"
integrity sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc= integrity sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=
@@ -666,7 +755,7 @@ babel-types@^6.0.19, babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.26
lodash "^4.17.4" lodash "^4.17.4"
to-fast-properties "^1.0.3" to-fast-properties "^1.0.3"
babylon@^6.0.18, babylon@^6.18.0: babylon@^6.18.0:
version "6.18.0" version "6.18.0"
resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3"
integrity sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ== integrity sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==
@@ -1039,6 +1128,13 @@ debug@^3.1.0, debug@^3.2.6:
dependencies: dependencies:
ms "^2.1.1" ms "^2.1.1"
debug@^4.1.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==
dependencies:
ms "^2.1.1"
decimal.js@^5.0.8: decimal.js@^5.0.8:
version "5.0.8" version "5.0.8"
resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-5.0.8.tgz#b48c3fb7d73a2d4d4940e0b38f1cd21db5b367ce" resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-5.0.8.tgz#b48c3fb7d73a2d4d4940e0b38f1cd21db5b367ce"
@@ -1157,6 +1253,14 @@ escodegen@1.8.x:
optionalDependencies: optionalDependencies:
source-map "~0.2.0" source-map "~0.2.0"
eslint-scope@3.7.1:
version "3.7.1"
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.1.tgz#3d63c3edfda02e06e01a452ad88caacc7cdcb6e8"
integrity sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=
dependencies:
esrecurse "^4.1.0"
estraverse "^4.1.1"
eslint-scope@^3.7.1: eslint-scope@^3.7.1:
version "3.7.3" version "3.7.3"
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.3.tgz#bb507200d3d17f60247636160b4826284b108535" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.3.tgz#bb507200d3d17f60247636160b4826284b108535"
@@ -1555,7 +1659,7 @@ glob@^7.1.2, glob@^7.1.3:
once "^1.3.0" once "^1.3.0"
path-is-absolute "^1.0.0" path-is-absolute "^1.0.0"
globals@^11.0.1: globals@^11.0.1, globals@^11.1.0:
version "11.12.0" version "11.12.0"
resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
@@ -1992,7 +2096,7 @@ jade@0.26.3:
commander "0.6.1" commander "0.6.1"
mkdirp "0.3.0" mkdirp "0.3.0"
"js-tokens@^3.0.0 || ^4.0.0": "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
version "4.0.0" version "4.0.0"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
@@ -2015,6 +2119,11 @@ jsesc@^1.3.0:
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b"
integrity sha1-RsP+yMGJKxKwgz25vHYiF226s0s= integrity sha1-RsP+yMGJKxKwgz25vHYiF226s0s=
jsesc@^2.5.1:
version "2.5.2"
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4"
integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==
jsesc@~0.5.0: jsesc@~0.5.0:
version "0.5.0" version "0.5.0"
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d"
@@ -2116,11 +2225,6 @@ lodash._getnative@^3.0.0:
resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5"
integrity sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U= integrity sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=
lodash.assign@^4.0.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7"
integrity sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=
lodash.isarguments@^3.0.0: lodash.isarguments@^3.0.0:
version "3.1.0" version "3.1.0"
resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a"
@@ -2140,11 +2244,6 @@ lodash.keys@^3.0.0:
lodash.isarguments "^3.0.0" lodash.isarguments "^3.0.0"
lodash.isarray "^3.0.0" lodash.isarray "^3.0.0"
lodash.pickby@^4.0.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash.pickby/-/lodash.pickby-4.6.0.tgz#7dea21d8c18d7703a27c704c15d3b84a67e33aff"
integrity sha1-feoh2MGNdwOifHBMFdO4SmfjOv8=
lodash.toarray@^3.0.0: lodash.toarray@^3.0.0:
version "3.0.2" version "3.0.2"
resolved "https://registry.yarnpkg.com/lodash.toarray/-/lodash.toarray-3.0.2.tgz#2b204f0fa4f51c285c6f00c81d1cea5a23041179" resolved "https://registry.yarnpkg.com/lodash.toarray/-/lodash.toarray-3.0.2.tgz#2b204f0fa4f51c285c6f00c81d1cea5a23041179"
@@ -2159,6 +2258,11 @@ lodash@^4.12.0, lodash@^4.17.4, lodash@^4.3.0:
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.14.tgz#9ce487ae66c96254fe20b599f21b6816028078ba" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.14.tgz#9ce487ae66c96254fe20b599f21b6816028078ba"
integrity sha512-mmKYbW3GLuJeX+iGP+Y7Gp1AiGHGbXHCOh/jZmrawMmsE7MS4znI3RL2FsjbqOyMayHInjOeykW7PEajUk1/xw== integrity sha512-mmKYbW3GLuJeX+iGP+Y7Gp1AiGHGbXHCOh/jZmrawMmsE7MS4znI3RL2FsjbqOyMayHInjOeykW7PEajUk1/xw==
lodash@^4.17.13:
version "4.17.15"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
loose-envify@^1.0.0: loose-envify@^1.0.0:
version "1.4.0" version "1.4.0"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
@@ -3072,7 +3176,7 @@ source-map-url@^0.4.0:
resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3"
integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=
source-map@^0.5.6, source-map@^0.5.7: source-map@^0.5.0, source-map@^0.5.6, source-map@^0.5.7:
version "0.5.7" version "0.5.7"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
@@ -3261,6 +3365,11 @@ to-fast-properties@^1.0.3:
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47"
integrity sha1-uDVx+k2MJbguIxsG46MFXeTKGkc= integrity sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=
to-fast-properties@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=
to-object-path@^0.3.0: to-object-path@^0.3.0:
version "0.3.0" version "0.3.0"
resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af"