mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-11-21 04:35:49 +00:00
Add transaction abort
This commit is contained in:
@@ -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':
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user