Merge branch 'master' of https://github.com/jedmccaleb/NewCoin into new_pathfinding

Conflicts:
	src/cpp/ripple/Application.cpp
	src/cpp/ripple/Pathfinder.cpp

Merge.
This commit is contained in:
JoelKatz
2013-03-29 10:27:54 -07:00
47 changed files with 715 additions and 486 deletions

View File

@@ -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:

View File

@@ -101,6 +101,7 @@
<ClCompile Include="src\cpp\ripple\AccountSetTransactor.cpp" />
<ClCompile Include="src\cpp\ripple\AccountState.cpp" />
<ClCompile Include="src\cpp\ripple\Amount.cpp" />
<ClCompile Include="src\cpp\ripple\AmountRound.cpp" />
<ClCompile Include="src\cpp\ripple\Application.cpp" />
<ClCompile Include="src\cpp\ripple\BitcoinUtil.cpp" />
<ClCompile Include="src\cpp\ripple\CallRPC.cpp" />
@@ -175,6 +176,7 @@
<ClCompile Include="src\cpp\ripple\Suppression.cpp" />
<ClCompile Include="src\cpp\ripple\Transaction.cpp" />
<ClCompile Include="src\cpp\ripple\TransactionAcquire.cpp" />
<ClCompile Include="src\cpp\ripple\TransactionCheck.cpp" />
<ClCompile Include="src\cpp\ripple\TransactionEngine.cpp" />
<ClCompile Include="src\cpp\ripple\TransactionErr.cpp" />
<ClCompile Include="src\cpp\ripple\TransactionFormats.cpp" />

View File

@@ -249,6 +249,9 @@
<ClCompile Include="src\cpp\ripple\TransactionEngine.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\cpp\ripple\TransactionCheck.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\cpp\ripple\TransactionErr.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@@ -363,6 +366,9 @@
<ClCompile Include="src\cpp\ripple\RPCSub.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\cpp\ripple\AmountRound.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="util\pugiconfig.hpp">

View File

@@ -175,6 +175,7 @@
<ClCompile Include="src\cpp\ripple\Suppression.cpp" />
<ClCompile Include="src\cpp\ripple\Transaction.cpp" />
<ClCompile Include="src\cpp\ripple\TransactionAcquire.cpp" />
<ClCompile Include="src\cpp\ripple\TransactionCheck.cpp" />
<ClCompile Include="src\cpp\ripple\TransactionEngine.cpp" />
<ClCompile Include="src\cpp\ripple\TransactionErr.cpp" />
<ClCompile Include="src\cpp\ripple\TransactionFormats.cpp" />

View File

@@ -243,6 +243,9 @@
<ClCompile Include="src\cpp\ripple\TransactionAcquire.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\cpp\ripple\TransactionCheck.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\cpp\ripple\TransactionEngine.cpp">
<Filter>Source Files</Filter>
</ClCompile>

View File

@@ -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.

View File

@@ -100,8 +100,7 @@ public:
static ValueAllocator *&valueAllocator()
{
static DefaultValueAllocator defaultAllocator;
static ValueAllocator *valueAllocator = &defaultAllocator;
static ValueAllocator *valueAllocator = new DefaultValueAllocator;
return valueAllocator;
}

View File

