Fixes for account_tx.

This commit is contained in:
Arthur Britto
2013-03-27 17:26:13 -07:00
parent a828ed3703
commit 09633c5f04
5 changed files with 126 additions and 116 deletions

View File

@@ -134,49 +134,70 @@ Json::Value RPCParser::parseInternal(const Json::Value& jvParams)
return v; return v;
} }
// account_tx <account> <minledger> <maxledger> // account_tx accountID [ledger_min [ledger_max [limit [offset]]]] [binary] [count] [descending]
// account_tx <account> <ledger>
// account_tx <account> binary
// account_tx <account> <minledger> <maxledger> binary
Json::Value RPCParser::parseAccountTransactions(const Json::Value& jvParams) Json::Value RPCParser::parseAccountTransactions(const Json::Value& jvParams)
{ {
Json::Value jvRequest(Json::objectValue); Json::Value jvRequest(Json::objectValue);
RippleAddress raAccount; RippleAddress raAccount;
unsigned int iParams = jvParams.size();
unsigned size = jvParams.size();
if ((size > 1) && (jvParams[size - 1].asString() == "binary"))
{
jvRequest["binary"] = true;
--size;
}
if (size < 2 || size > 3)
return rpcError(rpcINVALID_PARAMS);
if (!raAccount.setAccountID(jvParams[0u].asString())) if (!raAccount.setAccountID(jvParams[0u].asString()))
return rpcError(rpcACT_MALFORMED); return rpcError(rpcACT_MALFORMED);
// YYY This could be more strict and report casting errors. jvRequest["account"] = raAccount.humanAccountID();
if (size == 2)
bool bDone = false;
while (!bDone && iParams >= 2) {
if (jvParams[iParams-1].asString() == "binary")
{ {
jvRequest["ledger"] = jvParams[1u].asUInt(); jvRequest["binary"] = true;
--iParams;
}
else if (jvParams[iParams-1].asString() == "count")
{
jvRequest["count"] = true;
--iParams;
}
else if (jvParams[iParams-1].asString() == "descending")
{
jvRequest["descending"] = true;
--iParams;
} }
else else
{ {
uint32 uLedgerMin = jvParams[1u].asUInt(); bDone = true;
uint32 uLedgerMax = jvParams[2u].asUInt(); }
}
if ((uLedgerMax < uLedgerMin) || (uLedgerMax == 0)) if (1 == iParams)
{
nothing();
}
else if (2 == iParams)
{
if (!jvParseLedger(jvRequest, jvParams[1u].asString()))
return jvRequest;
}
else
{
int64 uLedgerMin = jvParams[1u].asInt();
int64 uLedgerMax = jvParams[2u].asInt();
if (uLedgerMax != -1 && uLedgerMax < uLedgerMin)
{ {
return rpcError(rpcLGR_IDXS_INVALID); return rpcError(rpcLGR_IDXS_INVALID);
} }
jvRequest["ledger_min"] = uLedgerMin; jvRequest["ledger_index_min"] = jvParams[1u].asInt();
jvRequest["ledger_max"] = uLedgerMax; jvRequest["ledger_index_max"] = jvParams[2u].asInt();
}
jvRequest["account"] = raAccount.humanAccountID(); if (iParams >= 4)
jvRequest["limit"] = jvParams[3u].asInt();
if (iParams >= 5)
jvRequest["offset"] = jvParams[4u].asInt();
}
return jvRequest; return jvRequest;
} }
@@ -626,7 +647,7 @@ Json::Value RPCParser::parseCommand(std::string strMethod, Json::Value jvParams)
{ "account_info", &RPCParser::parseAccountItems, 1, 2 }, { "account_info", &RPCParser::parseAccountItems, 1, 2 },
{ "account_lines", &RPCParser::parseAccountItems, 1, 2 }, { "account_lines", &RPCParser::parseAccountItems, 1, 2 },
{ "account_offers", &RPCParser::parseAccountItems, 1, 2 }, { "account_offers", &RPCParser::parseAccountItems, 1, 2 },
{ "account_tx", &RPCParser::parseAccountTransactions, 2, 4 }, { "account_tx", &RPCParser::parseAccountTransactions, 1, 8 },
{ "book_offers", &RPCParser::parseBookOffers, 2, 7 }, { "book_offers", &RPCParser::parseBookOffers, 2, 7 },
{ "connect", &RPCParser::parseConnect, 1, 2 }, { "connect", &RPCParser::parseConnect, 1, 2 },
{ "consensus_info", &RPCParser::parseAsIs, 0, 0 }, { "consensus_info", &RPCParser::parseAsIs, 0, 0 },

