more OrderBook performance optimizations

OrderBook: emit 'model' event only after last transaction in closed ledger
run AutobridgeCalculator only once in a ledger
This commit is contained in:
Ivan Tivonenko
2015-09-23 04:41:32 +03:00
parent f282585c3f
commit 3e17d91edf
5 changed files with 319 additions and 208 deletions

View File

@@ -2,7 +2,6 @@
const _ = require('lodash'); const _ = require('lodash');
const assert = require('assert'); const assert = require('assert');
const UInt160 = require('./uint160').UInt160;
const Amount = require('./amount').Amount; const Amount = require('./amount').Amount;
const Utils = require('./orderbookutils'); const Utils = require('./orderbookutils');
@@ -38,15 +37,39 @@ AutobridgeCalculator.NULL_AMOUNT = Utils.normalizeAmount('0');
* @return {Array} * @return {Array}
*/ */
AutobridgeCalculator.prototype.calculate = function() { AutobridgeCalculator.prototype.calculate = function(callback) {
let legOnePointer = 0; const legOnePointer = 0;
let legTwoPointer = 0; const legTwoPointer = 0;
const offersAutobridged = []; const offersAutobridged = [];
this.clearOwnerFundsLeftover(); this.clearOwnerFundsLeftover();
this._calculateInternal(legOnePointer, legTwoPointer, offersAutobridged,
callback);
};
AutobridgeCalculator.prototype._calculateInternal = function(
legOnePointer_, legTwoPointer_, offersAutobridged, callback
) {
let legOnePointer = legOnePointer_;
let legTwoPointer = legTwoPointer_;
const startTime = Date.now();
while (this.legOneOffers[legOnePointer] && this.legTwoOffers[legTwoPointer]) { while (this.legOneOffers[legOnePointer] && this.legTwoOffers[legTwoPointer]) {
// manually implement cooperative multitasking that yields after 30ms
// of execution so user's browser stays responsive
const lasted = (Date.now() - startTime);
if (lasted > 30) {
setTimeout(() => {
this._calculateInternal(legOnePointer, legTwoPointer, offersAutobridged,
callback);
}, 0);
return;
}
const legOneOffer = this.legOneOffers[legOnePointer]; const legOneOffer = this.legOneOffers[legOnePointer];
const legTwoOffer = this.legTwoOffers[legTwoPointer]; const legTwoOffer = this.legTwoOffers[legTwoPointer];
const leftoverFunds = this.getLeftoverOwnerFunds(legOneOffer.Account); const leftoverFunds = this.getLeftoverOwnerFunds(legOneOffer.Account);
@@ -103,7 +126,7 @@ AutobridgeCalculator.prototype.calculate = function() {
offersAutobridged.push(autobridgedOffer); offersAutobridged.push(autobridgedOffer);
} }
return offersAutobridged; callback(offersAutobridged);
}; };
/** /**
@@ -308,7 +331,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;
@@ -432,7 +456,7 @@ function(legOneOffer, takerGets) {
const legOneQuality = Utils.getOfferQuality(legOneOffer, this._currencyGets); const legOneQuality = Utils.getOfferQuality(legOneOffer, this._currencyGets);
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

@@ -74,29 +74,30 @@ function OrderBook(remote,
this._gotOffersFromLegOne = false; this._gotOffersFromLegOne = false;
this._gotOffersFromLegTwo = false; this._gotOffersFromLegTwo = false;
this._waitingForOffers = false;
this._offersModifiedAt = 0;
this._transactionsLeft = 0;
this._calculatorRunning = false;
this.sortOffers = this._currencyGets.has_interest() ? this.sortOffers = this._currencyGets.has_interest() ?
_sortOffers.bind(this) : _sortOffersQuick; _sortOffers.bind(this) : _sortOffersQuick;
this.notifyDirectOffersChanged =
_.debounce(
this.notifyDirectOffersChangedInternal,
OrderBook.NOTIFY_TIMEOUT,
{maxWait: OrderBook.NOTIFY_MAXWAIT});
this._isAutobridgeable = !this._currencyGets.is_native() this._isAutobridgeable = !this._currencyGets.is_native()
&& !this._currencyPays.is_native(); && !this._currencyPays.is_native();
this._autobridgeThrottleTimeMultiplier = 1;
this.createDebouncedOffersWrapper();
function computeAutobridgedOffersWrapperOne() { function computeAutobridgedOffersWrapperOne() {
if (!self._gotOffersFromLegOne) {
self._gotOffersFromLegOne = true; self._gotOffersFromLegOne = true;
self.computeAutobridgedOffersThrottled(); self.computeAutobridgedOffersWrapper();
}
} }
function computeAutobridgedOffersWrapperTwo() { function computeAutobridgedOffersWrapperTwo() {
if (!self._gotOffersFromLegTwo) {
self._gotOffersFromLegTwo = true; self._gotOffersFromLegTwo = true;
self.computeAutobridgedOffersThrottled(); self.computeAutobridgedOffersWrapper();
}
} }
function onDisconnect() { function onDisconnect() {
@@ -125,8 +126,12 @@ function OrderBook(remote,
}); });
} }
function updateFundedAmountsWrapper(transaction) { function onTransactionWrapper(transaction) {
self.updateFundedAmounts(transaction); self.onTransaction(transaction);
}
function onLedgerClosedWrapper(message) {
self.onLedgerClosed(message);
} }
function listenersModified(action, event) { function listenersModified(action, event) {
@@ -139,7 +144,8 @@ function OrderBook(remote,
self._shouldSubscribe = true; self._shouldSubscribe = true;
self.subscribe(); self.subscribe();
self._remote.on('transaction', updateFundedAmountsWrapper); self._remote.on('transaction', onTransactionWrapper);
self._remote.on('ledger_closed', onLedgerClosedWrapper);
self._remote.once('disconnect', onDisconnect); self._remote.once('disconnect', onDisconnect);
if (self._isAutobridgeable) { if (self._isAutobridgeable) {
@@ -168,7 +174,8 @@ function OrderBook(remote,
this.on('unsubscribe', function() { this.on('unsubscribe', function() {
self.resetCache(); self.resetCache();
self._remote.removeListener('transaction', updateFundedAmountsWrapper); self._remote.removeListener('transaction', onTransactionWrapper);
self._remote.removeListener('ledger_closed', onLedgerClosedWrapper);
self._remote.removeListener('disconnect', onDisconnect); self._remote.removeListener('disconnect', onDisconnect);
self._gotOffersFromLegOne = false; self._gotOffersFromLegOne = false;
@@ -199,16 +206,6 @@ OrderBook.EVENTS = [
OrderBook.DEFAULT_TRANSFER_RATE = Amount.from_json(1000000000); OrderBook.DEFAULT_TRANSFER_RATE = Amount.from_json(1000000000);
OrderBook.NOTIFY_TIMEOUT = 100;
OrderBook.NOTIFY_MAXWAIT = 250;
OrderBook.AUTOBRIDGE_CALCULATE_THROTTLE_TIME = 1000;
OrderBook.AUTOBRIDGE_CALCULATE_DEBOUNCE_TIME = 250;
OrderBook.AUTOBRIDGE_CALCULATE_DEBOUNCE_MAXWAIT = 500;
OrderBook.ZERO_NATIVE_AMOUNT = Amount.from_json('0'); OrderBook.ZERO_NATIVE_AMOUNT = Amount.from_json('0');
OrderBook.ZERO_NORMALIZED_AMOUNT = OrderBookUtils.normalizeAmount('0'); OrderBook.ZERO_NORMALIZED_AMOUNT = OrderBookUtils.normalizeAmount('0');
@@ -345,6 +342,8 @@ OrderBook.prototype.requestOffers = function(callback = function() {},
log.info('requesting offers', this._key); log.info('requesting offers', this._key);
} }
this._synchronized = false;
if (this._isAutobridgeable && !internal) { if (this._isAutobridgeable && !internal) {
this._gotOffersFromLegOne = false; this._gotOffersFromLegOne = false;
this._gotOffersFromLegTwo = false; this._gotOffersFromLegTwo = false;
@@ -359,9 +358,12 @@ OrderBook.prototype.requestOffers = function(callback = function() {},
return; return;
} }
self._waitingForOffers = false;
if (!Array.isArray(res.offers)) { if (!Array.isArray(res.offers)) {
// XXX What now? // XXX What now?
callback(new Error('Invalid response')); callback(new Error('Invalid response'));
self.emit('model', []);
return; return;
} }
@@ -370,7 +372,12 @@ OrderBook.prototype.requestOffers = function(callback = function() {},
} }
self.setOffers(res.offers); self.setOffers(res.offers);
self._synchronized = true; self._synchronized = true;
self.notifyDirectOffersChanged();
if (self._isAutobridgeable) {
self.computeAutobridgedOffersWrapper();
} else {
self.emit('model', self._offers);
}
callback(null, self._offers); callback(null, self._offers);
} }
@@ -381,9 +388,12 @@ OrderBook.prototype.requestOffers = function(callback = function() {},
log.info('failed to request offers', self._key, err); log.info('failed to request offers', self._key, err);
} }
self._waitingForOffers = false;
callback(err); callback(err);
} }
this._waitingForOffers = true;
const request = this._remote.requestBookOffers(this.toJSON()); const request = this._remote.requestBookOffers(this.toJSON());
request.once('success', handleOffers); request.once('success', handleOffers);
request.once('error', handleError); request.once('error', handleError);
@@ -479,18 +489,6 @@ OrderBook.prototype.subscribeTransactions = function(callback) {
return request; return request;
}; };
/**
* Handles notifying listeners that direct offers have changed. For autobridged
* books, an additional merge step is also performed
*/
OrderBook.prototype.notifyDirectOffersChangedInternal = function() {
if (this._isAutobridgeable) {
this.mergeDirectAndAutobridgedBooks();
} else {
this.emit('model', this._offers);
}
};
/** /**
* Reset cached owner's funds, offer counts, and offer sums * Reset cached owner's funds, offer counts, and offer sums
@@ -828,6 +826,32 @@ OrderBook.prototype.isBalanceChangeNode = function(node) {
return true; return true;
}; };
OrderBook.prototype._canRunAutobridgeCalc = function(): boolean {
return !this._calculatorRunning;
}
OrderBook.prototype.onTransaction = function(transaction) {
this.updateFundedAmounts(transaction);
if (--this._transactionsLeft === 0 && !this._waitingForOffers) {
const lastClosedLedger = this._remote.getLedgerSequence() - 1;
if (this._isAutobridgeable) {
if (this._canRunAutobridgeCalc()) {
if (this._legOneBook._offersModifiedAt === lastClosedLedger ||
this._legTwoBook._offersModifiedAt === lastClosedLedger
) {
this.computeAutobridgedOffersWrapper();
} else if (this._offersModifiedAt === lastClosedLedger) {
this.mergeDirectAndAutobridgedBooks();
}
}
} else if (this._offersModifiedAt === lastClosedLedger) {
this.emit('model', this._offers);
}
}
}
/** /**
* Updates funded amounts/balances using modified balance nodes * Updates funded amounts/balances using modified balance nodes
* *
@@ -929,6 +953,16 @@ OrderBook.prototype.updateOwnerOffersFundedAmount = function(account) {
}); });
}; };
OrderBook.prototype.onLedgerClosed = function(message) {
if (!message || (message && !_.isNumber(message.txn_count)) ||
!this._subscribed || this._destroyed || this._waitingForOffers
) {
return;
}
this._transactionsLeft = message.txn_count;
}
/** /**
* Notify orderbook of a relevant transaction * Notify orderbook of a relevant transaction
* *
@@ -1029,7 +1063,7 @@ OrderBook.prototype.notify = function(transaction) {
this.emit('transaction', transaction); this.emit('transaction', transaction);
this.notifyDirectOffersChanged(); this._offersModifiedAt = this._remote.getLedgerSequence() - 1;
if (!takerGetsTotal.is_zero()) { if (!takerGetsTotal.is_zero()) {
this.emit('trade', takerPaysTotal, takerGetsTotal); this.emit('trade', takerPaysTotal, takerGetsTotal);
@@ -1312,15 +1346,13 @@ OrderBook.prototype.is_valid = function() {
* IOU:XRP and XRP:IOU books * IOU:XRP and XRP:IOU books
*/ */
OrderBook.prototype.computeAutobridgedOffers = function() { OrderBook.prototype.computeAutobridgedOffers = function(callback = function() {}
) {
assert(!this._currencyGets.is_native() && !this._currencyPays.is_native(), assert(!this._currencyGets.is_native() && !this._currencyPays.is_native(),
'Autobridging is only for IOU:IOU orderbooks'); 'Autobridging is only for IOU:IOU orderbooks');
if (this._destroyed) {
return;
}
if (!this._gotOffersFromLegOne || !this._gotOffersFromLegTwo) { if (this._destroyed) {
return; return;
} }
@@ -1333,35 +1365,28 @@ OrderBook.prototype.computeAutobridgedOffers = function() {
this._issuerPays this._issuerPays
); );
this._offersAutobridged = autobridgeCalculator.calculate(); const lastClosedLedger = this._remote.getLedgerSequence() - 1;
autobridgeCalculator.calculate((autobridgedOffers) => {
this._offersAutobridged = autobridgedOffers;
callback();
});
}; };
OrderBook.prototype.computeAutobridgedOffersWrapper = function() { OrderBook.prototype.computeAutobridgedOffersWrapper = function() {
var startTime = Date.now(); if (!this._gotOffersFromLegOne || !this._gotOffersFromLegTwo ||
this.computeAutobridgedOffers(); !this._synchronized || this._destroyed || this._calculatorRunning
this.mergeDirectAndAutobridgedBooks(); ) {
var lasted = (Date.now() - startTime); return;
const newMult =
((lasted * 2 / OrderBook.AUTOBRIDGE_CALCULATE_THROTTLE_TIME) << 0) + 1;
if (newMult !== this._autobridgeThrottleTimeMultiplier) {
this._autobridgeThrottleTimeMultiplier = newMult;
this.createDebouncedOffersWrapper();
} }
}
OrderBook.prototype.createDebouncedOffersWrapper = function() {
const m = this._autobridgeThrottleTimeMultiplier;
this.computeAutobridgedOffersThrottled =
_.debounce(
_.throttle(
this.computeAutobridgedOffersWrapper,
OrderBook.AUTOBRIDGE_CALCULATE_THROTTLE_TIME * m,
{leading: true, trailing: true}),
OrderBook.AUTOBRIDGE_CALCULATE_DEBOUNCE_TIME,
{maxWait: OrderBook.AUTOBRIDGE_CALCULATE_DEBOUNCE_MAXWAIT});
}
this._calculatorRunning = true;
const startTime = Date.now();
this.computeAutobridgedOffers(() => {
this.mergeDirectAndAutobridgedBooks();
this._calculatorRunning = false;
});
};
function _sortOffers(a, b) { function _sortOffers(a, b) {
const aQuality = OrderBookUtils.getOfferQuality(a, this._currencyGets); const aQuality = OrderBookUtils.getOfferQuality(a, this._currencyGets);

View File

@@ -16,6 +16,7 @@ describe('OrderBook Autobridging', function() {
function createRemote() { function createRemote() {
const remote = new Remote(); const remote = new Remote();
remote._ledger_current_index = 32570;
remote.isConnected = function() { remote.isConnected = function() {
return true; return true;
}; };
@@ -37,7 +38,7 @@ describe('OrderBook Autobridging', function() {
assert.deepEqual(book._legTwoBook._currencyPays.to_hex(), Currency.from_json('XRP').to_hex()); assert.deepEqual(book._legTwoBook._currencyPays.to_hex(), Currency.from_json('XRP').to_hex());
}); });
it('Compute autobridged offers', function() { it('Compute autobridged offers', function(done) {
const book = createRemote().createOrderBook({ const book = createRemote().createOrderBook({
currency_gets: 'EUR', currency_gets: 'EUR',
issuer_gets: addresses.ISSUER, issuer_gets: addresses.ISSUER,
@@ -56,8 +57,7 @@ describe('OrderBook Autobridging', function() {
book._legTwoBook.setOffers(legTwoOffers); book._legTwoBook.setOffers(legTwoOffers);
book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true; book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true;
book.computeAutobridgedOffers(); book.computeAutobridgedOffers(() => {
assert.strictEqual(book._offersAutobridged.length, 1); assert.strictEqual(book._offersAutobridged.length, 1);
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '17.07639524223001'); assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '17.07639524223001');
@@ -67,9 +67,13 @@ describe('OrderBook Autobridging', function() {
assert.strictEqual(book._offersAutobridged[0].taker_pays_funded, '58.61727326122974'); assert.strictEqual(book._offersAutobridged[0].taker_pays_funded, '58.61727326122974');
assert(book._offersAutobridged[0].autobridged); assert(book._offersAutobridged[0].autobridged);
done();
}); });
it('Compute autobridged offers - leg one partially funded', function() { });
it('Compute autobridged offers - leg one partially funded', function(done) {
const book = createRemote().createOrderBook({ const book = createRemote().createOrderBook({
currency_gets: 'EUR', currency_gets: 'EUR',
issuer_gets: addresses.ISSUER, issuer_gets: addresses.ISSUER,
@@ -90,17 +94,20 @@ describe('OrderBook Autobridging', function() {
book._legTwoBook.setOffers(legTwoOffers); book._legTwoBook.setOffers(legTwoOffers);
book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true; book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true;
book.computeAutobridgedOffers(); book.computeAutobridgedOffers(() => {
assert.strictEqual(book._offersAutobridged.length, 1); assert.strictEqual(book._offersAutobridged.length, 1);
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '7.273651248813431'); assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '7.273651248813431');
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '24.96789265329184'); assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '24.96789265329184');
assert(book._offersAutobridged[0].autobridged); assert(book._offersAutobridged[0].autobridged);
done();
}); });
it('Compute autobridged offers - leg two partially funded', function() { });
it('Compute autobridged offers - leg two partially funded', function(done) {
const book = createRemote().createOrderBook({ const book = createRemote().createOrderBook({
currency_gets: 'EUR', currency_gets: 'EUR',
issuer_gets: addresses.ISSUER, issuer_gets: addresses.ISSUER,
@@ -121,17 +128,20 @@ describe('OrderBook Autobridging', function() {
book._legTwoBook.setOffers(legTwoOffers); book._legTwoBook.setOffers(legTwoOffers);
book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true; book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true;
book.computeAutobridgedOffers(); book.computeAutobridgedOffers(() => {
assert.strictEqual(book._offersAutobridged.length, 1); assert.strictEqual(book._offersAutobridged.length, 1);
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '10'); assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '10');
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '34.32649132449533'); assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '34.32649132449533');
assert(book._offersAutobridged[0].autobridged); assert(book._offersAutobridged[0].autobridged);
done();
}); });
it('Compute autobridged offers - leg two transfer rate', function() { });
it('Compute autobridged offers - leg two transfer rate', function(done) {
const book = createRemote().createOrderBook({ const book = createRemote().createOrderBook({
currency_gets: 'EUR', currency_gets: 'EUR',
issuer_gets: addresses.ISSUER, issuer_gets: addresses.ISSUER,
@@ -152,15 +162,18 @@ describe('OrderBook Autobridging', function() {
book._legTwoBook.setOffers(legTwoOffers); book._legTwoBook.setOffers(legTwoOffers);
book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true; book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true;
book.computeAutobridgedOffers(); book.computeAutobridgedOffers(() => {
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '9.980039920159681'); assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '9.980039920159681');
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '34.25797537722665'); assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '34.25797537722665');
assert(book._offersAutobridged[0].autobridged); assert(book._offersAutobridged[0].autobridged);
done();
}); });
it('Compute autobridged offers - taker funds < leg two in', function() { });
it('Compute autobridged offers - taker funds < leg two in', function(done) {
const book = createRemote().createOrderBook({ const book = createRemote().createOrderBook({
currency_gets: 'EUR', currency_gets: 'EUR',
issuer_gets: addresses.ISSUER, issuer_gets: addresses.ISSUER,
@@ -185,17 +198,20 @@ describe('OrderBook Autobridging', function() {
book._legTwoBook.setOffers(legTwoOffers); book._legTwoBook.setOffers(legTwoOffers);
book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true; book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true;
book.computeAutobridgedOffers(); book.computeAutobridgedOffers(() => {
assert.strictEqual(book._offersAutobridged.length, 1); assert.strictEqual(book._offersAutobridged.length, 1);
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '108.6682345172846'); assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '108.6682345172846');
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '373.019921005'); assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '373.019921005');
assert(book._offersAutobridged[0].autobridged); assert(book._offersAutobridged[0].autobridged);
done();
}); });
it('Compute autobridged offers - leg one partially funded - owners equal', function() { });
it('Compute autobridged offers - leg one partially funded - owners equal', function(done) {
const book = createRemote().createOrderBook({ const book = createRemote().createOrderBook({
currency_gets: 'EUR', currency_gets: 'EUR',
issuer_gets: addresses.ISSUER, issuer_gets: addresses.ISSUER,
@@ -218,17 +234,20 @@ describe('OrderBook Autobridging', function() {
book._legTwoBook.setOffers(legTwoOffers); book._legTwoBook.setOffers(legTwoOffers);
book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true; book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true;
book.computeAutobridgedOffers(); book.computeAutobridgedOffers(() => {
assert.strictEqual(book._offersAutobridged.length, 1); assert.strictEqual(book._offersAutobridged.length, 1);
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '17.07639524223001'); assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '17.07639524223001');
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '58.61727326122974'); assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '58.61727326122974');
assert(book._offersAutobridged[0].autobridged); assert(book._offersAutobridged[0].autobridged);
done();
}); });
it('Compute autobridged offers - leg one partially funded - owners equal - leg two in > leg one out', function() { });
it('Compute autobridged offers - leg one partially funded - owners equal - leg two in > leg one out', function(done) {
const book = createRemote().createOrderBook({ const book = createRemote().createOrderBook({
currency_gets: 'EUR', currency_gets: 'EUR',
issuer_gets: addresses.ISSUER, issuer_gets: addresses.ISSUER,
@@ -254,17 +273,20 @@ describe('OrderBook Autobridging', function() {
book._legTwoBook.setOffers(legTwoOffers); book._legTwoBook.setOffers(legTwoOffers);
book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true; book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true;
book.computeAutobridgedOffers(); book.computeAutobridgedOffers(() => {
assert.strictEqual(book._offersAutobridged.length, 1); assert.strictEqual(book._offersAutobridged.length, 1);
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '108.6682345172846'); assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '108.6682345172846');
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '373.0199210049999'); assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '373.0199210049999');
assert(book._offersAutobridged[0].autobridged); assert(book._offersAutobridged[0].autobridged);
done();
}); });
it('Compute autobridged offers - leg one consumes leg two fully', function() { });
it('Compute autobridged offers - leg one consumes leg two fully', function(done) {
const book = createRemote().createOrderBook({ const book = createRemote().createOrderBook({
currency_gets: 'EUR', currency_gets: 'EUR',
issuer_gets: addresses.ISSUER, issuer_gets: addresses.ISSUER,
@@ -283,8 +305,7 @@ describe('OrderBook Autobridging', function() {
book._legTwoBook.setOffers(legTwoOffers); book._legTwoBook.setOffers(legTwoOffers);
book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true; book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true;
book.computeAutobridgedOffers(); book.computeAutobridgedOffers(() => {
assert.strictEqual(book._offersAutobridged.length, 2); assert.strictEqual(book._offersAutobridged.length, 2);
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '17.07639524223001'); assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '17.07639524223001');
@@ -296,9 +317,13 @@ describe('OrderBook Autobridging', function() {
assert.strictEqual(book._offersAutobridged[1].TakerPays.value, '314.4026477437702'); assert.strictEqual(book._offersAutobridged[1].TakerPays.value, '314.4026477437702');
assert(book._offersAutobridged[1].autobridged); assert(book._offersAutobridged[1].autobridged);
done();
}); });
it('Compute autobridged offers - leg two consumes first leg one offer fully', function() { });
it('Compute autobridged offers - leg two consumes first leg one offer fully', function(done) {
const book = createRemote().createOrderBook({ const book = createRemote().createOrderBook({
currency_gets: 'EUR', currency_gets: 'EUR',
issuer_gets: addresses.ISSUER, issuer_gets: addresses.ISSUER,
@@ -321,8 +346,7 @@ describe('OrderBook Autobridging', function() {
book._legTwoBook.setOffers(legTwoOffers); book._legTwoBook.setOffers(legTwoOffers);
book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true; book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true;
book.computeAutobridgedOffers(); book.computeAutobridgedOffers(() => {
assert.strictEqual(book._offersAutobridged.length, 2); assert.strictEqual(book._offersAutobridged.length, 2);
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '108.6682345172846'); assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '108.6682345172846');
@@ -334,9 +358,13 @@ describe('OrderBook Autobridging', function() {
assert.strictEqual(book._offersAutobridged[1].TakerPays.value, '213.1791399943838'); assert.strictEqual(book._offersAutobridged[1].TakerPays.value, '213.1791399943838');
assert(book._offersAutobridged[1].autobridged); assert(book._offersAutobridged[1].autobridged);
done();
}); });
it('Compute autobridged offers - owners equal', function() { });
it('Compute autobridged offers - owners equal', function(done) {
const book = createRemote().createOrderBook({ const book = createRemote().createOrderBook({
currency_gets: 'EUR', currency_gets: 'EUR',
issuer_gets: addresses.ISSUER, issuer_gets: addresses.ISSUER,
@@ -360,8 +388,7 @@ describe('OrderBook Autobridging', function() {
book._legTwoBook.setOffers(legTwoOffers); book._legTwoBook.setOffers(legTwoOffers);
book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true; book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true;
book.computeAutobridgedOffers(); book.computeAutobridgedOffers(() => {
assert.strictEqual(book._offersAutobridged.length, 2); assert.strictEqual(book._offersAutobridged.length, 2);
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '17.07639524223001'); assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '17.07639524223001');
@@ -373,9 +400,13 @@ describe('OrderBook Autobridging', function() {
assert.strictEqual(book._offersAutobridged[1].TakerPays.value, '24.96789265329184'); assert.strictEqual(book._offersAutobridged[1].TakerPays.value, '24.96789265329184');
assert(book._offersAutobridged[1].autobridged); assert(book._offersAutobridged[1].autobridged);
done();
}); });
it('Compute autobridged offers - owners equal - leg one overfunded', function() { });
it('Compute autobridged offers - owners equal - leg one overfunded', function(done) {
const book = createRemote().createOrderBook({ const book = createRemote().createOrderBook({
currency_gets: 'EUR', currency_gets: 'EUR',
issuer_gets: addresses.ISSUER, issuer_gets: addresses.ISSUER,
@@ -400,8 +431,7 @@ describe('OrderBook Autobridging', function() {
book._legTwoBook.setOffers(legTwoOffers); book._legTwoBook.setOffers(legTwoOffers);
book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true; book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true;
book.computeAutobridgedOffers(); book.computeAutobridgedOffers(() => {
assert.strictEqual(book._offersAutobridged.length, 2); assert.strictEqual(book._offersAutobridged.length, 2);
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '17.07639524223001'); assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '17.07639524223001');
@@ -413,9 +443,13 @@ describe('OrderBook Autobridging', function() {
assert.strictEqual(book._offersAutobridged[1].TakerPays.value, '314.4026477437702'); assert.strictEqual(book._offersAutobridged[1].TakerPays.value, '314.4026477437702');
assert(book._offersAutobridged[1].autobridged); assert(book._offersAutobridged[1].autobridged);
done();
}); });
it('Compute autobridged offers - TakerPays < Quality * TakerGets', function() { });
it('Compute autobridged offers - TakerPays < Quality * TakerGets', function(done) {
const book = createRemote().createOrderBook({ const book = createRemote().createOrderBook({
currency_gets: 'EUR', currency_gets: 'EUR',
issuer_gets: addresses.ISSUER, issuer_gets: addresses.ISSUER,
@@ -458,17 +492,20 @@ describe('OrderBook Autobridging', function() {
]); ]);
book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true; book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true;
book.computeAutobridgedOffers(); book.computeAutobridgedOffers(() => {
assert.strictEqual(book._offersAutobridged.length, 1); assert.strictEqual(book._offersAutobridged.length, 1);
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '75'); assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '75');
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '75'); assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '75');
assert(book._offersAutobridged[0].autobridged); assert(book._offersAutobridged[0].autobridged);
done();
}); });
it('Compute autobridged offers - update funded amount', function() { });
it('Compute autobridged offers - update funded amount', function(done) {
const book = createRemote().createOrderBook({ const book = createRemote().createOrderBook({
currency_gets: 'EUR', currency_gets: 'EUR',
issuer_gets: addresses.ISSUER, issuer_gets: addresses.ISSUER,
@@ -534,8 +571,7 @@ describe('OrderBook Autobridging', function() {
]); ]);
book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true; book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true;
book.computeAutobridgedOffers(); book.computeAutobridgedOffers(() => {
assert.strictEqual(book._offersAutobridged.length, 3); assert.strictEqual(book._offersAutobridged.length, 3);
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '90'); assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '90');
@@ -552,9 +588,13 @@ describe('OrderBook Autobridging', function() {
assert.strictEqual(book._offersAutobridged[2].TakerPays.value, '80'); assert.strictEqual(book._offersAutobridged[2].TakerPays.value, '80');
assert(book._offersAutobridged[2].autobridged); assert(book._offersAutobridged[2].autobridged);
done();
}); });
it('Compute autobridged offers - update funded amount - owners equal', function() { });
it('Compute autobridged offers - update funded amount - owners equal', function(done) {
const book = createRemote().createOrderBook({ const book = createRemote().createOrderBook({
currency_gets: 'EUR', currency_gets: 'EUR',
issuer_gets: addresses.ISSUER, issuer_gets: addresses.ISSUER,
@@ -620,8 +660,7 @@ describe('OrderBook Autobridging', function() {
]); ]);
book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true; book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true;
book.computeAutobridgedOffers(); book.computeAutobridgedOffers(() => {
assert.strictEqual(book._offersAutobridged.length, 3); assert.strictEqual(book._offersAutobridged.length, 3);
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '90'); assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '90');
@@ -638,9 +677,13 @@ describe('OrderBook Autobridging', function() {
assert.strictEqual(book._offersAutobridged[2].TakerPays.value, '100'); assert.strictEqual(book._offersAutobridged[2].TakerPays.value, '100');
assert(book._offersAutobridged[2].autobridged); assert(book._offersAutobridged[2].autobridged);
done();
}); });
it('Compute autobridged offers - update funded amount - first two owners equal', function() { });
it('Compute autobridged offers - update funded amount - first two owners equal', function(done) {
const book = createRemote().createOrderBook({ const book = createRemote().createOrderBook({
currency_gets: 'EUR', currency_gets: 'EUR',
issuer_gets: addresses.ISSUER, issuer_gets: addresses.ISSUER,
@@ -717,8 +760,7 @@ describe('OrderBook Autobridging', function() {
]); ]);
book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true; book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true;
book.computeAutobridgedOffers(); book.computeAutobridgedOffers(() => {
assert.strictEqual(book._offersAutobridged.length, 4); assert.strictEqual(book._offersAutobridged.length, 4);
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '90'); assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '90');
@@ -740,9 +782,13 @@ describe('OrderBook Autobridging', function() {
assert.strictEqual(book._offersAutobridged[3].TakerPays.value, '80'); assert.strictEqual(book._offersAutobridged[3].TakerPays.value, '80');
assert(book._offersAutobridged[3].autobridged); assert(book._offersAutobridged[3].autobridged);
done();
}); });
it('Compute autobridged offers - unfunded offer - owners equal', function() { });
it('Compute autobridged offers - unfunded offer - owners equal', function(done) {
const book = createRemote().createOrderBook({ const book = createRemote().createOrderBook({
currency_gets: 'EUR', currency_gets: 'EUR',
issuer_gets: addresses.ISSUER, issuer_gets: addresses.ISSUER,
@@ -785,13 +831,16 @@ describe('OrderBook Autobridging', function() {
]); ]);
book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true; book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true;
book.computeAutobridgedOffers(); book.computeAutobridgedOffers(() => {
assert.strictEqual(book._offersAutobridged.length, 1); assert.strictEqual(book._offersAutobridged.length, 1);
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '75'); assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '75');
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '75'); assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '75');
assert(book._offersAutobridged[0].autobridged); assert(book._offersAutobridged[0].autobridged);
done();
});
}); });
}); });

View File

@@ -15,7 +15,7 @@ describe('OrderBook', function() {
function createRemote() { function createRemote() {
const remote = new Remote(); const remote = new Remote();
remote._ledger_current_index = 32570;
remote.isConnected = function() { remote.isConnected = function() {
return true; return true;
}; };
@@ -1613,9 +1613,15 @@ describe('OrderBook', function() {
const offer2 = fixtures.transactionWithCreatedOffer(); const offer2 = fixtures.transactionWithCreatedOffer();
const offer3 = fixtures.transactionWithCreatedOffer(); const offer3 = fixtures.transactionWithCreatedOffer();
remote.emit('ledger_closed', {txn_count: 3});
book.notify(offer); book.notify(offer);
remote.emit('transaction', offer);
book.notify(offer2); book.notify(offer2);
remote.emit('transaction', offer2);
book.notify(offer3); book.notify(offer3);
remote.emit('transaction', offer3);
assert.strictEqual(numTransactionEvents, 3); assert.strictEqual(numTransactionEvents, 3);
assert.strictEqual(numOfferAddedEvents, 3); assert.strictEqual(numOfferAddedEvents, 3);
@@ -1706,7 +1712,10 @@ describe('OrderBook', function() {
const message = fixtures.transactionWithDeletedOffer(); const message = fixtures.transactionWithDeletedOffer();
remote.emit('ledger_closed', {txn_count: 1});
book.notify(message); book.notify(message);
remote.emit('transaction', message);
assert.strictEqual(numTransactionEvents, 1); assert.strictEqual(numTransactionEvents, 1);
assert.strictEqual(numTradeEvents, 1); assert.strictEqual(numTradeEvents, 1);
@@ -1865,7 +1874,10 @@ describe('OrderBook', function() {
const message = fixtures.transactionWithModifiedOffer(); const message = fixtures.transactionWithModifiedOffer();
remote.emit('ledger_closed', {txn_count: 1});
book.notify(message); book.notify(message);
remote.emit('transaction', message);
assert.strictEqual(numTransactionEvents, 1); assert.strictEqual(numTransactionEvents, 1);
assert.strictEqual(numTradeEvents, 1); assert.strictEqual(numTradeEvents, 1);

View File

@@ -671,6 +671,7 @@ describe('Remote', function() {
function() { function() {
const message = require('./fixtures/transaction-offercreate'); const message = require('./fixtures/transaction-offercreate');
let i = 0; let i = 0;
remote._ledger_current_index = 32570;
const orderbook = remote.createOrderBook({ const orderbook = remote.createOrderBook({
currency_gets: 'USD', currency_gets: 'USD',
issuer_gets: 'rJy64aCJLP3vf8o3WPKn4iQKtfpjh6voAR', issuer_gets: 'rJy64aCJLP3vf8o3WPKn4iQKtfpjh6voAR',