@@ -4,6 +4,7 @@
#include <limits.h>
#include <boost/lexical_cast.hpp>
#include <boost/regex.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <boost/test/unit_test.hpp>
@@ -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<int64>(sAmount);
if (a >= 0)
{
uValue = static_cast<uint64>(a);
}
else
{
uValue = static_cast<uint64>(-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<uint64>(sAmount.substr(0, uExp)) : 0;
if (iInteger >= 0)
{
uValue = static_cast<uint64>(iInteger);
}
mValue = lexical_cast_s<uint64>(smMatch[2]);
mOffset = 0;
}
else
{ // integer and fraction
mValue = lexical_cast_s<uint64>(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<int>(smMatch[7]);
else
{
uValue = static_cast<uint64>(-iInteger);
mIsNegative = true;
}
iOffset = lexical_cast_st<uint64>(sAmount.substr(uExp+1));
}
catch (...)
{
cLog(lsINFO) << "Bad e amount: " << sAmount;
return false;
mOffset += lexical_cast_s<int>(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<uint64>(sAmount.substr(0, uDecimal)) : 0;
if (iInteger >= 0)
{
uInteger = static_cast<uint64>(iInteger);
}
else
{
uInteger = static_cast<uint64>(-iInteger);
mIsNegative = true;
}
uint64 uFraction = iOffset ? lexical_cast_st<uint64>(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");

View File

@@ -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)

View File

@@ -134,72 +134,70 @@ Json::Value RPCParser::parseInternal(const Json::Value& jvParams)
return v;
}
// account_info <account>|<nickname>|<account_public_key>
// account_info <seed>|<pass_phrase>|<key> [[<index>] <ledger>]
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<int>(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> <minledger> <maxledger>
// account_tx <account> <ledger>
// account_tx <account> binary
// account_tx <account> <minledger> <maxledger> 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 <account>|<nickname>|<account_public_key>
// owner_info <seed>|<pass_phrase>|<key> [<index>]
Json::Value RPCParser::parseOwnerInfo(const Json::Value& jvParams)
{
return parseAccountInfo(jvParams);
}
// account_lines <account>|<nickname>|<account_public_key> [<index>]
// account_offers <account>|<nickname>|<account_public_key> [<index>]
// owner_info <seed>|<pass_phrase>|<key> [<ledfer>]
// account_info <account>|<nickname>|<account_public_key>
// account_info <seed>|<pass_phrase>|<key> [<ledger>]
// account_lines <account>|<nickname>|<account_public_key> [<ledger>]
// account_offers <account>|<nickname>|<account_public_key> [<ledger>]
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<int>(jvParams[1u].asString()) : 0;
// TODO: Get index from an alternate syntax: rXYZ:<index>
int iIndex = 0;
// int iIndex = jvParams.size() >= 2 ? lexical_cast_s<int>(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 },

View File

@@ -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);

View File

@@ -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 } }
};

View File

@@ -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), \

View File

@@ -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 = ?;");

View File

@@ -50,6 +50,11 @@ public:
sMultiThreaded = true;
}
static void shutdown()
{
sMultiThreaded = false;
}
static bool isMultiThread()
{
return sMultiThreaded;

View File

@@ -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;

View File

@@ -8,6 +8,7 @@
#include <boost/thread/mutex.hpp>
#include <boost/thread/condition_variable.hpp>
#include <boost/make_shared.hpp>
#include <boost/asio.hpp>
#include <boost/ref.hpp>
#include "../json/value.h"
@@ -93,14 +94,19 @@ protected:
int mThreadCount;
bool mShuttingDown;
int mIOThreadCount;
int mMaxIOThreadCount;
boost::asio::io_service& mIOService;
std::map<JobType, std::pair<int, int > > 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<void(Job&)>& job);

View File

@@ -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<Peer::pointer> 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::neededHash_t> LedgerAcquire::getNeededHashes()
}
if (!mHaveState)
{
std::vector<uint256> v = mLedger->getNeededAccountStateHashes(16);
std::vector<uint256> 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<uint256> v = mLedger->getNeededAccountStateHashes(16);
std::vector<uint256> 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<ripple::TMLedgerData> packet_ptr,
boost::weak_ptr<Peer> 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<ripple::TMLedgerData> packet_ptr, boost::weak_ptr<Peer> 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_ptr<ripple::TMLedger
peer->punishPeer(LT_InvalidRequest);
return;
}
ledger->noAwaitData();
if (!peer)
return;
if (packet.type() == ripple::liBASE)
{

View File

@@ -87,11 +87,14 @@ public:
protected:
Ledger::pointer mLedger;
bool mHaveBase, mHaveState, mHaveTransactions, mAborted, mSignaled, mAccept, mByHash;
int mWaitCount;
std::set<SHAMapNode> mRecentTXNodes;
std::set<SHAMapNode> mRecentASNodes;
std::vector< FUNCTION_TYPE<void (LedgerAcquire::pointer)> > mOnComplete;
std::vector<uint64> mRecentPeers;
std::vector< FUNCTION_TYPE<void (LedgerAcquire::pointer)> > 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<ripple::TMGetObjectByHash::ObjectType, uint256> neededHash_t;
std::vector<neededHash_t> 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<ripple::TMLedgerData> packet, boost::weak_ptr<Peer> peer);
bool awaitLedgerData(const uint256& ledgerHash);
void gotLedgerData(Job&, uint256 hash, boost::shared_ptr<ripple::TMLedgerData> packet, boost::weak_ptr<Peer> peer);
int getFetchCount(int& timeoutCount);
void logFailure(const uint256& h) { mRecentFailures.add(h); }

