mirror of
https://github.com/XRPLF/rippled.git
synced 2026-04-29 15:37:57 +00:00
Merge branch 'pf_api' into develop
This commit is contained in:
@@ -157,6 +157,7 @@
|
||||
<ClCompile Include="src\cpp\ripple\PaymentTransactor.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\Peer.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\PeerDoor.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\PFRequest.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\PlatRand.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\ProofOfWork.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\RangeSet.cpp" />
|
||||
|
||||
@@ -174,6 +174,9 @@
|
||||
<ClCompile Include="src\cpp\ripple\PeerDoor.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\cpp\ripple\PFRequest.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\cpp\ripple\PlatRand.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
||||
@@ -145,6 +145,7 @@
|
||||
<ClCompile Include="src\cpp\ripple\PaymentTransactor.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\Peer.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\PeerDoor.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\PFReqeuest.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\PlatRand.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\ProofOfWork.cpp" />
|
||||
<ClCompile Include="src\cpp\ripple\RangeSet.cpp" />
|
||||
|
||||
@@ -171,6 +171,9 @@
|
||||
<ClCompile Include="src\cpp\ripple\PeerDoor.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\cpp\ripple\PFRequest.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\cpp\ripple\PlatRand.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
||||
@@ -281,7 +281,7 @@ bool STAmount::setValue(const std::string& sAmount)
|
||||
|
||||
try
|
||||
{
|
||||
if ((smMatch[2].length() + smMatch[4].length()) > 25)
|
||||
if ((smMatch[2].length() + smMatch[4].length()) > 32)
|
||||
{
|
||||
cLog(lsWARNING) << "Overlong number: " << sAmount;
|
||||
return false;
|
||||
|
||||
@@ -736,6 +736,7 @@ Json::Value RPCParser::parseCommand(std::string strMethod, Json::Value jvParams)
|
||||
#endif
|
||||
|
||||
// Evented methods
|
||||
{ "path_find", &RPCParser::parseEvented, -1, -1 },
|
||||
{ "subscribe", &RPCParser::parseEvented, -1, -1 },
|
||||
{ "unsubscribe", &RPCParser::parseEvented, -1, -1 },
|
||||
};
|
||||
|
||||
@@ -43,6 +43,7 @@ const char* Job::toString(JobType t)
|
||||
case jtPROOFWORK: return "proofOfWork";
|
||||
case jtPROPOSAL_ut: return "untrustedProposal";
|
||||
case jtLEDGER_DATA: return "ledgerData";
|
||||
case jtUPDATE_PF: return "updatePaths";
|
||||
case jtCLIENT: return "clientCommand";
|
||||
case jtTRANSACTION: return "transaction";
|
||||
case jtPUBLEDGER: return "publishNewLedger";
|
||||
|
||||
@@ -28,16 +28,17 @@ enum JobType
|
||||
jtPROOFWORK = 4, // A proof of work demand from another server
|
||||
jtPROPOSAL_ut = 5, // A proposal from an untrusted source
|
||||
jtLEDGER_DATA = 6, // Received data for a ledger we're acquiring
|
||||
jtCLIENT = 7, // A websocket command from the client
|
||||
jtTRANSACTION = 8, // A transaction received from the network
|
||||
jtPUBLEDGER = 9, // Publish a fully-accepted ledger
|
||||
jtWAL = 10, // Write-ahead logging
|
||||
jtVALIDATION_t = 11, // A validation from a trusted source
|
||||
jtWRITE = 12, // Write out hashed objects
|
||||
jtTRANSACTION_l = 13, // A local transaction
|
||||
jtPROPOSAL_t = 14, // A proposal from a trusted source
|
||||
jtADMIN = 15, // An administrative operation
|
||||
jtDEATH = 16, // job of death, used internally
|
||||
jtUPDATE_PF = 7, // Update pathfinding requests
|
||||
jtCLIENT = 8, // A websocket command from the client
|
||||
jtTRANSACTION = 9, // A transaction received from the network
|
||||
jtPUBLEDGER = 10, // Publish a fully-accepted ledger
|
||||
jtWAL = 11, // Write-ahead logging
|
||||
jtVALIDATION_t = 12, // A validation from a trusted source
|
||||
jtWRITE = 13, // Write out hashed objects
|
||||
jtTRANSACTION_l = 14, // A local transaction
|
||||
jtPROPOSAL_t = 15, // A proposal from a trusted source
|
||||
jtADMIN = 16, // An administrative operation
|
||||
jtDEATH = 17, // job of death, used internally
|
||||
|
||||
// special types not dispatched by the job pool
|
||||
jtPEER = 24,
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "Application.h"
|
||||
#include "RippleAddress.h"
|
||||
#include "Log.h"
|
||||
#include "PFRequest.h"
|
||||
|
||||
SETUP_LOG();
|
||||
|
||||
@@ -627,12 +628,20 @@ void LedgerMaster::tryPublish()
|
||||
mPubThread = true;
|
||||
theApp->getJobQueue().addJob(jtPUBLEDGER, "Ledger::pubThread",
|
||||
BIND_TYPE(&LedgerMaster::pubThread, this));
|
||||
mPathFindNewLedger = true;
|
||||
if (!mPathFindThread)
|
||||
{
|
||||
mPathFindThread = true;
|
||||
theApp->getJobQueue().addJob(jtUPDATE_PF, "updatePaths",
|
||||
BIND_TYPE(&LedgerMaster::updatePaths, this));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LedgerMaster::pubThread()
|
||||
{
|
||||
std::list<Ledger::pointer> ledgers;
|
||||
bool published = false;
|
||||
|
||||
while (1)
|
||||
{
|
||||
@@ -644,6 +653,12 @@ void LedgerMaster::pubThread()
|
||||
if (ledgers.empty())
|
||||
{
|
||||
mPubThread = false;
|
||||
if (published && !mPathFindThread)
|
||||
{
|
||||
mPathFindThread = true;
|
||||
theApp->getJobQueue().addJob(jtUPDATE_PF, "updatePaths",
|
||||
BIND_TYPE(&LedgerMaster::updatePaths, this));
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -653,8 +668,52 @@ void LedgerMaster::pubThread()
|
||||
cLog(lsDEBUG) << "Publishing ledger " << l->getLedgerSeq();
|
||||
setFullLedger(l); // OPTIMIZEME: This is actually more work than we need to do
|
||||
theApp->getOPs().pubLedger(l);
|
||||
published = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LedgerMaster::updatePaths()
|
||||
{
|
||||
Ledger::pointer lastLedger;
|
||||
do
|
||||
{
|
||||
bool newOnly = false;
|
||||
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock ml(mLock);
|
||||
if (mPathFindNewLedger || (lastLedger && (lastLedger.get() != mPubLedger.get())))
|
||||
lastLedger = mPubLedger;
|
||||
else if (mPathFindNewRequest)
|
||||
{
|
||||
newOnly = true;
|
||||
lastLedger = boost::make_shared<Ledger>(*mCurrentLedger, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
mPathFindThread = false;
|
||||
return;
|
||||
}
|
||||
lastLedger = mPubLedger;
|
||||
mPathFindNewLedger = false;
|
||||
mPathFindNewRequest = false;
|
||||
}
|
||||
|
||||
PFRequest::updateAll(lastLedger, newOnly);
|
||||
|
||||
} while(1);
|
||||
}
|
||||
|
||||
void LedgerMaster::newPFRequest()
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock ml(mLock);
|
||||
mPathFindNewRequest = true;
|
||||
if (!mPathFindThread)
|
||||
{
|
||||
mPathFindThread = true;
|
||||
theApp->getJobQueue().addJob(jtUPDATE_PF, "updatePaths",
|
||||
BIND_TYPE(&LedgerMaster::updatePaths, this));
|
||||
}
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
|
||||
@@ -46,6 +46,10 @@ protected:
|
||||
std::list<Ledger::pointer> mPubLedgers; // List of ledgers to publish
|
||||
bool mPubThread; // Publish thread is running
|
||||
|
||||
bool mPathFindThread; // Pathfind thread is running
|
||||
bool mPathFindNewLedger;
|
||||
bool mPathFindNewRequest;
|
||||
|
||||
void applyFutureTransactions(uint32 ledgerIndex);
|
||||
bool isValidTransaction(Transaction::ref trans);
|
||||
bool isTransactionOnFutureList(Transaction::ref trans);
|
||||
@@ -54,11 +58,13 @@ protected:
|
||||
void asyncAccept(Ledger::pointer);
|
||||
void missingAcquireComplete(LedgerAcquire::pointer);
|
||||
void pubThread();
|
||||
void updatePaths();
|
||||
|
||||
public:
|
||||
|
||||
LedgerMaster() : mHeldTransactions(uint256()), mMissingSeq(0),
|
||||
mMinValidations(0), mLastValidateSeq(0), mPubThread(false)
|
||||
mMinValidations(0), mLastValidateSeq(0), mPubThread(false),
|
||||
mPathFindThread(false), mPathFindNewLedger(false), mPathFindNewRequest(false)
|
||||
{ ; }
|
||||
|
||||
uint32 getCurrentLedgerIndex();
|
||||
@@ -155,6 +161,7 @@ public:
|
||||
void checkAccept(const uint256& hash);
|
||||
void checkAccept(const uint256& hash, uint32 seq);
|
||||
void tryPublish();
|
||||
void newPFRequest();
|
||||
|
||||
static bool shouldAcquire(uint32 currentLedgerID, uint32 ledgerHistory, uint32 targetLedger);
|
||||
};
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
class Peer;
|
||||
class LedgerConsensus;
|
||||
class PFRequest;
|
||||
|
||||
DEFINE_INSTANCE(InfoSub);
|
||||
|
||||
@@ -29,6 +30,7 @@ class InfoSub : public IS_INSTANCE(InfoSub)
|
||||
protected:
|
||||
boost::unordered_set<RippleAddress> mSubAccountInfo;
|
||||
boost::unordered_set<RippleAddress> mSubAccountTransaction;
|
||||
boost::shared_ptr<PFRequest> mPFRequest;
|
||||
|
||||
boost::mutex mLockInfo;
|
||||
|
||||
@@ -64,6 +66,21 @@ public:
|
||||
|
||||
mSubAccountInfo.insert(addr);
|
||||
}
|
||||
|
||||
void clearPFRequest()
|
||||
{
|
||||
mPFRequest.reset();
|
||||
}
|
||||
|
||||
void setPFRequest(const boost::shared_ptr<PFRequest>& req)
|
||||
{
|
||||
mPFRequest = req;
|
||||
}
|
||||
|
||||
const boost::shared_ptr<PFRequest>& getPFRequest()
|
||||
{
|
||||
return mPFRequest;
|
||||
}
|
||||
};
|
||||
|
||||
class NetworkOPs
|
||||
|
||||
328
src/cpp/ripple/PFRequest.cpp
Normal file
328
src/cpp/ripple/PFRequest.cpp
Normal file
@@ -0,0 +1,328 @@
|
||||
#include "PFRequest.h"
|
||||
|
||||
#include "NetworkOPs.h"
|
||||
#include "RPCErr.h"
|
||||
#include "Ledger.h"
|
||||
#include "Application.h"
|
||||
#include "Pathfinder.h"
|
||||
#include "RippleCalc.h"
|
||||
#include "LedgerFormats.h"
|
||||
|
||||
SETUP_LOG();
|
||||
|
||||
boost::recursive_mutex PFRequest::sLock;
|
||||
std::set<PFRequest::wptr> PFRequest::sRequests;
|
||||
|
||||
PFRequest::PFRequest(const boost::shared_ptr<InfoSub>& subscriber) :
|
||||
wpSubscriber(subscriber), jvStatus(Json::objectValue), bValid(false), bNew(true)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
bool PFRequest::isValid()
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
return bValid;
|
||||
}
|
||||
|
||||
bool PFRequest::isNew()
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
return bNew;
|
||||
}
|
||||
|
||||
bool PFRequest::isValid(Ledger::ref lrLedger)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
bValid = raSrcAccount.isSet() && raDstAccount.isSet() && saDstAmount.isPositive();
|
||||
if (bValid)
|
||||
{
|
||||
AccountState::pointer asSrc = theApp->getOPs().getAccountState(lrLedger, raSrcAccount);
|
||||
if (!asSrc)
|
||||
{ // no source account
|
||||
bValid = false;
|
||||
jvStatus = rpcError(rpcSRC_ACT_NOT_FOUND);
|
||||
}
|
||||
else
|
||||
{
|
||||
AccountState::pointer asDst = theApp->getOPs().getAccountState(lrLedger, raDstAccount);
|
||||
Json::Value jvDestCur;
|
||||
if (!asDst)
|
||||
{ // no destination account
|
||||
jvDestCur.append(Json::Value("XRP"));
|
||||
if(!saDstAmount.isNative())
|
||||
{ // only XRP can be send to a non-existent account
|
||||
bValid = false;
|
||||
jvStatus = rpcError(rpcACT_NOT_FOUND);
|
||||
}
|
||||
else if (saDstAmount < STAmount(lrLedger->getReserve(0)))
|
||||
{ // payment must meet reserve
|
||||
bValid = false;
|
||||
jvStatus = rpcError(rpcDST_AMT_MALFORMED);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
boost::unordered_set<uint160> usDestCurrID = usAccountDestCurrencies(raDstAccount, lrLedger, true);
|
||||
BOOST_FOREACH(const uint160& uCurrency, usDestCurrID)
|
||||
jvDestCur.append(STAmount::createHumanCurrency(uCurrency));
|
||||
jvStatus["destination_tag"] = (asDst->peekSLE().getFlags() & lsfRequireDestTag) != 0;
|
||||
}
|
||||
jvStatus["destination_currencies"] = jvDestCur;
|
||||
}
|
||||
}
|
||||
jvStatus["ledger_hash"] = lrLedger->getHash().GetHex();
|
||||
jvStatus["ledger_index"] = lrLedger->getLedgerSeq();
|
||||
return bValid;
|
||||
}
|
||||
|
||||
Json::Value PFRequest::doCreate(Ledger::ref lrLedger, const Json::Value& value)
|
||||
{
|
||||
assert(lrLedger->isClosed());
|
||||
|
||||
Json::Value status;
|
||||
bool mValid;
|
||||
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
if (parseJson(value, true) != PFR_PJ_INVALID)
|
||||
{
|
||||
mValid = isValid(lrLedger);
|
||||
if (mValid)
|
||||
{
|
||||
RLCache::pointer cache = boost::make_shared<RLCache>(lrLedger);
|
||||
doUpdate(cache, true);
|
||||
}
|
||||
}
|
||||
else
|
||||
mValid = false;
|
||||
}
|
||||
|
||||
if (mValid)
|
||||
{
|
||||
cLog(lsINFO) << "Request created: " << raSrcAccount.humanAccountID() <<
|
||||
" -> " << raDstAccount.humanAccountID();
|
||||
cLog(lsINFO) << "Deliver: " << saDstAmount.getFullText();
|
||||
|
||||
boost::recursive_mutex::scoped_lock sl(sLock);
|
||||
sRequests.insert(shared_from_this());
|
||||
}
|
||||
|
||||
return jvStatus;
|
||||
}
|
||||
|
||||
int PFRequest::parseJson(const Json::Value& jvParams, bool complete)
|
||||
{
|
||||
int ret = PFR_PJ_NOCHANGE;
|
||||
|
||||
if (jvParams.isMember("source_account"))
|
||||
{
|
||||
if (!raSrcAccount.setAccountID(jvParams["source_account"].asString()))
|
||||
{
|
||||
jvStatus = rpcError(rpcSRC_ACT_MALFORMED);
|
||||
return PFR_PJ_INVALID;
|
||||
}
|
||||
}
|
||||
else if (complete)
|
||||
{
|
||||
jvStatus = rpcError(rpcSRC_ACT_MISSING);
|
||||
return PFR_PJ_INVALID;
|
||||
}
|
||||
|
||||
if (jvParams.isMember("destination_account"))
|
||||
{
|
||||
if (!raDstAccount.setAccountID(jvParams["destination_account"].asString()))
|
||||
{
|
||||
jvStatus = rpcError(rpcDST_ACT_MALFORMED);
|
||||
return PFR_PJ_INVALID;
|
||||
}
|
||||
}
|
||||
else if (complete)
|
||||
{
|
||||
jvStatus = rpcError(rpcDST_ACT_MISSING);
|
||||
return PFR_PJ_INVALID;
|
||||
}
|
||||
|
||||
if (jvParams.isMember("destination_amount"))
|
||||
{
|
||||
if (!saDstAmount.bSetJson(jvParams["destination_amount"]) ||
|
||||
(saDstAmount.getCurrency().isZero() && saDstAmount.getIssuer().isNonZero()) ||
|
||||
(saDstAmount.getCurrency() == CURRENCY_BAD) ||
|
||||
!saDstAmount.isPositive())
|
||||
{
|
||||
jvStatus = rpcError(rpcDST_AMT_MALFORMED);
|
||||
return PFR_PJ_INVALID;
|
||||
}
|
||||
}
|
||||
else if (complete)
|
||||
{
|
||||
jvStatus = rpcError(rpcDST_ACT_MISSING);
|
||||
return PFR_PJ_INVALID;
|
||||
}
|
||||
|
||||
if (jvParams.isMember("source_currencies"))
|
||||
{
|
||||
const Json::Value& jvSrcCur = jvParams["source_currencies"];
|
||||
if (!jvSrcCur.isArray())
|
||||
{
|
||||
jvStatus = rpcError(rpcSRC_CUR_MALFORMED);
|
||||
return PFR_PJ_INVALID;
|
||||
}
|
||||
sciSourceCurrencies.clear();
|
||||
for (unsigned i = 0; i < jvSrcCur.size(); ++i)
|
||||
{
|
||||
const Json::Value& jvCur = jvSrcCur[i];
|
||||
uint160 uCur, uIss;
|
||||
if (!jvCur.isMember("currency") || !STAmount::currencyFromString(uCur, jvCur["currency"].asString()))
|
||||
{
|
||||
jvStatus = rpcError(rpcSRC_CUR_MALFORMED);
|
||||
return PFR_PJ_INVALID;
|
||||
}
|
||||
if (jvCur.isMember("issuer") && !STAmount::issuerFromString(uIss, jvCur["issuer"].asString()))
|
||||
{
|
||||
jvStatus = rpcError(rpcSRC_ISR_MALFORMED);
|
||||
}
|
||||
if (uCur.isZero() && uIss.isNonZero())
|
||||
{
|
||||
jvStatus = rpcError(rpcSRC_CUR_MALFORMED);
|
||||
return PFR_PJ_INVALID;
|
||||
}
|
||||
sciSourceCurrencies.insert(currIssuer_t(uCur, uIss));
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
Json::Value PFRequest::doClose(const Json::Value&)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
return jvStatus;
|
||||
}
|
||||
|
||||
Json::Value PFRequest::doStatus(const Json::Value&)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
return jvStatus;
|
||||
}
|
||||
|
||||
bool PFRequest::doUpdate(RLCache::ref cache, bool fast)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
jvStatus = Json::objectValue;
|
||||
if (!isValid(cache->getLedger()))
|
||||
return false;
|
||||
if (!fast)
|
||||
bNew = false;
|
||||
|
||||
std::set<currIssuer_t> sourceCurrencies(sciSourceCurrencies);
|
||||
if (sourceCurrencies.empty())
|
||||
{
|
||||
boost::unordered_set<uint160> usCurrencies =
|
||||
usAccountSourceCurrencies(raSrcAccount, cache->getLedger(), true);
|
||||
bool sameAccount = raSrcAccount == raDstAccount;
|
||||
BOOST_FOREACH(const uint160& c, usCurrencies)
|
||||
{
|
||||
if (!sameAccount || (c != saDstAmount.getCurrency()))
|
||||
{
|
||||
if (c.isZero())
|
||||
sourceCurrencies.insert(std::make_pair(c, ACCOUNT_XRP));
|
||||
else
|
||||
sourceCurrencies.insert(std::make_pair(c, raSrcAccount.getAccountID()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
jvStatus["source_account"] = raSrcAccount.humanAccountID();
|
||||
jvStatus["destination_account"] = raDstAccount.humanAccountID();
|
||||
|
||||
Json::Value jvArray = Json::arrayValue;
|
||||
|
||||
BOOST_FOREACH(const currIssuer_t& currIssuer, sourceCurrencies)
|
||||
{
|
||||
{
|
||||
STAmount test(currIssuer.first, currIssuer.second, 1);
|
||||
cLog(lsDEBUG) << "Trying to find paths: " << test.getFullText();
|
||||
}
|
||||
bool valid;
|
||||
STPathSet spsPaths;
|
||||
Pathfinder pf(cache, raSrcAccount, raDstAccount,
|
||||
currIssuer.first, currIssuer.second, saDstAmount, valid);
|
||||
tLog(!valid, lsINFO) << "PF request not valid";
|
||||
if (valid && pf.findPaths(theConfig.PATH_SEARCH_SIZE - (fast ? 0 : 1), 3, spsPaths))
|
||||
{
|
||||
LedgerEntrySet lesSandbox(cache->getLedger(), tapNONE);
|
||||
std::vector<PathState::pointer> vpsExpanded;
|
||||
STAmount saMaxAmountAct;
|
||||
STAmount saDstAmountAct;
|
||||
STAmount saMaxAmount(currIssuer.first,
|
||||
currIssuer.second.isNonZero() ? currIssuer.second :
|
||||
(currIssuer.first.isZero() ? ACCOUNT_XRP : raSrcAccount.getAccountID()), 1);
|
||||
saMaxAmount.negate();
|
||||
cLog(lsDEBUG) << "Paths found, calling rippleCalc";
|
||||
TER terResult = RippleCalc::rippleCalc(lesSandbox, saMaxAmountAct, saDstAmountAct,
|
||||
vpsExpanded, saMaxAmount, saDstAmount, raDstAccount.getAccountID(), raSrcAccount.getAccountID(),
|
||||
spsPaths, false, false, false, true);
|
||||
if (terResult == tesSUCCESS)
|
||||
{
|
||||
Json::Value jvEntry(Json::objectValue);
|
||||
jvEntry["source_amount"] = saMaxAmountAct.getJson(0);
|
||||
jvEntry["paths_computed"] = spsPaths.getJson(0);
|
||||
jvArray.append(jvEntry);
|
||||
}
|
||||
else
|
||||
{
|
||||
cLog(lsINFO) << "rippleCalc returns " << transHuman(terResult);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cLog(lsINFO) << "No paths found";
|
||||
}
|
||||
}
|
||||
jvStatus["alternatives"] = jvArray;
|
||||
return true;
|
||||
}
|
||||
|
||||
void PFRequest::updateAll(Ledger::ref ledger, bool newOnly)
|
||||
{
|
||||
std::set<wptr> requests;
|
||||
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(sLock);
|
||||
requests = sRequests;
|
||||
}
|
||||
|
||||
if (requests.empty())
|
||||
return;
|
||||
|
||||
RLCache::pointer cache = boost::make_shared<RLCache>(ledger);
|
||||
|
||||
BOOST_FOREACH(wref wRequest, requests)
|
||||
{
|
||||
bool remove = true;
|
||||
PFRequest::pointer pRequest = wRequest.lock();
|
||||
if (pRequest && (!newOnly || pRequest->isNew()))
|
||||
{
|
||||
InfoSub::pointer ipSub = pRequest->wpSubscriber.lock();
|
||||
if (ipSub)
|
||||
{
|
||||
Json::Value update;
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(pRequest->mLock);
|
||||
pRequest->doUpdate(cache, false);
|
||||
update = pRequest->jvStatus;
|
||||
}
|
||||
update["type"] = "path_find";
|
||||
ipSub->send(update, false);
|
||||
remove = false;
|
||||
}
|
||||
}
|
||||
if (remove)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(sLock);
|
||||
sRequests.erase(wRequest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
84
src/cpp/ripple/PFRequest.h
Normal file
84
src/cpp/ripple/PFRequest.h
Normal file
@@ -0,0 +1,84 @@
|
||||
#ifndef _PFREQUEST__H
|
||||
#define _PFREQUEST__H
|
||||
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/thread/recursive_mutex.hpp>
|
||||
#include <boost/enable_shared_from_this.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/weak_ptr.hpp>
|
||||
|
||||
#include "../json/value.h"
|
||||
|
||||
#include "uint256.h"
|
||||
#include "RippleAddress.h"
|
||||
#include "SerializedTypes.h"
|
||||
#include "Pathfinder.h"
|
||||
|
||||
// A pathfinding request submitted by a client
|
||||
// The request issuer must maintain a strong pointer
|
||||
|
||||
class Ledger;
|
||||
class InfoSub;
|
||||
class STAmount;
|
||||
class RLCache;
|
||||
|
||||
// Return values from parseJson <0 = invalid, >0 = valid
|
||||
#define PFR_PJ_INVALID -1
|
||||
#define PFR_PJ_NOCHANGE 0
|
||||
#define PFR_PJ_CHANGE 1
|
||||
|
||||
class PFRequest : public boost::enable_shared_from_this<PFRequest>
|
||||
{
|
||||
public:
|
||||
typedef boost::weak_ptr<PFRequest> wptr;
|
||||
typedef boost::shared_ptr<PFRequest> pointer;
|
||||
typedef const pointer& ref;
|
||||
typedef const wptr& wref;
|
||||
typedef std::pair<uint160, uint160> currIssuer_t;
|
||||
|
||||
|
||||
protected:
|
||||
boost::recursive_mutex mLock;
|
||||
boost::weak_ptr<InfoSub> wpSubscriber; // Who this request came from
|
||||
Json::Value jvStatus; // Last result
|
||||
|
||||
// Client request parameters
|
||||
RippleAddress raSrcAccount;
|
||||
RippleAddress raDstAccount;
|
||||
STAmount saDstAmount;
|
||||
std::set<currIssuer_t> sciSourceCurrencies;
|
||||
std::vector<Json::Value> vjvBridges;
|
||||
|
||||
bool bValid;
|
||||
bool bNew;
|
||||
|
||||
// Track all requests
|
||||
static std::set<wptr> sRequests;
|
||||
static boost::recursive_mutex sLock;
|
||||
|
||||
void setValid();
|
||||
int parseJson(const Json::Value&, bool complete);
|
||||
|
||||
public:
|
||||
|
||||
PFRequest(const boost::shared_ptr<InfoSub>& subscriber);
|
||||
|
||||
bool isValid(const boost::shared_ptr<Ledger>&);
|
||||
bool isValid();
|
||||
bool isNew();
|
||||
Json::Value getStatus();
|
||||
|
||||
Json::Value doCreate(const boost::shared_ptr<Ledger>&, const Json::Value&);
|
||||
Json::Value doClose(const Json::Value&);
|
||||
Json::Value doStatus(const Json::Value&);
|
||||
|
||||
bool doUpdate(const boost::shared_ptr<RLCache>&, bool fast); // update jvStatus
|
||||
|
||||
static void updateAll(const boost::shared_ptr<Ledger>& ledger, bool newOnly);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
// vim:ts=4
|
||||
@@ -138,7 +138,7 @@ static int getEffectiveLength(const STPath& spPath)
|
||||
return length;
|
||||
}
|
||||
|
||||
Pathfinder::Pathfinder(Ledger::ref ledger,
|
||||
Pathfinder::Pathfinder(RLCache::ref cache,
|
||||
const RippleAddress& uSrcAccountID, const RippleAddress& uDstAccountID,
|
||||
const uint160& uSrcCurrencyID, const uint160& uSrcIssuerID, const STAmount& saDstAmount, bool& bValid)
|
||||
: mSrcAccountID(uSrcAccountID.getAccountID()),
|
||||
@@ -147,7 +147,7 @@ Pathfinder::Pathfinder(Ledger::ref ledger,
|
||||
mSrcCurrencyID(uSrcCurrencyID),
|
||||
mSrcIssuerID(uSrcIssuerID),
|
||||
mSrcAmount(uSrcCurrencyID, uSrcIssuerID, 1u, 0, true),
|
||||
mLedger(ledger)
|
||||
mLedger(cache->getLedger()), mRLCache(cache)
|
||||
{
|
||||
|
||||
if (((mSrcAccountID == mDstAccountID) && (mSrcCurrencyID == mDstAmount.getCurrency())) || mDstAmount.isZero())
|
||||
@@ -443,7 +443,7 @@ bool Pathfinder::findPaths(const unsigned int iMaxSteps, const unsigned int iMax
|
||||
bool bRequireAuth = isSetBit(sleEnd->getFieldU32(sfFlags), lsfRequireAuth);
|
||||
bool dstCurrency = speEnd.mCurrencyID == mDstAmount.getCurrency();
|
||||
|
||||
AccountItems& rippleLines(getRippleLines(speEnd.mAccountID));
|
||||
AccountItems& rippleLines(mRLCache->getRippleLines(speEnd.mAccountID));
|
||||
|
||||
std::vector< std::pair<int, uint160> > candidates;
|
||||
candidates.reserve(rippleLines.getItems().size());
|
||||
@@ -776,8 +776,9 @@ boost::unordered_set<uint160> usAccountDestCurrencies(const RippleAddress& raAcc
|
||||
return usCurrencies;
|
||||
}
|
||||
|
||||
AccountItems& Pathfinder::getRippleLines(const uint160& accountID)
|
||||
AccountItems& RLCache::getRippleLines(const uint160& accountID)
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mLock);
|
||||
boost::unordered_map<uint160, AccountItems::pointer>::iterator it = mRLMap.find(accountID);
|
||||
if (it == mRLMap.end())
|
||||
it = mRLMap.insert(std::make_pair(accountID, boost::make_shared<AccountItems>
|
||||
@@ -799,7 +800,7 @@ int Pathfinder::getPathsOut(const uint160& currencyID, const uint160& accountID,
|
||||
return it->second;
|
||||
|
||||
int count = 0;
|
||||
AccountItems& rippleLines(getRippleLines(accountID));
|
||||
AccountItems& rippleLines(mRLCache->getRippleLines(accountID));
|
||||
BOOST_FOREACH(AccountItem::ref item, rippleLines.getItems())
|
||||
{
|
||||
RippleState* rspEntry = (RippleState*) item.get();
|
||||
|
||||
@@ -35,6 +35,23 @@ public:
|
||||
};
|
||||
#endif
|
||||
|
||||
class RLCache
|
||||
{
|
||||
protected:
|
||||
boost::mutex mLock;
|
||||
Ledger::pointer mLedger;
|
||||
boost::unordered_map<uint160, AccountItems::pointer> mRLMap;
|
||||
|
||||
public:
|
||||
typedef boost::shared_ptr<RLCache> pointer;
|
||||
typedef const pointer& ref;
|
||||
|
||||
RLCache(Ledger::ref l) : mLedger(l) { ; }
|
||||
Ledger::ref getLedger() { return mLedger; }
|
||||
|
||||
AccountItems& getRippleLines(const uint160& accountID);
|
||||
};
|
||||
|
||||
class Pathfinder
|
||||
{
|
||||
uint160 mSrcAccountID;
|
||||
@@ -47,6 +64,7 @@ class Pathfinder
|
||||
Ledger::pointer mLedger;
|
||||
PathState::pointer mPsDefault;
|
||||
LoadEvent::pointer mLoadMonitor;
|
||||
RLCache::pointer mRLCache;
|
||||
|
||||
boost::unordered_map<uint160, AccountItems::pointer> mRLMap;
|
||||
boost::unordered_map<std::pair<uint160, uint160>, int> mPOMap;
|
||||
@@ -63,13 +81,11 @@ class Pathfinder
|
||||
|
||||
bool matchesOrigin(const uint160& currency, const uint160& issuer);
|
||||
|
||||
AccountItems& getRippleLines(const uint160& accountID);
|
||||
|
||||
int getPathsOut(const uint160& currency, const uint160& accountID,
|
||||
bool isAuthRequired, bool isDestCurrency, const uint160& dest);
|
||||
|
||||
public:
|
||||
Pathfinder(Ledger::ref ledger,
|
||||
Pathfinder(RLCache::ref cache,
|
||||
const RippleAddress& srcAccountID, const RippleAddress& dstAccountID,
|
||||
const uint160& srcCurrencyID, const uint160& srcIssuerID, const STAmount& dstAmount, bool& bValid);
|
||||
|
||||
|
||||
@@ -54,6 +54,7 @@ Json::Value rpcError(int iError, Json::Value jvResult)
|
||||
{ rpcNO_NETWORK, "noNetwork", "Network not available." },
|
||||
{ rpcNO_PATH, "noPath", "Unable to find a ripple path." },
|
||||
{ rpcNO_PERMISSION, "noPermission", "You don't have permission for this command." },
|
||||
{ rpcNO_PF_REQUEST, "noPathRequest", "No pathfinding request in progress." },
|
||||
{ rpcNOT_STANDALONE, "notStandAlone", "Operation valid in debug mode only." },
|
||||
{ rpcNOT_SUPPORTED, "notSupported", "Operation not supported." },
|
||||
{ rpcPASSWD_CHANGED, "passwdChanged", "Wrong key, password changed." },
|
||||
|
||||
@@ -42,6 +42,7 @@ enum {
|
||||
// Malformed command
|
||||
rpcINVALID_PARAMS,
|
||||
rpcUNKNOWN_COMMAND,
|
||||
rpcNO_PF_REQUEST,
|
||||
|
||||
// Bad parameter
|
||||
rpcACT_BITCOIN,
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "NicknameState.h"
|
||||
#include "InstanceCounter.h"
|
||||
#include "Offer.h"
|
||||
#include "PFRequest.h"
|
||||
|
||||
SETUP_LOG();
|
||||
|
||||
@@ -180,7 +181,8 @@ Json::Value RPCHandler::transactionSign(Json::Value jvRequest, bool bSubmit)
|
||||
{
|
||||
ScopedUnlock su(theApp->getMasterLock());
|
||||
bool bValid;
|
||||
Pathfinder pf(lSnapshot, raSrcAddressID, dstAccountID,
|
||||
RLCache::pointer cache = boost::make_shared<RLCache>(lSnapshot);
|
||||
Pathfinder pf(cache, raSrcAddressID, dstAccountID,
|
||||
saSendMax.getCurrency(), saSendMax.getIssuer(), saSend, bValid);
|
||||
|
||||
if (!bValid || !pf.findPaths(theConfig.PATH_SEARCH_SIZE, 3, spsPaths))
|
||||
@@ -1167,6 +1169,49 @@ Json::Value RPCHandler::doRandom(Json::Value jvRequest, int& cost, ScopedLock& M
|
||||
}
|
||||
}
|
||||
|
||||
Json::Value RPCHandler::doPathFind(Json::Value jvRequest, int& cost, ScopedLock& MasterLockHolder)
|
||||
{
|
||||
if (!jvRequest.isMember("subcommand") || !jvRequest["subcommand"].isString())
|
||||
return rpcError(rpcINVALID_PARAMS);
|
||||
|
||||
if (!mInfoSub)
|
||||
return rpcError(rpcNO_EVENTS);
|
||||
|
||||
std::string sSubCommand = jvRequest["subcommand"].asString();
|
||||
|
||||
if (sSubCommand == "create")
|
||||
{
|
||||
mInfoSub->clearPFRequest();
|
||||
PFRequest::pointer request = boost::make_shared<PFRequest>(mInfoSub);
|
||||
Json::Value result = request->doCreate(mNetOps->getClosedLedger(), jvRequest);
|
||||
if (request->isValid())
|
||||
{
|
||||
mInfoSub->setPFRequest(request);
|
||||
theApp->getLedgerMaster().newPFRequest();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
if (sSubCommand == "close")
|
||||
{
|
||||
PFRequest::pointer request = mInfoSub->getPFRequest();
|
||||
if (!request)
|
||||
return rpcError(rpcNO_PF_REQUEST);
|
||||
mInfoSub->clearPFRequest();
|
||||
return request->doClose(jvRequest);
|
||||
}
|
||||
|
||||
if (sSubCommand == "status")
|
||||
{
|
||||
PFRequest::pointer request = mInfoSub->getPFRequest();
|
||||
if (!request)
|
||||
return rpcNO_PF_REQUEST;
|
||||
return request->doStatus(jvRequest);
|
||||
}
|
||||
|
||||
return rpcError(rpcINVALID_PARAMS);
|
||||
}
|
||||
|
||||
// TODO:
|
||||
// - Add support for specifying non-endpoint issuer.
|
||||
// - Return fully expanded path with proof.
|
||||
@@ -1268,6 +1313,7 @@ Json::Value RPCHandler::doRipplePathFind(Json::Value jvRequest, int& cost, Scope
|
||||
jvResult["destination_account"] = raDst.humanAccountID();
|
||||
|
||||
Json::Value jvArray(Json::arrayValue);
|
||||
RLCache::pointer cache = boost::make_shared<RLCache>(lSnapShot);
|
||||
|
||||
for (unsigned int i=0; i != jvSrcCurrencies.size(); ++i) {
|
||||
Json::Value jvSource = jvSrcCurrencies[i];
|
||||
@@ -1303,7 +1349,7 @@ Json::Value RPCHandler::doRipplePathFind(Json::Value jvRequest, int& cost, Scope
|
||||
|
||||
STPathSet spsComputed;
|
||||
bool bValid;
|
||||
Pathfinder pf(lSnapShot, raSrc, raDst, uSrcCurrencyID, uSrcIssuerID, saDstAmount, bValid);
|
||||
Pathfinder pf(cache, raSrc, raDst, uSrcCurrencyID, uSrcIssuerID, saDstAmount, bValid);
|
||||
|
||||
if (!bValid || !pf.findPaths(theConfig.PATH_SEARCH_SIZE, 3, spsComputed))
|
||||
{
|
||||
@@ -3293,6 +3339,7 @@ Json::Value RPCHandler::doCommand(const Json::Value& jvRequest, int iRole, int &
|
||||
// { "nickname_info", &RPCHandler::doNicknameInfo, false, optCurrent },
|
||||
{ "owner_info", &RPCHandler::doOwnerInfo, false, optCurrent },
|
||||
{ "peers", &RPCHandler::doPeers, true, optNone },
|
||||
{ "path_find", &RPCHandler::doPathFind, false, optCurrent },
|
||||
{ "ping", &RPCHandler::doPing, false, optNone },
|
||||
// { "profile", &RPCHandler::doProfile, false, optCurrent },
|
||||
{ "random", &RPCHandler::doRandom, false, optNone },
|
||||
|
||||
@@ -69,6 +69,7 @@ class RPCHandler
|
||||
Json::Value doNicknameInfo(Json::Value params, int& cost, ScopedLock& mlh);
|
||||
Json::Value doOwnerInfo(Json::Value params, int& cost, ScopedLock& mlh);
|
||||
Json::Value doPeers(Json::Value params, int& cost, ScopedLock& mlh);
|
||||
Json::Value doPathFind(Json::Value params, int& cost, ScopedLock& mlh);
|
||||
Json::Value doPing(Json::Value params, int& cost, ScopedLock& mlh);
|
||||
Json::Value doProfile(Json::Value params, int& cost, ScopedLock& mlh);
|
||||
Json::Value doRandom(Json::Value jvRequest, int& cost, ScopedLock& mlh);
|
||||
|
||||
Reference in New Issue
Block a user