From ef83019a4b3c6ddc30d6aa93f113b356316b1714 Mon Sep 17 00:00:00 2001 From: wltsmrz Date: Thu, 5 Sep 2013 13:17:32 -0700 Subject: [PATCH 1/8] Resubmit transactions on tooBusy --- src/js/ripple/transactionmanager.js | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/js/ripple/transactionmanager.js b/src/js/ripple/transactionmanager.js index a35feced..54cda38e 100644 --- a/src/js/ripple/transactionmanager.js +++ b/src/js/ripple/transactionmanager.js @@ -242,11 +242,15 @@ TransactionManager.prototype._request = function(tx) { } function submission_error(error) { - //Decrement sequence - self._next_sequence--; - tx.set_state('remoteError'); - tx.emit('submitted', error); - tx.emit('error', new RippleError(error)); + if (self._is_too_busy(error)) { + self._resubmit(1); + } else { + //Decrement sequence + self._next_sequence--; + tx.set_state('remoteError'); + tx.emit('submitted', error); + tx.emit('error', new RippleError(error)); + } } function submission_success(message) { @@ -294,12 +298,18 @@ TransactionManager.prototype._request = function(tx) { return submit_request; }; -TransactionManager.prototype._is_not_found = function(error) { - var not_found_re = /^(txnNotFound|transactionNotFound)$/; +TransactionManager.prototype._is_remote_error = function(error) { return error && typeof error === 'object' && error.error === 'remoteError' && typeof error.remote === 'object' - && not_found_re.test(error.remote.error); +}; + +TransactionManager.prototype._is_not_found = function(error) { + return this._is_remote_error(error) && /^(txnNotFound|transactionNotFound)$/.test(error.remote.error); +}; + +TransactionManager.prototype._is_too_busy = function(error) { + return this._is_remote_error(error) && error.remote.error === 'tooBusy'; }; /** From ceea368a5c8aa0798a84cd1e827f78c3c2058038 Mon Sep 17 00:00:00 2001 From: wltsmrz Date: Thu, 5 Sep 2013 15:07:18 -0700 Subject: [PATCH 2/8] Proper max_fee comparison --- src/js/ripple/transactionmanager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/ripple/transactionmanager.js b/src/js/ripple/transactionmanager.js index 54cda38e..9cb1d675 100644 --- a/src/js/ripple/transactionmanager.js +++ b/src/js/ripple/transactionmanager.js @@ -351,7 +351,7 @@ TransactionManager.prototype.submit = function(tx) { tx.emit('error', new RippleError('tejAbort', 'Transaction aborted')); }); - var fee = tx.tx_json.Fee; + var fee = Number(tx.tx_json.Fee); var remote = this.remote; if (!tx._secret && !tx.tx_json.TxnSignature) { From d89f04a622cb741259840db129d28e468126eb1a Mon Sep 17 00:00:00 2001 From: wltsmrz Date: Thu, 5 Sep 2013 15:20:17 -0700 Subject: [PATCH 3/8] Fix currency in path_rewrite --- src/js/ripple/transaction.js | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/js/ripple/transaction.js b/src/js/ripple/transaction.js index 8743efe0..3c39d56b 100644 --- a/src/js/ripple/transaction.js +++ b/src/js/ripple/transaction.js @@ -74,10 +74,10 @@ function Transaction(remote) { this.hash = void(0); // ledger_current_index was this when transaction was submited. - this.submit_index = void(0); + this.submit_index = void(0); // Under construction. - this.state = void(0); + this.state = void(0); this.finalized = false; this._previous_signing_hash = void(0); @@ -208,7 +208,7 @@ Transaction.prototype.sign = function () { var seed = Seed.from_json(this._secret); var hash = this.signing_hash(); - var previously_signed = this.tx_json.TxnSignature + var previously_signed = this.tx_json.TxnSignature && hash === this._previous_signing_hash; if (previously_signed) return; @@ -232,7 +232,7 @@ Transaction.prototype.build_path = function (build) { return this; } -// tag should be undefined or a 32 bit integer. +// tag should be undefined or a 32 bit integer. // YYY Add range checking for tag. Transaction.prototype.destination_tag = function (tag) { if (tag !== void(0)) { @@ -242,19 +242,19 @@ Transaction.prototype.destination_tag = function (tag) { } Transaction._path_rewrite = function (path) { - var props = [ - 'account' - , 'issuer' - , 'currency' - ] - var path_new = path.map(function(node) { var node_new = { }; - for (var prop in node) { - if (~props.indexOf(prop)) { - node_new[prop] = UInt160.json_rewrite(node[prop]); - } + if (node.hasOwnProperty('account')) { + node_new.account = UInt160.json_rewrite(node.account); + } + + if (node.hasOwnProperty('issuer')) { + node_new.issuer = UInt160.json_rewrite(node.issuer); + } + + if (node.hasOwnProperty('currency')) { + node_new.currency = Currency.json_rewrite(node.currency); } return node_new; @@ -290,7 +290,7 @@ Transaction.prototype.send_max = function (send_max) { return this; }; -// tag should be undefined or a 32 bit integer. +// tag should be undefined or a 32 bit integer. // YYY Add range checking for tag. Transaction.prototype.source_tag = function (tag) { if (tag) { From 18c02cbb427f8c9289a54721e1a2b79540dd1177 Mon Sep 17 00:00:00 2001 From: wltsmrz Date: Thu, 5 Sep 2013 15:29:26 -0700 Subject: [PATCH 4/8] Add message.result = message.engine_result --- src/js/ripple/transactionmanager.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/js/ripple/transactionmanager.js b/src/js/ripple/transactionmanager.js index 9cb1d675..4ddad185 100644 --- a/src/js/ripple/transactionmanager.js +++ b/src/js/ripple/transactionmanager.js @@ -260,9 +260,9 @@ TransactionManager.prototype._request = function(tx) { tx.emit('submitted', message); - var engine_result = message.engine_result || ''; + message.result = message.engine_result || ''; - switch (engine_result.slice(0, 3)) { + switch (message.result.slice(0, 3)) { case 'tec': tx.emit('error', message); break; From b723e031d1375a12f1c89a9115fb8751ba79de61 Mon Sep 17 00:00:00 2001 From: wltsmrz Date: Thu, 5 Sep 2013 16:13:24 -0700 Subject: [PATCH 5/8] Update rippleerror --- src/js/ripple/rippleerror.js | 13 +- src/js/ripple/transactionmanager.js | 369 ---------------------------- 2 files changed, 8 insertions(+), 374 deletions(-) delete mode 100644 src/js/ripple/transactionmanager.js diff --git a/src/js/ripple/rippleerror.js b/src/js/ripple/rippleerror.js index 2906df8d..30fbf780 100644 --- a/src/js/ripple/rippleerror.js +++ b/src/js/ripple/rippleerror.js @@ -2,11 +2,14 @@ var util = require('util'); var extend = require('extend'); function RippleError(code, message) { - if (typeof code === 'object') { - extend(this, code); - } else { - this.result = code; - this.result_message = message; + switch (typeof code) { + case 'object': + extend(this, code); + break; + case 'string': + this.result = code; + this.result_message = message; + break; } this.result = this.result || this.error || 'Error'; diff --git a/src/js/ripple/transactionmanager.js b/src/js/ripple/transactionmanager.js deleted file mode 100644 index 4ddad185..00000000 --- a/src/js/ripple/transactionmanager.js +++ /dev/null @@ -1,369 +0,0 @@ -var util = require('util'); -var EventEmitter = require('events').EventEmitter; -var RippleError = require('./rippleerror').RippleError; -var Queue = require('./transactionqueue').TransactionQueue; -var Amount = require('./amount'); - -/** - * @constructor TransactionManager - * @param {Object} account - */ - -function TransactionManager(account) { - EventEmitter.call(this); - - var self = this; - - this.account = account; - this.remote = account._remote; - this._timeout = void(0); - this._pending = new Queue; - this._next_sequence = void(0); - this._cache = { }; - - //XX Fee units - this._max_fee = this.remote.max_fee; - - this._submission_timeout = this.remote._submission_timeout; - - function remote_reconnected() { - self.account.get_next_sequence(function(err, sequence) { - sequence_loaded(err, sequence); - self._resubmit(3); - }); - } - - function remote_disconnected() { - self.remote.once('connect', remote_reconnected); - } - - this.remote.on('disconnect', remote_disconnected); - - function sequence_loaded(err, sequence, callback) { - self._next_sequence = sequence; - self.emit('sequence_loaded', sequence); - callback && callback(); - } - - this.account.get_next_sequence(sequence_loaded); - - function adjust_fees() { - self._pending.forEach(function(pending) { - if (self.remote.local_fee && pending.tx_json.Fee) { - var old_fee = pending.tx_json.Fee; - var new_fee = self.remote.fee_tx(pending.fee_units()).to_json(); - pending.tx_json.Fee = new_fee; - pending.emit('fee_adjusted', old_fee, new_fee); - } - }); - } - - this.remote.on('load_changed', adjust_fees); - - function cache_transaction(message) { - var hash = message.transaction.hash; - var transaction = { - ledger_hash: message.ledger_hash, - ledger_index: message.ledger_index, - metadata: message.meta, - tx_json: message.transaction - } - - transaction.tx_json.ledger_index = transaction.ledger_index; - transaction.tx_json.inLedger = transaction.ledger_index; - - var pending = self._pending.get('hash', hash); - - if (pending) { - pending.emit('success', transaction); - } else { - self._cache[hash] = transaction; - } - } - - this.account.on('transaction-outbound', cache_transaction); - - function update_pending_status(ledger) { - self._pending.forEach(function(pending) { - pending.last_ledger = ledger; - switch (ledger.ledger_index - pending.submit_index) { - case 8: - pending.emit('lost', ledger); - pending.emit('error', new RippleError('tejLost', 'Transaction lost')); - break; - case 4: - pending.set_state('client_missing'); - pending.emit('missing', ledger); - break; - } - }); - } - - this.remote.on('ledger_closed', update_pending_status); -}; - -util.inherits(TransactionManager, EventEmitter); - -// request_tx presents transactions in -// a format slightly different from -// request_transaction_entry -function rewrite_transaction(tx) { - try { - var result = { - ledger_index: tx.ledger_index, - metadata: tx.meta, - tx_json: { - Account: tx.Account, - Amount: tx.Amount, - Destination: tx.Destination, - Fee: tx.Fee, - Flags: tx.Flags, - Sequence: tx.Sequence, - SigningPubKey: tx.SigningPubKey, - TransactionType: tx.TransactionType, - hash: tx.hash - } - } - } catch(exception) { } - return result || { }; -}; - -TransactionManager.prototype._resubmit = function(wait_ledgers) { - var self = this; - - if (wait_ledgers) { - var ledgers = Number(wait_ledgers) || 3; - this._wait_ledgers(ledgers, function() { - self._pending.forEach(resubmit_transaction); - }); - } else { - self._pending.forEach(resubmit_transaction); - } - - function resubmit_transaction(pending) { - if (!pending || pending.finalized) { - // Transaction has been finalized, nothing to do - return; - } - - pending.emit('resubmit'); - - if (!pending.hash) { - self._request(pending); - } else if (self._cache[pending.hash]) { - var cached = self._cache[pending.hash]; - pending.emit('success', cached); - delete self._cache[pending.hash]; - } else { - // Transaction was successfully submitted, and - // its hash discovered, but not validated - self.remote.request_tx(pending.hash, pending_check); - - function pending_check(err, res) { - if (self._is_not_found(err)) { - //XX - self._request(pending); - } else { - pending.emit('success', rewrite_transaction(res)); - } - } - } - } -}; - -TransactionManager.prototype._wait_ledgers = function(ledgers, callback) { - var self = this; - var closes = 0; - - function ledger_closed() { - if (++closes === ledgers) { - callback(); - self.remote.removeListener('ledger_closed', ledger_closed); - } - } - - this.remote.on('ledger_closed', ledger_closed); -} - -TransactionManager.prototype._request = function(tx) { - var self = this; - var remote = this.remote; - - if (tx.attempts > 5) { - tx.emit('error', new RippleError('tejAttemptsExceeded')); - return; - } - - var submit_request = remote.request_submit(); - - if (remote.local_signing) { - tx.sign(); - submit_request.tx_blob(tx.serialize().to_hex()); - } else { - submit_request.secret(tx._secret); - submit_request.build_path(tx._build_path); - submit_request.tx_json(tx.tx_json); - } - - function transaction_proposed(message) { - tx.hash = message.tx_json.hash; - tx.set_state('client_proposed'); - tx.emit('proposed', { - tx_json: message.tx_json, - engine_result: message.engine_result, - engine_result_code: message.engine_result_code, - engine_result_message: message.engine_result_message, - // If server is honest, don't expect a final if rejected. - rejected: tx.isRejected(message.engine_result_code), - }); - } - - function transaction_failed(message) { - function transaction_requested(err, res) { - if (self._is_not_found(err)) { - self._resubmit(1); - } else { - //XX - tx.emit('error', new RippleError(message)); - } - } - - self.remote.request_tx(tx.hash, transaction_requested); - } - - function transaction_retry(message) { - switch (message.engine_result) { - case 'terPRE_SEQ': - self._resubmit(1); - break; - default: - submission_error(new RippleError(message)); - } - } - - function submission_error(error) { - if (self._is_too_busy(error)) { - self._resubmit(1); - } else { - //Decrement sequence - self._next_sequence--; - tx.set_state('remoteError'); - tx.emit('submitted', error); - tx.emit('error', new RippleError(error)); - } - } - - function submission_success(message) { - if (!tx.hash) { - tx.hash = message.tx_json.hash; - } - - tx.emit('submitted', message); - - message.result = message.engine_result || ''; - - switch (message.result.slice(0, 3)) { - case 'tec': - tx.emit('error', message); - break; - case 'tes': - transaction_proposed(message); - break; - case 'tef': - //tefPAST_SEQ - transaction_failed(message); - break; - case 'ter': - transaction_retry(message); - break; - default: - submission_error(message); - } - } - - submit_request.once('success', submission_success); - submit_request.once('error', submission_error); - submit_request.request(); - - submit_request.timeout(this._submission_timeout, function() { - tx.emit('timeout'); - if (self.remote._connected) { - self._resubmit(1); - } - }); - - tx.set_state('client_submitted'); - tx.attempts++; - - return submit_request; -}; - -TransactionManager.prototype._is_remote_error = function(error) { - return error && typeof error === 'object' - && error.error === 'remoteError' - && typeof error.remote === 'object' -}; - -TransactionManager.prototype._is_not_found = function(error) { - return this._is_remote_error(error) && /^(txnNotFound|transactionNotFound)$/.test(error.remote.error); -}; - -TransactionManager.prototype._is_too_busy = function(error) { - return this._is_remote_error(error) && error.remote.error === 'tooBusy'; -}; - -/** - * Entry point for TransactionManager submission - * - * @param {Object} tx - */ - -TransactionManager.prototype.submit = function(tx) { - var self = this; - - // If sequence number is not yet known, defer until it is. - if (!this._next_sequence) { - function resubmit_transaction() { - self.submit(tx); - } - this.once('sequence_loaded', resubmit_transaction); - return; - } - - tx.tx_json.Sequence = this._next_sequence++; - tx.submit_index = this.remote._ledger_current_index; - tx.last_ledger = void(0); - tx.attempts = 0; - tx.complete(); - - function finalize(message) { - if (!tx.finalized) { - //XX - self._pending.removeSequence(tx.tx_json.Sequence); - tx.finalized = true; - tx.emit('final', message); - } - } - - tx.on('error', finalize); - tx.once('success', finalize); - tx.once('abort', function() { - tx.emit('error', new RippleError('tejAbort', 'Transaction aborted')); - }); - - var fee = Number(tx.tx_json.Fee); - var remote = this.remote; - - if (!tx._secret && !tx.tx_json.TxnSignature) { - tx.emit('error', new RippleError('tejSecretUnknown', 'Missing secret')); - } else if (!remote.trusted && !remote.local_signing) { - tx.emit('error', new RippleError('tejServerUntrusted', 'Attempt to give secret to untrusted server')); - } else if (fee && fee > this._max_fee) { - tx.emit('error', new RippleError('tejMaxFeeExceeded', 'Max fee exceeded')); - } else { - this._pending.push(tx); - this._request(tx); - } -}; - -exports.TransactionManager = TransactionManager; From 10ac0a5e42ed46950dfda77512ae2eb0b6add04f Mon Sep 17 00:00:00 2001 From: wltsmrz Date: Thu, 5 Sep 2013 16:13:38 -0700 Subject: [PATCH 6/8] Set message.result before emitting submitted event --- src/js/ripple/transactionmanager.js | 369 ++++++++++++++++++++++++++++ 1 file changed, 369 insertions(+) create mode 100644 src/js/ripple/transactionmanager.js diff --git a/src/js/ripple/transactionmanager.js b/src/js/ripple/transactionmanager.js new file mode 100644 index 00000000..55c1899f --- /dev/null +++ b/src/js/ripple/transactionmanager.js @@ -0,0 +1,369 @@ +var util = require('util'); +var EventEmitter = require('events').EventEmitter; +var RippleError = require('./rippleerror').RippleError; +var Queue = require('./transactionqueue').TransactionQueue; +var Amount = require('./amount'); + +/** + * @constructor TransactionManager + * @param {Object} account + */ + +function TransactionManager(account) { + EventEmitter.call(this); + + var self = this; + + this.account = account; + this.remote = account._remote; + this._timeout = void(0); + this._pending = new Queue; + this._next_sequence = void(0); + this._cache = { }; + + //XX Fee units + this._max_fee = this.remote.max_fee; + + this._submission_timeout = this.remote._submission_timeout; + + function remote_reconnected() { + self.account.get_next_sequence(function(err, sequence) { + sequence_loaded(err, sequence); + self._resubmit(3); + }); + } + + function remote_disconnected() { + self.remote.once('connect', remote_reconnected); + } + + this.remote.on('disconnect', remote_disconnected); + + function sequence_loaded(err, sequence, callback) { + self._next_sequence = sequence; + self.emit('sequence_loaded', sequence); + callback && callback(); + } + + this.account.get_next_sequence(sequence_loaded); + + function adjust_fees() { + self._pending.forEach(function(pending) { + if (self.remote.local_fee && pending.tx_json.Fee) { + var old_fee = pending.tx_json.Fee; + var new_fee = self.remote.fee_tx(pending.fee_units()).to_json(); + pending.tx_json.Fee = new_fee; + pending.emit('fee_adjusted', old_fee, new_fee); + } + }); + } + + this.remote.on('load_changed', adjust_fees); + + function cache_transaction(message) { + var hash = message.transaction.hash; + var transaction = { + ledger_hash: message.ledger_hash, + ledger_index: message.ledger_index, + metadata: message.meta, + tx_json: message.transaction + } + + transaction.tx_json.ledger_index = transaction.ledger_index; + transaction.tx_json.inLedger = transaction.ledger_index; + + var pending = self._pending.get('hash', hash); + + if (pending) { + pending.emit('success', transaction); + } else { + self._cache[hash] = transaction; + } + } + + this.account.on('transaction-outbound', cache_transaction); + + function update_pending_status(ledger) { + self._pending.forEach(function(pending) { + pending.last_ledger = ledger; + switch (ledger.ledger_index - pending.submit_index) { + case 8: + pending.emit('lost', ledger); + pending.emit('error', new RippleError('tejLost', 'Transaction lost')); + break; + case 4: + pending.set_state('client_missing'); + pending.emit('missing', ledger); + break; + } + }); + } + + this.remote.on('ledger_closed', update_pending_status); +}; + +util.inherits(TransactionManager, EventEmitter); + +// request_tx presents transactions in +// a format slightly different from +// request_transaction_entry +function rewrite_transaction(tx) { + try { + var result = { + ledger_index: tx.ledger_index, + metadata: tx.meta, + tx_json: { + Account: tx.Account, + Amount: tx.Amount, + Destination: tx.Destination, + Fee: tx.Fee, + Flags: tx.Flags, + Sequence: tx.Sequence, + SigningPubKey: tx.SigningPubKey, + TransactionType: tx.TransactionType, + hash: tx.hash + } + } + } catch(exception) { } + return result || { }; +}; + +TransactionManager.prototype._resubmit = function(wait_ledgers) { + var self = this; + + if (wait_ledgers) { + var ledgers = Number(wait_ledgers) || 3; + this._wait_ledgers(ledgers, function() { + self._pending.forEach(resubmit_transaction); + }); + } else { + self._pending.forEach(resubmit_transaction); + } + + function resubmit_transaction(pending) { + if (!pending || pending.finalized) { + // Transaction has been finalized, nothing to do + return; + } + + pending.emit('resubmit'); + + if (!pending.hash) { + self._request(pending); + } else if (self._cache[pending.hash]) { + var cached = self._cache[pending.hash]; + pending.emit('success', cached); + delete self._cache[pending.hash]; + } else { + // Transaction was successfully submitted, and + // its hash discovered, but not validated + self.remote.request_tx(pending.hash, pending_check); + + function pending_check(err, res) { + if (self._is_not_found(err)) { + //XX + self._request(pending); + } else { + pending.emit('success', rewrite_transaction(res)); + } + } + } + } +}; + +TransactionManager.prototype._wait_ledgers = function(ledgers, callback) { + var self = this; + var closes = 0; + + function ledger_closed() { + if (++closes === ledgers) { + callback(); + self.remote.removeListener('ledger_closed', ledger_closed); + } + } + + this.remote.on('ledger_closed', ledger_closed); +} + +TransactionManager.prototype._request = function(tx) { + var self = this; + var remote = this.remote; + + if (tx.attempts > 5) { + tx.emit('error', new RippleError('tejAttemptsExceeded')); + return; + } + + var submit_request = remote.request_submit(); + + if (remote.local_signing) { + tx.sign(); + submit_request.tx_blob(tx.serialize().to_hex()); + } else { + submit_request.secret(tx._secret); + submit_request.build_path(tx._build_path); + submit_request.tx_json(tx.tx_json); + } + + function transaction_proposed(message) { + tx.hash = message.tx_json.hash; + tx.set_state('client_proposed'); + tx.emit('proposed', { + tx_json: message.tx_json, + engine_result: message.engine_result, + engine_result_code: message.engine_result_code, + engine_result_message: message.engine_result_message, + // If server is honest, don't expect a final if rejected. + rejected: tx.isRejected(message.engine_result_code), + }); + } + + function transaction_failed(message) { + function transaction_requested(err, res) { + if (self._is_not_found(err)) { + self._resubmit(1); + } else { + //XX + tx.emit('error', new RippleError(message)); + } + } + + self.remote.request_tx(tx.hash, transaction_requested); + } + + function transaction_retry(message) { + switch (message.engine_result) { + case 'terPRE_SEQ': + self._resubmit(1); + break; + default: + submission_error(new RippleError(message)); + } + } + + function submission_error(error) { + if (self._is_too_busy(error)) { + self._resubmit(1); + } else { + //Decrement sequence + self._next_sequence--; + tx.set_state('remoteError'); + tx.emit('submitted', error); + tx.emit('error', new RippleError(error)); + } + } + + function submission_success(message) { + if (!tx.hash) { + tx.hash = message.tx_json.hash; + } + + message.result = message.engine_result || ''; + + tx.emit('submitted', message); + + switch (message.result.slice(0, 3)) { + case 'tec': + tx.emit('error', message); + break; + case 'tes': + transaction_proposed(message); + break; + case 'tef': + //tefPAST_SEQ + transaction_failed(message); + break; + case 'ter': + transaction_retry(message); + break; + default: + submission_error(message); + } + } + + submit_request.once('success', submission_success); + submit_request.once('error', submission_error); + submit_request.request(); + + submit_request.timeout(this._submission_timeout, function() { + tx.emit('timeout'); + if (self.remote._connected) { + self._resubmit(1); + } + }); + + tx.set_state('client_submitted'); + tx.attempts++; + + return submit_request; +}; + +TransactionManager.prototype._is_remote_error = function(error) { + return error && typeof error === 'object' + && error.error === 'remoteError' + && typeof error.remote === 'object' +}; + +TransactionManager.prototype._is_not_found = function(error) { + return this._is_remote_error(error) && /^(txnNotFound|transactionNotFound)$/.test(error.remote.error); +}; + +TransactionManager.prototype._is_too_busy = function(error) { + return this._is_remote_error(error) && error.remote.error === 'tooBusy'; +}; + +/** + * Entry point for TransactionManager submission + * + * @param {Object} tx + */ + +TransactionManager.prototype.submit = function(tx) { + var self = this; + + // If sequence number is not yet known, defer until it is. + if (!this._next_sequence) { + function resubmit_transaction() { + self.submit(tx); + } + this.once('sequence_loaded', resubmit_transaction); + return; + } + + tx.tx_json.Sequence = this._next_sequence++; + tx.submit_index = this.remote._ledger_current_index; + tx.last_ledger = void(0); + tx.attempts = 0; + tx.complete(); + + function finalize(message) { + if (!tx.finalized) { + //XX + self._pending.removeSequence(tx.tx_json.Sequence); + tx.finalized = true; + tx.emit('final', message); + } + } + + tx.on('error', finalize); + tx.once('success', finalize); + tx.once('abort', function() { + tx.emit('error', new RippleError('tejAbort', 'Transaction aborted')); + }); + + var fee = Number(tx.tx_json.Fee); + var remote = this.remote; + + if (!tx._secret && !tx.tx_json.TxnSignature) { + tx.emit('error', new RippleError('tejSecretUnknown', 'Missing secret')); + } else if (!remote.trusted && !remote.local_signing) { + tx.emit('error', new RippleError('tejServerUntrusted', 'Attempt to give secret to untrusted server')); + } else if (fee && fee > this._max_fee) { + tx.emit('error', new RippleError('tejMaxFeeExceeded', 'Max fee exceeded')); + } else { + this._pending.push(tx); + this._request(tx); + } +}; + +exports.TransactionManager = TransactionManager; From 2e15bce98bf3b128a1b4d0db9902537268ea21c4 Mon Sep 17 00:00:00 2001 From: jatchili Date: Thu, 5 Sep 2013 17:16:43 -0700 Subject: [PATCH 7/8] Translate LedgerEntryType and TransactionResult into strings --- src/js/ripple/serializedobject.js | 58 +++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 15 deletions(-) diff --git a/src/js/ripple/serializedobject.js b/src/js/ripple/serializedobject.js index a5744d2d..11cad35e 100644 --- a/src/js/ripple/serializedobject.js +++ b/src/js/ripple/serializedobject.js @@ -57,19 +57,6 @@ SerializedObject.prototype.resetPointer = function () { this.pointer = 0; }; -/* -SerializedObject.prototype.read = function (numberOfBytes) { - var start = this.pointer; - var end = start+numberOfBytes; - if (end > this.buffer.length) { - throw new Error("There aren't that many bytes left to read."); - } else { - var result = this.buffer.slice(start,end); - this.pointer = end; - return result; - } -}; -*/ var readOrPeek = function (advance) { return function(numberOfBytes) { @@ -113,6 +100,45 @@ var TRANSACTION_TYPES = { 101:"SetFee" }; +var LEDGER_ENTRY_TYPES = { + 97:"AccountRoot", + 99:"Contract", + 100:"DirectoryNode", + 102:"Features", + 103:"GeneratorMap", + 104:"LedgerHashes", + 110:"Nickname", + 111:"Offer", + 114:"RippleState", + 115:"FeeSettings" +}; + +var TRANSACTION_RESULTS = { + 0 :"tesSUCCESS", + 100:"tecCLAIM", + 101:"tecPATH_PARTIAL", + 102:"tecUNFUNDED_ADD", + 103:"tecUNFUNDED_OFFER", + 104:"tecUNFUNDED_PAYMENT", + 105:"tecFAILED_PROCESSING", + 121:"tecDIR_FULL", + 122:"tecINSUF_RESERVE_LINE", + 123:"tecINSUF_RESERVE_OFFER", + 124:"tecNO_DST", + 125:"tecNO_DST_INSUF_XRP", + 126:"tecNO_LINE_INSUF_RESERVE", + 127:"tecNO_LINE_REDUNDANT", + 128:"tecPATH_DRY", + 129:"tecUNFUNDED", // Deprecated, old ambiguous unfunded. + 130:"tecMASTER_DISABLED", + 131:"tecNO_REGULAR_KEY", + 132:"tecOWNERS" +}; + + + + + SerializedObject.prototype.to_json = function() { var old_pointer = this.pointer; this.resetPointer(); @@ -139,7 +165,9 @@ function jsonify_structure(thing,field_name) { if ("number" === typeof thing) { //Special codes if (field_name) { if (field_name === "LedgerEntryType") { - output = thing; //TODO: Do we have special codes for LedgerEntryType? + output = LEDGER_ENTRY_TYPES[thing] || thing; + } else if (field_name === "TransactionResult") { + output = TRANSACTION_RESULTS[thing] || thing; } else if (field_name === "TransactionType") { output = TRANSACTION_TYPES[thing] || thing; } else { @@ -166,7 +194,7 @@ function jsonify_structure(thing,field_name) { for (var i=0; i Date: Thu, 5 Sep 2013 22:07:08 -0700 Subject: [PATCH 8/8] Changing to simplified version of new account_tx API. --- src/js/ripple/remote.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/js/ripple/remote.js b/src/js/ripple/remote.js index 205f3edc..c1fc27a5 100644 --- a/src/js/ripple/remote.js +++ b/src/js/ripple/remote.js @@ -838,8 +838,7 @@ Remote.prototype.request_account_tx = function (obj, callback) { //extended account_tx , 'forward' //false - , 'fwd_marker' - , 'rev_marker' + , 'marker' ]; for (var key in obj) {