From b3822cc4c5f55e3f648aa91422aaf28233923677 Mon Sep 17 00:00:00 2001 From: wltsmrz Date: Wed, 14 Aug 2013 17:20:51 -0700 Subject: [PATCH] Update everything --- src/js/ripple/account.js | 6 +- src/js/ripple/remote.js | 224 +++++++++-------- src/js/ripple/request.js | 118 +++++---- src/js/ripple/rippleerror.js | 4 +- src/js/ripple/transaction.js | 122 ++++------ src/js/ripple/transactionmanager.js | 362 +++++++++++++++++----------- src/js/ripple/transactionqueue.js | 59 +++++ 7 files changed, 507 insertions(+), 388 deletions(-) create mode 100644 src/js/ripple/transactionqueue.js diff --git a/src/js/ripple/account.js b/src/js/ripple/account.js index 2f8ed878..09b15574 100644 --- a/src/js/ripple/account.js +++ b/src/js/ripple/account.js @@ -110,7 +110,7 @@ Account.prototype.is_valid = function () { return this._account.is_valid(); }; -Account.prototype.get_account_info = function(callback) { +Account.prototype.get_info = function(callback) { var callback = typeof callback === 'function' ? callback : function(){}; this._remote.request_account_info(this._account_id, callback); }; @@ -127,7 +127,7 @@ Account.prototype.entry = function (callback) { var self = this; var callback = typeof callback === 'function' ? callback : function(){}; - this.get_account_info(function account_info(err, info) { + this.get_info(function account_info(err, info) { if (err) { callback(err); } else { @@ -143,7 +143,7 @@ Account.prototype.entry = function (callback) { Account.prototype.get_next_sequence = function(callback) { var callback = typeof callback === 'function' ? callback : function(){}; - this.get_account_info(function account_info(err, info) { + this.get_info(function account_info(err, info) { if (err) { callback(err); } else { diff --git a/src/js/ripple/remote.js b/src/js/ripple/remote.js index 4141d23f..403d41e0 100644 --- a/src/js/ripple/remote.js +++ b/src/js/ripple/remote.js @@ -28,6 +28,7 @@ var Account = require('./account').Account; var Meta = require('./meta').Meta; var OrderBook = require('./orderbook').OrderBook; var PathFind = require('./pathfind').PathFind; +var RippleError = require('./rippleerror').RippleError; var utils = require('./utils'); var config = require('./config'); @@ -253,12 +254,11 @@ Remote.create_remote = function(options, callback) { 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 }); + var server = new Server(this, { + host : opts.host || opts.websocket_ip, + port : opts.port || opts.websocket_port, + secure : opts.secure || opts.websocket_ssl + }); function server_message(data) { self._handle_message(data, server); @@ -338,39 +338,36 @@ Remote.prototype._trace = function() { * Connect to the Ripple network. */ Remote.prototype.connect = function (online) { - // Downwards compatibility + if (!this._servers.length) { + throw new Error('No servers available.'); + } + switch (typeof online) { case 'undefined': break; - case 'function': this.once('connect', online); break; - default: + // Downwards compatibility if (!Boolean(online)) { return this.disconnect(); } - break; } - if (!this._servers.length) { - throw new Error('No servers available.'); - } else { - var self = this; + var self = this; - ;(function nextServer(i) { - var server = self._servers[i]; - server.connect(); - server._sid = ++i; + ;(function nextServer(i) { + var server = self._servers[i]; + server.connect(); + server._sid = ++i; - if (i < self._servers.length) { - setTimeout(function() { - nextServer(i); - }, self._connection_offset); - } - })(0); - } + if (i < self._servers.length) { + setTimeout(function() { + nextServer(i); + }, self._connection_offset); + } + })(0); return this; }; @@ -379,6 +376,10 @@ Remote.prototype.connect = function (online) { * Disconnect from the Ripple network. */ Remote.prototype.disconnect = function (online) { + if (!this._servers.length) { + throw new Error('No servers available, not disconnecting'); + } + this._servers.forEach(function(server) { server.disconnect(); }); @@ -398,10 +399,7 @@ Remote.prototype._handle_message = function (message, server) { if (unexpected) { // Unexpected response from remote. - this.emit('error', { - error: 'remoteUnexpected', - error_message: 'Unexpected response from remote.' - }); + this.emit('error', new RippleError('remoteUnexpected', 'Unexpected response from remote')); return; } @@ -461,34 +459,34 @@ Remote.prototype._handle_message = function (message, server) { this.emit('transaction_all', message); break; - case 'path_find': - // Pass the event to the currently open PathFind object - if (this._cur_path_find) { - this._cur_path_find.notify_update(message); - } + case 'path_find': + // Pass the event to the currently open PathFind object + if (this._cur_path_find) { + this._cur_path_find.notify_update(message); + } - this.emit('path_find_all', message); - break; - case 'serverStatus': - self.emit('server_status', message); + this.emit('path_find_all', message); + break; + case 'serverStatus': + self.emit('server_status', message); - var load_changed = message.hasOwnProperty('load_base') - && message.hasOwnProperty('load_factor') - && (message.load_base !== self._load_base || message.load_factor !== self._load_factor) - ; + var load_changed = message.hasOwnProperty('load_base') + && message.hasOwnProperty('load_factor') + && (message.load_base !== self._load_base || message.load_factor !== self._load_factor) + ; - if (load_changed) { - self._load_base = message.load_base; - self._load_factor = message.load_factor; - self.emit('load', { 'load_base' : self._load_base, 'load_factor' : self.load_factor }); - } - break; + if (load_changed) { + self._load_base = message.load_base; + self._load_factor = message.load_factor; + self.emit('load', { 'load_base' : self._load_base, 'load_factor' : self.load_factor }); + } + break; // All other messages - default: - this._trace('remote: '+message.type+': %s', message); - this.emit('net_' + message.type, message); - break; + default: + this._trace('remote: ' + message.type + ': %s', message); + this.emit('net_' + message.type, message); + break; } }; @@ -544,8 +542,10 @@ Remote.prototype.request = function (request) { request.emit('error', new Error('No servers available')); } else if (!this._connected) { this.once('connect', this.request.bind(this, request)); + } else if (request.server === null) { + this.emit('error', new Error('Server does not exist')); } else { - var server = this._get_server(); + var server = request.server || this._get_server(); if (server) { server.request(request); } else { @@ -739,9 +739,9 @@ Remote.prototype.request_transaction_entry = function (tx_hash, ledger_hash, cal case 'string': request.ledger_hash(ledger_hash); break; - case 'function': + default: + request.ledger_index('validated'); callback = ledger_hash; - break; } request.callback(callback); @@ -752,20 +752,16 @@ Remote.prototype.request_transaction_entry = function (tx_hash, ledger_hash, cal // DEPRECATED: use request_transaction_entry Remote.prototype.request_tx = function (hash, callback) { var request = new Request(this, 'tx'); - request.message.transaction = hash; request.callback(callback); - return request; }; 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); request.callback(callback); - return request; }; @@ -875,32 +871,23 @@ Remote.prototype.request_book_offers = function (gets, pays, taker, callback) { 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.callback(callback); }; Remote.prototype.request_sign = function (secret, tx_json, callback) { utils.assert(this.trusted); // Don't send secrets. - var request = new Request(this, 'sign'); - request.message.secret = secret; request.message.tx_json = tx_json; - request.callback(callback); - return request; }; // Submit a transaction. Remote.prototype.request_submit = function (callback) { - var request = new Request(this, 'submit'); - request.callback(callback); - return request; + return new Request(this, 'submit').callback(callback); }; // @@ -926,7 +913,7 @@ Remote.prototype._server_prepare_subscribe = function (callback) { var request = this.request_subscribe(feeds); - request.on('success', function (message) { + request.once('success', function (message) { self._stand_alone = !!message.stand_alone; self._testnet = !!message.testnet; @@ -949,7 +936,7 @@ Remote.prototype._server_prepare_subscribe = function (callback) { // XXX When we have multiple server support, most of this should be tracked // by the Server objects and then aggregated/interpreted by Remote. self._load_base = message.load_base || 256; - self._load_factor = message.load_factor || 1.0; + self._load_factor = message.load_factor || 256; self._fee_ref = message.fee_ref; self._fee_base = message.fee_base; self._reserve_base = message.reserve_base; @@ -979,52 +966,42 @@ Remote.prototype.ledger_accept = function (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, callback) { var request = this.request_ledger_entry('account_root'); - request.account_root(account); request.ledger_choose(current); request.once('success', function (message) { request.emit('account_balance', Amount.from_json(message.node.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'); - request.account_root(account); request.ledger_choose(current); request.on('success', function (message) { request.emit('account_flags', message.node.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'); - request.account_root(account); request.ledger_choose(current); request.on('success', function (message) { request.emit('owner_count', message.node.OwnerCount); }); - request.callback(callback, 'owner_count'); - return request; }; @@ -1082,7 +1059,7 @@ Remote.prototype.account_seq = function (account, advance) { if (account_info && account_info.seq) { seq = account_info.seq; - account_info.seq += { ADVANCE: 1, REWIND: -1 }[advance] || 0; + account_info.seq += { ADVANCE: 1, REWIND: -1 }[advance.toUpperCase()] || 0; } return seq; @@ -1103,7 +1080,7 @@ Remote.prototype.account_seq_cache = function (account, current, callback) { var self = this; if (!this.accounts.hasOwnProperty(account)) { - self.accounts[account] = { }; + this.accounts[account] = { }; } var account_info = this.accounts[account]; @@ -1198,6 +1175,20 @@ Remote.prototype.request_ripple_balance = function (account, issuer, currency, c return request; }; +function prepare_currencies(ci) { + var ci_new = { }; + + if (ci.hasOwnProperty('issuer')) { + ci_new.issuer = UInt160.json_rewrite(ci.issuer); + } + + if (ci.hasOwnProperty('currency')) { + ci_new.currency = Currency.json_rewrite(ci.currency); + } + + return ci_new; +} + Remote.prototype.request_ripple_path_find = function (src_account, dst_account, dst_amount, src_currencies, callback) { var request = new Request(this, 'ripple_path_find'); @@ -1206,19 +1197,7 @@ Remote.prototype.request_ripple_path_find = function (src_account, dst_account, request.message.destination_amount = Amount.json_rewrite(dst_amount); if (src_currencies) { - request.message.source_currencies = src_currencies.map(function (ci) { - var ci_new = { }; - - if (ci.hasOwnProperty('issuer')) { - ci_new.issuer = UInt160.json_rewrite(ci.issuer); - } - - if (ci.hasOwnProperty('currency')) { - ci_new.currency = Currency.json_rewrite(ci.currency); - } - - return ci_new; - }); + request.message.source_currencies = src_currencies.map(prepare_currencies); } request.callback(callback); @@ -1227,7 +1206,6 @@ Remote.prototype.request_ripple_path_find = function (src_account, dst_account, }; Remote.prototype.request_path_find_create = function (src_account, dst_account, dst_amount, src_currencies, callback) { - var self = this; var request = new Request(this, 'path_find'); request.message.subcommand = 'create'; @@ -1236,19 +1214,7 @@ Remote.prototype.request_path_find_create = function (src_account, dst_account, request.message.destination_amount = Amount.json_rewrite(dst_amount); if (src_currencies) { - request.message.source_currencies = src_currencies.map(function (ci) { - var ci_new = {}; - - if (ci.hasOwnProperty('issuer')) { - ci_new.issuer = UInt160.json_rewrite(ci.issuer); - } - - if (ci.hasOwnProperty('currency')) { - ci_new.currency = Currency.json_rewrite(ci.currency); - } - - return ci_new; - }); + request.message.source_currencies = src_currencies.map(prepare_currencies); } request.callback(callback); @@ -1263,9 +1229,7 @@ Remote.prototype.request_path_find_close = function () { }; Remote.prototype.request_unl_list = function (callback) { - var request = new Request(this, 'unl_list'); - request.callback(callback); - return request; + return new Request(this, 'unl_list').callback(callback); }; Remote.prototype.request_unl_add = function (addr, comment, callback) { @@ -1291,9 +1255,7 @@ Remote.prototype.request_unl_delete = function (node, callback) { }; Remote.prototype.request_peers = function (callback) { - var request = new Request(this, 'peers'); - request.callback(callback); - return request; + return new Request(this, 'peers').callback(callback); }; Remote.prototype.request_connect = function (ip, port, callback) { @@ -1372,6 +1334,36 @@ Remote.prototype.reserve = function (owner_count) { return reserve_base.add(reserve_inc.product_human(owner_count)); }; +Remote.prototype.ping = function(host, callback) { + var request = new Request(this, 'ping'); + + switch (typeof host) { + case 'function': + callback = host; + break; + case 'string': + var server = null; + for (var i=0, s; s=this._servers[i]; i++) { + if (s._host === host) { + server = s; + break; + } + } + request.set_server(server); + break; + } + + var then = Date.now(); + + request.once('success', function() { + request.emit('pong', Date.now() - then); + }); + + request.callback(callback, 'pong'); + + return request; +}; + exports.Remote = Remote; // vim:sw=2:sts=2:ts=8:et diff --git a/src/js/ripple/request.js b/src/js/ripple/request.js index 651b89e7..960c927e 100644 --- a/src/js/ripple/request.js +++ b/src/js/ripple/request.js @@ -6,6 +6,7 @@ var Transaction = require('./transaction').Transaction; var Account = require('./account').Account; var Meta = require('./meta').Meta; var OrderBook = require('./orderbook').OrderBook; +var RippleError = require('./rippleerror').RippleError; // Request events emitted: // 'success' : Request successful. @@ -15,6 +16,7 @@ var OrderBook = require('./orderbook').OrderBook; // 'remoteDisconnected' function Request(remote, command) { EventEmitter.call(this); + this.remote = remote; this.requested = false; this.message = { @@ -28,7 +30,7 @@ util.inherits(Request, EventEmitter); // Send the request to a remote. Request.prototype.request = function (remote) { if (!this.requested) { - this.requested = true; + this.requested = true; this.remote.request(this); this.emit('request', remote); } @@ -36,8 +38,19 @@ 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)); + function request_success(message) { + callback.call(this, null, message); + } + + function request_error(error) { + if (!(error instanceof RippleError)) { + error = new RippleError(error); + } + callback.call(this, error); + } + + this.once(successEvent || 'success', request_success); + this.once(errorEvent || 'error' , request_error); this.request(); } @@ -45,12 +58,16 @@ Request.prototype.callback = function(callback, successEvent, errorEvent) { }; Request.prototype.timeout = function(duration, callback) { - if (!this.requested) { - this.once('request', this.timeout.bind(this, duration, callback)); - return; - }; + var self = this; + + if (!this.requested) { + function requested() { + self.timeout(duration, callback); + } + this.once('request', requested); + return; + } - var self = this; var emit = this.emit; var timed_out = false; @@ -61,19 +78,23 @@ Request.prototype.timeout = function(duration, callback) { }, duration); this.emit = function() { - if (timed_out) return; - else clearTimeout(timeout); - emit.apply(self, arguments); + if (!timed_out) { + clearTimeout(timeout); + emit.apply(self, arguments); + } }; return this; }; +Request.prototype.set_server = function(server) { + this.server = server; +}; + Request.prototype.build_path = function (build) { if (build) { this.message.build_path = true; } - return this; }; @@ -83,16 +104,14 @@ Request.prototype.ledger_choose = function (current) { } else { this.message.ledger_hash = this.remote._ledger_hash; } - return this; }; // Set the ledger for a request. // - ledger_entry // - transaction_entry -Request.prototype.ledger_hash = function (h) { - this.message.ledger_hash = h; - +Request.prototype.ledger_hash = function (hash) { + this.message.ledger_hash = hash; return this; }; @@ -100,7 +119,6 @@ Request.prototype.ledger_hash = function (h) { // - ledger_entry Request.prototype.ledger_index = function (ledger_index) { this.message.ledger_index = ledger_index; - return this; }; @@ -114,10 +132,10 @@ Request.prototype.ledger_select = function (ledger_spec) { default: // XXX Better test needed - if (String(ledger_spec).length > 12) { - this.message.ledger_hash = ledger_spec; + if (Number(ledger_spec)) { + this.message.ledger_index = ledger_spec; } else { - this.message.ledger_index = ledger_spec; + this.message.ledger_hash = ledger_spec; } break; } @@ -127,13 +145,11 @@ Request.prototype.ledger_select = function (ledger_spec) { Request.prototype.account_root = function (account) { this.message.account_root = UInt160.json_rewrite(account); - return this; }; Request.prototype.index = function (hash) { this.message.index = hash; - return this; }; @@ -145,52 +161,45 @@ Request.prototype.offer_id = function (account, seq) { account: UInt160.json_rewrite(account), seq: seq }; - return this; }; // --> index : ledger entry index. Request.prototype.offer_index = function (index) { this.message.offer = index; - return this; }; -Request.prototype.secret = function (s) { - if (s) { - this.message.secret = s; +Request.prototype.secret = function (secret) { + if (secret) { + this.message.secret = secret; } - return this; }; -Request.prototype.tx_hash = function (h) { - this.message.tx_hash = h; - +Request.prototype.tx_hash = function (hash) { + this.message.tx_hash = hash; return this; }; -Request.prototype.tx_json = function (j) { - this.message.tx_json = j; - +Request.prototype.tx_json = function (json) { + this.message.tx_json = json; return this; }; -Request.prototype.tx_blob = function (j) { - this.message.tx_blob = j; - +Request.prototype.tx_blob = function (json) { + this.message.tx_blob = json; return this; }; Request.prototype.ripple_state = function (account, issuer, currency) { this.message.ripple_state = { - 'accounts' : [ + currency : currency, + accounts : [ UInt160.json_rewrite(account), UInt160.json_rewrite(issuer) - ], - 'currency' : currency + ] }; - return this; }; @@ -200,14 +209,14 @@ Request.prototype.accounts = function (accounts, realtime) { } // Process accounts parameters - var procAccounts = accounts.map(function(account) { + var processedAccounts = accounts.map(function(account) { return UInt160.json_rewrite(account); }); if (realtime) { - this.message.rt_accounts = procAccounts; + this.message.rt_accounts = processedAccounts; } else { - this.message.accounts = procAccounts; + this.message.accounts = processedAccounts; } return this; @@ -218,14 +227,16 @@ Request.prototype.rt_accounts = function (accounts) { }; Request.prototype.books = function (books, snapshot) { - var procBooks = []; + var processedBooks = [ ]; for (var i = 0, l = books.length; i < l; i++) { var book = books[i]; - var json = {}; + var json = { }; function processSide(side) { - if (!book[side]) throw new Error('Missing '+side); + if (!book[side]) { + throw new Error('Missing ' + side); + } var obj = json[side] = { currency: Currency.json_rewrite(book[side].currency) @@ -239,13 +250,18 @@ Request.prototype.books = function (books, snapshot) { processSide('taker_gets'); processSide('taker_pays'); - if (snapshot) json.snapshot = true; - if (book.both) json.both = true; + if (snapshot) { + json.snapshot = true; + } - procBooks.push(json); + if (book.both) { + json.both = true; + } + + processedBooks.push(json); } - this.message.books = procBooks; + this.message.books = processedBooks; return this; }; diff --git a/src/js/ripple/rippleerror.js b/src/js/ripple/rippleerror.js index 68761b68..a1f9a4e6 100644 --- a/src/js/ripple/rippleerror.js +++ b/src/js/ripple/rippleerror.js @@ -5,9 +5,9 @@ function RippleError(code, message) { if (typeof code === 'object') { extend(this, code); } else { - this.result = code; + this.result = code; + this.message = message; this.result_message = message; - this.message = message; } this.message = this.result_message || 'Error'; diff --git a/src/js/ripple/transaction.js b/src/js/ripple/transaction.js index 6bb43271..5e6fee95 100644 --- a/src/js/ripple/transaction.js +++ b/src/js/ripple/transaction.js @@ -182,8 +182,10 @@ Transaction.prototype.get_fee = function() { Transaction.prototype.complete = function () { var tx_json = this.tx_json; - if (typeof tx_json.Fee === 'undefined' && this.remote.local_fee) { - this.tx_json.Fee = this.remote.fee_tx(this.fee_units()).to_json(); + if (typeof tx_json.Fee === 'undefined') { + if (this.remote.local_fee || !this.remote.trusted) { + this.tx_json.Fee = this.remote.fee_tx(this.fee_units()).to_json(); + } } if (typeof tx_json.SigningPubKey === 'undefined' && (!this.remote || this.remote.local_signing)) { @@ -200,10 +202,7 @@ Transaction.prototype.serialize = function () { }; Transaction.prototype.signing_hash = function () { - var prefix = config.testnet - ? Transaction.HASH_SIGN_TESTNET - : Transaction.HASH_SIGN; - + var prefix = Transaction[config.testnet ? 'HASH_SIGN_TESTNET' : 'HASH_SIGN']; return SerializedObject.from_json(this.tx_json).signing_hash(prefix); }; @@ -223,13 +222,6 @@ Transaction.prototype.sign = function () { this.tx_json.TxnSignature = hex; }; -Transaction.prototype._hasTransactionListeners = function() { - return this.listeners('final').length - || this.listeners('lost').length - || this.listeners('pending').length -}; - - // // Set options for Transactions // @@ -239,7 +231,6 @@ Transaction.prototype._hasTransactionListeners = function() { // "blindly" because the sender has no idea of the actual cost except that is must be less than send max. Transaction.prototype.build_path = function (build) { this._build_path = build; - return this; } @@ -249,28 +240,27 @@ Transaction.prototype.destination_tag = function (tag) { if (tag !== void(0)) { this.tx_json.DestinationTag = tag; } - return this; } Transaction._path_rewrite = function (path) { - var path_new = []; + var props = [ + 'account' + , 'issuer' + , 'currency' + ] - for (var i=0, l=path.length; i rate: In billionths. Transaction.prototype.transfer_rate = function (rate) { @@ -324,7 +310,7 @@ Transaction.prototype.transfer_rate = function (rate) { } return this; -} +}; // Add flags to a transaction. // --> flags: undefined, _flag_, or [ _flags_ ] @@ -339,12 +325,9 @@ Transaction.prototype.set_flags = function (flags) { var flag_set = Array.isArray(flags) ? flags : [ flags ]; - for (var index in flag_set) { - if (!flag_set.hasOwnProperty(index)) continue; - - var flag = flag_set[index]; - - if (flag in transaction_flags) { + for (var i=0, l=flag_set.length; i this._config.maxFee) { -// if (this._timeout) clearTimeout(this._timeout); -// return; -// } -// -// // resubmit everything raising fee if needed -// for (var i=0; i= 8) { - // Lost - tx.emit('error', message); - tx.emit('lost', message); - } else if (dif >= 4) { - // Missing - tx.set_state('client_missing'); - tx.emit('pending', message); - } else { - // Pending - tx.emit('pending', message); - } + if (self._is_not_found(err)) { + var dif = ledger_index - tx.submit_index; + if (dif >= 8) { + // Lost + tx.emit('error', message); + tx.emit('lost', message); + } else if (dif >= 4) { + // Missing + tx.set_state('client_missing'); + tx.emit('missing', message); } else { - // Transaction was found in the ledger, - // consider this transaction successful - if (message.metadata) { - tx.set_state(message.metadata.TransactionResult); - } - tx.emit('success', message); + // Pending + tx.emit('pending', message); } - }); + } else { + // Transaction was found in the ledger, + // consider this transaction successful + if (message.metadata) { + tx.set_state(message.metadata.TransactionResult); + } + tx.emit('success', message); + } + } + + function ledger_closed(message) { + if (!tx.finalized && !checked_ledgers[message.ledger_hash]) { + remote.request_transaction_entry(tx.hash, message.ledger_hash, entry_callback); + } } function transaction_proposed() { @@ -253,10 +324,12 @@ TransactionManager.prototype._detect_ledger_entry = function(tx) { function transaction_finalized() { // Stop checking the ledger remote.removeListener('ledger_closed', ledger_closed); + tx.removeListener('proposed', transaction_proposed); } tx.once('proposed', transaction_proposed); tx.once('final', transaction_finalized); + tx.once('resubmit', transaction_finalized); }; /** @@ -265,19 +338,24 @@ TransactionManager.prototype._detect_ledger_entry = function(tx) { TransactionManager.prototype.submit = function(tx) { // If sequence number is not yet known, defer until it is. + var self = this; + if (!this._next_sequence) { - this.once('sequence_loaded', this.submit.bind(this, tx)); + function resubmit_transaction() { + self.submit(tx); + } + this.once('sequence_loaded', resubmit_transaction); return; } - var seq = this._next_sequence++; - var fee = tx.fee_units(); + tx.tx_json.Sequence = this._next_sequence++; + tx.complete(); - if (fee <= this._config.max_fee) { - tx.tx_json.Sequence = seq; - tx.tx_json.Fee = fee; - tx.complete(); + this._pending.push(tx); + var fee = tx.tx_json.Fee; + + if (fee === void(0) || fee <= this._max_fee) { this._request(tx); } }; diff --git a/src/js/ripple/transactionqueue.js b/src/js/ripple/transactionqueue.js new file mode 100644 index 00000000..ce2f8116 --- /dev/null +++ b/src/js/ripple/transactionqueue.js @@ -0,0 +1,59 @@ + +function TransactionQueue() { + this._queue = [ ]; +} + +TransactionQueue.prototype.length = function() { + return this._queue.length; +}; + +TransactionQueue.prototype.push = function(o) { + return this._queue.push(o); +}; + +TransactionQueue.prototype.hasHash = function(hash) { + return this.indexOf('hash', hash) !== -1; +}; + +TransactionQueue.prototype.hasSequence = function(sequence) { + return this.indexOf('sequence', sequence) !== -1; +}; + +TransactionQueue.prototype.indexOf = function(prop, val) { + var index = -1; + for (var i=0, tx; tx=this._queue[i]; i++) { + if (tx[prop] === val) { + index = i; + break; + } + } + return index; +}; + +TransactionQueue.prototype.removeSequence = function(sequence) { + var result = [ ]; + for (var i=0, tx; tx=this._queue[i]; i++) { + if (!tx.tx_json) continue; + if (tx.tx_json.Sequence !== sequence) + result.push(tx); + } + this._queue = result; +}; + +TransactionQueue.prototype.removeHash = function(hash) { + var result = [ ]; + for (var i=0, tx; tx=this._queue[i]; i++) { + if (!tx.tx_json) continue; + if (tx.hash !== hash) + result.push(tx); + } + this._queue = result; +}; + +TransactionQueue.prototype.forEach = function(fn) { + for (var i=0, tx; tx=this._queue[i]; i++) { + fn(tx, i); + } +}; + +exports.TransactionQueue = TransactionQueue;