Files
xahau.js/packages/ripple-binary-codec/test/binary-parser-test.js
Elliot Lee d3b29945ea Restrict to node version 10
Many unit tests fail under node 12. It is possible that some of these
are caused by issues in node that will be fixed in the future.

This also removes the dependency on 'assert-diff' since it is not
necessary. It can easily be re-added in the future, if needed.
2019-07-16 21:55:42 -07:00

342 lines
12 KiB
JavaScript

/* eslint-disable func-style */
const coreTypes = require('../src/coretypes');
const _ = require('lodash');
const assert = require('assert');
const {encodeAccountID} = require('ripple-address-codec');
const {binary: {makeParser, readJSON}, Field, Amount, Hash160} = coreTypes;
const {enums: {TransactionType}} = coreTypes;
const utils = require('./utils');
const {parseHexOnly, assertEqualAmountJSON, hexOnly, loadFixture} = utils;
const {bytesToHex} = require('../src/utils/bytes-utils');
const fixtures = loadFixture('data-driven-tests.json');
const {BytesList} = require('../src/serdes/binary-serializer');
const __ = hexOnly;
function unused() {}
function toJSON(v) {
return v.toJSON ? v.toJSON() : v;
}
function basicApiTests() {
const bytes = parseHexOnly('00,01020304,0506', Uint8Array);
it('can read slices of bytes', () => {
const parser = makeParser(bytes);
assert.deepEqual(parser.pos(), 0);
assert(parser._buf instanceof Uint8Array);
const read1 = parser.read(1);
assert(read1 instanceof Uint8Array);
assert.deepEqual(read1, [0]);
assert.deepEqual(parser.read(4), [1, 2, 3, 4]);
assert.deepEqual(parser.read(2), [5, 6]);
assert.throws(() => parser.read(1));
});
it('can read a Uint32 at full', () => {
const parser = makeParser('FFFFFFFF');
assert.equal(parser.readUInt32(), 0xFFFFFFFF);
});
}
function transactionParsingTests() {
const transaction = {
json: {
'Account': 'raD5qJMAShLeHZXf9wjUmo6vRK4arj9cF3',
'Fee': '10',
'Flags': 0,
'Sequence': 103929,
'SigningPubKey':
'028472865AF4CB32AA285834B57576B7290AA8C31B459047DB27E16F418D6A7166',
'TakerGets': {'currency': 'ILS',
'issuer': 'rNPRNzBB92BVpAhhZr4iXDTveCgV5Pofm9',
'value': '1694.768'},
'TakerPays': '98957503520',
'TransactionType': 'OfferCreate',
'TxnSignature': __(`
304502202ABE08D5E78D1E74A4C18F2714F64E87B8BD57444AF
A5733109EB3C077077520022100DB335EE97386E4C0591CAC02
4D50E9230D8F171EEB901B5E5E4BD6D1E0AEF98C`)
},
binary: __(`
120007220000000024000195F964400000170A53AC2065D5460561E
C9DE000000000000000000000000000494C53000000000092D70596
8936C419CE614BF264B5EEB1CEA47FF468400000000000000A73210
28472865AF4CB32AA285834B57576B7290AA8C31B459047DB27E16F
418D6A71667447304502202ABE08D5E78D1E74A4C18F2714F64E87B
8BD57444AFA5733109EB3C077077520022100DB335EE97386E4C059
1CAC024D50E9230D8F171EEB901B5E5E4BD6D1E0AEF98C811439408
A69F0895E62149CFCC006FB89FA7D1E6E5D`)
};
const tx_json = transaction.json;
// These tests are basically development logs
it('can be done with low level apis', () => {
const parser = makeParser(transaction.binary);
assert.equal(parser.readField(), Field.TransactionType);
assert.equal(parser.readUInt16(), 7);
assert.equal(parser.readField(), Field.Flags);
assert.equal(parser.readUInt32(), 0);
assert.equal(parser.readField(), Field.Sequence);
assert.equal(parser.readUInt32(), 103929);
assert.equal(parser.readField(), Field.TakerPays);
parser.read(8);
assert.equal(parser.readField(), Field.TakerGets);
// amount value
assert(parser.read(8));
// amount currency
assert(Hash160.fromParser(parser));
assert.equal(encodeAccountID(parser.read(20)),
tx_json.TakerGets.issuer);
assert.equal(parser.readField(), Field.Fee);
assert(parser.read(8));
assert.equal(parser.readField(), Field.SigningPubKey);
assert.equal(parser.readVLLength(), 33);
assert.equal(bytesToHex(parser.read(33)), tx_json.SigningPubKey);
assert.equal(parser.readField(), Field.TxnSignature);
assert.equal(bytesToHex(parser.readVL()), tx_json.TxnSignature);
assert.equal(parser.readField(), Field.Account);
assert.equal(encodeAccountID(parser.readVL()), tx_json.Account);
assert(parser.end());
});
it('can be done with high level apis', () => {
const parser = makeParser(transaction.binary);
function readField() {
return parser.readFieldAndValue();
}
{
const [field, value] = readField();
assert.equal(field, Field.TransactionType);
assert.equal(value, TransactionType.OfferCreate);
}
{
const [field, value] = readField();
assert.equal(field, Field.Flags);
assert.equal(value, 0);
}
{
const [field, value] = readField();
assert.equal(field, Field.Sequence);
assert.equal(value, 103929);
}
{
const [field, value] = readField();
assert.equal(field, Field.TakerPays);
assert.equal(value.currency.isNative(), true);
assert.equal(value.currency.toJSON(), 'XRP');
}
{
const [field, value] = readField();
assert.equal(field, Field.TakerGets);
assert.equal(value.currency.isNative(), false);
assert.equal(value.issuer.toJSON(), tx_json.TakerGets.issuer);
}
{
const [field, value] = readField();
assert.equal(field, Field.Fee);
assert.equal(value.currency.isNative(), true);
}
{
const [field, value] = readField();
assert.equal(field, Field.SigningPubKey);
assert.equal(value.toJSON(), tx_json.SigningPubKey);
}
{
const [field, value] = readField();
assert.equal(field, Field.TxnSignature);
assert.equal(value.toJSON(), tx_json.TxnSignature);
}
{
const [field, value] = readField();
assert.equal(field, Field.Account);
assert.equal(value.toJSON(), tx_json.Account);
}
assert(parser.end());
});
it('can be done with higher level apis', () => {
const parser = makeParser(transaction.binary);
const jsonFromBinary = readJSON(parser);
assert.deepEqual(jsonFromBinary, tx_json);
});
it('readJSON (binary.decode) does not return STObject ', () => {
const parser = makeParser(transaction.binary);
const jsonFromBinary = readJSON(parser);
assert((jsonFromBinary instanceof coreTypes.STObject) === false);
assert(_.isPlainObject(jsonFromBinary));
});
}
function amountParsingTests() {
_.filter(fixtures.values_tests, {type: 'Amount'}).forEach((f, i) => {
if (f.error) {
return;
}
const parser = makeParser(f.expected_hex);
const testName =
`values_tests[${i}] parses ${f.expected_hex.slice(0, 16)}...
as ${JSON.stringify(f.test_json)}`;
it(testName, () => {
const value = parser.readType(Amount);
// May not actually be in canonical form. The fixtures are to be used
// also for json -> binary;
assertEqualAmountJSON(toJSON(value), f.test_json);
if (f.exponent) {
assert.equal(value.exponent(), f.exponent);
}
});
});
}
function fieldParsingTests() {
fixtures.fields_tests.forEach((f, i) => {
const parser = makeParser(f.expected_hex);
it(`fields[${i}]: parses ${f.expected_hex} as ${f.name}`, () => {
const field = parser.readField();
assert.equal(field.name, f.name);
assert.equal(field.type.name, f.type_name);
});
});
}
function assertRecyclable(json, forField) {
const Type = forField.associatedType;
const recycled = Type.from(json).toJSON();
assert.deepEqual(recycled, json);
const sink = new BytesList();
Type.from(recycled).toBytesSink(sink);
const recycledAgain = makeParser(sink.toHex())
.readType(Type)
.toJSON();
assert.deepEqual(recycledAgain, json);
}
function nestedObjectTests() {
function disabled(i) {
unused(i);
return false; // !_.includes([2], i);
}
fixtures.whole_objects.forEach((f, i) => {
if (disabled(i)) {
return;
}
it(`whole_objects[${i}]: can parse blob into
${JSON.stringify(f.tx_json)}`,
/* */ () => {
const parser = makeParser(f.blob_with_no_signing);
let ix = 0;
while (!parser.end()) {
const [field, value] = parser.readFieldAndValue();
const expected = f.fields[ix];
const expectedJSON = expected[1].json;
const expectedField = expected[0];
const actual = toJSON(value);
try {
assert.deepEqual(actual, expectedJSON);
} catch (e) {
throw new Error(`${e} ${field} a: ${actual} e: ${expectedJSON}`);
}
assert.equal(field.name, expectedField);
assertRecyclable(actual, field);
ix++;
}
});
});
}
function pathSetBinaryTests() {
const bytes = __(
`1200002200000000240000002E2E00004BF161D4C71AFD498D00000000000000
0000000000000055534400000000000A20B3C85F482532A9578DBB3950B85CA0
6594D168400000000000000A69D446F8038585E9400000000000000000000000
00425443000000000078CA21A6014541AB7B26C3929B9E0CD8C284D61C732103
A4665B1F0B7AE2BCA12E2DB80A192125BBEA660F80E9CEE137BA444C1B0769EC
7447304502205A964536805E35785C659D1F9670D057749AE39668175D6AA75D
25B218FE682E0221009252C0E5DDD5F2712A48F211669DE17B54113918E0D2C2
66F818095E9339D7D3811478CA21A6014541AB7B26C3929B9E0CD8C284D61C83
140A20B3C85F482532A9578DBB3950B85CA06594D1011231585E1F3BD02A15D6
185F8BB9B57CC60DEDDB37C10000000000000000000000004254430000000000
585E1F3BD02A15D6185F8BB9B57CC60DEDDB37C131E4FE687C90257D3D2D694C
8531CDEECBE84F33670000000000000000000000004254430000000000E4FE68
7C90257D3D2D694C8531CDEECBE84F3367310A20B3C85F482532A9578DBB3950
B85CA06594D100000000000000000000000042544300000000000A20B3C85F48
2532A9578DBB3950B85CA06594D1300000000000000000000000005553440000
0000000A20B3C85F482532A9578DBB3950B85CA06594D1FF31585E1F3BD02A15
D6185F8BB9B57CC60DEDDB37C100000000000000000000000042544300000000
00585E1F3BD02A15D6185F8BB9B57CC60DEDDB37C131E4FE687C90257D3D2D69
4C8531CDEECBE84F33670000000000000000000000004254430000000000E4FE
687C90257D3D2D694C8531CDEECBE84F33673115036E2D3F5437A83E5AC3CAEE
34FF2C21DEB618000000000000000000000000425443000000000015036E2D3F
5437A83E5AC3CAEE34FF2C21DEB6183000000000000000000000000055534400
000000000A20B3C85F482532A9578DBB3950B85CA06594D1FF31585E1F3BD02A
15D6185F8BB9B57CC60DEDDB37C1000000000000000000000000425443000000
0000585E1F3BD02A15D6185F8BB9B57CC60DEDDB37C13157180C769B66D942EE
69E6DCC940CA48D82337AD000000000000000000000000425443000000000057
180C769B66D942EE69E6DCC940CA48D82337AD10000000000000000000000000
58525000000000003000000000000000000000000055534400000000000A20B3
C85F482532A9578DBB3950B85CA06594D100`);
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'}]];
it('works with long paths', () => {
const parser = makeParser(bytes);
const txn = readJSON(parser);
assert.deepEqual(txn.Paths, expectedJSON);
// TODO: this should go elsewhere
assert.deepEqual(
coreTypes.PathSet.from(txn.Paths).toJSON(),
expectedJSON
);
});
}
describe('BinaryParser', function() {
function dataDrivenTests() {
describe('Amount parsing tests', amountParsingTests);
describe('Field Tests', fieldParsingTests);
describe('Parsing nested objects', nestedObjectTests);
}
describe('pathSetBinaryTests', pathSetBinaryTests);
describe('Basic API', basicApiTests);
describe('Parsing a transaction', transactionParsingTests);
describe('Data Driven Tests', dataDrivenTests);
});