Add transaction abort

This commit is contained in:
wltsmrz
2013-09-03 13:48:59 -07:00
parent f6cfcb6217
commit b5a50aeba9
3 changed files with 68 additions and 131 deletions

View File

@@ -735,11 +735,11 @@ Remote.prototype.request_unsubscribe = function (streams, callback) {
// .ledger_hash()
// .ledger_index()
Remote.prototype.request_transaction =
Remote.prototype.request_transaction_entry = function (tx_hash, ledger_hash, callback) {
Remote.prototype.request_transaction_entry = function (hash, ledger_hash, callback) {
//utils.assert(this.trusted); // If not trusted, need to check proof, maybe talk packet protocol.
var request = new Request(this, 'transaction_entry');
request.tx_hash(tx_hash);
request.tx_hash(hash);
switch (typeof ledger_hash) {
case 'string':

View File

@@ -180,18 +180,16 @@ Transaction.prototype.get_fee = function() {
* information and other fields.
*/
Transaction.prototype.complete = function () {
var tx_json = this.tx_json;
if (typeof tx_json.Fee === 'undefined') {
if (this.remote && typeof this.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)) {
if (typeof this.tx_json.SigningPubKey === 'undefined' && (!this.remote || this.remote.local_signing)) {
var seed = Seed.from_json(this._secret);
var key = seed.get_key(this.tx_json.Account);
tx_json.SigningPubKey = key.to_hex_pub();
this.tx_json.SigningPubKey = key.to_hex_pub();
}
return this.tx_json;
@@ -551,6 +549,7 @@ Transaction.prototype.submit = function (callback) {
self.callback(null, message);
}
this.on('error', function(){});
this.once('error', submission_error);
this.once('success', submission_success);
@@ -566,6 +565,14 @@ Transaction.prototype.submit = function (callback) {
return this;
};
Transaction.prototype.abort = function(callback) {
if (!this.finalized) {
var callback = typeof callback === 'function' ? callback : function(){};
this.once('final', callback);
this.emit('abort');
}
};
exports.Transaction = Transaction;
// vim:sw=2:sts=2:ts=8:et

View File

@@ -47,7 +47,21 @@ function TransactionManager(account) {
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,
@@ -58,21 +72,16 @@ function TransactionManager(account) {
transaction.tx_json.ledger_index = transaction.ledger_index;
transaction.tx_json.inLedger = transaction.ledger_index;
self._cache[message.transaction.Sequence] = transaction;
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 adjust_fees() {
self._pending.forEach(function(pending) {
if (pending.tx_json.Fee) {
var newFee = self.remote.fee_tx(pending.fee_units()).to_json();
pending.tx_json.Fee = newFee;
}
});
}
this.remote.on('load_changed', adjust_fees);
};
util.inherits(TransactionManager, EventEmitter);
@@ -107,31 +116,30 @@ TransactionManager.prototype._resubmit = function(wait_ledgers) {
if (wait_ledgers) {
var ledgers = Number(wait_ledgers) || 3;
this._wait_ledgers(ledgers, function() {
self._pending.forEach(resubmit);
self._pending.forEach(resubmit_transaction);
});
} else {
self._pending.forEach(resubmit);
self._pending.forEach(resubmit_transaction);
}
function resubmit(pending, index) {
if (pending.finalized) {
function resubmit_transaction(pending) {
if (!pending || pending.finalized) {
// Transaction has been finalized, nothing to do
return;
}
var sequence = pending.tx_json.Sequence;
var cached = self._cache[sequence];
pending.emit('resubmit');
if (cached) {
// Transaction was received while waiting for
// resubmission
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[sequence];
} else if (pending.hash) {
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)) {
@@ -141,16 +149,6 @@ TransactionManager.prototype._resubmit = function(wait_ledgers) {
pending.emit('success', rewrite_transaction(res));
}
}
var request = self.remote.request_tx(pending.hash, pending_check);
request.timeout(self._submission_timeout, function() {
if (self.remote._connected) {
self._resubmit(1);
}
});
} else {
self._request(pending);
}
}
};
@@ -173,11 +171,10 @@ TransactionManager.prototype._request = function(tx) {
var self = this;
var remote = this.remote;
// Listen for 'ledger closed' events to verify
// that the transaction is discovered in the
// ledger before considering the transaction
// successful
this._detect_ledger_entry(tx);
if (tx.attempts > 5) {
tx.emit('error', new RippleError('tejAttemptsExceeded'));
return;
}
var submit_request = remote.request_submit();
@@ -190,8 +187,6 @@ TransactionManager.prototype._request = function(tx) {
submit_request.tx_json(tx.tx_json);
}
tx.submit_index = remote._ledger_current_index;
function transaction_proposed(message) {
tx.hash = message.tx_json.hash;
tx.set_state('client_proposed');
@@ -206,8 +201,6 @@ TransactionManager.prototype._request = function(tx) {
}
function transaction_failed(message) {
if (!tx.hash) tx.hash = message.tx_json.hash;
function transaction_requested(err, res) {
if (self._is_not_found(err)) {
self._resubmit(1);
@@ -239,16 +232,13 @@ TransactionManager.prototype._request = function(tx) {
}
function submission_success(message) {
tx.emit('submitted', message);
if (tx.attempts > 5) {
tx.emit('error', new RippleError(message));
return;
if (!tx.hash) {
tx.hash = message.tx_json.hash;
}
var engine_result = message.engine_result || '';
tx.emit('submitted', message);
tx.hash = message.tx_json.hash;
var engine_result = message.engine_result || '';
switch (engine_result.slice(0, 3)) {
case 'tes':
@@ -271,6 +261,7 @@ TransactionManager.prototype._request = function(tx) {
submit_request.request();
submit_request.timeout(this._submission_timeout, function() {
tx.emit('timeout');
if (self.remote._connected) {
self._resubmit(1);
}
@@ -290,74 +281,9 @@ TransactionManager.prototype._is_not_found = function(error) {
&& not_found_re.test(error.remote.error);
};
TransactionManager.prototype._detect_ledger_entry = function(tx) {
var self = this;
var remote = this.remote;
var checked_ledgers = { };
function entry_callback(err, message) {
if (typeof message !== 'object') return;
var ledger_hash = message.ledger_hash;
var ledger_index = message.ledger_index;
if (tx.finalized || checked_ledgers[ledger_hash]) {
// Transaction submission has already erred or
// this ledger has already been checked for
// transaction
return;
}
checked_ledgers[ledger_hash] = true;
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 {
// 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() {
// Check the ledger for transaction entry
remote.addListener('ledger_closed', ledger_closed);
}
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('abort', transaction_finalized);
tx.once('resubmit', transaction_finalized);
};
/**
* Entry point for TransactionManager submission
*
* @param {Object} tx
*/
@@ -374,6 +300,7 @@ TransactionManager.prototype.submit = function(tx) {
}
tx.tx_json.Sequence = this._next_sequence++;
tx.submit_index = this.remote._ledger_current_index;
tx.attempts = 0;
tx.complete();
@@ -385,8 +312,11 @@ TransactionManager.prototype.submit = function(tx) {
}
}
tx.once('error', finalize);
tx.on('error', finalize);
tx.once('success', finalize);
tx.once('abort', function() {
tx.emit('error', new RippleError('tejAbort', 'Transaction aborted'));
});
var fee = tx.tx_json.Fee;
var remote = this.remote;