diff --git a/newcoin.vcxproj b/newcoin.vcxproj index dfaccea23..07ca73e10 100644 --- a/newcoin.vcxproj +++ b/newcoin.vcxproj @@ -101,6 +101,7 @@ + diff --git a/newcoin.vcxproj.filters b/newcoin.vcxproj.filters index 2ccbac5b4..483cb0c2d 100644 --- a/newcoin.vcxproj.filters +++ b/newcoin.vcxproj.filters @@ -366,6 +366,9 @@ Source Files + + Source Files + diff --git a/src/cpp/ripple/NetworkOPs.cpp b/src/cpp/ripple/NetworkOPs.cpp index 4f2457c64..e655fe1f0 100644 --- a/src/cpp/ripple/NetworkOPs.cpp +++ b/src/cpp/ripple/NetworkOPs.cpp @@ -1064,17 +1064,53 @@ void NetworkOPs::setMode(OperatingMode om) } +std::string + 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 %s FROM " + "AccountTransactions INNER JOIN Transactions ON Transactions.TransID = AccountTransactions.TransID " + "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 DESC, 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(); @@ -1103,15 +1139,11 @@ 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 DESC, 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(); @@ -1149,6 +1181,25 @@ 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 3047aa139..a3b780936 100644 --- a/src/cpp/ripple/NetworkOPs.h +++ b/src/cpp/ripple/NetworkOPs.h @@ -292,17 +292,21 @@ public: bool addWantedHash(const uint256& h); bool isWantedHash(const uint256& h, bool remove); + //Helper function to generate SQL query to get transactions + 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 f4b7bb358..8a505aa60 100644 --- a/src/cpp/ripple/RPCHandler.cpp +++ b/src/cpp/ripple/RPCHandler.cpp @@ -1665,8 +1665,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); @@ -1676,8 +1682,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 { @@ -1693,6 +1699,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 { @@ -1706,7 +1729,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) @@ -1715,16 +1738,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); @@ -1736,15 +1764,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 }