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"
},
"babel-runtime": {
"version": "5.8.25",
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-5.8.25.tgz",
"version": "5.8.29",
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-5.8.29.tgz",
"dependencies": {
"core-js": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-1.1.4.tgz"
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.3.tgz"
}
}
},
"bignumber.js": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-2.0.7.tgz"
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-2.1.0.tgz"
},
"bn.js": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-3.1.2.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"
}
}
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-3.2.0.tgz"
},
"extend": {
"version": "1.2.1",
@@ -121,8 +111,8 @@
"resolved": "https://registry.npmjs.org/ripple-address-codec/-/ripple-address-codec-2.0.1.tgz",
"dependencies": {
"x-address-codec": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/x-address-codec/-/x-address-codec-0.7.0.tgz",
"version": "0.7.2",
"resolved": "https://registry.npmjs.org/x-address-codec/-/x-address-codec-0.7.2.tgz",
"dependencies": {
"base-x": {
"version": "1.0.1",
@@ -136,17 +126,13 @@
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/ripple-binary-codec/-/ripple-binary-codec-0.0.6.tgz",
"dependencies": {
"bn.js": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-3.2.0.tgz"
},
"create-hash": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.2.tgz",
"dependencies": {
"cipher-base": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.1.tgz"
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.2.tgz"
},
"ripemd160": {
"version": "1.0.1",
@@ -177,8 +163,8 @@
"resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.2.tgz",
"dependencies": {
"cipher-base": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.1.tgz"
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.2.tgz"
},
"inherits": {
"version": "2.0.1",
@@ -205,8 +191,8 @@
"resolved": "https://registry.npmjs.org/brorand/-/brorand-1.0.5.tgz"
},
"elliptic": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-5.1.0.tgz",
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-5.2.0.tgz",
"dependencies": {
"inherits": {
"version": "2.0.1",
@@ -222,7 +208,13 @@
},
"ripple-lib-value": {
"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": {
"version": "0.1.0",
@@ -232,20 +224,6 @@
"version": "0.7.2",
"resolved": "https://registry.npmjs.org/ws/-/ws-0.7.2.tgz",
"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": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz"
@@ -253,20 +231,6 @@
"ultron": {
"version": "1.0.2",
"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",
"bignumber.js": "^2.0.3",
"bn.js": "^3.1.1",
"es6-promisify": "^2.0.0",
"extend": "~1.2.1",
"hash.js": "^1.0.3",
"https-proxy-agent": "^1.0.0",
@@ -39,7 +38,7 @@
"assert-diff": "^1.0.1",
"babel": "^5.8.21",
"babel-core": "^5.8.22",
"babel-eslint": "^4.0.5",
"babel-eslint": "^4.1.3",
"babel-loader": "^5.3.2",
"coveralls": "~2.10.0",
"eslint": "^1.3.0",

View File

@@ -2,7 +2,9 @@
const {EventEmitter} = require('events');
const WebSocket = require('ws');
// 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) {
return type === 'ledgerClosed' ||
@@ -10,48 +12,6 @@ function isStreamMessageType(type) {
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 {
constructor(url, options = {}) {
super();
@@ -64,31 +24,42 @@ class Connection extends EventEmitter {
this._nextRequestID = 1;
}
_onMessage(message) {
try {
// return value is array of arguments to Connection.emit
_parseMessage(message) {
const data = JSON.parse(message);
if (data.type === 'response') {
if (!(Number.isInteger(data.id) && data.id >= 0)) {
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)) {
if (data.type === 'ledgerClosed') {
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) {
this.emit('error', data.error, data.error_message); // e.g. slowDown
} else {
return ['error', data.error, data.error_message]; // e.g. slowDown
}
throw new UnexpectedError('unrecognized message type: ' + data.type);
}
_onMessage(message) {
let parameters;
try {
parameters = this._parseMessage(message);
} catch (error) {
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;
}
@@ -96,32 +67,34 @@ class Connection extends EventEmitter {
return this._ws !== null;
}
isConnected() {
return this._state === WebSocket.OPEN && this._isReady;
}
_onUnexpectedClose() {
this._isReady = false;
this.connect().then();
}
_onOpen() {
const subscribeRequest = {
const request = {
command: 'subscribe',
streams: ['ledger']
};
return this.request(subscribeRequest).then(() => {
return this.request({command: 'server_info'}).then(response => {
this._ledgerVersion = Number(response.info.validated_ledger.seq);
return this.request(request).then(response => {
this._ledgerVersion = Number(response.ledger_index);
this._availableLedgerVersions.parseAndAddRanges(
response.info.complete_ledgers);
response.validated_ledgers);
this._isReady = true;
this.emit('connected');
});
});
}
connect() {
return new Promise((resolve, reject) => {
if (this.state === WebSocket.OPEN) {
if (this._state === WebSocket.OPEN) {
resolve();
} else if (this.state === WebSocket.CONNECTING) {
} else if (this._state === WebSocket.CONNECTING) {
this._ws.once('open', resolve);
} else {
this._ws = new WebSocket(this._url);
@@ -133,10 +106,10 @@ class Connection extends EventEmitter {
}
disconnect() {
return new Promise((resolve) => {
if (this.state === WebSocket.CLOSED) {
return new Promise(resolve => {
if (this._state === WebSocket.CLOSED) {
resolve();
} else if (this.state === WebSocket.CLOSING) {
} else if (this._state === WebSocket.CLOSING) {
this._ws.once('close', resolve);
} else {
this._ws.removeListener('close', this._onUnexpectedClose);
@@ -158,7 +131,7 @@ class Connection extends EventEmitter {
return new Promise((resolve, reject) => {
if (!this._shouldBeConnected) {
reject(new NotConnectedError());
} else if (this.state === WebSocket.OPEN && this._isReady) {
} else if (this._state === WebSocket.OPEN && this._isReady) {
promise.then(resolve, reject);
} else {
this.once('connected', () => promise.then(resolve, reject));
@@ -167,14 +140,13 @@ class Connection extends EventEmitter {
}
getLedgerVersion() {
return this._whenReady(
new Promise(resolve => resolve(this._ledgerVersion)));
return this._whenReady(Promise.resolve(this._ledgerVersion));
}
hasLedgerVersions(lowLedgerVersion, highLedgerVersion) {
return this._whenReady(new Promise(resolve =>
resolve(this._availableLedgerVersions.containsRange(
lowLedgerVersion, highLedgerVersion))));
return this._whenReady(Promise.resolve(
this._availableLedgerVersions.containsRange(
lowLedgerVersion, highLedgerVersion || this._ledgerVersion)));
}
hasLedgerVersion(ledgerVersion) {
@@ -214,8 +186,10 @@ class Connection extends EventEmitter {
function cleanup() {
clearTimeout(timer);
self.removeAllListeners(eventName);
if (self._ws !== null) {
self._ws.removeListener('close', onDisconnect);
}
}
function _resolve(response) {
cleanup();

View File

@@ -1,17 +1,26 @@
'use strict';
const core = require('./utils').core;
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 = {
passwordSpent: flags.PasswordSpent,
requireDestinationTag: flags.RequireDestTag,
requireAuthorization: flags.RequireAuth,
disallowIncomingXRP: flags.DisallowXRP,
disableMasterKey: flags.DisableMaster,
noFreeze: flags.NoFreeze,
globalFreeze: flags.GlobalFreeze,
defaultRipple: flags.DefaultRipple
passwordSpent: accountRootFlags.PasswordSpent,
requireDestinationTag: accountRootFlags.RequireDestTag,
requireAuthorization: accountRootFlags.RequireAuth,
disallowIncomingXRP: accountRootFlags.DisallowXRP,
disableMasterKey: accountRootFlags.DisableMaster,
noFreeze: accountRootFlags.NoFreeze,
globalFreeze: accountRootFlags.GlobalFreeze,
defaultRipple: accountRootFlags.DefaultRipple
};
const AccountFlagIndices = {

View File

@@ -34,6 +34,48 @@ RippleError.prototype.inspect = function(depth) {
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) {
this.message = message;
}
@@ -117,5 +159,11 @@ module.exports = {
MissingLedgerHistoryError,
TimeOutError,
ApiError,
RippleError
RippleError,
ConnectionError,
RippledError,
NotConnectedError,
DisconnectedError,
TimeoutError,
UnexpectedError
};

View File

@@ -3,7 +3,6 @@ const utils = require('./utils');
module.exports = {
Connection: require('./connection'),
core: utils.core,
constants: require('./constants'),
errors: require('./errors'),
validate: require('./validate'),
@@ -13,12 +12,11 @@ module.exports = {
xrpToDrops: utils.xrpToDrops,
toRippledAmount: utils.toRippledAmount,
generateAddress: utils.generateAddress,
composeAsync: utils.composeAsync,
wrapCatch: utils.wrapCatch,
generateAddressAPI: utils.generateAddressAPI,
removeUndefined: utils.removeUndefined,
convertErrors: utils.convertErrors,
convertExceptions: utils.convertExceptions,
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.json'),
require('./schemas/quality.json'),
require('./schemas/remote-options.json'),
require('./schemas/api-options.json'),
require('./schemas/sequence.json'),
require('./schemas/settings-options.json'),
require('./schemas/settings-transaction.json'),

View File

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

View File

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

View File

@@ -2,9 +2,7 @@
'use strict';
const _ = require('lodash');
const BigNumber = require('bignumber.js');
const core = require('../../core');
const errors = require('./errors');
const es6promisify = require('es6-promisify');
const keypairs = require('ripple-keypairs');
import type {Amount, RippledAmount} from './types.js';
@@ -36,63 +34,12 @@ function generateAddress(options?: Object): Object {
return {secret, address};
}
type AsyncFunction = (...x: any) => void
function wrapCatch(asyncFunction: AsyncFunction): AsyncFunction {
return function() {
function generateAddressAPI(options?: Object): Object {
try {
asyncFunction.apply(this, arguments);
} 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);
return generateAddress(options);
} catch (error) {
throw new errors.ApiError(error.message);
}
};
}
const FINDSNAKE = /([a-zA-Z]_[a-zA-Z])/g;
@@ -111,25 +58,38 @@ function convertKeysFromSnakeCaseToCamelCase(obj: any): any {
return obj;
}
function promisify(asyncFunction: AsyncFunction): Function {
return es6promisify(wrapCatch(asyncFunction));
}
function removeUndefined(obj: Object): Object {
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 = {
core,
dropsToXrp,
xrpToDrops,
toRippledAmount,
generateAddress,
composeAsync,
wrapCatch,
convertExceptions,
convertErrors,
generateAddressAPI,
convertKeysFromSnakeCaseToCamelCase,
promisify,
removeUndefined
removeUndefined,
rippleToUnixTimestamp,
unixToRippleTimestamp
};

View File

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

View File

@@ -36,22 +36,34 @@ const prepareSettings = require('./transaction/settings');
const sign = require('./transaction/sign');
const submit = require('./transaction/submit');
const errors = require('./common').errors;
const convertExceptions = require('./common').convertExceptions;
const generateAddress = convertExceptions(common.generateAddress);
const generateAddress = common.generateAddressAPI;
const computeLedgerHash = require('./offline/ledgerhash');
const getLedger = require('./ledger/ledger');
function RippleAPI(options: {}) {
common.validate.remoteOptions(options);
type APIOptions = {
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
EventEmitter.call(this);
}
const _options = _.assign({}, options, {automatic_resubmission: false});
this._feeCushion = _options.feeCushion || 1.2;
this.remote = new common.core.Remote(_options);
this.remote.on('ledger_closed', message => {
if (options.servers !== undefined) {
const servers: Array<string> = options.servers;
if (servers.length === 1) {
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));
});
} else {
throw new errors.RippleError('Multi-server not implemented');
}
}
}
util.inherits(RippleAPI, EventEmitter);

View File

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

View File

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

View File

@@ -1,11 +1,8 @@
/* @flow */
'use strict';
const _ = require('lodash');
const async = require('async');
const utils = require('./utils');
const getTrustlines = require('./trustlines');
const {validate, composeAsync, convertErrors} = utils.common;
import type {Remote, GetLedgerSequenceCallback} from '../../core/remote';
const {validate} = utils.common;
import type {Connection} from '../common/connection.js';
import type {TrustlinesOptions, Trustline} from './trustlines-types.js';
@@ -43,42 +40,26 @@ function formatBalances(options, balances) {
return result;
}
function getTrustlinesAsync(account: string, options: TrustlinesOptions,
callback
) {
getTrustlines.call(this, account, options)
.then(data => callback(null, data))
.catch(callback);
}
function getLedgerVersionHelper(remote: Remote, optionValue?: number,
callback: GetLedgerSequenceCallback
) {
function getLedgerVersionHelper(connection: Connection, optionValue?: number
): Promise<number> {
if (optionValue !== undefined && optionValue !== null) {
callback(null, optionValue);
} else {
remote.getLedgerSequence(callback);
return Promise.resolve(optionValue);
}
}
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)));
return connection.getLedgerVersion();
}
function getBalances(account: string, options: TrustlinesOptions = {}
): 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;

View File

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

View File

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

View File

@@ -1,47 +1,42 @@
/* @flow */
'use strict';
const _ = require('lodash');
const async = require('async');
const utils = require('./utils');
const {validate, composeAsync, convertErrors} = utils.common;
const {validate} = utils.common;
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';
type GetOrders = Array<Order>
function requestAccountOffers(remote: Remote, address: string,
ledgerVersion: number, marker: string, limit: number, callback
) {
remote.rawRequest({
function requestAccountOffers(connection: Connection, address: string,
ledgerVersion: number, marker: string, limit: number
): Promise {
return connection.request({
command: 'account_offers',
account: address,
marker: marker,
limit: utils.clamp(limit, 10, 400),
ledger_index: ledgerVersion
},
composeAsync((data) => ({
}).then(data => {
return {
marker: data.marker,
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 = {}
): Promise<GetOrders> {
return utils.promisify(async.seq(
utils.getLedgerOptionsWithLedgerVersion,
getOrdersAsync)).call(this, account, options);
validate.address(account);
validate.getOrdersOptions(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;

View File

@@ -1,7 +1,7 @@
/* @flow */
'use strict';
const utils = require('./utils');
const flags = utils.core.Remote.flags.offer;
const flags = require('./flags').orderFlags;
const parseAmount = require('./amount');
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';
const _ = require('lodash');
const utils = require('./utils');
const flags = utils.core.Remote.flags.offer;
const flags = require('./flags').orderFlags;
const parseAmount = require('./amount');
function parseOrderbookOrder(order: Object): Object {

View File

@@ -2,8 +2,8 @@
'use strict';
const _ = require('lodash');
const transactionParser = require('ripple-lib-transactionparser');
const toTimestamp = require('../../../core/utils').toTimestamp;
const utils = require('../utils');
const rippleToUnixTimestamp = utils.common.rippleToUnixTimestamp;
const BigNumber = require('bignumber.js');
function adjustQualityForXRP(
@@ -19,7 +19,8 @@ function adjustQualityForXRP(
}
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) {
@@ -87,6 +88,5 @@ module.exports = {
dropsToXrp: utils.common.dropsToXrp,
constants: utils.common.constants,
txFlags: utils.common.txFlags,
core: utils.common.core,
removeUndefined: utils.common.removeUndefined
};

View File

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

View File

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

View File

@@ -1,28 +1,22 @@
/* @flow */
'use strict';
const _ = require('lodash');
const async = require('async');
const utils = require('./utils');
const parseTransaction = require('./parse/transaction');
const {validate, convertErrors, errors} = utils.common;
const RippleError = require('../../core/rippleerror').RippleError;
const {validate, errors} = utils.common;
import type {Connection} from '../common/connection.js';
import type {TransactionType, TransactionOptions} from './transaction-types';
import type {Remote} from '../../core/remote';
import type {CallbackType, TransactionType,
GetTransactionResponseCallback, TransactionOptions}
from './transaction-types';
function attachTransactionDate(remote: Remote, tx: Object,
callback: CallbackType
) {
function attachTransactionDate(connection: Connection, tx: Object
): Promise<TransactionType> {
if (tx.date) {
callback(null, tx);
return;
return Promise.resolve(tx);
}
if (!tx.ledger_index) {
callback(new errors.NotFoundError('ledger_index not found in tx'));
return;
return new Promise(() => {
throw new errors.NotFoundError('ledger_index not found in tx');
});
}
const request = {
@@ -30,14 +24,16 @@ function attachTransactionDate(remote: Remote, tx: Object,
ledger_index: tx.ledger_index
};
remote.rawRequest(request, (error, data) => {
if (error) {
callback(new errors.NotFoundError('Transaction ledger not found'));
} 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'));
return connection.request(request).then(data => {
if (typeof data.ledger.close_time === 'number') {
return _.assign({date: data.ledger.close_time}, tx);
}
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);
}
function getTransactionAsync(identifier: string, options: TransactionOptions,
callback: GetTransactionResponseCallback
) {
validate.identifier(identifier);
validate.getTransactionOptions(options);
const remote = this.remote;
function callbackWrapper(error_?: Error, tx?: Object,
maxLedgerVersion?: number
) {
let error = error_;
if (!error && tx && tx.validated !== true) {
return callback(new errors.NotFoundError('Transaction not found'));
function convertError(connection: Connection, options: TransactionOptions,
error: Error
): Promise<Error> {
const _error = (error.message === 'txnNotFound') ?
new errors.NotFoundError('Transaction not found') : error;
if (_error instanceof errors.NotFoundError) {
return utils.hasCompleteLedgerRange(connection, options.minLedgerVersion,
options.maxLedgerVersion).then(hasCompleteLedgerRange => {
if (!hasCompleteLedgerRange) {
return utils.isPendingLedgerVersion(
connection, options.maxLedgerVersion)
.then(isPendingLedgerVersion => {
return isPendingLedgerVersion ?
new errors.PendingLedgerVersionError() :
new errors.MissingLedgerHistoryError();
});
}
if (error instanceof RippleError && error.remote &&
error.remote.error === 'txnNotFound') {
error = new errors.NotFoundError('Transaction not found');
return _error;
});
}
return Promise.resolve(_error);
}
// 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());
function formatResponse(options: TransactionOptions, tx: TransactionType
): TransactionType {
if (tx.validated !== true || !isTransactionInRange(tx, options)) {
throw new errors.NotFoundError('Transaction not found');
}
// 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));
return parseTransaction(tx);
}
function getTransaction(identifier: string,
options: TransactionOptions = {}
): 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;

View File

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

View File

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

View File

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

View File

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

View File

@@ -37,18 +37,11 @@ function createOrderTransaction(account: string, order: Order): Object {
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,
instructions: Instructions = {}
): Promise<Prepare> {
return utils.promisify(prepareOrderAsync.bind(this))(
account, order, instructions);
const txJSON = createOrderTransaction(account, order);
return utils.prepareTransaction(txJSON, this, instructions);
}
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,
instructions: Instructions = {}
): Promise<Prepare> {
return utils.promisify(prepareOrderCancellationAsync.bind(this))(
account, sequence, instructions);
const txJSON = createOrderCancellationTransaction(account, sequence);
return utils.prepareTransaction(txJSON, this, instructions);
}
module.exports = prepareOrderCancellation;

View File

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

View File

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

View File

@@ -2,9 +2,7 @@
'use strict';
const _ = require('lodash');
const utils = require('./utils');
const validate = utils.common.validate;
const Request = utils.common.core.Request;
const convertErrors = utils.common.convertErrors;
const {validate, convertKeysFromSnakeCaseToCamelCase} = utils.common;
type Submit = {
success: boolean,
@@ -25,29 +23,20 @@ function isImmediateRejection(engineResult: string): boolean {
return _.startsWith(engineResult, 'tem') || _.startsWith(engineResult, 'tej');
}
function convertSubmitErrors(callback) {
return function(error, data) {
if (!error && isImmediateRejection(data.engineResult)) {
callback(new utils.common.errors.RippleError('Submit failed'), data);
} else {
callback(error, data);
function formatResponse(response) {
if (isImmediateRejection(response.engine_result)) {
throw new utils.common.errors.RippledError('Submit failed');
}
};
}
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))));
return convertKeysFromSnakeCaseToCamelCase(response);
}
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;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -4,12 +4,6 @@ const _ = require('lodash');
const assert = require('assert');
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 = {}) {
return {
@@ -58,150 +52,6 @@ function createRemote(remoteOptions = {}) {
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() {
it('should throw an error if the key is invalid', function() {
@@ -271,7 +121,6 @@ describe('Account', function() {
account.publicKeyIsActive(
'025B32A54BFA33FB781581F49B235C0E2820C929FF41E677ADA5D3E53CFBA46332',
function(err, is_valid) {
assert(err === null);
assert(is_valid === false);
done();

View File

@@ -193,10 +193,9 @@ describe('RippleAPI', function() {
it('submit - failure', function() {
return this.api.submit('BAD').then(() => {
assert(false, 'Should throw RippleError');
assert(false, 'Should throw RippledError');
}).catch(error => {
assert(error instanceof this.api.errors.RippleError);
assert(error.data);
assert(error instanceof this.api.errors.RippledError);
});
});
@@ -361,7 +360,7 @@ describe('RippleAPI', function() {
it('getTransaction - missing ledger history', function() {
const hash = hashes.NOTFOUND_TRANSACTION_HASH;
// 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(() => {
assert(false, 'Should throw MissingLedgerHistoryError');
}).catch(error => {
@@ -601,8 +600,8 @@ describe('RippleAPI', function() {
return this.api.getServerInfo().then(() => {
assert(false, 'Should throw NetworkError');
}).catch(error => {
assert(error instanceof this.api.errors.NetworkError);
assert(error.message.indexOf('too much load') !== -1);
assert(error instanceof this.api.errors.RippledError);
assert(_.includes(error.message, 'slowDown'));
});
});
@@ -759,17 +758,20 @@ describe('RippleAPI', function() {
assert.deepEqual(utils.renameCounterpartyToIssuer(amountArg), amountArg);
});
it('ledger utils - getRecursive', function(done) {
function getter(marker, limit, callback) {
it('ledger utils - getRecursive', function() {
function getter(marker, limit) {
return new Promise((resolve, reject) => {
if (marker === undefined) {
callback(null, {marker: 'A', limit: limit, results: [1]});
resolve({marker: 'A', limit: limit, results: [1]});
} 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);
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() {
it('TransactionError', function() {
@@ -909,7 +889,7 @@ describe('RippleAPI', function() {
checkResult(responses.ledgerClosed, 'ledgerClosed', message);
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() {
const api = new RippleAPI({trace: true, servers: ['wss://s:1']});
assert(api.remote.trace);
assert.deepEqual(api.remote.servers, ['wss://s:1']);
const api = new RippleAPI({servers: ['wss://s:1']});
assert.deepEqual(api.connection._url, 'wss://s:1');
});
it('RippleAPI invalid server uri', function() {

View File

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

View File

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