Update pending transaction fees

This commit is contained in:
wltsmrz
2013-08-26 10:43:13 -07:00
parent 27fed3e737
commit 75859a2c4d
3 changed files with 114 additions and 64 deletions

View File

@@ -80,9 +80,9 @@ function Remote(opts, trace) {
var self = this;
this.trusted = opts.trusted;
this.local_sequence = opts.local_sequence; // Locally track sequence numbers
this.local_fee = opts.local_fee; // Locally set fees
this.trusted = Boolean(opts.trusted);
this.local_sequence = Boolean(opts.local_sequence); // Locally track sequence numbers
this.local_fee = (typeof opts.local_fee === 'undefined') ? true : Boolean(opts.local_fee); // Locally set fees
this.local_signing = (typeof opts.local_signing === 'undefined') ? true : Boolean(opts.local_signing);
this.fee_cushion = (typeof opts.fee_cushion === 'undefined') ? 1.5 : Number(opts.fee_cushion);
this.max_fee = (typeof opts.max_fee === 'undefined') ? Infinity : Number(opts.max_fee);
@@ -110,6 +110,7 @@ function Remote(opts, trace) {
this._connection_count = 0;
this._connected = false;
this._connection_offset = 1000 * (Number(opts.connection_offset) || 5);
this._submission_timeout = 1000 * (Number(opts.submission_timeout) || 10);
this._last_tx = null;
this._cur_path_find = null;
@@ -477,7 +478,13 @@ Remote.prototype._handle_message = function (message, server) {
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 });
var obj = {
load_base: self._load_base,
load_factor: self._load_factor,
fee_units: self.fee_tx_unit()
}
self.emit('load', obj);
self.emit('load_changed', obj);
}
break;

View File

@@ -564,7 +564,7 @@ Transaction.prototype.submit = function (callback) {
}
return this;
}
};
exports.Transaction = Transaction;

View File

@@ -2,6 +2,7 @@ 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
@@ -11,32 +12,38 @@ var Queue = require('./transactionqueue').TransactionQueue;
function TransactionManager(account) {
EventEmitter.call(this);
var self = this;
var self = this;
this.account = account;
this.remote = account._remote;
this._timeout = void(0);
this._resubmitting = false;
this._pending = new Queue;
this._next_sequence = void(0);
this._cache = { };
//XX Fee units
this._max_fee = Number(this.remote.max_fee) || Infinity;
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() {
function remote_reconnected() {
self._resubmit();
};
self.remote.once('connect', remote_reconnected);
};
}
this.remote.on('disconnect', remote_disconnected);
function sequence_loaded(err, sequence) {
self._next_sequence = sequence;
function sequence_loaded(err, sequence, callback) {
self._next_sequence = sequence;
self.emit('sequence_loaded', sequence);
};
callback && callback();
}
this.account.get_next_sequence(sequence_loaded);
@@ -54,7 +61,18 @@ function TransactionManager(account) {
self._cache[message.transaction.Sequence] = transaction;
}
this.account.on('transaction', 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);
@@ -79,14 +97,22 @@ function rewrite_transaction(tx) {
hash: tx.hash
}
}
} catch(exception) {
}
} catch(exception) { }
return result || { };
};
TransactionManager.prototype._resubmit = function() {
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);
});
} else {
self._pending.forEach(resubmit);
}
function resubmit(pending, index) {
if (pending.finalized) {
// Transaction has been finalized, nothing to do
@@ -109,22 +135,25 @@ TransactionManager.prototype._resubmit = function() {
function pending_check(err, res) {
if (self._is_not_found(err)) {
//XX
self._request(pending);
} else {
pending.emit('success', rewrite_transaction(res));
}
}
self.remote.request_tx(pending.hash, pending_check);
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);
}
}
this._wait_ledgers(3, function() {
self._pending.forEach(resubmit);
});
}
};
TransactionManager.prototype._wait_ledgers = function(ledgers, callback) {
var self = this;
@@ -144,27 +173,6 @@ TransactionManager.prototype._request = function(tx) {
var self = this;
var remote = this.remote;
if (!tx._secret && !tx.tx_json.TxnSignature) {
tx.emit('error', new RippleError('tejSecretUnknown', 'Missing secret'));
return;
}
if (!remote.trusted && !remote.local_signing) {
tx.emit('error', new RippleError('tejServerUntrusted', 'Attempt to give secret to untrusted server'));
return;
}
function finalize(message) {
if (!tx.finalized) {
tx.finalized = true;
tx.emit('final', message);
self._pending.removeSequence(tx.tx_json.Sequence);
}
}
tx.once('error', finalize);
tx.once('success', finalize);
// Listen for 'ledger closed' events to verify
// that the transaction is discovered in the
// ledger before considering the transaction
@@ -189,16 +197,9 @@ TransactionManager.prototype._request = function(tx) {
tx.set_state('client_proposed');
tx.emit('proposed', {
tx_json: message.tx_json,
result: message.engine_result,
engine_result: message.engine_result,
result_code: message.engine_result_code,
engine_result_code: message.engine_result_code,
result_message: message.engine_result_message,
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),
});
@@ -209,33 +210,56 @@ TransactionManager.prototype._request = function(tx) {
function transaction_requested(err, res) {
if (self._is_not_found(err)) {
self._resubmit();
self._resubmit(1);
} else {
//XX
tx.emit('error', new RippleError(message));
self._pending.removeSequence(tx.tx_json.Sequence);
}
}
self.remote.request_tx(tx.hash, transaction_requested);
}
function submission_error(err) {
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) {
//Decrement sequence
self._next_sequence--;
tx.set_state('remoteError');
tx.emit('error', new RippleError(err));
tx.emit('submitted', error);
tx.emit('error', new RippleError(error));
}
function submission_success(message) {
tx.emit('submitted', message);
if (tx.attempts > 5) {
tx.emit('error', new RippleError(message));
return;
}
var engine_result = message.engine_result || '';
tx.hash = message.tx_json.hash;
switch (engine_result.slice(0, 3)) {
case 'tes':
transaction_proposed(message);
break;
case 'tef':
//tefPAST_SEQ
transaction_failed(message);
break;
case 'tes':
transaction_proposed(message);
case 'ter':
transaction_retry(message);
break;
default:
submission_error(message);
@@ -246,14 +270,14 @@ TransactionManager.prototype._request = function(tx) {
submit_request.once('error', submission_error);
submit_request.request();
submit_request.timeout(1000 * 10, function() {
submit_request.timeout(this._submission_timeout, function() {
if (self.remote._connected) {
self._resubmit();
self._resubmit(1);
}
});
tx.set_state('client_submitted');
tx.emit('submitted');
tx.attempts++;
return submit_request;
};
@@ -329,6 +353,7 @@ TransactionManager.prototype._detect_ledger_entry = function(tx) {
tx.once('proposed', transaction_proposed);
tx.once('final', transaction_finalized);
tx.once('abort', transaction_finalized);
tx.once('resubmit', transaction_finalized);
};
@@ -349,13 +374,31 @@ TransactionManager.prototype.submit = function(tx) {
}
tx.tx_json.Sequence = this._next_sequence++;
tx.attempts = 0;
tx.complete();
this._pending.push(tx);
function finalize(message) {
if (!tx.finalized) {
self._pending.removeSequence(tx.tx_json.Sequence);
tx.finalized = true;
tx.emit('final', message);
}
}
tx.once('error', finalize);
tx.once('success', finalize);
var fee = tx.tx_json.Fee;
var remote = this.remote;
if (fee === void(0) || fee <= this._max_fee) {
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);
}
};