diff --git a/bin/rlint.js b/bin/rlint.js new file mode 100755 index 000000000..9b37668d8 --- /dev/null +++ b/bin/rlint.js @@ -0,0 +1,159 @@ +#!/usr/bin/node + +var async = require('async'); +var Remote = require('../src/js/remote').Remote; +var Transaction = require('../src/js/transaction').Transaction; +var UInt160 = require('../src/js/uint160').UInt160; +var Amount = require('../src/js/amount').Amount; + +var book_key = function (book) { + return book.taker_pays.currency + + ":" + book.taker_pays.issuer + + ":" + book.taker_gets.currency + + ":" + book.taker_gets.issuer; +}; + +var book_key_cross = function (book) { + return book.taker_gets.currency + + ":" + book.taker_gets.issuer + + ":" + book.taker_pays.currency + + ":" + book.taker_pays.issuer; +}; + +var ledger_verify = function (ledger) { + var dir_nodes = ledger.accountState.filter(function (entry) { + return entry.LedgerEntryType === 'DirectoryNode' // Only directories + && entry.index === entry.RootIndex // Only root nodes + && 'TakerGetsCurrency' in entry; // Only offer directories + }); + + var books = {}; + + dir_nodes.forEach(function (node) { + var book = { + taker_gets: { + currency: UInt160.from_generic(node.TakerGetsCurrency).to_json(), + issuer: UInt160.from_generic(node.TakerGetsIssuer).to_json() + }, + taker_pays: { + currency: UInt160.from_generic(node.TakerPaysCurrency).to_json(), + issuer: UInt160.from_generic(node.TakerPaysIssuer).to_json() + }, + quality: Amount.from_quality(node.RootIndex) + }; + + books[book_key(book)] = book; + +// console.log(JSON.stringify(node, undefined, 2)); + }); + +// console.log(JSON.stringify(dir_entry, undefined, 2)); + console.log("#%s books: %s", ledger.ledger_index, Object.keys(books).length); + + Object.keys(books).forEach(function (key) { + var book = books[key]; + var key_cross = book_key_cross(book); + var book_cross = books[key_cross]; + + if (book && book_cross && !book_cross.done) + { + var book_cross_quality_inverted = Amount.from_json("1.0/1/1").divide(book_cross.quality); + + if (book_cross_quality_inverted.compareTo(book.quality) > 0) + { + console.log("crossing: #%s :: %s :: %s :: %s", ledger.ledger_index, key, book.quality.to_text(), book_cross.quality.to_text()); + } + + book_cross.done = true; + } + }); +}; + +var ledger_request = function (remote, ledger_index, done) { + remote.request_ledger(undefined, { + accounts: true, + expand: true, + }) + .ledger_index(ledger_index) + .on('success', function (m) { + // console.log("ledger: ", ledger_index); + // console.log("ledger: ", JSON.stringify(m, undefined, 2)); + done(m.ledger); + }) + .on('error', function (m) { + console.log("error"); + done(); + }) + .request(); +}; + +var usage = function () { + console.log("rlint.js _websocket_ip_ _websocket_port_ "); +}; + +var finish = function (remote) { + remote.disconnect(); + + // XXX Because remote.disconnect() doesn't work: + process.exit(); +}; + +console.log("args: ", process.argv.length); +console.log("args: ", process.argv); + +if (process.argv.length < 4) { + usage(); +} +else { + var remote = Remote.from_config({ + websocket_ip: process.argv[2], + websocket_port: process.argv[3], + }) + .once('ledger_closed', function (m) { + console.log("ledger_closed: ", JSON.stringify(m, undefined, 2)); + + if (process.argv.length === 5) { + var ledger_index = process.argv[4]; + + ledger_request(remote, ledger_index, function (l) { + if (l) { + ledger_verify(l); + } + + finish(remote); + }); + + } else if (process.argv.length === 6) { + var ledger_start = Number(process.argv[4]); + var ledger_end = Number(process.argv[5]); + var ledger_cursor = ledger_end; + + async.whilst( + function () { + return ledger_start <= ledger_cursor && ledger_cursor <=ledger_end; + }, + function (callback) { + // console.log(ledger_cursor); + + --ledger_cursor; + + ledger_request(remote, ledger_cursor, function (l) { + if (l) { + ledger_verify(l); + } + + callback(); + }); + }, + function (error) { + finish(remote); + }); + + } else { + finish(remote); + } + }) + .connect(); +} + +// vim:sw=2:sts=2:ts=8:et diff --git a/src/cpp/ripple/Amount.cpp b/src/cpp/ripple/Amount.cpp index 8dd45f147..c3d2c7a57 100644 --- a/src/cpp/ripple/Amount.cpp +++ b/src/cpp/ripple/Amount.cpp @@ -1057,7 +1057,7 @@ bool STAmount::applyOffer( // Offer has limited funding, limit offer gets and pays by funds available. saOfferPaysAvailable = saOfferFundsAvailable; - saOfferGetsAvailable = std::min(saOfferGets, multiply(saOfferPaysAvailable, saOfferRate, saOfferGets)); + saOfferGetsAvailable = std::min(saOfferGets, mulRound(saOfferPaysAvailable, saOfferRate, saOfferGets, true)); } cLog(lsINFO) << "applyOffer: saOfferPaysAvailable=" << saOfferFundsAvailable.getFullText(); @@ -1069,14 +1069,14 @@ bool STAmount::applyOffer( cLog(lsINFO) << "applyOffer: saTakerPaysMax=" << saTakerPaysMax.getFullText(); STAmount saTakerGetsMax = saTakerPaysMax >= saOfferGetsAvailable ? saOfferPaysAvailable // Potentially take entire offer. Avoid math shenanigans. - : std::min(saOfferPaysAvailable, divide(saTakerPaysMax, saOfferRate, saTakerGets)); // Taker a portion of offer. + : std::min(saOfferPaysAvailable, divRound(saTakerPaysMax, saOfferRate, saTakerGets, !saTakerGets.isNative())); // Taker a portion of offer. cLog(lsINFO) << "applyOffer: saOfferRate=" << saOfferRate.getFullText(); cLog(lsINFO) << "applyOffer: saTakerGetsMax=" << saTakerGetsMax.getFullText(); saTakerGot = std::min(saTakerGets, saTakerGetsMax); // Limit by wanted. saTakerPaid = saTakerGot == saOfferPaysAvailable ? saOfferGetsAvailable - : std::min(saOfferGetsAvailable, multiply(saTakerGot, saOfferRate, saTakerFunds)); + : std::min(saOfferGetsAvailable, mulRound(saTakerGot, saOfferRate, saTakerFunds, true)); cLog(lsINFO) << "applyOffer: saTakerGot=" << saTakerGot.getFullText(); cLog(lsINFO) << "applyOffer: saTakerPaid=" << saTakerPaid.getFullText(); @@ -1093,7 +1093,7 @@ bool STAmount::applyOffer( cLog(lsINFO) << "applyOffer: saTransferRate=" << saTransferRate.getFullText(); // TakerCost includes transfer fees. - STAmount saTakerCost = STAmount::multiply(saTakerPaid, saTransferRate); + STAmount saTakerCost = STAmount::mulRound(saTakerPaid, saTransferRate, true); cLog(lsINFO) << "applyOffer: saTakerCost=" << saTakerCost.getFullText(); cLog(lsINFO) << "applyOffer: saTakerFunds=" << saTakerFunds.getFullText(); @@ -1111,7 +1111,7 @@ bool STAmount::applyOffer( else { // Compute fees in a rounding safe way. - STAmount saOfferCost = STAmount::multiply(saTakerGot, STAmount(CURRENCY_ONE, ACCOUNT_ONE, uOfferPaysRate, -9)); + STAmount saOfferCost = STAmount::mulRound(saTakerGot, STAmount(CURRENCY_ONE, ACCOUNT_ONE, uOfferPaysRate, -9), true); saOfferIssuerFee = saOfferCost > saOfferFunds ? saOfferFunds-saTakerGot // Not enough funds to cover fee, stiff issuer the rounding error. diff --git a/src/cpp/ripple/RippleCalc.cpp b/src/cpp/ripple/RippleCalc.cpp index 15d0f0446..cd7d8e9f0 100644 --- a/src/cpp/ripple/RippleCalc.cpp +++ b/src/cpp/ripple/RippleCalc.cpp @@ -1306,7 +1306,7 @@ TER RippleCalc::calcNodeDeliverRev( { // Adjust output to conform to limited input. // XXX Verify it is impossible for these to be larger than available funds. - saOutPass = STAmount::divRound(saInPassAct, saOfrRate, saTakerGets, true); + saOutPass = STAmount::divRound(saInPassAct, saOfrRate, saTakerGets, !saTakerGets.isNative()); saOutPlusFees = STAmount::mulRound(saOutPass, saOutFeeRate, true); cLog(lsINFO) << boost::str(boost::format("calcNodeDeliverRev: adjusted: saOutPass=%s saOutPlusFees=%s") @@ -1447,7 +1447,7 @@ TER RippleCalc::calcNodeDeliverFwd( STAmount saInTotal = STAmount::mulRound(saInFunded, saInFeeRate, true); // Offer maximum in with fees. STAmount saInSum = std::min(saInTotal, saInReq-saInAct-saInFees); // In limited by remaining. STAmount saInPassAct = STAmount::divRound(saInSum, saInFeeRate, true); // In without fees. - STAmount saOutPassMax = STAmount::divRound(saInPassAct, saOfrRate, saTakerGets, true); // Out limited by in remaining. + STAmount saOutPassMax = STAmount::divRound(saInPassAct, saOfrRate, saTakerGets, !saTakerGets.isNative()); // Out limited by in remaining. STAmount saInPassFeesMax = saInSum-saInPassAct; @@ -2280,7 +2280,7 @@ TER RippleCalc::calcNodeAccountFwd( STAmount saIssueCrd = uQualityIn >= QUALITY_ONE ? saPrvIssueReq // No fee. - : STAmount::mulRound(saPrvIssueReq, STAmount(CURRENCY_ONE, ACCOUNT_ONE, uQualityIn, -9), false); // Amount to credit. + : STAmount::mulRound(saPrvIssueReq, STAmount(CURRENCY_ONE, ACCOUNT_ONE, uQualityIn, -9), true); // Amount to credit. // Amount to credit. Credit for less than received as a surcharge. saCurReceive = saPrvRedeemReq+saIssueCrd;