mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-22 12:05:53 +00:00
Work in progress on pathfind.
This commit is contained in:
@@ -42,14 +42,15 @@ bool PaymentNode::operator==(const PaymentNode& pnOther) const {
|
|||||||
// Return true, iff lhs has less priority than rhs.
|
// Return true, iff lhs has less priority than rhs.
|
||||||
bool PathState::lessPriority(PathState& lhs, PathState& rhs)
|
bool PathState::lessPriority(PathState& lhs, PathState& rhs)
|
||||||
{
|
{
|
||||||
|
// First rank is quality.
|
||||||
if (lhs.uQuality != rhs.uQuality)
|
if (lhs.uQuality != rhs.uQuality)
|
||||||
return lhs.uQuality > rhs.uQuality; // Bigger is worse.
|
return lhs.uQuality > rhs.uQuality; // Bigger is worse.
|
||||||
|
|
||||||
// Best quanity is second rank.
|
// Second rank is best quantity.
|
||||||
if (lhs.saOutPass != rhs.saOutPass)
|
if (lhs.saOutPass != rhs.saOutPass)
|
||||||
return lhs.saOutPass < rhs.saOutPass; // Smaller is worse.
|
return lhs.saOutPass < rhs.saOutPass; // Smaller is worse.
|
||||||
|
|
||||||
// Path index is third rank.
|
// Third rank is path index.
|
||||||
return lhs.mIndex > rhs.mIndex; // Bigger is worse.
|
return lhs.mIndex > rhs.mIndex; // Bigger is worse.
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2271,58 +2272,58 @@ TER RippleCalc::calcNodeRev(const unsigned int uNode, PathState& psCur, const bo
|
|||||||
// Calculate the next increment of a path.
|
// Calculate the next increment of a path.
|
||||||
// The increment is what can satisfy a portion or all of the requested output at the best quality.
|
// The increment is what can satisfy a portion or all of the requested output at the best quality.
|
||||||
// <-- psCur.uQuality
|
// <-- psCur.uQuality
|
||||||
void RippleCalc::pathNext(PathState& psCur, const int iPaths, const LedgerEntrySet& lesCheckpoint, LedgerEntrySet& lesCurrent)
|
void RippleCalc::pathNext(PathState::ref psrCur, const int iPaths, const LedgerEntrySet& lesCheckpoint, LedgerEntrySet& lesCurrent)
|
||||||
{
|
{
|
||||||
// The next state is what is available in preference order.
|
// The next state is what is available in preference order.
|
||||||
// This is calculated when referenced accounts changed.
|
// This is calculated when referenced accounts changed.
|
||||||
const bool bMultiQuality = iPaths == 1;
|
const bool bMultiQuality = iPaths == 1;
|
||||||
const unsigned int uLast = psCur.vpnNodes.size() - 1;
|
const unsigned int uLast = psrCur->vpnNodes.size() - 1;
|
||||||
|
|
||||||
psCur.bConsumed = false;
|
psrCur->bConsumed = false;
|
||||||
|
|
||||||
// YYY This clearing should only be needed for nice logging.
|
// YYY This clearing should only be needed for nice logging.
|
||||||
psCur.saInPass = STAmount(psCur.saInReq.getCurrency(), psCur.saInReq.getIssuer());
|
psrCur->saInPass = STAmount(psrCur->saInReq.getCurrency(), psrCur->saInReq.getIssuer());
|
||||||
psCur.saOutPass = STAmount(psCur.saOutReq.getCurrency(), psCur.saOutReq.getIssuer());
|
psrCur->saOutPass = STAmount(psrCur->saOutReq.getCurrency(), psrCur->saOutReq.getIssuer());
|
||||||
|
|
||||||
psCur.vUnfundedBecame.clear();
|
psrCur->vUnfundedBecame.clear();
|
||||||
psCur.umReverse.clear();
|
psrCur->umReverse.clear();
|
||||||
|
|
||||||
cLog(lsINFO) << "Path In: " << psCur.getJson();
|
cLog(lsINFO) << "pathNext: Path In: " << psrCur->getJson();
|
||||||
|
|
||||||
assert(psCur.vpnNodes.size() >= 2);
|
assert(psrCur->vpnNodes.size() >= 2);
|
||||||
|
|
||||||
lesCurrent = lesCheckpoint; // Restore from checkpoint.
|
lesCurrent = lesCheckpoint; // Restore from checkpoint.
|
||||||
lesCurrent.bumpSeq(); // Begin ledger varance.
|
lesCurrent.bumpSeq(); // Begin ledger varance.
|
||||||
|
|
||||||
psCur.terStatus = calcNodeRev(uLast, psCur, bMultiQuality);
|
psrCur->terStatus = calcNodeRev(uLast, *psrCur, bMultiQuality);
|
||||||
|
|
||||||
cLog(lsINFO) << "Path after reverse: " << psCur.getJson();
|
cLog(lsINFO) << "pathNext: Path after reverse: " << psrCur->getJson();
|
||||||
|
|
||||||
if (tesSUCCESS == psCur.terStatus)
|
if (tesSUCCESS == psrCur->terStatus)
|
||||||
{
|
{
|
||||||
// Do forward.
|
// Do forward.
|
||||||
lesCurrent = lesCheckpoint; // Restore from checkpoint.
|
lesCurrent = lesCheckpoint; // Restore from checkpoint.
|
||||||
lesCurrent.bumpSeq(); // Begin ledger varance.
|
lesCurrent.bumpSeq(); // Begin ledger varance.
|
||||||
|
|
||||||
psCur.terStatus = calcNodeFwd(0, psCur, bMultiQuality);
|
psrCur->terStatus = calcNodeFwd(0, *psrCur, bMultiQuality);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tesSUCCESS == psCur.terStatus)
|
if (tesSUCCESS == psrCur->terStatus)
|
||||||
{
|
{
|
||||||
tLog(!psCur.saInPass || !psCur.saOutPass, lsDEBUG)
|
tLog(!psrCur->saInPass || !psrCur->saOutPass, lsDEBUG)
|
||||||
<< boost::str(boost::format("saOutPass=%s saInPass=%s")
|
<< boost::str(boost::format("pathNext: saOutPass=%s saInPass=%s")
|
||||||
% psCur.saOutPass.getFullText()
|
% psrCur->saOutPass.getFullText()
|
||||||
% psCur.saInPass.getFullText());
|
% psrCur->saInPass.getFullText());
|
||||||
|
|
||||||
assert(!!psCur.saOutPass && !!psCur.saInPass);
|
assert(!!psrCur->saOutPass && !!psrCur->saInPass);
|
||||||
|
|
||||||
psCur.uQuality = STAmount::getRate(psCur.saOutPass, psCur.saInPass); // Calculate relative quality.
|
psrCur->uQuality = STAmount::getRate(psrCur->saOutPass, psrCur->saInPass); // Calculate relative quality.
|
||||||
|
|
||||||
cLog(lsINFO) << "Path after forward: " << psCur.getJson();
|
cLog(lsINFO) << "pathNext: Path after forward: " << psrCur->getJson();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
psCur.uQuality = 0;
|
psrCur->uQuality = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2458,14 +2459,18 @@ int iPass = 0;
|
|||||||
int iDry = 0;
|
int iDry = 0;
|
||||||
|
|
||||||
// Find the best path.
|
// Find the best path.
|
||||||
BOOST_FOREACH(PathState::pointer pspCur, vpsExpanded)
|
BOOST_FOREACH(PathState::ref pspCur, vpsExpanded)
|
||||||
{
|
{
|
||||||
if (pspCur->uQuality)
|
if (pspCur->uQuality)
|
||||||
{
|
{
|
||||||
pspCur->saInAct = saMaxAmountAct; // Update to current amount processed.
|
pspCur->saInAct = saMaxAmountAct; // Update to current amount processed.
|
||||||
pspCur->saOutAct = saDstAmountAct;
|
pspCur->saOutAct = saDstAmountAct;
|
||||||
|
|
||||||
rc.pathNext(*pspCur, vpsExpanded.size(), lesCheckpoint, lesActive); // Compute increment.
|
rc.pathNext(pspCur, vpsExpanded.size(), lesCheckpoint, lesActive); // Compute increment.
|
||||||
|
cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: AFTER: mIndex=%d uQuality=%d rate=%s")
|
||||||
|
% pspCur->mIndex
|
||||||
|
% pspCur->uQuality
|
||||||
|
% STAmount::saFromRate(pspCur->uQuality));
|
||||||
|
|
||||||
if (!pspCur->uQuality) {
|
if (!pspCur->uQuality) {
|
||||||
// Path was dry.
|
// Path was dry.
|
||||||
@@ -2485,7 +2490,9 @@ int iPass = 0;
|
|||||||
&& (iBest < 0 // Best is not yet set.
|
&& (iBest < 0 // Best is not yet set.
|
||||||
|| PathState::lessPriority(*vpsExpanded[iBest], *pspCur))) // Current is better than set.
|
|| PathState::lessPriority(*vpsExpanded[iBest], *pspCur))) // Current is better than set.
|
||||||
{
|
{
|
||||||
cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: better: uQuality=%s saInPass=%s saOutPass=%s")
|
cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: better: mIndex=%d uQuality=%s rate=%s saInPass=%s saOutPass=%s")
|
||||||
|
% pspCur->mIndex
|
||||||
|
% pspCur->uQuality
|
||||||
% STAmount::saFromRate(pspCur->uQuality)
|
% STAmount::saFromRate(pspCur->uQuality)
|
||||||
% pspCur->saInPass.getFullText()
|
% pspCur->saInPass.getFullText()
|
||||||
% pspCur->saOutPass.getFullText());
|
% pspCur->saOutPass.getFullText());
|
||||||
@@ -2499,7 +2506,12 @@ int iPass = 0;
|
|||||||
cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Summary: Pass: %d Dry: %d Paths: %d") % ++iPass % iDry % vpsExpanded.size());
|
cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Summary: Pass: %d Dry: %d Paths: %d") % ++iPass % iDry % vpsExpanded.size());
|
||||||
BOOST_FOREACH(PathState::ref pspCur, vpsExpanded)
|
BOOST_FOREACH(PathState::ref pspCur, vpsExpanded)
|
||||||
{
|
{
|
||||||
cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Summary: %d quality:%d best: %d consumed: %d") % pspCur->mIndex % pspCur->uQuality % (iBest == pspCur->getIndex()) % pspCur->bConsumed);
|
cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Summary: %d rate: %s quality:%d best: %d consumed: %d")
|
||||||
|
% pspCur->mIndex
|
||||||
|
% STAmount::saFromRate(pspCur->uQuality)
|
||||||
|
% pspCur->uQuality
|
||||||
|
% (iBest == pspCur->getIndex())
|
||||||
|
% pspCur->bConsumed);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (iBest >= 0)
|
if (iBest >= 0)
|
||||||
|
|||||||
@@ -161,7 +161,7 @@ public:
|
|||||||
// If the transaction fails to meet some constraint, still need to delete unfunded offers.
|
// If the transaction fails to meet some constraint, still need to delete unfunded offers.
|
||||||
boost::unordered_set<uint256> musUnfundedFound; // Offers that were found unfunded.
|
boost::unordered_set<uint256> musUnfundedFound; // Offers that were found unfunded.
|
||||||
|
|
||||||
void pathNext(PathState& psCur, const int iPaths, const LedgerEntrySet& lesCheckpoint, LedgerEntrySet& lesCurrent);
|
void pathNext(PathState::ref psrCur, const int iPaths, const LedgerEntrySet& lesCheckpoint, LedgerEntrySet& lesCurrent);
|
||||||
TER calcNode(const unsigned int uNode, PathState& psCur, const bool bMultiQuality);
|
TER calcNode(const unsigned int uNode, PathState& psCur, const bool bMultiQuality);
|
||||||
TER calcNodeRev(const unsigned int uNode, PathState& psCur, const bool bMultiQuality);
|
TER calcNodeRev(const unsigned int uNode, PathState& psCur, const bool bMultiQuality);
|
||||||
TER calcNodeFwd(const unsigned int uNode, PathState& psCur, const bool bMultiQuality);
|
TER calcNodeFwd(const unsigned int uNode, PathState& psCur, const bool bMultiQuality);
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ require("../src/js/remote.js").config = require("./config.js");
|
|||||||
|
|
||||||
buster.testRunner.timeout = 5000;
|
buster.testRunner.timeout = 5000;
|
||||||
|
|
||||||
buster.testCase("Path finding", {
|
if (false)
|
||||||
|
buster.testCase("Basic Path finding", {
|
||||||
// 'setUp' : testutils.build_setup({ verbose: true, no_server: true }),
|
// 'setUp' : testutils.build_setup({ verbose: true, no_server: true }),
|
||||||
// 'setUp' : testutils.build_setup({ verbose: true }),
|
// 'setUp' : testutils.build_setup({ verbose: true }),
|
||||||
'setUp' : testutils.build_setup(),
|
'setUp' : testutils.build_setup(),
|
||||||
@@ -221,4 +222,215 @@ buster.testCase("Path finding", {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
buster.testCase("Extended Path finding", {
|
||||||
|
// 'setUp' : testutils.build_setup({ verbose: true, no_server: true }),
|
||||||
|
'setUp' : testutils.build_setup({ verbose: true }),
|
||||||
|
// 'setUp' : testutils.build_setup(),
|
||||||
|
'tearDown' : testutils.build_teardown(),
|
||||||
|
|
||||||
|
"alternative paths - consume both" :
|
||||||
|
function (done) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
async.waterfall([
|
||||||
|
function (callback) {
|
||||||
|
self.what = "Create accounts.";
|
||||||
|
|
||||||
|
testutils.create_accounts(self.remote, "root", "10000", ["alice", "bob", "mtgox", "bitstamp"], callback);
|
||||||
|
},
|
||||||
|
function (callback) {
|
||||||
|
self.what = "Set credit limits.";
|
||||||
|
|
||||||
|
testutils.credit_limits(self.remote,
|
||||||
|
{
|
||||||
|
"alice" : [ "600/USD/mtgox", "800/USD/bitstamp" ],
|
||||||
|
"bob" : [ "700/USD/mtgox", "900/USD/bitstamp" ]
|
||||||
|
},
|
||||||
|
callback);
|
||||||
|
},
|
||||||
|
function (callback) {
|
||||||
|
self.what = "Distribute funds.";
|
||||||
|
|
||||||
|
testutils.payments(self.remote,
|
||||||
|
{
|
||||||
|
"bitstamp" : "70/USD/alice",
|
||||||
|
"mtgox" : "70/USD/alice",
|
||||||
|
},
|
||||||
|
callback);
|
||||||
|
},
|
||||||
|
function (callback) {
|
||||||
|
self.what = "Payment with auto path";
|
||||||
|
|
||||||
|
self.remote.transaction()
|
||||||
|
.payment('alice', 'bob', "140/USD/bob")
|
||||||
|
.build_path(true)
|
||||||
|
.once('proposed', function (m) {
|
||||||
|
// console.log("proposed: %s", JSON.stringify(m));
|
||||||
|
callback(m.result !== 'tesSUCCESS');
|
||||||
|
})
|
||||||
|
.submit();
|
||||||
|
},
|
||||||
|
function (callback) {
|
||||||
|
self.what = "Verify balances.";
|
||||||
|
|
||||||
|
testutils.verify_balances(self.remote,
|
||||||
|
{
|
||||||
|
"alice" : [ "0/USD/mtgox", "0/USD/bitstamp" ],
|
||||||
|
"bob" : [ "70/USD/mtgox", "70/USD/bitstamp" ],
|
||||||
|
"bitstamp" : [ "0/USD/alice", "-70/USD/bob" ],
|
||||||
|
"mtgox" : [ "0/USD/alice", "-70/USD/bob" ],
|
||||||
|
},
|
||||||
|
callback);
|
||||||
|
},
|
||||||
|
], function (error) {
|
||||||
|
buster.refute(error, self.what);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
"alternative paths - consume best transfer" :
|
||||||
|
function (done) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
async.waterfall([
|
||||||
|
function (callback) {
|
||||||
|
self.what = "Create accounts.";
|
||||||
|
|
||||||
|
testutils.create_accounts(self.remote, "root", "10000", ["alice", "bob", "mtgox", "bitstamp"], callback);
|
||||||
|
},
|
||||||
|
function (callback) {
|
||||||
|
self.what = "Set transfer rate.";
|
||||||
|
|
||||||
|
self.remote.transaction()
|
||||||
|
.account_set("bitstamp")
|
||||||
|
.transfer_rate(1e9*1.1)
|
||||||
|
.once('proposed', function (m) {
|
||||||
|
// console.log("proposed: %s", JSON.stringify(m));
|
||||||
|
callback(m.result !== 'tesSUCCESS');
|
||||||
|
})
|
||||||
|
.submit();
|
||||||
|
},
|
||||||
|
function (callback) {
|
||||||
|
self.what = "Set credit limits.";
|
||||||
|
|
||||||
|
testutils.credit_limits(self.remote,
|
||||||
|
{
|
||||||
|
"alice" : [ "600/USD/mtgox", "800/USD/bitstamp" ],
|
||||||
|
"bob" : [ "700/USD/mtgox", "900/USD/bitstamp" ]
|
||||||
|
},
|
||||||
|
callback);
|
||||||
|
},
|
||||||
|
function (callback) {
|
||||||
|
self.what = "Distribute funds.";
|
||||||
|
|
||||||
|
testutils.payments(self.remote,
|
||||||
|
{
|
||||||
|
"bitstamp" : "70/USD/alice",
|
||||||
|
"mtgox" : "70/USD/alice",
|
||||||
|
},
|
||||||
|
callback);
|
||||||
|
},
|
||||||
|
function (callback) {
|
||||||
|
self.what = "Payment with auto path";
|
||||||
|
|
||||||
|
self.remote.transaction()
|
||||||
|
.payment('alice', 'bob', "70/USD/bob")
|
||||||
|
.build_path(true)
|
||||||
|
.once('proposed', function (m) {
|
||||||
|
// console.log("proposed: %s", JSON.stringify(m));
|
||||||
|
callback(m.result !== 'tesSUCCESS');
|
||||||
|
})
|
||||||
|
.submit();
|
||||||
|
},
|
||||||
|
function (callback) {
|
||||||
|
self.what = "Verify balances.";
|
||||||
|
|
||||||
|
testutils.verify_balances(self.remote,
|
||||||
|
{
|
||||||
|
"alice" : [ "0/USD/mtgox", "70/USD/bitstamp" ],
|
||||||
|
"bob" : [ "70/USD/mtgox", "0/USD/bitstamp" ],
|
||||||
|
"bitstamp" : [ "-70/USD/alice", "0/USD/bob" ],
|
||||||
|
"mtgox" : [ "0/USD/alice", "-70/USD/bob" ],
|
||||||
|
},
|
||||||
|
callback);
|
||||||
|
},
|
||||||
|
], function (error) {
|
||||||
|
buster.refute(error, self.what);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
"=>alternative paths - consume best transfer first" :
|
||||||
|
function (done) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
async.waterfall([
|
||||||
|
function (callback) {
|
||||||
|
self.what = "Create accounts.";
|
||||||
|
|
||||||
|
testutils.create_accounts(self.remote, "root", "10000", ["alice", "bob", "mtgox", "bitstamp"], callback);
|
||||||
|
},
|
||||||
|
function (callback) {
|
||||||
|
self.what = "Set transfer rate.";
|
||||||
|
|
||||||
|
self.remote.transaction()
|
||||||
|
.account_set("bitstamp")
|
||||||
|
.transfer_rate(1e9*1.1)
|
||||||
|
.once('proposed', function (m) {
|
||||||
|
// console.log("proposed: %s", JSON.stringify(m));
|
||||||
|
callback(m.result !== 'tesSUCCESS');
|
||||||
|
})
|
||||||
|
.submit();
|
||||||
|
},
|
||||||
|
function (callback) {
|
||||||
|
self.what = "Set credit limits.";
|
||||||
|
|
||||||
|
testutils.credit_limits(self.remote,
|
||||||
|
{
|
||||||
|
"alice" : [ "600/USD/mtgox", "800/USD/bitstamp" ],
|
||||||
|
"bob" : [ "700/USD/mtgox", "900/USD/bitstamp" ]
|
||||||
|
},
|
||||||
|
callback);
|
||||||
|
},
|
||||||
|
function (callback) {
|
||||||
|
self.what = "Distribute funds.";
|
||||||
|
|
||||||
|
testutils.payments(self.remote,
|
||||||
|
{
|
||||||
|
"bitstamp" : "70/USD/alice",
|
||||||
|
"mtgox" : "70/USD/alice",
|
||||||
|
},
|
||||||
|
callback);
|
||||||
|
},
|
||||||
|
function (callback) {
|
||||||
|
self.what = "Payment with auto path";
|
||||||
|
|
||||||
|
self.remote.transaction()
|
||||||
|
.payment('alice', 'bob', "77/USD/bob")
|
||||||
|
.build_path(true)
|
||||||
|
.once('proposed', function (m) {
|
||||||
|
// console.log("proposed: %s", JSON.stringify(m));
|
||||||
|
callback(m.result !== 'tesSUCCESS');
|
||||||
|
})
|
||||||
|
.submit();
|
||||||
|
},
|
||||||
|
function (callback) {
|
||||||
|
self.what = "Verify balances.";
|
||||||
|
|
||||||
|
testutils.verify_balances(self.remote,
|
||||||
|
{
|
||||||
|
"alice" : [ "0/USD/mtgox", "63.63636363636363/USD/bitstamp" ],
|
||||||
|
"bob" : [ "70/USD/mtgox", "7/USD/bitstamp" ],
|
||||||
|
"bitstamp" : [ "-63.63636363636363/USD/alice", "-7/USD/bob" ],
|
||||||
|
"mtgox" : [ "0/USD/alice", "-70/USD/bob" ],
|
||||||
|
},
|
||||||
|
callback);
|
||||||
|
},
|
||||||
|
], function (error) {
|
||||||
|
buster.refute(error, self.what);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
// vim:sw=2:sts=2:ts=8:et
|
// vim:sw=2:sts=2:ts=8:et
|
||||||
|
|||||||
Reference in New Issue
Block a user