mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-11-30 00:55:49 +00:00
Separate Server fee calculation, select fee-optimal servers while submitting a transaction
This commit is contained in:
@@ -28,10 +28,9 @@ var Meta = require('./meta').Meta;
|
||||
var OrderBook = require('./orderbook').OrderBook;
|
||||
var PathFind = require('./pathfind').PathFind;
|
||||
var RippleError = require('./rippleerror').RippleError;
|
||||
|
||||
var utils = require('./utils');
|
||||
var config = require('./config');
|
||||
var sjcl = require('./utils').sjcl;
|
||||
var config = require('./config');
|
||||
|
||||
/**
|
||||
Interface to manage the connection to a Ripple server.
|
||||
@@ -99,13 +98,6 @@ function Remote(opts, trace) {
|
||||
this.state = 'offline'; // 'online', 'offline'
|
||||
this.retry_timer = void(0);
|
||||
this.retry = void(0);
|
||||
|
||||
this._load_base = 256;
|
||||
this._load_factor = 256;
|
||||
this._fee_ref = 10;
|
||||
this._fee_base = 10;
|
||||
this._reserve_base = void(0);
|
||||
this._reserve_inc = void(0);
|
||||
this._connection_count = 0;
|
||||
this._connected = false;
|
||||
this._connection_offset = 1000 * (typeof opts.connection_offset === 'number' ? opts.connection_offset : 5)
|
||||
@@ -172,6 +164,7 @@ function Remote(opts, trace) {
|
||||
|
||||
// This is used to remove Node EventEmitter warnings
|
||||
var maxListeners = opts.maxListeners || opts.max_listeners || 0;
|
||||
|
||||
this._servers.concat(this).forEach(function(emitter) {
|
||||
emitter.setMaxListeners(maxListeners);
|
||||
});
|
||||
@@ -440,6 +433,10 @@ Remote.prototype._handleMessage = function(message, server) {
|
||||
}
|
||||
break;
|
||||
|
||||
case 'serverStatus':
|
||||
self.emit('server_status', message);
|
||||
break;
|
||||
|
||||
case 'transaction':
|
||||
// To get these events, just subscribe to them. A subscribes and
|
||||
// unsubscribes will be added as needed.
|
||||
@@ -493,29 +490,6 @@ Remote.prototype._handleMessage = function(message, server) {
|
||||
this.emit('path_find_all', message);
|
||||
break;
|
||||
|
||||
case 'serverStatus':
|
||||
self.emit('server_status', message);
|
||||
|
||||
var loadChanged = message.hasOwnProperty('load_base')
|
||||
&& message.hasOwnProperty('load_factor')
|
||||
&& (message.load_base !== self._load_base || message.load_factor !== self._load_factor)
|
||||
;
|
||||
|
||||
if (loadChanged) {
|
||||
self._load_base = message.load_base;
|
||||
self._load_factor = message.load_factor;
|
||||
|
||||
var obj = {
|
||||
load_base: self._load_base,
|
||||
load_factor: self._load_factor,
|
||||
fee_units: self.feeTxUnit()
|
||||
}
|
||||
|
||||
self.emit('load', obj);
|
||||
self.emit('load_changed', obj);
|
||||
}
|
||||
break;
|
||||
|
||||
// All other messages
|
||||
default:
|
||||
this._trace('remote: ' + message.type + ': ', message);
|
||||
@@ -542,6 +516,11 @@ Remote.isValidLedgerData = function(ledger) {
|
||||
&& (typeof ledger.txn_count === 'number')
|
||||
};
|
||||
|
||||
Remote.isLoadStatus = function(message) {
|
||||
return (typeof message.load_base === 'number')
|
||||
&& (typeof message.load_factor === 'number');
|
||||
};
|
||||
|
||||
Remote.prototype.ledgerHash = function() {
|
||||
return this._ledger_hash;
|
||||
};
|
||||
@@ -1010,10 +989,6 @@ Remote.prototype.requestSubmit = function(callback) {
|
||||
return new Request(this, 'submit').callback(callback);
|
||||
};
|
||||
|
||||
//
|
||||
// Higher level functions.
|
||||
//
|
||||
|
||||
/**
|
||||
* Create a subscribe request with current subscriptions.
|
||||
*
|
||||
@@ -1034,15 +1009,17 @@ Remote.prototype._serverPrepareSubscribe = function(callback) {
|
||||
|
||||
var request = this.requestSubscribe(feeds);
|
||||
|
||||
request.once('success', function(message) {
|
||||
function serverSubscribed(message) {
|
||||
self._stand_alone = !!message.stand_alone;
|
||||
self._testnet = !!message.testnet;
|
||||
|
||||
if (typeof message.random === 'string') {
|
||||
var rand = message.random.match(/[0-9A-F]{8}/ig);
|
||||
|
||||
while (rand && rand.length) {
|
||||
sjcl.random.addEntropy(parseInt(rand.pop(), 16));
|
||||
}
|
||||
|
||||
self.emit('random', utils.hexToArray(message.random));
|
||||
}
|
||||
|
||||
@@ -1053,22 +1030,14 @@ Remote.prototype._serverPrepareSubscribe = function(callback) {
|
||||
self.emit('ledger_closed', message);
|
||||
}
|
||||
|
||||
// FIXME Use this to estimate fee.
|
||||
// XXX When we have multiple server support, most of this should be tracked
|
||||
// by the Server objects and then aggregated/interpreted by Remote.
|
||||
self._load_base = message.load_base || 256;
|
||||
self._load_factor = message.load_factor || 256;
|
||||
self._fee_ref = message.fee_ref;
|
||||
self._fee_base = message.fee_base;
|
||||
self._reserve_base = message.reserve_base;
|
||||
self._reserve_inc = message.reserve_inc;
|
||||
|
||||
self.emit('subscribed');
|
||||
});
|
||||
};
|
||||
|
||||
request.once('success', serverSubscribed);
|
||||
|
||||
self.emit('prepare_subscribe', request);
|
||||
|
||||
request.callback(callback);
|
||||
request.callback(callback, 'subscribed');
|
||||
|
||||
// XXX Could give error events, maybe even time out.
|
||||
|
||||
@@ -1123,7 +1092,7 @@ Remote.prototype.requestAccountBalance = function(account, ledger, callback) {
|
||||
return Amount.from_json(message.node.Balance);
|
||||
};
|
||||
|
||||
var args = Array.prototype.concat.apply(['account_balance', responseFilter], arguments);
|
||||
var args = Array.prototype.concat.apply(['account_balance', responseFilter], arguments);
|
||||
var request = Remote.accountRootRequest.apply(this, args);
|
||||
|
||||
return request;
|
||||
@@ -1135,7 +1104,7 @@ Remote.prototype.requestAccountFlags = function(account, ledger, callback) {
|
||||
return message.node.Flags;
|
||||
};
|
||||
|
||||
var args = Array.prototype.concat.apply(['account_flags', responseFilter], arguments);
|
||||
var args = Array.prototype.concat.apply(['account_flags', responseFilter], arguments);
|
||||
var request = Remote.accountRootRequest.apply(this, args);
|
||||
|
||||
return request;
|
||||
@@ -1147,7 +1116,7 @@ Remote.prototype.requestOwnerCount = function(account, ledger, callback) {
|
||||
return message.node.OwnerCount;
|
||||
};
|
||||
|
||||
var args = Array.prototype.concat.apply(['owner_count', responseFilter], arguments);
|
||||
var args = Array.prototype.concat.apply(['owner_count', responseFilter], arguments);
|
||||
var request = Remote.accountRootRequest.apply(this, args);
|
||||
|
||||
return request;
|
||||
@@ -1330,7 +1299,8 @@ Remote.prototype.requestRippleBalance = function(account, issuer, currency, ledg
|
||||
|
||||
request.rippleState(account, issuer, currency);
|
||||
request.ledgerChoose(ledger);
|
||||
request.once('success', function(message) {
|
||||
|
||||
function rippleState(message) {
|
||||
var node = message.node;
|
||||
var lowLimit = Amount.from_json(node.LowLimit);
|
||||
var highLimit = Amount.from_json(node.HighLimit);
|
||||
@@ -1352,8 +1322,9 @@ Remote.prototype.requestRippleBalance = function(account, issuer, currency, ledg
|
||||
account_quality_out : ( accountHigh ? node.HighQualityOut : node.LowQualityOut),
|
||||
peer_quality_out : (!accountHigh ? node.HighQualityOut : node.LowQualityOut),
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
request.once('success', rippleState);
|
||||
request.callback(callback, 'ripple_state');
|
||||
|
||||
return request;
|
||||
@@ -1499,9 +1470,9 @@ Remote.prototype.transaction = function(source, destination, amount, callback) {
|
||||
*
|
||||
* @return {Amount} Final fee in XRP for specified number of fee units.
|
||||
*/
|
||||
|
||||
Remote.prototype.feeTx = function(units) {
|
||||
var fee_unit = this.feeTxUnit();
|
||||
return Amount.from_json(String(Math.ceil(units * fee_unit)));
|
||||
return this._getServer().feeTx(units);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1512,16 +1483,9 @@ Remote.prototype.feeTx = function(units) {
|
||||
*
|
||||
* @return {Number} Recommended amount for one fee unit as float.
|
||||
*/
|
||||
|
||||
Remote.prototype.feeTxUnit = function() {
|
||||
var fee_unit = this._fee_base / this._fee_ref;
|
||||
|
||||
// Apply load fees
|
||||
fee_unit *= this._load_factor / this._load_base;
|
||||
|
||||
// Apply fee cushion (a safety margin in case fees rise since we were last updated
|
||||
fee_unit *= this.fee_cushion;
|
||||
|
||||
return fee_unit;
|
||||
return this._getServer().feeTxUnit();
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1529,16 +1493,9 @@ Remote.prototype.feeTxUnit = function() {
|
||||
*
|
||||
* Returns the base reserve with load fees and safety margin applied.
|
||||
*/
|
||||
|
||||
Remote.prototype.reserve = function(owner_count) {
|
||||
var reserve_base = Amount.from_json(String(this._reserve_base));
|
||||
var reserve_inc = Amount.from_json(String(this._reserve_inc));
|
||||
var owner_count = owner_count || 0;
|
||||
|
||||
if (owner_count < 0) {
|
||||
throw new Error('Owner count must not be negative.');
|
||||
}
|
||||
|
||||
return reserve_base.add(reserve_inc.product_human(owner_count));
|
||||
return this._getServer().reserve(owner_count);
|
||||
};
|
||||
|
||||
Remote.prototype.ping = function(host, callback) {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
var Amount = require('./amount').Amount;
|
||||
var utils = require('./utils');
|
||||
var util = require('util');
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var Transaction = require('./transaction').Transaction;
|
||||
var Amount = require('./amount').Amount;
|
||||
var utils = require('./utils');
|
||||
|
||||
/**
|
||||
* @constructor Server
|
||||
@@ -295,7 +296,7 @@ Server.prototype._handleClose = function() {
|
||||
this._setState('offline');
|
||||
|
||||
// Prevent additional events from this socket
|
||||
ws.onopen = ws.onerror = ws.onclose = ws.onmessage = function() {};
|
||||
ws.onopen = ws.onerror = ws.onclose = ws.onmessage = function noOp() {};
|
||||
|
||||
if (self._shouldConnect) {
|
||||
this._retryConnect();
|
||||
@@ -314,22 +315,34 @@ Server.prototype._handleMessage = function(message) {
|
||||
|
||||
try { message = JSON.parse(message); } catch(e) { }
|
||||
|
||||
if (!this.isValidMessage(message)) return;
|
||||
if (!Server.isValidMessage(message)) return;
|
||||
|
||||
switch (message.type) {
|
||||
case 'serverStatus':
|
||||
// This message is only received when online.
|
||||
// As we are connected, it is the definitive final state.
|
||||
this._setState(~(Server._onlineStates.indexOf(message.server_status)) ? 'online' : 'offline');
|
||||
break;
|
||||
|
||||
case 'ledgerClosed':
|
||||
this._lastLedgerClose = Date.now();
|
||||
this.emit('ledger_closed', message);
|
||||
break;
|
||||
|
||||
case 'path_find':
|
||||
this._remote._trace('server: path_find:', self._opts.url, message);
|
||||
case 'serverStatus':
|
||||
// This message is only received when online.
|
||||
// As we are connected, it is the definitive final state.
|
||||
|
||||
this._setState(~(Server.onlineStates.indexOf(message.server_status)) ? 'online' : 'offline');
|
||||
|
||||
if (Server.isLoadStatus(message)) {
|
||||
self.emit('load', message, self);
|
||||
self._remote.emit('load', message, self);
|
||||
|
||||
var loadChanged = ((message.load_base !== self._load_base) ||
|
||||
(message.load_factor !== self._load_factor));
|
||||
|
||||
if (loadChanged) {
|
||||
self._load_base = message.load_base;
|
||||
self._load_factor = message.load_factor;
|
||||
self.emit('load_changed', message, self);
|
||||
self._remote.emit('load_changed', message, self);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'response':
|
||||
@@ -357,6 +370,11 @@ Server.prototype._handleMessage = function(message) {
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
case 'path_find':
|
||||
this._remote._trace('server: path_find:', self._opts.url, message);
|
||||
break;
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
@@ -366,11 +384,23 @@ Server.prototype._handleMessage = function(message) {
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Server.prototype.isValidMessage = function(message) {
|
||||
Server.isValidMessage = function(message) {
|
||||
return (typeof message === 'object')
|
||||
&& (typeof message.type === 'string');
|
||||
};
|
||||
|
||||
/**
|
||||
* Check that received serverStatus message contains
|
||||
* load status information
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Server.isLoadStatus = function(message) {
|
||||
return (typeof message.load_base === 'number')
|
||||
&& (typeof message.load_factor === 'number');
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle subscription response messages. Subscription response
|
||||
* messages indicate that a connection to the server is ready
|
||||
@@ -382,6 +412,14 @@ Server.prototype._handleResponseSubscribe = function(message) {
|
||||
if (~(Server.onlineStates.indexOf(message.server_status))) {
|
||||
this._setState('online');
|
||||
}
|
||||
if (Server.isLoadStatus(message)) {
|
||||
this._load_base = message.load_base || 256;
|
||||
this._load_factor = message.load_factor || 256;
|
||||
this._fee_ref = message.fee_ref;
|
||||
this._fee_base = message.fee_base;
|
||||
this._reserve_base = message.reserve_base;
|
||||
this._reserve_inc = message.reserve_inc;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -437,7 +475,33 @@ Server.prototype.request = function(request) {
|
||||
};
|
||||
|
||||
Server.prototype._isConnected = function(request) {
|
||||
return this._connected || (request.message.command === 'subscribe' && this._ws.readyState === 1);
|
||||
var isSubscribeRequest = request
|
||||
&& request.message.command === 'subscribe'
|
||||
&& this._ws.readyState === 1;
|
||||
|
||||
return this._connected || (this._ws && isSubscribeRequest);
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculate transaction fee
|
||||
*
|
||||
* @param {Transaction|Number} Fee units for a provided transaction
|
||||
* @return {Number} Final fee in XRP for specified number of fee units
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Server.prototype.computeFee = function(transaction) {
|
||||
var units;
|
||||
|
||||
if (transaction instanceof Transaction) {
|
||||
units = transaction.feeUnits();
|
||||
} else if (typeof transaction === 'number') {
|
||||
units = transaction;
|
||||
} else {
|
||||
throw new Error('Invalid argument');
|
||||
}
|
||||
|
||||
return this.feeTx(units).to_json();
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -445,8 +509,10 @@ Server.prototype._isConnected = function(request) {
|
||||
*
|
||||
* This takes into account the last known network and local load fees.
|
||||
*
|
||||
* @param {Number} Fee units for a provided transaction
|
||||
* @return {Amount} Final fee in XRP for specified number of fee units.
|
||||
*/
|
||||
|
||||
Server.prototype.feeTx = function(units) {
|
||||
var fee_unit = this.feeTxUnit();
|
||||
return Amount.from_json(String(Math.ceil(units * fee_unit)));
|
||||
@@ -460,13 +526,14 @@ Server.prototype.feeTx = function(units) {
|
||||
*
|
||||
* @return {Number} Recommended amount for one fee unit as float.
|
||||
*/
|
||||
|
||||
Server.prototype.feeTxUnit = function() {
|
||||
var fee_unit = this._fee_base / this._fee_ref;
|
||||
|
||||
// Apply load fees
|
||||
fee_unit *= this._load_factor / this._load_base;
|
||||
|
||||
// Apply fee cushion (a safety margin in case fees rise since we were last updated
|
||||
// Apply fee cushion (a safety margin in case fees rise since we were last updated)
|
||||
fee_unit *= this._fee_cushion;
|
||||
|
||||
return fee_unit;
|
||||
@@ -477,6 +544,7 @@ Server.prototype.feeTxUnit = function() {
|
||||
*
|
||||
* Returns the base reserve with load fees and safety margin applied.
|
||||
*/
|
||||
|
||||
Server.prototype.reserve = function(owner_count) {
|
||||
var reserve_base = Amount.from_json(String(this._reserve_base));
|
||||
var reserve_inc = Amount.from_json(String(this._reserve_inc));
|
||||
|
||||
@@ -44,18 +44,14 @@
|
||||
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var util = require('util');
|
||||
|
||||
var sjcl = require('./utils').sjcl;
|
||||
|
||||
var Amount = require('./amount').Amount;
|
||||
var Currency = require('./amount').Currency;
|
||||
var UInt160 = require('./amount').UInt160;
|
||||
var Seed = require('./seed').Seed;
|
||||
var SerializedObject = require('./serializedobject').SerializedObject;
|
||||
var RippleError = require('./rippleerror').RippleError;
|
||||
|
||||
var hashprefixes = require('./hashprefixes');
|
||||
|
||||
var config = require('./config');
|
||||
|
||||
// A class to implement transactions.
|
||||
@@ -86,7 +82,7 @@ function Transaction(remote) {
|
||||
// of all submitted transactionIDs (which can change due to load_factor
|
||||
// effecting the Fee amount). This should be populated with a transactionID
|
||||
// any time it goes on the network
|
||||
this.submittedTxnIDs = []
|
||||
this.submittedTxnIDs = [ ]
|
||||
};
|
||||
|
||||
util.inherits(Transaction, EventEmitter);
|
||||
@@ -177,12 +173,43 @@ Transaction.prototype.setState = function(state) {
|
||||
};
|
||||
|
||||
/**
|
||||
* TODO
|
||||
* Actually do this right
|
||||
* Returns the number of fee units this transaction will cost.
|
||||
*
|
||||
* Each Ripple transaction based on its type and makeup costs a certain number
|
||||
* of fee units. The fee units are calculated on a per-server basis based on the
|
||||
* current load on both the network and the server.
|
||||
*
|
||||
* @see https://ripple.com/wiki/Transaction_Fee
|
||||
*
|
||||
* @return {Number} Number of fee units for this transaction.
|
||||
*/
|
||||
|
||||
Transaction.prototype.getFee = function() {
|
||||
return Transaction.fees['default'].to_json();
|
||||
Transaction.prototype.getFee =
|
||||
Transaction.prototype.feeUnits = function() {
|
||||
return Transaction.fee_units['default'];
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the server whose fee is currently the lowest
|
||||
*/
|
||||
|
||||
Transaction.prototype._getServer = function() {
|
||||
var self = this;
|
||||
var servers = this.remote._servers;
|
||||
var fee = Infinity;
|
||||
var result;
|
||||
|
||||
for (var i=0; i<servers.length; i++) {
|
||||
var server = servers[i];
|
||||
if (!server._connected) continue;
|
||||
var n = server.computeFee(this);
|
||||
if (n < fee) {
|
||||
result = server;
|
||||
fee = n;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -192,10 +219,12 @@ Transaction.prototype.getFee = function() {
|
||||
* SigningPubKey, which can be determined by the library based on network
|
||||
* information and other fields.
|
||||
*/
|
||||
|
||||
Transaction.prototype.complete = function() {
|
||||
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();
|
||||
this._server = this._getServer();
|
||||
this.tx_json.Fee = this._server.computeFee(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,22 +247,19 @@ Transaction.prototype.signingHash = function() {
|
||||
|
||||
Transaction.prototype.addSubmittedTxnID = function(hash) {
|
||||
if (this.submittedTxnIDs.indexOf(hash) === -1) {
|
||||
this.submittedTxnIDs.push(hash);
|
||||
this.submittedTxnIDs.unshift(hash);
|
||||
}
|
||||
};
|
||||
|
||||
Transaction.prototype.findResultInCache = function(cache) {
|
||||
var cached;
|
||||
var result;
|
||||
|
||||
for (var i = this.submittedTxnIDs.length - 1; i >= 0; i--) {
|
||||
for (var i=0; i<this.submittedTxnIDs.length; i++) {
|
||||
var hash = this.submittedTxnIDs[i];
|
||||
cached = cache[hash];
|
||||
if (cached != null) {
|
||||
break;
|
||||
};
|
||||
};
|
||||
if (result = cache[hash]) break;
|
||||
}
|
||||
|
||||
return cached;
|
||||
return result;
|
||||
};
|
||||
|
||||
Transaction.prototype.hash = function(prefix, as_uint256) {
|
||||
@@ -262,7 +288,7 @@ Transaction.prototype.sign = function() {
|
||||
// If the hash is the same, we can re-use the previous signature
|
||||
if (prev_sig && hash === this._previous_signing_hash) {
|
||||
this.tx_json.TxnSignature = prev_sig;
|
||||
return;
|
||||
return this;
|
||||
}
|
||||
|
||||
var key = seed.get_key(this.tx_json.Account);
|
||||
@@ -271,6 +297,8 @@ Transaction.prototype.sign = function() {
|
||||
|
||||
this.tx_json.TxnSignature = hex;
|
||||
this._previous_signing_hash = hash;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
//
|
||||
@@ -345,6 +373,7 @@ Transaction.prototype.paths = function(paths) {
|
||||
// If the secret is in the config object, it does not need to be provided.
|
||||
Transaction.prototype.secret = function(secret) {
|
||||
this._secret = secret;
|
||||
return this;
|
||||
};
|
||||
|
||||
Transaction.prototype.sendMax = function(send_max) {
|
||||
@@ -568,7 +597,9 @@ Transaction.prototype.payment = function(src, dst, amount) {
|
||||
amount = options.amount;
|
||||
dst = options.destination || options.to;
|
||||
src = options.source || options.from;
|
||||
if (options.invoiceID) this.invoiceID(options.invoiceID);
|
||||
if (options.invoiceID) {
|
||||
this.invoiceID(options.invoiceID);
|
||||
}
|
||||
}
|
||||
|
||||
if (!UInt160.is_valid(src)) {
|
||||
@@ -609,7 +640,6 @@ Transaction.prototype.rippleLineSet = function(src, limit, quality_in, quality_o
|
||||
this.tx_json.TransactionType = 'TrustSet';
|
||||
this.tx_json.Account = UInt160.json_rewrite(src);
|
||||
|
||||
// Allow limit of 0 through.
|
||||
if (limit !== void(0)) {
|
||||
this.tx_json.LimitAmount = Amount.json_rewrite(limit);
|
||||
}
|
||||
@@ -650,21 +680,6 @@ Transaction.prototype.walletAdd = function(src, amount, authorized_key, public_k
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the number of fee units this transaction will cost.
|
||||
*
|
||||
* Each Ripple transaction based on its type and makeup costs a certain number
|
||||
* of fee units. The fee units are calculated on a per-server basis based on the
|
||||
* current load on both the network and the server.
|
||||
*
|
||||
* @see https://ripple.com/wiki/Transaction_Fee
|
||||
*
|
||||
* @return {Number} Number of fee units for this transaction.
|
||||
*/
|
||||
Transaction.prototype.feeUnits = function() {
|
||||
return Transaction.fee_units['default'];
|
||||
};
|
||||
|
||||
// Submit a transaction to the network.
|
||||
Transaction.prototype.submit = function(callback) {
|
||||
var self = this;
|
||||
|
||||
@@ -53,14 +53,19 @@ function TransactionManager(account) {
|
||||
|
||||
this._account.on('transaction-outbound', transactionReceived);
|
||||
|
||||
function adjustFees() {
|
||||
function adjustFees(loadData, server) {
|
||||
// ND: note, that `Fee` is a component of a transactionID
|
||||
self._pending.forEach(function(pending) {
|
||||
if (self._remote.local_fee && pending.tx_json.Fee) {
|
||||
var shouldAdjust = pending._server === server
|
||||
&& self._remote.local_fee && pending.tx_json.Fee;
|
||||
|
||||
if (shouldAdjust) {
|
||||
var oldFee = pending.tx_json.Fee;
|
||||
var newFee = self._remote.feeTx(pending.fee_units()).to_json();
|
||||
var newFee = server.feeTx(pending.fee_units()).to_json();
|
||||
|
||||
pending.tx_json.Fee = newFee;
|
||||
pending.emit('fee_adjusted', oldFee, newFee);
|
||||
|
||||
self._remote._trace('transactionmanager: adjusting_fees:', pending.tx_json, oldFee, newFee);
|
||||
}
|
||||
});
|
||||
@@ -256,6 +261,9 @@ TransactionManager.prototype._resubmit = function(ledgers, pending) {
|
||||
this._waitLedgers(ledgers, resubmitTransactions);
|
||||
};
|
||||
|
||||
TransactionManager.prototype._selectServer = function() {
|
||||
};
|
||||
|
||||
TransactionManager.prototype._waitLedgers = function(ledgers, callback) {
|
||||
if (ledgers < 1) {
|
||||
return callback();
|
||||
@@ -297,9 +305,7 @@ TransactionManager.prototype._request = function(tx) {
|
||||
|
||||
// ND: We could consider sharing the work with tx_blob when doing
|
||||
// local_signing
|
||||
|
||||
tx.addSubmittedTxnID(tx.hash());
|
||||
// tx._hash = tx.hash();
|
||||
|
||||
remote._trace('transactionmanager: submit:', tx.tx_json);
|
||||
|
||||
@@ -321,7 +327,7 @@ TransactionManager.prototype._request = function(tx) {
|
||||
};
|
||||
|
||||
function transactionRetry(message) {
|
||||
if (self._is_no_op(tx)) {
|
||||
if (TransactionManager._isNoOp(tx)) {
|
||||
self._resubmit(1, tx);
|
||||
} else {
|
||||
self._fillSequence(tx, function() {
|
||||
@@ -338,7 +344,7 @@ TransactionManager.prototype._request = function(tx) {
|
||||
// Finalized (e.g. aborted) transactions must stop all activity
|
||||
if (tx.finalized) return;
|
||||
|
||||
if (self._is_too_busy(error)) {
|
||||
if (TransactionManager._isTooBusy(error)) {
|
||||
self._resubmit(1, tx);
|
||||
} else {
|
||||
self._nextSequence--;
|
||||
@@ -417,23 +423,27 @@ TransactionManager.prototype._request = function(tx) {
|
||||
return submitRequest;
|
||||
};
|
||||
|
||||
TransactionManager.prototype._is_no_op = function(transaction) {
|
||||
return transaction.tx_json.TransactionType === 'AccountSet'
|
||||
&& transaction.tx_json.Flags === 0;
|
||||
TransactionManager._isNoOp = function(transaction) {
|
||||
return (typeof transaction === 'object')
|
||||
&& (typeof transaction.tx_json === 'object')
|
||||
&& (transaction.tx_json.TransactionType === 'AccountSet')
|
||||
&& (transaction.tx_json.Flags === 0);
|
||||
};
|
||||
|
||||
TransactionManager.prototype._is_remote_error = function(error) {
|
||||
return error && typeof error === 'object'
|
||||
&& error.error === 'remoteError'
|
||||
&& typeof error.remote === 'object'
|
||||
TransactionManager._isRemoteError = function(error) {
|
||||
return (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._isNotFound = function(error) {
|
||||
return TransactionManager._isRemoteError(error)
|
||||
&& /^(txnNotFound|transactionNotFound)$/.test(error.remote.error);
|
||||
};
|
||||
|
||||
TransactionManager.prototype._is_too_busy = function(error) {
|
||||
return this._is_remote_error(error) && error.remote.error === 'tooBusy';
|
||||
TransactionManager._isTooBusy = function(error) {
|
||||
return TransactionManager._isRemoteError(error)
|
||||
&& error.remote.error === 'tooBusy';
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user