ripplelib
Version:
A JavaScript API for interacting with Ripple in Node.js and the browser
54 lines (51 loc) • 1.51 MB
JavaScript
var ripple =
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ exports: {},
/******/ id: moduleId,
/******/ loaded: false
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {
eval("'use strict';\n\nvar _Object$keys = __webpack_require__(28)['default'];\n\nexports.Remote = __webpack_require__(1).Remote;\nexports.Request = __webpack_require__(2).Request;\nexports.Amount = __webpack_require__(3).Amount;\nexports.Account = __webpack_require__(4).Account;\nexports.Transaction = __webpack_require__(5).Transaction;\nexports.Currency = __webpack_require__(6).Currency;\nexports.Base = __webpack_require__(7).Base;\nexports.UInt128 = __webpack_require__(8).UInt128;\nexports.UInt160 = __webpack_require__(9).UInt160;\nexports.UInt256 = __webpack_require__(10).UInt256;\nexports.Seed = __webpack_require__(11).Seed;\nexports.KeyPair = __webpack_require__(12).KeyPair;\nexports.Meta = __webpack_require__(13).Meta;\nexports.SerializedObject = __webpack_require__(14).SerializedObject;\nexports.RippleError = __webpack_require__(15).RippleError;\nexports.Message = __webpack_require__(16).Message;\nexports.binformat = __webpack_require__(17);\nexports.utils = __webpack_require__(18);\nexports.Server = __webpack_require__(19).Server;\nexports.Ledger = __webpack_require__(20).Ledger;\nexports.TransactionQueue = __webpack_require__(21).TransactionQueue;\nexports.RangeSet = __webpack_require__(22).RangeSet;\nexports.convertBase = __webpack_require__(23);\n\nexports._test = {\n Log: __webpack_require__(24),\n PathFind: __webpack_require__(25).PathFind,\n TransactionManager: __webpack_require__(26).TransactionManager\n};\n\n// Important: We do not guarantee any specific version of SJCL or for any\n// specific features to be included. The version and configuration may change at\n// any time without warning.\n//\n// However, for programs that are tied to a specific version of ripple.js like\n// the official client, it makes sense to expose the SJCL instance so we don't\n// have to include it twice.\nexports.sjcl = __webpack_require__(18).sjcl;\nexports.types = __webpack_require__(27);\n\n// camelCase to under_scored API conversion\nfunction attachUnderscored(name) {\n var o = exports[name];\n\n _Object$keys(o.prototype).forEach(function (key) {\n var UPPERCASE = /([A-Z]{1})[a-z]+/g;\n\n if (!UPPERCASE.test(key)) {\n return;\n }\n\n var underscored = key.replace(UPPERCASE, function (c) {\n return '_' + c.toLowerCase();\n });\n\n o.prototype[underscored] = o.prototype[key];\n });\n}\n\n['Remote', 'Request', 'Transaction', 'Account', 'Server'].forEach(attachUnderscored);\n\n// vim:sw=2:sts=2:ts=8:et\n\n/*****************\n ** WEBPACK FOOTER\n ** ./src/index.js\n ** module id = 0\n ** module chunks = 0\n **/\n//# sourceURL=webpack:///./src/index.js?");
/***/ },
/* 1 */
/***/ function(module, exports, __webpack_require__) {
eval("'use strict';\n\n// Interface to manage connections to rippled servers\n//\n// - We never send binary data.\n// - We use the W3C interface for node and browser compatibility:\n// http://www.w3.org/TR/websockets/#the-websocket-interface\n//\n// This class is intended for both browser and Node.js use.\n//\n// This class is designed to work via peer protocol via either the public or\n// private WebSocket interfaces. The JavaScript class for the peer protocol\n// has not yet been implemented. However, this class has been designed for it\n// to be a very simple drop option.\n\nvar _Object$keys = __webpack_require__(28)['default'];\n\nvar EventEmitter = __webpack_require__(40).EventEmitter;\nvar util = __webpack_require__(41);\nvar assert = __webpack_require__(46);\nvar LRU = __webpack_require__(47);\nvar async = __webpack_require__(48);\nvar lodash = __webpack_require__(42);\nvar Server = __webpack_require__(19).Server;\nvar Request = __webpack_require__(2).Request;\nvar Amount = __webpack_require__(3).Amount;\nvar Currency = __webpack_require__(6).Currency;\nvar UInt160 = __webpack_require__(9).UInt160;\nvar UInt256 = __webpack_require__(10).UInt256;\nvar Transaction = __webpack_require__(5).Transaction;\nvar Account = __webpack_require__(4).Account;\nvar Meta = __webpack_require__(13).Meta;\nvar OrderBook = __webpack_require__(29).OrderBook;\nvar PathFind = __webpack_require__(25).PathFind;\nvar SerializedObject = __webpack_require__(14).SerializedObject;\nvar RippleError = __webpack_require__(15).RippleError;\nvar utils = __webpack_require__(18);\nvar hashprefixes = __webpack_require__(30);\nvar log = __webpack_require__(24).internal.sub('remote');\nvar Seed = __webpack_require__(11).Seed;\nvar KeyPair = __webpack_require__(12).KeyPair;\n\n/**\r\n * Interface to manage connections to rippled servers\r\n *\r\n * @param {Object} Options\r\n */\n\nfunction Remote() {\n var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];\n\n EventEmitter.call(this);\n\n var self = this;\n\n lodash.merge(this, lodash.defaults(options, Remote.DEFAULTS));\n\n this.state = 'offline'; // 'online', 'offline'\n this._server_fatal = false; // server exited\n this._stand_alone = undefined;\n this._testnet = undefined;\n\n this._ledger_current_index = undefined;\n this._ledger_hash = undefined;\n this._ledger_time = undefined;\n\n this._connection_count = 0;\n this._connected = false;\n this._should_connect = true;\n\n this._transaction_listeners = 0;\n this._received_tx = new LRU({ max: 100 });\n this._cur_path_find = null;\n\n if (this.local_signing) {\n // Local signing implies local fees and sequences\n this.local_sequence = true;\n this.local_fee = true;\n }\n\n this._servers = [];\n this._primary_server = undefined;\n\n // Cache information for accounts.\n // DEPRECATED, will be removed\n // Consider sequence numbers stable if you know you're not generating bad\n // transactions.\n // Otherwise, clear it to have it automatically refreshed from the network.\n // account : { seq : __ }\n this.accounts = {};\n\n // Account objects by AccountId.\n this._accounts = {};\n\n // OrderBook objects\n this._books = {};\n\n // Secrets that we know about.\n // Secrets can be set by calling set_secret(account, secret).\n // account : secret\n this.secrets = {};\n\n // cache keypairs.\n this.keyPairs = {};\n\n // Cache for various ledgers.\n // XXX Clear when ledger advances.\n this.ledgers = {\n current: {\n account_root: {}\n }\n };\n\n if (typeof this.trusted !== 'boolean') {\n throw new TypeError('trusted must be a boolean');\n }\n if (typeof this.trace !== 'boolean') {\n throw new TypeError('trace must be a boolean');\n }\n if (typeof this.allow_partial_history !== 'boolean') {\n throw new TypeError('allow_partial_history must be a boolean');\n }\n if (typeof this.max_fee !== 'number') {\n throw new TypeError('max_fee must be a number');\n }\n if (typeof this.max_attempts !== 'number') {\n throw new TypeError('max_attempts must be a number');\n }\n if (typeof this.fee_cushion !== 'number') {\n throw new TypeError('fee_cushion must be a number');\n }\n if (typeof this.local_signing !== 'boolean') {\n throw new TypeError('local_signing must be a boolean');\n }\n if (typeof this.local_fee !== 'boolean') {\n throw new TypeError('local_fee must be a boolean');\n }\n if (typeof this.local_sequence !== 'boolean') {\n throw new TypeError('local_sequence must be a boolean');\n }\n if (typeof this.canonical_signing !== 'boolean') {\n throw new TypeError('canonical_signing must be a boolean');\n }\n if (typeof this.submission_timeout !== 'number') {\n throw new TypeError('submission_timeout must be a number');\n }\n if (typeof this.last_ledger_offset !== 'number') {\n throw new TypeError('last_ledger_offset must be a number');\n }\n if (typeof this.orderbook_limit !== 'number') {\n throw new TypeError('orderbook_limit must be a number');\n }\n if (!Array.isArray(this.servers)) {\n throw new TypeError('servers must be an array');\n }\n\n this.setMaxListeners(this.max_listeners);\n\n this.servers.forEach(function (serverOptions) {\n var server = self.addServer(serverOptions);\n server.setMaxListeners(self.max_listeners);\n });\n\n function listenersModified(action, event) {\n // Automatically subscribe and unsubscribe to orderbook\n // on the basis of existing event listeners\n if (lodash.contains(Remote.TRANSACTION_EVENTS, event)) {\n switch (action) {\n case 'add':\n if (++self._transaction_listeners === 1) {\n self.requestSubscribe('transactions').request();\n }\n break;\n case 'remove':\n if (--self._transaction_listeners === 0) {\n self.requestUnsubscribe('transactions').request();\n }\n break;\n }\n }\n }\n\n this.on('newListener', function (event) {\n listenersModified('add', event);\n });\n\n this.on('removeListener', function (event) {\n listenersModified('remove', event);\n });\n}\n\nutil.inherits(Remote, EventEmitter);\n\nRemote.DEFAULTS = {\n trusted: false,\n trace: false,\n allow_partial_history: true,\n local_sequence: true,\n local_fee: true,\n local_signing: true,\n canonical_signing: true,\n fee_cushion: 1.2,\n max_fee: 1000000, // 1 XRP\n max_attempts: 10,\n submission_timeout: 1000 * 20,\n last_ledger_offset: 3,\n orderbook_limit: 100,\n servers: [],\n max_listeners: 0 // remove Node EventEmitter warnings\n};\n\nRemote.TRANSACTION_EVENTS = ['transaction', 'transaction_all'];\n\n// Flags for ledger entries. In support of account_root().\nRemote.flags = {\n // AccountRoot\n account_root: {\n PasswordSpent: 0x00010000, // password set fee is spent\n RequireDestTag: 0x00020000, // require a DestinationTag for payments\n RequireAuth: 0x00040000, // require a authorization to hold IOUs\n DisallowXRP: 0x00080000, // disallow sending XRP\n DisableMaster: 0x00100000, // force regular key\n DefaultRipple: 0x00800000,\n NoFreeze: 0x00200000, // permanently disallowed freezing trustlines\n GlobalFreeze: 0x00400000 // trustlines globally frozen\n },\n // Offer\n offer: {\n Passive: 0x00010000,\n Sell: 0x00020000 // offer was placed as a sell\n },\n // Ripple state\n state: {\n LowReserve: 0x00010000, // entry counts toward reserve\n HighReserve: 0x00020000,\n LowAuth: 0x00040000,\n HighAuth: 0x00080000,\n LowNoRipple: 0x00100000,\n HighNoRipple: 0x00200000,\n LowFreeze: 0x00400000,\n HighFreeze: 0x00800000\n }\n};\n\n/**\r\n * Check that server message is valid\r\n *\r\n * @param {Object} message\r\n * @return Boolean\r\n */\n\nRemote.isValidMessage = function (message) {\n return typeof message === 'object' && typeof message.type === 'string';\n};\n\n/**\r\n * Check that server message contains valid\r\n * ledger data\r\n *\r\n * @param {Object} message\r\n * @return {Boolean}\r\n */\n\nRemote.isValidLedgerData = function (message) {\n return typeof message === 'object' && typeof message.fee_base === 'number' && typeof message.fee_ref === 'number' && typeof message.ledger_hash === 'string' && typeof message.ledger_index === 'number' && typeof message.ledger_time === 'number' && typeof message.reserve_base === 'number' && typeof message.reserve_inc === 'number';\n};\n\n/**\r\n * Check that server message contains valid\r\n * load status data\r\n *\r\n * @param {Object} message\r\n * @return {Boolean}\r\n */\n\nRemote.isValidLoadStatus = function (message) {\n return typeof message.load_base === 'number' && typeof message.load_factor === 'number';\n};\n\n/**\r\n * Check that provided ledger is validated\r\n *\r\n * @param {Object} ledger\r\n * @return {Boolean}\r\n */\n\nRemote.isValidated = function (message) {\n return message && typeof message === 'object' && message.validated === true;\n};\n\n/**\r\n * Set the emitted state: 'online' or 'offline'\r\n *\r\n * @param {String} state\r\n */\n\nRemote.prototype._setState = function (state) {\n if (this.state !== state) {\n if (this.trace) {\n log.info('set_state:', state);\n }\n\n this.state = state;\n this.emit('state', state);\n\n switch (state) {\n case 'online':\n this._online_state = 'open';\n this._connected = true;\n this.emit('connect');\n this.emit('connected');\n break;\n case 'offline':\n this._online_state = 'closed';\n this._connected = false;\n this.emit('disconnect');\n this.emit('disconnected');\n break;\n }\n }\n};\n\n/**\r\n * Inform remote that the remote server is not comming back.\r\n */\n\nRemote.prototype.setServerFatal = function () {\n this._server_fatal = true;\n};\n\n/**\r\n * Enable debug output\r\n *\r\n * @param {Boolean} trace\r\n */\n\nRemote.prototype.setTrace = function (trace) {\n this.trace = trace === undefined || trace;\n return this;\n};\n\nRemote.prototype._trace = function () {\n if (this.trace) {\n log.info.apply(log, arguments);\n }\n};\n\n/**\r\n * Store a secret - allows the Remote to automatically fill\r\n * out auth information.\r\n *\r\n * @param {String} account\r\n * @param {String} secret\r\n */\n\nRemote.prototype.setSecret = function (account, secret, id) {\n this.secrets[account] = secret;\n\n // try to cache keypairs, so we don't need to re-generate it for each tx.\n this.setKey(account, this.generateKey(secret, id));\n};\n\n// Generate KeyPair from given secret.\n// id could be an address, or index of account family.\nRemote.prototype.generateKey = Remote.prototype.generateKeyPair = function (secret, id) {\n try {\n return Seed.from_json(secret).get_key(id);\n } catch (e) {\n throw new Error('Invalid Secret!');\n }\n};\n\n/**\r\n * Store a keypair \r\n *\r\n * @param {String} account\r\n * @param {String | KeyPair} privatekey string or KeyPair\r\n * privatekey can be in Hex, RFC1751, base58\r\n */\nRemote.prototype.setKey = Remote.prototype.setKeyPair = function (account, key) {\n key = KeyPair.from_json(key);\n if (!key.is_valid()) throw new Error('Invalid private/keyPair!');\n this.keyPairs[account] = key;\n};\n\n// get keypairs from cache, or generate from secret;\nRemote.prototype.getKey = Remote.prototype.getKeyPair = function (account) {\n var key = this.keyPairs[account];\n if (!key) key = this.keyPairs[account] = this.generateKey(this.secrets[account]);\n return key;\n};\n\nRemote.prototype.addServer = function (options) {\n var self = this;\n var server = new Server(this, options);\n\n function serverMessage(data) {\n self._handleMessage(data, server);\n }\n\n server.on('message', serverMessage);\n\n function serverConnect() {\n self._connection_count += 1;\n\n if (options.primary) {\n self._setPrimaryServer(server);\n }\n if (self._connection_count === 1) {\n self._setState('online');\n }\n if (self._connection_count === self._servers.length) {\n self.emit('ready');\n }\n }\n\n server.on('connect', serverConnect);\n\n function serverDisconnect() {\n self._connection_count--;\n if (self._connection_count === 0) {\n self._setState('offline');\n }\n }\n\n server.on('disconnect', serverDisconnect);\n\n this._servers.push(server);\n\n return server;\n};\n\n/**\r\n * Reconnect to Ripple network\r\n */\n\nRemote.prototype.reconnect = function () {\n if (!this._should_connect) {\n return;\n }\n\n log.info('reconnecting');\n\n this._servers.forEach(function (server) {\n server.reconnect();\n });\n};\n\n/**\r\n * Connect to the Ripple network\r\n *\r\n * @param {Function} callback\r\n * @api public\r\n */\n\nRemote.prototype.connect = function (callback) {\n if (!this._servers.length) {\n throw new Error('No servers available.');\n }\n\n if (typeof callback !== 'function') callback = function () {};\n\n if (this.isConnected()) {\n callback();\n return this;\n }\n\n this.once('connect', callback);\n this._should_connect = true;\n\n this._servers.forEach(function (server) {\n server.connect();\n });\n\n return this;\n};\n\n/**\r\n * Disconnect from the Ripple network.\r\n *\r\n * @param {Function} callback\r\n * @api public\r\n */\n\nRemote.prototype.disconnect = function (callback_) {\n if (!this._servers.length) {\n throw new Error('No servers available, not disconnecting');\n }\n\n var callback = lodash.isFunction(callback_) ? callback_ : function () {};\n\n this._should_connect = false;\n\n if (!this.isConnected()) {\n callback();\n return this;\n }\n\n this.once('disconnect', callback);\n\n this._servers.forEach(function (server) {\n server.disconnect();\n });\n\n this._set_state('offline');\n\n return this;\n};\n\n/**\r\n * Handle server message. Server messages are proxied to\r\n * the Remote, such that global events can be handled\r\n *\r\n * It is possible for messages to be dispatched after the\r\n * connection is closed.\r\n *\r\n * @param {JSON} message\r\n * @param {Server} server\r\n */\n\nRemote.prototype._handleMessage = function (message, server) {\n if (!Remote.isValidMessage(message)) {\n // Unexpected response from remote.\n var error = new RippleError('remoteUnexpected', 'Unexpected response from remote: ' + JSON.stringify(message));\n\n this.emit('error', error);\n log.error(error);\n return;\n }\n\n switch (message.type) {\n case 'ledgerClosed':\n this._handleLedgerClosed(message, server);\n break;\n case 'serverStatus':\n this._handleServerStatus(message, server);\n break;\n case 'transaction':\n this._handleTransaction(message, server);\n break;\n case 'path_find':\n this._handlePathFind(message, server);\n break;\n default:\n if (this.trace) {\n log.info(message.type + ': ', message);\n }\n break;\n }\n};\n\nRemote.prototype.getLedgerSequence = function () {\n if (!this._ledger_current_index) {\n throw new Error('Ledger sequence has not yet been initialized');\n }\n return this._ledger_current_index;\n};\n\n/**\r\n * Handle server ledger_closed event\r\n *\r\n * @param {Object} message\r\n */\n\nRemote.prototype._handleLedgerClosed = function (message, server) {\n var self = this;\n\n // XXX If not trusted, need to verify we consider ledger closed.\n // XXX Also need to consider a slow server or out of order response.\n // XXX Be more defensive fields could be missing or of wrong type.\n // YYY Might want to do some cache management.\n if (!Remote.isValidLedgerData(message)) {\n return;\n }\n\n var ledgerAdvanced = message.ledger_index >= this._ledger_current_index;\n\n if (isNaN(this._ledger_current_index) || ledgerAdvanced) {\n this._ledger_time = message.ledger_time;\n this._ledger_hash = message.ledger_hash;\n this._ledger_current_index = message.ledger_index + 1;\n\n if (this.isConnected()) {\n this.emit('ledger_closed', message, server);\n } else {\n this.once('connect', function () {\n // Delay until server is 'online'\n self.emit('ledger_closed', message, server);\n });\n }\n }\n};\n\n/**\r\n * Handle server server_status event\r\n *\r\n * @param {Object} message\r\n */\n\nRemote.prototype._handleServerStatus = function (message, server) {\n this.emit('server_status', message, server);\n};\n\n/**\r\n * Handle server transaction event\r\n *\r\n * @param {Object} message\r\n */\n\nRemote.prototype._handleTransaction = function (message, server) {\n // XXX If not trusted, need proof.\n var transactionHash = message.transaction.hash;\n\n if (this._received_tx.get(transactionHash)) {\n // De-duplicate transactions\n return;\n }\n\n if (message.validated) {\n this._received_tx.set(transactionHash, true);\n }\n\n if (this.trace) {\n log.info('tx:', message);\n }\n\n var metadata = message.meta || message.metadata;\n\n if (metadata) {\n // Process metadata\n message.mmeta = new Meta(metadata);\n\n // Pass the event on to any related Account objects\n message.mmeta.getAffectedAccounts().forEach(function (account) {\n if (this._accounts[account]) {\n this._accounts[account].notify(message);\n }\n }, this);\n\n // Pass the event on to any related OrderBooks\n message.mmeta.getAffectedBooks().forEach(function (book) {\n if (this._books[book]) {\n this._books[book].notify(message);\n }\n }, this);\n } else {\n // Transaction could be from proposed transaction stream\n // XX\n ['Account', 'Destination'].forEach(function (prop) {\n if (this._accounts[message.transaction[prop]]) {\n this._accounts[message.transaction[prop]].notify(message);\n }\n }, this);\n }\n\n this.emit('transaction', message, server);\n this.emit('transaction_all', message, server);\n};\n\n/**\r\n * Handle server path_find event\r\n *\r\n * @param {Object} message\r\n */\n\nRemote.prototype._handlePathFind = function (message, server) {\n // Pass the event to the currently open PathFind object\n if (this._cur_path_find) {\n this._cur_path_find.notify_update(message);\n }\n\n this.emit('path_find_all', message, server);\n};\n\n/**\r\n * Returns the current ledger hash\r\n *\r\n * @return {String} ledger hash\r\n */\n\nRemote.prototype.getLedgerHash = function () {\n return this._ledger_hash;\n};\n\n/**\r\n * Set primary server. Primary server will be selected\r\n * to handle requested regardless of its internally-tracked\r\n * priority score\r\n *\r\n * @param {Server} server\r\n */\n\nRemote.prototype._setPrimaryServer = Remote.prototype.setPrimaryServer = function (server) {\n if (this._primary_server) {\n this._primary_server._primary = false;\n }\n this._primary_server = server;\n this._primary_server._primary = true;\n};\n\n/**\r\n * Get connected state\r\n *\r\n * @return {Boolean} connected\r\n */\n\nRemote.prototype.isConnected = function () {\n return this._connected;\n};\n\n/**\r\n * Get array of connected servers\r\n */\n\nRemote.prototype.getConnectedServers = function () {\n return this._servers.filter(function (server) {\n return server.isConnected();\n });\n};\n\n/**\r\n * Select a server to handle a request. Servers are\r\n * automatically prioritized\r\n */\n\nRemote.prototype._getServer = Remote.prototype.getServer = function () {\n if (this._primary_server && this._primary_server.isConnected()) {\n return this._primary_server;\n }\n\n if (!this._servers.length) {\n return null;\n }\n\n var connectedServers = this.getConnectedServers();\n if (connectedServers.length === 0 || !connectedServers[0]) {\n return null;\n }\n\n var server = connectedServers[0];\n var cScore = server._score + server._fee;\n\n for (var i = 1; i < connectedServers.length; i++) {\n var _server = connectedServers[i];\n var bScore = _server._score + _server._fee;\n if (bScore < cScore) {\n server = _server;\n cScore = bScore;\n }\n }\n\n return server;\n};\n\n/**\r\n * Send a request. This method is called internally by Request\r\n * objects. Each Request contains a reference to Remote, and\r\n * Request.request calls Request.remote.request\r\n *\r\n * @param {Request} request\r\n */\n\nRemote.prototype.request = function (request_) {\n var request = request_;\n\n if (typeof request === 'string') {\n if (!/^request_/.test(request)) {\n request = 'request_' + request;\n }\n\n if (typeof this[request] === 'function') {\n var args = Array.prototype.slice.call(arguments, 1);\n return this[request].apply(this, args);\n }\n\n throw new Error('Command does not exist: ' + request);\n }\n\n if (!(request instanceof Request)) {\n throw new Error('Argument is not a Request');\n }\n\n if (!this._servers.length) {\n return request.emit('error', new Error('No servers available'));\n }\n if (!this.isConnected()) {\n return this.once('connect', this.request.bind(this, request));\n }\n if (request.server === null) {\n return request.emit('error', new Error('Server does not exist'));\n }\n\n var server = request.server || this.getServer();\n if (server) {\n server._request(request);\n } else {\n request.emit('error', new Error('No servers available'));\n }\n};\n\n/**\r\n * Request ping\r\n *\r\n * @param [String] server host\r\n * @param [Function] callback\r\n * @return {Request} request\r\n */\n\nRemote.prototype.ping = Remote.prototype.requestPing = function (host, callback_) {\n var request = new Request(this, 'ping');\n var callback = callback_;\n\n switch (typeof host) {\n case 'function':\n callback = host;\n break;\n case 'string':\n request.setServer(host);\n break;\n }\n\n var then = Date.now();\n\n request.once('success', function () {\n request.emit('pong', Date.now() - then);\n });\n\n request.callback(callback, 'pong');\n\n return request;\n};\n\n/**\r\n * Request server_info\r\n *\r\n * @param [Function] callback\r\n * @return {Request} request\r\n */\n\nRemote.prototype.requestServerInfo = function (callback) {\n return new Request(this, 'server_info').callback(callback);\n};\n\n/**\r\n * Request ledger\r\n *\r\n * @return {Request} request\r\n */\n\nRemote.prototype.requestLedger = function (options, callback_) {\n // XXX This is a bad command. Some variants don't scale.\n // XXX Require the server to be trusted.\n // utils.assert(this.trusted);\n\n var request = new Request(this, 'ledger');\n var callback = callback_;\n\n switch (typeof options) {\n case 'undefined':\n break;\n case 'function':\n callback = options;\n break;\n\n case 'object':\n if (!options) {\n break;\n }\n\n _Object$keys(options).forEach(function (o) {\n switch (o) {\n case 'full':\n case 'expand':\n case 'transactions':\n case 'accounts':\n request.message[o] = options[o] ? true : false;\n break;\n case 'ledger':\n request.selectLedger(options.ledger);\n break;\n case 'ledger_index':\n case 'ledger_hash':\n request.message[o] = options[o];\n break;\n case 'closed':\n case 'current':\n case 'validated':\n request.message.ledger_index = o;\n break;\n }\n }, options);\n break;\n\n default:\n request.selectLedger(options);\n break;\n }\n\n request.callback(callback);\n\n return request;\n};\n\n/**\r\n * Request ledger_closed\r\n *\r\n * @param [Function] callback\r\n * @return {Request} request\r\n */\n\nRemote.prototype.requestLedgerClosed = Remote.prototype.requestLedgerHash = function (callback) {\n // utils.assert(this.trusted); // If not trusted, need to check proof.\n return new Request(this, 'ledger_closed').callback(callback);\n};\n\n/**\r\n * Request ledger_header\r\n *\r\n * @param [Function] callback\r\n * @return {Request} request\r\n */\n\nRemote.prototype.requestLedgerHeader = function (callback) {\n return new Request(this, 'ledger_header').callback(callback);\n};\n\n/**\r\n * Request ledger_current\r\n *\r\n * Get the current proposed ledger entry. May be closed (and revised)\r\n * at any time (even before returning).\r\n *\r\n * Only for unit testing.\r\n *\r\n * @param [Function] callback\r\n * @return {Request} request\r\n */\n\nRemote.prototype.requestLedgerCurrent = function (callback) {\n return new Request(this, 'ledger_current').callback(callback);\n};\n\n/**\r\n * Request ledger_data\r\n *\r\n * Get the contents of a specified ledger\r\n *\r\n * @param {Object} options\r\n * @property {Boolean} [options.binary]- Flag which determines if rippled\r\n * returns binary or parsed JSON\r\n * @property {String|Number} [options.ledger] - Hash or sequence of a ledger\r\n * to get contents for\r\n * @property {Number} [options.limit] - Number of contents to retrieve\r\n * from the ledger\r\n * @property {Function} callback\r\n *\r\n * @callback\r\n * @param {Error} error\r\n * @param {LedgerData} ledgerData\r\n *\r\n * @return {Request} request\r\n */\n\nRemote.prototype.requestLedgerData = function (options, callback) {\n var request = new Request(this, 'ledger_data');\n\n request.message.binary = options.binary !== false;\n request.selectLedger(options.ledger);\n request.message.limit = options.limit;\n\n request.once('success', function (res) {\n if (options.binary === false) {\n request.emit('state', res);\n return;\n }\n\n function iterator(ledgerData, next) {\n async.setImmediate(function () {\n next(null, Remote.parseBinaryLedgerData(ledgerData));\n });\n }\n\n function complete(err, state) {\n if (err) {\n request.emit('error', err);\n } else {\n res.state = state;\n request.emit('state', res);\n }\n }\n\n async.mapSeries(res.state, iterator, complete);\n });\n\n request.callback(callback, 'state');\n\n return request;\n};\n\n/**\r\n * Request ledger_entry\r\n *\r\n * @param [String] type\r\n * @param [Function] callback\r\n * @return {Request} request\r\n */\n\nRemote.prototype.requestLedgerEntry = function (type, callback_) {\n // utils.assert(this.trusted);\n // If not trusted, need to check proof, maybe talk packet protocol.\n\n var self = this;\n\n var request = new Request(this, 'ledger_entry');\n var callback = lodash.isFunction(type) ? type : callback_;\n\n // Transparent caching. When .request() is invoked, look in the Remote object\n // for the result. If not found, listen, cache result, and emit it.\n //\n // Transparent caching:\n if (type === 'account_root') {\n request.request_default = request.request;\n\n request.request = function () {\n // Intercept default request.\n var bDefault = true;\n\n if (!self._ledger_hash && type === 'account_root') {\n var cache = self.ledgers.current.account_root;\n\n if (!cache) {\n cache = self.ledgers.current.account_root = {};\n }\n\n var node = self.ledgers.current.account_root[request.message.account_root];\n\n if (node) {\n // Emulate fetch of ledger entry.\n // console.log('request_ledger_entry: emulating');\n // YYY Missing lots of fields.\n request.emit('success', { node: node });\n bDefault = false;\n } else {\n // Was not cached.\n // XXX Only allow with trusted mode. Must sync response with advance\n switch (type) {\n case 'account_root':\n request.once('success', function (message) {\n // Cache node.\n // console.log('request_ledger_entry: caching');\n self.ledgers.current.account_root[message.node.Account] = message.node;\n });\n break;\n\n default:\n // This type not cached.\n // console.log('request_ledger_entry: non-cached type');\n }\n }\n }\n\n if (bDefault) {\n // console.log('request_ledger_entry: invoking');\n request.request_default();\n }\n };\n }\n\n request.callback(callback);\n\n return request;\n};\n\n/**\r\n * Request subscribe\r\n *\r\n * @param {Array} streams\r\n * @param [Function] callback\r\n * @return {Request} request\r\n */\n\nRemote.prototype.requestSubscribe = function (streams, callback) {\n var request = new Request(this, 'subscribe');\n\n if (streams) {\n request.message.streams = Array.isArray(streams) ? streams : [streams];\n }\n\n request.callback(callback);\n\n return request;\n};\n\n/**\r\n * Request usubscribe\r\n *\r\n * @param {Array} streams\r\n * @param [Function] callback\r\n * @return {Request} request\r\n */\n\nRemote.prototype.requestUnsubscribe = function (streams, callback) {\n var request = new Request(this, 'unsubscribe');\n\n if (streams) {\n request.message.streams = Array.isArray(streams) ? streams : [streams];\n }\n\n request.callback(callback);\n\n return request;\n};\n\n/**\r\n * Request transaction_entry\r\n *\r\n * @param {String} transaction hash\r\n * @param {String|Number} ledger hash or sequence\r\n * @param [Function] callback\r\n * @return {Request} request\r\n */\n\nRemote.prototype.requestTransactionEntry = function (options_, callback_) {\n // If not trusted, need to check proof, maybe talk packet protocol.\n // utils.assert(this.trusted);\n var options = undefined,\n callback = callback_;\n\n if (lodash.isPlainObject(options_)) {\n options = lodash.merge({\n ledger: options_.ledger_index || options_.ledger_hash\n }, options_);\n } else {\n log.warn('DEPRECATED: First argument to request constructor should be' + ' an object containing request properties');\n\n var args = Array.prototype.slice.call(arguments);\n\n if (lodash.isFunction(lodash.last(args))) {\n callback = args.pop();\n }\n\n options = {\n hash: args.shift(),\n ledger: args.shift()\n };\n }\n\n var request = new Request(this, 'transaction_entry');\n request.txHash(options.hash);\n\n switch (typeof options.ledger) {\n case 'string':\n case 'number':\n request.selectLedger(options.ledger);\n break;\n case 'undefined':\n request.ledgerIndex('validated');\n break;\n default:\n throw new Error('ledger must be a ledger index or hash');\n }\n\n request.callback(callback);\n\n return request;\n};\n\n/**\r\n * Request tx\r\n *\r\n * @param {Object|String} hash\r\n * @property {String} hash.hash - Transaction hash\r\n * @property {Boolean} [hash.binary=true] - Flag which determines if rippled\r\n * returns binary or parsed JSON\r\n * @param [Function] callback\r\n * @return {Request} request\r\n */\n\nRemote.prototype.requestTransaction = Remote.prototype.requestTx = function (options_, callback_) {\n var options = undefined,\n callback = callback_;\n\n if (lodash.isPlainObject(options_)) {\n options = lodash.merge({}, options_);\n } else {\n log.warn('DEPRECATED: First argument to request constructor should be' + ' an object containing request properties');\n\n var args = Array.prototype.slice.call(arguments);\n\n if (lodash.isFunction(lodash.last(args))) {\n callback = args.pop();\n }\n\n options = {\n hash: args.shift(),\n binary: args.shift()\n };\n }\n\n var request = new Request(this, 'tx');\n request.message.binary = options.binary !== false;\n request.message.transaction = options.hash;\n\n request.once('success', function (res) {\n if (options.binary === false) {\n request.emit('transaction', res);\n return;\n }\n\n request.emit('transaction', Remote.parseBinaryTransaction(res));\n });\n\n request.callback(callback, 'transaction');\n\n return request;\n};\n\n/**\r\n * Account Request\r\n *\r\n * Optional paging with limit and marker options\r\n * supported in rippled for 'account_lines' and 'account_offers'\r\n *\r\n * The paged responses aren't guaranteed to be reliable between\r\n * ledger closes. You have to supply a ledger_index or ledger_hash\r\n * when paging to ensure a complete response\r\n *\r\n * @param {String} type - request name, e.g. 'account_lines'\r\n * @param {Object} options - all optional\r\n * @param {String} account - ripple address\r\n * @param {String} peer - ripple address\r\n * @param [String|Number] ledger identifier\r\n * @param [Number] limit - max results per response\r\n * @param {String} marker - start position in response paging\r\n * @param [Function] callback\r\n * @return {Request}\r\n * @throws {Error} if a marker is provided, but no ledger_index or ledger_hash\r\n */\n\nRemote.accountRequest = function (command, options_, callback_) {\n var options = undefined,\n callback = callback_;\n\n if (lodash.isPlainObject(options_)) {\n options = lodash.merge({}, options_);\n } else {\n log.warn('DEPRECATED: First argument to request constructor should be' + ' an object containing request properties');\n\n var args = Array.prototype.slice.call(arguments);\n\n if (lodash.isFunction(lodash.last(args))) {\n callback = args.pop();\n }\n\n options = {\n account: args.shift(),\n peer: args.shift(),\n ledger: args.shift(),\n limit: args.shift(),\n marker: args.shift()\n };\n }\n\n var _options = options;\n var account = _options.account;\n var ledger = _options.ledger;\n var peer = _options.peer;\n var limit = _options.limit;\n var marker = _options.marker;\n\n // if a marker is given, we need a ledger\n // check if a valid ledger_index or ledger_hash is provided\n if (marker) {\n if (!(Number(ledger) > 0) && !UInt256.is_valid(ledger)) {\n throw new Error('A ledger_index or ledger_hash must be provided when using a marker');\n }\n }\n\n var request = new Request(this, command);\n\n if (account) {\n account = UInt160.json_rewrite(account);\n request.message.account = account;\n }\n\n request.selectLedger(ledger);\n\n if (UInt160.is_valid(peer)) {\n request.message.peer = UInt160.json_rewrite(peer);\n }\n\n if (!isNaN(limit)) {\n limit = Number(limit);\n\n // max for 32-bit unsigned int is 4294967295\n // we'll clamp to 1e9\n if (limit > 1e9) {\n limit = 1e9;\n }\n // min for 32-bit unsigned int is 0\n // we'll clamp to 0\n if (limit < 0) {\n limit = 0;\n }\n\n request.message.limit = limit;\n }\n\n if (marker) {\n request.message.marker = marker;\n }\n\n ['strict', 'queue', 'signer_lists'].forEach(function (key) {\n if (_options.hasOwnProperty(key)) request.message[key] = _options[key];\n });\n\n request.callback(callback);\n\n return request;\n};\n\n/**\r\n * Request account_info\r\n *\r\n * @param {Object} options\r\n * @param {String} account - ripple address\r\n * @param {String} peer - ripple address\r\n * @param [String|Number] ledger identifier\r\n * @param [Function] callback\r\n * @return {Request}\r\n */\n\nRemote.prototype.requestAccountInfo = function () {\n for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {\n args[_key] = arguments[_key];\n }\n\n var options = ['account_info'].concat(args);\n return Remote.accountRequest.apply(this, options);\n};\n\n/**\r\n * Request account_currencies\r\n *\r\n * @param {Object} options\r\n * @param {String} account - ripple address\r\n * @param {String} peer - ripple address\r\n * @param [String|Number] ledger identifier\r\n * @param [Function] callback\r\n * @return {Request}\r\n */\n\nRemote.prototype.requestAccountCurrencies = function () {\n for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {\n args[_key2] = arguments[_key2];\n }\n\n var options = ['account_currencies'].concat(args);\n return Remote.accountRequest.apply(this, options);\n};\n\n/**\r\n * Request account_lines\r\n *\r\n * Requests for account_lines support paging, provide a limit and marker\r\n * to page through responses.\r\n *\r\n * The paged responses aren't guaranteed to be reliable between\r\n * ledger closes. You have to supply a ledger_index or ledger_hash\r\n * when paging to ensure a complete response\r\n *\r\n * @param {Object} options\r\n * @param {String} account - ripple address\r\n * @param {String} peer - ripple address\r\n * @param [String|Number] ledger identifier\r\n * @param [Number] limit - max results per response\r\n * @param {String} marker - start position in response paging\r\n * @param [Function] callback\r\n * @return {Request}\r\n */\n\nRemote.prototype.requestAccountLines = function () {\n for (var _len3 = arguments.length, args = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {\n args[_key3] = arguments[_key3];\n }\n\n // XXX Does this require the server to be trusted?\n // utils.assert(this.trusted);\n var options = ['account_lines'].concat(args);\n return Remote.accountRequest.apply(this, options);\n};\n\n/**\r\n * Request account_offers\r\n *\r\n * Requests for account_offers support paging, provide a limit and marker\r\n * to page through responses.\r\n *\r\n * The paged responses aren't guaranteed to be reliable between\r\n * ledger closes. You have to supply a ledger_index or ledger_hash\r\n * when paging to ensure a complete response\r\n *\r\n * @param {Object} options\r\n * @param {String} account - ripple address\r\n * @param [String|Number] ledger identifier\r\n * @param [Number] limit - max results per response\r\n * @param {String} marker - start position in response paging\r\n * @param [Function] callback\r\n * @return {Request}\r\n */\n\nRemote.prototype.requestAccountOffers = function () {\n for (var _len4 = arguments.length, args = Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {\n args[_key4] = arguments[_key4];\n }\n\n var options = ['account_offers'].concat(args);\n return Remote.accountRequest.apply(this, options);\n};\n\n/**\r\n * Request account_tx\r\n *\r\n * @param {Object} options\r\n *\r\n * @param {String} account\r\n * @param [Number] ledger_index_min defaults to -1\r\n * @param [Number] ledger_index_max defaults to -1\r\n * @param [Boolean] binary, defaults to true\r\n * @param [Boolean] parseBinary, defaults to true\r\n * @param [Boolean] count, defaults to false\r\n * @param [Boolean] descending, defaults to false\r\n * @param [Number] offset, defaults to 0\r\n * @param [Number] limit\r\n *\r\n * @param [Function] callback\r\n * @return {Request}\r\n */\n\nRemote.prototype.requestAccountTransactions = Remote.prototype.requestAccountTx = function (options, callback) {\n // XXX Does this require the server to be trusted?\n // utils.assert(this.trusted);\n\n var request = new Request(this, 'account_tx');\n\n options.binary = options.binary !== false;\n\n if (options.min_ledger !== undefined) {\n options.ledger_index_min = options.min_ledger;\n }\n\n if (options.max_ledger !== undefined) {\n options.ledger_index_max = options.max_ledger;\n }\n\n if (options.binary && options.parseBinary === undefined) {\n options.parseBinary = true;\n }\n\n _Object$keys(options).forEach(function (o) {\n switch (o) {\n case 'account':\n case 'ledger_index_min': // earliest\n case 'ledger_index_max': // latest\n case 'binary': // false\n case 'count': // false\n case 'descending': // false\n case 'offset': // 0\n case 'limit':\n\n // extended account_tx\n case 'forward': // false\n case 'marker':\n request.message[o] = this[o];\n break;\n }\n }, options);\n\n request.once('success', function (res) {\n if (!options.parseBinary) {\n request.emit('transactions', res);\n return;\n }\n\n function iterator(transaction, next) {\n async.setImmediate(function () {\n next(null, Remote.parseBinaryAccountTransaction(transaction));\n });\n }\n\n function complete(err, transactions) {\n if (err) {\n request.emit('error', err);\n } else {\n res.transactions = transactions;\n request.emit('transactions', res);\n }\n }\n\n async.mapSeries(res.transactions, iterator, complete);\n });\n\n request.callback(callback, 'transactions');\n\n return request;\n};\n\n/**\r\n * @param {Object} transaction\r\n * @return {Transaction}\r\n */\n\nRemote.parseBinaryAccountTransaction = function (transaction) {\n var tx_obj = new SerializedObject(transaction.tx_blob);\n var tx_obj_json = tx_obj.to_json();\n var meta = new SerializedObject(transaction.meta).to_json();\n\n var tx_result = {\n validated: transaction.validated\n };\n\n tx_result.meta = meta;\n tx_result.tx = tx_obj_json;\n tx_result.tx.hash = tx_obj.hash(hashprefixes.HASH_TX_ID).to_hex();\n tx_result.tx.ledger_index = transaction.ledger_index;\n tx_result.tx.inLedger = transaction.ledger_index;\n\n if (typeof meta.DeliveredAmount === 'object') {\n tx_result.meta.delivered_amount = meta.DeliveredAmount;\n } else {\n switch (typeof tx_obj_json.Amount) {\n case 'string':\n case 'object':\n tx_result.meta.delivered_amount = tx_obj_json.Amount;\n break;\n }\n }\n\n return tx_result;\n};\n\nRemote.parseBinaryTransaction = function (transaction) {\n var tx_obj = new SerializedObject(transaction.tx).to_json();\n var meta = new SerializedObject(transaction.meta).to_json();\n\n var tx_result = tx_obj;\n\n tx_result.date = transaction.date;\n tx_result.hash = transaction.hash;\n tx_result.inLedger = transaction.inLedger;\n tx_result.ledger_index = transaction.ledger_index;\n tx_result.meta = meta;\n tx_result.validated = transaction.validated;\n\n switch (typeof meta.DeliveredAmount) {\n case 'string':\n case 'object':\n tx_result.meta.delivered_amount = meta.DeliveredAmount;\n break;\n default:\n switch (typeof tx_obj.Amount) {\n case 'string':\n case 'object':\n tx_result.meta.delivered_amount = tx_obj.Amount;\n break;\n }\n }\n\n return tx_result;\n};\n\n/**\r\n * Parse binary ledger state data\r\n *\r\n * @param {Object} ledgerData\r\n * @property {String} ledgerData.data\r\n * @property {String} ledgerData.index\r\n *\r\n * @return {State}\r\n */\n\nRemote.parseBinaryLedgerData = function (ledgerData) {\n var data = new SerializedObject(ledgerData.data).to_json();\n data.index = ledgerData.index;\n return data;\n};\n\n/**\r\n * Request the overall transaction history.\r\n *\r\n * Returns a list of transactions that happened recently on the network. The\r\n * default number of transactions to be returned is 20.\r\n *\r\n * @param [Number] start\r\n * @param [Function] callback\r\n * @return {Request}\r\n */\n\nRemote.prototype.requestTransactionHistory = Remote.prototype.requestTxHistory = function (start_, callback_) {\n // XXX Does this require the server to be trusted?\n // utils.assert(this.trusted);\n\n var request = new Request(this, 'tx_history');\n var start = start_;\n var callback = callback_;\n\n if (lodash.isPlainObject(start)) {\n start = start.start;\n } else {\n log.warn('DEPRECATED: First argument to request constructor should be' + ' an object containing request properties');\n }\n\n request.message.start = start;\n request.callback(callback);\n\n return request;\n};\n\n/**\r\n * Request book_offers\r\n *\r\n * @param {Object} options\r\n * @param {Object} options.gets - taker_gets with issuer and currency\r\n * @param {Object} options.pays - taker_pays with issuer and currency\r\n * @param {String} [options.taker]\r\n * @param {String} [options.ledger]\r\n * @param {String|Number} [options.limit]\r\n * @param [Function] callback\r\n * @return {Request}\r\n */\n\nRemote.prototype.requestBookOffers = function (options_, callback_) {\n var options = undefined,\n callback = callback_;\n\n if (options_.gets || options_.taker_gets) {\n options = lodash.merge({\n pays: options_.taker_pays,\n gets: options_.taker_gets\n }, options_);\n } else {\n log.warn('DEPRECATED: First argument to request constructor should be' + ' an object containing request properties');\n\n var args = Array.prototype.slice.call(arguments);\n\n if (lodash.isFunction(lodash.last(args))) {\n callback = args.pop();\n }\n\n options = {\n gets: args.shift(),\n pays: args.shift(),\n taker: args.shift(),\n ledger: args.shift(),\n limit: args.shift()\n };\n }\n\n var _options2 = options;\n var gets = _options2.gets;\n var pays = _options2.pays;\n var taker = _options2.taker;\n var ledger = _options2.ledger;\n var limit = _options2.limit;\n\n var request = new Request(this, 'book_offers');\n\n request.message.taker_gets = {\n currency: Currency.json_rewrite(gets.currency, { force_hex: true })\n };\n\n if (!Currency.from_json(request.message.taker_gets.currency).is_native()) {\n request.message.taker_gets.issuer = UInt160.json_rewrite(gets.issuer);\n }\n\n request.message.taker_pays = {\n currency: Currency.json_rewrite(pays.currency, { force_hex: true })\n };\n\n if (!Currency.from_json(request.message.taker_pays.currency).is_native()) {\n request.message.taker_pays.issuer = UInt160.json_rewrite(pays.issuer);\n }\n\n request.message.taker = taker ? taker : UInt160.ACCOUNT_ONE;\n request.selectLedger(ledger);\n\n if (!isNaN(limit)) {\n limit = Number(limit);\n\n // max for 32-bit unsigned int is 4294967295\n // we'll clamp to 1e9\