mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-12-06 17:27:59 +00:00
Throw an error when Amount or Fee contains a decimal (Fix #31)
Thanks to @jwbusch for review
This commit is contained in:
@@ -16,15 +16,15 @@ describe('ripple-binary-codec', function() {
|
||||
entries.forEach((t, test_n) => {
|
||||
// eslint-disable-next-line max-len
|
||||
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
|
||||
it(`${name}[${test_n}] can decode ${truncateForDisplay(t.binary)} to ${truncateForDisplay(json(t.json))}`,
|
||||
() => {
|
||||
const decoded = decode(t.binary);
|
||||
assert.deepEqual(t.json, decoded);
|
||||
});
|
||||
() => {
|
||||
const decoded = decode(t.binary);
|
||||
assert.deepEqual(t.json, decoded);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -34,10 +34,10 @@ describe('ripple-binary-codec', function() {
|
||||
describe('ledgerData', function() {
|
||||
fixtures.ledgerData.forEach((t, test_n) => {
|
||||
it(`ledgerData[${test_n}] can decode ${t.binary} to ${json(t.json)}`,
|
||||
() => {
|
||||
const decoded = decodeLedgerData(t.binary);
|
||||
assert.deepEqual(t.json, decoded);
|
||||
});
|
||||
() => {
|
||||
const decoded = decodeLedgerData(t.binary);
|
||||
assert.deepEqual(t.json, decoded);
|
||||
});
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
@@ -48,8 +48,8 @@ function transactionParsingTests() {
|
||||
'SigningPubKey':
|
||||
'028472865AF4CB32AA285834B57576B7290AA8C31B459047DB27E16F418D6A7166',
|
||||
'TakerGets': {'currency': 'ILS',
|
||||
'issuer': 'rNPRNzBB92BVpAhhZr4iXDTveCgV5Pofm9',
|
||||
'value': '1694.768'},
|
||||
'issuer': 'rNPRNzBB92BVpAhhZr4iXDTveCgV5Pofm9',
|
||||
'value': '1694.768'},
|
||||
'TakerPays': '98957503520',
|
||||
'TransactionType': 'OfferCreate',
|
||||
'TxnSignature': __(`
|
||||
@@ -88,7 +88,7 @@ function transactionParsingTests() {
|
||||
// amount currency
|
||||
assert(Hash160.fromParser(parser));
|
||||
assert.equal(encodeAccountID(parser.read(20)),
|
||||
tx_json.TakerGets.issuer);
|
||||
tx_json.TakerGets.issuer);
|
||||
assert.equal(parser.readField(), Field.Fee);
|
||||
assert(parser.read(8));
|
||||
assert.equal(parser.readField(), Field.SigningPubKey);
|
||||
@@ -209,8 +209,8 @@ function assertRecyclable(json, forField) {
|
||||
const sink = new BytesList();
|
||||
Type.from(recycled).toBytesSink(sink);
|
||||
const recycledAgain = makeParser(sink.toHex())
|
||||
.readType(Type)
|
||||
.toJSON();
|
||||
.readType(Type)
|
||||
.toJSON();
|
||||
assert.deepEqual(recycledAgain, json);
|
||||
}
|
||||
|
||||
@@ -285,36 +285,36 @@ function pathSetBinaryTests() {
|
||||
|
||||
const expectedJSON =
|
||||
[[{account: 'r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K',
|
||||
currency: 'BTC',
|
||||
issuer: 'r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K'},
|
||||
{account: 'rM1oqKtfh1zgjdAgbFmaRm3btfGBX25xVo',
|
||||
currency: 'BTC',
|
||||
issuer: 'rM1oqKtfh1zgjdAgbFmaRm3btfGBX25xVo'},
|
||||
{account: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
|
||||
currency: 'BTC',
|
||||
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'},
|
||||
{currency: 'USD',
|
||||
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'}],
|
||||
[{account: 'r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K',
|
||||
currency: 'BTC',
|
||||
issuer: 'r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K'},
|
||||
{account: 'rM1oqKtfh1zgjdAgbFmaRm3btfGBX25xVo',
|
||||
currency: 'BTC',
|
||||
issuer: 'rM1oqKtfh1zgjdAgbFmaRm3btfGBX25xVo'},
|
||||
{account: 'rpvfJ4mR6QQAeogpXEKnuyGBx8mYCSnYZi',
|
||||
currency: 'BTC',
|
||||
issuer: 'rpvfJ4mR6QQAeogpXEKnuyGBx8mYCSnYZi'},
|
||||
{currency: 'USD',
|
||||
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'}],
|
||||
[{account: 'r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K',
|
||||
currency: 'BTC',
|
||||
issuer: 'r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K'},
|
||||
{account: 'r3AWbdp2jQLXLywJypdoNwVSvr81xs3uhn',
|
||||
currency: 'BTC',
|
||||
issuer: 'r3AWbdp2jQLXLywJypdoNwVSvr81xs3uhn'},
|
||||
{currency: '0000000000000000000000005852500000000000'},
|
||||
{currency: 'USD',
|
||||
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'}]];
|
||||
currency: 'BTC',
|
||||
issuer: 'r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K'},
|
||||
{account: 'rM1oqKtfh1zgjdAgbFmaRm3btfGBX25xVo',
|
||||
currency: 'BTC',
|
||||
issuer: 'rM1oqKtfh1zgjdAgbFmaRm3btfGBX25xVo'},
|
||||
{account: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
|
||||
currency: 'BTC',
|
||||
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'},
|
||||
{currency: 'USD',
|
||||
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'}],
|
||||
[{account: 'r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K',
|
||||
currency: 'BTC',
|
||||
issuer: 'r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K'},
|
||||
{account: 'rM1oqKtfh1zgjdAgbFmaRm3btfGBX25xVo',
|
||||
currency: 'BTC',
|
||||
issuer: 'rM1oqKtfh1zgjdAgbFmaRm3btfGBX25xVo'},
|
||||
{account: 'rpvfJ4mR6QQAeogpXEKnuyGBx8mYCSnYZi',
|
||||
currency: 'BTC',
|
||||
issuer: 'rpvfJ4mR6QQAeogpXEKnuyGBx8mYCSnYZi'},
|
||||
{currency: 'USD',
|
||||
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'}],
|
||||
[{account: 'r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K',
|
||||
currency: 'BTC',
|
||||
issuer: 'r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K'},
|
||||
{account: 'r3AWbdp2jQLXLywJypdoNwVSvr81xs3uhn',
|
||||
currency: 'BTC',
|
||||
issuer: 'r3AWbdp2jQLXLywJypdoNwVSvr81xs3uhn'},
|
||||
{currency: '0000000000000000000000005852500000000000'},
|
||||
{currency: 'USD',
|
||||
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'}]];
|
||||
|
||||
it('works with long paths', () => {
|
||||
const parser = makeParser(bytes);
|
||||
|
||||
@@ -80,8 +80,8 @@ function nestedObjectTests() {
|
||||
fixtures.whole_objects.forEach((f, i) => {
|
||||
it(`whole_objects[${i}]: can parse blob and dump out same blob`,
|
||||
/* */ () => {
|
||||
assertRecycles(f.blob_with_no_signing);
|
||||
});
|
||||
assertRecycles(f.blob_with_no_signing);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ describe('Hash256', function() {
|
||||
});
|
||||
it('supports getting the nibblet values at given positions', function() {
|
||||
const h = Hash256.from(
|
||||
'1359BD0000000000000000000000000000000000000000000000000000000000');
|
||||
'1359BD0000000000000000000000000000000000000000000000000000000000');
|
||||
assert.equal(h.nibblet(0), 0x1);
|
||||
assert.equal(h.nibblet(1), 0x3);
|
||||
assert.equal(h.nibblet(2), 0x5);
|
||||
@@ -53,6 +53,6 @@ describe('Currency', function() {
|
||||
assert.throws(() => Currency.from(new Uint8Array(19)));
|
||||
assert.throws(() => Currency.from(1));
|
||||
assert.throws(() => Currency.from(
|
||||
'00000000000000000000000000000000000000m'));
|
||||
'00000000000000000000000000000000000000m'));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,11 +9,11 @@ describe('Ledger Hashes', function() {
|
||||
const ledger = loadFixture(ledgerFixture);
|
||||
it('computes correct account state hash', function() {
|
||||
assert.equal(accountStateHash(ledger.accountState).toHex(),
|
||||
ledger.account_hash);
|
||||
ledger.account_hash);
|
||||
});
|
||||
it('computes correct transaction tree hash', function() {
|
||||
assert.equal(transactionTreeHash(ledger.transactions).toHex(),
|
||||
ledger.transaction_hash);
|
||||
ledger.transaction_hash);
|
||||
});
|
||||
it('computes correct ledger header hash', function() {
|
||||
assert.equal(ledgerHash(ledger).toHex(), ledger.hash);
|
||||
|
||||
@@ -25,82 +25,82 @@ describe('Signing data', function() {
|
||||
it('can create single signing blobs', function() {
|
||||
const actual = encodeForSigning(tx_json);
|
||||
assert.equal(actual,
|
||||
['53545800', // signingPrefix
|
||||
// TransactionType
|
||||
'12',
|
||||
'0000',
|
||||
// Flags
|
||||
'22',
|
||||
'80000000',
|
||||
// Sequence
|
||||
'24',
|
||||
'00000001',
|
||||
// Amount
|
||||
'61',
|
||||
// native amount
|
||||
'40000000000003E8',
|
||||
// Fee
|
||||
'68',
|
||||
// native amount
|
||||
'400000000000000A',
|
||||
// SigningPubKey
|
||||
'73',
|
||||
// VLLength
|
||||
'21',
|
||||
'ED5F5AC8B98974A3CA843326D9B88CEBD0560177B973EE0B149F782CFAA06DC66A',
|
||||
// Account
|
||||
'81',
|
||||
// VLLength
|
||||
'14',
|
||||
'5B812C9D57731E27A2DA8B1830195F88EF32A3B6',
|
||||
// Destination
|
||||
'83',
|
||||
// VLLength
|
||||
'14',
|
||||
'B5F762798A53D543A014CAF8B297CFF8F2F937E8'].join('')
|
||||
);
|
||||
['53545800', // signingPrefix
|
||||
// TransactionType
|
||||
'12',
|
||||
'0000',
|
||||
// Flags
|
||||
'22',
|
||||
'80000000',
|
||||
// Sequence
|
||||
'24',
|
||||
'00000001',
|
||||
// Amount
|
||||
'61',
|
||||
// native amount
|
||||
'40000000000003E8',
|
||||
// Fee
|
||||
'68',
|
||||
// native amount
|
||||
'400000000000000A',
|
||||
// SigningPubKey
|
||||
'73',
|
||||
// VLLength
|
||||
'21',
|
||||
'ED5F5AC8B98974A3CA843326D9B88CEBD0560177B973EE0B149F782CFAA06DC66A',
|
||||
// Account
|
||||
'81',
|
||||
// VLLength
|
||||
'14',
|
||||
'5B812C9D57731E27A2DA8B1830195F88EF32A3B6',
|
||||
// Destination
|
||||
'83',
|
||||
// VLLength
|
||||
'14',
|
||||
'B5F762798A53D543A014CAF8B297CFF8F2F937E8'].join('')
|
||||
);
|
||||
});
|
||||
it('can create multi signing blobs', function() {
|
||||
const signingAccount = 'rJZdUusLDtY9NEsGea7ijqhVrXv98rYBYN';
|
||||
const signingJson = _.assign({}, tx_json, {SigningPubKey: ''});
|
||||
const actual = encodeForMultisigning(signingJson, signingAccount);
|
||||
assert.equal(actual,
|
||||
['534D5400', // signingPrefix
|
||||
// TransactionType
|
||||
'12',
|
||||
'0000',
|
||||
// Flags
|
||||
'22',
|
||||
'80000000',
|
||||
// Sequence
|
||||
'24',
|
||||
'00000001',
|
||||
// Amount
|
||||
'61',
|
||||
// native amount
|
||||
'40000000000003E8',
|
||||
// Fee
|
||||
'68',
|
||||
// native amount
|
||||
'400000000000000A',
|
||||
// SigningPubKey
|
||||
'73',
|
||||
// VLLength
|
||||
'00',
|
||||
// '',
|
||||
// Account
|
||||
'81',
|
||||
// VLLength
|
||||
'14',
|
||||
'5B812C9D57731E27A2DA8B1830195F88EF32A3B6',
|
||||
// Destination
|
||||
'83',
|
||||
// VLLength
|
||||
'14',
|
||||
'B5F762798A53D543A014CAF8B297CFF8F2F937E8',
|
||||
// signingAccount suffix
|
||||
'C0A5ABEF242802EFED4B041E8F2D4A8CC86AE3D1'].join('')
|
||||
);
|
||||
['534D5400', // signingPrefix
|
||||
// TransactionType
|
||||
'12',
|
||||
'0000',
|
||||
// Flags
|
||||
'22',
|
||||
'80000000',
|
||||
// Sequence
|
||||
'24',
|
||||
'00000001',
|
||||
// Amount
|
||||
'61',
|
||||
// native amount
|
||||
'40000000000003E8',
|
||||
// Fee
|
||||
'68',
|
||||
// native amount
|
||||
'400000000000000A',
|
||||
// SigningPubKey
|
||||
'73',
|
||||
// VLLength
|
||||
'00',
|
||||
// '',
|
||||
// Account
|
||||
'81',
|
||||
// VLLength
|
||||
'14',
|
||||
'5B812C9D57731E27A2DA8B1830195F88EF32A3B6',
|
||||
// Destination
|
||||
'83',
|
||||
// VLLength
|
||||
'14',
|
||||
'B5F762798A53D543A014CAF8B297CFF8F2F937E8',
|
||||
// signingAccount suffix
|
||||
'C0A5ABEF242802EFED4B041E8F2D4A8CC86AE3D1'].join('')
|
||||
);
|
||||
});
|
||||
it('can create claim blob', function() {
|
||||
const channel =
|
||||
|
||||
124
packages/ripple-binary-codec/test/tx-encode-decode-test.js
Normal file
124
packages/ripple-binary-codec/test/tx-encode-decode-test.js
Normal 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'
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -77,7 +77,7 @@ function assertEqualAmountJSON(actual, expected) {
|
||||
assert.equal(actual.issuer, expected.issuer);
|
||||
assert(actual.value === expected.value ||
|
||||
new Decimal(actual.value).equals(
|
||||
new Decimal(expected.value)));
|
||||
new Decimal(expected.value)));
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||
Reference in New Issue
Block a user