Merge pull request #612 from clark800/delete-core

BREAKING CHANGE: Remove dependency of src/api on src/core, removes multiserver support
This commit is contained in:
Chris Clark
2015-10-27 11:54:29 -07:00
44 changed files with 682 additions and 1064 deletions

82
npm-shrinkwrap.json generated
View File

@@ -9,32 +9,22 @@
"resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz" "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz"
}, },
"babel-runtime": { "babel-runtime": {
"version": "5.8.25", "version": "5.8.29",
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-5.8.25.tgz", "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-5.8.29.tgz",
"dependencies": { "dependencies": {
"core-js": { "core-js": {
"version": "1.1.4", "version": "1.2.3",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-1.1.4.tgz" "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.3.tgz"
} }
} }
}, },
"bignumber.js": { "bignumber.js": {
"version": "2.0.7", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-2.0.7.tgz" "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-2.1.0.tgz"
}, },
"bn.js": { "bn.js": {
"version": "3.1.2", "version": "3.2.0",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-3.1.2.tgz" "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-3.2.0.tgz"
},
"es6-promisify": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-2.0.0.tgz",
"dependencies": {
"es6-promise": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-2.3.0.tgz"
}
}
}, },
"extend": { "extend": {
"version": "1.2.1", "version": "1.2.1",
@@ -121,8 +111,8 @@
"resolved": "https://registry.npmjs.org/ripple-address-codec/-/ripple-address-codec-2.0.1.tgz", "resolved": "https://registry.npmjs.org/ripple-address-codec/-/ripple-address-codec-2.0.1.tgz",
"dependencies": { "dependencies": {
"x-address-codec": { "x-address-codec": {
"version": "0.7.0", "version": "0.7.2",
"resolved": "https://registry.npmjs.org/x-address-codec/-/x-address-codec-0.7.0.tgz", "resolved": "https://registry.npmjs.org/x-address-codec/-/x-address-codec-0.7.2.tgz",
"dependencies": { "dependencies": {
"base-x": { "base-x": {
"version": "1.0.1", "version": "1.0.1",
@@ -136,17 +126,13 @@
"version": "0.0.6", "version": "0.0.6",
"resolved": "https://registry.npmjs.org/ripple-binary-codec/-/ripple-binary-codec-0.0.6.tgz", "resolved": "https://registry.npmjs.org/ripple-binary-codec/-/ripple-binary-codec-0.0.6.tgz",
"dependencies": { "dependencies": {
"bn.js": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-3.2.0.tgz"
},
"create-hash": { "create-hash": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.2.tgz", "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.2.tgz",
"dependencies": { "dependencies": {
"cipher-base": { "cipher-base": {
"version": "1.0.1", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.1.tgz" "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.2.tgz"
}, },
"ripemd160": { "ripemd160": {
"version": "1.0.1", "version": "1.0.1",
@@ -177,8 +163,8 @@
"resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.2.tgz", "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.2.tgz",
"dependencies": { "dependencies": {
"cipher-base": { "cipher-base": {
"version": "1.0.1", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.1.tgz" "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.2.tgz"
}, },
"inherits": { "inherits": {
"version": "2.0.1", "version": "2.0.1",
@@ -205,8 +191,8 @@
"resolved": "https://registry.npmjs.org/brorand/-/brorand-1.0.5.tgz" "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.0.5.tgz"
}, },
"elliptic": { "elliptic": {
"version": "5.1.0", "version": "5.2.0",
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-5.1.0.tgz", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-5.2.0.tgz",
"dependencies": { "dependencies": {
"inherits": { "inherits": {
"version": "2.0.1", "version": "2.0.1",
@@ -222,7 +208,13 @@
}, },
"ripple-lib-value": { "ripple-lib-value": {
"version": "0.1.0", "version": "0.1.0",
"resolved": "https://registry.npmjs.org/ripple-lib-value/-/ripple-lib-value-0.1.0.tgz" "resolved": "https://registry.npmjs.org/ripple-lib-value/-/ripple-lib-value-0.1.0.tgz",
"dependencies": {
"bignumber.js": {
"version": "2.0.8",
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-2.0.8.tgz"
}
}
}, },
"sjcl-codec": { "sjcl-codec": {
"version": "0.1.0", "version": "0.1.0",
@@ -232,20 +224,6 @@
"version": "0.7.2", "version": "0.7.2",
"resolved": "https://registry.npmjs.org/ws/-/ws-0.7.2.tgz", "resolved": "https://registry.npmjs.org/ws/-/ws-0.7.2.tgz",
"dependencies": { "dependencies": {
"bufferutil": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-1.1.0.tgz",
"dependencies": {
"bindings": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz"
},
"nan": {
"version": "1.8.4",
"resolved": "https://registry.npmjs.org/nan/-/nan-1.8.4.tgz"
}
}
},
"options": { "options": {
"version": "0.0.6", "version": "0.0.6",
"resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz" "resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz"
@@ -253,20 +231,6 @@
"ultron": { "ultron": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz" "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz"
},
"utf-8-validate": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-1.1.0.tgz",
"dependencies": {
"bindings": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz"
},
"nan": {
"version": "1.8.4",
"resolved": "https://registry.npmjs.org/nan/-/nan-1.8.4.tgz"
}
}
} }
} }
} }

View File

@@ -19,7 +19,6 @@
"babel-runtime": "^5.5.4", "babel-runtime": "^5.5.4",
"bignumber.js": "^2.0.3", "bignumber.js": "^2.0.3",
"bn.js": "^3.1.1", "bn.js": "^3.1.1",
"es6-promisify": "^2.0.0",
"extend": "~1.2.1", "extend": "~1.2.1",
"hash.js": "^1.0.3", "hash.js": "^1.0.3",
"https-proxy-agent": "^1.0.0", "https-proxy-agent": "^1.0.0",
@@ -39,7 +38,7 @@
"assert-diff": "^1.0.1", "assert-diff": "^1.0.1",
"babel": "^5.8.21", "babel": "^5.8.21",
"babel-core": "^5.8.22", "babel-core": "^5.8.22",
"babel-eslint": "^4.0.5", "babel-eslint": "^4.1.3",
"babel-loader": "^5.3.2", "babel-loader": "^5.3.2",
"coveralls": "~2.10.0", "coveralls": "~2.10.0",
"eslint": "^1.3.0", "eslint": "^1.3.0",

View File

@@ -2,7 +2,9 @@
const {EventEmitter} = require('events'); const {EventEmitter} = require('events');
const WebSocket = require('ws'); const WebSocket = require('ws');
// temporary: RangeSet will be moved to api/common soon // temporary: RangeSet will be moved to api/common soon
const RangeSet = require('./utils').core._test.RangeSet; const RangeSet = require('./rangeset').RangeSet;
const {RippledError, DisconnectedError, NotConnectedError,
TimeoutError, UnexpectedError} = require('./errors');
function isStreamMessageType(type) { function isStreamMessageType(type) {
return type === 'ledgerClosed' || return type === 'ledgerClosed' ||
@@ -10,48 +12,6 @@ function isStreamMessageType(type) {
type === 'path_find'; type === 'path_find';
} }
class RippledError extends Error {
constructor(message) {
super(message);
this.name = this.constructor.name;
this.message = message;
Error.captureStackTrace(this, this.constructor.name);
}
}
class ConnectionError extends Error {
constructor(message) {
super(message);
this.name = this.constructor.name;
this.message = message;
Error.captureStackTrace(this, this.constructor.name);
}
}
class NotConnectedError extends ConnectionError {
constructor(message) {
super(message);
}
}
class DisconnectedError extends ConnectionError {
constructor(message) {
super(message);
}
}
class TimeoutError extends ConnectionError {
constructor(message) {
super(message);
}
}
class UnexpectedError extends ConnectionError {
constructor(message) {
super(message);
}
}
class Connection extends EventEmitter { class Connection extends EventEmitter {
constructor(url, options = {}) { constructor(url, options = {}) {
super(); super();
@@ -64,31 +24,42 @@ class Connection extends EventEmitter {
this._nextRequestID = 1; this._nextRequestID = 1;
} }
_onMessage(message) { // return value is array of arguments to Connection.emit
try { _parseMessage(message) {
const data = JSON.parse(message); const data = JSON.parse(message);
if (data.type === 'response') { if (data.type === 'response') {
if (!(Number.isInteger(data.id) && data.id >= 0)) { if (!(Number.isInteger(data.id) && data.id >= 0)) {
throw new UnexpectedError('valid id not found in response'); throw new UnexpectedError('valid id not found in response');
} }
this.emit(data.id.toString(), data); return [data.id.toString(), data];
} else if (isStreamMessageType(data.type)) { } else if (isStreamMessageType(data.type)) {
if (data.type === 'ledgerClosed') { if (data.type === 'ledgerClosed') {
this._ledgerVersion = Number(data.ledger_index); this._ledgerVersion = Number(data.ledger_index);
this._availableLedgerVersions.addValue(this._ledgerVersion); this._availableLedgerVersions.reset();
this._availableLedgerVersions.parseAndAddRanges(
data.validated_ledgers);
} }
this.emit(data.type, data); return [data.type, data];
} else if (data.type === undefined && data.error) { } else if (data.type === undefined && data.error) {
this.emit('error', data.error, data.error_message); // e.g. slowDown return ['error', data.error, data.error_message]; // e.g. slowDown
} else { }
throw new UnexpectedError('unrecognized message type: ' + data.type); throw new UnexpectedError('unrecognized message type: ' + data.type);
} }
_onMessage(message) {
let parameters;
try {
parameters = this._parseMessage(message);
} catch (error) { } catch (error) {
this.emit('error', 'badMessage', message); this.emit('error', 'badMessage', message);
return;
} }
// we don't want this inside the try/catch or exceptions in listener
// will be caught
this.emit.apply(this, parameters);
} }
get state() { get _state() {
return this._ws ? this._ws.readyState : WebSocket.CLOSED; return this._ws ? this._ws.readyState : WebSocket.CLOSED;
} }
@@ -96,32 +67,34 @@ class Connection extends EventEmitter {
return this._ws !== null; return this._ws !== null;
} }
isConnected() {
return this._state === WebSocket.OPEN && this._isReady;
}
_onUnexpectedClose() { _onUnexpectedClose() {
this._isReady = false; this._isReady = false;
this.connect().then(); this.connect().then();
} }
_onOpen() { _onOpen() {
const subscribeRequest = { const request = {
command: 'subscribe', command: 'subscribe',
streams: ['ledger'] streams: ['ledger']
}; };
return this.request(subscribeRequest).then(() => { return this.request(request).then(response => {
return this.request({command: 'server_info'}).then(response => { this._ledgerVersion = Number(response.ledger_index);
this._ledgerVersion = Number(response.info.validated_ledger.seq);
this._availableLedgerVersions.parseAndAddRanges( this._availableLedgerVersions.parseAndAddRanges(
response.info.complete_ledgers); response.validated_ledgers);
this._isReady = true; this._isReady = true;
this.emit('connected'); this.emit('connected');
}); });
});
} }
connect() { connect() {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (this.state === WebSocket.OPEN) { if (this._state === WebSocket.OPEN) {
resolve(); resolve();
} else if (this.state === WebSocket.CONNECTING) { } else if (this._state === WebSocket.CONNECTING) {
this._ws.once('open', resolve); this._ws.once('open', resolve);
} else { } else {
this._ws = new WebSocket(this._url); this._ws = new WebSocket(this._url);
@@ -133,10 +106,10 @@ class Connection extends EventEmitter {
} }
disconnect() { disconnect() {
return new Promise((resolve) => { return new Promise(resolve => {
if (this.state === WebSocket.CLOSED) { if (this._state === WebSocket.CLOSED) {
resolve(); resolve();
} else if (this.state === WebSocket.CLOSING) { } else if (this._state === WebSocket.CLOSING) {
this._ws.once('close', resolve); this._ws.once('close', resolve);
} else { } else {
this._ws.removeListener('close', this._onUnexpectedClose); this._ws.removeListener('close', this._onUnexpectedClose);
@@ -158,7 +131,7 @@ class Connection extends EventEmitter {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (!this._shouldBeConnected) { if (!this._shouldBeConnected) {
reject(new NotConnectedError()); reject(new NotConnectedError());
} else if (this.state === WebSocket.OPEN && this._isReady) { } else if (this._state === WebSocket.OPEN && this._isReady) {
promise.then(resolve, reject); promise.then(resolve, reject);
} else { } else {
this.once('connected', () => promise.then(resolve, reject)); this.once('connected', () => promise.then(resolve, reject));
@@ -167,14 +140,13 @@ class Connection extends EventEmitter {
} }
getLedgerVersion() { getLedgerVersion() {
return this._whenReady( return this._whenReady(Promise.resolve(this._ledgerVersion));
new Promise(resolve => resolve(this._ledgerVersion)));
} }
hasLedgerVersions(lowLedgerVersion, highLedgerVersion) { hasLedgerVersions(lowLedgerVersion, highLedgerVersion) {
return this._whenReady(new Promise(resolve => return this._whenReady(Promise.resolve(
resolve(this._availableLedgerVersions.containsRange( this._availableLedgerVersions.containsRange(
lowLedgerVersion, highLedgerVersion)))); lowLedgerVersion, highLedgerVersion || this._ledgerVersion)));
} }
hasLedgerVersion(ledgerVersion) { hasLedgerVersion(ledgerVersion) {
@@ -214,8 +186,10 @@ class Connection extends EventEmitter {
function cleanup() { function cleanup() {
clearTimeout(timer); clearTimeout(timer);
self.removeAllListeners(eventName); self.removeAllListeners(eventName);
if (self._ws !== null) {
self._ws.removeListener('close', onDisconnect); self._ws.removeListener('close', onDisconnect);
} }
}
function _resolve(response) { function _resolve(response) {
cleanup(); cleanup();

View File

@@ -1,17 +1,26 @@
'use strict'; 'use strict';
const core = require('./utils').core;
const flagIndices = require('./txflags').txFlagIndices.AccountSet; const flagIndices = require('./txflags').txFlagIndices.AccountSet;
const flags = core.Remote.flags.account_root;
const accountRootFlags = {
PasswordSpent: 0x00010000, // password set fee is spent
RequireDestTag: 0x00020000, // require a DestinationTag for payments
RequireAuth: 0x00040000, // require a authorization to hold IOUs
DisallowXRP: 0x00080000, // disallow sending XRP
DisableMaster: 0x00100000, // force regular key
NoFreeze: 0x00200000, // permanently disallowed freezing trustlines
GlobalFreeze: 0x00400000, // trustlines globally frozen
DefaultRipple: 0x00800000
};
const AccountFlags = { const AccountFlags = {
passwordSpent: flags.PasswordSpent, passwordSpent: accountRootFlags.PasswordSpent,
requireDestinationTag: flags.RequireDestTag, requireDestinationTag: accountRootFlags.RequireDestTag,
requireAuthorization: flags.RequireAuth, requireAuthorization: accountRootFlags.RequireAuth,
disallowIncomingXRP: flags.DisallowXRP, disallowIncomingXRP: accountRootFlags.DisallowXRP,
disableMasterKey: flags.DisableMaster, disableMasterKey: accountRootFlags.DisableMaster,
noFreeze: flags.NoFreeze, noFreeze: accountRootFlags.NoFreeze,
globalFreeze: flags.GlobalFreeze, globalFreeze: accountRootFlags.GlobalFreeze,
defaultRipple: flags.DefaultRipple defaultRipple: accountRootFlags.DefaultRipple
}; };
const AccountFlagIndices = { const AccountFlagIndices = {

View File

@@ -34,6 +34,48 @@ RippleError.prototype.inspect = function(depth) {
return this.toString(); return this.toString();
}; };
class RippledError extends RippleError {
constructor(message) {
super(message);
this.name = this.constructor.name;
this.message = message;
Error.captureStackTrace(this, this.constructor.name);
}
}
class ConnectionError extends RippleError {
constructor(message) {
super(message);
this.name = this.constructor.name;
this.message = message;
Error.captureStackTrace(this, this.constructor.name);
}
}
class NotConnectedError extends ConnectionError {
constructor(message) {
super(message);
}
}
class DisconnectedError extends ConnectionError {
constructor(message) {
super(message);
}
}
class TimeoutError extends ConnectionError {
constructor(message) {
super(message);
}
}
class UnexpectedError extends ConnectionError {
constructor(message) {
super(message);
}
}
function ValidationError(message) { function ValidationError(message) {
this.message = message; this.message = message;
} }
@@ -117,5 +159,11 @@ module.exports = {
MissingLedgerHistoryError, MissingLedgerHistoryError,
TimeOutError, TimeOutError,
ApiError, ApiError,
RippleError RippleError,
ConnectionError,
RippledError,
NotConnectedError,
DisconnectedError,
TimeoutError,
UnexpectedError
}; };

View File

@@ -3,7 +3,6 @@ const utils = require('./utils');
module.exports = { module.exports = {
Connection: require('./connection'), Connection: require('./connection'),
core: utils.core,
constants: require('./constants'), constants: require('./constants'),
errors: require('./errors'), errors: require('./errors'),
validate: require('./validate'), validate: require('./validate'),
@@ -13,12 +12,11 @@ module.exports = {
xrpToDrops: utils.xrpToDrops, xrpToDrops: utils.xrpToDrops,
toRippledAmount: utils.toRippledAmount, toRippledAmount: utils.toRippledAmount,
generateAddress: utils.generateAddress, generateAddress: utils.generateAddress,
composeAsync: utils.composeAsync, generateAddressAPI: utils.generateAddressAPI,
wrapCatch: utils.wrapCatch,
removeUndefined: utils.removeUndefined, removeUndefined: utils.removeUndefined,
convertErrors: utils.convertErrors,
convertExceptions: utils.convertExceptions, convertExceptions: utils.convertExceptions,
convertKeysFromSnakeCaseToCamelCase: convertKeysFromSnakeCaseToCamelCase:
utils.convertKeysFromSnakeCaseToCamelCase, utils.convertKeysFromSnakeCaseToCamelCase,
promisify: utils.promisify rippleToUnixTimestamp: utils.rippleToUnixTimestamp,
unixToRippleTimestamp: utils.unixToRippleTimestamp
}; };

View File

@@ -0,0 +1,61 @@
/* @flow */
'use strict';
const _ = require('lodash');
const assert = require('assert');
const ranges = Symbol();
function mergeIntervals(intervals: Array<[number, number]>) {
const stack = [[-Infinity, -Infinity]];
_.forEach(_.sortBy(intervals, x => x[0]), interval => {
const lastInterval = stack.pop();
if (interval[0] <= lastInterval[1] + 1) {
stack.push([lastInterval[0], Math.max(interval[1], lastInterval[1])]);
} else {
stack.push(lastInterval);
stack.push(interval);
}
});
return stack.slice(1);
}
class RangeSet {
constructor() {
this.reset();
}
reset() {
this[ranges] = [];
}
serialize() {
return this[ranges].map(range =>
range[0].toString() + '-' + range[1].toString()).join(',');
}
addRange(start: number, end: number) {
assert(start <= end, 'invalid range');
this[ranges] = mergeIntervals(this[ranges].concat([[start, end]]));
}
addValue(value: number) {
this.addRange(value, value);
}
parseAndAddRanges(rangesString: string) {
const rangeStrings = rangesString.split(',');
_.forEach(rangeStrings, rangeString => {
const range = rangeString.split('-').map(Number);
this.addRange(range[0], range.length === 1 ? range[0] : range[1]);
});
}
containsRange(start: number, end: number) {
return _.some(this[ranges], range => range[0] <= start && range[1] >= end);
}
containsValue(value: number) {
return this.containsRange(value, value);
}
}
module.exports.RangeSet = RangeSet;

View File

@@ -53,7 +53,7 @@ function loadSchemas() {
require('./schemas/payment-transaction.json'), require('./schemas/payment-transaction.json'),
require('./schemas/payment.json'), require('./schemas/payment.json'),
require('./schemas/quality.json'), require('./schemas/quality.json'),
require('./schemas/remote-options.json'), require('./schemas/api-options.json'),
require('./schemas/sequence.json'), require('./schemas/sequence.json'),
require('./schemas/settings-options.json'), require('./schemas/settings-options.json'),
require('./schemas/settings-transaction.json'), require('./schemas/settings-transaction.json'),

View File

@@ -1,6 +1,6 @@
{ {
"$schema": "http://json-schema.org/draft-04/schema#", "$schema": "http://json-schema.org/draft-04/schema#",
"title": "remote-options", "title": "api-options",
"type": "object", "type": "object",
"properties": { "properties": {
"trace": {"type": "boolean"}, "trace": {"type": "boolean"},

View File

@@ -1,7 +1,7 @@
'use strict'; 'use strict';
const _ = require('lodash'); const _ = require('lodash');
const {RippledNetworkError} = require('./errors'); const {convertKeysFromSnakeCaseToCamelCase} = require('./utils');
const {promisify, convertKeysFromSnakeCaseToCamelCase} = require('./utils'); import type {Connection} from './connection';
export type GetServerInfoResponse = { export type GetServerInfoResponse = {
buildVersion: string, buildVersion: string,
@@ -32,21 +32,10 @@ export type GetServerInfoResponse = {
validationQuorum: number validationQuorum: number
} }
function getServerInfoAsync(remote, function getServerInfo(connection: Connection): Promise<GetServerInfoResponse> {
callback: (err: any, data?: GetServerInfoResponse) => void return connection.request({command: 'server_info'}).then(response =>
): void { convertKeysFromSnakeCaseToCamelCase(response.info)
remote.rawRequest({command: 'server_info'}, (error, response) => { );
if (error) {
const message = _.get(error, ['remote', 'error_message'], error.message);
callback(new RippledNetworkError(message));
} else {
callback(null, convertKeysFromSnakeCaseToCamelCase(response.info));
}
});
}
function getServerInfo(remote: Object): Promise<GetServerInfoResponse> {
return promisify(getServerInfoAsync)(remote);
} }
function computeFeeFromServerInfo(cushion: number, function computeFeeFromServerInfo(cushion: number,
@@ -56,8 +45,8 @@ function computeFeeFromServerInfo(cushion: number,
* Number(serverInfo.loadFactor) * cushion).toString(); * Number(serverInfo.loadFactor) * cushion).toString();
} }
function getFee(remote: Object, cushion: number) { function getFee(connection: Connection, cushion: number) {
return getServerInfo(remote).then( return getServerInfo(connection).then(
_.partial(computeFeeFromServerInfo, cushion)); _.partial(computeFeeFromServerInfo, cushion));
} }

View File

@@ -2,9 +2,7 @@
'use strict'; 'use strict';
const _ = require('lodash'); const _ = require('lodash');
const BigNumber = require('bignumber.js'); const BigNumber = require('bignumber.js');
const core = require('../../core');
const errors = require('./errors'); const errors = require('./errors');
const es6promisify = require('es6-promisify');
const keypairs = require('ripple-keypairs'); const keypairs = require('ripple-keypairs');
import type {Amount, RippledAmount} from './types.js'; import type {Amount, RippledAmount} from './types.js';
@@ -36,63 +34,12 @@ function generateAddress(options?: Object): Object {
return {secret, address}; return {secret, address};
} }
type AsyncFunction = (...x: any) => void function generateAddressAPI(options?: Object): Object {
function wrapCatch(asyncFunction: AsyncFunction): AsyncFunction {
return function() {
try { try {
asyncFunction.apply(this, arguments); return generateAddress(options);
} catch (error) {
const callback = arguments[arguments.length - 1];
callback(error);
}
};
}
type Callback = (err: any, data: any) => void
type Wrapper = (data: any) => any
function composeAsync(wrapper: Wrapper, callback: Callback): Callback {
return function(error, data) {
if (error) {
callback(error, data);
return;
}
let result;
try {
result = wrapper(data);
} catch (exception) {
callback(exception);
return;
}
callback(null, result);
};
}
function convertErrors(callback: Callback): () => void {
return function(error, data) {
if (error && !(error instanceof errors.RippleError)) {
const message = _.get(error, ['remote', 'error_message'], error.message);
const error_ = new errors.RippleError(message);
error_.data = data;
callback(error_, data);
} else if (error) {
error.data = data;
callback(error, data);
} else {
callback(error, data);
}
};
}
function convertExceptions<T>(f: () => T): () => T {
return function() {
try {
return f.apply(this, arguments);
} catch (error) { } catch (error) {
throw new errors.ApiError(error.message); throw new errors.ApiError(error.message);
} }
};
} }
const FINDSNAKE = /([a-zA-Z]_[a-zA-Z])/g; const FINDSNAKE = /([a-zA-Z]_[a-zA-Z])/g;
@@ -111,25 +58,38 @@ function convertKeysFromSnakeCaseToCamelCase(obj: any): any {
return obj; return obj;
} }
function promisify(asyncFunction: AsyncFunction): Function {
return es6promisify(wrapCatch(asyncFunction));
}
function removeUndefined(obj: Object): Object { function removeUndefined(obj: Object): Object {
return _.omit(obj, _.isUndefined); return _.omit(obj, _.isUndefined);
} }
/**
* @param {Number} rpepoch (seconds since 1/1/2000 GMT)
* @return {Number} ms since unix epoch
*
*/
function rippleToUnixTimestamp(rpepoch: number): number {
return (rpepoch + 0x386D4380) * 1000;
}
/**
* @param {Number|Date} timestamp (ms since unix epoch)
* @return {Number} seconds since ripple epoch ( 1/1/2000 GMT)
*/
function unixToRippleTimestamp(timestamp: number | Date): number {
const timestamp_ = timestamp instanceof Date ?
timestamp.getTime() :
timestamp;
return Math.round(timestamp_ / 1000) - 0x386D4380;
}
module.exports = { module.exports = {
core,
dropsToXrp, dropsToXrp,
xrpToDrops, xrpToDrops,
toRippledAmount, toRippledAmount,
generateAddress, generateAddress,
composeAsync, generateAddressAPI,
wrapCatch,
convertExceptions,
convertErrors,
convertKeysFromSnakeCaseToCamelCase, convertKeysFromSnakeCaseToCamelCase,
promisify, removeUndefined,
removeUndefined rippleToUnixTimestamp,
unixToRippleTimestamp
}; };

View File

@@ -88,6 +88,6 @@ module.exports = {
getTransactionOptions: _.partial(validateOptions, 'transaction-options'), getTransactionOptions: _.partial(validateOptions, 'transaction-options'),
getLedgerOptions: _.partial(validateOptions, 'ledger-options'), getLedgerOptions: _.partial(validateOptions, 'ledger-options'),
options: _.partial(validateOptions, 'options'), options: _.partial(validateOptions, 'options'),
remoteOptions: _.partial(schemaValidate, 'remote-options'), apiOptions: _.partial(schemaValidate, 'api-options'),
instructions: _.partial(schemaValidate, 'instructions') instructions: _.partial(schemaValidate, 'instructions')
}; };

View File

@@ -36,22 +36,34 @@ const prepareSettings = require('./transaction/settings');
const sign = require('./transaction/sign'); const sign = require('./transaction/sign');
const submit = require('./transaction/submit'); const submit = require('./transaction/submit');
const errors = require('./common').errors; const errors = require('./common').errors;
const convertExceptions = require('./common').convertExceptions; const generateAddress = common.generateAddressAPI;
const generateAddress = convertExceptions(common.generateAddress);
const computeLedgerHash = require('./offline/ledgerhash'); const computeLedgerHash = require('./offline/ledgerhash');
const getLedger = require('./ledger/ledger'); const getLedger = require('./ledger/ledger');
function RippleAPI(options: {}) { type APIOptions = {
common.validate.remoteOptions(options); servers?: Array<string>,
feeCushion?: number,
trace?: boolean,
proxy?: string
}
function RippleAPI(options: APIOptions = {}) {
common.validate.apiOptions(options);
if (EventEmitter instanceof Function) { // always true, needed for flow if (EventEmitter instanceof Function) { // always true, needed for flow
EventEmitter.call(this); EventEmitter.call(this);
} }
const _options = _.assign({}, options, {automatic_resubmission: false}); if (options.servers !== undefined) {
this._feeCushion = _options.feeCushion || 1.2; const servers: Array<string> = options.servers;
this.remote = new common.core.Remote(_options); if (servers.length === 1) {
this.remote.on('ledger_closed', message => { this._feeCushion = options.feeCushion || 1.2;
this.connection = new common.Connection(servers[0], options);
this.connection.on('ledgerClosed', message => {
this.emit('ledgerClosed', server.formatLedgerClose(message)); this.emit('ledgerClosed', server.formatLedgerClose(message));
}); });
} else {
throw new errors.RippleError('Multi-server not implemented');
}
}
} }
util.inherits(RippleAPI, EventEmitter); util.inherits(RippleAPI, EventEmitter);

View File

@@ -2,7 +2,7 @@
'use strict'; 'use strict';
const utils = require('./utils'); const utils = require('./utils');
const {validate, composeAsync, convertErrors, removeUndefined} = utils.common; const {validate, removeUndefined} = utils.common;
type AccountData = { type AccountData = {
Sequence: number, Sequence: number,
@@ -29,8 +29,6 @@ type AccountInfoOptions = {
ledgerVersion?: number ledgerVersion?: number
} }
type AccountInfoCallback = (err: any, data: AccountInfoResponse) => void
type AccountInfoResponse = { type AccountInfoResponse = {
sequence: number, sequence: number,
xrpBalance: string, xrpBalance: string,
@@ -52,9 +50,8 @@ function formatAccountInfo(response: AccountDataResponse) {
}); });
} }
function getAccountInfoAsync(account: string, options: AccountInfoOptions, function getAccountInfo(account: string, options: AccountInfoOptions = {}
callback: AccountInfoCallback ): Promise<AccountInfoResponse> {
) {
validate.address(account); validate.address(account);
validate.getAccountInfoOptions(options); validate.getAccountInfoOptions(options);
@@ -64,13 +61,7 @@ function getAccountInfoAsync(account: string, options: AccountInfoOptions,
ledger_index: options.ledgerVersion || 'validated' ledger_index: options.ledgerVersion || 'validated'
}; };
this.remote.rawRequest(request, return this.connection.request(request).then(formatAccountInfo);
composeAsync(formatAccountInfo, convertErrors(callback)));
}
function getAccountInfo(account: string, options: AccountInfoOptions = {}
): Promise<AccountInfoResponse> {
return utils.promisify(getAccountInfoAsync).call(this, account, options);
} }
module.exports = getAccountInfo; module.exports = getAccountInfo;

View File

@@ -3,7 +3,7 @@
const _ = require('lodash'); const _ = require('lodash');
const utils = require('./utils'); const utils = require('./utils');
const {validate, composeAsync, convertErrors} = utils.common; const {validate} = utils.common;
import type {Amount} from '../common/types.js'; import type {Amount} from '../common/types.js';
type BalanceSheetOptions = { type BalanceSheetOptions = {
@@ -47,42 +47,22 @@ function formatBalanceSheet(balanceSheet): GetBalanceSheet {
return result; return result;
} }
function getBalanceSheetAsync(address: string, options: BalanceSheetOptions, function getBalanceSheet(address: string, options: BalanceSheetOptions = {}
callback ): Promise<GetBalanceSheet> {
) {
validate.address(address); validate.address(address);
validate.getBalanceSheetOptions(options); validate.getBalanceSheetOptions(options);
return utils.ensureLedgerVersion.call(this, options).then(_options => {
const request = { const request = {
command: 'gateway_balances', command: 'gateway_balances',
account: address, account: address,
strict: true, strict: true,
hotwallet: options.excludeAddresses, hotwallet: _options.excludeAddresses,
ledger_index: options.ledgerVersion ledger_index: _options.ledgerVersion
}; };
const requestCallback = composeAsync( return this.connection.request(request).then(formatBalanceSheet);
formatBalanceSheet, convertErrors(callback));
if (_.isUndefined(request.ledger_index)) {
this.remote.getLedgerSequence((err, ledgerVersion) => {
if (err) {
convertErrors(callback)(err);
return;
}
request.ledger_index = ledgerVersion;
this.remote.rawRequest(request, requestCallback);
}); });
} else {
this.remote.rawRequest(request, requestCallback);
}
}
function getBalanceSheet(address: string, options: BalanceSheetOptions = {}
): Promise<GetBalanceSheet> {
return utils.promisify(getBalanceSheetAsync).call(this, address, options);
} }
module.exports = getBalanceSheet; module.exports = getBalanceSheet;

View File

@@ -1,11 +1,8 @@
/* @flow */ /* @flow */
'use strict'; 'use strict';
const _ = require('lodash');
const async = require('async');
const utils = require('./utils'); const utils = require('./utils');
const getTrustlines = require('./trustlines'); const {validate} = utils.common;
const {validate, composeAsync, convertErrors} = utils.common; import type {Connection} from '../common/connection.js';
import type {Remote, GetLedgerSequenceCallback} from '../../core/remote';
import type {TrustlinesOptions, Trustline} from './trustlines-types.js'; import type {TrustlinesOptions, Trustline} from './trustlines-types.js';
@@ -43,42 +40,26 @@ function formatBalances(options, balances) {
return result; return result;
} }
function getTrustlinesAsync(account: string, options: TrustlinesOptions, function getLedgerVersionHelper(connection: Connection, optionValue?: number
callback ): Promise<number> {
) {
getTrustlines.call(this, account, options)
.then(data => callback(null, data))
.catch(callback);
}
function getLedgerVersionHelper(remote: Remote, optionValue?: number,
callback: GetLedgerSequenceCallback
) {
if (optionValue !== undefined && optionValue !== null) { if (optionValue !== undefined && optionValue !== null) {
callback(null, optionValue); return Promise.resolve(optionValue);
} else {
remote.getLedgerSequence(callback);
} }
} return connection.getLedgerVersion();
function getBalancesAsync(account: string, options: TrustlinesOptions,
callback
) {
validate.address(account);
validate.getBalancesOptions(options);
async.parallel({
xrp: async.seq(
_.partial(getLedgerVersionHelper, this.remote, options.ledgerVersion),
_.partial(utils.getXRPBalance, this.remote, account)
),
trustlines: _.partial(getTrustlinesAsync.bind(this), account, options)
}, composeAsync(_.partial(formatBalances, options), convertErrors(callback)));
} }
function getBalances(account: string, options: TrustlinesOptions = {} function getBalances(account: string, options: TrustlinesOptions = {}
): Promise<GetBalances> { ): Promise<GetBalances> {
return utils.promisify(getBalancesAsync).call(this, account, options); validate.address(account);
validate.getBalancesOptions(options);
return Promise.all([
getLedgerVersionHelper(this.connection, options.ledgerVersion).then(
ledgerVersion =>
utils.getXRPBalance(this.connection, account, ledgerVersion)),
this.getTrustlines(account, options)
]).then(results =>
formatBalances(options, {xrp: results[0], trustlines: results[1]}));
} }
module.exports = getBalances; module.exports = getBalances;

View File

@@ -1,7 +1,7 @@
/* @flow */ /* @flow */
'use strict'; 'use strict';
const utils = require('./utils'); const utils = require('./utils');
const {validate, composeAsync, convertErrors} = utils.common; const {validate} = utils.common;
const parseLedger = require('./parse/ledger'); const parseLedger = require('./parse/ledger');
import type {GetLedger} from './types.js'; import type {GetLedger} from './types.js';
@@ -13,7 +13,7 @@ type LedgerOptions = {
} }
function getLedgerAsync(options: LedgerOptions, callback) { function getLedger(options: LedgerOptions = {}): Promise<GetLedger> {
validate.getLedgerOptions(options); validate.getLedgerOptions(options);
const request = { const request = {
@@ -24,13 +24,8 @@ function getLedgerAsync(options: LedgerOptions, callback) {
accounts: options.includeState accounts: options.includeState
}; };
this.remote.rawRequest(request, return this.connection.request(request).then(response =>
composeAsync(response => parseLedger(response.ledger), parseLedger(response.ledger));
convertErrors(callback)));
}
function getLedger(options: LedgerOptions = {}): Promise<GetLedger> {
return utils.promisify(getLedgerAsync).call(this, options);
} }
module.exports = getLedger; module.exports = getLedger;

View File

@@ -1,11 +1,10 @@
/* @flow */ /* @flow */
'use strict'; 'use strict';
const _ = require('lodash'); const _ = require('lodash');
const async = require('async');
const utils = require('./utils'); const utils = require('./utils');
const {validate, composeAsync, convertErrors} = utils.common; const {validate} = utils.common;
const parseOrderbookOrder = require('./parse/orderbook-order'); const parseOrderbookOrder = require('./parse/orderbook-order');
import type {Remote} from '../../core/remote'; import type {Connection} from '../common/connection.js';
import type {OrdersOptions, OrderSpecification} from './types.js'; import type {OrdersOptions, OrderSpecification} from './types.js';
import type {Amount, Issue} from '../common/types.js'; import type {Amount, Issue} from '../common/types.js';
@@ -36,18 +35,18 @@ type GetOrderbook = {
// account is to specify a "perspective", which affects which unfunded offers // account is to specify a "perspective", which affects which unfunded offers
// are returned // are returned
function getBookOffers(remote: Remote, account: string, function getBookOffers(connection: Connection, account: string,
ledgerVersion?: number, limit?: number, takerGets: Issue, ledgerVersion?: number, limit?: number, takerGets: Issue,
takerPays: Issue, callback takerPays: Issue
) { ): Promise {
remote.rawRequest(utils.renameCounterpartyToIssuerInOrder({ return connection.request(utils.renameCounterpartyToIssuerInOrder({
command: 'book_offers', command: 'book_offers',
taker_gets: takerGets, taker_gets: takerGets,
taker_pays: takerPays, taker_pays: takerPays,
ledger_index: ledgerVersion || 'validated', ledger_index: ledgerVersion || 'validated',
limit: limit, limit: limit,
taker: account taker: account
}), composeAsync(data => data.offers, convertErrors(callback))); })).then(data => data.offers);
} }
function isSameIssue(a: Amount, b: Amount) { function isSameIssue(a: Amount, b: Amount) {
@@ -92,27 +91,19 @@ function formatBidsAndAsks(orderbook: Orderbook, offers) {
return {bids, asks}; return {bids, asks};
} }
function getOrderbookAsync(account: string, orderbook: Orderbook, function getOrderbook(account: string, orderbook: Orderbook,
options: OrdersOptions, callback options: OrdersOptions = {}
) { ): Promise<GetOrderbook> {
validate.address(account); validate.address(account);
validate.orderbook(orderbook); validate.orderbook(orderbook);
validate.getOrderbookOptions(options); validate.getOrderbookOptions(options);
const getter = _.partial(getBookOffers, this.remote, account, const getter = _.partial(getBookOffers, this.connection, account,
options.ledgerVersion, options.limit); options.ledgerVersion, options.limit);
const getOffers = _.partial(getter, orderbook.base, orderbook.counter); const getOffers = _.partial(getter, orderbook.base, orderbook.counter);
const getReverseOffers = _.partial(getter, orderbook.counter, orderbook.base); const getReverseOffers = _.partial(getter, orderbook.counter, orderbook.base);
async.parallel([getOffers, getReverseOffers], return Promise.all([getOffers(), getReverseOffers()]).then(data =>
composeAsync((data) => formatBidsAndAsks(orderbook, _.flatten(data)), formatBidsAndAsks(orderbook, _.flatten(data)));
callback));
}
function getOrderbook(account: string, orderbook: Orderbook,
options: OrdersOptions = {}
): Promise<GetOrderbook> {
return utils.promisify(getOrderbookAsync).call(this,
account, orderbook, options);
} }
module.exports = getOrderbook; module.exports = getOrderbook;

View File

@@ -1,47 +1,42 @@
/* @flow */ /* @flow */
'use strict'; 'use strict';
const _ = require('lodash'); const _ = require('lodash');
const async = require('async');
const utils = require('./utils'); const utils = require('./utils');
const {validate, composeAsync, convertErrors} = utils.common; const {validate} = utils.common;
const parseAccountOrder = require('./parse/account-order'); const parseAccountOrder = require('./parse/account-order');
import type {Remote} from '../../core/remote'; import type {Connection} from '../common/connection.js';
import type {OrdersOptions, Order} from './types.js'; import type {OrdersOptions, Order} from './types.js';
type GetOrders = Array<Order> type GetOrders = Array<Order>
function requestAccountOffers(remote: Remote, address: string, function requestAccountOffers(connection: Connection, address: string,
ledgerVersion: number, marker: string, limit: number, callback ledgerVersion: number, marker: string, limit: number
) { ): Promise {
remote.rawRequest({ return connection.request({
command: 'account_offers', command: 'account_offers',
account: address, account: address,
marker: marker, marker: marker,
limit: utils.clamp(limit, 10, 400), limit: utils.clamp(limit, 10, 400),
ledger_index: ledgerVersion ledger_index: ledgerVersion
}, }).then(data => {
composeAsync((data) => ({ return {
marker: data.marker, marker: data.marker,
results: data.offers.map(_.partial(parseAccountOrder, address)) results: data.offers.map(_.partial(parseAccountOrder, address))
}), convertErrors(callback))); };
} });
function getOrdersAsync(account: string, options: OrdersOptions, callback) {
validate.address(account);
validate.getOrdersOptions(options);
const getter = _.partial(requestAccountOffers, this.remote, account,
options.ledgerVersion);
utils.getRecursive(getter, options.limit,
composeAsync((orders) => _.sortBy(orders,
(order) => order.properties.sequence), callback));
} }
function getOrders(account: string, options: OrdersOptions = {} function getOrders(account: string, options: OrdersOptions = {}
): Promise<GetOrders> { ): Promise<GetOrders> {
return utils.promisify(async.seq( validate.address(account);
utils.getLedgerOptionsWithLedgerVersion, validate.getOrdersOptions(options);
getOrdersAsync)).call(this, account, options);
return utils.ensureLedgerVersion.call(this, options).then(_options => {
const getter = _.partial(requestAccountOffers, this.connection, account,
_options.ledgerVersion);
return utils.getRecursive(getter, _options.limit).then(orders =>
_.sortBy(orders, (order) => order.properties.sequence));
});
} }
module.exports = getOrders; module.exports = getOrders;

View File

@@ -1,7 +1,7 @@
/* @flow */ /* @flow */
'use strict'; 'use strict';
const utils = require('./utils'); const utils = require('./utils');
const flags = utils.core.Remote.flags.offer; const flags = require('./flags').orderFlags;
const parseAmount = require('./amount'); const parseAmount = require('./amount');
const BigNumber = require('bignumber.js'); const BigNumber = require('bignumber.js');

View File

@@ -0,0 +1,22 @@
'use strict';
const orderFlags = {
Passive: 0x00010000,
Sell: 0x00020000 // offer was placed as a sell
};
const trustlineFlags = {
LowReserve: 0x00010000, // entry counts toward reserve
HighReserve: 0x00020000,
LowAuth: 0x00040000,
HighAuth: 0x00080000,
LowNoRipple: 0x00100000,
HighNoRipple: 0x00200000,
LowFreeze: 0x00400000,
HighFreeze: 0x00800000
};
module.exports = {
orderFlags,
trustlineFlags
};

View File

@@ -2,7 +2,7 @@
'use strict'; 'use strict';
const _ = require('lodash'); const _ = require('lodash');
const utils = require('./utils'); const utils = require('./utils');
const flags = utils.core.Remote.flags.offer; const flags = require('./flags').orderFlags;
const parseAmount = require('./amount'); const parseAmount = require('./amount');
function parseOrderbookOrder(order: Object): Object { function parseOrderbookOrder(order: Object): Object {

View File

@@ -2,8 +2,8 @@
'use strict'; 'use strict';
const _ = require('lodash'); const _ = require('lodash');
const transactionParser = require('ripple-lib-transactionparser'); const transactionParser = require('ripple-lib-transactionparser');
const toTimestamp = require('../../../core/utils').toTimestamp;
const utils = require('../utils'); const utils = require('../utils');
const rippleToUnixTimestamp = utils.common.rippleToUnixTimestamp;
const BigNumber = require('bignumber.js'); const BigNumber = require('bignumber.js');
function adjustQualityForXRP( function adjustQualityForXRP(
@@ -19,7 +19,8 @@ function adjustQualityForXRP(
} }
function parseTimestamp(tx: {date: string}): string | void { function parseTimestamp(tx: {date: string}): string | void {
return tx.date ? (new Date(toTimestamp(tx.date))).toISOString() : undefined; return tx.date ? (new Date(rippleToUnixTimestamp(tx.date))).toISOString()
: undefined;
} }
function removeEmptyCounterparty(amount) { function removeEmptyCounterparty(amount) {
@@ -87,6 +88,5 @@ module.exports = {
dropsToXrp: utils.common.dropsToXrp, dropsToXrp: utils.common.dropsToXrp,
constants: utils.common.constants, constants: utils.common.constants,
txFlags: utils.common.txFlags, txFlags: utils.common.txFlags,
core: utils.common.core,
removeUndefined: utils.common.removeUndefined removeUndefined: utils.common.removeUndefined
}; };

View File

@@ -1,14 +1,13 @@
/* @flow */ /* @flow */
'use strict'; 'use strict';
const _ = require('lodash'); const _ = require('lodash');
const async = require('async');
const BigNumber = require('bignumber.js'); const BigNumber = require('bignumber.js');
const utils = require('./utils'); const utils = require('./utils');
const parsePathfind = require('./parse/pathfind'); const parsePathfind = require('./parse/pathfind');
const {validate, composeAsync, convertErrors, toRippledAmount} = utils.common; const {validate, toRippledAmount} = utils.common;
const NotFoundError = utils.common.errors.NotFoundError; const NotFoundError = utils.common.errors.NotFoundError;
const ValidationError = utils.common.errors.ValidationError; const ValidationError = utils.common.errors.ValidationError;
import type {Remote} from '../../core/remote'; import type {Connection} from '../common/connection';
import type {RippledAmount} from '../common/types.js'; import type {RippledAmount} from '../common/types.js';
import type {GetPaths, PathFind, RippledPathsResponse, PathFindRequest} import type {GetPaths, PathFind, RippledPathsResponse, PathFindRequest}
from './pathfind-types.js'; from './pathfind-types.js';
@@ -21,7 +20,7 @@ function addParams(request: PathFindRequest, result: RippledPathsResponse) {
}), {destination_amount: request.destination_amount}); }), {destination_amount: request.destination_amount});
} }
function requestPathFind(remote: Remote, pathfind: PathFind, callback) { function requestPathFind(connection: Connection, pathfind: PathFind): Promise {
const destinationAmount = _.assign({value: -1}, pathfind.destination.amount); const destinationAmount = _.assign({value: -1}, pathfind.destination.amount);
const request: PathFindRequest = { const request: PathFindRequest = {
command: 'ripple_path_find', command: 'ripple_path_find',
@@ -53,8 +52,7 @@ function requestPathFind(remote: Remote, pathfind: PathFind, callback) {
} }
} }
remote.rawRequest(request, return connection.request(request).then(paths => addParams(request, paths));
composeAsync(_.partial(addParams, request), convertErrors(callback)));
} }
function addDirectXrpPath(paths: RippledPathsResponse, xrpBalance: string function addDirectXrpPath(paths: RippledPathsResponse, xrpBalance: string
@@ -76,16 +74,15 @@ function isRippledIOUAmount(amount: RippledAmount) {
amount.currency && (amount.currency !== 'XRP'); amount.currency && (amount.currency !== 'XRP');
} }
function conditionallyAddDirectXRPPath(remote: Remote, address: string, function conditionallyAddDirectXRPPath(connection: Connection, address: string,
paths: RippledPathsResponse, callback paths: RippledPathsResponse
) { ): Promise {
if (isRippledIOUAmount(paths.destination_amount) if (isRippledIOUAmount(paths.destination_amount)
|| !_.includes(paths.destination_currencies, 'XRP')) { || !_.includes(paths.destination_currencies, 'XRP')) {
callback(null, paths); return Promise.resolve(paths);
} else {
utils.getXRPBalance(remote, address, undefined,
composeAsync(_.partial(addDirectXrpPath, paths), callback));
} }
return utils.getXRPBalance(connection, address, undefined).then(
xrpBalance => addDirectXrpPath(paths, xrpBalance));
} }
function formatResponse(pathfind: PathFind, paths: RippledPathsResponse) { function formatResponse(pathfind: PathFind, paths: RippledPathsResponse) {
@@ -113,18 +110,13 @@ function formatResponse(pathfind: PathFind, paths: RippledPathsResponse) {
} }
} }
function getPathsAsync(pathfind: PathFind, callback) { function getPaths(pathfind: PathFind): Promise<GetPaths> {
validate.pathfind(pathfind); validate.pathfind(pathfind);
const address = pathfind.source.address; const address = pathfind.source.address;
async.waterfall([ return requestPathFind(this.connection, pathfind).then(paths =>
_.partial(requestPathFind, this.remote, pathfind), conditionallyAddDirectXRPPath(this.connection, address, paths)
_.partial(conditionallyAddDirectXRPPath, this.remote, address) ).then(paths => formatResponse(pathfind, paths));
], composeAsync(_.partial(formatResponse, pathfind), callback));
}
function getPaths(pathfind: PathFind): Promise<GetPaths> {
return utils.promisify(getPathsAsync).call(this, pathfind);
} }
module.exports = getPaths; module.exports = getPaths;

View File

@@ -3,7 +3,7 @@
const _ = require('lodash'); const _ = require('lodash');
const utils = require('./utils'); const utils = require('./utils');
const parseFields = require('./parse/fields'); const parseFields = require('./parse/fields');
const {validate, composeAsync, convertErrors} = utils.common; const {validate} = utils.common;
const AccountFlags = utils.common.constants.AccountFlags; const AccountFlags = utils.common.constants.AccountFlags;
type SettingsOptions = { type SettingsOptions = {
@@ -48,7 +48,8 @@ function formatSettings(response) {
return _.assign({}, parsedFlags, parsedFields); return _.assign({}, parsedFlags, parsedFields);
} }
function getSettingsAsync(account: string, options: SettingsOptions, callback) { function getSettings(account: string, options: SettingsOptions = {}
): Promise<GetSettings> {
validate.address(account); validate.address(account);
validate.getSettingsOptions(options); validate.getSettingsOptions(options);
@@ -58,13 +59,7 @@ function getSettingsAsync(account: string, options: SettingsOptions, callback) {
ledger_index: options.ledgerVersion || 'validated' ledger_index: options.ledgerVersion || 'validated'
}; };
this.remote.rawRequest(request, return this.connection.request(request).then(formatSettings);
composeAsync(formatSettings, convertErrors(callback)));
}
function getSettings(account: string, options: SettingsOptions = {}
): Promise<GetSettings> {
return utils.promisify(getSettingsAsync).call(this, account, options);
} }
module.exports = getSettings; module.exports = getSettings;

View File

@@ -1,28 +1,22 @@
/* @flow */ /* @flow */
'use strict'; 'use strict';
const _ = require('lodash'); const _ = require('lodash');
const async = require('async');
const utils = require('./utils'); const utils = require('./utils');
const parseTransaction = require('./parse/transaction'); const parseTransaction = require('./parse/transaction');
const {validate, convertErrors, errors} = utils.common; const {validate, errors} = utils.common;
const RippleError = require('../../core/rippleerror').RippleError; import type {Connection} from '../common/connection.js';
import type {TransactionType, TransactionOptions} from './transaction-types';
import type {Remote} from '../../core/remote'; function attachTransactionDate(connection: Connection, tx: Object
): Promise<TransactionType> {
import type {CallbackType, TransactionType,
GetTransactionResponseCallback, TransactionOptions}
from './transaction-types';
function attachTransactionDate(remote: Remote, tx: Object,
callback: CallbackType
) {
if (tx.date) { if (tx.date) {
callback(null, tx); return Promise.resolve(tx);
return;
} }
if (!tx.ledger_index) { if (!tx.ledger_index) {
callback(new errors.NotFoundError('ledger_index not found in tx')); return new Promise(() => {
return; throw new errors.NotFoundError('ledger_index not found in tx');
});
} }
const request = { const request = {
@@ -30,14 +24,16 @@ function attachTransactionDate(remote: Remote, tx: Object,
ledger_index: tx.ledger_index ledger_index: tx.ledger_index
}; };
remote.rawRequest(request, (error, data) => { return connection.request(request).then(data => {
if (error) { if (typeof data.ledger.close_time === 'number') {
callback(new errors.NotFoundError('Transaction ledger not found')); return _.assign({date: data.ledger.close_time}, tx);
} else if (typeof data.ledger.close_time === 'number') {
callback(null, _.assign({date: data.ledger.close_time}, tx));
} else {
callback(new errors.ApiError('Ledger missing close_time'));
} }
throw new errors.ApiError('Ledger missing close_time');
}).catch(error => {
if (error instanceof errors.ApiError) {
throw error;
}
throw new errors.NotFoundError('Transaction ledger not found');
}); });
} }
@@ -48,69 +44,59 @@ function isTransactionInRange(tx: Object, options: TransactionOptions) {
|| tx.ledger_index <= options.maxLedgerVersion); || tx.ledger_index <= options.maxLedgerVersion);
} }
function getTransactionAsync(identifier: string, options: TransactionOptions, function convertError(connection: Connection, options: TransactionOptions,
callback: GetTransactionResponseCallback error: Error
) { ): Promise<Error> {
validate.identifier(identifier); const _error = (error.message === 'txnNotFound') ?
validate.getTransactionOptions(options); new errors.NotFoundError('Transaction not found') : error;
if (_error instanceof errors.NotFoundError) {
const remote = this.remote; return utils.hasCompleteLedgerRange(connection, options.minLedgerVersion,
options.maxLedgerVersion).then(hasCompleteLedgerRange => {
function callbackWrapper(error_?: Error, tx?: Object, if (!hasCompleteLedgerRange) {
maxLedgerVersion?: number return utils.isPendingLedgerVersion(
) { connection, options.maxLedgerVersion)
let error = error_; .then(isPendingLedgerVersion => {
return isPendingLedgerVersion ?
if (!error && tx && tx.validated !== true) { new errors.PendingLedgerVersionError() :
return callback(new errors.NotFoundError('Transaction not found')); new errors.MissingLedgerHistoryError();
});
}
return _error;
});
}
return Promise.resolve(_error);
} }
if (error instanceof RippleError && error.remote && function formatResponse(options: TransactionOptions, tx: TransactionType
error.remote.error === 'txnNotFound') { ): TransactionType {
error = new errors.NotFoundError('Transaction not found'); if (tx.validated !== true || !isTransactionInRange(tx, options)) {
throw new errors.NotFoundError('Transaction not found');
} }
return parseTransaction(tx);
// Missing complete ledger range
if (error instanceof errors.NotFoundError
&& !utils.hasCompleteLedgerRange(remote, options.minLedgerVersion,
maxLedgerVersion)) {
if (utils.isPendingLedgerVersion(remote, maxLedgerVersion)) {
callback(new errors.PendingLedgerVersionError());
} else {
callback(new errors.MissingLedgerHistoryError());
}
// Transaction is found, but not in specified range
} else if (!error && tx && !isTransactionInRange(tx, options)) {
callback(new errors.NotFoundError('Transaction not found'));
// Transaction is not found
} else if (error) {
convertErrors(callback)(error);
} else if (!tx) {
callback(new errors.ApiError('Internal error'));
} else {
callback(error, parseTransaction(tx));
}
}
function maxLedgerGetter(error_?: Error, tx?: Object) {
this.getLedgerVersion().then((version) => {
const maxLedgerVersion = options.maxLedgerVersion || version;
callbackWrapper(error_, tx, maxLedgerVersion);
}, callbackWrapper);
}
async.waterfall([
_.partial(remote.rawRequest.bind(remote),
{command: 'tx', transaction: identifier, binary: false}),
_.partial(attachTransactionDate, remote)
], maxLedgerGetter.bind(this));
} }
function getTransaction(identifier: string, function getTransaction(identifier: string,
options: TransactionOptions = {} options: TransactionOptions = {}
): Promise<TransactionType> { ): Promise<TransactionType> {
return utils.promisify(getTransactionAsync).call(this, identifier, options); validate.identifier(identifier);
validate.getTransactionOptions(options);
const request = {
command: 'tx',
transaction: identifier,
binary: false
};
return utils.ensureLedgerVersion.call(this, options).then(_options => {
return this.connection.request(request).then(tx =>
attachTransactionDate(this.connection, tx)
).then(_.partial(formatResponse, _options))
.catch(error => {
return convertError(this.connection, _options, error).then(_error => {
throw _error;
});
});
});
} }
module.exports = getTransaction; module.exports = getTransaction;

View File

@@ -7,8 +7,8 @@ const {computeTransactionHash} = require('ripple-hashes');
const utils = require('./utils'); const utils = require('./utils');
const parseTransaction = require('./parse/transaction'); const parseTransaction = require('./parse/transaction');
const getTransaction = require('./transaction'); const getTransaction = require('./transaction');
const {validate, composeAsync, convertErrors} = utils.common; const {validate} = utils.common;
import type {Remote} from '../../core/remote'; import type {Connection} from '../common/connection.js';
import type {TransactionType} from './transaction-types'; import type {TransactionType} from './transaction-types';
@@ -28,8 +28,6 @@ type TransactionsOptions = {
type GetTransactionsResponse = Array<TransactionType> type GetTransactionsResponse = Array<TransactionType>
type CallbackType = (err?: ?Error, data?: GetTransactionsResponse) => void
function parseBinaryTransaction(transaction) { function parseBinaryTransaction(transaction) {
const tx = binary.decode(transaction.tx_blob); const tx = binary.decode(transaction.tx_blob);
tx.hash = computeTransactionHash(tx); tx.hash = computeTransactionHash(tx);
@@ -103,8 +101,8 @@ function formatPartialResponse(address: string,
}; };
} }
function getAccountTx(remote: Remote, address: string, function getAccountTx(connection: Connection, address: string,
options: TransactionsOptions, marker: string, limit: number, callback options: TransactionsOptions, marker: string, limit: number
) { ) {
const request = { const request = {
command: 'account_tx', command: 'account_tx',
@@ -119,13 +117,12 @@ function getAccountTx(remote: Remote, address: string,
marker: marker marker: marker
}; };
remote.rawRequest(request, return connection.request(request).then(response =>
composeAsync(_.partial(formatPartialResponse, address, options), formatPartialResponse(address, options, response));
convertErrors(callback)));
} }
function checkForLedgerGaps(remote: Remote, options: TransactionsOptions, function checkForLedgerGaps(connection: Connection,
transactions: GetTransactionsResponse options: TransactionsOptions, transactions: GetTransactionsResponse
) { ) {
let {minLedgerVersion, maxLedgerVersion} = options; let {minLedgerVersion, maxLedgerVersion} = options;
@@ -140,54 +137,49 @@ function checkForLedgerGaps(remote: Remote, options: TransactionsOptions,
} }
} }
if (!utils.hasCompleteLedgerRange(remote, minLedgerVersion, return utils.hasCompleteLedgerRange(connection, minLedgerVersion,
maxLedgerVersion)) { maxLedgerVersion).then(hasCompleteLedgerRange => {
if (!hasCompleteLedgerRange) {
throw new utils.common.errors.MissingLedgerHistoryError(); throw new utils.common.errors.MissingLedgerHistoryError();
} }
});
} }
function formatResponse(remote: Remote, options: TransactionsOptions, function formatResponse(connection: Connection, options: TransactionsOptions,
transactions: GetTransactionsResponse transactions: GetTransactionsResponse
) { ) {
const compare = options.earliestFirst ? utils.compareTransactions : const compare = options.earliestFirst ? utils.compareTransactions :
_.rearg(utils.compareTransactions, 1, 0); _.rearg(utils.compareTransactions, 1, 0);
const sortedTransactions = transactions.sort(compare); const sortedTransactions = transactions.sort(compare);
checkForLedgerGaps(remote, options, sortedTransactions); return checkForLedgerGaps(connection, options, sortedTransactions).then(
return sortedTransactions; () => sortedTransactions);
} }
function getTransactionsInternal(remote: Remote, address: string, function getTransactionsInternal(connection: Connection, address: string,
options: TransactionsOptions, callback options: TransactionsOptions
) { ): Promise<GetTransactionsResponse> {
const getter = _.partial(getAccountTx, remote, address, options); const getter = _.partial(getAccountTx, connection, address, options);
const format = _.partial(formatResponse, remote, options); const format = _.partial(formatResponse, connection, options);
utils.getRecursive(getter, options.limit, composeAsync(format, callback)); return utils.getRecursive(getter, options.limit).then(format);
} }
function getTransactionsAsync(account: string, function getTransactions(account: string, options: TransactionsOptions = {}
options: TransactionsOptions, callback: CallbackType ): Promise<GetTransactionsResponse> {
) {
validate.address(account); validate.address(account);
validate.getTransactionsOptions(options); validate.getTransactionsOptions(options);
const defaults = {maxLedgerVersion: -1}; const defaults = {maxLedgerVersion: -1};
if (options.start) { if (options.start) {
getTransaction.call(this, options.start).then(tx => { return getTransaction.call(this, options.start).then(tx => {
const ledgerVersion = tx.outcome.ledgerVersion; const ledgerVersion = tx.outcome.ledgerVersion;
const bound = options.earliestFirst ? const bound = options.earliestFirst ?
{minLedgerVersion: ledgerVersion} : {maxLedgerVersion: ledgerVersion}; {minLedgerVersion: ledgerVersion} : {maxLedgerVersion: ledgerVersion};
const newOptions = _.assign(defaults, options, {startTx: tx}, bound); const newOptions = _.assign(defaults, options, {startTx: tx}, bound);
getTransactionsInternal(this.remote, account, newOptions, callback); return getTransactionsInternal(this.connection, account, newOptions);
}).catch(callback); });
} else { }
const newOptions = _.assign(defaults, options); const newOptions = _.assign(defaults, options);
getTransactionsInternal(this.remote, account, newOptions, callback); return getTransactionsInternal(this.connection, account, newOptions);
}
}
function getTransactions(account: string, options: TransactionsOptions = {}
): Promise<GetTransactionsResponse> {
return utils.promisify(getTransactionsAsync).call(this, account, options);
} }
module.exports = getTransactions; module.exports = getTransactions;

View File

@@ -1,12 +1,10 @@
/* @flow */ /* @flow */
'use strict'; 'use strict';
const _ = require('lodash'); const _ = require('lodash');
const async = require('async');
const utils = require('./utils'); const utils = require('./utils');
const {validate, composeAsync, convertErrors} = utils.common; const {validate} = utils.common;
const parseAccountTrustline = require('./parse/account-trustline'); const parseAccountTrustline = require('./parse/account-trustline');
import type {Connection} from '../common/connection.js';
import type {Remote} from '../../core/remote';
import type {TrustlinesOptions, Trustline} from './trustlines-types.js'; import type {TrustlinesOptions, Trustline} from './trustlines-types.js';
@@ -24,9 +22,10 @@ function formatResponse(options: TrustlinesOptions, data) {
}; };
} }
function getAccountLines(remote: Remote, address: string, ledgerVersion: number, function getAccountLines(connection: Connection, address: string,
options: TrustlinesOptions, marker: string, limit: number, callback ledgerVersion: number, options: TrustlinesOptions, marker: string,
) { limit: number
): Promise<GetTrustlinesResponse> {
const request = { const request = {
command: 'account_lines', command: 'account_lines',
account: address, account: address,
@@ -36,27 +35,19 @@ function getAccountLines(remote: Remote, address: string, ledgerVersion: number,
peer: options.counterparty peer: options.counterparty
}; };
remote.rawRequest(request, return connection.request(request).then(_.partial(formatResponse, options));
composeAsync(_.partial(formatResponse, options),
convertErrors(callback)));
}
function getTrustlinesAsync(account: string, options: TrustlinesOptions,
callback: () => void
): void {
validate.address(account);
validate.getTrustlinesOptions(options);
const getter = _.partial(getAccountLines, this.remote, account,
options.ledgerVersion, options);
utils.getRecursive(getter, options.limit, callback);
} }
function getTrustlines(account: string, options: TrustlinesOptions = {} function getTrustlines(account: string, options: TrustlinesOptions = {}
): Promise<GetTrustlinesResponse> { ): Promise<GetTrustlinesResponse> {
return utils.promisify(async.seq( validate.address(account);
utils.getLedgerOptionsWithLedgerVersion, validate.getTrustlinesOptions(options);
getTrustlinesAsync)).call(this, account, options);
return this.getLedgerVersion().then(ledgerVersion => {
const getter = _.partial(getAccountLines, this.connection, account,
options.ledgerVersion || ledgerVersion, options);
return utils.getRecursive(getter, options.limit);
});
} }
module.exports = getTrustlines; module.exports = getTrustlines;

View File

@@ -4,63 +4,51 @@ const _ = require('lodash');
const assert = require('assert'); const assert = require('assert');
const common = require('../common'); const common = require('../common');
const dropsToXrp = common.dropsToXrp; const dropsToXrp = common.dropsToXrp;
const composeAsync = common.composeAsync;
import type {Remote} from '../../core/remote';
import type {TransactionType} from './transaction-types'; import type {TransactionType} from './transaction-types';
import type {Issue} from '../common/types.js'; import type {Issue} from '../common/types.js';
import type {Connection} from '../common/connection';
type Callback = (err: any, data: any) => void
type RecursiveData = { type RecursiveData = {
marker: string, marker: string,
results: Array<any> results: Array<any>
} }
type RecursiveCallback = (err: any, data: RecursiveData) => void type Getter = (marker: ?string, limit: number) => Promise<RecursiveData>
function clamp(value: number, min: number, max: number): number { function clamp(value: number, min: number, max: number): number {
assert(min <= max, 'Illegal clamp bounds'); assert(min <= max, 'Illegal clamp bounds');
return Math.min(Math.max(value, min), max); return Math.min(Math.max(value, min), max);
} }
function getXRPBalance(remote: Remote, address: string, ledgerVersion?: number, function getXRPBalance(connection: Connection, address: string,
callback: Callback ledgerVersion?: number
): void { ): Promise<number> {
const request = { const request = {
command: 'account_info', command: 'account_info',
account: address, account: address,
ledger_index: ledgerVersion ledger_index: ledgerVersion
}; };
remote.rawRequest(request, return connection.request(request).then(data =>
composeAsync((data) => dropsToXrp(data.account_data.Balance), callback)); dropsToXrp(data.account_data.Balance));
} }
type Getter = (marker: ?string, limit: number,
callback: RecursiveCallback) => void
// If the marker is omitted from a response, you have reached the end // If the marker is omitted from a response, you have reached the end
// getter(marker, limit, callback), callback(error, {marker, results}) // getter(marker, limit, callback), callback(error, {marker, results})
function getRecursiveRecur(getter: Getter, marker?: string, limit: number, function getRecursiveRecur(getter: Getter, marker?: string, limit: number
callback: Callback ): Promise {
): void { return getter(marker, limit).then(data => {
getter(marker, limit, (error, data) => {
if (error) {
return callback(error);
}
const remaining = limit - data.results.length; const remaining = limit - data.results.length;
if (remaining > 0 && data.marker !== undefined) { if (remaining > 0 && data.marker !== undefined) {
getRecursiveRecur(getter, data.marker, remaining, (_error, results) => { return getRecursiveRecur(getter, data.marker, remaining).then(results =>
return _error ? callback(_error) : data.results.concat(results)
callback(null, data.results.concat(results)); );
});
} else {
return callback(null, data.results.slice(0, limit));
} }
return data.results.slice(0, limit);
}); });
} }
function getRecursive(getter: Getter, limit?: number, callback: Callback) { function getRecursive(getter: Getter, limit?: number): Promise {
getRecursiveRecur(getter, undefined, limit || Infinity, callback); return getRecursiveRecur(getter, undefined, limit || Infinity);
} }
function renameCounterpartyToIssuer(amount?: Issue): ?{issuer?: string} { function renameCounterpartyToIssuer(amount?: Issue): ?{issuer?: string} {
@@ -109,46 +97,41 @@ function compareTransactions(first: TransactionType, second: TransactionType
return first.outcome.ledgerVersion < second.outcome.ledgerVersion ? -1 : 1; return first.outcome.ledgerVersion < second.outcome.ledgerVersion ? -1 : 1;
} }
function hasCompleteLedgerRange(remote: Remote, minLedgerVersion?: number, function hasCompleteLedgerRange(connection: Connection,
maxLedgerVersion?: number minLedgerVersion?: number, maxLedgerVersion?: number
): boolean { ): Promise<boolean> {
const firstLedgerVersion = 32570; // earlier versions have been lost const firstLedgerVersion = 32570; // earlier versions have been lost
return remote.getServer().hasLedgerRange( return connection.hasLedgerVersions(
minLedgerVersion || firstLedgerVersion, minLedgerVersion || firstLedgerVersion, maxLedgerVersion);
maxLedgerVersion || remote.getLedgerSequenceSync());
} }
function isPendingLedgerVersion(remote: Remote, maxLedgerVersion: ?number function isPendingLedgerVersion(connection: Connection,
): boolean { maxLedgerVersion: ?number
const currentLedger = remote.getLedgerSequenceSync(); ): Promise<boolean> {
return currentLedger < (maxLedgerVersion || 0); return connection.getLedgerVersion().then(ledgerVersion =>
ledgerVersion < (maxLedgerVersion || 0));
} }
function getLedgerOptionsWithLedgerVersion(account: string, options: Object, function ensureLedgerVersion(options: Object
callback: (err?: ?Error, account?: string, options: Object) => void ): Promise<number> {
) {
if (Boolean(options) && options.ledgerVersion !== undefined && if (Boolean(options) && options.ledgerVersion !== undefined &&
options.ledgerVersion !== null options.ledgerVersion !== null
) { ) {
callback(null, account, options); return Promise.resolve(options);
} else {
this.getLedgerVersion().then((version) => {
callback(null, account, _.assign({}, options, {ledgerVersion: version}));
}, callback);
} }
return this.getLedgerVersion().then(ledgerVersion =>
_.assign({}, options, {ledgerVersion}));
} }
module.exports = { module.exports = {
getXRPBalance, getXRPBalance,
getLedgerOptionsWithLedgerVersion, ensureLedgerVersion,
compareTransactions, compareTransactions,
renameCounterpartyToIssuer, renameCounterpartyToIssuer,
renameCounterpartyToIssuerInOrder, renameCounterpartyToIssuerInOrder,
getRecursive, getRecursive,
hasCompleteLedgerRange, hasCompleteLedgerRange,
isPendingLedgerVersion, isPendingLedgerVersion,
promisify: common.promisify,
clamp: clamp, clamp: clamp,
common: common common: common
}; };

View File

@@ -4,45 +4,32 @@ const common = require('../common');
import type {GetServerInfoResponse} from '../common/serverinfo'; import type {GetServerInfoResponse} from '../common/serverinfo';
function isConnected(): boolean { function isConnected(): boolean {
const server = this.remote.getServer(); return this.connection.isConnected();
return Boolean(server && server.isConnected());
} }
function getLedgerVersion(): Promise<number> { function getLedgerVersion(): Promise<number> {
return common.promisify(this.remote.getLedgerSequence).call(this.remote); return this.connection.getLedgerVersion();
} }
function connect(): Promise<void> { function connect(): Promise<void> {
return common.promisify(callback => { return this.connection.connect();
try {
this.remote.connect(() => callback(null));
} catch (error) {
callback(new common.errors.RippledNetworkError(error.message));
}
})();
} }
function disconnect(): Promise<void> { function disconnect(): Promise<void> {
return common.promisify(callback => { return this.connection.disconnect();
try {
this.remote.disconnect(() => callback(null));
} catch (error) {
callback(new common.errors.RippledNetworkError(error.message));
}
})();
} }
function getServerInfo(): Promise<GetServerInfoResponse> { function getServerInfo(): Promise<GetServerInfoResponse> {
return common.serverInfo.getServerInfo(this.remote); return common.serverInfo.getServerInfo(this.connection);
} }
function getFee(): Promise<number> { function getFee(): Promise<number> {
const cushion = this._feeCushion || 1.2; const cushion = this._feeCushion || 1.2;
return common.serverInfo.getFee(this.remote, cushion); return common.serverInfo.getFee(this.connection, cushion);
} }
function rippleTimeToISO8601(rippleTime: string): string { function rippleTimeToISO8601(rippleTime: string): string {
return new Date(common.core.utils.toTimestamp(rippleTime)).toISOString(); return new Date(common.rippleToUnixTimestamp(rippleTime)).toISOString();
} }
function formatLedgerClose(ledgerClose: Object): Object { function formatLedgerClose(ledgerClose: Object): Object {

View File

@@ -37,18 +37,11 @@ function createOrderTransaction(account: string, order: Order): Object {
return txJSON; return txJSON;
} }
function prepareOrderAsync(account: string, order: Order,
instructions: Instructions, callback
) {
const txJSON = createOrderTransaction(account, order);
utils.prepareTransaction(txJSON, this, instructions, callback);
}
function prepareOrder(account: string, order: Order, function prepareOrder(account: string, order: Order,
instructions: Instructions = {} instructions: Instructions = {}
): Promise<Prepare> { ): Promise<Prepare> {
return utils.promisify(prepareOrderAsync.bind(this))( const txJSON = createOrderTransaction(account, order);
account, order, instructions); return utils.prepareTransaction(txJSON, this, instructions);
} }
module.exports = prepareOrder; module.exports = prepareOrder;

View File

@@ -17,18 +17,11 @@ function createOrderCancellationTransaction(account: string,
}; };
} }
function prepareOrderCancellationAsync(account: string, sequence: number,
instructions: Instructions, callback
) {
const txJSON = createOrderCancellationTransaction(account, sequence);
utils.prepareTransaction(txJSON, this, instructions, callback);
}
function prepareOrderCancellation(account: string, sequence: number, function prepareOrderCancellation(account: string, sequence: number,
instructions: Instructions = {} instructions: Instructions = {}
): Promise<Prepare> { ): Promise<Prepare> {
return utils.promisify(prepareOrderCancellationAsync.bind(this))( const txJSON = createOrderCancellationTransaction(account, sequence);
account, sequence, instructions); return utils.prepareTransaction(txJSON, this, instructions);
} }
module.exports = prepareOrderCancellation; module.exports = prepareOrderCancellation;

View File

@@ -141,18 +141,11 @@ function createPaymentTransaction(account: string, paymentArgument: Payment
return txJSON; return txJSON;
} }
function preparePaymentAsync(account: string, payment: Payment,
instructions: Instructions, callback
) {
const txJSON = createPaymentTransaction(account, payment);
utils.prepareTransaction(txJSON, this, instructions, callback);
}
function preparePayment(account: string, payment: Payment, function preparePayment(account: string, payment: Payment,
instructions: Instructions = {} instructions: Instructions
): Promise<Prepare> { ): Promise<Prepare> {
return utils.promisify(preparePaymentAsync.bind(this))( const txJSON = createPaymentTransaction(account, payment);
account, payment, instructions); return utils.prepareTransaction(txJSON, this, instructions);
} }
module.exports = preparePayment; module.exports = preparePayment;

View File

@@ -95,18 +95,11 @@ function createSettingsTransaction(account: string, settings: Settings
return txJSON; return txJSON;
} }
function prepareSettingsAsync(account: string, settings: Settings, function prepareSettings(account: string, settings: Settings,
instructions: Instructions, callback
) {
const txJSON = createSettingsTransaction(account, settings);
utils.prepareTransaction(txJSON, this, instructions, callback);
}
function prepareSettings(account: string, settings: Object,
instructions: Instructions = {} instructions: Instructions = {}
): Promise<Prepare> { ): Promise<Prepare> {
return utils.promisify(prepareSettingsAsync.bind(this))( const txJSON = createSettingsTransaction(account, settings);
account, settings, instructions); return utils.prepareTransaction(txJSON, this, instructions);
} }
module.exports = prepareSettings; module.exports = prepareSettings;

View File

@@ -2,9 +2,7 @@
'use strict'; 'use strict';
const _ = require('lodash'); const _ = require('lodash');
const utils = require('./utils'); const utils = require('./utils');
const validate = utils.common.validate; const {validate, convertKeysFromSnakeCaseToCamelCase} = utils.common;
const Request = utils.common.core.Request;
const convertErrors = utils.common.convertErrors;
type Submit = { type Submit = {
success: boolean, success: boolean,
@@ -25,29 +23,20 @@ function isImmediateRejection(engineResult: string): boolean {
return _.startsWith(engineResult, 'tem') || _.startsWith(engineResult, 'tej'); return _.startsWith(engineResult, 'tem') || _.startsWith(engineResult, 'tej');
} }
function convertSubmitErrors(callback) { function formatResponse(response) {
return function(error, data) { if (isImmediateRejection(response.engine_result)) {
if (!error && isImmediateRejection(data.engineResult)) { throw new utils.common.errors.RippledError('Submit failed');
callback(new utils.common.errors.RippleError('Submit failed'), data);
} else {
callback(error, data);
} }
}; return convertKeysFromSnakeCaseToCamelCase(response);
}
function submitAsync(txBlob: string, callback: (err: any, data: any) => void
): void {
validate.blob(txBlob);
const request = new Request(this.remote, 'submit');
request.message.tx_blob = txBlob;
request.request(null,
utils.common.composeAsync(
data => utils.common.convertKeysFromSnakeCaseToCamelCase(data),
convertSubmitErrors(convertErrors(callback))));
} }
function submit(txBlob: string): Promise<Submit> { function submit(txBlob: string): Promise<Submit> {
return utils.promisify(submitAsync.bind(this))(txBlob); validate.blob(txBlob);
const request = {
command: 'submit',
tx_blob: txBlob
};
return this.connection.request(request).then(formatResponse);
} }
module.exports = submit; module.exports = submit;

View File

@@ -30,19 +30,12 @@ function createSuspendedPaymentCancellationTransaction(account: string,
return txJSON; return txJSON;
} }
function prepareSuspendedPaymentCancellationAsync(account: string,
payment: SuspendedPaymentCancellation, instructions: Instructions, callback
) {
const txJSON =
createSuspendedPaymentCancellationTransaction(account, payment);
utils.prepareTransaction(txJSON, this, instructions, callback);
}
function prepareSuspendedPaymentCancellation(account: string, function prepareSuspendedPaymentCancellation(account: string,
payment: SuspendedPaymentCancellation, instructions: Instructions = {} payment: SuspendedPaymentCancellation, instructions: Instructions = {}
): Promise<Prepare> { ): Promise<Prepare> {
return utils.promisify(prepareSuspendedPaymentCancellationAsync) const txJSON =
.call(this, account, payment, instructions); createSuspendedPaymentCancellationTransaction(account, payment);
return utils.prepareTransaction(txJSON, this, instructions);
} }
module.exports = prepareSuspendedPaymentCancellation; module.exports = prepareSuspendedPaymentCancellation;

View File

@@ -2,8 +2,7 @@
'use strict'; 'use strict';
const _ = require('lodash'); const _ = require('lodash');
const utils = require('./utils'); const utils = require('./utils');
const validate = utils.common.validate; const {validate, unixToRippleTimestamp, toRippledAmount} = utils.common;
const toRippledAmount = utils.common.toRippledAmount;
import type {Instructions, Prepare} from './types.js'; import type {Instructions, Prepare} from './types.js';
import type {Adjustment, MaxAdjustment, Memo} from '../common/types.js'; import type {Adjustment, MaxAdjustment, Memo} from '../common/types.js';
@@ -33,10 +32,10 @@ function createSuspendedPaymentCreationTransaction(account: string,
txJSON.Digest = payment.digest; txJSON.Digest = payment.digest;
} }
if (payment.allowCancelAfter !== undefined) { if (payment.allowCancelAfter !== undefined) {
txJSON.CancelAfter = utils.fromTimestamp(payment.allowCancelAfter); txJSON.CancelAfter = unixToRippleTimestamp(payment.allowCancelAfter);
} }
if (payment.allowExecuteAfter !== undefined) { if (payment.allowExecuteAfter !== undefined) {
txJSON.FinishAfter = utils.fromTimestamp(payment.allowExecuteAfter); txJSON.FinishAfter = unixToRippleTimestamp(payment.allowExecuteAfter);
} }
if (payment.source.tag !== undefined) { if (payment.source.tag !== undefined) {
txJSON.SourceTag = payment.source.tag; txJSON.SourceTag = payment.source.tag;
@@ -50,18 +49,11 @@ function createSuspendedPaymentCreationTransaction(account: string,
return txJSON; return txJSON;
} }
function prepareSuspendedPaymentCreationAsync(account: string,
payment: SuspendedPaymentCreation, instructions: Instructions, callback
) {
const txJSON = createSuspendedPaymentCreationTransaction(account, payment);
utils.prepareTransaction(txJSON, this, instructions, callback);
}
function prepareSuspendedPaymentCreation(account: string, function prepareSuspendedPaymentCreation(account: string,
payment: SuspendedPaymentCreation, instructions: Instructions = {} payment: SuspendedPaymentCreation, instructions: Instructions = {}
): Promise<Prepare> { ): Promise<Prepare> {
return utils.promisify(prepareSuspendedPaymentCreationAsync) const txJSON = createSuspendedPaymentCreationTransaction(account, payment);
.call(this, account, payment, instructions); return utils.prepareTransaction(txJSON, this, instructions);
} }
module.exports = prepareSuspendedPaymentCreation; module.exports = prepareSuspendedPaymentCreation;

View File

@@ -43,18 +43,11 @@ function createSuspendedPaymentExecutionTransaction(account: string,
return txJSON; return txJSON;
} }
function prepareSuspendedPaymentExecutionAsync(account: string,
payment: SuspendedPaymentExecution, instructions: Instructions, callback
) {
const txJSON = createSuspendedPaymentExecutionTransaction(account, payment);
utils.prepareTransaction(txJSON, this, instructions, callback);
}
function prepareSuspendedPaymentExecution(account: string, function prepareSuspendedPaymentExecution(account: string,
payment: SuspendedPaymentExecution, instructions: Instructions = {} payment: SuspendedPaymentExecution, instructions: Instructions = {}
): Promise<Prepare> { ): Promise<Prepare> {
return utils.promisify(prepareSuspendedPaymentExecutionAsync) const txJSON = createSuspendedPaymentExecutionTransaction(account, payment);
.call(this, account, payment, instructions); return utils.prepareTransaction(txJSON, this, instructions);
} }
module.exports = prepareSuspendedPaymentExecution; module.exports = prepareSuspendedPaymentExecution;

View File

@@ -50,18 +50,11 @@ function createTrustlineTransaction(account: string,
return txJSON; return txJSON;
} }
function prepareTrustlineAsync(account: string,
trustline: TrustLineSpecification, instructions: Instructions, callback
) {
const txJSON = createTrustlineTransaction(account, trustline);
utils.prepareTransaction(txJSON, this, instructions, callback);
}
function prepareTrustline(account: string, function prepareTrustline(account: string,
trustline: TrustLineSpecification, instructions: Instructions = {} trustline: TrustLineSpecification, instructions: Instructions = {}
): Promise<Prepare> { ): Promise<Prepare> {
return utils.promisify(prepareTrustlineAsync.bind(this))( const txJSON = createTrustlineTransaction(account, trustline);
account, trustline, instructions); return utils.prepareTransaction(txJSON, this, instructions);
} }
module.exports = prepareTrustline; module.exports = prepareTrustline;

View File

@@ -1,15 +1,10 @@
/* @flow */ /* @flow */
'use strict'; 'use strict';
const _ = require('lodash'); const _ = require('lodash');
const async = require('async');
const BigNumber = require('bignumber.js'); const BigNumber = require('bignumber.js');
const common = require('../common'); const common = require('../common');
const txFlags = common.txFlags; const txFlags = common.txFlags;
import type {Instructions} from './types.js'; import type {Instructions, Prepare} from './types.js';
function removeUndefined(obj: Object): Object {
return _.omit(obj, _.isUndefined);
}
function formatPrepareResponse(txJSON: Object): Object { function formatPrepareResponse(txJSON: Object): Object {
const instructions = { const instructions = {
@@ -31,36 +26,34 @@ function setCanonicalFlag(txJSON) {
txJSON.Flags = txJSON.Flags >>> 0; txJSON.Flags = txJSON.Flags >>> 0;
} }
type Callback = (err: ?(typeof Error),
data: {txJSON: string, instructions: Instructions}) => void;
function prepareTransaction(txJSON: Object, api: Object, function prepareTransaction(txJSON: Object, api: Object,
instructions: Instructions, callback: Callback instructions: Instructions
): void { ): Promise<Prepare> {
common.validate.instructions(instructions); common.validate.instructions(instructions);
const account = txJSON.Account; const account = txJSON.Account;
setCanonicalFlag(txJSON); setCanonicalFlag(txJSON);
function prepareMaxLedgerVersion(callback_) { function prepareMaxLedgerVersion(): Promise<Object> {
if (instructions.maxLedgerVersion !== undefined) { if (instructions.maxLedgerVersion !== undefined) {
txJSON.LastLedgerSequence = instructions.maxLedgerVersion; txJSON.LastLedgerSequence = instructions.maxLedgerVersion;
callback_(); return Promise.resolve(txJSON);
} else { }
const offset = instructions.maxLedgerVersionOffset !== undefined ? const offset = instructions.maxLedgerVersionOffset !== undefined ?
instructions.maxLedgerVersionOffset : 3; instructions.maxLedgerVersionOffset : 3;
api.remote.getLedgerSequence((error, ledgerVersion) => { return api.connection.getLedgerVersion().then(ledgerVersion => {
txJSON.LastLedgerSequence = ledgerVersion + offset; txJSON.LastLedgerSequence = ledgerVersion + offset;
callback_(error); return txJSON;
}); });
} }
}
function prepareFee(callback_) { function prepareFee(): Promise<Object> {
if (instructions.fee !== undefined) { if (instructions.fee !== undefined) {
txJSON.Fee = common.xrpToDrops(instructions.fee); txJSON.Fee = common.xrpToDrops(instructions.fee);
callback_(); return Promise.resolve(txJSON);
} else { }
common.serverInfo.getFee(api.remote, api._feeCushion).then(fee => { const cushion = api._feeCushion;
return common.serverInfo.getFee(api.connection, cushion).then(fee => {
const feeDrops = common.xrpToDrops(fee); const feeDrops = common.xrpToDrops(fee);
if (instructions.maxFee !== undefined) { if (instructions.maxFee !== undefined) {
const maxFeeDrops = common.xrpToDrops(instructions.maxFee); const maxFeeDrops = common.xrpToDrops(instructions.maxFee);
@@ -68,34 +61,30 @@ function prepareTransaction(txJSON: Object, api: Object,
} else { } else {
txJSON.Fee = feeDrops; txJSON.Fee = feeDrops;
} }
callback_(); return txJSON;
}); });
} }
}
function prepareSequence(callback_) { function prepareSequence(): Promise<Object> {
if (instructions.sequence !== undefined) { if (instructions.sequence !== undefined) {
txJSON.Sequence = instructions.sequence; txJSON.Sequence = instructions.sequence;
callback_(null, formatPrepareResponse(txJSON)); return Promise.resolve(txJSON);
} else { }
const request = { const request = {
command: 'account_info', command: 'account_info',
account: account account: account
}; };
api.remote.rawRequest(request, function(error, response) { return api.connection.request(request).then(response => {
txJSON.Sequence = response.account_data.Sequence; txJSON.Sequence = response.account_data.Sequence;
callback_(error, formatPrepareResponse(txJSON)); return txJSON;
}); });
} }
}
async.series([ return Promise.all([
prepareMaxLedgerVersion, prepareMaxLedgerVersion(),
prepareFee, prepareFee(),
prepareSequence prepareSequence()
], common.convertErrors(function(error, results) { ]).then(() => formatPrepareResponse(txJSON));
callback(error, results && results[2]);
}));
} }
function convertStringToHex(string: string) { function convertStringToHex(string: string) {
@@ -105,7 +94,7 @@ function convertStringToHex(string: string) {
function convertMemo(memo: Object): Object { function convertMemo(memo: Object): Object {
return { return {
Memo: removeUndefined({ Memo: common.removeUndefined({
MemoData: convertStringToHex(memo.data), MemoData: convertStringToHex(memo.data),
MemoType: convertStringToHex(memo.type), MemoType: convertStringToHex(memo.type),
MemoFormat: convertStringToHex(memo.format) MemoFormat: convertStringToHex(memo.format)
@@ -113,33 +102,9 @@ function convertMemo(memo: Object): Object {
}; };
} }
/**
* @param {Number} rpepoch (seconds since 1/1/2000 GMT)
* @return {Number} ms since unix epoch
*
*/
function toTimestamp(rpepoch: number): number {
return (rpepoch + 0x386D4380) * 1000;
}
/**
* @param {Number|Date} timestamp (ms since unix epoch)
* @return {Number} seconds since ripple epoch ( 1/1/2000 GMT)
*/
function fromTimestamp(timestamp: number | Date): number {
const timestamp_ = timestamp instanceof Date ?
timestamp.getTime() :
timestamp;
return Math.round(timestamp_ / 1000) - 0x386D4380;
}
module.exports = { module.exports = {
removeUndefined,
convertStringToHex, convertStringToHex,
fromTimestamp,
toTimestamp,
convertMemo, convertMemo,
prepareTransaction, prepareTransaction,
common, common
promisify: common.promisify
}; };

View File

@@ -4,12 +4,6 @@ const _ = require('lodash');
const assert = require('assert'); const assert = require('assert');
const Account = require('ripple-lib').Account; const Account = require('ripple-lib').Account;
const addresses = require('./fixtures/addresses');
const fixtures = require('./fixtures/api');
const accountLinesResponse = require('./fixtures/api/rippled/account-lines');
const setupAPI = require('./setup-api');
function createRemote(remoteOptions = {}) { function createRemote(remoteOptions = {}) {
return { return {
@@ -58,150 +52,6 @@ function createRemote(remoteOptions = {}) {
describe('Account', function() { describe('Account', function() {
describe('mocked', function() {
beforeEach(setupAPI.setup);
afterEach(setupAPI.teardown);
// this test is artificial, just to increase coverage
// because code inside listenerRemoved function in Account object
// will never be called in normal situation
// (Account object is subscribed to own events, so _subs counter never
// reach zero)
it('unsubscribe ', function(done) {
const account = new Account(this.api.remote, addresses.ACCOUNT);
this.mockRippled.expect({
request_subscribe: 1,
request_unsubscribe: 1,
request_account_info: 1
});
function dumb() {}
account.on('entry', dumb);
account._subs -= 1;
account.removeListener('entry', dumb);
setTimeout(() => {
done();
}, 100);
});
it('toJson', function() {
const account = new Account(this.api.remote, addresses.ACCOUNT);
const json = account.toJson();
assert.strictEqual(json, addresses.ACCOUNT);
});
it('entry', function(done) {
const account = new Account(this.api.remote, addresses.ACCOUNT);
account.entry((error, info) => {
assert.deepEqual(info, fixtures.rippled.account_info.normal.result);
done(error);
});
});
it('entry error', function(done) {
const account = new Account(this.api.remote, addresses.NOTFOUND);
account.entry((error) => {
assert(error);
error.remote.id = 0;
assert.deepEqual(error.remote, fixtures.rippled.account_info.notfound);
done();
});
});
it('getNextSequence not found', function(done) {
const account = new Account(this.api.remote, addresses.NOTFOUND);
account.getNextSequence((error, sequence) => {
assert.strictEqual(sequence, 1);
done(error);
});
});
it('lines', function(done) {
const account = new Account(this.api.remote, addresses.ACCOUNT);
account.lines((error, lines) => {
assert(lines);
const expected = JSON.parse(accountLinesResponse.normal({}))
.result.lines;
assert.deepEqual(lines.lines, expected);
done(error);
});
});
it('line', function(done) {
const account = new Account(this.api.remote, addresses.ACCOUNT);
account.line('015841551A748AD2C1F76FF6ECB0CCCD00000000',
'rs9M85karFkCRjvc6KMWn8Coigm9cbcgcx', (error, line) => {
const expected = JSON.parse(accountLinesResponse.normal({}))
.result.lines[22];
assert.deepEqual(line, expected);
done(error);
});
});
it('line when account not found', function(done) {
const account = new Account(this.api.remote, addresses.NOTFOUND);
account.line('', 'rs9M85karFkCRjvc6KMWn8Coigm9cbcgcx', (error) => {
assert(error);
error.remote.id = 0;
assert.deepEqual(error.remote, fixtures.rippled.account_info.notfound);
done();
});
});
it('submit ', function(done) {
const account = new Account(this.api.remote, addresses.ACCOUNT);
account._transactionManager.submit = function() {
done();
};
account.submit({});
});
// this one just for coverage - _subs can't be zero
it('notify - no subscribers ', function(done) {
const account = new Account(this.api.remote, addresses.ACCOUNT);
let fired = false;
account.on('transaction', function() {
fired = true;
});
account._subs = 0;
account.notify({});
setTimeout(() => {
assert(!fired);
done();
}, 100);
});
it('notify - transaction without account field ', function(done) {
const account = new Account(this.api.remote, addresses.ACCOUNT);
let transactionFired;
let transactionInboundFired = false;
account.on('transaction', function(transaction) {
transactionFired = transaction;
});
account.on('transaction-inbound', function() {
transactionInboundFired = true;
});
account.notify({transaction: {}});
setTimeout(() => {
assert.deepEqual(transactionFired, {transaction: {}});
assert(!transactionInboundFired);
done();
}, 100);
});
it('notify - transaction-inbound', function(done) {
const account = new Account(this.api.remote, addresses.ACCOUNT);
account.on('transaction-inbound', function(transaction) {
assert.deepEqual(transaction,
{transaction: {Account: addresses.NOTFOUND}});
done();
});
account.notify({transaction: {Account: addresses.NOTFOUND}});
});
});
describe('#_publicKeyToAddress()', function() { describe('#_publicKeyToAddress()', function() {
it('should throw an error if the key is invalid', function() { it('should throw an error if the key is invalid', function() {
@@ -271,7 +121,6 @@ describe('Account', function() {
account.publicKeyIsActive( account.publicKeyIsActive(
'025B32A54BFA33FB781581F49B235C0E2820C929FF41E677ADA5D3E53CFBA46332', '025B32A54BFA33FB781581F49B235C0E2820C929FF41E677ADA5D3E53CFBA46332',
function(err, is_valid) { function(err, is_valid) {
assert(err === null); assert(err === null);
assert(is_valid === false); assert(is_valid === false);
done(); done();

View File

@@ -193,10 +193,9 @@ describe('RippleAPI', function() {
it('submit - failure', function() { it('submit - failure', function() {
return this.api.submit('BAD').then(() => { return this.api.submit('BAD').then(() => {
assert(false, 'Should throw RippleError'); assert(false, 'Should throw RippledError');
}).catch(error => { }).catch(error => {
assert(error instanceof this.api.errors.RippleError); assert(error instanceof this.api.errors.RippledError);
assert(error.data);
}); });
}); });
@@ -361,7 +360,7 @@ describe('RippleAPI', function() {
it('getTransaction - missing ledger history', function() { it('getTransaction - missing ledger history', function() {
const hash = hashes.NOTFOUND_TRANSACTION_HASH; const hash = hashes.NOTFOUND_TRANSACTION_HASH;
// make gaps in history // make gaps in history
this.api.remote.getServer().emit('message', ledgerClosed); this.api.connection._ws.emit('message', JSON.stringify(ledgerClosed));
return this.api.getTransaction(hash).then(() => { return this.api.getTransaction(hash).then(() => {
assert(false, 'Should throw MissingLedgerHistoryError'); assert(false, 'Should throw MissingLedgerHistoryError');
}).catch(error => { }).catch(error => {
@@ -601,8 +600,8 @@ describe('RippleAPI', function() {
return this.api.getServerInfo().then(() => { return this.api.getServerInfo().then(() => {
assert(false, 'Should throw NetworkError'); assert(false, 'Should throw NetworkError');
}).catch(error => { }).catch(error => {
assert(error instanceof this.api.errors.NetworkError); assert(error instanceof this.api.errors.RippledError);
assert(error.message.indexOf('too much load') !== -1); assert(_.includes(error.message, 'slowDown'));
}); });
}); });
@@ -759,17 +758,20 @@ describe('RippleAPI', function() {
assert.deepEqual(utils.renameCounterpartyToIssuer(amountArg), amountArg); assert.deepEqual(utils.renameCounterpartyToIssuer(amountArg), amountArg);
}); });
it('ledger utils - getRecursive', function(done) { it('ledger utils - getRecursive', function() {
function getter(marker, limit, callback) { function getter(marker, limit) {
return new Promise((resolve, reject) => {
if (marker === undefined) { if (marker === undefined) {
callback(null, {marker: 'A', limit: limit, results: [1]}); resolve({marker: 'A', limit: limit, results: [1]});
} else { } else {
callback(new Error(), null); reject(new Error());
} }
});
} }
utils.getRecursive(getter, 10, (error) => { return utils.getRecursive(getter, 10).then(() => {
assert(false, 'Should throw Error');
}).catch(error => {
assert(error instanceof Error); assert(error instanceof Error);
done();
}); });
}); });
@@ -852,28 +854,6 @@ describe('RippleAPI', function() {
}); });
describe('common utils', function() {
it('wrapCatch', function(done) {
common.wrapCatch(function() {
throw new Error('error');
})(function(error) {
assert(error instanceof Error);
done();
});
});
it('convertExceptions', function() {
assert.throws(common.convertExceptions(function() {
throw new Error('fall through');
}), this.api.errors.ApiError);
assert.throws(common.convertExceptions(function() {
throw new Error('fall through');
}), /fall through/);
});
});
describe('common errors', function() { describe('common errors', function() {
it('TransactionError', function() { it('TransactionError', function() {
@@ -909,7 +889,7 @@ describe('RippleAPI', function() {
checkResult(responses.ledgerClosed, 'ledgerClosed', message); checkResult(responses.ledgerClosed, 'ledgerClosed', message);
done(); done();
}); });
this.api.remote.getServer().emit('message', ledgerClosed); this.api.connection._ws.emit('message', JSON.stringify(ledgerClosed));
}); });
}); });
@@ -965,9 +945,8 @@ describe('RippleAPI - offline', function() {
}); });
it('RippleAPI valid options', function() { it('RippleAPI valid options', function() {
const api = new RippleAPI({trace: true, servers: ['wss://s:1']}); const api = new RippleAPI({servers: ['wss://s:1']});
assert(api.remote.trace); assert.deepEqual(api.connection._url, 'wss://s:1');
assert.deepEqual(api.remote.servers, ['wss://s:1']);
}); });
it('RippleAPI invalid server uri', function() { it('RippleAPI invalid server uri', function() {

View File

@@ -107,8 +107,6 @@ module.exports = function(port) {
assert.strictEqual(request.command, 'subscribe'); assert.strictEqual(request.command, 'subscribe');
if (request.accounts) { if (request.accounts) {
assert(_.indexOf(_.values(addresses), request.accounts[0]) !== -1); assert(_.indexOf(_.values(addresses), request.accounts[0]) !== -1);
} else {
assert.deepEqual(request.streams, ['ledger', 'server']);
} }
conn.send(createResponse(request, fixtures.subscribe)); conn.send(createResponse(request, fixtures.subscribe));
}); });

View File

@@ -25,8 +25,8 @@ function setupMockRippledConnection(testcase, port, done) {
testcase.mockRippled = createMockRippled(port); testcase.mockRippled = createMockRippled(port);
testcase.api = new RippleAPI({servers: ['ws://localhost:' + port]}); testcase.api = new RippleAPI({servers: ['ws://localhost:' + port]});
testcase.api.connect().then(() => { testcase.api.connect().then(() => {
testcase.api.remote.getServer().once('ledger_closed', () => done()); testcase.api.once('ledgerClosed', () => done());
testcase.api.remote.getServer().emit('message', ledgerClosed); testcase.api.connection._ws.emit('message', JSON.stringify(ledgerClosed));
}).catch(done); }).catch(done);
} }