From 8002f8469e3f736bd8c6c171596baf0ac5be8cf0 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Thu, 28 Feb 2013 16:38:52 -0800 Subject: [PATCH 01/56] Improve usage. --- src/cpp/ripple/main.cpp | 44 ++++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/src/cpp/ripple/main.cpp b/src/cpp/ripple/main.cpp index 80e55a7e3f..1444b1a915 100644 --- a/src/cpp/ripple/main.cpp +++ b/src/cpp/ripple/main.cpp @@ -51,7 +51,6 @@ void startServer() theApp->run(); // Blocks till we get a stop RPC. } - bool init_unit_test() { theApp = new Application(); @@ -66,34 +65,30 @@ void printHelp(const po::options_description& desc) cerr << desc << endl; cerr << "Commands: " << endl; - cerr << " account_domain_set []" << endl; - cerr << " account_email_set []" << endl; - cerr << " account_tx || |( )" << endl; - cerr << " account_lines || []" << endl; - cerr << " account_offers || []" << endl; cerr << " account_info |" << endl; cerr << " account_info || []" << endl; - cerr << " account_message_set " << endl; - cerr << " account_publish_set " << endl; - cerr << " account_rate_set " << endl; - cerr << " account_wallet_set []" << endl; + cerr << " account_lines || []" << endl; + cerr << " account_offers || []" << endl; + cerr << " account_tx || |( )" << endl; + cerr << " book_offers [ ]" << endl; cerr << " connect []" << endl; + cerr << " consensus_info" << endl; +#if ENABLE_INSECURE cerr << " data_delete " << endl; cerr << " data_fetch " << endl; cerr << " data_store " << endl; +#endif + cerr << " get_counts" << endl; cerr << " ledger [|current|lastclosed] [full]" << endl; + cerr << " ledger_accept" << endl; + cerr << " ledger_closed" << endl; + cerr << " ledger_current" << endl; + cerr << " ledger_header " << endl; cerr << " logrotate " << endl; - cerr << " nickname_info " << endl; - cerr << " nickname_set [] []" << endl; - cerr << " offer_create [passive]" << endl; - cerr << " offer_cancel " << endl; - cerr << " password_fund []" << endl; - cerr << " password_set []" << endl; cerr << " peers" << endl; cerr << " random" << endl; cerr << " ripple ..." << endl; -// cerr << " ripple_line_set [] []" << endl; - cerr << " send [] [] []" << endl; +// cerr << " send [] [] []" << endl; cerr << " stop" << endl; cerr << " tx " << endl; cerr << " unl_add | []" << endl; @@ -109,6 +104,19 @@ void printHelp(const po::options_description& desc) cerr << " wallet_claim [] []" << endl; cerr << " wallet_seed [||]" << endl; cerr << " wallet_propose []" << endl; + +// Transaction helpers (that were removed): +// cerr << " account_domain_set []" << endl; +// cerr << " account_email_set []" << endl; +// cerr << " account_rate_set " << endl; +// cerr << " account_wallet_set []" << endl; +// cerr << " nickname_info " << endl; +// cerr << " nickname_set [] []" << endl; +// cerr << " offer_create [passive]" << endl; +// cerr << " offer_cancel " << endl; +// cerr << " password_fund []" << endl; +// cerr << " password_set []" << endl; +// cerr << " trust_set [] []" << endl; } int main(int argc, char* argv[]) From cf2a07fbff9c5df18d218fb58615cb7171b33e93 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Thu, 28 Feb 2013 16:40:38 -0800 Subject: [PATCH 02/56] Remove redundant RPC accept_ledger. --- src/cpp/ripple/RPCHandler.cpp | 13 ------------- src/cpp/ripple/RPCHandler.h | 2 -- 2 files changed, 15 deletions(-) diff --git a/src/cpp/ripple/RPCHandler.cpp b/src/cpp/ripple/RPCHandler.cpp index 215d239ce0..ee8a4f468c 100644 --- a/src/cpp/ripple/RPCHandler.cpp +++ b/src/cpp/ripple/RPCHandler.cpp @@ -558,18 +558,6 @@ Json::Value RPCHandler::accountFromString(Ledger::ref lrLedger, RippleAddress& n return Json::Value(Json::objectValue); } -Json::Value RPCHandler::doAcceptLedger(Json::Value jvRequest) -{ - if (!theConfig.RUN_STANDALONE) - return rpcError(rpcNOT_STANDALONE); - - Json::Value jvResult(Json::objectValue); - - jvResult["newLedger"] = theApp->getOPs().acceptLedger(); - - return jvResult; -} - // { // ident : , // account_index : // optional @@ -2807,7 +2795,6 @@ Json::Value RPCHandler::doCommand(const Json::Value& jvRequest, int iRole) unsigned int iOptions; } commandsA[] = { // Request-response methods - { "accept_ledger", &RPCHandler::doAcceptLedger, true, optCurrent }, { "account_info", &RPCHandler::doAccountInfo, false, optCurrent }, { "account_lines", &RPCHandler::doAccountLines, false, optCurrent }, { "account_offers", &RPCHandler::doAccountOffers, false, optCurrent }, diff --git a/src/cpp/ripple/RPCHandler.h b/src/cpp/ripple/RPCHandler.h index 47e30c2085..ac49c05f10 100644 --- a/src/cpp/ripple/RPCHandler.h +++ b/src/cpp/ripple/RPCHandler.h @@ -43,8 +43,6 @@ class RPCHandler Json::Value accountFromString(Ledger::ref lrLedger, RippleAddress& naAccount, bool& bIndex, const std::string& strIdent, const int iIndex, const bool bStrict); - Json::Value doAcceptLedger(Json::Value jvRequest); - Json::Value doAccountInfo(Json::Value params); Json::Value doAccountLines(Json::Value params); Json::Value doAccountOffers(Json::Value params); From 1571ae4a9a966205bf58095dc8aea6c88fdb7162 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Thu, 28 Feb 2013 16:40:51 -0800 Subject: [PATCH 03/56] Add isRpcError(). --- src/cpp/ripple/RPCErr.cpp | 5 +++++ src/cpp/ripple/RPCErr.h | 2 ++ 2 files changed, 7 insertions(+) diff --git a/src/cpp/ripple/RPCErr.cpp b/src/cpp/ripple/RPCErr.cpp index 9e06a5d9d7..de5b0a1650 100644 --- a/src/cpp/ripple/RPCErr.cpp +++ b/src/cpp/ripple/RPCErr.cpp @@ -88,4 +88,9 @@ Json::Value rpcError(int iError, Json::Value jvResult) return jvResult; } +bool isRpcError(Json::Value jvResult) +{ + return jvResult.isObject() && jvResult.isMember("error"); +} + // vim:ts=4 diff --git a/src/cpp/ripple/RPCErr.h b/src/cpp/ripple/RPCErr.h index 8aa371328a..f1f4da0976 100644 --- a/src/cpp/ripple/RPCErr.h +++ b/src/cpp/ripple/RPCErr.h @@ -77,6 +77,8 @@ enum { rpcNO_GEN_DECRPYT, }; +bool isRpcError(Json::Value jvResult); Json::Value rpcError(int iError, Json::Value jvResult=Json::Value(Json::objectValue)); + #endif // vim:ts=4 From d0c79083ab180094bf31a8ce3715beedd33bde46 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Thu, 28 Feb 2013 16:41:18 -0800 Subject: [PATCH 04/56] Add CLI support for RPC book_offers. --- src/cpp/ripple/CallRPC.cpp | 115 ++++++++++++++++++++++++++++++++++++- src/cpp/ripple/CallRPC.h | 1 + 2 files changed, 114 insertions(+), 2 deletions(-) diff --git a/src/cpp/ripple/CallRPC.cpp b/src/cpp/ripple/CallRPC.cpp index 50cd485fd6..cf30221c4c 100644 --- a/src/cpp/ripple/CallRPC.cpp +++ b/src/cpp/ripple/CallRPC.cpp @@ -1,3 +1,9 @@ +// +// This a trusted interface, the user is expected to provide valid input to perform valid requests. +// Error catching and reporting is not a requirement of this command line interface. +// +// Improvements to be more strict and to provide better diagnostics are welcome. +// #include #include @@ -9,6 +15,7 @@ #include #include #include +#include #include #include @@ -55,6 +62,65 @@ std::string EncodeBase64(const std::string& s) return result; } +// TODO New routine for parsing ledger parameters, other routines should standardize on this. +static bool jvParseLedger(Json::Value& jvRequest, const std::string& strLedger) +{ + if (strLedger == "closed") + { + jvRequest["ledger_index"] = -1; + } + else if (strLedger == "current") + { + jvRequest["ledger_index"] = -2; + } + else if (strLedger == "validated") + { + jvRequest["ledger_index"] = -3; + } + else if (strLedger.length() > 12) + { + // YYY Could confirm this is a uint256. + jvRequest["ledger_hash"] = strLedger; + } + else + { + jvRequest["ledger_index"] = lexical_cast_s(strLedger); + } + + return true; +} + +// Build a object { "currency" : "XYZ", "issuer" : "rXYX" } +static Json::Value jvParseCurrencyIssuer(const std::string& strCurrencyIssuer) +{ + static boost::regex reCurIss("\\`([[:alpha:]]{3})(?:/(.+))?\\'"); + + boost::smatch smMatch; + + bool bMatch = boost::regex_match(strCurrencyIssuer, smMatch, reCurIss); // Match status code. + + if (bMatch) + { + Json::Value jvResult(Json::objectValue); + std::string strCurrency = smMatch[1]; + std::string strIssuer = smMatch[2]; + + jvResult["currency"] = strCurrency; + + if (strIssuer.length()) + { + // Could confirm issuer is a valid Ripple address. + jvResult["issuer"] = strIssuer; + } + + return jvResult; + } + else + { + return rpcError(rpcINVALID_PARAMS); + } +} + Json::Value RPCParser::parseAsIs(const Json::Value& jvParams) { Json::Value v(Json::objectValue); @@ -145,6 +211,51 @@ Json::Value RPCParser::parseAccountTransactions(const Json::Value& jvParams) return jvRequest; } +// book_offers [ ] +// +// Mnemonic: taker puts --> offer --> taker gets +Json::Value RPCParser::parseBookOffers(const Json::Value& jvParams) +{ + Json::Value jvRequest(Json::objectValue); + + Json::Value jvTakerPuts = jvParseCurrencyIssuer(jvParams[0u].asString()); + Json::Value jvTakerGets = jvParseCurrencyIssuer(jvParams[1u].asString()); + + if (isRpcError(jvTakerPuts)) + { + return jvTakerPuts; + } + else + { + jvRequest["taker_puts"] = jvTakerPuts; + } + + if (isRpcError(jvTakerGets)) + { + return jvTakerGets; + } + else + { + jvRequest["taker_gets"] = jvTakerGets; + } + + if (!jvParseLedger(jvRequest, jvParams[2u].asString())) + return jvRequest; + + if (jvParams.size() >= 4) + { + int iLimit = jvParams[3u].asInt(); + + if (iLimit > 0) + jvRequest["limit"] = iLimit; + } + + if (jvParams.size() == 5) + jvRequest["marker"] = jvParams[4u]; + + return jvRequest; +} + // connect [port] Json::Value RPCParser::parseConnect(const Json::Value& jvParams) { @@ -509,11 +620,11 @@ Json::Value RPCParser::parseCommand(std::string strMethod, Json::Value jvParams) // Request-response methods // - Returns an error, or the request. // - To modify the method, provide a new method in the request. - { "accept_ledger", &RPCParser::parseAsIs, 0, 0 }, { "account_info", &RPCParser::parseAccountInfo, 1, 2 }, { "account_lines", &RPCParser::parseAccountItems, 1, 2 }, { "account_offers", &RPCParser::parseAccountItems, 1, 2 }, { "account_tx", &RPCParser::parseAccountTransactions, 2, 4 }, + { "book_offers", &RPCParser::parseBookOffers, 3, 5 }, { "connect", &RPCParser::parseConnect, 1, 2 }, { "consensus_info", &RPCParser::parseAsIs, 0, 0 }, { "get_counts", &RPCParser::parseGetCounts, 0, 1 }, @@ -622,7 +733,7 @@ int commandLineRPC(const std::vector& vCmd) jvRequest = rpParser.parseCommand(vCmd[0], jvRpcParams); - // std::cerr << "Request: " << jvRequest << std::endl; + std::cerr << "Request: " << jvRequest << std::endl; if (jvRequest.isMember("error")) { diff --git a/src/cpp/ripple/CallRPC.h b/src/cpp/ripple/CallRPC.h index 11cf518128..adde89f708 100644 --- a/src/cpp/ripple/CallRPC.h +++ b/src/cpp/ripple/CallRPC.h @@ -14,6 +14,7 @@ protected: Json::Value parseAccountItems(const Json::Value& jvParams); Json::Value parseAccountTransactions(const Json::Value& jvParams); Json::Value parseAsIs(const Json::Value& jvParams); + Json::Value parseBookOffers(const Json::Value& jvParams); Json::Value parseConnect(const Json::Value& jvParams); #if ENABLE_INSECURE Json::Value parseDataDelete(const Json::Value& jvParams); From 2f85057920c557d6339387914b812e98cbf61062 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Thu, 28 Feb 2013 16:57:19 -0800 Subject: [PATCH 05/56] Less debugging. --- src/cpp/ripple/CallRPC.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/cpp/ripple/CallRPC.cpp b/src/cpp/ripple/CallRPC.cpp index cf30221c4c..1c847b1b78 100644 --- a/src/cpp/ripple/CallRPC.cpp +++ b/src/cpp/ripple/CallRPC.cpp @@ -97,13 +97,11 @@ static Json::Value jvParseCurrencyIssuer(const std::string& strCurrencyIssuer) boost::smatch smMatch; - bool bMatch = boost::regex_match(strCurrencyIssuer, smMatch, reCurIss); // Match status code. - - if (bMatch) + if (boost::regex_match(strCurrencyIssuer, smMatch, reCurIss)) { Json::Value jvResult(Json::objectValue); - std::string strCurrency = smMatch[1]; - std::string strIssuer = smMatch[2]; + std::string strCurrency = smMatch[1]; + std::string strIssuer = smMatch[2]; jvResult["currency"] = strCurrency; @@ -733,7 +731,7 @@ int commandLineRPC(const std::vector& vCmd) jvRequest = rpParser.parseCommand(vCmd[0], jvRpcParams); - std::cerr << "Request: " << jvRequest << std::endl; + cLog(lsTRACE) << "RPC Request: " << jvRequest << std::endl; if (jvRequest.isMember("error")) { From 9cc53bd7168671c80fc497f38bebef269b1f259b Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Thu, 28 Feb 2013 18:10:24 -0800 Subject: [PATCH 06/56] Pass path for RPC subscribe by url. --- src/cpp/ripple/CallRPC.cpp | 4 +++- src/cpp/ripple/RPCSub.cpp | 6 ++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/cpp/ripple/CallRPC.cpp b/src/cpp/ripple/CallRPC.cpp index 1c847b1b78..b84d3ee5bd 100644 --- a/src/cpp/ripple/CallRPC.cpp +++ b/src/cpp/ripple/CallRPC.cpp @@ -865,6 +865,8 @@ bool responseRPC( // Build the request. void requestRPC(const std::string& strMethod, const Json::Value& jvParams, const std::map& mHeaders, const std::string& strPath, boost::asio::streambuf& sb, const std::string& strHost) { + cLog(lsDEBUG) << "requestRPC: strPath='" << strPath << "'"; + std::ostream osRequest(&sb); osRequest << @@ -913,7 +915,7 @@ void callRPC( strMethod, jvParams, mapRequestHeaders, - "/", _1, _2), + strPath, _1, _2), RPC_REPLY_MAX_BYTES, boost::posix_time::seconds(RPC_NOTIFY_SECONDS), boost::bind(&responseRPC, callbackFuncP, _1, _2, _3)); diff --git a/src/cpp/ripple/RPCSub.cpp b/src/cpp/ripple/RPCSub.cpp index 2a6ac6af89..779cff0ed1 100644 --- a/src/cpp/ripple/RPCSub.cpp +++ b/src/cpp/ripple/RPCSub.cpp @@ -29,6 +29,12 @@ RPCSub::RPCSub(const std::string& strUrl, const std::string& strUsername, const if (mPort < 0) mPort = mSSL ? 443 : 80; + + cLog(lsINFO) << boost::str(boost::format("callRPC sub: ip='%s' port=%d ssl=%d path='%s'") + % mIp + % mPort + % mSSL + % mPath); } // XXX Could probably create a bunch of send jobs in a single get of the lock. From 85cd5bc47f506240fb44282eae52c507dfb9fce0 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Thu, 28 Feb 2013 19:41:03 -0800 Subject: [PATCH 07/56] Fix the bug that crashed ec2. Must hold a TransactionAcquire::pointer to call a function that calls erase. --- src/cpp/ripple/LedgerConsensus.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/cpp/ripple/LedgerConsensus.cpp b/src/cpp/ripple/LedgerConsensus.cpp index 79fe9f9454..a2e56d5cbf 100644 --- a/src/cpp/ripple/LedgerConsensus.cpp +++ b/src/cpp/ripple/LedgerConsensus.cpp @@ -1044,7 +1044,10 @@ bool LedgerConsensus::peerHasSet(Peer::ref peer, const uint256& hashSet, ripple: set.push_back(peer); boost::unordered_map::iterator acq = mAcquiring.find(hashSet); if (acq != mAcquiring.end()) - acq->second->peerHas(peer); + { + TransactionAcquire::pointer ta = acq->second; // make sure it doesn't go away + ta->peerHas(peer); + } return true; } From 5cb119c08fcdc04a42ce1bba469fd4224dc9000d Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Thu, 28 Feb 2013 20:31:18 -0800 Subject: [PATCH 08/56] Eliminate extra HttpsClient pop. --- src/cpp/ripple/HttpsClient.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/cpp/ripple/HttpsClient.cpp b/src/cpp/ripple/HttpsClient.cpp index 4cbb32ea15..6ffc2fa464 100644 --- a/src/cpp/ripple/HttpsClient.cpp +++ b/src/cpp/ripple/HttpsClient.cpp @@ -370,10 +370,14 @@ void HttpsClient::invokeComplete(const boost::system::error_code& ecResult, int if (ecCancel) { - cLog(lsTRACE) << "Deadline cancel error: " << ecCancel.message(); + cLog(lsTRACE) << "HttpsClient::invokeComplete: Deadline cancel error: " << ecCancel.message(); } - mDeqSites.pop_front(); + cLog(lsINFO) << "HttpsClient::invokeComplete: Deadline popping: " << mDeqSites.size(); + if (!mDeqSites.empty()) + { + mDeqSites.pop_front(); + } bool bAgain = true; From b4c0469752d0974d7f2b5e9738b32f45212ed35a Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Thu, 28 Feb 2013 20:31:51 -0800 Subject: [PATCH 09/56] Start of RPC book_offers. --- src/cpp/ripple/RPCErr.cpp | 1 + src/cpp/ripple/RPCErr.h | 1 + src/cpp/ripple/RPCHandler.cpp | 80 +++++++++++++++++++++++++++++++++++ src/cpp/ripple/RPCHandler.h | 1 + 4 files changed, 83 insertions(+) diff --git a/src/cpp/ripple/RPCErr.cpp b/src/cpp/ripple/RPCErr.cpp index de5b0a1650..9d1f01259a 100644 --- a/src/cpp/ripple/RPCErr.cpp +++ b/src/cpp/ripple/RPCErr.cpp @@ -25,6 +25,7 @@ Json::Value rpcError(int iError, Json::Value jvResult) { rpcDST_ACT_MALFORMED, "dstActMalformed", "Destination account is malformed." }, { rpcDST_ACT_MISSING, "dstActMissing", "Destination account does not exist." }, { rpcDST_AMT_MALFORMED, "dstAmtMalformed", "Destination amount/currency/issuer is malformed." }, + { rpcDST_ISR_MALFORMED, "dstIsrMalformed", "Destination issuer is malformed." }, { rpcFORBIDDEN, "forbidden", "Bad credentials." }, { rpcFAIL_GEN_DECRPYT, "failGenDecrypt", "Failed to decrypt generator." }, { rpcGETS_ACT_MALFORMED, "getsActMalformed", "Gets account malformed." }, diff --git a/src/cpp/ripple/RPCErr.h b/src/cpp/ripple/RPCErr.h index f1f4da0976..45407969e9 100644 --- a/src/cpp/ripple/RPCErr.h +++ b/src/cpp/ripple/RPCErr.h @@ -50,6 +50,7 @@ enum { rpcDST_ACT_MALFORMED, rpcDST_ACT_MISSING, rpcDST_AMT_MALFORMED, + rpcDST_ISR_MALFORMED, rpcGETS_ACT_MALFORMED, rpcGETS_AMT_MALFORMED, rpcHOST_IP_MALFORMED, diff --git a/src/cpp/ripple/RPCHandler.cpp b/src/cpp/ripple/RPCHandler.cpp index ee8a4f468c..6d6f388016 100644 --- a/src/cpp/ripple/RPCHandler.cpp +++ b/src/cpp/ripple/RPCHandler.cpp @@ -1021,6 +1021,85 @@ Json::Value RPCHandler::doAccountOffers(Json::Value jvRequest) return jvResult; } +// { +// "ledger_hash" : ledger, // Optional +// "ledger_index" : ledger_index, // Optional +// "taker_gets" : { "currency": currency, "issuer" : address }, +// "taker_pays" : { "currency": currency, "issuer" : address }, +// "marker" : element, // Optional. +// "limit" : integer, // Optional. +// "proof" : boolean // Defaults to false. +// } +Json::Value RPCHandler::doBookOffers(Json::Value jvRequest) +{ + Ledger::pointer lpLedger; + Json::Value jvResult = lookupLedger(jvRequest, lpLedger); + + if (!lpLedger) + return jvResult; + + ScopedUnlock su(theApp->getMasterLock()); + + if (!jvRequest.isMember("taker_pays") || !jvRequest.isMember("taker_gets")) + return rpcError(rpcINVALID_PARAMS); + + uint160 uTakerPaysCurrencyID; + uint160 uTakerPaysIssuerID; + Json::Value jvTakerPays = jvRequest["taker_pays"]; + + // Parse mandatory currency. + if (!jvTakerPays.isMember("currency") + || !STAmount::currencyFromString(uTakerPaysCurrencyID, jvTakerPays["currency"].asString())) + { + cLog(lsINFO) << "Bad taker_pays currency."; + + return rpcError(rpcSRC_CUR_MALFORMED); + } + // Parse optional issuer. + else if (((jvTakerPays.isMember("issuer")) + && (!jvTakerPays["issuer"].isString() + || !STAmount::issuerFromString(uTakerPaysIssuerID, jvTakerPays["issuer"].asString()))) + // Don't allow illegal issuers. + || !uTakerPaysCurrencyID + || !uTakerPaysIssuerID + || ACCOUNT_ONE == uTakerPaysIssuerID) + { + cLog(lsINFO) << "Bad taker_pays issuer."; + + return rpcError(rpcSRC_ISR_MALFORMED); + } + + uint160 uTakerGetsCurrencyID; + uint160 uTakerGetsIssuerID; + Json::Value jvTakerGets = jvRequest["taker_pays"]; + + // Parse mandatory currency. + if (!jvTakerGets.isMember("currency") + || !STAmount::currencyFromString(uTakerGetsCurrencyID, jvTakerGets["currency"].asString())) + { + cLog(lsINFO) << "Bad taker_pays currency."; + + return rpcError(rpcSRC_CUR_MALFORMED); + } + // Parse optional issuer. + else if (((jvTakerGets.isMember("issuer")) + && (!jvTakerGets["issuer"].isString() + || !STAmount::issuerFromString(uTakerGetsIssuerID, jvTakerGets["issuer"].asString()))) + // Don't allow illegal issuers. + || !uTakerGetsCurrencyID + || !uTakerGetsIssuerID + || ACCOUNT_ONE == uTakerGetsIssuerID) + { + cLog(lsINFO) << "Bad taker_gets issuer."; + + return rpcError(rpcDST_ISR_MALFORMED); + } + + jvResult["hello"] = "world"; + + return jvResult; +} + // Result: // { // random: @@ -2799,6 +2878,7 @@ Json::Value RPCHandler::doCommand(const Json::Value& jvRequest, int iRole) { "account_lines", &RPCHandler::doAccountLines, false, optCurrent }, { "account_offers", &RPCHandler::doAccountOffers, false, optCurrent }, { "account_tx", &RPCHandler::doAccountTransactions, false, optNetwork }, + { "book_offers", &RPCHandler::doBookOffers, false, optCurrent }, { "connect", &RPCHandler::doConnect, true, optNone }, { "consensus_info", &RPCHandler::doConsensusInfo, true, optNone }, { "get_counts", &RPCHandler::doGetCounts, true, optNone }, diff --git a/src/cpp/ripple/RPCHandler.h b/src/cpp/ripple/RPCHandler.h index ac49c05f10..48fe696cee 100644 --- a/src/cpp/ripple/RPCHandler.h +++ b/src/cpp/ripple/RPCHandler.h @@ -47,6 +47,7 @@ class RPCHandler Json::Value doAccountLines(Json::Value params); Json::Value doAccountOffers(Json::Value params); Json::Value doAccountTransactions(Json::Value params); + Json::Value doBookOffers(Json::Value params); Json::Value doConnect(Json::Value params); Json::Value doConsensusInfo(Json::Value params); #if ENABLE_INSECURE From e824baf15f8099d50cf2b2becdf1859c2eea3ba4 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Thu, 28 Feb 2013 20:32:17 -0800 Subject: [PATCH 10/56] Begining of ledger browser hack. --- bin/browser.js | 100 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100755 bin/browser.js diff --git a/bin/browser.js b/bin/browser.js new file mode 100755 index 0000000000..f7a8d09ffd --- /dev/null +++ b/bin/browser.js @@ -0,0 +1,100 @@ +#!/usr/bin/node + +var http = require("http"); + +var Remote = require("../src/js/remote.js").Remote; + +var program = process.argv[1]; + +if (process.argv.length < 4 || process.argv.length > 7) { + console.log("Usage: %s ws_ip ws_port [ [ []]]", program); +} +else { + var ws_ip = process.argv[2]; + var ws_port = process.argv[3]; + var ip = process.argv.length > 4 ? process.argv[4] : "127.0.0.1"; + var port = process.argv.length > 5 ? process.argv[5] : "8080"; + +console.log("START"); + var self = this; + var remote = (new Remote({ + websocket_ip: ws_ip, + websocket_port: ws_port, + trace: true + })) + .on('state', function (m) { + console.log("STATE: %s", m); + + self.state = m; + }) +// .once('ledger_closed', callback) + .connect() + ; + +console.log("SERVE"); + var server = http.createServer(function (req, res) { + var input = ""; + + req.setEncoding(); + + req.on('data', function (buffer) { + // console.log("DATA: %s", buffer); + input = input + buffer; + }); + + req.on('end', function () { + console.log("URL: %s", req.url); + console.log("HEADERS: %s", JSON.stringify(req.headers, undefined, 2)); + + if (req.url === "/ledger_header") { + + var request = remote + .request_ledger_header() + .ledger_index(-1) + .on('success', function (m) { + console.log("Ledger: %s", JSON.stringify(m, undefined, 2)); + + res.statusCode = 200; + res.end( + "" + + "Title" + + "" + + "State: " + self.state + + "" + + "
"
+                        + JSON.stringify(m, undefined, 2)
+                        + "
" + + "" + + "" + ); + }) + .request(); + } + else { + res.statusCode = req.url === "/" ? 200 : 400; + res.end( + "" + + "Title" + + "" + + "State: " + self.state + + "" + + "" + + "" + ); + } + }); + }); + + server.listen(port, ip, undefined, + function () { + console.log("Listening at: http://%s:%s", ip, port); + }); +} + +// vim:sw=2:sts=2:ts=8:et From fca5683d3137521568825418425d929923017865 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Thu, 28 Feb 2013 22:52:01 -0800 Subject: [PATCH 11/56] Trivial const fix. --- src/cpp/ripple/SerializedLedger.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cpp/ripple/SerializedLedger.cpp b/src/cpp/ripple/SerializedLedger.cpp index 8f07ec715a..b4285dd879 100644 --- a/src/cpp/ripple/SerializedLedger.cpp +++ b/src/cpp/ripple/SerializedLedger.cpp @@ -51,7 +51,7 @@ SerializedLedgerEntry::SerializedLedgerEntry(LedgerEntryType type, const uint256 SerializedLedgerEntry::pointer SerializedLedgerEntry::getMutable() const { - SerializedLedgerEntry::pointer ret = boost::make_shared(boost::ref(*this)); + SerializedLedgerEntry::pointer ret = boost::make_shared(boost::cref(*this)); ret->mMutable = true; return ret; } From 7a9037d1cabbc21ed8bf45d72a27c2a5d3bfdc6f Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Thu, 28 Feb 2013 23:52:53 -0800 Subject: [PATCH 12/56] Large speed up of getField* functions on non-free objects such as transactions and ledger nodes. --- src/cpp/ripple/FieldNames.h | 1 + src/cpp/ripple/LedgerFormats.cpp | 6 +-- src/cpp/ripple/LedgerFormats.h | 4 +- src/cpp/ripple/SerializedObject.cpp | 55 ++++++++++++++----------- src/cpp/ripple/SerializedObject.h | 38 +++++++++++------ src/cpp/ripple/SerializedValidation.cpp | 27 ++++++------ src/cpp/ripple/TransactionFormats.cpp | 6 +-- src/cpp/ripple/TransactionFormats.h | 4 +- src/cpp/ripple/main.cpp | 8 ++++ 9 files changed, 83 insertions(+), 66 deletions(-) diff --git a/src/cpp/ripple/FieldNames.h b/src/cpp/ripple/FieldNames.h index 4919569e15..f199c1f95c 100644 --- a/src/cpp/ripple/FieldNames.h +++ b/src/cpp/ripple/FieldNames.h @@ -96,6 +96,7 @@ public: bool isKnown() const { return fieldType != STI_UNKNOWN; } bool isBinary() const { return fieldValue < 256; } bool isDiscardable() const { return fieldValue > 256; } + int getCode() const { return fieldCode; } bool isSigningField() const { return signingField; } void notSigningField() { signingField = false; } diff --git a/src/cpp/ripple/LedgerFormats.cpp b/src/cpp/ripple/LedgerFormats.cpp index c1c36bb7b8..fc6c70da33 100644 --- a/src/cpp/ripple/LedgerFormats.cpp +++ b/src/cpp/ripple/LedgerFormats.cpp @@ -11,7 +11,7 @@ std::map LedgerEntryFormat::byName; #define DECLARE_LEF(name, type) lef = new LedgerEntryFormat(#name, type); (*lef) LEF_BASE -static bool LEFInit() +void LEFInit() { LedgerEntryFormat* lef; @@ -111,12 +111,8 @@ static bool LEFInit() << SOElement(sfReserveBase, SOE_REQUIRED) << SOElement(sfReserveIncrement, SOE_REQUIRED) ; - - return true; } -bool LEFInitComplete = LEFInit(); - LedgerEntryFormat* LedgerEntryFormat::getLgrFormat(LedgerEntryType t) { std::map::iterator it = byType.find(static_cast(t)); diff --git a/src/cpp/ripple/LedgerFormats.h b/src/cpp/ripple/LedgerFormats.h index a491a58e3c..bfc692a669 100644 --- a/src/cpp/ripple/LedgerFormats.h +++ b/src/cpp/ripple/LedgerFormats.h @@ -58,7 +58,7 @@ class LedgerEntryFormat public: std::string t_name; LedgerEntryType t_type; - std::vector elements; + SOTemplate elements; static std::map byType; static std::map byName; @@ -70,7 +70,7 @@ public: } LedgerEntryFormat& operator<<(const SOElement& el) { - elements.push_back(new SOElement(el)); + elements.push_back(el); return *this; } diff --git a/src/cpp/ripple/SerializedObject.cpp b/src/cpp/ripple/SerializedObject.cpp index cff7a32724..2ab80d4147 100644 --- a/src/cpp/ripple/SerializedObject.cpp +++ b/src/cpp/ripple/SerializedObject.cpp @@ -130,14 +130,26 @@ std::auto_ptr STObject::makeDeserializedObject(SerializedTypeID } } -void STObject::set(const std::vector& type) +void SOTemplate::push_back(const SOElement &r) +{ + assert(mMap.find(r.e_field.getCode()) == mMap.end()); + mMap[r.e_field.getCode()] = mTypes.size(); + mTypes.push_back(new SOElement(r)); +} + +int SOTemplate::getIndex(SField::ref f) const +{ + std::map::const_iterator it = mMap.find(f.getCode()); + return (it == mMap.end()) ? -1 : it->second; +} + +void STObject::set(const SOTemplate& type) { mData.clear(); - mType.clear(); + mType = &type; - BOOST_FOREACH(SOElement::ref elem, type) + BOOST_FOREACH(const SOElement* elem, type.peek()) { - mType.push_back(elem); if (elem->flags != SOE_REQUIRED) giveObject(makeNonPresentObject(elem->e_field)); else @@ -145,15 +157,14 @@ void STObject::set(const std::vector& type) } } -bool STObject::setType(const std::vector &type) +bool STObject::setType(const SOTemplate &type) { - boost::ptr_vector newData(type.size()); + boost::ptr_vector newData(type.peek().size()); bool valid = true; - mType.clear(); - mType.reserve(type.size()); + mType = &type; - BOOST_FOREACH(SOElement::ref elem, type) + BOOST_FOREACH(const SOElement* elem, type.peek()) { bool match = false; for (boost::ptr_vector::iterator it = mData.begin(); it != mData.end(); ++it) @@ -180,8 +191,6 @@ bool STObject::setType(const std::vector &type) } newData.push_back(makeNonPresentObject(elem->e_field)); } - - mType.push_back(elem); } BOOST_FOREACH(const SerializedType& t, mData) @@ -201,7 +210,7 @@ bool STObject::setType(const std::vector &type) bool STObject::isValidForType() { boost::ptr_vector::iterator it = mData.begin(); - BOOST_FOREACH(SOElement::ref elem, mType) + BOOST_FOREACH(const SOElement* elem, mType->peek()) { if (it == mData.end()) return false; @@ -215,14 +224,9 @@ bool STObject::isValidForType() bool STObject::isFieldAllowed(SField::ref field) { - if (isFree()) + if (mType == NULL) return true; - BOOST_FOREACH(SOElement::ref elem, mType) - { // are any required elemnents missing - if (elem->e_field == field) - return true; - } - return false; + return mType->getIndex(field) != -1; } bool STObject::set(SerializerIterator& sit, int depth) @@ -361,6 +365,9 @@ uint256 STObject::getSigningHash(uint32 prefix) const int STObject::getFieldIndex(SField::ref field) const { + if (mType != NULL) + return mType->getIndex(field); + int i = 0; BOOST_FOREACH(const SerializedType& elem, mData) { @@ -1243,11 +1250,11 @@ BOOST_AUTO_TEST_CASE( FieldManipulation_test ) SField sfTestU32(STI_UINT32, 255, "TestU32"); SField sfTestObject(STI_OBJECT, 255, "TestObject"); - std::vector elements; - elements.push_back(new SOElement(sfFlags, SOE_REQUIRED)); - elements.push_back(new SOElement(sfTestVL, SOE_REQUIRED)); - elements.push_back(new SOElement(sfTestH256, SOE_OPTIONAL)); - elements.push_back(new SOElement(sfTestU32, SOE_REQUIRED)); + SOTemplate elements; + elements.push_back(SOElement(sfFlags, SOE_REQUIRED)); + elements.push_back(SOElement(sfTestVL, SOE_REQUIRED)); + elements.push_back(SOElement(sfTestH256, SOE_OPTIONAL)); + elements.push_back(SOElement(sfTestU32, SOE_REQUIRED)); STObject object1(elements, sfTestObject); STObject object2(object1); diff --git a/src/cpp/ripple/SerializedObject.h b/src/cpp/ripple/SerializedObject.h index 545ce91fbb..bd5e5b2c16 100644 --- a/src/cpp/ripple/SerializedObject.h +++ b/src/cpp/ripple/SerializedObject.h @@ -18,32 +18,44 @@ DEFINE_INSTANCE(SerializedArray); class SOElement { // An element in the description of a serialized object public: - typedef SOElement const * ref; // used to point to one element + SField::ref e_field; + const SOE_Flags flags; - SField::ref e_field; - const SOE_Flags flags; + SOElement(SField::ref fi, SOE_Flags fl) : e_field(fi), flags(fl) { ; } +}; - SOElement(SField::ref fi, SOE_Flags fl) : e_field(fi), flags(fl) { ; } +class SOTemplate +{ +protected: + std::vector mTypes; + std::map mMap; // field code -> index + +public: + SOTemplate() { ; } + const std::vector& peek() const { return mTypes; } + void push_back(const SOElement& r); + int getIndex(SField::ref) const; }; class STObject : public SerializedType, private IS_INSTANCE(SerializedObject) { protected: boost::ptr_vector mData; - std::vector mType; + const SOTemplate* mType; STObject* duplicate() const { return new STObject(*this); } - STObject(SField::ref name, boost::ptr_vector& data) : SerializedType(name) { mData.swap(data); } + STObject(SField::ref name, boost::ptr_vector& data) : SerializedType(name), mType(NULL) + { mData.swap(data); } public: - STObject() { ; } + STObject() : mType(NULL) { ; } - STObject(SField::ref name) : SerializedType(name) { ; } + STObject(SField::ref name) : SerializedType(name), mType(NULL) { ; } - STObject(const std::vector& type, SField::ref name) : SerializedType(name) + STObject(const SOTemplate& type, SField::ref name) : SerializedType(name) { set(type); } - STObject(const std::vector& type, SerializerIterator& sit, SField::ref name) : SerializedType(name) + STObject(const SOTemplate& type, SerializerIterator& sit, SField::ref name) : SerializedType(name) { set(sit); setType(type); } std::auto_ptr oClone() const { return std::auto_ptr(new STObject(*this)); } @@ -54,12 +66,12 @@ public: static std::auto_ptr deserialize(SerializerIterator& sit, SField::ref name); - bool setType(const std::vector& type); + bool setType(const SOTemplate& type); bool isValidForType(); bool isFieldAllowed(SField::ref); - bool isFree() const { return mType.empty(); } + bool isFree() const { return mType == NULL; } - void set(const std::vector&); + void set(const SOTemplate&); bool set(SerializerIterator& u, int depth = 0); virtual SerializedTypeID getSType() const { return STI_OBJECT; } diff --git a/src/cpp/ripple/SerializedValidation.cpp b/src/cpp/ripple/SerializedValidation.cpp index f50ed08947..f4031c0c7d 100644 --- a/src/cpp/ripple/SerializedValidation.cpp +++ b/src/cpp/ripple/SerializedValidation.cpp @@ -6,25 +6,22 @@ DECLARE_INSTANCE(SerializedValidation); -std::vector sValidationFormat; +SOTemplate sValidationFormat; -static bool SVFInit() +void SVFInit() { - sValidationFormat.push_back(new SOElement(sfFlags, SOE_REQUIRED)); - sValidationFormat.push_back(new SOElement(sfLedgerHash, SOE_REQUIRED)); - sValidationFormat.push_back(new SOElement(sfLedgerSequence, SOE_OPTIONAL)); - sValidationFormat.push_back(new SOElement(sfCloseTime, SOE_OPTIONAL)); - sValidationFormat.push_back(new SOElement(sfLoadFee, SOE_OPTIONAL)); - sValidationFormat.push_back(new SOElement(sfBaseFee, SOE_OPTIONAL)); - sValidationFormat.push_back(new SOElement(sfFeatures, SOE_OPTIONAL)); - sValidationFormat.push_back(new SOElement(sfSigningTime, SOE_REQUIRED)); - sValidationFormat.push_back(new SOElement(sfSigningPubKey, SOE_REQUIRED)); - sValidationFormat.push_back(new SOElement(sfSignature, SOE_OPTIONAL)); - return true; + sValidationFormat.push_back(SOElement(sfFlags, SOE_REQUIRED)); + sValidationFormat.push_back(SOElement(sfLedgerHash, SOE_REQUIRED)); + sValidationFormat.push_back(SOElement(sfLedgerSequence, SOE_OPTIONAL)); + sValidationFormat.push_back(SOElement(sfCloseTime, SOE_OPTIONAL)); + sValidationFormat.push_back(SOElement(sfLoadFee, SOE_OPTIONAL)); + sValidationFormat.push_back(SOElement(sfBaseFee, SOE_OPTIONAL)); + sValidationFormat.push_back(SOElement(sfFeatures, SOE_OPTIONAL)); + sValidationFormat.push_back(SOElement(sfSigningTime, SOE_REQUIRED)); + sValidationFormat.push_back(SOElement(sfSigningPubKey, SOE_REQUIRED)); + sValidationFormat.push_back(SOElement(sfSignature, SOE_OPTIONAL)); }; -bool SVFinitComplete = SVFInit(); - const uint32 SerializedValidation::sFullFlag = 0x1; SerializedValidation::SerializedValidation(SerializerIterator& sit, bool checkSignature) diff --git a/src/cpp/ripple/TransactionFormats.cpp b/src/cpp/ripple/TransactionFormats.cpp index effb983698..66d3a01e0b 100644 --- a/src/cpp/ripple/TransactionFormats.cpp +++ b/src/cpp/ripple/TransactionFormats.cpp @@ -16,7 +16,7 @@ std::map TransactionFormat::byName; #define DECLARE_TF(name, type) tf = new TransactionFormat(#name, type); (*tf) TF_BASE -static bool TFInit() +void TFInit() { TransactionFormat* tf; @@ -84,12 +84,8 @@ static bool TFInit() << SOElement(sfReserveBase, SOE_REQUIRED) << SOElement(sfReserveIncrement, SOE_REQUIRED) ; - - return true; } -bool TFInitComplete = TFInit(); - TransactionFormat* TransactionFormat::getTxnFormat(TransactionType t) { std::map::iterator it = byType.find(static_cast(t)); diff --git a/src/cpp/ripple/TransactionFormats.h b/src/cpp/ripple/TransactionFormats.h index ceb5c9b7d8..7e69ce7416 100644 --- a/src/cpp/ripple/TransactionFormats.h +++ b/src/cpp/ripple/TransactionFormats.h @@ -31,7 +31,7 @@ class TransactionFormat public: std::string t_name; TransactionType t_type; - std::vector elements; + SOTemplate elements; static std::map byType; static std::map byName; @@ -43,7 +43,7 @@ public: } TransactionFormat& operator<<(const SOElement& el) { - elements.push_back(new SOElement(el)); + elements.push_back(el); return *this; } diff --git a/src/cpp/ripple/main.cpp b/src/cpp/ripple/main.cpp index 1444b1a915..6be4fd138d 100644 --- a/src/cpp/ripple/main.cpp +++ b/src/cpp/ripple/main.cpp @@ -16,6 +16,10 @@ namespace po = boost::program_options; extern bool AddSystemEntropy(); +extern void TFInit(); +extern void LEFInit(); +extern void SVFInit(); + using namespace std; using namespace boost::unit_test; @@ -188,6 +192,10 @@ int main(int argc, char* argv[]) InstanceType::multiThread(); + TFInit(); + LEFInit(); + SVFInit(); + if (vm.count("unittest")) { unit_test_main(init_unit_test, argc, argv); From a2efb1e21cd495b9e31bf8625dc682c7d70059f7 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Fri, 1 Mar 2013 00:42:25 -0800 Subject: [PATCH 13/56] JS: Clean up ledger selection. --- src/js/remote.js | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/src/js/remote.js b/src/js/remote.js index 72a9f6b627..7eca87f002 100644 --- a/src/js/remote.js +++ b/src/js/remote.js @@ -91,6 +91,26 @@ Request.prototype.ledger_index = function (ledger_index) { return this; }; +Request.prototype.ledger_select = function (ledger_spec) { + if (ledger_spec === 'closed') { + this.message.ledger_index = -1; + + } else if (ledger_spec === 'current') { + this.message.ledger_index = -2; + + } else if (ledger_spec === 'verified') { + this.message.ledger_index = -3; + + } else if (String(ledger_spec).length > 12) { // XXX Better test needed + this.message.ledger_hash = ledger_spec; + + } else { + this.message.ledger_index = ledger_spec; + } + + return this; +}; + Request.prototype.account_root = function (account) { this.message.account_root = UInt160.json_rewrite(account); @@ -846,12 +866,13 @@ Remote.prototype.request_unsubscribe = function (streams) { return request; }; -// --> current: true, for the current ledger. -Remote.prototype.request_transaction_entry = function (hash, current) { +// .ledger_choose() +// .ledger_hash() +// .ledger_index() +Remote.prototype.request_transaction_entry = function (hash) { //utils.assert(this.trusted); // If not trusted, need to check proof, maybe talk packet protocol. return (new Request(this, 'transaction_entry')) - .ledger_choose(current) .tx_hash(hash); }; From 67db174b8bc4930b52e5154c436ca97b08b95d62 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Fri, 1 Mar 2013 00:42:53 -0800 Subject: [PATCH 14/56] More work on browser. --- bin/browser.js | 173 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 168 insertions(+), 5 deletions(-) diff --git a/bin/browser.js b/bin/browser.js index f7a8d09ffd..ab33850d05 100755 --- a/bin/browser.js +++ b/bin/browser.js @@ -1,11 +1,105 @@ #!/usr/bin/node +// +// ledger_header?l=L +// transaction?h=H +// ledger_entry?l=L&h=H +// account_root?l=L&a=A +// directory?l=L&dir_root=H&i=I +// directory?l=L&o=A&i=I // owner directory +// offer?l=L&offer=H +// offer?l=L&account=A&i=I +// ripple_state=l=L&a=A&b=A&c=C +// account_lines?l=L&a=A +// +// A=address +// C=currency 3 letter code +// H=hash +// I=index +// L=current | closed | validated | index | hash +// +var extend = require("extend"); var http = require("http"); +var url = require("url"); var Remote = require("../src/js/remote.js").Remote; var program = process.argv[1]; +// Build a link to a type. +var build_uri = function (params, opts) { + var c; + + if (params.type === 'account_root') { + c = { + pathname: 'account_root', + query: { + l: params.ledger, + a: params.account, + }, + }; + + } else if (params.type === 'ledger_header') { + c = { + pathname: 'ledger_header', + query: { + l: params.ledger, + }, + }; + + } else if (params.type === 'transaction') { + c = { + pathname: 'transaction', + query: { + h: params.hash, + }, + }; + } else { + c = {}; + } + + c.protocol = "http"; + c.hostname = opts.hostname; + c.port = opts.port; + + return url.format(c); +}; + +var build_link = function (item, link) { +console.log(link); + return "" + item + ""; +}; + +var rewrite_object = function (obj, opts) { + var out = extend({}, obj); + + if ('ledger_index' in obj) { + out.ledger_index = + build_link( + obj.ledger_index, + build_uri({ + type: 'ledger_header', + ledger: obj.ledger_index, + }, opts) + ); + } + + if ('node' in obj) { + if (obj.node.LedgerEntryType === 'AccountRoot') { + out.node.PreviousTxnID = + build_link( + obj.node.PreviousTxnID, + build_uri({ + type: 'transaction', + hash: obj.node.PreviousTxnID, + }, opts) + ); + } + } + + return out; +}; + if (process.argv.length < 4 || process.argv.length > 7) { console.log("Usage: %s ws_ip ws_port [ [ []]]", program); } @@ -17,6 +111,12 @@ else { console.log("START"); var self = this; + + self.base = { + hostname: ip, + port: port, + }; + var remote = (new Remote({ websocket_ip: ws_ip, websocket_port: ws_port, @@ -44,10 +144,39 @@ console.log("SERVE"); req.on('end', function () { console.log("URL: %s", req.url); - console.log("HEADERS: %s", JSON.stringify(req.headers, undefined, 2)); + // console.log("HEADERS: %s", JSON.stringify(req.headers, undefined, 2)); - if (req.url === "/ledger_header") { + var _parsed = url.parse(req.url, true); + var _url = JSON.stringify(_parsed, undefined, 2); + if (_parsed.pathname === "/account_root") { + var request = remote + .request_ledger_entry('account_root') + .ledger_index(-1) + .account_root(_parsed.query.a) + .on('success', function (m) { + console.log("account_root: %s", JSON.stringify(m, undefined, 2)); + + res.statusCode = 200; + res.end( + "" + + "Title" + + "" + + "State: " + self.state + + "" + + "
"
+                        + JSON.stringify(rewrite_object(m, self.base), undefined, 2)
+                        + "
" + + "" + + "" + ); + }) + .request(); + + } else if (_parsed.pathname === "/ledger_header") { var request = remote .request_ledger_header() .ledger_index(-1) @@ -72,9 +201,41 @@ console.log("SERVE"); ); }) .request(); - } - else { - res.statusCode = req.url === "/" ? 200 : 400; + + } else if (_parsed.pathname === "/transaction") { + var request = remote + .request_transaction_entry(_parsed.query.h) +// .ledger_select(_parsed.query.l) + .on('success', function (m) { + console.log("transaction: %s", JSON.stringify(m, undefined, 2)); + + res.statusCode = 200; + res.end( + "" + + "Title" + + "" + + "State: " + self.state + + "" + + "
"
+                        + JSON.stringify(rewrite_object(m, self.base), undefined, 2)
+                        + "
" + + "" + + "" + ); + }) + .request(); + + } else { + var test = build_uri({ + type: 'account_root', + ledger: 'closed', + account: 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', + }, self.base); + + res.statusCode = req.url === "/" ? 200 : 404; res.end( "" + "Title" @@ -82,8 +243,10 @@ console.log("SERVE"); + "State: " + self.state + "" + + "
"+_url+"
" + "" + "" ); From e9ad3562fb92178d61385ea982cc227a515eca4f Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Fri, 1 Mar 2013 01:17:11 -0800 Subject: [PATCH 15/56] Handle transactions with bad source public keys. --- src/cpp/ripple/Transaction.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/cpp/ripple/Transaction.cpp b/src/cpp/ripple/Transaction.cpp index bbe9f19384..75dce8f892 100644 --- a/src/cpp/ripple/Transaction.cpp +++ b/src/cpp/ripple/Transaction.cpp @@ -116,7 +116,11 @@ bool Transaction::sign(const RippleAddress& naAccountPrivate) bool Transaction::checkSign() const { - assert(mFromPubKey.isValid()); + if (!mFromPubKey.isValid()) + { + cLog(lsWARNING) << "Transaction has bad source public key"; + return false; + } return mTransaction->checkSign(mFromPubKey); } From 40dbdfedaf3856b2bb2ec5d117db5af57d4db55c Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Fri, 1 Mar 2013 01:21:03 -0800 Subject: [PATCH 16/56] Grr. --- src/cpp/ripple/Transaction.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cpp/ripple/Transaction.cpp b/src/cpp/ripple/Transaction.cpp index 75dce8f892..4ae2a4636a 100644 --- a/src/cpp/ripple/Transaction.cpp +++ b/src/cpp/ripple/Transaction.cpp @@ -118,7 +118,7 @@ bool Transaction::checkSign() const { if (!mFromPubKey.isValid()) { - cLog(lsWARNING) << "Transaction has bad source public key"; + Log(lsWARNING) << "Transaction has bad source public key"; return false; } return mTransaction->checkSign(mFromPubKey); From 036d0a5b0ae3a3f0cb7a9570610460817727585b Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Fri, 1 Mar 2013 10:28:40 -0800 Subject: [PATCH 17/56] Logging reduction. --- src/cpp/ripple/HttpsClient.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cpp/ripple/HttpsClient.cpp b/src/cpp/ripple/HttpsClient.cpp index 6ffc2fa464..1ae01e19f2 100644 --- a/src/cpp/ripple/HttpsClient.cpp +++ b/src/cpp/ripple/HttpsClient.cpp @@ -373,7 +373,7 @@ void HttpsClient::invokeComplete(const boost::system::error_code& ecResult, int cLog(lsTRACE) << "HttpsClient::invokeComplete: Deadline cancel error: " << ecCancel.message(); } - cLog(lsINFO) << "HttpsClient::invokeComplete: Deadline popping: " << mDeqSites.size(); + cLog(lsDEBUG) << "HttpsClient::invokeComplete: Deadline popping: " << mDeqSites.size(); if (!mDeqSites.empty()) { mDeqSites.pop_front(); From 1167158f64f810027e00525e8b134d0f7b033541 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Fri, 1 Mar 2013 10:28:50 -0800 Subject: [PATCH 18/56] Reduce I/O needed on startup. --- src/cpp/ripple/Ledger.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cpp/ripple/Ledger.cpp b/src/cpp/ripple/Ledger.cpp index a43d2bee44..5404264202 100644 --- a/src/cpp/ripple/Ledger.cpp +++ b/src/cpp/ripple/Ledger.cpp @@ -403,9 +403,9 @@ uint256 Ledger::getHash() void Ledger::saveAcceptedLedger(bool fromConsensus, LoadEvent::pointer event) { // can be called in a different thread cLog(lsTRACE) << "saveAcceptedLedger " << (fromConsensus ? "fromConsensus " : "fromAcquire ") << getLedgerSeq(); - static boost::format ledgerExists("SELECT LedgerSeq FROM Ledgers where LedgerSeq = %d;"); + static boost::format ledgerExists("SELECT LedgerSeq FROM Ledgers INDEXED BY SeqLedger where LedgerSeq = %d;"); static boost::format deleteLedger("DELETE FROM Ledgers WHERE LedgerSeq = %d;"); - static boost::format AcctTransExists("SELECT LedgerSeq FROM AccountTransactions WHERE TransId = '%s';"); + static boost::format AcctTransExists("SELECT LedgerSeq FROM AccountTransactions WHERE TransID = '%s';"); static boost::format transExists("SELECT Status FROM Transactions WHERE TransID = '%s';"); static boost::format updateTx("UPDATE Transactions SET LedgerSeq = %d, Status = '%c', TxnMeta = %s WHERE TransID = '%s';"); @@ -729,7 +729,7 @@ bool Ledger::getHashesByIndex(uint32 ledgerIndex, uint256& ledgerHash, uint256& ScopedLock sl(con->getDBLock()); static SqliteStatement pSt(con->getDB()->getSqliteDB(), - "SELECT LedgerHash,PrevHash FROM Ledgers Where LedgerSeq = ?;"); + "SELECT LedgerHash,PrevHash FROM Ledgers INDEXED BY SeqLedger Where LedgerSeq = ?;"); pSt.reset(); pSt.bind(1, ledgerIndex); From 51ebd75a4a7594173fdd5f5368156f8cc6f06b83 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Fri, 1 Mar 2013 10:33:38 -0800 Subject: [PATCH 19/56] Reset prepared statements after use, not before. --- src/cpp/ripple/HashedObject.cpp | 5 +++-- src/cpp/ripple/Ledger.cpp | 8 +++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/cpp/ripple/HashedObject.cpp b/src/cpp/ripple/HashedObject.cpp index 87481802ba..6dd34b6ef2 100644 --- a/src/cpp/ripple/HashedObject.cpp +++ b/src/cpp/ripple/HashedObject.cpp @@ -113,7 +113,6 @@ void HashedObjectStore::bulkWrite() default: type = "U"; } - pSt.reset(); pSt.bind(1, it->getHash().GetHex()); pSt.bind(2, type); pSt.bind(3, it->getIndex()); @@ -124,6 +123,7 @@ void HashedObjectStore::bulkWrite() cLog(lsFATAL) << "Error saving hashed object " << ret; assert(false); } + pSt.reset(); } db->executeSQL("END TRANSACTION;"); @@ -186,12 +186,12 @@ HashedObject::pointer HashedObjectStore::retrieve(const uint256& hash) static SqliteStatement pSt(theApp->getHashNodeDB()->getDB()->getSqliteDB(), "SELECT ObjType,LedgerIndex,Object FROM CommittedObjects WHERE Hash = ?;"); - pSt.reset(); pSt.bind(1, hash.GetHex()); int ret = pSt.step(); if (pSt.isDone(ret)) { + pSt.reset(); mNegativeCache.add(hash); cLog(lsTRACE) << "HOS: " << hash <<" fetch: not in db"; return obj; @@ -200,6 +200,7 @@ HashedObject::pointer HashedObjectStore::retrieve(const uint256& hash) type = pSt.peekString(0); index = pSt.getUInt32(1); pSt.getBlob(2).swap(data); + pSt.reset(); } #else diff --git a/src/cpp/ripple/Ledger.cpp b/src/cpp/ripple/Ledger.cpp index 5404264202..0ca0b7d93c 100644 --- a/src/cpp/ripple/Ledger.cpp +++ b/src/cpp/ripple/Ledger.cpp @@ -548,9 +548,9 @@ Ledger::pointer Ledger::loadByIndex(uint32 ledgerIndex) "ClosingTime,PrevClosingTime,CloseTimeRes,CloseFlags,LedgerSeq" " from Ledgers WHERE LedgerSeq = ?;"); - pSt.reset(); pSt.bind(1, ledgerIndex); ledger = getSQL1(&pSt); + pSt.reset(); } if (ledger) Ledger::getSQL2(ledger); @@ -569,9 +569,9 @@ Ledger::pointer Ledger::loadByHash(const uint256& ledgerHash) "ClosingTime,PrevClosingTime,CloseTimeRes,CloseFlags,LedgerSeq" " from Ledgers WHERE LedgerHash = ?;"); - pSt.reset(); pSt.bind(1, ledgerHash.GetHex()); ledger = getSQL1(&pSt); + pSt.reset(); } if (ledger) { @@ -731,17 +731,18 @@ bool Ledger::getHashesByIndex(uint32 ledgerIndex, uint256& ledgerHash, uint256& static SqliteStatement pSt(con->getDB()->getSqliteDB(), "SELECT LedgerHash,PrevHash FROM Ledgers INDEXED BY SeqLedger Where LedgerSeq = ?;"); - pSt.reset(); pSt.bind(1, ledgerIndex); int ret = pSt.step(); if (pSt.isDone(ret)) { + pSt.reset(); cLog(lsTRACE) << "Don't have ledger " << ledgerIndex; return false; } if (!pSt.isRow(ret)) { + pSt.reset(); assert(false); cLog(lsFATAL) << "Unexpected statement result " << ret; return false; @@ -749,6 +750,7 @@ bool Ledger::getHashesByIndex(uint32 ledgerIndex, uint256& ledgerHash, uint256& ledgerHash.SetHex(pSt.peekString(0), true); parentHash.SetHex(pSt.peekString(1), true); + pSt.reset(); return true; From 5089db15185c24f2fa45a189cce2608a699800ce Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Fri, 1 Mar 2013 13:17:14 -0800 Subject: [PATCH 20/56] Add some extra debug logging to track absence of CT consensus. --- src/cpp/ripple/LedgerConsensus.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cpp/ripple/LedgerConsensus.cpp b/src/cpp/ripple/LedgerConsensus.cpp index a2e56d5cbf..fb4345f390 100644 --- a/src/cpp/ripple/LedgerConsensus.cpp +++ b/src/cpp/ripple/LedgerConsensus.cpp @@ -790,10 +790,11 @@ void LedgerConsensus::updateOurPositions() thresh = ((thresh * neededWeight) + (neededWeight / 2)) / 100; if (thresh == 0) thresh = 1; + cLog(lsINFO) << "Proposers:" << mPeerPositions.size() << " nw:" << neededWeight << " thr:" << thresh; for (std::map::iterator it = closeTimes.begin(), end = closeTimes.end(); it != end; ++it) { - cLog(lsTRACE) << "CCTime: " << it->first << " has " << it->second << ", " << thresh << " required"; + cLog(lsDEBUG) << "CCTime: " << it->first << " has " << it->second << ", " << thresh << " required"; if (it->second >= thresh) { cLog(lsDEBUG) << "Close time consensus reached: " << it->first; From 92c1766f49871b2433d8ce93e18cfe86f722fd8a Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Fri, 1 Mar 2013 13:20:08 -0800 Subject: [PATCH 21/56] Ack! A delayed consensus causes the close time threshold to ratchet the wrong way. --- src/cpp/ripple/LedgerConsensus.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/cpp/ripple/LedgerConsensus.cpp b/src/cpp/ripple/LedgerConsensus.cpp index fb4345f390..53c6148150 100644 --- a/src/cpp/ripple/LedgerConsensus.cpp +++ b/src/cpp/ripple/LedgerConsensus.cpp @@ -766,10 +766,8 @@ void LedgerConsensus::updateOurPositions() neededWeight = AV_INIT_CONSENSUS_PCT; else if (mClosePercent < AV_LATE_CONSENSUS_TIME) neededWeight = AV_MID_CONSENSUS_PCT; - else if (mClosePercent < AV_STUCK_CONSENSUS_TIME) - neededWeight = AV_LATE_CONSENSUS_PCT; else - neededWeight = AV_STUCK_CONSENSUS_TIME; + neededWeight = AV_LATE_CONSENSUS_PCT; uint32 closeTime = 0; mHaveCloseTimeConsensus = false; From 301909fa01ada1e816378edd0d9195e0ffd6e1fd Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Fri, 1 Mar 2013 13:38:37 -0800 Subject: [PATCH 22/56] Fix framework for RPC book_offers. --- src/cpp/ripple/CallRPC.cpp | 25 +++++++++++++-------- src/cpp/ripple/NetworkOPs.cpp | 6 +++++ src/cpp/ripple/NetworkOPs.h | 7 +++++- src/cpp/ripple/RPCErr.cpp | 1 + src/cpp/ripple/RPCErr.h | 1 + src/cpp/ripple/RPCHandler.cpp | 41 ++++++++++++++++++++++------------- src/cpp/ripple/main.cpp | 2 +- 7 files changed, 57 insertions(+), 26 deletions(-) diff --git a/src/cpp/ripple/CallRPC.cpp b/src/cpp/ripple/CallRPC.cpp index b84d3ee5bd..9f67475f74 100644 --- a/src/cpp/ripple/CallRPC.cpp +++ b/src/cpp/ripple/CallRPC.cpp @@ -209,23 +209,25 @@ Json::Value RPCParser::parseAccountTransactions(const Json::Value& jvParams) return jvRequest; } -// book_offers [ ] +// book_offers [ [ [ []]]] +// limit: 0 = no limit +// proof: 0 or 1 // // Mnemonic: taker puts --> offer --> taker gets Json::Value RPCParser::parseBookOffers(const Json::Value& jvParams) { Json::Value jvRequest(Json::objectValue); - Json::Value jvTakerPuts = jvParseCurrencyIssuer(jvParams[0u].asString()); + Json::Value jvTakerPays = jvParseCurrencyIssuer(jvParams[0u].asString()); Json::Value jvTakerGets = jvParseCurrencyIssuer(jvParams[1u].asString()); - if (isRpcError(jvTakerPuts)) + if (isRpcError(jvTakerPays)) { - return jvTakerPuts; + return jvTakerPays; } else { - jvRequest["taker_puts"] = jvTakerPuts; + jvRequest["taker_pays"] = jvTakerPays; } if (isRpcError(jvTakerGets)) @@ -237,7 +239,7 @@ Json::Value RPCParser::parseBookOffers(const Json::Value& jvParams) jvRequest["taker_gets"] = jvTakerGets; } - if (!jvParseLedger(jvRequest, jvParams[2u].asString())) + if (jvParams.size() >= 3 && !jvParseLedger(jvRequest, jvParams[2u].asString())) return jvRequest; if (jvParams.size() >= 4) @@ -248,8 +250,13 @@ Json::Value RPCParser::parseBookOffers(const Json::Value& jvParams) jvRequest["limit"] = iLimit; } - if (jvParams.size() == 5) - jvRequest["marker"] = jvParams[4u]; + if (jvParams.size() >= 5 && jvParams[4u].asInt()) + { + jvRequest["proof"] = true; + } + + if (jvParams.size() == 6) + jvRequest["marker"] = jvParams[5u]; return jvRequest; } @@ -622,7 +629,7 @@ Json::Value RPCParser::parseCommand(std::string strMethod, Json::Value jvParams) { "account_lines", &RPCParser::parseAccountItems, 1, 2 }, { "account_offers", &RPCParser::parseAccountItems, 1, 2 }, { "account_tx", &RPCParser::parseAccountTransactions, 2, 4 }, - { "book_offers", &RPCParser::parseBookOffers, 3, 5 }, + { "book_offers", &RPCParser::parseBookOffers, 2, 6 }, { "connect", &RPCParser::parseConnect, 1, 2 }, { "consensus_info", &RPCParser::parseAsIs, 0, 0 }, { "get_counts", &RPCParser::parseGetCounts, 0, 1 }, diff --git a/src/cpp/ripple/NetworkOPs.cpp b/src/cpp/ripple/NetworkOPs.cpp index 593d9cb7cc..8d6f604808 100644 --- a/src/cpp/ripple/NetworkOPs.cpp +++ b/src/cpp/ripple/NetworkOPs.cpp @@ -1774,5 +1774,11 @@ InfoSub::pointer NetworkOPs::addRpcSub(const std::string& strUrl, InfoSub::ref r return rspEntry; } +void NetworkOPs::getBookPage(Ledger::pointer lpLedger, const uint160& uTakerPaysCurrencyID, const uint160& uTakerPaysIssuerID, const uint160& uTakerGetsCurrencyID, const uint160& uTakerGetsIssuerID, const bool bProof, const unsigned int iLimit, const Json::Value& jvMarker, Json::Value& jvResult) +{ + jvResult["offers"] = Json::Value(Json::arrayValue); +// jvResult["marker"] = Json::Value(Json::arrayValue); +// jvResult["nodes"] = Json::Value(Json::arrayValue); +} // vim:ts=4 diff --git a/src/cpp/ripple/NetworkOPs.h b/src/cpp/ripple/NetworkOPs.h index 1dd10c501b..1795b41ead 100644 --- a/src/cpp/ripple/NetworkOPs.h +++ b/src/cpp/ripple/NetworkOPs.h @@ -119,7 +119,6 @@ protected: boost::recursive_mutex mMonitorLock; subInfoMapType mSubAccount; subInfoMapType mSubRTAccount; - subRpcMapType mRpcSubMap; @@ -230,6 +229,12 @@ public: Json::Value getOwnerInfo(Ledger::pointer lpLedger, const RippleAddress& naAccount); + // + // Book functions + // + + void getBookPage(Ledger::pointer lpLedger, const uint160& uTakerPaysCurrencyID, const uint160& uTakerPaysIssuerID, const uint160& uTakerGetsCurrencyID, const uint160& uTakerGetsIssuerID, const bool bProof, const unsigned int iLimit, const Json::Value& jvMarker, Json::Value& jvResult); + // raw object operations bool findRawLedger(const uint256& ledgerHash, std::vector& rawLedger); bool findRawTransaction(const uint256& transactionHash, std::vector& rawTransaction); diff --git a/src/cpp/ripple/RPCErr.cpp b/src/cpp/ripple/RPCErr.cpp index 9d1f01259a..bf8c7103eb 100644 --- a/src/cpp/ripple/RPCErr.cpp +++ b/src/cpp/ripple/RPCErr.cpp @@ -19,6 +19,7 @@ Json::Value rpcError(int iError, Json::Value jvResult) { rpcACT_MALFORMED, "actMalformed", "Account malformed." }, { rpcACT_NOT_FOUND, "actNotFound", "Account not found." }, { rpcBAD_BLOB, "badBlob", "Blob must be a non-empty hex string." }, + { rpcBAD_MARKET, "badMarket", "No such market." }, { rpcBAD_SEED, "badSeed", "Disallowed seed." }, { rpcBAD_SYNTAX, "badSyntax", "Syntax error." }, { rpcCOMMAND_MISSING, "commandMissing", "Missing command entry." }, diff --git a/src/cpp/ripple/RPCErr.h b/src/cpp/ripple/RPCErr.h index 45407969e9..e34fef9228 100644 --- a/src/cpp/ripple/RPCErr.h +++ b/src/cpp/ripple/RPCErr.h @@ -45,6 +45,7 @@ enum { rpcACT_MALFORMED, rpcQUALITY_MALFORMED, rpcBAD_BLOB, + rpcBAD_MARKET, rpcBAD_SEED, rpcCOMMAND_MISSING, rpcDST_ACT_MALFORMED, diff --git a/src/cpp/ripple/RPCHandler.cpp b/src/cpp/ripple/RPCHandler.cpp index 6d6f388016..5189c1515c 100644 --- a/src/cpp/ripple/RPCHandler.cpp +++ b/src/cpp/ripple/RPCHandler.cpp @@ -176,7 +176,7 @@ Json::Value RPCHandler::transactionSign(Json::Value jvRequest, bool bSubmit) } Ledger::pointer lSnapshot = boost::make_shared( - boost::ref(*theApp->getOPs().getCurrentLedger()), false); + boost::ref(*mNetOps->getCurrentLedger()), false); { ScopedUnlock su(theApp->getMasterLock()); Pathfinder pf(lSnapshot, raSrcAddressID, dstAccountID, @@ -1060,8 +1060,7 @@ Json::Value RPCHandler::doBookOffers(Json::Value jvRequest) && (!jvTakerPays["issuer"].isString() || !STAmount::issuerFromString(uTakerPaysIssuerID, jvTakerPays["issuer"].asString()))) // Don't allow illegal issuers. - || !uTakerPaysCurrencyID - || !uTakerPaysIssuerID + || (!uTakerPaysCurrencyID != !uTakerPaysIssuerID) || ACCOUNT_ONE == uTakerPaysIssuerID) { cLog(lsINFO) << "Bad taker_pays issuer."; @@ -1071,7 +1070,7 @@ Json::Value RPCHandler::doBookOffers(Json::Value jvRequest) uint160 uTakerGetsCurrencyID; uint160 uTakerGetsIssuerID; - Json::Value jvTakerGets = jvRequest["taker_pays"]; + Json::Value jvTakerGets = jvRequest["taker_gets"]; // Parse mandatory currency. if (!jvTakerGets.isMember("currency") @@ -1086,8 +1085,7 @@ Json::Value RPCHandler::doBookOffers(Json::Value jvRequest) && (!jvTakerGets["issuer"].isString() || !STAmount::issuerFromString(uTakerGetsIssuerID, jvTakerGets["issuer"].asString()))) // Don't allow illegal issuers. - || !uTakerGetsCurrencyID - || !uTakerGetsIssuerID + || (!uTakerGetsCurrencyID != !uTakerGetsIssuerID) || ACCOUNT_ONE == uTakerGetsIssuerID) { cLog(lsINFO) << "Bad taker_gets issuer."; @@ -1095,7 +1093,18 @@ Json::Value RPCHandler::doBookOffers(Json::Value jvRequest) return rpcError(rpcDST_ISR_MALFORMED); } - jvResult["hello"] = "world"; + if (uTakerPaysCurrencyID == uTakerGetsCurrencyID + && uTakerPaysIssuerID == uTakerGetsIssuerID) { + cLog(lsINFO) << "taker_gets same as taker_pays."; + + return rpcError(rpcBAD_MARKET); + } + + const bool bProof = jvRequest.isMember("proof"); + const unsigned int iLimit = jvRequest.isMember("limit") ? jvRequest["limit"].asUInt() : 0; + const Json::Value jvMarker = jvRequest.isMember("marker") ? jvRequest["marker"] : Json::Value(Json::nullValue); + + mNetOps->getBookPage(lpLedger, uTakerPaysCurrencyID, uTakerPaysIssuerID, uTakerGetsCurrencyID, uTakerGetsIssuerID, bProof, iLimit, jvMarker, jvResult); return jvResult; } @@ -1433,7 +1442,7 @@ Json::Value RPCHandler::doConsensusInfo(Json::Value) { Json::Value ret(Json::objectValue); - ret["info"] = theApp->getOPs().getConsensusInfo(); + ret["info"] = mNetOps->getConsensusInfo(); return ret; } @@ -1442,7 +1451,7 @@ Json::Value RPCHandler::doServerInfo(Json::Value) { Json::Value ret(Json::objectValue); - ret["info"] = theApp->getOPs().getServerInfo(true, mRole == ADMIN); + ret["info"] = mNetOps->getServerInfo(true, mRole == ADMIN); return ret; } @@ -1451,7 +1460,7 @@ Json::Value RPCHandler::doServerState(Json::Value) { Json::Value ret(Json::objectValue); - ret["state"] = theApp->getOPs().getServerInfo(false, mRole == ADMIN); + ret["state"] = mNetOps->getServerInfo(false, mRole == ADMIN); return ret; } @@ -1520,7 +1529,7 @@ Json::Value RPCHandler::doTx(Json::Value jvRequest) if (txn->getLedger() != 0) { - Ledger::pointer lgr = theApp->getOPs().getLedgerBySeq(txn->getLedger()); + Ledger::pointer lgr = mNetOps->getLedgerBySeq(txn->getLedger()); if (lgr) { bool okay = false; @@ -1543,7 +1552,7 @@ Json::Value RPCHandler::doTx(Json::Value jvRequest) } } if (okay) - ret["validated"] = theApp->getOPs().isValidated(lgr); + ret["validated"] = mNetOps->isValidated(lgr); } } @@ -1661,7 +1670,7 @@ Json::Value RPCHandler::doAccountTransactions(Json::Value jvRequest) try { #endif - int vl = theApp->getOPs().getValidatedSeq(); + int vl = mNetOps->getValidatedSeq(); ScopedUnlock su(theApp->getMasterLock()); Json::Value ret(Json::objectValue); @@ -1680,7 +1689,7 @@ Json::Value RPCHandler::doAccountTransactions(Json::Value jvRequest) obj["inLedger"] = it->get<2>(); if (it->get<2>() > vl) obj["validated"] = false; - else if (theApp->getOPs().haveLedger(it->get<2>())) + else if (mNetOps->haveLedger(it->get<2>())) obj["validated"] = true; ret["transactions"].append(obj); } @@ -1701,7 +1710,7 @@ Json::Value RPCHandler::doAccountTransactions(Json::Value jvRequest) uint32 s = it->second->getLgrSeq(); if (s > vl) obj["validated"] = false; - else if (theApp->getOPs().haveLedger(s)) + else if (mNetOps->haveLedger(s)) obj["validated"] = true; } @@ -2955,6 +2964,8 @@ Json::Value RPCHandler::doCommand(const Json::Value& jvRequest, int iRole) && mNetOps->getOperatingMode() != NetworkOPs::omTRACKING && mNetOps->getOperatingMode() != NetworkOPs::omFULL) { + cLog(lsINFO) << "Insufficient network mode for RPC: " << mNetOps->strOperatingMode(); + return rpcError(rpcNO_NETWORK); } // XXX Should verify we have a current ledger. diff --git a/src/cpp/ripple/main.cpp b/src/cpp/ripple/main.cpp index 6be4fd138d..c3ec7a9a22 100644 --- a/src/cpp/ripple/main.cpp +++ b/src/cpp/ripple/main.cpp @@ -74,7 +74,7 @@ void printHelp(const po::options_description& desc) cerr << " account_lines || []" << endl; cerr << " account_offers || []" << endl; cerr << " account_tx || |( )" << endl; - cerr << " book_offers [ ]" << endl; + cerr << " book_offers [ [ [ []]]]" << endl; cerr << " connect []" << endl; cerr << " consensus_info" << endl; #if ENABLE_INSECURE From 4b97f422f39ad5d8385c2dc0ee31221e7d93863c Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Fri, 1 Mar 2013 13:46:13 -0800 Subject: [PATCH 23/56] Rework how we declare a close time consensus to prevent stalls. --- src/cpp/ripple/LedgerConsensus.cpp | 37 +++++++++++++++++++----------- src/cpp/ripple/LedgerTiming.h | 15 ++++++------ 2 files changed, 32 insertions(+), 20 deletions(-) diff --git a/src/cpp/ripple/LedgerConsensus.cpp b/src/cpp/ripple/LedgerConsensus.cpp index 53c6148150..b9816ede8f 100644 --- a/src/cpp/ripple/LedgerConsensus.cpp +++ b/src/cpp/ripple/LedgerConsensus.cpp @@ -762,47 +762,58 @@ void LedgerConsensus::updateOurPositions() int neededWeight; + if (mClosePercent < AV_MID_CONSENSUS_TIME) neededWeight = AV_INIT_CONSENSUS_PCT; else if (mClosePercent < AV_LATE_CONSENSUS_TIME) neededWeight = AV_MID_CONSENSUS_PCT; - else + else if (mClosePercent < AV_STUCK_CONSENSUS_TIME) neededWeight = AV_LATE_CONSENSUS_PCT; + else + neededWeight = AV_STUCK_CONSENSUS_PCT; uint32 closeTime = 0; mHaveCloseTimeConsensus = false; - int thresh = mPeerPositions.size(); - if (thresh == 0) + if (mPeerPositions.empty()) { // no other times mHaveCloseTimeConsensus = true; closeTime = roundCloseTime(mOurPosition->getCloseTime()); } else { + int threshVote = mPeerPositions.size(); // Threshold for non-zero vote + int threshConsensus = mPeerPositions.size(); // Threshold to declare consensus if (mProposing) { ++closeTimes[roundCloseTime(mOurPosition->getCloseTime())]; - ++thresh; + ++threshVote; + ++threshConsensus; } - thresh = ((thresh * neededWeight) + (neededWeight / 2)) / 100; - if (thresh == 0) - thresh = 1; - cLog(lsINFO) << "Proposers:" << mPeerPositions.size() << " nw:" << neededWeight << " thr:" << thresh; + threshVote = ((threshVote * neededWeight) + (neededWeight / 2)) / 100; + threshConsensus = ((threshConsensus * AV_CT_CONSENSUS_PCT) + (AV_CT_CONSENSUS_PCT / 2)) / 100; + + if (threshVote == 0) + threshVote = 1; + if (threshConsensus == 0) + threshConsensus = 1; + cLog(lsINFO) << "Proposers:" << mPeerPositions.size() << " nw:" << neededWeight + << " thrV:" << threshVote << " thrC:" << threshConsensus; for (std::map::iterator it = closeTimes.begin(), end = closeTimes.end(); it != end; ++it) { - cLog(lsDEBUG) << "CCTime: " << it->first << " has " << it->second << ", " << thresh << " required"; - if (it->second >= thresh) + cLog(lsDEBUG) << "CCTime: " << it->first << " has " << it->second << ", " << threshVote << " required"; + if (it->second >= threshVote) { cLog(lsDEBUG) << "Close time consensus reached: " << it->first; - mHaveCloseTimeConsensus = true; closeTime = it->first; - thresh = it->second; + threshVote = it->second; + if (threshVote >= threshConsensus) + mHaveCloseTimeConsensus = true; } } tLog(!mHaveCloseTimeConsensus, lsDEBUG) << "No CT consensus: Proposers:" << mPeerPositions.size() - << " Proposing:" << (mProposing ? "yes" : "no") << " Thresh:" << thresh << " Pos:" << closeTime; + << " Proposing:" << (mProposing ? "yes" : "no") << " Thresh:" << threshConsensus << " Pos:" << closeTime; } if (!changes && diff --git a/src/cpp/ripple/LedgerTiming.h b/src/cpp/ripple/LedgerTiming.h index db8fc8b942..c7539c081a 100644 --- a/src/cpp/ripple/LedgerTiming.h +++ b/src/cpp/ripple/LedgerTiming.h @@ -39,17 +39,18 @@ # define PROPOSE_INTERVAL 12 // Avalanche tuning -#define AV_INIT_CONSENSUS_PCT 50 // percentage of nodes on our UNL that must vote yes +# define AV_INIT_CONSENSUS_PCT 50 // percentage of nodes on our UNL that must vote yes -#define AV_MID_CONSENSUS_TIME 50 // percentage of previous close time before we advance -#define AV_MID_CONSENSUS_PCT 65 // percentage of nodes that most vote yes after advancing +# define AV_MID_CONSENSUS_TIME 50 // percentage of previous close time before we advance +# define AV_MID_CONSENSUS_PCT 65 // percentage of nodes that most vote yes after advancing -#define AV_LATE_CONSENSUS_TIME 85 // percentage of previous close time before we advance -#define AV_LATE_CONSENSUS_PCT 70 // percentage of nodes that most vote yes after advancing +# define AV_LATE_CONSENSUS_TIME 85 // percentage of previous close time before we advance +# define AV_LATE_CONSENSUS_PCT 70 // percentage of nodes that most vote yes after advancing -#define AV_STUCK_CONSENSUS_TIME 200 -#define AV_STUCK_CONSENSUS_PCT 95 +# define AV_STUCK_CONSENSUS_TIME 200 +# define AV_STUCK_CONSENSUS_PCT 95 +# define AV_CT_CONSENSUS_PCT 75 class ContinuousLedgerTiming { From d31fa25b95d3efff3fda4d3a4c3bc70491d31069 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Fri, 1 Mar 2013 17:01:44 -0800 Subject: [PATCH 24/56] Fix a rare race condition involving subscribing and unsubscribing. --- src/cpp/ripple/NetworkOPs.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/cpp/ripple/NetworkOPs.cpp b/src/cpp/ripple/NetworkOPs.cpp index 8d6f604808..499c204e52 100644 --- a/src/cpp/ripple/NetworkOPs.cpp +++ b/src/cpp/ripple/NetworkOPs.cpp @@ -1696,12 +1696,14 @@ bool NetworkOPs::subLedger(InfoSub::ref isrListener, Json::Value& jvResult) jvResult["reserve_base"] = Json::UInt(lpClosed->getReserve(0)); jvResult["reserve_inc"] = Json::UInt(lpClosed->getReserveInc()); + boost::recursive_mutex::scoped_lock sl(mMonitorLock); return mSubLedger.insert(std::make_pair(isrListener->getSeq(), isrListener)).second; } // <-- bool: true=erased, false=was not there bool NetworkOPs::unsubLedger(uint64 uSeq) { + boost::recursive_mutex::scoped_lock sl(mMonitorLock); return !!mSubLedger.erase(uSeq); } @@ -1722,36 +1724,42 @@ bool NetworkOPs::subServer(InfoSub::ref isrListener, Json::Value& jvResult) jvResult["load_base"] = theApp->getFeeTrack().getLoadBase(); jvResult["load_factor"] = theApp->getFeeTrack().getLoadFactor(); + boost::recursive_mutex::scoped_lock sl(mMonitorLock); return mSubServer.insert(std::make_pair(isrListener->getSeq(), isrListener)).second; } // <-- bool: true=erased, false=was not there bool NetworkOPs::unsubServer(uint64 uSeq) { + boost::recursive_mutex::scoped_lock sl(mMonitorLock); return !!mSubServer.erase(uSeq); } // <-- bool: true=added, false=already there bool NetworkOPs::subTransactions(InfoSub::ref isrListener) { + boost::recursive_mutex::scoped_lock sl(mMonitorLock); return mSubTransactions.insert(std::make_pair(isrListener->getSeq(), isrListener)).second; } // <-- bool: true=erased, false=was not there bool NetworkOPs::unsubTransactions(uint64 uSeq) { + boost::recursive_mutex::scoped_lock sl(mMonitorLock); return !!mSubTransactions.erase(uSeq); } // <-- bool: true=added, false=already there bool NetworkOPs::subRTTransactions(InfoSub::ref isrListener) { + boost::recursive_mutex::scoped_lock sl(mMonitorLock); return mSubTransactions.insert(std::make_pair(isrListener->getSeq(), isrListener)).second; } // <-- bool: true=erased, false=was not there bool NetworkOPs::unsubRTTransactions(uint64 uSeq) { + boost::recursive_mutex::scoped_lock sl(mMonitorLock); return !!mSubTransactions.erase(uSeq); } From c984f83eb67b011de0d182ca93514a8119dcd4f9 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Fri, 1 Mar 2013 18:27:07 -0800 Subject: [PATCH 25/56] Fix for job destruction race condition. --- src/cpp/ripple/JobQueue.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/cpp/ripple/JobQueue.cpp b/src/cpp/ripple/JobQueue.cpp index cc2aadb40b..3dc9386d0e 100644 --- a/src/cpp/ripple/JobQueue.cpp +++ b/src/cpp/ripple/JobQueue.cpp @@ -256,16 +256,18 @@ void JobQueue::threadEntry() break; std::set::iterator it = mJobSet.begin(); - Job job(*it); - mJobSet.erase(it); - --mJobCounts[job.getType()]; + { + Job job(*it); + mJobSet.erase(it); + --mJobCounts[job.getType()]; - if (job.getType() == jtDEATH) - break; + if (job.getType() == jtDEATH) + break; - sl.unlock(); - cLog(lsTRACE) << "Doing " << Job::toString(job.getType()) << " job"; - job.doJob(); + sl.unlock(); + cLog(lsTRACE) << "Doing " << Job::toString(job.getType()) << " job"; + job.doJob(); + } // must destroy job without holding lock sl.lock(); } --mThreadCount; From 2699f20f3f3772f73e5b1eac64dfc14065c79eae Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Fri, 1 Mar 2013 20:30:25 -0800 Subject: [PATCH 26/56] First cut at RPC book_offers. --- src/cpp/ripple/NetworkOPs.cpp | 66 ++++++++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/src/cpp/ripple/NetworkOPs.cpp b/src/cpp/ripple/NetworkOPs.cpp index 8d6f604808..728d18d278 100644 --- a/src/cpp/ripple/NetworkOPs.cpp +++ b/src/cpp/ripple/NetworkOPs.cpp @@ -1776,7 +1776,71 @@ InfoSub::pointer NetworkOPs::addRpcSub(const std::string& strUrl, InfoSub::ref r void NetworkOPs::getBookPage(Ledger::pointer lpLedger, const uint160& uTakerPaysCurrencyID, const uint160& uTakerPaysIssuerID, const uint160& uTakerGetsCurrencyID, const uint160& uTakerGetsIssuerID, const bool bProof, const unsigned int iLimit, const Json::Value& jvMarker, Json::Value& jvResult) { - jvResult["offers"] = Json::Value(Json::arrayValue); + Json::Value jvOffers = Json::Value(Json::arrayValue); + const uint256 uBookBase = Ledger::getBookBase(uTakerGetsCurrencyID, uTakerGetsIssuerID, uTakerPaysCurrencyID, uTakerPaysIssuerID); + const uint256 uBookEnd = Ledger::getQualityNext(uBookBase); + uint256 uTipIndex = uBookBase; + + cLog(lsTRACE) << boost::str(boost::format("getBookPage: uBookBase=%s") % uBookBase); + cLog(lsTRACE) << boost::str(boost::format("getBookPage: uBookEnd=%s") % uBookEnd); + cLog(lsTRACE) << boost::str(boost::format("getBookPage: uTipIndex=%s") % uTipIndex); + + LedgerEntrySet lesActive(lpLedger); + + bool bDone = false; + bool bDirectAdvance = true; + + SLE::pointer sleOfferDir; + uint256 uOfferIndex; + unsigned int uBookEntry; + + while (!bDone) { + if (bDirectAdvance) { + bDirectAdvance = false; + + cLog(lsTRACE) << boost::str(boost::format("getBookPage: bDirectAdvance")); + + sleOfferDir = lesActive.entryCache(ltDIR_NODE, lpLedger->getNextLedgerIndex(uTipIndex, uBookEnd)); + if (!sleOfferDir) + { + cLog(lsTRACE) << boost::str(boost::format("getBookPage: bDone")); + bDone = true; + } + else + { + uTipIndex = sleOfferDir->getIndex(); + + SLE::pointer sleBookNode; + + lesActive.dirFirst(uTipIndex, sleBookNode, uBookEntry, uOfferIndex); + + cLog(lsTRACE) << boost::str(boost::format("getBookPage: uTipIndex=%s") % uTipIndex); + cLog(lsTRACE) << boost::str(boost::format("getBookPage: uOfferIndex=%s") % uOfferIndex); + } + } + + if (!bDone) + { + SLE::pointer sleOffer = lesActive.entryCache(ltOFFER, uOfferIndex); + const uint160 uOfferOwnerID = sleOffer->getFieldAccount(sfAccount).getAccountID(); + STAmount saOfferPays = sleOffer->getFieldAmount(sfTakerGets); + STAmount saOfferGets = sleOffer->getFieldAmount(sfTakerPays); + STAmount saOfferFunds = lesActive.accountFunds(uOfferOwnerID, saOfferPays); + + if (!lesActive.dirNext(uTipIndex, sleOfferDir, uBookEntry, uOfferIndex)) + { + bDirectAdvance = true; + } + else + { + cLog(lsTRACE) << boost::str(boost::format("getBookPage: uOfferIndex=%s") % uOfferIndex); + + jvOffers.append(sleOffer->getJson(0)); + } + } + } + + jvResult["offers"] = jvOffers; // jvResult["marker"] = Json::Value(Json::arrayValue); // jvResult["nodes"] = Json::Value(Json::arrayValue); } From dcdb42589f4883b105040d2aabdfb231fd9426ed Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Fri, 1 Mar 2013 21:46:18 -0800 Subject: [PATCH 27/56] Don't barf on a transaction over 12KB. --- src/cpp/ripple/Serializer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cpp/ripple/Serializer.cpp b/src/cpp/ripple/Serializer.cpp index 4c602eb043..7cf33a6fe2 100644 --- a/src/cpp/ripple/Serializer.cpp +++ b/src/cpp/ripple/Serializer.cpp @@ -476,8 +476,8 @@ int Serializer::encodeLengthLength(int length) if (length < 0) throw std::overflow_error("len<0"); if (length <= 192) return 1; if (length <= 12480) return 2; - if (length >= 918744) return 3; - throw std::overflow_error("len>918644"); + if (length <= 918744) return 3; + throw std::overflow_error("len>918744"); } int Serializer::decodeLengthLength(int b1) From f340a636cbbfb2b440f3adaf55998ccaf85a62ee Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Sat, 2 Mar 2013 01:06:13 -0800 Subject: [PATCH 28/56] Increment ripple-lib version. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f8cee61ad4..bf860e7370 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ripple-lib", - "version": "0.7.0", + "version": "0.7.1", "description": "Ripple JavaScript client library", "files": [ From c6b2b5b819bcf6d8bee32882d682b140dcfc5187 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sat, 2 Mar 2013 22:16:39 -0800 Subject: [PATCH 29/56] Add a get function. --- src/cpp/ripple/InstanceCounter.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/cpp/ripple/InstanceCounter.h b/src/cpp/ripple/InstanceCounter.h index 48f82d0332..95301cbbfa 100644 --- a/src/cpp/ripple/InstanceCounter.h +++ b/src/cpp/ripple/InstanceCounter.h @@ -50,6 +50,11 @@ public: sMultiThreaded = true; } + static bool isMultiThread() + { + return sMultiThreaded; + } + void addInstance() { if (sMultiThreaded) From 402f296c36349be5a75befc2526184dc23a13b22 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sat, 2 Mar 2013 22:16:52 -0800 Subject: [PATCH 30/56] A more sensible implementaiton SOTemplate::getIndex. --- src/cpp/ripple/FieldNames.cpp | 1 + src/cpp/ripple/FieldNames.h | 12 +++++++++++- src/cpp/ripple/SerializedObject.cpp | 11 +++++++---- src/cpp/ripple/SerializedObject.h | 2 +- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/cpp/ripple/FieldNames.cpp b/src/cpp/ripple/FieldNames.cpp index 6bd34da299..18271e48c7 100644 --- a/src/cpp/ripple/FieldNames.cpp +++ b/src/cpp/ripple/FieldNames.cpp @@ -12,6 +12,7 @@ // These must stay at the top of this file std::map SField::codeToField; boost::mutex SField::mapMutex; +int SField::num = 0; SField sfInvalid(-1), sfGeneric(0); SField sfLedgerEntry(STI_LEDGERENTRY, 1, "LedgerEntry"); diff --git a/src/cpp/ripple/FieldNames.h b/src/cpp/ripple/FieldNames.h index f199c1f95c..b2ae08aa72 100644 --- a/src/cpp/ripple/FieldNames.h +++ b/src/cpp/ripple/FieldNames.h @@ -51,6 +51,7 @@ public: protected: static std::map codeToField; static boost::mutex mapMutex; + static int num; SField(SerializedTypeID id, int val); @@ -61,6 +62,7 @@ public: const int fieldValue; // Code number for protocol std::string fieldName; int fieldMeta; + int fieldNum; bool signingField; SField(int fc, SerializedTypeID tid, int fv, const char* fn) : @@ -68,6 +70,7 @@ public: { boost::mutex::scoped_lock sl(mapMutex); codeToField[fieldCode] = this; + fieldNum = ++num; } SField(SerializedTypeID tid, int fv, const char *fn) : @@ -76,9 +79,14 @@ public: { boost::mutex::scoped_lock sl(mapMutex); codeToField[fieldCode] = this; + fieldNum = ++num; } - SField(int fc) : fieldCode(fc), fieldType(STI_UNKNOWN), fieldValue(0), fieldMeta(sMD_Never) { ; } + SField(int fc) : fieldCode(fc), fieldType(STI_UNKNOWN), fieldValue(0), fieldMeta(sMD_Never) + { + boost::mutex::scoped_lock sl(mapMutex); + fieldNum = ++num; + } ~SField(); @@ -97,6 +105,8 @@ public: bool isBinary() const { return fieldValue < 256; } bool isDiscardable() const { return fieldValue > 256; } int getCode() const { return fieldCode; } + int getNum() const { return fieldNum; } + static int getNumFields() { return num; } bool isSigningField() const { return signingField; } void notSigningField() { signingField = false; } diff --git a/src/cpp/ripple/SerializedObject.cpp b/src/cpp/ripple/SerializedObject.cpp index 2ab80d4147..75dbfb108c 100644 --- a/src/cpp/ripple/SerializedObject.cpp +++ b/src/cpp/ripple/SerializedObject.cpp @@ -132,15 +132,18 @@ std::auto_ptr STObject::makeDeserializedObject(SerializedTypeID void SOTemplate::push_back(const SOElement &r) { - assert(mMap.find(r.e_field.getCode()) == mMap.end()); - mMap[r.e_field.getCode()] = mTypes.size(); + if (mIndex.empty()) + mIndex.resize(SField::getNumFields() + 1, -1); + assert(r.e_field.getNum() < mIndex.size()); + assert(getIndex(r.e_field) == -1); + mIndex[r.e_field.getNum()] = mTypes.size(); mTypes.push_back(new SOElement(r)); } int SOTemplate::getIndex(SField::ref f) const { - std::map::const_iterator it = mMap.find(f.getCode()); - return (it == mMap.end()) ? -1 : it->second; + assert(f.getNum() < mIndex.size()); + return mIndex[f.getNum()]; } void STObject::set(const SOTemplate& type) diff --git a/src/cpp/ripple/SerializedObject.h b/src/cpp/ripple/SerializedObject.h index bd5e5b2c16..8638d4e9e8 100644 --- a/src/cpp/ripple/SerializedObject.h +++ b/src/cpp/ripple/SerializedObject.h @@ -28,7 +28,7 @@ class SOTemplate { protected: std::vector mTypes; - std::map mMap; // field code -> index + std::vector mIndex; // field num -> index public: SOTemplate() { ; } From 768007cee6c4936a906abb9d32168a27cc8d001c Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Sat, 2 Mar 2013 23:55:45 -0800 Subject: [PATCH 31/56] Make RPC book_offers usuable. - Missing: transfer fees - Adds taker support - Adds taker_gets_funded and taker_pays_funded - Adds don't return unfunded offers - Adds follow offer owner balances. --- src/cpp/ripple/CallRPC.cpp | 23 ++++++----- src/cpp/ripple/NetworkOPs.cpp | 76 +++++++++++++++++++++++++++++++---- src/cpp/ripple/NetworkOPs.h | 2 +- src/cpp/ripple/RPCErr.cpp | 3 +- src/cpp/ripple/RPCErr.h | 3 +- src/cpp/ripple/RPCHandler.cpp | 18 +++++++-- src/cpp/ripple/main.cpp | 2 +- 7 files changed, 103 insertions(+), 24 deletions(-) diff --git a/src/cpp/ripple/CallRPC.cpp b/src/cpp/ripple/CallRPC.cpp index 9f67475f74..e4a71d56fe 100644 --- a/src/cpp/ripple/CallRPC.cpp +++ b/src/cpp/ripple/CallRPC.cpp @@ -209,11 +209,11 @@ Json::Value RPCParser::parseAccountTransactions(const Json::Value& jvParams) return jvRequest; } -// book_offers [ [ [ []]]] +// book_offers [ [ [ [ []]]]] // limit: 0 = no limit // proof: 0 or 1 // -// Mnemonic: taker puts --> offer --> taker gets +// Mnemonic: taker pays --> offer --> taker gets Json::Value RPCParser::parseBookOffers(const Json::Value& jvParams) { Json::Value jvRequest(Json::objectValue); @@ -239,24 +239,29 @@ Json::Value RPCParser::parseBookOffers(const Json::Value& jvParams) jvRequest["taker_gets"] = jvTakerGets; } - if (jvParams.size() >= 3 && !jvParseLedger(jvRequest, jvParams[2u].asString())) + if (jvParams.size() >= 3) + { + jvRequest["issuer"] = jvParams[2u].asString(); + } + + if (jvParams.size() >= 4 && !jvParseLedger(jvRequest, jvParams[3u].asString())) return jvRequest; - if (jvParams.size() >= 4) + if (jvParams.size() >= 5) { - int iLimit = jvParams[3u].asInt(); + int iLimit = jvParams[5u].asInt(); if (iLimit > 0) jvRequest["limit"] = iLimit; } - if (jvParams.size() >= 5 && jvParams[4u].asInt()) + if (jvParams.size() >= 6 && jvParams[5u].asInt()) { jvRequest["proof"] = true; } - if (jvParams.size() == 6) - jvRequest["marker"] = jvParams[5u]; + if (jvParams.size() == 7) + jvRequest["marker"] = jvParams[6u]; return jvRequest; } @@ -629,7 +634,7 @@ Json::Value RPCParser::parseCommand(std::string strMethod, Json::Value jvParams) { "account_lines", &RPCParser::parseAccountItems, 1, 2 }, { "account_offers", &RPCParser::parseAccountItems, 1, 2 }, { "account_tx", &RPCParser::parseAccountTransactions, 2, 4 }, - { "book_offers", &RPCParser::parseBookOffers, 2, 6 }, + { "book_offers", &RPCParser::parseBookOffers, 2, 7 }, { "connect", &RPCParser::parseConnect, 1, 2 }, { "consensus_info", &RPCParser::parseAsIs, 0, 0 }, { "get_counts", &RPCParser::parseGetCounts, 0, 1 }, diff --git a/src/cpp/ripple/NetworkOPs.cpp b/src/cpp/ripple/NetworkOPs.cpp index dcd526c842..61b2ded308 100644 --- a/src/cpp/ripple/NetworkOPs.cpp +++ b/src/cpp/ripple/NetworkOPs.cpp @@ -1782,13 +1782,17 @@ InfoSub::pointer NetworkOPs::addRpcSub(const std::string& strUrl, InfoSub::ref r return rspEntry; } -void NetworkOPs::getBookPage(Ledger::pointer lpLedger, const uint160& uTakerPaysCurrencyID, const uint160& uTakerPaysIssuerID, const uint160& uTakerGetsCurrencyID, const uint160& uTakerGetsIssuerID, const bool bProof, const unsigned int iLimit, const Json::Value& jvMarker, Json::Value& jvResult) +// FIXME : support iLimit. +void NetworkOPs::getBookPage(Ledger::pointer lpLedger, const uint160& uTakerPaysCurrencyID, const uint160& uTakerPaysIssuerID, const uint160& uTakerGetsCurrencyID, const uint160& uTakerGetsIssuerID, const uint160& uTakerID, const bool bProof, const unsigned int iLimit, const Json::Value& jvMarker, Json::Value& jvResult) { + boost::unordered_map umBalance; Json::Value jvOffers = Json::Value(Json::arrayValue); - const uint256 uBookBase = Ledger::getBookBase(uTakerGetsCurrencyID, uTakerGetsIssuerID, uTakerPaysCurrencyID, uTakerPaysIssuerID); + const uint256 uBookBase = Ledger::getBookBase(uTakerPaysCurrencyID, uTakerPaysIssuerID, uTakerGetsCurrencyID, uTakerGetsIssuerID); const uint256 uBookEnd = Ledger::getQualityNext(uBookBase); uint256 uTipIndex = uBookBase; + cLog(lsTRACE) << boost::str(boost::format("getBookPage: uTakerPaysCurrencyID=%s uTakerPaysIssuerID=%s") % STAmount::createHumanCurrency(uTakerPaysCurrencyID) % RippleAddress::createHumanAccountID(uTakerPaysIssuerID)); + cLog(lsTRACE) << boost::str(boost::format("getBookPage: uTakerGetsCurrencyID=%s uTakerGetsIssuerID=%s") % STAmount::createHumanCurrency(uTakerGetsCurrencyID) % RippleAddress::createHumanAccountID(uTakerGetsIssuerID)); cLog(lsTRACE) << boost::str(boost::format("getBookPage: uBookBase=%s") % uBookBase); cLog(lsTRACE) << boost::str(boost::format("getBookPage: uBookEnd=%s") % uBookEnd); cLog(lsTRACE) << boost::str(boost::format("getBookPage: uTipIndex=%s") % uTipIndex); @@ -1801,6 +1805,9 @@ void NetworkOPs::getBookPage(Ledger::pointer lpLedger, const uint160& uTakerPays SLE::pointer sleOfferDir; uint256 uOfferIndex; unsigned int uBookEntry; + STAmount saDirRate; + +// unsigned int iLeft = iLimit; while (!bDone) { if (bDirectAdvance) { @@ -1817,7 +1824,7 @@ void NetworkOPs::getBookPage(Ledger::pointer lpLedger, const uint160& uTakerPays else { uTipIndex = sleOfferDir->getIndex(); - + saDirRate = STAmount::setRate(Ledger::getQuality(uTipIndex)); SLE::pointer sleBookNode; lesActive.dirFirst(uTipIndex, sleBookNode, uBookEntry, uOfferIndex); @@ -1831,9 +1838,64 @@ void NetworkOPs::getBookPage(Ledger::pointer lpLedger, const uint160& uTakerPays { SLE::pointer sleOffer = lesActive.entryCache(ltOFFER, uOfferIndex); const uint160 uOfferOwnerID = sleOffer->getFieldAccount(sfAccount).getAccountID(); - STAmount saOfferPays = sleOffer->getFieldAmount(sfTakerGets); - STAmount saOfferGets = sleOffer->getFieldAmount(sfTakerPays); - STAmount saOfferFunds = lesActive.accountFunds(uOfferOwnerID, saOfferPays); + STAmount saOwnerFunds; + + boost::unordered_map::const_iterator umBalanceEntry = umBalance.find(uOfferOwnerID); + + if (umBalanceEntry == umBalance.end()) + { + // Did not find balance in table. + STAmount saDefault(uTakerGetsCurrencyID, uTakerGetsIssuerID); + // cLog(lsINFO) << boost::str(boost::format("getBookPage: saDefault=%s") % saDefault.getFullText()); + + saOwnerFunds = lesActive.accountFunds(uOfferOwnerID, saDefault); + // cLog(lsINFO) << boost::str(boost::format("getBookPage: saOwnerFunds=%s (new)") % saOwnerFunds.getFullText()); + } + else + { + saOwnerFunds = umBalanceEntry->second; + // cLog(lsINFO) << boost::str(boost::format("getBookPage: saOwnerFunds=%s (cached)") % saOwnerFunds.getFullText()); + } + + STAmount saTakerGets = sleOffer->getFieldAmount(sfTakerGets); + STAmount saTakerPays = sleOffer->getFieldAmount(sfTakerPays); + + Json::Value jvOffer = sleOffer->getJson(0); + + STAmount saTakerGetsFunded; + + if (saOwnerFunds >= saTakerGets) + { + // Sufficient funds no shenanigans. + saTakerGetsFunded = saTakerGets; + } + else + { + // cLog(lsINFO) << boost::str(boost::format("getBookPage: saTakerGets=%s") % saTakerGets.getFullText()); + // cLog(lsINFO) << boost::str(boost::format("getBookPage: saTakerPays=%s") % saTakerPays.getFullText()); + // cLog(lsINFO) << boost::str(boost::format("getBookPage: saOwnerFunds=%s") % saOwnerFunds.getFullText()); + // cLog(lsINFO) << boost::str(boost::format("getBookPage: saDirRate=%s") % saDirRate.getText()); + // cLog(lsINFO) << boost::str(boost::format("getBookPage: multiply=%s") % STAmount::multiply(saTakerGetsFunded, saDirRate).getFullText()); + // cLog(lsINFO) << boost::str(boost::format("getBookPage: multiply=%s") % STAmount::multiply(saTakerGetsFunded, saDirRate, saTakerPays).getFullText()); + STAmount saTakerPaysFunded; + + saTakerGetsFunded = saOwnerFunds; + saTakerPaysFunded = std::min(saTakerPays, STAmount::multiply(saTakerGetsFunded, saDirRate, saTakerPays)); + + // Only provide, if not fully funded. + jvOffer["taker_gets_funded"] = saTakerGetsFunded.getJson(0); + jvOffer["taker_pays_funded"] = saTakerPaysFunded.getJson(0); + } + + STAmount saOwnerBalance = saOwnerFunds-saTakerGetsFunded; + + umBalance[uOfferOwnerID] = saOwnerBalance; + + if (!saOwnerFunds.isZero() || uOfferOwnerID == uTakerID) + { + // Only provide funded offers and offers of the taker. + jvOffers.append(jvOffer); + } if (!lesActive.dirNext(uTipIndex, sleOfferDir, uBookEntry, uOfferIndex)) { @@ -1842,8 +1904,6 @@ void NetworkOPs::getBookPage(Ledger::pointer lpLedger, const uint160& uTakerPays else { cLog(lsTRACE) << boost::str(boost::format("getBookPage: uOfferIndex=%s") % uOfferIndex); - - jvOffers.append(sleOffer->getJson(0)); } } } diff --git a/src/cpp/ripple/NetworkOPs.h b/src/cpp/ripple/NetworkOPs.h index 1795b41ead..188543d7c6 100644 --- a/src/cpp/ripple/NetworkOPs.h +++ b/src/cpp/ripple/NetworkOPs.h @@ -233,7 +233,7 @@ public: // Book functions // - void getBookPage(Ledger::pointer lpLedger, const uint160& uTakerPaysCurrencyID, const uint160& uTakerPaysIssuerID, const uint160& uTakerGetsCurrencyID, const uint160& uTakerGetsIssuerID, const bool bProof, const unsigned int iLimit, const Json::Value& jvMarker, Json::Value& jvResult); + void getBookPage(Ledger::pointer lpLedger, const uint160& uTakerPaysCurrencyID, const uint160& uTakerPaysIssuerID, const uint160& uTakerGetsCurrencyID, const uint160& uTakerGetsIssuerID, const uint160& uTakerID, const bool bProof, const unsigned int iLimit, const Json::Value& jvMarker, Json::Value& jvResult); // raw object operations bool findRawLedger(const uint256& ledgerHash, std::vector& rawLedger); diff --git a/src/cpp/ripple/RPCErr.cpp b/src/cpp/ripple/RPCErr.cpp index bf8c7103eb..d7307cb776 100644 --- a/src/cpp/ripple/RPCErr.cpp +++ b/src/cpp/ripple/RPCErr.cpp @@ -19,7 +19,9 @@ Json::Value rpcError(int iError, Json::Value jvResult) { rpcACT_MALFORMED, "actMalformed", "Account malformed." }, { rpcACT_NOT_FOUND, "actNotFound", "Account not found." }, { rpcBAD_BLOB, "badBlob", "Blob must be a non-empty hex string." }, + { rpcBAD_ISSUER, "badIssuer", "Issuer account malformed." }, { rpcBAD_MARKET, "badMarket", "No such market." }, + { rpcBAD_SECRET, "badSecret", "Secret does not match account." }, { rpcBAD_SEED, "badSeed", "Disallowed seed." }, { rpcBAD_SYNTAX, "badSyntax", "Syntax error." }, { rpcCOMMAND_MISSING, "commandMissing", "Missing command entry." }, @@ -62,7 +64,6 @@ Json::Value rpcError(int iError, Json::Value jvResult) { rpcSRC_ACT_MALFORMED, "srcActMalformed", "Source account is malformed." }, { rpcSRC_ACT_MISSING, "srcActMissing", "Source account not provided." }, { rpcSRC_ACT_NOT_FOUND, "srcActNotFound", "Source account not found." }, - { rpcBAD_SECRET, "badSecret", "Secret does not match account." }, { rpcSRC_AMT_MALFORMED, "srcAmtMalformed", "Source amount/currency/issuer is malformed." }, { rpcSRC_CUR_MALFORMED, "srcCurMalformed", "Source currency is malformed." }, { rpcSRC_ISR_MALFORMED, "srcIsrMalformed", "Source issuer is malformed." }, diff --git a/src/cpp/ripple/RPCErr.h b/src/cpp/ripple/RPCErr.h index e34fef9228..ef27251cb9 100644 --- a/src/cpp/ripple/RPCErr.h +++ b/src/cpp/ripple/RPCErr.h @@ -45,7 +45,9 @@ enum { rpcACT_MALFORMED, rpcQUALITY_MALFORMED, rpcBAD_BLOB, + rpcBAD_ISSUER, rpcBAD_MARKET, + rpcBAD_SECRET, rpcBAD_SEED, rpcCOMMAND_MISSING, rpcDST_ACT_MALFORMED, @@ -69,7 +71,6 @@ enum { rpcSRC_AMT_MALFORMED, rpcSRC_CUR_MALFORMED, rpcSRC_ISR_MALFORMED, - rpcBAD_SECRET, // Internal error (should never happen) rpcINTERNAL, // Generic internal error. diff --git a/src/cpp/ripple/RPCHandler.cpp b/src/cpp/ripple/RPCHandler.cpp index 5189c1515c..ff93bfd0e9 100644 --- a/src/cpp/ripple/RPCHandler.cpp +++ b/src/cpp/ripple/RPCHandler.cpp @@ -1022,10 +1022,11 @@ Json::Value RPCHandler::doAccountOffers(Json::Value jvRequest) } // { -// "ledger_hash" : ledger, // Optional -// "ledger_index" : ledger_index, // Optional +// "ledger_hash" : ledger, // Optional. +// "ledger_index" : ledger_index, // Optional. // "taker_gets" : { "currency": currency, "issuer" : address }, // "taker_pays" : { "currency": currency, "issuer" : address }, +// "taker" : address, // Optional. // "marker" : element, // Optional. // "limit" : integer, // Optional. // "proof" : boolean // Defaults to false. @@ -1100,11 +1101,22 @@ Json::Value RPCHandler::doBookOffers(Json::Value jvRequest) return rpcError(rpcBAD_MARKET); } + RippleAddress raTakerID; + + if (!jvRequest.isMember("taker")) + { + raTakerID.setAccountID(ACCOUNT_ONE); + } + else if (!raTakerID.setAccountID(jvRequest["taker"].asString())) + { + return rpcError(rpcBAD_ISSUER); + } + const bool bProof = jvRequest.isMember("proof"); const unsigned int iLimit = jvRequest.isMember("limit") ? jvRequest["limit"].asUInt() : 0; const Json::Value jvMarker = jvRequest.isMember("marker") ? jvRequest["marker"] : Json::Value(Json::nullValue); - mNetOps->getBookPage(lpLedger, uTakerPaysCurrencyID, uTakerPaysIssuerID, uTakerGetsCurrencyID, uTakerGetsIssuerID, bProof, iLimit, jvMarker, jvResult); + mNetOps->getBookPage(lpLedger, uTakerPaysCurrencyID, uTakerPaysIssuerID, uTakerGetsCurrencyID, uTakerGetsIssuerID, raTakerID.getAccountID(), bProof, iLimit, jvMarker, jvResult); return jvResult; } diff --git a/src/cpp/ripple/main.cpp b/src/cpp/ripple/main.cpp index c3ec7a9a22..15783f8afb 100644 --- a/src/cpp/ripple/main.cpp +++ b/src/cpp/ripple/main.cpp @@ -74,7 +74,7 @@ void printHelp(const po::options_description& desc) cerr << " account_lines || []" << endl; cerr << " account_offers || []" << endl; cerr << " account_tx || |( )" << endl; - cerr << " book_offers [ [ [ []]]]" << endl; + cerr << " book_offers [ [ [ []]]]]" << endl; cerr << " connect []" << endl; cerr << " consensus_info" << endl; #if ENABLE_INSECURE From 51a85504a8afcdccf67f638e0fcfd60c935f08e8 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sun, 3 Mar 2013 02:09:19 -0800 Subject: [PATCH 32/56] Startup and shutdown improvements. Performance improvements. --- src/cpp/ripple/Application.cpp | 3 ++- src/cpp/ripple/Application.h | 3 +++ src/cpp/ripple/Ledger.cpp | 39 +++++++++++++++++++++++++++++++++ src/cpp/ripple/Ledger.h | 1 + src/cpp/ripple/LedgerMaster.cpp | 18 ++++++++++++--- src/cpp/ripple/SHAMap.cpp | 8 +++++-- src/cpp/ripple/SHAMap.h | 8 +++---- 7 files changed, 70 insertions(+), 10 deletions(-) diff --git a/src/cpp/ripple/Application.cpp b/src/cpp/ripple/Application.cpp index 3081fb3743..fac0ce76a6 100644 --- a/src/cpp/ripple/Application.cpp +++ b/src/cpp/ripple/Application.cpp @@ -45,7 +45,7 @@ Application::Application() : mSNTPClient(mAuxService), mRPCHandler(&mNetOps), mFeeTrack(), mRpcDB(NULL), mTxnDB(NULL), mLedgerDB(NULL), mWalletDB(NULL), mHashNodeDB(NULL), mNetNodeDB(NULL), mConnectionPool(mIOService), mPeerDoor(NULL), mRPCDoor(NULL), mWSPublicDoor(NULL), mWSPrivateDoor(NULL), - mSweepTimer(mAuxService) + mSweepTimer(mAuxService), mShutdown(false) { getRand(mNonce256.begin(), mNonce256.size()); getRand(reinterpret_cast(&mNonceST), sizeof(mNonceST)); @@ -58,6 +58,7 @@ bool Instance::running = true; void Application::stop() { cLog(lsINFO) << "Received shutdown request"; + mShutdown = true; mIOService.stop(); mHashedObjectStore.bulkWrite(); mValidations.flush(); diff --git a/src/cpp/ripple/Application.h b/src/cpp/ripple/Application.h index b8ef316499..cafa5b7c77 100644 --- a/src/cpp/ripple/Application.h +++ b/src/cpp/ripple/Application.h @@ -87,6 +87,8 @@ class Application std::map mPeerMap; boost::recursive_mutex mPeerMapLock; + volatile bool mShutdown; + void startNewLedger(); void loadOldLedger(const std::string&); @@ -140,6 +142,7 @@ public: uint256 getNonce256() { return mNonce256; } std::size_t getNonceST() { return mNonceST; } + bool isShutdown() { return mShutdown; } void setup(); void run(); void stop(); diff --git a/src/cpp/ripple/Ledger.cpp b/src/cpp/ripple/Ledger.cpp index 0ca0b7d93c..543d2d1053 100644 --- a/src/cpp/ripple/Ledger.cpp +++ b/src/cpp/ripple/Ledger.cpp @@ -781,6 +781,45 @@ bool Ledger::getHashesByIndex(uint32 ledgerIndex, uint256& ledgerHash, uint256& #endif } +std::map< uint32, std::pair > Ledger::getHashesByIndex(uint32 minSeq, uint32 maxSeq) +{ +#ifndef NO_SQLITE_PREPARE + std::map< uint32, std::pair > ret; + DatabaseCon *con = theApp->getLedgerDB(); + ScopedLock sl(con->getDBLock()); + + static SqliteStatement pSt(con->getDB()->getSqliteDB(), + "SELECT LedgerSeq,LedgerHash,PrevHash FROM Ledgers INDEXED BY SeqLedger " + "WHERE LedgerSeq >= ? AND LedgerSeq <= ?;"); + + std::pair hashes; + + pSt.bind(1, minSeq); + pSt.bind(2, maxSeq); + + do + { + int r = pSt.step(); + if (pSt.isDone(r)) + { + pSt.reset(); + return ret; + } + if (!pSt.isRow(r)) + { + pSt.reset(); + return ret; + } + hashes.first.SetHex(pSt.peekString(1), true); + hashes.second.SetHex(pSt.peekString(2), true); + ret[pSt.getUInt32(0)] = hashes; + } while(1); + +#else +#error SQLite prepare is required +#endif +} + Ledger::pointer Ledger::getLastFullLedger() { try diff --git a/src/cpp/ripple/Ledger.h b/src/cpp/ripple/Ledger.h index 62de1c8195..34d88b8fd7 100644 --- a/src/cpp/ripple/Ledger.h +++ b/src/cpp/ripple/Ledger.h @@ -202,6 +202,7 @@ public: static Ledger::pointer loadByHash(const uint256& ledgerHash); static uint256 getHashByIndex(uint32 index); static bool getHashesByIndex(uint32 index, uint256& ledgerHash, uint256& parentHash); + static std::map< uint32, std::pair > getHashesByIndex(uint32 minSeq, uint32 maxSeq); void pendSave(bool fromConsensus); // next/prev function diff --git a/src/cpp/ripple/LedgerMaster.cpp b/src/cpp/ripple/LedgerMaster.cpp index a3f35cdf26..8f538f8bf9 100644 --- a/src/cpp/ripple/LedgerMaster.cpp +++ b/src/cpp/ripple/LedgerMaster.cpp @@ -145,6 +145,8 @@ void LedgerMaster::asyncAccept(Ledger::pointer ledger) uint32 seq = ledger->getLedgerSeq(); uint256 prevHash = ledger->getParentHash(); + std::map< uint32, std::pair > ledgerHashes; + while (seq > 0) { { @@ -155,10 +157,20 @@ void LedgerMaster::asyncAccept(Ledger::pointer ledger) break; } - uint256 tHash, pHash; - if (!Ledger::getHashesByIndex(seq, tHash, pHash) || (tHash != prevHash)) + std::map< uint32, std::pair >::iterator it = ledgerHashes.find(seq); + if (it == ledgerHashes.end()) + { + if (theApp->isShutdown()) + return; + ledgerHashes = Ledger::getHashesByIndex((seq < 500) ? 0 : (seq - 499), seq); + it = ledgerHashes.find(seq); + if (it == ledgerHashes.end()) + break; + } + + if (it->second.first != prevHash) break; - prevHash = pHash; + prevHash = it->second.second; } resumeAcquiring(); diff --git a/src/cpp/ripple/SHAMap.cpp b/src/cpp/ripple/SHAMap.cpp index 294cad85c7..c733d46e75 100644 --- a/src/cpp/ripple/SHAMap.cpp +++ b/src/cpp/ripple/SHAMap.cpp @@ -23,8 +23,12 @@ DECLARE_INSTANCE(SHAMapTreeNode); void SHAMapNode::setHash() const { - std::size_t h = theApp->getNonceST() + mDepth; - mHash = mNodeID.hash_combine(h); + std::size_t h = theApp->getNonceST() + (mDepth * 0x9e3779b9); + + const unsigned int *ptr = reinterpret_cast(mNodeID.begin()); + for (int i = (mDepth + 3) / 4; i >= 0; --i) + boost::hash_combine(h, *ptr++); + mHash = h; } std::size_t hash_value(const SHAMapNode& mn) diff --git a/src/cpp/ripple/SHAMap.h b/src/cpp/ripple/SHAMap.h index 5e2eb8d093..ff42121f0f 100644 --- a/src/cpp/ripple/SHAMap.h +++ b/src/cpp/ripple/SHAMap.h @@ -43,20 +43,21 @@ public: SHAMapNode() : mDepth(0), mHash(0) { ; } SHAMapNode(int depth, const uint256& hash); - virtual ~SHAMapNode() { ; } int getDepth() const { return mDepth; } const uint256& getNodeID() const { return mNodeID; } - bool isValid() const { return (mDepth >= 0) && (mDepth < 64); } + bool isValid() const { return (mDepth >= 0) && (mDepth < 64); } + bool isRoot() const { return mDepth == 0; } size_t getHash() const { if (mHash == 0) setHash(); return mHash; } - virtual bool isPopulated() const { return false; } + virtual bool isPopulated() const { return false; } SHAMapNode getParentNodeID() const { assert(mDepth); return SHAMapNode(mDepth - 1, mNodeID); } + SHAMapNode getChildNodeID(int m) const; int selectBranch(const uint256& hash) const; @@ -68,7 +69,6 @@ public: bool operator!=(const uint256&) const; bool operator<=(const SHAMapNode&) const; bool operator>=(const SHAMapNode&) const; - bool isRoot() const { return mDepth == 0; } virtual std::string getString() const; void dump() const; From f99fc4c36e1a2b00742926803e90ac75f0c8f60f Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sun, 3 Mar 2013 02:44:24 -0800 Subject: [PATCH 33/56] Fix a missing index. --- src/cpp/ripple/DBInit.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cpp/ripple/DBInit.cpp b/src/cpp/ripple/DBInit.cpp index bdc6c29b5e..fcf5a3a84a 100644 --- a/src/cpp/ripple/DBInit.cpp +++ b/src/cpp/ripple/DBInit.cpp @@ -20,7 +20,7 @@ const char *TxnDBInit[] = { TxnMeta BLOB \ );", "CREATE TABLE AccountTransactions ( \ - TransID CHARACTER(64), \ + TransID CHARACTER(64) PRIMARY KEY, \ Account CHARACTER(64), \ LedgerSeq BIGINT UNSIGNED \ );", From 1c79ccc446d8671d109915a0aa39b528897f7497 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sun, 3 Mar 2013 02:52:57 -0800 Subject: [PATCH 34/56] Remove dead code. --- src/cpp/ripple/NetworkOPs.cpp | 24 ------------------------ src/cpp/ripple/Transaction.cpp | 10 ---------- src/cpp/ripple/Transaction.h | 1 - 3 files changed, 35 deletions(-) diff --git a/src/cpp/ripple/NetworkOPs.cpp b/src/cpp/ripple/NetworkOPs.cpp index 61b2ded308..1c81e5c407 100644 --- a/src/cpp/ripple/NetworkOPs.cpp +++ b/src/cpp/ripple/NetworkOPs.cpp @@ -426,30 +426,6 @@ Transaction::pointer NetworkOPs::findTransactionByID(const uint256& transactionI return Transaction::load(transactionID); } -#if 0 -int NetworkOPs::findTransactionsBySource(const uint256& uLedger, std::list& txns, - const RippleAddress& sourceAccount, uint32 minSeq, uint32 maxSeq) -{ - AccountState::pointer state = getAccountState(uLedger, sourceAccount); - if (!state) return 0; - if (minSeq > state->getSeq()) return 0; - if (maxSeq > state->getSeq()) maxSeq = state->getSeq(); - if (maxSeq > minSeq) return 0; - - int count = 0; - for(unsigned int i = minSeq; i <= maxSeq; ++i) - { - Transaction::pointer txn = Transaction::findFrom(sourceAccount, i); - if(txn) - { - txns.push_back(txn); - ++count; - } - } - return count; -} -#endif - int NetworkOPs::findTransactionsByDestination(std::list& txns, const RippleAddress& destinationAccount, uint32 startLedgerSeq, uint32 endLedgerSeq, int maxTransactions) { diff --git a/src/cpp/ripple/Transaction.cpp b/src/cpp/ripple/Transaction.cpp index 4ae2a4636a..7d4369e745 100644 --- a/src/cpp/ripple/Transaction.cpp +++ b/src/cpp/ripple/Transaction.cpp @@ -249,16 +249,6 @@ Transaction::pointer Transaction::load(const uint256& id) return transactionFromSQL(sql); } -Transaction::pointer Transaction::findFrom(const RippleAddress& fromID, uint32 seq) -{ - std::string sql = "SELECT LedgerSeq,Status,RawTxn FROM Transactions WHERE FromID='"; - sql.append(fromID.humanAccountID()); - sql.append("' AND FromSeq='"); - sql.append(boost::lexical_cast(seq)); - sql.append("';"); - return transactionFromSQL(sql); -} - bool Transaction::convertToTransactions(uint32 firstLedgerSeq, uint32 secondLedgerSeq, bool checkFirstTransactions, bool checkSecondTransactions, const SHAMap::SHAMapDiff& inMap, std::map >& outMap) diff --git a/src/cpp/ripple/Transaction.h b/src/cpp/ripple/Transaction.h index d000dea5aa..0ec8eadba9 100644 --- a/src/cpp/ripple/Transaction.h +++ b/src/cpp/ripple/Transaction.h @@ -99,7 +99,6 @@ public: // database functions void save(); static Transaction::pointer load(const uint256& id); - static Transaction::pointer findFrom(const RippleAddress& fromID, uint32 seq); // conversion function static bool convertToTransactions(uint32 ourLedgerSeq, uint32 otherLedgerSeq, From f30a148e19f6a8f6e4c22b4b7f7daa8871a18bb0 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sun, 3 Mar 2013 04:07:04 -0800 Subject: [PATCH 35/56] Missing lock. --- src/cpp/ripple/SHAMap.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cpp/ripple/SHAMap.cpp b/src/cpp/ripple/SHAMap.cpp index c733d46e75..0f1a56daf5 100644 --- a/src/cpp/ripple/SHAMap.cpp +++ b/src/cpp/ripple/SHAMap.cpp @@ -68,6 +68,7 @@ SHAMap::SHAMap(SHAMapType t, const uint256& hash) : mSeq(1), mState(smsSynching) SHAMap::pointer SHAMap::snapShot(bool isMutable) { // Return a new SHAMap that is an immutable snapshot of this one // Initially nodes are shared, but CoW is forced on both ledgers + boost::recursive_mutex::scoped_lock sl(mLock); SHAMap::pointer ret = boost::make_shared(mType); SHAMap& newMap = *ret; newMap.mSeq = ++mSeq; @@ -209,7 +210,6 @@ SHAMapTreeNode::pointer SHAMap::getNode(const SHAMapNode& id, const uint256& has std::cerr << "ID: " << id << std::endl; std::cerr << "TgtHash " << hash << std::endl; std::cerr << "NodHash " << node->getNodeHash() << std::endl; - dump(); throw std::runtime_error("invalid node"); } #endif From 9716882bc0cacd61745112668891969da584c4fc Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sun, 3 Mar 2013 04:53:15 -0800 Subject: [PATCH 36/56] Be more consistent. --- src/cpp/ripple/SHAMap.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cpp/ripple/SHAMap.cpp b/src/cpp/ripple/SHAMap.cpp index 0f1a56daf5..a918c99ec3 100644 --- a/src/cpp/ripple/SHAMap.cpp +++ b/src/cpp/ripple/SHAMap.cpp @@ -24,7 +24,6 @@ DECLARE_INSTANCE(SHAMapTreeNode); void SHAMapNode::setHash() const { std::size_t h = theApp->getNonceST() + (mDepth * 0x9e3779b9); - const unsigned int *ptr = reinterpret_cast(mNodeID.begin()); for (int i = (mDepth + 3) / 4; i >= 0; --i) boost::hash_combine(h, *ptr++); @@ -163,10 +162,11 @@ SHAMapTreeNode::pointer SHAMap::walkTo(const uint256& id, bool modify) while (!inNode->isLeaf()) { int branch = inNode->selectBranch(id); - if (inNode->isEmptyBranch(branch)) - return inNode; uint256 childHash = inNode->getChildHash(branch); + if (childHash.isZero()) + return inNode; + try { inNode = getNode(inNode->getChildNodeID(branch), childHash, false); From 19c7406b96041219c01291e79f10820ffb5a698f Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sun, 3 Mar 2013 05:00:28 -0800 Subject: [PATCH 37/56] Correct INSERT statement. --- src/cpp/ripple/SerializedTransaction.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cpp/ripple/SerializedTransaction.cpp b/src/cpp/ripple/SerializedTransaction.cpp index 7486e0329d..0aec1aacb8 100644 --- a/src/cpp/ripple/SerializedTransaction.cpp +++ b/src/cpp/ripple/SerializedTransaction.cpp @@ -233,7 +233,7 @@ std::string SerializedTransaction::getSQLInsertReplaceHeader() std::string SerializedTransaction::getMetaSQLInsertHeader() { - return "INSERT INTO Transactions " + getMetaSQLValueHeader() + " VALUES "; + return "INSERT OR REPLACE INTO Transactions " + getMetaSQLValueHeader() + " VALUES "; } std::string SerializedTransaction::getSQL(uint32 inLedger, char status) const From 23569c32aa4986540d60f4a8b8c12c8808280bca Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sun, 3 Mar 2013 05:03:48 -0800 Subject: [PATCH 38/56] Don't duplicate transactions in the DB. --- src/cpp/ripple/Ledger.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cpp/ripple/Ledger.cpp b/src/cpp/ripple/Ledger.cpp index 543d2d1053..e34b6d678a 100644 --- a/src/cpp/ripple/Ledger.cpp +++ b/src/cpp/ripple/Ledger.cpp @@ -465,7 +465,7 @@ void Ledger::saveAcceptedLedger(bool fromConsensus, LoadEvent::pointer event) if (!accts.empty()) { - std::string sql = "INSERT INTO AccountTransactions (TransID, Account, LedgerSeq) VALUES "; + std::string sql = "INSERT OR REPLACE INTO AccountTransactions (TransID, Account, LedgerSeq) VALUES "; bool first = true; for (std::vector::const_iterator it = accts.begin(), end = accts.end(); it != end; ++it) { From 87bd9ed78f84e68a79cdedb4eb163605bd2702ca Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sun, 3 Mar 2013 05:54:40 -0800 Subject: [PATCH 39/56] Quick and dirty fix for deadlock. We have code that holds the ioMutex and acquires the mPeerLock but also code that holds the mPeerLock but acquires the ioMutex. --- src/cpp/ripple/ConnectionPool.cpp | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/src/cpp/ripple/ConnectionPool.cpp b/src/cpp/ripple/ConnectionPool.cpp index 7bbfc9e158..3d184d4cda 100644 --- a/src/cpp/ripple/ConnectionPool.cpp +++ b/src/cpp/ripple/ConnectionPool.cpp @@ -235,14 +235,10 @@ void ConnectionPool::policyHandler(const boost::system::error_code& ecResult) int ConnectionPool::relayMessage(Peer* fromPeer, const PackedMessage::pointer& msg) { int sentTo = 0; - boost::recursive_mutex::scoped_lock sl(mPeerLock); - - BOOST_FOREACH(const vtConMap& pair, mConnectedMap) + std::vector peerVector = getPeerVector(); + BOOST_FOREACH(Peer::ref peer, peerVector) { - Peer::ref peer = pair.second; - if (!peer) - std::cerr << "CP::RM null peer in list" << std::endl; - else if ((!fromPeer || !(peer.get() == fromPeer)) && peer->isConnected()) + if ((!fromPeer || !(peer.get() == fromPeer)) && peer->isConnected()) { ++sentTo; peer->sendPacket(msg); @@ -254,11 +250,9 @@ int ConnectionPool::relayMessage(Peer* fromPeer, const PackedMessage::pointer& m void ConnectionPool::relayMessageBut(const std::set& fromPeers, const PackedMessage::pointer& msg) { // Relay message to all but the specified peers - boost::recursive_mutex::scoped_lock sl(mPeerLock); - - BOOST_FOREACH(const vtConMap& pair, mConnectedMap) + std::vector peerVector = getPeerVector(); + BOOST_FOREACH(Peer::ref peer, peerVector) { - Peer::ref peer = pair.second; if (peer->isConnected() && (fromPeers.count(peer->getPeerId()) == 0)) peer->sendPacket(msg); } @@ -267,13 +261,11 @@ void ConnectionPool::relayMessageBut(const std::set& fromPeers, const Pa void ConnectionPool::relayMessageTo(const std::set& fromPeers, const PackedMessage::pointer& msg) { // Relay message to the specified peers - boost::recursive_mutex::scoped_lock sl(mPeerLock); - - BOOST_FOREACH(const uint64& peerID, fromPeers) + std::vector peerVector = getPeerVector(); + BOOST_FOREACH(Peer::ref peer, peerVector) { - const boost::unordered_map::iterator& it = mPeerIdMap.find(peerID); - if ((it != mPeerIdMap.end()) && it->second->isConnected()) - it->second->sendPacket(msg); + if (peer->isConnected() && (fromPeers.count(peer->getPeerId()) != 0)) + peer->sendPacket(msg); } } From 15b6fbed0b8876d3271ed9c6e4485c0b0c82882e Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sun, 3 Mar 2013 05:55:14 -0800 Subject: [PATCH 40/56] Temporarily back out this hash change. --- src/cpp/ripple/SHAMap.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/cpp/ripple/SHAMap.cpp b/src/cpp/ripple/SHAMap.cpp index a918c99ec3..3e323b58a0 100644 --- a/src/cpp/ripple/SHAMap.cpp +++ b/src/cpp/ripple/SHAMap.cpp @@ -24,10 +24,13 @@ DECLARE_INSTANCE(SHAMapTreeNode); void SHAMapNode::setHash() const { std::size_t h = theApp->getNonceST() + (mDepth * 0x9e3779b9); + mHash = mNodeID.hash_combine(h); +#if 0 const unsigned int *ptr = reinterpret_cast(mNodeID.begin()); for (int i = (mDepth + 3) / 4; i >= 0; --i) boost::hash_combine(h, *ptr++); mHash = h; +#endif } std::size_t hash_value(const SHAMapNode& mn) From c1f8bed3b80fac693ba56751161e91adb3bfa815 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sun, 3 Mar 2013 05:59:47 -0800 Subject: [PATCH 41/56] Fix a possible bug in commented out code. --- src/cpp/ripple/SHAMap.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cpp/ripple/SHAMap.cpp b/src/cpp/ripple/SHAMap.cpp index 3e323b58a0..77051f17ff 100644 --- a/src/cpp/ripple/SHAMap.cpp +++ b/src/cpp/ripple/SHAMap.cpp @@ -27,7 +27,7 @@ void SHAMapNode::setHash() const mHash = mNodeID.hash_combine(h); #if 0 const unsigned int *ptr = reinterpret_cast(mNodeID.begin()); - for (int i = (mDepth + 3) / 4; i >= 0; --i) + for (int i = (mDepth + 3) / 4; i != 0; --i) boost::hash_combine(h, *ptr++); mHash = h; #endif From a3dcc36e050b3ed087c02aa49c87e874e4302e37 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sun, 3 Mar 2013 14:07:46 -0800 Subject: [PATCH 42/56] Typo and some extra debug. --- src/cpp/ripple/LedgerConsensus.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cpp/ripple/LedgerConsensus.cpp b/src/cpp/ripple/LedgerConsensus.cpp index b9816ede8f..623cd542fa 100644 --- a/src/cpp/ripple/LedgerConsensus.cpp +++ b/src/cpp/ripple/LedgerConsensus.cpp @@ -392,7 +392,7 @@ void LedgerConsensus::checkLCL() { case lcsPRE_CLOSE: status = "PreClose"; break; case lcsESTABLISH: status = "Establish"; break; - case lcsFINISHED: status = "Finised"; break; + case lcsFINISHED: status = "Finished"; break; case lcsACCEPTED: status = "Accepted"; break; default: status = "unknown"; } @@ -400,6 +400,7 @@ void LedgerConsensus::checkLCL() cLog(lsWARNING) << "View of consensus changed during " << status << " (" << netLgrCount << ") status=" << status << ", " << (mHaveCorrectLCL ? "CorrectLCL" : "IncorrectLCL"); cLog(lsWARNING) << mPrevLedgerHash << " to " << netLgr; + cLog(lsWARNING) << mPreviousLedger->getJson(0); if (sLog(lsDEBUG)) { From f0c029ef0d09e773b1216de1497d007912d44f5e Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sun, 3 Mar 2013 16:24:47 -0800 Subject: [PATCH 43/56] Rework the way load is accounted to be more accurate and more specific. --- src/cpp/database/SqliteDatabase.cpp | 2 +- src/cpp/ripple/HashedObject.cpp | 3 ++- src/cpp/ripple/JobQueue.cpp | 4 ++-- src/cpp/ripple/JobQueue.h | 14 +++++++++----- src/cpp/ripple/LedgerMaster.cpp | 6 ++++-- src/cpp/ripple/LoadManager.cpp | 3 +++ src/cpp/ripple/LoadMonitor.cpp | 7 ++++++- src/cpp/ripple/LoadMonitor.h | 10 ++++++++-- src/cpp/ripple/NetworkOPs.cpp | 2 +- src/cpp/ripple/Peer.cpp | 8 ++++---- src/cpp/ripple/ValidationCollection.cpp | 3 ++- src/cpp/ripple/WSHandler.h | 8 +++++--- 12 files changed, 47 insertions(+), 23 deletions(-) diff --git a/src/cpp/database/SqliteDatabase.cpp b/src/cpp/database/SqliteDatabase.cpp index 35062d29e0..e9e70fed4a 100644 --- a/src/cpp/database/SqliteDatabase.cpp +++ b/src/cpp/database/SqliteDatabase.cpp @@ -223,7 +223,7 @@ void SqliteDatabase::doHook(const char *db, int pages) { walRunning = true; if (mWalQ) - mWalQ->addJob(jtWAL, boost::bind(&SqliteDatabase::runWal, this)); + mWalQ->addJob(jtWAL, std::string("WAL:") + db, boost::bind(&SqliteDatabase::runWal, this)); else boost::thread(boost::bind(&SqliteDatabase::runWal, this)).detach(); } diff --git a/src/cpp/ripple/HashedObject.cpp b/src/cpp/ripple/HashedObject.cpp index 6dd34b6ef2..489ab6b45d 100644 --- a/src/cpp/ripple/HashedObject.cpp +++ b/src/cpp/ripple/HashedObject.cpp @@ -51,7 +51,8 @@ bool HashedObjectStore::store(HashedObjectType type, uint32 index, if (!mWritePending) { mWritePending = true; - theApp->getJobQueue().addJob(jtWRITE, boost::bind(&HashedObjectStore::bulkWrite, this)); + theApp->getJobQueue().addJob(jtWRITE, "HasedObject::store", + boost::bind(&HashedObjectStore::bulkWrite, this)); } } // else diff --git a/src/cpp/ripple/JobQueue.cpp b/src/cpp/ripple/JobQueue.cpp index 3dc9386d0e..cc4c7ca013 100644 --- a/src/cpp/ripple/JobQueue.cpp +++ b/src/cpp/ripple/JobQueue.cpp @@ -98,7 +98,7 @@ bool Job::operator<=(const Job& j) const return mJobIndex <= j.mJobIndex; } -void JobQueue::addJob(JobType type, const boost::function& jobFunc) +void JobQueue::addJob(JobType type, const std::string& name, const boost::function& jobFunc) { assert(type != jtINVALID); @@ -107,7 +107,7 @@ void JobQueue::addJob(JobType type, const boost::function& jobFunc) if (type != jtCLIENT) // FIXME: Workaround incorrect client shutdown ordering assert(mThreadCount != 0); // do not add jobs to a queue with no threads - mJobSet.insert(Job(type, ++mLastJob, mJobLoads[type], jobFunc)); + mJobSet.insert(Job(type, name, ++mLastJob, mJobLoads[type], jobFunc)); ++mJobCounts[type]; mJobCond.notify_one(); } diff --git a/src/cpp/ripple/JobQueue.h b/src/cpp/ripple/JobQueue.h index 10dfbc7ddc..386ccf5ff4 100644 --- a/src/cpp/ripple/JobQueue.h +++ b/src/cpp/ripple/JobQueue.h @@ -54,6 +54,7 @@ protected: uint64 mJobIndex; boost::function mJob; LoadEvent::pointer mLoadMonitor; + std::string mName; public: @@ -62,12 +63,15 @@ public: Job(JobType type, uint64 index) : mType(type), mJobIndex(index) { ; } - Job(JobType type, uint64 index, LoadMonitor& lm, const boost::function& job) - : mType(type), mJobIndex(index), mJob(job) - { mLoadMonitor = boost::make_shared(boost::ref(lm), true, 1); } + Job(JobType type, const std::string& name, uint64 index, LoadMonitor& lm, const boost::function& job) + : mType(type), mJobIndex(index), mJob(job), mName(name) + { + mLoadMonitor = boost::make_shared(boost::ref(lm), false, 1); + } JobType getType() const { return mType; } - void doJob(void) { mJob(*this); } + void doJob(void) { mLoadMonitor->start(); mJob(*this); mLoadMonitor->setName(mName); } + void rename(const std::string& n) { mName = n; } bool operator<(const Job& j) const; bool operator>(const Job& j) const; @@ -97,7 +101,7 @@ public: JobQueue(); - void addJob(JobType type, const boost::function& job); + void addJob(JobType type, const std::string& name, const boost::function& job); int getJobCount(JobType t); // Jobs at this priority int getJobCountGE(JobType t); // All jobs at or greater than this priority diff --git a/src/cpp/ripple/LedgerMaster.cpp b/src/cpp/ripple/LedgerMaster.cpp index 8f538f8bf9..ab87238da1 100644 --- a/src/cpp/ripple/LedgerMaster.cpp +++ b/src/cpp/ripple/LedgerMaster.cpp @@ -186,7 +186,8 @@ bool LedgerMaster::acquireMissingLedger(Ledger::ref origLedger, const uint256& l { cLog(lsTRACE) << "Ledger hash found in database"; mTooFast = true; - theApp->getJobQueue().addJob(jtPUBOLDLEDGER, boost::bind(&LedgerMaster::asyncAccept, this, ledger)); + theApp->getJobQueue().addJob(jtPUBOLDLEDGER, "LedgerMaster::asyncAccept", + boost::bind(&LedgerMaster::asyncAccept, this, ledger)); return true; } @@ -540,7 +541,8 @@ void LedgerMaster::tryPublish() { theApp->getOPs().clearNeedNetworkLedger(); mPubThread = true; - theApp->getJobQueue().addJob(jtPUBLEDGER, boost::bind(&LedgerMaster::pubThread, this)); + theApp->getJobQueue().addJob(jtPUBLEDGER, "Ledger::pubThread", + boost::bind(&LedgerMaster::pubThread, this)); } } diff --git a/src/cpp/ripple/LoadManager.cpp b/src/cpp/ripple/LoadManager.cpp index a476c37e19..dc5ac0891e 100644 --- a/src/cpp/ripple/LoadManager.cpp +++ b/src/cpp/ripple/LoadManager.cpp @@ -318,7 +318,10 @@ void LoadManager::threadEntry() bool change; if (theApp->getJobQueue().isOverloaded()) + { + cLog(lsINFO) << theApp->getJobQueue().getJson(0); change = theApp->getFeeTrack().raiseLocalFee(); + } else change = theApp->getFeeTrack().lowerLocalFee(); if (change) diff --git a/src/cpp/ripple/LoadMonitor.cpp b/src/cpp/ripple/LoadMonitor.cpp index 567d08c948..88f304c6d3 100644 --- a/src/cpp/ripple/LoadMonitor.cpp +++ b/src/cpp/ripple/LoadMonitor.cpp @@ -1,4 +1,7 @@ #include "LoadMonitor.h" +#include "Log.h" + +SETUP_LOG(); void LoadMonitor::update() { // call with the mutex @@ -52,8 +55,10 @@ void LoadMonitor::addLatency(int latency) mLatencyMSPeak = lp; } -void LoadMonitor::addCountAndLatency(int counts, int latency) +void LoadMonitor::addCountAndLatency(const std::string& name, int counts, int latency) { + if (latency > 1000) + cLog(lsWARNING) << "Job: " << name << " Latency: " << latency; if (latency == 1) latency = 0; boost::mutex::scoped_lock sl(mLock); diff --git a/src/cpp/ripple/LoadMonitor.h b/src/cpp/ripple/LoadMonitor.h index 18cbb228dd..4989f1e14d 100644 --- a/src/cpp/ripple/LoadMonitor.h +++ b/src/cpp/ripple/LoadMonitor.h @@ -32,7 +32,7 @@ public: void addCount(int counts); void addLatency(int latency); - void addCountAndLatency(int counts, int latency); + void addCountAndLatency(const std::string& name, int counts, int latency); void setTargetLatency(uint64 avg, uint64 pk) { @@ -60,6 +60,7 @@ protected: LoadMonitor& mMonitor; bool mRunning; int mCount; + std::string mName; boost::posix_time::ptime mStartTime; public: @@ -76,6 +77,11 @@ public: stop(); } + void setName(const std::string& name) + { + mName = name; + } + void start() { // okay to call if already started mRunning = true; @@ -86,7 +92,7 @@ public: { assert(mRunning); mRunning = false; - mMonitor.addCountAndLatency(mCount, + mMonitor.addCountAndLatency(mName, mCount, static_cast((boost::posix_time::microsec_clock::universal_time() - mStartTime).total_milliseconds())); } }; diff --git a/src/cpp/ripple/NetworkOPs.cpp b/src/cpp/ripple/NetworkOPs.cpp index 1c81e5c407..fd3ff143a4 100644 --- a/src/cpp/ripple/NetworkOPs.cpp +++ b/src/cpp/ripple/NetworkOPs.cpp @@ -1377,7 +1377,7 @@ void NetworkOPs::reportFeeChange() (theApp->getFeeTrack().getLoadFactor() == mLastLoadFactor)) return; - theApp->getJobQueue().addJob(jtCLIENT, boost::bind(&NetworkOPs::pubServer, this)); + theApp->getJobQueue().addJob(jtCLIENT, "reportFeeChange->pubServer", boost::bind(&NetworkOPs::pubServer, this)); } Json::Value NetworkOPs::transJson(const SerializedTransaction& stTxn, TER terResult, bool bAccepted, Ledger::ref lpCurrent, const std::string& strType) diff --git a/src/cpp/ripple/Peer.cpp b/src/cpp/ripple/Peer.cpp index 37e9bcb210..e8a8b88154 100644 --- a/src/cpp/ripple/Peer.cpp +++ b/src/cpp/ripple/Peer.cpp @@ -857,7 +857,7 @@ void Peer::recvTransaction(ripple::TMTransaction& packet) return; } - theApp->getJobQueue().addJob(jtTRANSACTION, + theApp->getJobQueue().addJob(jtTRANSACTION, "recvTransction->checkTransaction", boost::bind(&checkTransaction, _1, flags, stx, boost::weak_ptr(shared_from_this()))); #ifndef TRUST_NETWORK @@ -986,7 +986,7 @@ void Peer::recvPropose(const boost::shared_ptr& packet) prevLedger.isNonZero() ? prevLedger : consensusLCL, set.proposeseq(), proposeHash, set.closetime(), signerPublic, suppression); - theApp->getJobQueue().addJob(isTrusted ? jtPROPOSAL_t : jtPROPOSAL_ut, + theApp->getJobQueue().addJob(isTrusted ? jtPROPOSAL_t : jtPROPOSAL_ut, "recvPropose->checkPropose", boost::bind(&checkPropose, _1, packet, proposal, consensusLCL, mNodePublic, boost::weak_ptr(shared_from_this()))); } @@ -1062,7 +1062,7 @@ void Peer::recvValidation(const boost::shared_ptr& packet) } bool isTrusted = theApp->getUNL().nodeInUNL(val->getSignerPublic()); - theApp->getJobQueue().addJob(isTrusted ? jtVALIDATION_t : jtVALIDATION_ut, + theApp->getJobQueue().addJob(isTrusted ? jtVALIDATION_t : jtVALIDATION_ut, "recvValidation->checkValidation", boost::bind(&checkValidation, _1, val, signingHash, isTrusted, packet, boost::weak_ptr(shared_from_this()))); } @@ -1299,7 +1299,7 @@ void Peer::recvProofWork(ripple::TMProofWork& packet) return; } - theApp->getJobQueue().addJob(jtPROOFWORK, + theApp->getJobQueue().addJob(jtPROOFWORK, "recvProof->doProof", boost::bind(&Peer::doProofOfWork, _1, boost::weak_ptr(shared_from_this()), pow)); return; diff --git a/src/cpp/ripple/ValidationCollection.cpp b/src/cpp/ripple/ValidationCollection.cpp index cfdec56cd8..699dc8afb4 100644 --- a/src/cpp/ripple/ValidationCollection.cpp +++ b/src/cpp/ripple/ValidationCollection.cpp @@ -293,7 +293,8 @@ void ValidationCollection::condWrite() if (mWriting) return; mWriting = true; - theApp->getJobQueue().addJob(jtWRITE, boost::bind(&ValidationCollection::doWrite, this, _1)); + theApp->getJobQueue().addJob(jtWRITE, "ValidationCollection::doWrite", + boost::bind(&ValidationCollection::doWrite, this, _1)); } void ValidationCollection::doWrite(Job&) diff --git a/src/cpp/ripple/WSHandler.h b/src/cpp/ripple/WSHandler.h index b525859b94..81011b24b5 100644 --- a/src/cpp/ripple/WSHandler.h +++ b/src/cpp/ripple/WSHandler.h @@ -152,17 +152,17 @@ public: ptr->preDestroy(); // Must be done before we return // Must be done without holding the websocket send lock - theApp->getJobQueue().addJob(jtCLIENT, + theApp->getJobQueue().addJob(jtCLIENT, "WSClient::destroy", boost::bind(&WSConnection::destroy, ptr)); } void on_message(connection_ptr cpClient, message_ptr mpMessage) { - theApp->getJobQueue().addJob(jtCLIENT, + theApp->getJobQueue().addJob(jtCLIENT, "WSClient::command", boost::bind(&WSServerHandler::do_message, this, _1, cpClient, mpMessage)); } - void do_message(Job&, connection_ptr cpClient, message_ptr mpMessage) + void do_message(Job& job, connection_ptr cpClient, message_ptr mpMessage) { Json::Value jvRequest; Json::Reader jrReader; @@ -190,6 +190,8 @@ public: } else { + if (jvRequest.isMember("command")) + job.rename(std::string("WSClient::") + jvRequest["command"].asString()); boost::shared_ptr< WSConnection > conn; { boost::mutex::scoped_lock sl(mMapLock); From bc99fdc45fb56fb418a42da4ef3fcb538ea13c70 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sun, 3 Mar 2013 16:27:11 -0800 Subject: [PATCH 44/56] More accurate field names. --- src/cpp/ripple/JobQueue.cpp | 4 ++-- src/cpp/ripple/LoadMonitor.cpp | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/cpp/ripple/JobQueue.cpp b/src/cpp/ripple/JobQueue.cpp index cc4c7ca013..c673a45577 100644 --- a/src/cpp/ripple/JobQueue.cpp +++ b/src/cpp/ripple/JobQueue.cpp @@ -176,9 +176,9 @@ Json::Value JobQueue::getJson(int) if (count != 0) pri["per_second"] = static_cast(count); if (latencyPeak != 0) - pri["peak_latency"] = static_cast(latencyPeak); + pri["peak_time"] = static_cast(latencyPeak); if (latencyAvg != 0) - pri["avg_latency"] = static_cast(latencyAvg); + pri["avg_time"] = static_cast(latencyAvg); priorities.append(pri); } } diff --git a/src/cpp/ripple/LoadMonitor.cpp b/src/cpp/ripple/LoadMonitor.cpp index 88f304c6d3..1302339d13 100644 --- a/src/cpp/ripple/LoadMonitor.cpp +++ b/src/cpp/ripple/LoadMonitor.cpp @@ -58,7 +58,9 @@ void LoadMonitor::addLatency(int latency) void LoadMonitor::addCountAndLatency(const std::string& name, int counts, int latency) { if (latency > 1000) - cLog(lsWARNING) << "Job: " << name << " Latency: " << latency; + { + cLog(lsWARNING) << "Job: " << name << " ExecutionTime: " << latency; + } if (latency == 1) latency = 0; boost::mutex::scoped_lock sl(mLock); From a96676f122542b9c65dc7ac0bba430cc7caebce1 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sun, 3 Mar 2013 16:46:24 -0800 Subject: [PATCH 45/56] Typo. --- src/cpp/ripple/HashedObject.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cpp/ripple/HashedObject.cpp b/src/cpp/ripple/HashedObject.cpp index 489ab6b45d..3515b5f55c 100644 --- a/src/cpp/ripple/HashedObject.cpp +++ b/src/cpp/ripple/HashedObject.cpp @@ -51,7 +51,7 @@ bool HashedObjectStore::store(HashedObjectType type, uint32 index, if (!mWritePending) { mWritePending = true; - theApp->getJobQueue().addJob(jtWRITE, "HasedObject::store", + theApp->getJobQueue().addJob(jtWRITE, "HashedObject::store", boost::bind(&HashedObjectStore::bulkWrite, this)); } } From edce4a83ce905ff82ac4aebf296e39b3675d4d8b Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Sun, 3 Mar 2013 20:04:32 -0800 Subject: [PATCH 46/56] JS: Add temporary support for RPC tx. --- src/js/remote.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/js/remote.js b/src/js/remote.js index 7eca87f002..c241a8b33e 100644 --- a/src/js/remote.js +++ b/src/js/remote.js @@ -876,6 +876,15 @@ Remote.prototype.request_transaction_entry = function (hash) { .tx_hash(hash); }; +// DEPRECATED: use request_transaction_entry +Remote.prototype.request_tx = function (hash) { + var request = new Request(this, 'tx'); + + request.message.transaction = hash; + + return request; +}; + Remote.prototype.request_account_info = function (accountID) { var request = new Request(this, 'account_info'); From da184a97c47e7415fdb5b9ca5714a4689f4571c5 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Sun, 3 Mar 2013 20:52:28 -0800 Subject: [PATCH 47/56] Fix RPC book_offers handling of negative balances and self-issued IOUs. --- src/cpp/ripple/NetworkOPs.cpp | 181 +++++++++++++++++++--------------- 1 file changed, 100 insertions(+), 81 deletions(-) diff --git a/src/cpp/ripple/NetworkOPs.cpp b/src/cpp/ripple/NetworkOPs.cpp index fd3ff143a4..2de3b9cb3e 100644 --- a/src/cpp/ripple/NetworkOPs.cpp +++ b/src/cpp/ripple/NetworkOPs.cpp @@ -1784,102 +1784,121 @@ void NetworkOPs::getBookPage(Ledger::pointer lpLedger, const uint160& uTakerPays STAmount saDirRate; // unsigned int iLeft = iLimit; + SLE::pointer sleGetsIssuer = lesActive.entryCache(ltACCOUNT_ROOT, Ledger::getAccountRootIndex(uTakerGetsIssuerID)); - while (!bDone) { - if (bDirectAdvance) { - bDirectAdvance = false; + if (!sleGetsIssuer) + { + // Order book can not exist if TakerGetsIssuer account does not exist. - cLog(lsTRACE) << boost::str(boost::format("getBookPage: bDirectAdvance")); + nothing(); + } + else + { + while (!bDone) { + if (bDirectAdvance) { + bDirectAdvance = false; - sleOfferDir = lesActive.entryCache(ltDIR_NODE, lpLedger->getNextLedgerIndex(uTipIndex, uBookEnd)); - if (!sleOfferDir) - { - cLog(lsTRACE) << boost::str(boost::format("getBookPage: bDone")); - bDone = true; - } - else - { - uTipIndex = sleOfferDir->getIndex(); - saDirRate = STAmount::setRate(Ledger::getQuality(uTipIndex)); - SLE::pointer sleBookNode; + cLog(lsTRACE) << boost::str(boost::format("getBookPage: bDirectAdvance")); - lesActive.dirFirst(uTipIndex, sleBookNode, uBookEntry, uOfferIndex); + sleOfferDir = lesActive.entryCache(ltDIR_NODE, lpLedger->getNextLedgerIndex(uTipIndex, uBookEnd)); + if (!sleOfferDir) + { + cLog(lsTRACE) << boost::str(boost::format("getBookPage: bDone")); + bDone = true; + } + else + { + uTipIndex = sleOfferDir->getIndex(); + saDirRate = STAmount::setRate(Ledger::getQuality(uTipIndex)); + SLE::pointer sleBookNode; - cLog(lsTRACE) << boost::str(boost::format("getBookPage: uTipIndex=%s") % uTipIndex); - cLog(lsTRACE) << boost::str(boost::format("getBookPage: uOfferIndex=%s") % uOfferIndex); - } - } + lesActive.dirFirst(uTipIndex, sleBookNode, uBookEntry, uOfferIndex); - if (!bDone) - { - SLE::pointer sleOffer = lesActive.entryCache(ltOFFER, uOfferIndex); - const uint160 uOfferOwnerID = sleOffer->getFieldAccount(sfAccount).getAccountID(); - STAmount saOwnerFunds; - - boost::unordered_map::const_iterator umBalanceEntry = umBalance.find(uOfferOwnerID); - - if (umBalanceEntry == umBalance.end()) - { - // Did not find balance in table. - STAmount saDefault(uTakerGetsCurrencyID, uTakerGetsIssuerID); - // cLog(lsINFO) << boost::str(boost::format("getBookPage: saDefault=%s") % saDefault.getFullText()); - - saOwnerFunds = lesActive.accountFunds(uOfferOwnerID, saDefault); - // cLog(lsINFO) << boost::str(boost::format("getBookPage: saOwnerFunds=%s (new)") % saOwnerFunds.getFullText()); - } - else - { - saOwnerFunds = umBalanceEntry->second; - // cLog(lsINFO) << boost::str(boost::format("getBookPage: saOwnerFunds=%s (cached)") % saOwnerFunds.getFullText()); + cLog(lsTRACE) << boost::str(boost::format("getBookPage: uTipIndex=%s") % uTipIndex); + cLog(lsTRACE) << boost::str(boost::format("getBookPage: uOfferIndex=%s") % uOfferIndex); + } } - STAmount saTakerGets = sleOffer->getFieldAmount(sfTakerGets); - STAmount saTakerPays = sleOffer->getFieldAmount(sfTakerPays); - - Json::Value jvOffer = sleOffer->getJson(0); - - STAmount saTakerGetsFunded; - - if (saOwnerFunds >= saTakerGets) + if (!bDone) { - // Sufficient funds no shenanigans. - saTakerGetsFunded = saTakerGets; - } - else - { - // cLog(lsINFO) << boost::str(boost::format("getBookPage: saTakerGets=%s") % saTakerGets.getFullText()); - // cLog(lsINFO) << boost::str(boost::format("getBookPage: saTakerPays=%s") % saTakerPays.getFullText()); - // cLog(lsINFO) << boost::str(boost::format("getBookPage: saOwnerFunds=%s") % saOwnerFunds.getFullText()); - // cLog(lsINFO) << boost::str(boost::format("getBookPage: saDirRate=%s") % saDirRate.getText()); - // cLog(lsINFO) << boost::str(boost::format("getBookPage: multiply=%s") % STAmount::multiply(saTakerGetsFunded, saDirRate).getFullText()); - // cLog(lsINFO) << boost::str(boost::format("getBookPage: multiply=%s") % STAmount::multiply(saTakerGetsFunded, saDirRate, saTakerPays).getFullText()); - STAmount saTakerPaysFunded; + SLE::pointer sleOffer = lesActive.entryCache(ltOFFER, uOfferIndex); + const uint160 uOfferOwnerID = sleOffer->getFieldAccount(sfAccount).getAccountID(); + STAmount saTakerGets = sleOffer->getFieldAmount(sfTakerGets); + STAmount saTakerPays = sleOffer->getFieldAmount(sfTakerPays); + STAmount saOwnerFunds; - saTakerGetsFunded = saOwnerFunds; - saTakerPaysFunded = std::min(saTakerPays, STAmount::multiply(saTakerGetsFunded, saDirRate, saTakerPays)); + if (uTakerGetsIssuerID == uOfferOwnerID) + { + // If offer is selling issuer's own IOUs, it is fully funded. + saOwnerFunds = saTakerGets; + } + else + { + boost::unordered_map::const_iterator umBalanceEntry = umBalance.find(uOfferOwnerID); - // Only provide, if not fully funded. - jvOffer["taker_gets_funded"] = saTakerGetsFunded.getJson(0); - jvOffer["taker_pays_funded"] = saTakerPaysFunded.getJson(0); - } + if (umBalanceEntry != umBalance.end()) + { + // Found in running balance table. - STAmount saOwnerBalance = saOwnerFunds-saTakerGetsFunded; + saOwnerFunds = umBalanceEntry->second; + // cLog(lsINFO) << boost::str(boost::format("getBookPage: saOwnerFunds=%s (cached)") % saOwnerFunds.getFullText()); + } + else + { + // Did not find balance in table. - umBalance[uOfferOwnerID] = saOwnerBalance; + saOwnerFunds = lesActive.accountHolds(uOfferOwnerID, uTakerGetsCurrencyID, uTakerGetsIssuerID); + // cLog(lsINFO) << boost::str(boost::format("getBookPage: saOwnerFunds=%s (new)") % saOwnerFunds.getFullText()); + if (saOwnerFunds.isNegative()) + saOwnerFunds.zero(); + } + } - if (!saOwnerFunds.isZero() || uOfferOwnerID == uTakerID) - { - // Only provide funded offers and offers of the taker. - jvOffers.append(jvOffer); - } + Json::Value jvOffer = sleOffer->getJson(0); - if (!lesActive.dirNext(uTipIndex, sleOfferDir, uBookEntry, uOfferIndex)) - { - bDirectAdvance = true; - } - else - { - cLog(lsTRACE) << boost::str(boost::format("getBookPage: uOfferIndex=%s") % uOfferIndex); + STAmount saTakerGetsFunded; + + if (saOwnerFunds >= saTakerGets) + { + // Sufficient funds no shenanigans. + saTakerGetsFunded = saTakerGets; + } + else + { + // cLog(lsINFO) << boost::str(boost::format("getBookPage: saTakerGets=%s") % saTakerGets.getFullText()); + // cLog(lsINFO) << boost::str(boost::format("getBookPage: saTakerPays=%s") % saTakerPays.getFullText()); + // cLog(lsINFO) << boost::str(boost::format("getBookPage: saOwnerFunds=%s") % saOwnerFunds.getFullText()); + // cLog(lsINFO) << boost::str(boost::format("getBookPage: saDirRate=%s") % saDirRate.getText()); + // cLog(lsINFO) << boost::str(boost::format("getBookPage: multiply=%s") % STAmount::multiply(saTakerGetsFunded, saDirRate).getFullText()); + // cLog(lsINFO) << boost::str(boost::format("getBookPage: multiply=%s") % STAmount::multiply(saTakerGetsFunded, saDirRate, saTakerPays).getFullText()); + STAmount saTakerPaysFunded; + + saTakerGetsFunded = saOwnerFunds; + saTakerPaysFunded = std::min(saTakerPays, STAmount::multiply(saTakerGetsFunded, saDirRate, saTakerPays)); + + // Only provide, if not fully funded. + jvOffer["taker_gets_funded"] = saTakerGetsFunded.getJson(0); + jvOffer["taker_pays_funded"] = saTakerPaysFunded.getJson(0); + } + + STAmount saOwnerBalance = saOwnerFunds-saTakerGetsFunded; + + umBalance[uOfferOwnerID] = saOwnerBalance; + + if (!saOwnerFunds.isZero() || uOfferOwnerID == uTakerID) + { + // Only provide funded offers and offers of the taker. + jvOffers.append(jvOffer); + } + + if (!lesActive.dirNext(uTipIndex, sleOfferDir, uBookEntry, uOfferIndex)) + { + bDirectAdvance = true; + } + else + { + cLog(lsTRACE) << boost::str(boost::format("getBookPage: uOfferIndex=%s") % uOfferIndex); + } } } } From 49458bff57650bf5593f95de928c1c6194b236c2 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Sun, 3 Mar 2013 21:31:34 -0800 Subject: [PATCH 48/56] Add support for transfer fees to RPC book_offers. --- src/cpp/ripple/LedgerEntrySet.cpp | 2 + src/cpp/ripple/NetworkOPs.cpp | 202 ++++++++++++++++-------------- 2 files changed, 111 insertions(+), 93 deletions(-) diff --git a/src/cpp/ripple/LedgerEntrySet.cpp b/src/cpp/ripple/LedgerEntrySet.cpp index e47ed099d9..09ecc4ab7e 100644 --- a/src/cpp/ripple/LedgerEntrySet.cpp +++ b/src/cpp/ripple/LedgerEntrySet.cpp @@ -1066,6 +1066,8 @@ STAmount LedgerEntrySet::rippleHolds(const uint160& uAccountID, const uint160& u return saBalance; } +// Returns the amount an account can spend without going into debt. +// // <-- saAmount: amount of uCurrencyID held by uAccountID. May be negative. STAmount LedgerEntrySet::accountHolds(const uint160& uAccountID, const uint160& uCurrencyID, const uint160& uIssuerID) { diff --git a/src/cpp/ripple/NetworkOPs.cpp b/src/cpp/ripple/NetworkOPs.cpp index 2de3b9cb3e..a3106768d0 100644 --- a/src/cpp/ripple/NetworkOPs.cpp +++ b/src/cpp/ripple/NetworkOPs.cpp @@ -1784,121 +1784,137 @@ void NetworkOPs::getBookPage(Ledger::pointer lpLedger, const uint160& uTakerPays STAmount saDirRate; // unsigned int iLeft = iLimit; - SLE::pointer sleGetsIssuer = lesActive.entryCache(ltACCOUNT_ROOT, Ledger::getAccountRootIndex(uTakerGetsIssuerID)); - if (!sleGetsIssuer) - { - // Order book can not exist if TakerGetsIssuer account does not exist. + uint32 uTransferRate = lesActive.rippleTransferRate(uTakerGetsIssuerID); - nothing(); - } - else - { - while (!bDone) { - if (bDirectAdvance) { - bDirectAdvance = false; + while (!bDone) { + if (bDirectAdvance) { + bDirectAdvance = false; - cLog(lsTRACE) << boost::str(boost::format("getBookPage: bDirectAdvance")); + cLog(lsTRACE) << boost::str(boost::format("getBookPage: bDirectAdvance")); - sleOfferDir = lesActive.entryCache(ltDIR_NODE, lpLedger->getNextLedgerIndex(uTipIndex, uBookEnd)); - if (!sleOfferDir) + sleOfferDir = lesActive.entryCache(ltDIR_NODE, lpLedger->getNextLedgerIndex(uTipIndex, uBookEnd)); + if (!sleOfferDir) + { + cLog(lsTRACE) << boost::str(boost::format("getBookPage: bDone")); + bDone = true; + } + else + { + uTipIndex = sleOfferDir->getIndex(); + saDirRate = STAmount::setRate(Ledger::getQuality(uTipIndex)); + SLE::pointer sleBookNode; + + lesActive.dirFirst(uTipIndex, sleBookNode, uBookEntry, uOfferIndex); + + cLog(lsTRACE) << boost::str(boost::format("getBookPage: uTipIndex=%s") % uTipIndex); + cLog(lsTRACE) << boost::str(boost::format("getBookPage: uOfferIndex=%s") % uOfferIndex); + } + } + + if (!bDone) + { + SLE::pointer sleOffer = lesActive.entryCache(ltOFFER, uOfferIndex); + const uint160 uOfferOwnerID = sleOffer->getFieldAccount(sfAccount).getAccountID(); + STAmount saTakerGets = sleOffer->getFieldAmount(sfTakerGets); + STAmount saTakerPays = sleOffer->getFieldAmount(sfTakerPays); + STAmount saOwnerFunds; + + if (uTakerGetsIssuerID == uOfferOwnerID) + { + // If offer is selling issuer's own IOUs, it is fully funded. + saOwnerFunds = saTakerGets; + } + else + { + boost::unordered_map::const_iterator umBalanceEntry = umBalance.find(uOfferOwnerID); + + if (umBalanceEntry != umBalance.end()) { - cLog(lsTRACE) << boost::str(boost::format("getBookPage: bDone")); - bDone = true; + // Found in running balance table. + + saOwnerFunds = umBalanceEntry->second; + // cLog(lsINFO) << boost::str(boost::format("getBookPage: saOwnerFunds=%s (cached)") % saOwnerFunds.getFullText()); } else { - uTipIndex = sleOfferDir->getIndex(); - saDirRate = STAmount::setRate(Ledger::getQuality(uTipIndex)); - SLE::pointer sleBookNode; + // Did not find balance in table. - lesActive.dirFirst(uTipIndex, sleBookNode, uBookEntry, uOfferIndex); + saOwnerFunds = lesActive.accountHolds(uOfferOwnerID, uTakerGetsCurrencyID, uTakerGetsIssuerID); + // cLog(lsINFO) << boost::str(boost::format("getBookPage: saOwnerFunds=%s (new)") % saOwnerFunds.getFullText()); + if (saOwnerFunds.isNegative()) + { + // Treat negative funds as zero. - cLog(lsTRACE) << boost::str(boost::format("getBookPage: uTipIndex=%s") % uTipIndex); - cLog(lsTRACE) << boost::str(boost::format("getBookPage: uOfferIndex=%s") % uOfferIndex); + saOwnerFunds.zero(); + } } } - if (!bDone) + Json::Value jvOffer = sleOffer->getJson(0); + + STAmount saTakerGetsFunded; + STAmount saOwnerFundsLimit; + uint32 uOfferRate; + + + if (uTransferRate != QUALITY_ONE // Have a tranfer fee. + && uTakerID != uTakerGetsIssuerID // Not taking offers of own IOUs. + && uTakerGetsIssuerID != uOfferOwnerID) { // Offer owner not issuing ownfunds + // Need to charge a transfer fee to offer owner. + uOfferRate = uTransferRate; + saOwnerFundsLimit = STAmount::divide(saOwnerFunds, STAmount(CURRENCY_ONE, ACCOUNT_ONE, uOfferRate, -9)); + } + else { - SLE::pointer sleOffer = lesActive.entryCache(ltOFFER, uOfferIndex); - const uint160 uOfferOwnerID = sleOffer->getFieldAccount(sfAccount).getAccountID(); - STAmount saTakerGets = sleOffer->getFieldAmount(sfTakerGets); - STAmount saTakerPays = sleOffer->getFieldAmount(sfTakerPays); - STAmount saOwnerFunds; + uOfferRate = QUALITY_ONE; + saOwnerFundsLimit = saOwnerFunds; + } - if (uTakerGetsIssuerID == uOfferOwnerID) - { - // If offer is selling issuer's own IOUs, it is fully funded. - saOwnerFunds = saTakerGets; - } - else - { - boost::unordered_map::const_iterator umBalanceEntry = umBalance.find(uOfferOwnerID); + if (saOwnerFundsLimit >= saTakerGets) + { + // Sufficient funds no shenanigans. + saTakerGetsFunded = saTakerGets; + } + else + { + // cLog(lsINFO) << boost::str(boost::format("getBookPage: saTakerGets=%s") % saTakerGets.getFullText()); + // cLog(lsINFO) << boost::str(boost::format("getBookPage: saTakerPays=%s") % saTakerPays.getFullText()); + // cLog(lsINFO) << boost::str(boost::format("getBookPage: saOwnerFunds=%s") % saOwnerFunds.getFullText()); + // cLog(lsINFO) << boost::str(boost::format("getBookPage: saDirRate=%s") % saDirRate.getText()); + // cLog(lsINFO) << boost::str(boost::format("getBookPage: multiply=%s") % STAmount::multiply(saTakerGetsFunded, saDirRate).getFullText()); + // cLog(lsINFO) << boost::str(boost::format("getBookPage: multiply=%s") % STAmount::multiply(saTakerGetsFunded, saDirRate, saTakerPays).getFullText()); + STAmount saTakerPaysFunded; - if (umBalanceEntry != umBalance.end()) - { - // Found in running balance table. + saTakerGetsFunded = saOwnerFundsLimit; + saTakerPaysFunded = std::min(saTakerPays, STAmount::multiply(saTakerGetsFunded, saDirRate, saTakerPays)); - saOwnerFunds = umBalanceEntry->second; - // cLog(lsINFO) << boost::str(boost::format("getBookPage: saOwnerFunds=%s (cached)") % saOwnerFunds.getFullText()); - } - else - { - // Did not find balance in table. + // Only provide, if not fully funded. + jvOffer["taker_gets_funded"] = saTakerGetsFunded.getJson(0); + jvOffer["taker_pays_funded"] = saTakerPaysFunded.getJson(0); - saOwnerFunds = lesActive.accountHolds(uOfferOwnerID, uTakerGetsCurrencyID, uTakerGetsIssuerID); - // cLog(lsINFO) << boost::str(boost::format("getBookPage: saOwnerFunds=%s (new)") % saOwnerFunds.getFullText()); - if (saOwnerFunds.isNegative()) - saOwnerFunds.zero(); - } - } + } + STAmount saOwnerPays = QUALITY_ONE == uOfferRate + ? saTakerGetsFunded + : std::min(saOwnerFunds, STAmount::multiply(saTakerGetsFunded, STAmount(CURRENCY_ONE, ACCOUNT_ONE, uOfferRate, -9))); - Json::Value jvOffer = sleOffer->getJson(0); + STAmount saOwnerBalance = saOwnerFunds-saOwnerPays; - STAmount saTakerGetsFunded; + umBalance[uOfferOwnerID] = saOwnerBalance; - if (saOwnerFunds >= saTakerGets) - { - // Sufficient funds no shenanigans. - saTakerGetsFunded = saTakerGets; - } - else - { - // cLog(lsINFO) << boost::str(boost::format("getBookPage: saTakerGets=%s") % saTakerGets.getFullText()); - // cLog(lsINFO) << boost::str(boost::format("getBookPage: saTakerPays=%s") % saTakerPays.getFullText()); - // cLog(lsINFO) << boost::str(boost::format("getBookPage: saOwnerFunds=%s") % saOwnerFunds.getFullText()); - // cLog(lsINFO) << boost::str(boost::format("getBookPage: saDirRate=%s") % saDirRate.getText()); - // cLog(lsINFO) << boost::str(boost::format("getBookPage: multiply=%s") % STAmount::multiply(saTakerGetsFunded, saDirRate).getFullText()); - // cLog(lsINFO) << boost::str(boost::format("getBookPage: multiply=%s") % STAmount::multiply(saTakerGetsFunded, saDirRate, saTakerPays).getFullText()); - STAmount saTakerPaysFunded; + if (!saOwnerFunds.isZero() || uOfferOwnerID == uTakerID) + { + // Only provide funded offers and offers of the taker. + jvOffers.append(jvOffer); + } - saTakerGetsFunded = saOwnerFunds; - saTakerPaysFunded = std::min(saTakerPays, STAmount::multiply(saTakerGetsFunded, saDirRate, saTakerPays)); - - // Only provide, if not fully funded. - jvOffer["taker_gets_funded"] = saTakerGetsFunded.getJson(0); - jvOffer["taker_pays_funded"] = saTakerPaysFunded.getJson(0); - } - - STAmount saOwnerBalance = saOwnerFunds-saTakerGetsFunded; - - umBalance[uOfferOwnerID] = saOwnerBalance; - - if (!saOwnerFunds.isZero() || uOfferOwnerID == uTakerID) - { - // Only provide funded offers and offers of the taker. - jvOffers.append(jvOffer); - } - - if (!lesActive.dirNext(uTipIndex, sleOfferDir, uBookEntry, uOfferIndex)) - { - bDirectAdvance = true; - } - else - { - cLog(lsTRACE) << boost::str(boost::format("getBookPage: uOfferIndex=%s") % uOfferIndex); - } + if (!lesActive.dirNext(uTipIndex, sleOfferDir, uBookEntry, uOfferIndex)) + { + bDirectAdvance = true; + } + else + { + cLog(lsTRACE) << boost::str(boost::format("getBookPage: uOfferIndex=%s") % uOfferIndex); } } } From 522d4f642a0eacca56c5f92a88dc8f22e9d8b6fb Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Sun, 3 Mar 2013 22:35:00 -0800 Subject: [PATCH 49/56] JS: Rename ADDRESS_ to ACCOUNT_ to be consistent with C++. --- src/js/uint.js | 4 ++-- src/js/uint160.js | 4 ++-- src/js/uint256.js | 3 --- test/amount-test.js | 6 +++--- 4 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/js/uint.js b/src/js/uint.js index d95ccb51fa..457e3e9590 100644 --- a/src/js/uint.js +++ b/src/js/uint.js @@ -106,14 +106,14 @@ UInt.prototype.parse_generic = function (j) { case undefined: case "0": case this.constructor.STR_ZERO: - case this.constructor.ADDRESS_ZERO: + case this.constructor.ACCOUNT_ZERO: case this.constructor.HEX_ZERO: this._value = nbi(); break; case "1": case this.constructor.STR_ONE: - case this.constructor.ADDRESS_ONE: + case this.constructor.ACCOUNT_ONE: case this.constructor.HEX_ONE: this._value = new BigInteger([1]); diff --git a/src/js/uint160.js b/src/js/uint160.js index 12c7ff04c6..66c980ec0e 100644 --- a/src/js/uint160.js +++ b/src/js/uint160.js @@ -24,8 +24,8 @@ UInt160.width = 20; UInt160.prototype = extend({}, UInt.prototype); UInt160.prototype.constructor = UInt160; -var ADDRESS_ZERO = UInt160.ADDRESS_ZERO = "rrrrrrrrrrrrrrrrrrrrrhoLvTp"; -var ADDRESS_ONE = UInt160.ADDRESS_ONE = "rrrrrrrrrrrrrrrrrrrrBZbvji"; +var ACCOUNT_ZERO = UInt160.ACCOUNT_ZERO = "rrrrrrrrrrrrrrrrrrrrrhoLvTp"; +var ACCOUNT_ONE = UInt160.ACCOUNT_ONE = "rrrrrrrrrrrrrrrrrrrrBZbvji"; var HEX_ZERO = UInt160.HEX_ZERO = "0000000000000000000000000000000000000000"; var HEX_ONE = UInt160.HEX_ONE = "0000000000000000000000000000000000000001"; var STR_ZERO = UInt160.STR_ZERO = utils.hexToString(HEX_ZERO); diff --git a/src/js/uint256.js b/src/js/uint256.js index ab5b6f70b6..42b01504a4 100644 --- a/src/js/uint256.js +++ b/src/js/uint256.js @@ -24,9 +24,6 @@ UInt256.width = 32; UInt256.prototype = extend({}, UInt.prototype); UInt256.prototype.constructor = UInt256; -// XXX Generate these constants (or remove them) -var ADDRESS_ZERO = UInt256.ADDRESS_ZERO = "XXX"; -var ADDRESS_ONE = UInt256.ADDRESS_ONE = "XXX"; var HEX_ZERO = UInt256.HEX_ZERO = "00000000000000000000000000000000" + "00000000000000000000000000000000"; var HEX_ONE = UInt256.HEX_ONE = "00000000000000000000000000000000" + diff --git a/test/amount-test.js b/test/amount-test.js index 31fa7ba085..6a62aff24f 100644 --- a/test/amount-test.js +++ b/test/amount-test.js @@ -17,16 +17,16 @@ buster.testCase("Amount", { buster.assert.equals(nbi(), UInt160.from_generic("0")._value); }, "Parse 0 export" : function () { - buster.assert.equals(UInt160.ADDRESS_ZERO, UInt160.from_generic("0").to_json()); + buster.assert.equals(UInt160.ACCOUNT_ZERO, UInt160.from_generic("0").to_json()); }, "Parse 1" : function () { buster.assert.equals(new BigInteger([1]), UInt160.from_generic("1")._value); }, "Parse rrrrrrrrrrrrrrrrrrrrrhoLvTp export" : function () { - buster.assert.equals(UInt160.ADDRESS_ZERO, UInt160.from_json("rrrrrrrrrrrrrrrrrrrrrhoLvTp").to_json()); + buster.assert.equals(UInt160.ACCOUNT_ZERO, UInt160.from_json("rrrrrrrrrrrrrrrrrrrrrhoLvTp").to_json()); }, "Parse rrrrrrrrrrrrrrrrrrrrBZbvji export" : function () { - buster.assert.equals(UInt160.ADDRESS_ONE, UInt160.from_json("rrrrrrrrrrrrrrrrrrrrBZbvji").to_json()); + buster.assert.equals(UInt160.ACCOUNT_ONE, UInt160.from_json("rrrrrrrrrrrrrrrrrrrrBZbvji").to_json()); }, "Parse mtgox export" : function () { buster.assert.equals(config.accounts["mtgox"].account, UInt160.from_json("mtgox").to_json()); From 2bb4501553500acce85859e02f2d2fc47229e5df Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Sun, 3 Mar 2013 23:01:19 -0800 Subject: [PATCH 50/56] Add tfMarket support to OfferCreate. --- src/cpp/ripple/OfferCreateTransactor.cpp | 2 ++ src/cpp/ripple/TransactionFormats.h | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/cpp/ripple/OfferCreateTransactor.cpp b/src/cpp/ripple/OfferCreateTransactor.cpp index d2acfe9250..e4b59be8c5 100644 --- a/src/cpp/ripple/OfferCreateTransactor.cpp +++ b/src/cpp/ripple/OfferCreateTransactor.cpp @@ -321,6 +321,7 @@ TER OfferCreateTransactor::doApply() cLog(lsWARNING) << "OfferCreate> " << mTxn.getJson(0); const uint32 uTxFlags = mTxn.getFlags(); const bool bPassive = isSetBit(uTxFlags, tfPassive); + const bool bMarket = isSetBit(uTxFlags, tfMarket); STAmount saTakerPays = mTxn.getFieldAmount(sfTakerPays); STAmount saTakerGets = mTxn.getFieldAmount(sfTakerGets); @@ -460,6 +461,7 @@ TER OfferCreateTransactor::doApply() if (tesSUCCESS != terResult || !saTakerPays // Wants nothing more. || !saTakerGets // Offering nothing more. + || bMarket // Do not persist. || !mEngine->getNodes().accountFunds(mTxnAccountID, saTakerGets).isPositive() // Not funded. || bUnfunded) // Consider unfunded. { diff --git a/src/cpp/ripple/TransactionFormats.h b/src/cpp/ripple/TransactionFormats.h index 7e69ce7416..a1c9161bb2 100644 --- a/src/cpp/ripple/TransactionFormats.h +++ b/src/cpp/ripple/TransactionFormats.h @@ -68,7 +68,8 @@ const uint32 tfAccountSetMask = ~(tfRequireDestTag|tfOptionalDestTag|tfRequireA // OfferCreate flags: const uint32 tfPassive = 0x00010000; -const uint32 tfOfferCreateMask = ~(tfPassive); +const uint32 tfMarket = 0x00020000; +const uint32 tfOfferCreateMask = ~(tfPassive|tfMarket); // Payment flags: const uint32 tfNoRippleDirect = 0x00010000; From df9884f48efb913bb44e5d79cab5305cb9b9aa2f Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sun, 3 Mar 2013 23:52:13 -0800 Subject: [PATCH 51/56] Cleanups. --- src/cpp/ripple/RippleCalc.cpp | 68 ++++++++++++++++++----------------- 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/src/cpp/ripple/RippleCalc.cpp b/src/cpp/ripple/RippleCalc.cpp index e1c41077a9..99a003ccfa 100644 --- a/src/cpp/ripple/RippleCalc.cpp +++ b/src/cpp/ripple/RippleCalc.cpp @@ -325,10 +325,10 @@ void PathState::setExpanded( uMaxCurrencyID, // Max specifes the currency. uSenderIssuerID); -cLog(lsDEBUG) << boost::str(boost::format("PathState: pushed: account=%s currency=%s issuer=%s") - % RippleAddress::createHumanAccountID(uSenderID) - % STAmount::createHumanCurrency(uMaxCurrencyID) - % RippleAddress::createHumanAccountID(uSenderIssuerID)); + cLog(lsDEBUG) << boost::str(boost::format("PathState: pushed: account=%s currency=%s issuer=%s") + % RippleAddress::createHumanAccountID(uSenderID) + % STAmount::createHumanCurrency(uMaxCurrencyID) + % RippleAddress::createHumanAccountID(uSenderIssuerID)); if (tesSUCCESS == terStatus && uMaxIssuerID != uSenderIssuerID) { // Issuer was not same as sender. @@ -347,19 +347,19 @@ cLog(lsDEBUG) << boost::str(boost::format("PathState: pushed: account=%s currenc : uOutIssuerID // Use implied node. : ACCOUNT_XRP; -cLog(lsDEBUG) << boost::str(boost::format("PathState: implied check: uNxtCurrencyID=%s uNxtAccountID=%s") - % RippleAddress::createHumanAccountID(uNxtCurrencyID) - % RippleAddress::createHumanAccountID(uNxtAccountID)); + cLog(lsDEBUG) << boost::str(boost::format("PathState: implied check: uNxtCurrencyID=%s uNxtAccountID=%s") + % RippleAddress::createHumanAccountID(uNxtCurrencyID) + % RippleAddress::createHumanAccountID(uNxtAccountID)); // Can't just use push implied, because it can't compensate for next account. if (!uNxtCurrencyID // Next is XRP, offer next. Must go through issuer. || uMaxCurrencyID != uNxtCurrencyID // Next is different currency, offer next... || uMaxIssuerID != uNxtAccountID) // Next is not implied issuer { -cLog(lsDEBUG) << boost::str(boost::format("PathState: sender implied: account=%s currency=%s issuer=%s") - % RippleAddress::createHumanAccountID(uMaxIssuerID) - % RippleAddress::createHumanAccountID(uMaxCurrencyID) - % RippleAddress::createHumanAccountID(uMaxIssuerID)); + cLog(lsDEBUG) << boost::str(boost::format("PathState: sender implied: account=%s currency=%s issuer=%s") + % RippleAddress::createHumanAccountID(uMaxIssuerID) + % RippleAddress::createHumanAccountID(uMaxCurrencyID) + % RippleAddress::createHumanAccountID(uMaxIssuerID)); // Add account implied by SendMax. terStatus = pushNode( !!uMaxCurrencyID @@ -375,7 +375,7 @@ cLog(lsDEBUG) << boost::str(boost::format("PathState: sender implied: account=%s { if (tesSUCCESS == terStatus) { -cLog(lsDEBUG) << boost::str(boost::format("PathState: element in path:")); + cLog(lsDEBUG) << boost::str(boost::format("PathState: element in path:")); terStatus = pushNode(speElement.getNodeType(), speElement.getAccountID(), speElement.getCurrency(), speElement.getIssuerID()); } } @@ -389,10 +389,10 @@ cLog(lsDEBUG) << boost::str(boost::format("PathState: element in path:")); || pnPrv.uAccountID != uOutIssuerID)) // Need the implied issuer. { // Add implied account. -cLog(lsDEBUG) << boost::str(boost::format("PathState: receiver implied: account=%s currency=%s issuer=%s") - % RippleAddress::createHumanAccountID(uOutIssuerID) - % RippleAddress::createHumanAccountID(uOutCurrencyID) - % RippleAddress::createHumanAccountID(uOutIssuerID)); + cLog(lsDEBUG) << boost::str(boost::format("PathState: receiver implied: account=%s currency=%s issuer=%s") + % RippleAddress::createHumanAccountID(uOutIssuerID) + % RippleAddress::createHumanAccountID(uOutCurrencyID) + % RippleAddress::createHumanAccountID(uOutIssuerID)); terStatus = pushNode( !!uOutCurrencyID ? STPathElement::typeAccount | STPathElement::typeCurrency | STPathElement::typeIssuer @@ -2495,9 +2495,9 @@ int iIndex = 0; pspExpanded->setExpanded(lesActive, spPath, uDstAccountID, uSrcAccountID); -cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Build path: %d: status: %s") - % ++iIndex - % transToken(pspExpanded->terStatus)); + cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Build path: %d: status: %s") + % ++iIndex + % transToken(pspExpanded->terStatus)); // Return, if the path specification was malformed. if (isTemMalformed(pspExpanded->terStatus)) @@ -2551,17 +2551,18 @@ int iPass = 0; pspCur->saOutAct = saDstAmountAct; rc.pathNext(pspCur, bMultiQuality, 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)); + 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) { // Path was dry. ++iDry; } - else { + else + { tLog(!pspCur->saInPass || !pspCur->saOutPass, lsDEBUG) << boost::str(boost::format("rippleCalc: better: uQuality=%s saInPass=%s saOutPass=%s") % STAmount::saFromRate(pspCur->uQuality) @@ -2587,15 +2588,18 @@ int iPass = 0; } } } -cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Summary: Pass: %d Dry: %d Paths: %d") % ++iPass % iDry % vpsExpanded.size()); - BOOST_FOREACH(PathState::ref pspCur, vpsExpanded) + if (sLog(lsDEBUG)) { -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); + cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Summary: Pass: %d Dry: %d Paths: %d") % ++iPass % iDry % vpsExpanded.size()); + BOOST_FOREACH(PathState::ref pspCur, vpsExpanded) + { + 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) From efdda7c11970339ce8bb056bc14045b9ace5ee41 Mon Sep 17 00:00:00 2001 From: Stefan Thomas Date: Mon, 4 Mar 2013 14:44:26 +0100 Subject: [PATCH 52/56] JS: Allow Ripple URI format (25/USD) in parse_json. --- src/js/amount.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/js/amount.js b/src/js/amount.js index a1f0aaf6a3..9af793f2b7 100644 --- a/src/js/amount.js +++ b/src/js/amount.js @@ -613,11 +613,15 @@ Amount.prototype.parse_issuer = function (issuer) { Amount.prototype.parse_json = function (j) { if ('string' === typeof j) { // .../.../... notation is not a wire format. But allowed for easier testing. - var m = j.match(/^(.+)\/(...)\/(.+)$/); + var m = j.match(/^(.+)\/(...)(?:\/(.+))?$/); if (m) { this._currency = Currency.from_json(m[2]); - this._issuer = UInt160.from_json(m[3]); + if (m[3]) { + this._issuer = UInt160.from_json(m[3]); + } else { + this._issuer = UInt160.from_json('1'); + } this.parse_value(m[1]); } else { From 2a73ec7f22bc1c062dde33bb55d74e84041cb0f7 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Mon, 4 Mar 2013 10:38:12 -0800 Subject: [PATCH 53/56] A structure for accepted ledgers that will allow us to do things like traverse the transactions in applied order and get the affected accounts without rebuilding things several times. --- src/cpp/ripple/AcceptedLedger.cpp | 35 +++++++++++++++++++++ src/cpp/ripple/AcceptedLedger.h | 52 +++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 src/cpp/ripple/AcceptedLedger.cpp create mode 100644 src/cpp/ripple/AcceptedLedger.h diff --git a/src/cpp/ripple/AcceptedLedger.cpp b/src/cpp/ripple/AcceptedLedger.cpp new file mode 100644 index 0000000000..9fdbf1fb7d --- /dev/null +++ b/src/cpp/ripple/AcceptedLedger.cpp @@ -0,0 +1,35 @@ +#include "AcceptedLedger.h" + +ALTransaction::ALTransaction(uint32 seq, SerializerIterator& sit) +{ + Serializer txnSer(sit.getVL()); + SerializerIterator txnIt(txnSer); + + mTxn = boost::make_shared(boost::ref(txnIt)); + mMeta = boost::make_shared(mTxn->getTransactionID(), seq, sit.getVL()); + mAffected = mMeta->getAffectedAccounts(); +} + +AcceptedLedger::AcceptedLedger(Ledger::ref ledger) : mLedger(ledger) +{ + SHAMap& txSet = *ledger->peekTransactionMap(); + for (SHAMapItem::pointer item = txSet.peekFirstItem(); !!item; item = txSet.peekNextItem(item->getTag())) + { + SerializerIterator sit(item->peekSerializer()); + insert(ALTransaction(ledger->getLedgerSeq(), sit)); + } +} + +void AcceptedLedger::insert(const ALTransaction& at) +{ + assert(mMap.find(at.getIndex()) == mMap.end()); + mMap.insert(std::make_pair(at.getIndex(), at)); +} + +const ALTransaction* AcceptedLedger::getTxn(int i) const +{ + map_t::const_iterator it = mMap.find(i); + if (it == mMap.end()) + return NULL; + return &it->second; +} \ No newline at end of file diff --git a/src/cpp/ripple/AcceptedLedger.h b/src/cpp/ripple/AcceptedLedger.h new file mode 100644 index 0000000000..59ba4425d0 --- /dev/null +++ b/src/cpp/ripple/AcceptedLedger.h @@ -0,0 +1,52 @@ +#ifndef ACCEPTED_LEDGER__H +#define ACCEPTED_LEDGER__H + +#include "SerializedTransaction.h" +#include "TransactionMeta.h" +#include "Ledger.h" + + +class ALTransaction +{ +protected: + SerializedTransaction::pointer mTxn; + TransactionMetaSet::pointer mMeta; + std::vector mAffected; + +public: + + ALTransaction(uint32 ledgerSeq, SerializerIterator& sit); + + SerializedTransaction::ref getTxn() const { return mTxn; } + TransactionMetaSet::ref getMeta() const { return mMeta; } + const std::vector& getAffected() const { return mAffected; } + int getIndex() const { return mMeta->getIndex(); } + TER getResult() const { return mMeta->getResultTER(); } +}; + +class AcceptedLedger +{ +public: + typedef std::map map_t; + typedef map_t::value_type value_type; + typedef map_t::const_iterator const_iterator; + +protected: + Ledger::pointer mLedger; + map_t mMap; + + void insert(const ALTransaction&); + +public: + AcceptedLedger(Ledger::ref ledger); + + Ledger::ref getLedger() const { return mLedger; } + const map_t& getMap() const { return mMap; } + + int getLedgerSeq() const { return mLedger->getLedgerSeq(); } + int getTxnCount() const { return mMap.size(); } + + const ALTransaction* getTxn(int) const; +}; + +#endif From 3a3f38927fcc1ea1883b14d2b1dc86ae91c7b135 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Mon, 4 Mar 2013 13:50:20 -0800 Subject: [PATCH 54/56] JS: Backout some of latest amount parsing changes. --- src/js/amount.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/js/amount.js b/src/js/amount.js index 9af793f2b7..da2e289cd4 100644 --- a/src/js/amount.js +++ b/src/js/amount.js @@ -613,7 +613,8 @@ Amount.prototype.parse_issuer = function (issuer) { Amount.prototype.parse_json = function (j) { if ('string' === typeof j) { // .../.../... notation is not a wire format. But allowed for easier testing. - var m = j.match(/^(.+)\/(...)(?:\/(.+))?$/); +// var m = j.match(/^(.+)\/(...)(?:\/(.+))?$/); + var m = j.match(/^(.+)\/(...)\/(.+)$/); if (m) { this._currency = Currency.from_json(m[2]); From dd398b7eb9b200ee36ecccaab27b4b7af5c88e33 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Mon, 4 Mar 2013 13:56:40 -0800 Subject: [PATCH 55/56] Make sure finally negative offers are not processed. --- src/cpp/ripple/OfferCreateTransactor.cpp | 15 +++++++++++++-- src/cpp/ripple/TransactionErr.cpp | 2 ++ src/cpp/ripple/TransactionErr.h | 2 ++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/cpp/ripple/OfferCreateTransactor.cpp b/src/cpp/ripple/OfferCreateTransactor.cpp index e4b59be8c5..8155956f26 100644 --- a/src/cpp/ripple/OfferCreateTransactor.cpp +++ b/src/cpp/ripple/OfferCreateTransactor.cpp @@ -458,8 +458,19 @@ TER OfferCreateTransactor::doApply() // cLog(lsWARNING) << "OfferCreate: takeOffers: uPaysIssuerID=" << RippleAddress::createHumanAccountID(uPaysIssuerID); // cLog(lsWARNING) << "OfferCreate: takeOffers: uGetsIssuerID=" << RippleAddress::createHumanAccountID(uGetsIssuerID); - if (tesSUCCESS != terResult - || !saTakerPays // Wants nothing more. + if (tesSUCCESS != terResult) + { + // Fail as is. + nothing(); + } + else if (saTakerPays.isNegative() || saTakerGets.isNegative()) + { + terResult = isSetBit(mParams, tapOPEN_LEDGER) + ? telFAILED_PROCESSING // Ledger is not final, can vote no. + : tecFAILED_PROCESSING; + } + else if ( + !saTakerPays // Wants nothing more. || !saTakerGets // Offering nothing more. || bMarket // Do not persist. || !mEngine->getNodes().accountFunds(mTxnAccountID, saTakerGets).isPositive() // Not funded. diff --git a/src/cpp/ripple/TransactionErr.cpp b/src/cpp/ripple/TransactionErr.cpp index 3ffe6d639a..a605a2f49a 100644 --- a/src/cpp/ripple/TransactionErr.cpp +++ b/src/cpp/ripple/TransactionErr.cpp @@ -10,6 +10,7 @@ bool transResultInfo(TER terCode, std::string& strToken, std::string& strHuman) } transResultInfoA[] = { { tecCLAIM, "tecCLAIM", "Fee claimed. Sequence used. No action." }, { tecDIR_FULL, "tecDIR_FULL", "Can not add entry to full directory." }, + { tecFAILED_PROCESSING, "tecFAILED_PROCESSING", "Failed to correctly process transaction." }, { tecINSUF_RESERVE_LINE, "tecINSUF_RESERVE_LINE", "Insufficent reserve to add trust line." }, { tecINSUF_RESERVE_OFFER, "tecINSUF_RESERVE_OFFER", "Insufficent reserve to create offer." }, { tecNO_DST, "tecNO_DST", "Destination does not exist. Send XRP to create it." }, @@ -42,6 +43,7 @@ bool transResultInfo(TER terCode, std::string& strToken, std::string& strHuman) { telBAD_DOMAIN, "telBAD_DOMAIN", "Domain too long." }, { telBAD_PATH_COUNT, "telBAD_PATH_COUNT", "Malformed: Too many paths." }, { telBAD_PUBLIC_KEY, "telBAD_PUBLIC_KEY", "Public key too long." }, + { telFAILED_PROCESSING, "telFAILED_PROCESSING", "Failed to correctly process transaction." }, { telINSUF_FEE_P, "telINSUF_FEE_P", "Fee insufficient." }, { telNO_DST_PARTIAL, "telNO_DST_PARTIAL", "Partial payment to create account not allowed." }, diff --git a/src/cpp/ripple/TransactionErr.h b/src/cpp/ripple/TransactionErr.h index 97f33f0918..a1d9fb6909 100644 --- a/src/cpp/ripple/TransactionErr.h +++ b/src/cpp/ripple/TransactionErr.h @@ -16,6 +16,7 @@ enum TER // aka TransactionEngineResult telBAD_DOMAIN, telBAD_PATH_COUNT, telBAD_PUBLIC_KEY, + telFAILED_PROCESSING, telINSUF_FEE_P, telNO_DST_PARTIAL, @@ -121,6 +122,7 @@ enum TER // aka TransactionEngineResult tecUNFUNDED_ADD = 102, tecUNFUNDED_OFFER = 103, tecUNFUNDED_PAYMENT = 104, + tecFAILED_PROCESSING = 105, tecDIR_FULL = 121, tecINSUF_RESERVE_LINE = 122, tecINSUF_RESERVE_OFFER = 123, From 51db2d2cd701334d4e071a62545466e4833c0511 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Mon, 4 Mar 2013 13:59:53 -0800 Subject: [PATCH 56/56] Reprocess accepted ledgers once. Track metadata, transaction data, and affected accounts. Process to SQL database and publish from this structure. Include number of transactions in ledger publish info. Publish transactions in applied order. --- newcoin.vcxproj | 2 ++ newcoin.vcxproj.filters | 6 ++++ ripple2010.vcxproj | 1 + ripple2010.vcxproj.filters | 3 ++ src/cpp/ripple/AcceptedLedger.cpp | 60 +++++++++++++++++++++++++++++-- src/cpp/ripple/AcceptedLedger.h | 30 ++++++++++++---- src/cpp/ripple/Application.cpp | 2 ++ src/cpp/ripple/Ledger.cpp | 38 ++++++++------------ src/cpp/ripple/LedgerMaster.cpp | 8 ++--- src/cpp/ripple/LedgerMaster.h | 2 +- src/cpp/ripple/NetworkOPs.cpp | 60 ++++++++++++++----------------- src/cpp/ripple/NetworkOPs.h | 7 ++-- src/cpp/ripple/OrderBookDB.cpp | 7 ++-- src/cpp/ripple/OrderBookDB.h | 3 +- 14 files changed, 152 insertions(+), 77 deletions(-) diff --git a/newcoin.vcxproj b/newcoin.vcxproj index 72e758a479..bf9067b2a4 100644 --- a/newcoin.vcxproj +++ b/newcoin.vcxproj @@ -96,6 +96,7 @@ + @@ -205,6 +206,7 @@ + diff --git a/newcoin.vcxproj.filters b/newcoin.vcxproj.filters index 365cb71098..98c8ac4639 100644 --- a/newcoin.vcxproj.filters +++ b/newcoin.vcxproj.filters @@ -345,6 +345,9 @@ Source Files + + Source Files + Source Files @@ -662,6 +665,9 @@ Header Files + + Header Files + Header Files diff --git a/ripple2010.vcxproj b/ripple2010.vcxproj index 18e3ea1d24..dbb3dd84b1 100644 --- a/ripple2010.vcxproj +++ b/ripple2010.vcxproj @@ -94,6 +94,7 @@ + diff --git a/ripple2010.vcxproj.filters b/ripple2010.vcxproj.filters index 77ef08370b..6ad621db5f 100644 --- a/ripple2010.vcxproj.filters +++ b/ripple2010.vcxproj.filters @@ -345,6 +345,9 @@ Source Files + + Source Files + Source Files diff --git a/src/cpp/ripple/AcceptedLedger.cpp b/src/cpp/ripple/AcceptedLedger.cpp index 9fdbf1fb7d..0043d332d5 100644 --- a/src/cpp/ripple/AcceptedLedger.cpp +++ b/src/cpp/ripple/AcceptedLedger.cpp @@ -1,13 +1,59 @@ #include "AcceptedLedger.h" +#include + +TaggedCache AcceptedLedger::ALCache("AcceptedLedger", 4, 60); + ALTransaction::ALTransaction(uint32 seq, SerializerIterator& sit) { Serializer txnSer(sit.getVL()); SerializerIterator txnIt(txnSer); mTxn = boost::make_shared(boost::ref(txnIt)); - mMeta = boost::make_shared(mTxn->getTransactionID(), seq, sit.getVL()); + mRawMeta= sit.getVL(); + mMeta = boost::make_shared(mTxn->getTransactionID(), seq, mRawMeta); mAffected = mMeta->getAffectedAccounts(); + mResult = mMeta->getResultTER(); +} + +ALTransaction::ALTransaction(SerializedTransaction::ref txn, TransactionMetaSet::ref met) : + mTxn(txn), mMeta(met), mAffected(met->getAffectedAccounts()) +{ + mResult = mMeta->getResultTER(); +} + +ALTransaction::ALTransaction(SerializedTransaction::ref txn, TER result) : + mTxn(txn), mResult(result), mAffected(txn->getMentionedAccounts()) +{ ; } + +std::string ALTransaction::getEscMeta() const +{ + assert(!mRawMeta.empty()); + return sqlEscape(mRawMeta); +} + +Json::Value ALTransaction::getJson(int j) const +{ + Json::Value ret(Json::objectValue); + ret["transaction"] = mTxn->getJson(j); + if (mMeta) + { + ret["meta"] = mMeta->getJson(j); + ret["raw_meta"] = strHex(mRawMeta); + } + ret["result"] = transHuman(mResult); + + if (!mAffected.empty()) + { + Json::Value affected(Json::arrayValue); + BOOST_FOREACH(const RippleAddress& ra, mAffected) + { + affected.append(ra.humanAccountID()); + } + ret["affected"] = affected; + } + + return ret; } AcceptedLedger::AcceptedLedger(Ledger::ref ledger) : mLedger(ledger) @@ -20,6 +66,16 @@ AcceptedLedger::AcceptedLedger(Ledger::ref ledger) : mLedger(ledger) } } +AcceptedLedger::pointer AcceptedLedger::makeAcceptedLedger(Ledger::ref ledger) +{ + AcceptedLedger::pointer ret = ALCache.fetch(ledger->getHash()); + if (ret) + return ret; + ret = AcceptedLedger::pointer(new AcceptedLedger(ledger)); + ALCache.canonicalize(ledger->getHash(), ret); + return ret; +} + void AcceptedLedger::insert(const ALTransaction& at) { assert(mMap.find(at.getIndex()) == mMap.end()); @@ -32,4 +88,4 @@ const ALTransaction* AcceptedLedger::getTxn(int i) const if (it == mMap.end()) return NULL; return &it->second; -} \ No newline at end of file +} diff --git a/src/cpp/ripple/AcceptedLedger.h b/src/cpp/ripple/AcceptedLedger.h index 59ba4425d0..e3237cd238 100644 --- a/src/cpp/ripple/AcceptedLedger.h +++ b/src/cpp/ripple/AcceptedLedger.h @@ -11,25 +11,38 @@ class ALTransaction protected: SerializedTransaction::pointer mTxn; TransactionMetaSet::pointer mMeta; + TER mResult; std::vector mAffected; + std::vector mRawMeta; public: ALTransaction(uint32 ledgerSeq, SerializerIterator& sit); + ALTransaction(SerializedTransaction::ref, TransactionMetaSet::ref); + ALTransaction(SerializedTransaction::ref, TER result); SerializedTransaction::ref getTxn() const { return mTxn; } TransactionMetaSet::ref getMeta() const { return mMeta; } const std::vector& getAffected() const { return mAffected; } - int getIndex() const { return mMeta->getIndex(); } - TER getResult() const { return mMeta->getResultTER(); } + + uint256 getTransactionID() const { return mTxn->getTransactionID(); } + TransactionType getTxnType() const { return mTxn->getTxnType(); } + TER getResult() const { return mResult; } + + bool isApplied() const { return !!mMeta; } + int getIndex() const { return mMeta ? mMeta->getIndex() : 0; } + std::string getEscMeta() const; + Json::Value getJson(int) const; }; class AcceptedLedger { public: - typedef std::map map_t; - typedef map_t::value_type value_type; - typedef map_t::const_iterator const_iterator; + typedef boost::shared_ptr pointer; + typedef const pointer& ret; + typedef std::map map_t; + typedef map_t::value_type value_type; + typedef map_t::const_iterator const_iterator; protected: Ledger::pointer mLedger; @@ -37,9 +50,14 @@ protected: void insert(const ALTransaction&); -public: + static TaggedCache ALCache; AcceptedLedger(Ledger::ref ledger); +public: + + static pointer makeAcceptedLedger(Ledger::ref ledger); + static void sweep() { ALCache.sweep(); } + Ledger::ref getLedger() const { return mLedger; } const map_t& getMap() const { return mMap; } diff --git a/src/cpp/ripple/Application.cpp b/src/cpp/ripple/Application.cpp index fac0ce76a6..1d6773dcae 100644 --- a/src/cpp/ripple/Application.cpp +++ b/src/cpp/ripple/Application.cpp @@ -1,5 +1,6 @@ #include "Application.h" +#include "AcceptedLedger.h" #include "Config.h" #include "PeerDoor.h" #include "RPCDoor.h" @@ -303,6 +304,7 @@ void Application::sweep() mValidations.sweep(); getMasterLedgerAcquire().sweep(); mSLECache.sweep(); + AcceptedLedger::sweep(); mSweepTimer.expires_from_now(boost::posix_time::seconds(theConfig.getSize(siSweepInterval))); mSweepTimer.async_wait(boost::bind(&Application::sweep, this)); } diff --git a/src/cpp/ripple/Ledger.cpp b/src/cpp/ripple/Ledger.cpp index e34b6d678a..3937964eb7 100644 --- a/src/cpp/ripple/Ledger.cpp +++ b/src/cpp/ripple/Ledger.cpp @@ -428,6 +428,8 @@ void Ledger::saveAcceptedLedger(bool fromConsensus, LoadEvent::pointer event) addRaw(s); theApp->getHashedObjectStore().store(hotLEDGER, mLedgerSeq, s.peekData(), mHash); + AcceptedLedger::pointer aLedger = AcceptedLedger::makeAcceptedLedger(shared_from_this()); + { { ScopedLock sl(theApp->getLedgerDB()->getDBLock()); @@ -435,33 +437,22 @@ void Ledger::saveAcceptedLedger(bool fromConsensus, LoadEvent::pointer event) theApp->getLedgerDB()->getDB()->executeSQL(boost::str(deleteLedger % mLedgerSeq)); } - SHAMap& txSet = *peekTransactionMap(); Database *db = theApp->getTxnDB()->getDB(); { ScopedLock dbLock(theApp->getTxnDB()->getDBLock()); db->executeSQL("BEGIN TRANSACTION;"); - SHAMapTreeNode::TNType type; - for (SHAMapItem::pointer item = txSet.peekFirstItem(type); !!item; - item = txSet.peekNextItem(item->getTag(), type)) + + BOOST_FOREACH(const AcceptedLedger::value_type& vt, aLedger->getMap()) { - assert(type == SHAMapTreeNode::tnTRANSACTION_MD); - SerializerIterator sit(item->peekSerializer()); - Serializer rawTxn(sit.getVL()); - Serializer rawMeta(sit.getVL()); - std::string escMeta(sqlEscape(rawMeta.peekData())); - - SerializerIterator txnIt(rawTxn); - - SerializedTransaction txn(txnIt); - assert(txn.getTransactionID() == item->getTag()); - TransactionMetaSet meta(item->getTag(), mLedgerSeq, rawMeta.peekData()); - theApp->getMasterTransaction().inLedger(item->getTag(), mLedgerSeq); + cLog(lsTRACE) << "Saving: " << vt.second.getJson(0); + uint256 txID = vt.second.getTransactionID(); + theApp->getMasterTransaction().inLedger(txID, mLedgerSeq); // Make sure transaction is in AccountTransactions. - if (!SQL_EXISTS(db, boost::str(AcctTransExists % item->getTag().GetHex()))) + if (!SQL_EXISTS(db, boost::str(AcctTransExists % txID.GetHex()))) { // Transaction not in AccountTransactions - const std::vector accts = meta.getAffectedAccounts(); + const std::vector& accts = vt.second.getAffected(); if (!accts.empty()) { @@ -476,7 +467,7 @@ void Ledger::saveAcceptedLedger(bool fromConsensus, LoadEvent::pointer event) sql += "('"; first = false; } - sql += txn.getTransactionID().GetHex(); + sql += txID.GetHex(); sql += "','"; sql += it->humanAccountID(); sql += "',"; @@ -491,20 +482,21 @@ void Ledger::saveAcceptedLedger(bool fromConsensus, LoadEvent::pointer event) cLog(lsWARNING) << "Transaction in ledger " << mLedgerSeq << " affects no accounts"; } - if (SQL_EXISTS(db, boost::str(transExists % txn.getTransactionID().GetHex()))) + if (SQL_EXISTS(db, boost::str(transExists % txID.GetHex()))) { // In Transactions, update LedgerSeq, metadata and Status. db->executeSQL(boost::str(updateTx % getLedgerSeq() % TXN_SQL_VALIDATED - % escMeta - % txn.getTransactionID().GetHex())); + % vt.second.getEscMeta() + % txID.GetHex())); } else { // Not in Transactions, insert the whole thing.. db->executeSQL( - txn.getMetaSQLInsertHeader() + txn.getMetaSQL(getLedgerSeq(), escMeta) + ";"); + SerializedTransaction::getMetaSQLInsertHeader() + + vt.second.getTxn()->getMetaSQL(getLedgerSeq(), vt.second.getEscMeta()) + ";"); } } db->executeSQL("COMMIT TRANSACTION;"); diff --git a/src/cpp/ripple/LedgerMaster.cpp b/src/cpp/ripple/LedgerMaster.cpp index ab87238da1..fcafae0cb3 100644 --- a/src/cpp/ripple/LedgerMaster.cpp +++ b/src/cpp/ripple/LedgerMaster.cpp @@ -119,11 +119,11 @@ Ledger::pointer LedgerMaster::closeLedger(bool recover) return closingLedger; } -TER LedgerMaster::doTransaction(const SerializedTransaction& txn, TransactionEngineParams params, bool& didApply) +TER LedgerMaster::doTransaction(SerializedTransaction::ref txn, TransactionEngineParams params, bool& didApply) { - TER result = mEngine.applyTransaction(txn, params, didApply); - // CHECKME: Should we call this even on gross failures? - theApp->getOPs().pubProposedTransaction(mEngine.getLedger(), txn, result); + TER result = mEngine.applyTransaction(*txn, params, didApply); +// if (didApply) + theApp->getOPs().pubProposedTransaction(mEngine.getLedger(), txn, result); return result; } diff --git a/src/cpp/ripple/LedgerMaster.h b/src/cpp/ripple/LedgerMaster.h index d14ab11e43..c79c7e4702 100644 --- a/src/cpp/ripple/LedgerMaster.h +++ b/src/cpp/ripple/LedgerMaster.h @@ -75,7 +75,7 @@ public: // The published ledger is the last fully validated ledger Ledger::ref getValidatedLedger() { return mPubLedger; } - TER doTransaction(const SerializedTransaction& txn, TransactionEngineParams params, bool& didApply); + TER doTransaction(SerializedTransaction::ref txn, TransactionEngineParams params, bool& didApply); void pushLedger(Ledger::ref newLedger); void pushLedger(Ledger::ref newLCL, Ledger::ref newOL, bool fromConsensus); diff --git a/src/cpp/ripple/NetworkOPs.cpp b/src/cpp/ripple/NetworkOPs.cpp index a3106768d0..93b87b0869 100644 --- a/src/cpp/ripple/NetworkOPs.cpp +++ b/src/cpp/ripple/NetworkOPs.cpp @@ -267,7 +267,7 @@ void NetworkOPs::runTransactionQueue() assert(dbtx); bool didApply; - TER r = mLedgerMaster->doTransaction(*dbtx->getSTransaction(), + TER r = mLedgerMaster->doTransaction(dbtx->getSTransaction(), tapOPEN_LEDGER | tapNO_CHECK_SIGN, didApply); dbtx->setResult(r); @@ -352,7 +352,7 @@ Transaction::pointer NetworkOPs::processTransaction(Transaction::pointer trans, boost::recursive_mutex::scoped_lock sl(theApp->getMasterLock()); Transaction::pointer dbtx = theApp->getMasterTransaction().fetch(trans->getID(), true); bool didApply; - TER r = mLedgerMaster->doTransaction(*trans->getSTransaction(), tapOPEN_LEDGER | tapNO_CHECK_SIGN, didApply); + TER r = mLedgerMaster->doTransaction(trans->getSTransaction(), tapOPEN_LEDGER | tapNO_CHECK_SIGN, didApply); trans->setResult(r); if (isTemMalformed(r)) // malformed, cache bad @@ -1289,9 +1289,9 @@ Json::Value NetworkOPs::pubBootstrapAccountInfo(Ledger::ref lpAccepted, const Ri return jvObj; } -void NetworkOPs::pubProposedTransaction(Ledger::ref lpCurrent, const SerializedTransaction& stTxn, TER terResult) +void NetworkOPs::pubProposedTransaction(Ledger::ref lpCurrent, SerializedTransaction::ref stTxn, TER terResult) { - Json::Value jvObj = transJson(stTxn, terResult, false, lpCurrent, "transaction"); + Json::Value jvObj = transJson(*stTxn, terResult, false, lpCurrent, "transaction"); { boost::recursive_mutex::scoped_lock sl(mMonitorLock); @@ -1308,15 +1308,19 @@ void NetworkOPs::pubProposedTransaction(Ledger::ref lpCurrent, const SerializedT it = mSubRTTransactions.erase(it); } } - TransactionMetaSet::pointer ret; - pubAccountTransaction(lpCurrent,stTxn,terResult,false,ret); + ALTransaction alt(stTxn, terResult); + cLog(lsTRACE) << "pubProposed: " << alt.getJson(0); + pubAccountTransaction(lpCurrent, ALTransaction(stTxn, terResult)); } -void NetworkOPs::pubLedger(Ledger::ref lpAccepted) +void NetworkOPs::pubLedger(Ledger::ref accepted) { // Ledgers are published only when they acquire sufficient validations // Holes are filled across connection loss or other catastrophe + AcceptedLedger::pointer alpAccepted = AcceptedLedger::makeAcceptedLedger(accepted); + Ledger::ref lpAccepted = alpAccepted->getLedger(); + { boost::recursive_mutex::scoped_lock sl(mMonitorLock); @@ -1334,6 +1338,8 @@ void NetworkOPs::pubLedger(Ledger::ref lpAccepted) jvObj["reserve_base"] = Json::UInt(lpAccepted->getReserve(0)); jvObj["reserve_inc"] = Json::UInt(lpAccepted->getReserveInc()); + jvObj["txn_count"] = Json::UInt(alpAccepted->getTxnCount()); + NetworkOPs::subMapType::const_iterator it = mSubLedger.begin(); while (it != mSubLedger.end()) { @@ -1352,21 +1358,10 @@ void NetworkOPs::pubLedger(Ledger::ref lpAccepted) // Don't lock since pubAcceptedTransaction is locking. if (!mSubTransactions.empty() || !mSubRTTransactions.empty() || !mSubAccount.empty() || !mSubRTAccount.empty()) { - SHAMap& txSet = *lpAccepted->peekTransactionMap(); - - for (SHAMapItem::pointer item = txSet.peekFirstItem(); !!item; item = txSet.peekNextItem(item->getTag())) + BOOST_FOREACH(const AcceptedLedger::value_type& vt, alpAccepted->getMap()) { - SerializerIterator it(item->peekSerializer()); - - // OPTIMIZEME: Could get transaction from txn master, but still must call getVL - Serializer txnSer(it.getVL()); - SerializerIterator txnIt(txnSer); - SerializedTransaction stTxn(txnIt); - - TransactionMetaSet::pointer meta = boost::make_shared( - stTxn.getTransactionID(), lpAccepted->getLedgerSeq(), it.getVL()); - - pubAcceptedTransaction(lpAccepted, stTxn, meta->getResultTER(), meta); + cLog(lsTRACE) << "pubAccepted: " << vt.second.getJson(0); + pubAcceptedTransaction(lpAccepted, vt.second); } } } @@ -1407,11 +1402,10 @@ Json::Value NetworkOPs::transJson(const SerializedTransaction& stTxn, TER terRes return jvObj; } -void NetworkOPs::pubAcceptedTransaction(Ledger::ref lpCurrent, const SerializedTransaction& stTxn, TER terResult,TransactionMetaSet::pointer& meta) +void NetworkOPs::pubAcceptedTransaction(Ledger::ref alAccepted, const ALTransaction& alTx) { - Json::Value jvObj = transJson(stTxn, terResult, true, lpCurrent, "transaction"); - - if (meta) jvObj["meta"] = meta->getJson(0); + Json::Value jvObj = transJson(*alTx.getTxn(), alTx.getResult(), true, alAccepted, "transaction"); + jvObj["meta"] = alTx.getMeta()->getJson(0); { boost::recursive_mutex::scoped_lock sl(mMonitorLock); @@ -1442,14 +1436,14 @@ void NetworkOPs::pubAcceptedTransaction(Ledger::ref lpCurrent, const SerializedT it = mSubRTTransactions.erase(it); } } - theApp->getOrderBookDB().processTxn(stTxn, terResult, meta, jvObj); - - pubAccountTransaction(lpCurrent, stTxn, terResult, true, meta); + theApp->getOrderBookDB().processTxn(alAccepted, alTx, jvObj); + pubAccountTransaction(alAccepted, alTx); } -void NetworkOPs::pubAccountTransaction(Ledger::ref lpCurrent, const SerializedTransaction& stTxn, TER terResult, bool bAccepted, TransactionMetaSet::pointer& meta) +void NetworkOPs::pubAccountTransaction(Ledger::ref lpCurrent, const ALTransaction& alTx) { boost::unordered_set notify; + bool bAccepted = alTx.isApplied(); int iProposed = 0; int iAccepted = 0; @@ -1460,8 +1454,7 @@ void NetworkOPs::pubAccountTransaction(Ledger::ref lpCurrent, const SerializedTr if (!mSubAccount.empty() || (!mSubRTAccount.empty()) ) { - std::vector accounts = meta ? meta->getAffectedAccounts() : stTxn.getMentionedAccounts(); - BOOST_FOREACH(const RippleAddress& affectedAccount, accounts) + BOOST_FOREACH(const RippleAddress& affectedAccount, alTx.getAffected()) { subInfoMapIterator simiIt = mSubRTAccount.find(affectedAccount.getAccountID()); @@ -1510,9 +1503,10 @@ void NetworkOPs::pubAccountTransaction(Ledger::ref lpCurrent, const SerializedTr if (!notify.empty()) { - Json::Value jvObj = transJson(stTxn, terResult, bAccepted, lpCurrent, "account"); + Json::Value jvObj = transJson(*alTx.getTxn(), alTx.getResult(), bAccepted, lpCurrent, "account"); - if (meta) jvObj["meta"] = meta->getJson(0); + if (alTx.isApplied()) + jvObj["meta"] = alTx.getMeta()->getJson(0); BOOST_FOREACH(InfoSub::ref isrListener, notify) { diff --git a/src/cpp/ripple/NetworkOPs.h b/src/cpp/ripple/NetworkOPs.h index 188543d7c6..01d502ae0e 100644 --- a/src/cpp/ripple/NetworkOPs.h +++ b/src/cpp/ripple/NetworkOPs.h @@ -14,6 +14,7 @@ #include "LedgerAcquire.h" #include "LedgerProposal.h" #include "JobQueue.h" +#include "AcceptedLedger.h" // Operations that clients may wish to perform against the network // Master operational handler, server sequencer, network tracker @@ -140,8 +141,8 @@ protected: Json::Value pubBootstrapAccountInfo(Ledger::ref lpAccepted, const RippleAddress& naAccountID); - void pubAcceptedTransaction(Ledger::ref lpCurrent, const SerializedTransaction& stTxn, TER terResult,TransactionMetaSet::pointer& meta); - void pubAccountTransaction(Ledger::ref lpCurrent, const SerializedTransaction& stTxn, TER terResult,bool accepted,TransactionMetaSet::pointer& meta); + void pubAcceptedTransaction(Ledger::ref alAccepted, const ALTransaction& alTransaction); + void pubAccountTransaction(Ledger::ref lpCurrent, const ALTransaction& alTransaction); void pubServer(); @@ -306,7 +307,7 @@ public: // Monitoring: publisher side // void pubLedger(Ledger::ref lpAccepted); - void pubProposedTransaction(Ledger::ref lpCurrent, const SerializedTransaction& stTxn, TER terResult); + void pubProposedTransaction(Ledger::ref lpCurrent, SerializedTransaction::ref stTxn, TER terResult); // diff --git a/src/cpp/ripple/OrderBookDB.cpp b/src/cpp/ripple/OrderBookDB.cpp index e58757decb..b17c98ab96 100644 --- a/src/cpp/ripple/OrderBookDB.cpp +++ b/src/cpp/ripple/OrderBookDB.cpp @@ -187,15 +187,14 @@ BookListeners::pointer OrderBookDB::getBookListeners(const uint160& currencyIn, */ // Based on the meta, send the meta to the streams that are listening // We need to determine which streams a given meta effects -void OrderBookDB::processTxn(const SerializedTransaction& stTxn, TER terResult, - TransactionMetaSet::pointer& meta, Json::Value& jvObj) +void OrderBookDB::processTxn(Ledger::ref ledger, const ALTransaction& alTx, Json::Value& jvObj) { boost::recursive_mutex::scoped_lock sl(mLock); - if (terResult == tesSUCCESS) + if (alTx.getResult() == tesSUCCESS) { // check if this is an offer or an offer cancel or a payment that consumes an offer //check to see what the meta looks like - BOOST_FOREACH(STObject& node, meta->getNodes()) + BOOST_FOREACH(STObject& node, alTx.getMeta()->getNodes()) { try { diff --git a/src/cpp/ripple/OrderBookDB.h b/src/cpp/ripple/OrderBookDB.h index 768ca05a55..f5119e88bd 100644 --- a/src/cpp/ripple/OrderBookDB.h +++ b/src/cpp/ripple/OrderBookDB.h @@ -6,6 +6,7 @@ #include #include "Ledger.h" +#include "AcceptedLedger.h" #include "OrderBook.h" @@ -66,7 +67,7 @@ public: const uint160& issuerIn, const uint160& issuerOut); // see if this txn effects any orderbook - void processTxn(const SerializedTransaction& stTxn, TER terResult,TransactionMetaSet::pointer& meta,Json::Value& jvObj); + void processTxn(Ledger::ref ledger, const ALTransaction& alTx, Json::Value& jvObj); };