]]]", 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