diff --git a/src/cpp/ripple/NetworkOPs.cpp b/src/cpp/ripple/NetworkOPs.cpp index f62390f619..7a8d374215 100644 --- a/src/cpp/ripple/NetworkOPs.cpp +++ b/src/cpp/ripple/NetworkOPs.cpp @@ -1060,28 +1060,52 @@ void NetworkOPs::setMode(OperatingMode om) std::string - NetworkOPs::transactionsSQL(const RippleAddress& account, uint32 minLedger, uint32 maxLedger, bool descending, uint32 offset, uint32 limit, bool binary, bool bAdmin) + NetworkOPs::transactionsSQL(std::string selection, const RippleAddress& account, int32 minLedger, int32 maxLedger, bool descending, uint32 offset, uint32 limit, bool binary, bool bAdmin) { + uint32 NONBINARY_PAGE_LENGTH = 200; + uint32 BINARY_PAGE_LENGTH = 500; + uint32 numberOfResults = limit; + if (limit == 0) {numberOfResults = std::numeric_limits::max();} + if (!bAdmin) { + if (numberOfResults < (binary ? BINARY_PAGE_LENGTH : NONBINARY_PAGE_LENGTH)) { + numberOfResults = (binary ? BINARY_PAGE_LENGTH : NONBINARY_PAGE_LENGTH); + } + } + // How to get only validated ledgers? + + std::string maxClause = ""; + std::string minClause = ""; + if (maxLedger != -1) { + maxClause = boost::str(boost::format("AND AccountTransactions.LedgerSeq <= '%u'") % maxLedger); + } + if (minLedger != -1) { + minClause = boost::str(boost::format("AND AccountTransactions.LedgerSeq >= '%u'") % minLedger); + } + std::string sql = - boost::str(boost::format("SELECT AccountTransactions.LedgerSeq,Status,RawTxn,TxnMeta FROM " + boost::str(boost::format("SELECT %s FROM " "AccountTransactions INNER JOIN Transactions ON Transactions.TransID = AccountTransactions.TransID " - "WHERE Account = '%s' AND AccountTransactions.LedgerSeq <= '%u' AND AccountTransactions.LedgerSeq >= '%u' " - "ORDER BY AccountTransactions.LedgerSeq,AccountTransactions.TransID %sSC%s;") - % account.humanAccountID() % maxLedger % minLedger % (descending ? "DE" : "A") % (bAdmin ? "" : (" LIMIT "+boost::lexical_cast(offset)+(binary?"500":"200") ) ) ); + "WHERE Account = '%s' %s %s " + "ORDER BY AccountTransactions.LedgerSeq %sSC, AccountTransactions.TransID %sSC LIMIT %u, %u;") + % selection + % account.humanAccountID() + % maxClause + % minClause + % (descending ? "DE" : "A") + % (descending ? "DE" : "A") + % boost::lexical_cast(offset) + % boost::lexical_cast(numberOfResults) + ); + std::cout << "SQL QUERY! " << sql; return sql; } std::vector< std::pair > - NetworkOPs::getAccountTxs(const RippleAddress& account, uint32 minLedger, uint32 maxLedger, bool bAdmin) + NetworkOPs::getAccountTxs(const RippleAddress& account, int32 minLedger, int32 maxLedger, bool descending, uint32 offset, uint32 limit, bool bAdmin) { // can be called with no locks std::vector< std::pair > ret; - std::string sql = - boost::str(boost::format("SELECT AccountTransactions.LedgerSeq,Status,RawTxn,TxnMeta FROM " - "AccountTransactions INNER JOIN Transactions ON Transactions.TransID = AccountTransactions.TransID " - "WHERE Account = '%s' AND AccountTransactions.LedgerSeq <= '%u' AND AccountTransactions.LedgerSeq >= '%u' " - "ORDER BY AccountTransactions.LedgerSeq,AccountTransactions.TransID DESC%s;") - % account.humanAccountID() % maxLedger % minLedger % (bAdmin ? "" : " LIMIT 200")); + std::string sql = NetworkOPs::transactionsSQL("AccountTransactions.LedgerSeq,Status,RawTxn,TxnMeta", account, minLedger, maxLedger, descending, offset, limit, false, bAdmin); { Database* db = theApp->getTxnDB()->getDB(); @@ -1110,15 +1134,13 @@ std::vector< std::pair > } std::vector NetworkOPs::getAccountTxsB( - const RippleAddress& account, uint32 minLedger, uint32 maxLedger, bool bAdmin) + const RippleAddress& account, int32 minLedger, int32 maxLedger, bool descending, uint32 offset, uint32 limit, bool bAdmin) { // can be called with no locks std::vector< txnMetaLedgerType> ret; - std::string sql = str(boost::format("SELECT AccountTransactions.LedgerSeq,Status,RawTxn,TxnMeta FROM " - "AccountTransactions INNER JOIN Transactions ON Transactions.TransID = AccountTransactions.TransID " - "WHERE Account = '%s' AND AccountTransactions.LedgerSeq <= '%u' AND AccountTransactions.LedgerSeq >= '%u' " - "ORDER BY AccountTransactions.LedgerSeq,AccountTransactions.TransID DESC%s;") - % account.humanAccountID() % maxLedger % minLedger % (bAdmin ? "" : " LIMIT 500")); + + std::string sql = NetworkOPs::transactionsSQL("AccountTransactions.LedgerSeq,Status,RawTxn,TxnMeta", account, minLedger, maxLedger, descending, offset, limit, true/*binary*/, bAdmin); + { Database* db = theApp->getTxnDB()->getDB(); @@ -1156,6 +1178,26 @@ std::vector NetworkOPs::getAccountTxsB( } + + +uint32 + NetworkOPs::countAccountTxs(const RippleAddress& account, int32 minLedger, int32 maxLedger, uint32 offset) +{ // can be called with no locks + uint32 ret = 0; + std::string sql = NetworkOPs::transactionsSQL("COUNT(1) AS 'TransactionCount'", account, minLedger, maxLedger, false, offset, 0, true, true); + + + Database* db = theApp->getTxnDB()->getDB(); + ScopedLock sl(theApp->getTxnDB()->getDBLock()); + SQL_FOREACH(db, sql) + { + ret = db->getInt("TransactionCount"); + } + + return ret; +} + + std::vector NetworkOPs::getLedgerAffectedAccounts(uint32 ledgerSeq) { diff --git a/src/cpp/ripple/NetworkOPs.h b/src/cpp/ripple/NetworkOPs.h index a738c87449..a3b7809369 100644 --- a/src/cpp/ripple/NetworkOPs.h +++ b/src/cpp/ripple/NetworkOPs.h @@ -293,20 +293,20 @@ public: bool isWantedHash(const uint256& h, bool remove); //Helper function to generate SQL query to get transactions - std::string transactionsSQL(const RippleAddress& account, uint32 minLedger, uint32 maxLedger, bool descending, uint32 offset, uint32 limit, bool binary, bool bAdmin); + std::string transactionsSQL(std::string selection, const RippleAddress& account, int32 minLedger, int32 maxLedger, bool descending, uint32 offset, uint32 limit, bool binary, bool bAdmin); // client information retrieval functions std::vector< std::pair > - getAccountTxs(const RippleAddress& account, uint32 minLedger, uint32 maxLedger, bool bAdmin); + getAccountTxs(const RippleAddress& account, int32 minLedger, int32 maxLedger, bool descending, uint32 offset, uint32 limit, bool bAdmin); typedef boost::tuple txnMetaLedgerType; std::vector - getAccountTxsB(const RippleAddress& account, uint32 minL, uint32 maxL, bool bAdmin); + getAccountTxsB(const RippleAddress& account, int32 minLedger, int32 maxLedger, bool descending, uint32 offset, uint32 limit, bool bAdmin); std::vector getLedgerAffectedAccounts(uint32 ledgerSeq); std::vector getLedgerTransactions(uint32 ledgerSeq); - + uint32 NetworkOPs::countAccountTxs(const RippleAddress& account, int32 minLedger, int32 maxLedger, uint32 offset); // // Monitoring: publisher side // diff --git a/src/cpp/ripple/RPCHandler.cpp b/src/cpp/ripple/RPCHandler.cpp index 7fdf59e00d..516cf7393e 100644 --- a/src/cpp/ripple/RPCHandler.cpp +++ b/src/cpp/ripple/RPCHandler.cpp @@ -1664,8 +1664,14 @@ Json::Value RPCHandler::doLedger(Json::Value jvRequest, int& cost) Json::Value RPCHandler::doAccountTransactions(Json::Value jvRequest, int& cost) { RippleAddress raAccount; - uint32 minLedger; - uint32 maxLedger; + int32 minLedger; + int32 maxLedger; + bool descending = false; + uint32 offset = 0; + uint32 limit = 0; + bool count = false; + + bool allValidated = true; if (!jvRequest.isMember("account")) return rpcError(rpcINVALID_PARAMS); @@ -1675,8 +1681,8 @@ Json::Value RPCHandler::doAccountTransactions(Json::Value jvRequest, int& cost) if (jvRequest.isMember("ledger_min") && jvRequest.isMember("ledger_max")) { - minLedger = jvRequest["ledger_min"].asUInt(); - maxLedger = jvRequest["ledger_max"].asUInt(); + minLedger = jvRequest["ledger_min"].asInt(); + maxLedger = jvRequest["ledger_max"].asInt(); } else { @@ -1692,6 +1698,23 @@ Json::Value RPCHandler::doAccountTransactions(Json::Value jvRequest, int& cost) return rpcError(rpcLGR_IDXS_INVALID); } + if (jvRequest.isMember("descending")) + { + descending = jvRequest["descending"].asBool(); + } + if (jvRequest.isMember("offset")) + { + offset = jvRequest["offset"].asUInt(); + } + if (jvRequest.isMember("limit")) + { + limit = jvRequest["limit"].asUInt(); + } + if (jvRequest.isMember("count")) + { + count = jvRequest["count"].asBool(); + } + #ifndef DEBUG try { @@ -1705,7 +1728,7 @@ Json::Value RPCHandler::doAccountTransactions(Json::Value jvRequest, int& cost) if (jvRequest.isMember("binary") && jvRequest["binary"].asBool()) { std::vector txns = - mNetOps->getAccountTxsB(raAccount, minLedger, maxLedger, mRole == ADMIN); + mNetOps->getAccountTxsB(raAccount, minLedger, maxLedger, descending, offset, limit, mRole == ADMIN); for (std::vector::const_iterator it = txns.begin(), end = txns.end(); it != end; ++it) @@ -1714,16 +1737,21 @@ Json::Value RPCHandler::doAccountTransactions(Json::Value jvRequest, int& cost) obj["transaction"] = it->get<0>(); obj["meta"] = it->get<1>(); obj["inLedger"] = it->get<2>(); - if (it->get<2>() > vl) + if (it->get<2>() > vl) { obj["validated"] = false; + allValidated = false; + } else if (mNetOps->haveLedger(it->get<2>())) obj["validated"] = true; + else { + //return rpcError(rpcLGR_NOT_FOUND); + } ret["transactions"].append(obj); } } else { - std::vector< std::pair > txns = mNetOps->getAccountTxs(raAccount, minLedger, maxLedger, mRole == ADMIN); + std::vector< std::pair > txns = mNetOps->getAccountTxs(raAccount, minLedger, maxLedger, descending, offset, limit, mRole == ADMIN); for (std::vector< std::pair >::iterator it = txns.begin(), end = txns.end(); it != end; ++it) { Json::Value obj(Json::objectValue); @@ -1735,15 +1763,31 @@ Json::Value RPCHandler::doAccountTransactions(Json::Value jvRequest, int& cost) obj["meta"] = it->second->getJson(0); uint32 s = it->second->getLgrSeq(); - if (s > vl) + if (s > vl) { obj["validated"] = false; + allValidated = false; + } else if (mNetOps->haveLedger(s)) obj["validated"] = true; + else { + //return rpcError(rpcLGR_NOT_FOUND); //Can this also happen when s > vl? + } } ret["transactions"].append(obj); } } + + //Add information about the original query + ret["validated"] = allValidated; + if(count) { + ret["count"] = mNetOps->countAccountTxs(raAccount, minLedger, maxLedger, offset); + } + ret["limit"] = limit; + ret["ledger_min"] = minLedger; + ret["ledger_max"] = maxLedger; + ret["offset"] = offset; + return ret; #ifndef DEBUG }