View File

@@ -1065,12 +1065,12 @@ void NetworkOPs::setMode(OperatingMode om)
std::string std::string
NetworkOPs::transactionsSQL(std::string selection, const RippleAddress& account, int32 minLedger, int32 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, int limit, bool binary, bool bAdmin)
{ {
uint32 NONBINARY_PAGE_LENGTH = 200; uint32 NONBINARY_PAGE_LENGTH = 200;
uint32 BINARY_PAGE_LENGTH = 500; uint32 BINARY_PAGE_LENGTH = 500;
uint32 numberOfResults = limit; uint32 numberOfResults = limit;
if (limit == 0) {numberOfResults = std::numeric_limits<uint32>::max();} if (limit == -1) {numberOfResults = std::numeric_limits<uint32>::max();}
if (!bAdmin) { if (!bAdmin) {
if (numberOfResults < (binary ? BINARY_PAGE_LENGTH : NONBINARY_PAGE_LENGTH)) { if (numberOfResults < (binary ? BINARY_PAGE_LENGTH : NONBINARY_PAGE_LENGTH)) {
numberOfResults = (binary ? BINARY_PAGE_LENGTH : NONBINARY_PAGE_LENGTH); numberOfResults = (binary ? BINARY_PAGE_LENGTH : NONBINARY_PAGE_LENGTH);
@@ -1106,7 +1106,7 @@ std::string
} }
std::vector< std::pair<Transaction::pointer, TransactionMetaSet::pointer> > std::vector< std::pair<Transaction::pointer, TransactionMetaSet::pointer> >
NetworkOPs::getAccountTxs(const RippleAddress& account, int32 minLedger, int32 maxLedger, bool descending, uint32 offset, uint32 limit, bool bAdmin) NetworkOPs::getAccountTxs(const RippleAddress& account, int32 minLedger, int32 maxLedger, bool descending, uint32 offset, int limit, bool bAdmin)
{ // can be called with no locks { // can be called with no locks
std::vector< std::pair<Transaction::pointer, TransactionMetaSet::pointer> > ret; std::vector< std::pair<Transaction::pointer, TransactionMetaSet::pointer> > ret;
@@ -1139,7 +1139,7 @@ std::vector< std::pair<Transaction::pointer, TransactionMetaSet::pointer> >
} }
std::vector<NetworkOPs::txnMetaLedgerType> NetworkOPs::getAccountTxsB( std::vector<NetworkOPs::txnMetaLedgerType> NetworkOPs::getAccountTxsB(
const RippleAddress& account, int32 minLedger, int32 maxLedger, bool descending, uint32 offset, uint32 limit, bool bAdmin) const RippleAddress& account, int32 minLedger, int32 maxLedger, bool descending, uint32 offset, int limit, bool bAdmin)
{ // can be called with no locks { // can be called with no locks
std::vector< txnMetaLedgerType> ret; std::vector< txnMetaLedgerType> ret;
@@ -1184,10 +1184,10 @@ std::vector<NetworkOPs::txnMetaLedgerType> NetworkOPs::getAccountTxsB(
uint32 uint32
NetworkOPs::countAccountTxs(const RippleAddress& account, int32 minLedger, int32 maxLedger, uint32 offset) NetworkOPs::countAccountTxs(const RippleAddress& account, int32 minLedger, int32 maxLedger)
{ // can be called with no locks { // can be called with no locks
uint32 ret = 0; uint32 ret = 0;
std::string sql = NetworkOPs::transactionsSQL("COUNT(1) AS 'TransactionCount'", account, minLedger, maxLedger, false, offset, 0, true, true); std::string sql = NetworkOPs::transactionsSQL("COUNT(*) AS 'TransactionCount'", account, minLedger, maxLedger, false, 0, -1, true, true);
Database* db = theApp->getTxnDB()->getDB(); Database* db = theApp->getTxnDB()->getDB();
ScopedLock sl(theApp->getTxnDB()->getDBLock()); ScopedLock sl(theApp->getTxnDB()->getDBLock());

View File

@@ -294,20 +294,20 @@ public:
bool isWantedHash(const uint256& h, bool remove); bool isWantedHash(const uint256& h, bool remove);
//Helper function to generate SQL query to get transactions //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); std::string transactionsSQL(std::string selection, const RippleAddress& account, int32 minLedger, int32 maxLedger, bool descending, uint32 offset, int limit, bool binary, bool bAdmin);
// client information retrieval functions // client information retrieval functions
std::vector< std::pair<Transaction::pointer, TransactionMetaSet::pointer> > std::vector< std::pair<Transaction::pointer, TransactionMetaSet::pointer> >
getAccountTxs(const RippleAddress& account, int32 minLedger, int32 maxLedger, bool descending, uint32 offset, uint32 limit, bool bAdmin); getAccountTxs(const RippleAddress& account, int32 minLedger, int32 maxLedger, bool descending, uint32 offset, int limit, bool bAdmin);
typedef boost::tuple<std::string, std::string, uint32> txnMetaLedgerType; typedef boost::tuple<std::string, std::string, uint32> txnMetaLedgerType;
std::vector<txnMetaLedgerType> std::vector<txnMetaLedgerType>
getAccountTxsB(const RippleAddress& account, int32 minLedger, int32 maxLedger, bool descending, uint32 offset, uint32 limit, bool bAdmin); getAccountTxsB(const RippleAddress& account, int32 minLedger, int32 maxLedger, bool descending, uint32 offset, int limit, bool bAdmin);
std::vector<RippleAddress> getLedgerAffectedAccounts(uint32 ledgerSeq); std::vector<RippleAddress> getLedgerAffectedAccounts(uint32 ledgerSeq);
std::vector<SerializedTransaction> getLedgerTransactions(uint32 ledgerSeq); std::vector<SerializedTransaction> getLedgerTransactions(uint32 ledgerSeq);
uint32 countAccountTxs(const RippleAddress& account, int32 minLedger, int32 maxLedger, uint32 offset); uint32 countAccountTxs(const RippleAddress& account, int32 minLedger, int32 maxLedger);
// //
// Monitoring: publisher side // Monitoring: publisher side
// //

View File

@@ -1656,23 +1656,29 @@ Json::Value RPCHandler::doLedger(Json::Value jvRequest, int& cost)
return ret; return ret;
} }
// { account: <account>, ledger: <integer> } // {
// { account: <account>, ledger_min: <integer>, ledger_max: <integer> } // account: account,
// THIS ROUTINE DOESN'T SCALE. // ledger_index_min: ledger_index,
// FIXME: Require admin. // ledger_index_max: ledger_index,
// FIXME: Doesn't report database holes. // binary: boolean, // optional, defaults to false
// FIXME: For consistency change inputs to: ledger_index, ledger_index_min, ledger_index_max. // count: boolean, // optional, defaults to false
// descending: boolean, // optional, defaults to false
// offset: integer, // optional, defaults to 0
// limit: integer // optional
// }
Json::Value RPCHandler::doAccountTransactions(Json::Value jvRequest, int& cost) Json::Value RPCHandler::doAccountTransactions(Json::Value jvRequest, int& cost)
{ {
// XXX Perhaps don't allow a limit of 0.
RippleAddress raAccount; RippleAddress raAccount;
int32 minLedger; uint32 offset = jvRequest.isMember("offset") ? jvRequest["offset"].asUInt() : 0;
int32 maxLedger; int limit = jvRequest.isMember("limit") ? jvRequest["limit"].asUInt() : -1;
bool descending = false; bool descending = jvRequest.isMember("descending") && jvRequest["descending"].asBool();
uint32 offset = 0; bool count = jvRequest.isMember("count") && jvRequest["count"].asBool();
uint32 limit = 0; uint32 uLedgerMin;
bool count = false; uint32 uLedgerMax;
uint32 uValidatedMin;
bool allValidated = true; uint32 uValidatedMax;
bool bValidated = mNetOps->getValidatedRange(uValidatedMin, uValidatedMax);
if (!jvRequest.isMember("account")) if (!jvRequest.isMember("account"))
return rpcError(rpcINVALID_PARAMS); return rpcError(rpcINVALID_PARAMS);
@@ -1680,10 +1686,23 @@ Json::Value RPCHandler::doAccountTransactions(Json::Value jvRequest, int& cost)
if (!raAccount.setAccountID(jvRequest["account"].asString())) if (!raAccount.setAccountID(jvRequest["account"].asString()))
return rpcError(rpcACT_MALFORMED); return rpcError(rpcACT_MALFORMED);
if (jvRequest.isMember("ledger_min") && jvRequest.isMember("ledger_max")) if (jvRequest.isMember("ledger_index_min") || jvRequest.isMember("ledger_index_max"))
{ {
minLedger = jvRequest["ledger_min"].asInt(); int64 iLedgerMin = jvRequest.isMember("ledger_index_min") ? jvRequest["ledger_index_min"].asInt() : -1;
maxLedger = jvRequest["ledger_max"].asInt(); int64 iLedgerMax = jvRequest.isMember("ledger_index_max") ? jvRequest["ledger_index_max"].asInt() : -1;
if (!bValidated && (iLedgerMin == -1 || iLedgerMax == -1)) {
// Don't have a validated ledger range.
return rpcError(rpcLGR_IDXS_INVALID);
}
uLedgerMin = iLedgerMin == -1 ? uValidatedMin : iLedgerMin;
uLedgerMax = iLedgerMax == -1 ? uValidatedMax : iLedgerMax;
if (uLedgerMax < uLedgerMin)
{
return rpcError(rpcLGR_IDXS_INVALID);
}
} }
else else
{ {
@@ -1691,104 +1710,74 @@ Json::Value RPCHandler::doAccountTransactions(Json::Value jvRequest, int& cost)
Json::Value ret = lookupLedger(jvRequest, l); Json::Value ret = lookupLedger(jvRequest, l);
if (!l) if (!l)
return ret; return ret;
minLedger = maxLedger = l->getLedgerSeq(); uLedgerMin = uLedgerMax = l->getLedgerSeq();
}
if ((maxLedger < minLedger) || (maxLedger == 0))
{
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 #ifndef DEBUG
try try
{ {
#endif #endif
unsigned int vl = mNetOps->getValidatedSeq();
ScopedUnlock su(theApp->getMasterLock()); ScopedUnlock su(theApp->getMasterLock());
Json::Value ret(Json::objectValue); Json::Value ret(Json::objectValue);
ret["account"] = raAccount.humanAccountID(); ret["account"] = raAccount.humanAccountID();
if (jvRequest.isMember("binary") && jvRequest["binary"].asBool()) if (jvRequest.isMember("binary") && jvRequest["binary"].asBool())
{ {
std::vector<NetworkOPs::txnMetaLedgerType> txns = std::vector<NetworkOPs::txnMetaLedgerType> txns =
mNetOps->getAccountTxsB(raAccount, minLedger, maxLedger, descending, offset, limit, mRole == ADMIN); mNetOps->getAccountTxsB(raAccount, uLedgerMin, uLedgerMax, descending, offset, limit, mRole == ADMIN);
for (std::vector<NetworkOPs::txnMetaLedgerType>::const_iterator it = txns.begin(), end = txns.end(); for (std::vector<NetworkOPs::txnMetaLedgerType>::const_iterator it = txns.begin(), end = txns.end();
it != end; ++it) it != end; ++it)
{ {
Json::Value obj(Json::objectValue); Json::Value jvObj(Json::objectValue);
obj["transaction"] = it->get<0>(); uint32 uLedgerIndex = it->get<2>();
obj["meta"] = it->get<1>();
obj["inLedger"] = it->get<2>(); jvObj["tx_blob"] = it->get<0>();
if (it->get<2>() > vl) { jvObj["meta"] = it->get<1>();
obj["validated"] = false; jvObj["ledger_index"] = uLedgerIndex;
allValidated = false; jvObj["validated"] = bValidated && uValidatedMin <= uLedgerIndex && uValidatedMax >= uLedgerIndex;
}
else if (mNetOps->haveLedger(it->get<2>())) ret["transactions"].append(jvObj);
obj["validated"] = true;
else {
//return rpcError(rpcLGR_NOT_FOUND);
}
ret["transactions"].append(obj);
} }
} }
else else
{ {
std::vector< std::pair<Transaction::pointer, TransactionMetaSet::pointer> > txns = mNetOps->getAccountTxs(raAccount, minLedger, maxLedger, descending, offset, limit, mRole == ADMIN); std::vector< std::pair<Transaction::pointer, TransactionMetaSet::pointer> > txns = mNetOps->getAccountTxs(raAccount, uLedgerMin, uLedgerMax, descending, offset, limit, mRole == ADMIN);
for (std::vector< std::pair<Transaction::pointer, TransactionMetaSet::pointer> >::iterator it = txns.begin(), end = txns.end(); it != end; ++it) for (std::vector< std::pair<Transaction::pointer, TransactionMetaSet::pointer> >::iterator it = txns.begin(), end = txns.end(); it != end; ++it)
{ {
Json::Value obj(Json::objectValue); Json::Value jvObj(Json::objectValue);
if (it->first) if (it->first)
obj["tx"] = it->first->getJson(1); jvObj["tx"] = it->first->getJson(1);
if (it->second) if (it->second)
{ {
obj["meta"] = it->second->getJson(0); uint32 uLedgerIndex = it->second->getLgrSeq();
uint32 s = it->second->getLgrSeq(); jvObj["meta"] = it->second->getJson(0);
if (s > vl) { jvObj["validated"] = bValidated && uValidatedMin <= uLedgerIndex && uValidatedMax >= uLedgerIndex;
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); ret["transactions"].append(jvObj);
} }
} }
//Add information about the original query //Add information about the original query
ret["validated"] = allValidated; ret["ledger_index_min"] = uLedgerMin;
if(count) { ret["ledger_index_max"] = uLedgerMax;
ret["count"] = mNetOps->countAccountTxs(raAccount, minLedger, maxLedger, offset); ret["validated"] = bValidated && uValidatedMin <= uLedgerMin && uValidatedMax >= uLedgerMax;
}
ret["limit"] = limit;
ret["ledger_min"] = minLedger;
ret["ledger_max"] = maxLedger;
ret["offset"] = offset; ret["offset"] = offset;
if (count)
ret["count"] = mNetOps->countAccountTxs(raAccount, uLedgerMin, uLedgerMax);
if (jvRequest.isMember("limit"))
ret["limit"] = limit;
return ret; return ret;
#ifndef DEBUG #ifndef DEBUG
} }

View File

@@ -73,7 +73,7 @@ void printHelp(const po::options_description& desc)
cerr << " account_info <account>|<nickname>|<seed>|<pass_phrase>|<key> [<ledger>]" << endl; cerr << " account_info <account>|<nickname>|<seed>|<pass_phrase>|<key> [<ledger>]" << endl;
cerr << " account_lines <account>|<nickname>|<account_public_key> [<ledger>]" << endl; cerr << " account_lines <account>|<nickname>|<account_public_key> [<ledger>]" << endl;
cerr << " account_offers <account>|<nickname>|<account_public_key> [<ledger>]" << endl; cerr << " account_offers <account>|<nickname>|<account_public_key> [<ledger>]" << endl;
cerr << " account_tx <account>|<nickname>|<account_public_key> <ledger>|(<minledger> <maxledger>)" << endl; cerr << " account_tx accountID [ledger_min [ledger_max [limit [offset]]]] [binary] [count] [descending]" << endl;
cerr << " book_offers <taker_pays> <taker_gets> [<taker [<ledger> [<limit> [<proof> [<marker>]]]]]" << endl; cerr << " book_offers <taker_pays> <taker_gets> [<taker [<ledger> [<limit> [<proof> [<marker>]]]]]" << endl;
cerr << " connect <ip> [<port>]" << endl; cerr << " connect <ip> [<port>]" << endl;
cerr << " consensus_info" << endl; cerr << " consensus_info" << endl;