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
}