From 2e7a2fd6961b2ebe91bd9ca36f4fbee7bf1f9393 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Mon, 11 Mar 2013 18:58:47 -0700 Subject: [PATCH 1/3] Canonicalize API protocol. --- CHANGELOG | 12 ++++++++++++ src/cpp/ripple/Ledger.cpp | 28 ++++++++++++++++++---------- src/cpp/ripple/RPCHandler.cpp | 14 ++++---------- 3 files changed, 34 insertions(+), 20 deletions(-) create mode 100644 CHANGELOG diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 0000000000..13013402e3 --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,12 @@ +Critical protocol changes +------------------------- + +* date +* The JSON field "metaData" changing to "meta". +* RPC ledger will no longer take "ledger", use "ledger_hash" or "ledger_index". +* "closedLedger" events: +** "hash" DEPRECATED: use "ledger_hash" +** "seqNum" DEPRECATED: use "ledger_index" +** "closeTime" DEPRECATED: use "close" or "close_human" +* stream "rt_accounts" --> "accounts_proposed" +* stream "rt_transactions" --> "transactions_proposed" diff --git a/src/cpp/ripple/Ledger.cpp b/src/cpp/ripple/Ledger.cpp index 1403ea3ae1..0d85bf1579 100644 --- a/src/cpp/ripple/Ledger.cpp +++ b/src/cpp/ripple/Ledger.cpp @@ -837,26 +837,34 @@ Json::Value Ledger::getJson(int options) boost::recursive_mutex::scoped_lock sl(mLock); - ledger["parentHash"] = mParentHash.GetHex(); - ledger["seqNum"] = boost::lexical_cast(mLedgerSeq); + ledger["seqNum"] = boost::lexical_cast(mLedgerSeq); // DEPRECATED + + ledger["parent_hash"] = mParentHash.GetHex(); + ledger["ledger_index"] = boost::lexical_cast(mLedgerSeq); if (mClosed || bFull) { if (mClosed) ledger["closed"] = true; - ledger["hash"] = mHash.GetHex(); - ledger["transactionHash"] = mTransHash.GetHex(); - ledger["accountHash"] = mAccountHash.GetHex(); - ledger["accepted"] = mAccepted; - ledger["totalCoins"] = boost::lexical_cast(mTotCoins); + + ledger["hash"] = mHash.GetHex(); // DEPRECATED + ledger["totalCoins"] = boost::lexical_cast(mTotCoins); // DEPRECATED + + ledger["ledger_hash"] = mHash.GetHex(); + ledger["transaction_hash"] = mTransHash.GetHex(); + ledger["account_hash"] = mAccountHash.GetHex(); + ledger["accepted"] = mAccepted; + ledger["total_coins"] = boost::lexical_cast(mTotCoins); + if (mCloseTime != 0) { if ((mCloseFlags & sLCF_NoConsensusTime) != 0) - ledger["closeTimeEstimate"] = boost::posix_time::to_simple_string(ptFromSeconds(mCloseTime)); + ledger["close_time_estimate"] = boost::posix_time::to_simple_string(ptFromSeconds(mCloseTime)); else { - ledger["closeTime"] = boost::posix_time::to_simple_string(ptFromSeconds(mCloseTime)); - ledger["closeTimeResolution"] = mCloseResolution; + ledger["close_time"] = mCloseTime; + ledger["close_time_human"] = boost::posix_time::to_simple_string(ptFromSeconds(mCloseTime)); + ledger["close_time_resolution"] = mCloseResolution; } } } diff --git a/src/cpp/ripple/RPCHandler.cpp b/src/cpp/ripple/RPCHandler.cpp index 8704235417..782fa80643 100644 --- a/src/cpp/ripple/RPCHandler.cpp +++ b/src/cpp/ripple/RPCHandler.cpp @@ -2617,14 +2617,6 @@ boost::unordered_set RPCHandler::parseAccountIds(const Json::Valu return usnaResult; } -/* -server : Sends a message anytime the server status changes such as network connectivity. -ledger : Sends a message at every ledger close. -transactions : Sends a message for every transaction that makes it into a ledger. -rt_transactions -accounts -rt_accounts -*/ Json::Value RPCHandler::doSubscribe(Json::Value jvRequest, int& cost) { InfoSub::pointer ispSub; @@ -2704,7 +2696,8 @@ Json::Value RPCHandler::doSubscribe(Json::Value jvRequest, int& cost) { mNetOps->subTransactions(ispSub); } - else if (streamName=="rt_transactions") + else if (streamName=="transactions_proposed" + || streamName=="rt_transactions") // DEPRECATED { mNetOps->subRTTransactions(ispSub); } @@ -2720,7 +2713,8 @@ Json::Value RPCHandler::doSubscribe(Json::Value jvRequest, int& cost) } } - if (jvRequest.isMember("rt_accounts")) + if (jvRequest.isMember("accounts_proposed") + || jvRequest.isMember("rt_accounts")) // DEPRECATED { boost::unordered_set usnaAccoundIds = parseAccountIds(jvRequest["rt_accounts"]); From d5c52df10da41c3b8d2b6c77804deb21bfba9e45 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Tue, 12 Mar 2013 11:41:04 -0700 Subject: [PATCH 2/3] Revise url subscribe username and password API --- CHANGELOG | 2 ++ src/cpp/ripple/RPCHandler.cpp | 12 ++++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 13013402e3..f0f1a88550 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -10,3 +10,5 @@ Critical protocol changes ** "closeTime" DEPRECATED: use "close" or "close_human" * stream "rt_accounts" --> "accounts_proposed" * stream "rt_transactions" --> "transactions_proposed" +* subscribe "username" --> "url_username" +* subscribe "password" --> "url_password" diff --git a/src/cpp/ripple/RPCHandler.cpp b/src/cpp/ripple/RPCHandler.cpp index 782fa80643..b543f60728 100644 --- a/src/cpp/ripple/RPCHandler.cpp +++ b/src/cpp/ripple/RPCHandler.cpp @@ -2639,8 +2639,16 @@ Json::Value RPCHandler::doSubscribe(Json::Value jvRequest, int& cost) return rpcError(rpcNO_PERMISSION); std::string strUrl = jvRequest["url"].asString(); - std::string strUsername = jvRequest.isMember("username") ? jvRequest["username"].asString() : ""; - std::string strPassword = jvRequest.isMember("password") ? jvRequest["password"].asString() : ""; + std::string strUsername = jvRequest.isMember("url_username") ? jvRequest["url_username"].asString() : ""; + std::string strPassword = jvRequest.isMember("url_password") ? jvRequest["url_password"].asString() : ""; + + // DEPRICATED + if (jvRequest.isMember("username")) + strUsername = jvRequest["username"].asString(); + + // DEPRICATED + if (jvRequest.isMember("password")) + strPassword = jvRequest["password"].asString(); ispSub = mNetOps->findRpcSub(strUrl); if (!ispSub) From 04a825298ee3d485abf8890e7026d4e7225fb69a Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Tue, 12 Mar 2013 12:53:06 -0700 Subject: [PATCH 3/3] Clean up and secure subscribe books. --- src/cpp/ripple/Ledger.cpp | 11 +++--- src/cpp/ripple/RPCHandler.cpp | 64 ++++++++++++++++++++++------------- 2 files changed, 44 insertions(+), 31 deletions(-) diff --git a/src/cpp/ripple/Ledger.cpp b/src/cpp/ripple/Ledger.cpp index c73a077ff5..2fbf26eb18 100644 --- a/src/cpp/ripple/Ledger.cpp +++ b/src/cpp/ripple/Ledger.cpp @@ -1307,6 +1307,9 @@ std::vector< std::pair > Ledger::getLedgerHashes() return ret; } +// XRP to XRP not allowed. +// Currencies must have appropriate issuer. +// Currencies or accounts must differ. bool Ledger::isValidBook(const uint160& uTakerPaysCurrency, const uint160& uTakerPaysIssuerID, const uint160& uTakerGetsCurrency, const uint160& uTakerGetsIssuerID) { @@ -1346,9 +1349,6 @@ bool Ledger::isValidBook(const uint160& uTakerPaysCurrency, const uint160& uTake uint256 Ledger::getBookBase(const uint160& uTakerPaysCurrency, const uint160& uTakerPaysIssuerID, const uint160& uTakerGetsCurrency, const uint160& uTakerGetsIssuerID) { - bool bInNative = uTakerPaysCurrency.isZero(); - bool bOutNative = uTakerGetsCurrency.isZero(); - Serializer s(82); s.add16(spaceBookDir); // 2 @@ -1366,10 +1366,7 @@ uint256 Ledger::getBookBase(const uint160& uTakerPaysCurrency, const uint160& uT % RippleAddress::createHumanAccountID(uTakerGetsIssuerID) % uBaseIndex.ToString()); - assert(!bInNative || !bOutNative); // XRP to XRP not allowed. - assert(bInNative == uTakerPaysIssuerID.isZero()); // Make sure issuer is specified as needed. - assert(bOutNative == uTakerGetsIssuerID.isZero()); // Make sure issuer is specified as needed. - assert(uTakerPaysCurrency != uTakerGetsCurrency || uTakerPaysIssuerID != uTakerGetsIssuerID); // Currencies or accounts must differ. + assert(isValidBook(uTakerPaysCurrency, uTakerPaysIssuerID, uTakerGetsCurrency, uTakerGetsIssuerID)); return uBaseIndex; } diff --git a/src/cpp/ripple/RPCHandler.cpp b/src/cpp/ripple/RPCHandler.cpp index 39b5cdc11f..1640285c3f 100644 --- a/src/cpp/ripple/RPCHandler.cpp +++ b/src/cpp/ripple/RPCHandler.cpp @@ -2642,11 +2642,11 @@ Json::Value RPCHandler::doSubscribe(Json::Value jvRequest, int& cost) std::string strUsername = jvRequest.isMember("url_username") ? jvRequest["url_username"].asString() : ""; std::string strPassword = jvRequest.isMember("url_password") ? jvRequest["url_password"].asString() : ""; - // DEPRICATED + // DEPRECATED if (jvRequest.isMember("username")) strUsername = jvRequest["username"].asString(); - // DEPRICATED + // DEPRECATED if (jvRequest.isMember("password")) strPassword = jvRequest["password"].asString(); @@ -2753,12 +2753,25 @@ Json::Value RPCHandler::doSubscribe(Json::Value jvRequest, int& cost) } if (jvRequest.isMember("books")) - { // FIXME: This can crash the server if the parameters to things like getBookPage are invalid + { for (Json::Value::iterator it = jvRequest["books"].begin(); it != jvRequest["books"].end(); it++) { - uint160 uTakerPaysCurrencyID; - uint160 uTakerPaysIssuerID; - Json::Value jvTakerPays = (*it)["taker_pays"]; + Json::Value& jvSubRequest = *it; + uint160 uTakerPaysCurrencyID; + uint160 uTakerPaysIssuerID; + uint160 uTakerGetsCurrencyID; + uint160 uTakerGetsIssuerID; + bool bBoth = (jvSubRequest.isMember("both") && jvSubRequest["both"].asBool()) + || (jvSubRequest.isMember("both_sides") && jvSubRequest["both_sides"].asBool()); // DEPRECATED + bool bSnapshot = (jvSubRequest.isMember("snapshot") && jvSubRequest["snapshot"].asBool()) + || (jvSubRequest.isMember("start_now") && jvSubRequest["start_now"].asBool()); // DEPRECATED + + + if (!jvSubRequest.isMember("taker_pays") || !jvSubRequest.isMember("taker_gets")) + return rpcError(rpcINVALID_PARAMS); + + Json::Value jvTakerPays = jvSubRequest["taker_pays"]; + Json::Value jvTakerGets = jvSubRequest["taker_gets"]; // Parse mandatory currency. if (!jvTakerPays.isMember("currency") @@ -2781,10 +2794,6 @@ Json::Value RPCHandler::doSubscribe(Json::Value jvRequest, int& cost) return rpcError(rpcSRC_ISR_MALFORMED); } - uint160 uTakerGetsCurrencyID; - uint160 uTakerGetsIssuerID; - Json::Value jvTakerGets = (*it)["taker_gets"]; - // Parse mandatory currency. if (!jvTakerGets.isMember("currency") || !STAmount::currencyFromString(uTakerGetsCurrencyID, jvTakerGets["currency"].asString())) @@ -2816,38 +2825,45 @@ Json::Value RPCHandler::doSubscribe(Json::Value jvRequest, int& cost) RippleAddress raTakerID; - if (!(*it).isMember("taker")) + if (!jvSubRequest.isMember("taker")) { raTakerID.setAccountID(ACCOUNT_ONE); } - else if (!raTakerID.setAccountID((*it)["taker"].asString())) + else if (!raTakerID.setAccountID(jvSubRequest["taker"].asString())) { return rpcError(rpcBAD_ISSUER); } - bool bothSides = (*it)["both_sides"].asBool(); - if (!Ledger::isValidBook(uTakerPaysCurrencyID, uTakerPaysIssuerID, uTakerGetsCurrencyID, uTakerGetsIssuerID)) { cLog(lsWARNING) << "Bad market: " << - uTakerPaysCurrencyID << ":" << uTakerPaysIssuerID << " -> " << + uTakerPaysCurrencyID << ":" << uTakerPaysIssuerID << " -> " << uTakerGetsCurrencyID << ":" << uTakerGetsIssuerID; return rpcError(rpcBAD_MARKET); } mNetOps->subBook(ispSub, uTakerPaysCurrencyID, uTakerGetsCurrencyID, uTakerPaysIssuerID, uTakerGetsIssuerID); - if (bothSides) mNetOps->subBook(ispSub, uTakerGetsCurrencyID, uTakerPaysCurrencyID, uTakerGetsIssuerID, uTakerPaysIssuerID); - if ((*it)["state_now"].asBool()) + if (bBoth) mNetOps->subBook(ispSub, uTakerGetsCurrencyID, uTakerPaysCurrencyID, uTakerGetsIssuerID, uTakerPaysIssuerID); + + if (bSnapshot) { - Ledger::pointer ledger= theApp->getLedgerMaster().getClosedLedger(); + Ledger::pointer lpLedger= theApp->getLedgerMaster().getClosedLedger(); const Json::Value jvMarker = Json::Value(Json::nullValue); - mNetOps->getBookPage(ledger, uTakerPaysCurrencyID, uTakerPaysIssuerID, uTakerGetsCurrencyID, uTakerGetsIssuerID, raTakerID.getAccountID(), false, 0, jvMarker, jvResult); - if (bothSides) + + if (bBoth) { - Json::Value tempJson(Json::objectValue); - if (jvResult.isMember("offers")) jvResult["bids"]=jvResult["offers"]; - mNetOps->getBookPage(ledger, uTakerGetsCurrencyID, uTakerGetsIssuerID, uTakerPaysCurrencyID, uTakerPaysIssuerID, raTakerID.getAccountID(), false, 0, jvMarker, tempJson); - if (tempJson.isMember("offers")) jvResult["asks"]=tempJson["offers"]; + Json::Value jvBids(Json::objectValue); + Json::Value jvAsks(Json::objectValue); + + mNetOps->getBookPage(lpLedger, uTakerPaysCurrencyID, uTakerPaysIssuerID, uTakerGetsCurrencyID, uTakerGetsIssuerID, raTakerID.getAccountID(), false, 0, jvMarker, jvBids); + if (jvBids.isMember("offers")) jvResult["bids"]=jvBids["offers"]; + + mNetOps->getBookPage(lpLedger, uTakerGetsCurrencyID, uTakerGetsIssuerID, uTakerPaysCurrencyID, uTakerPaysIssuerID, raTakerID.getAccountID(), false, 0, jvMarker, jvAsks); + if (jvAsks.isMember("offers")) jvResult["asks"]=jvAsks["offers"]; + } + else + { + mNetOps->getBookPage(lpLedger, uTakerPaysCurrencyID, uTakerPaysIssuerID, uTakerGetsCurrencyID, uTakerGetsIssuerID, raTakerID.getAccountID(), false, 0, jvMarker, jvResult); } } }