diff --git a/src/js/ripple/pathfind.js b/src/js/ripple/pathfind.js new file mode 100644 index 00000000..6dba8579 --- /dev/null +++ b/src/js/ripple/pathfind.js @@ -0,0 +1,78 @@ +var EventEmitter = require('events').EventEmitter; +var util = require('util'); + +var Amount = require('./amount').Amount; + +var extend = require('extend'); + +/** + * Represents a persistent path finding request. + * + * Only one path find request is allowed per connection, so when another path + * find request is triggered it will supercede the existing one, making it emit + * the 'end' and 'superceded' events. + */ +var PathFind = function (remote, src_account, dst_account, + dst_amount, src_currencies) +{ + EventEmitter.call(this); + + this.remote = remote; + + this.src_account = src_account; + this.dst_account = dst_account; + this.dst_amount = dst_amount; + this.src_currencies = src_currencies; +}; + +util.inherits(PathFind, EventEmitter); + +/** + * Submits a path_find_create request to the network. + * + * This starts a path find request, superceding all previous path finds. + * + * This will be called automatically by Remote when this object is instantiated, + * so you should only have to call it if the path find was closed or superceded + * and you wish to restart it. + */ +PathFind.prototype.create = function () +{ + var req = this.remote.request_path_find_create(this.src_account, + this.dst_account, + this.dst_amount, + this.src_currencies); + + req.request(); +}; + +PathFind.prototype.close = function () +{ + this.remote.request_path_find_close().request(); + this.emit('end'); + this.emit('close'); +}; + +PathFind.prototype.notify_update = function (message) +{ + var src_account = message.source_account; + var dst_account = message.destination_account; + var dst_amount = Amount.from_json(message.destination_amount); + + // Only pass the event along if this path find response matches what we were + // looking for. + if (message.alternatives && message.alternatives.length) console.log(Amount.from_json(message.alternatives[0].source_amount).to_text_full()); + if (this.src_account === src_account && + this.dst_account === dst_account && + this.dst_amount.equals(dst_amount)) { + this.emit('update', message); + } +}; + +PathFind.prototype.notify_superceded = function () +{ + this.emit('end'); + this.emit('superceded'); +}; + +exports.PathFind = PathFind; diff --git a/src/js/ripple/remote.js b/src/js/ripple/remote.js index a6aebc12..d65044df 100644 --- a/src/js/ripple/remote.js +++ b/src/js/ripple/remote.js @@ -41,12 +41,16 @@ var sjcl = require('../../../build/sjcl'); Keys for opts: trusted : truthy, if remote is trusted - websocket_ip - websocket_port - websocket_ssl trace maxListeners fee_cushion : Extra fee multiplier to account for async fee changes. + servers : Array of server objects with the following form + + { + host: , + port: , + secure: + } Events: 'connect' @@ -107,6 +111,7 @@ function Remote(opts, trace) { this._connected = false; this._last_tx = null; + this._cur_path_find = null; // Local signing implies local fees and sequences if (this.local_signing) { @@ -435,16 +440,24 @@ Remote.prototype._handle_message = function (json) { this.emit('transaction_all', message); break; - // XXX Should be tracked by the Server object - case 'serverStatus': - self.emit('server_status', message); + case 'path_find': + // Pass the event to the currently open PathFind object + if (this._cur_path_find) { + this._cur_path_find.notify_update(message); + } - if ('load_base' in message && 'load_factor' in message && - (message.load_base !== self._load_base || message.load_factor != self._load_factor)) - { - self._load_base = message.load_base; - self._load_factor = message.load_factor; + this.emit('path_find_all', message); + break; + // XXX Should be tracked by the Server object + case 'serverStatus': + self.emit('server_status', message); + + if ('load_base' in message && 'load_factor' in message && + (message.load_base !== self._load_base || message.load_factor != self._load_factor)) + { + self._load_base = message.load_base; + self._load_factor = message.load_factor; self.emit('load', { 'load_base' : self._load_base, 'load_factor' : self.load_factor }); } break; @@ -986,6 +999,24 @@ Remote.prototype.account = function (accountId) { return account; }; +Remote.prototype.path_find = function (src_account, dst_account, + dst_amount, src_currencies) +{ + var path_find = new PathFind(this, + src_account, dst_account, + dst_amount, src_currencies); + + if (this._cur_path_find) { + this._cur_path_find.notify_superceded(); + } + + path_find.create(); + + this._cur_path_find = path_find; + + return path_find; +}; + Remote.prototype.book = function (currency_gets, issuer_gets, currency_pays, issuer_pays) { var gets = currency_gets; @@ -1160,6 +1191,47 @@ Remote.prototype.request_ripple_path_find = function (src_account, dst_account, return request; }; +Remote.prototype.request_path_find_create = function (src_account, dst_account, + dst_amount, + src_currencies, callback) +{ + var self = this; + var request = new Request(this, 'path_find'); + + request.message.subcommand = 'create'; + request.message.source_account = UInt160.json_rewrite(src_account); + request.message.destination_account = UInt160.json_rewrite(dst_account); + request.message.destination_amount = Amount.json_rewrite(dst_amount); + + if (src_currencies) { + request.message.source_currencies = src_currencies.map(function (ci) { + var ci_new = {}; + + if ('issuer' in ci) + ci_new.issuer = UInt160.json_rewrite(ci.issuer); + + if ('currency' in ci) + ci_new.currency = Currency.json_rewrite(ci.currency); + + return ci_new; + }); + } + + request.callback(callback); + + return request; +}; + +Remote.prototype.request_path_find_close = function () +{ + var self = this; + var request = new Request(this, 'path_find'); + + request.message.subcommand = 'close'; + + return request; +}; + Remote.prototype.request_unl_list = function (callback) { var request = new Request(this, 'unl_list'); request.callback(callback);