From d862d17770621ee3b849299e6870699fc36383fe Mon Sep 17 00:00:00 2001 From: Stefan Thomas Date: Mon, 24 Jun 2013 15:08:29 +0200 Subject: [PATCH 01/39] Fix typo. --- src/js/ripple/remote.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/ripple/remote.js b/src/js/ripple/remote.js index 7e35a98d..f798e83f 100644 --- a/src/js/ripple/remote.js +++ b/src/js/ripple/remote.js @@ -1390,7 +1390,7 @@ Remote.prototype.request_ripple_path_find = function(src_account, dst_account, d request.message.destination_account = UInt160.json_rewrite(opts.dst_account); request.message.destination_amount = Amount.json_rewrite(opts.dst_amount); - if (source_currencies) { + if (opts.src_currencies) { request.message.source_currencies = opts.src_currencies.map(function(ci) { var ci_new = {}; From dde93f5e8dfede0d6a7683760027e5b564a2f50c Mon Sep 17 00:00:00 2001 From: Stefan Thomas Date: Mon, 24 Jun 2013 17:18:25 +0200 Subject: [PATCH 02/39] Fix typo. --- src/js/ripple/remote.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/ripple/remote.js b/src/js/ripple/remote.js index f798e83f..5a49a27c 100644 --- a/src/js/ripple/remote.js +++ b/src/js/ripple/remote.js @@ -1380,7 +1380,7 @@ Remote.prototype.request_ripple_path_find = function(src_account, dst_account, d } else { opts.src_account = src_account; opts.dst_account = dst_account; - opts.dst_ammount = dst_amount; + opts.dst_amount = dst_amount; opts.src_currencies = src_currencies; } From dc290f69ec94d092dec2faa414f9d1c8de629ad6 Mon Sep 17 00:00:00 2001 From: Stefan Thomas Date: Tue, 25 Jun 2013 17:41:37 +0200 Subject: [PATCH 03/39] Move require('ws') back to where it was. Add comment explaining why it's there. --- src/js/ripple/server.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/js/ripple/server.js b/src/js/ripple/server.js index 26f146a2..17c143c0 100644 --- a/src/js/ripple/server.js +++ b/src/js/ripple/server.js @@ -1,6 +1,5 @@ var EventEmitter = require('events').EventEmitter; var util = require('util'); -var WebSocket = require('ws'); var utils = require('./utils'); /** @@ -97,6 +96,9 @@ Server.prototype.connect = function() { this._ws.close(); } + // We require this late, because websocket shims may be loaded after + // ripple-lib. + var WebSocket = require('ws'); var ws = this._ws = new WebSocket(this._opts.url); this._should_connect = true; From 28408bfa244dffd6b9b8cb98851b4a64f992a303 Mon Sep 17 00:00:00 2001 From: wltsmrz Date: Wed, 26 Jun 2013 03:28:50 +0900 Subject: [PATCH 04/39] Revert "Add offline queue, update code comments" This reverts commit 9dd337a16c715745d0e6675aa68dc5f7f074ec9d. --- src/js/ripple/remote.js | 143 ++++++++++++++-------------------------- 1 file changed, 48 insertions(+), 95 deletions(-) diff --git a/src/js/ripple/remote.js b/src/js/ripple/remote.js index 5a49a27c..d64e4aac 100644 --- a/src/js/ripple/remote.js +++ b/src/js/ripple/remote.js @@ -58,9 +58,7 @@ util.inherits(Request, EventEmitter); // Send the request to a remote. Request.prototype.request = function(remote) { - if (!this.remote._connected) { - this.remote._offline_queue.push(this); - } else if (!this.requested) { + if (!this.requested) { this.requested = true; this.remote.request(this); this.emit('request', remote); @@ -255,72 +253,39 @@ Request.prototype.books = function(books, snapshot) { return this; }; - +//------------------------------------------------------------------------------ /** Interface to manage the connection to a Ripple server. + This implementation uses WebSockets. - Configuration options: + Keys for opts: - + `trusted` {Boolean} - if remote is trusted - - + `trace` {Boolean} - - + `maxListeners` {Number} - set maxListeners for EventEmitters to prevent - leak warnings. set to 0 for infinite - - + `servers` {Array} - list of remote servers to use. each entry - has the form: - - { - host: - port: - secure: - } + trusted : truthy, if remote is trusted + websocket_ip + websocket_port + websocket_ssl + trace + maxListeners Events: - - + 'connect' - at least one server has connected. the remote - is ready to begin processing requests - - + 'connected' (DEPRECATED) - - + 'disconnect' - there are no more available servers. the - remote is unprepared to process requests - - + 'disconnected' (DEPRECATED) - - + 'state' - either 'online' or 'offline' - - + 'online' - connected and subscribed - - + 'offline' - not subscribed or not connected - - + 'subscribed' - this indicates stand-alone is available + 'connect' + 'connected' (DEPRECATED) + 'disconnect' + 'disconnected' (DEPRECATED) + 'state': + - 'online' : Connected and subscribed. + - 'offline' : Not subscribed or not connected. + 'subscribed' : This indicates stand-alone is available. Server events: + 'ledger_closed' : A good indicate of ready to serve. + 'transaction' : Transactions we receive based on current subscriptions. + 'transaction_all' : Listening triggers a subscribe to all transactions + globally in the network. - + 'ledger_closed' - a good indicate of ready to serve - - + 'transaction' - transactions we receive based on current subscriptions - - + 'transaction_all' - listening triggers a subscribe to all transactions - globally in the network - - @param {Object} opts Connection options. - @param {Boolean} trace + @param opts Connection options. + @param trace */ var Remote = function(opts, trace) { @@ -343,12 +308,10 @@ var Remote = function(opts, trace) { this._testnet = void(0); this._transaction_subs = 0; this.online_target = false; - this._connected = false; this._online_state = 'closed'; // 'open', 'closed', 'connecting', 'closing' this.state = 'offline'; // 'online', 'offline' this.retry_timer = void(0); this.retry = void(0); - this._offline_queue = [ ]; this._load_base = 256; this._load_factor = 1.0; @@ -441,14 +404,6 @@ var Remote = function(opts, trace) { } } }); - - this.once('connect', function offlineQueueListener() { - var offline_queue = self._offline_queue; - var request; - while (request = offline_queue.shift()) { - request.request(); - } - }); }; util.inherits(Remote, EventEmitter); @@ -535,9 +490,7 @@ Remote.prototype.server_fatal = function() { // Set the emitted state: 'online' or 'offline' Remote.prototype._set_state = function(state) { - if (this.trace) { - console.log('remote: set_state: %s', state); - } + if (this.trace) console.log('remote: set_state: %s', state); if (this.state !== state) { this.state = state; @@ -546,15 +499,13 @@ Remote.prototype._set_state = function(state) { switch (state) { case 'online': - this._online_state = 'open'; - this._connected = true; + this._online_state = 'open'; this.emit('connect'); this.emit('connected'); break; case 'offline': - this._online_state = 'closed'; - this._connected = false; + this._online_state = 'closed'; this.emit('disconnect'); this.emit('disconnected'); break; @@ -571,13 +522,7 @@ Remote.prototype.set_trace = function(trace) { /** * Connect to the Ripple network. */ -Remote.prototype.connect = function(online, callback) { - if (typeof online === 'function') { - callback = online; - online = void(0); - this.once('connect', callback); - } - +Remote.prototype.connect = function(online) { // Downwards compatibility if (typeof online !== 'undefined' && !online) { this.disconnect(); @@ -590,7 +535,6 @@ Remote.prototype.connect = function(online, callback) { } } } - return this; }; @@ -753,11 +697,15 @@ Remote.prototype._get_server = function() { // Send a request. // <-> request: what to send, consumed. Remote.prototype.request = function(request) { - var server = this._get_server(); - if (server) { - server.request(request); + if (!this._servers.length) { + request.emit('error', new Error('No servers available')); } else { - request.emit('error', new Error('No servers availale')); + var server = this._get_server(); + if (server) { + server.request(request); + } else { + request.emit('error', new Error('No servers availale')); + } } }; @@ -803,7 +751,7 @@ Remote.prototype.request_ledger = function(ledger, opts, callback) { request.message.full = true; } - return request.callback(callback); + return request.callback(callback);; }; // Only for unit testing. @@ -839,8 +787,7 @@ Remote.prototype.request_ledger_entry = function(type, callback) { // If not found, listen, cache result, and emit it. // // Transparent caching: - - if (type === 'account_root') { + if ('account_root' === type) { request.request_default = request.request; request.request = function() { // Intercept default request. @@ -857,7 +804,7 @@ Remote.prototype.request_ledger_entry = function(type, callback) { } // else if (req.ledger_index) // else if ('ripple_state' === request.type) // YYY Could be cached per ledger. - else if (type === 'account_root') { + else if ('account_root' === type) { var cache = self.ledgers.current.account_root; if (!cache) { @@ -910,7 +857,10 @@ Remote.prototype.request_subscribe = function(streams, callback) { var request = new Request(this, 'subscribe'); if (streams) { - request.message.streams = Array.isArray(streams) ? streams : [ streams ]; + if ('object' !== typeof streams) { + streams = [streams]; + } + request.message.streams = streams; } return request.callback(callback); @@ -920,7 +870,10 @@ Remote.prototype.request_unsubscribe = function(streams, callback) { var request = new Request(this, 'unsubscribe'); if (streams) { - request.message.streams = Array.isArray(streams) ? streams : [ streams ]; + if ('object' !== typeof streams) { + streams = [streams]; + } + request.message.streams = streams; } return request.callback(callback); @@ -932,7 +885,7 @@ Remote.prototype.request_unsubscribe = function(streams, callback) { Remote.prototype.request_transaction_entry = function(hash, callback) { //utils.assert(this.trusted); // If not trusted, need to check proof, maybe talk packet protocol. - return (new Request(this, 'transaction_entry')).tx_hash(hash).callback(callback); + return (new Request(this, 'transaction_entry', callback)).tx_hash(hash); }; // DEPRECATED: use request_transaction_entry From ce1338cf20c65a816514340fe28d3fff5dbb9082 Mon Sep 17 00:00:00 2001 From: wltsmrz Date: Wed, 26 Jun 2013 03:29:49 +0900 Subject: [PATCH 05/39] Revert "Support callback style" This reverts commit c2b03e9d93c12a237023e906ff2bc93fbb37be73. Conflicts: src/js/ripple/remote.js --- src/js/ripple/remote.js | 379 ++++++++++++++++++---------------------- 1 file changed, 166 insertions(+), 213 deletions(-) diff --git a/src/js/ripple/remote.js b/src/js/ripple/remote.js index d64e4aac..06f0bfe5 100644 --- a/src/js/ripple/remote.js +++ b/src/js/ripple/remote.js @@ -37,27 +37,23 @@ var sjcl = require('../../../build/sjcl'); // 'remoteError' // 'remoteUnexpected' // 'remoteDisconnected' -var Request = function(remote, command, callback) { +var Request = function (remote, command) { EventEmitter.call(this); var self = this; this.remote = remote; - this.requested = false; - this.message = { 'command': command, 'id': void(0) }; - - this.callback(callback); }; util.inherits(Request, EventEmitter); // Send the request to a remote. -Request.prototype.request = function(remote) { +Request.prototype.request = function (remote) { if (!this.requested) { this.requested = true; this.remote.request(this); @@ -65,16 +61,7 @@ Request.prototype.request = function(remote) { } }; -Request.prototype.callback = function(callback, successEvent) { - if (typeof callback === 'function') { - this.once('error', callback); - this.once(successEvent || 'success', callback.bind(this, null)); - this.request(); - } - return this; -}; - -Request.prototype.build_path = function(build) { +Request.prototype.build_path = function (build) { if (build) { this.message.build_path = true; } @@ -82,7 +69,7 @@ Request.prototype.build_path = function(build) { return this; }; -Request.prototype.ledger_choose = function(current) { +Request.prototype.ledger_choose = function (current) { if (current) { this.message.ledger_index = this.remote._ledger_current_index; } else { @@ -95,7 +82,7 @@ Request.prototype.ledger_choose = function(current) { // Set the ledger for a request. // - ledger_entry // - transaction_entry -Request.prototype.ledger_hash = function(h) { +Request.prototype.ledger_hash = function (h) { this.message.ledger_hash = h; return this; @@ -103,13 +90,13 @@ Request.prototype.ledger_hash = function(h) { // Set the ledger_index for a request. // - ledger_entry -Request.prototype.ledger_index = function(ledger_index) { +Request.prototype.ledger_index = function (ledger_index) { this.message.ledger_index = ledger_index; return this; }; -Request.prototype.ledger_select = function(ledger_spec) { +Request.prototype.ledger_select = function (ledger_spec) { switch (ledger_spec) { case 'current': case 'closed': @@ -128,13 +115,13 @@ Request.prototype.ledger_select = function(ledger_spec) { return this; }; -Request.prototype.account_root = function(account) { +Request.prototype.account_root = function (account) { this.message.account_root = UInt160.json_rewrite(account); return this; }; -Request.prototype.index = function(hash) { +Request.prototype.index = function (hash) { this.message.index = hash; return this; @@ -143,7 +130,7 @@ Request.prototype.index = function(hash) { // Provide the information id an offer. // --> account // --> seq : sequence number of transaction creating offer (integer) -Request.prototype.offer_id = function(account, seq) { +Request.prototype.offer_id = function (account, seq) { this.message.offer = { 'account': UInt160.json_rewrite(account), 'seq': seq @@ -153,13 +140,13 @@ Request.prototype.offer_id = function(account, seq) { }; // --> index : ledger entry index. -Request.prototype.offer_index = function(index) { +Request.prototype.offer_index = function (index) { this.message.offer = index; return this; }; -Request.prototype.secret = function(s) { +Request.prototype.secret = function (s) { if (s) { this.message.secret = s; } @@ -167,25 +154,25 @@ Request.prototype.secret = function(s) { return this; }; -Request.prototype.tx_hash = function(h) { +Request.prototype.tx_hash = function (h) { this.message.tx_hash = h; return this; }; -Request.prototype.tx_json = function(j) { +Request.prototype.tx_json = function (j) { this.message.tx_json = j; return this; }; -Request.prototype.tx_blob = function(j) { +Request.prototype.tx_blob = function (j) { this.message.tx_blob = j; return this; }; -Request.prototype.ripple_state = function(account, issuer, currency) { +Request.prototype.ripple_state = function (account, issuer, currency) { this.message.ripple_state = { 'accounts' : [ UInt160.json_rewrite(account), @@ -197,7 +184,7 @@ Request.prototype.ripple_state = function(account, issuer, currency) { return this; }; -Request.prototype.accounts = function(accounts, realtime) { +Request.prototype.accounts = function (accounts, realtime) { if (!Array.isArray(accounts)) { accounts = [ accounts ]; } @@ -216,11 +203,11 @@ Request.prototype.accounts = function(accounts, realtime) { return this; }; -Request.prototype.rt_accounts = function(accounts) { +Request.prototype.rt_accounts = function (accounts) { return this.accounts(accounts, true); }; -Request.prototype.books = function(books, snapshot) { +Request.prototype.books = function (books, snapshot) { var procBooks = []; for (var i = 0, l = books.length; i < l; i++) { @@ -288,7 +275,7 @@ Request.prototype.books = function(books, snapshot) { @param trace */ -var Remote = function(opts, trace) { +var Remote = function (opts, trace) { EventEmitter.call(this); var self = this; @@ -387,7 +374,7 @@ var Remote = function(opts, trace) { }); } - this.on('newListener', function(type, listener) { + this.on('newListener', function (type, listener) { if ('transaction_all' === type) { if (!self._transaction_subs && self._online_state === 'open') { self.request_subscribe('transactions').request(); @@ -396,7 +383,7 @@ var Remote = function(opts, trace) { } }); - this.on('removeListener', function(type, listener) { + this.on('removeListener', function (type, listener) { if ('transaction_all' === type) { self._transaction_subs -= 1; if (!self._transaction_subs && self._online_state === 'open') { @@ -418,7 +405,7 @@ Remote.flags = { } }; -Remote.from_config = function(obj, trace) { +Remote.from_config = function (obj, trace) { var serverConfig = typeof obj === 'string' ? config.servers[obj] : obj; var remote = new Remote(serverConfig, trace); @@ -438,15 +425,15 @@ Remote.from_config = function(obj, trace) { return remote; }; -var isTemMalformed = function(engine_result_code) { +var isTemMalformed = function (engine_result_code) { return (engine_result_code >= -299 && engine_result_code < 199); }; -var isTefFailure = function(engine_result_code) { +var isTefFailure = function (engine_result_code) { return (engine_result_code >= -299 && engine_result_code < 199); }; -Remote.prototype.add_server = function(opts) { +Remote.prototype.add_server = function (opts) { var self = this; var url = (opts.secure || opts.websocket_ssl ? 'wss://' : 'ws://') @@ -459,11 +446,11 @@ Remote.prototype.add_server = function(opts) { server.setMaxListeners(opts.maxListeners); } - server.on('message', function(data) { + server.on('message', function (data) { self._handle_message(data); }); - server.on('connect', function() { + server.on('connect', function () { if (opts.primary || !self._primary_server) { self._set_primary_server(server); } @@ -471,7 +458,7 @@ Remote.prototype.add_server = function(opts) { self._set_state('online'); }); - server.on('disconnect', function() { + server.on('disconnect', function () { self._connection_count--; if (!self._connection_count) { self._set_state('offline'); @@ -484,12 +471,12 @@ Remote.prototype.add_server = function(opts) { }; // Inform remote that the remote server is not comming back. -Remote.prototype.server_fatal = function() { +Remote.prototype.server_fatal = function () { this._server_fatal = true; }; // Set the emitted state: 'online' or 'offline' -Remote.prototype._set_state = function(state) { +Remote.prototype._set_state = function (state) { if (this.trace) console.log('remote: set_state: %s', state); if (this.state !== state) { @@ -513,7 +500,7 @@ Remote.prototype._set_state = function(state) { } }; -Remote.prototype.set_trace = function(trace) { +Remote.prototype.set_trace = function (trace) { this.trace = undefined === trace || trace; return this; @@ -522,27 +509,28 @@ Remote.prototype.set_trace = function(trace) { /** * Connect to the Ripple network. */ -Remote.prototype.connect = function(online) { +Remote.prototype.connect = function (online) { // Downwards compatibility if (typeof online !== 'undefined' && !online) { - this.disconnect(); + return this.disconnect(); + } + + if (!this._servers.length) { + throw new Error('No servers available.'); } else { - if (!this._servers.length) { - throw new Error('No servers available.'); - } else { - for (var i=0, l=this._servers.length; i request: what to send, consumed. -Remote.prototype.request = function(request) { +Remote.prototype.request = function (request) { if (!this._servers.length) { - request.emit('error', new Error('No servers available')); + request.emit('error', new Error('No servers availale')); } else { var server = this._get_server(); if (server) { @@ -709,21 +689,15 @@ Remote.prototype.request = function(request) { } }; -Remote.prototype.server_info = -Remote.prototype.request_server_info = function(callback) { - return new Request(this, 'server_info').callback(callback); +Remote.prototype.request_server_info = function () { + return new Request(this, 'server_info'); }; // XXX This is a bad command. Some varients don't scale. // XXX Require the server to be trusted. -Remote.prototype.request_ledger = function(ledger, opts, callback) { +Remote.prototype.request_ledger = function (ledger, opts) { //utils.assert(this.trusted); - if (typeof opts === 'function') { - callback = opts; - opts = { }; - } - var request = new Request(this, 'ledger'); if (ledger) { @@ -732,7 +706,7 @@ Remote.prototype.request_ledger = function(ledger, opts, callback) { request.message.ledger = ledger; } - if (typeof opts === 'object') { + if ('object' == typeof opts) { if (opts.full) request.message.full = true; @@ -751,33 +725,33 @@ Remote.prototype.request_ledger = function(ledger, opts, callback) { request.message.full = true; } - return request.callback(callback);; + return request; }; // Only for unit testing. -Remote.prototype.request_ledger_hash = function(callback) { +Remote.prototype.request_ledger_hash = function () { //utils.assert(this.trusted); // If not trusted, need to check proof. - return new Request(this, 'ledger_closed').callback(callback); + return new Request(this, 'ledger_closed'); }; // .ledger() // .ledger_index() -Remote.prototype.request_ledger_header = function(callback) { - return new Request(this, 'ledger_header').callback(callback); +Remote.prototype.request_ledger_header = function () { + return new Request(this, 'ledger_header'); }; // Get the current proposed ledger entry. May be closed (and revised) at any time (even before returning). // Only for unit testing. -Remote.prototype.request_ledger_current = function(callback) { - return new Request(this, 'ledger_current').callback(callback); +Remote.prototype.request_ledger_current = function () { + return new Request(this, 'ledger_current'); }; // --> type : the type of ledger entry. // .ledger() // .ledger_index() // .offer_id() -Remote.prototype.request_ledger_entry = function(type, callback) { +Remote.prototype.request_ledger_entry = function (type) { //utils.assert(this.trusted); // If not trusted, need to check proof, maybe talk packet protocol. var self = this; @@ -790,7 +764,7 @@ Remote.prototype.request_ledger_entry = function(type, callback) { if ('account_root' === type) { request.request_default = request.request; - request.request = function() { // Intercept default request. + request.request = function () { // Intercept default request. var bDefault = true; // .self = Remote // this = Request @@ -829,7 +803,7 @@ Remote.prototype.request_ledger_entry = function(type, callback) { // XXX Only allow with trusted mode. Must sync response with advance. switch (type) { case 'account_root': - request.on('success', function(message) { + request.on('success', function (message) { // Cache node. // console.log('request_ledger_entry: caching'); self.ledgers.current.account_root[message.node.Account] = message.node; @@ -849,11 +823,11 @@ Remote.prototype.request_ledger_entry = function(type, callback) { } }; - return request.callback(callback); + return request; }; // .accounts(accounts, realtime) -Remote.prototype.request_subscribe = function(streams, callback) { +Remote.prototype.request_subscribe = function (streams) { var request = new Request(this, 'subscribe'); if (streams) { @@ -863,10 +837,10 @@ Remote.prototype.request_subscribe = function(streams, callback) { request.message.streams = streams; } - return request.callback(callback); + return request; }; -Remote.prototype.request_unsubscribe = function(streams, callback) { +Remote.prototype.request_unsubscribe = function (streams) { var request = new Request(this, 'unsubscribe'); if (streams) { @@ -876,39 +850,39 @@ Remote.prototype.request_unsubscribe = function(streams, callback) { request.message.streams = streams; } - return request.callback(callback); + return request; }; // .ledger_choose() // .ledger_hash() // .ledger_index() -Remote.prototype.request_transaction_entry = function(hash, callback) { +Remote.prototype.request_transaction_entry = function (hash) { //utils.assert(this.trusted); // If not trusted, need to check proof, maybe talk packet protocol. - return (new Request(this, 'transaction_entry', callback)).tx_hash(hash); + return (new Request(this, 'transaction_entry')).tx_hash(hash); }; // DEPRECATED: use request_transaction_entry -Remote.prototype.request_tx = function(hash, callback) { +Remote.prototype.request_tx = function (hash) { var request = new Request(this, 'tx'); request.message.transaction = hash; - return request.callback(callback); + return request; }; -Remote.prototype.request_account_info = function(accountID, callback) { +Remote.prototype.request_account_info = function (accountID) { var request = new Request(this, 'account_info'); request.message.ident = UInt160.json_rewrite(accountID); // DEPRECATED request.message.account = UInt160.json_rewrite(accountID); - return request.callback(callback); + return request; }; // --> account_index: sub_account index (optional) // --> current: true, for the current ledger. -Remote.prototype.request_account_lines = function(accountID, account_index, current, callback) { +Remote.prototype.request_account_lines = function (accountID, account_index, current) { // XXX Does this require the server to be trusted? //utils.assert(this.trusted); @@ -920,12 +894,12 @@ Remote.prototype.request_account_lines = function(accountID, account_index, curr request.message.index = account_index; } - return request.ledger_choose(current).callback(callback); + return request.ledger_choose(current); }; // --> account_index: sub_account index (optional) // --> current: true, for the current ledger. -Remote.prototype.request_account_offers = function(accountID, account_index, current, callback) { +Remote.prototype.request_account_offers = function (accountID, account_index, current) { var request = new Request(this, 'account_offers'); request.message.account = UInt160.json_rewrite(accountID); @@ -934,7 +908,7 @@ Remote.prototype.request_account_offers = function(accountID, account_index, cur request.message.index = account_index; } - return request.ledger_choose(current).callback(callback); + return request.ledger_choose(current); }; @@ -949,7 +923,7 @@ Remote.prototype.request_account_offers = function(accountID, account_index, cur limit: integer // optional */ -Remote.prototype.request_account_tx = function(obj, callback) { +Remote.prototype.request_account_tx = function (obj) { // XXX Does this require the server to be trusted? //utils.assert(this.trusted); @@ -970,10 +944,10 @@ Remote.prototype.request_account_tx = function(obj, callback) { if ('undefined' !== typeof obj.limit) {request.message.limit = obj.limit;} } - return request.callback(callback); + return request; }; -Remote.prototype.request_book_offers = function(gets, pays, taker, callback) { +Remote.prototype.request_book_offers = function (gets, pays, taker) { var request = new Request(this, 'book_offers'); request.message.taker_gets = { @@ -994,20 +968,20 @@ Remote.prototype.request_book_offers = function(gets, pays, taker, callback) { request.message.taker = taker ? taker : UInt160.ACCOUNT_ONE; - return request.callback(callback); + return request; }; -Remote.prototype.request_wallet_accounts = function(seed, callback) { +Remote.prototype.request_wallet_accounts = function (seed) { utils.assert(this.trusted); // Don't send secrets. var request = new Request(this, 'wallet_accounts'); request.message.seed = seed; - return request.callback(callback); + return request; }; -Remote.prototype.request_sign = function(secret, tx_json, callback) { +Remote.prototype.request_sign = function (secret, tx_json) { utils.assert(this.trusted); // Don't send secrets. var request = new Request(this, 'sign'); @@ -1015,12 +989,16 @@ Remote.prototype.request_sign = function(secret, tx_json, callback) { request.message.secret = secret; request.message.tx_json = tx_json; - return request.callback(callback); + return request; }; // Submit a transaction. -Remote.prototype.request_submit = function(callback) { - return new Request(this, 'submit').callback(callback); +Remote.prototype.request_submit = function () { + var self = this; + + var request = new Request(this, 'submit'); + + return request; }; // @@ -1035,16 +1013,16 @@ Remote.prototype.request_submit = function(callback) { * * This function will create and return the request, but not submit it. */ -Remote.prototype._server_prepare_subscribe = function(callback) { +Remote.prototype._server_prepare_subscribe = function () { var self = this; var feeds = [ 'ledger', 'server' ]; if (this._transaction_subs) feeds.push('transactions'); - var request = this.request_subscribe(feeds); + var req = this.request_subscribe(feeds); - request.on('success', function(message) { + req.on('success', function (message) { self._stand_alone = !!message.stand_alone; self._testnet = !!message.testnet; @@ -1077,87 +1055,70 @@ Remote.prototype._server_prepare_subscribe = function(callback) { self.emit('subscribed'); }); - self.emit('prepare_subscribe', request); + self.emit('prepare_subscribe', req); // XXX Could give error events, maybe even time out. - return request.callback(callback); + return req; }; // For unit testing: ask the remote to accept the current ledger. // - To be notified when the ledger is accepted, server_subscribe() then listen to 'ledger_hash' events. // A good way to be notified of the result of this is: -// remote.once('ledger_closed', function(ledger_closed, ledger_index) { ... } ); -Remote.prototype.ledger_accept = function(callback) { +// remote.once('ledger_closed', function (ledger_closed, ledger_index) { ... } ); +Remote.prototype.ledger_accept = function () { if (this._stand_alone || undefined === this._stand_alone) { var request = new Request(this, 'ledger_accept'); - request.callback(callback).request(); + request.request(); } else { - var err = { 'error' : 'notStandAlone' } - if (typeof callback === 'function') { - callback(err); - } - this.emit('error', err); + this.emit('error', { + 'error' : 'notStandAlone' + }); } return this; }; // Return a request to refresh the account balance. -Remote.prototype.request_account_balance = function(account, current, callback) { +Remote.prototype.request_account_balance = function (account, current) { var request = this.request_ledger_entry('account_root'); - request.account_root(account) + return request + .account_root(account) .ledger_choose(current) - .on('success', function(message) { + .on('success', function (message) { // If the caller also waits for 'success', they might run before this. request.emit('account_balance', Amount.from_json(message.node.Balance)); }); - - if (typeof callback === 'function') { - request.callback(callback, 'account_balance'); - } - - return request; }; // Return a request to return the account flags. -Remote.prototype.request_account_flags = function(account, current, callback) { +Remote.prototype.request_account_flags = function (account, current) { var request = this.request_ledger_entry('account_root'); - request.account_root(account) + return request + .account_root(account) .ledger_choose(current) - .on('success', function(message) { + .on('success', function (message) { // If the caller also waits for 'success', they might run before this. request.emit('account_flags', message.node.Flags); }); - - if (typeof callback === 'function') { - request.callback(callback, 'account_flags'); - } - - return request; }; // Return a request to emit the owner count. -Remote.prototype.request_owner_count = function(account, current, callback) { +Remote.prototype.request_owner_count = function (account, current) { var request = this.request_ledger_entry('account_root'); - request.account_root(account) + return request + .account_root(account) .ledger_choose(current) - .on('success', function(message) { + .on('success', function (message) { // If the caller also waits for 'success', they might run before this. request.emit('owner_count', message.node.OwnerCount); }); - - if (typeof callback === 'function') { - request.callback(callback, 'owner_count'); - } - - return request; }; -Remote.prototype.account = function(accountId) { +Remote.prototype.account = function (accountId) { accountId = UInt160.json_rewrite(accountId); if (!this._accounts[accountId]) { @@ -1171,7 +1132,7 @@ Remote.prototype.account = function(accountId) { return this._accounts[accountId]; }; -Remote.prototype.book = function(currency_gets, issuer_gets, currency_pays, issuer_pays) { +Remote.prototype.book = function (currency_gets, issuer_gets, currency_pays, issuer_pays) { var gets = currency_gets; if (gets !== 'XRP') gets += '/' + issuer_gets; var pays = currency_pays; @@ -1192,7 +1153,7 @@ Remote.prototype.book = function(currency_gets, issuer_gets, currency_pays, issu // Return the next account sequence if possible. // <-- undefined or Sequence -Remote.prototype.account_seq = function(account, advance) { +Remote.prototype.account_seq = function (account, advance) { var account = UInt160.json_rewrite(account); var account_info = this.accounts[account]; var seq; @@ -1211,7 +1172,7 @@ Remote.prototype.account_seq = function(account, advance) { return seq; }; -Remote.prototype.set_account_seq = function(account, seq) { +Remote.prototype.set_account_seq = function (account, seq) { var account = UInt160.json_rewrite(account); if (!this.accounts[account]) { @@ -1222,7 +1183,7 @@ Remote.prototype.set_account_seq = function(account, seq) { }; // Return a request to refresh accounts[account].seq. -Remote.prototype.account_seq_cache = function(account, current, callback) { +Remote.prototype.account_seq_cache = function (account, current) { var self = this; var request; @@ -1236,7 +1197,7 @@ Remote.prototype.account_seq_cache = function(account, current, callback) { request = self.request_ledger_entry('account_root') .account_root(account) .ledger_choose(current) - .on('success', function(message) { + .on('success', function (message) { delete account_info.caching_seq_request; var seq = message.node.Sequence; @@ -1247,7 +1208,7 @@ Remote.prototype.account_seq_cache = function(account, current, callback) { // If the caller also waits for 'success', they might run before this. request.emit('success_account_seq_cache', message); }) - .on('error', function(message) { + .on('error', function (message) { // console.log('error: %s', account); delete account_info.caching_seq_request; @@ -1257,22 +1218,18 @@ Remote.prototype.account_seq_cache = function(account, current, callback) { account_info.caching_seq_request = request; } - if (typeof callback === 'function') { - request.callback(callback, 'success_account_seq_cache'); - } - return request; }; // Mark an account's root node as dirty. -Remote.prototype.dirty_account_root = function(account) { - var account = UInt160.json_rewrite(account); +Remote.prototype.dirty_account_root = function (account) { + var account = UInt160.json_rewrite(account); delete this.ledgers.current.account_root[account]; }; // Store a secret - allows the Remote to automatically fill out auth information. -Remote.prototype.set_secret = function(account, secret) { +Remote.prototype.set_secret = function (account, secret) { this.secrets[account] = secret; }; @@ -1285,13 +1242,13 @@ Remote.prototype.set_secret = function(account, secret) { // --> current: bool : true = current ledger // // If does not exist: emit('error', 'error' : 'remoteError', 'remote' : { 'error' : 'entryNotFound' }) -Remote.prototype.request_ripple_balance = function(account, issuer, currency, current, callback) { +Remote.prototype.request_ripple_balance = function (account, issuer, currency, current) { var request = this.request_ledger_entry('ripple_state'); // YYY Could be cached per ledger. - request + return request .ripple_state(account, issuer, currency) .ledger_choose(current) - .on('success', function(message) { + .on('success', function (message) { var node = message.node; var lowLimit = Amount.from_json(node.LowLimit); @@ -1315,16 +1272,11 @@ Remote.prototype.request_ripple_balance = function(account, issuer, currency, cu 'peer_quality_out' : (!accountHigh ? node.HighQualityOut : node.LowQualityOut), }); }); - - if (typeof callback === 'function') { - request.callback(callback, 'ripple_state'); - } - - return request; }; -Remote.prototype.request_ripple_path_find = function(src_account, dst_account, dst_amount, src_currencies, callback) { +Remote.prototype.request_ripple_path_find = function (src_account, dst_account, dst_amount, source_currencies) { var self = this; +<<<<<<< HEAD var opts = { }; @@ -1337,14 +1289,21 @@ Remote.prototype.request_ripple_path_find = function(src_account, dst_account, d opts.src_currencies = src_currencies; } +======= +>>>>>>> parent of c2b03e9... Support callback style var request = new Request(this, 'ripple_path_find'); - request.message.source_account = UInt160.json_rewrite(opts.src_account); - request.message.destination_account = UInt160.json_rewrite(opts.dst_account); - request.message.destination_amount = Amount.json_rewrite(opts.dst_amount); + 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); +<<<<<<< HEAD if (opts.src_currencies) { request.message.source_currencies = opts.src_currencies.map(function(ci) { +======= + if (source_currencies) { + request.message.source_currencies = source_currencies.map(function (ci) { +>>>>>>> parent of c2b03e9... Support callback style var ci_new = {}; if ('issuer' in ci) { @@ -1358,45 +1317,39 @@ Remote.prototype.request_ripple_path_find = function(src_account, dst_account, d }); } - return request.callback(callback); + return request; }; -Remote.prototype.request_unl_list = function(callback) { - return new Request(this, 'unl_list').callback(callback); +Remote.prototype.request_unl_list = function () { + return new Request(this, 'unl_list'); }; -Remote.prototype.request_unl_add = function(addr, comment, callback) { +Remote.prototype.request_unl_add = function (addr, comment) { var request = new Request(this, 'unl_add'); request.message.node = addr; - switch (typeof comment) { - case 'string': - request.message.comment = comment; - break; - - case 'function': - callback = comment; - break; + if (typeof comment !== 'undefined') { + request.message.comment = note; } - return request.callback(callback); + return request; }; // --> node: | -Remote.prototype.request_unl_delete = function(node) { +Remote.prototype.request_unl_delete = function (node) { var request = new Request(this, 'unl_delete'); request.message.node = node; - return request.callback(callback); + return request; }; -Remote.prototype.request_peers = function(callback) { - return new Request(this, 'peers', callback); +Remote.prototype.request_peers = function () { + return new Request(this, 'peers'); }; -Remote.prototype.request_connect = function(ip, port, callback) { +Remote.prototype.request_connect = function (ip, port) { var request = new Request(this, 'connect'); request.message.ip = ip; @@ -1405,10 +1358,10 @@ Remote.prototype.request_connect = function(ip, port, callback) { request.message.port = port; } - return request.callback(callback); + return request; }; -Remote.prototype.transaction = function() { +Remote.prototype.transaction = function () { return new Transaction(this); }; From fa1a4a0307a0b3275031c80fc199a286b183162c Mon Sep 17 00:00:00 2001 From: Stefan Thomas Date: Tue, 25 Jun 2013 20:34:10 +0200 Subject: [PATCH 06/39] Undo change to the way ripple-lib unsubscribed from ws events. --- src/js/ripple/server.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/js/ripple/server.js b/src/js/ripple/server.js index 17c143c0..2373a85b 100644 --- a/src/js/ripple/server.js +++ b/src/js/ripple/server.js @@ -158,8 +158,7 @@ Server.prototype.connect = function() { self._set_state('offline'); // Prevent additional events from this socket - ws.removeAllListeners(); - ws.on('error', function() {}); + ws.onopen = ws.onerror = ws.onclose = ws.onmessage = function () {}; // Should we be connected? if (!self._should_connect) return; From 705a9ad087e00b1ef96291210d3bfa4d321c3f47 Mon Sep 17 00:00:00 2001 From: wltsmrz Date: Wed, 26 Jun 2013 03:36:58 +0900 Subject: [PATCH 07/39] Fix --- src/js/ripple/remote.js | 29 ++++------------------------- 1 file changed, 4 insertions(+), 25 deletions(-) diff --git a/src/js/ripple/remote.js b/src/js/ripple/remote.js index 06f0bfe5..4f194cab 100644 --- a/src/js/ripple/remote.js +++ b/src/js/ripple/remote.js @@ -1274,44 +1274,23 @@ Remote.prototype.request_ripple_balance = function (account, issuer, currency, c }); }; -Remote.prototype.request_ripple_path_find = function (src_account, dst_account, dst_amount, source_currencies) { +Remote.prototype.request_ripple_path_find = function (src_account, dst_account, dst_amount, src_currencies) { var self = this; -<<<<<<< HEAD - - var opts = { }; - - if (typeof src_account === 'object') { - opts = src_account; - } else { - opts.src_account = src_account; - opts.dst_account = dst_account; - opts.dst_amount = dst_amount; - opts.src_currencies = src_currencies; - } - -======= ->>>>>>> parent of c2b03e9... Support callback style var request = new Request(this, 'ripple_path_find'); 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); -<<<<<<< HEAD - if (opts.src_currencies) { - request.message.source_currencies = opts.src_currencies.map(function(ci) { -======= if (source_currencies) { request.message.source_currencies = source_currencies.map(function (ci) { ->>>>>>> parent of c2b03e9... Support callback style var ci_new = {}; - if ('issuer' in ci) { + if ('issuer' in ci) ci_new.issuer = UInt160.json_rewrite(ci.issuer); - } - if ('currency' in ci) { + + if ('currency' in ci) ci_new.currency = Currency.json_rewrite(ci.currency); - } return ci_new; }); From db5c3f1bf204575b2c920fc1a7de4a071d28ff71 Mon Sep 17 00:00:00 2001 From: wltsmrz Date: Wed, 26 Jun 2013 03:37:10 +0900 Subject: [PATCH 08/39] Revert "Add JSDoc style comments" This reverts commit 75d3a4f827badf8dbb464a51b045f27d7970037d. --- src/js/ripple/server.js | 60 ----------------------------------------- 1 file changed, 60 deletions(-) diff --git a/src/js/ripple/server.js b/src/js/ripple/server.js index 26f146a2..03e10a61 100644 --- a/src/js/ripple/server.js +++ b/src/js/ripple/server.js @@ -6,9 +6,6 @@ var utils = require('./utils'); /** * Server * - * Options must contain `url` to - * WebSocket server - * * @constructor * @param {Object} remote * @param {Object} opts @@ -53,7 +50,6 @@ util.inherits(Server, EventEmitter); * Our requirements are that the server can process transactions and notify * us of changes. */ - Server.online_states = [ 'syncing' , 'tracking' @@ -62,23 +58,10 @@ Server.online_states = [ , 'full' ]; -/** - * Determine if a server status qualifies - * as 'online' - * - * @param {String} status - * @return {Boolean} - * @api private - */ - Server.prototype.is_online = function(status) { return Server.online_states.indexOf(status) !== -1; }; -/** - * Connect to WebSocket server - */ - Server.prototype.connect = function() { var self = this; @@ -183,12 +166,6 @@ Server.prototype.connect = function() { }; }; -/** - * Disconnect from WebSocket server - * - * @api public - */ - Server.prototype.disconnect = function() { this._should_connect = false; this._set_state('offline'); @@ -197,13 +174,6 @@ Server.prototype.disconnect = function() { } }; -/** - * Send stringified message to WebSocket server - * - * @param {Object} message - * @api private - */ - Server.prototype.send = function(message) { if (this._ws) { this._ws.send(JSON.stringify(message)); @@ -212,11 +182,7 @@ Server.prototype.send = function(message) { /** * Submit a Request object to this server. - * - * @param {Object} request - * @api public */ - Server.prototype.request = function(request) { var self = this; @@ -251,18 +217,6 @@ Server.prototype.request = function(request) { } }; -/** - * Set server state - * - * Examples: - * - * set_state('online') - * set_state('offline') - * - * @param {String} state - * @api private - */ - Server.prototype._set_state = function(state) { if (state !== this._state) { this._state = state; @@ -279,13 +233,6 @@ Server.prototype._set_state = function(state) { } }; -/** - * Handle WebSocket message - * - * @param {String} json - * @api private - */ - Server.prototype._handle_message = function(json) { var self = this; var unexpected = false; @@ -343,13 +290,6 @@ Server.prototype._handle_message = function(json) { } }; -/** - * Handle subscribe response - * - * @param {Object} message - * @api private - */ - Server.prototype._handle_response_subscribe = function(message) { var self = this; From 2a340d09a418cb424b78c107d021d8dc2fe7c8f8 Mon Sep 17 00:00:00 2001 From: wltsmrz Date: Wed, 26 Jun 2013 03:40:11 +0900 Subject: [PATCH 09/39] Typo --- src/js/ripple/remote.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/js/ripple/remote.js b/src/js/ripple/remote.js index 4f194cab..2d6310f3 100644 --- a/src/js/ripple/remote.js +++ b/src/js/ripple/remote.js @@ -1282,8 +1282,8 @@ Remote.prototype.request_ripple_path_find = function (src_account, dst_account, request.message.destination_account = UInt160.json_rewrite(dst_account); request.message.destination_amount = Amount.json_rewrite(dst_amount); - if (source_currencies) { - request.message.source_currencies = source_currencies.map(function (ci) { + if (src_currencies) { + request.message.source_currencies = src_currencies.map(function (ci) { var ci_new = {}; if ('issuer' in ci) From 84e79abbaa1cb13932cf7a364fffc84e4b212271 Mon Sep 17 00:00:00 2001 From: wltsmrz Date: Wed, 26 Jun 2013 03:40:35 +0900 Subject: [PATCH 10/39] Revert "Cleanup, fix .disconnect" This reverts commit c2ad23996ddee8a6a8619e02788bd55415670562. --- src/js/ripple/server.js | 167 ++++++++++++++++++---------------------- 1 file changed, 75 insertions(+), 92 deletions(-) diff --git a/src/js/ripple/server.js b/src/js/ripple/server.js index 03e10a61..67b343aa 100644 --- a/src/js/ripple/server.js +++ b/src/js/ripple/server.js @@ -1,27 +1,33 @@ var EventEmitter = require('events').EventEmitter; var util = require('util'); var WebSocket = require('ws'); + var utils = require('./utils'); +//------------------------------------------------------------------------------ /** - * Server - * - * @constructor - * @param {Object} remote - * @param {Object} opts - */ + Constructor -function Server(remote, opts) { + Keys for cfg: + + url + + @param remote The Remote object + @param cfg Configuration parameters. +*/ + +var Server = function (remote, cfg) { EventEmitter.call(this); - if (typeof opts !== 'object' || typeof opts.url !== 'string') { + + if (typeof cfg !== 'object' || typeof cfg.url !== 'string') { throw new Error('Invalid server configuration.'); } var self = this; this._remote = remote; - this._opts = opts; + this._cfg = cfg; this._ws = void(0); this._connected = false; @@ -42,27 +48,32 @@ function Server(remote, opts) { }); }; +//------------------------------------------------------------------------------ + util.inherits(Server, EventEmitter); +function to_set(list) { + var result = { }; + for (var i=0; i Date: Wed, 26 Jun 2013 03:41:33 +0900 Subject: [PATCH 11/39] Revert "Add field to server configuration for concurrent connections to the same host" This reverts commit 348615671a23a870c42d562a8395e05ebc1babbe. --- src/js/ripple/remote.js | 23 ++++++++++------------- src/js/ripple/server.js | 35 +++++++++++++---------------------- 2 files changed, 23 insertions(+), 35 deletions(-) diff --git a/src/js/ripple/remote.js b/src/js/ripple/remote.js index 2d6310f3..ffa752a4 100644 --- a/src/js/ripple/remote.js +++ b/src/js/ripple/remote.js @@ -361,10 +361,7 @@ var Remote = function (opts, trace) { // Initialize servers opts.servers.forEach(function(server) { - var i = typeof server.pool === 'number' ? server.pool : 1; - while (i--) { - self.add_server(server); - } + self.add_server(server); }); // This is used to remove EventEmitter warnings @@ -379,13 +376,13 @@ var Remote = function (opts, trace) { if (!self._transaction_subs && self._online_state === 'open') { self.request_subscribe('transactions').request(); } - self._transaction_subs += 1; + self._transaction_subs += 1; } }); this.on('removeListener', function (type, listener) { if ('transaction_all' === type) { - self._transaction_subs -= 1; + self._transaction_subs -= 1; if (!self._transaction_subs && self._online_state === 'open') { self.request_unsubscribe('transactions').request(); } @@ -623,8 +620,8 @@ Remote.prototype._handle_message = function (json) { // All other messages default: - if (this.trace) utils.logObject('remote: ' + message.type+': %s', message); - this.emit('net_' + message.type, message); + if (this.trace) utils.logObject('remote: '+message.type+': %s', message); + this.emit('net_'+message.type, message); break; } } @@ -634,8 +631,8 @@ Remote.prototype._handle_message = function (json) { console.log('unexpected message from trusted remote: %s', json); (request || this).emit('error', { - 'error': 'remoteUnexpected', - 'error_message': 'Unexpected response from remote.' + 'error' : 'remoteUnexpected', + 'error_message' : 'Unexpected response from remote.' }); } }; @@ -1154,8 +1151,8 @@ Remote.prototype.book = function (currency_gets, issuer_gets, currency_pays, iss // Return the next account sequence if possible. // <-- undefined or Sequence Remote.prototype.account_seq = function (account, advance) { - var account = UInt160.json_rewrite(account); - var account_info = this.accounts[account]; + account = UInt160.json_rewrite(account); + var account_info = this.accounts[account]; var seq; if (account_info && account_info.seq) { @@ -1243,7 +1240,7 @@ Remote.prototype.set_secret = function (account, secret) { // // If does not exist: emit('error', 'error' : 'remoteError', 'remote' : { 'error' : 'entryNotFound' }) Remote.prototype.request_ripple_balance = function (account, issuer, currency, current) { - var request = this.request_ledger_entry('ripple_state'); // YYY Could be cached per ledger. + var request = this.request_ledger_entry('ripple_state'); // YYY Could be cached per ledger. return request .ripple_state(account, issuer, currency) diff --git a/src/js/ripple/server.js b/src/js/ripple/server.js index 67b343aa..506a84d9 100644 --- a/src/js/ripple/server.js +++ b/src/js/ripple/server.js @@ -1,8 +1,7 @@ -var EventEmitter = require('events').EventEmitter; -var util = require('util'); -var WebSocket = require('ws'); +var EventEmitter = require('events').EventEmitter; +var util = require('util'); -var utils = require('./utils'); +var utils = require('./utils'); //------------------------------------------------------------------------------ /** @@ -20,7 +19,7 @@ var Server = function (remote, cfg) { EventEmitter.call(this); - if (typeof cfg !== 'object' || typeof cfg.url !== 'string') { + if ('object' !== typeof cfg || 'string' !== typeof cfg.url) { throw new Error('Invalid server configuration.'); } @@ -82,15 +81,12 @@ Server.prototype.connect = function () { // we will automatically reconnect. if (this._connected === true) return; - if (this._remote.trace) { - console.log('server: connect: %s', this._cfg.url); - } + if (this._remote.trace) console.log('server: connect: %s', this._cfg.url); // Ensure any existing socket is given the command to close first. - if (this._ws) { - this._ws.close(); - } + if (this._ws) this._ws.close(); + var WebSocket = require('ws'); var ws = this._ws = new WebSocket(this._cfg.url); this._should_connect = true; @@ -112,9 +108,7 @@ Server.prototype.connect = function () { // If we are no longer the active socket, simply ignore any event if (ws !== self._ws) return; - if (self._remote.trace) { - console.log('server: onerror: %s', e.data || e); - } + if (self._remote.trace) console.log('server: onerror: %s', e.data || e); // Most connection errors for WebSockets are conveyed as 'close' events with // code 1006. This is done for security purposes and therefore unlikely to @@ -138,9 +132,7 @@ Server.prototype.connect = function () { // If we are no longer the active socket, simply ignore any event if (ws !== self._ws) return; - if (self._remote.trace) { - console.log('server: onclose: %s', ws.readyState); - } + if (self._remote.trace) console.log('server: onclose: %s', ws.readyState); handleConnectionClose(); }; @@ -150,17 +142,16 @@ Server.prototype.connect = function () { self._set_state('offline'); // Prevent additional events from this socket - ws.removeAllListeners(); - ws.on('error', function() {}); + ws.onopen = ws.onerror = ws.onclose = ws.onmessage = function () {}; // Should we be connected? if (!self._should_connect) return; // Delay and retry. - self._retry += 1; - + self._retry += 1; self._retry_timer = setTimeout(function () { if (self._remote.trace) console.log('server: retry'); + if (!self._should_connect) return; self.connect(); }, self._retry < 40 @@ -170,7 +161,7 @@ Server.prototype.connect = function () { : self._retry < 40+60+60 ? 10*1000 // Then, for 10 minutes: once every 10 seconds : 30*1000); // Then: once every 30 seconds - }; + } ws.onmessage = function (msg) { self.emit('message', msg.data); From 13b7c9bfbb58c2b410382d102b72bbd06d1a0fd0 Mon Sep 17 00:00:00 2001 From: wltsmrz Date: Wed, 26 Jun 2013 03:41:46 +0900 Subject: [PATCH 12/39] Revert "Wrap JSON.parse in try/catch" This reverts commit 67041201053b8ca5bfe90defa5318ee018d6c171. --- src/js/ripple/remote.js | 85 +++++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 42 deletions(-) diff --git a/src/js/ripple/remote.js b/src/js/ripple/remote.js index ffa752a4..2c286ffa 100644 --- a/src/js/ripple/remote.js +++ b/src/js/ripple/remote.js @@ -45,8 +45,8 @@ var Request = function (remote, command) { this.remote = remote; this.requested = false; this.message = { - 'command': command, - 'id': void(0) + 'command' : command, + 'id' : void(0), }; }; @@ -108,7 +108,7 @@ Request.prototype.ledger_select = function (ledger_spec) { if (String(ledger_spec).length > 12) { this.message.ledger_hash = ledger_spec; } else { - this.message.ledger_index = ledger_spec; + this.message.ledger_index = ledger_spec; } } @@ -132,8 +132,8 @@ Request.prototype.index = function (hash) { // --> seq : sequence number of transaction creating offer (integer) Request.prototype.offer_id = function (account, seq) { this.message.offer = { - 'account': UInt160.json_rewrite(account), - 'seq': seq + 'account' : UInt160.json_rewrite(account), + 'seq' : seq }; return this; @@ -185,19 +185,20 @@ Request.prototype.ripple_state = function (account, issuer, currency) { }; Request.prototype.accounts = function (accounts, realtime) { - if (!Array.isArray(accounts)) { - accounts = [ accounts ]; + if (typeof accounts !== 'object') { + accounts = [accounts]; } // Process accounts parameters - var procAccounts = accounts.map(function(account) { - return UInt160.json_rewrite(account); - }); + var procAccounts = []; + for (var i = 0, l = accounts.length; i < l; i++) { + procAccounts.push(UInt160.json_rewrite(accounts[i])); + } if (realtime) { this.message.rt_accounts = procAccounts; } else { - this.message.accounts = procAccounts; + this.message.accounts = procAccounts; } return this; @@ -313,7 +314,7 @@ var Remote = function (opts, trace) { // Local signing implies local fees and sequences if (this.local_signing) { this.local_sequence = true; - this.local_fee = true; + this.local_fee = true; } this._servers = []; @@ -326,6 +327,7 @@ var Remote = function (opts, trace) { // Otherwise, clear it to have it automatically refreshed from the network. // account : { seq : __ } + }; // Hash map of Account objects by AccountId. @@ -349,31 +351,33 @@ var Remote = function (opts, trace) { } }; - // Support old API if (!('servers' in opts)) { - opts.servers = [ { - host: opts.websocket_ip, - port: opts.websocket_port, - secure: opts.websocket_ssl, - trusted: opts.trusted - } ] + opts.servers = [ + { + host: opts.websocket_ip, + port: opts.websocket_port, + secure: opts.websocket_ssl, + trusted: opts.trusted + } + ] } - // Initialize servers - opts.servers.forEach(function(server) { - self.add_server(server); - }); + for (var i=0; i Date: Wed, 26 Jun 2013 03:42:46 +0900 Subject: [PATCH 13/39] Revert "Enable multi-server fallback" This reverts commit 5a1b9d1f90b436b38f1dfb234b7b1e1424ef123f. --- src/js/ripple/remote.js | 261 +++++++++++++++++----------------------- 1 file changed, 108 insertions(+), 153 deletions(-) diff --git a/src/js/ripple/remote.js b/src/js/ripple/remote.js index 2c286ffa..65fc0732 100644 --- a/src/js/ripple/remote.js +++ b/src/js/ripple/remote.js @@ -42,12 +42,12 @@ var Request = function (remote, command) { var self = this; - this.remote = remote; - this.requested = false; this.message = { 'command' : command, - 'id' : void(0), + 'id' : undefined, }; + this.remote = remote; + this.requested = false; }; util.inherits(Request, EventEmitter); @@ -194,7 +194,6 @@ Request.prototype.accounts = function (accounts, realtime) { for (var i = 0, l = accounts.length; i < l; i++) { procAccounts.push(UInt160.json_rewrite(accounts[i])); } - if (realtime) { this.message.rt_accounts = procAccounts; } else { @@ -282,6 +281,9 @@ var Remote = function (opts, trace) { var self = this; this.trusted = opts.trusted; + this.websocket_ip = opts.websocket_ip; + this.websocket_port = opts.websocket_port; + this.websocket_ssl = opts.websocket_ssl; this.local_sequence = opts.local_sequence; // Locally track sequence numbers this.local_fee = opts.local_fee; // Locally set fees this.local_signing = ('undefined' === typeof opts.local_signing) @@ -317,8 +319,7 @@ var Remote = function (opts, trace) { this.local_fee = true; } - this._servers = []; - this._primary_server = void(0); + this._servers = []; // Cache information for accounts. // DEPRECATED, will be removed @@ -351,34 +352,25 @@ var Remote = function (opts, trace) { } }; - if (!('servers' in opts)) { - opts.servers = [ - { - host: opts.websocket_ip, - port: opts.websocket_port, - secure: opts.websocket_ssl, - trusted: opts.trusted - } - ] - } + // XXX Add support for multiple servers + var url = (this.websocket_ssl ? 'wss://' : 'ws://') + + this.websocket_ip + ':' + this.websocket_port; - for (var i=0; i= -299 && engine_result_code < 199); }; -Remote.prototype.add_server = function (opts) { +Remote.prototype.add_server = function (server) { var self = this; - var url = (opts.secure || opts.websocket_ssl ? 'wss://' : 'ws://') - + (opts.host || opts.websocket_ip) + ':' - + (opts.port || opts.websocket_port); - - var server = new Server(this, { url: url }) - - if ('maxListeners' in opts) { - server.setMaxListeners(opts.maxListeners); - } - server.on('message', function (data) { self._handle_message(data); }); server.on('connect', function () { - if (opts.primary || !self._primary_server) { - self._set_primary_server(server); - } self._connection_count++; self._set_state('online'); }); @@ -519,7 +500,8 @@ Remote.prototype.connect = function (online) { if (!this._servers.length) { throw new Error('No servers available.'); } else { - for (var i = 0, l = this._servers.length; i < l; i++) { + // XXX Add support for multiple servers + for (var i=0; i request: what to send, consumed. Remote.prototype.request = function (request) { if (!this._servers.length) { - request.emit('error', new Error('No servers availale')); + throw new Error('No servers available.'); } else { - var server = this._get_server(); - if (server) { - server.request(request); - } else { - request.emit('error', new Error('No servers availale')); - } + // XXX Add support for multiple servers + this._servers[0].request(request); } }; @@ -793,7 +739,7 @@ Remote.prototype.request_ledger_entry = function (type) { }); bDefault = false; - } + } else { // Was not cached. @@ -806,6 +752,7 @@ Remote.prototype.request_ledger_entry = function (type) { self.ledgers.current.account_root[message.node.Account] = message.node; }); break; + default: // This type not cached. // console.log('request_ledger_entry: non-cached type'); @@ -857,7 +804,8 @@ Remote.prototype.request_unsubscribe = function (streams) { Remote.prototype.request_transaction_entry = function (hash) { //utils.assert(this.trusted); // If not trusted, need to check proof, maybe talk packet protocol. - return (new Request(this, 'transaction_entry')).tx_hash(hash); + return (new Request(this, 'transaction_entry')) + .tx_hash(hash); }; // DEPRECATED: use request_transaction_entry @@ -1011,12 +959,14 @@ Remote.prototype.request_submit = function () { * * This function will create and return the request, but not submit it. */ -Remote.prototype._server_prepare_subscribe = function () { +Remote.prototype._server_prepare_subscribe = function () +{ var self = this; var feeds = [ 'ledger', 'server' ]; - if (this._transaction_subs) feeds.push('transactions'); + if (this._transaction_subs) + feeds.push('transactions'); var req = this.request_subscribe(feeds); @@ -1026,9 +976,9 @@ Remote.prototype._server_prepare_subscribe = function () { if ('string' === typeof message.random) { var rand = message.random.match(/[0-9A-F]{8}/ig); - while (rand && rand.length) { + while (rand && rand.length) sjcl.random.addEntropy(parseInt(rand.pop(), 16)); - } + self.emit('random', utils.hexToArray(message.random)); } @@ -1065,13 +1015,17 @@ Remote.prototype._server_prepare_subscribe = function () { // A good way to be notified of the result of this is: // remote.once('ledger_closed', function (ledger_closed, ledger_index) { ... } ); Remote.prototype.ledger_accept = function () { - if (this._stand_alone || undefined === this._stand_alone) { + if (this._stand_alone || undefined === this._stand_alone) + { var request = new Request(this, 'ledger_accept'); - request .request(); - } else { + + request + .request(); + } + else { this.emit('error', { - 'error' : 'notStandAlone' - }); + 'error' : 'notStandAlone' + }); } return this; @@ -1085,9 +1039,9 @@ Remote.prototype.request_account_balance = function (account, current) { .account_root(account) .ledger_choose(current) .on('success', function (message) { - // If the caller also waits for 'success', they might run before this. - request.emit('account_balance', Amount.from_json(message.node.Balance)); - }); + // If the caller also waits for 'success', they might run before this. + request.emit('account_balance', Amount.from_json(message.node.Balance)); + }); }; // Return a request to return the account flags. @@ -1098,9 +1052,9 @@ Remote.prototype.request_account_flags = function (account, current) { .account_root(account) .ledger_choose(current) .on('success', function (message) { - // If the caller also waits for 'success', they might run before this. - request.emit('account_flags', message.node.Flags); - }); + // If the caller also waits for 'success', they might run before this. + request.emit('account_flags', message.node.Flags); + }); }; // Return a request to emit the owner count. @@ -1111,9 +1065,9 @@ Remote.prototype.request_owner_count = function (account, current) { .account_root(account) .ledger_choose(current) .on('success', function (message) { - // If the caller also waits for 'success', they might run before this. - request.emit('owner_count', message.node.OwnerCount); - }); + // If the caller also waits for 'success', they might run before this. + request.emit('owner_count', message.node.OwnerCount); + }); }; Remote.prototype.account = function (accountId) { @@ -1130,7 +1084,8 @@ Remote.prototype.account = function (accountId) { return this._accounts[accountId]; }; -Remote.prototype.book = function (currency_gets, issuer_gets, currency_pays, issuer_pays) { +Remote.prototype.book = function (currency_gets, issuer_gets, + currency_pays, issuer_pays) { var gets = currency_gets; if (gets !== 'XRP') gets += '/' + issuer_gets; var pays = currency_pays; @@ -1139,7 +1094,9 @@ Remote.prototype.book = function (currency_gets, issuer_gets, currency_pays, iss var key = gets + ':' + pays; if (!this._books[key]) { - var book = new OrderBook(this, currency_gets, issuer_gets, currency_pays, issuer_pays); + var book = new OrderBook(this, + currency_gets, issuer_gets, + currency_pays, issuer_pays); if (!book.is_valid()) return book; @@ -1147,7 +1104,7 @@ Remote.prototype.book = function (currency_gets, issuer_gets, currency_pays, iss } return this._books[key]; -}; +} // Return the next account sequence if possible. // <-- undefined or Sequence @@ -1156,29 +1113,29 @@ Remote.prototype.account_seq = function (account, advance) { var account_info = this.accounts[account]; var seq; - if (account_info && account_info.seq) { + if (account_info && account_info.seq) + { seq = account_info.seq; if (advance === 'ADVANCE') account_info.seq += 1; if (advance === 'REWIND') account_info.seq -= 1; // console.log('cached: %s current=%d next=%d', account, seq, account_info.seq); - } else { + } + else { // console.log('uncached: %s', account); } return seq; -}; +} Remote.prototype.set_account_seq = function (account, seq) { - var account = UInt160.json_rewrite(account); + var account = UInt160.json_rewrite(account); - if (!this.accounts[account]) { - this.accounts[account] = { }; - } + if (!this.accounts[account]) this.accounts[account] = {}; this.accounts[account].seq = seq; -}; +} // Return a request to refresh accounts[account].seq. Remote.prototype.account_seq_cache = function (account, current) { @@ -1196,22 +1153,22 @@ Remote.prototype.account_seq_cache = function (account, current) { .account_root(account) .ledger_choose(current) .on('success', function (message) { - delete account_info.caching_seq_request; + delete account_info.caching_seq_request; - var seq = message.node.Sequence; + var seq = message.node.Sequence; - account_info.seq = seq; + account_info.seq = seq; - // console.log('caching: %s %d', account, seq); - // If the caller also waits for 'success', they might run before this. - request.emit('success_account_seq_cache', message); - }) + // console.log('caching: %s %d', account, seq); + // If the caller also waits for 'success', they might run before this. + request.emit('success_account_seq_cache', message); + }) .on('error', function (message) { - // console.log('error: %s', account); - delete account_info.caching_seq_request; + // console.log('error: %s', account); + delete account_info.caching_seq_request; - request.emit('error_account_seq_cache', message); - }); + request.emit('error_account_seq_cache', message); + }); account_info.caching_seq_request = request; } @@ -1247,29 +1204,29 @@ Remote.prototype.request_ripple_balance = function (account, issuer, currency, c .ripple_state(account, issuer, currency) .ledger_choose(current) .on('success', function (message) { - var node = message.node; + var node = message.node; - var lowLimit = Amount.from_json(node.LowLimit); - var highLimit = Amount.from_json(node.HighLimit); - // The amount the low account holds of issuer. - var balance = Amount.from_json(node.Balance); - // accountHigh implies: for account: balance is negated, highLimit is the limit set by account. - var accountHigh = UInt160.from_json(account).equals(highLimit.issuer()); + var lowLimit = Amount.from_json(node.LowLimit); + var highLimit = Amount.from_json(node.HighLimit); + // The amount the low account holds of issuer. + var balance = Amount.from_json(node.Balance); + // accountHigh implies: for account: balance is negated, highLimit is the limit set by account. + var accountHigh = UInt160.from_json(account).equals(highLimit.issuer()); - request.emit('ripple_state', { - 'account_balance' : ( accountHigh ? balance.negate() : balance.clone()).parse_issuer(account), - 'peer_balance' : (!accountHigh ? balance.negate() : balance.clone()).parse_issuer(issuer), + request.emit('ripple_state', { + 'account_balance' : ( accountHigh ? balance.negate() : balance.clone()).parse_issuer(account), + 'peer_balance' : (!accountHigh ? balance.negate() : balance.clone()).parse_issuer(issuer), - 'account_limit' : ( accountHigh ? highLimit : lowLimit).clone().parse_issuer(issuer), - 'peer_limit' : (!accountHigh ? highLimit : lowLimit).clone().parse_issuer(account), + 'account_limit' : ( accountHigh ? highLimit : lowLimit).clone().parse_issuer(issuer), + 'peer_limit' : (!accountHigh ? highLimit : lowLimit).clone().parse_issuer(account), - 'account_quality_in' : ( accountHigh ? node.HighQualityIn : node.LowQualityIn), - 'peer_quality_in' : (!accountHigh ? node.HighQualityIn : node.LowQualityIn), + 'account_quality_in' : ( accountHigh ? node.HighQualityIn : node.LowQualityIn), + 'peer_quality_in' : (!accountHigh ? node.HighQualityIn : node.LowQualityIn), - 'account_quality_out' : ( accountHigh ? node.HighQualityOut : node.LowQualityOut), - 'peer_quality_out' : (!accountHigh ? node.HighQualityOut : node.LowQualityOut), + 'account_quality_out' : ( accountHigh ? node.HighQualityOut : node.LowQualityOut), + 'peer_quality_out' : (!accountHigh ? node.HighQualityOut : node.LowQualityOut), + }); }); - }); }; Remote.prototype.request_ripple_path_find = function (src_account, dst_account, dst_amount, src_currencies) { @@ -1304,11 +1261,10 @@ Remote.prototype.request_unl_list = function () { Remote.prototype.request_unl_add = function (addr, comment) { var request = new Request(this, 'unl_add'); - request.message.node = addr; + request.message.node = addr; - if (typeof comment !== 'undefined') { + if (comment !== undefined) request.message.comment = note; - } return request; }; @@ -1331,9 +1287,8 @@ Remote.prototype.request_connect = function (ip, port) { request.message.ip = ip; - if (typeof port !== 'undefined') { + if (port) request.message.port = port; - } return request; }; From 07d33d817bb9be2c8d1f2ac5e90c1123e408228f Mon Sep 17 00:00:00 2001 From: wltsmrz Date: Wed, 26 Jun 2013 04:09:30 +0900 Subject: [PATCH 14/39] Fix .disconnect --- src/js/ripple/server.js | 65 ++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 34 deletions(-) diff --git a/src/js/ripple/server.js b/src/js/ripple/server.js index b115cdbe..428a97ab 100644 --- a/src/js/ripple/server.js +++ b/src/js/ripple/server.js @@ -50,26 +50,39 @@ var Server = function (remote, opts) { util.inherits(Server, EventEmitter); -function to_set(list) { - var result = { }; - for (var i=0; i Date: Wed, 26 Jun 2013 04:22:12 +0900 Subject: [PATCH 15/39] Multi-server API --- src/js/ripple/remote.js | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/src/js/ripple/remote.js b/src/js/ripple/remote.js index 65fc0732..c4b4ed94 100644 --- a/src/js/ripple/remote.js +++ b/src/js/ripple/remote.js @@ -358,13 +358,28 @@ var Remote = function (opts, trace) { var server = new Server (this, {url: url}) - if ('maxListeners' in opts) { - // This is used to remove Emitter warnings - server.setMaxListeners (opts.maxListeners) - this.setMaxListeners (opts.maxListeners) + if (!('servers' in opts)) { + opts.servers = [ + { + host: opts.websocket_ip, + port: opts.websocket_port, + secure: opts.websocket_ssl, + trusted: opts.trusted + } + ] } - this.add_server(server) + opts.servers.forEach(function(server) { + var i = Number(server.pool) || 1; + while (i--) { self.add_server(server); } + }); + + if ('maxListeners' in opts) { + // This is used to remove Emitter warnings + opts.servers.concat(this).forEach(function(emitter) { + emitter.setMaxListeners(opts.maxListeners); + }); + } this.on('newListener', function (type, listener) { if ('transaction_all' === type) { @@ -428,9 +443,16 @@ var isTefFailure = function (engine_result_code) { return (engine_result_code >= -299 && engine_result_code < 199); }; -Remote.prototype.add_server = function (server) { +Remote.prototype.add_server = function (opts) { var self = this; + var url = (opts.secure || opts.websocket_ssl) ? 'wss://' : 'ws://' + + (opts.host || opts.websocket_ip) + ':' + + (opts.port || opts.websocket_port) + ; + + var server = new Server(this, {url: url}) + server.on('message', function (data) { self._handle_message(data); }); From 3812ac7d5357da35e42e94b2c58b4203bf9f85b5 Mon Sep 17 00:00:00 2001 From: wltsmrz Date: Wed, 26 Jun 2013 04:48:29 +0900 Subject: [PATCH 16/39] Cleanup --- src/js/ripple/remote.js | 121 ++++++++++++++++++---------------------- 1 file changed, 55 insertions(+), 66 deletions(-) diff --git a/src/js/ripple/remote.js b/src/js/ripple/remote.js index c4b4ed94..753d54bf 100644 --- a/src/js/ripple/remote.js +++ b/src/js/ripple/remote.js @@ -103,6 +103,7 @@ Request.prototype.ledger_select = function (ledger_spec) { case 'verified': this.message.ledger_index = ledger_spec; break; + default: // XXX Better test needed if (String(ledger_spec).length > 12) { @@ -110,6 +111,7 @@ Request.prototype.ledger_select = function (ledger_spec) { } else { this.message.ledger_index = ledger_spec; } + break; } return this; @@ -185,15 +187,15 @@ Request.prototype.ripple_state = function (account, issuer, currency) { }; Request.prototype.accounts = function (accounts, realtime) { - if (typeof accounts !== 'object') { - accounts = [accounts]; + if (!Array.isArray(accounts)) { + accounts = [ accounts ]; } // Process accounts parameters - var procAccounts = []; - for (var i = 0, l = accounts.length; i < l; i++) { - procAccounts.push(UInt160.json_rewrite(accounts[i])); - } + var procAccounts = accounts.map(function(account) { + return UInt160.json_rewrite(account); + }); + if (realtime) { this.message.rt_accounts = procAccounts; } else { @@ -280,38 +282,38 @@ var Remote = function (opts, trace) { var self = this; - this.trusted = opts.trusted; - this.websocket_ip = opts.websocket_ip; - this.websocket_port = opts.websocket_port; - this.websocket_ssl = opts.websocket_ssl; - this.local_sequence = opts.local_sequence; // Locally track sequence numbers - this.local_fee = opts.local_fee; // Locally set fees - this.local_signing = ('undefined' === typeof opts.local_signing) + this.trusted = opts.trusted; + this.local_sequence = opts.local_sequence; // Locally track sequence numbers + this.local_fee = opts.local_fee; // Locally set fees + this.local_signing = ('undefined' === typeof opts.local_signing) ? true : opts.local_signing; - this.id = 0; - this.trace = opts.trace || trace; - this._server_fatal = false; // True, if we know server exited. - this._ledger_current_index = void(0); - this._ledger_hash = void(0); - this._ledger_time = void(0); - this._stand_alone = void(0); - this._testnet = void(0); - this._transaction_subs = 0; - this.online_target = false; - this._online_state = 'closed'; // 'open', 'closed', 'connecting', 'closing' - this.state = 'offline'; // 'online', 'offline' - this.retry_timer = void(0); - this.retry = void(0); - this._load_base = 256; - this._load_factor = 1.0; - this._fee_ref = void(0); - this._fee_base = void(0); - this._reserve_base = void(0); - this._reserve_inc = void(0); - this._connection_count = 0; + this.id = 0; + this.trace = opts.trace || trace; + this._server_fatal = false; // True, if we know server exited. + this._ledger_current_index = void(0); + this._ledger_hash = void(0); + this._ledger_time = void(0); + this._stand_alone = void(0); + this._testnet = void(0); + this._transaction_subs = 0; + this.online_target = false; + this._online_state = 'closed'; // 'open', 'closed', 'connecting', 'closing' + this.state = 'offline'; // 'online', 'offline' + this.retry_timer = void(0); + this.retry = void(0); - this._last_tx = null; + this._load_base = 256; + this._load_factor = 1.0; + this._fee_ref = void(0); + this._fee_base = void(0); + this._reserve_base = void(0); + this._reserve_inc = void(0); + this._connection_count = 0; + this._connected = false; + + + this._last_tx = null; // Local signing implies local fees and sequences if (this.local_signing) { @@ -328,7 +330,6 @@ var Remote = function (opts, trace) { // Otherwise, clear it to have it automatically refreshed from the network. // account : { seq : __ } - }; // Hash map of Account objects by AccountId. @@ -352,12 +353,6 @@ var Remote = function (opts, trace) { } }; - // XXX Add support for multiple servers - var url = (this.websocket_ssl ? 'wss://' : 'ws://') + - this.websocket_ip + ':' + this.websocket_port; - - var server = new Server (this, {url: url}) - if (!('servers' in opts)) { opts.servers = [ { @@ -383,21 +378,18 @@ var Remote = function (opts, trace) { this.on('newListener', function (type, listener) { if ('transaction_all' === type) { - if (!self._transaction_subs && 'open' === self._online_state) { - self.request_subscribe([ 'transactions' ]) - .request(); + if (!self._transaction_subs && self._connected) { + self.request_subscribe('transactions').request(); } - self._transaction_subs += 1; + self._transaction_subs += 1; } }); this.on('removeListener', function (type, listener) { if ('transaction_all' === type) { self._transaction_subs -= 1; - - if (!self._transaction_subs && 'open' === self._online_state) { - self.request_unsubscribe([ 'transactions' ]) - .request(); + if (!self._transaction_subs && self._connected) { + self.request_unsubscribe('transactions').request(); } } }); @@ -490,13 +482,15 @@ Remote.prototype._set_state = function (state) { switch (state) { case 'online': - this._online_state = 'open'; + this._online_state = 'open'; + this._connected = true; this.emit('connect'); this.emit('connected'); break; case 'offline': - this._online_state = 'closed'; + this._online_state = 'closed'; + this._connected = false; this.emit('disconnect'); this.emit('disconnected'); break; @@ -637,9 +631,9 @@ Remote.prototype._handle_message = function (json) { console.log('unexpected message from trusted remote: %s', json); (request || this).emit('error', { - 'error' : 'remoteUnexpected', - 'error_message' : 'Unexpected response from remote.' - }); + 'error' : 'remoteUnexpected', + 'error_message' : 'Unexpected response from remote.' + }); } }; @@ -1037,17 +1031,14 @@ Remote.prototype._server_prepare_subscribe = function () // A good way to be notified of the result of this is: // remote.once('ledger_closed', function (ledger_closed, ledger_index) { ... } ); Remote.prototype.ledger_accept = function () { - if (this._stand_alone || undefined === this._stand_alone) - { + if (this._stand_alone || undefined === this._stand_alone) { var request = new Request(this, 'ledger_accept'); - - request - .request(); + request .request(); } else { this.emit('error', { - 'error' : 'notStandAlone' - }); + 'error' : 'notStandAlone' + }); } return this; @@ -1135,16 +1126,14 @@ Remote.prototype.account_seq = function (account, advance) { var account_info = this.accounts[account]; var seq; - if (account_info && account_info.seq) - { + if (account_info && account_info.seq) { seq = account_info.seq; if (advance === 'ADVANCE') account_info.seq += 1; if (advance === 'REWIND') account_info.seq -= 1; // console.log('cached: %s current=%d next=%d', account, seq, account_info.seq); - } - else { + } else { // console.log('uncached: %s', account); } From be6bd8cc5d18d14eef54fd47a687df54fd422ead Mon Sep 17 00:00:00 2001 From: Stefan Thomas Date: Mon, 1 Jul 2013 16:11:36 +0100 Subject: [PATCH 17/39] Fix typo. --- src/js/ripple/server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/ripple/server.js b/src/js/ripple/server.js index 428a97ab..261ec863 100644 --- a/src/js/ripple/server.js +++ b/src/js/ripple/server.js @@ -93,7 +93,7 @@ Server.prototype.connect = function () { // we will automatically reconnect. if (this._connected === true) return; - if (this._remote.trace) console.log('server: connect: %s', this._cfg.url); + if (this._remote.trace) console.log('server: connect: %s', this._opts.url); // Ensure any existing socket is given the command to close first. if (this._ws) this._ws.close(); From 44bb8658f0ac57f641d4e5a651f9c3d17974a66a Mon Sep 17 00:00:00 2001 From: Stefan Thomas Date: Mon, 1 Jul 2013 16:11:50 +0100 Subject: [PATCH 18/39] Implement serializing Hash256. --- src/js/ripple/serializedtypes.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/js/ripple/serializedtypes.js b/src/js/ripple/serializedtypes.js index e402788c..5fe7383c 100644 --- a/src/js/ripple/serializedtypes.js +++ b/src/js/ripple/serializedtypes.js @@ -12,6 +12,7 @@ var extend = require('extend'), var amount = require('./amount'), UInt160 = amount.UInt160, + UInt256 = require('./uint256').UInt256, Amount = amount.Amount, Currency= amount.Currency; @@ -109,8 +110,8 @@ var STHash128 = exports.Hash128 = new SerializedType({ var STHash256 = exports.Hash256 = new SerializedType({ serialize: function (so, val) { - // XXX - throw new Error("Serializing Hash256 not implemented"); + var hash = UInt256.from_json(val); + this.serialize_hex(so, hash.to_hex()); }, parse: function (so) { // XXX From 0fcce464bbde7d8a2a5221d4da7b25f78f902c6a Mon Sep 17 00:00:00 2001 From: Stefan Thomas Date: Mon, 1 Jul 2013 16:17:26 +0100 Subject: [PATCH 19/39] Fix invalid parentheses. --- src/js/ripple/remote.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/ripple/remote.js b/src/js/ripple/remote.js index 753d54bf..b1931165 100644 --- a/src/js/ripple/remote.js +++ b/src/js/ripple/remote.js @@ -438,7 +438,7 @@ var isTefFailure = function (engine_result_code) { Remote.prototype.add_server = function (opts) { var self = this; - var url = (opts.secure || opts.websocket_ssl) ? 'wss://' : 'ws://' + var url = ((opts.secure || opts.websocket_ssl) ? 'wss://' : 'ws://') + (opts.host || opts.websocket_ip) + ':' + (opts.port || opts.websocket_port) ; From 087f3577140a78ec024b5f8c006b136c1f8f3158 Mon Sep 17 00:00:00 2001 From: wltsmrz Date: Wed, 10 Jul 2013 05:59:00 +0900 Subject: [PATCH 20/39] Callbacks --- src/js/ripple/remote.js | 391 +++++++++++++++++++++++----------------- 1 file changed, 228 insertions(+), 163 deletions(-) diff --git a/src/js/ripple/remote.js b/src/js/ripple/remote.js index b1931165..0c557b02 100644 --- a/src/js/ripple/remote.js +++ b/src/js/ripple/remote.js @@ -42,12 +42,12 @@ var Request = function (remote, command) { var self = this; + this.remote = remote; + this.requested = false; this.message = { 'command' : command, 'id' : undefined, }; - this.remote = remote; - this.requested = false; }; util.inherits(Request, EventEmitter); @@ -61,6 +61,15 @@ Request.prototype.request = function (remote) { } }; +Request.prototype.callback = function(callback, successEvent, errorEvent) { + if (callback && typeof callback === 'function') { + this.once(successEvent || 'success', callback.bind(this, null)); + this.once(errorEvent || 'error', callback); + this.request(); + } + return this; +}; + Request.prototype.build_path = function (build) { if (build) { this.message.build_path = true; @@ -285,7 +294,7 @@ var Remote = function (opts, trace) { this.trusted = opts.trusted; this.local_sequence = opts.local_sequence; // Locally track sequence numbers this.local_fee = opts.local_fee; // Locally set fees - this.local_signing = ('undefined' === typeof opts.local_signing) + this.local_signing = (typeof opts.local_signing === 'undefined') ? true : opts.local_signing; this.id = 0; @@ -321,7 +330,8 @@ var Remote = function (opts, trace) { this.local_fee = true; } - this._servers = []; + this._servers = [ ]; + this._primary_server = void(0); // Cache information for accounts. // DEPRECATED, will be removed @@ -353,7 +363,8 @@ var Remote = function (opts, trace) { } }; - if (!('servers' in opts)) { + // Fallback for previous API + if (!opts.hasOwnProperty('servers')) { opts.servers = [ { host: opts.websocket_ip, @@ -369,15 +380,15 @@ var Remote = function (opts, trace) { while (i--) { self.add_server(server); } }); - if ('maxListeners' in opts) { - // This is used to remove Emitter warnings + // This is used to remove Node EventEmitter warnings + if (opts.hasOwnProperty('maxListeners')) { opts.servers.concat(this).forEach(function(emitter) { emitter.setMaxListeners(opts.maxListeners); }); } this.on('newListener', function (type, listener) { - if ('transaction_all' === type) { + if (type === 'transaction_all') { if (!self._transaction_subs && self._connected) { self.request_subscribe('transactions').request(); } @@ -386,8 +397,8 @@ var Remote = function (opts, trace) { }); this.on('removeListener', function (type, listener) { - if ('transaction_all' === type) { - self._transaction_subs -= 1; + if (type === 'transaction_all') { + self._transaction_subs -= 1; if (!self._transaction_subs && self._connected) { self.request_unsubscribe('transactions').request(); } @@ -408,13 +419,15 @@ Remote.flags = { }; Remote.from_config = function (obj, trace) { - var serverConfig = 'string' === typeof obj ? config.servers[obj] : obj; + var serverConfig = typeof obj === 'string' + ? config.servers[obj] + : obj; var remote = new Remote(serverConfig, trace); for (var account in config.accounts) { var accountInfo = config.accounts[account]; - if ('object' === typeof accountInfo) { + if (typeof accountInfo === 'object') { if (accountInfo.secret) { // Index by nickname ... remote.set_secret(account, accountInfo.secret); @@ -450,6 +463,9 @@ Remote.prototype.add_server = function (opts) { }); server.on('connect', function () { + if (opts.primary || !self._primary_server) { + self._set_primary_server(server); + } self._connection_count++; self._set_state('online'); }); @@ -509,16 +525,16 @@ Remote.prototype.set_trace = function (trace) { */ Remote.prototype.connect = function (online) { // Downwards compatibility - if ('undefined' !== typeof online && !online) { - return this.disconnect(); - } - - if (!this._servers.length) { - throw new Error('No servers available.'); + if (typeof online !== 'undefined' && !online) { + this.disconnect(); } else { - // XXX Add support for multiple servers - for (var i=0; i request: what to send, consumed. Remote.prototype.request = function (request) { - if (!this._servers.length) { - throw new Error('No servers available.'); + if (!this._connected) { + this.once('connect', this.request.bind(this, request)); + } else if (!this._servers.length) { + request.emit('error', new Error('No servers available')); } else { - // XXX Add support for multiple servers - this._servers[0].request(request); + var server = this._get_server(); + if (server) { + server.request(request); + } else { + request.emit('error', new Error('No servers available')); + } } }; -Remote.prototype.request_server_info = function () { - return new Request(this, 'server_info'); +Remote.prototype.request_server_info = function(callback) { + return new Request(this, 'server_info').callback(callback); }; // XXX This is a bad command. Some varients don't scale. // XXX Require the server to be trusted. -Remote.prototype.request_ledger = function (ledger, opts) { +Remote.prototype.request_ledger = function (ledger, opts, callback) { //utils.assert(this.trusted); var request = new Request(this, 'ledger'); @@ -684,33 +739,33 @@ Remote.prototype.request_ledger = function (ledger, opts) { request.message.full = true; } - return request; + return request.callback(callback); }; // Only for unit testing. -Remote.prototype.request_ledger_hash = function () { +Remote.prototype.request_ledger_hash = function (callback) { //utils.assert(this.trusted); // If not trusted, need to check proof. - return new Request(this, 'ledger_closed'); + return new Request(this, 'ledger_closed').callback(callback); }; // .ledger() // .ledger_index() -Remote.prototype.request_ledger_header = function () { - return new Request(this, 'ledger_header'); +Remote.prototype.request_ledger_header = function (callback) { + return new Request(this, 'ledger_header').callback(callback); }; // Get the current proposed ledger entry. May be closed (and revised) at any time (even before returning). // Only for unit testing. -Remote.prototype.request_ledger_current = function () { - return new Request(this, 'ledger_current'); +Remote.prototype.request_ledger_current = function (callback) { + return new Request(this, 'ledger_current').callback(callback); }; // --> type : the type of ledger entry. // .ledger() // .ledger_index() // .offer_id() -Remote.prototype.request_ledger_entry = function (type) { +Remote.prototype.request_ledger_entry = function (type, callback) { //utils.assert(this.trusted); // If not trusted, need to check proof, maybe talk packet protocol. var self = this; @@ -720,7 +775,7 @@ Remote.prototype.request_ledger_entry = function (type) { // If not found, listen, cache result, and emit it. // // Transparent caching: - if ('account_root' === type) { + if (type === 'account_root') { request.request_default = request.request; request.request = function () { // Intercept default request. @@ -737,7 +792,7 @@ Remote.prototype.request_ledger_entry = function (type) { } // else if (req.ledger_index) // else if ('ripple_state' === request.type) // YYY Could be cached per ledger. - else if ('account_root' === type) { + else if (type === 'account_root') { var cache = self.ledgers.current.account_root; if (!cache) { @@ -755,8 +810,7 @@ Remote.prototype.request_ledger_entry = function (type) { }); bDefault = false; - } - else { + } else { // Was not cached. // XXX Only allow with trusted mode. Must sync response with advance. @@ -783,68 +837,69 @@ Remote.prototype.request_ledger_entry = function (type) { } }; - return request; + return request.callback(callback); }; // .accounts(accounts, realtime) -Remote.prototype.request_subscribe = function (streams) { +Remote.prototype.request_subscribe = function (streams, callback) { var request = new Request(this, 'subscribe'); if (streams) { - if ('object' !== typeof streams) { - streams = [streams]; + if (!Array.isArray(streams)) { + streams = [ streams ]; } request.message.streams = streams; } - return request; + return request.callback(callback); }; // .accounts(accounts, realtime) -Remote.prototype.request_unsubscribe = function (streams) { +Remote.prototype.request_unsubscribe = function (streams, callback) { var request = new Request(this, 'unsubscribe'); if (streams) { - if ('object' !== typeof streams) { - streams = [streams]; + if (!Array.isArray(streams)) { + streams = [ streams ]; } request.message.streams = streams; } - return request; + return request.callback(callback); }; // .ledger_choose() // .ledger_hash() // .ledger_index() -Remote.prototype.request_transaction_entry = function (hash) { +Remote.prototype.request_transaction_entry = function (hash, callback) { //utils.assert(this.trusted); // If not trusted, need to check proof, maybe talk packet protocol. return (new Request(this, 'transaction_entry')) - .tx_hash(hash); + .tx_hash(hash) + .callback(callback); }; // DEPRECATED: use request_transaction_entry -Remote.prototype.request_tx = function (hash) { +Remote.prototype.request_tx = function (hash, callback) { var request = new Request(this, 'tx'); request.message.transaction = hash; - return request; + return request.callback(callback); }; -Remote.prototype.request_account_info = function (accountID) { +Remote.prototype.request_account_info = function (accountID, callback) { var request = new Request(this, 'account_info'); request.message.ident = UInt160.json_rewrite(accountID); // DEPRECATED request.message.account = UInt160.json_rewrite(accountID); - return request; + return request.callback(callback); }; // --> account_index: sub_account index (optional) // --> current: true, for the current ledger. -Remote.prototype.request_account_lines = function (accountID, account_index, current) { +Remote.prototype.request_account_lines = function (accountID, account_index, current, callback) { // XXX Does this require the server to be trusted? //utils.assert(this.trusted); @@ -856,12 +911,12 @@ Remote.prototype.request_account_lines = function (accountID, account_index, cur request.message.index = account_index; } - return request.ledger_choose(current); + return request.ledger_choose(current).callback(callback); }; // --> account_index: sub_account index (optional) // --> current: true, for the current ledger. -Remote.prototype.request_account_offers = function (accountID, account_index, current) { +Remote.prototype.request_account_offers = function (accountID, account_index, current, callback) { var request = new Request(this, 'account_offers'); request.message.account = UInt160.json_rewrite(accountID); @@ -870,7 +925,7 @@ Remote.prototype.request_account_offers = function (accountID, account_index, cu request.message.index = account_index; } - return request.ledger_choose(current); + return request.ledger_choose(current).callback(callback); }; @@ -885,7 +940,7 @@ Remote.prototype.request_account_offers = function (accountID, account_index, cu limit: integer // optional */ -Remote.prototype.request_account_tx = function (obj) { +Remote.prototype.request_account_tx = function (obj, callback) { // XXX Does this require the server to be trusted? //utils.assert(this.trusted); @@ -897,19 +952,19 @@ Remote.prototype.request_account_tx = function (obj) { //request.message.ledger = ledger_min; } else { - if ('undefined' !== typeof obj.ledger_index_min) {request.message.ledger_index_min = obj.ledger_index_min;} - if ('undefined' !== typeof obj.ledger_index_max) {request.message.ledger_index_max = obj.ledger_index_max;} - if ('undefined' !== typeof obj.binary) {request.message.binary = obj.binary;} - if ('undefined' !== typeof obj.count) {request.message.count = obj.count;} - if ('undefined' !== typeof obj.descending) {request.message.descending = obj.descending;} - if ('undefined' !== typeof obj.offset) {request.message.offset = obj.offset;} - if ('undefined' !== typeof obj.limit) {request.message.limit = obj.limit;} + if (typeof obj.ledger_index_min !== 'undefined') {request.message.ledger_index_min = obj.ledger_index_min;} + if (typeof obj.ledger_index_max !== 'undefined') {request.message.ledger_index_max = obj.ledger_index_max;} + if (typeof obj.binary !== 'undefined') {request.message.binary = obj.binary;} + if (typeof obj.count !== 'undefined') {request.message.count = obj.count;} + if (typeof obj.descending !== 'undefined') {request.message.descending = obj.descending;} + if (typeof obj.offset !== 'undefined') {request.message.offset = obj.offset;} + if (typeof obj.limit !== 'undefined') {request.message.limit = obj.limit;} } - return request; + return request.callback(callback); }; -Remote.prototype.request_book_offers = function (gets, pays, taker) { +Remote.prototype.request_book_offers = function (gets, pays, taker, callback) { var request = new Request(this, 'book_offers'); request.message.taker_gets = { @@ -930,20 +985,20 @@ Remote.prototype.request_book_offers = function (gets, pays, taker) { request.message.taker = taker ? taker : UInt160.ACCOUNT_ONE; - return request; + return request.callback(callback); }; -Remote.prototype.request_wallet_accounts = function (seed) { +Remote.prototype.request_wallet_accounts = function (seed, callback) { utils.assert(this.trusted); // Don't send secrets. var request = new Request(this, 'wallet_accounts'); request.message.seed = seed; - return request; + return request.callback(callback); }; -Remote.prototype.request_sign = function (secret, tx_json) { +Remote.prototype.request_sign = function (secret, tx_json, callback) { utils.assert(this.trusted); // Don't send secrets. var request = new Request(this, 'sign'); @@ -951,16 +1006,16 @@ Remote.prototype.request_sign = function (secret, tx_json) { request.message.secret = secret; request.message.tx_json = tx_json; - return request; + return request.callback(callback); }; // Submit a transaction. -Remote.prototype.request_submit = function () { +Remote.prototype.request_submit = function (callback) { var self = this; var request = new Request(this, 'submit'); - return request; + return request.callback(callback); }; // @@ -975,26 +1030,26 @@ Remote.prototype.request_submit = function () { * * This function will create and return the request, but not submit it. */ -Remote.prototype._server_prepare_subscribe = function () -{ +Remote.prototype._server_prepare_subscribe = function (callback) { var self = this; var feeds = [ 'ledger', 'server' ]; - if (this._transaction_subs) + if (this._transaction_subs) { feeds.push('transactions'); + } - var req = this.request_subscribe(feeds); + var request = this.request_subscribe(feeds); - req.on('success', function (message) { + request.on('success', function (message) { self._stand_alone = !!message.stand_alone; self._testnet = !!message.testnet; - if ('string' === typeof message.random) { + if (typeof message.random === 'string') { var rand = message.random.match(/[0-9A-F]{8}/ig); - while (rand && rand.length) + while (rand && rand.length) { sjcl.random.addEntropy(parseInt(rand.pop(), 16)); - + } self.emit('random', utils.hexToArray(message.random)); } @@ -1019,71 +1074,73 @@ Remote.prototype._server_prepare_subscribe = function () self.emit('subscribed'); }); - self.emit('prepare_subscribe', req); + self.emit('prepare_subscribe', request); // XXX Could give error events, maybe even time out. - return req; + return request.callback(callback); }; // For unit testing: ask the remote to accept the current ledger. // - To be notified when the ledger is accepted, server_subscribe() then listen to 'ledger_hash' events. // A good way to be notified of the result of this is: // remote.once('ledger_closed', function (ledger_closed, ledger_index) { ... } ); -Remote.prototype.ledger_accept = function () { +Remote.prototype.ledger_accept = function (callback) { if (this._stand_alone || undefined === this._stand_alone) { var request = new Request(this, 'ledger_accept'); - request .request(); - } - else { + request.request(); + request.callback(callback); + } else { this.emit('error', { 'error' : 'notStandAlone' }); } - return this; }; // Return a request to refresh the account balance. -Remote.prototype.request_account_balance = function (account, current) { +Remote.prototype.request_account_balance = function (account, current, callback) { var request = this.request_ledger_entry('account_root'); return request .account_root(account) .ledger_choose(current) .on('success', function (message) { - // If the caller also waits for 'success', they might run before this. - request.emit('account_balance', Amount.from_json(message.node.Balance)); - }); + // If the caller also waits for 'success', they might run before this. + request.emit('account_balance', Amount.from_json(message.node.Balance)); + }) + .callback(callback, 'account_balance'); }; // Return a request to return the account flags. -Remote.prototype.request_account_flags = function (account, current) { +Remote.prototype.request_account_flags = function (account, current, callback) { var request = this.request_ledger_entry('account_root'); return request .account_root(account) .ledger_choose(current) .on('success', function (message) { - // If the caller also waits for 'success', they might run before this. - request.emit('account_flags', message.node.Flags); - }); + // If the caller also waits for 'success', they might run before this. + request.emit('account_flags', message.node.Flags); + }) + .callback(callback, 'account_flags'); }; // Return a request to emit the owner count. -Remote.prototype.request_owner_count = function (account, current) { +Remote.prototype.request_owner_count = function (account, current, callback) { var request = this.request_ledger_entry('account_root'); return request .account_root(account) .ledger_choose(current) .on('success', function (message) { - // If the caller also waits for 'success', they might run before this. - request.emit('owner_count', message.node.OwnerCount); - }); + // If the caller also waits for 'success', they might run before this. + request.emit('owner_count', message.node.OwnerCount); + }) + .callback(callback, 'owner_count'); }; -Remote.prototype.account = function (accountId) { +Remote.prototype.account = function (accountId, callback) { accountId = UInt160.json_rewrite(accountId); if (!this._accounts[accountId]) { @@ -1094,7 +1151,13 @@ Remote.prototype.account = function (accountId) { this._accounts[accountId] = account; } - return this._accounts[accountId]; + var account = this._accounts[accountId]; + + if (typeof callback === 'function') { + callback(account); + } + + return account; }; Remote.prototype.book = function (currency_gets, issuer_gets, @@ -1107,9 +1170,10 @@ Remote.prototype.book = function (currency_gets, issuer_gets, var key = gets + ':' + pays; if (!this._books[key]) { - var book = new OrderBook(this, - currency_gets, issuer_gets, - currency_pays, issuer_pays); + var book = new OrderBook( this, + currency_gets, issuer_gets, + currency_pays, issuer_pays + ); if (!book.is_valid()) return book; @@ -1122,8 +1186,8 @@ Remote.prototype.book = function (currency_gets, issuer_gets, // Return the next account sequence if possible. // <-- undefined or Sequence Remote.prototype.account_seq = function (account, advance) { - account = UInt160.json_rewrite(account); - var account_info = this.accounts[account]; + var account = UInt160.json_rewrite(account); + var account_info = this.accounts[account]; var seq; if (account_info && account_info.seq) { @@ -1141,7 +1205,7 @@ Remote.prototype.account_seq = function (account, advance) { } Remote.prototype.set_account_seq = function (account, seq) { - var account = UInt160.json_rewrite(account); + var account = UInt160.json_rewrite(account); if (!this.accounts[account]) this.accounts[account] = {}; @@ -1149,42 +1213,40 @@ Remote.prototype.set_account_seq = function (account, seq) { } // Return a request to refresh accounts[account].seq. -Remote.prototype.account_seq_cache = function (account, current) { - var self = this; - var request; +Remote.prototype.account_seq_cache = function (account, current, callback) { + var self = this; if (!self.accounts[account]) self.accounts[account] = {}; var account_info = self.accounts[account]; + var request = account_info.caching_seq_request; - request = account_info.caching_seq_request; if (!request) { // console.log('starting: %s', account); request = self.request_ledger_entry('account_root') .account_root(account) .ledger_choose(current) .on('success', function (message) { - delete account_info.caching_seq_request; + delete account_info.caching_seq_request; - var seq = message.node.Sequence; + var seq = message.node.Sequence; + account_info.seq = seq; - account_info.seq = seq; - - // console.log('caching: %s %d', account, seq); - // If the caller also waits for 'success', they might run before this. - request.emit('success_account_seq_cache', message); - }) + // console.log('caching: %s %d', account, seq); + // If the caller also waits for 'success', they might run before this. + request.emit('success_account_seq_cache', message); + }) .on('error', function (message) { - // console.log('error: %s', account); - delete account_info.caching_seq_request; + // console.log('error: %s', account); + delete account_info.caching_seq_request; - request.emit('error_account_seq_cache', message); - }); + request.emit('error_account_seq_cache', message); + }); account_info.caching_seq_request = request; } - return request; + return request.callback(callback, 'success_account_seq_cache', 'error_account_seq_cache'); }; // Mark an account's root node as dirty. @@ -1208,39 +1270,40 @@ Remote.prototype.set_secret = function (account, secret) { // --> current: bool : true = current ledger // // If does not exist: emit('error', 'error' : 'remoteError', 'remote' : { 'error' : 'entryNotFound' }) -Remote.prototype.request_ripple_balance = function (account, issuer, currency, current) { +Remote.prototype.request_ripple_balance = function (account, issuer, currency, current, callback) { var request = this.request_ledger_entry('ripple_state'); // YYY Could be cached per ledger. return request .ripple_state(account, issuer, currency) .ledger_choose(current) .on('success', function (message) { - var node = message.node; + var node = message.node; - var lowLimit = Amount.from_json(node.LowLimit); - var highLimit = Amount.from_json(node.HighLimit); - // The amount the low account holds of issuer. - var balance = Amount.from_json(node.Balance); - // accountHigh implies: for account: balance is negated, highLimit is the limit set by account. - var accountHigh = UInt160.from_json(account).equals(highLimit.issuer()); + var lowLimit = Amount.from_json(node.LowLimit); + var highLimit = Amount.from_json(node.HighLimit); + // The amount the low account holds of issuer. + var balance = Amount.from_json(node.Balance); + // accountHigh implies: for account: balance is negated, highLimit is the limit set by account. + var accountHigh = UInt160.from_json(account).equals(highLimit.issuer()); - request.emit('ripple_state', { - 'account_balance' : ( accountHigh ? balance.negate() : balance.clone()).parse_issuer(account), - 'peer_balance' : (!accountHigh ? balance.negate() : balance.clone()).parse_issuer(issuer), + request.emit('ripple_state', { + 'account_balance' : ( accountHigh ? balance.negate() : balance.clone()).parse_issuer(account), + 'peer_balance' : (!accountHigh ? balance.negate() : balance.clone()).parse_issuer(issuer), - 'account_limit' : ( accountHigh ? highLimit : lowLimit).clone().parse_issuer(issuer), - 'peer_limit' : (!accountHigh ? highLimit : lowLimit).clone().parse_issuer(account), + 'account_limit' : ( accountHigh ? highLimit : lowLimit).clone().parse_issuer(issuer), + 'peer_limit' : (!accountHigh ? highLimit : lowLimit).clone().parse_issuer(account), - 'account_quality_in' : ( accountHigh ? node.HighQualityIn : node.LowQualityIn), - 'peer_quality_in' : (!accountHigh ? node.HighQualityIn : node.LowQualityIn), + 'account_quality_in' : ( accountHigh ? node.HighQualityIn : node.LowQualityIn), + 'peer_quality_in' : (!accountHigh ? node.HighQualityIn : node.LowQualityIn), - 'account_quality_out' : ( accountHigh ? node.HighQualityOut : node.LowQualityOut), - 'peer_quality_out' : (!accountHigh ? node.HighQualityOut : node.LowQualityOut), - }); + 'account_quality_out' : ( accountHigh ? node.HighQualityOut : node.LowQualityOut), + 'peer_quality_out' : (!accountHigh ? node.HighQualityOut : node.LowQualityOut), }); + }) + .callback(callback, 'ripple_state'); }; -Remote.prototype.request_ripple_path_find = function (src_account, dst_account, dst_amount, src_currencies) { +Remote.prototype.request_ripple_path_find = function (src_account, dst_account, dst_amount, src_currencies, callback) { var self = this; var request = new Request(this, 'ripple_path_find'); @@ -1262,46 +1325,48 @@ Remote.prototype.request_ripple_path_find = function (src_account, dst_account, }); } - return request; + return request.callback(callback); }; -Remote.prototype.request_unl_list = function () { - return new Request(this, 'unl_list'); +Remote.prototype.request_unl_list = function (callback) { + return new Request(this, 'unl_list').callback(callback); }; -Remote.prototype.request_unl_add = function (addr, comment) { +Remote.prototype.request_unl_add = function (addr, comment, callback) { var request = new Request(this, 'unl_add'); request.message.node = addr; - if (comment !== undefined) + if (comment) { request.message.comment = note; + } - return request; + return request.callback(callback); }; // --> node: | -Remote.prototype.request_unl_delete = function (node) { +Remote.prototype.request_unl_delete = function (node, callback) { var request = new Request(this, 'unl_delete'); request.message.node = node; - return request; + return request.callback(callback); }; Remote.prototype.request_peers = function () { return new Request(this, 'peers'); }; -Remote.prototype.request_connect = function (ip, port) { +Remote.prototype.request_connect = function (ip, port, callback) { var request = new Request(this, 'connect'); request.message.ip = ip; - if (port) + if (port) { request.message.port = port; + } - return request; + return request.callback(callback); }; Remote.prototype.transaction = function () { From d93145fc1eb7876ed8b87ae1376350de7b14eb44 Mon Sep 17 00:00:00 2001 From: wltsmrz Date: Wed, 10 Jul 2013 06:16:49 +0900 Subject: [PATCH 21/39] Cleanup --- src/js/ripple/server.js | 82 ++++----- src/js/ripple/transaction.js | 318 ++++++++++++++++++----------------- 2 files changed, 208 insertions(+), 192 deletions(-) diff --git a/src/js/ripple/server.js b/src/js/ripple/server.js index 261ec863..2263336d 100644 --- a/src/js/ripple/server.js +++ b/src/js/ripple/server.js @@ -1,24 +1,20 @@ -var EventEmitter = require('events').EventEmitter; -var util = require('util'); +var EventEmitter = require('events').EventEmitter; +var util = require('util'); var utils = require('./utils'); -//------------------------------------------------------------------------------ /** - Constructor - - Keys for cfg: - - url - - @param remote The Remote object - @param cfg Configuration parameters. -*/ + * @constructor Server + * @param remote The Remote object + * @param cfg Configuration parameters. + * + * Keys for cfg: + * url + */ var Server = function (remote, opts) { EventEmitter.call(this); - - if ('object' !== typeof opts || 'string' !== typeof opts.url) { + if (typeof opts !== 'object' || typeof opts.url !== 'string') { throw new Error('Invalid server configuration.'); } @@ -46,8 +42,6 @@ var Server = function (remote, opts) { }); }; -//------------------------------------------------------------------------------ - util.inherits(Server, EventEmitter); /** @@ -231,34 +225,44 @@ Server.prototype.request = function (request) { Server.prototype._handle_message = function (json) { var self = this; - var message = JSON.parse(json); + var message; + + try { + message = JSON.parse(json); + } catch(exception) { return; } - if (message.type === 'response') { - // A response to a request. - var request = self._requests[message.id]; + switch(message.type) { + case 'response': + // A response to a request. + var request = self._requests[message.id]; - delete self._requests[message.id]; + delete self._requests[message.id]; - if (!request) { - if (self._remote.trace) utils.logObject('server: UNEXPECTED: %s', message); - } else if ('success' === message.status) { - if (self._remote.trace) utils.logObject('server: response: %s', message); + if (!request) { + if (self._remote.trace) utils.logObject('server: UNEXPECTED: %s', message); + } else if ('success' === message.status) { + if (self._remote.trace) utils.logObject('server: response: %s', message); - request.emit('success', message.result); - self.emit('response_'+request.message.command, message.result, request, message); - self._remote.emit('response_'+request.message.command, message.result, request, message); - } else if (message.error) { - if (self._remote.trace) utils.logObject('server: error: %s', message); + request.emit('success', message.result); - request.emit('error', { - 'error' : 'remoteError', - 'error_message' : 'Remote reported an error.', - 'remote' : message - }); - } - } else if (message.type === 'serverStatus') { - // This message is only received when online. As we are connected, it is the definative final state. - self._set_state(self._is_online(message.server_status) ? 'online' : 'offline'); + [ self, self._remote ].forEach(function(emitter) { + emitter.emit('response_' + request.message.command, message.result, request, message); + }); + } else if (message.error) { + if (self._remote.trace) utils.logObject('server: error: %s', message); + + request.emit('error', { + 'error' : 'remoteError', + 'error_message' : 'Remote reported an error.', + 'remote' : message + }); + } + break; + + case 'serverStatus': + // This message is only received when online. As we are connected, it is the definative final state. + self._set_state(self._is_online(message.server_status) ? 'online' : 'offline'); + break; } }; diff --git a/src/js/ripple/transaction.js b/src/js/ripple/transaction.js index 6b2531ff..974f0501 100644 --- a/src/js/ripple/transaction.js +++ b/src/js/ripple/transaction.js @@ -72,43 +72,46 @@ var Transaction = function (remote) { this.remote = remote; this._secret = undefined; this._build_path = false; - this.tx_json = { // Transaction data. - 'Flags' : 0, // XXX Would be nice if server did not require this. + + // Transaction data. + this.tx_json = { + 'Flags' : 0, // XXX Would be nice if server did not require this. }; + this.hash = undefined; this.submit_index = undefined; // ledger_current_index was this when transaction was submited. this.state = undefined; // Under construction. this.finalized = false; this.on('success', function (message) { - if (message.engine_result) { - self.hash = message.tx_json.hash; + if (message.engine_result) { + self.hash = message.tx_json.hash; - self.set_state('client_proposed'); + self.set_state('client_proposed'); - self.emit('proposed', { - 'tx_json' : message.tx_json, - 'result' : message.engine_result, - 'result_code' : message.engine_result_code, - 'result_message' : message.engine_result_message, - 'rejected' : self.isRejected(message.engine_result_code), // If server is honest, don't expect a final if rejected. - }); - } - }); + self.emit('proposed', { + 'tx_json' : message.tx_json, + 'result' : message.engine_result, + 'result_code' : message.engine_result_code, + 'result_message' : message.engine_result_message, + 'rejected' : self.isRejected(message.engine_result_code), // If server is honest, don't expect a final if rejected. + }); + } + }); this.on('error', function (message) { - // Might want to give more detailed information. - self.set_state('remoteError'); - }); + // Might want to give more detailed information. + self.set_state('remoteError'); + }); }; util.inherits(Transaction, EventEmitter); // XXX This needs to be determined from the network. Transaction.fees = { - 'default' : Amount.from_json("10"), - 'nickname_create' : Amount.from_json("1000"), - 'offer' : Amount.from_json("10"), + 'default' : Amount.from_json('10'), + 'nickname_create' : Amount.from_json('1000'), + 'offer' : Amount.from_json('10'), }; Transaction.flags = { @@ -194,11 +197,11 @@ Transaction.prototype.set_state = function (state) { Transaction.prototype.complete = function () { var tx_json = this.tx_json; - if (undefined === tx_json.Fee && this.remote.local_fee) { - tx_json.Fee = Transaction.fees['default'].to_json(); + if (tx_json.Fee === undefined && this.remote.local_fee) { + tx_json.Fee = Transaction.fees['default'].to_json(); } - if (undefined === tx_json.SigningPubKey && (!this.remote || this.remote.local_signing)) { + if (tx_json.SigningPubKey === undefined && (!this.remote || this.remote.local_signing)) { var seed = Seed.from_json(this._secret); var key = seed.get_key(this.tx_json.Account); tx_json.SigningPubKey = key.to_hex_pub(); @@ -211,23 +214,28 @@ Transaction.prototype.serialize = function () { Transaction.prototype.signing_hash = function () { var prefix = config.testnet - ? Transaction.HASH_SIGN_TESTNET - : Transaction.HASH_SIGN; + ? Transaction.HASH_SIGN_TESTNET + : Transaction.HASH_SIGN; return SerializedObject.from_json(this.tx_json).signing_hash(prefix); }; Transaction.prototype.sign = function () { - var seed = Seed.from_json(this._secret), - hash = this.signing_hash(); - - var key = seed.get_key(this.tx_json.Account), - sig = key.sign(hash, 0), - hex = sjcl.codec.hex.fromBits(sig).toUpperCase(); + var seed = Seed.from_json(this._secret); + var hash = this.signing_hash(); + var key = seed.get_key(this.tx_json.Account); + var sig = key.sign(hash, 0); + var hex = sjcl.codec.hex.fromBits(sig).toUpperCase(); this.tx_json.TxnSignature = hex; }; +Transaction.prototype._hasTransactionListeners = function() { + return this.listeners('final').length + || this.listeners('lost').length + || this.listeners('pending').length +}; + // Submit a transaction to the network. // XXX Don't allow a submit without knowing ledger_index. // XXX Have a network canSubmit(), post events for following. @@ -248,12 +256,11 @@ Transaction.prototype.submit = function (callback) { this.callback = callback; - if ('string' !== typeof tx_json.Account) - { + if (typeof tx_json.Account !== 'string') { (this.callback || this.emit)('error', { - 'error' : 'tejInvalidAccount', - 'error_message' : 'Bad account.' - }); + 'error' : 'tejInvalidAccount', + 'error_message' : 'Bad account.' + }); return this; } @@ -261,66 +268,62 @@ Transaction.prototype.submit = function (callback) { this.complete(); - if (this.callback || this.listeners('final').length || this.listeners('lost').length || this.listeners('pending').length) { + if (this.callback || this._hasTransactionListeners()) { // There are listeners for callback, 'final', 'lost', or 'pending' arrange to emit them. this.submit_index = this.remote._ledger_current_index; // When a ledger closes, look for the result. - var on_ledger_closed = function (message) { - var ledger_hash = message.ledger_hash; - var ledger_index = message.ledger_index; - var stop = false; + function on_ledger_closed(message) { + var ledger_hash = message.ledger_hash; + var ledger_index = message.ledger_index; + var stop = false; -// XXX make sure self.hash is available. - self.remote.request_transaction_entry(self.hash) - .ledger_hash(ledger_hash) - .on('success', function (message) { - if (self.finalized) return; + // XXX make sure self.hash is available. + var transaction_entry = self.remote.request_transaction_entry(self.hash) + transaction_entry.ledger_hash(ledger_hash) + transaction_entry.on('success', function (message) { + if (self.finalized) return; + self.set_state(message.metadata.TransactionResult); + self.remote.removeListener('ledger_closed', on_ledger_closed); + self.emit('final', message); + self.finalized = true; + if (self.callback) { + self.callback(message.metadata.TransactionResult, message); + } + }); + transaction_entry.on('error', function (message) { + if (self.finalized) return; - self.set_state(message.metadata.TransactionResult); - self.remote.removeListener('ledger_closed', on_ledger_closed); - self.emit('final', message); - self.finalized = true; + if (message.error === 'remoteError' && message.remote.error === 'transactionNotFound') { + if (self.submit_index + SUBMIT_LOST < ledger_index) { + self.set_state('client_lost'); // Gave up. + self.emit('lost'); + if (self.callback) { + self.callback('tejLost', message); + } + self.remote.removeListener('ledger_closed', on_ledger_closed); + self.emit('final', message); + self.finalized = true; + } else if (self.submit_index + SUBMIT_MISSING < ledger_index) { + self.set_state('client_missing'); // We don't know what happened to transaction, still might find. + self.emit('pending'); + } else { + self.emit('pending'); + } + } + // XXX Could log other unexpectedness. + }); - if (self.callback) - self.callback(message.metadata.TransactionResult, message); - }) - .on('error', function (message) { - if (self.finalized) return; - - if ('remoteError' === message.error - && 'transactionNotFound' === message.remote.error) { - if (self.submit_index + SUBMIT_LOST < ledger_index) { - self.set_state('client_lost'); // Gave up. - self.emit('lost'); - - if (self.callback) - self.callback('tejLost', message); - - self.remote.removeListener('ledger_closed', on_ledger_closed); - self.emit('final', message); - self.finalized = true; - } - else if (self.submit_index + SUBMIT_MISSING < ledger_index) { - self.set_state('client_missing'); // We don't know what happened to transaction, still might find. - self.emit('pending'); - } - else { - self.emit('pending'); - } - } - // XXX Could log other unexpectedness. - }) - .request(); - }; + transaction_entry.request(); + }; this.remote.on('ledger_closed', on_ledger_closed); if (this.callback) { - this.on('error', function (message) { - self.callback(message.error, message); - }); + this.once('error', function (message) { + self.callback(message.error, message); + }); } } @@ -332,69 +335,70 @@ Transaction.prototype.submit = function (callback) { if (!self.tx_json.Sequence) { // Look in the last closed ledger. - this.remote.account_seq_cache(self.tx_json.Account, false) + var account_seq = this.remote.account_seq_cache(self.tx_json.Account, false) + + account_seq.on('success_account_seq_cache', function () { + // Try again. + self.submit(); + }) + + account_seq.on('error_account_seq_cache', function (message) { + // XXX Maybe be smarter about this. Don't want to trust an untrusted server for this seq number. + // Look in the current ledger. + self.remote.account_seq_cache(self.tx_json.Account, 'CURRENT') .on('success_account_seq_cache', function () { // Try again. self.submit(); }) .on('error_account_seq_cache', function (message) { - // XXX Maybe be smarter about this. Don't want to trust an untrusted server for this seq number. - - // Look in the current ledger. - self.remote.account_seq_cache(self.tx_json.Account, 'CURRENT') - .on('success_account_seq_cache', function () { - // Try again. - self.submit(); - }) - .on('error_account_seq_cache', function (message) { - // Forward errors. - self.emit('error', message); - }) - .request(); + // Forward errors. + self.emit('error', message); }) .request(); + }) + + account_seq.request(); + return this; } // If the transaction fails we want to either undo incrementing the sequence // or submit a noop transaction to consume the sequence remotely. this.on('success', function (res) { - if (!res || "string" !== typeof res.engine_result) return; + if (res && typeof res.engine_result === 'string') { + switch (res.engine_result.slice(0, 3)) { + // Synchronous local error + case 'tej': + self.remote.account_seq(self.tx_json.Account, 'REWIND'); + break; - switch (res.engine_result.slice(0, 3)) { - // Synchronous local error - case 'tej': - self.remote.account_seq(self.tx_json.Account, 'REWIND'); - break; - // XXX: What do we do in case of ter? - case 'tel': - case 'tem': - case 'tef': - // XXX Once we have a transaction submission manager class, we can - // check if there are any other transactions pending. If there are, - // we should submit a dummy transaction to ensure those - // transactions are still valid. - //var noop = self.remote.transaction().account_set(self.tx_json.Account); - //noop.submit(); + case 'ter': + // XXX: What do we do in case of ter? + break; - // XXX Hotfix. This only works if no other transactions are pending. - self.remote.account_seq(self.tx_json.Account, 'REWIND'); - break; + case 'tel': + case 'tem': + case 'tef': + // XXX Once we have a transaction submission manager class, we can + // check if there are any other transactions pending. If there are, + // we should submit a dummy transaction to ensure those + // transactions are still valid. + //var noop = self.remote.transaction().account_set(self.tx_json.Account); + //noop.submit(); + + // XXX Hotfix. This only works if no other transactions are pending. + self.remote.account_seq(self.tx_json.Account, 'REWIND'); + break; + } } }); } // Prepare request - var request = this.remote.request_submit(); - // Forward successes and errors. - request.on('success', function (message) { - self.emit('success', message); - }); - request.on('error', function (message) { - self.emit('error', message); - }); + // Forward events + request.emit = this.emit.bind(this); if (!this._secret && !this.tx_json.Signature) { this.emit('error', { @@ -440,8 +444,9 @@ Transaction.prototype.build_path = function (build) { // tag should be undefined or a 32 bit integer. // YYY Add range checking for tag. Transaction.prototype.destination_tag = function (tag) { - if (undefined !== tag) - this.tx_json.DestinationTag = tag; + if (tag !== undefined) { + this.tx_json.DestinationTag = tag; + } return this; } @@ -491,8 +496,9 @@ Transaction.prototype.secret = function (secret) { } Transaction.prototype.send_max = function (send_max) { - if (send_max) - this.tx_json.SendMax = Amount.json_rewrite(send_max); + if (send_max) { + this.tx_json.SendMax = Amount.json_rewrite(send_max); + } return this; } @@ -500,8 +506,9 @@ Transaction.prototype.send_max = function (send_max) { // tag should be undefined or a 32 bit integer. // YYY Add range checking for tag. Transaction.prototype.source_tag = function (tag) { - if (undefined !== tag) - this.tx_json.SourceTag = tag; + if (tag) { + this.tx_json.SourceTag = tag; + } return this; } @@ -510,8 +517,9 @@ Transaction.prototype.source_tag = function (tag) { Transaction.prototype.transfer_rate = function (rate) { this.tx_json.TransferRate = Number(rate); - if (this.tx_json.TransferRate < 1e9) - throw 'invalidTransferRate'; + if (this.tx_json.TransferRate < 1e9) { + throw new Error('invalidTransferRate'); + } return this; } @@ -520,24 +528,26 @@ Transaction.prototype.transfer_rate = function (rate) { // --> flags: undefined, _flag_, or [ _flags_ ] Transaction.prototype.set_flags = function (flags) { if (flags) { - var transaction_flags = Transaction.flags[this.tx_json.TransactionType]; + var transaction_flags = Transaction.flags[this.tx_json.TransactionType]; - if (undefined == this.tx_json.Flags) // We plan to not define this field on new Transaction. - this.tx_json.Flags = 0; + // We plan to not define this field on new Transaction. + if (this.tx_json.Flags === undefined) { + this.tx_json.Flags = 0; + } - var flag_set = 'object' === typeof flags ? flags : [ flags ]; + var flag_set = Array.isArray(flags) ? flags : [ flags ]; - for (var index in flag_set) { - if (!flag_set.hasOwnProperty(index)) continue; + for (var index in flag_set) { + if (!flag_set.hasOwnProperty(index)) continue; - var flag = flag_set[index]; + var flag = flag_set[index]; - if (flag in transaction_flags) { - this.tx_json.Flags += transaction_flags[flag]; - } else { - // XXX Immediately report an error or mark it. - } + if (flag in transaction_flags) { + this.tx_json.Flags += transaction_flags[flag]; + } else { + // XXX Immediately report an error or mark it. } + } } return this; @@ -598,16 +608,18 @@ Transaction.prototype.offer_create = function (src, taker_pays, taker_gets, expi this.tx_json.TakerGets = Amount.json_rewrite(taker_gets); if (this.remote.local_fee) { - this.tx_json.Fee = Transaction.fees.offer.to_json(); + this.tx_json.Fee = Transaction.fees.offer.to_json(); } - if (expiration) - this.tx_json.Expiration = Date === expiration.constructor - ? expiration.getTime() - : Number(expiration); + if (expiration) { + this.tx_json.Expiration = expiration instanceof Date + ? expiration.getTime() + : Number(expiration); + } - if (cancel_sequence) - this.tx_json.OfferSequence = Number(cancel_sequence); + if (cancel_sequence) { + this.tx_json.OfferSequence = Number(cancel_sequence); + } return this; }; @@ -664,7 +676,7 @@ Transaction.prototype.ripple_line_set = function (src, limit, quality_in, qualit this.tx_json.Account = UInt160.json_rewrite(src); // Allow limit of 0 through. - if (undefined !== limit) + if (limit !== undefined) this.tx_json.LimitAmount = Amount.json_rewrite(limit); if (quality_in) From 9ec72ee8c5a91374425ee0ce9f491dde0524e829 Mon Sep 17 00:00:00 2001 From: wltsmrz Date: Thu, 11 Jul 2013 07:00:33 +0900 Subject: [PATCH 22/39] Default maxListeners to infinity --- src/js/ripple/remote.js | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/js/ripple/remote.js b/src/js/ripple/remote.js index 0c557b02..534be189 100644 --- a/src/js/ripple/remote.js +++ b/src/js/ripple/remote.js @@ -381,11 +381,10 @@ var Remote = function (opts, trace) { }); // This is used to remove Node EventEmitter warnings - if (opts.hasOwnProperty('maxListeners')) { - opts.servers.concat(this).forEach(function(emitter) { - emitter.setMaxListeners(opts.maxListeners); - }); - } + var maxListeners = opts.maxListeners || 0; + this._servers.concat(this).forEach(function(emitter) { + emitter.setMaxListeners(maxListeners); + }); this.on('newListener', function (type, listener) { if (type === 'transaction_all') { @@ -419,9 +418,7 @@ Remote.flags = { }; Remote.from_config = function (obj, trace) { - var serverConfig = typeof obj === 'string' - ? config.servers[obj] - : obj; + var serverConfig = typeof obj === 'string' ? config.servers[obj] : obj; var remote = new Remote(serverConfig, trace); @@ -456,7 +453,7 @@ Remote.prototype.add_server = function (opts) { + (opts.port || opts.websocket_port) ; - var server = new Server(this, {url: url}) + var server = new Server(this, {url: url}); server.on('message', function (data) { self._handle_message(data); @@ -525,13 +522,12 @@ Remote.prototype.set_trace = function (trace) { */ Remote.prototype.connect = function (online) { // Downwards compatibility - if (typeof online !== 'undefined' && !online) { + if (!online && typeof online !== 'undefined') { this.disconnect(); } else { if (!this._servers.length) { throw new Error('No servers available.'); } else { - // XXX Add support for multiple servers for (var i=0; i Date: Sat, 13 Jul 2013 05:31:40 +0900 Subject: [PATCH 23/39] Fixes and cleanup for Remote, update readme --- README.md | 103 +++++++++++++++++++- src/js/ripple/orderbook.js | 178 ++++++++++++++++++----------------- src/js/ripple/remote.js | 59 +++++------- src/js/ripple/server.js | 11 ++- src/js/ripple/transaction.js | 127 +++++++++++++------------ 5 files changed, 292 insertions(+), 186 deletions(-) diff --git a/README.md b/README.md index 90a430ad..ce66b9b1 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,108 @@ Ripple JavaScript Library - ripple-lib This library can connect to the Ripple network via the WebSocket protocol and runs in Node.js as well as in the browser. -Build instructions: +##Building + * https://ripple.com/wiki/Ripple_JavaScript_library -For more information: +##See also + * https://ripple.com * https://ripple.com/wiki + +##Initializing a remote connection + +[ripple-lib.remote](https://github.com/ripple/ripple-lib/blob/develop/src/js/ripple/remote.js) is responsible for managing connections to rippled servers. + +```js +var remote = require('ripple-lib').Remote({ + servers: [ + { + host: '' + , port: 1111, + , secure: true + } + ] +}); +``` + +##Remote functions + +Each remote function returns a `Request` object. This object is an `EventEmitter`. You may listen for success or failure events from each request, or provide a callback. Example: + +```js + var request = remote.request_server_info(); + request.on('success', function(res) { + //handle success conditions + }); + request.on('error', function(err) { + //handle error conditions + }); + request.request(); +``` + +Or: + +```js + remote.request_server_info(function(err, res) { + + }); +``` + +**remote.request_server_info([callback])** + +**remote.request_ledger(ledger, [opts], [callback])** + +**remote.request_ledger_hash([callback])** + +**remote.request_ledger_header([callback])** + +**remote.request_ledger_current([callback])** + +**remote.request_ledger_entry(type, [callback])** + +**remote.request_subscribe(streams, [callback])** + +**remote.request_unsubscribe(streams, [callback])** + +**remote.request_transaction_entry(hash, [callback])** + +**remote.request_tx(hash, [callback])** + +**remote.request_account_info(accountID, [callback])** + +**remote.request_account_lines(accountID, account_index, current, [callback])** + +**remote.request_account_offers(accountID, account_index, current, [callback])** + +**remote.request_account_tx(opts, [callback])** + +**remote.request_book_offers(gets, pays, taker, [callback])** + +**remote.request_wallet_accounts(seed, [callback])** + +**remote.request_sign(secret, tx_json, [callback])** + +**remote.request_submit([callback])** + +**remote.request_account_balance(account, current, [callback])** + +**remote.request_account_flags(account, current, [callback])** + +**remote.request_owner_count(account, current, [callback])** + +**remote.request_ripple_balance(account, issuer, currency, current, [callback])** + +**remote.request_ripple_path_find(src_account, dst_account, dst_amount, src_currencies, [callback])** + +**remote.request_unl_list([callback])** + +**remote.request_unl_add(addr, comment, [callback])** + +**remote.request_unl_delete(node, [callback])** + +**remote.request_peers([callback])** + +**remote.request_connect(ip, port, [callback])** + +**remote.transaction()** diff --git a/src/js/ripple/orderbook.js b/src/js/ripple/orderbook.js index 70baa4d5..a67c60c7 100644 --- a/src/js/ripple/orderbook.js +++ b/src/js/ripple/orderbook.js @@ -9,35 +9,32 @@ // var network = require("./network.js"); var EventEmitter = require('events').EventEmitter; -var util = require('util'); +var util = require('util'); -var Amount = require('./amount').Amount; -var UInt160 = require('./uint160').UInt160; -var Currency = require('./currency').Currency; +var Amount = require('./amount').Amount; +var UInt160 = require('./uint160').UInt160; +var Currency = require('./currency').Currency; -var extend = require('extend'); +var extend = require('extend'); -var OrderBook = function (remote, - currency_gets, issuer_gets, - currency_pays, issuer_pays) { +var OrderBook = function (remote, currency_gets, issuer_gets, currency_pays, issuer_pays) { EventEmitter.call(this); - var self = this; + var self = this; - this._remote = remote; + this._remote = remote; this._currency_gets = currency_gets; - this._issuer_gets = issuer_gets; + this._issuer_gets = issuer_gets; this._currency_pays = currency_pays; - this._issuer_pays = issuer_pays; - - this._subs = 0; + this._issuer_pays = issuer_pays; + this._subs = 0; // We consider ourselves synchronized if we have a current copy of the offers, // we are online and subscribed to updates. - this._sync = false; + this._sync = false; // Offers - this._offers = []; + this._offers = []; this.on('newListener', function (type, listener) { if (OrderBook.subscribe_events.indexOf(type) !== -1) { @@ -49,10 +46,9 @@ var OrderBook = function (remote, }); this.on('removeListener', function (type, listener) { - if (OrderBook.subscribe_events.indexOf(type) !== -1) { + if (~OrderBook.subscribe_events.indexOf(type)) { self._subs -= 1; - - if (!self._subs && 'open' === self._remote._online_state) { + if (!self._subs && self._remote._connected) { self._sync = false; self._remote.request_unsubscribe() .books([self.to_json()]) @@ -86,8 +82,7 @@ OrderBook.subscribe_events = ['transaction', 'model', 'trade']; * * @private */ -OrderBook.prototype._subscribe = function () -{ +OrderBook.prototype._subscribe = function () { var self = this; self._remote.request_subscribe() .books([self.to_json()], true) @@ -95,26 +90,28 @@ OrderBook.prototype._subscribe = function () // XXX What now? }) .on('success', function (res) { - self._sync = true; + self._sync = true; self._offers = res.offers; self.emit('model', self._offers); }) .request(); }; -OrderBook.prototype.to_json = function () -{ +OrderBook.prototype.to_json = function () { var json = { - "taker_gets": { - "currency": this._currency_gets + 'taker_gets': { + 'currency': this._currency_gets }, - "taker_pays": { - "currency": this._currency_pays + 'taker_pays': { + 'currency': this._currency_pays } }; - if (this._currency_gets !== "XRP") json["taker_gets"]["issuer"] = this._issuer_gets; - if (this._currency_pays !== "XRP") json["taker_pays"]["issuer"] = this._issuer_pays; + if (this._currency_gets !== 'XRP') + json['taker_gets']['issuer'] = this._issuer_gets; + + if (this._currency_pays !== 'XRP') + json['taker_pays']['issuer'] = this._issuer_pays; return json; }; @@ -125,78 +122,88 @@ OrderBook.prototype.to_json = function () * Note: This only checks whether the parameters (currencies and issuer) are * syntactically valid. It does not check anything against the ledger. */ -OrderBook.prototype.is_valid = function () -{ +OrderBook.prototype.is_valid = function () { // XXX Should check for same currency (non-native) && same issuer return ( Currency.is_valid(this._currency_pays) && - (this._currency_pays === "XRP" || UInt160.is_valid(this._issuer_pays)) && + (this._currency_pays === 'XRP' || UInt160.is_valid(this._issuer_pays)) && Currency.is_valid(this._currency_gets) && - (this._currency_gets === "XRP" || UInt160.is_valid(this._issuer_gets)) && - !(this._currency_pays === "XRP" && this._currency_gets === "XRP") + (this._currency_gets === 'XRP' || UInt160.is_valid(this._issuer_gets)) && + !(this._currency_pays === 'XRP' && this._currency_gets === 'XRP') ); }; +OrderBook.prototype.trade = function(type) { + var tradeStr = '0' + + (this['_currency_' + type] === 'XRP') ? '' : '/' + + this['_currency_' + type ] + '/' + + this['_issuer_' + type]; + return Amount.from_json(tradeStr); +}; + /** * Notify object of a relevant transaction. * * This is only meant to be called by the Remote class. You should never have to * call this yourself. */ -OrderBook.prototype.notifyTx = function (message) -{ - var self = this; - - var changed = false; - - var trade_gets = Amount.from_json("0" + ((this._currency_gets === 'XRP') ? "" : - "/" + this._currency_gets + - "/" + this._issuer_gets)); - var trade_pays = Amount.from_json("0" + ((this._currency_pays === 'XRP') ? "" : - "/" + this._currency_pays + - "/" + this._issuer_pays)); +OrderBook.prototype.notifyTx = function (message) { + var self = this; + var changed = false; + var trade_gets = this.trade('gets'); + var trade_pays = this.trade('pays'); message.mmeta.each(function (an) { if (an.entryType !== 'Offer') return; var i, l, offer; - if (an.diffType === 'DeletedNode' || - an.diffType === 'ModifiedNode') { - for (i = 0, l = self._offers.length; i < l; i++) { - offer = self._offers[i]; - if (offer.index === an.ledgerIndex) { - if (an.diffType === 'DeletedNode') { - self._offers.splice(i, 1); + + switch(an.diffType) { + case 'DeletedNode': + case 'ModifiedNode': + var deletedNode = an.diffType === 'DeletedNode'; + + for (i = 0, l = self._offers.length; i < l; i++) { + offer = self._offers[i]; + if (offer.index === an.ledgerIndex) { + if (deletedNode) { + self._offers.splice(i, 1); + } else { + extend(offer, an.fieldsFinal); + } + changed = true; + break; } - else extend(offer, an.fieldsFinal); - changed = true; - break; } - } - // We don't want to count a OfferCancel as a trade - if (message.transaction.TransactionType === "OfferCancel") return; + // We don't want to count a OfferCancel as a trade + if (message.transaction.TransactionType === 'OfferCancel') return; - trade_gets = trade_gets.add(an.fieldsPrev.TakerGets); - trade_pays = trade_pays.add(an.fieldsPrev.TakerPays); - if (an.diffType === 'ModifiedNode') { - trade_gets = trade_gets.subtract(an.fieldsFinal.TakerGets); - trade_pays = trade_pays.subtract(an.fieldsFinal.TakerPays); - } - } else if (an.diffType === 'CreatedNode') { - var price = Amount.from_json(an.fields.TakerPays).ratio_human(an.fields.TakerGets); - for (i = 0, l = self._offers.length; i < l; i++) { - offer = self._offers[i]; - var priceItem = Amount.from_json(offer.TakerPays).ratio_human(offer.TakerGets); + trade_gets = trade_gets.add(an.fieldsPrev.TakerGets); + trade_pays = trade_pays.add(an.fieldsPrev.TakerPays); - if (price.compareTo(priceItem) <= 0) { - var obj = an.fields; - obj.index = an.ledgerIndex; - self._offers.splice(i, 0, an.fields); - changed = true; - break; + if (!deletedNode) { + trade_gets = trade_gets.subtract(an.fieldsFinal.TakerGets); + trade_pays = trade_pays.subtract(an.fieldsFinal.TakerPays); } - } + break; + + case 'CreatedNode': + var price = Amount.from_json(an.fields.TakerPays).ratio_human(an.fields.TakerGets); + + for (i = 0, l = self._offers.length; i < l; i++) { + offer = self._offers[i]; + var priceItem = Amount.from_json(offer.TakerPays).ratio_human(offer.TakerGets); + + if (price.compareTo(priceItem) <= 0) { + var obj = an.fields; + obj.index = an.ledgerIndex; + self._offers.splice(i, 0, an.fields); + changed = true; + break; + } + } + break; } }); @@ -218,17 +225,13 @@ OrderBook.prototype.notifyTx = function (message) * * If the data is available immediately, the callback may be called synchronously. */ -OrderBook.prototype.offers = function (callback) -{ +OrderBook.prototype.offers = function (callback) { var self = this; - - if ("function" === typeof callback) { + if (typeof callback === 'function') { if (this._sync) { callback(this._offers); } else { - this.once('model', function (offers) { - callback(offers); - }); + this.once('model', callback); } } return this; @@ -240,11 +243,10 @@ OrderBook.prototype.offers = function (callback) * Usually, this will just be an empty array if the order book hasn't been * loaded yet. But this accessor may be convenient in some circumstances. */ -OrderBook.prototype.offersSync = function () -{ +OrderBook.prototype.offersSync = function () { return this._offers; }; -exports.OrderBook = OrderBook; +exports.OrderBook = OrderBook; // vim:sw=2:sts=2:ts=8:et diff --git a/src/js/ripple/remote.js b/src/js/ripple/remote.js index 534be189..9ca1481d 100644 --- a/src/js/ripple/remote.js +++ b/src/js/ripple/remote.js @@ -37,16 +37,13 @@ var sjcl = require('../../../build/sjcl'); // 'remoteError' // 'remoteUnexpected' // 'remoteDisconnected' -var Request = function (remote, command) { +function Request(remote, command) { EventEmitter.call(this); - - var self = this; - this.remote = remote; this.requested = false; this.message = { - 'command' : command, - 'id' : undefined, + command : command, + id : void(0) }; }; @@ -67,6 +64,7 @@ Request.prototype.callback = function(callback, successEvent, errorEvent) { this.once(errorEvent || 'error', callback); this.request(); } + return this; }; @@ -143,8 +141,8 @@ Request.prototype.index = function (hash) { // --> seq : sequence number of transaction creating offer (integer) Request.prototype.offer_id = function (account, seq) { this.message.offer = { - 'account' : UInt160.json_rewrite(account), - 'seq' : seq + account: UInt160.json_rewrite(account), + seq: seq }; return this; @@ -228,20 +226,20 @@ Request.prototype.books = function (books, snapshot) { function processSide(side) { if (!book[side]) throw new Error('Missing '+side); - var obj = {}; - obj['currency'] = Currency.json_rewrite(book[side]['currency']); - if (obj['currency'] !== 'XRP') { - obj.issuer = UInt160.json_rewrite(book[side]['issuer']); + var obj = json[side] = { + currency: Currency.json_rewrite(book[side].currency) + }; + + if (obj.currency !== 'XRP') { + obj.issuer = UInt160.json_rewrite(book[side].issuer); } - - json[side] = obj; } processSide('taker_gets'); processSide('taker_pays'); - if (snapshot || book['snapshot']) json['snapshot'] = true; - if (book['both']) json['both'] = true; + if (snapshot) json.snapshot = true; + if (book.both) json.both = true; procBooks.push(json); } @@ -286,7 +284,7 @@ Request.prototype.books = function (books, snapshot) { @param trace */ -var Remote = function (opts, trace) { +function Remote(opts, trace) { EventEmitter.call(this); var self = this; @@ -321,7 +319,6 @@ var Remote = function (opts, trace) { this._connection_count = 0; this._connected = false; - this._last_tx = null; // Local signing implies local fees and sequences @@ -661,6 +658,7 @@ Remote.prototype._server_is_available = function (server) { Remote.prototype._next_server = function () { var result = null; + for (var i=0; i Date: Sat, 13 Jul 2013 05:39:56 +0900 Subject: [PATCH 24/39] Update readme --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ce66b9b1..09c2a9a8 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,8 @@ This library can connect to the Ripple network via the WebSocket protocol and ru [ripple-lib.remote](https://github.com/ripple/ripple-lib/blob/develop/src/js/ripple/remote.js) is responsible for managing connections to rippled servers. ```js -var remote = require('ripple-lib').Remote({ +var Remote = require('ripple-lib').Remote; +var remote = new Remote({ servers: [ { host: '' From 151a4fa1c01450a33a8c922adc0bf4dd581c12ab Mon Sep 17 00:00:00 2001 From: wltsmrz Date: Sat, 13 Jul 2013 05:46:33 +0900 Subject: [PATCH 25/39] Bump ripple-lib and dependencies versions --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index fab74725..7ab814a1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ripple-lib", - "version": "0.7.15", + "version": "0.7.16", "description": "Ripple JavaScript client library", "files": [ "src/js/ripple/*.js", @@ -13,7 +13,7 @@ }, "dependencies": { "async": "~0.2.9", - "ws": "~0.4.25", + "ws": "~0.4.27", "extend": "~1.1.3", "simple-jsonrpc": "~0.0.2" }, From 451e6f9b8623db370eb485112254ed8305f22ba3 Mon Sep 17 00:00:00 2001 From: wltsmrz Date: Wed, 17 Jul 2013 04:44:34 +0900 Subject: [PATCH 26/39] Update readme --- README.md | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 09c2a9a8..c355b0e3 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,9 @@ This library can connect to the Ripple network via the WebSocket protocol and ru ```js var Remote = require('ripple-lib').Remote; + var remote = new Remote({ + trusted: false, servers: [ { host: '' @@ -27,6 +29,8 @@ var remote = new Remote({ } ] }); + +remote.connect(); ``` ##Remote functions @@ -34,22 +38,22 @@ var remote = new Remote({ Each remote function returns a `Request` object. This object is an `EventEmitter`. You may listen for success or failure events from each request, or provide a callback. Example: ```js - var request = remote.request_server_info(); - request.on('success', function(res) { - //handle success conditions - }); - request.on('error', function(err) { - //handle error conditions - }); - request.request(); +var request = remote.request_server_info(); +request.on('success', function(res) { + //handle success conditions +}); +request.on('error', function(err) { + //handle error conditions +}); +request.request(); ``` Or: ```js - remote.request_server_info(function(err, res) { - - }); +remote.request_server_info(function(err, res) { + +}); ``` **remote.request_server_info([callback])** @@ -84,8 +88,12 @@ Or: **remote.request_wallet_accounts(seed, [callback])** ++ requires trusted remote + **remote.request_sign(secret, tx_json, [callback])** ++ requires trusted remote + **remote.request_submit([callback])** **remote.request_account_balance(account, current, [callback])** From 4eb516e072ac21c262dc4e55d70b77fb0675dfa9 Mon Sep 17 00:00:00 2001 From: wltsmrz Date: Thu, 18 Jul 2013 03:48:27 +0900 Subject: [PATCH 27/39] Add .connect callback --- src/js/ripple/remote.js | 78 +++++++++++++++++++++-------------------- 1 file changed, 40 insertions(+), 38 deletions(-) diff --git a/src/js/ripple/remote.js b/src/js/ripple/remote.js index 9ca1481d..ecbc528c 100644 --- a/src/js/ripple/remote.js +++ b/src/js/ripple/remote.js @@ -61,7 +61,7 @@ Request.prototype.request = function (remote) { Request.prototype.callback = function(callback, successEvent, errorEvent) { if (callback && typeof callback === 'function') { this.once(successEvent || 'success', callback.bind(this, null)); - this.once(errorEvent || 'error', callback); + this.once(errorEvent || 'error', callback.bind(this)); this.request(); } @@ -509,8 +509,7 @@ Remote.prototype._set_state = function (state) { }; Remote.prototype.set_trace = function (trace) { - this.trace = undefined === trace || trace; - + this.trace = trace === void(0) || trace; return this; }; @@ -519,15 +518,22 @@ Remote.prototype.set_trace = function (trace) { */ Remote.prototype.connect = function (online) { // Downwards compatibility - if (!online && typeof online !== 'undefined') { - this.disconnect(); + switch(typeof online) { + case 'undefined': + break; + case 'function': + this.once('connect', online); + break; + default: + if (!Boolean(online)) return this.disconnect() + break; + } + + if (!this._servers.length) { + throw new Error('No servers available.'); } else { - if (!this._servers.length) { - throw new Error('No servers available.'); - } else { - for (var i=0; i account_index: sub_account index (optional) @@ -1046,7 +1053,6 @@ Remote.prototype._server_prepare_subscribe = function (callback) { self._ledger_time = message.ledger_time; self._ledger_hash = message.ledger_hash; self._ledger_current_index = message.ledger_index+1; - self.emit('ledger_closed', message); } @@ -1091,8 +1097,7 @@ Remote.prototype.ledger_accept = function (callback) { Remote.prototype.request_account_balance = function (account, current, callback) { var request = this.request_ledger_entry('account_root'); - return request - .account_root(account) + return request.account_root(account) .ledger_choose(current) .on('success', function (message) { // If the caller also waits for 'success', they might run before this. @@ -1105,8 +1110,7 @@ Remote.prototype.request_account_balance = function (account, current, callback) Remote.prototype.request_account_flags = function (account, current, callback) { var request = this.request_ledger_entry('account_root'); - return request - .account_root(account) + return request.account_root(account) .ledger_choose(current) .on('success', function (message) { // If the caller also waits for 'success', they might run before this. @@ -1119,8 +1123,7 @@ Remote.prototype.request_account_flags = function (account, current, callback) { Remote.prototype.request_owner_count = function (account, current, callback) { var request = this.request_ledger_entry('account_root'); - return request - .account_root(account) + return request.account_root(account) .ledger_choose(current) .on('success', function (message) { // If the caller also waits for 'success', they might run before this. @@ -1262,8 +1265,7 @@ Remote.prototype.set_secret = function (account, secret) { Remote.prototype.request_ripple_balance = function (account, issuer, currency, current, callback) { var request = this.request_ledger_entry('ripple_state'); // YYY Could be cached per ledger. - return request - .ripple_state(account, issuer, currency) + return request.ripple_state(account, issuer, currency) .ledger_choose(current) .on('success', function (message) { var node = message.node; From b51d6771457626bc27a7ba1435fe578db1a1bb5c Mon Sep 17 00:00:00 2001 From: wltsmrz Date: Thu, 18 Jul 2013 05:29:27 +0900 Subject: [PATCH 28/39] Add request.timeout --- src/js/ripple/remote.js | 146 ++++++++++++++++++++++++++++++---------- 1 file changed, 111 insertions(+), 35 deletions(-) diff --git a/src/js/ripple/remote.js b/src/js/ripple/remote.js index ecbc528c..ce8d3277 100644 --- a/src/js/ripple/remote.js +++ b/src/js/ripple/remote.js @@ -61,13 +61,38 @@ Request.prototype.request = function (remote) { Request.prototype.callback = function(callback, successEvent, errorEvent) { if (callback && typeof callback === 'function') { this.once(successEvent || 'success', callback.bind(this, null)); - this.once(errorEvent || 'error', callback.bind(this)); + this.once(errorEvent || 'error' , callback.bind(this)); this.request(); } return this; }; +Request.prototype.timeout = function(duration) { + if (!this.requested) { + this.once('request', this.timeout.bind(this, duration)); + return; + }; + + var self = this; + var emit = this.emit; + var timed_out = false; + + var timeout = setTimeout(function() { + timed_out = true; + emit.call(self, 'error', new Error('Request timeout')); + emit.call(self, 'timeout'); + }, duration); + + this.emit = function() { + if (timed_out) return; + else clearTimeout(timeout); + emit.apply(self, arguments); + }; + + return this; +}; + Request.prototype.build_path = function (build) { if (build) { this.message.build_path = true; @@ -434,6 +459,12 @@ Remote.from_config = function (obj, trace) { return remote; }; +Remote.create_remote = function(options, callback) { + var remote = Remote.from_config(options); + remote.connect(callback); + return remote; +}; + var isTemMalformed = function (engine_result_code) { return (engine_result_code >= -299 && engine_result_code < 199); }; @@ -525,7 +556,8 @@ Remote.prototype.connect = function (online) { this.once('connect', online); break; default: - if (!Boolean(online)) return this.disconnect() + if (!Boolean(online)) + return this.disconnect() break; } @@ -692,10 +724,10 @@ Remote.prototype._get_server = function () { // Send a request. // <-> request: what to send, consumed. Remote.prototype.request = function (request) { - if (!this._connected) { - this.once('connect', this.request.bind(this, request)); - } else if (!this._servers.length) { + if (!this._servers.length) { request.emit('error', new Error('No servers available')); + } else if (!this._connected) { + this.once('connect', this.request.bind(this, request)); } else { var server = this._get_server(); if (server) { @@ -741,7 +773,9 @@ Remote.prototype.request_ledger = function (ledger, opts, callback) { break; } - return request.callback(callback); + request.callback(callback); + + return request; }; // Only for unit testing. @@ -838,7 +872,9 @@ Remote.prototype.request_ledger_entry = function (type, callback) { } }; - return request.callback(callback); + request.callback(callback); + + return request; }; // .accounts(accounts, realtime) @@ -849,7 +885,9 @@ Remote.prototype.request_subscribe = function (streams, callback) { request.message.streams = Array.isArray(streams) ? streams : [ streams ]; } - return request.callback(callback); + request.callback(callback); + + return request; }; // .accounts(accounts, realtime) @@ -860,7 +898,9 @@ Remote.prototype.request_unsubscribe = function (streams, callback) { request.message.streams = Array.isArray(streams) ? streams : [ streams ]; } - return request.callback(callback); + request.callback(callback); + + return request; }; // .ledger_choose() @@ -879,8 +919,9 @@ Remote.prototype.request_tx = function (hash, callback) { var request = new Request(this, 'tx'); request.message.transaction = hash; + request.callback(callback); - return request.callback(callback); + return request; }; Remote.prototype.request_account_info = function (accountID, callback) { @@ -907,7 +948,10 @@ Remote.prototype.request_account_lines = function (accountID, account_index, cur request.message.index = account_index; } - return request.ledger_choose(current).callback(callback); + request.ledger_choose(current); + request.callback(callback); + + return request; }; // --> account_index: sub_account index (optional) @@ -921,7 +965,10 @@ Remote.prototype.request_account_offers = function (accountID, account_index, cu request.message.index = account_index; } - return request.ledger_choose(current).callback(callback); + request.ledger_choose(current); + request.callback(callback); + + return request; }; @@ -957,7 +1004,9 @@ Remote.prototype.request_account_tx = function (obj, callback) { if (typeof obj.limit !== 'undefined') {request.message.limit = obj.limit;} } - return request.callback(callback); + request.callback(callback); + + return request; }; Remote.prototype.request_book_offers = function (gets, pays, taker, callback) { @@ -981,7 +1030,9 @@ Remote.prototype.request_book_offers = function (gets, pays, taker, callback) { request.message.taker = taker ? taker : UInt160.ACCOUNT_ONE; - return request.callback(callback); + request.callback(callback); + + return request; }; Remote.prototype.request_wallet_accounts = function (seed, callback) { @@ -1001,8 +1052,9 @@ Remote.prototype.request_sign = function (secret, tx_json, callback) { request.message.secret = secret; request.message.tx_json = tx_json; - - return request.callback(callback); + request.callback(callback); + + return request; }; // Submit a transaction. @@ -1011,7 +1063,9 @@ Remote.prototype.request_submit = function (callback) { var request = new Request(this, 'submit'); - return request.callback(callback); + request.callback(callback); + + return request; }; // @@ -1071,9 +1125,12 @@ Remote.prototype._server_prepare_subscribe = function (callback) { self.emit('prepare_subscribe', request); + request.callback(callback); + + // XXX Could give error events, maybe even time out. - return request.callback(callback); + return request; }; // For unit testing: ask the remote to accept the current ledger. @@ -1090,6 +1147,7 @@ Remote.prototype.ledger_accept = function (callback) { 'error' : 'notStandAlone' }); } + return this; }; @@ -1097,39 +1155,48 @@ Remote.prototype.ledger_accept = function (callback) { Remote.prototype.request_account_balance = function (account, current, callback) { var request = this.request_ledger_entry('account_root'); - return request.account_root(account) + request.account_root(account) .ledger_choose(current) .on('success', function (message) { // If the caller also waits for 'success', they might run before this. request.emit('account_balance', Amount.from_json(message.node.Balance)); }) - .callback(callback, 'account_balance'); + + request.callback(callback, 'account_balance'); + + return request; }; // Return a request to return the account flags. Remote.prototype.request_account_flags = function (account, current, callback) { var request = this.request_ledger_entry('account_root'); - return request.account_root(account) + request.account_root(account) .ledger_choose(current) .on('success', function (message) { // If the caller also waits for 'success', they might run before this. request.emit('account_flags', message.node.Flags); }) - .callback(callback, 'account_flags'); + + request.callback(callback, 'account_flags'); + + return request; }; // Return a request to emit the owner count. Remote.prototype.request_owner_count = function (account, current, callback) { var request = this.request_ledger_entry('account_root'); - return request.account_root(account) + request.account_root(account) .ledger_choose(current) .on('success', function (message) { // If the caller also waits for 'success', they might run before this. request.emit('owner_count', message.node.OwnerCount); }) - .callback(callback, 'owner_count'); + + request.callback(callback, 'owner_count'); + + return request; }; Remote.prototype.account = function (accountId, callback) { @@ -1145,10 +1212,6 @@ Remote.prototype.account = function (accountId, callback) { var account = this._accounts[accountId]; - if (typeof callback === 'function') { - callback(account); - } - return account; }; @@ -1238,7 +1301,9 @@ Remote.prototype.account_seq_cache = function (account, current, callback) { account_info.caching_seq_request = request; } - return request.callback(callback, 'success_account_seq_cache', 'error_account_seq_cache'); + request.callback(callback, 'success_account_seq_cache', 'error_account_seq_cache'); + + return request; }; // Mark an account's root node as dirty. @@ -1316,11 +1381,15 @@ Remote.prototype.request_ripple_path_find = function (src_account, dst_account, }); } - return request.callback(callback); + request.callback(callback); + + return request; }; Remote.prototype.request_unl_list = function (callback) { - return new Request(this, 'unl_list').callback(callback); + var request = new Request(this, 'unl_list'); + request.callback(callback); + return request; }; Remote.prototype.request_unl_add = function (addr, comment, callback) { @@ -1332,7 +1401,9 @@ Remote.prototype.request_unl_add = function (addr, comment, callback) { request.message.comment = note; } - return request.callback(callback); + request.callback(callback); + + return request; }; // --> node: | @@ -1340,12 +1411,15 @@ Remote.prototype.request_unl_delete = function (node, callback) { var request = new Request(this, 'unl_delete'); request.message.node = node; + request.callback(callback); - return request.callback(callback); + return request; }; Remote.prototype.request_peers = function (callback) { - return new Request(this, 'peers').callback(callback); + var request = new Request(this, 'peers'); + request.callback(callback); + return request; }; Remote.prototype.request_connect = function (ip, port, callback) { @@ -1357,7 +1431,9 @@ Remote.prototype.request_connect = function (ip, port, callback) { request.message.port = port; } - return request.callback(callback); + request.callback(callback); + + return request; }; Remote.prototype.transaction = function () { From 1222599c6c6b2b8839b3fcd7d71b2c7be5abd71a Mon Sep 17 00:00:00 2001 From: wltsmrz Date: Thu, 18 Jul 2013 07:23:39 +0900 Subject: [PATCH 29/39] Update readme --- README.md | 73 +++++++++++++++++++++++------------------ src/js/ripple/remote.js | 6 ---- 2 files changed, 41 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index c355b0e3..d1db3b14 100644 --- a/README.md +++ b/README.md @@ -33,9 +33,16 @@ var remote = new Remote({ remote.connect(); ``` +Once a connection is formed to any of the supplied servers, a `connect` event is emitted, indicating that the remote is ready to begin fulfilling requests. When there are no more connected servers to fulfill requests, a `disconnect` event is emitted. If you send requests before ripple-lib is connected to any servers, requests are deferred until the `connect` event is received. + +```js +var remote = new Remote({ /* options */ }).connect(); +remote.request_server_info(function(err, info) { }); // will defer until connected +``` + ##Remote functions -Each remote function returns a `Request` object. This object is an `EventEmitter`. You may listen for success or failure events from each request, or provide a callback. Example: +Each remote function returns a `Request` object. is object is an `EventEmitter`. You may listen for success or failure events from each request, or provide a callback. Example: ```js var request = remote.request_server_info(); @@ -56,64 +63,66 @@ remote.request_server_info(function(err, res) { }); ``` -**remote.request_server_info([callback])** +###remote.request_server_info([callback]) -**remote.request_ledger(ledger, [opts], [callback])** +###remote.request_ledger(ledger, [opts], [callback]) -**remote.request_ledger_hash([callback])** +###remote.request_ledger_hash([callback]) -**remote.request_ledger_header([callback])** +###remote.request_ledger_header([callback]) -**remote.request_ledger_current([callback])** +###remote.request_ledger_current([callback]) -**remote.request_ledger_entry(type, [callback])** +###remote.request_ledger_entry(type, [callback]) -**remote.request_subscribe(streams, [callback])** +###remote.request_subscribe(streams, [callback]) -**remote.request_unsubscribe(streams, [callback])** +###remote.request_unsubscribe(streams, [callback]) -**remote.request_transaction_entry(hash, [callback])** +###remote.request_transaction_entry(hash, [callback]) -**remote.request_tx(hash, [callback])** +###remote.request_tx(hash, [callback]) -**remote.request_account_info(accountID, [callback])** +###remote.request_account_info(accountID, [callback]) -**remote.request_account_lines(accountID, account_index, current, [callback])** +###remote.request_account_lines(accountID, account_index, current, [callback]) -**remote.request_account_offers(accountID, account_index, current, [callback])** +###remote.request_account_offers(accountID, account_index, current, [callback]) -**remote.request_account_tx(opts, [callback])** +###remote.request_account_tx(opts, [callback]) -**remote.request_book_offers(gets, pays, taker, [callback])** +###remote.request_book_offers(gets, pays, taker, [callback]) -**remote.request_wallet_accounts(seed, [callback])** +###remote.request_wallet_accounts(seed, [callback]) -+ requires trusted remote ++ requires trusted ###remote -**remote.request_sign(secret, tx_json, [callback])** +###remote.request_sign(secret, tx_json, [callback]) -+ requires trusted remote ++ requires trusted ###remote -**remote.request_submit([callback])** +###remote.request_submit([callback]) -**remote.request_account_balance(account, current, [callback])** +###remote.request_account_balance(account, current, [callback]) -**remote.request_account_flags(account, current, [callback])** +###remote.request_account_flags(account, current, [callback]) -**remote.request_owner_count(account, current, [callback])** +###remote.request_owner_count(account, current, [callback]) -**remote.request_ripple_balance(account, issuer, currency, current, [callback])** +###remote.request_ripple_balance(account, issuer, currency, current, [callback]) -**remote.request_ripple_path_find(src_account, dst_account, dst_amount, src_currencies, [callback])** +###remote.request_ripple_path_find(src_account, dst_account, dst_amount, src_currencies, [callback]) -**remote.request_unl_list([callback])** +###remote.request_unl_list([callback]) -**remote.request_unl_add(addr, comment, [callback])** +###remote.request_unl_add(addr, comment, [callback]) -**remote.request_unl_delete(node, [callback])** +###remote.request_unl_delete(node, [callback]) -**remote.request_peers([callback])** +###remote.request_peers([callback]) -**remote.request_connect(ip, port, [callback])** +###remote.request_connect(ip, port, [callback]) -**remote.transaction()** +###remote.transaction() + ++ returns a [Transaction](https://github.com/ripple/ripple-lib/blob/develop/src/js/ripple/transaction.js) object diff --git a/src/js/ripple/remote.js b/src/js/ripple/remote.js index ce8d3277..827b95ca 100644 --- a/src/js/ripple/remote.js +++ b/src/js/ripple/remote.js @@ -1059,12 +1059,8 @@ Remote.prototype.request_sign = function (secret, tx_json, callback) { // Submit a transaction. Remote.prototype.request_submit = function (callback) { - var self = this; - var request = new Request(this, 'submit'); - request.callback(callback); - return request; }; @@ -1409,10 +1405,8 @@ Remote.prototype.request_unl_add = function (addr, comment, callback) { // --> node: | Remote.prototype.request_unl_delete = function (node, callback) { var request = new Request(this, 'unl_delete'); - request.message.node = node; request.callback(callback); - return request; }; From 913df130e884834e3fe532500ec0a6357fc0c239 Mon Sep 17 00:00:00 2001 From: wltsmrz Date: Thu, 18 Jul 2013 07:24:23 +0900 Subject: [PATCH 30/39] Update readme --- README.md | 58 +++++++++++++++++++++++++++---------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index d1db3b14..bc6ff0a6 100644 --- a/README.md +++ b/README.md @@ -63,66 +63,66 @@ remote.request_server_info(function(err, res) { }); ``` -###remote.request_server_info([callback]) +###request_server_info([callback]) -###remote.request_ledger(ledger, [opts], [callback]) +###request_ledger(ledger, [opts], [callback]) -###remote.request_ledger_hash([callback]) +###request_ledger_hash([callback]) -###remote.request_ledger_header([callback]) +###request_ledger_header([callback]) -###remote.request_ledger_current([callback]) +###request_ledger_current([callback]) -###remote.request_ledger_entry(type, [callback]) +###request_ledger_entry(type, [callback]) -###remote.request_subscribe(streams, [callback]) +###request_subscribe(streams, [callback]) -###remote.request_unsubscribe(streams, [callback]) +###request_unsubscribe(streams, [callback]) -###remote.request_transaction_entry(hash, [callback]) +###request_transaction_entry(hash, [callback]) -###remote.request_tx(hash, [callback]) +###request_tx(hash, [callback]) -###remote.request_account_info(accountID, [callback]) +###request_account_info(accountID, [callback]) -###remote.request_account_lines(accountID, account_index, current, [callback]) +###request_account_lines(accountID, account_index, current, [callback]) -###remote.request_account_offers(accountID, account_index, current, [callback]) +###request_account_offers(accountID, account_index, current, [callback]) -###remote.request_account_tx(opts, [callback]) +###request_account_tx(opts, [callback]) -###remote.request_book_offers(gets, pays, taker, [callback]) +###request_book_offers(gets, pays, taker, [callback]) -###remote.request_wallet_accounts(seed, [callback]) +###request_wallet_accounts(seed, [callback]) + requires trusted ###remote -###remote.request_sign(secret, tx_json, [callback]) +###request_sign(secret, tx_json, [callback]) + requires trusted ###remote -###remote.request_submit([callback]) +###request_submit([callback]) -###remote.request_account_balance(account, current, [callback]) +###request_account_balance(account, current, [callback]) -###remote.request_account_flags(account, current, [callback]) +###request_account_flags(account, current, [callback]) -###remote.request_owner_count(account, current, [callback]) +###request_owner_count(account, current, [callback]) -###remote.request_ripple_balance(account, issuer, currency, current, [callback]) +###request_ripple_balance(account, issuer, currency, current, [callback]) -###remote.request_ripple_path_find(src_account, dst_account, dst_amount, src_currencies, [callback]) +###request_ripple_path_find(src_account, dst_account, dst_amount, src_currencies, [callback]) -###remote.request_unl_list([callback]) +###request_unl_list([callback]) -###remote.request_unl_add(addr, comment, [callback]) +###request_unl_add(addr, comment, [callback]) -###remote.request_unl_delete(node, [callback]) +###request_unl_delete(node, [callback]) -###remote.request_peers([callback]) +###request_peers([callback]) -###remote.request_connect(ip, port, [callback]) +###request_connect(ip, port, [callback]) -###remote.transaction() +###transaction() + returns a [Transaction](https://github.com/ripple/ripple-lib/blob/develop/src/js/ripple/transaction.js) object From 9a6d4adfbe0784295ade58d811a4905b389d693f Mon Sep 17 00:00:00 2001 From: wltsmrz Date: Thu, 18 Jul 2013 07:26:34 +0900 Subject: [PATCH 31/39] Update readme --- README.md | 67 +++++++++++++++++++++++++------------------------------ 1 file changed, 31 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index bc6ff0a6..9efbca86 100644 --- a/README.md +++ b/README.md @@ -3,12 +3,7 @@ Ripple JavaScript Library - ripple-lib This library can connect to the Ripple network via the WebSocket protocol and runs in Node.js as well as in the browser. -##Building - * https://ripple.com/wiki/Ripple_JavaScript_library - -##See also - * https://ripple.com * https://ripple.com/wiki @@ -63,66 +58,66 @@ remote.request_server_info(function(err, res) { }); ``` -###request_server_info([callback]) +**request_server_info([callback])** -###request_ledger(ledger, [opts], [callback]) +**request_ledger(ledger, [opts], [callback])** -###request_ledger_hash([callback]) +**request_ledger_hash([callback])** -###request_ledger_header([callback]) +**request_ledger_header([callback])** -###request_ledger_current([callback]) +**request_ledger_current([callback])** -###request_ledger_entry(type, [callback]) +**request_ledger_entry(type, [callback])** -###request_subscribe(streams, [callback]) +**request_subscribe(streams, [callback])** -###request_unsubscribe(streams, [callback]) +**request_unsubscribe(streams, [callback])** -###request_transaction_entry(hash, [callback]) +**request_transaction_entry(hash, [callback])** -###request_tx(hash, [callback]) +**request_tx(hash, [callback])** -###request_account_info(accountID, [callback]) +**request_account_info(accountID, [callback])** -###request_account_lines(accountID, account_index, current, [callback]) +**request_account_lines(accountID, account_index, current, [callback])** -###request_account_offers(accountID, account_index, current, [callback]) +**request_account_offers(accountID, account_index, current, [callback])** -###request_account_tx(opts, [callback]) +**request_account_tx(opts, [callback])** -###request_book_offers(gets, pays, taker, [callback]) +**request_book_offers(gets, pays, taker, [callback])** -###request_wallet_accounts(seed, [callback]) +**request_wallet_accounts(seed, [callback])** -+ requires trusted ###remote ++ requires trusted **remote -###request_sign(secret, tx_json, [callback]) +**request_sign(secret, tx_json, [callback])** -+ requires trusted ###remote ++ requires trusted **remote -###request_submit([callback]) +**request_submit([callback])** -###request_account_balance(account, current, [callback]) +**request_account_balance(account, current, [callback])** -###request_account_flags(account, current, [callback]) +**request_account_flags(account, current, [callback])** -###request_owner_count(account, current, [callback]) +**request_owner_count(account, current, [callback])** -###request_ripple_balance(account, issuer, currency, current, [callback]) +**request_ripple_balance(account, issuer, currency, current, [callback])** -###request_ripple_path_find(src_account, dst_account, dst_amount, src_currencies, [callback]) +**request_ripple_path_find(src_account, dst_account, dst_amount, src_currencies, [callback])** -###request_unl_list([callback]) +**request_unl_list([callback])** -###request_unl_add(addr, comment, [callback]) +**request_unl_add(addr, comment, [callback])** -###request_unl_delete(node, [callback]) +**request_unl_delete(node, [callback])** -###request_peers([callback]) +**request_peers([callback])** -###request_connect(ip, port, [callback]) +**request_connect(ip, port, [callback])** -###transaction() +**transaction()** + returns a [Transaction](https://github.com/ripple/ripple-lib/blob/develop/src/js/ripple/transaction.js) object From 5b11795dc36bef15b0dae0f17da25d93003dd227 Mon Sep 17 00:00:00 2001 From: wltsmrz Date: Thu, 18 Jul 2013 07:37:06 +0900 Subject: [PATCH 32/39] Bump devDependencies --- package.json | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 7ab814a1..20d7d56c 100644 --- a/package.json +++ b/package.json @@ -15,15 +15,16 @@ "async": "~0.2.9", "ws": "~0.4.27", "extend": "~1.1.3", - "simple-jsonrpc": "~0.0.2" + "simple-jsonrpc": "~0.0.2", + "jshint-loader": "~0.5.0" }, "devDependencies": { "grunt": "~0.4.1", "grunt-contrib-concat": "~0.3.0", - "grunt-contrib-watch": "~0.4.0", - "grunt-webpack": "~0.10.2", - "grunt-dox": "~0.4.1", - "buster": "~0.6.2" + "grunt-contrib-watch": "~0.4.4", + "grunt-webpack": "~0.10.5", + "grunt-dox": "~0.5.0", + "buster": "~0.6.12" }, "scripts": { "test": "node_modules/buster/bin/buster test" From 06c3a879291af51b58e5fc1a75c651fd01df2084 Mon Sep 17 00:00:00 2001 From: wltsmrz Date: Thu, 18 Jul 2013 07:58:55 +0900 Subject: [PATCH 33/39] Add npmignore --- .npmignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .npmignore diff --git a/.npmignore b/.npmignore new file mode 100644 index 00000000..da0f9eb2 --- /dev/null +++ b/.npmignore @@ -0,0 +1,2 @@ +build +deploy From c70e8dcf0170bf6313bf84fc5974846327333921 Mon Sep 17 00:00:00 2001 From: wltsmrz Date: Fri, 19 Jul 2013 04:06:45 +0900 Subject: [PATCH 34/39] Do not emit error on timeout, allow timeout callback --- src/js/ripple/remote.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/js/ripple/remote.js b/src/js/ripple/remote.js index 827b95ca..8e1ded41 100644 --- a/src/js/ripple/remote.js +++ b/src/js/ripple/remote.js @@ -68,9 +68,9 @@ Request.prototype.callback = function(callback, successEvent, errorEvent) { return this; }; -Request.prototype.timeout = function(duration) { +Request.prototype.timeout = function(duration, callback) { if (!this.requested) { - this.once('request', this.timeout.bind(this, duration)); + this.once('request', this.timeout.bind(this, duration, callback)); return; }; @@ -80,7 +80,7 @@ Request.prototype.timeout = function(duration) { var timeout = setTimeout(function() { timed_out = true; - emit.call(self, 'error', new Error('Request timeout')); + if (typeof callback === 'function') callback(); emit.call(self, 'timeout'); }, duration); From 0d00a30d19f7245ccfd1520ce55a5a16358a908c Mon Sep 17 00:00:00 2001 From: Stefan Thomas Date: Tue, 23 Jul 2013 19:50:09 -0700 Subject: [PATCH 35/39] Add ability to limit number of significant digits in Amount#to_human. --- src/js/ripple/amount.js | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/js/ripple/amount.js b/src/js/ripple/amount.js index b86b5c5d..5e09e3eb 100644 --- a/src/js/ripple/amount.js +++ b/src/js/ripple/amount.js @@ -882,6 +882,8 @@ Amount.prototype.to_text = function (allow_nan) { * @param opts.min_precision {Number} Min. number of digits after dec. point. * @param opts.skip_empty_fraction {Boolean} Don't show fraction if it is zero, * even if min_precision is set. + * @param opts.max_sig_digits {Number} Maximum number of significant digits. + * Will cut fractional part, but never integer part. * @param opts.group_sep {Boolean|String} Whether to show a separator every n * digits, if a string, that value will be used as the separator. Default: "," * @param opts.group_width {Number} How many numbers will be grouped together, @@ -914,10 +916,38 @@ Amount.prototype.to_human = function (opts) fraction_part = fraction_part.replace(/0*$/, ''); if (fraction_part.length || !opts.skip_empty_fraction) { + // Enforce the maximum number of decimal digits (precision) if ("number" === typeof opts.precision) { fraction_part = fraction_part.slice(0, opts.precision); } + // Limit the number of significant digits (max_sig_digits) + if ("number" === typeof opts.max_sig_digits) { + // First, we count the significant digits we have. + // A zero in the integer part does not count. + var int_is_zero = +int_part === 0; + var digits = int_is_zero ? 0 : int_part.length; + + // Don't count leading zeros in the fractional part if the integer part is + // zero. + var sig_frac = int_is_zero ? fraction_part.replace(/^0*/, '') : fraction_part; + digits += sig_frac.length; + + // Now we calculate where we are compared to the maximum + var rounding = digits - opts.max_sig_digits; + + // If we're under the maximum we want to cut no (=0) digits + rounding = Math.max(rounding, 0); + + // If we're over the maximum we still only want to cut digits from the + // fractional part, not from the integer part. + rounding = Math.min(rounding, fraction_part.length); + + // Now we cut `rounding` digits off the right. + if (rounding > 0) fraction_part = fraction_part.slice(0, -rounding); + } + + // Enforce the minimum number of decimal digits (min_precision) if ("number" === typeof opts.min_precision) { while (fraction_part.length < opts.min_precision) { fraction_part += "0"; From 6bc2493d8d641a0e06851571f65101e40bcbc678 Mon Sep 17 00:00:00 2001 From: Stefan Thomas Date: Tue, 23 Jul 2013 20:59:14 -0700 Subject: [PATCH 36/39] Add fee calculation based on load. --- src/js/ripple/remote.js | 34 ++++++++++++++++++++++++++++++++++ src/js/ripple/transaction.js | 32 ++++++++++++++++++++------------ 2 files changed, 54 insertions(+), 12 deletions(-) diff --git a/src/js/ripple/remote.js b/src/js/ripple/remote.js index 8e1ded41..7b95f8d3 100644 --- a/src/js/ripple/remote.js +++ b/src/js/ripple/remote.js @@ -288,6 +288,7 @@ Request.prototype.books = function (books, snapshot) { websocket_ssl trace maxListeners + fee_cushion : Extra fee multiplier to account for async fee changes. Events: 'connect' @@ -319,6 +320,8 @@ function Remote(opts, trace) { this.local_fee = opts.local_fee; // Locally set fees this.local_signing = (typeof opts.local_signing === 'undefined') ? true : opts.local_signing; + this.fee_cushion = (typeof opts.fee_cushion === 'undefined') + ? 1.05 : opts.fee_cushion; this.id = 0; this.trace = opts.trace || trace; @@ -1434,6 +1437,37 @@ Remote.prototype.transaction = function () { return new Transaction(this); }; +/** + * Get the current recommended transaction fee unit. + * + * Multiply this value with the number of fee units in order to calculate the + * recommended fee for the transaction you are trying to submit. + * + * @return {Number} Recommended amount for one fee unit. + */ +Remote.prototype.fee_tx = function () +{ + var fee_unit = this._fee_base / this._fee_ref; + + // Apply load fees + fee_unit *= this._load_factor / this._load_base; + + // Apply fee cushion (a safety margin in case fees rise since we were last updated + fee_unit *= this.fee_cushion; + + return fee_unit; +}; + +/** + * Get the current recommended reserve base. + * + * Returns the base reserve with load fees and safety margin applied. + */ +Remote.prototype.fee_reserve_base = function () +{ + // XXX +}; + exports.Remote = Remote; // vim:sw=2:sts=2:ts=8:et diff --git a/src/js/ripple/transaction.js b/src/js/ripple/transaction.js index 66b2ba14..f3910dbc 100644 --- a/src/js/ripple/transaction.js +++ b/src/js/ripple/transaction.js @@ -109,9 +109,7 @@ util.inherits(Transaction, EventEmitter); // XXX This needs to be determined from the network. Transaction.fees = { - 'default' : Amount.from_json('10'), - 'nickname_create' : Amount.from_json('1000'), - 'offer' : Amount.from_json('10'), + 'default' : 10, }; Transaction.flags = { @@ -197,11 +195,11 @@ Transaction.prototype.set_state = function (state) { Transaction.prototype.complete = function () { var tx_json = this.tx_json; - if (tx_json.Fee === undefined && this.remote.local_fee) { - tx_json.Fee = Transaction.fees['default'].to_json(); + if ("undefined" === typeof tx_json.Fee && this.remote.local_fee) { + this.tx_json.Fee = "" + Math.ceil(this.remote.fee_tx() * this.fee_units()); } - if (tx_json.SigningPubKey === undefined && (!this.remote || this.remote.local_signing)) { + if ("undefined" === typeof tx_json.SigningPubKey && (!this.remote || this.remote.local_signing)) { var seed = Seed.from_json(this._secret); var key = seed.get_key(this.tx_json.Account); tx_json.SigningPubKey = key.to_hex_pub(); @@ -257,8 +255,8 @@ Transaction.prototype.submit = function (callback) { var self = this; var tx_json = this.tx_json; - this.callback = typeof callback === 'function' - ? callback + this.callback = typeof callback === 'function' + ? callback : function(){}; function finish(err) { @@ -616,10 +614,6 @@ Transaction.prototype.offer_create = function (src, taker_pays, taker_gets, expi this.tx_json.TakerPays = Amount.json_rewrite(taker_pays); this.tx_json.TakerGets = Amount.json_rewrite(taker_gets); - if (this.remote.local_fee) { - this.tx_json.Fee = Transaction.fees.offer.to_json(); - } - if (expiration) { this.tx_json.Expiration = expiration instanceof Date ? expiration.getTime() @@ -710,6 +704,20 @@ Transaction.prototype.wallet_add = function (src, amount, authorized_key, public return this; }; +/** + * Returns the number of fee units this transaction will cost. + * + * Each Ripple transaction based on its type and makeup costs a certain number + * of fee units. The fee units are calculated on a per-server basis based on the + * current load on both the network and the server. + * + * @see https://ripple.com/wiki/Transaction_Fee + */ +Transaction.prototype.fee_units = function () +{ + return Transaction.fees["default"]; +}; + exports.Transaction = Transaction; // vim:sw=2:sts=2:ts=8:et From 3e1e62c03add25e525faaedbd17d753c47d3e610 Mon Sep 17 00:00:00 2001 From: Stefan Thomas Date: Tue, 23 Jul 2013 21:26:02 -0700 Subject: [PATCH 37/39] Dirty fix: Don't request transaction_entry on finalized transactions. --- src/js/ripple/transaction.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/js/ripple/transaction.js b/src/js/ripple/transaction.js index f3910dbc..8cabe68e 100644 --- a/src/js/ripple/transaction.js +++ b/src/js/ripple/transaction.js @@ -284,6 +284,8 @@ Transaction.prototype.submit = function (callback) { // When a ledger closes, look for the result. function on_ledger_closed(message) { + if (self.finalized) return; + var ledger_hash = message.ledger_hash; var ledger_index = message.ledger_index; var stop = false; From 1f40bb7ea8c2ac5139464304daf7c131120f9e22 Mon Sep 17 00:00:00 2001 From: Stefan Thomas Date: Tue, 23 Jul 2013 21:30:18 -0700 Subject: [PATCH 38/39] Increase default fee cushion. --- src/js/ripple/remote.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/ripple/remote.js b/src/js/ripple/remote.js index 7b95f8d3..1ecb16dd 100644 --- a/src/js/ripple/remote.js +++ b/src/js/ripple/remote.js @@ -321,7 +321,7 @@ function Remote(opts, trace) { this.local_signing = (typeof opts.local_signing === 'undefined') ? true : opts.local_signing; this.fee_cushion = (typeof opts.fee_cushion === 'undefined') - ? 1.05 : opts.fee_cushion; + ? 1.5 : opts.fee_cushion; this.id = 0; this.trace = opts.trace || trace; From d08e06ad53108e72db46fd6d29b3dfffcb4b13c4 Mon Sep 17 00:00:00 2001 From: Stefan Thomas Date: Tue, 23 Jul 2013 21:36:31 -0700 Subject: [PATCH 39/39] Bump version to 0.7.17. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 20d7d56c..35b59005 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ripple-lib", - "version": "0.7.16", + "version": "0.7.17", "description": "Ripple JavaScript client library", "files": [ "src/js/ripple/*.js",