mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-11-21 12:45:50 +00:00
[FIX] on connect return error if server doesn't have validated ledgers
This commit is contained in:
@@ -92,8 +92,8 @@ class RippleAPI extends EventEmitter {
|
|||||||
this.connection.on('connected', () => {
|
this.connection.on('connected', () => {
|
||||||
this.emit('connected');
|
this.emit('connected');
|
||||||
});
|
});
|
||||||
this.connection.on('disconnected', onError => {
|
this.connection.on('disconnected', code => {
|
||||||
this.emit('disconnected', onError);
|
this.emit('disconnected', code);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// use null object pattern to provide better error message if user
|
// use null object pattern to provide better error message if user
|
||||||
|
|||||||
@@ -146,25 +146,36 @@ class Connection extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_onOpen() {
|
_onOpen() {
|
||||||
this._ws.removeListener('close', this._onUnexpectedCloseBound);
|
|
||||||
this._onUnexpectedCloseBound =
|
|
||||||
this._onUnexpectedClose.bind(this, false, null, null);
|
|
||||||
this._ws.once('close', this._onUnexpectedCloseBound);
|
|
||||||
|
|
||||||
this._ws.removeListener('error', this._onOpenErrorBound);
|
this._ws.removeListener('error', this._onOpenErrorBound);
|
||||||
this._onOpenErrorBound = null;
|
this._onOpenErrorBound = null;
|
||||||
this._retry = 0;
|
|
||||||
this._ws.on('error', error =>
|
|
||||||
this.emit('error', 'websocket', error.message, error));
|
|
||||||
|
|
||||||
const request = {
|
const request = {
|
||||||
command: 'subscribe',
|
command: 'subscribe',
|
||||||
streams: ['ledger']
|
streams: ['ledger']
|
||||||
};
|
};
|
||||||
return this.request(request).then(data => {
|
return this.request(request).then(data => {
|
||||||
|
if (_.isEmpty(data) || !data.ledger_index) {
|
||||||
|
// rippled instance doesn't have validated ledgers
|
||||||
|
return this._disconnect(false).then(() => {
|
||||||
|
throw new NotConnectedError('Rippled not initialized');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
this._updateLedgerVersions(data);
|
this._updateLedgerVersions(data);
|
||||||
|
|
||||||
|
this._ws.removeListener('close', this._onUnexpectedCloseBound);
|
||||||
|
this._onUnexpectedCloseBound =
|
||||||
|
this._onUnexpectedClose.bind(this, false, null, null);
|
||||||
|
this._ws.once('close', this._onUnexpectedCloseBound);
|
||||||
|
|
||||||
|
this._retry = 0;
|
||||||
|
this._ws.on('error', error =>
|
||||||
|
this.emit('error', 'websocket', error.message, error));
|
||||||
|
|
||||||
this._isReady = true;
|
this._isReady = true;
|
||||||
this.emit('connected');
|
this.emit('connected');
|
||||||
|
|
||||||
|
return undefined;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -252,8 +263,14 @@ class Connection extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
disconnect() {
|
disconnect() {
|
||||||
this._clearReconnectTimer();
|
return this._disconnect(true);
|
||||||
this._retry = 0;
|
}
|
||||||
|
|
||||||
|
_disconnect(calledByUser) {
|
||||||
|
if (calledByUser) {
|
||||||
|
this._clearReconnectTimer();
|
||||||
|
this._retry = 0;
|
||||||
|
}
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
if (this._state === WebSocket.CLOSED) {
|
if (this._state === WebSocket.CLOSED) {
|
||||||
resolve();
|
resolve();
|
||||||
@@ -264,7 +281,9 @@ class Connection extends EventEmitter {
|
|||||||
this._ws.once('close', code => {
|
this._ws.once('close', code => {
|
||||||
this._ws = null;
|
this._ws = null;
|
||||||
this._isReady = false;
|
this._isReady = false;
|
||||||
this.emit('disconnected', code || 1000); // 1000 - CLOSE_NORMAL
|
if (calledByUser) {
|
||||||
|
this.emit('disconnected', code || 1000); // 1000 - CLOSE_NORMAL
|
||||||
|
}
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
this._ws.close();
|
this._ws.close();
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
'use strict';
|
'use strict'; // eslint-disable-line
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const {convertKeysFromSnakeCaseToCamelCase} = require('./utils');
|
const {convertKeysFromSnakeCaseToCamelCase} = require('./utils');
|
||||||
import type {Connection} from './connection';
|
import type {Connection} from './connection';
|
||||||
@@ -43,18 +43,20 @@ function getServerInfo(connection: Connection): Promise<GetServerInfoResponse> {
|
|||||||
return connection.request({command: 'server_info'}).then(response => {
|
return connection.request({command: 'server_info'}).then(response => {
|
||||||
const info = convertKeysFromSnakeCaseToCamelCase(response.info);
|
const info = convertKeysFromSnakeCaseToCamelCase(response.info);
|
||||||
renameKeys(info, {hostid: 'hostID'});
|
renameKeys(info, {hostid: 'hostID'});
|
||||||
renameKeys(info.validatedLedger, {
|
if (info.validatedLedger) {
|
||||||
baseFeeXrp: 'baseFeeXRP',
|
renameKeys(info.validatedLedger, {
|
||||||
reserveBaseXrp: 'reserveBaseXRP',
|
baseFeeXrp: 'baseFeeXRP',
|
||||||
reserveIncXrp: 'reserveIncrementXRP',
|
reserveBaseXrp: 'reserveBaseXRP',
|
||||||
seq: 'ledgerVersion'
|
reserveIncXrp: 'reserveIncrementXRP',
|
||||||
});
|
seq: 'ledgerVersion'
|
||||||
info.validatedLedger.baseFeeXRP =
|
});
|
||||||
info.validatedLedger.baseFeeXRP.toString();
|
info.validatedLedger.baseFeeXRP =
|
||||||
info.validatedLedger.reserveBaseXRP =
|
info.validatedLedger.baseFeeXRP.toString();
|
||||||
info.validatedLedger.reserveBaseXRP.toString();
|
info.validatedLedger.reserveBaseXRP =
|
||||||
info.validatedLedger.reserveIncrementXRP =
|
info.validatedLedger.reserveBaseXRP.toString();
|
||||||
info.validatedLedger.reserveIncrementXRP.toString();
|
info.validatedLedger.reserveIncrementXRP =
|
||||||
|
info.validatedLedger.reserveIncrementXRP.toString();
|
||||||
|
}
|
||||||
return info;
|
return info;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/* eslint-disable max-nested-callbacks */
|
/* eslint-disable max-nested-callbacks */
|
||||||
'use strict';
|
'use strict'; // eslint-disable-line
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const assert = require('assert-diff');
|
const assert = require('assert-diff');
|
||||||
const setupAPI = require('./setup-api');
|
const setupAPI = require('./setup-api');
|
||||||
@@ -981,6 +981,19 @@ describe('RippleAPI', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('getServerInfo - no validated ledger', function() {
|
||||||
|
this.api.connection._send(JSON.stringify({
|
||||||
|
command: 'config',
|
||||||
|
data: {serverInfoWithoutValidated: true}
|
||||||
|
}));
|
||||||
|
|
||||||
|
return this.api.getServerInfo().then(info => {
|
||||||
|
assert.strictEqual(info.networkLedger, 'waiting');
|
||||||
|
}).catch(error => {
|
||||||
|
assert(false, 'Should not throw Error, got ' + String(error));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('getFee', function() {
|
it('getFee', function() {
|
||||||
return this.api.getFee().then(fee => {
|
return this.api.getFee().then(fee => {
|
||||||
assert.strictEqual(fee, '0.000012');
|
assert.strictEqual(fee, '0.000012');
|
||||||
|
|||||||
@@ -379,4 +379,58 @@ describe('Connection', function() {
|
|||||||
});
|
});
|
||||||
this.api.connection._ws.emit('message', JSON.stringify(message));
|
this.api.connection._ws.emit('message', JSON.stringify(message));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should throw NotConnectedError if server does not have validated ledgers',
|
||||||
|
function() {
|
||||||
|
if (process.browser) {
|
||||||
|
// do not work in browser now, skipping
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.timeout(3000);
|
||||||
|
|
||||||
|
this.api.connection._send(JSON.stringify({
|
||||||
|
command: 'global_config',
|
||||||
|
data: {returnEmptySubscribeRequest: 1}
|
||||||
|
}));
|
||||||
|
|
||||||
|
const api = new RippleAPI({server: this.api.connection._url});
|
||||||
|
return api.connect().then(() => {
|
||||||
|
assert(false, 'Must have thrown!');
|
||||||
|
}, error => {
|
||||||
|
assert(error instanceof this.api.errors.NotConnectedError,
|
||||||
|
'Must throw NotConnectedError, got instead ' + String(error));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should try to reconnect on empty subscribe response on reconnect',
|
||||||
|
function(done) {
|
||||||
|
if (process.browser) {
|
||||||
|
// do not work in browser now, skipping
|
||||||
|
done();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.timeout(23000);
|
||||||
|
|
||||||
|
this.api.on('error', error => {
|
||||||
|
done(error || new Error('Should not emit error.'));
|
||||||
|
});
|
||||||
|
let disconncedCount = 0;
|
||||||
|
this.api.on('connected', () => {
|
||||||
|
done(disconncedCount !== 1 ?
|
||||||
|
new Error('Wrong number of disconnects') : undefined);
|
||||||
|
});
|
||||||
|
this.api.on('disconnected', () => {
|
||||||
|
disconncedCount++;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.api.connection._send(JSON.stringify({
|
||||||
|
command: 'global_config',
|
||||||
|
data: {returnEmptySubscribeRequest: 3}
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.api.connection._send(JSON.stringify({
|
||||||
|
command: 'test_command',
|
||||||
|
data: {disconnectIn: 10}
|
||||||
|
}));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
7
test/fixtures/rippled/empty.json
vendored
Normal file
7
test/fixtures/rippled/empty.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"id": 0,
|
||||||
|
"status": "success",
|
||||||
|
"type": "response",
|
||||||
|
"result": {
|
||||||
|
}
|
||||||
|
}
|
||||||
4
test/fixtures/rippled/index.js
vendored
4
test/fixtures/rippled/index.js
vendored
@@ -1,4 +1,4 @@
|
|||||||
'use strict';
|
'use strict'; // eslint-disable-line
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
submit: {
|
submit: {
|
||||||
@@ -12,6 +12,7 @@ module.exports = {
|
|||||||
withSettingsTx: require('./ledger-with-settings-tx'),
|
withSettingsTx: require('./ledger-with-settings-tx'),
|
||||||
withStateAsHashes: require('./ledger-with-state-as-hashes')
|
withStateAsHashes: require('./ledger-with-state-as-hashes')
|
||||||
},
|
},
|
||||||
|
empty: require('./empty'),
|
||||||
subscribe: require('./subscribe'),
|
subscribe: require('./subscribe'),
|
||||||
unsubscribe: require('./unsubscribe'),
|
unsubscribe: require('./unsubscribe'),
|
||||||
account_info: {
|
account_info: {
|
||||||
@@ -31,6 +32,7 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
server_info: {
|
server_info: {
|
||||||
normal: require('./server-info'),
|
normal: require('./server-info'),
|
||||||
|
noValidated: require('./server-info-no-validated'),
|
||||||
error: require('./server-info-error')
|
error: require('./server-info-error')
|
||||||
},
|
},
|
||||||
path_find: {
|
path_find: {
|
||||||
|
|||||||
31
test/fixtures/rippled/server-info-no-validated.json
vendored
Normal file
31
test/fixtures/rippled/server-info-no-validated.json
vendored
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"id": 0,
|
||||||
|
"status": "success",
|
||||||
|
"type": "response",
|
||||||
|
"result": {
|
||||||
|
"info": {
|
||||||
|
"build_version": "0.30.1-hf2",
|
||||||
|
"complete_ledgers": "empty",
|
||||||
|
"hostid": "ARTS",
|
||||||
|
"io_latency_ms": 1,
|
||||||
|
"last_close": {
|
||||||
|
"converge_time_s": 2.007,
|
||||||
|
"proposers": 4
|
||||||
|
},
|
||||||
|
"load_factor": 1,
|
||||||
|
"peers": 53,
|
||||||
|
"pubkey_node": "n94wWvFUmaKGYrKUGgpv1DyYgDeXRGdACkNQaSe7zJiy5Znio7UC",
|
||||||
|
"server_state": "connected",
|
||||||
|
"network_ledger" : "waiting",
|
||||||
|
"closed_ledger": {
|
||||||
|
"age": 5,
|
||||||
|
"base_fee_xrp": 0.00001,
|
||||||
|
"hash": "4482DEE5362332F54A4036ED57EE1767C9F33CF7CE5A6670355C16CECE381D46",
|
||||||
|
"reserve_base_xrp": 20,
|
||||||
|
"reserve_inc_xrp": 5,
|
||||||
|
"seq": 6595042
|
||||||
|
},
|
||||||
|
"validation_quorum": 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
'use strict';
|
'use strict'; // eslint-disable-line
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const WebSocketServer = require('ws').Server;
|
const WebSocketServer = require('ws').Server;
|
||||||
@@ -79,6 +79,8 @@ module.exports = function(port) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
mock.config = {};
|
||||||
|
|
||||||
mock.onAny(function() {
|
mock.onAny(function() {
|
||||||
if (this.event.indexOf('request_') !== 0) {
|
if (this.event.indexOf('request_') !== 0) {
|
||||||
return;
|
return;
|
||||||
@@ -101,6 +103,18 @@ module.exports = function(port) {
|
|||||||
conn.config = _.assign(conn.config, request.data);
|
conn.config = _.assign(conn.config, request.data);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
mock.on('request_test_command', function(request, conn) {
|
||||||
|
assert.strictEqual(request.command, 'test_command');
|
||||||
|
if (request.data.disconnectIn) {
|
||||||
|
setTimeout(conn.terminate.bind(conn), request.data.disconnectIn);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mock.on('request_global_config', function(request, conn) {
|
||||||
|
assert.strictEqual(request.command, 'global_config');
|
||||||
|
mock.config = _.assign(conn.config, request.data);
|
||||||
|
});
|
||||||
|
|
||||||
mock.on('request_echo', function(request, conn) {
|
mock.on('request_echo', function(request, conn) {
|
||||||
assert.strictEqual(request.command, 'echo');
|
assert.strictEqual(request.command, 'echo');
|
||||||
conn.send(JSON.stringify(request.data));
|
conn.send(JSON.stringify(request.data));
|
||||||
@@ -112,6 +126,8 @@ module.exports = function(port) {
|
|||||||
conn.send(createResponse(request, fixtures.server_info.error));
|
conn.send(createResponse(request, fixtures.server_info.error));
|
||||||
} else if (conn.config.disconnectOnServerInfo) {
|
} else if (conn.config.disconnectOnServerInfo) {
|
||||||
conn.close();
|
conn.close();
|
||||||
|
} else if (conn.config.serverInfoWithoutValidated) {
|
||||||
|
conn.send(createResponse(request, fixtures.server_info.noValidated));
|
||||||
} else {
|
} else {
|
||||||
conn.send(createResponse(request, fixtures.server_info.normal));
|
conn.send(createResponse(request, fixtures.server_info.normal));
|
||||||
}
|
}
|
||||||
@@ -119,7 +135,10 @@ module.exports = function(port) {
|
|||||||
|
|
||||||
mock.on('request_subscribe', function(request, conn) {
|
mock.on('request_subscribe', function(request, conn) {
|
||||||
assert.strictEqual(request.command, 'subscribe');
|
assert.strictEqual(request.command, 'subscribe');
|
||||||
if (request.accounts) {
|
if (mock.config.returnEmptySubscribeRequest) {
|
||||||
|
mock.config.returnEmptySubscribeRequest--;
|
||||||
|
conn.send(createResponse(request, fixtures.empty));
|
||||||
|
} else if (request.accounts) {
|
||||||
assert(_.indexOf(_.values(addresses), request.accounts[0]) !== -1);
|
assert(_.indexOf(_.values(addresses), request.accounts[0]) !== -1);
|
||||||
}
|
}
|
||||||
conn.send(createResponse(request, fixtures.subscribe));
|
conn.send(createResponse(request, fixtures.subscribe));
|
||||||
|
|||||||
Reference in New Issue
Block a user