diff --git a/SConstruct b/SConstruct
index 83dd04ef4e..8362a9383b 100644
--- a/SConstruct
+++ b/SConstruct
@@ -106,7 +106,7 @@ env.Append(LINKFLAGS = ['-rdynamic', '-pthread'])
env.Append(CCFLAGS = ['-pthread', '-Wall', '-Wno-sign-compare', '-Wno-char-subscripts', '-DSQLITE_THREADSAFE=1'])
env.Append(CXXFLAGS = ['-O0', '-pthread', '-Wno-invalid-offsetof', '-Wformat']+BOOSTFLAGS+DEBUGFLAGS)
-if (GCC_VERSION[0] > 4 or (GCC_VERSION[0] == 4 and GCC_VERSION[1] >= 7)):
+if (int(GCC_VERSION[0]) > 4 or (int(GCC_VERSION[0]) == 4 and int(GCC_VERSION[1]) >= 7)):
env.Append(CXXFLAGS = ['-std=c++11'])
if OSX:
diff --git a/newcoin.vcxproj b/newcoin.vcxproj
index fbd79cb56d..07ca73e105 100644
--- a/newcoin.vcxproj
+++ b/newcoin.vcxproj
@@ -101,6 +101,7 @@
+
@@ -175,6 +176,7 @@
+
diff --git a/newcoin.vcxproj.filters b/newcoin.vcxproj.filters
index 961347ae6f..483cb0c2d0 100644
--- a/newcoin.vcxproj.filters
+++ b/newcoin.vcxproj.filters
@@ -249,6 +249,9 @@
Source Files
+
+ Source Files
+
Source Files
@@ -363,6 +366,9 @@
Source Files
+
+ Source Files
+
diff --git a/ripple2010.vcxproj b/ripple2010.vcxproj
index 843c6f9dcf..d3e9aff5c2 100644
--- a/ripple2010.vcxproj
+++ b/ripple2010.vcxproj
@@ -175,6 +175,7 @@
+
diff --git a/ripple2010.vcxproj.filters b/ripple2010.vcxproj.filters
index 0ef380379d..bbdf822554 100644
--- a/ripple2010.vcxproj.filters
+++ b/ripple2010.vcxproj.filters
@@ -243,6 +243,9 @@
Source Files
+
+ Source Files
+
Source Files
diff --git a/rippled-example.cfg b/rippled-example.cfg
index 1d5fa6d73e..fa5690e56e 100644
--- a/rippled-example.cfg
+++ b/rippled-example.cfg
@@ -17,7 +17,7 @@
#
# Example: debug.log
#
-# [validators]:
+# [validators]
# List of nodes to always accept as validators. Nodes are specified by domain
# or public key.
#
@@ -32,7 +32,7 @@
# n9KorY8QtTdRx7TVDpwnG9NvyxsDwHUKUEeDLY3AkiGncVaSXZi5
# n9MqiExBcoG19UXwoLjBJnhsxEhAZMuWwJDRdkyDz1EkEkwzQTNt John Doe
#
-# [validators_file]:
+# [validators_file]
# Path to file contain a list of nodes to always accept as validators. Use
# this to specify a file other than this file to manage your validators list.
#
@@ -51,13 +51,13 @@
# C:/home/johndoe/ripple/validators.txt
# /home/johndoe/ripple/validators.txt
#
-# [validators_site]:
+# [validators_site]
# Specifies where to find validators.txt for UNL boostrapping and RPC
# unl_network command.
#
# Example: ripple.com
#
-# [ips]:
+# [ips]
# List of ips where the Ripple protocol is served. For a starter list, you
# can copy entries from: https://ripple.com/ripple.txt
#
@@ -80,38 +80,38 @@
# time.nist.gov
# pool.ntp.org
#
-# [peer_ip]:
+# [peer_ip]
# IP address or domain to bind to allow external connections from peers.
# Defaults to not binding, which disallows external connections from peers.
#
# Examples: 0.0.0.0 - Bind on all interfaces.
#
-# [peer_port]:
+# [peer_port]
# If peer_ip is supplied, corresponding port to bind to for peer connections.
#
-# [peer_private]:
+# [peer_private]
# 0 or 1.
# 0: request peers to broadcast your address. [default]
# 1: request peers not broadcast your address.
#
-# [rpc_ip]:
+# [rpc_ip]
# IP address or domain to bind to allow insecure RPC connections.
# Defaults to not binding, which disallows RPC connections.
#
-# [rpc_port]:
+# [rpc_port]
# If rpc_ip is supplied, corresponding port to bind to for peer connections.
#
-# [rpc_allow_remote]:
+# [rpc_allow_remote]
# 0 or 1.
# 0: Allow RPC connections only from 127.0.0.1. [default]
# 1: Allow RPC connections from any IP.
#
-# [rpc_admin_allow]:
+# [rpc_admin_allow]
# Specify an list of IP addresses allowed to have admin access. One per line.
#
# Defaults to 127.0.0.1.
#
-# [rpc_user]:
+# [rpc_user]
# As a server, require a this user to specified and require rpc_password to
# be checked for RPC access via the rpc_ip and rpc_port. The user and password
# must be specified via HTTP's basic authentication method.
@@ -119,7 +119,7 @@
# As a client, supply this to the server via HTTP's basic authentication
# method.
#
-# [rpc_password]:
+# [rpc_password]
# As a server, require a this password to specified and require rpc_user to
# be checked for RPC access via the rpc_ip and rpc_port. The user and password
# must be specified via HTTP's basic authentication method.
@@ -127,7 +127,7 @@
# As a client, supply this to the server via HTTP's basic authentication
# method.
#
-# [rpc_admin_user]:
+# [rpc_admin_user]
# As a server, require this as the admin user to be specified. Also, require
# rpc_admin_user and rpc_admin_password to be checked for RPC admin functions.
# The request must specify these as the admin_user and admin_password in the
@@ -135,7 +135,7 @@
#
# As a client, supply this to the server in the request object.
#
-# [rpc_admin_password]:
+# [rpc_admin_password]
# As a server, require this as the admin pasword to be specified. Also,
# require rpc_admin_user and rpc_admin_password to be checked for RPC admin
# functions. The request must specify these as the admin_user and
@@ -143,12 +143,12 @@
#
# As a client, supply this to the server in the request object.
#
-# [validation_quorum]:
+# [validation_quorum]
# Sets the minimum number of trusted validations a ledger must have before
# the server considers it fully validated. Note that if you are validating,
# your validation counts.
#
-# [websocket_public_ip]:
+# [websocket_public_ip]
# IP address or domain to bind to allow untrusted connections from clients.
# In the future, this option will go away and the peer_ip will accept
# websocket client connections.
@@ -156,7 +156,7 @@
# Examples: 0.0.0.0 - Bind on all interfaces.
# 127.0.0.1 - Bind on localhost interface. Only local programs may connect.
#
-# [websocket_public_port]:
+# [websocket_public_port]
# Port to bind to allow untrusted connections from clients. In the future,
# this option will go away and the peer_ip will accept websocket client
# connections.
@@ -172,14 +172,14 @@
# reference client currently shares secrets with its server, this should be
# enabled.
#
-# [websocket_ip]:
+# [websocket_ip]
# IP address or domain to bind to allow trusted ADMIN connections from backend
# applications.
#
# Examples: 0.0.0.0 - Bind on all interfaces.
# 127.0.0.1 - Bind on localhost interface. Only local programs may connect.
#
-# [websocket_port]:
+# [websocket_port]
# Port to bind to allow trusted ADMIN connections from backend applications.
#
# [websocket_secure]
@@ -188,14 +188,14 @@
# 1: Provide ws and wss service for websocket_ip/websocket_port
# 2: Provide wss service for websocket_ip/websocket_port.
#
-# [websocket_ssl_key]:
+# [websocket_ssl_key]
# Specify the filename holding the SSL key in PEM format.
#
-# [websocket_ssl_cert]:
+# [websocket_ssl_cert]
# Specify the path to the SSL certificate file in PEM format.
# This is not needed if the chain includes it.
#
-# [websocket_ssl_chain]:
+# [websocket_ssl_chain]
# If you need a certificate chain, specify the path to the certificate chain
# here. The chain may include the end certificate.
#
@@ -205,7 +205,7 @@
# that the server will accept for verifying HTTP servers. Used only for
# outbound HTTPS client connections.
#
-# [validation_seed]:
+# [validation_seed]
# To perform validation, this section should contain either a validation seed
# or key. The validation seed is used to generate the validation
# public/private key pair. To obtain a validation seed, use the
@@ -214,7 +214,7 @@
# Examples: RASH BUSH MILK LOOK BAD BRIM AVID GAFF BAIT ROT POD LOVE
# shfArahZT9Q9ckTf3s1psJ7C7qzVN
#
-# [node_seed]:
+# [node_seed]
# This is used for clustering. To force a particular node seed or key, the
# key can be set here. The format is the same as the validation_seed field.
# To obtain a validation seed, use the validation_create command.
@@ -222,19 +222,19 @@
# Examples: RASH BUSH MILK LOOK BAD BRIM AVID GAFF BAIT ROT POD LOVE
# shfArahZT9Q9ckTf3s1psJ7C7qzVN
#
-# [node_size]:
+# [node_size]
# Tunes the servers based on the expected load and available memory. Legal
# sizes are "tiny", "small", "medium", "large", and "huge". We recommend
# you start at the default and raise the setting if you have extra memory.
# The default is "tiny".
#
-# [cluster_nodes]:
+# [cluster_nodes]
# To extend full trust to other nodes, place their node public keys here.
# Generally, you should only do this for nodes under common administration.
# Node public keys start with an 'n'. To give a node a name for identification
# place a space after the public key and then the name.
#
-# [ledger_history]:
+# [ledger_history]
# The number of past ledgers to acquire on server startup and the minimum to
# maintain while running.
#
@@ -244,7 +244,7 @@
#
# The default is: 256
#
-# [database_path]:
+# [database_path]
# Full path of database directory.
#
# [path_search_size]
@@ -253,10 +253,12 @@
#
# The default is: 5
#
-# [rpc_startup]:
+# [rpc_startup]
# Specify a list of RPC commands to run at startup.
#
-# Example: { "command" : "server_info" }
+# Examples:
+# { "command" : "server_info" }
+# { "command" : "log_level", "partition" : "ripplecalc", "severity" : "trace" }
#
# Allow other peers to connect to this server.
diff --git a/src/cpp/json/json_value.cpp b/src/cpp/json/json_value.cpp
index b1b1a5221f..63b48020e6 100644
--- a/src/cpp/json/json_value.cpp
+++ b/src/cpp/json/json_value.cpp
@@ -100,8 +100,7 @@ public:
static ValueAllocator *&valueAllocator()
{
- static DefaultValueAllocator defaultAllocator;
- static ValueAllocator *valueAllocator = &defaultAllocator;
+ static ValueAllocator *valueAllocator = new DefaultValueAllocator;
return valueAllocator;
}
diff --git a/src/cpp/ripple/Amount.cpp b/src/cpp/ripple/Amount.cpp
index 2232fae789..7546aa9cfa 100644
--- a/src/cpp/ripple/Amount.cpp
+++ b/src/cpp/ripple/Amount.cpp
@@ -4,6 +4,7 @@
#include
#include
+#include
#include
#include
#include
@@ -257,134 +258,77 @@ std::string STAmount::createHumanCurrency(const uint160& uCurrency)
return sCurrency;
}
-// Assumes trusted input.
bool STAmount::setValue(const std::string& sAmount)
{ // Note: mIsNative and mCurrency must be set already!
- uint64 uValue;
- int iOffset;
- size_t uDecimal = sAmount.find_first_of(mIsNative ? "^" : ".");
- size_t uExp = uDecimal == std::string::npos ? sAmount.find_first_of("e") : std::string::npos;
- bool bInteger = uDecimal == std::string::npos && uExp == std::string::npos;
- mIsNegative = false;
- if (bInteger)
+ static boost::regex reNumber("\\`([+-]?)(\\d*)(\\.(\\d*))?([eE]([+-]?)(\\d+))?\\'");
+ boost::smatch smMatch;
+
+ if (!boost::regex_match(sAmount, smMatch, reNumber))
{
- // Integer input: does not necessarily mean native.
+ cLog(lsWARNING) << "Number not valid: \"" << sAmount << "\"";
+ return false;
+ }
- try
+ // Match fields: 0 = whole input, 1 = sign, 2 = integer portion, 3 = whole fraction (with '.')
+ // 4 = fraction (without '.'), 5 = whole exponent (with 'e'), 6 = exponent sign, 7 = exponent number
+
+ try
+ {
+ if ((smMatch[2].length() + smMatch[4].length()) > 18)
{
- int64 a = sAmount.empty() ? 0 : lexical_cast_st(sAmount);
- if (a >= 0)
- {
- uValue = static_cast(a);
- }
- else
- {
- uValue = static_cast(-a);
- mIsNegative = true;
- }
-
- }
- catch (...)
- {
- cLog(lsINFO) << "Bad integer amount: " << sAmount;
-
+ cLog(lsWARNING) << "Overlong number: " << sAmount;
return false;
}
- iOffset = 0;
- }
- else if (uExp != std::string::npos)
- {
- // e input
- try
+ mIsNegative = (smMatch[1].matched && (smMatch[1] == "-"));
+
+ if (!smMatch[4].matched) // integer only
{
- int64 iInteger = uExp ? lexical_cast_st(sAmount.substr(0, uExp)) : 0;
- if (iInteger >= 0)
- {
- uValue = static_cast(iInteger);
- }
+ mValue = lexical_cast_s(smMatch[2]);
+ mOffset = 0;
+ }
+ else
+ { // integer and fraction
+ mValue = lexical_cast_s(smMatch[2] + smMatch[4]);
+ mOffset = -(smMatch[4].length());
+ }
+
+ if (smMatch[5].matched)
+ { // we have an exponent
+ if (smMatch[6].matched && (smMatch[6] == "-"))
+ mOffset -= lexical_cast_s(smMatch[7]);
else
- {
- uValue = static_cast(-iInteger);
- mIsNegative = true;
- }
-
- iOffset = lexical_cast_st(sAmount.substr(uExp+1));
- }
- catch (...)
- {
- cLog(lsINFO) << "Bad e amount: " << sAmount;
-
- return false;
+ mOffset += lexical_cast_s(smMatch[7]);
}
}
- else
+ catch (...)
{
- // Float input: has a decimal
-
- // Example size decimal size-decimal offset
- // ^1 2 0 2 -1
- // 123^ 4 3 1 0
- // 1^23 4 1 3 -2
- try
- {
- iOffset = -int(sAmount.size() - uDecimal - 1);
-
- // Issolate integer and fraction.
- uint64 uInteger;
- int64 iInteger = uDecimal ? lexical_cast_st(sAmount.substr(0, uDecimal)) : 0;
- if (iInteger >= 0)
- {
- uInteger = static_cast(iInteger);
- }
- else
- {
- uInteger = static_cast(-iInteger);
- mIsNegative = true;
- }
-
- uint64 uFraction = iOffset ? lexical_cast_st(sAmount.substr(uDecimal+1)) : 0;
-
- // Scale the integer portion to the same offset as the fraction.
- uValue = uInteger;
- for (int i = -iOffset; i--;)
- uValue *= 10;
-
- // Add in the fraction.
- uValue += uFraction;
- }
- catch (...)
- {
- cLog(lsINFO) << "Bad float amount: " << sAmount;
-
- return false;
- }
+ cLog(lsWARNING) << "Number not parsed: \"" << sAmount << "\"";
+ return false;
}
+ cLog(lsTRACE) << "Float \"" << sAmount << "\" parsed to " << mValue << " : " << mOffset;
+
if (mIsNative)
{
- if (bInteger)
- iOffset = -SYSTEM_CURRENCY_PRECISION;
+ if (smMatch[3].matched)
+ mOffset -= SYSTEM_CURRENCY_PRECISION;
- while (iOffset > -SYSTEM_CURRENCY_PRECISION) {
- uValue *= 10;
- --iOffset;
+ while (mOffset > 0)
+ {
+ mValue *= 10;
+ --mOffset;
}
- while (iOffset < -SYSTEM_CURRENCY_PRECISION) {
- uValue /= 10;
- ++iOffset;
+ while (mOffset < 0) {
+ mValue /= 10;
+ ++mOffset;
}
-
- mValue = uValue;
}
else
- {
- mValue = uValue;
- mOffset = iOffset;
canonicalize();
- }
+
return true;
}
@@ -1287,11 +1231,13 @@ BOOST_AUTO_TEST_CASE( setValue_test )
{
STAmount saTmp;
+#if 0
// Check native floats
saTmp.setFullValue("1^0"); BOOST_CHECK_MESSAGE(SYSTEM_CURRENCY_PARTS == saTmp.getNValue(), "float integer failed");
saTmp.setFullValue("0^1"); BOOST_CHECK_MESSAGE(SYSTEM_CURRENCY_PARTS/10 == saTmp.getNValue(), "float fraction failed");
saTmp.setFullValue("0^12"); BOOST_CHECK_MESSAGE(12*SYSTEM_CURRENCY_PARTS/100 == saTmp.getNValue(), "float fraction failed");
saTmp.setFullValue("1^2"); BOOST_CHECK_MESSAGE(SYSTEM_CURRENCY_PARTS+(2*SYSTEM_CURRENCY_PARTS/10) == saTmp.getNValue(), "float combined failed");
+#endif
// Check native integer
saTmp.setFullValue("1"); BOOST_CHECK_MESSAGE(1 == saTmp.getNValue(), "integer failed");
diff --git a/src/cpp/ripple/Application.cpp b/src/cpp/ripple/Application.cpp
index e43b959184..e7961cc33a 100644
--- a/src/cpp/ripple/Application.cpp
+++ b/src/cpp/ripple/Application.cpp
@@ -48,7 +48,8 @@ DatabaseCon::~DatabaseCon()
Application::Application() :
mIOWork(mIOService), mAuxWork(mAuxService), mUNL(mIOService), mNetOps(mIOService, &mLedgerMaster),
mTempNodeCache("NodeCache", 16384, 90), mHashedObjectStore(16384, 300), mSLECache("LedgerEntryCache", 4096, 120),
- mSNTPClient(mAuxService), mFeeTrack(), mRpcDB(NULL), mTxnDB(NULL), mLedgerDB(NULL), mWalletDB(NULL),
+ mSNTPClient(mAuxService), mJobQueue(mIOService), mFeeTrack(),
+ mRpcDB(NULL), mTxnDB(NULL), mLedgerDB(NULL), mWalletDB(NULL),
mHashNodeDB(NULL), mNetNodeDB(NULL), mPathFindDB(NULL),
mConnectionPool(mIOService), mPeerDoor(NULL), mRPCDoor(NULL), mWSPublicDoor(NULL), mWSPrivateDoor(NULL),
mSweepTimer(mAuxService), mShutdown(false)
diff --git a/src/cpp/ripple/CallRPC.cpp b/src/cpp/ripple/CallRPC.cpp
index 662170f2a8..6224869405 100644
--- a/src/cpp/ripple/CallRPC.cpp
+++ b/src/cpp/ripple/CallRPC.cpp
@@ -134,72 +134,70 @@ Json::Value RPCParser::parseInternal(const Json::Value& jvParams)
return v;
}
-// account_info ||
-// account_info || [[] ]
-Json::Value RPCParser::parseAccountInfo(const Json::Value& jvParams)
-{
- Json::Value jvRequest(Json::objectValue);
- std::string strIdent = jvParams[0u].asString();
- // YYY This could be more strict and report casting errors.
- int iIndex = 2 == jvParams.size() ? lexical_cast_s(jvParams[1u].asString()) : 0;
-
- RippleAddress raAddress;
-
- if (!raAddress.setAccountPublic(strIdent) && !raAddress.setAccountID(strIdent) && !raAddress.setSeedGeneric(strIdent))
- return rpcError(rpcACT_MALFORMED);
-
- jvRequest["ident"] = strIdent;
- jvRequest["account_index"] = iIndex;
-
- if (jvParams.size() == 3 && !jvParseLedger(jvRequest, jvParams[2u].asString()))
- return rpcError(rpcLGR_IDX_MALFORMED);
-
- return jvRequest;
-}
-
-// account_tx
-// account_tx
-// account_tx binary
-// account_tx binary
+// account_tx accountID [ledger_min [ledger_max [limit [offset]]]] [binary] [count] [descending]
Json::Value RPCParser::parseAccountTransactions(const Json::Value& jvParams)
{
Json::Value jvRequest(Json::objectValue);
RippleAddress raAccount;
-
- unsigned size = jvParams.size();
-
- if ((size > 1) && (jvParams[size - 1].asString() == "binary"))
- {
- jvRequest["binary"] = true;
- --size;
- }
-
- if (size < 2 || size > 3)
- return rpcError(rpcINVALID_PARAMS);
+ unsigned int iParams = jvParams.size();
if (!raAccount.setAccountID(jvParams[0u].asString()))
return rpcError(rpcACT_MALFORMED);
- // YYY This could be more strict and report casting errors.
- if (size == 2)
+ jvRequest["account"] = raAccount.humanAccountID();
+
+ bool bDone = false;
+
+ while (!bDone && iParams >= 2) {
+ if (jvParams[iParams-1].asString() == "binary")
+ {
+ jvRequest["binary"] = true;
+ --iParams;
+ }
+ else if (jvParams[iParams-1].asString() == "count")
+ {
+ jvRequest["count"] = true;
+ --iParams;
+ }
+ else if (jvParams[iParams-1].asString() == "descending")
+ {
+ jvRequest["descending"] = true;
+ --iParams;
+ }
+ else
+ {
+ bDone = true;
+ }
+ }
+
+ if (1 == iParams)
{
- jvRequest["ledger"] = jvParams[1u].asUInt();
+ nothing();
+ }
+ else if (2 == iParams)
+ {
+ if (!jvParseLedger(jvRequest, jvParams[1u].asString()))
+ return jvRequest;
}
else
{
- uint32 uLedgerMin = jvParams[1u].asUInt();
- uint32 uLedgerMax = jvParams[2u].asUInt();
+ int64 uLedgerMin = jvParams[1u].asInt();
+ int64 uLedgerMax = jvParams[2u].asInt();
- if ((uLedgerMax < uLedgerMin) || (uLedgerMax == 0))
+ if (uLedgerMax != -1 && uLedgerMax < uLedgerMin)
{
return rpcError(rpcLGR_IDXS_INVALID);
}
- jvRequest["ledger_min"] = uLedgerMin;
- jvRequest["ledger_max"] = uLedgerMax;
- }
+ jvRequest["ledger_index_min"] = jvParams[1u].asInt();
+ jvRequest["ledger_index_max"] = jvParams[2u].asInt();
- jvRequest["account"] = raAccount.humanAccountID();
+ if (iParams >= 4)
+ jvRequest["limit"] = jvParams[3u].asInt();
+
+ if (iParams >= 5)
+ jvRequest["offset"] = jvParams[4u].asInt();
+ }
return jvRequest;
}
@@ -420,30 +418,34 @@ Json::Value RPCParser::parseLogLevel(const Json::Value& jvParams)
}
// owner_info ||
-// owner_info || []
-Json::Value RPCParser::parseOwnerInfo(const Json::Value& jvParams)
-{
- return parseAccountInfo(jvParams);
-}
-
-// account_lines || []
-// account_offers || []
+// owner_info || []
+// account_info ||
+// account_info || []
+// account_lines || []
+// account_offers || []
Json::Value RPCParser::parseAccountItems(const Json::Value& jvParams)
{
std::string strIdent = jvParams[0u].asString();
- bool bIndex = 2 == jvParams.size();
- int iIndex = bIndex ? lexical_cast_s(jvParams[1u].asString()) : 0;
+ // TODO: Get index from an alternate syntax: rXYZ:
+ int iIndex = 0;
+// int iIndex = jvParams.size() >= 2 ? lexical_cast_s(jvParams[1u].asString()) : 0;
- if (bIndex && !iIndex) // Don't send default.
- bIndex = false;
+ RippleAddress raAddress;
+
+ if (!raAddress.setAccountPublic(strIdent) && !raAddress.setAccountID(strIdent) && !raAddress.setSeedGeneric(strIdent))
+ return rpcError(rpcACT_MALFORMED);
// Get info on account.
Json::Value jvRequest(Json::objectValue);
jvRequest["account"] = strIdent;
- if (bIndex)
+
+ if (iIndex)
jvRequest["account_index"] = iIndex;
+ if (jvParams.size() == 2 && !jvParseLedger(jvRequest, jvParams[1u].asString()))
+ return rpcError(rpcLGR_IDX_MALFORMED);
+
return jvRequest;
}
@@ -456,13 +458,13 @@ Json::Value RPCParser::parseRipplePathFind(const Json::Value& jvParams)
cLog(lsTRACE) << "RPC json: " << jvParams[0u];
- if (bLedger)
- {
- jvParseLedger(jvRequest, jvParams[1u].asString());
- }
-
if (reader.parse(jvParams[0u].asString(), jvRequest))
{
+ if (bLedger)
+ {
+ jvParseLedger(jvRequest, jvParams[1u].asString());
+ }
+
return jvRequest;
}
@@ -642,10 +644,10 @@ 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.
- { "account_info", &RPCParser::parseAccountInfo, 1, 3 },
+ { "account_info", &RPCParser::parseAccountItems, 1, 2 },
{ "account_lines", &RPCParser::parseAccountItems, 1, 2 },
{ "account_offers", &RPCParser::parseAccountItems, 1, 2 },
- { "account_tx", &RPCParser::parseAccountTransactions, 2, 4 },
+ { "account_tx", &RPCParser::parseAccountTransactions, 1, 8 },
{ "book_offers", &RPCParser::parseBookOffers, 2, 7 },
{ "connect", &RPCParser::parseConnect, 1, 2 },
{ "consensus_info", &RPCParser::parseAsIs, 0, 0 },
@@ -660,7 +662,7 @@ Json::Value RPCParser::parseCommand(std::string strMethod, Json::Value jvParams)
{ "log_level", &RPCParser::parseLogLevel, 0, 2 },
{ "logrotate", &RPCParser::parseAsIs, 0, 0 },
// { "nickname_info", &RPCParser::parseNicknameInfo, 1, 1 },
- { "owner_info", &RPCParser::parseOwnerInfo, 1, 2 },
+ { "owner_info", &RPCParser::parseAccountItems, 1, 2 },
{ "peers", &RPCParser::parseAsIs, 0, 0 },
{ "ping", &RPCParser::parseAsIs, 0, 0 },
// { "profile", &RPCParser::parseProfile, 1, 9 },
diff --git a/src/cpp/ripple/CallRPC.h b/src/cpp/ripple/CallRPC.h
index c87ffbe958..4bc3e8439d 100644
--- a/src/cpp/ripple/CallRPC.h
+++ b/src/cpp/ripple/CallRPC.h
@@ -10,7 +10,6 @@ class RPCParser
protected:
typedef Json::Value (RPCParser::*parseFuncPtr)(const Json::Value &jvParams);
- Json::Value parseAccountInfo(const Json::Value& jvParams);
Json::Value parseAccountItems(const Json::Value& jvParams);
Json::Value parseAccountTransactions(const Json::Value& jvParams);
Json::Value parseAsIs(const Json::Value& jvParams);
diff --git a/src/cpp/ripple/Config.cpp b/src/cpp/ripple/Config.cpp
index 096683d3f7..eb904eb594 100644
--- a/src/cpp/ripple/Config.cpp
+++ b/src/cpp/ripple/Config.cpp
@@ -306,7 +306,7 @@ void Config::load()
smtTmp = sectionEntries(secConfig, SECTION_RPC_STARTUP);
if (smtTmp)
{
- Json::Value jvArray(Json::arrayValue);
+ RPC_STARTUP = Json::arrayValue;
BOOST_FOREACH(const std::string& strJson, *smtTmp)
{
@@ -316,10 +316,8 @@ void Config::load()
if (!jrReader.parse(strJson, jvCommand))
throw std::runtime_error(boost::str(boost::format("Couldn't parse [" SECTION_RPC_STARTUP "] command: %s") % strJson));
- jvArray.append(jvCommand);
+ RPC_STARTUP.append(jvCommand);
}
-
- RPC_STARTUP = jvArray;
}
if (sectionSingleB(secConfig, SECTION_DATABASE_PATH, DATABASE_PATH))
@@ -498,7 +496,7 @@ int Config::getSize(SizedItemName item)
{ siLineCacheSize, { 8192, 32768, 131072, 1048576, 0 } },
{ siLineCacheAge, { 500, 600, 1800, 3600, 7200 } },
{ siHashNodeDBCache, { 24, 48, 64, 128, 256 } },
- { siTxnDBCache, { 4, 8, 32, 64, 128 } },
+ { siTxnDBCache, { 4, 12, 48, 96, 192 } },
{ siLgrDBCache, { 4, 8, 32, 64, 128 } }
};
diff --git a/src/cpp/ripple/DBInit.cpp b/src/cpp/ripple/DBInit.cpp
index 082ccd5677..60210514aa 100644
--- a/src/cpp/ripple/DBInit.cpp
+++ b/src/cpp/ripple/DBInit.cpp
@@ -19,6 +19,9 @@ const char *TxnDBInit[] = {
RawTxn BLOB, \
TxnMeta BLOB \
);",
+ "CREATE INDEX TxLgrIndex ON \
+ Transactions(LedgerSeq);",
+
"CREATE TABLE AccountTransactions ( \
TransID CHARACTER(64), \
Account CHARACTER(64), \
diff --git a/src/cpp/ripple/HashedObject.cpp b/src/cpp/ripple/HashedObject.cpp
index 7e97602a8c..0725d5530f 100644
--- a/src/cpp/ripple/HashedObject.cpp
+++ b/src/cpp/ripple/HashedObject.cpp
@@ -183,8 +183,8 @@ HashedObject::pointer HashedObjectStore::retrieve(const uint256& hash)
#ifndef NO_SQLITE3_PREPARE
{
- LoadEvent::autoptr event(theApp->getJobQueue().getLoadEventAP(jtDISK, "HOS::retrieve"));
ScopedLock sl(theApp->getHashNodeDB()->getDBLock());
+ LoadEvent::autoptr event(theApp->getJobQueue().getLoadEventAP(jtDISK, "HOS::retrieve"));
static SqliteStatement pSt(theApp->getHashNodeDB()->getDB()->getSqliteDB(),
"SELECT ObjType,LedgerIndex,Object FROM CommittedObjects WHERE Hash = ?;");
diff --git a/src/cpp/ripple/InstanceCounter.h b/src/cpp/ripple/InstanceCounter.h
index 95301cbbfa..5a8fe1c612 100644
--- a/src/cpp/ripple/InstanceCounter.h
+++ b/src/cpp/ripple/InstanceCounter.h
@@ -50,6 +50,11 @@ public:
sMultiThreaded = true;
}
+ static void shutdown()
+ {
+ sMultiThreaded = false;
+ }
+
static bool isMultiThread()
{
return sMultiThreaded;
diff --git a/src/cpp/ripple/JobQueue.cpp b/src/cpp/ripple/JobQueue.cpp
index 303035fe44..d169a9ddad 100644
--- a/src/cpp/ripple/JobQueue.cpp
+++ b/src/cpp/ripple/JobQueue.cpp
@@ -6,10 +6,12 @@
#include "Log.h"
#include "Config.h"
+#include "Application.h"
SETUP_LOG();
-JobQueue::JobQueue() : mLastJob(0), mThreadCount(0), mShuttingDown(false)
+JobQueue::JobQueue(boost::asio::io_service& svc)
+ : mLastJob(0), mThreadCount(0), mShuttingDown(false), mIOThreadCount(0), mMaxIOThreadCount(1), mIOService(svc)
{
mJobLoads[jtPUBOLDLEDGER].setTargetLatency(10000, 15000);
mJobLoads[jtVALIDATION_ut].setTargetLatency(2000, 5000);
@@ -240,6 +242,8 @@ void JobQueue::setThreadCount(int c)
boost::mutex::scoped_lock sl(mJobLock);
+ mMaxIOThreadCount = 1 + (c / 3);
+
while (mJobCounts[jtDEATH].first != 0)
mJobCond.wait(sl);
@@ -261,6 +265,26 @@ void JobQueue::setThreadCount(int c)
mJobCond.notify_one(); // in case we sucked up someone else's signal
}
+void JobQueue::IOThread(boost::mutex::scoped_lock& sl)
+{ // call with a lock
+ ++mIOThreadCount;
+ sl.unlock();
+ NameThread("IO+");
+ try
+ {
+ do
+ NameThread("IO+");
+ while ((mIOService.poll_one() == 1) && !theApp->isShutdown());
+ }
+ catch (...)
+ {
+ cLog(lsWARNING) << "Exception in IOThread";
+ }
+ NameThread("waiting");
+ sl.lock();
+ --mIOThreadCount;
+}
+
void JobQueue::threadEntry()
{ // do jobs until asked to stop
boost::mutex::scoped_lock sl(mJobLock);
@@ -268,7 +292,12 @@ void JobQueue::threadEntry()
{
NameThread("waiting");
while (mJobSet.empty() && !mShuttingDown)
- mJobCond.wait(sl);
+ {
+ if ((mIOThreadCount < mMaxIOThreadCount) && !theApp->isShutdown())
+ IOThread(sl);
+ else
+ mJobCond.wait(sl);
+ }
if (mShuttingDown)
break;
diff --git a/src/cpp/ripple/JobQueue.h b/src/cpp/ripple/JobQueue.h
index 705744db66..62e058c009 100644
--- a/src/cpp/ripple/JobQueue.h
+++ b/src/cpp/ripple/JobQueue.h
@@ -8,6 +8,7 @@
#include
#include
#include
+#include
#include
#include "../json/value.h"
@@ -93,14 +94,19 @@ protected:
int mThreadCount;
bool mShuttingDown;
+ int mIOThreadCount;
+ int mMaxIOThreadCount;
+ boost::asio::io_service& mIOService;
+
std::map > mJobCounts;
- void threadEntry(void);
+ void threadEntry();
+ void IOThread(boost::mutex::scoped_lock&);
public:
- JobQueue();
+ JobQueue(boost::asio::io_service&);
void addJob(JobType type, const std::string& name, const FUNCTION_TYPE& job);
diff --git a/src/cpp/ripple/LedgerAcquire.cpp b/src/cpp/ripple/LedgerAcquire.cpp
index e345dc18ca..30b61e0af5 100644
--- a/src/cpp/ripple/LedgerAcquire.cpp
+++ b/src/cpp/ripple/LedgerAcquire.cpp
@@ -13,9 +13,9 @@ SETUP_LOG();
DECLARE_INSTANCE(LedgerAcquire);
#define LA_DEBUG
-#define LEDGER_ACQUIRE_TIMEOUT 1000 // millisecond for each ledger timeout
+#define LEDGER_ACQUIRE_TIMEOUT 2000 // millisecond for each ledger timeout
#define LEDGER_TIMEOUT_COUNT 10 // how many timeouts before we giveup
-#define LEDGER_TIMEOUT_AGGRESSIVE 4 // how many timeouts before we get aggressive
+#define LEDGER_TIMEOUT_AGGRESSIVE 6 // how many timeouts before we get aggressive
#define TRUST_NETWORK
PeerSet::PeerSet(const uint256& hash, int interval) : mHash(hash), mTimerInterval(interval), mTimeouts(0),
@@ -98,7 +98,7 @@ bool PeerSet::isActive()
LedgerAcquire::LedgerAcquire(const uint256& hash) : PeerSet(hash, LEDGER_ACQUIRE_TIMEOUT),
mHaveBase(false), mHaveState(false), mHaveTransactions(false), mAborted(false), mSignaled(false), mAccept(false),
- mByHash(true)
+ mByHash(true), mWaitCount(0)
{
#ifdef LA_DEBUG
cLog(lsTRACE) << "Acquiring ledger " << mHash;
@@ -191,6 +191,9 @@ bool LedgerAcquire::tryLocal()
void LedgerAcquire::onTimer(bool progress)
{
+ mRecentTXNodes.clear();
+ mRecentASNodes.clear();
+
if (getTimeouts() > LEDGER_TIMEOUT_COUNT)
{
cLog(lsWARNING) << "Too many timeouts for ledger " << mHash;
@@ -199,21 +202,31 @@ void LedgerAcquire::onTimer(bool progress)
return;
}
- mRecentTXNodes.clear();
- mRecentASNodes.clear();
-
if (!progress)
{
mAggressive = true;
mByHash = true;
- cLog(lsDEBUG) << "No progress for ledger " << mHash;
- if (!getPeerCount())
+ int pc = getPeerCount();
+ cLog(lsDEBUG) << "No progress(" << pc << ") for ledger " << pc << mHash;
+ if (pc == 0)
addPeers();
else
trigger(Peer::pointer());
}
}
+void LedgerAcquire::awaitData()
+{
+ boost::recursive_mutex::scoped_lock sl(mLock);
+ ++mWaitCount;
+}
+
+void LedgerAcquire::noAwaitData()
+{
+ boost::recursive_mutex::scoped_lock sl(mLock);
+ if (mWaitCount > 0 ) --mWaitCount;
+}
+
void LedgerAcquire::addPeers()
{
std::vector peerList = theApp->getConnectionPool().getPeerVector();
@@ -295,8 +308,16 @@ void LedgerAcquire::trigger(Peer::ref peer)
boost::recursive_mutex::scoped_lock sl(mLock);
if (mAborted || mComplete || mFailed)
{
- cLog(lsTRACE) << "Trigger on ledger:" <<
- (mAborted ? " aborted": "") << (mComplete ? " completed": "") << (mFailed ? " failed" : "");
+ cLog(lsDEBUG) << "Trigger on ledger:" <<
+ (mAborted ? " aborted": "") << (mComplete ? " completed": "") << (mFailed ? " failed" : "") <<
+ " wc=" << mWaitCount;
+ return;
+ }
+
+ if ((mWaitCount > 0) && peer)
+ {
+ mRecentPeers.push_back(peer->getPeerId());
+ cLog(lsTRACE) << "Deferring peer";
return;
}
@@ -484,6 +505,8 @@ void LedgerAcquire::trigger(Peer::ref peer)
}
}
+ mRecentPeers.clear();
+
if (mComplete || mFailed)
{
cLog(lsDEBUG) << "Done:" << (mComplete ? " complete" : "") << (mFailed ? " failed " : " ")
@@ -786,13 +809,13 @@ std::vector LedgerAcquire::getNeededHashes()
}
if (!mHaveState)
{
- std::vector v = mLedger->getNeededAccountStateHashes(16);
+ std::vector v = mLedger->getNeededAccountStateHashes(4);
BOOST_FOREACH(const uint256& h, v)
ret.push_back(std::make_pair(ripple::TMGetObjectByHash::otSTATE_NODE, h));
}
if (!mHaveTransactions)
{
- std::vector v = mLedger->getNeededAccountStateHashes(16);
+ std::vector v = mLedger->getNeededAccountStateHashes(4);
BOOST_FOREACH(const uint256& h, v)
ret.push_back(std::make_pair(ripple::TMGetObjectByHash::otTRANSACTION_NODE, h));
}
@@ -846,21 +869,21 @@ void LedgerAcquireMaster::dropLedger(const uint256& hash)
mLedgers.erase(hash);
}
-void LedgerAcquireMaster::gotLedgerData(Job&, boost::shared_ptr packet_ptr,
- boost::weak_ptr wPeer)
+bool LedgerAcquireMaster::awaitLedgerData(const uint256& ledgerHash)
+{
+ LedgerAcquire::pointer ledger = find(ledgerHash);
+ if (!ledger)
+ return false;
+ ledger->awaitData();
+ return true;
+}
+
+void LedgerAcquireMaster::gotLedgerData(Job&, uint256 hash,
+ boost::shared_ptr packet_ptr, boost::weak_ptr wPeer)
{
ripple::TMLedgerData& packet = *packet_ptr;
Peer::pointer peer = wPeer.lock();
- if (!peer)
- return;
- uint256 hash;
- if (packet.ledgerhash().size() != 32)
- {
- peer->punishPeer(LT_InvalidRequest);
- return;
- }
- memcpy(hash.begin(), packet.ledgerhash().data(), 32);
cLog(lsTRACE) << "Got data (" << packet.nodes().size() << ") for acquiring ledger: " << hash;
LedgerAcquire::pointer ledger = find(hash);
@@ -870,6 +893,10 @@ void LedgerAcquireMaster::gotLedgerData(Job&, boost::shared_ptrpunishPeer(LT_InvalidRequest);
return;
}
+ ledger->noAwaitData();
+
+ if (!peer)
+ return;
if (packet.type() == ripple::liBASE)
{
diff --git a/src/cpp/ripple/LedgerAcquire.h b/src/cpp/ripple/LedgerAcquire.h
index c255ef6b7e..4193ce96e1 100644
--- a/src/cpp/ripple/LedgerAcquire.h
+++ b/src/cpp/ripple/LedgerAcquire.h
@@ -87,11 +87,14 @@ public:
protected:
Ledger::pointer mLedger;
bool mHaveBase, mHaveState, mHaveTransactions, mAborted, mSignaled, mAccept, mByHash;
+ int mWaitCount;
std::set mRecentTXNodes;
std::set mRecentASNodes;
- std::vector< FUNCTION_TYPE > mOnComplete;
+ std::vector mRecentPeers;
+
+ std::vector< FUNCTION_TYPE > mOnComplete;
void done();
void onTimer(bool progress);
@@ -124,6 +127,8 @@ public:
void trigger(Peer::ref);
bool tryLocal();
void addPeers();
+ void awaitData();
+ void noAwaitData();
typedef std::pair neededHash_t;
std::vector getNeededHashes();
@@ -148,7 +153,9 @@ public:
LedgerAcquire::pointer find(const uint256& hash);
bool hasLedger(const uint256& ledgerHash);
void dropLedger(const uint256& ledgerHash);
- void gotLedgerData(Job&, boost::shared_ptr packet, boost::weak_ptr peer);
+
+ bool awaitLedgerData(const uint256& ledgerHash);
+ void gotLedgerData(Job&, uint256 hash, boost::shared_ptr packet, boost::weak_ptr peer);
int getFetchCount(int& timeoutCount);
void logFailure(const uint256& h) { mRecentFailures.add(h); }
diff --git a/src/cpp/ripple/LedgerEntrySet.cpp b/src/cpp/ripple/LedgerEntrySet.cpp
index cdf9d30f30..d96a820fda 100644
--- a/src/cpp/ripple/LedgerEntrySet.cpp
+++ b/src/cpp/ripple/LedgerEntrySet.cpp
@@ -230,6 +230,15 @@ void LedgerEntrySet::entryDelete(SLE::ref sle)
}
}
+bool LedgerEntrySet::hasChanges()
+{
+ typedef std::map::value_type u256_LES_pair;
+ BOOST_FOREACH(u256_LES_pair& it, mEntries)
+ if (it.second.mAction != taaCACHED)
+ return true;
+ return false;
+}
+
bool LedgerEntrySet::intersect(const LedgerEntrySet& lesLeft, const LedgerEntrySet& lesRight)
{
return true; // XXX Needs implementation
diff --git a/src/cpp/ripple/LedgerEntrySet.h b/src/cpp/ripple/LedgerEntrySet.h
index c326cc5389..09f9bb0cee 100644
--- a/src/cpp/ripple/LedgerEntrySet.h
+++ b/src/cpp/ripple/LedgerEntrySet.h
@@ -95,6 +95,7 @@ public:
void entryCreate(SLE::ref); // This entry will be created
void entryDelete(SLE::ref); // This entry will be deleted
void entryModify(SLE::ref); // This entry will be modified
+ bool hasChanges(); // True if LES has any changes
// higher-level ledger functions
SLE::pointer entryCreate(LedgerEntryType letType, const uint256& uIndex);
diff --git a/src/cpp/ripple/LedgerMaster.cpp b/src/cpp/ripple/LedgerMaster.cpp
index 4d4920f746..67381e195e 100644
--- a/src/cpp/ripple/LedgerMaster.cpp
+++ b/src/cpp/ripple/LedgerMaster.cpp
@@ -141,6 +141,22 @@ bool LedgerMaster::haveLedger(uint32 seq)
return mCompleteLedgers.hasValue(seq);
}
+bool LedgerMaster::getValidatedRange(uint32& minVal, uint32& maxVal)
+{
+ boost::recursive_mutex::scoped_lock sl(mLock);
+ if (!mValidLedger)
+ return false;
+ maxVal = mValidLedger->getLedgerSeq();
+ if (maxVal == 0)
+ return false;
+ minVal = mCompleteLedgers.prevMissing(maxVal);
+ if (minVal == RangeSet::RangeSetAbsent)
+ minVal = 0;
+ else
+ ++minVal;
+ return true;
+}
+
void LedgerMaster::asyncAccept(Ledger::pointer ledger)
{
uint32 seq = ledger->getLedgerSeq();
@@ -232,7 +248,7 @@ bool LedgerMaster::acquireMissingLedger(Ledger::ref origLedger, const uint256& l
int timeoutCount;
int fetchCount = theApp->getMasterLedgerAcquire().getFetchCount(timeoutCount);
- if (fetchCount < fetchMax)
+ if ((fetchCount < fetchMax) && theApp->getOPs().isFull())
{
if (timeoutCount > 2)
{
@@ -289,7 +305,7 @@ bool LedgerMaster::shouldAcquire(uint32 currentLedger, uint32 ledgerHistory, uin
void LedgerMaster::resumeAcquiring()
{
- if (theApp->getOPs().isNeedNetworkLedger())
+ if (!theApp->getOPs().isFull())
return;
boost::recursive_mutex::scoped_lock ml(mLock);
diff --git a/src/cpp/ripple/LedgerMaster.h b/src/cpp/ripple/LedgerMaster.h
index d2dc0c2d57..6c9d87122b 100644
--- a/src/cpp/ripple/LedgerMaster.h
+++ b/src/cpp/ripple/LedgerMaster.h
@@ -143,6 +143,7 @@ public:
bool haveLedgerRange(uint32 from, uint32 to);
bool haveLedger(uint32 seq);
+ bool getValidatedRange(uint32& minVal, uint32& maxVal);
void resumeAcquiring();
diff --git a/src/cpp/ripple/NetworkOPs.cpp b/src/cpp/ripple/NetworkOPs.cpp
index 7b46154400..8b3e8aa98c 100644
--- a/src/cpp/ripple/NetworkOPs.cpp
+++ b/src/cpp/ripple/NetworkOPs.cpp
@@ -564,7 +564,7 @@ Json::Value NetworkOPs::getOwnerInfo(Ledger::pointer lpLedger, const RippleAddre
//
void NetworkOPs::setStateTimer()
-{ // set timer early if ledger is closing
+{
mNetTimer.expires_from_now(boost::posix_time::milliseconds(LEDGER_GRANULARITY));
mNetTimer.async_wait(boost::bind(&NetworkOPs::checkState, this, boost::asio::placeholders::error));
}
@@ -594,36 +594,41 @@ void NetworkOPs::checkState(const boost::system::error_code& result)
{ // Network state machine
if ((result == boost::asio::error::operation_aborted) || theConfig.RUN_STANDALONE)
+ {
+ cLog(lsFATAL) << "Network state timer error: " << result;
return;
+ }
+
+ {
+ ScopedLock(theApp->getMasterLock());
+
+ std::vector peerList = theApp->getConnectionPool().getPeerVector();
+
+ // do we have sufficient peers? If not, we are disconnected.
+ if (peerList.size() < theConfig.NETWORK_QUORUM)
+ {
+ if (mMode != omDISCONNECTED)
+ {
+ setMode(omDISCONNECTED);
+ cLog(lsWARNING) << "Node count (" << peerList.size() <<
+ ") has fallen below quorum (" << theConfig.NETWORK_QUORUM << ").";
+ }
+ return;
+ }
+ if (mMode == omDISCONNECTED)
+ {
+ setMode(omCONNECTED);
+ cLog(lsINFO) << "Node count (" << peerList.size() << ") is sufficient.";
+ }
+
+ if (!mConsensus)
+ tryStartConsensus();
+
+ if (mConsensus)
+ mConsensus->timerEntry();
+ }
setStateTimer();
-
- ScopedLock(theApp->getMasterLock());
-
- std::vector peerList = theApp->getConnectionPool().getPeerVector();
-
- // do we have sufficient peers? If not, we are disconnected.
- if (peerList.size() < theConfig.NETWORK_QUORUM)
- {
- if (mMode != omDISCONNECTED)
- {
- setMode(omDISCONNECTED);
- cLog(lsWARNING) << "Node count (" << peerList.size() <<
- ") has fallen below quorum (" << theConfig.NETWORK_QUORUM << ").";
- }
- return;
- }
- if (mMode == omDISCONNECTED)
- {
- setMode(omCONNECTED);
- cLog(lsINFO) << "Node count (" << peerList.size() << ") is sufficient.";
- }
-
- if (!mConsensus)
- tryStartConsensus();
-
- if (mConsensus)
- mConsensus->timerEntry();
}
void NetworkOPs::tryStartConsensus()
@@ -1059,17 +1064,56 @@ void NetworkOPs::setMode(OperatingMode om)
}
+std::string
+ NetworkOPs::transactionsSQL(std::string selection, const RippleAddress& account,
+ int32 minLedger, int32 maxLedger, bool descending, uint32 offset, int limit,
+ bool binary, bool count, bool bAdmin)
+{
+ uint32 NONBINARY_PAGE_LENGTH = 200;
+ uint32 BINARY_PAGE_LENGTH = 500;
+
+ uint32 numberOfResults;
+ if (count)
+ numberOfResults = 1000000000;
+ else if (limit < 0)
+ numberOfResults = binary ? BINARY_PAGE_LENGTH : NONBINARY_PAGE_LENGTH;
+ else if (!bAdmin)
+ numberOfResults = std::min(binary ? BINARY_PAGE_LENGTH : NONBINARY_PAGE_LENGTH, static_cast(limit));
+ else
+ numberOfResults = limit;
+
+ std::string maxClause = "";
+ std::string minClause = "";
+ if (maxLedger != -1)
+ maxClause = boost::str(boost::format("AND AccountTransactions.LedgerSeq <= '%u'") % maxLedger);
+ if (minLedger != -1)
+ minClause = boost::str(boost::format("AND AccountTransactions.LedgerSeq >= '%u'") % minLedger);
+
+ std::string sql =
+ boost::str(boost::format("SELECT %s FROM "
+ "AccountTransactions INNER JOIN Transactions ON Transactions.TransID = AccountTransactions.TransID "
+ "WHERE Account = '%s' %s %s "
+ "ORDER BY AccountTransactions.LedgerSeq %s, AccountTransactions.TransID %s LIMIT %u, %u;")
+ % selection
+ % account.humanAccountID()
+ % maxClause
+ % minClause
+ % (descending ? "DESC" : "ASC")
+ % (descending ? "DESC" : "ASC")
+ % boost::lexical_cast(offset)
+ % boost::lexical_cast(numberOfResults)
+ );
+ cLog(lsTRACE) << "txSQL query: " << sql;
+ return sql;
+}
+
std::vector< std::pair >
- NetworkOPs::getAccountTxs(const RippleAddress& account, uint32 minLedger, uint32 maxLedger)
+ NetworkOPs::getAccountTxs(const RippleAddress& account, int32 minLedger, int32 maxLedger, bool descending, uint32 offset, int limit, bool bAdmin)
{ // can be called with no locks
std::vector< std::pair > ret;
- std::string sql =
- str(boost::format("SELECT AccountTransactions.LedgerSeq,Status,RawTxn,TxnMeta FROM "
- "AccountTransactions INNER JOIN Transactions ON Transactions.TransID = AccountTransactions.TransID "
- "WHERE Account = '%s' AND AccountTransactions.LedgerSeq <= '%u' AND AccountTransactions.LedgerSeq >= '%u' "
- "ORDER BY AccountTransactions.LedgerSeq,AccountTransactions.TransID DESC LIMIT 200;")
- % account.humanAccountID() % maxLedger % minLedger);
+ std::string sql = NetworkOPs::transactionsSQL("AccountTransactions.LedgerSeq,Status,RawTxn,TxnMeta", account,
+ minLedger, maxLedger, descending, offset, limit, false, false, bAdmin);
{
Database* db = theApp->getTxnDB()->getDB();
@@ -1098,15 +1142,12 @@ std::vector< std::pair >
}
std::vector NetworkOPs::getAccountTxsB(
- const RippleAddress& account, uint32 minLedger, uint32 maxLedger)
+ const RippleAddress& account, int32 minLedger, int32 maxLedger, bool descending, uint32 offset, int limit, bool bAdmin)
{ // can be called with no locks
std::vector< txnMetaLedgerType> ret;
- std::string sql = str(boost::format("SELECT AccountTransactions.LedgerSeq,Status,RawTxn,TxnMeta FROM "
- "AccountTransactions INNER JOIN Transactions ON Transactions.TransID = AccountTransactions.TransID "
- "WHERE Account = '%s' AND AccountTransactions.LedgerSeq <= '%u' AND AccountTransactions.LedgerSeq >= '%u' "
- "ORDER BY AccountTransactions.LedgerSeq,AccountTransactions.TransID DESC LIMIT 500;")
- % account.humanAccountID() % maxLedger % minLedger);
+ std::string sql = NetworkOPs::transactionsSQL("AccountTransactions.LedgerSeq,Status,RawTxn,TxnMeta", account,
+ minLedger, maxLedger, descending, offset, limit, true/*binary*/, false, bAdmin);
{
Database* db = theApp->getTxnDB()->getDB();
@@ -1144,6 +1185,26 @@ std::vector NetworkOPs::getAccountTxsB(
}
+
+
+uint32
+ NetworkOPs::countAccountTxs(const RippleAddress& account, int32 minLedger, int32 maxLedger)
+{ // can be called with no locks
+ uint32 ret = 0;
+ std::string sql = NetworkOPs::transactionsSQL("COUNT(*) AS 'TransactionCount'", account,
+ minLedger, maxLedger, false, 0, -1, true, true, true);
+
+ Database* db = theApp->getTxnDB()->getDB();
+ ScopedLock sl(theApp->getTxnDB()->getDBLock());
+ SQL_FOREACH(db, sql)
+ {
+ ret = db->getInt("TransactionCount");
+ }
+
+ return ret;
+}
+
+
std::vector
NetworkOPs::getLedgerAffectedAccounts(uint32 ledgerSeq)
{
diff --git a/src/cpp/ripple/NetworkOPs.h b/src/cpp/ripple/NetworkOPs.h
index 00cb710c92..ee656a0612 100644
--- a/src/cpp/ripple/NetworkOPs.h
+++ b/src/cpp/ripple/NetworkOPs.h
@@ -175,6 +175,7 @@ public:
bool isValidated(uint32 seq);
bool isValidated(uint32 seq, const uint256& hash);
bool isValidated(Ledger::ref l) { return isValidated(l->getLedgerSeq(), l->getHash()); }
+ bool getValidatedRange(uint32& minVal, uint32& maxVal) { return mLedgerMaster->getValidatedRange(minVal, maxVal); }
SerializedValidation::ref getLastValidation() { return mLastValidation; }
void setLastValidation(SerializedValidation::ref v) { mLastValidation = v; }
@@ -272,6 +273,7 @@ public:
void needNetworkLedger() { mNeedNetworkLedger = true; }
void clearNeedNetworkLedger() { mNeedNetworkLedger = false; }
bool isNeedNetworkLedger() { return mNeedNetworkLedger; }
+ bool isFull() { return !mNeedNetworkLedger && (mMode == omFULL); }
void setProposing(bool p, bool v) { mProposing = p; mValidating = v; }
bool isProposing() { return mProposing; }
bool isValidating() { return mValidating; }
@@ -292,17 +294,23 @@ public:
bool addWantedHash(const uint256& h);
bool isWantedHash(const uint256& h, bool remove);
+ //Helper function to generate SQL query to get transactions
+ std::string transactionsSQL(std::string selection, const RippleAddress& account,
+ int32 minLedger, int32 maxLedger, bool descending, uint32 offset, int limit,
+ bool binary, bool count, bool bAdmin);
+
+
// client information retrieval functions
std::vector< std::pair >
- getAccountTxs(const RippleAddress& account, uint32 minLedger, uint32 maxLedger);
+ getAccountTxs(const RippleAddress& account, int32 minLedger, int32 maxLedger, bool descending, uint32 offset, int limit, bool bAdmin);
typedef boost::tuple txnMetaLedgerType;
std::vector
- getAccountTxsB(const RippleAddress& account, uint32 minL, uint32 maxL);
+ getAccountTxsB(const RippleAddress& account, int32 minLedger, int32 maxLedger, bool descending, uint32 offset, int limit, bool bAdmin);
std::vector getLedgerAffectedAccounts(uint32 ledgerSeq);
std::vector getLedgerTransactions(uint32 ledgerSeq);
-
+ uint32 countAccountTxs(const RippleAddress& account, int32 minLedger, int32 maxLedger);
//
// Monitoring: publisher side
//
diff --git a/src/cpp/ripple/Pathfinder.cpp b/src/cpp/ripple/Pathfinder.cpp
index ee29d50fe2..c1438825f1 100644
--- a/src/cpp/ripple/Pathfinder.cpp
+++ b/src/cpp/ripple/Pathfinder.cpp
@@ -119,6 +119,10 @@ bool Pathfinder::bDefaultPath(const STPath& spPath)
bool bDefault;
LedgerEntrySet lesActive(mLedger, tapNONE);
+ cLog(lsTRACE) << boost::str(boost::format("bDefaultPath> mSrcAmount=%s mDstAmount=%s")
+ % mSrcAmount.getFullText()
+ % mDstAmount.getFullText());
+
// Expand the current path.
pspCurrent->setExpanded(lesActive, spPath, mDstAccountID, mSrcAccountID);
@@ -126,8 +130,8 @@ bool Pathfinder::bDefaultPath(const STPath& spPath)
// When path is a default (implied). Don't need to add it to return set.
bDefault = pspCurrent->vpnNodes == mPsDefault->vpnNodes;
- cLog(lsTRACE) << "findPaths: expanded path: " << pspCurrent->getJson();
- cLog(lsTRACE) << "findPaths: default path: indirect: " << spPath.getJson(0);
+ cLog(lsTRACE) << "bDefaultPath: expanded path: " << pspCurrent->getJson();
+ cLog(lsTRACE) << "bDefaultPath: default path: indirect: " << spPath.getJson(0);
return bDefault;
}
@@ -162,6 +166,10 @@ Pathfinder::Pathfinder(Ledger::ref ledger,
LedgerEntrySet lesActive(mLedger, tapNONE);
+ cLog(lsTRACE) << boost::str(boost::format("Pathfinder> mSrcAmount=%s mDstAmount=%s")
+ % mSrcAmount.getFullText()
+ % mDstAmount.getFullText());
+
psDefault->setExpanded(lesActive, STPath(), mDstAccountID, mSrcAccountID);
if (tesSUCCESS == psDefault->terStatus)
@@ -296,6 +304,12 @@ bool Pathfinder::findPaths(const unsigned int iMaxSteps, const unsigned int iMax
if (sLog(lsTRACE))
{
+ cLog(lsTRACE) << boost::str(boost::format("findPaths: spe: %s/%s: %s amt: %s")
+ % RippleAddress::createHumanAccountID(speEnd.mAccountID)
+ % RippleAddress::createHumanAccountID(speEnd.mIssuerID)
+ % RippleAddress::createHumanAccountID(mDstAccountID)
+ % RippleAddress::createHumanAccountID(mDstAmount.getIssuer()));
+
cLog(lsTRACE) << "findPaths: finish? account: " << (speEnd.mAccountID == mDstAccountID);
cLog(lsTRACE) << "findPaths: finish? currency: " << (speEnd.mCurrencyID == mDstAmount.getCurrency());
cLog(lsTRACE) << "findPaths: finish? issuer: "
@@ -408,7 +422,7 @@ bool Pathfinder::findPaths(const unsigned int iMaxSteps, const unsigned int iMax
SLE::pointer sleEnd = lesActive.entryCache(ltACCOUNT_ROOT, Ledger::getAccountRootIndex(speEnd.mAccountID));
tLog(!sleEnd, lsDEBUG)
- << boost::str(boost::format("findPaths: order book: %s/%s : ")
+ << boost::str(boost::format("findPaths: tail: %s/%s : ")
% RippleAddress::createHumanAccountID(speEnd.mAccountID)
% RippleAddress::createHumanAccountID(speEnd.mIssuerID));
@@ -445,11 +459,14 @@ bool Pathfinder::findPaths(const unsigned int iMaxSteps, const unsigned int iMax
{
// Path has no credit left. Ignore it.
cLog(lsTRACE) <<
- boost::str(boost::format("findPaths: No credit: %s/%s -> %s/%s")
+ boost::str(boost::format("findPaths: No credit: %s/%s -> %s/%s balance=%s limit=%s")
% RippleAddress::createHumanAccountID(speEnd.mAccountID)
% STAmount::createHumanCurrency(speEnd.mCurrencyID)
% RippleAddress::createHumanAccountID(uPeerID)
- % STAmount::createHumanCurrency(speEnd.mCurrencyID));
+ % STAmount::createHumanCurrency(speEnd.mCurrencyID)
+ % rspEntry->getBalance().getFullText()
+ % rspEntry->getLimitPeer().getFullText()
+ );
}
else
{
@@ -463,19 +480,23 @@ bool Pathfinder::findPaths(const unsigned int iMaxSteps, const unsigned int iMax
bContinued = true;
cLog(lsTRACE) <<
- boost::str(boost::format("findPaths: push explore: %s/%s -> %s/%s")
+ boost::str(boost::format("findPaths: push explore: %s/%s -> %s/%s balance=%s limit=%s limit_peer=%s")
% STAmount::createHumanCurrency(speEnd.mCurrencyID)
% RippleAddress::createHumanAccountID(speEnd.mAccountID)
% STAmount::createHumanCurrency(speEnd.mCurrencyID)
- % RippleAddress::createHumanAccountID(uPeerID));
+ % RippleAddress::createHumanAccountID(uPeerID)
+ % rspEntry->getBalance().getFullText()
+ % rspEntry->getLimit().getFullText()
+ % rspEntry->getLimitPeer().getFullText());
}
}
}
- // XXX Flip argument order to norm.
+ // XXX Flip argument order to norm. (currency, issuer)
std::vector books;
theApp->getOrderBookDB().getBooksByTakerPays(speEnd.mIssuerID, speEnd.mCurrencyID, books);
+
BOOST_FOREACH(OrderBook::ref book, books)
{
if (!spPath.hasSeen(ACCOUNT_XRP, book->getCurrencyOut(), book->getIssuerOut()))
@@ -533,8 +554,10 @@ bool Pathfinder::findPaths(const unsigned int iMaxSteps, const unsigned int iMax
TER terResult;
try {
+ LedgerEntrySet lesSandbox(lesActive.duplicate());
+
terResult = RippleCalc::rippleCalc(
- lesActive,
+ lesSandbox,
saMaxAmountAct,
saDstAmountAct,
vpsExpanded,
diff --git a/src/cpp/ripple/Peer.cpp b/src/cpp/ripple/Peer.cpp
index 4a87a4dc4d..55bb4f4e09 100644
--- a/src/cpp/ripple/Peer.cpp
+++ b/src/cpp/ripple/Peer.cpp
@@ -1659,18 +1659,17 @@ void Peer::recvLedger(const boost::shared_ptr& packet_ptr)
return;
}
+ uint256 hash;
+ if(packet.ledgerhash().size() != 32)
+ {
+ cLog(lsWARNING) << "TX candidate reply with invalid hash size";
+ punishPeer(LT_InvalidRequest);
+ return;
+ }
+ memcpy(hash.begin(), packet.ledgerhash().data(), 32);
+
if (packet.type() == ripple::liTS_CANDIDATE)
{ // got data for a candidate transaction set
- uint256 hash;
- if(packet.ledgerhash().size() != 32)
- {
- cLog(lsWARNING) << "TX candidate reply with invalid hash size";
- punishPeer(LT_InvalidRequest);
- return;
- }
- memcpy(hash.begin(), packet.ledgerhash().data(), 32);
-
-
std::list nodeIDs;
std::list< std::vector > nodeData;
@@ -1692,9 +1691,12 @@ void Peer::recvLedger(const boost::shared_ptr& packet_ptr)
return;
}
- theApp->getJobQueue().addJob(jtLEDGER_DATA, "gotLedgerData",
- BIND_TYPE(&LedgerAcquireMaster::gotLedgerData, &theApp->getMasterLedgerAcquire(),
- P_1, packet_ptr, boost::weak_ptr(shared_from_this())));
+ if (theApp->getMasterLedgerAcquire().awaitLedgerData(hash))
+ theApp->getJobQueue().addJob(jtLEDGER_DATA, "gotLedgerData",
+ BIND_TYPE(&LedgerAcquireMaster::gotLedgerData, &theApp->getMasterLedgerAcquire(),
+ P_1, hash, packet_ptr, boost::weak_ptr(shared_from_this())));
+ else
+ punishPeer(LT_UnwantedData);
}
bool Peer::hasLedger(const uint256& hash) const
diff --git a/src/cpp/ripple/RPCHandler.cpp b/src/cpp/ripple/RPCHandler.cpp
index cc0a84f7ee..dc798aefa3 100644
--- a/src/cpp/ripple/RPCHandler.cpp
+++ b/src/cpp/ripple/RPCHandler.cpp
@@ -9,7 +9,6 @@
#include "Pathfinder.h"
#include "Log.h"
-#include "NetworkOPs.h"
#include "RPCHandler.h"
#include "RPCSub.h"
#include "Application.h"
@@ -1235,7 +1234,6 @@ Json::Value RPCHandler::doRipplePathFind(Json::Value jvRequest, int& cost)
cost = rpcCOST_EXPENSIVE;
Ledger::pointer lSnapShot = boost::make_shared(boost::ref(*lpLedger), false);
- LedgerEntrySet lesSnapshot(lSnapShot, tapNONE);
ScopedUnlock su(theApp->getMasterLock()); // As long as we have a locked copy of the ledger, we can unlock.
@@ -1243,6 +1241,7 @@ Json::Value RPCHandler::doRipplePathFind(Json::Value jvRequest, int& cost)
for (unsigned int i=0; i != jvSrcCurrencies.size(); ++i) {
Json::Value jvSource = jvSrcCurrencies[i];
+
uint160 uSrcCurrencyID;
uint160 uSrcIssuerID;
@@ -1277,7 +1276,7 @@ Json::Value RPCHandler::doRipplePathFind(Json::Value jvRequest, int& cost)
if (!pf.findPaths(theConfig.PATH_SEARCH_SIZE, 3, spsComputed))
{
- cLog(lsDEBUG) << "ripple_path_find: No paths found.";
+ cLog(lsWARNING) << "ripple_path_find: No paths found.";
}
else
{
@@ -1294,9 +1293,11 @@ Json::Value RPCHandler::doRipplePathFind(Json::Value jvRequest, int& cost)
1);
saMaxAmount.negate();
+ LedgerEntrySet lesSandbox(lSnapShot, tapNONE);
+
TER terResult =
RippleCalc::rippleCalc(
- lesSnapshot,
+ lesSandbox,
saMaxAmountAct, // <--
saDstAmountAct, // <--
vpsExpanded, // <--
@@ -1314,8 +1315,7 @@ Json::Value RPCHandler::doRipplePathFind(Json::Value jvRequest, int& cost)
// cLog(lsDEBUG) << "ripple_path_find: PATHS IN: " << spsComputed.size() << " : " << spsComputed.getJson(0);
// cLog(lsDEBUG) << "ripple_path_find: PATHS EXP: " << vpsExpanded.size();
-
- cLog(lsDEBUG)
+ cLog(lsWARNING)
<< boost::str(boost::format("ripple_path_find: saMaxAmount=%s saDstAmount=%s saMaxAmountAct=%s saDstAmountAct=%s")
% saMaxAmount
% saDstAmount
@@ -1656,17 +1656,29 @@ Json::Value RPCHandler::doLedger(Json::Value jvRequest, int& cost)
return ret;
}
-// { account: , ledger: }
-// { account: , ledger_min: , ledger_max: }
-// THIS ROUTINE DOESN'T SCALE.
-// FIXME: Require admin.
-// FIXME: Doesn't report database holes.
-// FIXME: For consistency change inputs to: ledger_index, ledger_index_min, ledger_index_max.
+// {
+// account: account,
+// ledger_index_min: ledger_index,
+// ledger_index_max: ledger_index,
+// binary: boolean, // optional, defaults to false
+// count: boolean, // optional, defaults to false
+// descending: boolean, // optional, defaults to false
+// offset: integer, // optional, defaults to 0
+// limit: integer // optional
+// }
Json::Value RPCHandler::doAccountTransactions(Json::Value jvRequest, int& cost)
{
RippleAddress raAccount;
- uint32 minLedger;
- uint32 maxLedger;
+ uint32 offset = jvRequest.isMember("offset") ? jvRequest["offset"].asUInt() : 0;
+ int limit = jvRequest.isMember("limit") ? jvRequest["limit"].asUInt() : -1;
+ bool bBinary = jvRequest.isMember("binary") && jvRequest["binary"].asBool();
+ bool bDescending = jvRequest.isMember("descending") && jvRequest["descending"].asBool();
+ bool bCount = jvRequest.isMember("count") && jvRequest["count"].asBool();
+ uint32 uLedgerMin;
+ uint32 uLedgerMax;
+ uint32 uValidatedMin;
+ uint32 uValidatedMax;
+ bool bValidated = mNetOps->getValidatedRange(uValidatedMin, uValidatedMax);
if (!jvRequest.isMember("account"))
return rpcError(rpcINVALID_PARAMS);
@@ -1674,10 +1686,37 @@ Json::Value RPCHandler::doAccountTransactions(Json::Value jvRequest, int& cost)
if (!raAccount.setAccountID(jvRequest["account"].asString()))
return rpcError(rpcACT_MALFORMED);
- if (jvRequest.isMember("ledger_min") && jvRequest.isMember("ledger_max"))
+ // DEPRECATED
+ if (jvRequest.isMember("ledger_min"))
{
- minLedger = jvRequest["ledger_min"].asUInt();
- maxLedger = jvRequest["ledger_max"].asUInt();
+ jvRequest["ledger_index_min"] = jvRequest["ledger_min"];
+ bDescending = true;
+ }
+
+ // DEPRECATED
+ if (jvRequest.isMember("ledger_max"))
+ {
+ jvRequest["ledger_index_max"] = jvRequest["ledger_max"];
+ bDescending = true;
+ }
+
+ if (jvRequest.isMember("ledger_index_min") || jvRequest.isMember("ledger_index_max"))
+ {
+ int64 iLedgerMin = jvRequest.isMember("ledger_index_min") ? jvRequest["ledger_index_min"].asInt() : -1;
+ int64 iLedgerMax = jvRequest.isMember("ledger_index_max") ? jvRequest["ledger_index_max"].asInt() : -1;
+
+ if (!bValidated && (iLedgerMin == -1 || iLedgerMax == -1)) {
+ // Don't have a validated ledger range.
+ return rpcError(rpcLGR_IDXS_INVALID);
+ }
+
+ uLedgerMin = iLedgerMin == -1 ? uValidatedMin : iLedgerMin;
+ uLedgerMax = iLedgerMax == -1 ? uValidatedMax : iLedgerMax;
+
+ if (uLedgerMax < uLedgerMin)
+ {
+ return rpcError(rpcLGR_IDXS_INVALID);
+ }
}
else
{
@@ -1685,65 +1724,74 @@ Json::Value RPCHandler::doAccountTransactions(Json::Value jvRequest, int& cost)
Json::Value ret = lookupLedger(jvRequest, l);
if (!l)
return ret;
- minLedger = maxLedger = l->getLedgerSeq();
- }
-
- if ((maxLedger < minLedger) || (maxLedger == 0))
- {
- return rpcError(rpcLGR_IDXS_INVALID);
+ uLedgerMin = uLedgerMax = l->getLedgerSeq();
}
#ifndef DEBUG
try
{
#endif
- unsigned int vl = mNetOps->getValidatedSeq();
ScopedUnlock su(theApp->getMasterLock());
Json::Value ret(Json::objectValue);
+
ret["account"] = raAccount.humanAccountID();
- if (jvRequest.isMember("binary") && jvRequest["binary"].asBool())
+ if (bBinary)
{
std::vector txns =
- mNetOps->getAccountTxsB(raAccount, minLedger, maxLedger);
+ mNetOps->getAccountTxsB(raAccount, uLedgerMin, uLedgerMax, bDescending, offset, limit, mRole == ADMIN);
+
for (std::vector::const_iterator it = txns.begin(), end = txns.end();
it != end; ++it)
{
- Json::Value obj(Json::objectValue);
- obj["transaction"] = it->get<0>();
- obj["meta"] = it->get<1>();
- obj["inLedger"] = it->get<2>();
- if (it->get<2>() > vl)
- obj["validated"] = false;
- else if (mNetOps->haveLedger(it->get<2>()))
- obj["validated"] = true;
- ret["transactions"].append(obj);
+ Json::Value jvObj(Json::objectValue);
+ uint32 uLedgerIndex = it->get<2>();
+
+ jvObj["tx_blob"] = it->get<0>();
+ jvObj["meta"] = it->get<1>();
+ jvObj["ledger_index"] = uLedgerIndex;
+ jvObj["validated"] = bValidated && uValidatedMin <= uLedgerIndex && uValidatedMax >= uLedgerIndex;
+
+ ret["transactions"].append(jvObj);
}
}
else
{
- std::vector< std::pair > txns = mNetOps->getAccountTxs(raAccount, minLedger, maxLedger);
+ std::vector< std::pair > txns = mNetOps->getAccountTxs(raAccount, uLedgerMin, uLedgerMax, bDescending, offset, limit, mRole == ADMIN);
+
for (std::vector< std::pair >::iterator it = txns.begin(), end = txns.end(); it != end; ++it)
{
- Json::Value obj(Json::objectValue);
+ Json::Value jvObj(Json::objectValue);
if (it->first)
- obj["tx"] = it->first->getJson(1);
+ jvObj["tx"] = it->first->getJson(1);
+
if (it->second)
{
- obj["meta"] = it->second->getJson(0);
+ uint32 uLedgerIndex = it->second->getLgrSeq();
- uint32 s = it->second->getLgrSeq();
- if (s > vl)
- obj["validated"] = false;
- else if (mNetOps->haveLedger(s))
- obj["validated"] = true;
+ jvObj["meta"] = it->second->getJson(0);
+ jvObj["validated"] = bValidated && uValidatedMin <= uLedgerIndex && uValidatedMax >= uLedgerIndex;
}
- ret["transactions"].append(obj);
+ ret["transactions"].append(jvObj);
}
}
+
+ //Add information about the original query
+ ret["ledger_index_min"] = uLedgerMin;
+ ret["ledger_index_max"] = uLedgerMax;
+ ret["validated"] = bValidated && uValidatedMin <= uLedgerMin && uValidatedMax >= uLedgerMax;
+ ret["offset"] = offset;
+
+ if (bCount)
+ ret["count"] = mNetOps->countAccountTxs(raAccount, uLedgerMin, uLedgerMax);
+
+ if (jvRequest.isMember("limit"))
+ ret["limit"] = limit;
+
+
return ret;
#ifndef DEBUG
}
diff --git a/src/cpp/ripple/RPCHandler.h b/src/cpp/ripple/RPCHandler.h
index 63ba7b556b..b716ebc55f 100644
--- a/src/cpp/ripple/RPCHandler.h
+++ b/src/cpp/ripple/RPCHandler.h
@@ -8,6 +8,7 @@
#include "RippleAddress.h"
#include "SerializedTypes.h"
#include "Ledger.h"
+#include "NetworkOPs.h"
#define LEDGER_CURRENT -1
#define LEDGER_CLOSED -2
diff --git a/src/cpp/ripple/RippleAddress.cpp b/src/cpp/ripple/RippleAddress.cpp
index aac1695e19..7ef204277e 100644
--- a/src/cpp/ripple/RippleAddress.cpp
+++ b/src/cpp/ripple/RippleAddress.cpp
@@ -18,9 +18,16 @@
#include "utils.h"
#include "Log.h"
#include "Serializer.h"
+#include "Application.h"
SETUP_LOG();
+std::size_t hash_value(const CBase58Data& b58)
+{
+ std::size_t seed = theApp->getNonceST() + (b58.nVersion * 0x9e3779b9);
+ boost::hash_combine(seed, b58.vchData);
+ return seed;
+}
RippleAddress::RippleAddress()
{
diff --git a/src/cpp/ripple/RippleCalc.cpp b/src/cpp/ripple/RippleCalc.cpp
index bf94a47839..a8fe832076 100644
--- a/src/cpp/ripple/RippleCalc.cpp
+++ b/src/cpp/ripple/RippleCalc.cpp
@@ -250,9 +250,15 @@ TER PathState::pushNode(
if (tesSUCCESS == terResult)
{
STAmount saOwed = lesEntries.rippleOwed(pnCur.uAccountID, pnBck.uAccountID, pnCur.uCurrencyID);
+ STAmount saLimit;
- if (!saOwed.isPositive() && -saOwed >= lesEntries.rippleLimit(pnCur.uAccountID, pnBck.uAccountID, pnCur.uCurrencyID))
+ if (!saOwed.isPositive()
+ && -saOwed >= (saLimit = lesEntries.rippleLimit(pnCur.uAccountID, pnBck.uAccountID, pnCur.uCurrencyID)))
{
+ cLog(lsWARNING) << boost::str(boost::format("pushNode: dry: saOwed=%s saLimit=%s")
+ % saOwed.getFullText()
+ % saLimit.getFullText());
+
terResult = tecPATH_DRY;
}
}
@@ -323,6 +329,8 @@ void PathState::setExpanded(
const uint160 uOutIssuerID = saOutReq.getIssuer();
const uint160 uSenderIssuerID = !!uMaxCurrencyID ? uSenderID : ACCOUNT_XRP; // Sender is always issuer for non-XRP.
+ // cLog(lsDEBUG) << boost::str(boost::format("setExpanded>"));
+
lesEntries = lesSource.duplicate();
terStatus = tesSUCCESS;
@@ -344,7 +352,7 @@ void PathState::setExpanded(
uMaxCurrencyID, // Max specifes the currency.
uSenderIssuerID);
- cLog(lsDEBUG) << boost::str(boost::format("PathState: pushed: account=%s currency=%s issuer=%s")
+ cLog(lsDEBUG) << boost::str(boost::format("setExpanded: pushed: account=%s currency=%s issuer=%s")
% RippleAddress::createHumanAccountID(uSenderID)
% STAmount::createHumanCurrency(uMaxCurrencyID)
% RippleAddress::createHumanAccountID(uSenderIssuerID));
@@ -366,8 +374,10 @@ void PathState::setExpanded(
: uOutIssuerID // Use implied node.
: ACCOUNT_XRP;
- cLog(lsDEBUG) << boost::str(boost::format("PathState: implied check: uNxtCurrencyID=%s uNxtAccountID=%s")
- % RippleAddress::createHumanAccountID(uNxtCurrencyID)
+ cLog(lsDEBUG) << boost::str(boost::format("setExpanded: implied check: uMaxIssuerID=%s uSenderIssuerID=%s uNxtCurrencyID=%s uNxtAccountID=%s")
+ % RippleAddress::createHumanAccountID(uMaxIssuerID)
+ % RippleAddress::createHumanAccountID(uSenderIssuerID)
+ % STAmount::createHumanCurrency(uNxtCurrencyID)
% RippleAddress::createHumanAccountID(uNxtAccountID));
// Can't just use push implied, because it can't compensate for next account.
@@ -375,9 +385,9 @@ void PathState::setExpanded(
|| 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")
+ cLog(lsDEBUG) << boost::str(boost::format("setExpanded: sender implied: account=%s currency=%s issuer=%s")
% RippleAddress::createHumanAccountID(uMaxIssuerID)
- % RippleAddress::createHumanAccountID(uMaxCurrencyID)
+ % STAmount::createHumanCurrency(uMaxCurrencyID)
% RippleAddress::createHumanAccountID(uMaxIssuerID));
// Add account implied by SendMax.
terStatus = pushNode(
@@ -394,7 +404,7 @@ void PathState::setExpanded(
{
if (tesSUCCESS == terStatus)
{
- cLog(lsDEBUG) << boost::str(boost::format("PathState: element in path:"));
+ cLog(lsDEBUG) << boost::str(boost::format("setExpanded: element in path:"));
terStatus = pushNode(speElement.getNodeType(), speElement.getAccountID(), speElement.getCurrency(), speElement.getIssuerID());
}
}
@@ -408,9 +418,9 @@ void PathState::setExpanded(
|| 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")
+ cLog(lsDEBUG) << boost::str(boost::format("setExpanded: receiver implied: account=%s currency=%s issuer=%s")
% RippleAddress::createHumanAccountID(uOutIssuerID)
- % RippleAddress::createHumanAccountID(uOutCurrencyID)
+ % STAmount::createHumanCurrency(uOutCurrencyID)
% RippleAddress::createHumanAccountID(uOutIssuerID));
terStatus = pushNode(
!!uOutCurrencyID
@@ -449,7 +459,7 @@ void PathState::setExpanded(
if (!umForward.insert(std::make_pair(boost::make_tuple(pnCur.uAccountID, pnCur.uCurrencyID, pnCur.uIssuerID), uNode)).second)
{
// Failed to insert. Have a loop.
- cLog(lsDEBUG) << boost::str(boost::format("PathState: loop detected: %s")
+ cLog(lsDEBUG) << boost::str(boost::format("setExpanded: loop detected: %s")
% getJson());
terStatus = temBAD_PATH_LOOP;
@@ -457,7 +467,7 @@ void PathState::setExpanded(
}
}
- cLog(lsDEBUG) << boost::str(boost::format("PathState: in=%s/%s out=%s/%s %s")
+ cLog(lsDEBUG) << boost::str(boost::format("setExpanded: in=%s/%s out=%s/%s %s")
% STAmount::createHumanCurrency(uMaxCurrencyID)
% RippleAddress::createHumanAccountID(uMaxIssuerID)
% STAmount::createHumanCurrency(uOutCurrencyID)
@@ -2612,6 +2622,10 @@ TER RippleCalc::rippleCalc(
{
RippleCalc rc(lesActive, bOpenLedger);
+ cLog(lsTRACE) << boost::str(boost::format("rippleCalc> saMaxAmountReq=%s saDstAmountReq=%s")
+ % saMaxAmountReq.getFullText()
+ % saDstAmountReq.getFullText());
+
TER terResult = temUNCERTAIN;
// YYY Might do basic checks on src and dst validity as per doPayment.
@@ -2669,6 +2683,12 @@ int iIndex = 0;
if (!pspExpanded)
return temUNKNOWN;
+ cLog(lsTRACE) << boost::str(boost::format("rippleCalc: EXPAND: saDstAmountReq=%s saMaxAmountReq=%s uDstAccountID=%s uSrcAccountID=%s")
+ % saDstAmountReq.getFullText()
+ % saMaxAmountReq.getFullText()
+ % RippleAddress::createHumanAccountID(uDstAccountID)
+ % RippleAddress::createHumanAccountID(uSrcAccountID));
+
pspExpanded->setExpanded(lesActive, spPath, uDstAccountID, uSrcAccountID);
cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Build path: %d: status: %s")
@@ -2764,6 +2784,7 @@ int iPass = 0;
}
}
}
+
if (sLog(lsDEBUG))
{
cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Summary: Pass: %d Dry: %d Paths: %d") % ++iPass % iDry % vpsExpanded.size());
@@ -3003,7 +3024,7 @@ void TransactionEngine::calcOfferBridgeNext(
{
// Offer must be redeeming IOUs.
- // No additional
+ // No additional
// XXX Broken
}
diff --git a/src/cpp/ripple/RippleCalc.h b/src/cpp/ripple/RippleCalc.h
index 8df0eebba9..c23f2be837 100644
--- a/src/cpp/ripple/RippleCalc.h
+++ b/src/cpp/ripple/RippleCalc.h
@@ -209,8 +209,8 @@ public:
const bool bPartialPayment,
const bool bLimitQuality,
const bool bNoRippleDirect,
- const bool bStandAlone,
- const bool bOpenLedger = true
+ const bool bStandAlone, // --> True, not to affect accounts.
+ const bool bOpenLedger = true // --> What kind of errors to return.
);
static void setCanonical(STPathSet& spsDst, const std::vector& vpsExpanded, bool bKeepDefault);
diff --git a/src/cpp/ripple/SHAMap.cpp b/src/cpp/ripple/SHAMap.cpp
index 22995fdee4..676290ccad 100644
--- a/src/cpp/ripple/SHAMap.cpp
+++ b/src/cpp/ripple/SHAMap.cpp
@@ -15,6 +15,10 @@
#include "SHAMap.h"
#include "Application.h"
+#ifndef STATE_MAP_BUCKETS
+#define STATE_MAP_BUCKETS 1024
+#endif
+
SETUP_LOG();
DECLARE_INSTANCE(SHAMap);
@@ -52,6 +56,8 @@ std::size_t hash_value(const uint160& u)
SHAMap::SHAMap(SHAMapType t, uint32 seq) : mSeq(seq), mState(smsModifying), mType(t)
{
+ if (t == smtSTATE)
+ mTNByID.rehash(STATE_MAP_BUCKETS);
root = boost::make_shared(mSeq, SHAMapNode(0, uint256()));
root->makeInner();
mTNByID[*root] = root;
@@ -59,6 +65,8 @@ SHAMap::SHAMap(SHAMapType t, uint32 seq) : mSeq(seq), mState(smsModifying), mTyp
SHAMap::SHAMap(SHAMapType t, const uint256& hash) : mSeq(1), mState(smsSynching), mType(t)
{ // FIXME: Need to acquire root node
+ if (t == smtSTATE)
+ mTNByID.rehash(STATE_MAP_BUCKETS);
root = boost::make_shared(mSeq, SHAMapNode(0, uint256()));
root->makeInner();
mTNByID[*root] = root;
@@ -205,10 +213,10 @@ SHAMapTreeNode::pointer SHAMap::getNode(const SHAMapNode& id, const uint256& has
#ifdef DEBUG
if (node->getNodeHash() != hash)
{
- std::cerr << "Attempt to get node, hash not in tree" << std::endl;
- std::cerr << "ID: " << id << std::endl;
- std::cerr << "TgtHash " << hash << std::endl;
- std::cerr << "NodHash " << node->getNodeHash() << std::endl;
+ cLog(lsFATAL) << "Attempt to get node, hash not in tree";
+ cLog(lsFATAL) << "ID: " << id;
+ cLog(lsFATAL) << "TgtHash " << hash;
+ cLog(lsFATAL) << "NodHash " << node->getNodeHash();
throw std::runtime_error("invalid node");
}
#endif
@@ -264,9 +272,6 @@ SHAMapItem::SHAMapItem(const uint256& tag, const Serializer& data)
SHAMapTreeNode* SHAMap::firstBelow(SHAMapTreeNode* node)
{
// Return the first item below this node
-#ifdef ST_DEBUG
- std::cerr << "firstBelow(" << *node << ")" << std::endl;
-#endif
do
{ // Walk down the tree
if (node->hasItem()) return node;
@@ -275,11 +280,6 @@ SHAMapTreeNode* SHAMap::firstBelow(SHAMapTreeNode* node)
for (int i = 0; i < 16; ++i)
if (!node->isEmptyBranch(i))
{
-#ifdef ST_DEBUG
- std::cerr << " FB: node " << *node << std::endl;
- std::cerr << " has non-empty branch " << i << " : " <<
- node->getChildNodeID(i) << ", " << node->getChildHash(i) << std::endl;
-#endif
node = getNodePointer(node->getChildNodeID(i), node->getChildHash(i));
foundNode = true;
break;
@@ -291,10 +291,6 @@ SHAMapTreeNode* SHAMap::firstBelow(SHAMapTreeNode* node)
SHAMapTreeNode* SHAMap::lastBelow(SHAMapTreeNode* node)
{
-#ifdef DEBUG
- std::cerr << "lastBelow(" << *node << ")" << std::endl;
-#endif
-
do
{ // Walk down the tree
if (node->hasItem())
@@ -330,7 +326,7 @@ SHAMapItem::pointer SHAMap::onlyBelow(SHAMapTreeNode* node)
if (!nextNode)
{
- std::cerr << *node << std::endl;
+ cLog(lsFATAL) << *node;
assert(false);
return SHAMapItem::pointer();
}
@@ -542,9 +538,6 @@ bool SHAMap::delItem(const uint256& id)
int bc = node->getBranchCount();
if (bc == 0)
{
-#ifdef DEBUG
- std::cerr << "delItem makes empty node" << std::endl;
-#endif
prevHash=uint256();
if (!mTNByID.erase(*node))
assert(false);
@@ -556,9 +549,6 @@ bool SHAMap::delItem(const uint256& id)
{
returnNode(node, true);
eraseChildren(node);
-#ifdef ST_DEBUG
- std::cerr << "Making item node " << *node << std::endl;
-#endif
node->setItem(item, type);
}
prevHash = node->getNodeHash();
@@ -577,10 +567,6 @@ bool SHAMap::delItem(const uint256& id)
bool SHAMap::addGiveItem(SHAMapItem::ref item, bool isTransaction, bool hasMeta)
{ // add the specified item, does not update
-#ifdef ST_DEBUG
- std::cerr << "aGI " << item->getTag() << std::endl;
-#endif
-
uint256 tag = item->getTag();
SHAMapTreeNode::TNType type = !isTransaction ? SHAMapTreeNode::tnACCOUNT_STATE :
(hasMeta ? SHAMapTreeNode::tnTRANSACTION_MD : SHAMapTreeNode::tnTRANSACTION_NM);
@@ -603,17 +589,14 @@ bool SHAMap::addGiveItem(SHAMapItem::ref item, bool isTransaction, bool hasMeta)
if (node->isInner())
{ // easy case, we end on an inner node
-#ifdef ST_DEBUG
- std::cerr << "aGI inner " << *node << std::endl;
-#endif
int branch = node->selectBranch(tag);
assert(node->isEmptyBranch(branch));
SHAMapTreeNode::pointer newNode =
boost::make_shared(node->getChildNodeID(branch), item, type, mSeq);
if (!mTNByID.emplace(SHAMapNode(*newNode), newNode).second)
{
- std::cerr << "Node: " << *node << std::endl;
- std::cerr << "NewNode: " << *newNode << std::endl;
+ cLog(lsFATAL) << "Node: " << *node;
+ cLog(lsFATAL) << "NewNode: " << *newNode;
dump();
assert(false);
throw std::runtime_error("invalid inner node");
@@ -623,10 +606,6 @@ bool SHAMap::addGiveItem(SHAMapItem::ref item, bool isTransaction, bool hasMeta)
}
else
{ // this is a leaf node that has to be made an inner node holding two items
-#ifdef ST_DEBUG
- std::cerr << "aGI leaf " << *node << std::endl;
- std::cerr << "Existing: " << node->peekItem()->getTag() << std::endl;
-#endif
SHAMapItem::pointer otherItem = node->peekItem();
assert(otherItem && (tag != otherItem->getTag()));
@@ -636,10 +615,6 @@ bool SHAMap::addGiveItem(SHAMapItem::ref item, bool isTransaction, bool hasMeta)
while ((b1 = node->selectBranch(tag)) == (b2 = node->selectBranch(otherItem->getTag())))
{ // we need a new inner node, since both go on same branch at this level
-#ifdef ST_DEBUG
- std::cerr << "need new inner node at " << node->getDepth() << ", "
- << b1 << "==" << b2 << std::endl;
-#endif
SHAMapTreeNode::pointer newNode =
boost::make_shared(mSeq, node->getChildNodeID(b1));
newNode->makeInner();
@@ -711,7 +686,7 @@ bool SHAMap::updateGiveItem(SHAMapItem::ref item, bool isTransaction, bool hasMe
void SHAMapItem::dump()
{
- std::cerr << "SHAMapItem(" << mTag << ") " << mData.size() << "bytes" << std::endl;
+ cLog(lsINFO) << "SHAMapItem(" << mTag << ") " << mData.size() << "bytes";
}
SHAMapTreeNode::pointer SHAMap::fetchNodeExternal(const SHAMapNode& id, const uint256& hash)
@@ -729,7 +704,7 @@ SHAMapTreeNode::pointer SHAMap::fetchNodeExternal(const SHAMapNode& id, const ui
try
{
SHAMapTreeNode::pointer ret =
- boost::make_shared(id, obj->getData(), mSeq, snfPREFIX, hash);
+ boost::make_shared(id, obj->getData(), mSeq, snfPREFIX, hash, true);
if (id != *ret)
{
cLog(lsFATAL) << "id:" << id << ", got:" << *ret;
@@ -880,25 +855,13 @@ void SHAMap::dropCache()
void SHAMap::dump(bool hash)
{
-#if 0
- std::cerr << "SHAMap::dump" << std::endl;
- SHAMapItem::pointer i=peekFirstItem();
- while (i)
- {
- std::cerr << "Item: id=" << i->getTag() << std::endl;
- i = peekNextItem(i->getTag());
- }
- std::cerr << "SHAMap::dump done" << std::endl;
-#endif
-
- std::cerr << " MAP Contains" << std::endl;
+ cLog(lsINFO) << " MAP Contains";
boost::recursive_mutex::scoped_lock sl(mLock);
for(boost::unordered_map::iterator it = mTNByID.begin();
it != mTNByID.end(); ++it)
{
- std::cerr << it->second->getString() << std::endl;
- if (hash)
- std::cerr << " " << it->second->getNodeHash() << std::endl;
+ cLog(lsINFO) << it->second->getString();
+ tLog(hash, lsINFO) << it->second->getNodeHash();
}
}
diff --git a/src/cpp/ripple/SHAMap.h b/src/cpp/ripple/SHAMap.h
index 30ad9e52d7..d241f18432 100644
--- a/src/cpp/ripple/SHAMap.h
+++ b/src/cpp/ripple/SHAMap.h
@@ -187,7 +187,7 @@ public:
// raw node functions
SHAMapTreeNode(const SHAMapNode& id, const std::vector& data, uint32 seq,
- SHANodeFormat format, const uint256& hash);
+ SHANodeFormat format, const uint256& hash, bool hashValid);
void addRaw(Serializer &, SHANodeFormat format);
virtual bool isPopulated() const { return true; }
diff --git a/src/cpp/ripple/SHAMapNodes.cpp b/src/cpp/ripple/SHAMapNodes.cpp
index ace6a10e1f..6989e80928 100644
--- a/src/cpp/ripple/SHAMapNodes.cpp
+++ b/src/cpp/ripple/SHAMapNodes.cpp
@@ -177,7 +177,7 @@ SHAMapTreeNode::SHAMapTreeNode(const SHAMapNode& node, SHAMapItem::ref item, TNT
}
SHAMapTreeNode::SHAMapTreeNode(const SHAMapNode& id, const std::vector& rawNode, uint32 seq,
- SHANodeFormat format, const uint256& hash) :
+ SHANodeFormat format, const uint256& hash, bool hashValid) :
SHAMapNode(id), mSeq(seq), mType(tnERROR), mIsBranch(0), mFullBelow(false)
{
if (format == snfWIRE)
@@ -317,9 +317,7 @@ SHAMapTreeNode::SHAMapTreeNode(const SHAMapNode& id, const std::vector& nodeIDs, std::vector& hashes, int max,
SHAMapSyncFilter* filter)
{
@@ -63,7 +65,7 @@ void SHAMap::getMissingNodes(std::vector& nodeIDs, std::vector= 1);
SHAMapTreeNode::pointer ptr =
- boost::make_shared(childID, nodeData, mSeq - 1, snfPREFIX, childHash);
+ boost::make_shared(childID, nodeData, mSeq - 1, snfPREFIX, childHash, true);
cLog(lsTRACE) << "Got sync node from cache: " << *ptr;
mTNByID[*ptr] = ptr;
d = ptr.get();
@@ -213,7 +215,7 @@ SMAddNode SHAMap::addRootNode(const std::vector& rootNode, SHANod
assert(mSeq >= 1);
SHAMapTreeNode::pointer node =
- boost::make_shared(SHAMapNode(), rootNode, mSeq - 1, format, uint256());
+ boost::make_shared(SHAMapNode(), rootNode, mSeq - 1, format, uZero, false);
if (!node)
return SMAddNode::invalid();
@@ -253,7 +255,7 @@ SMAddNode SHAMap::addRootNode(const uint256& hash, const std::vector= 1);
SHAMapTreeNode::pointer node =
- boost::make_shared(SHAMapNode(), rootNode, mSeq - 1, format, uint256());
+ boost::make_shared(SHAMapNode(), rootNode, mSeq - 1, format, uZero, false);
if (!node || node->getNodeHash() != hash)
return SMAddNode::invalid();
@@ -332,7 +334,7 @@ SMAddNode SHAMap::addKnownNode(const SHAMapNode& node, const std::vector= 1);
SHAMapTreeNode::pointer newNode =
- boost::make_shared(node, rawNode, mSeq - 1, snfWIRE, uint256());
+ boost::make_shared(node, rawNode, mSeq - 1, snfWIRE, uZero, false);
if (hash != newNode->getNodeHash()) // these aren't the droids we're looking for
return SMAddNode::invalid();
diff --git a/src/cpp/ripple/TransactionCheck.cpp b/src/cpp/ripple/TransactionCheck.cpp
new file mode 100644
index 0000000000..50b1a7f7cf
--- /dev/null
+++ b/src/cpp/ripple/TransactionCheck.cpp
@@ -0,0 +1,11 @@
+
+#include "TransactionErr.h"
+#include "TransactionEngine.h"
+
+// Double check a transaction's metadata to make sure no system invariants were broken
+// Call right before 'calcRawMeta'
+
+bool TransactionEngine::checkInvariants(TER result, const SerializedTransaction& txn, TransactionEngineParams params)
+{
+ return true;
+}
diff --git a/src/cpp/ripple/TransactionEngine.cpp b/src/cpp/ripple/TransactionEngine.cpp
index 1e0beac41d..1c0049fb20 100644
--- a/src/cpp/ripple/TransactionEngine.cpp
+++ b/src/cpp/ripple/TransactionEngine.cpp
@@ -93,7 +93,7 @@ TER TransactionEngine::applyTransaction(const SerializedTransaction& txn, Transa
}
#endif
- UPTR_T transactor = Transactor::makeTransactor(txn,params,this);
+ UPTR_T transactor = Transactor::makeTransactor(txn, params, this);
if (transactor.get() != NULL)
{
uint256 txID = txn.getTransactionID();
@@ -153,28 +153,40 @@ TER TransactionEngine::applyTransaction(const SerializedTransaction& txn, Transa
if (didApply)
{
- // Transaction succeeded fully or (retries are not allowed and the transaction could claim a fee)
- Serializer m;
- mNodes.calcRawMeta(m, terResult, mTxnSeq++);
-
- txnWrite();
-
- Serializer s;
- txn.add(s);
-
- if (isSetBit(params, tapOPEN_LEDGER))
+ if (!checkInvariants(terResult, txn, params))
{
- if (!mLedger->addTransaction(txID, s))
- assert(false);
+ cLog(lsFATAL) << "Transaction violates invariants";
+ cLog(lsFATAL) << txn.getJson(0);
+ cLog(lsFATAL) << transToken(terResult) << ": " << transHuman(terResult);
+ cLog(lsFATAL) << mNodes.getJson(0);
+ didApply = false;
+ terResult = tefINTERNAL;
}
else
{
- if (!mLedger->addTransaction(txID, s, m))
+ // Transaction succeeded fully or (retries are not allowed and the transaction could claim a fee)
+ Serializer m;
+ mNodes.calcRawMeta(m, terResult, mTxnSeq++);
+
+ txnWrite();
+
+ Serializer s;
+ txn.add(s);
+
+ if (isSetBit(params, tapOPEN_LEDGER))
+ {
+ if (!mLedger->addTransaction(txID, s))
+ assert(false);
+ }
+ else
+ {
+ if (!mLedger->addTransaction(txID, s, m))
assert(false);
- // Charge whatever fee they specified.
- STAmount saPaid = txn.getTransactionFee();
- mLedger->destroyCoins(saPaid.getNValue());
+ // Charge whatever fee they specified.
+ STAmount saPaid = txn.getTransactionFee();
+ mLedger->destroyCoins(saPaid.getNValue());
+ }
}
}
diff --git a/src/cpp/ripple/TransactionEngine.h b/src/cpp/ripple/TransactionEngine.h
index 2ad074efb6..17e1e02f94 100644
--- a/src/cpp/ripple/TransactionEngine.h
+++ b/src/cpp/ripple/TransactionEngine.h
@@ -65,6 +65,7 @@ public:
void entryModify(SLE::ref sleEntry) { mNodes.entryModify(sleEntry); }
TER applyTransaction(const SerializedTransaction&, TransactionEngineParams, bool& didApply);
+ bool checkInvariants(TER result, const SerializedTransaction& txn, TransactionEngineParams params);
};
inline TransactionEngineParams operator|(const TransactionEngineParams& l1, const TransactionEngineParams& l2)
diff --git a/src/cpp/ripple/TransactionErr.cpp b/src/cpp/ripple/TransactionErr.cpp
index 160064ffeb..1709cfc368 100644
--- a/src/cpp/ripple/TransactionErr.cpp
+++ b/src/cpp/ripple/TransactionErr.cpp
@@ -38,6 +38,7 @@ bool transResultInfo(TER terCode, std::string& strToken, std::string& strHuman)
{ tefGEN_IN_USE, "tefGEN_IN_USE", "Generator already in use." },
{ tefNO_AUTH_REQUIRED, "tefNO_AUTH_REQUIRED", "Auth is not required." },
{ tefPAST_SEQ, "tefPAST_SEQ", "This sequence number has already past." },
+ { tefINTERNAL, "tefINTERNAL", "Internal error." },
{ telLOCAL_ERROR, "telLOCAL_ERROR", "Local failure." },
{ telBAD_DOMAIN, "telBAD_DOMAIN", "Domain too long." },
diff --git a/src/cpp/ripple/TransactionErr.h b/src/cpp/ripple/TransactionErr.h
index fe54d9f4bb..186aa3c145 100644
--- a/src/cpp/ripple/TransactionErr.h
+++ b/src/cpp/ripple/TransactionErr.h
@@ -81,6 +81,7 @@ enum TER // aka TransactionEngineResult
tefGEN_IN_USE,
tefNO_AUTH_REQUIRED, // Can't set auth if auth is not required.
tefPAST_SEQ,
+ tefINTERNAL,
// -99 .. -1: R Retry (sequence too high, no funds for txn fee, originating account non-existent)
// Causes:
diff --git a/src/cpp/ripple/base58.h b/src/cpp/ripple/base58.h
index 5f08f48718..35a6acfef1 100644
--- a/src/cpp/ripple/base58.h
+++ b/src/cpp/ripple/base58.h
@@ -164,11 +164,8 @@ protected:
unsigned char nVersion;
std::vector vchData;
- CBase58Data()
- {
- nVersion = 1;
- vchData.clear();
- }
+ CBase58Data() : nVersion(1)
+ { ; }
~CBase58Data()
{
@@ -247,13 +244,7 @@ public:
friend std::size_t hash_value(const CBase58Data& b58);
};
-inline std::size_t hash_value(const CBase58Data& b58)
-{
- std::size_t seed = boost::hash_value(b58.nVersion);
+extern std::size_t hash_value(const CBase58Data& b58);
- boost::hash_combine(seed, b58.vchData);
-
- return seed;
-}
#endif
// vim:ts=4
diff --git a/src/cpp/ripple/main.cpp b/src/cpp/ripple/main.cpp
index 8a7ed48b24..239d8301ac 100644
--- a/src/cpp/ripple/main.cpp
+++ b/src/cpp/ripple/main.cpp
@@ -70,11 +70,10 @@ void printHelp(const po::options_description& desc)
cerr << desc << endl;
cerr << "Commands: " << endl;
- cerr << " account_info |" << endl;
- cerr << " account_info || []" << endl;
- cerr << " account_lines || []" << endl;
- cerr << " account_offers || []" << endl;
- cerr << " account_tx || |( )" << endl;
+ cerr << " account_info |||| []" << endl;
+ cerr << " account_lines || []" << endl;
+ cerr << " account_offers || []" << endl;
+ cerr << " account_tx accountID [ledger_min [ledger_max [limit [offset]]]] [binary] [count] [descending]" << endl;
cerr << " book_offers [ [ [ []]]]]" << endl;
cerr << " connect []" << endl;
cerr << " consensus_info" << endl;
@@ -188,8 +187,17 @@ int main(int argc, char* argv[])
}
}
+ if (iResult)
+ {
+ nothing();
+ }
+ else if (vm.count("help"))
+ {
+ iResult = 1;
+ }
+
if (HaveSustain() &&
- !vm.count("parameters") && !vm.count("fg") && !vm.count("standalone") && !vm.count("unittest"))
+ !iResult && !vm.count("parameters") && !vm.count("fg") && !vm.count("standalone") && !vm.count("unittest"))
{
std::string logMe = DoSustain();
if (!logMe.empty())
@@ -251,16 +259,13 @@ int main(int argc, char* argv[])
{
nothing();
}
- else if (vm.count("help"))
- {
- iResult = 1;
- }
else if (!vm.count("parameters"))
{
// No arguments. Run server.
setupServer();
NameThread("io");
startServer();
+ InstanceType::shutdown();
}
else
{
diff --git a/src/cpp/ripple/utils.cpp b/src/cpp/ripple/utils.cpp
index a8bbaa75a6..fb488f19ff 100644
--- a/src/cpp/ripple/utils.cpp
+++ b/src/cpp/ripple/utils.cpp
@@ -4,6 +4,10 @@
#include
#include
#endif
+#ifdef __FreeBSD__
+#include
+#include
+#endif
#include
@@ -20,6 +24,9 @@
void getRand(unsigned char *buf, int num)
{
+#ifdef PURIFY
+ memset(buf, 0, num);
+#endif
if (RAND_bytes(buf, num) != 1)
{
assert(false);
@@ -336,8 +343,8 @@ uint32_t htobe32(uint32_t value)
}
uint32_t be32toh(uint32_t value)
-{
- return( _byteswap_ulong(value));
+{
+ return( _byteswap_ulong(value));
}
#endif
@@ -443,8 +450,8 @@ std::string DoSustain()
#else
bool HaveSustain() { return false; }
-std::string DoSustain() { return std::string; }
-std::string StopSustain() { return std::string; }
+std::string DoSustain() { return std::string(); }
+std::string StopSustain() { return std::string(); }
#endif
diff --git a/src/js/network.js b/src/js/network.js
index 714aa335f1..3f7697a995 100644
--- a/src/js/network.js
+++ b/src/js/network.js
@@ -48,7 +48,7 @@ Network.protocol.start = function () {
};
-// Target state: disconnectted
+// Target state: disconnect
Network.protocol.stop = function () {
};
diff --git a/test/server.js b/test/server.js
index 5b9e639586..bb0936ca3a 100644
--- a/test/server.js
+++ b/test/server.js
@@ -192,7 +192,8 @@ Server.prototype.stop = function () {
if (!self.quiet) console.log("server: stop: server exited");
self.emit('stopped');
- delete this.child;
+
+ delete self.child;
});
this.child.kill();
diff --git a/test/testutils.js b/test/testutils.js
index b81df48612..39495f213a 100644
--- a/test/testutils.js
+++ b/test/testutils.js
@@ -129,14 +129,14 @@ var build_teardown = function (host) {
.connect(false);
},
function stopServerStep(callback) {
-
if (opts.no_server)
{
+ return callback();
+ }
- return callback();
- }
-
- data.server.on('stopped', callback).stop();
+ data.server
+ .on('stopped', callback)
+ .stop();
}
], done);
};