diff --git a/bin/browser.js b/bin/browser.js new file mode 100755 index 0000000000..ab33850d05 --- /dev/null +++ b/bin/browser.js @@ -0,0 +1,263 @@ +#!/usr/bin/node +// +// ledger_header?l=L +// transaction?h=H +// ledger_entry?l=L&h=H +// account_root?l=L&a=A +// directory?l=L&dir_root=H&i=I +// directory?l=L&o=A&i=I // owner directory +// offer?l=L&offer=H +// offer?l=L&account=A&i=I +// ripple_state=l=L&a=A&b=A&c=C +// account_lines?l=L&a=A +// +// A=address +// C=currency 3 letter code +// H=hash +// I=index +// L=current | closed | validated | index | hash +// + +var extend = require("extend"); +var http = require("http"); +var url = require("url"); + +var Remote = require("../src/js/remote.js").Remote; + +var program = process.argv[1]; + +// Build a link to a type. +var build_uri = function (params, opts) { + var c; + + if (params.type === 'account_root') { + c = { + pathname: 'account_root', + query: { + l: params.ledger, + a: params.account, + }, + }; + + } else if (params.type === 'ledger_header') { + c = { + pathname: 'ledger_header', + query: { + l: params.ledger, + }, + }; + + } else if (params.type === 'transaction') { + c = { + pathname: 'transaction', + query: { + h: params.hash, + }, + }; + } else { + c = {}; + } + + c.protocol = "http"; + c.hostname = opts.hostname; + c.port = opts.port; + + return url.format(c); +}; + +var build_link = function (item, link) { +console.log(link); + return "" + item + ""; +}; + +var rewrite_object = function (obj, opts) { + var out = extend({}, obj); + + if ('ledger_index' in obj) { + out.ledger_index = + build_link( + obj.ledger_index, + build_uri({ + type: 'ledger_header', + ledger: obj.ledger_index, + }, opts) + ); + } + + if ('node' in obj) { + if (obj.node.LedgerEntryType === 'AccountRoot') { + out.node.PreviousTxnID = + build_link( + obj.node.PreviousTxnID, + build_uri({ + type: 'transaction', + hash: obj.node.PreviousTxnID, + }, opts) + ); + } + } + + return out; +}; + +if (process.argv.length < 4 || process.argv.length > 7) { + console.log("Usage: %s ws_ip ws_port [ [ []]]", program); +} +else { + var ws_ip = process.argv[2]; + var ws_port = process.argv[3]; + var ip = process.argv.length > 4 ? process.argv[4] : "127.0.0.1"; + var port = process.argv.length > 5 ? process.argv[5] : "8080"; + +console.log("START"); + var self = this; + + self.base = { + hostname: ip, + port: port, + }; + + var remote = (new Remote({ + websocket_ip: ws_ip, + websocket_port: ws_port, + trace: true + })) + .on('state', function (m) { + console.log("STATE: %s", m); + + self.state = m; + }) +// .once('ledger_closed', callback) + .connect() + ; + +console.log("SERVE"); + var server = http.createServer(function (req, res) { + var input = ""; + + req.setEncoding(); + + req.on('data', function (buffer) { + // console.log("DATA: %s", buffer); + input = input + buffer; + }); + + req.on('end', function () { + console.log("URL: %s", req.url); + // console.log("HEADERS: %s", JSON.stringify(req.headers, undefined, 2)); + + var _parsed = url.parse(req.url, true); + var _url = JSON.stringify(_parsed, undefined, 2); + + if (_parsed.pathname === "/account_root") { + var request = remote + .request_ledger_entry('account_root') + .ledger_index(-1) + .account_root(_parsed.query.a) + .on('success', function (m) { + console.log("account_root: %s", JSON.stringify(m, undefined, 2)); + + res.statusCode = 200; + res.end( + "" + + "Title" + + "" + + "State: " + self.state + + "" + + "
"
+                        + JSON.stringify(rewrite_object(m, self.base), undefined, 2)
+                        + "
" + + "" + + "" + ); + }) + .request(); + + } else if (_parsed.pathname === "/ledger_header") { + var request = remote + .request_ledger_header() + .ledger_index(-1) + .on('success', function (m) { + console.log("Ledger: %s", JSON.stringify(m, undefined, 2)); + + res.statusCode = 200; + res.end( + "" + + "Title" + + "" + + "State: " + self.state + + "" + + "
"
+                        + JSON.stringify(m, undefined, 2)
+                        + "
" + + "" + + "" + ); + }) + .request(); + + } else if (_parsed.pathname === "/transaction") { + var request = remote + .request_transaction_entry(_parsed.query.h) +// .ledger_select(_parsed.query.l) + .on('success', function (m) { + console.log("transaction: %s", JSON.stringify(m, undefined, 2)); + + res.statusCode = 200; + res.end( + "" + + "Title" + + "" + + "State: " + self.state + + "" + + "
"
+                        + JSON.stringify(rewrite_object(m, self.base), undefined, 2)
+                        + "
" + + "" + + "" + ); + }) + .request(); + + } else { + var test = build_uri({ + type: 'account_root', + ledger: 'closed', + account: 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', + }, self.base); + + res.statusCode = req.url === "/" ? 200 : 404; + res.end( + "" + + "Title" + + "" + + "State: " + self.state + + "" + + "
"+_url+"
" + + "" + + "" + ); + } + }); + }); + + server.listen(port, ip, undefined, + function () { + console.log("Listening at: http://%s:%s", ip, port); + }); +} + +// vim:sw=2:sts=2:ts=8:et diff --git a/newcoin.vcxproj b/newcoin.vcxproj index 72e758a479..bf9067b2a4 100644 --- a/newcoin.vcxproj +++ b/newcoin.vcxproj @@ -96,6 +96,7 @@ + @@ -205,6 +206,7 @@ + diff --git a/newcoin.vcxproj.filters b/newcoin.vcxproj.filters index 365cb71098..98c8ac4639 100644 --- a/newcoin.vcxproj.filters +++ b/newcoin.vcxproj.filters @@ -345,6 +345,9 @@ Source Files + + Source Files + Source Files @@ -662,6 +665,9 @@ Header Files + + Header Files + Header Files diff --git a/package.json b/package.json index f8cee61ad4..bf860e7370 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ripple-lib", - "version": "0.7.0", + "version": "0.7.1", "description": "Ripple JavaScript client library", "files": [ diff --git a/ripple2010.vcxproj b/ripple2010.vcxproj index 18e3ea1d24..dbb3dd84b1 100644 --- a/ripple2010.vcxproj +++ b/ripple2010.vcxproj @@ -94,6 +94,7 @@ + diff --git a/ripple2010.vcxproj.filters b/ripple2010.vcxproj.filters index 77ef08370b..6ad621db5f 100644 --- a/ripple2010.vcxproj.filters +++ b/ripple2010.vcxproj.filters @@ -345,6 +345,9 @@ Source Files + + Source Files + Source Files diff --git a/src/cpp/database/SqliteDatabase.cpp b/src/cpp/database/SqliteDatabase.cpp index 35062d29e0..e9e70fed4a 100644 --- a/src/cpp/database/SqliteDatabase.cpp +++ b/src/cpp/database/SqliteDatabase.cpp @@ -223,7 +223,7 @@ void SqliteDatabase::doHook(const char *db, int pages) { walRunning = true; if (mWalQ) - mWalQ->addJob(jtWAL, boost::bind(&SqliteDatabase::runWal, this)); + mWalQ->addJob(jtWAL, std::string("WAL:") + db, boost::bind(&SqliteDatabase::runWal, this)); else boost::thread(boost::bind(&SqliteDatabase::runWal, this)).detach(); } diff --git a/src/cpp/ripple/AcceptedLedger.cpp b/src/cpp/ripple/AcceptedLedger.cpp new file mode 100644 index 0000000000..0043d332d5 --- /dev/null +++ b/src/cpp/ripple/AcceptedLedger.cpp @@ -0,0 +1,91 @@ +#include "AcceptedLedger.h" + +#include + +TaggedCache AcceptedLedger::ALCache("AcceptedLedger", 4, 60); + +ALTransaction::ALTransaction(uint32 seq, SerializerIterator& sit) +{ + Serializer txnSer(sit.getVL()); + SerializerIterator txnIt(txnSer); + + mTxn = boost::make_shared(boost::ref(txnIt)); + mRawMeta= sit.getVL(); + mMeta = boost::make_shared(mTxn->getTransactionID(), seq, mRawMeta); + mAffected = mMeta->getAffectedAccounts(); + mResult = mMeta->getResultTER(); +} + +ALTransaction::ALTransaction(SerializedTransaction::ref txn, TransactionMetaSet::ref met) : + mTxn(txn), mMeta(met), mAffected(met->getAffectedAccounts()) +{ + mResult = mMeta->getResultTER(); +} + +ALTransaction::ALTransaction(SerializedTransaction::ref txn, TER result) : + mTxn(txn), mResult(result), mAffected(txn->getMentionedAccounts()) +{ ; } + +std::string ALTransaction::getEscMeta() const +{ + assert(!mRawMeta.empty()); + return sqlEscape(mRawMeta); +} + +Json::Value ALTransaction::getJson(int j) const +{ + Json::Value ret(Json::objectValue); + ret["transaction"] = mTxn->getJson(j); + if (mMeta) + { + ret["meta"] = mMeta->getJson(j); + ret["raw_meta"] = strHex(mRawMeta); + } + ret["result"] = transHuman(mResult); + + if (!mAffected.empty()) + { + Json::Value affected(Json::arrayValue); + BOOST_FOREACH(const RippleAddress& ra, mAffected) + { + affected.append(ra.humanAccountID()); + } + ret["affected"] = affected; + } + + return ret; +} + +AcceptedLedger::AcceptedLedger(Ledger::ref ledger) : mLedger(ledger) +{ + SHAMap& txSet = *ledger->peekTransactionMap(); + for (SHAMapItem::pointer item = txSet.peekFirstItem(); !!item; item = txSet.peekNextItem(item->getTag())) + { + SerializerIterator sit(item->peekSerializer()); + insert(ALTransaction(ledger->getLedgerSeq(), sit)); + } +} + +AcceptedLedger::pointer AcceptedLedger::makeAcceptedLedger(Ledger::ref ledger) +{ + AcceptedLedger::pointer ret = ALCache.fetch(ledger->getHash()); + if (ret) + return ret; + ret = AcceptedLedger::pointer(new AcceptedLedger(ledger)); + ALCache.canonicalize(ledger->getHash(), ret); + return ret; +} + +void AcceptedLedger::insert(const ALTransaction& at) +{ + assert(mMap.find(at.getIndex()) == mMap.end()); + mMap.insert(std::make_pair(at.getIndex(), at)); +} + +const ALTransaction* AcceptedLedger::getTxn(int i) const +{ + map_t::const_iterator it = mMap.find(i); + if (it == mMap.end()) + return NULL; + return &it->second; +} diff --git a/src/cpp/ripple/AcceptedLedger.h b/src/cpp/ripple/AcceptedLedger.h new file mode 100644 index 0000000000..e3237cd238 --- /dev/null +++ b/src/cpp/ripple/AcceptedLedger.h @@ -0,0 +1,70 @@ +#ifndef ACCEPTED_LEDGER__H +#define ACCEPTED_LEDGER__H + +#include "SerializedTransaction.h" +#include "TransactionMeta.h" +#include "Ledger.h" + + +class ALTransaction +{ +protected: + SerializedTransaction::pointer mTxn; + TransactionMetaSet::pointer mMeta; + TER mResult; + std::vector mAffected; + std::vector mRawMeta; + +public: + + ALTransaction(uint32 ledgerSeq, SerializerIterator& sit); + ALTransaction(SerializedTransaction::ref, TransactionMetaSet::ref); + ALTransaction(SerializedTransaction::ref, TER result); + + SerializedTransaction::ref getTxn() const { return mTxn; } + TransactionMetaSet::ref getMeta() const { return mMeta; } + const std::vector& getAffected() const { return mAffected; } + + uint256 getTransactionID() const { return mTxn->getTransactionID(); } + TransactionType getTxnType() const { return mTxn->getTxnType(); } + TER getResult() const { return mResult; } + + bool isApplied() const { return !!mMeta; } + int getIndex() const { return mMeta ? mMeta->getIndex() : 0; } + std::string getEscMeta() const; + Json::Value getJson(int) const; +}; + +class AcceptedLedger +{ +public: + typedef boost::shared_ptr pointer; + typedef const pointer& ret; + typedef std::map map_t; + typedef map_t::value_type value_type; + typedef map_t::const_iterator const_iterator; + +protected: + Ledger::pointer mLedger; + map_t mMap; + + void insert(const ALTransaction&); + + static TaggedCache ALCache; + AcceptedLedger(Ledger::ref ledger); + +public: + + static pointer makeAcceptedLedger(Ledger::ref ledger); + static void sweep() { ALCache.sweep(); } + + Ledger::ref getLedger() const { return mLedger; } + const map_t& getMap() const { return mMap; } + + int getLedgerSeq() const { return mLedger->getLedgerSeq(); } + int getTxnCount() const { return mMap.size(); } + + const ALTransaction* getTxn(int) const; +}; + +#endif diff --git a/src/cpp/ripple/Application.cpp b/src/cpp/ripple/Application.cpp index 3081fb3743..1d6773dcae 100644 --- a/src/cpp/ripple/Application.cpp +++ b/src/cpp/ripple/Application.cpp @@ -1,5 +1,6 @@ #include "Application.h" +#include "AcceptedLedger.h" #include "Config.h" #include "PeerDoor.h" #include "RPCDoor.h" @@ -45,7 +46,7 @@ Application::Application() : mSNTPClient(mAuxService), mRPCHandler(&mNetOps), mFeeTrack(), mRpcDB(NULL), mTxnDB(NULL), mLedgerDB(NULL), mWalletDB(NULL), mHashNodeDB(NULL), mNetNodeDB(NULL), mConnectionPool(mIOService), mPeerDoor(NULL), mRPCDoor(NULL), mWSPublicDoor(NULL), mWSPrivateDoor(NULL), - mSweepTimer(mAuxService) + mSweepTimer(mAuxService), mShutdown(false) { getRand(mNonce256.begin(), mNonce256.size()); getRand(reinterpret_cast(&mNonceST), sizeof(mNonceST)); @@ -58,6 +59,7 @@ bool Instance::running = true; void Application::stop() { cLog(lsINFO) << "Received shutdown request"; + mShutdown = true; mIOService.stop(); mHashedObjectStore.bulkWrite(); mValidations.flush(); @@ -302,6 +304,7 @@ void Application::sweep() mValidations.sweep(); getMasterLedgerAcquire().sweep(); mSLECache.sweep(); + AcceptedLedger::sweep(); mSweepTimer.expires_from_now(boost::posix_time::seconds(theConfig.getSize(siSweepInterval))); mSweepTimer.async_wait(boost::bind(&Application::sweep, this)); } diff --git a/src/cpp/ripple/Application.h b/src/cpp/ripple/Application.h index b8ef316499..cafa5b7c77 100644 --- a/src/cpp/ripple/Application.h +++ b/src/cpp/ripple/Application.h @@ -87,6 +87,8 @@ class Application std::map mPeerMap; boost::recursive_mutex mPeerMapLock; + volatile bool mShutdown; + void startNewLedger(); void loadOldLedger(const std::string&); @@ -140,6 +142,7 @@ public: uint256 getNonce256() { return mNonce256; } std::size_t getNonceST() { return mNonceST; } + bool isShutdown() { return mShutdown; } void setup(); void run(); void stop(); diff --git a/src/cpp/ripple/CallRPC.cpp b/src/cpp/ripple/CallRPC.cpp index 50cd485fd6..e4a71d56fe 100644 --- a/src/cpp/ripple/CallRPC.cpp +++ b/src/cpp/ripple/CallRPC.cpp @@ -1,3 +1,9 @@ +// +// This a trusted interface, the user is expected to provide valid input to perform valid requests. +// Error catching and reporting is not a requirement of this command line interface. +// +// Improvements to be more strict and to provide better diagnostics are welcome. +// #include #include @@ -9,6 +15,7 @@ #include #include #include +#include #include #include @@ -55,6 +62,63 @@ std::string EncodeBase64(const std::string& s) return result; } +// TODO New routine for parsing ledger parameters, other routines should standardize on this. +static bool jvParseLedger(Json::Value& jvRequest, const std::string& strLedger) +{ + if (strLedger == "closed") + { + jvRequest["ledger_index"] = -1; + } + else if (strLedger == "current") + { + jvRequest["ledger_index"] = -2; + } + else if (strLedger == "validated") + { + jvRequest["ledger_index"] = -3; + } + else if (strLedger.length() > 12) + { + // YYY Could confirm this is a uint256. + jvRequest["ledger_hash"] = strLedger; + } + else + { + jvRequest["ledger_index"] = lexical_cast_s(strLedger); + } + + return true; +} + +// Build a object { "currency" : "XYZ", "issuer" : "rXYX" } +static Json::Value jvParseCurrencyIssuer(const std::string& strCurrencyIssuer) +{ + static boost::regex reCurIss("\\`([[:alpha:]]{3})(?:/(.+))?\\'"); + + boost::smatch smMatch; + + if (boost::regex_match(strCurrencyIssuer, smMatch, reCurIss)) + { + Json::Value jvResult(Json::objectValue); + std::string strCurrency = smMatch[1]; + std::string strIssuer = smMatch[2]; + + jvResult["currency"] = strCurrency; + + if (strIssuer.length()) + { + // Could confirm issuer is a valid Ripple address. + jvResult["issuer"] = strIssuer; + } + + return jvResult; + } + else + { + return rpcError(rpcINVALID_PARAMS); + } +} + Json::Value RPCParser::parseAsIs(const Json::Value& jvParams) { Json::Value v(Json::objectValue); @@ -145,6 +209,63 @@ Json::Value RPCParser::parseAccountTransactions(const Json::Value& jvParams) return jvRequest; } +// book_offers [ [ [ [ []]]]] +// limit: 0 = no limit +// proof: 0 or 1 +// +// Mnemonic: taker pays --> offer --> taker gets +Json::Value RPCParser::parseBookOffers(const Json::Value& jvParams) +{ + Json::Value jvRequest(Json::objectValue); + + Json::Value jvTakerPays = jvParseCurrencyIssuer(jvParams[0u].asString()); + Json::Value jvTakerGets = jvParseCurrencyIssuer(jvParams[1u].asString()); + + if (isRpcError(jvTakerPays)) + { + return jvTakerPays; + } + else + { + jvRequest["taker_pays"] = jvTakerPays; + } + + if (isRpcError(jvTakerGets)) + { + return jvTakerGets; + } + else + { + jvRequest["taker_gets"] = jvTakerGets; + } + + if (jvParams.size() >= 3) + { + jvRequest["issuer"] = jvParams[2u].asString(); + } + + if (jvParams.size() >= 4 && !jvParseLedger(jvRequest, jvParams[3u].asString())) + return jvRequest; + + if (jvParams.size() >= 5) + { + int iLimit = jvParams[5u].asInt(); + + if (iLimit > 0) + jvRequest["limit"] = iLimit; + } + + if (jvParams.size() >= 6 && jvParams[5u].asInt()) + { + jvRequest["proof"] = true; + } + + if (jvParams.size() == 7) + jvRequest["marker"] = jvParams[6u]; + + return jvRequest; +} + // connect [port] Json::Value RPCParser::parseConnect(const Json::Value& jvParams) { @@ -509,11 +630,11 @@ Json::Value RPCParser::parseCommand(std::string strMethod, Json::Value jvParams) // Request-response methods // - Returns an error, or the request. // - To modify the method, provide a new method in the request. - { "accept_ledger", &RPCParser::parseAsIs, 0, 0 }, { "account_info", &RPCParser::parseAccountInfo, 1, 2 }, { "account_lines", &RPCParser::parseAccountItems, 1, 2 }, { "account_offers", &RPCParser::parseAccountItems, 1, 2 }, { "account_tx", &RPCParser::parseAccountTransactions, 2, 4 }, + { "book_offers", &RPCParser::parseBookOffers, 2, 7 }, { "connect", &RPCParser::parseConnect, 1, 2 }, { "consensus_info", &RPCParser::parseAsIs, 0, 0 }, { "get_counts", &RPCParser::parseGetCounts, 0, 1 }, @@ -622,7 +743,7 @@ int commandLineRPC(const std::vector& vCmd) jvRequest = rpParser.parseCommand(vCmd[0], jvRpcParams); - // std::cerr << "Request: " << jvRequest << std::endl; + cLog(lsTRACE) << "RPC Request: " << jvRequest << std::endl; if (jvRequest.isMember("error")) { @@ -756,6 +877,8 @@ bool responseRPC( // Build the request. void requestRPC(const std::string& strMethod, const Json::Value& jvParams, const std::map& mHeaders, const std::string& strPath, boost::asio::streambuf& sb, const std::string& strHost) { + cLog(lsDEBUG) << "requestRPC: strPath='" << strPath << "'"; + std::ostream osRequest(&sb); osRequest << @@ -804,7 +927,7 @@ void callRPC( strMethod, jvParams, mapRequestHeaders, - "/", _1, _2), + strPath, _1, _2), RPC_REPLY_MAX_BYTES, boost::posix_time::seconds(RPC_NOTIFY_SECONDS), boost::bind(&responseRPC, callbackFuncP, _1, _2, _3)); diff --git a/src/cpp/ripple/CallRPC.h b/src/cpp/ripple/CallRPC.h index 11cf518128..adde89f708 100644 --- a/src/cpp/ripple/CallRPC.h +++ b/src/cpp/ripple/CallRPC.h @@ -14,6 +14,7 @@ protected: Json::Value parseAccountItems(const Json::Value& jvParams); Json::Value parseAccountTransactions(const Json::Value& jvParams); Json::Value parseAsIs(const Json::Value& jvParams); + Json::Value parseBookOffers(const Json::Value& jvParams); Json::Value parseConnect(const Json::Value& jvParams); #if ENABLE_INSECURE Json::Value parseDataDelete(const Json::Value& jvParams); diff --git a/src/cpp/ripple/ConnectionPool.cpp b/src/cpp/ripple/ConnectionPool.cpp index 7bbfc9e158..3d184d4cda 100644 --- a/src/cpp/ripple/ConnectionPool.cpp +++ b/src/cpp/ripple/ConnectionPool.cpp @@ -235,14 +235,10 @@ void ConnectionPool::policyHandler(const boost::system::error_code& ecResult) int ConnectionPool::relayMessage(Peer* fromPeer, const PackedMessage::pointer& msg) { int sentTo = 0; - boost::recursive_mutex::scoped_lock sl(mPeerLock); - - BOOST_FOREACH(const vtConMap& pair, mConnectedMap) + std::vector peerVector = getPeerVector(); + BOOST_FOREACH(Peer::ref peer, peerVector) { - Peer::ref peer = pair.second; - if (!peer) - std::cerr << "CP::RM null peer in list" << std::endl; - else if ((!fromPeer || !(peer.get() == fromPeer)) && peer->isConnected()) + if ((!fromPeer || !(peer.get() == fromPeer)) && peer->isConnected()) { ++sentTo; peer->sendPacket(msg); @@ -254,11 +250,9 @@ int ConnectionPool::relayMessage(Peer* fromPeer, const PackedMessage::pointer& m void ConnectionPool::relayMessageBut(const std::set& fromPeers, const PackedMessage::pointer& msg) { // Relay message to all but the specified peers - boost::recursive_mutex::scoped_lock sl(mPeerLock); - - BOOST_FOREACH(const vtConMap& pair, mConnectedMap) + std::vector peerVector = getPeerVector(); + BOOST_FOREACH(Peer::ref peer, peerVector) { - Peer::ref peer = pair.second; if (peer->isConnected() && (fromPeers.count(peer->getPeerId()) == 0)) peer->sendPacket(msg); } @@ -267,13 +261,11 @@ void ConnectionPool::relayMessageBut(const std::set& fromPeers, const Pa void ConnectionPool::relayMessageTo(const std::set& fromPeers, const PackedMessage::pointer& msg) { // Relay message to the specified peers - boost::recursive_mutex::scoped_lock sl(mPeerLock); - - BOOST_FOREACH(const uint64& peerID, fromPeers) + std::vector peerVector = getPeerVector(); + BOOST_FOREACH(Peer::ref peer, peerVector) { - const boost::unordered_map::iterator& it = mPeerIdMap.find(peerID); - if ((it != mPeerIdMap.end()) && it->second->isConnected()) - it->second->sendPacket(msg); + if (peer->isConnected() && (fromPeers.count(peer->getPeerId()) != 0)) + peer->sendPacket(msg); } } diff --git a/src/cpp/ripple/DBInit.cpp b/src/cpp/ripple/DBInit.cpp index bdc6c29b5e..fcf5a3a84a 100644 --- a/src/cpp/ripple/DBInit.cpp +++ b/src/cpp/ripple/DBInit.cpp @@ -20,7 +20,7 @@ const char *TxnDBInit[] = { TxnMeta BLOB \ );", "CREATE TABLE AccountTransactions ( \ - TransID CHARACTER(64), \ + TransID CHARACTER(64) PRIMARY KEY, \ Account CHARACTER(64), \ LedgerSeq BIGINT UNSIGNED \ );", diff --git a/src/cpp/ripple/FieldNames.cpp b/src/cpp/ripple/FieldNames.cpp index 6bd34da299..18271e48c7 100644 --- a/src/cpp/ripple/FieldNames.cpp +++ b/src/cpp/ripple/FieldNames.cpp @@ -12,6 +12,7 @@ // These must stay at the top of this file std::map SField::codeToField; boost::mutex SField::mapMutex; +int SField::num = 0; SField sfInvalid(-1), sfGeneric(0); SField sfLedgerEntry(STI_LEDGERENTRY, 1, "LedgerEntry"); diff --git a/src/cpp/ripple/FieldNames.h b/src/cpp/ripple/FieldNames.h index 4919569e15..b2ae08aa72 100644 --- a/src/cpp/ripple/FieldNames.h +++ b/src/cpp/ripple/FieldNames.h @@ -51,6 +51,7 @@ public: protected: static std::map codeToField; static boost::mutex mapMutex; + static int num; SField(SerializedTypeID id, int val); @@ -61,6 +62,7 @@ public: const int fieldValue; // Code number for protocol std::string fieldName; int fieldMeta; + int fieldNum; bool signingField; SField(int fc, SerializedTypeID tid, int fv, const char* fn) : @@ -68,6 +70,7 @@ public: { boost::mutex::scoped_lock sl(mapMutex); codeToField[fieldCode] = this; + fieldNum = ++num; } SField(SerializedTypeID tid, int fv, const char *fn) : @@ -76,9 +79,14 @@ public: { boost::mutex::scoped_lock sl(mapMutex); codeToField[fieldCode] = this; + fieldNum = ++num; } - SField(int fc) : fieldCode(fc), fieldType(STI_UNKNOWN), fieldValue(0), fieldMeta(sMD_Never) { ; } + SField(int fc) : fieldCode(fc), fieldType(STI_UNKNOWN), fieldValue(0), fieldMeta(sMD_Never) + { + boost::mutex::scoped_lock sl(mapMutex); + fieldNum = ++num; + } ~SField(); @@ -96,6 +104,9 @@ public: bool isKnown() const { return fieldType != STI_UNKNOWN; } bool isBinary() const { return fieldValue < 256; } bool isDiscardable() const { return fieldValue > 256; } + int getCode() const { return fieldCode; } + int getNum() const { return fieldNum; } + static int getNumFields() { return num; } bool isSigningField() const { return signingField; } void notSigningField() { signingField = false; } diff --git a/src/cpp/ripple/HashedObject.cpp b/src/cpp/ripple/HashedObject.cpp index 87481802ba..3515b5f55c 100644 --- a/src/cpp/ripple/HashedObject.cpp +++ b/src/cpp/ripple/HashedObject.cpp @@ -51,7 +51,8 @@ bool HashedObjectStore::store(HashedObjectType type, uint32 index, if (!mWritePending) { mWritePending = true; - theApp->getJobQueue().addJob(jtWRITE, boost::bind(&HashedObjectStore::bulkWrite, this)); + theApp->getJobQueue().addJob(jtWRITE, "HashedObject::store", + boost::bind(&HashedObjectStore::bulkWrite, this)); } } // else @@ -113,7 +114,6 @@ void HashedObjectStore::bulkWrite() default: type = "U"; } - pSt.reset(); pSt.bind(1, it->getHash().GetHex()); pSt.bind(2, type); pSt.bind(3, it->getIndex()); @@ -124,6 +124,7 @@ void HashedObjectStore::bulkWrite() cLog(lsFATAL) << "Error saving hashed object " << ret; assert(false); } + pSt.reset(); } db->executeSQL("END TRANSACTION;"); @@ -186,12 +187,12 @@ HashedObject::pointer HashedObjectStore::retrieve(const uint256& hash) static SqliteStatement pSt(theApp->getHashNodeDB()->getDB()->getSqliteDB(), "SELECT ObjType,LedgerIndex,Object FROM CommittedObjects WHERE Hash = ?;"); - pSt.reset(); pSt.bind(1, hash.GetHex()); int ret = pSt.step(); if (pSt.isDone(ret)) { + pSt.reset(); mNegativeCache.add(hash); cLog(lsTRACE) << "HOS: " << hash <<" fetch: not in db"; return obj; @@ -200,6 +201,7 @@ HashedObject::pointer HashedObjectStore::retrieve(const uint256& hash) type = pSt.peekString(0); index = pSt.getUInt32(1); pSt.getBlob(2).swap(data); + pSt.reset(); } #else diff --git a/src/cpp/ripple/HttpsClient.cpp b/src/cpp/ripple/HttpsClient.cpp index 4cbb32ea15..1ae01e19f2 100644 --- a/src/cpp/ripple/HttpsClient.cpp +++ b/src/cpp/ripple/HttpsClient.cpp @@ -370,10 +370,14 @@ void HttpsClient::invokeComplete(const boost::system::error_code& ecResult, int if (ecCancel) { - cLog(lsTRACE) << "Deadline cancel error: " << ecCancel.message(); + cLog(lsTRACE) << "HttpsClient::invokeComplete: Deadline cancel error: " << ecCancel.message(); } - mDeqSites.pop_front(); + cLog(lsDEBUG) << "HttpsClient::invokeComplete: Deadline popping: " << mDeqSites.size(); + if (!mDeqSites.empty()) + { + mDeqSites.pop_front(); + } bool bAgain = true; diff --git a/src/cpp/ripple/InstanceCounter.h b/src/cpp/ripple/InstanceCounter.h index 48f82d0332..95301cbbfa 100644 --- a/src/cpp/ripple/InstanceCounter.h +++ b/src/cpp/ripple/InstanceCounter.h @@ -50,6 +50,11 @@ public: sMultiThreaded = true; } + static bool isMultiThread() + { + return sMultiThreaded; + } + void addInstance() { if (sMultiThreaded) diff --git a/src/cpp/ripple/JobQueue.cpp b/src/cpp/ripple/JobQueue.cpp index cc2aadb40b..c673a45577 100644 --- a/src/cpp/ripple/JobQueue.cpp +++ b/src/cpp/ripple/JobQueue.cpp @@ -98,7 +98,7 @@ bool Job::operator<=(const Job& j) const return mJobIndex <= j.mJobIndex; } -void JobQueue::addJob(JobType type, const boost::function& jobFunc) +void JobQueue::addJob(JobType type, const std::string& name, const boost::function& jobFunc) { assert(type != jtINVALID); @@ -107,7 +107,7 @@ void JobQueue::addJob(JobType type, const boost::function& jobFunc) if (type != jtCLIENT) // FIXME: Workaround incorrect client shutdown ordering assert(mThreadCount != 0); // do not add jobs to a queue with no threads - mJobSet.insert(Job(type, ++mLastJob, mJobLoads[type], jobFunc)); + mJobSet.insert(Job(type, name, ++mLastJob, mJobLoads[type], jobFunc)); ++mJobCounts[type]; mJobCond.notify_one(); } @@ -176,9 +176,9 @@ Json::Value JobQueue::getJson(int) if (count != 0) pri["per_second"] = static_cast(count); if (latencyPeak != 0) - pri["peak_latency"] = static_cast(latencyPeak); + pri["peak_time"] = static_cast(latencyPeak); if (latencyAvg != 0) - pri["avg_latency"] = static_cast(latencyAvg); + pri["avg_time"] = static_cast(latencyAvg); priorities.append(pri); } } @@ -256,16 +256,18 @@ void JobQueue::threadEntry() break; std::set::iterator it = mJobSet.begin(); - Job job(*it); - mJobSet.erase(it); - --mJobCounts[job.getType()]; + { + Job job(*it); + mJobSet.erase(it); + --mJobCounts[job.getType()]; - if (job.getType() == jtDEATH) - break; + if (job.getType() == jtDEATH) + break; - sl.unlock(); - cLog(lsTRACE) << "Doing " << Job::toString(job.getType()) << " job"; - job.doJob(); + sl.unlock(); + cLog(lsTRACE) << "Doing " << Job::toString(job.getType()) << " job"; + job.doJob(); + } // must destroy job without holding lock sl.lock(); } --mThreadCount; diff --git a/src/cpp/ripple/JobQueue.h b/src/cpp/ripple/JobQueue.h index 10dfbc7ddc..386ccf5ff4 100644 --- a/src/cpp/ripple/JobQueue.h +++ b/src/cpp/ripple/JobQueue.h @@ -54,6 +54,7 @@ protected: uint64 mJobIndex; boost::function mJob; LoadEvent::pointer mLoadMonitor; + std::string mName; public: @@ -62,12 +63,15 @@ public: Job(JobType type, uint64 index) : mType(type), mJobIndex(index) { ; } - Job(JobType type, uint64 index, LoadMonitor& lm, const boost::function& job) - : mType(type), mJobIndex(index), mJob(job) - { mLoadMonitor = boost::make_shared(boost::ref(lm), true, 1); } + Job(JobType type, const std::string& name, uint64 index, LoadMonitor& lm, const boost::function& job) + : mType(type), mJobIndex(index), mJob(job), mName(name) + { + mLoadMonitor = boost::make_shared(boost::ref(lm), false, 1); + } JobType getType() const { return mType; } - void doJob(void) { mJob(*this); } + void doJob(void) { mLoadMonitor->start(); mJob(*this); mLoadMonitor->setName(mName); } + void rename(const std::string& n) { mName = n; } bool operator<(const Job& j) const; bool operator>(const Job& j) const; @@ -97,7 +101,7 @@ public: JobQueue(); - void addJob(JobType type, const boost::function& job); + void addJob(JobType type, const std::string& name, const boost::function& job); int getJobCount(JobType t); // Jobs at this priority int getJobCountGE(JobType t); // All jobs at or greater than this priority diff --git a/src/cpp/ripple/Ledger.cpp b/src/cpp/ripple/Ledger.cpp index a43d2bee44..3937964eb7 100644 --- a/src/cpp/ripple/Ledger.cpp +++ b/src/cpp/ripple/Ledger.cpp @@ -403,9 +403,9 @@ uint256 Ledger::getHash() void Ledger::saveAcceptedLedger(bool fromConsensus, LoadEvent::pointer event) { // can be called in a different thread cLog(lsTRACE) << "saveAcceptedLedger " << (fromConsensus ? "fromConsensus " : "fromAcquire ") << getLedgerSeq(); - static boost::format ledgerExists("SELECT LedgerSeq FROM Ledgers where LedgerSeq = %d;"); + static boost::format ledgerExists("SELECT LedgerSeq FROM Ledgers INDEXED BY SeqLedger where LedgerSeq = %d;"); static boost::format deleteLedger("DELETE FROM Ledgers WHERE LedgerSeq = %d;"); - static boost::format AcctTransExists("SELECT LedgerSeq FROM AccountTransactions WHERE TransId = '%s';"); + static boost::format AcctTransExists("SELECT LedgerSeq FROM AccountTransactions WHERE TransID = '%s';"); static boost::format transExists("SELECT Status FROM Transactions WHERE TransID = '%s';"); static boost::format updateTx("UPDATE Transactions SET LedgerSeq = %d, Status = '%c', TxnMeta = %s WHERE TransID = '%s';"); @@ -428,6 +428,8 @@ void Ledger::saveAcceptedLedger(bool fromConsensus, LoadEvent::pointer event) addRaw(s); theApp->getHashedObjectStore().store(hotLEDGER, mLedgerSeq, s.peekData(), mHash); + AcceptedLedger::pointer aLedger = AcceptedLedger::makeAcceptedLedger(shared_from_this()); + { { ScopedLock sl(theApp->getLedgerDB()->getDBLock()); @@ -435,37 +437,26 @@ void Ledger::saveAcceptedLedger(bool fromConsensus, LoadEvent::pointer event) theApp->getLedgerDB()->getDB()->executeSQL(boost::str(deleteLedger % mLedgerSeq)); } - SHAMap& txSet = *peekTransactionMap(); Database *db = theApp->getTxnDB()->getDB(); { ScopedLock dbLock(theApp->getTxnDB()->getDBLock()); db->executeSQL("BEGIN TRANSACTION;"); - SHAMapTreeNode::TNType type; - for (SHAMapItem::pointer item = txSet.peekFirstItem(type); !!item; - item = txSet.peekNextItem(item->getTag(), type)) + + BOOST_FOREACH(const AcceptedLedger::value_type& vt, aLedger->getMap()) { - assert(type == SHAMapTreeNode::tnTRANSACTION_MD); - SerializerIterator sit(item->peekSerializer()); - Serializer rawTxn(sit.getVL()); - Serializer rawMeta(sit.getVL()); - std::string escMeta(sqlEscape(rawMeta.peekData())); - - SerializerIterator txnIt(rawTxn); - - SerializedTransaction txn(txnIt); - assert(txn.getTransactionID() == item->getTag()); - TransactionMetaSet meta(item->getTag(), mLedgerSeq, rawMeta.peekData()); - theApp->getMasterTransaction().inLedger(item->getTag(), mLedgerSeq); + cLog(lsTRACE) << "Saving: " << vt.second.getJson(0); + uint256 txID = vt.second.getTransactionID(); + theApp->getMasterTransaction().inLedger(txID, mLedgerSeq); // Make sure transaction is in AccountTransactions. - if (!SQL_EXISTS(db, boost::str(AcctTransExists % item->getTag().GetHex()))) + if (!SQL_EXISTS(db, boost::str(AcctTransExists % txID.GetHex()))) { // Transaction not in AccountTransactions - const std::vector accts = meta.getAffectedAccounts(); + const std::vector& accts = vt.second.getAffected(); if (!accts.empty()) { - std::string sql = "INSERT INTO AccountTransactions (TransID, Account, LedgerSeq) VALUES "; + std::string sql = "INSERT OR REPLACE INTO AccountTransactions (TransID, Account, LedgerSeq) VALUES "; bool first = true; for (std::vector::const_iterator it = accts.begin(), end = accts.end(); it != end; ++it) { @@ -476,7 +467,7 @@ void Ledger::saveAcceptedLedger(bool fromConsensus, LoadEvent::pointer event) sql += "('"; first = false; } - sql += txn.getTransactionID().GetHex(); + sql += txID.GetHex(); sql += "','"; sql += it->humanAccountID(); sql += "',"; @@ -491,20 +482,21 @@ void Ledger::saveAcceptedLedger(bool fromConsensus, LoadEvent::pointer event) cLog(lsWARNING) << "Transaction in ledger " << mLedgerSeq << " affects no accounts"; } - if (SQL_EXISTS(db, boost::str(transExists % txn.getTransactionID().GetHex()))) + if (SQL_EXISTS(db, boost::str(transExists % txID.GetHex()))) { // In Transactions, update LedgerSeq, metadata and Status. db->executeSQL(boost::str(updateTx % getLedgerSeq() % TXN_SQL_VALIDATED - % escMeta - % txn.getTransactionID().GetHex())); + % vt.second.getEscMeta() + % txID.GetHex())); } else { // Not in Transactions, insert the whole thing.. db->executeSQL( - txn.getMetaSQLInsertHeader() + txn.getMetaSQL(getLedgerSeq(), escMeta) + ";"); + SerializedTransaction::getMetaSQLInsertHeader() + + vt.second.getTxn()->getMetaSQL(getLedgerSeq(), vt.second.getEscMeta()) + ";"); } } db->executeSQL("COMMIT TRANSACTION;"); @@ -548,9 +540,9 @@ Ledger::pointer Ledger::loadByIndex(uint32 ledgerIndex) "ClosingTime,PrevClosingTime,CloseTimeRes,CloseFlags,LedgerSeq" " from Ledgers WHERE LedgerSeq = ?;"); - pSt.reset(); pSt.bind(1, ledgerIndex); ledger = getSQL1(&pSt); + pSt.reset(); } if (ledger) Ledger::getSQL2(ledger); @@ -569,9 +561,9 @@ Ledger::pointer Ledger::loadByHash(const uint256& ledgerHash) "ClosingTime,PrevClosingTime,CloseTimeRes,CloseFlags,LedgerSeq" " from Ledgers WHERE LedgerHash = ?;"); - pSt.reset(); pSt.bind(1, ledgerHash.GetHex()); ledger = getSQL1(&pSt); + pSt.reset(); } if (ledger) { @@ -729,19 +721,20 @@ bool Ledger::getHashesByIndex(uint32 ledgerIndex, uint256& ledgerHash, uint256& ScopedLock sl(con->getDBLock()); static SqliteStatement pSt(con->getDB()->getSqliteDB(), - "SELECT LedgerHash,PrevHash FROM Ledgers Where LedgerSeq = ?;"); + "SELECT LedgerHash,PrevHash FROM Ledgers INDEXED BY SeqLedger Where LedgerSeq = ?;"); - pSt.reset(); pSt.bind(1, ledgerIndex); int ret = pSt.step(); if (pSt.isDone(ret)) { + pSt.reset(); cLog(lsTRACE) << "Don't have ledger " << ledgerIndex; return false; } if (!pSt.isRow(ret)) { + pSt.reset(); assert(false); cLog(lsFATAL) << "Unexpected statement result " << ret; return false; @@ -749,6 +742,7 @@ bool Ledger::getHashesByIndex(uint32 ledgerIndex, uint256& ledgerHash, uint256& ledgerHash.SetHex(pSt.peekString(0), true); parentHash.SetHex(pSt.peekString(1), true); + pSt.reset(); return true; @@ -779,6 +773,45 @@ bool Ledger::getHashesByIndex(uint32 ledgerIndex, uint256& ledgerHash, uint256& #endif } +std::map< uint32, std::pair > Ledger::getHashesByIndex(uint32 minSeq, uint32 maxSeq) +{ +#ifndef NO_SQLITE_PREPARE + std::map< uint32, std::pair > ret; + DatabaseCon *con = theApp->getLedgerDB(); + ScopedLock sl(con->getDBLock()); + + static SqliteStatement pSt(con->getDB()->getSqliteDB(), + "SELECT LedgerSeq,LedgerHash,PrevHash FROM Ledgers INDEXED BY SeqLedger " + "WHERE LedgerSeq >= ? AND LedgerSeq <= ?;"); + + std::pair hashes; + + pSt.bind(1, minSeq); + pSt.bind(2, maxSeq); + + do + { + int r = pSt.step(); + if (pSt.isDone(r)) + { + pSt.reset(); + return ret; + } + if (!pSt.isRow(r)) + { + pSt.reset(); + return ret; + } + hashes.first.SetHex(pSt.peekString(1), true); + hashes.second.SetHex(pSt.peekString(2), true); + ret[pSt.getUInt32(0)] = hashes; + } while(1); + +#else +#error SQLite prepare is required +#endif +} + Ledger::pointer Ledger::getLastFullLedger() { try diff --git a/src/cpp/ripple/Ledger.h b/src/cpp/ripple/Ledger.h index 62de1c8195..34d88b8fd7 100644 --- a/src/cpp/ripple/Ledger.h +++ b/src/cpp/ripple/Ledger.h @@ -202,6 +202,7 @@ public: static Ledger::pointer loadByHash(const uint256& ledgerHash); static uint256 getHashByIndex(uint32 index); static bool getHashesByIndex(uint32 index, uint256& ledgerHash, uint256& parentHash); + static std::map< uint32, std::pair > getHashesByIndex(uint32 minSeq, uint32 maxSeq); void pendSave(bool fromConsensus); // next/prev function diff --git a/src/cpp/ripple/LedgerConsensus.cpp b/src/cpp/ripple/LedgerConsensus.cpp index 79fe9f9454..623cd542fa 100644 --- a/src/cpp/ripple/LedgerConsensus.cpp +++ b/src/cpp/ripple/LedgerConsensus.cpp @@ -392,7 +392,7 @@ void LedgerConsensus::checkLCL() { case lcsPRE_CLOSE: status = "PreClose"; break; case lcsESTABLISH: status = "Establish"; break; - case lcsFINISHED: status = "Finised"; break; + case lcsFINISHED: status = "Finished"; break; case lcsACCEPTED: status = "Accepted"; break; default: status = "unknown"; } @@ -400,6 +400,7 @@ void LedgerConsensus::checkLCL() cLog(lsWARNING) << "View of consensus changed during " << status << " (" << netLgrCount << ") status=" << status << ", " << (mHaveCorrectLCL ? "CorrectLCL" : "IncorrectLCL"); cLog(lsWARNING) << mPrevLedgerHash << " to " << netLgr; + cLog(lsWARNING) << mPreviousLedger->getJson(0); if (sLog(lsDEBUG)) { @@ -762,6 +763,7 @@ void LedgerConsensus::updateOurPositions() int neededWeight; + if (mClosePercent < AV_MID_CONSENSUS_TIME) neededWeight = AV_INIT_CONSENSUS_PCT; else if (mClosePercent < AV_LATE_CONSENSUS_TIME) @@ -769,41 +771,50 @@ void LedgerConsensus::updateOurPositions() else if (mClosePercent < AV_STUCK_CONSENSUS_TIME) neededWeight = AV_LATE_CONSENSUS_PCT; else - neededWeight = AV_STUCK_CONSENSUS_TIME; + neededWeight = AV_STUCK_CONSENSUS_PCT; uint32 closeTime = 0; mHaveCloseTimeConsensus = false; - int thresh = mPeerPositions.size(); - if (thresh == 0) + if (mPeerPositions.empty()) { // no other times mHaveCloseTimeConsensus = true; closeTime = roundCloseTime(mOurPosition->getCloseTime()); } else { + int threshVote = mPeerPositions.size(); // Threshold for non-zero vote + int threshConsensus = mPeerPositions.size(); // Threshold to declare consensus if (mProposing) { ++closeTimes[roundCloseTime(mOurPosition->getCloseTime())]; - ++thresh; + ++threshVote; + ++threshConsensus; } - thresh = ((thresh * neededWeight) + (neededWeight / 2)) / 100; - if (thresh == 0) - thresh = 1; + threshVote = ((threshVote * neededWeight) + (neededWeight / 2)) / 100; + threshConsensus = ((threshConsensus * AV_CT_CONSENSUS_PCT) + (AV_CT_CONSENSUS_PCT / 2)) / 100; + + if (threshVote == 0) + threshVote = 1; + if (threshConsensus == 0) + threshConsensus = 1; + cLog(lsINFO) << "Proposers:" << mPeerPositions.size() << " nw:" << neededWeight + << " thrV:" << threshVote << " thrC:" << threshConsensus; for (std::map::iterator it = closeTimes.begin(), end = closeTimes.end(); it != end; ++it) { - cLog(lsTRACE) << "CCTime: " << it->first << " has " << it->second << ", " << thresh << " required"; - if (it->second >= thresh) + cLog(lsDEBUG) << "CCTime: " << it->first << " has " << it->second << ", " << threshVote << " required"; + if (it->second >= threshVote) { cLog(lsDEBUG) << "Close time consensus reached: " << it->first; - mHaveCloseTimeConsensus = true; closeTime = it->first; - thresh = it->second; + threshVote = it->second; + if (threshVote >= threshConsensus) + mHaveCloseTimeConsensus = true; } } tLog(!mHaveCloseTimeConsensus, lsDEBUG) << "No CT consensus: Proposers:" << mPeerPositions.size() - << " Proposing:" << (mProposing ? "yes" : "no") << " Thresh:" << thresh << " Pos:" << closeTime; + << " Proposing:" << (mProposing ? "yes" : "no") << " Thresh:" << threshConsensus << " Pos:" << closeTime; } if (!changes && @@ -1044,7 +1055,10 @@ bool LedgerConsensus::peerHasSet(Peer::ref peer, const uint256& hashSet, ripple: set.push_back(peer); boost::unordered_map::iterator acq = mAcquiring.find(hashSet); if (acq != mAcquiring.end()) - acq->second->peerHas(peer); + { + TransactionAcquire::pointer ta = acq->second; // make sure it doesn't go away + ta->peerHas(peer); + } return true; } diff --git a/src/cpp/ripple/LedgerEntrySet.cpp b/src/cpp/ripple/LedgerEntrySet.cpp index e47ed099d9..09ecc4ab7e 100644 --- a/src/cpp/ripple/LedgerEntrySet.cpp +++ b/src/cpp/ripple/LedgerEntrySet.cpp @@ -1066,6 +1066,8 @@ STAmount LedgerEntrySet::rippleHolds(const uint160& uAccountID, const uint160& u return saBalance; } +// Returns the amount an account can spend without going into debt. +// // <-- saAmount: amount of uCurrencyID held by uAccountID. May be negative. STAmount LedgerEntrySet::accountHolds(const uint160& uAccountID, const uint160& uCurrencyID, const uint160& uIssuerID) { diff --git a/src/cpp/ripple/LedgerFormats.cpp b/src/cpp/ripple/LedgerFormats.cpp index c1c36bb7b8..fc6c70da33 100644 --- a/src/cpp/ripple/LedgerFormats.cpp +++ b/src/cpp/ripple/LedgerFormats.cpp @@ -11,7 +11,7 @@ std::map LedgerEntryFormat::byName; #define DECLARE_LEF(name, type) lef = new LedgerEntryFormat(#name, type); (*lef) LEF_BASE -static bool LEFInit() +void LEFInit() { LedgerEntryFormat* lef; @@ -111,12 +111,8 @@ static bool LEFInit() << SOElement(sfReserveBase, SOE_REQUIRED) << SOElement(sfReserveIncrement, SOE_REQUIRED) ; - - return true; } -bool LEFInitComplete = LEFInit(); - LedgerEntryFormat* LedgerEntryFormat::getLgrFormat(LedgerEntryType t) { std::map::iterator it = byType.find(static_cast(t)); diff --git a/src/cpp/ripple/LedgerFormats.h b/src/cpp/ripple/LedgerFormats.h index a491a58e3c..bfc692a669 100644 --- a/src/cpp/ripple/LedgerFormats.h +++ b/src/cpp/ripple/LedgerFormats.h @@ -58,7 +58,7 @@ class LedgerEntryFormat public: std::string t_name; LedgerEntryType t_type; - std::vector elements; + SOTemplate elements; static std::map byType; static std::map byName; @@ -70,7 +70,7 @@ public: } LedgerEntryFormat& operator<<(const SOElement& el) { - elements.push_back(new SOElement(el)); + elements.push_back(el); return *this; } diff --git a/src/cpp/ripple/LedgerMaster.cpp b/src/cpp/ripple/LedgerMaster.cpp index a3f35cdf26..fcafae0cb3 100644 --- a/src/cpp/ripple/LedgerMaster.cpp +++ b/src/cpp/ripple/LedgerMaster.cpp @@ -119,11 +119,11 @@ Ledger::pointer LedgerMaster::closeLedger(bool recover) return closingLedger; } -TER LedgerMaster::doTransaction(const SerializedTransaction& txn, TransactionEngineParams params, bool& didApply) +TER LedgerMaster::doTransaction(SerializedTransaction::ref txn, TransactionEngineParams params, bool& didApply) { - TER result = mEngine.applyTransaction(txn, params, didApply); - // CHECKME: Should we call this even on gross failures? - theApp->getOPs().pubProposedTransaction(mEngine.getLedger(), txn, result); + TER result = mEngine.applyTransaction(*txn, params, didApply); +// if (didApply) + theApp->getOPs().pubProposedTransaction(mEngine.getLedger(), txn, result); return result; } @@ -145,6 +145,8 @@ void LedgerMaster::asyncAccept(Ledger::pointer ledger) uint32 seq = ledger->getLedgerSeq(); uint256 prevHash = ledger->getParentHash(); + std::map< uint32, std::pair > ledgerHashes; + while (seq > 0) { { @@ -155,10 +157,20 @@ void LedgerMaster::asyncAccept(Ledger::pointer ledger) break; } - uint256 tHash, pHash; - if (!Ledger::getHashesByIndex(seq, tHash, pHash) || (tHash != prevHash)) + std::map< uint32, std::pair >::iterator it = ledgerHashes.find(seq); + if (it == ledgerHashes.end()) + { + if (theApp->isShutdown()) + return; + ledgerHashes = Ledger::getHashesByIndex((seq < 500) ? 0 : (seq - 499), seq); + it = ledgerHashes.find(seq); + if (it == ledgerHashes.end()) + break; + } + + if (it->second.first != prevHash) break; - prevHash = pHash; + prevHash = it->second.second; } resumeAcquiring(); @@ -174,7 +186,8 @@ bool LedgerMaster::acquireMissingLedger(Ledger::ref origLedger, const uint256& l { cLog(lsTRACE) << "Ledger hash found in database"; mTooFast = true; - theApp->getJobQueue().addJob(jtPUBOLDLEDGER, boost::bind(&LedgerMaster::asyncAccept, this, ledger)); + theApp->getJobQueue().addJob(jtPUBOLDLEDGER, "LedgerMaster::asyncAccept", + boost::bind(&LedgerMaster::asyncAccept, this, ledger)); return true; } @@ -528,7 +541,8 @@ void LedgerMaster::tryPublish() { theApp->getOPs().clearNeedNetworkLedger(); mPubThread = true; - theApp->getJobQueue().addJob(jtPUBLEDGER, boost::bind(&LedgerMaster::pubThread, this)); + theApp->getJobQueue().addJob(jtPUBLEDGER, "Ledger::pubThread", + boost::bind(&LedgerMaster::pubThread, this)); } } diff --git a/src/cpp/ripple/LedgerMaster.h b/src/cpp/ripple/LedgerMaster.h index d14ab11e43..c79c7e4702 100644 --- a/src/cpp/ripple/LedgerMaster.h +++ b/src/cpp/ripple/LedgerMaster.h @@ -75,7 +75,7 @@ public: // The published ledger is the last fully validated ledger Ledger::ref getValidatedLedger() { return mPubLedger; } - TER doTransaction(const SerializedTransaction& txn, TransactionEngineParams params, bool& didApply); + TER doTransaction(SerializedTransaction::ref txn, TransactionEngineParams params, bool& didApply); void pushLedger(Ledger::ref newLedger); void pushLedger(Ledger::ref newLCL, Ledger::ref newOL, bool fromConsensus); diff --git a/src/cpp/ripple/LedgerTiming.h b/src/cpp/ripple/LedgerTiming.h index db8fc8b942..c7539c081a 100644 --- a/src/cpp/ripple/LedgerTiming.h +++ b/src/cpp/ripple/LedgerTiming.h @@ -39,17 +39,18 @@ # define PROPOSE_INTERVAL 12 // Avalanche tuning -#define AV_INIT_CONSENSUS_PCT 50 // percentage of nodes on our UNL that must vote yes +# define AV_INIT_CONSENSUS_PCT 50 // percentage of nodes on our UNL that must vote yes -#define AV_MID_CONSENSUS_TIME 50 // percentage of previous close time before we advance -#define AV_MID_CONSENSUS_PCT 65 // percentage of nodes that most vote yes after advancing +# define AV_MID_CONSENSUS_TIME 50 // percentage of previous close time before we advance +# define AV_MID_CONSENSUS_PCT 65 // percentage of nodes that most vote yes after advancing -#define AV_LATE_CONSENSUS_TIME 85 // percentage of previous close time before we advance -#define AV_LATE_CONSENSUS_PCT 70 // percentage of nodes that most vote yes after advancing +# define AV_LATE_CONSENSUS_TIME 85 // percentage of previous close time before we advance +# define AV_LATE_CONSENSUS_PCT 70 // percentage of nodes that most vote yes after advancing -#define AV_STUCK_CONSENSUS_TIME 200 -#define AV_STUCK_CONSENSUS_PCT 95 +# define AV_STUCK_CONSENSUS_TIME 200 +# define AV_STUCK_CONSENSUS_PCT 95 +# define AV_CT_CONSENSUS_PCT 75 class ContinuousLedgerTiming { diff --git a/src/cpp/ripple/LoadManager.cpp b/src/cpp/ripple/LoadManager.cpp index a476c37e19..dc5ac0891e 100644 --- a/src/cpp/ripple/LoadManager.cpp +++ b/src/cpp/ripple/LoadManager.cpp @@ -318,7 +318,10 @@ void LoadManager::threadEntry() bool change; if (theApp->getJobQueue().isOverloaded()) + { + cLog(lsINFO) << theApp->getJobQueue().getJson(0); change = theApp->getFeeTrack().raiseLocalFee(); + } else change = theApp->getFeeTrack().lowerLocalFee(); if (change) diff --git a/src/cpp/ripple/LoadMonitor.cpp b/src/cpp/ripple/LoadMonitor.cpp index 567d08c948..1302339d13 100644 --- a/src/cpp/ripple/LoadMonitor.cpp +++ b/src/cpp/ripple/LoadMonitor.cpp @@ -1,4 +1,7 @@ #include "LoadMonitor.h" +#include "Log.h" + +SETUP_LOG(); void LoadMonitor::update() { // call with the mutex @@ -52,8 +55,12 @@ void LoadMonitor::addLatency(int latency) mLatencyMSPeak = lp; } -void LoadMonitor::addCountAndLatency(int counts, int latency) +void LoadMonitor::addCountAndLatency(const std::string& name, int counts, int latency) { + if (latency > 1000) + { + cLog(lsWARNING) << "Job: " << name << " ExecutionTime: " << latency; + } if (latency == 1) latency = 0; boost::mutex::scoped_lock sl(mLock); diff --git a/src/cpp/ripple/LoadMonitor.h b/src/cpp/ripple/LoadMonitor.h index 18cbb228dd..4989f1e14d 100644 --- a/src/cpp/ripple/LoadMonitor.h +++ b/src/cpp/ripple/LoadMonitor.h @@ -32,7 +32,7 @@ public: void addCount(int counts); void addLatency(int latency); - void addCountAndLatency(int counts, int latency); + void addCountAndLatency(const std::string& name, int counts, int latency); void setTargetLatency(uint64 avg, uint64 pk) { @@ -60,6 +60,7 @@ protected: LoadMonitor& mMonitor; bool mRunning; int mCount; + std::string mName; boost::posix_time::ptime mStartTime; public: @@ -76,6 +77,11 @@ public: stop(); } + void setName(const std::string& name) + { + mName = name; + } + void start() { // okay to call if already started mRunning = true; @@ -86,7 +92,7 @@ public: { assert(mRunning); mRunning = false; - mMonitor.addCountAndLatency(mCount, + mMonitor.addCountAndLatency(mName, mCount, static_cast((boost::posix_time::microsec_clock::universal_time() - mStartTime).total_milliseconds())); } }; diff --git a/src/cpp/ripple/NetworkOPs.cpp b/src/cpp/ripple/NetworkOPs.cpp index 593d9cb7cc..93b87b0869 100644 --- a/src/cpp/ripple/NetworkOPs.cpp +++ b/src/cpp/ripple/NetworkOPs.cpp @@ -267,7 +267,7 @@ void NetworkOPs::runTransactionQueue() assert(dbtx); bool didApply; - TER r = mLedgerMaster->doTransaction(*dbtx->getSTransaction(), + TER r = mLedgerMaster->doTransaction(dbtx->getSTransaction(), tapOPEN_LEDGER | tapNO_CHECK_SIGN, didApply); dbtx->setResult(r); @@ -352,7 +352,7 @@ Transaction::pointer NetworkOPs::processTransaction(Transaction::pointer trans, boost::recursive_mutex::scoped_lock sl(theApp->getMasterLock()); Transaction::pointer dbtx = theApp->getMasterTransaction().fetch(trans->getID(), true); bool didApply; - TER r = mLedgerMaster->doTransaction(*trans->getSTransaction(), tapOPEN_LEDGER | tapNO_CHECK_SIGN, didApply); + TER r = mLedgerMaster->doTransaction(trans->getSTransaction(), tapOPEN_LEDGER | tapNO_CHECK_SIGN, didApply); trans->setResult(r); if (isTemMalformed(r)) // malformed, cache bad @@ -426,30 +426,6 @@ Transaction::pointer NetworkOPs::findTransactionByID(const uint256& transactionI return Transaction::load(transactionID); } -#if 0 -int NetworkOPs::findTransactionsBySource(const uint256& uLedger, std::list& txns, - const RippleAddress& sourceAccount, uint32 minSeq, uint32 maxSeq) -{ - AccountState::pointer state = getAccountState(uLedger, sourceAccount); - if (!state) return 0; - if (minSeq > state->getSeq()) return 0; - if (maxSeq > state->getSeq()) maxSeq = state->getSeq(); - if (maxSeq > minSeq) return 0; - - int count = 0; - for(unsigned int i = minSeq; i <= maxSeq; ++i) - { - Transaction::pointer txn = Transaction::findFrom(sourceAccount, i); - if(txn) - { - txns.push_back(txn); - ++count; - } - } - return count; -} -#endif - int NetworkOPs::findTransactionsByDestination(std::list& txns, const RippleAddress& destinationAccount, uint32 startLedgerSeq, uint32 endLedgerSeq, int maxTransactions) { @@ -1313,9 +1289,9 @@ Json::Value NetworkOPs::pubBootstrapAccountInfo(Ledger::ref lpAccepted, const Ri return jvObj; } -void NetworkOPs::pubProposedTransaction(Ledger::ref lpCurrent, const SerializedTransaction& stTxn, TER terResult) +void NetworkOPs::pubProposedTransaction(Ledger::ref lpCurrent, SerializedTransaction::ref stTxn, TER terResult) { - Json::Value jvObj = transJson(stTxn, terResult, false, lpCurrent, "transaction"); + Json::Value jvObj = transJson(*stTxn, terResult, false, lpCurrent, "transaction"); { boost::recursive_mutex::scoped_lock sl(mMonitorLock); @@ -1332,15 +1308,19 @@ void NetworkOPs::pubProposedTransaction(Ledger::ref lpCurrent, const SerializedT it = mSubRTTransactions.erase(it); } } - TransactionMetaSet::pointer ret; - pubAccountTransaction(lpCurrent,stTxn,terResult,false,ret); + ALTransaction alt(stTxn, terResult); + cLog(lsTRACE) << "pubProposed: " << alt.getJson(0); + pubAccountTransaction(lpCurrent, ALTransaction(stTxn, terResult)); } -void NetworkOPs::pubLedger(Ledger::ref lpAccepted) +void NetworkOPs::pubLedger(Ledger::ref accepted) { // Ledgers are published only when they acquire sufficient validations // Holes are filled across connection loss or other catastrophe + AcceptedLedger::pointer alpAccepted = AcceptedLedger::makeAcceptedLedger(accepted); + Ledger::ref lpAccepted = alpAccepted->getLedger(); + { boost::recursive_mutex::scoped_lock sl(mMonitorLock); @@ -1358,6 +1338,8 @@ void NetworkOPs::pubLedger(Ledger::ref lpAccepted) jvObj["reserve_base"] = Json::UInt(lpAccepted->getReserve(0)); jvObj["reserve_inc"] = Json::UInt(lpAccepted->getReserveInc()); + jvObj["txn_count"] = Json::UInt(alpAccepted->getTxnCount()); + NetworkOPs::subMapType::const_iterator it = mSubLedger.begin(); while (it != mSubLedger.end()) { @@ -1376,21 +1358,10 @@ void NetworkOPs::pubLedger(Ledger::ref lpAccepted) // Don't lock since pubAcceptedTransaction is locking. if (!mSubTransactions.empty() || !mSubRTTransactions.empty() || !mSubAccount.empty() || !mSubRTAccount.empty()) { - SHAMap& txSet = *lpAccepted->peekTransactionMap(); - - for (SHAMapItem::pointer item = txSet.peekFirstItem(); !!item; item = txSet.peekNextItem(item->getTag())) + BOOST_FOREACH(const AcceptedLedger::value_type& vt, alpAccepted->getMap()) { - SerializerIterator it(item->peekSerializer()); - - // OPTIMIZEME: Could get transaction from txn master, but still must call getVL - Serializer txnSer(it.getVL()); - SerializerIterator txnIt(txnSer); - SerializedTransaction stTxn(txnIt); - - TransactionMetaSet::pointer meta = boost::make_shared( - stTxn.getTransactionID(), lpAccepted->getLedgerSeq(), it.getVL()); - - pubAcceptedTransaction(lpAccepted, stTxn, meta->getResultTER(), meta); + cLog(lsTRACE) << "pubAccepted: " << vt.second.getJson(0); + pubAcceptedTransaction(lpAccepted, vt.second); } } } @@ -1401,7 +1372,7 @@ void NetworkOPs::reportFeeChange() (theApp->getFeeTrack().getLoadFactor() == mLastLoadFactor)) return; - theApp->getJobQueue().addJob(jtCLIENT, boost::bind(&NetworkOPs::pubServer, this)); + theApp->getJobQueue().addJob(jtCLIENT, "reportFeeChange->pubServer", boost::bind(&NetworkOPs::pubServer, this)); } Json::Value NetworkOPs::transJson(const SerializedTransaction& stTxn, TER terResult, bool bAccepted, Ledger::ref lpCurrent, const std::string& strType) @@ -1431,11 +1402,10 @@ Json::Value NetworkOPs::transJson(const SerializedTransaction& stTxn, TER terRes return jvObj; } -void NetworkOPs::pubAcceptedTransaction(Ledger::ref lpCurrent, const SerializedTransaction& stTxn, TER terResult,TransactionMetaSet::pointer& meta) +void NetworkOPs::pubAcceptedTransaction(Ledger::ref alAccepted, const ALTransaction& alTx) { - Json::Value jvObj = transJson(stTxn, terResult, true, lpCurrent, "transaction"); - - if (meta) jvObj["meta"] = meta->getJson(0); + Json::Value jvObj = transJson(*alTx.getTxn(), alTx.getResult(), true, alAccepted, "transaction"); + jvObj["meta"] = alTx.getMeta()->getJson(0); { boost::recursive_mutex::scoped_lock sl(mMonitorLock); @@ -1466,14 +1436,14 @@ void NetworkOPs::pubAcceptedTransaction(Ledger::ref lpCurrent, const SerializedT it = mSubRTTransactions.erase(it); } } - theApp->getOrderBookDB().processTxn(stTxn, terResult, meta, jvObj); - - pubAccountTransaction(lpCurrent, stTxn, terResult, true, meta); + theApp->getOrderBookDB().processTxn(alAccepted, alTx, jvObj); + pubAccountTransaction(alAccepted, alTx); } -void NetworkOPs::pubAccountTransaction(Ledger::ref lpCurrent, const SerializedTransaction& stTxn, TER terResult, bool bAccepted, TransactionMetaSet::pointer& meta) +void NetworkOPs::pubAccountTransaction(Ledger::ref lpCurrent, const ALTransaction& alTx) { boost::unordered_set notify; + bool bAccepted = alTx.isApplied(); int iProposed = 0; int iAccepted = 0; @@ -1484,8 +1454,7 @@ void NetworkOPs::pubAccountTransaction(Ledger::ref lpCurrent, const SerializedTr if (!mSubAccount.empty() || (!mSubRTAccount.empty()) ) { - std::vector accounts = meta ? meta->getAffectedAccounts() : stTxn.getMentionedAccounts(); - BOOST_FOREACH(const RippleAddress& affectedAccount, accounts) + BOOST_FOREACH(const RippleAddress& affectedAccount, alTx.getAffected()) { subInfoMapIterator simiIt = mSubRTAccount.find(affectedAccount.getAccountID()); @@ -1534,9 +1503,10 @@ void NetworkOPs::pubAccountTransaction(Ledger::ref lpCurrent, const SerializedTr if (!notify.empty()) { - Json::Value jvObj = transJson(stTxn, terResult, bAccepted, lpCurrent, "account"); + Json::Value jvObj = transJson(*alTx.getTxn(), alTx.getResult(), bAccepted, lpCurrent, "account"); - if (meta) jvObj["meta"] = meta->getJson(0); + if (alTx.isApplied()) + jvObj["meta"] = alTx.getMeta()->getJson(0); BOOST_FOREACH(InfoSub::ref isrListener, notify) { @@ -1696,12 +1666,14 @@ bool NetworkOPs::subLedger(InfoSub::ref isrListener, Json::Value& jvResult) jvResult["reserve_base"] = Json::UInt(lpClosed->getReserve(0)); jvResult["reserve_inc"] = Json::UInt(lpClosed->getReserveInc()); + boost::recursive_mutex::scoped_lock sl(mMonitorLock); return mSubLedger.insert(std::make_pair(isrListener->getSeq(), isrListener)).second; } // <-- bool: true=erased, false=was not there bool NetworkOPs::unsubLedger(uint64 uSeq) { + boost::recursive_mutex::scoped_lock sl(mMonitorLock); return !!mSubLedger.erase(uSeq); } @@ -1722,36 +1694,42 @@ bool NetworkOPs::subServer(InfoSub::ref isrListener, Json::Value& jvResult) jvResult["load_base"] = theApp->getFeeTrack().getLoadBase(); jvResult["load_factor"] = theApp->getFeeTrack().getLoadFactor(); + boost::recursive_mutex::scoped_lock sl(mMonitorLock); return mSubServer.insert(std::make_pair(isrListener->getSeq(), isrListener)).second; } // <-- bool: true=erased, false=was not there bool NetworkOPs::unsubServer(uint64 uSeq) { + boost::recursive_mutex::scoped_lock sl(mMonitorLock); return !!mSubServer.erase(uSeq); } // <-- bool: true=added, false=already there bool NetworkOPs::subTransactions(InfoSub::ref isrListener) { + boost::recursive_mutex::scoped_lock sl(mMonitorLock); return mSubTransactions.insert(std::make_pair(isrListener->getSeq(), isrListener)).second; } // <-- bool: true=erased, false=was not there bool NetworkOPs::unsubTransactions(uint64 uSeq) { + boost::recursive_mutex::scoped_lock sl(mMonitorLock); return !!mSubTransactions.erase(uSeq); } // <-- bool: true=added, false=already there bool NetworkOPs::subRTTransactions(InfoSub::ref isrListener) { + boost::recursive_mutex::scoped_lock sl(mMonitorLock); return mSubTransactions.insert(std::make_pair(isrListener->getSeq(), isrListener)).second; } // <-- bool: true=erased, false=was not there bool NetworkOPs::unsubRTTransactions(uint64 uSeq) { + boost::recursive_mutex::scoped_lock sl(mMonitorLock); return !!mSubTransactions.erase(uSeq); } @@ -1774,5 +1752,170 @@ InfoSub::pointer NetworkOPs::addRpcSub(const std::string& strUrl, InfoSub::ref r return rspEntry; } +// FIXME : support iLimit. +void NetworkOPs::getBookPage(Ledger::pointer lpLedger, const uint160& uTakerPaysCurrencyID, const uint160& uTakerPaysIssuerID, const uint160& uTakerGetsCurrencyID, const uint160& uTakerGetsIssuerID, const uint160& uTakerID, const bool bProof, const unsigned int iLimit, const Json::Value& jvMarker, Json::Value& jvResult) +{ + boost::unordered_map umBalance; + Json::Value jvOffers = Json::Value(Json::arrayValue); + const uint256 uBookBase = Ledger::getBookBase(uTakerPaysCurrencyID, uTakerPaysIssuerID, uTakerGetsCurrencyID, uTakerGetsIssuerID); + const uint256 uBookEnd = Ledger::getQualityNext(uBookBase); + uint256 uTipIndex = uBookBase; + + cLog(lsTRACE) << boost::str(boost::format("getBookPage: uTakerPaysCurrencyID=%s uTakerPaysIssuerID=%s") % STAmount::createHumanCurrency(uTakerPaysCurrencyID) % RippleAddress::createHumanAccountID(uTakerPaysIssuerID)); + cLog(lsTRACE) << boost::str(boost::format("getBookPage: uTakerGetsCurrencyID=%s uTakerGetsIssuerID=%s") % STAmount::createHumanCurrency(uTakerGetsCurrencyID) % RippleAddress::createHumanAccountID(uTakerGetsIssuerID)); + cLog(lsTRACE) << boost::str(boost::format("getBookPage: uBookBase=%s") % uBookBase); + cLog(lsTRACE) << boost::str(boost::format("getBookPage: uBookEnd=%s") % uBookEnd); + cLog(lsTRACE) << boost::str(boost::format("getBookPage: uTipIndex=%s") % uTipIndex); + + LedgerEntrySet lesActive(lpLedger); + + bool bDone = false; + bool bDirectAdvance = true; + + SLE::pointer sleOfferDir; + uint256 uOfferIndex; + unsigned int uBookEntry; + STAmount saDirRate; + +// unsigned int iLeft = iLimit; + + uint32 uTransferRate = lesActive.rippleTransferRate(uTakerGetsIssuerID); + + while (!bDone) { + if (bDirectAdvance) { + bDirectAdvance = false; + + cLog(lsTRACE) << boost::str(boost::format("getBookPage: bDirectAdvance")); + + sleOfferDir = lesActive.entryCache(ltDIR_NODE, lpLedger->getNextLedgerIndex(uTipIndex, uBookEnd)); + if (!sleOfferDir) + { + cLog(lsTRACE) << boost::str(boost::format("getBookPage: bDone")); + bDone = true; + } + else + { + uTipIndex = sleOfferDir->getIndex(); + saDirRate = STAmount::setRate(Ledger::getQuality(uTipIndex)); + SLE::pointer sleBookNode; + + lesActive.dirFirst(uTipIndex, sleBookNode, uBookEntry, uOfferIndex); + + cLog(lsTRACE) << boost::str(boost::format("getBookPage: uTipIndex=%s") % uTipIndex); + cLog(lsTRACE) << boost::str(boost::format("getBookPage: uOfferIndex=%s") % uOfferIndex); + } + } + + if (!bDone) + { + SLE::pointer sleOffer = lesActive.entryCache(ltOFFER, uOfferIndex); + const uint160 uOfferOwnerID = sleOffer->getFieldAccount(sfAccount).getAccountID(); + STAmount saTakerGets = sleOffer->getFieldAmount(sfTakerGets); + STAmount saTakerPays = sleOffer->getFieldAmount(sfTakerPays); + STAmount saOwnerFunds; + + if (uTakerGetsIssuerID == uOfferOwnerID) + { + // If offer is selling issuer's own IOUs, it is fully funded. + saOwnerFunds = saTakerGets; + } + else + { + boost::unordered_map::const_iterator umBalanceEntry = umBalance.find(uOfferOwnerID); + + if (umBalanceEntry != umBalance.end()) + { + // Found in running balance table. + + saOwnerFunds = umBalanceEntry->second; + // cLog(lsINFO) << boost::str(boost::format("getBookPage: saOwnerFunds=%s (cached)") % saOwnerFunds.getFullText()); + } + else + { + // Did not find balance in table. + + saOwnerFunds = lesActive.accountHolds(uOfferOwnerID, uTakerGetsCurrencyID, uTakerGetsIssuerID); + // cLog(lsINFO) << boost::str(boost::format("getBookPage: saOwnerFunds=%s (new)") % saOwnerFunds.getFullText()); + if (saOwnerFunds.isNegative()) + { + // Treat negative funds as zero. + + saOwnerFunds.zero(); + } + } + } + + Json::Value jvOffer = sleOffer->getJson(0); + + STAmount saTakerGetsFunded; + STAmount saOwnerFundsLimit; + uint32 uOfferRate; + + + if (uTransferRate != QUALITY_ONE // Have a tranfer fee. + && uTakerID != uTakerGetsIssuerID // Not taking offers of own IOUs. + && uTakerGetsIssuerID != uOfferOwnerID) { // Offer owner not issuing ownfunds + // Need to charge a transfer fee to offer owner. + uOfferRate = uTransferRate; + saOwnerFundsLimit = STAmount::divide(saOwnerFunds, STAmount(CURRENCY_ONE, ACCOUNT_ONE, uOfferRate, -9)); + } + else + { + uOfferRate = QUALITY_ONE; + saOwnerFundsLimit = saOwnerFunds; + } + + if (saOwnerFundsLimit >= saTakerGets) + { + // Sufficient funds no shenanigans. + saTakerGetsFunded = saTakerGets; + } + else + { + // cLog(lsINFO) << boost::str(boost::format("getBookPage: saTakerGets=%s") % saTakerGets.getFullText()); + // cLog(lsINFO) << boost::str(boost::format("getBookPage: saTakerPays=%s") % saTakerPays.getFullText()); + // cLog(lsINFO) << boost::str(boost::format("getBookPage: saOwnerFunds=%s") % saOwnerFunds.getFullText()); + // cLog(lsINFO) << boost::str(boost::format("getBookPage: saDirRate=%s") % saDirRate.getText()); + // cLog(lsINFO) << boost::str(boost::format("getBookPage: multiply=%s") % STAmount::multiply(saTakerGetsFunded, saDirRate).getFullText()); + // cLog(lsINFO) << boost::str(boost::format("getBookPage: multiply=%s") % STAmount::multiply(saTakerGetsFunded, saDirRate, saTakerPays).getFullText()); + STAmount saTakerPaysFunded; + + saTakerGetsFunded = saOwnerFundsLimit; + saTakerPaysFunded = std::min(saTakerPays, STAmount::multiply(saTakerGetsFunded, saDirRate, saTakerPays)); + + // Only provide, if not fully funded. + jvOffer["taker_gets_funded"] = saTakerGetsFunded.getJson(0); + jvOffer["taker_pays_funded"] = saTakerPaysFunded.getJson(0); + + } + STAmount saOwnerPays = QUALITY_ONE == uOfferRate + ? saTakerGetsFunded + : std::min(saOwnerFunds, STAmount::multiply(saTakerGetsFunded, STAmount(CURRENCY_ONE, ACCOUNT_ONE, uOfferRate, -9))); + + STAmount saOwnerBalance = saOwnerFunds-saOwnerPays; + + umBalance[uOfferOwnerID] = saOwnerBalance; + + if (!saOwnerFunds.isZero() || uOfferOwnerID == uTakerID) + { + // Only provide funded offers and offers of the taker. + jvOffers.append(jvOffer); + } + + if (!lesActive.dirNext(uTipIndex, sleOfferDir, uBookEntry, uOfferIndex)) + { + bDirectAdvance = true; + } + else + { + cLog(lsTRACE) << boost::str(boost::format("getBookPage: uOfferIndex=%s") % uOfferIndex); + } + } + } + + jvResult["offers"] = jvOffers; +// jvResult["marker"] = Json::Value(Json::arrayValue); +// jvResult["nodes"] = Json::Value(Json::arrayValue); +} // vim:ts=4 diff --git a/src/cpp/ripple/NetworkOPs.h b/src/cpp/ripple/NetworkOPs.h index 1dd10c501b..01d502ae0e 100644 --- a/src/cpp/ripple/NetworkOPs.h +++ b/src/cpp/ripple/NetworkOPs.h @@ -14,6 +14,7 @@ #include "LedgerAcquire.h" #include "LedgerProposal.h" #include "JobQueue.h" +#include "AcceptedLedger.h" // Operations that clients may wish to perform against the network // Master operational handler, server sequencer, network tracker @@ -119,7 +120,6 @@ protected: boost::recursive_mutex mMonitorLock; subInfoMapType mSubAccount; subInfoMapType mSubRTAccount; - subRpcMapType mRpcSubMap; @@ -141,8 +141,8 @@ protected: Json::Value pubBootstrapAccountInfo(Ledger::ref lpAccepted, const RippleAddress& naAccountID); - void pubAcceptedTransaction(Ledger::ref lpCurrent, const SerializedTransaction& stTxn, TER terResult,TransactionMetaSet::pointer& meta); - void pubAccountTransaction(Ledger::ref lpCurrent, const SerializedTransaction& stTxn, TER terResult,bool accepted,TransactionMetaSet::pointer& meta); + void pubAcceptedTransaction(Ledger::ref alAccepted, const ALTransaction& alTransaction); + void pubAccountTransaction(Ledger::ref lpCurrent, const ALTransaction& alTransaction); void pubServer(); @@ -230,6 +230,12 @@ public: Json::Value getOwnerInfo(Ledger::pointer lpLedger, const RippleAddress& naAccount); + // + // Book functions + // + + void getBookPage(Ledger::pointer lpLedger, const uint160& uTakerPaysCurrencyID, const uint160& uTakerPaysIssuerID, const uint160& uTakerGetsCurrencyID, const uint160& uTakerGetsIssuerID, const uint160& uTakerID, const bool bProof, const unsigned int iLimit, const Json::Value& jvMarker, Json::Value& jvResult); + // raw object operations bool findRawLedger(const uint256& ledgerHash, std::vector& rawLedger); bool findRawTransaction(const uint256& transactionHash, std::vector& rawTransaction); @@ -301,7 +307,7 @@ public: // Monitoring: publisher side // void pubLedger(Ledger::ref lpAccepted); - void pubProposedTransaction(Ledger::ref lpCurrent, const SerializedTransaction& stTxn, TER terResult); + void pubProposedTransaction(Ledger::ref lpCurrent, SerializedTransaction::ref stTxn, TER terResult); // diff --git a/src/cpp/ripple/OfferCreateTransactor.cpp b/src/cpp/ripple/OfferCreateTransactor.cpp index d2acfe9250..8155956f26 100644 --- a/src/cpp/ripple/OfferCreateTransactor.cpp +++ b/src/cpp/ripple/OfferCreateTransactor.cpp @@ -321,6 +321,7 @@ TER OfferCreateTransactor::doApply() cLog(lsWARNING) << "OfferCreate> " << mTxn.getJson(0); const uint32 uTxFlags = mTxn.getFlags(); const bool bPassive = isSetBit(uTxFlags, tfPassive); + const bool bMarket = isSetBit(uTxFlags, tfMarket); STAmount saTakerPays = mTxn.getFieldAmount(sfTakerPays); STAmount saTakerGets = mTxn.getFieldAmount(sfTakerGets); @@ -457,9 +458,21 @@ TER OfferCreateTransactor::doApply() // cLog(lsWARNING) << "OfferCreate: takeOffers: uPaysIssuerID=" << RippleAddress::createHumanAccountID(uPaysIssuerID); // cLog(lsWARNING) << "OfferCreate: takeOffers: uGetsIssuerID=" << RippleAddress::createHumanAccountID(uGetsIssuerID); - if (tesSUCCESS != terResult - || !saTakerPays // Wants nothing more. + if (tesSUCCESS != terResult) + { + // Fail as is. + nothing(); + } + else if (saTakerPays.isNegative() || saTakerGets.isNegative()) + { + terResult = isSetBit(mParams, tapOPEN_LEDGER) + ? telFAILED_PROCESSING // Ledger is not final, can vote no. + : tecFAILED_PROCESSING; + } + else if ( + !saTakerPays // Wants nothing more. || !saTakerGets // Offering nothing more. + || bMarket // Do not persist. || !mEngine->getNodes().accountFunds(mTxnAccountID, saTakerGets).isPositive() // Not funded. || bUnfunded) // Consider unfunded. { diff --git a/src/cpp/ripple/OrderBookDB.cpp b/src/cpp/ripple/OrderBookDB.cpp index e58757decb..b17c98ab96 100644 --- a/src/cpp/ripple/OrderBookDB.cpp +++ b/src/cpp/ripple/OrderBookDB.cpp @@ -187,15 +187,14 @@ BookListeners::pointer OrderBookDB::getBookListeners(const uint160& currencyIn, */ // Based on the meta, send the meta to the streams that are listening // We need to determine which streams a given meta effects -void OrderBookDB::processTxn(const SerializedTransaction& stTxn, TER terResult, - TransactionMetaSet::pointer& meta, Json::Value& jvObj) +void OrderBookDB::processTxn(Ledger::ref ledger, const ALTransaction& alTx, Json::Value& jvObj) { boost::recursive_mutex::scoped_lock sl(mLock); - if (terResult == tesSUCCESS) + if (alTx.getResult() == tesSUCCESS) { // check if this is an offer or an offer cancel or a payment that consumes an offer //check to see what the meta looks like - BOOST_FOREACH(STObject& node, meta->getNodes()) + BOOST_FOREACH(STObject& node, alTx.getMeta()->getNodes()) { try { diff --git a/src/cpp/ripple/OrderBookDB.h b/src/cpp/ripple/OrderBookDB.h index 768ca05a55..f5119e88bd 100644 --- a/src/cpp/ripple/OrderBookDB.h +++ b/src/cpp/ripple/OrderBookDB.h @@ -6,6 +6,7 @@ #include #include "Ledger.h" +#include "AcceptedLedger.h" #include "OrderBook.h" @@ -66,7 +67,7 @@ public: const uint160& issuerIn, const uint160& issuerOut); // see if this txn effects any orderbook - void processTxn(const SerializedTransaction& stTxn, TER terResult,TransactionMetaSet::pointer& meta,Json::Value& jvObj); + void processTxn(Ledger::ref ledger, const ALTransaction& alTx, Json::Value& jvObj); }; diff --git a/src/cpp/ripple/Peer.cpp b/src/cpp/ripple/Peer.cpp index 37e9bcb210..e8a8b88154 100644 --- a/src/cpp/ripple/Peer.cpp +++ b/src/cpp/ripple/Peer.cpp @@ -857,7 +857,7 @@ void Peer::recvTransaction(ripple::TMTransaction& packet) return; } - theApp->getJobQueue().addJob(jtTRANSACTION, + theApp->getJobQueue().addJob(jtTRANSACTION, "recvTransction->checkTransaction", boost::bind(&checkTransaction, _1, flags, stx, boost::weak_ptr(shared_from_this()))); #ifndef TRUST_NETWORK @@ -986,7 +986,7 @@ void Peer::recvPropose(const boost::shared_ptr& packet) prevLedger.isNonZero() ? prevLedger : consensusLCL, set.proposeseq(), proposeHash, set.closetime(), signerPublic, suppression); - theApp->getJobQueue().addJob(isTrusted ? jtPROPOSAL_t : jtPROPOSAL_ut, + theApp->getJobQueue().addJob(isTrusted ? jtPROPOSAL_t : jtPROPOSAL_ut, "recvPropose->checkPropose", boost::bind(&checkPropose, _1, packet, proposal, consensusLCL, mNodePublic, boost::weak_ptr(shared_from_this()))); } @@ -1062,7 +1062,7 @@ void Peer::recvValidation(const boost::shared_ptr& packet) } bool isTrusted = theApp->getUNL().nodeInUNL(val->getSignerPublic()); - theApp->getJobQueue().addJob(isTrusted ? jtVALIDATION_t : jtVALIDATION_ut, + theApp->getJobQueue().addJob(isTrusted ? jtVALIDATION_t : jtVALIDATION_ut, "recvValidation->checkValidation", boost::bind(&checkValidation, _1, val, signingHash, isTrusted, packet, boost::weak_ptr(shared_from_this()))); } @@ -1299,7 +1299,7 @@ void Peer::recvProofWork(ripple::TMProofWork& packet) return; } - theApp->getJobQueue().addJob(jtPROOFWORK, + theApp->getJobQueue().addJob(jtPROOFWORK, "recvProof->doProof", boost::bind(&Peer::doProofOfWork, _1, boost::weak_ptr(shared_from_this()), pow)); return; diff --git a/src/cpp/ripple/RPCErr.cpp b/src/cpp/ripple/RPCErr.cpp index 9e06a5d9d7..d7307cb776 100644 --- a/src/cpp/ripple/RPCErr.cpp +++ b/src/cpp/ripple/RPCErr.cpp @@ -19,12 +19,16 @@ Json::Value rpcError(int iError, Json::Value jvResult) { rpcACT_MALFORMED, "actMalformed", "Account malformed." }, { rpcACT_NOT_FOUND, "actNotFound", "Account not found." }, { rpcBAD_BLOB, "badBlob", "Blob must be a non-empty hex string." }, + { rpcBAD_ISSUER, "badIssuer", "Issuer account malformed." }, + { rpcBAD_MARKET, "badMarket", "No such market." }, + { rpcBAD_SECRET, "badSecret", "Secret does not match account." }, { rpcBAD_SEED, "badSeed", "Disallowed seed." }, { rpcBAD_SYNTAX, "badSyntax", "Syntax error." }, { rpcCOMMAND_MISSING, "commandMissing", "Missing command entry." }, { rpcDST_ACT_MALFORMED, "dstActMalformed", "Destination account is malformed." }, { rpcDST_ACT_MISSING, "dstActMissing", "Destination account does not exist." }, { rpcDST_AMT_MALFORMED, "dstAmtMalformed", "Destination amount/currency/issuer is malformed." }, + { rpcDST_ISR_MALFORMED, "dstIsrMalformed", "Destination issuer is malformed." }, { rpcFORBIDDEN, "forbidden", "Bad credentials." }, { rpcFAIL_GEN_DECRPYT, "failGenDecrypt", "Failed to decrypt generator." }, { rpcGETS_ACT_MALFORMED, "getsActMalformed", "Gets account malformed." }, @@ -60,7 +64,6 @@ Json::Value rpcError(int iError, Json::Value jvResult) { rpcSRC_ACT_MALFORMED, "srcActMalformed", "Source account is malformed." }, { rpcSRC_ACT_MISSING, "srcActMissing", "Source account not provided." }, { rpcSRC_ACT_NOT_FOUND, "srcActNotFound", "Source account not found." }, - { rpcBAD_SECRET, "badSecret", "Secret does not match account." }, { rpcSRC_AMT_MALFORMED, "srcAmtMalformed", "Source amount/currency/issuer is malformed." }, { rpcSRC_CUR_MALFORMED, "srcCurMalformed", "Source currency is malformed." }, { rpcSRC_ISR_MALFORMED, "srcIsrMalformed", "Source issuer is malformed." }, @@ -88,4 +91,9 @@ Json::Value rpcError(int iError, Json::Value jvResult) return jvResult; } +bool isRpcError(Json::Value jvResult) +{ + return jvResult.isObject() && jvResult.isMember("error"); +} + // vim:ts=4 diff --git a/src/cpp/ripple/RPCErr.h b/src/cpp/ripple/RPCErr.h index 8aa371328a..ef27251cb9 100644 --- a/src/cpp/ripple/RPCErr.h +++ b/src/cpp/ripple/RPCErr.h @@ -45,11 +45,15 @@ enum { rpcACT_MALFORMED, rpcQUALITY_MALFORMED, rpcBAD_BLOB, + rpcBAD_ISSUER, + rpcBAD_MARKET, + rpcBAD_SECRET, rpcBAD_SEED, rpcCOMMAND_MISSING, rpcDST_ACT_MALFORMED, rpcDST_ACT_MISSING, rpcDST_AMT_MALFORMED, + rpcDST_ISR_MALFORMED, rpcGETS_ACT_MALFORMED, rpcGETS_AMT_MALFORMED, rpcHOST_IP_MALFORMED, @@ -67,7 +71,6 @@ enum { rpcSRC_AMT_MALFORMED, rpcSRC_CUR_MALFORMED, rpcSRC_ISR_MALFORMED, - rpcBAD_SECRET, // Internal error (should never happen) rpcINTERNAL, // Generic internal error. @@ -77,6 +80,8 @@ enum { rpcNO_GEN_DECRPYT, }; +bool isRpcError(Json::Value jvResult); Json::Value rpcError(int iError, Json::Value jvResult=Json::Value(Json::objectValue)); + #endif // vim:ts=4 diff --git a/src/cpp/ripple/RPCHandler.cpp b/src/cpp/ripple/RPCHandler.cpp index 215d239ce0..ff93bfd0e9 100644 --- a/src/cpp/ripple/RPCHandler.cpp +++ b/src/cpp/ripple/RPCHandler.cpp @@ -176,7 +176,7 @@ Json::Value RPCHandler::transactionSign(Json::Value jvRequest, bool bSubmit) } Ledger::pointer lSnapshot = boost::make_shared( - boost::ref(*theApp->getOPs().getCurrentLedger()), false); + boost::ref(*mNetOps->getCurrentLedger()), false); { ScopedUnlock su(theApp->getMasterLock()); Pathfinder pf(lSnapshot, raSrcAddressID, dstAccountID, @@ -558,18 +558,6 @@ Json::Value RPCHandler::accountFromString(Ledger::ref lrLedger, RippleAddress& n return Json::Value(Json::objectValue); } -Json::Value RPCHandler::doAcceptLedger(Json::Value jvRequest) -{ - if (!theConfig.RUN_STANDALONE) - return rpcError(rpcNOT_STANDALONE); - - Json::Value jvResult(Json::objectValue); - - jvResult["newLedger"] = theApp->getOPs().acceptLedger(); - - return jvResult; -} - // { // ident : , // account_index : // optional @@ -1033,6 +1021,106 @@ Json::Value RPCHandler::doAccountOffers(Json::Value jvRequest) return jvResult; } +// { +// "ledger_hash" : ledger, // Optional. +// "ledger_index" : ledger_index, // Optional. +// "taker_gets" : { "currency": currency, "issuer" : address }, +// "taker_pays" : { "currency": currency, "issuer" : address }, +// "taker" : address, // Optional. +// "marker" : element, // Optional. +// "limit" : integer, // Optional. +// "proof" : boolean // Defaults to false. +// } +Json::Value RPCHandler::doBookOffers(Json::Value jvRequest) +{ + Ledger::pointer lpLedger; + Json::Value jvResult = lookupLedger(jvRequest, lpLedger); + + if (!lpLedger) + return jvResult; + + ScopedUnlock su(theApp->getMasterLock()); + + if (!jvRequest.isMember("taker_pays") || !jvRequest.isMember("taker_gets")) + return rpcError(rpcINVALID_PARAMS); + + uint160 uTakerPaysCurrencyID; + uint160 uTakerPaysIssuerID; + Json::Value jvTakerPays = jvRequest["taker_pays"]; + + // Parse mandatory currency. + if (!jvTakerPays.isMember("currency") + || !STAmount::currencyFromString(uTakerPaysCurrencyID, jvTakerPays["currency"].asString())) + { + cLog(lsINFO) << "Bad taker_pays currency."; + + return rpcError(rpcSRC_CUR_MALFORMED); + } + // Parse optional issuer. + else if (((jvTakerPays.isMember("issuer")) + && (!jvTakerPays["issuer"].isString() + || !STAmount::issuerFromString(uTakerPaysIssuerID, jvTakerPays["issuer"].asString()))) + // Don't allow illegal issuers. + || (!uTakerPaysCurrencyID != !uTakerPaysIssuerID) + || ACCOUNT_ONE == uTakerPaysIssuerID) + { + cLog(lsINFO) << "Bad taker_pays issuer."; + + return rpcError(rpcSRC_ISR_MALFORMED); + } + + uint160 uTakerGetsCurrencyID; + uint160 uTakerGetsIssuerID; + Json::Value jvTakerGets = jvRequest["taker_gets"]; + + // Parse mandatory currency. + if (!jvTakerGets.isMember("currency") + || !STAmount::currencyFromString(uTakerGetsCurrencyID, jvTakerGets["currency"].asString())) + { + cLog(lsINFO) << "Bad taker_pays currency."; + + return rpcError(rpcSRC_CUR_MALFORMED); + } + // Parse optional issuer. + else if (((jvTakerGets.isMember("issuer")) + && (!jvTakerGets["issuer"].isString() + || !STAmount::issuerFromString(uTakerGetsIssuerID, jvTakerGets["issuer"].asString()))) + // Don't allow illegal issuers. + || (!uTakerGetsCurrencyID != !uTakerGetsIssuerID) + || ACCOUNT_ONE == uTakerGetsIssuerID) + { + cLog(lsINFO) << "Bad taker_gets issuer."; + + return rpcError(rpcDST_ISR_MALFORMED); + } + + if (uTakerPaysCurrencyID == uTakerGetsCurrencyID + && uTakerPaysIssuerID == uTakerGetsIssuerID) { + cLog(lsINFO) << "taker_gets same as taker_pays."; + + return rpcError(rpcBAD_MARKET); + } + + RippleAddress raTakerID; + + if (!jvRequest.isMember("taker")) + { + raTakerID.setAccountID(ACCOUNT_ONE); + } + else if (!raTakerID.setAccountID(jvRequest["taker"].asString())) + { + return rpcError(rpcBAD_ISSUER); + } + + const bool bProof = jvRequest.isMember("proof"); + const unsigned int iLimit = jvRequest.isMember("limit") ? jvRequest["limit"].asUInt() : 0; + const Json::Value jvMarker = jvRequest.isMember("marker") ? jvRequest["marker"] : Json::Value(Json::nullValue); + + mNetOps->getBookPage(lpLedger, uTakerPaysCurrencyID, uTakerPaysIssuerID, uTakerGetsCurrencyID, uTakerGetsIssuerID, raTakerID.getAccountID(), bProof, iLimit, jvMarker, jvResult); + + return jvResult; +} + // Result: // { // random: @@ -1366,7 +1454,7 @@ Json::Value RPCHandler::doConsensusInfo(Json::Value) { Json::Value ret(Json::objectValue); - ret["info"] = theApp->getOPs().getConsensusInfo(); + ret["info"] = mNetOps->getConsensusInfo(); return ret; } @@ -1375,7 +1463,7 @@ Json::Value RPCHandler::doServerInfo(Json::Value) { Json::Value ret(Json::objectValue); - ret["info"] = theApp->getOPs().getServerInfo(true, mRole == ADMIN); + ret["info"] = mNetOps->getServerInfo(true, mRole == ADMIN); return ret; } @@ -1384,7 +1472,7 @@ Json::Value RPCHandler::doServerState(Json::Value) { Json::Value ret(Json::objectValue); - ret["state"] = theApp->getOPs().getServerInfo(false, mRole == ADMIN); + ret["state"] = mNetOps->getServerInfo(false, mRole == ADMIN); return ret; } @@ -1453,7 +1541,7 @@ Json::Value RPCHandler::doTx(Json::Value jvRequest) if (txn->getLedger() != 0) { - Ledger::pointer lgr = theApp->getOPs().getLedgerBySeq(txn->getLedger()); + Ledger::pointer lgr = mNetOps->getLedgerBySeq(txn->getLedger()); if (lgr) { bool okay = false; @@ -1476,7 +1564,7 @@ Json::Value RPCHandler::doTx(Json::Value jvRequest) } } if (okay) - ret["validated"] = theApp->getOPs().isValidated(lgr); + ret["validated"] = mNetOps->isValidated(lgr); } } @@ -1594,7 +1682,7 @@ Json::Value RPCHandler::doAccountTransactions(Json::Value jvRequest) try { #endif - int vl = theApp->getOPs().getValidatedSeq(); + int vl = mNetOps->getValidatedSeq(); ScopedUnlock su(theApp->getMasterLock()); Json::Value ret(Json::objectValue); @@ -1613,7 +1701,7 @@ Json::Value RPCHandler::doAccountTransactions(Json::Value jvRequest) obj["inLedger"] = it->get<2>(); if (it->get<2>() > vl) obj["validated"] = false; - else if (theApp->getOPs().haveLedger(it->get<2>())) + else if (mNetOps->haveLedger(it->get<2>())) obj["validated"] = true; ret["transactions"].append(obj); } @@ -1634,7 +1722,7 @@ Json::Value RPCHandler::doAccountTransactions(Json::Value jvRequest) uint32 s = it->second->getLgrSeq(); if (s > vl) obj["validated"] = false; - else if (theApp->getOPs().haveLedger(s)) + else if (mNetOps->haveLedger(s)) obj["validated"] = true; } @@ -2807,11 +2895,11 @@ Json::Value RPCHandler::doCommand(const Json::Value& jvRequest, int iRole) unsigned int iOptions; } commandsA[] = { // Request-response methods - { "accept_ledger", &RPCHandler::doAcceptLedger, true, optCurrent }, { "account_info", &RPCHandler::doAccountInfo, false, optCurrent }, { "account_lines", &RPCHandler::doAccountLines, false, optCurrent }, { "account_offers", &RPCHandler::doAccountOffers, false, optCurrent }, { "account_tx", &RPCHandler::doAccountTransactions, false, optNetwork }, + { "book_offers", &RPCHandler::doBookOffers, false, optCurrent }, { "connect", &RPCHandler::doConnect, true, optNone }, { "consensus_info", &RPCHandler::doConsensusInfo, true, optNone }, { "get_counts", &RPCHandler::doGetCounts, true, optNone }, @@ -2888,6 +2976,8 @@ Json::Value RPCHandler::doCommand(const Json::Value& jvRequest, int iRole) && mNetOps->getOperatingMode() != NetworkOPs::omTRACKING && mNetOps->getOperatingMode() != NetworkOPs::omFULL) { + cLog(lsINFO) << "Insufficient network mode for RPC: " << mNetOps->strOperatingMode(); + return rpcError(rpcNO_NETWORK); } // XXX Should verify we have a current ledger. diff --git a/src/cpp/ripple/RPCHandler.h b/src/cpp/ripple/RPCHandler.h index 47e30c2085..48fe696cee 100644 --- a/src/cpp/ripple/RPCHandler.h +++ b/src/cpp/ripple/RPCHandler.h @@ -43,12 +43,11 @@ class RPCHandler Json::Value accountFromString(Ledger::ref lrLedger, RippleAddress& naAccount, bool& bIndex, const std::string& strIdent, const int iIndex, const bool bStrict); - Json::Value doAcceptLedger(Json::Value jvRequest); - Json::Value doAccountInfo(Json::Value params); Json::Value doAccountLines(Json::Value params); Json::Value doAccountOffers(Json::Value params); Json::Value doAccountTransactions(Json::Value params); + Json::Value doBookOffers(Json::Value params); Json::Value doConnect(Json::Value params); Json::Value doConsensusInfo(Json::Value params); #if ENABLE_INSECURE diff --git a/src/cpp/ripple/RPCSub.cpp b/src/cpp/ripple/RPCSub.cpp index 2a6ac6af89..779cff0ed1 100644 --- a/src/cpp/ripple/RPCSub.cpp +++ b/src/cpp/ripple/RPCSub.cpp @@ -29,6 +29,12 @@ RPCSub::RPCSub(const std::string& strUrl, const std::string& strUsername, const if (mPort < 0) mPort = mSSL ? 443 : 80; + + cLog(lsINFO) << boost::str(boost::format("callRPC sub: ip='%s' port=%d ssl=%d path='%s'") + % mIp + % mPort + % mSSL + % mPath); } // XXX Could probably create a bunch of send jobs in a single get of the lock. diff --git a/src/cpp/ripple/RippleCalc.cpp b/src/cpp/ripple/RippleCalc.cpp index 6123c930d3..d98adaa76f 100644 --- a/src/cpp/ripple/RippleCalc.cpp +++ b/src/cpp/ripple/RippleCalc.cpp @@ -325,10 +325,10 @@ void PathState::setExpanded( uMaxCurrencyID, // Max specifes the currency. uSenderIssuerID); -cLog(lsDEBUG) << boost::str(boost::format("PathState: pushed: account=%s currency=%s issuer=%s") - % RippleAddress::createHumanAccountID(uSenderID) - % STAmount::createHumanCurrency(uMaxCurrencyID) - % RippleAddress::createHumanAccountID(uSenderIssuerID)); + cLog(lsDEBUG) << boost::str(boost::format("PathState: pushed: account=%s currency=%s issuer=%s") + % RippleAddress::createHumanAccountID(uSenderID) + % STAmount::createHumanCurrency(uMaxCurrencyID) + % RippleAddress::createHumanAccountID(uSenderIssuerID)); if (tesSUCCESS == terStatus && uMaxIssuerID != uSenderIssuerID) { // Issuer was not same as sender. @@ -347,19 +347,19 @@ cLog(lsDEBUG) << boost::str(boost::format("PathState: pushed: account=%s currenc : uOutIssuerID // Use implied node. : ACCOUNT_XRP; -cLog(lsDEBUG) << boost::str(boost::format("PathState: implied check: uNxtCurrencyID=%s uNxtAccountID=%s") - % RippleAddress::createHumanAccountID(uNxtCurrencyID) - % RippleAddress::createHumanAccountID(uNxtAccountID)); + cLog(lsDEBUG) << boost::str(boost::format("PathState: implied check: uNxtCurrencyID=%s uNxtAccountID=%s") + % RippleAddress::createHumanAccountID(uNxtCurrencyID) + % RippleAddress::createHumanAccountID(uNxtAccountID)); // Can't just use push implied, because it can't compensate for next account. if (!uNxtCurrencyID // Next is XRP, offer next. Must go through issuer. || uMaxCurrencyID != uNxtCurrencyID // Next is different currency, offer next... || uMaxIssuerID != uNxtAccountID) // Next is not implied issuer { -cLog(lsDEBUG) << boost::str(boost::format("PathState: sender implied: account=%s currency=%s issuer=%s") - % RippleAddress::createHumanAccountID(uMaxIssuerID) - % RippleAddress::createHumanAccountID(uMaxCurrencyID) - % RippleAddress::createHumanAccountID(uMaxIssuerID)); + cLog(lsDEBUG) << boost::str(boost::format("PathState: sender implied: account=%s currency=%s issuer=%s") + % RippleAddress::createHumanAccountID(uMaxIssuerID) + % RippleAddress::createHumanAccountID(uMaxCurrencyID) + % RippleAddress::createHumanAccountID(uMaxIssuerID)); // Add account implied by SendMax. terStatus = pushNode( !!uMaxCurrencyID @@ -375,7 +375,7 @@ cLog(lsDEBUG) << boost::str(boost::format("PathState: sender implied: account=%s { if (tesSUCCESS == terStatus) { -cLog(lsDEBUG) << boost::str(boost::format("PathState: element in path:")); + cLog(lsDEBUG) << boost::str(boost::format("PathState: element in path:")); terStatus = pushNode(speElement.getNodeType(), speElement.getAccountID(), speElement.getCurrency(), speElement.getIssuerID()); } } @@ -389,10 +389,10 @@ cLog(lsDEBUG) << boost::str(boost::format("PathState: element in path:")); || pnPrv.uAccountID != uOutIssuerID)) // Need the implied issuer. { // Add implied account. -cLog(lsDEBUG) << boost::str(boost::format("PathState: receiver implied: account=%s currency=%s issuer=%s") - % RippleAddress::createHumanAccountID(uOutIssuerID) - % RippleAddress::createHumanAccountID(uOutCurrencyID) - % RippleAddress::createHumanAccountID(uOutIssuerID)); + cLog(lsDEBUG) << boost::str(boost::format("PathState: receiver implied: account=%s currency=%s issuer=%s") + % RippleAddress::createHumanAccountID(uOutIssuerID) + % RippleAddress::createHumanAccountID(uOutCurrencyID) + % RippleAddress::createHumanAccountID(uOutIssuerID)); terStatus = pushNode( !!uOutCurrencyID ? STPathElement::typeAccount | STPathElement::typeCurrency | STPathElement::typeIssuer @@ -2494,9 +2494,9 @@ int iIndex = 0; pspExpanded->setExpanded(lesActive, spPath, uDstAccountID, uSrcAccountID); -cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Build path: %d: status: %s") - % ++iIndex - % transToken(pspExpanded->terStatus)); + cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Build path: %d: status: %s") + % ++iIndex + % transToken(pspExpanded->terStatus)); // Return, if the path specification was malformed. if (isTemMalformed(pspExpanded->terStatus)) @@ -2550,17 +2550,18 @@ int iPass = 0; pspCur->saOutAct = saDstAmountAct; rc.pathNext(pspCur, bMultiQuality, lesCheckpoint, lesActive); // Compute increment. - cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: AFTER: mIndex=%d uQuality=%d rate=%s") - % pspCur->mIndex - % pspCur->uQuality - % STAmount::saFromRate(pspCur->uQuality)); + cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: AFTER: mIndex=%d uQuality=%d rate=%s") + % pspCur->mIndex + % pspCur->uQuality + % STAmount::saFromRate(pspCur->uQuality)); if (!pspCur->uQuality) { // Path was dry. ++iDry; } - else { + else + { tLog(!pspCur->saInPass || !pspCur->saOutPass, lsDEBUG) << boost::str(boost::format("rippleCalc: better: uQuality=%s saInPass=%s saOutPass=%s") % STAmount::saFromRate(pspCur->uQuality) @@ -2586,15 +2587,18 @@ int iPass = 0; } } } -cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Summary: Pass: %d Dry: %d Paths: %d") % ++iPass % iDry % vpsExpanded.size()); - BOOST_FOREACH(PathState::ref pspCur, vpsExpanded) + if (sLog(lsDEBUG)) { -cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Summary: %d rate: %s quality:%d best: %d consumed: %d") - % pspCur->mIndex - % STAmount::saFromRate(pspCur->uQuality) - % pspCur->uQuality - % (iBest == pspCur->getIndex()) - % pspCur->bConsumed); + cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Summary: Pass: %d Dry: %d Paths: %d") % ++iPass % iDry % vpsExpanded.size()); + BOOST_FOREACH(PathState::ref pspCur, vpsExpanded) + { + cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Summary: %d rate: %s quality:%d best: %d consumed: %d") + % pspCur->mIndex + % STAmount::saFromRate(pspCur->uQuality) + % pspCur->uQuality + % (iBest == pspCur->getIndex()) + % pspCur->bConsumed); + } } if (iBest >= 0) diff --git a/src/cpp/ripple/SHAMap.cpp b/src/cpp/ripple/SHAMap.cpp index 294cad85c7..77051f17ff 100644 --- a/src/cpp/ripple/SHAMap.cpp +++ b/src/cpp/ripple/SHAMap.cpp @@ -23,8 +23,14 @@ DECLARE_INSTANCE(SHAMapTreeNode); void SHAMapNode::setHash() const { - std::size_t h = theApp->getNonceST() + mDepth; + std::size_t h = theApp->getNonceST() + (mDepth * 0x9e3779b9); mHash = mNodeID.hash_combine(h); +#if 0 + const unsigned int *ptr = reinterpret_cast(mNodeID.begin()); + for (int i = (mDepth + 3) / 4; i != 0; --i) + boost::hash_combine(h, *ptr++); + mHash = h; +#endif } std::size_t hash_value(const SHAMapNode& mn) @@ -64,6 +70,7 @@ SHAMap::SHAMap(SHAMapType t, const uint256& hash) : mSeq(1), mState(smsSynching) SHAMap::pointer SHAMap::snapShot(bool isMutable) { // Return a new SHAMap that is an immutable snapshot of this one // Initially nodes are shared, but CoW is forced on both ledgers + boost::recursive_mutex::scoped_lock sl(mLock); SHAMap::pointer ret = boost::make_shared(mType); SHAMap& newMap = *ret; newMap.mSeq = ++mSeq; @@ -158,10 +165,11 @@ SHAMapTreeNode::pointer SHAMap::walkTo(const uint256& id, bool modify) while (!inNode->isLeaf()) { int branch = inNode->selectBranch(id); - if (inNode->isEmptyBranch(branch)) - return inNode; uint256 childHash = inNode->getChildHash(branch); + if (childHash.isZero()) + return inNode; + try { inNode = getNode(inNode->getChildNodeID(branch), childHash, false); @@ -205,7 +213,6 @@ SHAMapTreeNode::pointer SHAMap::getNode(const SHAMapNode& id, const uint256& has std::cerr << "ID: " << id << std::endl; std::cerr << "TgtHash " << hash << std::endl; std::cerr << "NodHash " << node->getNodeHash() << std::endl; - dump(); throw std::runtime_error("invalid node"); } #endif diff --git a/src/cpp/ripple/SHAMap.h b/src/cpp/ripple/SHAMap.h index 5e2eb8d093..ff42121f0f 100644 --- a/src/cpp/ripple/SHAMap.h +++ b/src/cpp/ripple/SHAMap.h @@ -43,20 +43,21 @@ public: SHAMapNode() : mDepth(0), mHash(0) { ; } SHAMapNode(int depth, const uint256& hash); - virtual ~SHAMapNode() { ; } int getDepth() const { return mDepth; } const uint256& getNodeID() const { return mNodeID; } - bool isValid() const { return (mDepth >= 0) && (mDepth < 64); } + bool isValid() const { return (mDepth >= 0) && (mDepth < 64); } + bool isRoot() const { return mDepth == 0; } size_t getHash() const { if (mHash == 0) setHash(); return mHash; } - virtual bool isPopulated() const { return false; } + virtual bool isPopulated() const { return false; } SHAMapNode getParentNodeID() const { assert(mDepth); return SHAMapNode(mDepth - 1, mNodeID); } + SHAMapNode getChildNodeID(int m) const; int selectBranch(const uint256& hash) const; @@ -68,7 +69,6 @@ public: bool operator!=(const uint256&) const; bool operator<=(const SHAMapNode&) const; bool operator>=(const SHAMapNode&) const; - bool isRoot() const { return mDepth == 0; } virtual std::string getString() const; void dump() const; diff --git a/src/cpp/ripple/SerializedLedger.cpp b/src/cpp/ripple/SerializedLedger.cpp index 8f07ec715a..b4285dd879 100644 --- a/src/cpp/ripple/SerializedLedger.cpp +++ b/src/cpp/ripple/SerializedLedger.cpp @@ -51,7 +51,7 @@ SerializedLedgerEntry::SerializedLedgerEntry(LedgerEntryType type, const uint256 SerializedLedgerEntry::pointer SerializedLedgerEntry::getMutable() const { - SerializedLedgerEntry::pointer ret = boost::make_shared(boost::ref(*this)); + SerializedLedgerEntry::pointer ret = boost::make_shared(boost::cref(*this)); ret->mMutable = true; return ret; } diff --git a/src/cpp/ripple/SerializedObject.cpp b/src/cpp/ripple/SerializedObject.cpp index cff7a32724..75dbfb108c 100644 --- a/src/cpp/ripple/SerializedObject.cpp +++ b/src/cpp/ripple/SerializedObject.cpp @@ -130,14 +130,29 @@ std::auto_ptr STObject::makeDeserializedObject(SerializedTypeID } } -void STObject::set(const std::vector& type) +void SOTemplate::push_back(const SOElement &r) +{ + if (mIndex.empty()) + mIndex.resize(SField::getNumFields() + 1, -1); + assert(r.e_field.getNum() < mIndex.size()); + assert(getIndex(r.e_field) == -1); + mIndex[r.e_field.getNum()] = mTypes.size(); + mTypes.push_back(new SOElement(r)); +} + +int SOTemplate::getIndex(SField::ref f) const +{ + assert(f.getNum() < mIndex.size()); + return mIndex[f.getNum()]; +} + +void STObject::set(const SOTemplate& type) { mData.clear(); - mType.clear(); + mType = &type; - BOOST_FOREACH(SOElement::ref elem, type) + BOOST_FOREACH(const SOElement* elem, type.peek()) { - mType.push_back(elem); if (elem->flags != SOE_REQUIRED) giveObject(makeNonPresentObject(elem->e_field)); else @@ -145,15 +160,14 @@ void STObject::set(const std::vector& type) } } -bool STObject::setType(const std::vector &type) +bool STObject::setType(const SOTemplate &type) { - boost::ptr_vector newData(type.size()); + boost::ptr_vector newData(type.peek().size()); bool valid = true; - mType.clear(); - mType.reserve(type.size()); + mType = &type; - BOOST_FOREACH(SOElement::ref elem, type) + BOOST_FOREACH(const SOElement* elem, type.peek()) { bool match = false; for (boost::ptr_vector::iterator it = mData.begin(); it != mData.end(); ++it) @@ -180,8 +194,6 @@ bool STObject::setType(const std::vector &type) } newData.push_back(makeNonPresentObject(elem->e_field)); } - - mType.push_back(elem); } BOOST_FOREACH(const SerializedType& t, mData) @@ -201,7 +213,7 @@ bool STObject::setType(const std::vector &type) bool STObject::isValidForType() { boost::ptr_vector::iterator it = mData.begin(); - BOOST_FOREACH(SOElement::ref elem, mType) + BOOST_FOREACH(const SOElement* elem, mType->peek()) { if (it == mData.end()) return false; @@ -215,14 +227,9 @@ bool STObject::isValidForType() bool STObject::isFieldAllowed(SField::ref field) { - if (isFree()) + if (mType == NULL) return true; - BOOST_FOREACH(SOElement::ref elem, mType) - { // are any required elemnents missing - if (elem->e_field == field) - return true; - } - return false; + return mType->getIndex(field) != -1; } bool STObject::set(SerializerIterator& sit, int depth) @@ -361,6 +368,9 @@ uint256 STObject::getSigningHash(uint32 prefix) const int STObject::getFieldIndex(SField::ref field) const { + if (mType != NULL) + return mType->getIndex(field); + int i = 0; BOOST_FOREACH(const SerializedType& elem, mData) { @@ -1243,11 +1253,11 @@ BOOST_AUTO_TEST_CASE( FieldManipulation_test ) SField sfTestU32(STI_UINT32, 255, "TestU32"); SField sfTestObject(STI_OBJECT, 255, "TestObject"); - std::vector elements; - elements.push_back(new SOElement(sfFlags, SOE_REQUIRED)); - elements.push_back(new SOElement(sfTestVL, SOE_REQUIRED)); - elements.push_back(new SOElement(sfTestH256, SOE_OPTIONAL)); - elements.push_back(new SOElement(sfTestU32, SOE_REQUIRED)); + SOTemplate elements; + elements.push_back(SOElement(sfFlags, SOE_REQUIRED)); + elements.push_back(SOElement(sfTestVL, SOE_REQUIRED)); + elements.push_back(SOElement(sfTestH256, SOE_OPTIONAL)); + elements.push_back(SOElement(sfTestU32, SOE_REQUIRED)); STObject object1(elements, sfTestObject); STObject object2(object1); diff --git a/src/cpp/ripple/SerializedObject.h b/src/cpp/ripple/SerializedObject.h index 545ce91fbb..8638d4e9e8 100644 --- a/src/cpp/ripple/SerializedObject.h +++ b/src/cpp/ripple/SerializedObject.h @@ -18,32 +18,44 @@ DEFINE_INSTANCE(SerializedArray); class SOElement { // An element in the description of a serialized object public: - typedef SOElement const * ref; // used to point to one element + SField::ref e_field; + const SOE_Flags flags; - SField::ref e_field; - const SOE_Flags flags; + SOElement(SField::ref fi, SOE_Flags fl) : e_field(fi), flags(fl) { ; } +}; - SOElement(SField::ref fi, SOE_Flags fl) : e_field(fi), flags(fl) { ; } +class SOTemplate +{ +protected: + std::vector mTypes; + std::vector mIndex; // field num -> index + +public: + SOTemplate() { ; } + const std::vector& peek() const { return mTypes; } + void push_back(const SOElement& r); + int getIndex(SField::ref) const; }; class STObject : public SerializedType, private IS_INSTANCE(SerializedObject) { protected: boost::ptr_vector mData; - std::vector mType; + const SOTemplate* mType; STObject* duplicate() const { return new STObject(*this); } - STObject(SField::ref name, boost::ptr_vector& data) : SerializedType(name) { mData.swap(data); } + STObject(SField::ref name, boost::ptr_vector& data) : SerializedType(name), mType(NULL) + { mData.swap(data); } public: - STObject() { ; } + STObject() : mType(NULL) { ; } - STObject(SField::ref name) : SerializedType(name) { ; } + STObject(SField::ref name) : SerializedType(name), mType(NULL) { ; } - STObject(const std::vector& type, SField::ref name) : SerializedType(name) + STObject(const SOTemplate& type, SField::ref name) : SerializedType(name) { set(type); } - STObject(const std::vector& type, SerializerIterator& sit, SField::ref name) : SerializedType(name) + STObject(const SOTemplate& type, SerializerIterator& sit, SField::ref name) : SerializedType(name) { set(sit); setType(type); } std::auto_ptr oClone() const { return std::auto_ptr(new STObject(*this)); } @@ -54,12 +66,12 @@ public: static std::auto_ptr deserialize(SerializerIterator& sit, SField::ref name); - bool setType(const std::vector& type); + bool setType(const SOTemplate& type); bool isValidForType(); bool isFieldAllowed(SField::ref); - bool isFree() const { return mType.empty(); } + bool isFree() const { return mType == NULL; } - void set(const std::vector&); + void set(const SOTemplate&); bool set(SerializerIterator& u, int depth = 0); virtual SerializedTypeID getSType() const { return STI_OBJECT; } diff --git a/src/cpp/ripple/SerializedTransaction.cpp b/src/cpp/ripple/SerializedTransaction.cpp index 7486e0329d..0aec1aacb8 100644 --- a/src/cpp/ripple/SerializedTransaction.cpp +++ b/src/cpp/ripple/SerializedTransaction.cpp @@ -233,7 +233,7 @@ std::string SerializedTransaction::getSQLInsertReplaceHeader() std::string SerializedTransaction::getMetaSQLInsertHeader() { - return "INSERT INTO Transactions " + getMetaSQLValueHeader() + " VALUES "; + return "INSERT OR REPLACE INTO Transactions " + getMetaSQLValueHeader() + " VALUES "; } std::string SerializedTransaction::getSQL(uint32 inLedger, char status) const diff --git a/src/cpp/ripple/SerializedValidation.cpp b/src/cpp/ripple/SerializedValidation.cpp index f50ed08947..f4031c0c7d 100644 --- a/src/cpp/ripple/SerializedValidation.cpp +++ b/src/cpp/ripple/SerializedValidation.cpp @@ -6,25 +6,22 @@ DECLARE_INSTANCE(SerializedValidation); -std::vector sValidationFormat; +SOTemplate sValidationFormat; -static bool SVFInit() +void SVFInit() { - sValidationFormat.push_back(new SOElement(sfFlags, SOE_REQUIRED)); - sValidationFormat.push_back(new SOElement(sfLedgerHash, SOE_REQUIRED)); - sValidationFormat.push_back(new SOElement(sfLedgerSequence, SOE_OPTIONAL)); - sValidationFormat.push_back(new SOElement(sfCloseTime, SOE_OPTIONAL)); - sValidationFormat.push_back(new SOElement(sfLoadFee, SOE_OPTIONAL)); - sValidationFormat.push_back(new SOElement(sfBaseFee, SOE_OPTIONAL)); - sValidationFormat.push_back(new SOElement(sfFeatures, SOE_OPTIONAL)); - sValidationFormat.push_back(new SOElement(sfSigningTime, SOE_REQUIRED)); - sValidationFormat.push_back(new SOElement(sfSigningPubKey, SOE_REQUIRED)); - sValidationFormat.push_back(new SOElement(sfSignature, SOE_OPTIONAL)); - return true; + sValidationFormat.push_back(SOElement(sfFlags, SOE_REQUIRED)); + sValidationFormat.push_back(SOElement(sfLedgerHash, SOE_REQUIRED)); + sValidationFormat.push_back(SOElement(sfLedgerSequence, SOE_OPTIONAL)); + sValidationFormat.push_back(SOElement(sfCloseTime, SOE_OPTIONAL)); + sValidationFormat.push_back(SOElement(sfLoadFee, SOE_OPTIONAL)); + sValidationFormat.push_back(SOElement(sfBaseFee, SOE_OPTIONAL)); + sValidationFormat.push_back(SOElement(sfFeatures, SOE_OPTIONAL)); + sValidationFormat.push_back(SOElement(sfSigningTime, SOE_REQUIRED)); + sValidationFormat.push_back(SOElement(sfSigningPubKey, SOE_REQUIRED)); + sValidationFormat.push_back(SOElement(sfSignature, SOE_OPTIONAL)); }; -bool SVFinitComplete = SVFInit(); - const uint32 SerializedValidation::sFullFlag = 0x1; SerializedValidation::SerializedValidation(SerializerIterator& sit, bool checkSignature) diff --git a/src/cpp/ripple/Serializer.cpp b/src/cpp/ripple/Serializer.cpp index 4c602eb043..7cf33a6fe2 100644 --- a/src/cpp/ripple/Serializer.cpp +++ b/src/cpp/ripple/Serializer.cpp @@ -476,8 +476,8 @@ int Serializer::encodeLengthLength(int length) if (length < 0) throw std::overflow_error("len<0"); if (length <= 192) return 1; if (length <= 12480) return 2; - if (length >= 918744) return 3; - throw std::overflow_error("len>918644"); + if (length <= 918744) return 3; + throw std::overflow_error("len>918744"); } int Serializer::decodeLengthLength(int b1) diff --git a/src/cpp/ripple/Transaction.cpp b/src/cpp/ripple/Transaction.cpp index bbe9f19384..7d4369e745 100644 --- a/src/cpp/ripple/Transaction.cpp +++ b/src/cpp/ripple/Transaction.cpp @@ -116,7 +116,11 @@ bool Transaction::sign(const RippleAddress& naAccountPrivate) bool Transaction::checkSign() const { - assert(mFromPubKey.isValid()); + if (!mFromPubKey.isValid()) + { + Log(lsWARNING) << "Transaction has bad source public key"; + return false; + } return mTransaction->checkSign(mFromPubKey); } @@ -245,16 +249,6 @@ Transaction::pointer Transaction::load(const uint256& id) return transactionFromSQL(sql); } -Transaction::pointer Transaction::findFrom(const RippleAddress& fromID, uint32 seq) -{ - std::string sql = "SELECT LedgerSeq,Status,RawTxn FROM Transactions WHERE FromID='"; - sql.append(fromID.humanAccountID()); - sql.append("' AND FromSeq='"); - sql.append(boost::lexical_cast(seq)); - sql.append("';"); - return transactionFromSQL(sql); -} - bool Transaction::convertToTransactions(uint32 firstLedgerSeq, uint32 secondLedgerSeq, bool checkFirstTransactions, bool checkSecondTransactions, const SHAMap::SHAMapDiff& inMap, std::map >& outMap) diff --git a/src/cpp/ripple/Transaction.h b/src/cpp/ripple/Transaction.h index d000dea5aa..0ec8eadba9 100644 --- a/src/cpp/ripple/Transaction.h +++ b/src/cpp/ripple/Transaction.h @@ -99,7 +99,6 @@ public: // database functions void save(); static Transaction::pointer load(const uint256& id); - static Transaction::pointer findFrom(const RippleAddress& fromID, uint32 seq); // conversion function static bool convertToTransactions(uint32 ourLedgerSeq, uint32 otherLedgerSeq, diff --git a/src/cpp/ripple/TransactionErr.cpp b/src/cpp/ripple/TransactionErr.cpp index 3ffe6d639a..a605a2f49a 100644 --- a/src/cpp/ripple/TransactionErr.cpp +++ b/src/cpp/ripple/TransactionErr.cpp @@ -10,6 +10,7 @@ bool transResultInfo(TER terCode, std::string& strToken, std::string& strHuman) } transResultInfoA[] = { { tecCLAIM, "tecCLAIM", "Fee claimed. Sequence used. No action." }, { tecDIR_FULL, "tecDIR_FULL", "Can not add entry to full directory." }, + { tecFAILED_PROCESSING, "tecFAILED_PROCESSING", "Failed to correctly process transaction." }, { tecINSUF_RESERVE_LINE, "tecINSUF_RESERVE_LINE", "Insufficent reserve to add trust line." }, { tecINSUF_RESERVE_OFFER, "tecINSUF_RESERVE_OFFER", "Insufficent reserve to create offer." }, { tecNO_DST, "tecNO_DST", "Destination does not exist. Send XRP to create it." }, @@ -42,6 +43,7 @@ bool transResultInfo(TER terCode, std::string& strToken, std::string& strHuman) { telBAD_DOMAIN, "telBAD_DOMAIN", "Domain too long." }, { telBAD_PATH_COUNT, "telBAD_PATH_COUNT", "Malformed: Too many paths." }, { telBAD_PUBLIC_KEY, "telBAD_PUBLIC_KEY", "Public key too long." }, + { telFAILED_PROCESSING, "telFAILED_PROCESSING", "Failed to correctly process transaction." }, { telINSUF_FEE_P, "telINSUF_FEE_P", "Fee insufficient." }, { telNO_DST_PARTIAL, "telNO_DST_PARTIAL", "Partial payment to create account not allowed." }, diff --git a/src/cpp/ripple/TransactionErr.h b/src/cpp/ripple/TransactionErr.h index 97f33f0918..a1d9fb6909 100644 --- a/src/cpp/ripple/TransactionErr.h +++ b/src/cpp/ripple/TransactionErr.h @@ -16,6 +16,7 @@ enum TER // aka TransactionEngineResult telBAD_DOMAIN, telBAD_PATH_COUNT, telBAD_PUBLIC_KEY, + telFAILED_PROCESSING, telINSUF_FEE_P, telNO_DST_PARTIAL, @@ -121,6 +122,7 @@ enum TER // aka TransactionEngineResult tecUNFUNDED_ADD = 102, tecUNFUNDED_OFFER = 103, tecUNFUNDED_PAYMENT = 104, + tecFAILED_PROCESSING = 105, tecDIR_FULL = 121, tecINSUF_RESERVE_LINE = 122, tecINSUF_RESERVE_OFFER = 123, diff --git a/src/cpp/ripple/TransactionFormats.cpp b/src/cpp/ripple/TransactionFormats.cpp index effb983698..66d3a01e0b 100644 --- a/src/cpp/ripple/TransactionFormats.cpp +++ b/src/cpp/ripple/TransactionFormats.cpp @@ -16,7 +16,7 @@ std::map TransactionFormat::byName; #define DECLARE_TF(name, type) tf = new TransactionFormat(#name, type); (*tf) TF_BASE -static bool TFInit() +void TFInit() { TransactionFormat* tf; @@ -84,12 +84,8 @@ static bool TFInit() << SOElement(sfReserveBase, SOE_REQUIRED) << SOElement(sfReserveIncrement, SOE_REQUIRED) ; - - return true; } -bool TFInitComplete = TFInit(); - TransactionFormat* TransactionFormat::getTxnFormat(TransactionType t) { std::map::iterator it = byType.find(static_cast(t)); diff --git a/src/cpp/ripple/TransactionFormats.h b/src/cpp/ripple/TransactionFormats.h index ceb5c9b7d8..a1c9161bb2 100644 --- a/src/cpp/ripple/TransactionFormats.h +++ b/src/cpp/ripple/TransactionFormats.h @@ -31,7 +31,7 @@ class TransactionFormat public: std::string t_name; TransactionType t_type; - std::vector elements; + SOTemplate elements; static std::map byType; static std::map byName; @@ -43,7 +43,7 @@ public: } TransactionFormat& operator<<(const SOElement& el) { - elements.push_back(new SOElement(el)); + elements.push_back(el); return *this; } @@ -68,7 +68,8 @@ const uint32 tfAccountSetMask = ~(tfRequireDestTag|tfOptionalDestTag|tfRequireA // OfferCreate flags: const uint32 tfPassive = 0x00010000; -const uint32 tfOfferCreateMask = ~(tfPassive); +const uint32 tfMarket = 0x00020000; +const uint32 tfOfferCreateMask = ~(tfPassive|tfMarket); // Payment flags: const uint32 tfNoRippleDirect = 0x00010000; diff --git a/src/cpp/ripple/ValidationCollection.cpp b/src/cpp/ripple/ValidationCollection.cpp index cfdec56cd8..699dc8afb4 100644 --- a/src/cpp/ripple/ValidationCollection.cpp +++ b/src/cpp/ripple/ValidationCollection.cpp @@ -293,7 +293,8 @@ void ValidationCollection::condWrite() if (mWriting) return; mWriting = true; - theApp->getJobQueue().addJob(jtWRITE, boost::bind(&ValidationCollection::doWrite, this, _1)); + theApp->getJobQueue().addJob(jtWRITE, "ValidationCollection::doWrite", + boost::bind(&ValidationCollection::doWrite, this, _1)); } void ValidationCollection::doWrite(Job&) diff --git a/src/cpp/ripple/WSHandler.h b/src/cpp/ripple/WSHandler.h index b525859b94..81011b24b5 100644 --- a/src/cpp/ripple/WSHandler.h +++ b/src/cpp/ripple/WSHandler.h @@ -152,17 +152,17 @@ public: ptr->preDestroy(); // Must be done before we return // Must be done without holding the websocket send lock - theApp->getJobQueue().addJob(jtCLIENT, + theApp->getJobQueue().addJob(jtCLIENT, "WSClient::destroy", boost::bind(&WSConnection::destroy, ptr)); } void on_message(connection_ptr cpClient, message_ptr mpMessage) { - theApp->getJobQueue().addJob(jtCLIENT, + theApp->getJobQueue().addJob(jtCLIENT, "WSClient::command", boost::bind(&WSServerHandler::do_message, this, _1, cpClient, mpMessage)); } - void do_message(Job&, connection_ptr cpClient, message_ptr mpMessage) + void do_message(Job& job, connection_ptr cpClient, message_ptr mpMessage) { Json::Value jvRequest; Json::Reader jrReader; @@ -190,6 +190,8 @@ public: } else { + if (jvRequest.isMember("command")) + job.rename(std::string("WSClient::") + jvRequest["command"].asString()); boost::shared_ptr< WSConnection > conn; { boost::mutex::scoped_lock sl(mMapLock); diff --git a/src/cpp/ripple/main.cpp b/src/cpp/ripple/main.cpp index 80e55a7e3f..15783f8afb 100644 --- a/src/cpp/ripple/main.cpp +++ b/src/cpp/ripple/main.cpp @@ -16,6 +16,10 @@ namespace po = boost::program_options; extern bool AddSystemEntropy(); +extern void TFInit(); +extern void LEFInit(); +extern void SVFInit(); + using namespace std; using namespace boost::unit_test; @@ -51,7 +55,6 @@ void startServer() theApp->run(); // Blocks till we get a stop RPC. } - bool init_unit_test() { theApp = new Application(); @@ -66,34 +69,30 @@ void printHelp(const po::options_description& desc) cerr << desc << endl; cerr << "Commands: " << endl; - cerr << " account_domain_set []" << endl; - cerr << " account_email_set []" << endl; - cerr << " account_tx || |( )" << endl; - cerr << " account_lines || []" << endl; - cerr << " account_offers || []" << endl; cerr << " account_info |" << endl; cerr << " account_info || []" << endl; - cerr << " account_message_set " << endl; - cerr << " account_publish_set " << endl; - cerr << " account_rate_set " << endl; - cerr << " account_wallet_set []" << endl; + cerr << " account_lines || []" << endl; + cerr << " account_offers || []" << endl; + cerr << " account_tx || |( )" << endl; + cerr << " book_offers [ [ [ []]]]]" << endl; cerr << " connect []" << endl; + cerr << " consensus_info" << endl; +#if ENABLE_INSECURE cerr << " data_delete " << endl; cerr << " data_fetch " << endl; cerr << " data_store " << endl; +#endif + cerr << " get_counts" << endl; cerr << " ledger [|current|lastclosed] [full]" << endl; + cerr << " ledger_accept" << endl; + cerr << " ledger_closed" << endl; + cerr << " ledger_current" << endl; + cerr << " ledger_header " << endl; cerr << " logrotate " << endl; - cerr << " nickname_info " << endl; - cerr << " nickname_set [] []" << endl; - cerr << " offer_create [passive]" << endl; - cerr << " offer_cancel " << endl; - cerr << " password_fund []" << endl; - cerr << " password_set []" << endl; cerr << " peers" << endl; cerr << " random" << endl; cerr << " ripple ..." << endl; -// cerr << " ripple_line_set [] []" << endl; - cerr << " send [] [] []" << endl; +// cerr << " send [] [] []" << endl; cerr << " stop" << endl; cerr << " tx " << endl; cerr << " unl_add | []" << endl; @@ -109,6 +108,19 @@ void printHelp(const po::options_description& desc) cerr << " wallet_claim [] []" << endl; cerr << " wallet_seed [||]" << endl; cerr << " wallet_propose []" << endl; + +// Transaction helpers (that were removed): +// cerr << " account_domain_set []" << endl; +// cerr << " account_email_set []" << endl; +// cerr << " account_rate_set " << endl; +// cerr << " account_wallet_set []" << endl; +// cerr << " nickname_info " << endl; +// cerr << " nickname_set [] []" << endl; +// cerr << " offer_create [passive]" << endl; +// cerr << " offer_cancel " << endl; +// cerr << " password_fund []" << endl; +// cerr << " password_set []" << endl; +// cerr << " trust_set [] []" << endl; } int main(int argc, char* argv[]) @@ -180,6 +192,10 @@ int main(int argc, char* argv[]) InstanceType::multiThread(); + TFInit(); + LEFInit(); + SVFInit(); + if (vm.count("unittest")) { unit_test_main(init_unit_test, argc, argv); diff --git a/src/js/amount.js b/src/js/amount.js index a1f0aaf6a3..da2e289cd4 100644 --- a/src/js/amount.js +++ b/src/js/amount.js @@ -613,11 +613,16 @@ Amount.prototype.parse_issuer = function (issuer) { Amount.prototype.parse_json = function (j) { if ('string' === typeof j) { // .../.../... notation is not a wire format. But allowed for easier testing. +// var m = j.match(/^(.+)\/(...)(?:\/(.+))?$/); var m = j.match(/^(.+)\/(...)\/(.+)$/); if (m) { this._currency = Currency.from_json(m[2]); - this._issuer = UInt160.from_json(m[3]); + if (m[3]) { + this._issuer = UInt160.from_json(m[3]); + } else { + this._issuer = UInt160.from_json('1'); + } this.parse_value(m[1]); } else { diff --git a/src/js/remote.js b/src/js/remote.js index 72a9f6b627..c241a8b33e 100644 --- a/src/js/remote.js +++ b/src/js/remote.js @@ -91,6 +91,26 @@ Request.prototype.ledger_index = function (ledger_index) { return this; }; +Request.prototype.ledger_select = function (ledger_spec) { + if (ledger_spec === 'closed') { + this.message.ledger_index = -1; + + } else if (ledger_spec === 'current') { + this.message.ledger_index = -2; + + } else if (ledger_spec === 'verified') { + this.message.ledger_index = -3; + + } else if (String(ledger_spec).length > 12) { // XXX Better test needed + this.message.ledger_hash = ledger_spec; + + } else { + this.message.ledger_index = ledger_spec; + } + + return this; +}; + Request.prototype.account_root = function (account) { this.message.account_root = UInt160.json_rewrite(account); @@ -846,15 +866,25 @@ Remote.prototype.request_unsubscribe = function (streams) { return request; }; -// --> current: true, for the current ledger. -Remote.prototype.request_transaction_entry = function (hash, current) { +// .ledger_choose() +// .ledger_hash() +// .ledger_index() +Remote.prototype.request_transaction_entry = function (hash) { //utils.assert(this.trusted); // If not trusted, need to check proof, maybe talk packet protocol. return (new Request(this, 'transaction_entry')) - .ledger_choose(current) .tx_hash(hash); }; +// DEPRECATED: use request_transaction_entry +Remote.prototype.request_tx = function (hash) { + var request = new Request(this, 'tx'); + + request.message.transaction = hash; + + return request; +}; + Remote.prototype.request_account_info = function (accountID) { var request = new Request(this, 'account_info'); diff --git a/src/js/uint.js b/src/js/uint.js index d95ccb51fa..457e3e9590 100644 --- a/src/js/uint.js +++ b/src/js/uint.js @@ -106,14 +106,14 @@ UInt.prototype.parse_generic = function (j) { case undefined: case "0": case this.constructor.STR_ZERO: - case this.constructor.ADDRESS_ZERO: + case this.constructor.ACCOUNT_ZERO: case this.constructor.HEX_ZERO: this._value = nbi(); break; case "1": case this.constructor.STR_ONE: - case this.constructor.ADDRESS_ONE: + case this.constructor.ACCOUNT_ONE: case this.constructor.HEX_ONE: this._value = new BigInteger([1]); diff --git a/src/js/uint160.js b/src/js/uint160.js index 12c7ff04c6..66c980ec0e 100644 --- a/src/js/uint160.js +++ b/src/js/uint160.js @@ -24,8 +24,8 @@ UInt160.width = 20; UInt160.prototype = extend({}, UInt.prototype); UInt160.prototype.constructor = UInt160; -var ADDRESS_ZERO = UInt160.ADDRESS_ZERO = "rrrrrrrrrrrrrrrrrrrrrhoLvTp"; -var ADDRESS_ONE = UInt160.ADDRESS_ONE = "rrrrrrrrrrrrrrrrrrrrBZbvji"; +var ACCOUNT_ZERO = UInt160.ACCOUNT_ZERO = "rrrrrrrrrrrrrrrrrrrrrhoLvTp"; +var ACCOUNT_ONE = UInt160.ACCOUNT_ONE = "rrrrrrrrrrrrrrrrrrrrBZbvji"; var HEX_ZERO = UInt160.HEX_ZERO = "0000000000000000000000000000000000000000"; var HEX_ONE = UInt160.HEX_ONE = "0000000000000000000000000000000000000001"; var STR_ZERO = UInt160.STR_ZERO = utils.hexToString(HEX_ZERO); diff --git a/src/js/uint256.js b/src/js/uint256.js index ab5b6f70b6..42b01504a4 100644 --- a/src/js/uint256.js +++ b/src/js/uint256.js @@ -24,9 +24,6 @@ UInt256.width = 32; UInt256.prototype = extend({}, UInt.prototype); UInt256.prototype.constructor = UInt256; -// XXX Generate these constants (or remove them) -var ADDRESS_ZERO = UInt256.ADDRESS_ZERO = "XXX"; -var ADDRESS_ONE = UInt256.ADDRESS_ONE = "XXX"; var HEX_ZERO = UInt256.HEX_ZERO = "00000000000000000000000000000000" + "00000000000000000000000000000000"; var HEX_ONE = UInt256.HEX_ONE = "00000000000000000000000000000000" + diff --git a/test/amount-test.js b/test/amount-test.js index 31fa7ba085..6a62aff24f 100644 --- a/test/amount-test.js +++ b/test/amount-test.js @@ -17,16 +17,16 @@ buster.testCase("Amount", { buster.assert.equals(nbi(), UInt160.from_generic("0")._value); }, "Parse 0 export" : function () { - buster.assert.equals(UInt160.ADDRESS_ZERO, UInt160.from_generic("0").to_json()); + buster.assert.equals(UInt160.ACCOUNT_ZERO, UInt160.from_generic("0").to_json()); }, "Parse 1" : function () { buster.assert.equals(new BigInteger([1]), UInt160.from_generic("1")._value); }, "Parse rrrrrrrrrrrrrrrrrrrrrhoLvTp export" : function () { - buster.assert.equals(UInt160.ADDRESS_ZERO, UInt160.from_json("rrrrrrrrrrrrrrrrrrrrrhoLvTp").to_json()); + buster.assert.equals(UInt160.ACCOUNT_ZERO, UInt160.from_json("rrrrrrrrrrrrrrrrrrrrrhoLvTp").to_json()); }, "Parse rrrrrrrrrrrrrrrrrrrrBZbvji export" : function () { - buster.assert.equals(UInt160.ADDRESS_ONE, UInt160.from_json("rrrrrrrrrrrrrrrrrrrrBZbvji").to_json()); + buster.assert.equals(UInt160.ACCOUNT_ONE, UInt160.from_json("rrrrrrrrrrrrrrrrrrrrBZbvji").to_json()); }, "Parse mtgox export" : function () { buster.assert.equals(config.accounts["mtgox"].account, UInt160.from_json("mtgox").to_json());