From 8feaae0e2dfb6d81a86247f4991b1eb98df72a2e Mon Sep 17 00:00:00 2001 From: wltsmrz Date: Sat, 8 Jun 2013 13:48:20 +0900 Subject: [PATCH 01/15] Cleanup --- src/js/ripple/remote.js | 251 ++++++++++++++++++++-------------------- 1 file changed, 123 insertions(+), 128 deletions(-) diff --git a/src/js/ripple/remote.js b/src/js/ripple/remote.js index 61fbc43c..2e8676ef 100644 --- a/src/js/ripple/remote.js +++ b/src/js/ripple/remote.js @@ -32,11 +32,11 @@ var config = require('./config'); var sjcl = require('../../../build/sjcl'); // Request events emitted: -// 'success' : Request successful. -// 'error' : Request failed. -// 'remoteError' -// 'remoteUnexpected' -// 'remoteDisconnected' +// 'success' : Request successful. +// 'error' : Request failed. +// 'remoteError' +// 'remoteUnexpected' +// 'remoteDisconnected' var Request = function (remote, command) { EventEmitter.call(this); @@ -62,18 +62,17 @@ Request.prototype.request = function (remote) { }; Request.prototype.build_path = function (build) { - if (build) + if (build) { this.message.build_path = true; + } return this; }; Request.prototype.ledger_choose = function (current) { - if (current) - { + if (current) { this.message.ledger_index = this.remote._ledger_current_index; - } - else { + } else { this.message.ledger_hash = this.remote._ledger_hash; } @@ -98,20 +97,19 @@ Request.prototype.ledger_index = function (ledger_index) { }; Request.prototype.ledger_select = function (ledger_spec) { - if (ledger_spec === 'current') { - this.message.ledger_index = ledger_spec; - - } else if (ledger_spec === 'closed') { - this.message.ledger_index = ledger_spec; - - } else if (ledger_spec === 'verified') { - this.message.ledger_index = ledger_spec; - - } else if (String(ledger_spec).length > 12) { // XXX Better test needed - this.message.ledger_hash = ledger_spec; - - } else { - this.message.ledger_index = ledger_spec; + switch (ledger_spec) { + case 'current': + case 'closed': + case 'verified': + this.message.ledger_index = ledger_spec; + break; + default: + // XXX Better test needed + if (String(ledger_spec).length > 12) { + this.message.ledger_hash = ledger_spec; + } else { + this.message.ledger_index = ledger_spec; + } } return this; @@ -149,8 +147,9 @@ Request.prototype.offer_index = function (index) { }; Request.prototype.secret = function (s) { - if (s) + if (s) { this.message.secret = s; + } return this; }; @@ -175,18 +174,18 @@ Request.prototype.tx_blob = function (j) { Request.prototype.ripple_state = function (account, issuer, currency) { this.message.ripple_state = { - 'accounts' : [ - UInt160.json_rewrite(account), - UInt160.json_rewrite(issuer) - ], - 'currency' : currency - }; + 'accounts' : [ + UInt160.json_rewrite(account), + UInt160.json_rewrite(issuer) + ], + 'currency' : currency + }; return this; }; Request.prototype.accounts = function (accounts, realtime) { - if ("object" !== typeof accounts) { + if (typeof accounts !== 'object') { accounts = [accounts]; } @@ -215,26 +214,27 @@ Request.prototype.books = function (books, snapshot) { var book = books[i]; var json = {}; - function process(side) { - if (!book[side]) throw new Error("Missing "+side); + 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"]); + obj['currency'] = Currency.json_rewrite(book[side]['currency']); + if (obj['currency'] !== 'XRP') { + obj.issuer = UInt160.json_rewrite(book[side]['issuer']); } json[side] = obj; } - process("taker_gets"); - process("taker_pays"); + processSide('taker_gets'); + processSide('taker_pays'); - if (snapshot || book["snapshot"]) json["snapshot"] = true; - if (book["both"]) json["both"] = true; + if (snapshot || book['snapshot']) json['snapshot'] = true; + if (book['both']) json['both'] = true; procBooks.push(json); } + this.message.books = procBooks; return this; @@ -286,29 +286,29 @@ var Remote = function (opts, trace) { 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.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 = undefined; - this._ledger_hash = undefined; - this._ledger_time = undefined; - this._stand_alone = undefined; - this._testnet = undefined; + 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 = undefined; - this.retry = undefined; + this.retry_timer = void(0); + this.retry = void(0); this._load_base = 256; this._load_factor = 1.0; - this._fee_ref = undefined; - this._fee_base = undefined; - this._reserve_base = undefined; - this._reserve_inc = undefined; + 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._last_tx = null; @@ -329,7 +329,7 @@ var Remote = function (opts, trace) { // account : { seq : __ } - }; + }; // Hash map of Account objects by AccountId. this._accounts = {}; @@ -353,15 +353,13 @@ 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 url = (this.websocket_ssl ? 'wss://' : 'ws://') + + this.websocket_ip + ':' + this.websocket_port; var server = new Server (this, {url: url}) - if ('maxListeners' in opts) - { + if ('maxListeners' in opts) { // This is used to remove Emitter warnings - // server.setMaxListeners (opts.maxListeners) this.setMaxListeners (opts.maxListeners) } @@ -369,29 +367,25 @@ var Remote = function (opts, trace) { this.add_server(server) this.on('newListener', function (type, listener) { - if ('transaction_all' === type) - { - if (!self._transaction_subs && 'open' === self._online_state) - { - self.request_subscribe([ 'transactions' ]) - .request(); - } - self._transaction_subs += 1; + if ('transaction_all' === type) { + if (!self._transaction_subs && 'open' === self._online_state) { + self.request_subscribe([ 'transactions' ]) + .request(); } - }); + self._transaction_subs += 1; + } + }); this.on('removeListener', function (type, listener) { - if ('transaction_all' === type) - { - self._transaction_subs -= 1; + 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 && 'open' === self._online_state) { + self.request_unsubscribe([ 'transactions' ]) + .request(); } - }); + } + }); }; util.inherits(Remote, EventEmitter); @@ -413,7 +407,7 @@ Remote.from_config = function (obj, trace) { for (var account in config.accounts) { var accountInfo = config.accounts[account]; - if ("object" === typeof accountInfo) { + if ('object' === typeof accountInfo) { if (accountInfo.secret) { // Index by nickname ... remote.set_secret(account, accountInfo.secret); @@ -448,7 +442,9 @@ Remote.prototype.add_server = function (server) { server.on('disconnect', function () { self._connection_count--; - if (!self._connection_count) self._set_state('offline'); + if (!self._connection_count) { + self._set_state('offline'); + } }); this._servers.push(server); @@ -463,7 +459,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; @@ -497,15 +493,17 @@ Remote.prototype.set_trace = function (trace) { */ Remote.prototype.connect = function (online) { // Downwards compatibility - if ("undefined" !== typeof online && !online) { + if ('undefined' !== typeof online && !online) { return this.disconnect(); } if (!this._servers.length) { - throw new Error("No servers available."); + throw new Error('No servers available.'); } else { // XXX Add support for multiple servers - this._servers[0].connect(); + 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."); + throw new Error('No servers available.'); } else { // XXX Add support for multiple servers this._servers[0].request(request); @@ -646,10 +643,9 @@ Remote.prototype.request_ledger = function (ledger, opts) { var request = new Request(this, 'ledger'); - if (ledger) - { + if (ledger) { // DEPRECATED: use .ledger_hash() or .ledger_index() - console.log("request_ledger: ledger parameter is deprecated"); + console.log('request_ledger: ledger parameter is deprecated'); request.message.ledger = ledger; } @@ -667,9 +663,8 @@ Remote.prototype.request_ledger = function (ledger, opts) { request.message.accounts = true; } // DEPRECATED: - else if (opts) - { - console.log("request_ledger: full parameter is deprecated"); + else if (opts) { + console.log('request_ledger: full parameter is deprecated'); request.message.full = true; } @@ -729,8 +724,7 @@ Remote.prototype.request_ledger_entry = function (type) { else if ('account_root' === type) { var cache = self.ledgers.current.account_root; - if (!cache) - { + if (!cache) { cache = self.ledgers.current.account_root = {}; } @@ -740,9 +734,9 @@ Remote.prototype.request_ledger_entry = function (type) { // Emulate fetch of ledger entry. // console.log('request_ledger_entry: emulating'); request.emit('success', { - // YYY Missing lots of fields. - 'node' : node, - }); + // YYY Missing lots of fields. + 'node' : node, + }); bDefault = false; } @@ -753,10 +747,10 @@ Remote.prototype.request_ledger_entry = function (type) { switch (type) { case 'account_root': request.on('success', function (message) { - // Cache node. - // console.log('request_ledger_entry: caching'); - self.ledgers.current.account_root[message.node.Account] = message.node; - }); + // Cache node. + // console.log('request_ledger_entry: caching'); + self.ledgers.current.account_root[message.node.Account] = message.node; + }); break; default: @@ -781,7 +775,7 @@ Remote.prototype.request_subscribe = function (streams) { var request = new Request(this, 'subscribe'); if (streams) { - if ("object" !== typeof streams) { + if ('object' !== typeof streams) { streams = [streams]; } request.message.streams = streams; @@ -795,7 +789,7 @@ Remote.prototype.request_unsubscribe = function (streams) { var request = new Request(this, 'unsubscribe'); if (streams) { - if ("object" !== typeof streams) { + if ('object' !== typeof streams) { streams = [streams]; } request.message.streams = streams; @@ -842,11 +836,11 @@ Remote.prototype.request_account_lines = function (accountID, account_index, cur request.message.account = UInt160.json_rewrite(accountID); - if (account_index) + if (account_index) { request.message.index = account_index; + } - return request - .ledger_choose(current); + return request.ledger_choose(current); }; // --> account_index: sub_account index (optional) @@ -856,11 +850,11 @@ Remote.prototype.request_account_offers = function (accountID, account_index, cu request.message.account = UInt160.json_rewrite(accountID); - if (account_index) + if (account_index) { request.message.index = account_index; + } - return request - .ledger_choose(current); + return request.ledger_choose(current); }; @@ -887,14 +881,15 @@ 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 ('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;} } + return request; }; @@ -979,7 +974,7 @@ Remote.prototype._server_prepare_subscribe = function () self._stand_alone = !!message.stand_alone; self._testnet = !!message.testnet; - if ("string" === typeof message.random) { + if ('string' === typeof message.random) { var rand = message.random.match(/[0-9A-F]{8}/ig); while (rand && rand.length) sjcl.random.addEntropy(parseInt(rand.pop(), 16)); @@ -1096,7 +1091,7 @@ Remote.prototype.book = function (currency_gets, issuer_gets, var pays = currency_pays; if (pays !== 'XRP') pays += '/' + issuer_pays; - var key = gets + ":" + pays; + var key = gets + ':' + pays; if (!this._books[key]) { var book = new OrderBook(this, @@ -1122,13 +1117,13 @@ Remote.prototype.account_seq = function (account, advance) { { seq = account_info.seq; - if (advance === "ADVANCE") account_info.seq += 1; - if (advance === "REWIND") account_info.seq -= 1; + 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); + // console.log('cached: %s current=%d next=%d', account, seq, account_info.seq); } else { - // console.log("uncached: %s", account); + // console.log('uncached: %s', account); } return seq; @@ -1153,7 +1148,7 @@ Remote.prototype.account_seq_cache = function (account, current) { request = account_info.caching_seq_request; if (!request) { - // console.log("starting: %s", account); + // console.log('starting: %s', account); request = self.request_ledger_entry('account_root') .account_root(account) .ledger_choose(current) @@ -1164,12 +1159,12 @@ Remote.prototype.account_seq_cache = function (account, current) { account_info.seq = seq; - // console.log("caching: %s %d", account, 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); }) .on('error', function (message) { - // console.log("error: %s", account); + // console.log('error: %s', account); delete account_info.caching_seq_request; request.emit('error_account_seq_cache', message); From 8a1c5a7b66bc10753efcacd7e61f61c8910f4a66 Mon Sep 17 00:00:00 2001 From: wltsmrz Date: Sun, 9 Jun 2013 09:37:26 +0900 Subject: [PATCH 02/15] Bump dependencies versions --- package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 2cb25123..d5781d0d 100644 --- a/package.json +++ b/package.json @@ -12,10 +12,10 @@ "test": "test" }, "dependencies": { - "async": "~0.1.22", - "ws": "~0.4.22", - "extend": "~1.1.1", - "simple-jsonrpc": "~0.0.1" + "async": "~0.2.9", + "ws": "~0.4.25", + "extend": "~1.1.3", + "simple-jsonrpc": "~0.0.2" }, "devDependencies": { "grunt": "~0.4.1", From 82224de494a7fcca07ac6b763965931dd8292dd6 Mon Sep 17 00:00:00 2001 From: wltsmrz Date: Sun, 9 Jun 2013 09:38:13 +0900 Subject: [PATCH 03/15] Add engines field --- package.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index d5781d0d..3ba6226d 100644 --- a/package.json +++ b/package.json @@ -32,5 +32,8 @@ "type": "git", "url": "git://github.com/rippleFoundation/ripple-lib.git" }, - "readmeFilename": "README.md" + "readmeFilename": "README.md", + "engines": { + "node": ">=0.10.0" + } } From 355a2b076bc56ca4f24e992380d09b01cd319163 Mon Sep 17 00:00:00 2001 From: wltsmrz Date: Tue, 11 Jun 2013 03:38:31 +0900 Subject: [PATCH 04/15] Set _connected --- src/js/ripple/server.js | 98 ++++++++++++++++++++++------------------- 1 file changed, 53 insertions(+), 45 deletions(-) diff --git a/src/js/ripple/server.js b/src/js/ripple/server.js index b7a342d0..506a84d9 100644 --- a/src/js/ripple/server.js +++ b/src/js/ripple/server.js @@ -15,51 +15,64 @@ var utils = require('./utils'); @param cfg Configuration parameters. */ -var Server = function (remote, cfg) -{ +var Server = function (remote, cfg) { EventEmitter.call(this); - if ("object" !== typeof cfg || "string" !== typeof cfg.url) { - throw new Error("Invalid server configuration."); + + if ('object' !== typeof cfg || 'string' !== typeof cfg.url) { + throw new Error('Invalid server configuration.'); } - this._remote = remote; - this._cfg = cfg; + var self = this; - this._ws = null; - this._connected = false; + this._remote = remote; + this._cfg = cfg; + + this._ws = void(0); + this._connected = false; this._should_connect = false; - this._state = null; + this._state = void(0); - this._id = 0; - this._retry = 0; + this._id = 0; + this._retry = 0; - this._requests = {}; + this._requests = { }; - this.on('message', this._handle_message.bind(this)); - this.on('response_subscribe', this._handle_response_subscribe.bind(this)); + this.on('message', function(message) { + self._handle_message(message); + }); + + this.on('response_subscribe', function(message) { + self._handle_response_subscribe(message); + }); }; //------------------------------------------------------------------------------ util.inherits(Server, EventEmitter); +function to_set(list) { + var result = { }; + for (var i=0; i Date: Tue, 11 Jun 2013 04:00:00 +0900 Subject: [PATCH 05/15] Enable multi-server fallback --- src/js/ripple/remote.js | 270 +++++++++++++++++++++++----------------- 1 file changed, 158 insertions(+), 112 deletions(-) diff --git a/src/js/ripple/remote.js b/src/js/ripple/remote.js index 2e8676ef..26662b06 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.message = { - 'command' : command, - 'id' : undefined, - }; this.remote = remote; this.requested = false; + this.message = { + 'command' : command, + 'id' : void(0), + }; }; util.inherits(Request, EventEmitter); @@ -194,6 +194,7 @@ 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 { @@ -281,9 +282,6 @@ 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) @@ -319,7 +317,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 @@ -352,25 +351,34 @@ var Remote = function (opts, trace) { } }; - // XXX Add support for multiple servers - var url = (this.websocket_ssl ? 'wss://' : 'ws://') + - this.websocket_ip + ':' + this.websocket_port; + if (!('servers' in opts)) { + opts.servers = [ + { + host: opts.websocket_ip, + port: opts.websocket_port, + secure: opts.websocket_ssl, + trusted: opts.trusted + } + ] + } - var server = new Server (this, {url: url}) + for (var i=0; i= -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 }) + + 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'); }); @@ -500,8 +519,7 @@ Remote.prototype.connect = function (online) { if (!this._servers.length) { throw new Error('No servers available.'); } 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.'); + request.emit('error', new Error('No servers availale')); } 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 availale')); + } } }; @@ -739,7 +793,7 @@ Remote.prototype.request_ledger_entry = function (type) { }); bDefault = false; - } + } else { // Was not cached. @@ -752,7 +806,6 @@ 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'); @@ -804,8 +857,7 @@ 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 @@ -959,14 +1011,12 @@ 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); @@ -976,9 +1026,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)); } @@ -1015,17 +1065,13 @@ 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; @@ -1039,9 +1085,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. @@ -1052,9 +1098,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. @@ -1065,9 +1111,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) { @@ -1084,8 +1130,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; @@ -1094,9 +1139,7 @@ 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; @@ -1104,7 +1147,7 @@ Remote.prototype.book = function (currency_gets, issuer_gets, } return this._books[key]; -} +}; // Return the next account sequence if possible. // <-- undefined or Sequence @@ -1113,29 +1156,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) { @@ -1153,22 +1196,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; } @@ -1204,29 +1247,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, source_currencies) { @@ -1241,11 +1284,12 @@ Remote.prototype.request_ripple_path_find = function (src_account, dst_account, request.message.source_currencies = source_currencies.map(function (ci) { 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; }); @@ -1261,10 +1305,11 @@ 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 (comment !== undefined) + if (typeof comment !== 'undefined') { request.message.comment = note; + } return request; }; @@ -1287,8 +1332,9 @@ Remote.prototype.request_connect = function (ip, port) { request.message.ip = ip; - if (port) + if (typeof port !== 'undefined') { request.message.port = port; + } return request; }; From 45a3ac3f667d9f949570201cf36bb8eac7e6b5f8 Mon Sep 17 00:00:00 2001 From: wltsmrz Date: Tue, 11 Jun 2013 08:27:31 +0900 Subject: [PATCH 06/15] Fallback to config-example for tests --- test/amount-test.js | 8 +++++++- test/base58-test.js | 8 +++++++- test/sign-test.js | 8 +++++++- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/test/amount-test.js b/test/amount-test.js index de85aedb..62228dc2 100644 --- a/test/amount-test.js +++ b/test/amount-test.js @@ -7,7 +7,13 @@ var nbi = jsbn.nbi; var Amount = require("../src/js/ripple/amount").Amount; var UInt160 = require("../src/js/ripple/uint160").UInt160; -var config = require('../src/js/ripple/config').load(require('./config')); +try { + var conf = require('./config'); +} catch(exception) { + var conf = require('./config-example'); +} + +var config = require('../src/js/ripple/config').load(conf); // XXX Add test cases that push XRP vs non-XRP behavior. diff --git a/test/base58-test.js b/test/base58-test.js index 901f1e28..10ffd2ac 100644 --- a/test/base58-test.js +++ b/test/base58-test.js @@ -2,7 +2,13 @@ var buster = require("buster"); var Seed = require("../src/js/ripple/seed").Seed; -var config = require('../src/js/ripple/config').load(require('./config')); +try { + var conf = require('./config'); +} catch(exception) { + var conf = require('./config-example'); +} + +var config = require('../src/js/ripple/config').load(conf); buster.testCase("Base58", { "Seed" : { diff --git a/test/sign-test.js b/test/sign-test.js index 47d053f7..9dfa9c7d 100644 --- a/test/sign-test.js +++ b/test/sign-test.js @@ -2,7 +2,13 @@ var buster = require("buster"); var Seed = require("../src/js/ripple/seed").Seed; -var config = require('../src/js/ripple/config').load(require('./config')); +try { + var conf = require('./config'); +} catch(exception) { + var conf = require('./config-example'); +} + +var config = require('../src/js/ripple/config').load(conf); buster.testCase("Signing", { "Keys" : { From 67041201053b8ca5bfe90defa5318ee018d6c171 Mon Sep 17 00:00:00 2001 From: wltsmrz Date: Wed, 12 Jun 2013 04:03:00 +0900 Subject: [PATCH 07/15] Wrap JSON.parse in try/catch --- src/js/ripple/remote.js | 85 ++++++++++++++++++++--------------------- 1 file changed, 42 insertions(+), 43 deletions(-) diff --git a/src/js/ripple/remote.js b/src/js/ripple/remote.js index 26662b06..70719d15 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,20 +185,19 @@ 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 { - this.message.accounts = procAccounts; + this.message.accounts = procAccounts; } return this; @@ -314,7 +313,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 = []; @@ -327,7 +326,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. @@ -351,33 +349,31 @@ 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 + } ] } - for (var i=0; i Date: Thu, 13 Jun 2013 04:06:15 +0900 Subject: [PATCH 08/15] Add field to server configuration for concurrent connections to the same host --- src/js/ripple/remote.js | 23 +++++++++++++---------- src/js/ripple/server.js | 35 ++++++++++++++++++++++------------- 2 files changed, 35 insertions(+), 23 deletions(-) diff --git a/src/js/ripple/remote.js b/src/js/ripple/remote.js index 70719d15..b0e285cd 100644 --- a/src/js/ripple/remote.js +++ b/src/js/ripple/remote.js @@ -361,7 +361,10 @@ var Remote = function (opts, trace) { // Initialize servers opts.servers.forEach(function(server) { - self.add_server(server); + var i = typeof server.pool === 'number' ? server.pool : 1; + while (i--) { + self.add_server(server); + } }); // This is used to remove EventEmitter warnings @@ -376,13 +379,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(); } @@ -620,8 +623,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; } } @@ -631,8 +634,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.' }); } }; @@ -1151,8 +1154,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) { - 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) { @@ -1240,7 +1243,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 506a84d9..67b343aa 100644 --- a/src/js/ripple/server.js +++ b/src/js/ripple/server.js @@ -1,7 +1,8 @@ -var EventEmitter = require('events').EventEmitter; -var util = require('util'); +var EventEmitter = require('events').EventEmitter; +var util = require('util'); +var WebSocket = require('ws'); -var utils = require('./utils'); +var utils = require('./utils'); //------------------------------------------------------------------------------ /** @@ -19,7 +20,7 @@ var Server = function (remote, cfg) { EventEmitter.call(this); - if ('object' !== typeof cfg || 'string' !== typeof cfg.url) { + if (typeof cfg !== 'object' || typeof cfg.url !== 'string') { throw new Error('Invalid server configuration.'); } @@ -81,12 +82,15 @@ 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; @@ -108,7 +112,9 @@ 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 @@ -132,7 +138,9 @@ 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(); }; @@ -142,16 +150,17 @@ Server.prototype.connect = function () { self._set_state('offline'); // Prevent additional events from this socket - ws.onopen = ws.onerror = ws.onclose = ws.onmessage = function () {}; + ws.removeAllListeners(); + ws.on('error', 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 @@ -161,7 +170,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 c2ad23996ddee8a6a8619e02788bd55415670562 Mon Sep 17 00:00:00 2001 From: wltsmrz Date: Thu, 13 Jun 2013 07:17:01 +0900 Subject: [PATCH 09/15] Cleanup, fix .disconnect --- src/js/ripple/server.js | 167 ++++++++++++++++++++++------------------ 1 file changed, 92 insertions(+), 75 deletions(-) diff --git a/src/js/ripple/server.js b/src/js/ripple/server.js index 67b343aa..03e10a61 100644 --- a/src/js/ripple/server.js +++ b/src/js/ripple/server.js @@ -1,33 +1,27 @@ var EventEmitter = require('events').EventEmitter; var util = require('util'); var WebSocket = require('ws'); - var utils = require('./utils'); -//------------------------------------------------------------------------------ /** - Constructor + * Server + * + * @constructor + * @param {Object} remote + * @param {Object} opts + */ - Keys for cfg: - - url - - @param remote The Remote object - @param cfg Configuration parameters. -*/ - -var Server = function (remote, cfg) { +function Server(remote, opts) { EventEmitter.call(this); - - if (typeof cfg !== 'object' || typeof cfg.url !== 'string') { + if (typeof opts !== 'object' || typeof opts.url !== 'string') { throw new Error('Invalid server configuration.'); } var self = this; this._remote = remote; - this._cfg = cfg; + this._opts = opts; this._ws = void(0); this._connected = false; @@ -48,32 +42,27 @@ var Server = function (remote, cfg) { }); }; -//------------------------------------------------------------------------------ - util.inherits(Server, EventEmitter); -function to_set(list) { - var result = { }; - for (var i=0; i Date: Thu, 13 Jun 2013 07:28:36 +0900 Subject: [PATCH 10/15] Add JSDoc style comments --- src/js/ripple/server.js | 60 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/src/js/ripple/server.js b/src/js/ripple/server.js index 03e10a61..26f146a2 100644 --- a/src/js/ripple/server.js +++ b/src/js/ripple/server.js @@ -6,6 +6,9 @@ var utils = require('./utils'); /** * Server * + * Options must contain `url` to + * WebSocket server + * * @constructor * @param {Object} remote * @param {Object} opts @@ -50,6 +53,7 @@ util.inherits(Server, EventEmitter); * Our requirements are that the server can process transactions and notify * us of changes. */ + Server.online_states = [ 'syncing' , 'tracking' @@ -58,10 +62,23 @@ 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; @@ -166,6 +183,12 @@ Server.prototype.connect = function() { }; }; +/** + * Disconnect from WebSocket server + * + * @api public + */ + Server.prototype.disconnect = function() { this._should_connect = false; this._set_state('offline'); @@ -174,6 +197,13 @@ 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)); @@ -182,7 +212,11 @@ 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; @@ -217,6 +251,18 @@ 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; @@ -233,6 +279,13 @@ 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; @@ -290,6 +343,13 @@ 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 c2b03e9d93c12a237023e906ff2bc93fbb37be73 Mon Sep 17 00:00:00 2001 From: wltsmrz Date: Thu, 13 Jun 2013 08:32:13 +0900 Subject: [PATCH 11/15] Support callback style --- src/js/ripple/remote.js | 385 +++++++++++++++++++++++----------------- 1 file changed, 226 insertions(+), 159 deletions(-) diff --git a/src/js/ripple/remote.js b/src/js/ripple/remote.js index b0e285cd..028e02c6 100644 --- a/src/js/ripple/remote.js +++ b/src/js/ripple/remote.js @@ -37,23 +37,27 @@ var sjcl = require('../../../build/sjcl'); // 'remoteError' // 'remoteUnexpected' // 'remoteDisconnected' -var Request = function (remote, command) { +var Request = function(remote, command, callback) { 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); @@ -61,7 +65,16 @@ Request.prototype.request = function (remote) { } }; -Request.prototype.build_path = function (build) { +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) { if (build) { this.message.build_path = true; } @@ -69,7 +82,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 { @@ -82,7 +95,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; @@ -90,13 +103,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': @@ -115,13 +128,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; @@ -130,7 +143,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 @@ -140,13 +153,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; } @@ -154,25 +167,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), @@ -184,7 +197,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 ]; } @@ -203,11 +216,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++) { @@ -275,7 +288,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; @@ -374,7 +387,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(); @@ -383,7 +396,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') { @@ -405,7 +418,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); @@ -425,15 +438,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://') @@ -446,11 +459,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); } @@ -458,7 +471,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'); @@ -471,12 +484,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) { @@ -500,7 +513,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; @@ -509,28 +522,27 @@ 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) { - return this.disconnect(); - } - - if (!this._servers.length) { - throw new Error('No servers available.'); + this.disconnect(); } else { - for (var i = 0, l = this._servers.length; i < l; i++) { - this._servers[i].connect(); + 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 availale')); + request.emit('error', new Error('No servers available')); } else { var server = this._get_server(); if (server) { @@ -689,15 +709,21 @@ Remote.prototype.request = function (request) { } }; -Remote.prototype.request_server_info = function () { - return new Request(this, 'server_info'); +Remote.prototype.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); + if (typeof opts === 'function') { + callback = opts; + opts = { }; + } + var request = new Request(this, 'ledger'); if (ledger) { @@ -706,7 +732,7 @@ Remote.prototype.request_ledger = function (ledger, opts) { request.message.ledger = ledger; } - if ('object' == typeof opts) { + if (typeof opts === 'object') { if (opts.full) request.message.full = true; @@ -725,33 +751,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; @@ -764,7 +790,7 @@ Remote.prototype.request_ledger_entry = function (type) { 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 @@ -803,7 +829,7 @@ Remote.prototype.request_ledger_entry = function (type) { // 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; @@ -823,11 +849,11 @@ 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) { @@ -837,10 +863,10 @@ Remote.prototype.request_subscribe = function (streams) { request.message.streams = streams; } - return request; + return request.callback(callback); }; -Remote.prototype.request_unsubscribe = function (streams) { +Remote.prototype.request_unsubscribe = function(streams, callback) { var request = new Request(this, 'unsubscribe'); if (streams) { @@ -850,39 +876,39 @@ Remote.prototype.request_unsubscribe = function (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); + return (new Request(this, 'transaction_entry', callback)).tx_hash(hash); }; // 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); @@ -894,12 +920,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); @@ -908,7 +934,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); }; @@ -923,7 +949,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); @@ -944,10 +970,10 @@ Remote.prototype.request_account_tx = function (obj) { if ('undefined' !== typeof obj.limit) {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 = { @@ -968,20 +994,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'); @@ -989,16 +1015,12 @@ 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 () { - var self = this; - - var request = new Request(this, 'submit'); - - return request; +Remote.prototype.request_submit = function(callback) { + return new Request(this, 'submit').callback(callback); }; // @@ -1013,16 +1035,16 @@ 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) 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; @@ -1055,70 +1077,87 @@ 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.once('ledger_closed', function(ledger_closed, ledger_index) { ... } ); +Remote.prototype.ledger_accept = function(callback) { if (this._stand_alone || undefined === this._stand_alone) { var request = new Request(this, 'ledger_accept'); - request.request(); + request.callback(callback).request(); } else { - this.emit('error', { - 'error' : 'notStandAlone' - }); + var err = { 'error' : 'notStandAlone' } + if (typeof callback === 'function') { + callback(err); + } + this.emit('error', err); } 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) + 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) { +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) { + .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) { +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) { + .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]) { @@ -1132,7 +1171,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; @@ -1153,7 +1192,7 @@ 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) { +Remote.prototype.account_seq = function(account, advance) { var account = UInt160.json_rewrite(account); var account_info = this.accounts[account]; var seq; @@ -1172,7 +1211,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]) { @@ -1183,7 +1222,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) { +Remote.prototype.account_seq_cache = function(account, current, callback) { var self = this; var request; @@ -1197,7 +1236,7 @@ Remote.prototype.account_seq_cache = function (account, current) { 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; @@ -1208,7 +1247,7 @@ Remote.prototype.account_seq_cache = function (account, current) { // 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; @@ -1218,18 +1257,22 @@ Remote.prototype.account_seq_cache = function (account, current) { 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; }; @@ -1242,13 +1285,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) { +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 + 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); @@ -1272,18 +1315,36 @@ Remote.prototype.request_ripple_balance = function (account, issuer, currency, c '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, source_currencies) { +Remote.prototype.request_ripple_path_find = function(src_account, dst_account, dst_amount, src_currencies, callback) { var self = this; + + var opts = { }; + + if (typeof src_account === 'object') { + opts = src_account; + } else { + opts.src_account = src_account; + opts.dst_account = dst_account; + opts.dst_ammount = dst_amount; + opts.src_currencies = src_currencies; + } + 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); + 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); if (source_currencies) { - request.message.source_currencies = source_currencies.map(function (ci) { + request.message.source_currencies = opts.src_currencies.map(function(ci) { var ci_new = {}; if ('issuer' in ci) { @@ -1297,39 +1358,45 @@ 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 (typeof comment !== 'undefined') { - request.message.comment = note; + switch (typeof comment) { + case 'string': + request.message.comment = comment; + break; + + case 'function': + callback = comment; + break; } - return request; + return request.callback(callback); }; // --> 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; + return request.callback(callback); }; -Remote.prototype.request_peers = function () { - return new Request(this, 'peers'); +Remote.prototype.request_peers = function(callback) { + return new Request(this, 'peers', callback); }; -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; @@ -1338,10 +1405,10 @@ Remote.prototype.request_connect = function (ip, port) { request.message.port = port; } - return request; + return request.callback(callback); }; -Remote.prototype.transaction = function () { +Remote.prototype.transaction = function() { return new Transaction(this); }; From 9dd337a16c715745d0e6675aa68dc5f7f074ec9d Mon Sep 17 00:00:00 2001 From: wltsmrz Date: Thu, 13 Jun 2013 13:39:55 +0900 Subject: [PATCH 12/15] Add offline queue, update code comments --- src/js/ripple/remote.js | 143 ++++++++++++++++++++++++++-------------- 1 file changed, 95 insertions(+), 48 deletions(-) diff --git a/src/js/ripple/remote.js b/src/js/ripple/remote.js index 028e02c6..7e35a98d 100644 --- a/src/js/ripple/remote.js +++ b/src/js/ripple/remote.js @@ -58,7 +58,9 @@ util.inherits(Request, EventEmitter); // Send the request to a remote. Request.prototype.request = function(remote) { - if (!this.requested) { + if (!this.remote._connected) { + this.remote._offline_queue.push(this); + } else if (!this.requested) { this.requested = true; this.remote.request(this); this.emit('request', remote); @@ -253,39 +255,72 @@ Request.prototype.books = function(books, snapshot) { return this; }; -//------------------------------------------------------------------------------ + /** Interface to manage the connection to a Ripple server. - This implementation uses WebSockets. - Keys for opts: + Configuration options: - trusted : truthy, if remote is trusted - websocket_ip - websocket_port - websocket_ssl - trace - maxListeners + + `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: + } Events: - 'connect' - 'connected' (DEPRECATED) - 'disconnect' - 'disconnected' (DEPRECATED) - 'state': - - 'online' : Connected and subscribed. - - 'offline' : Not subscribed or not connected. - 'subscribed' : This indicates stand-alone is available. + + + '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 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. - @param opts Connection options. - @param trace + + '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 */ var Remote = function(opts, trace) { @@ -308,10 +343,12 @@ 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; @@ -404,6 +441,14 @@ 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); @@ -490,7 +535,9 @@ 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; @@ -499,13 +546,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; @@ -522,7 +571,13 @@ Remote.prototype.set_trace = function(trace) { /** * Connect to the Ripple network. */ -Remote.prototype.connect = function(online) { +Remote.prototype.connect = function(online, callback) { + if (typeof online === 'function') { + callback = online; + online = void(0); + this.once('connect', callback); + } + // Downwards compatibility if (typeof online !== 'undefined' && !online) { this.disconnect(); @@ -535,6 +590,7 @@ Remote.prototype.connect = function(online) { } } } + return this; }; @@ -697,15 +753,11 @@ Remote.prototype._get_server = function() { // Send a request. // <-> request: what to send, consumed. Remote.prototype.request = function(request) { - if (!this._servers.length) { - request.emit('error', new Error('No servers available')); + var server = this._get_server(); + if (server) { + server.request(request); } else { - var server = this._get_server(); - if (server) { - server.request(request); - } else { - request.emit('error', new Error('No servers availale')); - } + request.emit('error', new Error('No servers availale')); } }; @@ -751,7 +803,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. @@ -787,7 +839,8 @@ Remote.prototype.request_ledger_entry = function(type, callback) { // 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. @@ -804,7 +857,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 ('account_root' === type) { + else if (type === 'account_root') { var cache = self.ledgers.current.account_root; if (!cache) { @@ -857,10 +910,7 @@ Remote.prototype.request_subscribe = function(streams, callback) { var request = new Request(this, 'subscribe'); if (streams) { - if ('object' !== typeof streams) { - streams = [streams]; - } - request.message.streams = streams; + request.message.streams = Array.isArray(streams) ? streams : [ streams ]; } return request.callback(callback); @@ -870,10 +920,7 @@ Remote.prototype.request_unsubscribe = function(streams, callback) { var request = new Request(this, 'unsubscribe'); if (streams) { - if ('object' !== typeof streams) { - streams = [streams]; - } - request.message.streams = streams; + request.message.streams = Array.isArray(streams) ? streams : [ streams ]; } return request.callback(callback); @@ -885,7 +932,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', callback)).tx_hash(hash); + return (new Request(this, 'transaction_entry')).tx_hash(hash).callback(callback); }; // DEPRECATED: use request_transaction_entry From be78db1563f8a07c171d6ef666ea7f661374fecd Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Sat, 15 Jun 2013 22:26:18 -0700 Subject: [PATCH 13/15] Add optional cancel support to offer_create. --- src/js/ripple/transaction.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/js/ripple/transaction.js b/src/js/ripple/transaction.js index 3d8a08a1..6b2531ff 100644 --- a/src/js/ripple/transaction.js +++ b/src/js/ripple/transaction.js @@ -588,8 +588,9 @@ Transaction.prototype.offer_cancel = function (src, sequence) { // Options: // .set_flags() -// --> expiration : Date or Number -Transaction.prototype.offer_create = function (src, taker_pays, taker_gets, expiration) { +// --> expiration : if not undefined, Date or Number +// --> cancel_sequence : if not undefined, Sequence +Transaction.prototype.offer_create = function (src, taker_pays, taker_gets, expiration, cancel_sequence) { this._secret = this._account_secret(src); this.tx_json.TransactionType = 'OfferCreate'; this.tx_json.Account = UInt160.json_rewrite(src); @@ -605,6 +606,9 @@ Transaction.prototype.offer_create = function (src, taker_pays, taker_gets, expi ? expiration.getTime() : Number(expiration); + if (cancel_sequence) + this.tx_json.OfferSequence = Number(cancel_sequence); + return this; }; From 7cb9787424494686bbf736c7fae66fcbd93b4c24 Mon Sep 17 00:00:00 2001 From: Stefan Thomas Date: Mon, 24 Jun 2013 13:47:55 +0200 Subject: [PATCH 14/15] Expose Base58 functionality in API. --- src/js/ripple/base.js | 35 ++++++++++++++++++++++++++++------- src/js/ripple/index.js | 1 + 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/js/ripple/base.js b/src/js/ripple/base.js index 903c2495..82833a38 100644 --- a/src/js/ripple/base.js +++ b/src/js/ripple/base.js @@ -64,6 +64,8 @@ Base.encode = function (input, alpha) { // --> input: String // <-- array of bytes or undefined. Base.decode = function (input, alpha) { + if ("string" !== typeof input) return undefined; + var alphabet = alphabets[alpha || 'ripple']; var bi_base = new BigInteger(String(alphabet.length)); var bi_value = nbi(); @@ -105,6 +107,17 @@ Base.decode = function (input, alpha) { return [].concat(utils.arraySet(zeros, 0), bytes); }; +Base.verify_checksum = function (bytes) { + var computed = sha256hash(bytes.slice(0, -4)).slice(0, 4); + var checksum = bytes.slice(-4); + + for (var i = 0; i < 4; i++) + if (computed[i] !== checksum[i]) + return false; + + return true; +}; + // --> input: Array // <-- String Base.encode_check = function (version, input, alphabet) { @@ -119,16 +132,24 @@ Base.encode_check = function (version, input, alphabet) { Base.decode_check = function (version, input, alphabet) { var buffer = Base.decode(input, alphabet); - if (!buffer || buffer[0] !== version || buffer.length < 5) + if (!buffer || buffer.length < 5) return NaN; - var computed = sha256hash(buffer.slice(0, -4)).slice(0, 4); - var checksum = buffer.slice(-4); - var i; + // Single valid version + if ("number" === typeof version && buffer[0] !== version) + return NaN; - for (i = 0; i != 4; i += 1) - if (computed[i] !== checksum[i]) - return NaN; + // Multiple allowed versions + if ("object" === typeof version && Array.isArray(version)) { + var match = false; + for (var i = 0, l = version.length; i < l; i++) { + match |= version[i] === buffer[0]; + } + if (!match) return NaN; + } + + if (!Base.verify_checksum(buffer)) + return NaN; // We'll use the version byte to add a leading zero, this ensures JSBN doesn't // intrepret the value as a negative number diff --git a/src/js/ripple/index.js b/src/js/ripple/index.js index bcfa829d..47b3b759 100644 --- a/src/js/ripple/index.js +++ b/src/js/ripple/index.js @@ -1,6 +1,7 @@ exports.Remote = require('./remote').Remote; exports.Amount = require('./amount').Amount; exports.Currency = require('./currency').Currency; +exports.Base = require('./base').Base; exports.UInt160 = require('./amount').UInt160; exports.Seed = require('./amount').Seed; exports.Transaction = require('./transaction').Transaction; From ed1bc7fb2569dd39c3a73f8824605b2831c87e74 Mon Sep 17 00:00:00 2001 From: Stefan Thomas Date: Mon, 24 Jun 2013 13:48:28 +0200 Subject: [PATCH 15/15] Bump version to 0.7.15. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3ba6226d..fab74725 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ripple-lib", - "version": "0.7.14", + "version": "0.7.15", "description": "Ripple JavaScript client library", "files": [ "src/js/ripple/*.js",