From 923446fb78e0b1a8de23e17b7dac5eadb2d04aa7 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Mon, 25 Feb 2013 12:51:06 -0800 Subject: [PATCH] Fix 'tx' output format. Begin supporting a binary output format. This adds support for binary in 'tx' and 'account_tx' commands. https://ripple.com/wiki/FormatChange --- src/cpp/ripple/CallRPC.cpp | 26 ++++++-- src/cpp/ripple/Ledger.cpp | 16 +++++ src/cpp/ripple/Ledger.h | 1 + src/cpp/ripple/NetworkOPs.cpp | 46 ++++++++++++++ src/cpp/ripple/NetworkOPs.h | 6 ++ src/cpp/ripple/RPCHandler.cpp | 80 ++++++++++++++++++------ src/cpp/ripple/SerializedTransaction.cpp | 13 +++- src/cpp/ripple/SerializedTransaction.h | 2 +- src/cpp/ripple/Transaction.cpp | 12 ++-- src/cpp/ripple/Transaction.h | 2 +- 10 files changed, 167 insertions(+), 37 deletions(-) diff --git a/src/cpp/ripple/CallRPC.cpp b/src/cpp/ripple/CallRPC.cpp index f3edd39c99..50cd485fd6 100644 --- a/src/cpp/ripple/CallRPC.cpp +++ b/src/cpp/ripple/CallRPC.cpp @@ -100,19 +100,29 @@ Json::Value RPCParser::parseAccountInfo(const Json::Value& jvParams) // account_tx // account_tx +// account_tx binary +// account_tx binary Json::Value RPCParser::parseAccountTransactions(const Json::Value& jvParams) { Json::Value jvRequest(Json::objectValue); RippleAddress raAccount; - if (jvParams.size() < 2 || jvParams.size() > 3) + 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())) return rpcError(rpcACT_MALFORMED); // YYY This could be more strict and report casting errors. - if (jvParams.size() == 2) + if (size == 2) { jvRequest["ledger"] = jvParams[1u].asUInt(); } @@ -365,8 +375,14 @@ Json::Value RPCParser::parseTx(const Json::Value& jvParams) { Json::Value jvRequest; + if (jvParams.size() > 1) + { + if (jvParams[1u].asString() == "binary") + jvRequest["binary"] = true; + } + jvRequest["transaction"] = jvParams[0u].asString(); - return jvRequest; + return jvRequest; } // tx_history @@ -497,7 +513,7 @@ Json::Value RPCParser::parseCommand(std::string strMethod, Json::Value jvParams) { "account_info", &RPCParser::parseAccountInfo, 1, 2 }, { "account_lines", &RPCParser::parseAccountItems, 1, 2 }, { "account_offers", &RPCParser::parseAccountItems, 1, 2 }, - { "account_tx", &RPCParser::parseAccountTransactions, 2, 3 }, + { "account_tx", &RPCParser::parseAccountTransactions, 2, 4 }, { "connect", &RPCParser::parseConnect, 1, 2 }, { "consensus_info", &RPCParser::parseAsIs, 0, 0 }, { "get_counts", &RPCParser::parseGetCounts, 0, 1 }, @@ -522,7 +538,7 @@ Json::Value RPCParser::parseCommand(std::string strMethod, Json::Value jvParams) { "server_state", &RPCParser::parseAsIs, 0, 0 }, { "stop", &RPCParser::parseAsIs, 0, 0 }, // { "transaction_entry", &RPCParser::parseTransactionEntry, -1, -1 }, - { "tx", &RPCParser::parseTx, 1, 1 }, + { "tx", &RPCParser::parseTx, 1, 2 }, { "tx_history", &RPCParser::parseTxHistory, 1, 1 }, { "unl_add", &RPCParser::parseUnlAdd, 1, 2 }, diff --git a/src/cpp/ripple/Ledger.cpp b/src/cpp/ripple/Ledger.cpp index 065358eb85..609f08787e 100644 --- a/src/cpp/ripple/Ledger.cpp +++ b/src/cpp/ripple/Ledger.cpp @@ -377,6 +377,22 @@ bool Ledger::getTransactionMeta(const uint256& txID, TransactionMetaSet::pointer return true; } +bool Ledger::getMetaHex(const uint256& transID, std::string& hex) +{ + SHAMapTreeNode::TNType type; + SHAMapItem::pointer item = mTransactionMap->peekItem(transID, type); + if (!item) + return false; + + if (type != SHAMapTreeNode::tnTRANSACTION_MD) + return false; + + SerializerIterator it(item->peekSerializer()); + it.getVL(); // skip transaction + hex = strHex(it.getVL()); + return true; +} + uint256 Ledger::getHash() { if (!mValidHash) diff --git a/src/cpp/ripple/Ledger.h b/src/cpp/ripple/Ledger.h index 563921d2b2..62de1c8195 100644 --- a/src/cpp/ripple/Ledger.h +++ b/src/cpp/ripple/Ledger.h @@ -184,6 +184,7 @@ public: Transaction::pointer getTransaction(const uint256& transID) const; bool getTransaction(const uint256& transID, Transaction::pointer& txn, TransactionMetaSet::pointer& txMeta); bool getTransactionMeta(const uint256& transID, TransactionMetaSet::pointer& txMeta); + bool getMetaHex(const uint256& transID, std::string& hex); static SerializedTransaction::pointer getSTransaction(SHAMapItem::ref, SHAMapTreeNode::TNType); SerializedTransaction::pointer getSMTransaction(SHAMapItem::ref, SHAMapTreeNode::TNType, diff --git a/src/cpp/ripple/NetworkOPs.cpp b/src/cpp/ripple/NetworkOPs.cpp index 74df6e366d..d8c3cfdbd9 100644 --- a/src/cpp/ripple/NetworkOPs.cpp +++ b/src/cpp/ripple/NetworkOPs.cpp @@ -1085,6 +1085,52 @@ std::vector< std::pair > return ret; } +std::vector NetworkOPs::getAccountTxsB( + const RippleAddress& account, uint32 minLedger, uint32 maxLedger) +{ // can be called with no locks + std::vector< txnMetaLedgerType> ret; + + std::string sql = + str(boost::format("SELECT LedgerSeq, RawTxn,TxnMeta FROM Transactions where TransID in (SELECT TransID from AccountTransactions " + " WHERE Account = '%s' AND LedgerSeq <= '%d' AND LedgerSeq >= '%d' LIMIT 500) ORDER BY LedgerSeq DESC;") + % account.humanAccountID() % maxLedger % minLedger); + + { + Database* db = theApp->getTxnDB()->getDB(); + ScopedLock sl(theApp->getTxnDB()->getDBLock()); + + SQL_FOREACH(db, sql) + { + int txnSize = 2048; + std::vector rawTxn(txnSize); + txnSize = db->getBinary("RawTxn", &rawTxn[0], rawTxn.size()); + if (txnSize > rawTxn.size()) + { + rawTxn.resize(txnSize); + db->getBinary("RawTxn", &*rawTxn.begin(), rawTxn.size()); + } + else + rawTxn.resize(txnSize); + + int metaSize = 2048; + std::vector rawMeta(2048); + metaSize = db->getBinary("TxnMeta", &rawMeta[0], rawMeta.size()); + if (metaSize > rawMeta.size()) + { + rawMeta.resize(metaSize); + db->getBinary("TxnMeta", &*rawMeta.begin(), rawMeta.size()); + } + else + rawMeta.resize(metaSize); + + ret.push_back(boost::make_tuple(strHex(rawTxn), strHex(rawMeta), db->getInt("LedgerSeq"))); + } + } + + return ret; +} + + std::vector NetworkOPs::getLedgerAffectedAccounts(uint32 ledgerSeq) { diff --git a/src/cpp/ripple/NetworkOPs.h b/src/cpp/ripple/NetworkOPs.h index 1c1ba558c0..8d04c75614 100644 --- a/src/cpp/ripple/NetworkOPs.h +++ b/src/cpp/ripple/NetworkOPs.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "AccountState.h" #include "LedgerMaster.h" @@ -273,6 +274,11 @@ public: // client information retrieval functions std::vector< std::pair > getAccountTxs(const RippleAddress& account, uint32 minLedger, uint32 maxLedger); + + typedef boost::tuple txnMetaLedgerType; + std::vector + getAccountTxsB(const RippleAddress& account, uint32 minL, uint32 maxL); + std::vector getLedgerAffectedAccounts(uint32 ledgerSeq); std::vector getLedgerTransactions(uint32 ledgerSeq); diff --git a/src/cpp/ripple/RPCHandler.cpp b/src/cpp/ripple/RPCHandler.cpp index d9adb4e573..a204fb502a 100644 --- a/src/cpp/ripple/RPCHandler.cpp +++ b/src/cpp/ripple/RPCHandler.cpp @@ -1433,6 +1433,8 @@ Json::Value RPCHandler::doTx(Json::Value jvRequest) if (!jvRequest.isMember("transaction")) return rpcError(rpcINVALID_PARAMS); + bool binary = jvRequest.isMember("binary") && jvRequest["binary"].asBool(); + std::string strTransaction = jvRequest["transaction"].asString(); if (Transaction::isHexTxID(strTransaction)) @@ -1441,21 +1443,39 @@ Json::Value RPCHandler::doTx(Json::Value jvRequest) Transaction::pointer txn = theApp->getMasterTransaction().fetch(txid, true); - if (!txn) return rpcError(rpcTXN_NOT_FOUND); + if (!txn) + return rpcError(rpcTXN_NOT_FOUND); - Json::Value ret = txn->getJson(0); + Json::Value ret; + + ret["transaction"] = txn->getJson(0, binary); if (txn->getLedger() != 0) { Ledger::pointer lgr = theApp->getLedgerMaster().getLedgerBySeq(txn->getLedger()); if (lgr) { - TransactionMetaSet::pointer set; - if (lgr->getTransactionMeta(txid, set)) + bool okay = false; + if (binary) { - ret["meta"] = set->getJson(0); - ret["validated"] = theApp->getOPs().isValidated(lgr); + std::string meta; + if (lgr->getMetaHex(txid, meta)) + { + ret["meta"] = meta; + okay = true; + } } + else + { + TransactionMetaSet::pointer set; + if (lgr->getTransactionMeta(txid, set)) + { + okay = true; + ret["meta"] = set->getJson(0); + } + } + if (okay) + ret["validated"] = theApp->getOPs().isValidated(lgr); } } @@ -1576,29 +1596,49 @@ Json::Value RPCHandler::doAccountTransactions(Json::Value jvRequest) int vl = theApp->getOPs().getValidatedSeq(); ScopedUnlock su(theApp->getMasterLock()); - std::vector< std::pair > txns = mNetOps->getAccountTxs(raAccount, minLedger, maxLedger); Json::Value ret(Json::objectValue); ret["account"] = raAccount.humanAccountID(); - Json::Value ledgers(Json::arrayValue); - for (std::vector< std::pair >::iterator it = txns.begin(), end = txns.end(); it != end; ++it) + if (jvRequest.isMember("binary") && jvRequest["binary"].asBool()) { - Json::Value obj(Json::objectValue); - - if (it->first) - obj["tx"] = it->first->getJson(1); - if (it->second) + std::vector txns = + mNetOps->getAccountTxsB(raAccount, minLedger, maxLedger); + for (std::vector::const_iterator it = txns.begin(), end = txns.end(); + it != end; ++it) { - obj["meta"] = it->second->getJson(0); - - uint32 s = it->second->getLgrSeq(); - if (s > vl) + Json::Value obj(Json::objectValue); + obj["transaction"] = it->get<0>(); + obj["meta"] = it->get<1>(); + obj["inLedger"] = it->get<2>(); + if (it->get<2>() > vl) obj["validated"] = false; - else if (theApp->getOPs().haveLedger(s)) + else if (theApp->getOPs().haveLedger(it->get<2>())) obj["validated"] = true; + ret["transactions"].append(obj); } + } + else + { + std::vector< std::pair > txns = mNetOps->getAccountTxs(raAccount, minLedger, maxLedger); + for (std::vector< std::pair >::iterator it = txns.begin(), end = txns.end(); it != end; ++it) + { + Json::Value obj(Json::objectValue); - ret["transactions"].append(obj); + if (it->first) + obj["tx"] = it->first->getJson(1); + if (it->second) + { + obj["meta"] = it->second->getJson(0); + + uint32 s = it->second->getLgrSeq(); + if (s > vl) + obj["validated"] = false; + else if (theApp->getOPs().haveLedger(s)) + obj["validated"] = true; + } + + ret["transactions"].append(obj); + } } return ret; #ifndef DEBUG diff --git a/src/cpp/ripple/SerializedTransaction.cpp b/src/cpp/ripple/SerializedTransaction.cpp index 285b5d58cd..7486e0329d 100644 --- a/src/cpp/ripple/SerializedTransaction.cpp +++ b/src/cpp/ripple/SerializedTransaction.cpp @@ -190,12 +190,19 @@ void SerializedTransaction::setSourceAccount(const RippleAddress& naSource) setFieldAccount(sfAccount, naSource); } -Json::Value SerializedTransaction::getJson(int options) const +Json::Value SerializedTransaction::getJson(int options, bool binary) const { + if (binary) + { + Json::Value ret; + Serializer s = STObject::getSerializer(); + ret["tx"] = strHex(s.peekData()); + ret["hash"] = getTransactionID().GetHex(); + return ret; + } + Json::Value ret = STObject::getJson(0); - ret["hash"] = getTransactionID().GetHex(); - return ret; } diff --git a/src/cpp/ripple/SerializedTransaction.h b/src/cpp/ripple/SerializedTransaction.h index 91186d3969..8727f19bef 100644 --- a/src/cpp/ripple/SerializedTransaction.h +++ b/src/cpp/ripple/SerializedTransaction.h @@ -64,7 +64,7 @@ public: uint256 getTransactionID() const; - virtual Json::Value getJson(int options) const; + virtual Json::Value getJson(int options, bool binary = false) const; void sign(const RippleAddress& naAccountPrivate); bool checkSign(const RippleAddress& naAccountPublic) const; diff --git a/src/cpp/ripple/Transaction.cpp b/src/cpp/ripple/Transaction.cpp index 3e1b0b4278..bbe9f19384 100644 --- a/src/cpp/ripple/Transaction.cpp +++ b/src/cpp/ripple/Transaction.cpp @@ -299,22 +299,20 @@ bool Transaction::convertToTransactions(uint32 firstLedgerSeq, uint32 secondLedg } // options 1 to include the date of the transaction -Json::Value Transaction::getJson(int options) const +Json::Value Transaction::getJson(int options, bool binary) const { - Json::Value ret(mTransaction->getJson(0)); + Json::Value ret(mTransaction->getJson(0, binary)); if (mInLedger) { - ret["inLedger"]=mInLedger; + ret["inLedger"] = mInLedger; - if(options==1) + if(options == 1) { Ledger::pointer ledger=theApp->getLedgerMaster().getLedgerBySeq(mInLedger); if(ledger) - { - ret["date"]=ledger->getCloseTimeNC(); - } + ret["date"] = ledger->getCloseTimeNC(); } } diff --git a/src/cpp/ripple/Transaction.h b/src/cpp/ripple/Transaction.h index e15bf0477f..d000dea5aa 100644 --- a/src/cpp/ripple/Transaction.h +++ b/src/cpp/ripple/Transaction.h @@ -113,7 +113,7 @@ public: bool operator<=(const Transaction&) const; bool operator>=(const Transaction&) const; - Json::Value getJson(int options) const; + Json::Value getJson(int options, bool binary = false) const; static bool isHexTxID(const std::string&);