View File

@@ -230,6 +230,15 @@ void LedgerEntrySet::entryDelete(SLE::ref sle)
}
}
bool LedgerEntrySet::hasChanges()
{
typedef std::map<uint256, LedgerEntrySetEntry>::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

View File

@@ -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);

View File

@@ -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);

View File

@@ -143,6 +143,7 @@ public:
bool haveLedgerRange(uint32 from, uint32 to);
bool haveLedger(uint32 seq);
bool getValidatedRange(uint32& minVal, uint32& maxVal);
void resumeAcquiring();

View File

@@ -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<Peer::pointer> 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<Peer::pointer> 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<uint32>(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<std::string>(offset)
% boost::lexical_cast<std::string>(numberOfResults)
);
cLog(lsTRACE) << "txSQL query: " << sql;
return sql;
}
std::vector< std::pair<Transaction::pointer, TransactionMetaSet::pointer> >
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<Transaction::pointer, TransactionMetaSet::pointer> > 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<Transaction::pointer, TransactionMetaSet::pointer> >
}
std::vector<NetworkOPs::txnMetaLedgerType> 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::txnMetaLedgerType> 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<RippleAddress>
NetworkOPs::getLedgerAffectedAccounts(uint32 ledgerSeq)
{

View File

@@ -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<Transaction::pointer, TransactionMetaSet::pointer> >
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<std::string, std::string, uint32> txnMetaLedgerType;
std::vector<txnMetaLedgerType>
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<RippleAddress> getLedgerAffectedAccounts(uint32 ledgerSeq);
std::vector<SerializedTransaction> getLedgerTransactions(uint32 ledgerSeq);
uint32 countAccountTxs(const RippleAddress& account, int32 minLedger, int32 maxLedger);
//
// Monitoring: publisher side
//

View File

@@ -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<OrderBook::pointer> 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,

View File

@@ -1659,18 +1659,17 @@ void Peer::recvLedger(const boost::shared_ptr<ripple::TMLedgerData>& 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<SHAMapNode> nodeIDs;
std::list< std::vector<unsigned char> > nodeData;
@@ -1692,9 +1691,12 @@ void Peer::recvLedger(const boost::shared_ptr<ripple::TMLedgerData>& packet_ptr)
return;
}
theApp->getJobQueue().addJob(jtLEDGER_DATA, "gotLedgerData",
BIND_TYPE(&LedgerAcquireMaster::gotLedgerData, &theApp->getMasterLedgerAcquire(),
P_1, packet_ptr, boost::weak_ptr<Peer>(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<Peer>(shared_from_this())));
else
punishPeer(LT_UnwantedData);
}
bool Peer::hasLedger(const uint256& hash) const

View File

@@ -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<Ledger>(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: <account>, ledger: <integer> }
// { account: <account>, ledger_min: <integer>, ledger_max: <integer> }
// 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<NetworkOPs::txnMetaLedgerType> txns =
mNetOps->getAccountTxsB(raAccount, minLedger, maxLedger);
mNetOps->getAccountTxsB(raAccount, uLedgerMin, uLedgerMax, bDescending, offset, limit, mRole == ADMIN);
for (std::vector<NetworkOPs::txnMetaLedgerType>::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<Transaction::pointer, TransactionMetaSet::pointer> > txns = mNetOps->getAccountTxs(raAccount, minLedger, maxLedger);
std::vector< std::pair<Transaction::pointer, TransactionMetaSet::pointer> > txns = mNetOps->getAccountTxs(raAccount, uLedgerMin, uLedgerMax, bDescending, offset, limit, mRole == ADMIN);
for (std::vector< std::pair<Transaction::pointer, TransactionMetaSet::pointer> >::iterator it = txns.begin(), end = txns.end(); it != end; ++it)
{
Json::Value obj(Json::objectValue);
Json::Value jvObj(Json::objectValue);
if (it->first)
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
}

View File

@@ -8,6 +8,7 @@
#include "RippleAddress.h"
#include "SerializedTypes.h"
#include "Ledger.h"
#include "NetworkOPs.h"
#define LEDGER_CURRENT -1
#define LEDGER_CLOSED -2

View File

@@ -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()
{

View File

@@ -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
}

View File

@@ -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<PathState::pointer>& vpsExpanded, bool bKeepDefault);

View File

@@ -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<SHAMapTreeNode>(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<SHAMapTreeNode>(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<SHAMapTreeNode>(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<SHAMapTreeNode>(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<SHAMapTreeNode>(id, obj->getData(), mSeq, snfPREFIX, hash);
boost::make_shared<SHAMapTreeNode>(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<SHAMapNode, SHAMapTreeNode::pointer>::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();
}
}

View File

@@ -187,7 +187,7 @@ public:
// raw node functions
SHAMapTreeNode(const SHAMapNode& id, const std::vector<unsigned char>& 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; }

View File

@@ -177,7 +177,7 @@ SHAMapTreeNode::SHAMapTreeNode(const SHAMapNode& node, SHAMapItem::ref item, TNT
}
SHAMapTreeNode::SHAMapTreeNode(const SHAMapNode& id, const std::vector<unsigned char>& 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<unsigned
throw std::runtime_error("Unknown format");
}
if (hash.isZero())
updateHash();
else
if (hashValid)
{
mHash = hash;
#ifdef PARANOID
@@ -327,6 +325,8 @@ SHAMapTreeNode::SHAMapTreeNode(const SHAMapNode& id, const std::vector<unsigned
assert(mHash == hash);
#endif
}
else
updateHash();
}
bool SHAMapTreeNode::updateHash()

View File

@@ -13,6 +13,8 @@
SETUP_LOG();
static const uint256 uZero;
void SHAMap::getMissingNodes(std::vector<SHAMapNode>& nodeIDs, std::vector<uint256>& hashes, int max,
SHAMapSyncFilter* filter)
{
@@ -63,7 +65,7 @@ void SHAMap::getMissingNodes(std::vector<SHAMapNode>& nodeIDs, std::vector<uint2
{
assert(mSeq >= 1);
SHAMapTreeNode::pointer ptr =
boost::make_shared<SHAMapTreeNode>(childID, nodeData, mSeq - 1, snfPREFIX, childHash);
boost::make_shared<SHAMapTreeNode>(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<unsigned char>& rootNode, SHANod
assert(mSeq >= 1);
SHAMapTreeNode::pointer node =
boost::make_shared<SHAMapTreeNode>(SHAMapNode(), rootNode, mSeq - 1, format, uint256());
boost::make_shared<SHAMapTreeNode>(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<unsigned ch
assert(mSeq >= 1);
SHAMapTreeNode::pointer node =
boost::make_shared<SHAMapTreeNode>(SHAMapNode(), rootNode, mSeq - 1, format, uint256());
boost::make_shared<SHAMapTreeNode>(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<unsigne
assert(mSeq >= 1);
SHAMapTreeNode::pointer newNode =
boost::make_shared<SHAMapTreeNode>(node, rawNode, mSeq - 1, snfWIRE, uint256());
boost::make_shared<SHAMapTreeNode>(node, rawNode, mSeq - 1, snfWIRE, uZero, false);
if (hash != newNode->getNodeHash()) // these aren't the droids we're looking for
return SMAddNode::invalid();

View File

@@ -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;
}

View File

@@ -93,7 +93,7 @@ TER TransactionEngine::applyTransaction(const SerializedTransaction& txn, Transa
}
#endif
UPTR_T<Transactor> transactor = Transactor::makeTransactor(txn,params,this);
UPTR_T<Transactor> 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());
}
}
}

View File

@@ -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)

View File

@@ -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." },

View File

@@ -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:

View File

@@ -164,11 +164,8 @@ protected:
unsigned char nVersion;
std::vector<unsigned char> 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

View File

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

View File

@@ -4,6 +4,10 @@
#include <sys/prctl.h>
#include <sys/wait.h>
#endif
#ifdef __FreeBSD__
#include <sys/types.h>
#include <sys/wait.h>
#endif
#include <fstream>
@@ -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

View File

@@ -48,7 +48,7 @@ Network.protocol.start = function () {
};
// Target state: disconnectted
// Target state: disconnect
Network.protocol.stop = function () {
};

View File

@@ -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();

View File

@@ -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);
};