optimize AutobridgeCalculator and Amount for speed

This commit is contained in:
Ivan Tivonenko
2015-09-17 18:51:29 +03:00
parent c7df5df163
commit 20fa8bc953
9 changed files with 214 additions and 123 deletions

View File

@@ -1,10 +1,7 @@
machine: machine:
node: node:
version: 0.12.0 version: 0.12.0
<<<<<<< HEAD
test: test:
override: override:
- bin/ci.sh "$CIRCLE_NODE_INDEX" "$CIRCLE_NODE_TOTAL": - bin/ci.sh "$CIRCLE_NODE_INDEX" "$CIRCLE_NODE_TOTAL":
parallel: true parallel: true
=======
>>>>>>> upstream/master

View File

@@ -105,6 +105,19 @@ Amount.NaN = function() {
return result; // but let's be careful return result; // but let's be careful
}; };
Amount.createFast = function(value: Value, currency: Currency, issuer: UInt160,
isNative: boolean
) {
const res = new Amount(value);
res._is_native = isNative;
res._currency = currency;
res._issuer = issuer;
res._value = value.isZero() && value.isNegative() ?
value.negate() : value;
return res;
};
// be sure that _is_native is set properly BEFORE calling _set_value // be sure that _is_native is set properly BEFORE calling _set_value
Amount.prototype._set_value = function(value: Value) { Amount.prototype._set_value = function(value: Value) {
@@ -122,27 +135,30 @@ Amount.prototype.abs = function() {
}; };
Amount.prototype.add = function(addend) { Amount.prototype.add = function(addend) {
const addendAmount = Amount.from_json(addend); const addendAmount = addend instanceof Amount ?
addend : Amount.from_json(addend);
if (!this.is_comparable(addendAmount)) { if (!this.is_comparable(addendAmount)) {
return new Amount(); return new Amount();
} }
return this._copy(this._value.add(addendAmount._value)); return this._copy(this._value.add(addendAmount._value));
}; };
Amount.prototype.subtract = function(subtrahend) { Amount.prototype.subtract = function(subtrahend) {
// Correctness over speed, less code has less bugs, reuse add code. // Correctness over speed, less code has less bugs, reuse add code.
return this.add(Amount.from_json(subtrahend).negate()); const subsAmount = subtrahend instanceof Amount ?
subtrahend : Amount.from_json(subtrahend);
return this.add(subsAmount.negate());
}; };
// XXX Diverges from cpp. // XXX Diverges from cpp.
Amount.prototype.multiply = function(multiplicand) { Amount.prototype.multiply = function(multiplicand) {
const multiplicandValue = multiplicand instanceof Amount ?
multiplicand._value :
Amount.from_json(multiplicand)._value;
const multiplicandAmount = Amount.from_json(multiplicand); return this._copy(this._value.multiply(multiplicandValue));
return this._copy(this._value.multiply(multiplicandAmount._value));
}; };
@@ -151,9 +167,11 @@ Amount.prototype.scale = function(scaleFactor) {
}; };
Amount.prototype.divide = function(divisor) { Amount.prototype.divide = function(divisor) {
const divisorAmount = Amount.from_json(divisor); const divisorValue = divisor instanceof Amount ?
divisor._value :
Amount.from_json(divisor)._value;
return this._copy(this._value.divide(divisorAmount._value)); return this._copy(this._value.divide(divisorValue));
}; };
/** /**
@@ -344,7 +362,7 @@ Amount.prototype._check_limits = function() {
}; };
Amount.prototype.clone = function(negate) { Amount.prototype.clone = function(negate) {
return this.copyTo(new Amount(), negate); return this.copyTo(new Amount(this._value), negate);
}; };
Amount.prototype._copy = function(value) { Amount.prototype._copy = function(value) {
@@ -354,9 +372,10 @@ Amount.prototype._copy = function(value) {
}; };
Amount.prototype.compareTo = function(to) { Amount.prototype.compareTo = function(to) {
const toAmount = Amount.from_json(to); const toAmount = to instanceof Amount ? to : Amount.from_json(to);
if (!this.is_comparable(toAmount)) { if (!this.is_comparable(toAmount)) {
return 0; throw new Error('Not comparable');
} }
return this._value.comparedTo(toAmount._value); return this._value.comparedTo(toAmount._value);
}; };
@@ -743,8 +762,9 @@ Amount.prototype.to_text = function() {
// not native // not native
const offset = this._value.getExponent() - 15; const offset = this._value.getExponent() - 15;
const sign = this._value.isNegative() ? '-' : ''; const sign = this._value.isNegative() ? '-' : '';
const mantissa = utils.getMantissa16FromString( const mantissa = this._value.isNegative() ?
this._value.abs().toString()); utils.getMantissa16FromString(this._value.abs().toString()) :
utils.getMantissa16FromString(this._value.toString());
if (offset !== 0 && (offset < -25 || offset > -4)) { if (offset !== 0 && (offset < -25 || offset > -4)) {
// Use e notation. // Use e notation.
// XXX Clamp output. // XXX Clamp output.

View File

@@ -18,19 +18,23 @@ function assertValidLegOneOffer(legOneOffer, message) {
} }
function AutobridgeCalculator(currencyGets, currencyPays, function AutobridgeCalculator(currencyGets, currencyPays,
legOneOffers, legTwoOffers, issuerGets, issuerPays) { legOneOffers, legTwoOffers, issuerGets, issuerPays
) {
this._currencyGets = currencyGets; this._currencyGets = currencyGets;
this._currencyPays = currencyPays;
this._currencyGetsHex = currencyGets.to_hex(); this._currencyGetsHex = currencyGets.to_hex();
this._currencyPaysHex = currencyPays.to_hex(); this._currencyPaysHex = currencyPays.to_hex();
this._issuerGets = issuerGets; this._issuerGets = issuerGets;
this._issuerGetsObj = UInt160.from_json(issuerGets);
this._issuerPays = issuerPays; this._issuerPays = issuerPays;
this._issuerPaysObj = UInt160.from_json(issuerPays);
this.legOneOffers = _.cloneDeep(legOneOffers); this.legOneOffers = _.cloneDeep(legOneOffers);
this.legTwoOffers = _.cloneDeep(legTwoOffers); this.legTwoOffers = _.cloneDeep(legTwoOffers);
this._ownerFundsLeftover = {}; this._ownerFundsLeftover = {};
} }
AutobridgeCalculator.NULL_AMOUNT = Utils.normalizeAmount('0'); const NULL_AMOUNT = Utils.normalizeAmount('0');
/** /**
* Calculates an ordered array of autobridged offers by quality * Calculates an ordered array of autobridged offers by quality
@@ -39,6 +43,9 @@ AutobridgeCalculator.NULL_AMOUNT = Utils.normalizeAmount('0');
*/ */
AutobridgeCalculator.prototype.calculate = function() { AutobridgeCalculator.prototype.calculate = function() {
const oldMode = Amount.strict_mode;
Amount.strict_mode = false;
let legOnePointer = 0; let legOnePointer = 0;
let legTwoPointer = 0; let legTwoPointer = 0;
@@ -58,8 +65,10 @@ AutobridgeCalculator.prototype.calculate = function() {
this.adjustLegOneFundedAmount(legOneOffer); this.adjustLegOneFundedAmount(legOneOffer);
} }
const legOneTakerGetsFunded = Utils.getOfferTakerGetsFunded(legOneOffer); const legOneTakerGetsFunded = Utils.getOfferTakerGetsFunded(legOneOffer,
const legTwoTakerPaysFunded = Utils.getOfferTakerPaysFunded(legTwoOffer); this._currencyPays, this._issuerPaysObj);
const legTwoTakerPaysFunded = Utils.getOfferTakerPaysFunded(legTwoOffer,
this._currencyGets, this._issuerGetsObj);
if (legOneTakerGetsFunded.is_zero()) { if (legOneTakerGetsFunded.is_zero()) {
legOnePointer++; legOnePointer++;
@@ -103,6 +112,7 @@ AutobridgeCalculator.prototype.calculate = function() {
offersAutobridged.push(autobridgedOffer); offersAutobridged.push(autobridgedOffer);
} }
Amount.strict_mode = oldMode;
return offersAutobridged; return offersAutobridged;
}; };
@@ -118,15 +128,20 @@ AutobridgeCalculator.prototype.calculate = function() {
AutobridgeCalculator.prototype.getAutobridgedOfferWithClampedLegOne = AutobridgeCalculator.prototype.getAutobridgedOfferWithClampedLegOne =
function(legOneOffer, legTwoOffer) { function(legOneOffer, legTwoOffer) {
const legOneTakerGetsFunded = Utils.getOfferTakerGetsFunded(legOneOffer); const legOneTakerGetsFunded = Utils.getOfferTakerGetsFunded(legOneOffer,
const legTwoTakerPaysFunded = Utils.getOfferTakerPaysFunded(legTwoOffer); this._currencyPays, this._issuerPaysObj);
const legOneQuality = Utils.getOfferQuality(legOneOffer, this._currencyGets); const legTwoTakerPaysFunded = Utils.getOfferTakerPaysFunded(legTwoOffer,
this._currencyGets, this._issuerGetsObj);
const legOneQuality = Utils.getOfferQuality(legOneOffer, this._currencyGets,
this._currencyPays, this._issuerPaysObj);
const autobridgedTakerGets = Utils.getOfferTakerGetsFunded(legTwoOffer); const autobridgedTakerGets = Utils.getOfferTakerGetsFunded(legTwoOffer,
this._currencyGets, this._issuerGetsObj);
const autobridgedTakerPays = legTwoTakerPaysFunded.multiply(legOneQuality); const autobridgedTakerPays = legTwoTakerPaysFunded.multiply(legOneQuality);
if (legOneOffer.Account === legTwoOffer.Account) { if (legOneOffer.Account === legTwoOffer.Account) {
const legOneTakerGets = Utils.getOfferTakerGets(legOneOffer); const legOneTakerGets = Utils.getOfferTakerGets(legOneOffer,
this._currencyPays, this._issuerPaysObj);
const updatedTakerGets = legOneTakerGets.subtract(legTwoTakerPaysFunded); const updatedTakerGets = legOneTakerGets.subtract(legTwoTakerPaysFunded);
this.setLegOneTakerGets(legOneOffer, updatedTakerGets); this.setLegOneTakerGets(legOneOffer, updatedTakerGets);
@@ -158,15 +173,20 @@ function(legOneOffer, legTwoOffer) {
AutobridgeCalculator.prototype.getAutobridgedOfferWithClampedLegTwo = AutobridgeCalculator.prototype.getAutobridgedOfferWithClampedLegTwo =
function(legOneOffer, legTwoOffer) { function(legOneOffer, legTwoOffer) {
const legOneTakerGetsFunded = Utils.getOfferTakerGetsFunded(legOneOffer); const legOneTakerGetsFunded = Utils.getOfferTakerGetsFunded(legOneOffer,
const legTwoTakerPaysFunded = Utils.getOfferTakerPaysFunded(legTwoOffer); this._currencyPays, this._issuerPaysObj);
const legTwoQuality = Utils.getOfferQuality(legTwoOffer, this._currencyGets); const legTwoTakerPaysFunded = Utils.getOfferTakerPaysFunded(legTwoOffer,
this._currencyGets, this._issuerGetsObj);
const legTwoQuality = Utils.getOfferQuality(legTwoOffer, this._currencyGets,
this._currencyGets, this._issuerGetsObj);
const autobridgedTakerGets = legOneTakerGetsFunded.divide(legTwoQuality); const autobridgedTakerGets = legOneTakerGetsFunded.divide(legTwoQuality);
const autobridgedTakerPays = Utils.getOfferTakerPaysFunded(legOneOffer); const autobridgedTakerPays = Utils.getOfferTakerPaysFunded(legOneOffer,
this._currencyPays, this._issuerPaysObj);
// Update funded amount since leg two offer was not completely consumed // Update funded amount since leg two offer was not completely consumed
legTwoOffer.taker_gets_funded = Utils.getOfferTakerGetsFunded(legTwoOffer) legTwoOffer.taker_gets_funded = Utils.getOfferTakerGetsFunded(legTwoOffer,
this._currencyGets, this._issuerGetsObj)
.subtract(autobridgedTakerGets) .subtract(autobridgedTakerGets)
.to_text(); .to_text();
legTwoOffer.taker_pays_funded = legTwoTakerPaysFunded legTwoOffer.taker_pays_funded = legTwoTakerPaysFunded
@@ -190,8 +210,10 @@ function(legOneOffer, legTwoOffer) {
AutobridgeCalculator.prototype.getAutobridgedOfferWithoutClamps = AutobridgeCalculator.prototype.getAutobridgedOfferWithoutClamps =
function(legOneOffer, legTwoOffer) { function(legOneOffer, legTwoOffer) {
const autobridgedTakerGets = Utils.getOfferTakerGetsFunded(legTwoOffer); const autobridgedTakerGets = Utils.getOfferTakerGetsFunded(legTwoOffer,
const autobridgedTakerPays = Utils.getOfferTakerPaysFunded(legOneOffer); this._currencyGets, this._issuerGetsObj);
const autobridgedTakerPays = Utils.getOfferTakerPaysFunded(legOneOffer,
this._currencyPays, this._issuerPaysObj);
return this.formatAutobridgedOffer( return this.formatAutobridgedOffer(
autobridgedTakerGets, autobridgedTakerGets,
@@ -216,7 +238,7 @@ AutobridgeCalculator.prototype.clearOwnerFundsLeftover = function() {
*/ */
AutobridgeCalculator.prototype.resetOwnerFundsLeftover = function(account) { AutobridgeCalculator.prototype.resetOwnerFundsLeftover = function(account) {
this._ownerFundsLeftover[account] = Utils.normalizeAmount('0'); this._ownerFundsLeftover[account] = NULL_AMOUNT.clone();
return this._ownerFundsLeftover[account]; return this._ownerFundsLeftover[account];
}; };
@@ -233,7 +255,7 @@ AutobridgeCalculator.prototype.getLeftoverOwnerFunds = function(account) {
let amount = this._ownerFundsLeftover[account]; let amount = this._ownerFundsLeftover[account];
if (!amount) { if (!amount) {
amount = AutobridgeCalculator.NULL_AMOUNT.clone(); amount = NULL_AMOUNT.clone();
} }
return amount; return amount;
@@ -308,7 +330,8 @@ function(takerGets, takerPays) {
autobridgedOffer.autobridged = true; autobridgedOffer.autobridged = true;
autobridgedOffer.BookDirectory = Utils.convertOfferQualityToHexFromText(autobridgedOffer.quality); autobridgedOffer.BookDirectory =
Utils.convertOfferQualityToHexFromText(autobridgedOffer.quality);
autobridgedOffer.qualityHex = autobridgedOffer.BookDirectory; autobridgedOffer.qualityHex = autobridgedOffer.BookDirectory;
return autobridgedOffer; return autobridgedOffer;
@@ -326,11 +349,13 @@ function(takerGets, takerPays) {
AutobridgeCalculator.prototype.unclampLegOneOwnerFunds = function(legOneOffer) { AutobridgeCalculator.prototype.unclampLegOneOwnerFunds = function(legOneOffer) {
assertValidLegOneOffer(legOneOffer, 'Leg one offer is invalid'); assertValidLegOneOffer(legOneOffer, 'Leg one offer is invalid');
legOneOffer.initTakerGetsFunded = Utils.getOfferTakerGetsFunded(legOneOffer); legOneOffer.initTakerGetsFunded = Utils.getOfferTakerGetsFunded(legOneOffer,
this._currencyPays, this._issuerPaysObj);
this.setLegOneTakerGetsFunded( this.setLegOneTakerGetsFunded(
legOneOffer, legOneOffer,
Utils.getOfferTakerGets(legOneOffer) Utils.getOfferTakerGets(legOneOffer, this._currencyPays,
this._issuerPaysObj)
); );
}; };
@@ -351,7 +376,8 @@ AutobridgeCalculator.prototype.unclampLegOneOwnerFunds = function(legOneOffer) {
AutobridgeCalculator.prototype.clampLegOneOwnerFunds = function(legOneOffer) { AutobridgeCalculator.prototype.clampLegOneOwnerFunds = function(legOneOffer) {
assertValidLegOneOffer(legOneOffer, 'Leg one offer is invalid'); assertValidLegOneOffer(legOneOffer, 'Leg one offer is invalid');
const takerGets = Utils.getOfferTakerGets(legOneOffer); const takerGets = Utils.getOfferTakerGets(legOneOffer, this._currencyPays,
this._issuerPaysObj);
if (takerGets.compareTo(legOneOffer.initTakerGetsFunded) > 0) { if (takerGets.compareTo(legOneOffer.initTakerGetsFunded) > 0) {
// After clamping, TakerGets is still greater than initial funded amount // After clamping, TakerGets is still greater than initial funded amount
@@ -376,12 +402,16 @@ function(legOneOffer) {
assertValidLegOneOffer(legOneOffer, 'Leg one offer is invalid'); assertValidLegOneOffer(legOneOffer, 'Leg one offer is invalid');
assert(!legOneOffer.is_fully_funded, 'Leg one offer cannot be fully funded'); assert(!legOneOffer.is_fully_funded, 'Leg one offer cannot be fully funded');
const fundedSum = Utils.getOfferTakerGetsFunded(legOneOffer) const fundedSum = Utils.getOfferTakerGetsFunded(legOneOffer,
this._currencyPays, this._issuerPaysObj)
.add(this.getLeftoverOwnerFunds(legOneOffer.Account)); .add(this.getLeftoverOwnerFunds(legOneOffer.Account));
if (fundedSum.compareTo(Utils.getOfferTakerGets(legOneOffer)) >= 0) { if (fundedSum.compareTo(Utils.getOfferTakerGets(legOneOffer,
this._currencyPays, this._issuerPaysObj)) >= 0
) {
// There are enough extra funds to fully fund the offer // There are enough extra funds to fully fund the offer
const legOneTakerGets = Utils.getOfferTakerGets(legOneOffer); const legOneTakerGets = Utils.getOfferTakerGets(legOneOffer,
this._currencyPays, this._issuerPaysObj);
const updatedLeftover = fundedSum.subtract(legOneTakerGets); const updatedLeftover = fundedSum.subtract(legOneTakerGets);
this.setLegOneTakerGetsFunded(legOneOffer, legOneTakerGets); this.setLegOneTakerGetsFunded(legOneOffer, legOneTakerGets);
@@ -408,8 +438,9 @@ function setLegOneTakerGetsFunded(legOneOffer, takerGetsFunded) {
legOneOffer.taker_gets_funded = takerGetsFunded.to_text(); legOneOffer.taker_gets_funded = takerGetsFunded.to_text();
legOneOffer.taker_pays_funded = takerGetsFunded legOneOffer.taker_pays_funded = takerGetsFunded
.multiply(Utils.getOfferQuality(legOneOffer, this._currencyGets)) .multiply(Utils.getOfferQuality(legOneOffer, this._currencyGets,
.to_text(); this._currencyPays, this._issuerPaysObj))
.to_text();
if (legOneOffer.taker_gets_funded === legOneOffer.TakerGets.value) { if (legOneOffer.taker_gets_funded === legOneOffer.TakerGets.value) {
legOneOffer.is_fully_funded = true; legOneOffer.is_fully_funded = true;
@@ -429,10 +460,11 @@ function(legOneOffer, takerGets) {
assertValidLegOneOffer(legOneOffer, 'Leg one offer is invalid'); assertValidLegOneOffer(legOneOffer, 'Leg one offer is invalid');
assert(takerGets instanceof Amount, 'Taker gets funded is invalid'); assert(takerGets instanceof Amount, 'Taker gets funded is invalid');
const legOneQuality = Utils.getOfferQuality(legOneOffer, this._currencyGets); const legOneQuality = Utils.getOfferQuality(legOneOffer, this._currencyGets,
this._currencyPays, this._issuerPaysObj);
legOneOffer.TakerGets = takerGets.to_text(); legOneOffer.TakerGets = takerGets.to_text();
legOneOffer.TakerPays = takerGets.multiply(legOneQuality); legOneOffer.TakerPays = takerGets.multiply(legOneQuality).to_json();
}; };
module.exports = AutobridgeCalculator; module.exports = AutobridgeCalculator;

View File

@@ -1,15 +1,15 @@
'use strict'; 'use strict';
var extend = require('extend'); const extend = require('extend');
var UInt160 = require('./uint160').UInt160; const UInt160 = require('./uint160').UInt160;
var utils = require('./utils'); const utils = require('./utils');
var Float = require('./ieee754').Float; const Float = require('./ieee754').Float;
// //
// Currency support // Currency support
// //
var Currency = extend(function() { const Currency = extend(function() {
// Internal form: 0 = XRP. 3 letter-code. // Internal form: 0 = XRP. 3 letter-code.
// XXX Internal should be 0 or hex with three letter annotation when valid. // XXX Internal should be 0 or hex with three letter annotation when valid.
@@ -61,12 +61,12 @@ Currency.HEX_CURRENCY_BAD = '0000000000000000000000005852500000000000';
* *
*/ */
/*eslint-disable max-len*/ /* eslint-disable max-len*/
Currency.prototype.human_RE = /^\s*([a-zA-Z0-9\<\>\(\)\{\}\[\]\|\?\!\@\#\$\%\^\&]{3})(\s*-\s*[- \w]+)?(\s*\(-?\d+\.?\d*%pa\))?\s*$/; Currency.prototype.human_RE = /^\s*([a-zA-Z0-9\<\>\(\)\{\}\[\]\|\?\!\@\#\$\%\^\&]{3})(\s*-\s*[- \w]+)?(\s*\(-?\d+\.?\d*%pa\))?\s*$/;
/*eslint-enable max-len*/ /* eslint-enable max-len*/
Currency.from_json = function(j, shouldInterpretXrpAsIou) { Currency.from_json = function(j, shouldInterpretXrpAsIou) {
return (new Currency()).parse_json(j, shouldInterpretXrpAsIou); return (new Currency()).parse_json(j, shouldInterpretXrpAsIou);
}; };
Currency.from_human = function(j, opts) { Currency.from_human = function(j, opts) {
@@ -78,7 +78,7 @@ Currency.prototype.parse_json = function(j, shouldInterpretXrpAsIou) {
this._value = NaN; this._value = NaN;
if (j instanceof Currency) { if (j instanceof Currency) {
this._value = j.copyTo({})._value; this._value = j._value;
this._update(); this._update();
return this; return this;
} }
@@ -111,10 +111,10 @@ Currency.prototype.parse_json = function(j, shouldInterpretXrpAsIou) {
} }
// match the given string to see if it's in an allowed format // match the given string to see if it's in an allowed format
var matches = j.match(this.human_RE); const matches = j.match(this.human_RE);
if (matches) { if (matches) {
var currencyCode = matches[1]; let currencyCode = matches[1];
// for the currency 'XRP' case // for the currency 'XRP' case
// we drop everything else that could have been provided // we drop everything else that could have been provided
@@ -131,14 +131,14 @@ Currency.prototype.parse_json = function(j, shouldInterpretXrpAsIou) {
// the full currency is matched as it is part of the valid currency // the full currency is matched as it is part of the valid currency
// format, but not stored // format, but not stored
// var full_currency = matches[2] || ''; // var full_currency = matches[2] || '';
var interest = matches[3] || ''; const interest = matches[3] || '';
// interest is defined as interest per year, per annum (pa) // interest is defined as interest per year, per annum (pa)
var percentage = interest.match(/(-?\d+\.?\d+)/); let percentage = interest.match(/(-?\d+\.?\d+)/);
currencyCode = currencyCode.toUpperCase(); currencyCode = currencyCode.toUpperCase();
var currencyData = utils.arraySet(20, 0); const currencyData = utils.arraySet(20, 0);
if (percentage) { if (percentage) {
/* /*
@@ -164,15 +164,15 @@ Currency.prototype.parse_json = function(j, shouldInterpretXrpAsIou) {
// the interest or demurrage is expressed as a yearly (per annum) // the interest or demurrage is expressed as a yearly (per annum)
// value // value
var secondsPerYear = 31536000; // 60 * 60 * 24 * 365 const secondsPerYear = 31536000; // 60 * 60 * 24 * 365
// Calculating the interest e-fold // Calculating the interest e-fold
// 0.5% demurrage is expressed 0.995, 0.005 less than 1 // 0.5% demurrage is expressed 0.995, 0.005 less than 1
// 0.5% interest is expressed as 1.005, 0.005 more than 1 // 0.5% interest is expressed as 1.005, 0.005 more than 1
var interestEfold = secondsPerYear / Math.log(1 + percentage / 100); const interestEfold = secondsPerYear / Math.log(1 + percentage / 100);
var bytes = Float.toIEEE754Double(interestEfold); const bytes = Float.toIEEE754Double(interestEfold);
for (var i = 0; i <= bytes.length; i++) { for (let i = 0; i <= bytes.length; i++) {
currencyData[8 + i] = bytes[i] & 0xff; currencyData[8 + i] = bytes[i] & 0xff;
} }
@@ -204,10 +204,10 @@ Currency.prototype.parse_human = function(j) {
*/ */
Currency.prototype._update = function() { Currency.prototype._update = function() {
var bytes = this.to_bytes(); const bytes = this.to_bytes();
// is it 0 everywhere except 12, 13, 14? // is it 0 everywhere except 12, 13, 14?
var isZeroExceptInStandardPositions = true; let isZeroExceptInStandardPositions = true;
if (!bytes) { if (!bytes) {
return; return;
@@ -219,7 +219,7 @@ Currency.prototype._update = function() {
this._interest_period = NaN; this._interest_period = NaN;
this._iso_code = ''; this._iso_code = '';
for (var i = 0; i < 20; i++) { for (let i = 0; i < 20; i++) {
isZeroExceptInStandardPositions = isZeroExceptInStandardPositions isZeroExceptInStandardPositions = isZeroExceptInStandardPositions
&& (i === 12 || i === 13 || i === 14 || bytes[i] === 0); && (i === 12 || i === 13 || i === 14 || bytes[i] === 0);
} }
@@ -249,6 +249,34 @@ Currency.prototype._update = function() {
} }
}; };
/**
* Returns copy.
*
* This copies code from UInt.copyTo so we do not call _update,
* bvecause to_bytes is very expensive.
*/
Currency.prototype.copyTo = function(d) {
d._value = this._value;
if (this._version_byte !== undefined) {
d._version_byte = this._version_byte;
}
if (!d.is_valid()) {
return d;
}
d._native = this._native;
d._type = this._type;
d._interest_start = this._interest_start;
d._interest_period = this._interest_period;
d._iso_code = this._iso_code;
return d;
};
// XXX Probably not needed anymore? // XXX Probably not needed anymore?
/* /*
Currency.prototype.parse_bytes = function(byte_array) { Currency.prototype.parse_bytes = function(byte_array) {
@@ -300,18 +328,20 @@ Currency.prototype.has_interest = function() {
/** /**
* *
* @param {number} referenceDate number of seconds since the Ripple Epoch * @param {number} referenceDate_ number of seconds since the Ripple Epoch
* (0:00 on January 1, 2000 UTC) used to calculate the * (0:00 on January 1, 2000 UTC) used to calculate the
* interest over provided interval pass in one years * interest over provided interval pass in one years
* worth of seconds to ge the yearly interest * worth of seconds to ge the yearly interest
* @returns {number} interest for provided interval, can be negative for * @returns {number} interest for provided interval, can be negative for
* demurred currencies * demurred currencies
*/ */
Currency.prototype.get_interest_at = function(referenceDate) { Currency.prototype.get_interest_at = function(referenceDate_) {
if (!this.has_interest()) { if (!this.has_interest()) {
return 0; return 0;
} }
let referenceDate = referenceDate_;
// use one year as a default period // use one year as a default period
if (!referenceDate) { if (!referenceDate) {
referenceDate = this._interest_start + 3600 * 24 * 365; referenceDate = this._interest_start + 3600 * 24 * 365;
@@ -326,13 +356,14 @@ Currency.prototype.get_interest_at = function(referenceDate) {
/ this._interest_period); / this._interest_period);
}; };
Currency.prototype.get_interest_percentage_at Currency.prototype.get_interest_percentage_at = function(referenceDate,
= function(referenceDate, decimals) { decimals
var interest = this.get_interest_at(referenceDate, decimals); ) {
let interest = this.get_interest_at(referenceDate, decimals);
// convert to percentage // convert to percentage
interest = (interest * 100) - 100; interest = (interest * 100) - 100;
var decimalMultiplier = decimals ? Math.pow(10, decimals) : 100; const decimalMultiplier = decimals ? Math.pow(10, decimals) : 100;
// round to two decimals behind the dot // round to two decimals behind the dot
return Math.round(interest * decimalMultiplier) / decimalMultiplier; return Math.round(interest * decimalMultiplier) / decimalMultiplier;
@@ -347,18 +378,14 @@ Currency.prototype.get_interest_percentage_at
// return UInt.prototype.is_valid() && ...; // return UInt.prototype.is_valid() && ...;
// }; // };
Currency.prototype.to_json = function(opts) { Currency.prototype.to_json = function(opts = {}) {
if (!this.is_valid()) { if (!this.is_valid()) {
// XXX This is backwards compatible behavior, but probably not very good. // XXX This is backwards compatible behavior, but probably not very good.
return 'XRP'; return 'XRP';
} }
if (!opts) { let currency;
opts = {}; const fullName = opts && opts.full_name ? ' - ' + opts.full_name : '';
}
var currency;
var fullName = opts && opts.full_name ? ' - ' + opts.full_name : '';
opts.show_interest = opts.show_interest !== undefined opts.show_interest = opts.show_interest !== undefined
? opts.show_interest ? opts.show_interest
: this.has_interest(); : this.has_interest();
@@ -366,8 +393,8 @@ Currency.prototype.to_json = function(opts) {
if (!opts.force_hex && /^[A-Z0-9]{3}$/.test(this._iso_code)) { if (!opts.force_hex && /^[A-Z0-9]{3}$/.test(this._iso_code)) {
currency = this._iso_code + fullName; currency = this._iso_code + fullName;
if (opts.show_interest) { if (opts.show_interest) {
var decimals = !isNaN(opts.decimals) ? opts.decimals : undefined; const decimals = !isNaN(opts.decimals) ? opts.decimals : undefined;
var interestPercentage = this.has_interest() const interestPercentage = this.has_interest()
? this.get_interest_percentage_at( ? this.get_interest_percentage_at(
this._interest_start + 3600 * 24 * 365, decimals this._interest_start + 3600 * 24 * 365, decimals
) )

View File

@@ -24,6 +24,17 @@ const OrderBookUtils = require('./orderbookutils');
const log = require('./log').internal.sub('orderbook'); const log = require('./log').internal.sub('orderbook');
const IOUValue = require('./iouvalue').IOUValue; const IOUValue = require('./iouvalue').IOUValue;
function _sortOffers(a, b) {
const aQuality = OrderBookUtils.getOfferQuality(a, this._currencyGets);
const bQuality = OrderBookUtils.getOfferQuality(b, this._currencyGets);
return aQuality._value.comparedTo(bQuality._value);
}
function _sortOffersQuick(a, b) {
return a.qualityHex.localeCompare(b.qualityHex);
}
/** /**
* @constructor OrderBook * @constructor OrderBook
* @param {Remote} remote * @param {Remote} remote
@@ -37,7 +48,8 @@ const IOUValue = require('./iouvalue').IOUValue;
*/ */
function OrderBook(remote, function OrderBook(remote,
currencyGets, issuerGets, currencyPays, issuerPays, key) { currencyGets, issuerGets, currencyPays, issuerPays, key
) {
EventEmitter.call(this); EventEmitter.call(this);
const self = this; const self = this;
@@ -1343,22 +1355,22 @@ OrderBook.prototype.computeAutobridgedOffers = function() {
}; };
OrderBook.prototype.computeAutobridgedOffersWrapper = function() { OrderBook.prototype.computeAutobridgedOffersWrapper = function() {
var startTime = Date.now(); const startTime = Date.now();
this.computeAutobridgedOffers(); this.computeAutobridgedOffers();
this.mergeDirectAndAutobridgedBooks(); this.mergeDirectAndAutobridgedBooks();
var lasted = (Date.now() - startTime); const lasted = (Date.now() - startTime);
const newMult = const newMult =
((lasted * 2 / OrderBook.AUTOBRIDGE_CALCULATE_THROTTLE_TIME) << 0) + 1; Math.floor(lasted * 2 / OrderBook.AUTOBRIDGE_CALCULATE_THROTTLE_TIME) + 1;
if (newMult !== this._autobridgeThrottleTimeMultiplier) { if (newMult !== this._autobridgeThrottleTimeMultiplier) {
this._autobridgeThrottleTimeMultiplier = newMult; this._autobridgeThrottleTimeMultiplier = newMult;
this.createDebouncedOffersWrapper(); this.createDebouncedOffersWrapper();
} }
} };
OrderBook.prototype.createDebouncedOffersWrapper = function() { OrderBook.prototype.createDebouncedOffersWrapper = function() {
const m = this._autobridgeThrottleTimeMultiplier; const m = this._autobridgeThrottleTimeMultiplier;
this.computeAutobridgedOffersThrottled = this.computeAutobridgedOffersThrottled =
_.debounce( _.debounce(
_.throttle( _.throttle(
this.computeAutobridgedOffersWrapper, this.computeAutobridgedOffersWrapper,
@@ -1366,20 +1378,9 @@ OrderBook.prototype.createDebouncedOffersWrapper = function() {
{leading: true, trailing: true}), {leading: true, trailing: true}),
OrderBook.AUTOBRIDGE_CALCULATE_DEBOUNCE_TIME, OrderBook.AUTOBRIDGE_CALCULATE_DEBOUNCE_TIME,
{maxWait: OrderBook.AUTOBRIDGE_CALCULATE_DEBOUNCE_MAXWAIT}); {maxWait: OrderBook.AUTOBRIDGE_CALCULATE_DEBOUNCE_MAXWAIT});
} };
function _sortOffers(a, b) {
const aQuality = OrderBookUtils.getOfferQuality(a, this._currencyGets);
const bQuality = OrderBookUtils.getOfferQuality(b, this._currencyGets);
return aQuality._value.comparedTo(bQuality._value);
}
function _sortOffersQuick(a, b) {
return a.qualityHex.localeCompare(b.qualityHex);
}
/** /**
* Merge direct and autobridged offers into a combined orderbook * Merge direct and autobridged offers into a combined orderbook
* *

View File

@@ -5,6 +5,9 @@ const assert = require('assert');
const SerializedObject = require('./serializedobject').SerializedObject; const SerializedObject = require('./serializedobject').SerializedObject;
const Types = require('./serializedtypes'); const Types = require('./serializedtypes');
const Amount = require('./amount').Amount; const Amount = require('./amount').Amount;
const Currency = require('./currency').Currency;
const UInt160 = require('./uint160').UInt160;
const IOUValue = require('./iouvalue').IOUValue;
const OrderBookUtils = {}; const OrderBookUtils = {};
function assertValidNumber(number, message) { function assertValidNumber(number, message) {
@@ -19,10 +22,17 @@ function assertValidNumber(number, message) {
* @return JSON amount object * @return JSON amount object
*/ */
function createAmount(value, currency, counterparty) { function createAmount(value, currency_, counterparty_) {
const newJSON =
{'value': value, 'currency': currency, 'issuer': counterparty}; const currency = currency_ instanceof Currency ?
return Amount.from_json(newJSON); currency_ :
Currency.from_json(currency_);
const counterparty = counterparty_ instanceof UInt160 ?
counterparty_ :
UInt160.from_json(counterparty_);
return Amount.createFast(new IOUValue(value), currency, counterparty, false);
} }
/** /**
@@ -52,11 +62,11 @@ function getIssuerFromOffer(offer) {
* @return {Amount} * @return {Amount}
*/ */
OrderBookUtils.getOfferTakerGetsFunded = function(offer) { OrderBookUtils.getOfferTakerGetsFunded = function(offer, currency_, issuer_) {
assertValidNumber(offer.taker_gets_funded, 'Taker gets funded is invalid'); assertValidNumber(offer.taker_gets_funded, 'Taker gets funded is invalid');
const currency = getCurrencyFromOffer(offer); const currency = currency_ || getCurrencyFromOffer(offer);
const issuer = getIssuerFromOffer(offer); const issuer = issuer_ || getIssuerFromOffer(offer);
return createAmount(offer.taker_gets_funded, currency, issuer); return createAmount(offer.taker_gets_funded, currency, issuer);
}; };
@@ -68,11 +78,11 @@ OrderBookUtils.getOfferTakerGetsFunded = function(offer) {
* @return {Amount} * @return {Amount}
*/ */
OrderBookUtils.getOfferTakerPaysFunded = function(offer) { OrderBookUtils.getOfferTakerPaysFunded = function(offer, currency_, issuer_) {
assertValidNumber(offer.taker_pays_funded, 'Taker gets funded is invalid'); assertValidNumber(offer.taker_pays_funded, 'Taker gets funded is invalid');
const currency = getCurrencyFromOffer(offer); const currency = currency_ || getCurrencyFromOffer(offer);
const issuer = getIssuerFromOffer(offer); const issuer = issuer_ || getIssuerFromOffer(offer);
return createAmount(offer.taker_pays_funded, currency, issuer); return createAmount(offer.taker_pays_funded, currency, issuer);
}; };
@@ -85,11 +95,11 @@ OrderBookUtils.getOfferTakerPaysFunded = function(offer) {
* @return {Amount} * @return {Amount}
*/ */
OrderBookUtils.getOfferTakerGets = function(offer) { OrderBookUtils.getOfferTakerGets = function(offer, currency_, issuer_) {
assert(typeof offer, 'object', 'Offer is invalid'); assert(typeof offer, 'object', 'Offer is invalid');
const currency = offer.TakerPays.currency; const currency = currency_ || offer.TakerPays.currency;
const issuer = offer.TakerPays.issuer; const issuer = issuer_ || offer.TakerPays.issuer;
return createAmount(offer.TakerGets, currency, issuer); return createAmount(offer.TakerGets, currency, issuer);
}; };
@@ -101,7 +111,9 @@ OrderBookUtils.getOfferTakerGets = function(offer) {
* @param {Currency} currencyGets * @param {Currency} currencyGets
*/ */
OrderBookUtils.getOfferQuality = function(offer, currencyGets) { OrderBookUtils.getOfferQuality = function(offer, currencyGets, currency_,
issuer_
) {
let amount; let amount;
if (currencyGets.has_interest()) { if (currencyGets.has_interest()) {
@@ -113,8 +125,8 @@ OrderBookUtils.getOfferQuality = function(offer, currencyGets) {
}); });
} else { } else {
const currency = getCurrencyFromOffer(offer); const currency = currency_ || getCurrencyFromOffer(offer);
const issuer = getIssuerFromOffer(offer); const issuer = issuer_ || getIssuerFromOffer(offer);
amount = createAmount(offer.quality, currency, issuer); amount = createAmount(offer.quality, currency, issuer);
} }
@@ -158,13 +170,17 @@ OrderBookUtils.convertOfferQualityToHexFromText = function(quality) {
}; };
OrderBookUtils.CURRENCY_ONE = Currency.from_json(1);
OrderBookUtils.ISSUER_ONE = UInt160.from_json(1);
/** /**
* *
*/ */
OrderBookUtils.normalizeAmount = function(value) { OrderBookUtils.normalizeAmount = function(value) {
return Amount.createFast(new IOUValue(value), OrderBookUtils.CURRENCY_ONE,
return Amount.from_number(value); OrderBookUtils.ISSUER_ONE, false);
}; };
module.exports = OrderBookUtils; module.exports = OrderBookUtils;

View File

@@ -1,11 +1,10 @@
/*eslint-disable max-len, no-param-reassign*/ /* eslint-disable max-len, no-param-reassign */
'use strict'; 'use strict';
const _ = require('lodash'); const _ = require('lodash');
const addresses = require('./addresses'); const addresses = require('./addresses');
const Meta = require('ripple-lib').Meta; const Meta = require('ripple-lib').Meta;
const Amount = require('ripple-lib').Amount;
const SerializedObject = require('ripple-lib').SerializedObject; const SerializedObject = require('ripple-lib').SerializedObject;
const Types = require('ripple-lib').types; const Types = require('ripple-lib').types;
const IOUValue = require('ripple-lib')._test.IOUValue; const IOUValue = require('ripple-lib')._test.IOUValue;
@@ -830,7 +829,7 @@ module.exports.transactionWithCreatedOffer = function(options) {
const BookDirectory = so.to_hex(); const BookDirectory = so.to_hex();
var meta = new Meta({ const meta = new Meta({
AffectedNodes: [ AffectedNodes: [
{ {
CreatedNode: { CreatedNode: {

View File

@@ -6,7 +6,6 @@ const _ = require('lodash');
const assert = require('assert-diff'); const assert = require('assert-diff');
const Remote = require('ripple-lib').Remote; const Remote = require('ripple-lib').Remote;
const Currency = require('ripple-lib').Currency; const Currency = require('ripple-lib').Currency;
const Amount = require('ripple-lib').Amount;
const addresses = require('./fixtures/addresses'); const addresses = require('./fixtures/addresses');
const fixtures = require('./fixtures/orderbook'); const fixtures = require('./fixtures/orderbook');
const IOUValue = require('ripple-lib')._test.IOUValue; const IOUValue = require('ripple-lib')._test.IOUValue;

View File

@@ -1706,7 +1706,7 @@ describe('OrderBook', function() {
setTimeout(function() { setTimeout(function() {
assert.strictEqual(numModelEvents, 1); assert.strictEqual(numModelEvents, 1);
done(); done();
}, 300) }, 300);
}); });
it('Notify - deleted node - trade', function(done) { it('Notify - deleted node - trade', function(done) {