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_hash()
// .ledger_index() // .ledger_index()
Remote.prototype.request_transaction = 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. //utils.assert(this.trusted); // If not trusted, need to check proof, maybe talk packet protocol.
var request = new Request(this, 'transaction_entry'); var request = new Request(this, 'transaction_entry');
request.tx_hash(tx_hash); request.tx_hash(hash);
switch (typeof ledger_hash) { switch (typeof ledger_hash) {
case 'string': case 'string':

View File

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

View File

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