mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
Move all remaining files into modules
This commit is contained in:
@@ -1,276 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
SETUP_LOG (AccountSetTransactor)
|
||||
|
||||
TER AccountSetTransactor::doApply ()
|
||||
{
|
||||
WriteLog (lsINFO, AccountSetTransactor) << "AccountSet>";
|
||||
|
||||
const uint32 uTxFlags = mTxn.getFlags ();
|
||||
|
||||
const uint32 uFlagsIn = mTxnAccount->getFieldU32 (sfFlags);
|
||||
uint32 uFlagsOut = uFlagsIn;
|
||||
|
||||
const uint32 uSetFlag = mTxn.getFieldU32 (sfSetFlag);
|
||||
const uint32 uClearFlag = mTxn.getFieldU32 (sfClearFlag);
|
||||
|
||||
// legacy AccountSet flags
|
||||
bool bSetRequireDest = (uTxFlags & TxFlag::requireDestTag) || (uSetFlag == asfRequireDest);
|
||||
bool bClearRequireDest = (uTxFlags & tfOptionalDestTag) || (uClearFlag == asfRequireDest);
|
||||
bool bSetRequireAuth = (uTxFlags & tfRequireAuth) || (uSetFlag == asfRequireAuth);
|
||||
bool bClearRequireAuth = (uTxFlags & tfOptionalAuth) || (uClearFlag == asfRequireAuth);
|
||||
bool bSetDisallowXRP = (uTxFlags & tfDisallowXRP) || (uSetFlag == asfDisallowXRP);
|
||||
bool bClearDisallowXRP = (uTxFlags & tfAllowXRP) || (uClearFlag == asfDisallowXRP);
|
||||
|
||||
if (uTxFlags & tfAccountSetMask)
|
||||
{
|
||||
WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: Malformed transaction: Invalid flags set.";
|
||||
|
||||
return temINVALID_FLAG;
|
||||
}
|
||||
|
||||
//
|
||||
// RequireAuth
|
||||
//
|
||||
|
||||
if (bSetRequireAuth && bClearRequireAuth)
|
||||
{
|
||||
WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: Malformed transaction: Contradictory flags set.";
|
||||
|
||||
return temINVALID_FLAG;
|
||||
}
|
||||
|
||||
if (bSetRequireAuth && !isSetBit (uFlagsIn, lsfRequireAuth))
|
||||
{
|
||||
if (mTxnAccount->getFieldU32 (sfOwnerCount))
|
||||
{
|
||||
WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: Retry: OwnerCount not zero.";
|
||||
|
||||
return isSetBit(mParams, tapRETRY) ? terOWNERS : tecOWNERS;
|
||||
}
|
||||
|
||||
WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: Set RequireAuth.";
|
||||
|
||||
uFlagsOut |= lsfRequireAuth;
|
||||
}
|
||||
|
||||
if (bClearRequireAuth && isSetBit (uFlagsIn, lsfRequireAuth))
|
||||
{
|
||||
WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: Clear RequireAuth.";
|
||||
|
||||
uFlagsOut &= ~lsfRequireAuth;
|
||||
}
|
||||
|
||||
//
|
||||
// RequireDestTag
|
||||
//
|
||||
|
||||
if (bSetRequireDest && bClearRequireDest)
|
||||
{
|
||||
WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: Malformed transaction: Contradictory flags set.";
|
||||
|
||||
return temINVALID_FLAG;
|
||||
}
|
||||
|
||||
if (bSetRequireDest && !isSetBit (uFlagsIn, lsfRequireDestTag))
|
||||
{
|
||||
WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: Set lsfRequireDestTag.";
|
||||
|
||||
uFlagsOut |= lsfRequireDestTag;
|
||||
}
|
||||
|
||||
if (bClearRequireDest && isSetBit (uFlagsIn, lsfRequireDestTag))
|
||||
{
|
||||
WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: Clear lsfRequireDestTag.";
|
||||
|
||||
uFlagsOut &= ~lsfRequireDestTag;
|
||||
}
|
||||
|
||||
//
|
||||
// DisallowXRP
|
||||
//
|
||||
|
||||
if (bSetDisallowXRP && bClearDisallowXRP)
|
||||
{
|
||||
WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: Malformed transaction: Contradictory flags set.";
|
||||
|
||||
return temINVALID_FLAG;
|
||||
}
|
||||
|
||||
if (bSetDisallowXRP && !isSetBit (uFlagsIn, lsfDisallowXRP))
|
||||
{
|
||||
WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: Set lsfDisallowXRP.";
|
||||
|
||||
uFlagsOut |= lsfDisallowXRP;
|
||||
}
|
||||
|
||||
if (bClearDisallowXRP && isSetBit (uFlagsIn, lsfDisallowXRP))
|
||||
{
|
||||
WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: Clear lsfDisallowXRP.";
|
||||
|
||||
uFlagsOut &= ~lsfDisallowXRP;
|
||||
}
|
||||
|
||||
//
|
||||
// DisableMaster
|
||||
//
|
||||
|
||||
if ((uSetFlag == asfDisableMaster) && (uClearFlag == asfDisableMaster))
|
||||
{
|
||||
WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: Malformed transaction: Contradictory flags set.";
|
||||
|
||||
return temINVALID_FLAG;
|
||||
}
|
||||
|
||||
if ((uSetFlag == asfDisableMaster) && !isSetBit (uFlagsIn, lsfDisableMaster))
|
||||
{
|
||||
if (!mTxnAccount->isFieldPresent (sfRegularKey))
|
||||
return tecNO_REGULAR_KEY;
|
||||
|
||||
WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: Set lsfDisableMaster.";
|
||||
|
||||
uFlagsOut |= lsfDisableMaster;
|
||||
}
|
||||
|
||||
if ((uClearFlag == asfDisableMaster) && isSetBit (uFlagsIn, lsfDisableMaster))
|
||||
{
|
||||
WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: Clear lsfDisableMaster.";
|
||||
|
||||
uFlagsOut &= ~lsfDisableMaster;
|
||||
}
|
||||
|
||||
//
|
||||
// EmailHash
|
||||
//
|
||||
|
||||
if (mTxn.isFieldPresent (sfEmailHash))
|
||||
{
|
||||
uint128 uHash = mTxn.getFieldH128 (sfEmailHash);
|
||||
|
||||
if (!uHash)
|
||||
{
|
||||
WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: unset email hash";
|
||||
|
||||
mTxnAccount->makeFieldAbsent (sfEmailHash);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: set email hash";
|
||||
|
||||
mTxnAccount->setFieldH128 (sfEmailHash, uHash);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// WalletLocator
|
||||
//
|
||||
|
||||
if (mTxn.isFieldPresent (sfWalletLocator))
|
||||
{
|
||||
uint256 uHash = mTxn.getFieldH256 (sfWalletLocator);
|
||||
|
||||
if (!uHash)
|
||||
{
|
||||
WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: unset wallet locator";
|
||||
|
||||
mTxnAccount->makeFieldAbsent (sfEmailHash);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: set wallet locator";
|
||||
|
||||
mTxnAccount->setFieldH256 (sfWalletLocator, uHash);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// MessageKey
|
||||
//
|
||||
|
||||
if (mTxn.isFieldPresent (sfMessageKey))
|
||||
{
|
||||
Blob vucPublic = mTxn.getFieldVL (sfMessageKey);
|
||||
|
||||
if (vucPublic.size () > PUBLIC_BYTES_MAX)
|
||||
{
|
||||
WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: message key too long";
|
||||
|
||||
return telBAD_PUBLIC_KEY;
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: set message key";
|
||||
|
||||
mTxnAccount->setFieldVL (sfMessageKey, vucPublic);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Domain
|
||||
//
|
||||
|
||||
if (mTxn.isFieldPresent (sfDomain))
|
||||
{
|
||||
Blob vucDomain = mTxn.getFieldVL (sfDomain);
|
||||
|
||||
if (vucDomain.empty ())
|
||||
{
|
||||
WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: unset domain";
|
||||
|
||||
mTxnAccount->makeFieldAbsent (sfDomain);
|
||||
}
|
||||
else if (vucDomain.size () > DOMAIN_BYTES_MAX)
|
||||
{
|
||||
WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: domain too long";
|
||||
|
||||
return telBAD_DOMAIN;
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: set domain";
|
||||
|
||||
mTxnAccount->setFieldVL (sfDomain, vucDomain);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// TransferRate
|
||||
//
|
||||
|
||||
if (mTxn.isFieldPresent (sfTransferRate))
|
||||
{
|
||||
uint32 uRate = mTxn.getFieldU32 (sfTransferRate);
|
||||
|
||||
if (!uRate || uRate == QUALITY_ONE)
|
||||
{
|
||||
WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: unset transfer rate";
|
||||
|
||||
mTxnAccount->makeFieldAbsent (sfTransferRate);
|
||||
}
|
||||
else if (uRate > QUALITY_ONE)
|
||||
{
|
||||
WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: set transfer rate";
|
||||
|
||||
mTxnAccount->setFieldU32 (sfTransferRate, uRate);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: bad transfer rate";
|
||||
|
||||
return temBAD_TRANSFER_RATE;
|
||||
}
|
||||
}
|
||||
|
||||
if (uFlagsIn != uFlagsOut)
|
||||
mTxnAccount->setFieldU32 (sfFlags, uFlagsOut);
|
||||
|
||||
WriteLog (lsINFO, AccountSetTransactor) << "AccountSet<";
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
@@ -1,19 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef ACCOUNTSETTRANSACTOR_H
|
||||
#define ACCOUNTSETTRANSACTOR_H
|
||||
|
||||
class AccountSetTransactor : public Transactor
|
||||
{
|
||||
public:
|
||||
AccountSetTransactor (const SerializedTransaction& txn, TransactionEngineParams params, TransactionEngine* engine) : Transactor (txn, params, engine) {}
|
||||
|
||||
TER doApply ();
|
||||
};
|
||||
#endif
|
||||
|
||||
// vim:ts=4
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,73 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef __CALLRPC__
|
||||
#define __CALLRPC__
|
||||
|
||||
class RPCParser
|
||||
{
|
||||
public:
|
||||
Json::Value parseCommand (std::string strMethod, Json::Value jvParams);
|
||||
|
||||
private:
|
||||
typedef Json::Value (RPCParser::*parseFuncPtr) (const Json::Value& jvParams);
|
||||
|
||||
Json::Value parseAccountRaw (const Json::Value& jvParams, bool bPeer);
|
||||
|
||||
Json::Value parseAccountItems (const Json::Value& jvParams);
|
||||
Json::Value parseAccountLines (const Json::Value& jvParams);
|
||||
Json::Value parseAccountTransactions (const Json::Value& jvParams);
|
||||
Json::Value parseAsIs (const Json::Value& jvParams);
|
||||
Json::Value parseBookOffers (const Json::Value& jvParams);
|
||||
Json::Value parseConnect (const Json::Value& jvParams);
|
||||
#if ENABLE_INSECURE
|
||||
Json::Value parseDataDelete (const Json::Value& jvParams);
|
||||
Json::Value parseDataFetch (const Json::Value& jvParams);
|
||||
Json::Value parseDataStore (const Json::Value& jvParams);
|
||||
#endif
|
||||
Json::Value parseEvented (const Json::Value& jvParams);
|
||||
Json::Value parseFeature (const Json::Value& jvParams);
|
||||
Json::Value parseFetchInfo (const Json::Value& jvParams);
|
||||
Json::Value parseGetCounts (const Json::Value& jvParams);
|
||||
Json::Value parseInternal (const Json::Value& jvParams);
|
||||
Json::Value parseJson (const Json::Value& jvParams);
|
||||
Json::Value parseLedger (const Json::Value& jvParams);
|
||||
Json::Value parseLedgerId (const Json::Value& jvParams);
|
||||
#if ENABLE_INSECURE
|
||||
Json::Value parseLogin (const Json::Value& jvParams);
|
||||
#endif
|
||||
Json::Value parseLogLevel (const Json::Value& jvParams);
|
||||
Json::Value parseOwnerInfo (const Json::Value& jvParams);
|
||||
Json::Value parseProofCreate (const Json::Value& jvParams);
|
||||
Json::Value parseProofSolve (const Json::Value& jvParams);
|
||||
Json::Value parseProofVerify (const Json::Value& jvParams);
|
||||
Json::Value parseRandom (const Json::Value& jvParams);
|
||||
Json::Value parseRipplePathFind (const Json::Value& jvParams);
|
||||
Json::Value parseSMS (const Json::Value& jvParams);
|
||||
Json::Value parseSignSubmit (const Json::Value& jvParams);
|
||||
Json::Value parseTx (const Json::Value& jvParams);
|
||||
Json::Value parseTxHistory (const Json::Value& jvParams);
|
||||
Json::Value parseUnlAdd (const Json::Value& jvParams);
|
||||
Json::Value parseUnlDelete (const Json::Value& jvParams);
|
||||
Json::Value parseValidationCreate (const Json::Value& jvParams);
|
||||
Json::Value parseValidationSeed (const Json::Value& jvParams);
|
||||
Json::Value parseWalletAccounts (const Json::Value& jvParams);
|
||||
Json::Value parseWalletPropose (const Json::Value& jvParams);
|
||||
Json::Value parseWalletSeed (const Json::Value& jvParams);
|
||||
};
|
||||
|
||||
extern int commandLineRPC (const std::vector<std::string>& vCmd);
|
||||
|
||||
extern void callRPC (
|
||||
boost::asio::io_service& io_service,
|
||||
const std::string& strIp, const int iPort,
|
||||
const std::string& strUsername, const std::string& strPassword,
|
||||
const std::string& strPath, const std::string& strMethod,
|
||||
const Json::Value& jvParams, const bool bSSL,
|
||||
FUNCTION_TYPE<void (const Json::Value& jvInput)> callbackFuncP = FUNCTION_TYPE<void (const Json::Value& jvInput)> ());
|
||||
#endif
|
||||
|
||||
// vim:ts=4
|
||||
@@ -1,125 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
SETUP_LOG (ChangeTransactor)
|
||||
|
||||
TER ChangeTransactor::doApply ()
|
||||
{
|
||||
if (mTxn.getTxnType () == ttFEATURE)
|
||||
return applyFeature ();
|
||||
|
||||
if (mTxn.getTxnType () == ttFEE)
|
||||
return applyFee ();
|
||||
|
||||
return temUNKNOWN;
|
||||
}
|
||||
|
||||
TER ChangeTransactor::checkSig ()
|
||||
{
|
||||
if (mTxn.getFieldAccount160 (sfAccount).isNonZero ())
|
||||
{
|
||||
WriteLog (lsWARNING, ChangeTransactor) << "Change transaction had bad source account";
|
||||
return temBAD_SRC_ACCOUNT;
|
||||
}
|
||||
|
||||
if (!mTxn.getSigningPubKey ().empty () || !mTxn.getSignature ().empty ())
|
||||
{
|
||||
WriteLog (lsWARNING, ChangeTransactor) << "Change transaction had bad signature";
|
||||
return temBAD_SIGNATURE;
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER ChangeTransactor::checkSeq ()
|
||||
{
|
||||
if ((mTxn.getSequence () != 0) || mTxn.isFieldPresent (sfPreviousTxnID))
|
||||
{
|
||||
WriteLog (lsWARNING, ChangeTransactor) << "Change transaction had bad sequence";
|
||||
return temBAD_SEQUENCE;
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER ChangeTransactor::payFee ()
|
||||
{
|
||||
if (mTxn.getTransactionFee () != STAmount ())
|
||||
{
|
||||
WriteLog (lsWARNING, ChangeTransactor) << "Change transaction with non-zero fee";
|
||||
return temBAD_FEE;
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER ChangeTransactor::preCheck ()
|
||||
{
|
||||
mTxnAccountID = mTxn.getSourceAccount ().getAccountID ();
|
||||
|
||||
if (mTxnAccountID.isNonZero ())
|
||||
{
|
||||
WriteLog (lsWARNING, ChangeTransactor) << "applyTransaction: bad source id";
|
||||
|
||||
return temBAD_SRC_ACCOUNT;
|
||||
}
|
||||
|
||||
if (isSetBit (mParams, tapOPEN_LEDGER))
|
||||
{
|
||||
WriteLog (lsWARNING, ChangeTransactor) << "Change transaction against open ledger";
|
||||
return temINVALID;
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER ChangeTransactor::applyFeature ()
|
||||
{
|
||||
uint256 feature = mTxn.getFieldH256 (sfFeature);
|
||||
|
||||
SLE::pointer featureObject = mEngine->entryCache (ltFEATURES, Ledger::getLedgerFeatureIndex ());
|
||||
|
||||
if (!featureObject)
|
||||
featureObject = mEngine->entryCreate (ltFEATURES, Ledger::getLedgerFeatureIndex ());
|
||||
|
||||
STVector256 features = featureObject->getFieldV256 (sfFeatures);
|
||||
|
||||
if (features.hasValue (feature))
|
||||
return tefALREADY;
|
||||
|
||||
features.addValue (feature);
|
||||
featureObject->setFieldV256 (sfFeatures, features);
|
||||
mEngine->entryModify (featureObject);
|
||||
|
||||
getApp().getFeatureTable ().enableFeature (feature);
|
||||
|
||||
if (!getApp().getFeatureTable ().isFeatureSupported (feature))
|
||||
getApp().getOPs ().setFeatureBlocked ();
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER ChangeTransactor::applyFee ()
|
||||
{
|
||||
|
||||
SLE::pointer feeObject = mEngine->entryCache (ltFEE_SETTINGS, Ledger::getLedgerFeeIndex ());
|
||||
|
||||
if (!feeObject)
|
||||
feeObject = mEngine->entryCreate (ltFEE_SETTINGS, Ledger::getLedgerFeeIndex ());
|
||||
|
||||
WriteLog (lsINFO, ChangeTransactor) << "Previous fee object: " << feeObject->getJson (0);
|
||||
|
||||
feeObject->setFieldU64 (sfBaseFee, mTxn.getFieldU64 (sfBaseFee));
|
||||
feeObject->setFieldU32 (sfReferenceFeeUnits, mTxn.getFieldU32 (sfReferenceFeeUnits));
|
||||
feeObject->setFieldU32 (sfReserveBase, mTxn.getFieldU32 (sfReserveBase));
|
||||
feeObject->setFieldU32 (sfReserveIncrement, mTxn.getFieldU32 (sfReserveIncrement));
|
||||
|
||||
mEngine->entryModify (feeObject);
|
||||
|
||||
WriteLog (lsINFO, ChangeTransactor) << "New fee object: " << feeObject->getJson (0);
|
||||
WriteLog (lsWARNING, ChangeTransactor) << "Fees have been changed";
|
||||
return tesSUCCESS;
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
class ChangeTransactor : public Transactor
|
||||
{
|
||||
public:
|
||||
ChangeTransactor (const SerializedTransaction& txn, TransactionEngineParams params, TransactionEngine* engine)
|
||||
: Transactor (txn, params, engine)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
TER doApply ();
|
||||
TER checkSig ();
|
||||
TER checkSeq ();
|
||||
TER payFee ();
|
||||
TER preCheck ();
|
||||
|
||||
private:
|
||||
TER applyFeature ();
|
||||
TER applyFee ();
|
||||
|
||||
// VFALCO TODO Can this be removed?
|
||||
bool mustHaveValidAccount ()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// vim:ts=4
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,444 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_NETWORKOPS_H
|
||||
#define RIPPLE_NETWORKOPS_H
|
||||
|
||||
// Operations that clients may wish to perform against the network
|
||||
// Master operational handler, server sequencer, network tracker
|
||||
|
||||
class Peer;
|
||||
class LedgerConsensus;
|
||||
|
||||
class NetworkOPs
|
||||
: public DeadlineTimer::Listener
|
||||
, LeakChecked <NetworkOPs>
|
||||
{
|
||||
public:
|
||||
enum Fault
|
||||
{
|
||||
// exceptions these functions can throw
|
||||
IO_ERROR = 1,
|
||||
NO_NETWORK = 2,
|
||||
};
|
||||
|
||||
enum OperatingMode
|
||||
{
|
||||
// how we process transactions or account balance requests
|
||||
omDISCONNECTED = 0, // not ready to process requests
|
||||
omCONNECTED = 1, // convinced we are talking to the network
|
||||
omSYNCING = 2, // fallen slightly behind
|
||||
omTRACKING = 3, // convinced we agree with the network
|
||||
omFULL = 4 // we have the ledger and can even validate
|
||||
};
|
||||
|
||||
#if 0
|
||||
// VFALCO TODO Make this happen
|
||||
/** Subscription data interface.
|
||||
*/
|
||||
class Subscriber
|
||||
{
|
||||
public:
|
||||
typedef boost::weak_ptr <Subscriber> WeakPtr;
|
||||
|
||||
/** Called every time new JSON data is available.
|
||||
*/
|
||||
virtual void onSubscriberReceiveJSON (Json::Value const& json) { }
|
||||
};
|
||||
typedef boost::unordered_map <uint64, Subscriber::WeakPtr> SubMapType;
|
||||
#endif
|
||||
|
||||
typedef boost::unordered_map <uint64, InfoSub::wptr> SubMapType;
|
||||
|
||||
public:
|
||||
// VFALCO TODO Make LedgerMaster a SharedObjectPtr or a reference.
|
||||
//
|
||||
explicit NetworkOPs (LedgerMaster* pLedgerMaster);
|
||||
|
||||
virtual ~NetworkOPs () { }
|
||||
|
||||
// network information
|
||||
uint32 getNetworkTimeNC (); // Our best estimate of wall time in seconds from 1/1/2000
|
||||
uint32 getCloseTimeNC (); // Our best estimate of current ledger close time
|
||||
uint32 getValidationTimeNC (); // Use *only* to timestamp our own validation
|
||||
void closeTimeOffset (int);
|
||||
boost::posix_time::ptime getNetworkTimePT ();
|
||||
uint32 getLedgerID (uint256 const& hash);
|
||||
uint32 getCurrentLedgerID ();
|
||||
OperatingMode getOperatingMode ()
|
||||
{
|
||||
return mMode;
|
||||
}
|
||||
std::string strOperatingMode ();
|
||||
|
||||
Ledger::ref getClosedLedger ()
|
||||
{
|
||||
return mLedgerMaster->getClosedLedger ();
|
||||
}
|
||||
Ledger::ref getValidatedLedger ()
|
||||
{
|
||||
return mLedgerMaster->getValidatedLedger ();
|
||||
}
|
||||
Ledger::ref getPublishedLedger ()
|
||||
{
|
||||
return mLedgerMaster->getPublishedLedger ();
|
||||
}
|
||||
Ledger::ref getCurrentLedger ()
|
||||
{
|
||||
return mLedgerMaster->getCurrentLedger ();
|
||||
}
|
||||
Ledger::ref getCurrentSnapshot ()
|
||||
{
|
||||
return mLedgerMaster->getCurrentSnapshot ();
|
||||
}
|
||||
Ledger::pointer getLedgerByHash (uint256 const& hash)
|
||||
{
|
||||
return mLedgerMaster->getLedgerByHash (hash);
|
||||
}
|
||||
Ledger::pointer getLedgerBySeq (const uint32 seq);
|
||||
void missingNodeInLedger (const uint32 seq);
|
||||
|
||||
uint256 getClosedLedgerHash ()
|
||||
{
|
||||
return mLedgerMaster->getClosedLedger ()->getHash ();
|
||||
}
|
||||
|
||||
// Do we have this inclusive range of ledgers in our database
|
||||
bool haveLedgerRange (uint32 from, uint32 to);
|
||||
bool haveLedger (uint32 seq);
|
||||
uint32 getValidatedSeq ();
|
||||
bool isValidated (uint32 seq);
|
||||
bool isValidated (uint32 seq, uint256 const& 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;
|
||||
}
|
||||
|
||||
SLE::pointer getSLE (Ledger::pointer lpLedger, uint256 const& uHash)
|
||||
{
|
||||
return lpLedger->getSLE (uHash);
|
||||
}
|
||||
SLE::pointer getSLEi (Ledger::pointer lpLedger, uint256 const& uHash)
|
||||
{
|
||||
return lpLedger->getSLEi (uHash);
|
||||
}
|
||||
|
||||
//
|
||||
// Transaction operations
|
||||
//
|
||||
typedef FUNCTION_TYPE<void (Transaction::pointer, TER)> stCallback; // must complete immediately
|
||||
void submitTransaction (Job&, SerializedTransaction::pointer, stCallback callback = stCallback ());
|
||||
Transaction::pointer submitTransactionSync (Transaction::ref tpTrans, bool bAdmin, bool bFailHard, bool bSubmit);
|
||||
|
||||
void runTransactionQueue ();
|
||||
Transaction::pointer processTransaction (Transaction::pointer, bool bAdmin, bool bFailHard, stCallback);
|
||||
Transaction::pointer processTransaction (Transaction::pointer transaction, bool bAdmin, bool bFailHard)
|
||||
{
|
||||
return processTransaction (transaction, bAdmin, bFailHard, stCallback ());
|
||||
}
|
||||
|
||||
Transaction::pointer findTransactionByID (uint256 const& transactionID);
|
||||
#if 0
|
||||
int findTransactionsBySource (uint256 const& uLedger, std::list<Transaction::pointer>&, const RippleAddress& sourceAccount,
|
||||
uint32 minSeq, uint32 maxSeq);
|
||||
#endif
|
||||
int findTransactionsByDestination (std::list<Transaction::pointer>&, const RippleAddress& destinationAccount,
|
||||
uint32 startLedgerSeq, uint32 endLedgerSeq, int maxTransactions);
|
||||
|
||||
//
|
||||
// Account functions
|
||||
//
|
||||
|
||||
AccountState::pointer getAccountState (Ledger::ref lrLedger, const RippleAddress& accountID);
|
||||
SLE::pointer getGenerator (Ledger::ref lrLedger, const uint160& uGeneratorID);
|
||||
|
||||
//
|
||||
// Directory functions
|
||||
//
|
||||
|
||||
STVector256 getDirNodeInfo (Ledger::ref lrLedger, uint256 const& uRootIndex,
|
||||
uint64& uNodePrevious, uint64& uNodeNext);
|
||||
|
||||
#if 0
|
||||
//
|
||||
// Nickname functions
|
||||
//
|
||||
|
||||
NicknameState::pointer getNicknameState (uint256 const& uLedger, const std::string& strNickname);
|
||||
#endif
|
||||
|
||||
//
|
||||
// Owner functions
|
||||
//
|
||||
|
||||
Json::Value getOwnerInfo (Ledger::pointer lpLedger, const RippleAddress& naAccount);
|
||||
|
||||
//
|
||||
// Book functions
|
||||
//
|
||||
|
||||
void getBookPage (Ledger::pointer lpLedger,
|
||||
const uint160& uTakerPaysCurrencyID,
|
||||
const uint160& uTakerPaysIssuerID,
|
||||
const uint160& uTakerGetsCurrencyID,
|
||||
const uint160& uTakerGetsIssuerID,
|
||||
const uint160& uTakerID,
|
||||
const bool bProof,
|
||||
const unsigned int iLimit,
|
||||
const Json::Value& jvMarker,
|
||||
Json::Value& jvResult);
|
||||
|
||||
// raw object operations
|
||||
bool findRawLedger (uint256 const& ledgerHash, Blob& rawLedger);
|
||||
bool findRawTransaction (uint256 const& transactionHash, Blob& rawTransaction);
|
||||
bool findAccountNode (uint256 const& nodeHash, Blob& rawAccountNode);
|
||||
bool findTransactionNode (uint256 const& nodeHash, Blob& rawTransactionNode);
|
||||
|
||||
// tree synchronization operations
|
||||
bool getTransactionTreeNodes (uint32 ledgerSeq, uint256 const& myNodeID,
|
||||
Blob const& myNode, std::list< Blob >& newNodes);
|
||||
bool getAccountStateNodes (uint32 ledgerSeq, uint256 const& myNodeId,
|
||||
Blob const& myNode, std::list< Blob >& newNodes);
|
||||
|
||||
// ledger proposal/close functions
|
||||
void processTrustedProposal (LedgerProposal::pointer proposal, boost::shared_ptr<protocol::TMProposeSet> set,
|
||||
RippleAddress nodePublic, uint256 checkLedger, bool sigGood);
|
||||
SHAMapAddNode gotTXData (const boost::shared_ptr<Peer>& peer, uint256 const& hash,
|
||||
const std::list<SHAMapNode>& nodeIDs, const std::list< Blob >& nodeData);
|
||||
bool recvValidation (SerializedValidation::ref val, const std::string& source);
|
||||
void takePosition (int seq, SHAMap::ref position);
|
||||
SHAMap::pointer getTXMap (uint256 const& hash);
|
||||
bool hasTXSet (const boost::shared_ptr<Peer>& peer, uint256 const& set, protocol::TxSetStatus status);
|
||||
void mapComplete (uint256 const& hash, SHAMap::ref map);
|
||||
bool stillNeedTXSet (uint256 const& hash);
|
||||
void makeFetchPack (Job&, boost::weak_ptr<Peer> peer, boost::shared_ptr<protocol::TMGetObjectByHash> request,
|
||||
Ledger::pointer wantLedger, Ledger::pointer haveLedger, uint32 uUptime);
|
||||
bool shouldFetchPack (uint32 seq);
|
||||
void gotFetchPack (bool progress, uint32 seq);
|
||||
void addFetchPack (uint256 const& hash, boost::shared_ptr< Blob >& data);
|
||||
bool getFetchPack (uint256 const& hash, Blob& data);
|
||||
int getFetchSize ();
|
||||
void sweepFetchPack ();
|
||||
|
||||
// network state machine
|
||||
|
||||
// VFALCO TODO Try to make all these private since they seem to be...private
|
||||
//
|
||||
void switchLastClosedLedger (Ledger::pointer newLedger, bool duringConsensus); // Used for the "jump" case
|
||||
bool checkLastClosedLedger (const std::vector<Peer::pointer>&, uint256& networkClosed);
|
||||
int beginConsensus (uint256 const& networkClosed, Ledger::pointer closingLedger);
|
||||
void tryStartConsensus ();
|
||||
void endConsensus (bool correctLCL);
|
||||
void setStandAlone ()
|
||||
{
|
||||
setMode (omFULL);
|
||||
}
|
||||
void setStateTimer ();
|
||||
void newLCL (int proposers, int convergeTime, uint256 const& ledgerHash);
|
||||
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;
|
||||
}
|
||||
bool isFeatureBlocked ()
|
||||
{
|
||||
return mFeatureBlocked;
|
||||
}
|
||||
void setFeatureBlocked ();
|
||||
void consensusViewChange ();
|
||||
int getPreviousProposers ()
|
||||
{
|
||||
return mLastCloseProposers;
|
||||
}
|
||||
int getPreviousConvergeTime ()
|
||||
{
|
||||
return mLastCloseConvergeTime;
|
||||
}
|
||||
uint32 getLastCloseTime ()
|
||||
{
|
||||
return mLastCloseTime;
|
||||
}
|
||||
void setLastCloseTime (uint32 t)
|
||||
{
|
||||
mLastCloseTime = t;
|
||||
}
|
||||
Json::Value getConsensusInfo ();
|
||||
Json::Value getServerInfo (bool human, bool admin);
|
||||
void clearLedgerFetch ();
|
||||
Json::Value getLedgerFetchInfo ();
|
||||
uint32 acceptLedger ();
|
||||
boost::unordered_map < uint160,
|
||||
std::list<LedgerProposal::pointer> > & peekStoredProposals ()
|
||||
{
|
||||
return mStoredProposals;
|
||||
}
|
||||
void storeProposal (LedgerProposal::ref proposal, const RippleAddress& peerPublic);
|
||||
uint256 getConsensusLCL ();
|
||||
void reportFeeChange ();
|
||||
|
||||
void doClusterReport ();
|
||||
|
||||
//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, 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, 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
|
||||
//
|
||||
void pubLedger (Ledger::ref lpAccepted);
|
||||
void pubProposedTransaction (Ledger::ref lpCurrent, SerializedTransaction::ref stTxn, TER terResult);
|
||||
|
||||
|
||||
//
|
||||
// Monitoring: subscriber side
|
||||
//
|
||||
void subAccount (InfoSub::ref ispListener, const boost::unordered_set<RippleAddress>& vnaAccountIDs, uint32 uLedgerIndex, bool rt);
|
||||
void unsubAccount (uint64 uListener, const boost::unordered_set<RippleAddress>& vnaAccountIDs, bool rt);
|
||||
|
||||
bool subLedger (InfoSub::ref ispListener, Json::Value& jvResult);
|
||||
bool unsubLedger (uint64 uListener);
|
||||
|
||||
bool subServer (InfoSub::ref ispListener, Json::Value& jvResult);
|
||||
bool unsubServer (uint64 uListener);
|
||||
|
||||
bool subBook (InfoSub::ref ispListener, const uint160& currencyPays, const uint160& currencyGets,
|
||||
const uint160& issuerPays, const uint160& issuerGets);
|
||||
bool unsubBook (uint64 uListener, const uint160& currencyPays, const uint160& currencyGets,
|
||||
const uint160& issuerPays, const uint160& issuerGets);
|
||||
|
||||
bool subTransactions (InfoSub::ref ispListener);
|
||||
bool unsubTransactions (uint64 uListener);
|
||||
|
||||
bool subRTTransactions (InfoSub::ref ispListener);
|
||||
bool unsubRTTransactions (uint64 uListener);
|
||||
|
||||
InfoSub::pointer findRpcSub (const std::string& strUrl);
|
||||
InfoSub::pointer addRpcSub (const std::string& strUrl, InfoSub::ref rspEntry);
|
||||
|
||||
private:
|
||||
void processNetTimer ();
|
||||
void onDeadlineTimer (DeadlineTimer& timer);
|
||||
|
||||
void setMode (OperatingMode);
|
||||
|
||||
Json::Value transJson (const SerializedTransaction& stTxn, TER terResult, bool bValidated, Ledger::ref lpCurrent);
|
||||
bool haveConsensusObject ();
|
||||
|
||||
Json::Value pubBootstrapAccountInfo (Ledger::ref lpAccepted, const RippleAddress& naAccountID);
|
||||
|
||||
void pubValidatedTransaction (Ledger::ref alAccepted, const AcceptedLedgerTx& alTransaction);
|
||||
void pubAccountTransaction (Ledger::ref lpCurrent, const AcceptedLedgerTx& alTransaction, bool isAccepted);
|
||||
|
||||
void pubServer ();
|
||||
|
||||
private:
|
||||
typedef boost::unordered_map <uint160, SubMapType> SubInfoMapType;
|
||||
typedef boost::unordered_map <uint160, SubMapType>::iterator SubInfoMapIterator;
|
||||
|
||||
typedef boost::unordered_map<std::string, InfoSub::pointer> subRpcMapType;
|
||||
|
||||
OperatingMode mMode;
|
||||
bool mNeedNetworkLedger;
|
||||
bool mProposing, mValidating;
|
||||
bool mFeatureBlocked;
|
||||
boost::posix_time::ptime mConnectTime;
|
||||
DeadlineTimer m_netTimer;
|
||||
DeadlineTimer m_clusterTimer;
|
||||
boost::shared_ptr<LedgerConsensus> mConsensus;
|
||||
boost::unordered_map < uint160,
|
||||
std::list<LedgerProposal::pointer> > mStoredProposals;
|
||||
|
||||
LedgerMaster* mLedgerMaster;
|
||||
InboundLedger::pointer mAcquiringLedger;
|
||||
|
||||
int mCloseTimeOffset;
|
||||
|
||||
// last ledger close
|
||||
int mLastCloseProposers, mLastCloseConvergeTime;
|
||||
uint256 mLastCloseHash;
|
||||
uint32 mLastCloseTime;
|
||||
uint32 mLastValidationTime;
|
||||
SerializedValidation::pointer mLastValidation;
|
||||
|
||||
// Recent positions taken
|
||||
std::map<uint256, std::pair<int, SHAMap::pointer> > mRecentPositions;
|
||||
|
||||
// XXX Split into more locks.
|
||||
boost::recursive_mutex mMonitorLock;
|
||||
SubInfoMapType mSubAccount;
|
||||
SubInfoMapType mSubRTAccount;
|
||||
|
||||
subRpcMapType mRpcSubMap;
|
||||
|
||||
SubMapType mSubLedger; // accepted ledgers
|
||||
SubMapType mSubServer; // when server changes connectivity state
|
||||
SubMapType mSubTransactions; // all accepted transactions
|
||||
SubMapType mSubRTTransactions; // all proposed and accepted transactions
|
||||
|
||||
TaggedCache< uint256, Blob , UptimeTimerAdapter > mFetchPack;
|
||||
uint32 mLastFetchPack;
|
||||
|
||||
// VFALCO TODO Document the special value uint32(-1) for this member
|
||||
// and replace uint32(-1) with a constant. It is initialized
|
||||
// in the ctor-initializer list to this constant.
|
||||
//
|
||||
uint32 mFetchSeq;
|
||||
|
||||
uint32 mLastLoadBase;
|
||||
uint32 mLastLoadFactor;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,57 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
SETUP_LOG (OfferCancelTransactor)
|
||||
|
||||
TER OfferCancelTransactor::doApply ()
|
||||
{
|
||||
TER terResult;
|
||||
const uint32 uOfferSequence = mTxn.getFieldU32 (sfOfferSequence);
|
||||
const uint32 uAccountSequenceNext = mTxnAccount->getFieldU32 (sfSequence);
|
||||
|
||||
WriteLog (lsDEBUG, OfferCancelTransactor) << "OfferCancel: uAccountSequenceNext=" << uAccountSequenceNext << " uOfferSequence=" << uOfferSequence;
|
||||
|
||||
const uint32 uTxFlags = mTxn.getFlags ();
|
||||
|
||||
if (uTxFlags)
|
||||
{
|
||||
WriteLog (lsINFO, OfferCancelTransactor) << "OfferCancel: Malformed transaction: Invalid flags set.";
|
||||
|
||||
return temINVALID_FLAG;
|
||||
}
|
||||
|
||||
if (!uOfferSequence || uAccountSequenceNext - 1 <= uOfferSequence)
|
||||
{
|
||||
WriteLog (lsINFO, OfferCancelTransactor) << "OfferCancel: uAccountSequenceNext=" << uAccountSequenceNext << " uOfferSequence=" << uOfferSequence;
|
||||
|
||||
terResult = temBAD_SEQUENCE;
|
||||
}
|
||||
else
|
||||
{
|
||||
const uint256 uOfferIndex = Ledger::getOfferIndex (mTxnAccountID, uOfferSequence);
|
||||
SLE::pointer sleOffer = mEngine->entryCache (ltOFFER, uOfferIndex);
|
||||
|
||||
if (sleOffer)
|
||||
{
|
||||
WriteLog (lsDEBUG, OfferCancelTransactor) << "OfferCancel: uOfferSequence=" << uOfferSequence;
|
||||
|
||||
terResult = mEngine->getNodes ().offerDelete (sleOffer, uOfferIndex, mTxnAccountID);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog (lsWARNING, OfferCancelTransactor) << "OfferCancel: offer not found: "
|
||||
<< RippleAddress::createHumanAccountID (mTxnAccountID)
|
||||
<< " : " << uOfferSequence
|
||||
<< " : " << uOfferIndex.ToString ();
|
||||
|
||||
terResult = tesSUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
return terResult;
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
@@ -1,19 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef OFFERCANCELTRANSACTOR_H
|
||||
#define OFFERCANCELTRANSACTOR_H
|
||||
|
||||
class OfferCancelTransactor : public Transactor
|
||||
{
|
||||
public:
|
||||
OfferCancelTransactor (const SerializedTransaction& txn, TransactionEngineParams params, TransactionEngine* engine) : Transactor (txn, params, engine) {}
|
||||
|
||||
TER doApply ();
|
||||
};
|
||||
#endif
|
||||
|
||||
// vim:ts=4
|
||||
@@ -1,720 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
SETUP_LOG (OfferCreateTransactor)
|
||||
|
||||
// Make sure an offer is still valid. If not, mark it unfunded.
|
||||
bool OfferCreateTransactor::bValidOffer (
|
||||
SLE::ref sleOfferDir,
|
||||
uint256 const& uOfferIndex,
|
||||
const uint160& uOfferOwnerID,
|
||||
const STAmount& saOfferPays,
|
||||
const STAmount& saOfferGets,
|
||||
const uint160& uTakerAccountID,
|
||||
boost::unordered_set<uint256>& usOfferUnfundedFound,
|
||||
boost::unordered_set<uint256>& usOfferUnfundedBecame,
|
||||
boost::unordered_set<uint160>& usAccountTouched,
|
||||
STAmount& saOfferFunds) // <--
|
||||
{
|
||||
bool bValid;
|
||||
|
||||
if (sleOfferDir->isFieldPresent (sfExpiration) && sleOfferDir->getFieldU32 (sfExpiration) <= mEngine->getLedger ()->getParentCloseTimeNC ())
|
||||
{
|
||||
// Offer is expired. Expired offers are considered unfunded. Delete it.
|
||||
WriteLog (lsINFO, OfferCreateTransactor) << "bValidOffer: encountered expired offer";
|
||||
|
||||
usOfferUnfundedFound.insert (uOfferIndex);
|
||||
|
||||
bValid = false;
|
||||
}
|
||||
else if (uOfferOwnerID == uTakerAccountID)
|
||||
{
|
||||
// Would take own offer. Consider old offer expired. Delete it.
|
||||
WriteLog (lsINFO, OfferCreateTransactor) << "bValidOffer: encountered taker's own old offer";
|
||||
|
||||
usOfferUnfundedFound.insert (uOfferIndex);
|
||||
|
||||
bValid = false;
|
||||
}
|
||||
else if (!saOfferGets.isPositive () || !saOfferPays.isPositive ())
|
||||
{
|
||||
// Offer has bad amounts. Consider offer expired. Delete it.
|
||||
WriteLog (lsWARNING, OfferCreateTransactor) << boost::str (boost::format ("bValidOffer: BAD OFFER: saOfferPays=%s saOfferGets=%s")
|
||||
% saOfferPays % saOfferGets);
|
||||
|
||||
usOfferUnfundedFound.insert (uOfferIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog (lsTRACE, OfferCreateTransactor) << "bValidOffer: saOfferPays=" << saOfferPays.getFullText ();
|
||||
|
||||
saOfferFunds = mEngine->getNodes ().accountFunds (uOfferOwnerID, saOfferPays);
|
||||
|
||||
if (!saOfferFunds.isPositive ())
|
||||
{
|
||||
// Offer is unfunded, possibly due to previous balance action.
|
||||
WriteLog (lsDEBUG, OfferCreateTransactor) << "bValidOffer: offer unfunded: delete";
|
||||
|
||||
boost::unordered_set<uint160>::iterator account = usAccountTouched.find (uOfferOwnerID);
|
||||
|
||||
if (account != usAccountTouched.end ())
|
||||
{
|
||||
// Previously touched account.
|
||||
usOfferUnfundedBecame.insert (uOfferIndex); // Delete unfunded offer on success.
|
||||
}
|
||||
else
|
||||
{
|
||||
// Never touched source account.
|
||||
usOfferUnfundedFound.insert (uOfferIndex); // Delete found unfunded offer when possible.
|
||||
}
|
||||
|
||||
bValid = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
bValid = true;
|
||||
}
|
||||
}
|
||||
|
||||
return bValid;
|
||||
}
|
||||
|
||||
// Take as much as possible. Adjusts account balances. Charges fees on top to taker.
|
||||
// --> uBookBase: The order book to take against.
|
||||
// --> saTakerPays: What the taker offers (w/ issuer)
|
||||
// --> saTakerGets: What the taker wanted (w/ issuer)
|
||||
// <-- saTakerPaid: What taker could have paid including saved not including fees. To reduce an offer.
|
||||
// <-- saTakerGot: What taker got not including fees. To reduce an offer.
|
||||
// <-- terResult: tesSUCCESS, terNO_ACCOUNT, telFAILED_PROCESSING, or tecFAILED_PROCESSING
|
||||
// <-- bUnfunded: if tesSUCCESS, consider offer unfunded after taking.
|
||||
TER OfferCreateTransactor::takeOffers (
|
||||
const bool bOpenLedger,
|
||||
const bool bPassive,
|
||||
const bool bSell,
|
||||
uint256 const& uBookBase,
|
||||
const uint160& uTakerAccountID,
|
||||
SLE::ref sleTakerAccount,
|
||||
const STAmount& saTakerPays,
|
||||
const STAmount& saTakerGets,
|
||||
STAmount& saTakerPaid,
|
||||
STAmount& saTakerGot,
|
||||
bool& bUnfunded)
|
||||
{
|
||||
// The book has the most elements. Take the perspective of the book.
|
||||
// Book is ordered for taker: taker pays / taker gets (smaller is better)
|
||||
|
||||
// The order is for the other books currencys for get and pays are opposites.
|
||||
// We want the same ratio for the respective currencies.
|
||||
// So we swap paid and gets for determing take quality.
|
||||
|
||||
assert (saTakerPays && saTakerGets);
|
||||
|
||||
WriteLog (lsDEBUG, OfferCreateTransactor) << "takeOffers: bSell: " << bSell << ": against book: " << uBookBase.ToString ();
|
||||
|
||||
LedgerEntrySet& lesActive = mEngine->getNodes ();
|
||||
uint256 uTipIndex = uBookBase;
|
||||
const uint256 uBookEnd = Ledger::getQualityNext (uBookBase);
|
||||
const uint64 uTakeQuality = STAmount::getRate (saTakerGets, saTakerPays);
|
||||
STAmount saTakerRate = STAmount::setRate (uTakeQuality);
|
||||
const uint160 uTakerPaysAccountID = saTakerPays.getIssuer ();
|
||||
const uint160 uTakerGetsAccountID = saTakerGets.getIssuer ();
|
||||
TER terResult = temUNCERTAIN;
|
||||
|
||||
boost::unordered_set<uint256> usOfferUnfundedBecame; // Offers that became unfunded.
|
||||
boost::unordered_set<uint160> usAccountTouched; // Accounts touched.
|
||||
|
||||
saTakerPaid = STAmount (saTakerPays.getCurrency (), saTakerPays.getIssuer ());
|
||||
saTakerGot = STAmount (saTakerGets.getCurrency (), saTakerGets.getIssuer ());
|
||||
bUnfunded = false;
|
||||
|
||||
while (temUNCERTAIN == terResult)
|
||||
{
|
||||
SLE::pointer sleOfferDir;
|
||||
uint64 uTipQuality = 0;
|
||||
STAmount saTakerFunds = lesActive.accountFunds (uTakerAccountID, saTakerPays);
|
||||
STAmount saSubTakerPays = saTakerPays - saTakerPaid; // How much more to spend.
|
||||
STAmount saSubTakerGets = saTakerGets - saTakerGot; // How much more is wanted.
|
||||
|
||||
// Figure out next offer to take, if needed.
|
||||
if (saTakerFunds.isPositive () // Taker has funds available.
|
||||
&& saSubTakerPays.isPositive ()
|
||||
&& saSubTakerGets.isPositive ())
|
||||
{
|
||||
sleOfferDir = mEngine->entryCache (ltDIR_NODE, lesActive.getNextLedgerIndex (uTipIndex, uBookEnd));
|
||||
|
||||
if (sleOfferDir)
|
||||
{
|
||||
uTipIndex = sleOfferDir->getIndex ();
|
||||
uTipQuality = Ledger::getQuality (uTipIndex);
|
||||
|
||||
WriteLog (lsDEBUG, OfferCreateTransactor) << boost::str (boost::format ("takeOffers: possible counter offer found: uTipQuality=%d uTipIndex=%s")
|
||||
% uTipQuality
|
||||
% uTipIndex.ToString ());
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog (lsTRACE, OfferCreateTransactor) << "takeOffers: counter offer book is empty: "
|
||||
<< uTipIndex.ToString ()
|
||||
<< " ... "
|
||||
<< uBookEnd.ToString ();
|
||||
}
|
||||
}
|
||||
|
||||
if (!saTakerFunds.isPositive ()) // Taker has no funds.
|
||||
{
|
||||
// Done. Ran out of funds on previous round. As fees aren't calculated directly in this routine, funds are checked here.
|
||||
WriteLog (lsDEBUG, OfferCreateTransactor) << "takeOffers: done: taker unfunded.";
|
||||
|
||||
bUnfunded = true; // Don't create an order.
|
||||
terResult = tesSUCCESS;
|
||||
}
|
||||
else if (!sleOfferDir // No offer directory to take.
|
||||
|| uTakeQuality < uTipQuality // No offers of sufficient quality available.
|
||||
|| (bPassive && uTakeQuality == uTipQuality))
|
||||
{
|
||||
// Done.
|
||||
STAmount saTipRate = sleOfferDir ? STAmount::setRate (uTipQuality) : saTakerRate;
|
||||
|
||||
WriteLog (lsDEBUG, OfferCreateTransactor) << boost::str (boost::format ("takeOffers: done: dir=%d uTakeQuality=%d %c uTipQuality=%d saTakerRate=%s %c saTipRate=%s bPassive=%d")
|
||||
% !!sleOfferDir
|
||||
% uTakeQuality
|
||||
% (uTakeQuality == uTipQuality
|
||||
? '='
|
||||
: uTakeQuality < uTipQuality
|
||||
? '<'
|
||||
: '>')
|
||||
% uTipQuality
|
||||
% saTakerRate
|
||||
% (saTakerRate == saTipRate
|
||||
? '='
|
||||
: saTakerRate < saTipRate
|
||||
? '<'
|
||||
: '>')
|
||||
% saTipRate
|
||||
% bPassive);
|
||||
|
||||
terResult = tesSUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Have an offer directory to consider.
|
||||
WriteLog (lsTRACE, OfferCreateTransactor) << "takeOffers: considering dir: " << sleOfferDir->getJson (0);
|
||||
|
||||
SLE::pointer sleBookNode;
|
||||
unsigned int uBookEntry;
|
||||
uint256 uOfferIndex;
|
||||
|
||||
lesActive.dirFirst (uTipIndex, sleBookNode, uBookEntry, uOfferIndex);
|
||||
|
||||
SLE::pointer sleOffer = mEngine->entryCache (ltOFFER, uOfferIndex);
|
||||
|
||||
WriteLog (lsDEBUG, OfferCreateTransactor) << "takeOffers: considering offer : " << sleOffer->getJson (0);
|
||||
|
||||
const uint160 uOfferOwnerID = sleOffer->getFieldAccount160 (sfAccount);
|
||||
STAmount saOfferPays = sleOffer->getFieldAmount (sfTakerGets);
|
||||
STAmount saOfferGets = sleOffer->getFieldAmount (sfTakerPays);
|
||||
|
||||
STAmount saOfferFunds; // Funds of offer owner to payout.
|
||||
bool bValid;
|
||||
|
||||
bValid = bValidOffer (
|
||||
sleOfferDir, uOfferIndex, uOfferOwnerID, saOfferPays, saOfferGets,
|
||||
uTakerAccountID,
|
||||
usOfferUnfundedFound, usOfferUnfundedBecame, usAccountTouched,
|
||||
saOfferFunds);
|
||||
|
||||
if (bValid)
|
||||
{
|
||||
STAmount saSubTakerPaid;
|
||||
STAmount saSubTakerGot;
|
||||
STAmount saTakerIssuerFee;
|
||||
STAmount saOfferIssuerFee;
|
||||
STAmount saOfferRate = STAmount::setRate (uTipQuality);
|
||||
|
||||
WriteLog (lsDEBUG, OfferCreateTransactor) << "takeOffers: applyOffer: saTakerPays: " << saTakerPays.getFullText ();
|
||||
WriteLog (lsDEBUG, OfferCreateTransactor) << "takeOffers: applyOffer: saTakerPaid: " << saTakerPaid.getFullText ();
|
||||
WriteLog (lsDEBUG, OfferCreateTransactor) << "takeOffers: applyOffer: saTakerFunds: " << saTakerFunds.getFullText ();
|
||||
WriteLog (lsDEBUG, OfferCreateTransactor) << "takeOffers: applyOffer: saOfferFunds: " << saOfferFunds.getFullText ();
|
||||
WriteLog (lsDEBUG, OfferCreateTransactor) << "takeOffers: applyOffer: saOfferPays: " << saOfferPays.getFullText ();
|
||||
WriteLog (lsDEBUG, OfferCreateTransactor) << "takeOffers: applyOffer: saOfferGets: " << saOfferGets.getFullText ();
|
||||
WriteLog (lsDEBUG, OfferCreateTransactor) << "takeOffers: applyOffer: saOfferRate: " << saOfferRate.getFullText ();
|
||||
WriteLog (lsDEBUG, OfferCreateTransactor) << "takeOffers: applyOffer: saSubTakerPays: " << saSubTakerPays.getFullText ();
|
||||
WriteLog (lsDEBUG, OfferCreateTransactor) << "takeOffers: applyOffer: saSubTakerGets: " << saSubTakerGets.getFullText ();
|
||||
WriteLog (lsDEBUG, OfferCreateTransactor) << "takeOffers: applyOffer: saTakerPays: " << saTakerPays.getFullText ();
|
||||
WriteLog (lsDEBUG, OfferCreateTransactor) << "takeOffers: applyOffer: saTakerGets: " << saTakerGets.getFullText ();
|
||||
|
||||
bool bOfferDelete = STAmount::applyOffer (
|
||||
bSell,
|
||||
lesActive.rippleTransferRate (uTakerAccountID, uOfferOwnerID, uTakerPaysAccountID),
|
||||
lesActive.rippleTransferRate (uOfferOwnerID, uTakerAccountID, uTakerGetsAccountID),
|
||||
saOfferRate,
|
||||
saOfferFunds,
|
||||
saTakerFunds,
|
||||
saOfferPays,
|
||||
saOfferGets,
|
||||
saSubTakerPays,
|
||||
saSubTakerGets,
|
||||
saSubTakerPaid,
|
||||
saSubTakerGot,
|
||||
saTakerIssuerFee,
|
||||
saOfferIssuerFee);
|
||||
|
||||
WriteLog (lsDEBUG, OfferCreateTransactor) << "takeOffers: applyOffer: saSubTakerPaid: " << saSubTakerPaid.getFullText ();
|
||||
WriteLog (lsDEBUG, OfferCreateTransactor) << "takeOffers: applyOffer: saSubTakerGot: " << saSubTakerGot.getFullText ();
|
||||
|
||||
// Adjust offer
|
||||
|
||||
// Offer owner will pay less. Subtract what taker just got.
|
||||
sleOffer->setFieldAmount (sfTakerGets, saOfferPays -= saSubTakerGot);
|
||||
|
||||
// Offer owner will get less. Subtract what owner just paid.
|
||||
sleOffer->setFieldAmount (sfTakerPays, saOfferGets -= saSubTakerPaid);
|
||||
|
||||
mEngine->entryModify (sleOffer);
|
||||
|
||||
if (bOfferDelete)
|
||||
{
|
||||
// Offer now fully claimed or now unfunded.
|
||||
WriteLog (lsDEBUG, OfferCreateTransactor) << "takeOffers: Offer claimed: Delete.";
|
||||
|
||||
usOfferUnfundedBecame.insert (uOfferIndex); // Delete unfunded offer on success.
|
||||
|
||||
// Offer owner's account is no longer pristine.
|
||||
usAccountTouched.insert (uOfferOwnerID);
|
||||
}
|
||||
else if (saSubTakerGot)
|
||||
{
|
||||
WriteLog (lsDEBUG, OfferCreateTransactor) << "takeOffers: Offer partial claim.";
|
||||
|
||||
if (!saOfferPays.isPositive () || !saOfferGets.isPositive ())
|
||||
{
|
||||
WriteLog (lsWARNING, OfferCreateTransactor) << "takeOffers: ILLEGAL OFFER RESULT.";
|
||||
bUnfunded = true;
|
||||
terResult = bOpenLedger ? telFAILED_PROCESSING : tecFAILED_PROCESSING;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Taker got nothing, probably due to rounding. Consider taker unfunded.
|
||||
WriteLog (lsDEBUG, OfferCreateTransactor) << "takeOffers: No claim.";
|
||||
|
||||
bUnfunded = true;
|
||||
terResult = tesSUCCESS; // Done.
|
||||
}
|
||||
|
||||
assert (uTakerGetsAccountID == saSubTakerGot.getIssuer ());
|
||||
assert (uTakerPaysAccountID == saSubTakerPaid.getIssuer ());
|
||||
|
||||
if (!bUnfunded)
|
||||
{
|
||||
// Distribute funds. The sends charge appropriate fees which are implied by offer.
|
||||
|
||||
terResult = lesActive.accountSend (uOfferOwnerID, uTakerAccountID, saSubTakerGot); // Offer owner pays taker.
|
||||
|
||||
if (tesSUCCESS == terResult)
|
||||
terResult = lesActive.accountSend (uTakerAccountID, uOfferOwnerID, saSubTakerPaid); // Taker pays offer owner.
|
||||
|
||||
if (!bSell)
|
||||
{
|
||||
// Buy semantics: Reduce amount considered paid by taker's rate. Not by actual cost which is lower.
|
||||
// That is, take less as to just satify our buy requirement.
|
||||
STAmount saTakerCould = saTakerPays - saTakerPaid; // Taker could pay.
|
||||
|
||||
if (saTakerFunds < saTakerCould)
|
||||
saTakerCould = saTakerFunds;
|
||||
|
||||
STAmount saTakerUsed = STAmount::multiply (saSubTakerGot, saTakerRate, saTakerPays);
|
||||
|
||||
WriteLog (lsDEBUG, OfferCreateTransactor) << "takeOffers: applyOffer: saTakerCould: " << saTakerCould.getFullText ();
|
||||
WriteLog (lsDEBUG, OfferCreateTransactor) << "takeOffers: applyOffer: saSubTakerGot: " << saSubTakerGot.getFullText ();
|
||||
WriteLog (lsDEBUG, OfferCreateTransactor) << "takeOffers: applyOffer: saTakerRate: " << saTakerRate.getFullText ();
|
||||
WriteLog (lsDEBUG, OfferCreateTransactor) << "takeOffers: applyOffer: saTakerUsed: " << saTakerUsed.getFullText ();
|
||||
|
||||
saSubTakerPaid = std::min (saTakerCould, saTakerUsed);
|
||||
}
|
||||
|
||||
saTakerPaid += saSubTakerPaid;
|
||||
saTakerGot += saSubTakerGot;
|
||||
|
||||
if (tesSUCCESS == terResult)
|
||||
terResult = temUNCERTAIN;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WriteLog (lsDEBUG, OfferCreateTransactor) << "takeOffers: " << transToken (terResult);
|
||||
|
||||
if (tesSUCCESS == terResult)
|
||||
{
|
||||
// On success, delete offers that became unfunded.
|
||||
BOOST_FOREACH (uint256 const & uOfferIndex, usOfferUnfundedBecame)
|
||||
{
|
||||
WriteLog (lsDEBUG, OfferCreateTransactor) << "takeOffers: became unfunded: " << uOfferIndex.ToString ();
|
||||
|
||||
terResult = lesActive.offerDelete (uOfferIndex);
|
||||
|
||||
if (tesSUCCESS != terResult)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
WriteLog (lsDEBUG, OfferCreateTransactor) << "takeOffers< " << transToken (terResult);
|
||||
|
||||
return terResult;
|
||||
}
|
||||
|
||||
TER OfferCreateTransactor::doApply ()
|
||||
{
|
||||
WriteLog (lsTRACE, OfferCreateTransactor) << "OfferCreate> " << mTxn.getJson (0);
|
||||
const uint32 uTxFlags = mTxn.getFlags ();
|
||||
const bool bPassive = isSetBit (uTxFlags, tfPassive);
|
||||
const bool bImmediateOrCancel = isSetBit (uTxFlags, tfImmediateOrCancel);
|
||||
const bool bFillOrKill = isSetBit (uTxFlags, tfFillOrKill);
|
||||
const bool bSell = isSetBit (uTxFlags, tfSell);
|
||||
STAmount saTakerPays = mTxn.getFieldAmount (sfTakerPays);
|
||||
STAmount saTakerGets = mTxn.getFieldAmount (sfTakerGets);
|
||||
|
||||
if (!saTakerPays.isLegalNet () || !saTakerGets.isLegalNet ())
|
||||
return temBAD_AMOUNT;
|
||||
|
||||
WriteLog (lsTRACE, OfferCreateTransactor) << boost::str (boost::format ("OfferCreate: saTakerPays=%s saTakerGets=%s")
|
||||
% saTakerPays.getFullText ()
|
||||
% saTakerGets.getFullText ());
|
||||
|
||||
const uint160 uPaysIssuerID = saTakerPays.getIssuer ();
|
||||
const uint160 uGetsIssuerID = saTakerGets.getIssuer ();
|
||||
const uint32 uExpiration = mTxn.getFieldU32 (sfExpiration);
|
||||
const bool bHaveExpiration = mTxn.isFieldPresent (sfExpiration);
|
||||
const bool bHaveCancel = mTxn.isFieldPresent (sfOfferSequence);
|
||||
const uint32 uCancelSequence = mTxn.getFieldU32 (sfOfferSequence);
|
||||
const uint32 uAccountSequenceNext = mTxnAccount->getFieldU32 (sfSequence);
|
||||
const uint32 uSequence = mTxn.getSequence ();
|
||||
|
||||
const uint256 uLedgerIndex = Ledger::getOfferIndex (mTxnAccountID, uSequence);
|
||||
|
||||
WriteLog (lsTRACE, OfferCreateTransactor) << "OfferCreate: Creating offer node: " << uLedgerIndex.ToString () << " uSequence=" << uSequence;
|
||||
|
||||
const uint160 uPaysCurrency = saTakerPays.getCurrency ();
|
||||
const uint160 uGetsCurrency = saTakerGets.getCurrency ();
|
||||
const uint64 uRate = STAmount::getRate (saTakerGets, saTakerPays);
|
||||
|
||||
TER terResult = tesSUCCESS;
|
||||
uint256 uDirectory; // Delete hints.
|
||||
uint64 uOwnerNode;
|
||||
uint64 uBookNode;
|
||||
|
||||
LedgerEntrySet& lesActive = mEngine->getNodes ();
|
||||
LedgerEntrySet lesCheckpoint = lesActive; // Checkpoint with just fees paid.
|
||||
lesActive.bumpSeq (); // Begin ledger variance.
|
||||
|
||||
SLE::pointer sleCreator = mEngine->entryCache (ltACCOUNT_ROOT, Ledger::getAccountRootIndex (mTxnAccountID));
|
||||
|
||||
if (uTxFlags & tfOfferCreateMask)
|
||||
{
|
||||
WriteLog (lsINFO, OfferCreateTransactor) << "OfferCreate: Malformed transaction: Invalid flags set.";
|
||||
|
||||
return temINVALID_FLAG;
|
||||
}
|
||||
else if (bImmediateOrCancel && bFillOrKill)
|
||||
{
|
||||
WriteLog (lsINFO, OfferCreateTransactor) << "OfferCreate: Malformed transaction: both IoC and FoK set.";
|
||||
|
||||
return temINVALID_FLAG;
|
||||
}
|
||||
else if (bHaveExpiration && !uExpiration)
|
||||
{
|
||||
WriteLog (lsWARNING, OfferCreateTransactor) << "OfferCreate: Malformed offer: bad expiration";
|
||||
|
||||
terResult = temBAD_EXPIRATION;
|
||||
}
|
||||
else if (bHaveExpiration && mEngine->getLedger ()->getParentCloseTimeNC () >= uExpiration)
|
||||
{
|
||||
WriteLog (lsWARNING, OfferCreateTransactor) << "OfferCreate: Expired transaction: offer expired";
|
||||
|
||||
terResult = tesSUCCESS; // Only charged fee.
|
||||
}
|
||||
else if (saTakerPays.isNative () && saTakerGets.isNative ())
|
||||
{
|
||||
WriteLog (lsWARNING, OfferCreateTransactor) << "OfferCreate: Malformed offer: XRP for XRP";
|
||||
|
||||
terResult = temBAD_OFFER;
|
||||
}
|
||||
else if (!saTakerPays.isPositive () || !saTakerGets.isPositive ())
|
||||
{
|
||||
WriteLog (lsWARNING, OfferCreateTransactor) << "OfferCreate: Malformed offer: bad amount";
|
||||
|
||||
terResult = temBAD_OFFER;
|
||||
}
|
||||
else if (uPaysCurrency == uGetsCurrency && uPaysIssuerID == uGetsIssuerID)
|
||||
{
|
||||
WriteLog (lsWARNING, OfferCreateTransactor) << "OfferCreate: Malformed offer: redundant offer";
|
||||
|
||||
terResult = temREDUNDANT;
|
||||
}
|
||||
else if (CURRENCY_BAD == uPaysCurrency || CURRENCY_BAD == uGetsCurrency)
|
||||
{
|
||||
WriteLog (lsWARNING, OfferCreateTransactor) << "OfferCreate: Malformed offer: Bad currency.";
|
||||
|
||||
terResult = temBAD_CURRENCY;
|
||||
}
|
||||
else if (saTakerPays.isNative () != !uPaysIssuerID || saTakerGets.isNative () != !uGetsIssuerID)
|
||||
{
|
||||
WriteLog (lsWARNING, OfferCreateTransactor) << "OfferCreate: Malformed offer: bad issuer";
|
||||
|
||||
terResult = temBAD_ISSUER;
|
||||
}
|
||||
else if (!lesActive.accountFunds (mTxnAccountID, saTakerGets).isPositive ())
|
||||
{
|
||||
WriteLog (lsWARNING, OfferCreateTransactor) << "OfferCreate: delay: Offers must be at least partially funded.";
|
||||
|
||||
terResult = tecUNFUNDED_OFFER;
|
||||
}
|
||||
else if (bHaveCancel && (!uCancelSequence || uAccountSequenceNext - 1 <= uCancelSequence))
|
||||
{
|
||||
WriteLog (lsINFO, OfferCreateTransactor) << "OfferCreate: uAccountSequenceNext=" << uAccountSequenceNext << " uOfferSequence=" << uCancelSequence;
|
||||
|
||||
terResult = temBAD_SEQUENCE;
|
||||
}
|
||||
|
||||
// Cancel offer.
|
||||
if (tesSUCCESS == terResult && bHaveCancel)
|
||||
{
|
||||
const uint256 uCancelIndex = Ledger::getOfferIndex (mTxnAccountID, uCancelSequence);
|
||||
SLE::pointer sleCancel = mEngine->entryCache (ltOFFER, uCancelIndex);
|
||||
|
||||
if (sleCancel)
|
||||
{
|
||||
WriteLog (lsWARNING, OfferCreateTransactor) << "OfferCreate: uCancelSequence=" << uCancelSequence;
|
||||
|
||||
terResult = mEngine->getNodes ().offerDelete (sleCancel, uCancelIndex, mTxnAccountID);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog (lsWARNING, OfferCreateTransactor) << "OfferCreate: offer not found: "
|
||||
<< RippleAddress::createHumanAccountID (mTxnAccountID)
|
||||
<< " : " << uCancelSequence
|
||||
<< " : " << uCancelIndex.ToString ();
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure authorized to hold what taker will pay.
|
||||
if (tesSUCCESS == terResult && !saTakerPays.isNative ())
|
||||
{
|
||||
SLE::pointer sleTakerPays = mEngine->entryCache (ltACCOUNT_ROOT, Ledger::getAccountRootIndex (uPaysIssuerID));
|
||||
|
||||
if (!sleTakerPays)
|
||||
{
|
||||
WriteLog (lsWARNING, OfferCreateTransactor) << "OfferCreate: delay: can't receive IOUs from non-existent issuer: " << RippleAddress::createHumanAccountID (uPaysIssuerID);
|
||||
|
||||
terResult = terNO_ACCOUNT;
|
||||
}
|
||||
else if (isSetBit (sleTakerPays->getFieldU32 (sfFlags), lsfRequireAuth))
|
||||
{
|
||||
SLE::pointer sleRippleState = mEngine->entryCache (ltRIPPLE_STATE, Ledger::getRippleStateIndex (mTxnAccountID, uPaysIssuerID, uPaysCurrency));
|
||||
bool bHigh = mTxnAccountID > uPaysIssuerID;
|
||||
|
||||
if (!sleRippleState
|
||||
|| !isSetBit (sleRippleState->getFieldU32 (sfFlags), (bHigh ? lsfHighAuth : lsfLowAuth)))
|
||||
{
|
||||
WriteLog (lsWARNING, OfferCreateTransactor) << "OfferCreate: delay: can't receive IOUs from issuer without auth.";
|
||||
|
||||
terResult = terNO_AUTH;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
STAmount saPaid;
|
||||
STAmount saGot;
|
||||
bool bUnfunded = false;
|
||||
const bool bOpenLedger = isSetBit (mParams, tapOPEN_LEDGER);
|
||||
|
||||
if (tesSUCCESS == terResult)
|
||||
{
|
||||
const uint256 uTakeBookBase = Ledger::getBookBase (uGetsCurrency, uGetsIssuerID, uPaysCurrency, uPaysIssuerID);
|
||||
|
||||
WriteLog (lsINFO, OfferCreateTransactor) << boost::str (boost::format ("OfferCreate: take against book: %s for %s -> %s")
|
||||
% uTakeBookBase.ToString ()
|
||||
% saTakerGets.getFullText ()
|
||||
% saTakerPays.getFullText ());
|
||||
|
||||
// Take using the parameters of the offer.
|
||||
WriteLog (lsDEBUG, OfferCreateTransactor) << "OfferCreate: takeOffers: BEFORE saTakerGets=" << saTakerGets.getFullText ();
|
||||
|
||||
terResult = takeOffers (
|
||||
bOpenLedger,
|
||||
bPassive,
|
||||
bSell,
|
||||
uTakeBookBase,
|
||||
mTxnAccountID,
|
||||
sleCreator,
|
||||
saTakerGets, // Reverse as we are the taker for taking.
|
||||
saTakerPays,
|
||||
saPaid, // Buy semantics: how much would have sold at full price. Sell semantics: how much was sold.
|
||||
saGot, // How much was got.
|
||||
bUnfunded);
|
||||
|
||||
WriteLog (lsDEBUG, OfferCreateTransactor) << "OfferCreate: takeOffers=" << terResult;
|
||||
WriteLog (lsDEBUG, OfferCreateTransactor) << "OfferCreate: takeOffers: saPaid=" << saPaid.getFullText ();
|
||||
WriteLog (lsDEBUG, OfferCreateTransactor) << "OfferCreate: takeOffers: saGot=" << saGot.getFullText ();
|
||||
|
||||
if (tesSUCCESS == terResult && !bUnfunded)
|
||||
{
|
||||
saTakerPays -= saGot; // Reduce pay in from takers by what offer just got.
|
||||
saTakerGets -= saPaid; // Reduce pay out to takers by what srcAccount just paid.
|
||||
|
||||
WriteLog (lsDEBUG, OfferCreateTransactor) << "OfferCreate: takeOffers: AFTER saTakerPays=" << saTakerPays.getFullText ();
|
||||
WriteLog (lsDEBUG, OfferCreateTransactor) << "OfferCreate: takeOffers: AFTER saTakerGets=" << saTakerGets.getFullText ();
|
||||
}
|
||||
}
|
||||
|
||||
WriteLog (lsDEBUG, OfferCreateTransactor) << "OfferCreate: takeOffers: saTakerPays=" << saTakerPays.getFullText ();
|
||||
WriteLog (lsDEBUG, OfferCreateTransactor) << "OfferCreate: takeOffers: saTakerGets=" << saTakerGets.getFullText ();
|
||||
WriteLog (lsDEBUG, OfferCreateTransactor) << "OfferCreate: takeOffers: mTxnAccountID=" << RippleAddress::createHumanAccountID (mTxnAccountID);
|
||||
WriteLog (lsDEBUG, OfferCreateTransactor) << "OfferCreate: takeOffers: FUNDS=" << lesActive.accountFunds (mTxnAccountID, saTakerGets).getFullText ();
|
||||
|
||||
// WriteLog (lsDEBUG, OfferCreateTransactor) << "OfferCreate: takeOffers: uPaysIssuerID=" << RippleAddress::createHumanAccountID(uPaysIssuerID);
|
||||
// WriteLog (lsDEBUG, OfferCreateTransactor) << "OfferCreate: takeOffers: uGetsIssuerID=" << RippleAddress::createHumanAccountID(uGetsIssuerID);
|
||||
|
||||
if (tesSUCCESS != terResult)
|
||||
{
|
||||
// Fail as is.
|
||||
nothing ();
|
||||
}
|
||||
else if (saTakerPays.isNegative () || saTakerGets.isNegative ())
|
||||
{
|
||||
// If ledger is not final, can vote no.
|
||||
terResult = bOpenLedger ? telFAILED_PROCESSING : tecFAILED_PROCESSING;
|
||||
}
|
||||
else if (bFillOrKill && (saTakerPays || saTakerGets))
|
||||
{
|
||||
// Fill or kill and have leftovers.
|
||||
lesActive.swapWith (lesCheckpoint); // Restore with just fees paid.
|
||||
}
|
||||
else if (
|
||||
!saTakerPays // Wants nothing more.
|
||||
|| !saTakerGets // Offering nothing more.
|
||||
|| bImmediateOrCancel // Do not persist.
|
||||
|| !lesActive.accountFunds (mTxnAccountID, saTakerGets).isPositive () // Not funded.
|
||||
|| bUnfunded) // Consider unfunded.
|
||||
{
|
||||
// Complete as is.
|
||||
nothing ();
|
||||
}
|
||||
else if (mPriorBalance.getNValue () < mEngine->getLedger ()->getReserve (sleCreator->getFieldU32 (sfOwnerCount) + 1))
|
||||
{
|
||||
if (bOpenLedger) // Ledger is not final, can vote no.
|
||||
{
|
||||
// Hope for more reserve to come in or more offers to consume.
|
||||
terResult = tecINSUF_RESERVE_OFFER;
|
||||
}
|
||||
else if (!saPaid && !saGot)
|
||||
{
|
||||
// Ledger is final, insufficent reserve to create offer, processed nothing.
|
||||
|
||||
terResult = tecINSUF_RESERVE_OFFER;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ledger is final, insufficent reserve to create offer, processed something.
|
||||
|
||||
// Consider the offer unfunded. Treat as tesSUCCESS.
|
||||
nothing ();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// We need to place the remainder of the offer into its order book.
|
||||
WriteLog (lsINFO, OfferCreateTransactor) << boost::str (boost::format ("OfferCreate: offer not fully consumed: saTakerPays=%s saTakerGets=%s")
|
||||
% saTakerPays.getFullText ()
|
||||
% saTakerGets.getFullText ());
|
||||
|
||||
// Add offer to owner's directory.
|
||||
terResult = lesActive.dirAdd (uOwnerNode, Ledger::getOwnerDirIndex (mTxnAccountID), uLedgerIndex,
|
||||
BIND_TYPE (&Ledger::ownerDirDescriber, P_1, mTxnAccountID));
|
||||
|
||||
|
||||
if (tesSUCCESS == terResult)
|
||||
{
|
||||
lesActive.ownerCountAdjust (mTxnAccountID, 1, sleCreator); // Update owner count.
|
||||
|
||||
uint256 uBookBase = Ledger::getBookBase (uPaysCurrency, uPaysIssuerID, uGetsCurrency, uGetsIssuerID);
|
||||
|
||||
WriteLog (lsINFO, OfferCreateTransactor) << boost::str (boost::format ("OfferCreate: adding to book: %s : %s/%s -> %s/%s")
|
||||
% uBookBase.ToString ()
|
||||
% saTakerPays.getHumanCurrency ()
|
||||
% RippleAddress::createHumanAccountID (saTakerPays.getIssuer ())
|
||||
% saTakerGets.getHumanCurrency ()
|
||||
% RippleAddress::createHumanAccountID (saTakerGets.getIssuer ()));
|
||||
|
||||
uDirectory = Ledger::getQualityIndex (uBookBase, uRate); // Use original rate.
|
||||
|
||||
// Add offer to order book.
|
||||
terResult = lesActive.dirAdd (uBookNode, uDirectory, uLedgerIndex,
|
||||
BIND_TYPE (&Ledger::qualityDirDescriber, P_1, saTakerPays.getCurrency (), uPaysIssuerID,
|
||||
saTakerGets.getCurrency (), uGetsIssuerID, uRate));
|
||||
}
|
||||
|
||||
if (tesSUCCESS == terResult)
|
||||
{
|
||||
WriteLog (lsDEBUG, OfferCreateTransactor) << "OfferCreate: sfAccount=" << RippleAddress::createHumanAccountID (mTxnAccountID);
|
||||
WriteLog (lsDEBUG, OfferCreateTransactor) << "OfferCreate: uPaysIssuerID=" << RippleAddress::createHumanAccountID (uPaysIssuerID);
|
||||
WriteLog (lsDEBUG, OfferCreateTransactor) << "OfferCreate: uGetsIssuerID=" << RippleAddress::createHumanAccountID (uGetsIssuerID);
|
||||
WriteLog (lsTRACE, OfferCreateTransactor) << "OfferCreate: saTakerPays.isNative()=" << saTakerPays.isNative ();
|
||||
WriteLog (lsTRACE, OfferCreateTransactor) << "OfferCreate: saTakerGets.isNative()=" << saTakerGets.isNative ();
|
||||
WriteLog (lsDEBUG, OfferCreateTransactor) << "OfferCreate: uPaysCurrency=" << saTakerPays.getHumanCurrency ();
|
||||
WriteLog (lsDEBUG, OfferCreateTransactor) << "OfferCreate: uGetsCurrency=" << saTakerGets.getHumanCurrency ();
|
||||
|
||||
SLE::pointer sleOffer = mEngine->entryCreate (ltOFFER, uLedgerIndex);
|
||||
|
||||
sleOffer->setFieldAccount (sfAccount, mTxnAccountID);
|
||||
sleOffer->setFieldU32 (sfSequence, uSequence);
|
||||
sleOffer->setFieldH256 (sfBookDirectory, uDirectory);
|
||||
sleOffer->setFieldAmount (sfTakerPays, saTakerPays);
|
||||
sleOffer->setFieldAmount (sfTakerGets, saTakerGets);
|
||||
sleOffer->setFieldU64 (sfOwnerNode, uOwnerNode);
|
||||
sleOffer->setFieldU64 (sfBookNode, uBookNode);
|
||||
|
||||
if (uExpiration)
|
||||
sleOffer->setFieldU32 (sfExpiration, uExpiration);
|
||||
|
||||
if (bPassive)
|
||||
sleOffer->setFlag (lsfPassive);
|
||||
|
||||
if (bSell)
|
||||
sleOffer->setFlag (lsfSell);
|
||||
|
||||
WriteLog (lsINFO, OfferCreateTransactor) << boost::str (boost::format ("OfferCreate: final terResult=%s sleOffer=%s")
|
||||
% transToken (terResult)
|
||||
% sleOffer->getJson (0));
|
||||
}
|
||||
}
|
||||
|
||||
// On storing meta data, delete offers that were found unfunded to prevent encountering them in future.
|
||||
if (tesSUCCESS == terResult)
|
||||
{
|
||||
BOOST_FOREACH (uint256 const & uOfferIndex, usOfferUnfundedFound)
|
||||
{
|
||||
|
||||
WriteLog (lsINFO, OfferCreateTransactor) << "takeOffers: found unfunded: " << uOfferIndex.ToString ();
|
||||
|
||||
terResult = lesActive.offerDelete (uOfferIndex);
|
||||
|
||||
if (tesSUCCESS != terResult)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CondLog (tesSUCCESS != terResult, lsINFO, OfferCreateTransactor) << boost::str (boost::format ("OfferCreate: final terResult=%s") % transToken (terResult));
|
||||
|
||||
if (isTesSuccess (terResult))
|
||||
getApp().getOrderBookDB ().invalidate ();
|
||||
|
||||
return terResult;
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
@@ -1,48 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef __OFFERCREATETRANSACTOR__
|
||||
#define __OFFERCREATETRANSACTOR__
|
||||
|
||||
class OfferCreateTransactor : public Transactor
|
||||
{
|
||||
public:
|
||||
OfferCreateTransactor (const SerializedTransaction& txn, TransactionEngineParams params, TransactionEngine* engine) : Transactor (txn, params, engine) {}
|
||||
TER doApply ();
|
||||
|
||||
private:
|
||||
bool bValidOffer (
|
||||
SLE::ref sleOfferDir,
|
||||
uint256 const& uOffer,
|
||||
const uint160& uOfferOwnerID,
|
||||
const STAmount& saOfferPays,
|
||||
const STAmount& saOfferGets,
|
||||
const uint160& uTakerAccountID,
|
||||
boost::unordered_set<uint256>& usOfferUnfundedFound,
|
||||
boost::unordered_set<uint256>& usOfferUnfundedBecame,
|
||||
boost::unordered_set<uint160>& usAccountTouched,
|
||||
STAmount& saOfferFunds);
|
||||
|
||||
TER takeOffers (
|
||||
const bool bOpenLedger,
|
||||
const bool bPassive,
|
||||
const bool bSell,
|
||||
uint256 const& uBookBase,
|
||||
const uint160& uTakerAccountID,
|
||||
SLE::ref sleTakerAccount,
|
||||
const STAmount& saTakerPays,
|
||||
const STAmount& saTakerGets,
|
||||
STAmount& saTakerPaid,
|
||||
STAmount& saTakerGot,
|
||||
bool& bUnfunded);
|
||||
|
||||
boost::unordered_set<uint256> usOfferUnfundedFound; // Offers found unfunded.
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
// vim:ts=4
|
||||
@@ -1,178 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
bool ParameterNode::setValue (const std::string& name, const Json::Value& value, Json::Value& error)
|
||||
{
|
||||
if (name.empty ()) // this node
|
||||
return setValue (value, error);
|
||||
|
||||
size_t dot = name.find ('.');
|
||||
|
||||
if (dot == std::string::npos) // a child of this node
|
||||
{
|
||||
std::map<std::string, Parameter::pointer>::iterator it = mChildren.find (name);
|
||||
|
||||
if (it == mChildren.end ())
|
||||
{
|
||||
error = Json::objectValue;
|
||||
error["error"] = "Name not found";
|
||||
error["name"] = name;
|
||||
return false;
|
||||
}
|
||||
|
||||
return it->second->setValue (value, error);
|
||||
}
|
||||
|
||||
std::map<std::string, Parameter::pointer>::iterator it = mChildren.find (name.substr (0, dot));
|
||||
|
||||
if (it == mChildren.end ())
|
||||
{
|
||||
error = Json::objectValue;
|
||||
error["error"] = "Name not found";
|
||||
error["name"] = name;
|
||||
return false;
|
||||
}
|
||||
|
||||
ParameterNode* n = dynamic_cast<ParameterNode*> (it->second.get ());
|
||||
|
||||
if (!n)
|
||||
{
|
||||
error = Json::objectValue;
|
||||
error["error"] = "Node has no children";
|
||||
error["name"] = it->second->getName ();
|
||||
return false;
|
||||
}
|
||||
|
||||
return n->setValue (name.substr (dot + 1), value, error);
|
||||
}
|
||||
|
||||
bool ParameterNode::addNode (const std::string& name, Parameter::ref node)
|
||||
{
|
||||
if (name.empty ()) // this node
|
||||
return false;
|
||||
|
||||
size_t dot = name.find ('.');
|
||||
|
||||
if (dot == std::string::npos) // a child of this node
|
||||
{
|
||||
std::map<std::string, Parameter::pointer>::iterator it = mChildren.find (name);
|
||||
|
||||
if (it != mChildren.end ())
|
||||
return false;
|
||||
|
||||
mChildren[name] = node;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::map<std::string, Parameter::pointer>::iterator it = mChildren.find (name.substr (0, dot));
|
||||
ParameterNode* n;
|
||||
|
||||
if (it == mChildren.end ())
|
||||
{
|
||||
// create a new inner node
|
||||
ParameterNode::pointer node = boost::make_shared<ParameterNode> (getShared (), name.substr (0, dot));
|
||||
n = dynamic_cast<ParameterNode*> (node.get ());
|
||||
assert (n);
|
||||
mChildren[name] = node;
|
||||
}
|
||||
else
|
||||
{
|
||||
// existing node passed through must be inner
|
||||
ParameterNode* n = dynamic_cast<ParameterNode*> (it->second.get ());
|
||||
|
||||
if (!n)
|
||||
return false;
|
||||
}
|
||||
|
||||
return n->addNode (name.substr (dot + 1), node);
|
||||
}
|
||||
|
||||
Json::Value ParameterNode::getValue (int i) const
|
||||
{
|
||||
Json::Value v (Json::objectValue);
|
||||
typedef std::map<std::string, Parameter::pointer>::value_type string_ref_pair;
|
||||
BOOST_FOREACH (const string_ref_pair & it, mChildren)
|
||||
{
|
||||
v[it.first] = it.second->getValue (i);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
bool ParameterNode::setValue (const Json::Value& value, Json::Value& error)
|
||||
{
|
||||
error = Json::objectValue;
|
||||
error["error"] = "Cannot end on an inner node";
|
||||
|
||||
Json::Value nodes (Json::arrayValue);
|
||||
typedef std::map<std::string, Parameter::pointer>::value_type string_ref_pair;
|
||||
BOOST_FOREACH (const string_ref_pair & it, mChildren)
|
||||
{
|
||||
nodes.append (it.first);
|
||||
}
|
||||
error["legal_nodes"] = nodes;
|
||||
return false;
|
||||
}
|
||||
|
||||
ParameterString::ParameterString (Parameter::ref parent, const std::string& name, const std::string& value)
|
||||
: Parameter (parent, name), mValue (value)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
Json::Value ParameterString::getValue (int) const
|
||||
{
|
||||
return Json::Value (mValue);
|
||||
}
|
||||
|
||||
bool ParameterString::setValue (const Json::Value& value, Json::Value& error)
|
||||
{
|
||||
if (!value.isConvertibleTo (Json::stringValue))
|
||||
{
|
||||
error = Json::objectValue;
|
||||
error["error"] = "Cannot convert to string";
|
||||
error["value"] = value;
|
||||
return false;
|
||||
}
|
||||
|
||||
mValue = value.asString ();
|
||||
return true;
|
||||
}
|
||||
|
||||
ParameterInt::ParameterInt (Parameter::ref parent, const std::string& name, int value)
|
||||
: Parameter (parent, name), mValue (value)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
Json::Value ParameterInt::getValue (int) const
|
||||
{
|
||||
return Json::Value (mValue);
|
||||
}
|
||||
|
||||
bool ParameterInt::setValue (const Json::Value& value, Json::Value& error)
|
||||
{
|
||||
if (value.isConvertibleTo (Json::intValue))
|
||||
{
|
||||
mValue = value.asInt ();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (value.isConvertibleTo (Json::stringValue))
|
||||
{
|
||||
try
|
||||
{
|
||||
mValue = lexical_cast_st<int> (value.asString ());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
error = Json::objectValue;
|
||||
error["error"] = "Cannot convert to integer";
|
||||
error["value"] = value;
|
||||
return false;
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_PARAMETERTABLE_H
|
||||
#define RIPPLE_PARAMETERTABLE_H
|
||||
|
||||
class Parameter : public boost::enable_shared_from_this<Parameter>
|
||||
{
|
||||
// abstract base class parameters are derived from
|
||||
public:
|
||||
typedef boost::shared_ptr<Parameter> pointer;
|
||||
typedef const boost::shared_ptr<Parameter>& ref;
|
||||
|
||||
public:
|
||||
Parameter (Parameter::ref parent, const std::string& name) : mParent (parent), mName (name)
|
||||
{
|
||||
;
|
||||
}
|
||||
virtual ~Parameter ()
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
const std::string& getName () const
|
||||
{
|
||||
return mName;
|
||||
}
|
||||
|
||||
virtual Json::Value getValue (int) const = 0;
|
||||
virtual bool setValue (const Json::Value& value, Json::Value& error) = 0;
|
||||
|
||||
Parameter::pointer getShared ()
|
||||
{
|
||||
return shared_from_this ();
|
||||
}
|
||||
|
||||
private:
|
||||
pointer mParent;
|
||||
std::string mName;
|
||||
};
|
||||
|
||||
class ParameterNode : public Parameter
|
||||
{
|
||||
public:
|
||||
ParameterNode (Parameter::ref parent, const std::string& name) : Parameter (parent, name)
|
||||
{
|
||||
;
|
||||
}
|
||||
bool addChildNode (Parameter::ref node);
|
||||
|
||||
bool setValue (const std::string& name, const Json::Value& value, Json::Value& error);
|
||||
bool addNode (const std::string& name, Parameter::ref node);
|
||||
|
||||
virtual Json::Value getValue (int) const;
|
||||
virtual bool setValue (const Json::Value& value, Json::Value& error);
|
||||
|
||||
private:
|
||||
std::map<std::string, Parameter::pointer> mChildren;
|
||||
};
|
||||
|
||||
class ParameterString : public Parameter
|
||||
{
|
||||
public:
|
||||
ParameterString (Parameter::ref parent, const std::string& name, const std::string& value);
|
||||
virtual Json::Value getValue (int) const;
|
||||
virtual bool setValue (const Json::Value& value, Json::Value& error);
|
||||
|
||||
private:
|
||||
std::string mValue;
|
||||
};
|
||||
|
||||
class ParameterInt : public Parameter
|
||||
{
|
||||
public:
|
||||
ParameterInt (Parameter::ref parent, const std::string& name, int value);
|
||||
virtual Json::Value getValue (int) const;
|
||||
virtual bool setValue (const Json::Value& value, Json::Value& error);
|
||||
|
||||
private:
|
||||
int mValue;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,246 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
SETUP_LOG (PaymentTransactor)
|
||||
|
||||
#define RIPPLE_PATHS_MAX 6
|
||||
|
||||
TER PaymentTransactor::doApply ()
|
||||
{
|
||||
// Ripple if source or destination is non-native or if there are paths.
|
||||
const uint32 uTxFlags = mTxn.getFlags ();
|
||||
const bool bPartialPayment = isSetBit (uTxFlags, tfPartialPayment);
|
||||
const bool bLimitQuality = isSetBit (uTxFlags, tfLimitQuality);
|
||||
const bool bNoRippleDirect = isSetBit (uTxFlags, tfNoRippleDirect);
|
||||
const bool bPaths = mTxn.isFieldPresent (sfPaths);
|
||||
const bool bMax = mTxn.isFieldPresent (sfSendMax);
|
||||
const uint160 uDstAccountID = mTxn.getFieldAccount160 (sfDestination);
|
||||
const STAmount saDstAmount = mTxn.getFieldAmount (sfAmount);
|
||||
const STAmount saMaxAmount = bMax
|
||||
? mTxn.getFieldAmount (sfSendMax)
|
||||
: saDstAmount.isNative ()
|
||||
? saDstAmount
|
||||
: STAmount (saDstAmount.getCurrency (), mTxnAccountID, saDstAmount.getMantissa (), saDstAmount.getExponent (), saDstAmount.isNegative ());
|
||||
const uint160 uSrcCurrency = saMaxAmount.getCurrency ();
|
||||
const uint160 uDstCurrency = saDstAmount.getCurrency ();
|
||||
const bool bXRPDirect = uSrcCurrency.isZero () && uDstCurrency.isZero ();
|
||||
|
||||
WriteLog (lsINFO, PaymentTransactor) << boost::str (boost::format ("Payment> saMaxAmount=%s saDstAmount=%s")
|
||||
% saMaxAmount.getFullText ()
|
||||
% saDstAmount.getFullText ());
|
||||
|
||||
if (!saDstAmount.isLegalNet () || !saMaxAmount.isLegalNet ())
|
||||
return temBAD_AMOUNT;
|
||||
|
||||
if (uTxFlags & tfPaymentMask)
|
||||
{
|
||||
WriteLog (lsINFO, PaymentTransactor) << "Payment: Malformed transaction: Invalid flags set.";
|
||||
|
||||
return temINVALID_FLAG;
|
||||
}
|
||||
else if (!uDstAccountID)
|
||||
{
|
||||
WriteLog (lsINFO, PaymentTransactor) << "Payment: Malformed transaction: Payment destination account not specified.";
|
||||
|
||||
return temDST_NEEDED;
|
||||
}
|
||||
else if (bMax && !saMaxAmount.isPositive ())
|
||||
{
|
||||
WriteLog (lsINFO, PaymentTransactor) << "Payment: Malformed transaction: bad max amount: " << saMaxAmount.getFullText ();
|
||||
|
||||
return temBAD_AMOUNT;
|
||||
}
|
||||
else if (!saDstAmount.isPositive ())
|
||||
{
|
||||
WriteLog (lsINFO, PaymentTransactor) << "Payment: Malformed transaction: bad dst amount: " << saDstAmount.getFullText ();
|
||||
|
||||
return temBAD_AMOUNT;
|
||||
}
|
||||
else if (CURRENCY_BAD == uSrcCurrency || CURRENCY_BAD == uDstCurrency)
|
||||
{
|
||||
WriteLog (lsINFO, PaymentTransactor) << "Payment: Malformed transaction: Bad currency.";
|
||||
|
||||
return temBAD_CURRENCY;
|
||||
}
|
||||
else if (mTxnAccountID == uDstAccountID && uSrcCurrency == uDstCurrency && !bPaths)
|
||||
{
|
||||
WriteLog (lsINFO, PaymentTransactor) << boost::str (boost::format ("Payment: Malformed transaction: Redundant transaction: src=%s, dst=%s, src_cur=%s, dst_cur=%s")
|
||||
% mTxnAccountID.ToString ()
|
||||
% uDstAccountID.ToString ()
|
||||
% uSrcCurrency.ToString ()
|
||||
% uDstCurrency.ToString ());
|
||||
|
||||
return temREDUNDANT;
|
||||
}
|
||||
else if (bMax && saMaxAmount == saDstAmount && saMaxAmount.getCurrency () == saDstAmount.getCurrency ())
|
||||
{
|
||||
WriteLog (lsINFO, PaymentTransactor) << "Payment: Malformed transaction: Redundant SendMax.";
|
||||
|
||||
return temREDUNDANT_SEND_MAX;
|
||||
}
|
||||
else if (bXRPDirect && bMax)
|
||||
{
|
||||
WriteLog (lsINFO, PaymentTransactor) << "Payment: Malformed transaction: SendMax specified for XRP to XRP.";
|
||||
|
||||
return temBAD_SEND_XRP_MAX;
|
||||
}
|
||||
else if (bXRPDirect && bPaths)
|
||||
{
|
||||
WriteLog (lsINFO, PaymentTransactor) << "Payment: Malformed transaction: Paths specified for XRP to XRP.";
|
||||
|
||||
return temBAD_SEND_XRP_PATHS;
|
||||
}
|
||||
else if (bXRPDirect && bPartialPayment)
|
||||
{
|
||||
WriteLog (lsINFO, PaymentTransactor) << "Payment: Malformed transaction: Partial payment specified for XRP to XRP.";
|
||||
|
||||
return temBAD_SEND_XRP_PARTIAL;
|
||||
}
|
||||
else if (bXRPDirect && bLimitQuality)
|
||||
{
|
||||
WriteLog (lsINFO, PaymentTransactor) << "Payment: Malformed transaction: Limit quality specified for XRP to XRP.";
|
||||
|
||||
return temBAD_SEND_XRP_LIMIT;
|
||||
}
|
||||
else if (bXRPDirect && bNoRippleDirect)
|
||||
{
|
||||
WriteLog (lsINFO, PaymentTransactor) << "Payment: Malformed transaction: No ripple direct specified for XRP to XRP.";
|
||||
|
||||
return temBAD_SEND_XRP_NO_DIRECT;
|
||||
}
|
||||
|
||||
SLE::pointer sleDst = mEngine->entryCache (ltACCOUNT_ROOT, Ledger::getAccountRootIndex (uDstAccountID));
|
||||
|
||||
if (!sleDst)
|
||||
{
|
||||
// Destination account does not exist.
|
||||
|
||||
if (!saDstAmount.isNative ())
|
||||
{
|
||||
WriteLog (lsINFO, PaymentTransactor) << "Payment: Delay transaction: Destination account does not exist.";
|
||||
|
||||
// Another transaction could create the account and then this transaction would succeed.
|
||||
return tecNO_DST;
|
||||
}
|
||||
else if (isSetBit (mParams, tapOPEN_LEDGER) && bPartialPayment)
|
||||
{
|
||||
WriteLog (lsINFO, PaymentTransactor) << "Payment: Delay transaction: Partial payment not allowed to create account.";
|
||||
// Make retry work smaller, by rejecting this.
|
||||
|
||||
// Another transaction could create the account and then this transaction would succeed.
|
||||
return telNO_DST_PARTIAL;
|
||||
}
|
||||
else if (saDstAmount.getNValue () < mEngine->getLedger ()->getReserve (0)) // Reserve is not scaled by load.
|
||||
{
|
||||
WriteLog (lsINFO, PaymentTransactor) << "Payment: Delay transaction: Destination account does not exist. Insufficent payment to create account.";
|
||||
|
||||
// Another transaction could create the account and then this transaction would succeed.
|
||||
return tecNO_DST_INSUF_XRP;
|
||||
}
|
||||
|
||||
// Create the account.
|
||||
sleDst = mEngine->entryCreate (ltACCOUNT_ROOT, Ledger::getAccountRootIndex (uDstAccountID));
|
||||
|
||||
sleDst->setFieldAccount (sfAccount, uDstAccountID);
|
||||
sleDst->setFieldU32 (sfSequence, 1);
|
||||
}
|
||||
else if ((sleDst->getFlags () & lsfRequireDestTag) && !mTxn.isFieldPresent (sfDestinationTag))
|
||||
{
|
||||
WriteLog (lsINFO, PaymentTransactor) << "Payment: Malformed transaction: DestinationTag required.";
|
||||
|
||||
return tefDST_TAG_NEEDED;
|
||||
}
|
||||
else
|
||||
{
|
||||
mEngine->entryModify (sleDst);
|
||||
}
|
||||
|
||||
TER terResult;
|
||||
// XXX Should bMax be sufficient to imply ripple?
|
||||
const bool bRipple = bPaths || bMax || !saDstAmount.isNative ();
|
||||
|
||||
if (bRipple)
|
||||
{
|
||||
// Ripple payment
|
||||
|
||||
STPathSet spsPaths = mTxn.getFieldPathSet (sfPaths);
|
||||
std::vector<PathState::pointer> vpsExpanded;
|
||||
STAmount saMaxAmountAct;
|
||||
STAmount saDstAmountAct;
|
||||
|
||||
try
|
||||
{
|
||||
terResult = isSetBit (mParams, tapOPEN_LEDGER) && spsPaths.size () > RIPPLE_PATHS_MAX
|
||||
? telBAD_PATH_COUNT // Too many paths for proposed ledger.
|
||||
: RippleCalc::rippleCalc (
|
||||
mEngine->getNodes (),
|
||||
saMaxAmountAct,
|
||||
saDstAmountAct,
|
||||
vpsExpanded,
|
||||
saMaxAmount,
|
||||
saDstAmount,
|
||||
uDstAccountID,
|
||||
mTxnAccountID,
|
||||
spsPaths,
|
||||
bPartialPayment,
|
||||
bLimitQuality,
|
||||
bNoRippleDirect, // Always compute for finalizing ledger.
|
||||
false, // Not standalone, delete unfundeds.
|
||||
isSetBit (mParams, tapOPEN_LEDGER));
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
WriteLog (lsINFO, PaymentTransactor) << "Payment: Caught throw: " << e.what ();
|
||||
|
||||
terResult = tefEXCEPTION;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Direct XRP payment.
|
||||
|
||||
const uint32 uOwnerCount = mTxnAccount->getFieldU32 (sfOwnerCount);
|
||||
const uint64 uReserve = mEngine->getLedger ()->getReserve (uOwnerCount);
|
||||
|
||||
// Make sure have enough reserve to send. Allow final spend to use reserve for fee.
|
||||
if (mPriorBalance < saDstAmount + uReserve) // Reserve is not scaled by fee.
|
||||
{
|
||||
// Vote no. However, transaction might succeed, if applied in a different order.
|
||||
WriteLog (lsINFO, PaymentTransactor) << "";
|
||||
WriteLog (lsINFO, PaymentTransactor) << boost::str (boost::format ("Payment: Delay transaction: Insufficient funds: %s / %s (%d)")
|
||||
% mPriorBalance.getText () % (saDstAmount + uReserve).getText () % uReserve);
|
||||
|
||||
terResult = tecUNFUNDED_PAYMENT;
|
||||
}
|
||||
else
|
||||
{
|
||||
mTxnAccount->setFieldAmount (sfBalance, mSourceBalance - saDstAmount);
|
||||
sleDst->setFieldAmount (sfBalance, sleDst->getFieldAmount (sfBalance) + saDstAmount);
|
||||
|
||||
// re-arm the password change fee if we can and need to
|
||||
if ((sleDst->getFlags () & lsfPasswordSpent))
|
||||
sleDst->clearFlag (lsfPasswordSpent);
|
||||
|
||||
terResult = tesSUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
std::string strToken;
|
||||
std::string strHuman;
|
||||
|
||||
if (transResultInfo (terResult, strToken, strHuman))
|
||||
{
|
||||
WriteLog (lsINFO, PaymentTransactor) << boost::str (boost::format ("Payment: %s: %s") % strToken % strHuman);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert (false);
|
||||
}
|
||||
|
||||
return terResult;
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
@@ -1,19 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef __PAYMENTTRANSACTOR__
|
||||
#define __PAYMENTTRANSACTOR__
|
||||
|
||||
class PaymentTransactor : public Transactor
|
||||
{
|
||||
public:
|
||||
PaymentTransactor (const SerializedTransaction& txn, TransactionEngineParams params, TransactionEngine* engine) : Transactor (txn, params, engine) {}
|
||||
|
||||
TER doApply ();
|
||||
};
|
||||
#endif
|
||||
|
||||
// vim:ts=4
|
||||
@@ -1,86 +0,0 @@
|
||||
#ifndef RIPPLE_PEER_H
|
||||
#define RIPPLE_PEER_H
|
||||
|
||||
typedef std::pair <std::string, int> ipPort;
|
||||
|
||||
class Peer : public boost::enable_shared_from_this <Peer>
|
||||
{
|
||||
public:
|
||||
typedef boost::shared_ptr<Peer> pointer;
|
||||
typedef const boost::shared_ptr<Peer>& ref;
|
||||
|
||||
static int const psbGotHello = 0;
|
||||
static int const psbSentHello = 1;
|
||||
static int const psbInMap = 2;
|
||||
static int const psbTrusted = 3;
|
||||
static int const psbNoLedgers = 4;
|
||||
static int const psbNoTransactions = 5;
|
||||
static int const psbDownLevel = 6;
|
||||
|
||||
public:
|
||||
static pointer New (boost::asio::io_service& io_service,
|
||||
boost::asio::ssl::context& ctx,
|
||||
uint64 id,
|
||||
bool inbound);
|
||||
|
||||
// VFALCO: TODO see if this and below can be private
|
||||
virtual void handleConnect (const boost::system::error_code& error,
|
||||
boost::asio::ip::tcp::resolver::iterator it) = 0;
|
||||
|
||||
virtual std::string& getIP () = 0;
|
||||
|
||||
virtual std::string getDisplayName () = 0;
|
||||
|
||||
virtual int getPort () = 0;
|
||||
|
||||
virtual void setIpPort (const std::string& strIP, int iPort) = 0;
|
||||
|
||||
virtual boost::asio::ssl::stream<boost::asio::ip::tcp::socket>::lowest_layer_type& getSocket () = 0;
|
||||
|
||||
virtual void connect (const std::string& strIp, int iPort) = 0;
|
||||
|
||||
virtual void connected (const boost::system::error_code& error) = 0;
|
||||
|
||||
virtual void detach (const char*, bool onIOStrand) = 0;
|
||||
|
||||
//virtual bool samePeer (Peer::ref p) = 0;
|
||||
//virtual bool samePeer (const Peer& p) = 0;
|
||||
|
||||
virtual void sendPacket (const PackedMessage::pointer& packet, bool onStrand) = 0;
|
||||
|
||||
virtual void sendGetPeers () = 0;
|
||||
|
||||
virtual void punishPeer (LoadType) = 0;
|
||||
|
||||
// VFALCO: NOTE, what's with this odd parameter passing? Why the static member?
|
||||
static void punishPeer (const boost::weak_ptr<Peer>&, LoadType);
|
||||
|
||||
virtual Json::Value getJson () = 0;
|
||||
|
||||
virtual bool isConnected () const = 0;
|
||||
|
||||
virtual bool isInCluster () const = 0;
|
||||
|
||||
virtual bool isInbound () const = 0;
|
||||
|
||||
virtual bool isOutbound () const = 0;
|
||||
|
||||
virtual const uint256& getClosedLedgerHash () const = 0;
|
||||
|
||||
virtual bool hasLedger (const uint256& hash, uint32 seq) const = 0;
|
||||
|
||||
virtual bool hasTxSet (const uint256& hash) const = 0;
|
||||
|
||||
virtual uint64 getPeerId () const = 0;
|
||||
|
||||
virtual const RippleAddress& getNodePublic () const = 0;
|
||||
|
||||
virtual void cycleStatus () = 0;
|
||||
|
||||
virtual bool hasProto (int version) = 0;
|
||||
|
||||
virtual bool hasRange (uint32 uMin, uint32 uMax) = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
// vim:ts=4
|
||||
@@ -1,107 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
SETUP_LOG (PeerDoor)
|
||||
|
||||
class PeerDoorImp : public PeerDoor, LeakChecked <PeerDoorImp>
|
||||
{
|
||||
public:
|
||||
PeerDoorImp (std::string const& ip,
|
||||
int port,
|
||||
std::string const& sslCiphers,
|
||||
boost::asio::io_service& io_service)
|
||||
: mAcceptor (
|
||||
io_service,
|
||||
boost::asio::ip::tcp::endpoint (boost::asio::ip::address ().from_string (ip.empty () ? "0.0.0.0" : ip),
|
||||
port))
|
||||
, mCtx (boost::asio::ssl::context::sslv23)
|
||||
, mDelayTimer (io_service)
|
||||
{
|
||||
mCtx.set_options (
|
||||
boost::asio::ssl::context::default_workarounds |
|
||||
boost::asio::ssl::context::no_sslv2 |
|
||||
boost::asio::ssl::context::single_dh_use);
|
||||
|
||||
SSL_CTX_set_tmp_dh_callback (mCtx.native_handle (), handleTmpDh);
|
||||
|
||||
if (SSL_CTX_set_cipher_list (mCtx.native_handle (), sslCiphers.c_str ()) != 1)
|
||||
std::runtime_error ("Error setting cipher list (no valid ciphers).");
|
||||
|
||||
if (! ip.empty () && port != 0)
|
||||
{
|
||||
Log (lsINFO) << "Peer port: " << ip << " " << port;
|
||||
startListening ();
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
boost::asio::ssl::context& getSSLContext ()
|
||||
{
|
||||
return mCtx;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void startListening ()
|
||||
{
|
||||
Peer::pointer new_connection = Peer::New (
|
||||
mAcceptor.get_io_service (),
|
||||
mCtx,
|
||||
getApp().getPeers ().assignPeerId (),
|
||||
true);
|
||||
|
||||
mAcceptor.async_accept (new_connection->getSocket (),
|
||||
boost::bind (&PeerDoorImp::handleConnect, this, new_connection,
|
||||
boost::asio::placeholders::error));
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void handleConnect (Peer::pointer new_connection,
|
||||
const boost::system::error_code& error)
|
||||
{
|
||||
bool delay = false;
|
||||
|
||||
if (!error)
|
||||
{
|
||||
new_connection->connected (error);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (error == boost::system::errc::too_many_files_open)
|
||||
delay = true;
|
||||
|
||||
WriteLog (lsERROR, PeerDoor) << error;
|
||||
}
|
||||
|
||||
if (delay)
|
||||
{
|
||||
mDelayTimer.expires_from_now (boost::posix_time::milliseconds (500));
|
||||
mDelayTimer.async_wait (boost::bind (&PeerDoorImp::startListening, this));
|
||||
}
|
||||
else
|
||||
{
|
||||
startListening ();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
boost::asio::ip::tcp::acceptor mAcceptor;
|
||||
boost::asio::ssl::context mCtx;
|
||||
boost::asio::deadline_timer mDelayTimer;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
PeerDoor* PeerDoor::New (
|
||||
std::string const& ip,
|
||||
int port,
|
||||
std::string const& sslCiphers,
|
||||
boost::asio::io_service& io_service)
|
||||
{
|
||||
return new PeerDoorImp (ip, port, sslCiphers, io_service);
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_PEERDOOR_H_INCLUDED
|
||||
#define RIPPLE_PEERDOOR_H_INCLUDED
|
||||
|
||||
/** Handles incoming connections from peers.
|
||||
*/
|
||||
class PeerDoor : LeakChecked <PeerDoor>
|
||||
{
|
||||
public:
|
||||
virtual ~PeerDoor () { }
|
||||
|
||||
static PeerDoor* New (
|
||||
std::string const& ip,
|
||||
int port,
|
||||
std::string const& sslCiphers,
|
||||
boost::asio::io_service& io_service);
|
||||
|
||||
virtual boost::asio::ssl::context& getSSLContext () = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,36 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_RPC_H_INCLUDED
|
||||
#define RIPPLE_RPC_H_INCLUDED
|
||||
|
||||
// VFALCO TODO Wrap these up into a class. It looks like they just do some
|
||||
// convenience packaging of JSON data from the pieces. It looks
|
||||
// Ripple client protocol-specific.
|
||||
//
|
||||
extern std::string JSONRPCRequest (const std::string& strMethod, const Json::Value& params,
|
||||
const Json::Value& id);
|
||||
|
||||
extern std::string JSONRPCReply (const Json::Value& result, const Json::Value& error, const Json::Value& id);
|
||||
|
||||
extern Json::Value JSONRPCError (int code, const std::string& message);
|
||||
|
||||
extern std::string createHTTPPost (const std::string& strHost, const std::string& strPath, const std::string& strMsg,
|
||||
const std::map<std::string, std::string>& mapRequestHeaders);
|
||||
|
||||
extern std::string HTTPReply (int nStatus, const std::string& strMsg);
|
||||
|
||||
// VFALCO TODO Create a HTTPHeaders class with a nice interface instead of the std::map
|
||||
//
|
||||
extern bool HTTPAuthorized (std::map <std::string, std::string> const& mapHeaders);
|
||||
|
||||
// VFALCO NOTE This one looks like it does some sort of stream i/o
|
||||
//
|
||||
extern int ReadHTTP (std::basic_istream<char>& stream,
|
||||
std::map<std::string, std::string>& mapHeadersRet,
|
||||
std::string& strMessageRet);
|
||||
|
||||
#endif
|
||||
@@ -1,101 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
SETUP_LOG (RPCDoor)
|
||||
|
||||
RPCDoor::RPCDoor (boost::asio::io_service& io_service, RPCServer::Handler& handler)
|
||||
: m_rpcServerHandler (handler)
|
||||
, mAcceptor (io_service,
|
||||
boost::asio::ip::tcp::endpoint (boost::asio::ip::address::from_string (theConfig.getRpcIP ()), theConfig.getRpcPort ()))
|
||||
, mDelayTimer (io_service)
|
||||
, mSSLContext (boost::asio::ssl::context::sslv23)
|
||||
{
|
||||
WriteLog (lsINFO, RPCDoor) << "RPC port: " << theConfig.getRpcAddress().toRawUTF8() << " allow remote: " << theConfig.RPC_ALLOW_REMOTE;
|
||||
|
||||
if (theConfig.RPC_SECURE != 0)
|
||||
{
|
||||
// VFALCO TODO This could be a method of theConfig
|
||||
//
|
||||
basio::SslContext::initializeFromFile (
|
||||
mSSLContext,
|
||||
theConfig.RPC_SSL_KEY,
|
||||
theConfig.RPC_SSL_CERT,
|
||||
theConfig.RPC_SSL_CHAIN);
|
||||
}
|
||||
|
||||
startListening ();
|
||||
}
|
||||
|
||||
RPCDoor::~RPCDoor ()
|
||||
{
|
||||
WriteLog (lsINFO, RPCDoor) << "RPC port: " << theConfig.getRpcAddress().toRawUTF8() << " allow remote: " << theConfig.RPC_ALLOW_REMOTE;
|
||||
}
|
||||
|
||||
void RPCDoor::startListening ()
|
||||
{
|
||||
RPCServer::pointer new_connection = RPCServer::New (mAcceptor.get_io_service (), mSSLContext, m_rpcServerHandler);
|
||||
mAcceptor.set_option (boost::asio::ip::tcp::acceptor::reuse_address (true));
|
||||
|
||||
mAcceptor.async_accept (new_connection->getRawSocket (),
|
||||
boost::bind (&RPCDoor::handleConnect, this, new_connection,
|
||||
boost::asio::placeholders::error));
|
||||
}
|
||||
|
||||
bool RPCDoor::isClientAllowed (const std::string& ip)
|
||||
{
|
||||
if (theConfig.RPC_ALLOW_REMOTE)
|
||||
return true;
|
||||
|
||||
// VFALCO TODO Represent ip addresses as a structure. Use isLoopback() member here
|
||||
//
|
||||
if (ip == "127.0.0.1")
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void RPCDoor::handleConnect (RPCServer::pointer new_connection, const boost::system::error_code& error)
|
||||
{
|
||||
bool delay = false;
|
||||
|
||||
if (!error)
|
||||
{
|
||||
// Restrict callers by IP
|
||||
try
|
||||
{
|
||||
if (! isClientAllowed (new_connection->getRemoteAddressText ()))
|
||||
{
|
||||
startListening ();
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// client may have disconnected
|
||||
startListening ();
|
||||
return;
|
||||
}
|
||||
|
||||
new_connection->getSocket ().async_handshake (AutoSocket::ssl_socket::server,
|
||||
boost::bind (&RPCServer::connected, new_connection));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (error == boost::system::errc::too_many_files_open)
|
||||
delay = true;
|
||||
|
||||
WriteLog (lsINFO, RPCDoor) << "RPCDoor::handleConnect Error: " << error;
|
||||
}
|
||||
|
||||
if (delay)
|
||||
{
|
||||
mDelayTimer.expires_from_now (boost::posix_time::milliseconds (1000));
|
||||
mDelayTimer.async_wait (boost::bind (&RPCDoor::startListening, this));
|
||||
}
|
||||
else
|
||||
startListening ();
|
||||
}
|
||||
// vim:ts=4
|
||||
@@ -1,35 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_RPCDOOR_H
|
||||
#define RIPPLE_RPCDOOR_H
|
||||
|
||||
/*
|
||||
Handles incoming connections from people making RPC Requests
|
||||
*/
|
||||
|
||||
class RPCDoor : LeakChecked <RPCDoor>
|
||||
{
|
||||
public:
|
||||
explicit RPCDoor (
|
||||
boost::asio::io_service& io_service,
|
||||
RPCServer::Handler& handler);
|
||||
~RPCDoor ();
|
||||
|
||||
private:
|
||||
RPCServer::Handler& m_rpcServerHandler;
|
||||
boost::asio::ip::tcp::acceptor mAcceptor;
|
||||
boost::asio::deadline_timer mDelayTimer;
|
||||
boost::asio::ssl::context mSSLContext;
|
||||
|
||||
void startListening ();
|
||||
void handleConnect (RPCServer::pointer new_connection,
|
||||
const boost::system::error_code& error);
|
||||
|
||||
bool isClientAllowed (const std::string& ip);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,106 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
struct RPCErr; // for Log
|
||||
|
||||
SETUP_LOG (RPCErr)
|
||||
|
||||
Json::Value rpcError (int iError, Json::Value jvResult)
|
||||
{
|
||||
static struct
|
||||
{
|
||||
int iError;
|
||||
const char* pToken;
|
||||
const char* pMessage;
|
||||
} errorInfoA[] =
|
||||
{
|
||||
{ rpcACT_BITCOIN, "actBitcoin", "Account is bitcoin address." },
|
||||
{ rpcACT_EXISTS, "actExists", "Account already exists." },
|
||||
{ rpcACT_MALFORMED, "actMalformed", "Account malformed." },
|
||||
{ rpcACT_NOT_FOUND, "actNotFound", "Account not found." },
|
||||
{ rpcBAD_BLOB, "badBlob", "Blob must be a non-empty hex string." },
|
||||
{ rpcBAD_FEATURE, "badFeature", "Feature unknown or invalid." },
|
||||
{ rpcBAD_ISSUER, "badIssuer", "Issuer account malformed." },
|
||||
{ rpcBAD_MARKET, "badMarket", "No such market." },
|
||||
{ rpcBAD_SECRET, "badSecret", "Secret does not match account." },
|
||||
{ rpcBAD_SEED, "badSeed", "Disallowed seed." },
|
||||
{ rpcBAD_SYNTAX, "badSyntax", "Syntax error." },
|
||||
{ rpcCOMMAND_MISSING, "commandMissing", "Missing command entry." },
|
||||
{ rpcDST_ACT_MALFORMED, "dstActMalformed", "Destination account is malformed." },
|
||||
{ rpcDST_ACT_MISSING, "dstActMissing", "Destination account does not exist." },
|
||||
{ rpcDST_AMT_MALFORMED, "dstAmtMalformed", "Destination amount/currency/issuer is malformed." },
|
||||
{ rpcDST_ISR_MALFORMED, "dstIsrMalformed", "Destination issuer is malformed." },
|
||||
{ rpcFORBIDDEN, "forbidden", "Bad credentials." },
|
||||
{ rpcFAIL_GEN_DECRPYT, "failGenDecrypt", "Failed to decrypt generator." },
|
||||
{ rpcGETS_ACT_MALFORMED, "getsActMalformed", "Gets account malformed." },
|
||||
{ rpcGETS_AMT_MALFORMED, "getsAmtMalformed", "Gets amount malformed." },
|
||||
{ rpcHOST_IP_MALFORMED, "hostIpMalformed", "Host IP is malformed." },
|
||||
{ rpcINSUF_FUNDS, "insufFunds", "Insufficient funds." },
|
||||
{ rpcINTERNAL, "internal", "Internal error." },
|
||||
{ rpcINVALID_PARAMS, "invalidParams", "Invalid parameters." },
|
||||
{ rpcJSON_RPC, "json_rpc", "JSON-RPC transport error." },
|
||||
{ rpcLGR_IDXS_INVALID, "lgrIdxsInvalid", "Ledger indexes invalid." },
|
||||
{ rpcLGR_IDX_MALFORMED, "lgrIdxMalformed", "Ledger index malformed." },
|
||||
{ rpcLGR_NOT_FOUND, "lgrNotFound", "Ledger not found." },
|
||||
{ rpcNICKNAME_MALFORMED, "nicknameMalformed", "Nickname is malformed." },
|
||||
{ rpcNICKNAME_MISSING, "nicknameMissing", "Nickname does not exist." },
|
||||
{ rpcNICKNAME_PERM, "nicknamePerm", "Account does not control nickname." },
|
||||
{ rpcNOT_IMPL, "notImpl", "Not implemented." },
|
||||
{ rpcNO_ACCOUNT, "noAccount", "No such account." },
|
||||
{ rpcNO_CLOSED, "noClosed", "Closed ledger is unavailable." },
|
||||
{ rpcNO_CURRENT, "noCurrent", "Current ledger is unavailable." },
|
||||
{ rpcNO_EVENTS, "noEvents", "Current transport does not support events." },
|
||||
{ rpcNO_GEN_DECRPYT, "noGenDectypt", "Password failed to decrypt master public generator." },
|
||||
{ rpcNO_NETWORK, "noNetwork", "Network not available." },
|
||||
{ rpcNO_PATH, "noPath", "Unable to find a ripple path." },
|
||||
{ rpcNO_PERMISSION, "noPermission", "You don't have permission for this command." },
|
||||
{ rpcNO_PF_REQUEST, "noPathRequest", "No pathfinding request in progress." },
|
||||
{ rpcNOT_STANDALONE, "notStandAlone", "Operation valid in debug mode only." },
|
||||
{ rpcNOT_SUPPORTED, "notSupported", "Operation not supported." },
|
||||
{ rpcPASSWD_CHANGED, "passwdChanged", "Wrong key, password changed." },
|
||||
{ rpcPAYS_ACT_MALFORMED, "paysActMalformed", "Pays account malformed." },
|
||||
{ rpcPAYS_AMT_MALFORMED, "paysAmtMalformed", "Pays amount malformed." },
|
||||
{ rpcPORT_MALFORMED, "portMalformed", "Port is malformed." },
|
||||
{ rpcPUBLIC_MALFORMED, "publicMalformed", "Public key is malformed." },
|
||||
{ rpcQUALITY_MALFORMED, "qualityMalformed", "Quality malformed." },
|
||||
{ rpcSRC_ACT_MALFORMED, "srcActMalformed", "Source account is malformed." },
|
||||
{ rpcSRC_ACT_MISSING, "srcActMissing", "Source account not provided." },
|
||||
{ rpcSRC_ACT_NOT_FOUND, "srcActNotFound", "Source account not found." },
|
||||
{ rpcSRC_AMT_MALFORMED, "srcAmtMalformed", "Source amount/currency/issuer is malformed." },
|
||||
{ rpcSRC_CUR_MALFORMED, "srcCurMalformed", "Source currency is malformed." },
|
||||
{ rpcSRC_ISR_MALFORMED, "srcIsrMalformed", "Source issuer is malformed." },
|
||||
{ rpcSRC_UNCLAIMED, "srcUnclaimed", "Source account is not claimed." },
|
||||
{ rpcTXN_NOT_FOUND, "txnNotFound", "Transaction not found." },
|
||||
{ rpcUNKNOWN_COMMAND, "unknownCmd", "Unknown method." },
|
||||
{ rpcWRONG_SEED, "wrongSeed", "The regular key does not point as the master key." },
|
||||
{ rpcTOO_BUSY, "tooBusy", "The server is too busy to help you now." },
|
||||
{ rpcSLOW_DOWN, "slowDown", "You are placing too much load on the server." },
|
||||
};
|
||||
|
||||
int i;
|
||||
|
||||
for (i = NUMBER (errorInfoA); i-- && errorInfoA[i].iError != iError;)
|
||||
;
|
||||
|
||||
jvResult["error"] = i >= 0 ? errorInfoA[i].pToken : lexical_cast_i (iError);
|
||||
jvResult["error_message"] = i >= 0 ? errorInfoA[i].pMessage : lexical_cast_i (iError);
|
||||
jvResult["error_code"] = iError;
|
||||
|
||||
if (i >= 0)
|
||||
{
|
||||
WriteLog (lsDEBUG, RPCErr) << "rpcError: "
|
||||
<< errorInfoA[i].pToken << ": " << errorInfoA[i].pMessage << std::endl;
|
||||
}
|
||||
|
||||
return jvResult;
|
||||
}
|
||||
|
||||
bool isRpcError (Json::Value jvResult)
|
||||
{
|
||||
return jvResult.isObject () && jvResult.isMember ("error");
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
@@ -1,97 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef __RPCERR__
|
||||
#define __RPCERR__
|
||||
|
||||
enum
|
||||
{
|
||||
rpcSUCCESS = 0,
|
||||
rpcBAD_SYNTAX, // Must be 1 to print usage to command line.
|
||||
rpcJSON_RPC,
|
||||
rpcFORBIDDEN,
|
||||
|
||||
// Error numbers beyond this line are not stable between versions.
|
||||
// Programs should use error tokens.
|
||||
|
||||
// Misc failure
|
||||
rpcLOAD_FAILED,
|
||||
rpcNO_PERMISSION,
|
||||
rpcNO_EVENTS,
|
||||
rpcNOT_STANDALONE,
|
||||
rpcTOO_BUSY,
|
||||
rpcSLOW_DOWN,
|
||||
|
||||
// Networking
|
||||
rpcNO_CLOSED,
|
||||
rpcNO_CURRENT,
|
||||
rpcNO_NETWORK,
|
||||
|
||||
// Ledger state
|
||||
rpcACT_EXISTS,
|
||||
rpcACT_NOT_FOUND,
|
||||
rpcINSUF_FUNDS,
|
||||
rpcLGR_NOT_FOUND,
|
||||
rpcNICKNAME_MISSING,
|
||||
rpcNO_ACCOUNT,
|
||||
rpcNO_PATH,
|
||||
rpcPASSWD_CHANGED,
|
||||
rpcSRC_MISSING,
|
||||
rpcSRC_UNCLAIMED,
|
||||
rpcTXN_NOT_FOUND,
|
||||
rpcWRONG_SEED,
|
||||
|
||||
// Malformed command
|
||||
rpcINVALID_PARAMS,
|
||||
rpcUNKNOWN_COMMAND,
|
||||
rpcNO_PF_REQUEST,
|
||||
|
||||
// Bad parameter
|
||||
rpcACT_BITCOIN,
|
||||
rpcACT_MALFORMED,
|
||||
rpcQUALITY_MALFORMED,
|
||||
rpcBAD_BLOB,
|
||||
rpcBAD_FEATURE,
|
||||
rpcBAD_ISSUER,
|
||||
rpcBAD_MARKET,
|
||||
rpcBAD_SECRET,
|
||||
rpcBAD_SEED,
|
||||
rpcCOMMAND_MISSING,
|
||||
rpcDST_ACT_MALFORMED,
|
||||
rpcDST_ACT_MISSING,
|
||||
rpcDST_AMT_MALFORMED,
|
||||
rpcDST_ISR_MALFORMED,
|
||||
rpcGETS_ACT_MALFORMED,
|
||||
rpcGETS_AMT_MALFORMED,
|
||||
rpcHOST_IP_MALFORMED,
|
||||
rpcLGR_IDXS_INVALID,
|
||||
rpcLGR_IDX_MALFORMED,
|
||||
rpcNICKNAME_MALFORMED,
|
||||
rpcNICKNAME_PERM,
|
||||
rpcPAYS_ACT_MALFORMED,
|
||||
rpcPAYS_AMT_MALFORMED,
|
||||
rpcPORT_MALFORMED,
|
||||
rpcPUBLIC_MALFORMED,
|
||||
rpcSRC_ACT_MALFORMED,
|
||||
rpcSRC_ACT_MISSING,
|
||||
rpcSRC_ACT_NOT_FOUND,
|
||||
rpcSRC_AMT_MALFORMED,
|
||||
rpcSRC_CUR_MALFORMED,
|
||||
rpcSRC_ISR_MALFORMED,
|
||||
|
||||
// Internal error (should never happen)
|
||||
rpcINTERNAL, // Generic internal error.
|
||||
rpcFAIL_GEN_DECRPYT,
|
||||
rpcNOT_IMPL,
|
||||
rpcNOT_SUPPORTED,
|
||||
rpcNO_GEN_DECRPYT,
|
||||
};
|
||||
|
||||
bool isRpcError (Json::Value jvResult);
|
||||
Json::Value rpcError (int iError, Json::Value jvResult = Json::Value (Json::objectValue));
|
||||
|
||||
#endif
|
||||
// vim:ts=4
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,191 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef __RPCHANDLER__
|
||||
#define __RPCHANDLER__
|
||||
|
||||
#define LEDGER_CURRENT -1
|
||||
#define LEDGER_CLOSED -2
|
||||
#define LEDGER_VALIDATED -3
|
||||
|
||||
// used by the RPCServer or WSDoor to carry out these RPC commands
|
||||
class NetworkOPs;
|
||||
class InfoSub;
|
||||
|
||||
// VFALCO TODO Refactor to abstract interface IRPCHandler
|
||||
//
|
||||
class RPCHandler
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
GUEST,
|
||||
USER,
|
||||
ADMIN,
|
||||
FORBID
|
||||
};
|
||||
|
||||
explicit RPCHandler (NetworkOPs* netOps);
|
||||
|
||||
RPCHandler (NetworkOPs* netOps, InfoSub::pointer infoSub);
|
||||
|
||||
Json::Value doCommand (const Json::Value& jvRequest, int role, LoadType* loadType);
|
||||
|
||||
Json::Value doRpcCommand (const std::string& strCommand, Json::Value const& jvParams, int iRole, LoadType* loadType);
|
||||
|
||||
private:
|
||||
typedef Json::Value (RPCHandler::*doFuncPtr) (
|
||||
Json::Value params,
|
||||
LoadType* loadType,
|
||||
Application::ScopedLockType& MasterLockHolder);
|
||||
|
||||
// VFALCO TODO Document these and give the enumeration a label.
|
||||
enum
|
||||
{
|
||||
optNone = 0,
|
||||
optNetwork = 1, // Need network
|
||||
optCurrent = 2 + optNetwork, // Need current ledger
|
||||
optClosed = 4 + optNetwork, // Need closed ledger
|
||||
};
|
||||
|
||||
// Utilities
|
||||
|
||||
void addSubmitPath (Json::Value& txJSON);
|
||||
|
||||
boost::unordered_set <RippleAddress> parseAccountIds (const Json::Value& jvArray);
|
||||
|
||||
Json::Value transactionSign (Json::Value jvRequest, bool bSubmit, bool bFailHard, Application::ScopedLockType& mlh);
|
||||
|
||||
Json::Value lookupLedger (Json::Value jvRequest, Ledger::pointer& lpLedger);
|
||||
|
||||
Json::Value getMasterGenerator (
|
||||
Ledger::ref lrLedger,
|
||||
const RippleAddress& naRegularSeed,
|
||||
RippleAddress& naMasterGenerator);
|
||||
|
||||
Json::Value authorize (
|
||||
Ledger::ref lrLedger,
|
||||
const RippleAddress& naRegularSeed,
|
||||
const RippleAddress& naSrcAccountID,
|
||||
RippleAddress& naAccountPublic,
|
||||
RippleAddress& naAccountPrivate,
|
||||
STAmount& saSrcBalance,
|
||||
const STAmount& saFee,
|
||||
AccountState::pointer& asSrc,
|
||||
const RippleAddress& naVerifyGenerator);
|
||||
|
||||
Json::Value accounts (
|
||||
Ledger::ref lrLedger,
|
||||
const RippleAddress& naMasterGenerator);
|
||||
|
||||
Json::Value accountFromString (
|
||||
Ledger::ref lrLedger,
|
||||
RippleAddress& naAccount,
|
||||
bool& bIndex,
|
||||
const std::string& strIdent,
|
||||
const int iIndex,
|
||||
const bool bStrict);
|
||||
|
||||
Json::Value doAccountInfo (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doAccountLines (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doAccountOffers (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doAccountTransactions (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doBookOffers (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doConnect (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doConsensusInfo (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doFeature (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doFetchInfo (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doGetCounts (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doInternal (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doLedger (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doLedgerAccept (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doLedgerClosed (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doLedgerCurrent (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doLedgerEntry (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doLedgerHeader (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doLogLevel (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doLogRotate (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doNicknameInfo (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doOwnerInfo (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doPathFind (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doPeers (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doPing (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doProfile (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doProofCreate (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doProofSolve (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doProofVerify (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doRandom (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doRipplePathFind (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doSMS (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doServerInfo (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh); // for humans
|
||||
Json::Value doServerState (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh); // for machines
|
||||
Json::Value doSessionClose (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doSessionOpen (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doSign (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doStop (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doSubmit (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doSubscribe (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doTransactionEntry (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doTx (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doTxHistory (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doUnlAdd (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doUnlDelete (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doUnlFetch (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doUnlList (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doUnlLoad (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doUnlNetwork (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doUnlReset (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doUnlScore (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doUnsubscribe (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doValidationCreate (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doValidationSeed (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doWalletAccounts (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doWalletLock (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doWalletPropose (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doWalletSeed (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doWalletUnlock (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doWalletVerify (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
|
||||
#if ENABLE_INSECURE
|
||||
Json::Value doDataDelete (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doDataFetch (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doDataStore (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
Json::Value doLogin (Json::Value params, LoadType* loadType, Application::ScopedLockType& mlh);
|
||||
#endif
|
||||
|
||||
private:
|
||||
NetworkOPs* mNetOps;
|
||||
InfoSub::pointer mInfoSub;
|
||||
|
||||
// VFALCO TODO Create an enumeration for this.
|
||||
int mRole;
|
||||
};
|
||||
|
||||
class RPCInternalHandler
|
||||
{
|
||||
public:
|
||||
typedef Json::Value (*handler_t) (const Json::Value&);
|
||||
|
||||
public:
|
||||
RPCInternalHandler (const std::string& name, handler_t handler);
|
||||
static Json::Value runHandler (const std::string& name, const Json::Value& params);
|
||||
|
||||
private:
|
||||
// VFALCO TODO Replace with a singleton with a well defined interface and
|
||||
// a lock free stack (if necessary).
|
||||
//
|
||||
static RPCInternalHandler* sHeadHandler;
|
||||
|
||||
RPCInternalHandler* mNextHandler;
|
||||
std::string mName;
|
||||
handler_t mHandler;
|
||||
};
|
||||
|
||||
// VFALCO TODO tidy up this loose function
|
||||
int iAdminGet (const Json::Value& jvRequest, const std::string& strRemoteIp);
|
||||
|
||||
#endif
|
||||
// vim:ts=4
|
||||
@@ -1,119 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
SETUP_LOG (RPCSub)
|
||||
|
||||
RPCSub::RPCSub (const std::string& strUrl, const std::string& strUsername, const std::string& strPassword)
|
||||
: mUrl (strUrl), mSSL (false), mUsername (strUsername), mPassword (strPassword), mSending (false)
|
||||
{
|
||||
std::string strScheme;
|
||||
|
||||
if (!parseUrl (strUrl, strScheme, mIp, mPort, mPath))
|
||||
{
|
||||
throw std::runtime_error ("Failed to parse url.");
|
||||
}
|
||||
else if (strScheme == "https")
|
||||
{
|
||||
mSSL = true;
|
||||
}
|
||||
else if (strScheme != "http")
|
||||
{
|
||||
throw std::runtime_error ("Only http and https is supported.");
|
||||
}
|
||||
|
||||
mSeq = 1;
|
||||
|
||||
if (mPort < 0)
|
||||
mPort = mSSL ? 443 : 80;
|
||||
|
||||
WriteLog (lsINFO, RPCSub) << boost::str (boost::format ("callRPC sub: ip='%s' port=%d ssl=%d path='%s'")
|
||||
% mIp
|
||||
% mPort
|
||||
% mSSL
|
||||
% mPath);
|
||||
}
|
||||
|
||||
// XXX Could probably create a bunch of send jobs in a single get of the lock.
|
||||
void RPCSub::sendThread ()
|
||||
{
|
||||
Json::Value jvEvent;
|
||||
bool bSend;
|
||||
|
||||
do
|
||||
{
|
||||
{
|
||||
// Obtain the lock to manipulate the queue and change sending.
|
||||
boost::mutex::scoped_lock sl (mLockInfo);
|
||||
|
||||
if (mDeque.empty ())
|
||||
{
|
||||
mSending = false;
|
||||
bSend = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::pair<int, Json::Value> pEvent = mDeque.front ();
|
||||
|
||||
mDeque.pop_front ();
|
||||
|
||||
jvEvent = pEvent.second;
|
||||
jvEvent["seq"] = pEvent.first;
|
||||
|
||||
bSend = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Send outside of the lock.
|
||||
if (bSend)
|
||||
{
|
||||
// XXX Might not need this in a try.
|
||||
try
|
||||
{
|
||||
WriteLog (lsINFO, RPCSub) << boost::str (boost::format ("callRPC calling: %s") % mIp);
|
||||
|
||||
callRPC (
|
||||
getApp().getIOService (),
|
||||
mIp, mPort,
|
||||
mUsername, mPassword,
|
||||
mPath, "event",
|
||||
jvEvent,
|
||||
mSSL);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
WriteLog (lsINFO, RPCSub) << boost::str (boost::format ("callRPC exception: %s") % e.what ());
|
||||
}
|
||||
}
|
||||
}
|
||||
while (bSend);
|
||||
}
|
||||
|
||||
void RPCSub::send (const Json::Value& jvObj, bool broadcast)
|
||||
{
|
||||
boost::mutex::scoped_lock sl (mLockInfo);
|
||||
|
||||
if (RPC_EVENT_QUEUE_MAX == mDeque.size ())
|
||||
{
|
||||
// Drop the previous event.
|
||||
WriteLog (lsWARNING, RPCSub) << boost::str (boost::format ("callRPC drop"));
|
||||
mDeque.pop_back ();
|
||||
}
|
||||
|
||||
WriteLog (broadcast ? lsDEBUG : lsINFO, RPCSub) << boost::str (boost::format ("callRPC push: %s") % jvObj);
|
||||
|
||||
mDeque.push_back (std::make_pair (mSeq++, jvObj));
|
||||
|
||||
if (!mSending)
|
||||
{
|
||||
// Start a sending thread.
|
||||
mSending = true;
|
||||
|
||||
WriteLog (lsINFO, RPCSub) << boost::str (boost::format ("callRPC start"));
|
||||
boost::thread (BIND_TYPE (&RPCSub::sendThread, this)).detach ();
|
||||
}
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
@@ -1,65 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef __RPCSUB__
|
||||
#define __RPCSUB__
|
||||
|
||||
#define RPC_EVENT_QUEUE_MAX 32
|
||||
|
||||
// Subscription object for JSON-RPC
|
||||
class RPCSub
|
||||
: public InfoSub
|
||||
, LeakChecked <RPCSub>
|
||||
{
|
||||
public:
|
||||
typedef boost::shared_ptr<RPCSub> pointer;
|
||||
typedef const pointer& ref;
|
||||
|
||||
RPCSub (const std::string& strUrl, const std::string& strUsername, const std::string& strPassword);
|
||||
|
||||
virtual ~RPCSub ()
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
// Implement overridden functions from base class:
|
||||
void send (const Json::Value& jvObj, bool broadcast);
|
||||
|
||||
void setUsername (const std::string& strUsername)
|
||||
{
|
||||
boost::mutex::scoped_lock sl (mLockInfo);
|
||||
|
||||
mUsername = strUsername;
|
||||
}
|
||||
|
||||
void setPassword (const std::string& strPassword)
|
||||
{
|
||||
boost::mutex::scoped_lock sl (mLockInfo);
|
||||
|
||||
mPassword = strPassword;
|
||||
}
|
||||
|
||||
protected:
|
||||
void sendThread ();
|
||||
|
||||
private:
|
||||
std::string mUrl;
|
||||
std::string mIp;
|
||||
int mPort;
|
||||
bool mSSL;
|
||||
std::string mUsername;
|
||||
std::string mPassword;
|
||||
std::string mPath;
|
||||
|
||||
int mSeq; // Next id to allocate.
|
||||
|
||||
bool mSending; // Sending threead is active.
|
||||
|
||||
std::deque<std::pair<int, Json::Value> > mDeque;
|
||||
};
|
||||
|
||||
#endif
|
||||
// vim:ts=4
|
||||
@@ -1,58 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
SETUP_LOG (RegularKeySetTransactor)
|
||||
|
||||
uint64 RegularKeySetTransactor::calculateBaseFee ()
|
||||
{
|
||||
if ( mTxnAccount
|
||||
&& (! (mTxnAccount->getFlags () & lsfPasswordSpent))
|
||||
&& (mSigningPubKey.getAccountID () == mTxnAccountID))
|
||||
{
|
||||
// flag is armed and they signed with the right account
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Transactor::calculateBaseFee ();
|
||||
}
|
||||
|
||||
|
||||
TER RegularKeySetTransactor::doApply ()
|
||||
{
|
||||
Log::out() << "RegularKeySet>";
|
||||
|
||||
const uint32 uTxFlags = mTxn.getFlags ();
|
||||
|
||||
if (uTxFlags)
|
||||
{
|
||||
WriteLog (lsINFO, RegularKeySetTransactor) << "RegularKeySet: Malformed transaction: Invalid flags set.";
|
||||
|
||||
return temINVALID_FLAG;
|
||||
}
|
||||
|
||||
if (mFeeDue.isZero ())
|
||||
{
|
||||
mTxnAccount->setFlag (lsfPasswordSpent);
|
||||
}
|
||||
|
||||
if (mTxn.isFieldPresent (sfRegularKey))
|
||||
{
|
||||
uint160 uAuthKeyID = mTxn.getFieldAccount160 (sfRegularKey);
|
||||
mTxnAccount->setFieldAccount (sfRegularKey, uAuthKeyID);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mTxnAccount->isFlag (lsfDisableMaster))
|
||||
return tecMASTER_DISABLED;
|
||||
mTxnAccount->makeFieldAbsent (sfRegularKey);
|
||||
}
|
||||
|
||||
Log::out() << "RegularKeySet<";
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
@@ -1,20 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef REGULARKEYSETTRANSACTOR_H
|
||||
#define REGULARKEYSETTRANSACTOR_H
|
||||
|
||||
class RegularKeySetTransactor : public Transactor
|
||||
{
|
||||
uint64 calculateBaseFee ();
|
||||
public:
|
||||
RegularKeySetTransactor (const SerializedTransaction& txn, TransactionEngineParams params, TransactionEngine* engine) : Transactor (txn, params, engine) {}
|
||||
TER checkFee ();
|
||||
TER doApply ();
|
||||
};
|
||||
#endif
|
||||
|
||||
// vim:ts=4
|
||||
@@ -1,365 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
Transaction::Transaction (SerializedTransaction::ref sit, bool bValidate)
|
||||
: mInLedger (0), mStatus (INVALID), mResult (temUNCERTAIN), mTransaction (sit)
|
||||
{
|
||||
try
|
||||
{
|
||||
mFromPubKey.setAccountPublic (mTransaction->getSigningPubKey ());
|
||||
mTransactionID = mTransaction->getTransactionID ();
|
||||
mAccountFrom = mTransaction->getSourceAccount ();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bValidate || checkSign ())
|
||||
mStatus = NEW;
|
||||
}
|
||||
|
||||
Transaction::pointer Transaction::sharedTransaction (Blob const& vucTransaction, bool bValidate)
|
||||
{
|
||||
try
|
||||
{
|
||||
Serializer s (vucTransaction);
|
||||
SerializerIterator sit (s);
|
||||
|
||||
SerializedTransaction::pointer st = boost::make_shared<SerializedTransaction> (boost::ref (sit));
|
||||
|
||||
return boost::make_shared<Transaction> (st, bValidate);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
Log (lsWARNING) << "Exception constructing transaction";
|
||||
return boost::shared_ptr<Transaction> ();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Generic transaction construction
|
||||
//
|
||||
|
||||
Transaction::Transaction (
|
||||
TxType ttKind,
|
||||
const RippleAddress& naPublicKey,
|
||||
const RippleAddress& naSourceAccount,
|
||||
uint32 uSeq,
|
||||
const STAmount& saFee,
|
||||
uint32 uSourceTag) :
|
||||
mAccountFrom (naSourceAccount), mFromPubKey (naPublicKey), mInLedger (0), mStatus (NEW), mResult (temUNCERTAIN)
|
||||
{
|
||||
assert (mFromPubKey.isValid ());
|
||||
|
||||
mTransaction = boost::make_shared<SerializedTransaction> (ttKind);
|
||||
|
||||
// Log(lsINFO) << str(boost::format("Transaction: account: %s") % naSourceAccount.humanAccountID());
|
||||
// Log(lsINFO) << str(boost::format("Transaction: mAccountFrom: %s") % mAccountFrom.humanAccountID());
|
||||
|
||||
mTransaction->setSigningPubKey (mFromPubKey);
|
||||
mTransaction->setSourceAccount (mAccountFrom);
|
||||
mTransaction->setSequence (uSeq);
|
||||
mTransaction->setTransactionFee (saFee);
|
||||
|
||||
if (uSourceTag)
|
||||
{
|
||||
mTransaction->makeFieldPresent (sfSourceTag);
|
||||
mTransaction->setFieldU32 (sfSourceTag, uSourceTag);
|
||||
}
|
||||
}
|
||||
|
||||
bool Transaction::sign (const RippleAddress& naAccountPrivate)
|
||||
{
|
||||
bool bResult = true;
|
||||
|
||||
if (!naAccountPrivate.isValid ())
|
||||
{
|
||||
Log (lsWARNING) << "No private key for signing";
|
||||
bResult = false;
|
||||
}
|
||||
|
||||
getSTransaction ()->sign (naAccountPrivate);
|
||||
|
||||
if (bResult)
|
||||
{
|
||||
updateID ();
|
||||
}
|
||||
else
|
||||
{
|
||||
mStatus = INCOMPLETE;
|
||||
}
|
||||
|
||||
return bResult;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Misc.
|
||||
//
|
||||
|
||||
bool Transaction::checkSign () const
|
||||
{
|
||||
if (!mFromPubKey.isValid ())
|
||||
{
|
||||
Log (lsWARNING) << "Transaction has bad source public key";
|
||||
return false;
|
||||
}
|
||||
|
||||
return mTransaction->checkSign (mFromPubKey);
|
||||
}
|
||||
|
||||
void Transaction::setStatus (TransStatus ts, uint32 lseq)
|
||||
{
|
||||
mStatus = ts;
|
||||
mInLedger = lseq;
|
||||
}
|
||||
|
||||
Transaction::pointer Transaction::transactionFromSQL (Database* db, bool bValidate)
|
||||
{
|
||||
Serializer rawTxn;
|
||||
std::string status;
|
||||
uint32 inLedger;
|
||||
|
||||
int txSize = 2048;
|
||||
rawTxn.resize (txSize);
|
||||
|
||||
db->getStr ("Status", status);
|
||||
inLedger = db->getInt ("LedgerSeq");
|
||||
txSize = db->getBinary ("RawTxn", &*rawTxn.begin (), rawTxn.getLength ());
|
||||
|
||||
if (txSize > rawTxn.getLength ())
|
||||
{
|
||||
rawTxn.resize (txSize);
|
||||
db->getBinary ("RawTxn", &*rawTxn.begin (), rawTxn.getLength ());
|
||||
}
|
||||
|
||||
rawTxn.resize (txSize);
|
||||
|
||||
SerializerIterator it (rawTxn);
|
||||
SerializedTransaction::pointer txn = boost::make_shared<SerializedTransaction> (boost::ref (it));
|
||||
Transaction::pointer tr = boost::make_shared<Transaction> (txn, bValidate);
|
||||
|
||||
TransStatus st (INVALID);
|
||||
|
||||
switch (status[0])
|
||||
{
|
||||
case TXN_SQL_NEW:
|
||||
st = NEW;
|
||||
break;
|
||||
|
||||
case TXN_SQL_CONFLICT:
|
||||
st = CONFLICTED;
|
||||
break;
|
||||
|
||||
case TXN_SQL_HELD:
|
||||
st = HELD;
|
||||
break;
|
||||
|
||||
case TXN_SQL_VALIDATED:
|
||||
st = COMMITTED;
|
||||
break;
|
||||
|
||||
case TXN_SQL_INCLUDED:
|
||||
st = INCLUDED;
|
||||
break;
|
||||
|
||||
case TXN_SQL_UNKNOWN:
|
||||
break;
|
||||
|
||||
default:
|
||||
assert (false);
|
||||
}
|
||||
|
||||
tr->setStatus (st);
|
||||
tr->setLedger (inLedger);
|
||||
return tr;
|
||||
}
|
||||
|
||||
// DAVID: would you rather duplicate this code or keep the lock longer?
|
||||
Transaction::pointer Transaction::transactionFromSQL (const std::string& sql)
|
||||
{
|
||||
Serializer rawTxn;
|
||||
std::string status;
|
||||
uint32 inLedger;
|
||||
|
||||
int txSize = 2048;
|
||||
rawTxn.resize (txSize);
|
||||
|
||||
{
|
||||
ScopedLock sl (getApp().getTxnDB ()->getDBLock ());
|
||||
Database* db = getApp().getTxnDB ()->getDB ();
|
||||
|
||||
if (!db->executeSQL (sql, true) || !db->startIterRows ())
|
||||
return Transaction::pointer ();
|
||||
|
||||
db->getStr ("Status", status);
|
||||
inLedger = db->getInt ("LedgerSeq");
|
||||
txSize = db->getBinary ("RawTxn", &*rawTxn.begin (), rawTxn.getLength ());
|
||||
|
||||
if (txSize > rawTxn.getLength ())
|
||||
{
|
||||
rawTxn.resize (txSize);
|
||||
db->getBinary ("RawTxn", &*rawTxn.begin (), rawTxn.getLength ());
|
||||
}
|
||||
|
||||
db->endIterRows ();
|
||||
}
|
||||
rawTxn.resize (txSize);
|
||||
|
||||
SerializerIterator it (rawTxn);
|
||||
SerializedTransaction::pointer txn = boost::make_shared<SerializedTransaction> (boost::ref (it));
|
||||
Transaction::pointer tr = boost::make_shared<Transaction> (txn, true);
|
||||
|
||||
TransStatus st (INVALID);
|
||||
|
||||
switch (status[0])
|
||||
{
|
||||
case TXN_SQL_NEW:
|
||||
st = NEW;
|
||||
break;
|
||||
|
||||
case TXN_SQL_CONFLICT:
|
||||
st = CONFLICTED;
|
||||
break;
|
||||
|
||||
case TXN_SQL_HELD:
|
||||
st = HELD;
|
||||
break;
|
||||
|
||||
case TXN_SQL_VALIDATED:
|
||||
st = COMMITTED;
|
||||
break;
|
||||
|
||||
case TXN_SQL_INCLUDED:
|
||||
st = INCLUDED;
|
||||
break;
|
||||
|
||||
case TXN_SQL_UNKNOWN:
|
||||
break;
|
||||
|
||||
default:
|
||||
assert (false);
|
||||
}
|
||||
|
||||
tr->setStatus (st);
|
||||
tr->setLedger (inLedger);
|
||||
return tr;
|
||||
}
|
||||
|
||||
|
||||
Transaction::pointer Transaction::load (uint256 const& id)
|
||||
{
|
||||
std::string sql = "SELECT LedgerSeq,Status,RawTxn FROM Transactions WHERE TransID='";
|
||||
sql.append (id.GetHex ());
|
||||
sql.append ("';");
|
||||
return transactionFromSQL (sql);
|
||||
}
|
||||
|
||||
bool Transaction::convertToTransactions (uint32 firstLedgerSeq, uint32 secondLedgerSeq,
|
||||
bool checkFirstTransactions, bool checkSecondTransactions, const SHAMap::Delta& inMap,
|
||||
std::map<uint256, std::pair<Transaction::pointer, Transaction::pointer> >& outMap)
|
||||
{
|
||||
// convert a straight SHAMap payload difference to a transaction difference table
|
||||
// return value: true=ledgers are valid, false=a ledger is invalid
|
||||
SHAMap::Delta::const_iterator it;
|
||||
|
||||
for (it = inMap.begin (); it != inMap.end (); ++it)
|
||||
{
|
||||
uint256 const& id = it->first;
|
||||
SHAMapItem::ref first = it->second.first;
|
||||
SHAMapItem::ref second = it->second.second;
|
||||
|
||||
Transaction::pointer firstTrans, secondTrans;
|
||||
|
||||
if (!!first)
|
||||
{
|
||||
// transaction in our table
|
||||
firstTrans = sharedTransaction (first->getData (), checkFirstTransactions);
|
||||
|
||||
if ((firstTrans->getStatus () == INVALID) || (firstTrans->getID () != id ))
|
||||
{
|
||||
firstTrans->setStatus (INVALID, firstLedgerSeq);
|
||||
return false;
|
||||
}
|
||||
else firstTrans->setStatus (INCLUDED, firstLedgerSeq);
|
||||
}
|
||||
|
||||
if (!!second)
|
||||
{
|
||||
// transaction in other table
|
||||
secondTrans = sharedTransaction (second->getData (), checkSecondTransactions);
|
||||
|
||||
if ((secondTrans->getStatus () == INVALID) || (secondTrans->getID () != id))
|
||||
{
|
||||
secondTrans->setStatus (INVALID, secondLedgerSeq);
|
||||
return false;
|
||||
}
|
||||
else secondTrans->setStatus (INCLUDED, secondLedgerSeq);
|
||||
}
|
||||
|
||||
assert (firstTrans || secondTrans);
|
||||
|
||||
if (firstTrans && secondTrans && (firstTrans->getStatus () != INVALID) && (secondTrans->getStatus () != INVALID))
|
||||
return false; // one or the other SHAMap is structurally invalid or a miracle has happened
|
||||
|
||||
outMap[id] = std::pair<Transaction::pointer, Transaction::pointer> (firstTrans, secondTrans);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// options 1 to include the date of the transaction
|
||||
Json::Value Transaction::getJson (int options, bool binary) const
|
||||
{
|
||||
Json::Value ret (mTransaction->getJson (0, binary));
|
||||
|
||||
if (mInLedger)
|
||||
{
|
||||
ret["inLedger"] = mInLedger; // Deprecated.
|
||||
ret["ledger_index"] = mInLedger;
|
||||
|
||||
if (options == 1)
|
||||
{
|
||||
Ledger::pointer ledger = getApp().getLedgerMaster ().getLedgerBySeq (mInLedger);
|
||||
|
||||
if (ledger)
|
||||
ret["date"] = ledger->getCloseTimeNC ();
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
//
|
||||
// Obsolete
|
||||
//
|
||||
|
||||
static bool isHex (char j)
|
||||
{
|
||||
if ((j >= '0') && (j <= '9')) return true;
|
||||
|
||||
if ((j >= 'A') && (j <= 'F')) return true;
|
||||
|
||||
if ((j >= 'a') && (j <= 'f')) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Transaction::isHexTxID (const std::string& txid)
|
||||
{
|
||||
if (txid.size () != 64) return false;
|
||||
|
||||
for (int i = 0; i < 64; ++i)
|
||||
if (!isHex (txid[i])) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
@@ -1,172 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_TRANSACTION_H
|
||||
#define RIPPLE_TRANSACTION_H
|
||||
|
||||
//
|
||||
// Transactions should be constructed in JSON with. Use STObject::parseJson to obtain a binary version.
|
||||
//
|
||||
|
||||
class Database;
|
||||
|
||||
enum TransStatus
|
||||
{
|
||||
NEW = 0, // just received / generated
|
||||
INVALID = 1, // no valid signature, insufficient funds
|
||||
INCLUDED = 2, // added to the current ledger
|
||||
CONFLICTED = 3, // losing to a conflicting transaction
|
||||
COMMITTED = 4, // known to be in a ledger
|
||||
HELD = 5, // not valid now, maybe later
|
||||
REMOVED = 6, // taken out of a ledger
|
||||
OBSOLETE = 7, // a compatible transaction has taken precedence
|
||||
INCOMPLETE = 8 // needs more signatures
|
||||
};
|
||||
|
||||
// This class is for constructing and examining transactions. Transactions are static so manipulation functions are unnecessary.
|
||||
class Transaction
|
||||
: public boost::enable_shared_from_this<Transaction>
|
||||
, public CountedObject <Transaction>
|
||||
{
|
||||
public:
|
||||
static char const* getCountedObjectName () { return "Transaction"; }
|
||||
|
||||
typedef boost::shared_ptr<Transaction> pointer;
|
||||
typedef const pointer& ref;
|
||||
|
||||
public:
|
||||
Transaction (SerializedTransaction::ref st, bool bValidate);
|
||||
|
||||
static Transaction::pointer sharedTransaction (Blob const & vucTransaction, bool bValidate);
|
||||
static Transaction::pointer transactionFromSQL (Database * db, bool bValidate);
|
||||
|
||||
Transaction (
|
||||
TxType ttKind,
|
||||
const RippleAddress & naPublicKey, // To prove transaction is consistent and authorized.
|
||||
const RippleAddress & naSourceAccount, // To identify the paying account.
|
||||
uint32 uSeq, // To order transactions.
|
||||
const STAmount & saFee, // Transaction fee.
|
||||
uint32 uSourceTag); // User call back value.
|
||||
|
||||
|
||||
bool sign (const RippleAddress & naAccountPrivate);
|
||||
|
||||
bool checkSign () const;
|
||||
|
||||
void updateID ()
|
||||
{
|
||||
mTransactionID = mTransaction->getTransactionID ();
|
||||
}
|
||||
|
||||
SerializedTransaction::ref getSTransaction ()
|
||||
{
|
||||
return mTransaction;
|
||||
}
|
||||
|
||||
uint256 const& getID () const
|
||||
{
|
||||
return mTransactionID;
|
||||
}
|
||||
|
||||
const RippleAddress& getFromAccount () const
|
||||
{
|
||||
return mAccountFrom;
|
||||
}
|
||||
|
||||
STAmount getAmount () const
|
||||
{
|
||||
return mTransaction->getFieldU64 (sfAmount);
|
||||
}
|
||||
|
||||
STAmount getFee () const
|
||||
{
|
||||
return mTransaction->getTransactionFee ();
|
||||
}
|
||||
|
||||
uint32 getFromAccountSeq () const
|
||||
{
|
||||
return mTransaction->getSequence ();
|
||||
}
|
||||
|
||||
uint32 getSourceTag () const
|
||||
{
|
||||
return mTransaction->getFieldU32 (sfSourceTag);
|
||||
}
|
||||
|
||||
// VFALCO TODO Should this return a const reference?
|
||||
Blob getSignature () const
|
||||
{
|
||||
return mTransaction->getSignature ();
|
||||
}
|
||||
|
||||
LedgerIndex getLedger () const
|
||||
{
|
||||
return mInLedger;
|
||||
}
|
||||
|
||||
TransStatus getStatus () const
|
||||
{
|
||||
return mStatus;
|
||||
}
|
||||
|
||||
TER getResult ()
|
||||
{
|
||||
return mResult;
|
||||
}
|
||||
|
||||
void setResult (TER terResult)
|
||||
{
|
||||
mResult = terResult;
|
||||
}
|
||||
|
||||
void setStatus (TransStatus status, uint32 ledgerSeq);
|
||||
|
||||
void setStatus (TransStatus status)
|
||||
{
|
||||
mStatus = status;
|
||||
}
|
||||
|
||||
void setLedger (LedgerIndex ledger)
|
||||
{
|
||||
mInLedger = ledger;
|
||||
}
|
||||
|
||||
bool operator< (const Transaction&) const;
|
||||
bool operator> (const Transaction&) const;
|
||||
bool operator== (const Transaction&) const;
|
||||
bool operator!= (const Transaction&) const;
|
||||
bool operator<= (const Transaction&) const;
|
||||
bool operator>= (const Transaction&) const;
|
||||
|
||||
Json::Value getJson (int options, bool binary = false) const;
|
||||
|
||||
static Transaction::pointer load (uint256 const & id);
|
||||
|
||||
// conversion function
|
||||
static bool convertToTransactions (uint32 ourLedgerSeq, uint32 otherLedgerSeq,
|
||||
bool checkFirstTransactions, bool checkSecondTransactions, const SHAMap::Delta & inMap,
|
||||
std::map<uint256, std::pair<Transaction::pointer, Transaction::pointer> >& outMap);
|
||||
|
||||
static bool isHexTxID (const std::string&);
|
||||
|
||||
protected:
|
||||
static Transaction::pointer transactionFromSQL (const std::string & statement);
|
||||
|
||||
private:
|
||||
uint256 mTransactionID;
|
||||
RippleAddress mAccountFrom;
|
||||
RippleAddress mFromPubKey; // Sign transaction with this. mSignPubKey
|
||||
RippleAddress mSourcePrivate; // Sign transaction with this.
|
||||
|
||||
LedgerIndex mInLedger;
|
||||
TransStatus mStatus;
|
||||
TER mResult;
|
||||
|
||||
SerializedTransaction::pointer mTransaction;
|
||||
};
|
||||
|
||||
#endif
|
||||
// vim:ts=4
|
||||
@@ -1,93 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
|
||||
// VFALCO TODO move this into TransactionEngine.cpp
|
||||
|
||||
// Double check a transaction's metadata to make sure no system invariants were broken
|
||||
|
||||
bool TransactionEngine::checkInvariants (TER result, const SerializedTransaction& txn, TransactionEngineParams params)
|
||||
{
|
||||
#if 0
|
||||
uint32 txnSeq = txn.getFieldU32 (sfSequence);
|
||||
|
||||
LedgerEntryAction leaAction;
|
||||
|
||||
uint256 srcActId = Ledger::getAccountRootIndex (txn.getFieldAccount (sfAccount));
|
||||
SLE::pointer origSrcAct = mLedger->getSLE (srcActId);
|
||||
SLE::pointer newSrcAct = mNodes.getEntry (srcActId, leaAction);
|
||||
|
||||
if (!newSrcAct || !origSrcAct)
|
||||
{
|
||||
WriteLog (lsFATAL, TransactionEngine) << "Transaction created or destroyed its issuing account";
|
||||
assert (false);
|
||||
return tefINTERNAL;
|
||||
}
|
||||
|
||||
if ((newSrcAct->getFieldU32 (sfSequence) != (txnSeq + 1)) ||
|
||||
(origSrcAct->getFieldU32 (sfSequence) != txnSeq))
|
||||
{
|
||||
WriteLog (lsFATAL, TransactionEngine) << "Transaction mangles sequence numbers";
|
||||
WriteLog (lsFATAL, TransactionEngine) << "t:" << txnSeq << " o: " << origSrcAct->getFieldU32 (sfSequence)
|
||||
<< " n: " << newSrcAct->getFieldU32 (sfSequence);
|
||||
assert (false);
|
||||
return tefINTERNAL;
|
||||
}
|
||||
|
||||
int64 xrpChange = txn.getFieldAmount (sfFee).getSNValue ();
|
||||
|
||||
for (LedgerEntrySet::const_iterator it = mNodes.begin (), end = mNodes.end (); it != end; ++it)
|
||||
{
|
||||
const LedgerEntrySetEntry& entry = it->second;
|
||||
|
||||
if (entry.mAction == taaMODIFY)
|
||||
{
|
||||
#if 0
|
||||
|
||||
if (entry.mEntry->getType () == ltRIPPLE_STATE)
|
||||
{
|
||||
// if this transaction pushes a ripple state over its limit, make sure it also modifies
|
||||
// an offer placed by that same user
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
if (entry.mEntry->getType () == ltACCOUNT_ROOT)
|
||||
{
|
||||
// account modified
|
||||
xrpChange += entry.mEntry->getFieldAmount (sfBalance).getSNValue ();
|
||||
xrpChange -= mLedger->getSLE (it->first)->getFieldAmount (sfBalance).getSNValue ();
|
||||
}
|
||||
}
|
||||
else if (entry.mAction == taaCREATE)
|
||||
{
|
||||
if (entry.mEntry->getType () == ltRIPPLE_STATE)
|
||||
{
|
||||
if (entry.mEntry->getFieldAmount (sfLowLimit).getIssuer () ==
|
||||
entry.mEntry->getFieldAmount (sfHighLimit).getIssuer ())
|
||||
{
|
||||
WriteLog (lsFATAL, TransactionEngine) << "Ripple line to self";
|
||||
assert (false);
|
||||
return tefINTERNAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (entry.mEntry->getType () == ltACCOUNT_ROOT) // account created
|
||||
xrpChange += entry.mEntry->getFieldAmount (sfBalance).getSNValue ();
|
||||
}
|
||||
}
|
||||
|
||||
if (xrpChange != 0)
|
||||
{
|
||||
WriteLog (lsFATAL, TransactionEngine) << "Transaction creates/destroys XRP";
|
||||
assert (false);
|
||||
return tefINTERNAL;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1,215 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
//
|
||||
// XXX Make sure all fields are recognized in transactions.
|
||||
//
|
||||
|
||||
SETUP_LOG (TransactionEngine)
|
||||
|
||||
void TransactionEngine::txnWrite ()
|
||||
{
|
||||
// Write back the account states
|
||||
typedef std::map<uint256, LedgerEntrySetEntry>::value_type u256_LES_pair;
|
||||
BOOST_FOREACH (u256_LES_pair & it, mNodes)
|
||||
{
|
||||
SLE::ref sleEntry = it.second.mEntry;
|
||||
|
||||
switch (it.second.mAction)
|
||||
{
|
||||
case taaNONE:
|
||||
assert (false);
|
||||
break;
|
||||
|
||||
case taaCACHED:
|
||||
break;
|
||||
|
||||
case taaCREATE:
|
||||
{
|
||||
WriteLog (lsINFO, TransactionEngine) << "applyTransaction: taaCREATE: " << sleEntry->getText ();
|
||||
|
||||
if (mLedger->writeBack (lepCREATE, sleEntry) & lepERROR)
|
||||
assert (false);
|
||||
}
|
||||
break;
|
||||
|
||||
case taaMODIFY:
|
||||
{
|
||||
WriteLog (lsINFO, TransactionEngine) << "applyTransaction: taaMODIFY: " << sleEntry->getText ();
|
||||
|
||||
if (mLedger->writeBack (lepNONE, sleEntry) & lepERROR)
|
||||
assert (false);
|
||||
}
|
||||
break;
|
||||
|
||||
case taaDELETE:
|
||||
{
|
||||
WriteLog (lsINFO, TransactionEngine) << "applyTransaction: taaDELETE: " << sleEntry->getText ();
|
||||
|
||||
if (!mLedger->peekAccountStateMap ()->delItem (it.first))
|
||||
assert (false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TER TransactionEngine::applyTransaction (const SerializedTransaction& txn, TransactionEngineParams params,
|
||||
bool& didApply)
|
||||
{
|
||||
WriteLog (lsTRACE, TransactionEngine) << "applyTransaction>";
|
||||
didApply = false;
|
||||
assert (mLedger);
|
||||
mNodes.init (mLedger, txn.getTransactionID (), mLedger->getLedgerSeq (), params);
|
||||
|
||||
#ifdef BEAST_DEBUG
|
||||
|
||||
if (1)
|
||||
{
|
||||
Serializer ser;
|
||||
txn.add (ser);
|
||||
SerializerIterator sit (ser);
|
||||
SerializedTransaction s2 (sit);
|
||||
|
||||
if (!s2.isEquivalent (txn))
|
||||
{
|
||||
WriteLog (lsFATAL, TransactionEngine) << "Transaction serdes mismatch";
|
||||
Json::StyledStreamWriter ssw;
|
||||
WriteLog (lsINFO, TransactionEngine) << txn.getJson (0);
|
||||
WriteLog (lsFATAL, TransactionEngine) << s2.getJson (0);
|
||||
assert (false);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
UPTR_T<Transactor> transactor = Transactor::makeTransactor (txn, params, this);
|
||||
|
||||
if (transactor.get () != NULL)
|
||||
{
|
||||
uint256 txID = txn.getTransactionID ();
|
||||
|
||||
if (!txID)
|
||||
{
|
||||
WriteLog (lsWARNING, TransactionEngine) << "applyTransaction: invalid transaction id";
|
||||
|
||||
return temINVALID;
|
||||
}
|
||||
|
||||
TER terResult = transactor->apply ();
|
||||
std::string strToken;
|
||||
std::string strHuman;
|
||||
|
||||
transResultInfo (terResult, strToken, strHuman);
|
||||
|
||||
WriteLog (lsINFO, TransactionEngine) << "applyTransaction: terResult=" << strToken << " : " << terResult << " : " << strHuman;
|
||||
|
||||
if (isTesSuccess (terResult))
|
||||
didApply = true;
|
||||
else if (isTecClaim (terResult) && !isSetBit (params, tapRETRY))
|
||||
{
|
||||
// only claim the transaction fee
|
||||
WriteLog (lsDEBUG, TransactionEngine) << "Reprocessing to only claim fee";
|
||||
mNodes.clear ();
|
||||
|
||||
SLE::pointer txnAcct = entryCache (ltACCOUNT_ROOT, Ledger::getAccountRootIndex (txn.getSourceAccount ()));
|
||||
|
||||
if (!txnAcct)
|
||||
terResult = terNO_ACCOUNT;
|
||||
else
|
||||
{
|
||||
uint32 t_seq = txn.getSequence ();
|
||||
uint32 a_seq = txnAcct->getFieldU32 (sfSequence);
|
||||
|
||||
if (a_seq < t_seq)
|
||||
terResult = terPRE_SEQ;
|
||||
else if (a_seq > t_seq)
|
||||
terResult = tefPAST_SEQ;
|
||||
else
|
||||
{
|
||||
STAmount fee = txn.getTransactionFee ();
|
||||
STAmount balance = txnAcct->getFieldAmount (sfBalance);
|
||||
|
||||
if (balance < fee)
|
||||
terResult = terINSUF_FEE_B;
|
||||
else
|
||||
{
|
||||
txnAcct->setFieldAmount (sfBalance, balance - fee);
|
||||
txnAcct->setFieldU32 (sfSequence, t_seq + 1);
|
||||
entryModify (txnAcct);
|
||||
didApply = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
WriteLog (lsDEBUG, TransactionEngine) << "Not applying transaction " << txID;
|
||||
|
||||
if (didApply)
|
||||
{
|
||||
if (!checkInvariants (terResult, txn, params))
|
||||
{
|
||||
WriteLog (lsFATAL, TransactionEngine) << "Transaction violates invariants";
|
||||
WriteLog (lsFATAL, TransactionEngine) << txn.getJson (0);
|
||||
WriteLog (lsFATAL, TransactionEngine) << transToken (terResult) << ": " << transHuman (terResult);
|
||||
WriteLog (lsFATAL, TransactionEngine) << mNodes.getJson (0);
|
||||
didApply = false;
|
||||
terResult = tefINTERNAL;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 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))
|
||||
{
|
||||
WriteLog (lsFATAL, TransactionEngine) << "Tried to add transaction to open ledger that already had it";
|
||||
assert (false);
|
||||
throw std::runtime_error ("Duplicate transaction applied");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!mLedger->addTransaction (txID, s, m))
|
||||
{
|
||||
WriteLog (lsFATAL, TransactionEngine) << "Tried to add transaction to ledger that already had it";
|
||||
assert (false);
|
||||
throw std::runtime_error ("Duplicate transaction applied to closed ledger");
|
||||
}
|
||||
|
||||
// Charge whatever fee they specified.
|
||||
STAmount saPaid = txn.getTransactionFee ();
|
||||
mLedger->destroyCoins (saPaid.getNValue ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mTxnAccount.reset ();
|
||||
mNodes.clear ();
|
||||
|
||||
if (!isSetBit (params, tapOPEN_LEDGER) && isTemMalformed (terResult))
|
||||
{
|
||||
// XXX Malformed or failed transaction in closed ledger must bow out.
|
||||
}
|
||||
|
||||
return terResult;
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog (lsWARNING, TransactionEngine) << "applyTransaction: Invalid transaction: unknown transaction type";
|
||||
return temUNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
@@ -1,104 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef __TRANSACTIONENGINE__
|
||||
#define __TRANSACTIONENGINE__
|
||||
|
||||
// A TransactionEngine applies serialized transactions to a ledger
|
||||
// It can also, verify signatures, verify fees, and give rejection reasons
|
||||
|
||||
// One instance per ledger.
|
||||
// Only one transaction applied at a time.
|
||||
class TransactionEngine
|
||||
: public CountedObject <TransactionEngine>
|
||||
{
|
||||
public:
|
||||
static char const* getCountedObjectName () { return "TransactionEngine"; }
|
||||
|
||||
private:
|
||||
LedgerEntrySet mNodes;
|
||||
|
||||
TER setAuthorized (const SerializedTransaction & txn, bool bMustSetGenerator);
|
||||
TER checkSig (const SerializedTransaction & txn);
|
||||
|
||||
TER takeOffers (
|
||||
bool bPassive,
|
||||
uint256 const & uBookBase,
|
||||
const uint160 & uTakerAccountID,
|
||||
SLE::ref sleTakerAccount,
|
||||
const STAmount & saTakerPays,
|
||||
const STAmount & saTakerGets,
|
||||
STAmount & saTakerPaid,
|
||||
STAmount & saTakerGot);
|
||||
|
||||
protected:
|
||||
Ledger::pointer mLedger;
|
||||
int mTxnSeq;
|
||||
|
||||
uint160 mTxnAccountID;
|
||||
SLE::pointer mTxnAccount;
|
||||
|
||||
void txnWrite ();
|
||||
|
||||
public:
|
||||
typedef boost::shared_ptr<TransactionEngine> pointer;
|
||||
|
||||
TransactionEngine () : mTxnSeq (0)
|
||||
{
|
||||
;
|
||||
}
|
||||
TransactionEngine (Ledger::ref ledger) : mLedger (ledger), mTxnSeq (0)
|
||||
{
|
||||
assert (mLedger);
|
||||
}
|
||||
|
||||
LedgerEntrySet& getNodes ()
|
||||
{
|
||||
return mNodes;
|
||||
}
|
||||
Ledger::ref getLedger ()
|
||||
{
|
||||
return mLedger;
|
||||
}
|
||||
void setLedger (Ledger::ref ledger)
|
||||
{
|
||||
assert (ledger);
|
||||
mLedger = ledger;
|
||||
}
|
||||
|
||||
SLE::pointer entryCreate (LedgerEntryType type, uint256 const & index)
|
||||
{
|
||||
return mNodes.entryCreate (type, index);
|
||||
}
|
||||
SLE::pointer entryCache (LedgerEntryType type, uint256 const & index)
|
||||
{
|
||||
return mNodes.entryCache (type, index);
|
||||
}
|
||||
void entryDelete (SLE::ref sleEntry)
|
||||
{
|
||||
mNodes.entryDelete (sleEntry);
|
||||
}
|
||||
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)
|
||||
{
|
||||
return static_cast<TransactionEngineParams> (static_cast<int> (l1) | static_cast<int> (l2));
|
||||
}
|
||||
|
||||
inline TransactionEngineParams operator& (const TransactionEngineParams& l1, const TransactionEngineParams& l2)
|
||||
{
|
||||
return static_cast<TransactionEngineParams> (static_cast<int> (l1) & static_cast<int> (l2));
|
||||
}
|
||||
|
||||
#endif
|
||||
// vim:ts=4
|
||||
@@ -1,95 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef CACHED_TRANSACTION_NUM
|
||||
#define CACHED_TRANSACTION_NUM 65536
|
||||
#endif
|
||||
|
||||
#ifndef CACHED_TRANSACTION_AGE
|
||||
#define CACHED_TRANSACTION_AGE 1800
|
||||
#endif
|
||||
|
||||
TransactionMaster::TransactionMaster () : mCache ("TransactionCache", CACHED_TRANSACTION_NUM, CACHED_TRANSACTION_AGE)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
bool TransactionMaster::inLedger (uint256 const& hash, uint32 ledger)
|
||||
{
|
||||
Transaction::pointer txn = mCache.fetch (hash);
|
||||
|
||||
if (!txn)
|
||||
return false;
|
||||
|
||||
txn->setStatus (COMMITTED, ledger);
|
||||
return true;
|
||||
}
|
||||
|
||||
Transaction::pointer TransactionMaster::fetch (uint256 const& txnID, bool checkDisk)
|
||||
{
|
||||
Transaction::pointer txn = mCache.fetch (txnID);
|
||||
|
||||
if (!checkDisk || txn)
|
||||
return txn;
|
||||
|
||||
txn = Transaction::load (txnID);
|
||||
|
||||
if (!txn)
|
||||
return txn;
|
||||
|
||||
mCache.canonicalize (txnID, txn);
|
||||
|
||||
return txn;
|
||||
}
|
||||
|
||||
SerializedTransaction::pointer TransactionMaster::fetch (SHAMapItem::ref item, SHAMapTreeNode::TNType type,
|
||||
bool checkDisk, uint32 uCommitLedger)
|
||||
{
|
||||
SerializedTransaction::pointer txn;
|
||||
Transaction::pointer iTx = getApp().getMasterTransaction ().fetch (item->getTag (), false);
|
||||
|
||||
if (!iTx)
|
||||
{
|
||||
|
||||
if (type == SHAMapTreeNode::tnTRANSACTION_NM)
|
||||
{
|
||||
SerializerIterator sit (item->peekSerializer ());
|
||||
txn = boost::make_shared<SerializedTransaction> (boost::ref (sit));
|
||||
}
|
||||
else if (type == SHAMapTreeNode::tnTRANSACTION_MD)
|
||||
{
|
||||
Serializer s;
|
||||
int length;
|
||||
item->peekSerializer ().getVL (s.modData (), 0, length);
|
||||
SerializerIterator sit (s);
|
||||
|
||||
txn = boost::make_shared<SerializedTransaction> (boost::ref (sit));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (uCommitLedger)
|
||||
iTx->setStatus (COMMITTED, uCommitLedger);
|
||||
|
||||
txn = iTx->getSTransaction ();
|
||||
}
|
||||
|
||||
return txn;
|
||||
}
|
||||
|
||||
bool TransactionMaster::canonicalize (Transaction::pointer& txn)
|
||||
{
|
||||
uint256 tid = txn->getID ();
|
||||
|
||||
if (!tid)
|
||||
return false;
|
||||
|
||||
if (mCache.canonicalize (tid, txn))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
// vim:ts=4
|
||||
@@ -1,34 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef __TRANSACTIONMASTER__
|
||||
#define __TRANSACTIONMASTER__
|
||||
|
||||
// Tracks all transactions in memory
|
||||
|
||||
class TransactionMaster : LeakChecked <TransactionMaster>
|
||||
{
|
||||
public:
|
||||
TransactionMaster ();
|
||||
|
||||
Transaction::pointer fetch (uint256 const& , bool checkDisk);
|
||||
SerializedTransaction::pointer fetch (SHAMapItem::ref item, SHAMapTreeNode:: TNType type,
|
||||
bool checkDisk, uint32 uCommitLedger);
|
||||
|
||||
// return value: true = we had the transaction already
|
||||
bool inLedger (uint256 const& hash, uint32 ledger);
|
||||
bool canonicalize (Transaction::pointer& txn);
|
||||
void sweep (void)
|
||||
{
|
||||
mCache.sweep ();
|
||||
}
|
||||
|
||||
private:
|
||||
TaggedCache <uint256, Transaction, UptimeTimerAdapter> mCache;
|
||||
};
|
||||
|
||||
#endif
|
||||
// vim:ts=4
|
||||
@@ -1,216 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
// VFALCO TODO rename class to TransactionMeta
|
||||
|
||||
SETUP_LOG (TransactionMetaSet)
|
||||
|
||||
TransactionMetaSet::TransactionMetaSet (uint256 const& txid, uint32 ledger, Blob const& vec) :
|
||||
mTransactionID (txid), mLedger (ledger), mNodes (sfAffectedNodes, 32)
|
||||
{
|
||||
Serializer s (vec);
|
||||
SerializerIterator sit (s);
|
||||
|
||||
UPTR_T<SerializedType> pobj = STObject::deserialize (sit, sfAffectedNodes);
|
||||
STObject* obj = static_cast<STObject*> (pobj.get ());
|
||||
|
||||
if (!obj)
|
||||
throw std::runtime_error ("bad metadata");
|
||||
|
||||
mResult = obj->getFieldU8 (sfTransactionResult);
|
||||
mIndex = obj->getFieldU32 (sfTransactionIndex);
|
||||
mNodes = * dynamic_cast<STArray*> (&obj->getField (sfAffectedNodes));
|
||||
}
|
||||
|
||||
bool TransactionMetaSet::isNodeAffected (uint256 const& node) const
|
||||
{
|
||||
BOOST_FOREACH (const STObject & it, mNodes)
|
||||
|
||||
if (it.getFieldH256 (sfLedgerIndex) == node)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void TransactionMetaSet::setAffectedNode (uint256 const& node, SField::ref type, uint16 nodeType)
|
||||
{
|
||||
// make sure the node exists and force its type
|
||||
BOOST_FOREACH (STObject & it, mNodes)
|
||||
{
|
||||
if (it.getFieldH256 (sfLedgerIndex) == node)
|
||||
{
|
||||
it.setFName (type);
|
||||
it.setFieldU16 (sfLedgerEntryType, nodeType);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
mNodes.push_back (STObject (type));
|
||||
STObject& obj = mNodes.back ();
|
||||
|
||||
assert (obj.getFName () == type);
|
||||
obj.setFieldH256 (sfLedgerIndex, node);
|
||||
obj.setFieldU16 (sfLedgerEntryType, nodeType);
|
||||
}
|
||||
|
||||
static void addIfUnique (std::vector<RippleAddress>& vector, const RippleAddress& address)
|
||||
{
|
||||
BOOST_FOREACH (const RippleAddress & a, vector)
|
||||
|
||||
if (a == address)
|
||||
return;
|
||||
|
||||
vector.push_back (address);
|
||||
}
|
||||
|
||||
std::vector<RippleAddress> TransactionMetaSet::getAffectedAccounts ()
|
||||
{
|
||||
std::vector<RippleAddress> accounts;
|
||||
accounts.reserve (10);
|
||||
|
||||
// This code should match the behavior of the JS method:
|
||||
// Meta#getAffectedAccounts
|
||||
BOOST_FOREACH (const STObject & it, mNodes)
|
||||
{
|
||||
int index = it.getFieldIndex ((it.getFName () == sfCreatedNode) ? sfNewFields : sfFinalFields);
|
||||
|
||||
if (index != -1)
|
||||
{
|
||||
const STObject* inner = dynamic_cast<const STObject*> (&it.peekAtIndex (index));
|
||||
|
||||
if (inner)
|
||||
{
|
||||
BOOST_FOREACH (const SerializedType & field, inner->peekData ())
|
||||
{
|
||||
const STAccount* sa = dynamic_cast<const STAccount*> (&field);
|
||||
|
||||
if (sa)
|
||||
addIfUnique (accounts, sa->getValueNCA ());
|
||||
else if ((field.getFName () == sfLowLimit) || (field.getFName () == sfHighLimit) ||
|
||||
(field.getFName () == sfTakerPays) || (field.getFName () == sfTakerGets))
|
||||
{
|
||||
const STAmount* lim = dynamic_cast<const STAmount*> (&field);
|
||||
|
||||
if (lim != NULL)
|
||||
{
|
||||
uint160 issuer = lim->getIssuer ();
|
||||
|
||||
if (issuer.isNonZero ())
|
||||
{
|
||||
RippleAddress na;
|
||||
na.setAccountID (issuer);
|
||||
addIfUnique (accounts, na);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog (lsFATAL, TransactionMetaSet) << "limit is not amount " << field.getJson (0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else assert (false);
|
||||
}
|
||||
}
|
||||
|
||||
return accounts;
|
||||
}
|
||||
|
||||
STObject& TransactionMetaSet::getAffectedNode (SLE::ref node, SField::ref type)
|
||||
{
|
||||
assert (&type);
|
||||
uint256 index = node->getIndex ();
|
||||
BOOST_FOREACH (STObject & it, mNodes)
|
||||
{
|
||||
if (it.getFieldH256 (sfLedgerIndex) == index)
|
||||
return it;
|
||||
}
|
||||
mNodes.push_back (STObject (type));
|
||||
STObject& obj = mNodes.back ();
|
||||
|
||||
assert (obj.getFName () == type);
|
||||
obj.setFieldH256 (sfLedgerIndex, index);
|
||||
obj.setFieldU16 (sfLedgerEntryType, node->getFieldU16 (sfLedgerEntryType));
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
STObject& TransactionMetaSet::getAffectedNode (uint256 const& node)
|
||||
{
|
||||
BOOST_FOREACH (STObject & it, mNodes)
|
||||
{
|
||||
if (it.getFieldH256 (sfLedgerIndex) == node)
|
||||
return it;
|
||||
}
|
||||
assert (false);
|
||||
throw std::runtime_error ("Affected node not found");
|
||||
}
|
||||
|
||||
const STObject& TransactionMetaSet::peekAffectedNode (uint256 const& node) const
|
||||
{
|
||||
BOOST_FOREACH (const STObject & it, mNodes)
|
||||
|
||||
if (it.getFieldH256 (sfLedgerIndex) == node)
|
||||
return it;
|
||||
|
||||
throw std::runtime_error ("Affected node not found");
|
||||
}
|
||||
|
||||
void TransactionMetaSet::init (uint256 const& id, uint32 ledger)
|
||||
{
|
||||
mTransactionID = id;
|
||||
mLedger = ledger;
|
||||
mNodes = STArray (sfAffectedNodes, 32);
|
||||
}
|
||||
|
||||
void TransactionMetaSet::swap (TransactionMetaSet& s)
|
||||
{
|
||||
assert ((mTransactionID == s.mTransactionID) && (mLedger == s.mLedger));
|
||||
mNodes.swap (s.mNodes);
|
||||
}
|
||||
|
||||
bool TransactionMetaSet::thread (STObject& node, uint256 const& prevTxID, uint32 prevLgrID)
|
||||
{
|
||||
if (node.getFieldIndex (sfPreviousTxnID) == -1)
|
||||
{
|
||||
assert (node.getFieldIndex (sfPreviousTxnLgrSeq) == -1);
|
||||
node.setFieldH256 (sfPreviousTxnID, prevTxID);
|
||||
node.setFieldU32 (sfPreviousTxnLgrSeq, prevLgrID);
|
||||
return true;
|
||||
}
|
||||
|
||||
assert (node.getFieldH256 (sfPreviousTxnID) == prevTxID);
|
||||
assert (node.getFieldU32 (sfPreviousTxnLgrSeq) == prevLgrID);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool compare (const STObject& o1, const STObject& o2)
|
||||
{
|
||||
return o1.getFieldH256 (sfLedgerIndex) < o2.getFieldH256 (sfLedgerIndex);
|
||||
}
|
||||
|
||||
STObject TransactionMetaSet::getAsObject () const
|
||||
{
|
||||
STObject metaData (sfTransactionMetaData);
|
||||
assert (mResult != 255);
|
||||
metaData.setFieldU8 (sfTransactionResult, mResult);
|
||||
metaData.setFieldU32 (sfTransactionIndex, mIndex);
|
||||
metaData.addObject (mNodes);
|
||||
return metaData;
|
||||
}
|
||||
|
||||
void TransactionMetaSet::addRaw (Serializer& s, TER result, uint32 index)
|
||||
{
|
||||
mResult = static_cast<int> (result);
|
||||
mIndex = index;
|
||||
assert ((mResult == 0) || ((mResult > 100) && (mResult <= 255)));
|
||||
|
||||
mNodes.sort (compare);
|
||||
|
||||
getAsObject ().add (s);
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
@@ -1,89 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_TRANSACTIONMETA_H
|
||||
#define RIPPLE_TRANSACTIONMETA_H
|
||||
|
||||
class TransactionMetaSet : LeakChecked <TransactionMetaSet>
|
||||
{
|
||||
public:
|
||||
typedef boost::shared_ptr<TransactionMetaSet> pointer;
|
||||
typedef const pointer& ref;
|
||||
|
||||
public:
|
||||
TransactionMetaSet () : mLedger (0), mIndex (static_cast<uint32> (-1)), mResult (255)
|
||||
{
|
||||
;
|
||||
}
|
||||
TransactionMetaSet (uint256 const& txID, uint32 ledger, uint32 index) :
|
||||
mTransactionID (txID), mLedger (ledger), mIndex (static_cast<uint32> (-1)), mResult (255)
|
||||
{
|
||||
;
|
||||
}
|
||||
TransactionMetaSet (uint256 const& txID, uint32 ledger, Blob const&);
|
||||
|
||||
void init (uint256 const& transactionID, uint32 ledger);
|
||||
void clear ()
|
||||
{
|
||||
mNodes.clear ();
|
||||
}
|
||||
void swap (TransactionMetaSet&);
|
||||
|
||||
uint256 const& getTxID ()
|
||||
{
|
||||
return mTransactionID;
|
||||
}
|
||||
uint32 getLgrSeq ()
|
||||
{
|
||||
return mLedger;
|
||||
}
|
||||
int getResult () const
|
||||
{
|
||||
return mResult;
|
||||
}
|
||||
TER getResultTER () const
|
||||
{
|
||||
return static_cast<TER> (mResult);
|
||||
}
|
||||
uint32 getIndex () const
|
||||
{
|
||||
return mIndex;
|
||||
}
|
||||
|
||||
bool isNodeAffected (uint256 const& ) const;
|
||||
void setAffectedNode (uint256 const& , SField::ref type, uint16 nodeType);
|
||||
STObject& getAffectedNode (SLE::ref node, SField::ref type); // create if needed
|
||||
STObject& getAffectedNode (uint256 const& );
|
||||
const STObject& peekAffectedNode (uint256 const& ) const;
|
||||
std::vector<RippleAddress> getAffectedAccounts ();
|
||||
|
||||
|
||||
Json::Value getJson (int p) const
|
||||
{
|
||||
return getAsObject ().getJson (p);
|
||||
}
|
||||
void addRaw (Serializer&, TER, uint32 index);
|
||||
|
||||
STObject getAsObject () const;
|
||||
STArray& getNodes ()
|
||||
{
|
||||
return (mNodes);
|
||||
}
|
||||
|
||||
static bool thread (STObject& node, uint256 const& prevTxID, uint32 prevLgrID);
|
||||
|
||||
private:
|
||||
uint256 mTransactionID;
|
||||
uint32 mLedger;
|
||||
uint32 mIndex;
|
||||
int mResult;
|
||||
|
||||
STArray mNodes;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
// vim:ts=4
|
||||
@@ -1,110 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
void TXQEntry::addCallbacks (const TXQEntry& otherEntry)
|
||||
{
|
||||
BOOST_FOREACH (const stCallback & callback, otherEntry.mCallbacks)
|
||||
mCallbacks.push_back (callback);
|
||||
}
|
||||
|
||||
void TXQEntry::doCallbacks (TER result)
|
||||
{
|
||||
BOOST_FOREACH (const stCallback & callback, mCallbacks)
|
||||
callback (mTxn, result);
|
||||
}
|
||||
|
||||
bool TXQueue::addEntryForSigCheck (TXQEntry::ref entry)
|
||||
{
|
||||
// we always dispatch a thread to check the signature
|
||||
boost::mutex::scoped_lock sl (mLock);
|
||||
|
||||
if (!mTxMap.insert (valueType (entry->getID (), entry)).second)
|
||||
{
|
||||
if (!entry->mCallbacks.empty ())
|
||||
mTxMap.left.find (entry->getID ())->second->addCallbacks (*entry);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TXQueue::addEntryForExecution (TXQEntry::ref entry)
|
||||
{
|
||||
boost::mutex::scoped_lock sl (mLock);
|
||||
|
||||
entry->mSigChecked = true;
|
||||
|
||||
std::pair<mapType::iterator, bool> it = mTxMap.insert (valueType (entry->getID (), entry));
|
||||
|
||||
if (!it.second)
|
||||
{
|
||||
// There was an existing entry
|
||||
it.first->right->mSigChecked = true;
|
||||
|
||||
if (!entry->mCallbacks.empty ())
|
||||
it.first->right->addCallbacks (*entry);
|
||||
}
|
||||
|
||||
if (mRunning)
|
||||
return false;
|
||||
|
||||
mRunning = true;
|
||||
return true; // A thread needs to handle this account
|
||||
}
|
||||
|
||||
TXQEntry::pointer TXQueue::removeEntry (uint256 const& id)
|
||||
{
|
||||
TXQEntry::pointer ret;
|
||||
|
||||
boost::mutex::scoped_lock sl (mLock);
|
||||
|
||||
mapType::left_map::iterator it = mTxMap.left.find (id);
|
||||
|
||||
if (it != mTxMap.left.end ())
|
||||
{
|
||||
ret = it->second;
|
||||
mTxMap.left.erase (it);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void TXQueue::getJob (TXQEntry::pointer& job)
|
||||
{
|
||||
boost::mutex::scoped_lock sl (mLock);
|
||||
assert (mRunning);
|
||||
|
||||
if (job)
|
||||
mTxMap.left.erase (job->getID ());
|
||||
|
||||
mapType::left_map::iterator it = mTxMap.left.begin ();
|
||||
|
||||
if (it == mTxMap.left.end () || !it->second->mSigChecked)
|
||||
{
|
||||
job.reset ();
|
||||
mRunning = false;
|
||||
}
|
||||
else
|
||||
job = it->second;
|
||||
}
|
||||
|
||||
bool TXQueue::stopProcessing (TXQEntry::ref finishedJob)
|
||||
{
|
||||
// returns true if a new thread must be dispatched
|
||||
boost::mutex::scoped_lock sl (mLock);
|
||||
assert (mRunning);
|
||||
|
||||
mTxMap.left.erase (finishedJob->getID ());
|
||||
|
||||
mapType::left_map::iterator it = mTxMap.left.begin ();
|
||||
|
||||
if ((it != mTxMap.left.end ()) && it->second->mSigChecked)
|
||||
return true;
|
||||
|
||||
mRunning = false;
|
||||
return false;
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef TRANSACTIONQUEUE__H
|
||||
#define TRANSACTIONQUEUE__H
|
||||
|
||||
// Allow transactions to be signature checked out of sequence but retired in sequence
|
||||
|
||||
class TXQeueue;
|
||||
|
||||
class TXQEntry
|
||||
{
|
||||
public:
|
||||
typedef boost::shared_ptr<TXQEntry> pointer;
|
||||
typedef const boost::shared_ptr<TXQEntry>& ref;
|
||||
typedef FUNCTION_TYPE<void (Transaction::pointer, TER)> stCallback; // must complete immediately
|
||||
|
||||
public:
|
||||
TXQEntry (Transaction::ref tx, bool sigChecked) : mTxn (tx), mSigChecked (sigChecked)
|
||||
{
|
||||
;
|
||||
}
|
||||
TXQEntry () : mSigChecked (false)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
Transaction::ref getTransaction () const
|
||||
{
|
||||
return mTxn;
|
||||
}
|
||||
bool getSigChecked () const
|
||||
{
|
||||
return mSigChecked;
|
||||
}
|
||||
uint256 const& getID () const
|
||||
{
|
||||
return mTxn->getID ();
|
||||
}
|
||||
|
||||
void doCallbacks (TER);
|
||||
|
||||
private:
|
||||
friend class TXQueue;
|
||||
|
||||
Transaction::pointer mTxn;
|
||||
bool mSigChecked;
|
||||
std::list<stCallback> mCallbacks;
|
||||
|
||||
void addCallbacks (const TXQEntry& otherEntry);
|
||||
};
|
||||
|
||||
class TXQueue : LeakChecked <TXQueue>
|
||||
{
|
||||
public:
|
||||
TXQueue () : mRunning (false)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
// Return: true = must dispatch signature checker thread
|
||||
bool addEntryForSigCheck (TXQEntry::ref);
|
||||
|
||||
// Call only if signature is okay. Returns true if new account, must dispatch
|
||||
bool addEntryForExecution (TXQEntry::ref);
|
||||
|
||||
// Call if signature is bad (returns entry so you can run its callbacks)
|
||||
TXQEntry::pointer removeEntry (uint256 const& txID);
|
||||
|
||||
// Transaction execution interface
|
||||
void getJob (TXQEntry::pointer&);
|
||||
bool stopProcessing (TXQEntry::ref finishedJob);
|
||||
|
||||
private:
|
||||
typedef boost::bimaps::unordered_set_of<uint256> leftType;
|
||||
typedef boost::bimaps::list_of<TXQEntry::pointer> rightType;
|
||||
typedef boost::bimap<leftType, rightType> mapType;
|
||||
typedef mapType::value_type valueType;
|
||||
|
||||
mapType mTxMap;
|
||||
bool mRunning;
|
||||
boost::mutex mLock;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,254 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
SETUP_LOG (Transactor)
|
||||
|
||||
UPTR_T<Transactor> Transactor::makeTransactor (const SerializedTransaction& txn, TransactionEngineParams params, TransactionEngine* engine)
|
||||
{
|
||||
switch (txn.getTxnType ())
|
||||
{
|
||||
case ttPAYMENT:
|
||||
return UPTR_T<Transactor> (new PaymentTransactor (txn, params, engine));
|
||||
|
||||
case ttACCOUNT_SET:
|
||||
return UPTR_T<Transactor> (new AccountSetTransactor (txn, params, engine));
|
||||
|
||||
case ttREGULAR_KEY_SET:
|
||||
return UPTR_T<Transactor> (new RegularKeySetTransactor (txn, params, engine));
|
||||
|
||||
case ttTRUST_SET:
|
||||
return UPTR_T<Transactor> (new TrustSetTransactor (txn, params, engine));
|
||||
|
||||
case ttOFFER_CREATE:
|
||||
return UPTR_T<Transactor> (new OfferCreateTransactor (txn, params, engine));
|
||||
|
||||
case ttOFFER_CANCEL:
|
||||
return UPTR_T<Transactor> (new OfferCancelTransactor (txn, params, engine));
|
||||
|
||||
case ttWALLET_ADD:
|
||||
return UPTR_T<Transactor> (new WalletAddTransactor (txn, params, engine));
|
||||
|
||||
case ttFEATURE:
|
||||
case ttFEE:
|
||||
return UPTR_T<Transactor> (new ChangeTransactor (txn, params, engine));
|
||||
|
||||
default:
|
||||
return UPTR_T<Transactor> ();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Transactor::Transactor (const SerializedTransaction& txn, TransactionEngineParams params, TransactionEngine* engine) : mTxn (txn), mEngine (engine), mParams (params)
|
||||
{
|
||||
mHasAuthKey = false;
|
||||
mSigMaster = false;
|
||||
}
|
||||
|
||||
void Transactor::calculateFee ()
|
||||
{
|
||||
mFeeDue = STAmount (mEngine->getLedger ()->scaleFeeLoad (calculateBaseFee (), isSetBit (mParams, tapADMIN)));
|
||||
}
|
||||
|
||||
uint64 Transactor::calculateBaseFee ()
|
||||
{
|
||||
return theConfig.FEE_DEFAULT;
|
||||
}
|
||||
|
||||
TER Transactor::payFee ()
|
||||
{
|
||||
STAmount saPaid = mTxn.getTransactionFee ();
|
||||
|
||||
if (!saPaid.isLegalNet ())
|
||||
return temBAD_AMOUNT;
|
||||
|
||||
// Only check fee is sufficient when the ledger is open.
|
||||
if (isSetBit (mParams, tapOPEN_LEDGER) && saPaid < mFeeDue)
|
||||
{
|
||||
WriteLog (lsINFO, Transactor) << boost::str (boost::format ("applyTransaction: Insufficient fee paid: %s/%s")
|
||||
% saPaid.getText ()
|
||||
% mFeeDue.getText ());
|
||||
|
||||
return telINSUF_FEE_P;
|
||||
}
|
||||
|
||||
if (saPaid.isNegative () || !saPaid.isNative ())
|
||||
return temBAD_FEE;
|
||||
|
||||
if (!saPaid) return tesSUCCESS;
|
||||
|
||||
// Deduct the fee, so it's not available during the transaction.
|
||||
// Will only write the account back, if the transaction succeeds.
|
||||
if (mSourceBalance < saPaid)
|
||||
{
|
||||
WriteLog (lsINFO, Transactor)
|
||||
<< boost::str (boost::format ("applyTransaction: Delay: insufficient balance: balance=%s paid=%s")
|
||||
% mSourceBalance.getText ()
|
||||
% saPaid.getText ());
|
||||
|
||||
return terINSUF_FEE_B;
|
||||
}
|
||||
|
||||
mSourceBalance -= saPaid;
|
||||
mTxnAccount->setFieldAmount (sfBalance, mSourceBalance);
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER Transactor::checkSig ()
|
||||
{
|
||||
// Consistency: Check signature
|
||||
// Verify the transaction's signing public key is the key authorized for signing.
|
||||
if (mSigningPubKey.getAccountID () == mTxnAccountID)
|
||||
{
|
||||
// Authorized to continue.
|
||||
mSigMaster = true;
|
||||
if (mTxnAccount->isFlag(lsfDisableMaster))
|
||||
return tefMASTER_DISABLED;
|
||||
}
|
||||
else if (mHasAuthKey && mSigningPubKey.getAccountID () == mTxnAccount->getFieldAccount160 (sfRegularKey))
|
||||
{
|
||||
// Authorized to continue.
|
||||
nothing ();
|
||||
}
|
||||
else if (mHasAuthKey)
|
||||
{
|
||||
WriteLog (lsINFO, Transactor) << "applyTransaction: Delay: Not authorized to use account.";
|
||||
|
||||
return tefBAD_AUTH;
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog (lsINFO, Transactor) << "applyTransaction: Invalid: Not authorized to use account.";
|
||||
|
||||
return temBAD_AUTH_MASTER;
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER Transactor::checkSeq ()
|
||||
{
|
||||
uint32 t_seq = mTxn.getSequence ();
|
||||
uint32 a_seq = mTxnAccount->getFieldU32 (sfSequence);
|
||||
|
||||
WriteLog (lsTRACE, Transactor) << "Aseq=" << a_seq << ", Tseq=" << t_seq;
|
||||
|
||||
if (t_seq != a_seq)
|
||||
{
|
||||
if (a_seq < t_seq)
|
||||
{
|
||||
WriteLog (lsINFO, Transactor) << "applyTransaction: future sequence number";
|
||||
|
||||
return terPRE_SEQ;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint256 txID = mTxn.getTransactionID ();
|
||||
|
||||
if (mEngine->getLedger ()->hasTransaction (txID))
|
||||
return tefALREADY;
|
||||
}
|
||||
|
||||
WriteLog (lsWARNING, Transactor) << "applyTransaction: past sequence number";
|
||||
|
||||
return tefPAST_SEQ;
|
||||
}
|
||||
|
||||
if (mTxn.isFieldPresent (sfPreviousTxnID) &&
|
||||
(mTxnAccount->getFieldH256 (sfPreviousTxnID) != mTxn.getFieldH256 (sfPreviousTxnID)))
|
||||
return tefWRONG_PRIOR;
|
||||
|
||||
mTxnAccount->setFieldU32 (sfSequence, t_seq + 1);
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
// check stuff before you bother to lock the ledger
|
||||
TER Transactor::preCheck ()
|
||||
{
|
||||
mTxnAccountID = mTxn.getSourceAccount ().getAccountID ();
|
||||
|
||||
if (!mTxnAccountID)
|
||||
{
|
||||
WriteLog (lsWARNING, Transactor) << "applyTransaction: bad source id";
|
||||
|
||||
return temBAD_SRC_ACCOUNT;
|
||||
}
|
||||
|
||||
// Extract signing key
|
||||
// Transactions contain a signing key. This allows us to trivially verify a transaction has at least been properly signed
|
||||
// without going to disk. Each transaction also notes a source account id. This is used to verify that the signing key is
|
||||
// associated with the account.
|
||||
// XXX This could be a lot cleaner to prevent unnecessary copying.
|
||||
mSigningPubKey = RippleAddress::createAccountPublic (mTxn.getSigningPubKey ());
|
||||
|
||||
// Consistency: really signed.
|
||||
if (!mTxn.isKnownGood ())
|
||||
{
|
||||
if (mTxn.isKnownBad () || (!isSetBit (mParams, tapNO_CHECK_SIGN) && !mTxn.checkSign (mSigningPubKey)))
|
||||
{
|
||||
mTxn.setBad ();
|
||||
WriteLog (lsWARNING, Transactor) << "applyTransaction: Invalid transaction: bad signature";
|
||||
return temINVALID;
|
||||
}
|
||||
|
||||
mTxn.setGood ();
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER Transactor::apply ()
|
||||
{
|
||||
TER terResult = tesSUCCESS;
|
||||
terResult = preCheck ();
|
||||
|
||||
if (terResult != tesSUCCESS) return (terResult);
|
||||
|
||||
boost::recursive_mutex::scoped_lock sl (mEngine->getLedger ()->mLock);
|
||||
|
||||
mTxnAccount = mEngine->entryCache (ltACCOUNT_ROOT, Ledger::getAccountRootIndex (mTxnAccountID));
|
||||
calculateFee ();
|
||||
|
||||
// Find source account
|
||||
// If are only forwarding, due to resource limitations, we might verifying only some transactions, this would be probabilistic.
|
||||
|
||||
if (!mTxnAccount)
|
||||
{
|
||||
if (mustHaveValidAccount ())
|
||||
{
|
||||
WriteLog (lsTRACE, Transactor) << boost::str (boost::format ("applyTransaction: Delay transaction: source account does not exist: %s") %
|
||||
mTxn.getSourceAccount ().humanAccountID ());
|
||||
|
||||
return terNO_ACCOUNT;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mPriorBalance = mTxnAccount->getFieldAmount (sfBalance);
|
||||
mSourceBalance = mPriorBalance;
|
||||
mHasAuthKey = mTxnAccount->isFieldPresent (sfRegularKey);
|
||||
}
|
||||
|
||||
terResult = checkSeq ();
|
||||
|
||||
if (terResult != tesSUCCESS) return (terResult);
|
||||
|
||||
terResult = payFee ();
|
||||
|
||||
if (terResult != tesSUCCESS) return (terResult);
|
||||
|
||||
terResult = checkSig ();
|
||||
|
||||
if (terResult != tesSUCCESS) return (terResult);
|
||||
|
||||
if (mTxnAccount)
|
||||
mEngine->entryModify (mTxnAccount);
|
||||
|
||||
return doApply ();
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
@@ -1,55 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef __TRANSACTOR__
|
||||
#define __TRANSACTOR__
|
||||
|
||||
class Transactor
|
||||
{
|
||||
public:
|
||||
typedef boost::shared_ptr<Transactor> pointer;
|
||||
|
||||
static UPTR_T<Transactor> makeTransactor (const SerializedTransaction& txn, TransactionEngineParams params, TransactionEngine* engine);
|
||||
|
||||
TER apply ();
|
||||
|
||||
protected:
|
||||
const SerializedTransaction& mTxn;
|
||||
TransactionEngine* mEngine;
|
||||
TransactionEngineParams mParams;
|
||||
|
||||
uint160 mTxnAccountID;
|
||||
STAmount mFeeDue;
|
||||
STAmount mPriorBalance; // Balance before fees.
|
||||
STAmount mSourceBalance; // Balance after fees.
|
||||
SLE::pointer mTxnAccount;
|
||||
bool mHasAuthKey;
|
||||
bool mSigMaster;
|
||||
RippleAddress mSigningPubKey;
|
||||
|
||||
virtual TER preCheck ();
|
||||
virtual TER checkSeq ();
|
||||
virtual TER payFee ();
|
||||
|
||||
void calculateFee ();
|
||||
|
||||
// Returns the fee, not scaled for load (Should be in fee units. FIXME)
|
||||
virtual uint64 calculateBaseFee ();
|
||||
|
||||
virtual TER checkSig ();
|
||||
virtual TER doApply () = 0;
|
||||
|
||||
Transactor (const SerializedTransaction& txn, TransactionEngineParams params, TransactionEngine* engine);
|
||||
|
||||
virtual bool mustHaveValidAccount ()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
// vim:ts=4
|
||||
@@ -1,338 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
SETUP_LOG (TrustSetTransactor)
|
||||
|
||||
TER TrustSetTransactor::doApply ()
|
||||
{
|
||||
TER terResult = tesSUCCESS;
|
||||
WriteLog (lsINFO, TrustSetTransactor) << "doTrustSet>";
|
||||
|
||||
const STAmount saLimitAmount = mTxn.getFieldAmount (sfLimitAmount);
|
||||
const bool bQualityIn = mTxn.isFieldPresent (sfQualityIn);
|
||||
const bool bQualityOut = mTxn.isFieldPresent (sfQualityOut);
|
||||
const uint160 uCurrencyID = saLimitAmount.getCurrency ();
|
||||
uint160 uDstAccountID = saLimitAmount.getIssuer ();
|
||||
const bool bHigh = mTxnAccountID > uDstAccountID; // true, iff current is high account.
|
||||
|
||||
uint32 uQualityIn = bQualityIn ? mTxn.getFieldU32 (sfQualityIn) : 0;
|
||||
uint32 uQualityOut = bQualityOut ? mTxn.getFieldU32 (sfQualityOut) : 0;
|
||||
|
||||
if (!saLimitAmount.isLegalNet ())
|
||||
return temBAD_AMOUNT;
|
||||
|
||||
if (bQualityIn && QUALITY_ONE == uQualityIn)
|
||||
uQualityIn = 0;
|
||||
|
||||
if (bQualityOut && QUALITY_ONE == uQualityOut)
|
||||
uQualityOut = 0;
|
||||
|
||||
const uint32 uTxFlags = mTxn.getFlags ();
|
||||
|
||||
if (uTxFlags & tfTrustSetMask)
|
||||
{
|
||||
WriteLog (lsINFO, TrustSetTransactor) << "doTrustSet: Malformed transaction: Invalid flags set.";
|
||||
|
||||
return temINVALID_FLAG;
|
||||
}
|
||||
|
||||
const bool bSetAuth = isSetBit (uTxFlags, tfSetfAuth);
|
||||
|
||||
if (bSetAuth && !isSetBit (mTxnAccount->getFieldU32 (sfFlags), lsfRequireAuth))
|
||||
{
|
||||
WriteLog (lsINFO, TrustSetTransactor) << "doTrustSet: Retry: Auth not required.";
|
||||
|
||||
return tefNO_AUTH_REQUIRED;
|
||||
}
|
||||
|
||||
if (saLimitAmount.isNative ())
|
||||
{
|
||||
WriteLog (lsINFO, TrustSetTransactor) << boost::str (boost::format ("doTrustSet: Malformed transaction: Native credit limit: %s")
|
||||
% saLimitAmount.getFullText ());
|
||||
|
||||
return temBAD_LIMIT;
|
||||
}
|
||||
|
||||
if (saLimitAmount.isNegative ())
|
||||
{
|
||||
WriteLog (lsINFO, TrustSetTransactor) << "doTrustSet: Malformed transaction: Negative credit limit.";
|
||||
|
||||
return temBAD_LIMIT;
|
||||
}
|
||||
|
||||
// Check if destination makes sense.
|
||||
if (!uDstAccountID || uDstAccountID == ACCOUNT_ONE)
|
||||
{
|
||||
WriteLog (lsINFO, TrustSetTransactor) << "doTrustSet: Malformed transaction: Destination account not specified.";
|
||||
|
||||
return temDST_NEEDED;
|
||||
}
|
||||
|
||||
if (mTxnAccountID == uDstAccountID)
|
||||
{
|
||||
SLE::pointer selDelete = mEngine->entryCache (ltRIPPLE_STATE, Ledger::getRippleStateIndex (mTxnAccountID, uDstAccountID, uCurrencyID));
|
||||
|
||||
if (selDelete)
|
||||
{
|
||||
WriteLog (lsWARNING, TrustSetTransactor) << "doTrustSet: Clearing redundant line.";
|
||||
|
||||
return mEngine->getNodes ().trustDelete (selDelete, mTxnAccountID, uDstAccountID);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog (lsINFO, TrustSetTransactor) << "doTrustSet: Malformed transaction: Can not extend credit to self.";
|
||||
|
||||
return temDST_IS_SRC;
|
||||
}
|
||||
}
|
||||
|
||||
SLE::pointer sleDst = mEngine->entryCache (ltACCOUNT_ROOT, Ledger::getAccountRootIndex (uDstAccountID));
|
||||
|
||||
if (!sleDst)
|
||||
{
|
||||
WriteLog (lsINFO, TrustSetTransactor) << "doTrustSet: Delay transaction: Destination account does not exist.";
|
||||
|
||||
return tecNO_DST;
|
||||
}
|
||||
|
||||
const uint32 uOwnerCount = mTxnAccount->getFieldU32 (sfOwnerCount);
|
||||
// The reserve required to create the line.
|
||||
const uint64 uReserveCreate = mEngine->getLedger ()->getReserve (uOwnerCount + 1);
|
||||
|
||||
STAmount saLimitAllow = saLimitAmount;
|
||||
saLimitAllow.setIssuer (mTxnAccountID);
|
||||
|
||||
SLE::pointer sleRippleState = mEngine->entryCache (ltRIPPLE_STATE, Ledger::getRippleStateIndex (mTxnAccountID, uDstAccountID, uCurrencyID));
|
||||
|
||||
if (sleRippleState)
|
||||
{
|
||||
STAmount saLowBalance;
|
||||
STAmount saLowLimit;
|
||||
STAmount saHighBalance;
|
||||
STAmount saHighLimit;
|
||||
uint32 uLowQualityIn;
|
||||
uint32 uLowQualityOut;
|
||||
uint32 uHighQualityIn;
|
||||
uint32 uHighQualityOut;
|
||||
const uint160& uLowAccountID = !bHigh ? mTxnAccountID : uDstAccountID;
|
||||
const uint160& uHighAccountID = bHigh ? mTxnAccountID : uDstAccountID;
|
||||
SLE::ref sleLowAccount = !bHigh ? mTxnAccount : sleDst;
|
||||
SLE::ref sleHighAccount = bHigh ? mTxnAccount : sleDst;
|
||||
|
||||
//
|
||||
// Balances
|
||||
//
|
||||
|
||||
saLowBalance = sleRippleState->getFieldAmount (sfBalance);
|
||||
saHighBalance = -saLowBalance;
|
||||
|
||||
//
|
||||
// Limits
|
||||
//
|
||||
|
||||
sleRippleState->setFieldAmount (!bHigh ? sfLowLimit : sfHighLimit, saLimitAllow);
|
||||
|
||||
saLowLimit = !bHigh ? saLimitAllow : sleRippleState->getFieldAmount (sfLowLimit);
|
||||
saHighLimit = bHigh ? saLimitAllow : sleRippleState->getFieldAmount (sfHighLimit);
|
||||
|
||||
//
|
||||
// Quality in
|
||||
//
|
||||
|
||||
if (!bQualityIn)
|
||||
{
|
||||
// Not setting. Just get it.
|
||||
|
||||
uLowQualityIn = sleRippleState->getFieldU32 (sfLowQualityIn);
|
||||
uHighQualityIn = sleRippleState->getFieldU32 (sfHighQualityIn);
|
||||
}
|
||||
else if (uQualityIn)
|
||||
{
|
||||
// Setting.
|
||||
|
||||
sleRippleState->setFieldU32 (!bHigh ? sfLowQualityIn : sfHighQualityIn, uQualityIn);
|
||||
|
||||
uLowQualityIn = !bHigh ? uQualityIn : sleRippleState->getFieldU32 (sfLowQualityIn);
|
||||
uHighQualityIn = bHigh ? uQualityIn : sleRippleState->getFieldU32 (sfHighQualityIn);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Clearing.
|
||||
|
||||
sleRippleState->makeFieldAbsent (!bHigh ? sfLowQualityIn : sfHighQualityIn);
|
||||
|
||||
uLowQualityIn = !bHigh ? 0 : sleRippleState->getFieldU32 (sfLowQualityIn);
|
||||
uHighQualityIn = bHigh ? 0 : sleRippleState->getFieldU32 (sfHighQualityIn);
|
||||
}
|
||||
|
||||
if (QUALITY_ONE == uLowQualityIn) uLowQualityIn = 0;
|
||||
|
||||
if (QUALITY_ONE == uHighQualityIn) uHighQualityIn = 0;
|
||||
|
||||
//
|
||||
// Quality out
|
||||
//
|
||||
|
||||
if (!bQualityOut)
|
||||
{
|
||||
// Not setting. Just get it.
|
||||
|
||||
uLowQualityOut = sleRippleState->getFieldU32 (sfLowQualityOut);
|
||||
uHighQualityOut = sleRippleState->getFieldU32 (sfHighQualityOut);
|
||||
}
|
||||
else if (uQualityOut)
|
||||
{
|
||||
// Setting.
|
||||
|
||||
sleRippleState->setFieldU32 (!bHigh ? sfLowQualityOut : sfHighQualityOut, uQualityOut);
|
||||
|
||||
uLowQualityOut = !bHigh ? uQualityOut : sleRippleState->getFieldU32 (sfLowQualityOut);
|
||||
uHighQualityOut = bHigh ? uQualityOut : sleRippleState->getFieldU32 (sfHighQualityOut);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Clearing.
|
||||
|
||||
sleRippleState->makeFieldAbsent (!bHigh ? sfLowQualityOut : sfHighQualityOut);
|
||||
|
||||
uLowQualityOut = !bHigh ? 0 : sleRippleState->getFieldU32 (sfLowQualityOut);
|
||||
uHighQualityOut = bHigh ? 0 : sleRippleState->getFieldU32 (sfHighQualityOut);
|
||||
}
|
||||
|
||||
if (QUALITY_ONE == uLowQualityOut) uLowQualityOut = 0;
|
||||
|
||||
if (QUALITY_ONE == uHighQualityOut) uHighQualityOut = 0;
|
||||
|
||||
const bool bLowReserveSet = uLowQualityIn || uLowQualityOut || !!saLowLimit || saLowBalance.isPositive ();
|
||||
const bool bLowReserveClear = !bLowReserveSet;
|
||||
|
||||
const bool bHighReserveSet = uHighQualityIn || uHighQualityOut || !!saHighLimit || saHighBalance.isPositive ();
|
||||
const bool bHighReserveClear = !bHighReserveSet;
|
||||
|
||||
const bool bDefault = bLowReserveClear && bHighReserveClear;
|
||||
|
||||
const uint32 uFlagsIn = sleRippleState->getFieldU32 (sfFlags);
|
||||
uint32 uFlagsOut = uFlagsIn;
|
||||
|
||||
const bool bLowReserved = isSetBit (uFlagsIn, lsfLowReserve);
|
||||
const bool bHighReserved = isSetBit (uFlagsIn, lsfHighReserve);
|
||||
|
||||
bool bReserveIncrease = false;
|
||||
|
||||
if (bSetAuth)
|
||||
{
|
||||
uFlagsOut |= (bHigh ? lsfHighAuth : lsfLowAuth);
|
||||
}
|
||||
|
||||
if (bLowReserveSet && !bLowReserved)
|
||||
{
|
||||
// Set reserve for low account.
|
||||
|
||||
mEngine->getNodes ().ownerCountAdjust (uLowAccountID, 1, sleLowAccount);
|
||||
uFlagsOut |= lsfLowReserve;
|
||||
|
||||
if (!bHigh)
|
||||
bReserveIncrease = true;
|
||||
}
|
||||
|
||||
if (bLowReserveClear && bLowReserved)
|
||||
{
|
||||
// Clear reserve for low account.
|
||||
|
||||
mEngine->getNodes ().ownerCountAdjust (uLowAccountID, -1, sleLowAccount);
|
||||
uFlagsOut &= ~lsfLowReserve;
|
||||
}
|
||||
|
||||
if (bHighReserveSet && !bHighReserved)
|
||||
{
|
||||
// Set reserve for high account.
|
||||
|
||||
mEngine->getNodes ().ownerCountAdjust (uHighAccountID, 1, sleHighAccount);
|
||||
uFlagsOut |= lsfHighReserve;
|
||||
|
||||
if (bHigh)
|
||||
bReserveIncrease = true;
|
||||
}
|
||||
|
||||
if (bHighReserveClear && bHighReserved)
|
||||
{
|
||||
// Clear reserve for high account.
|
||||
|
||||
mEngine->getNodes ().ownerCountAdjust (uHighAccountID, -1, sleHighAccount);
|
||||
uFlagsOut &= ~lsfHighReserve;
|
||||
}
|
||||
|
||||
if (uFlagsIn != uFlagsOut)
|
||||
sleRippleState->setFieldU32 (sfFlags, uFlagsOut);
|
||||
|
||||
if (bDefault || CURRENCY_BAD == uCurrencyID)
|
||||
{
|
||||
// Delete.
|
||||
|
||||
terResult = mEngine->getNodes ().trustDelete (sleRippleState, uLowAccountID, uHighAccountID);
|
||||
}
|
||||
else if (bReserveIncrease
|
||||
&& mPriorBalance.getNValue () < uReserveCreate) // Reserve is not scaled by load.
|
||||
{
|
||||
WriteLog (lsINFO, TrustSetTransactor) << "doTrustSet: Delay transaction: Insufficent reserve to add trust line.";
|
||||
|
||||
// Another transaction could provide XRP to the account and then this transaction would succeed.
|
||||
terResult = tecINSUF_RESERVE_LINE;
|
||||
}
|
||||
else
|
||||
{
|
||||
mEngine->entryModify (sleRippleState);
|
||||
|
||||
WriteLog (lsINFO, TrustSetTransactor) << "doTrustSet: Modify ripple line";
|
||||
}
|
||||
}
|
||||
// Line does not exist.
|
||||
else if (!saLimitAmount // Setting default limit.
|
||||
&& (!bQualityIn || !uQualityIn) // Not setting quality in or setting default quality in.
|
||||
&& (!bQualityOut || !uQualityOut)) // Not setting quality out or setting default quality out.
|
||||
{
|
||||
WriteLog (lsINFO, TrustSetTransactor) << "doTrustSet: Redundant: Setting non-existent ripple line to defaults.";
|
||||
|
||||
return tecNO_LINE_REDUNDANT;
|
||||
}
|
||||
else if (mPriorBalance.getNValue () < uReserveCreate) // Reserve is not scaled by load.
|
||||
{
|
||||
WriteLog (lsINFO, TrustSetTransactor) << "doTrustSet: Delay transaction: Line does not exist. Insufficent reserve to create line.";
|
||||
|
||||
// Another transaction could create the account and then this transaction would succeed.
|
||||
terResult = tecNO_LINE_INSUF_RESERVE;
|
||||
}
|
||||
else if (CURRENCY_BAD == uCurrencyID)
|
||||
{
|
||||
terResult = temBAD_CURRENCY;
|
||||
}
|
||||
else
|
||||
{
|
||||
STAmount saBalance = STAmount (uCurrencyID, ACCOUNT_ONE); // Zero balance in currency.
|
||||
|
||||
WriteLog (lsINFO, TrustSetTransactor) << "doTrustSet: Creating ripple line: "
|
||||
<< Ledger::getRippleStateIndex (mTxnAccountID, uDstAccountID, uCurrencyID).ToString ();
|
||||
|
||||
// Create a new ripple line.
|
||||
terResult = mEngine->getNodes ().trustCreate (
|
||||
bHigh,
|
||||
mTxnAccountID,
|
||||
uDstAccountID,
|
||||
Ledger::getRippleStateIndex (mTxnAccountID, uDstAccountID, uCurrencyID),
|
||||
mTxnAccount,
|
||||
bSetAuth,
|
||||
saBalance,
|
||||
saLimitAllow, // Limit for who is being charged.
|
||||
uQualityIn,
|
||||
uQualityOut);
|
||||
}
|
||||
|
||||
WriteLog (lsINFO, TrustSetTransactor) << "doTrustSet<";
|
||||
|
||||
return terResult;
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
@@ -1,19 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef TRUSTSETTRANSACTOR_H
|
||||
#define TRUSTSETTRANSACTOR_H
|
||||
|
||||
class TrustSetTransactor : public Transactor
|
||||
{
|
||||
public:
|
||||
TrustSetTransactor (const SerializedTransaction& txn, TransactionEngineParams params, TransactionEngine* engine) : Transactor (txn, params, engine) {}
|
||||
|
||||
TER doApply ();
|
||||
};
|
||||
#endif
|
||||
|
||||
// vim:ts=4
|
||||
@@ -1,7 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
SETUP_LOGN (WSConnectionLog,"WSConnection")
|
||||
@@ -1,284 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
|
||||
#ifndef RIPPLE_WSCONNECTION_H
|
||||
#define RIPPLE_WSCONNECTION_H
|
||||
|
||||
// This is for logging
|
||||
struct WSConnectionLog;
|
||||
|
||||
template <typename endpoint_type>
|
||||
class WSServerHandler;
|
||||
//
|
||||
// Storage for connection specific info
|
||||
// - Subscriptions
|
||||
//
|
||||
template <typename endpoint_type>
|
||||
class WSConnection
|
||||
: public InfoSub
|
||||
, public boost::enable_shared_from_this< WSConnection<endpoint_type> >
|
||||
, public CountedObject <WSConnection <endpoint_type> >
|
||||
{
|
||||
public:
|
||||
static char const* getCountedObjectName () { return "WSConnection"; }
|
||||
|
||||
typedef typename endpoint_type::connection_type connection;
|
||||
typedef typename boost::shared_ptr<connection> connection_ptr;
|
||||
typedef typename boost::weak_ptr<connection> weak_connection_ptr;
|
||||
typedef typename endpoint_type::handler::message_ptr message_ptr;
|
||||
|
||||
public:
|
||||
// WSConnection()
|
||||
// : mHandler((WSServerHandler<websocketpp::WSDOOR_SERVER>*)(NULL)),
|
||||
// mConnection(connection_ptr()) { ; }
|
||||
|
||||
WSConnection (WSServerHandler<endpoint_type>* wshpHandler, const connection_ptr& cpConnection)
|
||||
: mHandler (wshpHandler), mConnection (cpConnection), mNetwork (getApp().getOPs ()),
|
||||
mRemoteIP (cpConnection->get_socket ().lowest_layer ().remote_endpoint ().address ().to_string ()),
|
||||
mLoadSource (mRemoteIP), mPingTimer (cpConnection->get_io_service ()), mPinged (false),
|
||||
mRcvQueueRunning (false), mDead (false)
|
||||
{
|
||||
WriteLog (lsDEBUG, WSConnectionLog) << "Websocket connection from " << mRemoteIP;
|
||||
setPingTimer ();
|
||||
}
|
||||
|
||||
void preDestroy ()
|
||||
{
|
||||
// sever connection
|
||||
mPingTimer.cancel ();
|
||||
mConnection.reset ();
|
||||
|
||||
boost::recursive_mutex::scoped_lock sl (mRcvQueueLock);
|
||||
mDead = true;
|
||||
}
|
||||
|
||||
virtual ~WSConnection ()
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
static void destroy (boost::shared_ptr< WSConnection<endpoint_type> >)
|
||||
{
|
||||
// Just discards the reference
|
||||
}
|
||||
|
||||
// Implement overridden functions from base class:
|
||||
void send (const Json::Value& jvObj, bool broadcast)
|
||||
{
|
||||
connection_ptr ptr = mConnection.lock ();
|
||||
|
||||
if (ptr)
|
||||
mHandler->send (ptr, jvObj, broadcast);
|
||||
}
|
||||
|
||||
void send (const Json::Value& jvObj, const std::string& sObj, bool broadcast)
|
||||
{
|
||||
connection_ptr ptr = mConnection.lock ();
|
||||
|
||||
if (ptr)
|
||||
mHandler->send (ptr, sObj, broadcast);
|
||||
}
|
||||
|
||||
// Utilities
|
||||
Json::Value invokeCommand (Json::Value& jvRequest)
|
||||
{
|
||||
if (getApp().getLoadManager ().shouldCutoff (mLoadSource))
|
||||
{
|
||||
// VFALCO TODO This must be implemented before open sourcing
|
||||
|
||||
#if SHOULD_DISCONNECT
|
||||
// FIXME: Must dispatch to strand
|
||||
connection_ptr ptr = mConnection.lock ();
|
||||
|
||||
if (ptr)
|
||||
ptr->close (websocketpp::close::status::PROTOCOL_ERROR, "overload");
|
||||
|
||||
return rpcError (rpcSLOW_DOWN);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Requests without "command" are invalid.
|
||||
//
|
||||
if (!jvRequest.isMember ("command"))
|
||||
{
|
||||
Json::Value jvResult (Json::objectValue);
|
||||
|
||||
jvResult["type"] = "response";
|
||||
jvResult["status"] = "error";
|
||||
jvResult["error"] = "missingCommand";
|
||||
jvResult["request"] = jvRequest;
|
||||
|
||||
if (jvRequest.isMember ("id"))
|
||||
{
|
||||
jvResult["id"] = jvRequest["id"];
|
||||
}
|
||||
|
||||
getApp().getLoadManager ().applyLoadCharge (mLoadSource, LT_RPCInvalid);
|
||||
|
||||
return jvResult;
|
||||
}
|
||||
|
||||
LoadType loadType = LT_RPCReference;
|
||||
RPCHandler mRPCHandler (&mNetwork, boost::dynamic_pointer_cast<InfoSub> (this->shared_from_this ()));
|
||||
Json::Value jvResult (Json::objectValue);
|
||||
|
||||
int iRole = mHandler->getPublic ()
|
||||
? RPCHandler::GUEST // Don't check on the public interface.
|
||||
: iAdminGet (jvRequest, mRemoteIP);
|
||||
|
||||
if (RPCHandler::FORBID == iRole)
|
||||
{
|
||||
jvResult["result"] = rpcError (rpcFORBIDDEN);
|
||||
}
|
||||
else
|
||||
{
|
||||
jvResult["result"] = mRPCHandler.doCommand (jvRequest, iRole, &loadType);
|
||||
}
|
||||
|
||||
// Debit/credit the load and see if we should include a warning.
|
||||
//
|
||||
if (getApp().getLoadManager ().applyLoadCharge (mLoadSource, loadType) &&
|
||||
getApp().getLoadManager ().shouldWarn (mLoadSource))
|
||||
{
|
||||
jvResult["warning"] = "load";
|
||||
}
|
||||
|
||||
// Currently we will simply unwrap errors returned by the RPC
|
||||
// API, in the future maybe we can make the responses
|
||||
// consistent.
|
||||
//
|
||||
// Regularize result. This is duplicate code.
|
||||
if (jvResult["result"].isMember ("error"))
|
||||
{
|
||||
jvResult = jvResult["result"];
|
||||
jvResult["status"] = "error";
|
||||
jvResult["request"] = jvRequest;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
jvResult["status"] = "success";
|
||||
}
|
||||
|
||||
if (jvRequest.isMember ("id"))
|
||||
{
|
||||
jvResult["id"] = jvRequest["id"];
|
||||
}
|
||||
|
||||
jvResult["type"] = "response";
|
||||
|
||||
return jvResult;
|
||||
}
|
||||
|
||||
bool onPingTimer (std::string&)
|
||||
{
|
||||
#ifdef DISCONNECT_ON_WEBSOCKET_PING_TIMEOUTS
|
||||
|
||||
if (mPinged)
|
||||
return true; // causes connection to close
|
||||
|
||||
#endif
|
||||
mPinged = true;
|
||||
setPingTimer ();
|
||||
return false; // causes ping to be sent
|
||||
}
|
||||
|
||||
void onPong (const std::string&)
|
||||
{
|
||||
mPinged = false;
|
||||
}
|
||||
|
||||
static void pingTimer (weak_connection_ptr c, WSServerHandler<endpoint_type>* h, const boost::system::error_code& e)
|
||||
{
|
||||
if (e)
|
||||
return;
|
||||
|
||||
connection_ptr ptr = c.lock ();
|
||||
|
||||
if (ptr)
|
||||
h->pingTimer (ptr);
|
||||
}
|
||||
|
||||
void setPingTimer ()
|
||||
{
|
||||
connection_ptr ptr = mConnection.lock ();
|
||||
|
||||
if (ptr)
|
||||
{
|
||||
mPingTimer.expires_from_now (boost::posix_time::seconds (theConfig.WEBSOCKET_PING_FREQ));
|
||||
mPingTimer.async_wait (ptr->get_strand ().wrap (boost::bind (
|
||||
&WSConnection<endpoint_type>::pingTimer, mConnection, mHandler, boost::asio::placeholders::error)));
|
||||
}
|
||||
}
|
||||
|
||||
void rcvMessage (message_ptr msg, bool& msgRejected, bool& runQueue)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl (mRcvQueueLock);
|
||||
|
||||
if (mDead)
|
||||
{
|
||||
msgRejected = false;
|
||||
runQueue = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (mDead || (mRcvQueue.size () >= 1000))
|
||||
{
|
||||
msgRejected = !mDead;
|
||||
runQueue = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
msgRejected = false;
|
||||
mRcvQueue.push (msg);
|
||||
|
||||
if (mRcvQueueRunning)
|
||||
runQueue = false;
|
||||
else
|
||||
{
|
||||
runQueue = true;
|
||||
mRcvQueueRunning = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
message_ptr getMessage ()
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl (mRcvQueueLock);
|
||||
|
||||
if (mDead || mRcvQueue.empty ())
|
||||
{
|
||||
mRcvQueueRunning = false;
|
||||
return message_ptr ();
|
||||
}
|
||||
|
||||
message_ptr m = mRcvQueue.front ();
|
||||
mRcvQueue.pop ();
|
||||
return m;
|
||||
}
|
||||
|
||||
private:
|
||||
typedef void (WSConnection::*doFuncPtr) (Json::Value& jvResult, Json::Value& jvRequest);
|
||||
|
||||
WSServerHandler<endpoint_type>* mHandler;
|
||||
weak_connection_ptr mConnection;
|
||||
NetworkOPs& mNetwork;
|
||||
std::string mRemoteIP;
|
||||
LoadSource mLoadSource;
|
||||
|
||||
boost::asio::deadline_timer mPingTimer;
|
||||
bool mPinged;
|
||||
|
||||
boost::recursive_mutex mRcvQueueLock;
|
||||
std::queue<message_ptr> mRcvQueue;
|
||||
bool mRcvQueueRunning;
|
||||
bool mDead;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
// vim:ts=4
|
||||
@@ -1,101 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
SETUP_LOG (WSDoor)
|
||||
|
||||
//
|
||||
// This is a light weight, untrusted interface for web clients.
|
||||
// For now we don't provide proof. Later we will.
|
||||
//
|
||||
// Might need to support this header for browsers: Access-Control-Allow-Origin: *
|
||||
// - https://developer.mozilla.org/en-US/docs/HTTP_access_control
|
||||
//
|
||||
|
||||
//
|
||||
// Strategy:
|
||||
// - We only talk to NetworkOPs (so we will work even in thin mode)
|
||||
// - NetworkOPs is smart enough to subscribe and or pass back messages
|
||||
//
|
||||
// VFALCO NOTE NetworkOPs isn't used here...
|
||||
//
|
||||
void WSDoor::startListening ()
|
||||
{
|
||||
setCallingThreadName ("websocket");
|
||||
// Generate a single SSL context for use by all connections.
|
||||
boost::shared_ptr<boost::asio::ssl::context> mCtx;
|
||||
mCtx = boost::make_shared<boost::asio::ssl::context> (boost::asio::ssl::context::sslv23);
|
||||
|
||||
mCtx->set_options (
|
||||
boost::asio::ssl::context::default_workarounds
|
||||
| boost::asio::ssl::context::no_sslv2
|
||||
| boost::asio::ssl::context::single_dh_use);
|
||||
|
||||
SSL_CTX_set_tmp_dh_callback (mCtx->native_handle (), handleTmpDh);
|
||||
|
||||
// Construct a single handler for all requests.
|
||||
websocketpp::server_autotls::handler::ptr handler (new WSServerHandler<websocketpp::server_autotls> (mCtx, mPublic));
|
||||
|
||||
// Construct a websocket server.
|
||||
mSEndpoint = new websocketpp::server_autotls (handler);
|
||||
|
||||
// mEndpoint->alog().unset_level(websocketpp::log::alevel::ALL);
|
||||
// mEndpoint->elog().unset_level(websocketpp::log::elevel::ALL);
|
||||
|
||||
// Call the main-event-loop of the websocket server.
|
||||
try
|
||||
{
|
||||
mSEndpoint->listen (
|
||||
boost::asio::ip::tcp::endpoint (
|
||||
boost::asio::ip::address ().from_string (mIp), mPort));
|
||||
}
|
||||
catch (websocketpp::exception& e)
|
||||
{
|
||||
WriteLog (lsWARNING, WSDoor) << "websocketpp exception: " << e.what ();
|
||||
|
||||
while (1) // temporary workaround for websocketpp throwing exceptions on access/close races
|
||||
{
|
||||
// https://github.com/zaphoyd/websocketpp/issues/98
|
||||
try
|
||||
{
|
||||
mSEndpoint->get_io_service ().run ();
|
||||
break;
|
||||
}
|
||||
catch (websocketpp::exception& e)
|
||||
{
|
||||
WriteLog (lsWARNING, WSDoor) << "websocketpp exception: " << e.what ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete mSEndpoint;
|
||||
}
|
||||
|
||||
WSDoor* WSDoor::createWSDoor (const std::string& strIp, const int iPort, bool bPublic)
|
||||
{
|
||||
WSDoor* wdpResult = new WSDoor (strIp, iPort, bPublic);
|
||||
|
||||
WriteLog (lsINFO, WSDoor) <<
|
||||
boost::str (boost::format ("Websocket: %s: Listening: %s %d ")
|
||||
% (bPublic ? "Public" : "Private")
|
||||
% strIp
|
||||
% iPort);
|
||||
|
||||
wdpResult->mThread = new boost::thread (BIND_TYPE (&WSDoor::startListening, wdpResult));
|
||||
|
||||
return wdpResult;
|
||||
}
|
||||
|
||||
void WSDoor::stop ()
|
||||
{
|
||||
if (mThread)
|
||||
{
|
||||
if (mSEndpoint)
|
||||
mSEndpoint->stop ();
|
||||
|
||||
|
||||
mThread->join ();
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_WSDOOR_RIPPLEHEADER
|
||||
#define RIPPLE_WSDOOR_RIPPLEHEADER
|
||||
|
||||
class WSDoor : LeakChecked <WSDoor>
|
||||
{
|
||||
private:
|
||||
websocketpp::server_autotls* mSEndpoint;
|
||||
|
||||
boost::thread* mThread;
|
||||
bool mPublic;
|
||||
std::string mIp;
|
||||
int mPort;
|
||||
|
||||
void startListening ();
|
||||
|
||||
public:
|
||||
|
||||
WSDoor (const std::string& strIp, int iPort, bool bPublic) : mSEndpoint (0), mThread (0), mPublic (bPublic), mIp (strIp), mPort (iPort)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
void stop ();
|
||||
|
||||
static WSDoor* createWSDoor (const std::string& strIp, const int iPort, bool bPublic);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
// vim:ts=4
|
||||
@@ -1,79 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
SETUP_LOG (WalletAddTransactor)
|
||||
|
||||
TER WalletAddTransactor::doApply ()
|
||||
{
|
||||
Log::out() << "WalletAdd>";
|
||||
|
||||
Blob const vucPubKey = mTxn.getFieldVL (sfPublicKey);
|
||||
Blob const vucSignature = mTxn.getFieldVL (sfSignature);
|
||||
const uint160 uAuthKeyID = mTxn.getFieldAccount160 (sfRegularKey);
|
||||
const RippleAddress naMasterPubKey = RippleAddress::createAccountPublic (vucPubKey);
|
||||
const uint160 uDstAccountID = naMasterPubKey.getAccountID ();
|
||||
|
||||
const uint32 uTxFlags = mTxn.getFlags ();
|
||||
|
||||
if (uTxFlags)
|
||||
{
|
||||
WriteLog (lsINFO, WalletAddTransactor) << "WalletAdd: Malformed transaction: Invalid flags set.";
|
||||
|
||||
return temINVALID_FLAG;
|
||||
}
|
||||
|
||||
// FIXME: This should be moved to the transaction's signature check logic and cached
|
||||
if (!naMasterPubKey.accountPublicVerify (Serializer::getSHA512Half (uAuthKeyID.begin (), uAuthKeyID.size ()), vucSignature))
|
||||
{
|
||||
Log::out() << "WalletAdd: unauthorized: bad signature ";
|
||||
|
||||
return tefBAD_ADD_AUTH;
|
||||
}
|
||||
|
||||
SLE::pointer sleDst = mEngine->entryCache (ltACCOUNT_ROOT, Ledger::getAccountRootIndex (uDstAccountID));
|
||||
|
||||
if (sleDst)
|
||||
{
|
||||
Log::out() << "WalletAdd: account already created";
|
||||
|
||||
return tefCREATED;
|
||||
}
|
||||
|
||||
// Direct XRP payment.
|
||||
|
||||
STAmount saDstAmount = mTxn.getFieldAmount (sfAmount);
|
||||
const STAmount saSrcBalance = mTxnAccount->getFieldAmount (sfBalance);
|
||||
const uint32 uOwnerCount = mTxnAccount->getFieldU32 (sfOwnerCount);
|
||||
const uint64 uReserve = mEngine->getLedger ()->getReserve (uOwnerCount);
|
||||
STAmount saPaid = mTxn.getTransactionFee ();
|
||||
|
||||
// Make sure have enough reserve to send. Allow final spend to use reserve for fee.
|
||||
if (saSrcBalance + saPaid < saDstAmount + uReserve) // Reserve is not scaled by fee.
|
||||
{
|
||||
// Vote no. However, transaction might succeed, if applied in a different order.
|
||||
WriteLog (lsINFO, WalletAddTransactor) << boost::str (boost::format ("WalletAdd: Delay transaction: Insufficient funds: %s / %s (%d)")
|
||||
% saSrcBalance.getText () % (saDstAmount + uReserve).getText () % uReserve);
|
||||
|
||||
return tecUNFUNDED_ADD;
|
||||
}
|
||||
|
||||
// Deduct initial balance from source account.
|
||||
mTxnAccount->setFieldAmount (sfBalance, saSrcBalance - saDstAmount);
|
||||
|
||||
// Create the account.
|
||||
sleDst = mEngine->entryCreate (ltACCOUNT_ROOT, Ledger::getAccountRootIndex (uDstAccountID));
|
||||
|
||||
sleDst->setFieldAccount (sfAccount, uDstAccountID);
|
||||
sleDst->setFieldU32 (sfSequence, 1);
|
||||
sleDst->setFieldAmount (sfBalance, saDstAmount);
|
||||
sleDst->setFieldAccount (sfRegularKey, uAuthKeyID);
|
||||
|
||||
Log::out() << "WalletAdd<";
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
@@ -1,19 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef WALLETADDTRANSACTOR_H
|
||||
#define WALLETADDTRANSACTOR_H
|
||||
|
||||
class WalletAddTransactor : public Transactor
|
||||
{
|
||||
public:
|
||||
WalletAddTransactor (const SerializedTransaction& txn, TransactionEngineParams params, TransactionEngine* engine) : Transactor (txn, params, engine) {}
|
||||
|
||||
TER doApply ();
|
||||
};
|
||||
#endif
|
||||
|
||||
// vim:ts=4
|
||||
@@ -1,364 +0,0 @@
|
||||
package protocol;
|
||||
|
||||
enum MessageType
|
||||
{
|
||||
// core
|
||||
mtHELLO = 1;
|
||||
mtERROR_MSG = 2;
|
||||
mtPING = 3;
|
||||
mtPROOFOFWORK = 4;
|
||||
mtCLUSTER = 5;
|
||||
|
||||
// network presence detection
|
||||
mtGET_CONTACTS = 10;
|
||||
mtCONTACT = 11;
|
||||
mtGET_PEERS = 12;
|
||||
mtPEERS = 13;
|
||||
|
||||
// operations for 'small' nodes
|
||||
mtSEARCH_TRANSACTION = 20;
|
||||
mtGET_ACCOUNT = 21;
|
||||
mtACCOUNT = 22;
|
||||
|
||||
// transaction and ledger processing
|
||||
mtTRANSACTION = 30;
|
||||
mtGET_LEDGER = 31;
|
||||
mtLEDGER_DATA = 32;
|
||||
mtPROPOSE_LEDGER = 33;
|
||||
mtSTATUS_CHANGE = 34;
|
||||
mtHAVE_SET = 35;
|
||||
|
||||
// data replication and synchronization
|
||||
mtGET_VALIDATIONS = 40;
|
||||
mtVALIDATION = 41;
|
||||
mtGET_OBJECTS = 42;
|
||||
}
|
||||
|
||||
|
||||
// token, iterations, target, challenge = issue demand for proof of work
|
||||
// token, response = give solution to proof of work
|
||||
// token, result = report result of pow
|
||||
|
||||
message TMProofWork
|
||||
{
|
||||
required string token = 1;
|
||||
optional uint32 iterations = 2;
|
||||
optional bytes target = 3;
|
||||
optional bytes challenge = 4;
|
||||
optional bytes response = 5;
|
||||
|
||||
enum POWResult
|
||||
{
|
||||
powrOK = 0;
|
||||
powrREUSED = 1;
|
||||
powrEXPIRED = 2; // You took too long solving
|
||||
powrTOOEASY = 3; // Difficulty went way up, sorry
|
||||
powrINVALID = 4;
|
||||
powrDISCONNECT = 5; // We are disconnecting
|
||||
}
|
||||
optional POWResult result = 6;
|
||||
}
|
||||
|
||||
// Sent on connect
|
||||
message TMHello
|
||||
{
|
||||
required uint32 protoVersion = 1;
|
||||
required uint32 protoVersionMin = 2;
|
||||
required bytes nodePublic = 3;
|
||||
required bytes nodeProof = 4;
|
||||
optional string fullVersion = 5;
|
||||
optional uint64 netTime = 6;
|
||||
optional uint32 ipv4Port = 7;
|
||||
optional uint32 ledgerIndex = 8;
|
||||
optional bytes ledgerClosed = 9; // our last closed ledger
|
||||
optional bytes ledgerPrevious = 10; // the ledger before the last closed ledger
|
||||
optional bool nodePrivate = 11; // Request to not forward IP.
|
||||
optional TMProofWork proofOfWork = 12; // request/provide proof of work
|
||||
optional bool testNet = 13; // Running as testnet.
|
||||
}
|
||||
|
||||
// The status of a node in our cluster
|
||||
message TMClusterNode
|
||||
{
|
||||
required string publicKey = 1;
|
||||
required uint32 reportTime = 2;
|
||||
required uint32 nodeLoad = 3;
|
||||
optional string nodeName = 4;
|
||||
optional string address = 5;
|
||||
}
|
||||
|
||||
// The status of all nodes in the cluster
|
||||
message TMCluster
|
||||
{
|
||||
repeated TMClusterNode clusterNodes = 1;
|
||||
}
|
||||
|
||||
|
||||
// A transaction can have only one input and one output.
|
||||
// If you want to send an amount that is greater than any single address of yours
|
||||
// you must first combine coins from one address to another.
|
||||
|
||||
enum TransactionStatus
|
||||
{
|
||||
tsNEW = 1; // origin node did/could not validate
|
||||
tsCURRENT = 2; // scheduled to go in this ledger
|
||||
tsCOMMITED = 3; // in a closed ledger
|
||||
tsREJECT_CONFLICT = 4;
|
||||
tsREJECT_INVALID = 5;
|
||||
tsREJECT_FUNDS = 6;
|
||||
tsHELD_SEQ = 7;
|
||||
tsHELD_LEDGER = 8; // held for future ledger
|
||||
}
|
||||
|
||||
message TMTransaction
|
||||
{
|
||||
required bytes rawTransaction = 1;
|
||||
required TransactionStatus status = 2;
|
||||
optional uint64 receiveTimestamp = 3;
|
||||
optional bool checkedSignature = 4; // no vouches for signature being correct
|
||||
}
|
||||
|
||||
|
||||
enum NodeStatus
|
||||
{
|
||||
nsCONNECTING = 1; // acquiring connections
|
||||
nsCONNECTED = 2; // convinced we are connected to the real network
|
||||
nsMONITORING = 3; // we know what the previous ledger is
|
||||
nsVALIDATING = 4; // we have the full ledger contents
|
||||
nsSHUTTING = 5; // node is shutting down
|
||||
}
|
||||
|
||||
enum NodeEvent
|
||||
{
|
||||
neCLOSING_LEDGER = 1; // closing a ledger because its close time has come
|
||||
neACCEPTED_LEDGER = 2; // accepting a closed ledger, we have finished computing it
|
||||
neSWITCHED_LEDGER = 3; // changing due to network consensus
|
||||
neLOST_SYNC = 4;
|
||||
}
|
||||
|
||||
message TMStatusChange
|
||||
{
|
||||
optional NodeStatus newStatus = 1;
|
||||
optional NodeEvent newEvent = 2;
|
||||
optional uint32 ledgerSeq = 3;
|
||||
optional bytes ledgerHash = 4;
|
||||
optional bytes ledgerHashPrevious = 5;
|
||||
optional uint64 networkTime = 6;
|
||||
optional uint32 firstSeq = 7;
|
||||
optional uint32 lastSeq = 8;
|
||||
}
|
||||
|
||||
|
||||
// Announce to the network our position on a closing ledger
|
||||
message TMProposeSet
|
||||
{
|
||||
required uint32 proposeSeq = 1;
|
||||
required bytes currentTxHash = 2; // the hash of the ledger we are proposing
|
||||
required bytes nodePubKey = 3;
|
||||
required uint32 closeTime = 4;
|
||||
required bytes signature = 5; // signature of above fields
|
||||
optional bytes previousledger = 6;
|
||||
optional bool checkedSignature = 7; // node vouches signature is correct
|
||||
repeated bytes addedTransactions = 10; // not required if number is large
|
||||
repeated bytes removedTransactions = 11; // not required if number is large
|
||||
}
|
||||
|
||||
enum TxSetStatus
|
||||
{
|
||||
tsHAVE = 1; // We have this set locally
|
||||
tsCAN_GET = 2; // We have a peer with this set
|
||||
tsNEED = 3; // We need this set and can't get it
|
||||
}
|
||||
|
||||
message TMHaveTransactionSet
|
||||
{
|
||||
required TxSetStatus status = 1;
|
||||
required bytes hash = 2;
|
||||
}
|
||||
|
||||
|
||||
// Used to sign a final closed ledger after reprocessing
|
||||
message TMValidation
|
||||
{
|
||||
required bytes validation = 1; // in SerializedValidation signed form
|
||||
optional bool checkedSignature = 2; // node vouches signature is correct
|
||||
}
|
||||
|
||||
|
||||
message TMGetValidations
|
||||
{
|
||||
required uint32 ledgerIndex = 1;
|
||||
repeated bytes hanko = 2;
|
||||
optional uint32 count = 3; // get random validations
|
||||
}
|
||||
|
||||
|
||||
|
||||
message TMContact
|
||||
{
|
||||
required bytes pubKey = 1;
|
||||
required uint32 softwareVersion = 2;
|
||||
required uint32 protoVersion = 3;
|
||||
required uint64 nodeFlags = 4;
|
||||
required uint64 timestamp = 5;
|
||||
repeated bytes nodeInfo = 6;
|
||||
required bytes signature = 7;
|
||||
}
|
||||
|
||||
// request node information
|
||||
message TMGetContacts
|
||||
{
|
||||
repeated bytes nodeIDs = 1; // specific nodes we want
|
||||
optional uint32 nodeCount = 2; // get some random nodes
|
||||
}
|
||||
|
||||
message TMGetPeers
|
||||
{
|
||||
required uint32 doWeNeedThis = 1; // yes since you are asserting that the packet size isn't 0 in PackedMessage
|
||||
}
|
||||
|
||||
message TMIPv4EndPoint
|
||||
{
|
||||
required uint32 ipv4 = 1;
|
||||
required uint32 ipv4Port = 2;
|
||||
}
|
||||
|
||||
message TMPeers
|
||||
{
|
||||
repeated TMIPv4EndPoint nodes = 1;
|
||||
}
|
||||
|
||||
|
||||
message TMSearchTransaction
|
||||
{
|
||||
required uint32 maxTrans = 1;
|
||||
optional bytes toAccount = 2;
|
||||
optional bytes fromAccount = 3;
|
||||
optional uint32 minLedger = 4;
|
||||
optional bytes fromAcctSeq = 5;
|
||||
repeated bytes transID = 6;
|
||||
}
|
||||
|
||||
message TMGetAccount
|
||||
{
|
||||
repeated bytes acctID = 1;
|
||||
optional uint32 seq = 2;
|
||||
}
|
||||
|
||||
message Account
|
||||
{
|
||||
required bytes accountID = 1;
|
||||
required uint64 balance = 2;
|
||||
required uint32 accountSeq = 3;
|
||||
required uint32 ledgerSeq = 4;
|
||||
}
|
||||
|
||||
message TMAccount
|
||||
{
|
||||
repeated Account accounts = 1;
|
||||
optional uint32 seq = 2;
|
||||
}
|
||||
|
||||
message TMIndexedObject
|
||||
{
|
||||
optional bytes hash = 1;
|
||||
optional bytes nodeID = 2;
|
||||
optional bytes index = 3;
|
||||
optional bytes data = 4;
|
||||
optional uint32 ledgerSeq = 5;
|
||||
}
|
||||
|
||||
message TMGetObjectByHash
|
||||
{
|
||||
enum ObjectType {
|
||||
otUNKNOWN = 0;
|
||||
otLEDGER = 1;
|
||||
otTRANSACTION = 2;
|
||||
otTRANSACTION_NODE = 3;
|
||||
otSTATE_NODE = 4;
|
||||
otCAS_OBJECT = 5;
|
||||
otFETCH_PACK = 6;
|
||||
}
|
||||
|
||||
required ObjectType type = 1;
|
||||
required bool query = 2; // is this a query or a reply?
|
||||
optional uint32 seq = 3; // used to match replies to queries
|
||||
optional bytes ledgerHash = 4; // the hash of the ledger these queries are for
|
||||
optional bool fat = 5; // return related nodes
|
||||
repeated TMIndexedObject objects = 6; // the specific objects requested
|
||||
}
|
||||
|
||||
|
||||
message TMLedgerNode
|
||||
{
|
||||
required bytes nodedata = 1;
|
||||
optional bytes nodeid = 2; // missing for ledger base data
|
||||
}
|
||||
|
||||
enum TMLedgerInfoType
|
||||
{
|
||||
liBASE = 0; // basic ledger info
|
||||
liTX_NODE = 1; // transaction node
|
||||
liAS_NODE = 2; // account state node
|
||||
liTS_CANDIDATE = 3; // candidate transaction set
|
||||
}
|
||||
|
||||
enum TMLedgerType
|
||||
{
|
||||
ltACCEPTED = 0;
|
||||
ltCURRENT = 1;
|
||||
ltCLOSED = 2;
|
||||
}
|
||||
|
||||
enum TMQueryType
|
||||
{
|
||||
qtINDIRECT = 0;
|
||||
}
|
||||
|
||||
message TMGetLedger
|
||||
{
|
||||
required TMLedgerInfoType itype = 1;
|
||||
optional TMLedgerType ltype = 2;
|
||||
optional bytes ledgerHash = 3; // Can also be the transaction set hash if liTS_CANDIDATE
|
||||
optional uint32 ledgerSeq = 4;
|
||||
repeated bytes nodeIDs = 5;
|
||||
optional uint64 requestCookie = 6;
|
||||
optional TMQueryType queryType = 7;
|
||||
}
|
||||
|
||||
enum TMReplyError
|
||||
{
|
||||
reNO_LEDGER = 1; // We don't have the ledger you are asking about
|
||||
reNO_NODE = 2; // We don't have any of the nodes you are asking for
|
||||
}
|
||||
|
||||
message TMLedgerData
|
||||
{
|
||||
required bytes ledgerHash = 1;
|
||||
required uint32 ledgerSeq = 2;
|
||||
required TMLedgerInfoType type = 3;
|
||||
repeated TMLedgerNode nodes = 4;
|
||||
optional uint32 requestCookie = 5;
|
||||
optional TMReplyError error = 6;
|
||||
}
|
||||
|
||||
message TMPing
|
||||
{
|
||||
enum pingType {
|
||||
ptPING = 0; // we want a reply
|
||||
ptPONG = 1; // this is a reply
|
||||
}
|
||||
required pingType type = 1;
|
||||
optional uint32 seq = 2; // detect stale replies, ensure other side is reading
|
||||
optional uint64 pingTime = 3; // know when we think we sent the ping
|
||||
optional uint64 netTime = 4;
|
||||
}
|
||||
|
||||
|
||||
message TMErrorMsg
|
||||
{
|
||||
optional int32 errorCode = 1;
|
||||
optional string message = 2;
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
@@ -1,11 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
AccountItem::AccountItem (SerializedLedgerEntry::ref ledger)
|
||||
: mLedgerEntry (ledger)
|
||||
{
|
||||
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_ACCOUNTITEM_H
|
||||
#define RIPPLE_ACCOUNTITEM_H
|
||||
|
||||
//
|
||||
// Fetch ledger entries from an account's owner dir.
|
||||
//
|
||||
/** Base class representing account items.
|
||||
|
||||
Account items include:
|
||||
|
||||
- Offers
|
||||
- Trust Lines
|
||||
|
||||
NOTE these are deprecated and will go away, to be replaced with
|
||||
simple visitor patterns.
|
||||
*/
|
||||
class AccountItem
|
||||
{
|
||||
public:
|
||||
typedef boost::shared_ptr <AccountItem> pointer;
|
||||
typedef const pointer& ref;
|
||||
|
||||
public:
|
||||
AccountItem ()
|
||||
{ }
|
||||
|
||||
/** Construct from a flat ledger entry.
|
||||
*/
|
||||
explicit AccountItem (SerializedLedgerEntry::ref ledger);
|
||||
|
||||
virtual ~AccountItem ()
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
virtual AccountItem::pointer makeItem (const uint160& accountID, SerializedLedgerEntry::ref ledgerEntry) = 0;
|
||||
|
||||
// VFALCO TODO Make this const and change derived classes
|
||||
virtual LedgerEntryType getType () = 0;
|
||||
|
||||
// VFALCO TODO Document the int parameter
|
||||
virtual Json::Value getJson (int) = 0;
|
||||
|
||||
SerializedLedgerEntry::pointer getSLE ()
|
||||
{
|
||||
return mLedgerEntry;
|
||||
}
|
||||
|
||||
const SerializedLedgerEntry& peekSLE () const
|
||||
{
|
||||
return *mLedgerEntry;
|
||||
}
|
||||
|
||||
SerializedLedgerEntry& peekSLE ()
|
||||
{
|
||||
return *mLedgerEntry;
|
||||
}
|
||||
|
||||
Blob getRaw () const;
|
||||
|
||||
// VFALCO TODO Make this private and use the existing accessors
|
||||
//
|
||||
protected:
|
||||
// VFALCO TODO Research making the object pointed to const
|
||||
SerializedLedgerEntry::pointer mLedgerEntry;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,68 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
AccountItems::AccountItems (uint160 const& accountID,
|
||||
Ledger::ref ledger,
|
||||
AccountItem::pointer ofType)
|
||||
{
|
||||
mOfType = ofType;
|
||||
|
||||
fillItems (accountID, ledger);
|
||||
}
|
||||
|
||||
void AccountItems::fillItems (const uint160& accountID, Ledger::ref ledger)
|
||||
{
|
||||
uint256 const rootIndex = Ledger::getOwnerDirIndex (accountID);
|
||||
uint256 currentIndex = rootIndex;
|
||||
|
||||
// VFALCO TODO Rewrite all infinite loops to have clear terminating
|
||||
// conditions defined in one location.
|
||||
//
|
||||
while (1)
|
||||
{
|
||||
SLE::pointer ownerDir = ledger->getDirNode (currentIndex);
|
||||
|
||||
// VFALCO TODO Rewrite to not return from the middle of the function
|
||||
if (!ownerDir)
|
||||
return;
|
||||
|
||||
BOOST_FOREACH (uint256 const & uNode, ownerDir->getFieldV256 (sfIndexes).peekValue ())
|
||||
{
|
||||
// VFALCO TODO rename getSLEi() to something legible.
|
||||
SLE::pointer sleCur = ledger->getSLEi (uNode);
|
||||
|
||||
AccountItem::pointer item = mOfType->makeItem (accountID, sleCur);
|
||||
|
||||
// VFALCO NOTE Under what conditions would makeItem() return nullptr?
|
||||
if (item)
|
||||
{
|
||||
mItems.push_back (item);
|
||||
}
|
||||
}
|
||||
|
||||
uint64 uNodeNext = ownerDir->getFieldU64 (sfIndexNext);
|
||||
|
||||
// VFALCO TODO Rewrite to not return from the middle of the function
|
||||
if (!uNodeNext)
|
||||
return;
|
||||
|
||||
currentIndex = Ledger::getDirNodeIndex (rootIndex, uNodeNext);
|
||||
}
|
||||
}
|
||||
|
||||
Json::Value AccountItems::getJson (int v)
|
||||
{
|
||||
Json::Value ret (Json::arrayValue);
|
||||
|
||||
BOOST_FOREACH (AccountItem::ref ai, mItems)
|
||||
{
|
||||
ret.append (ai->getJson (v));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
@@ -1,45 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_ACCOUNTITEMS_H
|
||||
#define RIPPLE_ACCOUNTITEMS_H
|
||||
|
||||
/** A set of AccountItem objects.
|
||||
*/
|
||||
class AccountItems : LeakChecked <AccountItems>
|
||||
{
|
||||
public:
|
||||
typedef boost::shared_ptr <AccountItems> pointer;
|
||||
|
||||
typedef std::vector <AccountItem::pointer> Container;
|
||||
|
||||
// VFALCO TODO Create a typedef uint160 AccountID and replace
|
||||
AccountItems (uint160 const& accountID,
|
||||
Ledger::ref ledger,
|
||||
AccountItem::pointer ofType);
|
||||
|
||||
// VFALCO TODO rename to getContainer and make this change in every interface
|
||||
// that exposes the caller to the type of container.
|
||||
//
|
||||
Container& getItems ()
|
||||
{
|
||||
return mItems;
|
||||
}
|
||||
|
||||
// VFALCO TODO What is the int for?
|
||||
Json::Value getJson (int);
|
||||
|
||||
private:
|
||||
void fillItems (const uint160& accountID, Ledger::ref ledger);
|
||||
|
||||
private:
|
||||
// VFALCO TODO This looks like its used as an exemplar, rename appropriately
|
||||
AccountItem::pointer mOfType;
|
||||
|
||||
Container mItems;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,69 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
AccountState::AccountState (RippleAddress const& naAccountID)
|
||||
: mAccountID (naAccountID)
|
||||
, mValid (false)
|
||||
{
|
||||
if (naAccountID.isValid ())
|
||||
{
|
||||
mValid = true;
|
||||
|
||||
mLedgerEntry = boost::make_shared <SerializedLedgerEntry> (
|
||||
ltACCOUNT_ROOT, Ledger::getAccountRootIndex (naAccountID));
|
||||
|
||||
mLedgerEntry->setFieldAccount (sfAccount, naAccountID.getAccountID ());
|
||||
}
|
||||
}
|
||||
|
||||
AccountState::AccountState (SLE::ref ledgerEntry, const RippleAddress& naAccountID) :
|
||||
mAccountID (naAccountID), mLedgerEntry (ledgerEntry), mValid (false)
|
||||
{
|
||||
if (!mLedgerEntry)
|
||||
return;
|
||||
|
||||
if (mLedgerEntry->getType () != ltACCOUNT_ROOT)
|
||||
return;
|
||||
|
||||
mValid = true;
|
||||
}
|
||||
|
||||
// VFALCO TODO Make this a generic utility function of some container class
|
||||
//
|
||||
std::string AccountState::createGravatarUrl (uint128 uEmailHash)
|
||||
{
|
||||
Blob vucMD5 (uEmailHash.begin (), uEmailHash.end ());
|
||||
std::string strMD5Lower = strHex (vucMD5);
|
||||
boost::to_lower (strMD5Lower);
|
||||
|
||||
// VFALCO TODO Give a name and move this constant to a more visible location.
|
||||
// Also shouldn't this be https?
|
||||
return str (boost::format ("http://www.gravatar.com/avatar/%s") % strMD5Lower);
|
||||
}
|
||||
|
||||
void AccountState::addJson (Json::Value& val)
|
||||
{
|
||||
val = mLedgerEntry->getJson (0);
|
||||
|
||||
if (mValid)
|
||||
{
|
||||
if (mLedgerEntry->isFieldPresent (sfEmailHash))
|
||||
val["urlgravatar"] = createGravatarUrl (mLedgerEntry->getFieldH128 (sfEmailHash));
|
||||
}
|
||||
else
|
||||
{
|
||||
val["Invalid"] = true;
|
||||
}
|
||||
}
|
||||
|
||||
void AccountState::dump ()
|
||||
{
|
||||
Json::Value j (Json::objectValue);
|
||||
addJson (j);
|
||||
Log (lsINFO) << j;
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
@@ -1,77 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_ACCOUNTSTATE_H
|
||||
#define RIPPLE_ACCOUNTSTATE_H
|
||||
|
||||
//
|
||||
// Provide abstract access to an account's state, such that access to the serialized format is hidden.
|
||||
//
|
||||
|
||||
class AccountState : LeakChecked <AccountState>
|
||||
{
|
||||
public:
|
||||
typedef boost::shared_ptr<AccountState> pointer;
|
||||
|
||||
public:
|
||||
// For new accounts
|
||||
explicit AccountState (RippleAddress const& naAccountID);
|
||||
|
||||
// For accounts in a ledger
|
||||
AccountState (SLE::ref ledgerEntry, RippleAddress const& naAccountI);
|
||||
|
||||
bool haveAuthorizedKey ()
|
||||
{
|
||||
return mLedgerEntry->isFieldPresent (sfRegularKey);
|
||||
}
|
||||
|
||||
RippleAddress getAuthorizedKey ()
|
||||
{
|
||||
return mLedgerEntry->getFieldAccount (sfRegularKey);
|
||||
}
|
||||
|
||||
STAmount getBalance () const
|
||||
{
|
||||
return mLedgerEntry->getFieldAmount (sfBalance);
|
||||
}
|
||||
|
||||
uint32 getSeq () const
|
||||
{
|
||||
return mLedgerEntry->getFieldU32 (sfSequence);
|
||||
}
|
||||
|
||||
SerializedLedgerEntry::pointer getSLE ()
|
||||
{
|
||||
return mLedgerEntry;
|
||||
}
|
||||
|
||||
SerializedLedgerEntry const& peekSLE () const
|
||||
{
|
||||
return *mLedgerEntry;
|
||||
}
|
||||
|
||||
SerializedLedgerEntry& peekSLE ()
|
||||
{
|
||||
return *mLedgerEntry;
|
||||
}
|
||||
|
||||
Blob getRaw () const;
|
||||
|
||||
void addJson (Json::Value& value);
|
||||
|
||||
void dump ();
|
||||
|
||||
static std::string createGravatarUrl (uint128 uEmailHash);
|
||||
|
||||
private:
|
||||
RippleAddress const mAccountID;
|
||||
RippleAddress mAuthorizedKey;
|
||||
SerializedLedgerEntry::pointer mLedgerEntry;
|
||||
|
||||
bool mValid;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,994 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
// VFALCO TODO Clean this global up
|
||||
volatile bool doShutdown = false;
|
||||
|
||||
class Application;
|
||||
|
||||
SETUP_LOG (Application)
|
||||
|
||||
// VFALCO TODO Move the function definitions into the class declaration
|
||||
class ApplicationImp
|
||||
: public Application
|
||||
, public SharedSingleton <ApplicationImp>
|
||||
, public Validators::Listener
|
||||
, public NodeStore::Scheduler
|
||||
, LeakChecked <ApplicationImp>
|
||||
{
|
||||
public:
|
||||
static ApplicationImp* createInstance ()
|
||||
{
|
||||
return new ApplicationImp;
|
||||
}
|
||||
|
||||
class Holder;
|
||||
|
||||
ApplicationImp ()
|
||||
//
|
||||
// VFALCO NOTE Change this to control whether or not the Application
|
||||
// object is destroyed on exit
|
||||
//
|
||||
#if 1
|
||||
// Application object will be deleted on exit. If the code doesn't exit
|
||||
// cleanly this could cause hangs or crashes on exit.
|
||||
//
|
||||
: SharedSingleton <ApplicationImp> (SingletonLifetime::persistAfterCreation)
|
||||
#else
|
||||
// This will make it so that the Application object is not deleted on exit.
|
||||
//
|
||||
: SharedSingleton <Application> (SingletonLifetime::neverDestroyed)
|
||||
#endif
|
||||
, mIOService ((theConfig.NODE_SIZE >= 2) ? 2 : 1)
|
||||
, mIOWork (mIOService)
|
||||
, mNetOps (new NetworkOPs (&mLedgerMaster))
|
||||
, m_rpcServerHandler (*mNetOps)
|
||||
, mTempNodeCache ("NodeCache", 16384, 90)
|
||||
, mSLECache ("LedgerEntryCache", 4096, 120)
|
||||
, mSNTPClient (mAuxService)
|
||||
, mJobQueue (mIOService)
|
||||
// VFALCO New stuff
|
||||
, m_nodeStore (NodeStore::New (
|
||||
theConfig.nodeDatabase,
|
||||
theConfig.ephemeralNodeDatabase,
|
||||
*this))
|
||||
, m_validators (Validators::New (this))
|
||||
, mFeatures (IFeatures::New (2 * 7 * 24 * 60 * 60, 200)) // two weeks, 200/256
|
||||
, mFeeVote (IFeeVote::New (10, 50 * SYSTEM_CURRENCY_PARTS, 12.5 * SYSTEM_CURRENCY_PARTS))
|
||||
, mFeeTrack (ILoadFeeTrack::New ())
|
||||
, mHashRouter (IHashRouter::New (IHashRouter::getDefaultHoldTime ()))
|
||||
, mValidations (IValidations::New ())
|
||||
, mUNL (UniqueNodeList::New ())
|
||||
, mProofOfWorkFactory (IProofOfWorkFactory::New ())
|
||||
, mPeers (IPeers::New (mIOService))
|
||||
, m_loadManager (ILoadManager::New ())
|
||||
// VFALCO End new stuff
|
||||
// VFALCO TODO replace all NULL with nullptr
|
||||
, mRpcDB (NULL)
|
||||
, mTxnDB (NULL)
|
||||
, mLedgerDB (NULL)
|
||||
, mWalletDB (NULL) // VFALCO NOTE are all these 'NULL' ctor params necessary?
|
||||
, mPeerDoor (NULL)
|
||||
, mRPCDoor (NULL)
|
||||
, mWSPublicDoor (NULL)
|
||||
, mWSPrivateDoor (NULL)
|
||||
, mSweepTimer (mAuxService)
|
||||
, mShutdown (false)
|
||||
{
|
||||
// VFALCO TODO remove these once the call is thread safe.
|
||||
HashMaps::getInstance ().initializeNonce <size_t> ();
|
||||
}
|
||||
|
||||
~ApplicationImp ()
|
||||
{
|
||||
mNetOps = nullptr;
|
||||
|
||||
// VFALCO TODO Wrap these in ScopedPointer
|
||||
delete mTxnDB;
|
||||
delete mLedgerDB;
|
||||
delete mWalletDB;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
static void callScheduledTask (NodeStore::Scheduler::Task* task, Job&)
|
||||
{
|
||||
task->performScheduledTask ();
|
||||
}
|
||||
|
||||
void scheduleTask (NodeStore::Scheduler::Task* task)
|
||||
{
|
||||
getJobQueue ().addJob (
|
||||
jtWRITE,
|
||||
"NodeObject::store",
|
||||
BIND_TYPE (&ApplicationImp::callScheduledTask, task, P_1));
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
LocalCredentials& getLocalCredentials ()
|
||||
{
|
||||
return m_localCredentials ;
|
||||
}
|
||||
|
||||
NetworkOPs& getOPs ()
|
||||
{
|
||||
return *mNetOps;
|
||||
}
|
||||
|
||||
boost::asio::io_service& getIOService ()
|
||||
{
|
||||
return mIOService;
|
||||
}
|
||||
|
||||
LedgerMaster& getLedgerMaster ()
|
||||
{
|
||||
return mLedgerMaster;
|
||||
}
|
||||
|
||||
InboundLedgers& getInboundLedgers ()
|
||||
{
|
||||
return m_inboundLedgers;
|
||||
}
|
||||
|
||||
TransactionMaster& getMasterTransaction ()
|
||||
{
|
||||
return mMasterTransaction;
|
||||
}
|
||||
|
||||
NodeCache& getTempNodeCache ()
|
||||
{
|
||||
return mTempNodeCache;
|
||||
}
|
||||
|
||||
NodeStore& getNodeStore ()
|
||||
{
|
||||
return *m_nodeStore;
|
||||
}
|
||||
|
||||
JobQueue& getJobQueue ()
|
||||
{
|
||||
return mJobQueue;
|
||||
}
|
||||
|
||||
MasterLockType& getMasterLock ()
|
||||
{
|
||||
return mMasterLock;
|
||||
}
|
||||
|
||||
ILoadManager& getLoadManager ()
|
||||
{
|
||||
return *m_loadManager;
|
||||
}
|
||||
|
||||
TXQueue& getTxnQueue ()
|
||||
{
|
||||
return mTxnQueue;
|
||||
}
|
||||
|
||||
PeerDoor& getPeerDoor ()
|
||||
{
|
||||
return *mPeerDoor;
|
||||
}
|
||||
|
||||
OrderBookDB& getOrderBookDB ()
|
||||
{
|
||||
return mOrderBookDB;
|
||||
}
|
||||
|
||||
SLECache& getSLECache ()
|
||||
{
|
||||
return mSLECache;
|
||||
}
|
||||
|
||||
Validators& getValidators ()
|
||||
{
|
||||
return *m_validators;
|
||||
}
|
||||
|
||||
IFeatures& getFeatureTable ()
|
||||
{
|
||||
return *mFeatures;
|
||||
}
|
||||
|
||||
ILoadFeeTrack& getFeeTrack ()
|
||||
{
|
||||
return *mFeeTrack;
|
||||
}
|
||||
|
||||
IFeeVote& getFeeVote ()
|
||||
{
|
||||
return *mFeeVote;
|
||||
}
|
||||
|
||||
IHashRouter& getHashRouter ()
|
||||
{
|
||||
return *mHashRouter;
|
||||
}
|
||||
|
||||
IValidations& getValidations ()
|
||||
{
|
||||
return *mValidations;
|
||||
}
|
||||
|
||||
UniqueNodeList& getUNL ()
|
||||
{
|
||||
return *mUNL;
|
||||
}
|
||||
|
||||
IProofOfWorkFactory& getProofOfWorkFactory ()
|
||||
{
|
||||
return *mProofOfWorkFactory;
|
||||
}
|
||||
|
||||
IPeers& getPeers ()
|
||||
{
|
||||
return *mPeers;
|
||||
}
|
||||
|
||||
// VFALCO TODO Move these to the .cpp
|
||||
bool running ()
|
||||
{
|
||||
return mTxnDB != NULL; // VFALCO TODO replace with nullptr when beast is available
|
||||
}
|
||||
bool getSystemTimeOffset (int& offset)
|
||||
{
|
||||
return mSNTPClient.getOffset (offset);
|
||||
}
|
||||
|
||||
DatabaseCon* getRpcDB ()
|
||||
{
|
||||
return mRpcDB;
|
||||
}
|
||||
DatabaseCon* getTxnDB ()
|
||||
{
|
||||
return mTxnDB;
|
||||
}
|
||||
DatabaseCon* getLedgerDB ()
|
||||
{
|
||||
return mLedgerDB;
|
||||
}
|
||||
DatabaseCon* getWalletDB ()
|
||||
{
|
||||
return mWalletDB;
|
||||
}
|
||||
|
||||
bool isShutdown ()
|
||||
{
|
||||
return mShutdown;
|
||||
}
|
||||
void setup ();
|
||||
void run ();
|
||||
void stop ();
|
||||
void sweep ();
|
||||
void doSweep (Job&);
|
||||
|
||||
private:
|
||||
void updateTables ();
|
||||
void startNewLedger ();
|
||||
bool loadOldLedger (const std::string&, bool);
|
||||
|
||||
private:
|
||||
boost::asio::io_service mIOService;
|
||||
boost::asio::io_service mAuxService;
|
||||
// The lifetime of the io_service::work object informs the io_service
|
||||
// of when the work starts and finishes. io_service::run() will not exit
|
||||
// while the work object exists.
|
||||
//
|
||||
boost::asio::io_service::work mIOWork;
|
||||
|
||||
MasterLockType mMasterLock;
|
||||
|
||||
LocalCredentials m_localCredentials;
|
||||
LedgerMaster mLedgerMaster;
|
||||
InboundLedgers m_inboundLedgers;
|
||||
TransactionMaster mMasterTransaction;
|
||||
ScopedPointer <NetworkOPs> mNetOps;
|
||||
RPCServerHandler m_rpcServerHandler;
|
||||
NodeCache mTempNodeCache;
|
||||
SLECache mSLECache;
|
||||
SNTPClient mSNTPClient;
|
||||
JobQueue mJobQueue;
|
||||
TXQueue mTxnQueue;
|
||||
OrderBookDB mOrderBookDB;
|
||||
|
||||
// VFALCO Clean stuff
|
||||
ScopedPointer <NodeStore> m_nodeStore;
|
||||
ScopedPointer <Validators> m_validators;
|
||||
ScopedPointer <IFeatures> mFeatures;
|
||||
ScopedPointer <IFeeVote> mFeeVote;
|
||||
ScopedPointer <ILoadFeeTrack> mFeeTrack;
|
||||
ScopedPointer <IHashRouter> mHashRouter;
|
||||
ScopedPointer <IValidations> mValidations;
|
||||
ScopedPointer <UniqueNodeList> mUNL;
|
||||
ScopedPointer <IProofOfWorkFactory> mProofOfWorkFactory;
|
||||
ScopedPointer <IPeers> mPeers;
|
||||
ScopedPointer <ILoadManager> m_loadManager;
|
||||
// VFALCO End Clean stuff
|
||||
|
||||
DatabaseCon* mRpcDB;
|
||||
DatabaseCon* mTxnDB;
|
||||
DatabaseCon* mLedgerDB;
|
||||
DatabaseCon* mWalletDB;
|
||||
|
||||
ScopedPointer <PeerDoor> mPeerDoor;
|
||||
ScopedPointer <RPCDoor> mRPCDoor;
|
||||
ScopedPointer <WSDoor> mWSPublicDoor;
|
||||
ScopedPointer <WSDoor> mWSPrivateDoor;
|
||||
|
||||
boost::asio::deadline_timer mSweepTimer;
|
||||
|
||||
bool volatile mShutdown;
|
||||
};
|
||||
|
||||
// VFALCO TODO Why do we even have this function?
|
||||
// It could just be handled in the destructor.
|
||||
//
|
||||
void ApplicationImp::stop ()
|
||||
{
|
||||
WriteLog (lsINFO, Application) << "Received shutdown request";
|
||||
StopSustain ();
|
||||
mShutdown = true;
|
||||
mIOService.stop ();
|
||||
m_nodeStore = nullptr;
|
||||
mValidations->flush ();
|
||||
mAuxService.stop ();
|
||||
mJobQueue.shutdown ();
|
||||
|
||||
WriteLog (lsINFO, Application) << "Stopped: " << mIOService.stopped ();
|
||||
mShutdown = false;
|
||||
}
|
||||
|
||||
static void InitDB (DatabaseCon** dbCon, const char* fileName, const char* dbInit[], int dbCount)
|
||||
{
|
||||
*dbCon = new DatabaseCon (fileName, dbInit, dbCount);
|
||||
}
|
||||
|
||||
#ifdef SIGINT
|
||||
void sigIntHandler (int)
|
||||
{
|
||||
doShutdown = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
// VFALCO TODO Figure this out it looks like the wrong tool
|
||||
static void runAux (boost::asio::io_service& svc)
|
||||
{
|
||||
setCallingThreadName ("aux");
|
||||
svc.run ();
|
||||
}
|
||||
|
||||
static void runIO (boost::asio::io_service& io)
|
||||
{
|
||||
setCallingThreadName ("io");
|
||||
io.run ();
|
||||
}
|
||||
|
||||
// VFALCO TODO Break this function up into many small initialization segments.
|
||||
// Or better yet refactor these initializations into RAII classes
|
||||
// which are members of the Application object.
|
||||
//
|
||||
void ApplicationImp::setup ()
|
||||
{
|
||||
// VFALCO NOTE: 0 means use heuristics to determine the thread count.
|
||||
mJobQueue.setThreadCount (0, theConfig.RUN_STANDALONE);
|
||||
|
||||
mSweepTimer.expires_from_now (boost::posix_time::seconds (10));
|
||||
mSweepTimer.async_wait (BIND_TYPE (&ApplicationImp::sweep, this));
|
||||
|
||||
m_loadManager->startThread ();
|
||||
|
||||
#if ! BEAST_WIN32
|
||||
#ifdef SIGINT
|
||||
|
||||
if (!theConfig.RUN_STANDALONE)
|
||||
{
|
||||
struct sigaction sa;
|
||||
memset (&sa, 0, sizeof (sa));
|
||||
sa.sa_handler = sigIntHandler;
|
||||
sigaction (SIGINT, &sa, NULL);
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
assert (mTxnDB == NULL);
|
||||
|
||||
if (!theConfig.DEBUG_LOGFILE.empty ())
|
||||
{
|
||||
// Let debug messages go to the file but only WARNING or higher to regular output (unless verbose)
|
||||
Log::setLogFile (theConfig.DEBUG_LOGFILE);
|
||||
|
||||
if (Log::getMinSeverity () > lsDEBUG)
|
||||
LogPartition::setSeverity (lsDEBUG);
|
||||
}
|
||||
|
||||
boost::thread (BIND_TYPE (runAux, boost::ref (mAuxService))).detach ();
|
||||
|
||||
if (!theConfig.RUN_STANDALONE)
|
||||
mSNTPClient.init (theConfig.SNTP_SERVERS);
|
||||
|
||||
//
|
||||
// Construct databases.
|
||||
//
|
||||
boost::thread t1 (BIND_TYPE (&InitDB, &mRpcDB, "rpc.db", RpcDBInit, RpcDBCount));
|
||||
boost::thread t2 (BIND_TYPE (&InitDB, &mTxnDB, "transaction.db", TxnDBInit, TxnDBCount));
|
||||
boost::thread t3 (BIND_TYPE (&InitDB, &mLedgerDB, "ledger.db", LedgerDBInit, LedgerDBCount));
|
||||
boost::thread t4 (BIND_TYPE (&InitDB, &mWalletDB, "wallet.db", WalletDBInit, WalletDBCount));
|
||||
t1.join ();
|
||||
t2.join ();
|
||||
t3.join ();
|
||||
t4.join ();
|
||||
|
||||
leveldb::Options options;
|
||||
options.create_if_missing = true;
|
||||
options.block_cache = leveldb::NewLRUCache (theConfig.getSize (siHashNodeDBCache) * 1024 * 1024);
|
||||
|
||||
getApp().getLedgerDB ()->getDB ()->executeSQL (boost::str (boost::format ("PRAGMA cache_size=-%d;") %
|
||||
(theConfig.getSize (siLgrDBCache) * 1024)));
|
||||
getApp().getTxnDB ()->getDB ()->executeSQL (boost::str (boost::format ("PRAGMA cache_size=-%d;") %
|
||||
(theConfig.getSize (siTxnDBCache) * 1024)));
|
||||
|
||||
mTxnDB->getDB ()->setupCheckpointing (&mJobQueue);
|
||||
mLedgerDB->getDB ()->setupCheckpointing (&mJobQueue);
|
||||
|
||||
if (!theConfig.RUN_STANDALONE)
|
||||
updateTables ();
|
||||
|
||||
mFeatures->addInitialFeatures ();
|
||||
|
||||
if (theConfig.START_UP == Config::FRESH)
|
||||
{
|
||||
WriteLog (lsINFO, Application) << "Starting new Ledger";
|
||||
|
||||
startNewLedger ();
|
||||
}
|
||||
else if ((theConfig.START_UP == Config::LOAD) || (theConfig.START_UP == Config::REPLAY))
|
||||
{
|
||||
WriteLog (lsINFO, Application) << "Loading specified Ledger";
|
||||
|
||||
if (!loadOldLedger (theConfig.START_LEDGER, theConfig.START_UP == Config::REPLAY))
|
||||
{
|
||||
getApp().stop ();
|
||||
exit (-1);
|
||||
}
|
||||
}
|
||||
else if (theConfig.START_UP == Config::NETWORK)
|
||||
{
|
||||
// This should probably become the default once we have a stable network
|
||||
if (!theConfig.RUN_STANDALONE)
|
||||
mNetOps->needNetworkLedger ();
|
||||
|
||||
startNewLedger ();
|
||||
}
|
||||
else
|
||||
startNewLedger ();
|
||||
|
||||
mOrderBookDB.setup (getApp().getLedgerMaster ().getCurrentLedger ());
|
||||
|
||||
//
|
||||
// Begin validation and ip maintenance.
|
||||
// - LocalCredentials maintains local information: including identity and network connection persistence information.
|
||||
//
|
||||
m_localCredentials.start ();
|
||||
|
||||
//
|
||||
// Set up UNL.
|
||||
//
|
||||
if (!theConfig.RUN_STANDALONE)
|
||||
getUNL ().nodeBootstrap ();
|
||||
|
||||
mValidations->tune (theConfig.getSize (siValidationsSize), theConfig.getSize (siValidationsAge));
|
||||
m_nodeStore->tune (theConfig.getSize (siNodeCacheSize), theConfig.getSize (siNodeCacheAge));
|
||||
mLedgerMaster.tune (theConfig.getSize (siLedgerSize), theConfig.getSize (siLedgerAge));
|
||||
mSLECache.setTargetSize (theConfig.getSize (siSLECacheSize));
|
||||
mSLECache.setTargetAge (theConfig.getSize (siSLECacheAge));
|
||||
|
||||
mLedgerMaster.setMinValidations (theConfig.VALIDATION_QUORUM);
|
||||
|
||||
//
|
||||
// Allow peer connections.
|
||||
//
|
||||
if (!theConfig.RUN_STANDALONE)
|
||||
{
|
||||
try
|
||||
{
|
||||
mPeerDoor = PeerDoor::New (
|
||||
theConfig.PEER_IP,
|
||||
theConfig.PEER_PORT,
|
||||
theConfig.PEER_SSL_CIPHER_LIST,
|
||||
mIOService);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
// Must run as directed or exit.
|
||||
WriteLog (lsFATAL, Application) << boost::str (boost::format ("Can not open peer service: %s") % e.what ());
|
||||
|
||||
exit (3);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog (lsINFO, Application) << "Peer interface: disabled";
|
||||
}
|
||||
|
||||
//
|
||||
// Allow RPC connections.
|
||||
//
|
||||
if (! theConfig.getRpcIP().empty () && theConfig.getRpcPort() != 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
mRPCDoor = new RPCDoor (mIOService, m_rpcServerHandler);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
// Must run as directed or exit.
|
||||
WriteLog (lsFATAL, Application) << boost::str (boost::format ("Can not open RPC service: %s") % e.what ());
|
||||
|
||||
exit (3);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog (lsINFO, Application) << "RPC interface: disabled";
|
||||
}
|
||||
|
||||
//
|
||||
// Allow private WS connections.
|
||||
//
|
||||
if (!theConfig.WEBSOCKET_IP.empty () && theConfig.WEBSOCKET_PORT)
|
||||
{
|
||||
try
|
||||
{
|
||||
mWSPrivateDoor = WSDoor::createWSDoor (theConfig.WEBSOCKET_IP, theConfig.WEBSOCKET_PORT, false);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
// Must run as directed or exit.
|
||||
WriteLog (lsFATAL, Application) << boost::str (boost::format ("Can not open private websocket service: %s") % e.what ());
|
||||
|
||||
exit (3);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog (lsINFO, Application) << "WS private interface: disabled";
|
||||
}
|
||||
|
||||
//
|
||||
// Allow public WS connections.
|
||||
//
|
||||
if (!theConfig.WEBSOCKET_PUBLIC_IP.empty () && theConfig.WEBSOCKET_PUBLIC_PORT)
|
||||
{
|
||||
try
|
||||
{
|
||||
mWSPublicDoor = WSDoor::createWSDoor (theConfig.WEBSOCKET_PUBLIC_IP, theConfig.WEBSOCKET_PUBLIC_PORT, true);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
// Must run as directed or exit.
|
||||
WriteLog (lsFATAL, Application) << boost::str (boost::format ("Can not open public websocket service: %s") % e.what ());
|
||||
|
||||
exit (3);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog (lsINFO, Application) << "WS public interface: disabled";
|
||||
}
|
||||
|
||||
//
|
||||
// Begin connecting to network.
|
||||
//
|
||||
if (!theConfig.RUN_STANDALONE)
|
||||
mPeers->start ();
|
||||
|
||||
if (theConfig.RUN_STANDALONE)
|
||||
{
|
||||
WriteLog (lsWARNING, Application) << "Running in standalone mode";
|
||||
|
||||
mNetOps->setStandAlone ();
|
||||
}
|
||||
else
|
||||
{
|
||||
// VFALCO NOTE the state timer resets the deadlock detector.
|
||||
//
|
||||
mNetOps->setStateTimer ();
|
||||
}
|
||||
}
|
||||
|
||||
void ApplicationImp::run ()
|
||||
{
|
||||
if (theConfig.NODE_SIZE >= 2)
|
||||
{
|
||||
boost::thread (BIND_TYPE (runIO, boost::ref (mIOService))).detach ();
|
||||
}
|
||||
|
||||
if (!theConfig.RUN_STANDALONE)
|
||||
{
|
||||
// VFALCO NOTE This seems unnecessary. If we properly refactor the load
|
||||
// manager then the deadlock detector can just always be "armed"
|
||||
//
|
||||
getApp().getLoadManager ().activateDeadlockDetector ();
|
||||
}
|
||||
|
||||
mIOService.run (); // This blocks
|
||||
|
||||
if (mWSPublicDoor)
|
||||
mWSPublicDoor->stop ();
|
||||
|
||||
if (mWSPrivateDoor)
|
||||
mWSPrivateDoor->stop ();
|
||||
|
||||
// VFALCO TODO Try to not have to do this early, by using observers to
|
||||
// eliminate LoadManager's dependency inversions.
|
||||
//
|
||||
// This deletes the object and therefore, stops the thread.
|
||||
m_loadManager = nullptr;
|
||||
|
||||
mSweepTimer.cancel();
|
||||
|
||||
WriteLog (lsINFO, Application) << "Done.";
|
||||
|
||||
// VFALCO NOTE This is a sign that something is wrong somewhere, it
|
||||
// shouldn't be necessary to sleep until some flag is set.
|
||||
while (mShutdown)
|
||||
boost::this_thread::sleep (boost::posix_time::milliseconds (100));
|
||||
}
|
||||
|
||||
void ApplicationImp::sweep ()
|
||||
{
|
||||
boost::filesystem::space_info space = boost::filesystem::space (theConfig.DATA_DIR);
|
||||
|
||||
// VFALCO TODO Give this magic constant a name and move it into a well documented header
|
||||
//
|
||||
if (space.available < (512 * 1024 * 1024))
|
||||
{
|
||||
WriteLog (lsFATAL, Application) << "Remaining free disk space is less than 512MB";
|
||||
getApp().stop ();
|
||||
}
|
||||
|
||||
mJobQueue.addJob(jtSWEEP, "sweep",
|
||||
BIND_TYPE(&ApplicationImp::doSweep, this, P_1));
|
||||
}
|
||||
|
||||
void ApplicationImp::doSweep(Job& j)
|
||||
{
|
||||
// VFALCO NOTE Does the order of calls matter?
|
||||
// VFALCO TODO fix the dependency inversion using an observer,
|
||||
// have listeners register for "onSweep ()" notification.
|
||||
//
|
||||
|
||||
mMasterTransaction.sweep ();
|
||||
m_nodeStore->sweep ();
|
||||
mLedgerMaster.sweep ();
|
||||
mTempNodeCache.sweep ();
|
||||
mValidations->sweep ();
|
||||
getInboundLedgers ().sweep ();
|
||||
mSLECache.sweep ();
|
||||
AcceptedLedger::sweep (); // VFALCO NOTE AcceptedLedger is/has a singleton?
|
||||
SHAMap::sweep (); // VFALCO NOTE SHAMap is/has a singleton?
|
||||
mNetOps->sweepFetchPack ();
|
||||
// VFALCO NOTE does the call to sweep() happen on another thread?
|
||||
mSweepTimer.expires_from_now (boost::posix_time::seconds (theConfig.getSize (siSweepInterval)));
|
||||
mSweepTimer.async_wait (BIND_TYPE (&ApplicationImp::sweep, this));
|
||||
}
|
||||
|
||||
void ApplicationImp::startNewLedger ()
|
||||
{
|
||||
// New stuff.
|
||||
RippleAddress rootSeedMaster = RippleAddress::createSeedGeneric ("masterpassphrase");
|
||||
RippleAddress rootGeneratorMaster = RippleAddress::createGeneratorPublic (rootSeedMaster);
|
||||
RippleAddress rootAddress = RippleAddress::createAccountPublic (rootGeneratorMaster, 0);
|
||||
|
||||
// Print enough information to be able to claim root account.
|
||||
WriteLog (lsINFO, Application) << "Root master seed: " << rootSeedMaster.humanSeed ();
|
||||
WriteLog (lsINFO, Application) << "Root account: " << rootAddress.humanAccountID ();
|
||||
|
||||
{
|
||||
Ledger::pointer firstLedger = boost::make_shared<Ledger> (rootAddress, SYSTEM_CURRENCY_START);
|
||||
assert (!!firstLedger->getAccountState (rootAddress));
|
||||
// WRITEME: Add any default features
|
||||
// WRITEME: Set default fee/reserve
|
||||
firstLedger->updateHash ();
|
||||
firstLedger->setClosed ();
|
||||
firstLedger->setAccepted ();
|
||||
mLedgerMaster.pushLedger (firstLedger);
|
||||
|
||||
Ledger::pointer secondLedger = boost::make_shared<Ledger> (true, boost::ref (*firstLedger));
|
||||
secondLedger->setClosed ();
|
||||
secondLedger->setAccepted ();
|
||||
mLedgerMaster.pushLedger (secondLedger, boost::make_shared<Ledger> (true, boost::ref (*secondLedger)), false);
|
||||
assert (!!secondLedger->getAccountState (rootAddress));
|
||||
mNetOps->setLastCloseTime (secondLedger->getCloseTimeNC ());
|
||||
}
|
||||
}
|
||||
|
||||
bool ApplicationImp::loadOldLedger (const std::string& l, bool bReplay)
|
||||
{
|
||||
try
|
||||
{
|
||||
Ledger::pointer loadLedger, replayLedger;
|
||||
|
||||
if (l.empty () || (l == "latest"))
|
||||
loadLedger = Ledger::getLastFullLedger ();
|
||||
else if (l.length () == 64)
|
||||
{
|
||||
// by hash
|
||||
uint256 hash;
|
||||
hash.SetHex (l);
|
||||
loadLedger = Ledger::loadByHash (hash);
|
||||
}
|
||||
else // assume by sequence
|
||||
loadLedger = Ledger::loadByIndex (boost::lexical_cast<uint32> (l));
|
||||
|
||||
if (!loadLedger)
|
||||
{
|
||||
WriteLog (lsFATAL, Application) << "No Ledger found?" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bReplay)
|
||||
{ // Replay a ledger close with same prior ledger and transactions
|
||||
replayLedger = loadLedger; // this ledger holds the transactions we want to replay
|
||||
loadLedger = Ledger::loadByIndex (replayLedger->getLedgerSeq() - 1); // this is the prior ledger
|
||||
if (!loadLedger || (replayLedger->getParentHash() != loadLedger->getHash()))
|
||||
{
|
||||
WriteLog (lsFATAL, Application) << "Replay ledger missing/damaged";
|
||||
assert (false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
loadLedger->setClosed ();
|
||||
|
||||
WriteLog (lsINFO, Application) << "Loading ledger " << loadLedger->getHash () << " seq:" << loadLedger->getLedgerSeq ();
|
||||
|
||||
if (loadLedger->getAccountHash ().isZero ())
|
||||
{
|
||||
WriteLog (lsFATAL, Application) << "Ledger is empty.";
|
||||
assert (false);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!loadLedger->walkLedger ())
|
||||
{
|
||||
WriteLog (lsFATAL, Application) << "Ledger is missing nodes.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!loadLedger->assertSane ())
|
||||
{
|
||||
WriteLog (lsFATAL, Application) << "Ledger is not sane.";
|
||||
return false;
|
||||
}
|
||||
|
||||
mLedgerMaster.setLedgerRangePresent (loadLedger->getLedgerSeq (), loadLedger->getLedgerSeq ());
|
||||
|
||||
Ledger::pointer openLedger = boost::make_shared<Ledger> (false, boost::ref (*loadLedger));
|
||||
mLedgerMaster.switchLedgers (loadLedger, openLedger);
|
||||
mLedgerMaster.forceValid(loadLedger);
|
||||
mNetOps->setLastCloseTime (loadLedger->getCloseTimeNC ());
|
||||
|
||||
if (bReplay)
|
||||
{ // inject transaction from replayLedger into consensus set
|
||||
SHAMap::ref txns = replayLedger->peekTransactionMap();
|
||||
Ledger::ref cur = getLedgerMaster().getCurrentLedger();
|
||||
|
||||
for (SHAMapItem::pointer it = txns->peekFirstItem(); it != nullptr; it = txns->peekNextItem(it->getTag()))
|
||||
{
|
||||
Transaction::pointer txn = replayLedger->getTransaction(it->getTag());
|
||||
WriteLog (lsINFO, Application) << txn->getJson(0);
|
||||
Serializer s;
|
||||
txn->getSTransaction()->add(s);
|
||||
if (!cur->addTransaction(it->getTag(), s))
|
||||
{
|
||||
WriteLog (lsWARNING, Application) << "Unable to add transaction " << it->getTag();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (SHAMapMissingNode&)
|
||||
{
|
||||
WriteLog (lsFATAL, Application) << "Data is missing for selected ledger";
|
||||
return false;
|
||||
}
|
||||
catch (boost::bad_lexical_cast&)
|
||||
{
|
||||
WriteLog (lsFATAL, Application) << "Ledger specified '" << l << "' is not valid";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool serverOkay (std::string& reason)
|
||||
{
|
||||
if (!theConfig.ELB_SUPPORT)
|
||||
return true;
|
||||
|
||||
if (getApp().isShutdown ())
|
||||
{
|
||||
reason = "Server is shutting down";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (getApp().getOPs ().isNeedNetworkLedger ())
|
||||
{
|
||||
reason = "Not synchronized with network yet";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (getApp().getOPs ().getOperatingMode () < NetworkOPs::omSYNCING)
|
||||
{
|
||||
reason = "Not synchronized with network";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!getApp().getLedgerMaster().isCaughtUp(reason))
|
||||
return false;
|
||||
|
||||
if (getApp().getFeeTrack ().isLoadedLocal ())
|
||||
{
|
||||
reason = "Too much load";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (getApp().getOPs ().isFeatureBlocked ())
|
||||
{
|
||||
reason = "Server version too old";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//VFALCO TODO clean this up since it is just a file holding a single member function definition
|
||||
|
||||
static std::vector<std::string> getSchema (DatabaseCon* dbc, const std::string& dbName)
|
||||
{
|
||||
std::vector<std::string> schema;
|
||||
|
||||
std::string sql = "SELECT sql FROM sqlite_master WHERE tbl_name='";
|
||||
sql += dbName;
|
||||
sql += "';";
|
||||
|
||||
SQL_FOREACH (dbc->getDB (), sql)
|
||||
{
|
||||
dbc->getDB ()->getStr ("sql", sql);
|
||||
schema.push_back (sql);
|
||||
}
|
||||
|
||||
return schema;
|
||||
}
|
||||
|
||||
static bool schemaHas (DatabaseCon* dbc, const std::string& dbName, int line, const std::string& content)
|
||||
{
|
||||
std::vector<std::string> schema = getSchema (dbc, dbName);
|
||||
|
||||
if (static_cast<int> (schema.size ()) <= line)
|
||||
{
|
||||
Log (lsFATAL) << "Schema for " << dbName << " has too few lines";
|
||||
throw std::runtime_error ("bad schema");
|
||||
}
|
||||
|
||||
return schema[line].find (content) != std::string::npos;
|
||||
}
|
||||
|
||||
static void addTxnSeqField ()
|
||||
{
|
||||
if (schemaHas (getApp().getTxnDB (), "AccountTransactions", 0, "TxnSeq"))
|
||||
return;
|
||||
|
||||
Log (lsWARNING) << "Transaction sequence field is missing";
|
||||
|
||||
Database* db = getApp().getTxnDB ()->getDB ();
|
||||
|
||||
std::vector< std::pair<uint256, int> > txIDs;
|
||||
txIDs.reserve (300000);
|
||||
|
||||
Log (lsINFO) << "Parsing transactions";
|
||||
int i = 0;
|
||||
uint256 transID;
|
||||
SQL_FOREACH (db, "SELECT TransID,TxnMeta FROM Transactions;")
|
||||
{
|
||||
Blob rawMeta;
|
||||
int metaSize = 2048;
|
||||
rawMeta.resize (metaSize);
|
||||
metaSize = db->getBinary ("TxnMeta", &*rawMeta.begin (), rawMeta.size ());
|
||||
|
||||
if (metaSize > static_cast<int> (rawMeta.size ()))
|
||||
{
|
||||
rawMeta.resize (metaSize);
|
||||
db->getBinary ("TxnMeta", &*rawMeta.begin (), rawMeta.size ());
|
||||
}
|
||||
else rawMeta.resize (metaSize);
|
||||
|
||||
std::string tid;
|
||||
db->getStr ("TransID", tid);
|
||||
transID.SetHex (tid, true);
|
||||
|
||||
if (rawMeta.size () == 0)
|
||||
{
|
||||
txIDs.push_back (std::make_pair (transID, -1));
|
||||
Log (lsINFO) << "No metadata for " << transID;
|
||||
}
|
||||
else
|
||||
{
|
||||
TransactionMetaSet m (transID, 0, rawMeta);
|
||||
txIDs.push_back (std::make_pair (transID, m.getIndex ()));
|
||||
}
|
||||
|
||||
if ((++i % 1000) == 0)
|
||||
Log (lsINFO) << i << " transactions read";
|
||||
}
|
||||
|
||||
Log (lsINFO) << "All " << i << " transactions read";
|
||||
|
||||
db->executeSQL ("BEGIN TRANSACTION;");
|
||||
|
||||
Log (lsINFO) << "Dropping old index";
|
||||
db->executeSQL ("DROP INDEX AcctTxIndex;");
|
||||
|
||||
Log (lsINFO) << "Altering table";
|
||||
db->executeSQL ("ALTER TABLE AccountTransactions ADD COLUMN TxnSeq INTEGER;");
|
||||
|
||||
typedef std::pair<uint256, int> u256_int_pair_t;
|
||||
boost::format fmt ("UPDATE AccountTransactions SET TxnSeq = %d WHERE TransID = '%s';");
|
||||
i = 0;
|
||||
BOOST_FOREACH (u256_int_pair_t & t, txIDs)
|
||||
{
|
||||
db->executeSQL (boost::str (fmt % t.second % t.first.GetHex ()));
|
||||
|
||||
if ((++i % 1000) == 0)
|
||||
Log (lsINFO) << i << " transactions updated";
|
||||
}
|
||||
|
||||
Log (lsINFO) << "Building new index";
|
||||
db->executeSQL ("CREATE INDEX AcctTxIndex ON AccountTransactions(Account, LedgerSeq, TxnSeq, TransID);");
|
||||
db->executeSQL ("END TRANSACTION;");
|
||||
}
|
||||
|
||||
void ApplicationImp::updateTables ()
|
||||
{
|
||||
if (theConfig.nodeDatabase.size () <= 0)
|
||||
{
|
||||
Log (lsFATAL) << "The [node_db] configuration setting has been updated and must be set";
|
||||
StopSustain ();
|
||||
exit (1);
|
||||
}
|
||||
|
||||
// perform any needed table updates
|
||||
assert (schemaHas (getApp().getTxnDB (), "AccountTransactions", 0, "TransID"));
|
||||
assert (!schemaHas (getApp().getTxnDB (), "AccountTransactions", 0, "foobar"));
|
||||
addTxnSeqField ();
|
||||
|
||||
if (schemaHas (getApp().getTxnDB (), "AccountTransactions", 0, "PRIMARY"))
|
||||
{
|
||||
Log (lsFATAL) << "AccountTransactions database should not have a primary key";
|
||||
StopSustain ();
|
||||
exit (1);
|
||||
}
|
||||
|
||||
if (theConfig.importNodeDatabase.size () > 0)
|
||||
{
|
||||
ScopedPointer <NodeStore> source (NodeStore::New (theConfig.importNodeDatabase));
|
||||
|
||||
WriteLog (lsWARNING, NodeObject) <<
|
||||
"Node import from '" << source->getName () << "' to '"
|
||||
<< getApp().getNodeStore().getName () << "'.";
|
||||
|
||||
getApp().getNodeStore().import (*source);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
Application& getApp ()
|
||||
{
|
||||
return *ApplicationImp::getInstance ();
|
||||
}
|
||||
@@ -1,208 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_IAPPLICATION_H
|
||||
#define RIPPLE_IAPPLICATION_H
|
||||
|
||||
// VFALCO TODO Fix forward declares required for header dependency loops
|
||||
class IFeatures;
|
||||
class IFeeVote;
|
||||
class IHashRouter;
|
||||
class ILoadFeeTrack;
|
||||
class IPeers;
|
||||
class IProofOfWorkFactory;
|
||||
class UniqueNodeList;
|
||||
class IValidations;
|
||||
|
||||
class NodeStore;
|
||||
class JobQueue;
|
||||
class InboundLedgers;
|
||||
class LedgerMaster;
|
||||
class LoadManager;
|
||||
class NetworkOPs;
|
||||
class OrderBookDB;
|
||||
class PeerDoor;
|
||||
class SerializedLedgerEntry;
|
||||
class TransactionMaster;
|
||||
class TXQueue;
|
||||
class LocalCredentials;
|
||||
|
||||
class DatabaseCon;
|
||||
|
||||
typedef TaggedCache <uint256, Blob , UptimeTimerAdapter> NodeCache;
|
||||
typedef TaggedCache <uint256, SerializedLedgerEntry, UptimeTimerAdapter> SLECache;
|
||||
|
||||
class Application
|
||||
{
|
||||
public:
|
||||
/* VFALCO NOTE
|
||||
|
||||
The master lock protects:
|
||||
|
||||
- The open ledger
|
||||
- Server global state
|
||||
* What the last closed ledger is
|
||||
* State of the consensus engine
|
||||
|
||||
other things
|
||||
*/
|
||||
#if 1
|
||||
class ScopedLockType;
|
||||
|
||||
class MasterLockType
|
||||
{
|
||||
public:
|
||||
MasterLockType ()
|
||||
: m_fileName ("")
|
||||
, m_lineNumber (0)
|
||||
{
|
||||
}
|
||||
|
||||
// Note that these are not exactly thread safe.
|
||||
|
||||
char const* getFileName () const noexcept
|
||||
{
|
||||
return m_fileName.get ();
|
||||
}
|
||||
|
||||
int getLineNumber () const noexcept
|
||||
{
|
||||
return m_lineNumber.get ();
|
||||
}
|
||||
|
||||
private:
|
||||
friend class ScopedLockType;
|
||||
|
||||
void setOwner (char const* fileName, int lineNumber)
|
||||
{
|
||||
m_fileName.set (fileName);
|
||||
m_lineNumber.set (lineNumber);
|
||||
}
|
||||
|
||||
void resetOwner ()
|
||||
{
|
||||
m_fileName.set ("");
|
||||
m_lineNumber.set (0);
|
||||
}
|
||||
|
||||
boost::recursive_mutex m_mutex;
|
||||
Atomic <char const*> m_fileName;
|
||||
Atomic <int> m_lineNumber;
|
||||
};
|
||||
|
||||
class ScopedLockType
|
||||
{
|
||||
public:
|
||||
explicit ScopedLockType (MasterLockType& mutex,
|
||||
char const* fileName,
|
||||
int lineNumber)
|
||||
: m_mutex (mutex)
|
||||
, m_lock (mutex.m_mutex)
|
||||
{
|
||||
mutex.setOwner (fileName, lineNumber);
|
||||
}
|
||||
|
||||
~ScopedLockType ()
|
||||
{
|
||||
if (m_lock.owns_lock ())
|
||||
m_mutex.resetOwner ();
|
||||
}
|
||||
|
||||
void unlock ()
|
||||
{
|
||||
if (m_lock.owns_lock ())
|
||||
m_mutex.resetOwner ();
|
||||
|
||||
m_lock.unlock ();
|
||||
}
|
||||
|
||||
private:
|
||||
MasterLockType& m_mutex;
|
||||
boost::recursive_mutex::scoped_lock m_lock;
|
||||
};
|
||||
|
||||
#else
|
||||
typedef boost::recursive_mutex MasterLockType;
|
||||
|
||||
typedef boost::recursive_mutex::scoped_lock ScopedLockType;
|
||||
|
||||
#endif
|
||||
|
||||
virtual MasterLockType& getMasterLock () = 0;
|
||||
|
||||
|
||||
|
||||
|
||||
public:
|
||||
struct State
|
||||
{
|
||||
// Stuff in here is accessed concurrently and requires a WriteAccess
|
||||
};
|
||||
|
||||
typedef SharedData <State> SharedState;
|
||||
|
||||
SharedState& getSharedState () noexcept { return m_sharedState; }
|
||||
|
||||
SharedState const& getSharedState () const noexcept { return m_sharedState; }
|
||||
|
||||
private:
|
||||
SharedState m_sharedState;
|
||||
|
||||
public:
|
||||
virtual ~Application () { }
|
||||
|
||||
virtual boost::asio::io_service& getIOService () = 0;
|
||||
|
||||
virtual NodeCache& getTempNodeCache () = 0;
|
||||
virtual SLECache& getSLECache () = 0;
|
||||
|
||||
virtual Validators& getValidators () = 0;
|
||||
virtual IFeatures& getFeatureTable () = 0;
|
||||
virtual IFeeVote& getFeeVote () = 0;
|
||||
virtual IHashRouter& getHashRouter () = 0;
|
||||
virtual ILoadFeeTrack& getFeeTrack () = 0;
|
||||
virtual ILoadManager& getLoadManager () = 0;
|
||||
virtual IPeers& getPeers () = 0;
|
||||
virtual IProofOfWorkFactory& getProofOfWorkFactory () = 0;
|
||||
virtual UniqueNodeList& getUNL () = 0;
|
||||
virtual IValidations& getValidations () = 0;
|
||||
|
||||
virtual NodeStore& getNodeStore () = 0;
|
||||
virtual JobQueue& getJobQueue () = 0;
|
||||
virtual InboundLedgers& getInboundLedgers () = 0;
|
||||
virtual LedgerMaster& getLedgerMaster () = 0;
|
||||
virtual NetworkOPs& getOPs () = 0;
|
||||
virtual OrderBookDB& getOrderBookDB () = 0;
|
||||
virtual PeerDoor& getPeerDoor () = 0;
|
||||
virtual TransactionMaster& getMasterTransaction () = 0;
|
||||
virtual TXQueue& getTxnQueue () = 0;
|
||||
virtual LocalCredentials& getLocalCredentials () = 0;
|
||||
|
||||
virtual DatabaseCon* getRpcDB () = 0;
|
||||
virtual DatabaseCon* getTxnDB () = 0;
|
||||
virtual DatabaseCon* getLedgerDB () = 0;
|
||||
|
||||
/** Retrieve the "wallet database"
|
||||
|
||||
It looks like this is used to store the unique node list.
|
||||
*/
|
||||
// VFALCO TODO Rename, document this
|
||||
// NOTE This will be replaced by class Validators
|
||||
//
|
||||
virtual DatabaseCon* getWalletDB () = 0;
|
||||
|
||||
virtual bool getSystemTimeOffset (int& offset) = 0;
|
||||
virtual bool isShutdown () = 0;
|
||||
virtual bool running () = 0;
|
||||
virtual void setup () = 0;
|
||||
virtual void run () = 0;
|
||||
virtual void stop () = 0;
|
||||
virtual void sweep () = 0;
|
||||
};
|
||||
|
||||
extern Application& getApp ();
|
||||
|
||||
#endif
|
||||
@@ -1,78 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
bool CanonicalTXSet::Key::operator< (Key const& rhs) const
|
||||
{
|
||||
if (mAccount < rhs.mAccount) return true;
|
||||
|
||||
if (mAccount > rhs.mAccount) return false;
|
||||
|
||||
if (mSeq < rhs.mSeq) return true;
|
||||
|
||||
if (mSeq > rhs.mSeq) return false;
|
||||
|
||||
return mTXid < rhs.mTXid;
|
||||
}
|
||||
|
||||
bool CanonicalTXSet::Key::operator> (Key const& rhs) const
|
||||
{
|
||||
if (mAccount > rhs.mAccount) return true;
|
||||
|
||||
if (mAccount < rhs.mAccount) return false;
|
||||
|
||||
if (mSeq > rhs.mSeq) return true;
|
||||
|
||||
if (mSeq < rhs.mSeq) return false;
|
||||
|
||||
return mTXid > rhs.mTXid;
|
||||
}
|
||||
|
||||
bool CanonicalTXSet::Key::operator<= (Key const& rhs) const
|
||||
{
|
||||
if (mAccount < rhs.mAccount) return true;
|
||||
|
||||
if (mAccount > rhs.mAccount) return false;
|
||||
|
||||
if (mSeq < rhs.mSeq) return true;
|
||||
|
||||
if (mSeq > rhs.mSeq) return false;
|
||||
|
||||
return mTXid <= rhs.mTXid;
|
||||
}
|
||||
|
||||
bool CanonicalTXSet::Key::operator>= (Key const& rhs)const
|
||||
{
|
||||
if (mAccount > rhs.mAccount) return true;
|
||||
|
||||
if (mAccount < rhs.mAccount) return false;
|
||||
|
||||
if (mSeq > rhs.mSeq) return true;
|
||||
|
||||
if (mSeq < rhs.mSeq) return false;
|
||||
|
||||
return mTXid >= rhs.mTXid;
|
||||
}
|
||||
|
||||
void CanonicalTXSet::push_back (SerializedTransaction::ref txn)
|
||||
{
|
||||
uint256 effectiveAccount = mSetHash;
|
||||
|
||||
effectiveAccount ^= txn->getSourceAccount ().getAccountID ().to256 ();
|
||||
|
||||
mMap.insert (std::make_pair (
|
||||
Key (effectiveAccount, txn->getSequence (), txn->getTransactionID ()),
|
||||
txn));
|
||||
}
|
||||
|
||||
CanonicalTXSet::iterator CanonicalTXSet::erase (iterator const& it)
|
||||
{
|
||||
iterator tmp = it;
|
||||
++tmp;
|
||||
mMap.erase (it);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,109 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_CANONICALTXSET_H
|
||||
#define RIPPLE_CANONICALTXSET_H
|
||||
|
||||
/** Holds transactions which were deferred to the next pass of consensus.
|
||||
|
||||
"Canonical" refers to the order in which transactions are applied.
|
||||
|
||||
- Puts transactions from the same account in sequence order
|
||||
|
||||
*/
|
||||
// VFALCO TODO rename to SortedTxSet
|
||||
class CanonicalTXSet : LeakChecked <CanonicalTXSet>
|
||||
{
|
||||
public:
|
||||
class Key
|
||||
{
|
||||
public:
|
||||
Key (uint256 const& account, uint32 seq, uint256 const& id)
|
||||
: mAccount (account)
|
||||
, mTXid (id)
|
||||
, mSeq (seq)
|
||||
{
|
||||
}
|
||||
|
||||
bool operator< (Key const& rhs) const;
|
||||
bool operator> (Key const& rhs) const;
|
||||
bool operator<= (Key const& rhs) const;
|
||||
bool operator>= (Key const& rhs) const;
|
||||
|
||||
bool operator== (Key const& rhs) const
|
||||
{
|
||||
return mTXid == rhs.mTXid;
|
||||
}
|
||||
bool operator!= (Key const& rhs) const
|
||||
{
|
||||
return mTXid != rhs.mTXid;
|
||||
}
|
||||
|
||||
uint256 const& getTXID () const
|
||||
{
|
||||
return mTXid;
|
||||
}
|
||||
|
||||
private:
|
||||
uint256 mAccount;
|
||||
uint256 mTXid;
|
||||
uint32 mSeq;
|
||||
};
|
||||
|
||||
typedef std::map <Key, SerializedTransaction::pointer>::iterator iterator;
|
||||
typedef std::map <Key, SerializedTransaction::pointer>::const_iterator const_iterator;
|
||||
|
||||
public:
|
||||
explicit CanonicalTXSet (LedgerHash const& lastClosedLedgerHash)
|
||||
: mSetHash (lastClosedLedgerHash)
|
||||
{
|
||||
}
|
||||
|
||||
void push_back (SerializedTransaction::ref txn);
|
||||
|
||||
// VFALCO TODO remove this function
|
||||
void reset (LedgerHash const& newLastClosedLedgerHash)
|
||||
{
|
||||
mSetHash = newLastClosedLedgerHash;
|
||||
|
||||
mMap.clear ();
|
||||
}
|
||||
|
||||
iterator erase (iterator const& it);
|
||||
|
||||
iterator begin ()
|
||||
{
|
||||
return mMap.begin ();
|
||||
}
|
||||
iterator end ()
|
||||
{
|
||||
return mMap.end ();
|
||||
}
|
||||
const_iterator begin () const
|
||||
{
|
||||
return mMap.begin ();
|
||||
}
|
||||
const_iterator end () const
|
||||
{
|
||||
return mMap.end ();
|
||||
}
|
||||
size_t size () const
|
||||
{
|
||||
return mMap.size ();
|
||||
}
|
||||
bool empty () const
|
||||
{
|
||||
return mMap.empty ();
|
||||
}
|
||||
|
||||
private:
|
||||
// Used to salt the accounts so people can't mine for low account numbers
|
||||
uint256 mSetHash;
|
||||
|
||||
std::map <Key, SerializedTransaction::pointer> mMap;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,55 +0,0 @@
|
||||
|
||||
|
||||
|
||||
#ifndef RIPPLE_CLUSTERNODESTATUS_H
|
||||
#define RIPPLE_CLUSTERNODESTATUS_H
|
||||
|
||||
class ClusterNodeStatus
|
||||
{
|
||||
public:
|
||||
|
||||
ClusterNodeStatus() : mLoadFee(0), mReportTime(0)
|
||||
{ ; }
|
||||
|
||||
ClusterNodeStatus(std::string const& name) : mNodeName(name), mLoadFee(0), mReportTime(0)
|
||||
{ ; }
|
||||
|
||||
ClusterNodeStatus(const std::string& name, uint32 fee, uint32 rtime) :
|
||||
mNodeName(name),
|
||||
mLoadFee(fee),
|
||||
mReportTime(rtime)
|
||||
{ ; }
|
||||
|
||||
std::string const& getName()
|
||||
{
|
||||
return mNodeName;
|
||||
}
|
||||
|
||||
uint32 getLoadFee()
|
||||
{
|
||||
return mLoadFee;
|
||||
}
|
||||
|
||||
uint32 getReportTime()
|
||||
{
|
||||
return mReportTime;
|
||||
}
|
||||
|
||||
bool update(ClusterNodeStatus const& status)
|
||||
{
|
||||
if (status.mReportTime <= mReportTime)
|
||||
return false;
|
||||
mLoadFee = status.mLoadFee;
|
||||
mReportTime = status.mReportTime;
|
||||
if (mNodeName.empty() || !status.mNodeName.empty())
|
||||
mNodeName = status.mNodeName;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string mNodeName;
|
||||
uint32 mLoadFee;
|
||||
uint32 mReportTime;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,137 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#define TRUST_NETWORK
|
||||
|
||||
// Track a peer's yes/no vote on a particular disputed transaction
|
||||
void DisputedTx::setVote (const uint160& peer, bool votesYes)
|
||||
{
|
||||
// VFALCO TODO Simplify this declaration. It doesn't exactly roll off the tongue!
|
||||
std::pair <boost::unordered_map <const uint160, bool>::iterator, bool> res =
|
||||
mVotes.insert (std::pair<const uint160, bool> (peer, votesYes));
|
||||
|
||||
// new vote
|
||||
if (res.second)
|
||||
{
|
||||
if (votesYes)
|
||||
{
|
||||
WriteLog (lsDEBUG, LedgerConsensus) << "Peer " << peer << " votes YES on " << mTransactionID;
|
||||
++mYays;
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog (lsDEBUG, LedgerConsensus) << "Peer " << peer << " votes NO on " << mTransactionID;
|
||||
++mNays;
|
||||
}
|
||||
}
|
||||
// changes vote to yes
|
||||
else if (votesYes && !res.first->second)
|
||||
{
|
||||
WriteLog (lsDEBUG, LedgerConsensus) << "Peer " << peer << " now votes YES on " << mTransactionID;
|
||||
--mNays;
|
||||
++mYays;
|
||||
res.first->second = true;
|
||||
}
|
||||
// changes vote to no
|
||||
else if (!votesYes && res.first->second)
|
||||
{
|
||||
WriteLog (lsDEBUG, LedgerConsensus) << "Peer " << peer << " now votes NO on " << mTransactionID;
|
||||
++mNays;
|
||||
--mYays;
|
||||
res.first->second = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove a peer's vote on this disputed transasction
|
||||
void DisputedTx::unVote (const uint160& peer)
|
||||
{
|
||||
boost::unordered_map<uint160, bool>::iterator it = mVotes.find (peer);
|
||||
|
||||
if (it != mVotes.end ())
|
||||
{
|
||||
if (it->second)
|
||||
--mYays;
|
||||
else
|
||||
--mNays;
|
||||
|
||||
mVotes.erase (it);
|
||||
}
|
||||
}
|
||||
|
||||
bool DisputedTx::updateVote (int percentTime, bool proposing)
|
||||
{
|
||||
// VFALCO TODO Give the return value a descriptive local variable name
|
||||
// and don't return from the middle.
|
||||
|
||||
if (mOurVote && (mNays == 0))
|
||||
return false;
|
||||
|
||||
if (!mOurVote && (mYays == 0))
|
||||
return false;
|
||||
|
||||
bool newPosition;
|
||||
int weight;
|
||||
|
||||
if (proposing) // give ourselves full weight
|
||||
{
|
||||
// This is basically the percentage of nodes voting 'yes' (including us)
|
||||
weight = (mYays * 100 + (mOurVote ? 100 : 0)) / (mNays + mYays + 1);
|
||||
|
||||
// VFALCO TODO Rename these macros and turn them into language constructs.
|
||||
// consolidate them into a class that collects all these related values.
|
||||
//
|
||||
// To prevent avalanche stalls, we increase the needed weight slightly over time
|
||||
if (percentTime < AV_MID_CONSENSUS_TIME)
|
||||
newPosition = weight > AV_INIT_CONSENSUS_PCT;
|
||||
else if (percentTime < AV_LATE_CONSENSUS_TIME)
|
||||
newPosition = weight > AV_MID_CONSENSUS_PCT;
|
||||
else if (percentTime < AV_STUCK_CONSENSUS_TIME)
|
||||
newPosition = weight > AV_LATE_CONSENSUS_PCT;
|
||||
else
|
||||
newPosition = weight > AV_STUCK_CONSENSUS_PCT;
|
||||
}
|
||||
else // don't let us outweigh a proposing node, just recognize consensus
|
||||
{
|
||||
weight = -1;
|
||||
newPosition = mYays > mNays;
|
||||
}
|
||||
|
||||
if (newPosition == mOurVote)
|
||||
{
|
||||
WriteLog (lsINFO, LedgerConsensus) <<
|
||||
"No change (" << (mOurVote ? "YES" : "NO") << ") : weight " << weight << ", percent " << percentTime;
|
||||
WriteLog (lsDEBUG, LedgerConsensus) << getJson ();
|
||||
return false;
|
||||
}
|
||||
|
||||
mOurVote = newPosition;
|
||||
WriteLog (lsDEBUG, LedgerConsensus) << "We now vote " << (mOurVote ? "YES" : "NO") << " on " << mTransactionID;
|
||||
WriteLog (lsDEBUG, LedgerConsensus) << getJson ();
|
||||
return true;
|
||||
}
|
||||
|
||||
Json::Value DisputedTx::getJson ()
|
||||
{
|
||||
Json::Value ret (Json::objectValue);
|
||||
|
||||
ret["yays"] = mYays;
|
||||
ret["nays"] = mNays;
|
||||
ret["our_vote"] = mOurVote;
|
||||
|
||||
if (!mVotes.empty ())
|
||||
{
|
||||
Json::Value votesj (Json::objectValue);
|
||||
typedef boost::unordered_map<uint160, bool>::value_type vt;
|
||||
BOOST_FOREACH (vt & vote, mVotes)
|
||||
{
|
||||
votesj[vote.first.GetHex ()] = vote.second;
|
||||
}
|
||||
ret["votes"] = votesj;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_DISPUTEDTX_H
|
||||
#define RIPPLE_DISPUTEDTX_H
|
||||
|
||||
/** A transaction discovered to be in dispute during conensus.
|
||||
|
||||
During consensus, a @ref DisputedTx is created when a transaction
|
||||
is discovered to be disputed. The object persists only as long as
|
||||
the dispute.
|
||||
|
||||
Undisputed transactions have no corresponding @ref DisputedTx object.
|
||||
*/
|
||||
class DisputedTx
|
||||
{
|
||||
public:
|
||||
typedef boost::shared_ptr <DisputedTx> pointer;
|
||||
|
||||
DisputedTx (uint256 const& txID,
|
||||
Blob const& tx,
|
||||
bool ourVote) :
|
||||
mTransactionID (txID), mYays (0), mNays (0), mOurVote (ourVote), transaction (tx)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
uint256 const& getTransactionID () const
|
||||
{
|
||||
return mTransactionID;
|
||||
}
|
||||
|
||||
bool getOurVote () const
|
||||
{
|
||||
return mOurVote;
|
||||
}
|
||||
|
||||
// VFALCO TODO make this const
|
||||
Serializer& peekTransaction ()
|
||||
{
|
||||
return transaction;
|
||||
}
|
||||
|
||||
void setOurVote (bool o)
|
||||
{
|
||||
mOurVote = o;
|
||||
}
|
||||
|
||||
// VFALCO NOTE its not really a peer, its the 160 bit hash of the validator's public key
|
||||
//
|
||||
void setVote (uint160 const& peer, bool votesYes);
|
||||
void unVote (uint160 const& peer);
|
||||
|
||||
bool updateVote (int percentTime, bool proposing);
|
||||
Json::Value getJson ();
|
||||
|
||||
private:
|
||||
uint256 mTransactionID;
|
||||
int mYays;
|
||||
int mNays;
|
||||
bool mOurVote;
|
||||
Serializer transaction;
|
||||
boost::unordered_map <uint160, bool> mVotes;
|
||||
};
|
||||
|
||||
// VFALCO TODO Rename and put these in a tidy place
|
||||
typedef std::map<uint256, DisputedTx::pointer>::value_type u256_lct_pair;
|
||||
typedef std::map<uint160, LedgerProposal::pointer>::value_type u160_prop_pair;
|
||||
#define LEDGER_TOTAL_PASSES 8
|
||||
#define LEDGER_RETRY_PASSES 5
|
||||
|
||||
#endif
|
||||
@@ -1,541 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
class Features;
|
||||
|
||||
SETUP_LOG (Features)
|
||||
|
||||
FeatureState* testFeature = NULL;
|
||||
|
||||
// VFALCO TODO Rename this to Features
|
||||
class Features : public IFeatures
|
||||
{
|
||||
protected:
|
||||
|
||||
typedef boost::unordered_map<uint256, FeatureState> featureMap_t;
|
||||
typedef std::pair<const uint256, FeatureState> featureIt_t;
|
||||
typedef boost::unordered_set<uint256> featureList_t;
|
||||
|
||||
boost::mutex mMutex;
|
||||
featureMap_t mFeatureMap;
|
||||
int mMajorityTime; // Seconds a feature must hold a majority
|
||||
int mMajorityFraction; // 256 = 100%
|
||||
uint32 mFirstReport; // close time of first majority report
|
||||
uint32 mLastReport; // close time of most recent majority report
|
||||
|
||||
FeatureState* getCreateFeature (uint256 const& feature, bool create);
|
||||
bool shouldEnable (uint32 closeTime, const FeatureState& fs);
|
||||
void setJson (Json::Value& v, const FeatureState&);
|
||||
|
||||
public:
|
||||
|
||||
Features (uint32 majorityTime, int majorityFraction)
|
||||
: mMajorityTime (majorityTime), mMajorityFraction (majorityFraction), mFirstReport (0), mLastReport (0)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
void addInitialFeatures ();
|
||||
|
||||
FeatureState* addKnownFeature (const char* featureID, const char* friendlyName, bool veto);
|
||||
uint256 getFeature (const std::string& name);
|
||||
|
||||
bool vetoFeature (uint256 const& feature);
|
||||
bool unVetoFeature (uint256 const& feature);
|
||||
|
||||
bool enableFeature (uint256 const& feature);
|
||||
bool disableFeature (uint256 const& feature);
|
||||
|
||||
bool isFeatureEnabled (uint256 const& feature);
|
||||
bool isFeatureSupported (uint256 const& feature);
|
||||
|
||||
void setEnabledFeatures (const std::vector<uint256>& features);
|
||||
void setSupportedFeatures (const std::vector<uint256>& features);
|
||||
|
||||
featureList_t getVetoedFeatures ();
|
||||
featureList_t getEnabledFeatures ();
|
||||
featureList_t getFeaturesToEnable (uint32 closeTime); // gets features we would vote to enable
|
||||
featureList_t getDesiredFeatures (); // features we support, do not veto, are not enabled
|
||||
|
||||
void reportValidations (const FeatureSet&);
|
||||
|
||||
Json::Value getJson (int);
|
||||
Json::Value getJson (uint256 const& );
|
||||
|
||||
void doValidation (Ledger::ref lastClosedLedger, STObject& baseValidation);
|
||||
void doVoting (Ledger::ref lastClosedLedger, SHAMap::ref initialPosition);
|
||||
};
|
||||
|
||||
void Features::addInitialFeatures ()
|
||||
{
|
||||
// For each feature this version supports, construct the FeatureState object by calling
|
||||
// getCreateFeature. Set any vetoes or defaults. A pointer to the FeatureState can be stashed
|
||||
|
||||
testFeature = addKnownFeature ("1234", "testFeature", false);
|
||||
}
|
||||
|
||||
FeatureState* Features::getCreateFeature (uint256 const& featureHash, bool create)
|
||||
{
|
||||
// call with the mutex held
|
||||
featureMap_t::iterator it = mFeatureMap.find (featureHash);
|
||||
|
||||
if (it == mFeatureMap.end ())
|
||||
{
|
||||
if (!create)
|
||||
return NULL;
|
||||
|
||||
FeatureState* feature = & (mFeatureMap[featureHash]);
|
||||
|
||||
{
|
||||
std::string query = "SELECT FirstMajority,LastMajority FROM Features WHERE hash='";
|
||||
query.append (featureHash.GetHex ());
|
||||
query.append ("';");
|
||||
|
||||
ScopedLock sl (getApp().getWalletDB ()->getDBLock ());
|
||||
Database* db = getApp().getWalletDB ()->getDB ();
|
||||
|
||||
if (db->executeSQL (query) && db->startIterRows ())
|
||||
{
|
||||
feature->mFirstMajority = db->getBigInt ("FirstMajority");
|
||||
feature->mLastMajority = db->getBigInt ("LastMajority");
|
||||
db->endIterRows ();
|
||||
}
|
||||
}
|
||||
|
||||
return feature;
|
||||
}
|
||||
|
||||
return & (it->second);
|
||||
}
|
||||
|
||||
uint256 Features::getFeature (const std::string& name)
|
||||
{
|
||||
if (!name.empty ())
|
||||
{
|
||||
BOOST_FOREACH (featureMap_t::value_type & it, mFeatureMap)
|
||||
{
|
||||
if (name == it.second.mFriendlyName)
|
||||
return it.first;
|
||||
}
|
||||
}
|
||||
|
||||
return uint256 ();
|
||||
}
|
||||
|
||||
FeatureState* Features::addKnownFeature (const char* featureID, const char* friendlyName, bool veto)
|
||||
{
|
||||
uint256 hash;
|
||||
hash.SetHex (featureID);
|
||||
|
||||
if (hash.isZero ())
|
||||
{
|
||||
assert (false);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FeatureState* f = getCreateFeature (hash, true);
|
||||
|
||||
if (friendlyName != NULL)
|
||||
f->setFriendlyName (friendlyName);
|
||||
|
||||
f->mVetoed = veto;
|
||||
f->mSupported = true;
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
bool Features::vetoFeature (uint256 const& feature)
|
||||
{
|
||||
boost::mutex::scoped_lock sl (mMutex);
|
||||
FeatureState* s = getCreateFeature (feature, true);
|
||||
|
||||
if (s->mVetoed)
|
||||
return false;
|
||||
|
||||
s->mVetoed = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Features::unVetoFeature (uint256 const& feature)
|
||||
{
|
||||
boost::mutex::scoped_lock sl (mMutex);
|
||||
FeatureState* s = getCreateFeature (feature, false);
|
||||
|
||||
if (!s || !s->mVetoed)
|
||||
return false;
|
||||
|
||||
s->mVetoed = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Features::enableFeature (uint256 const& feature)
|
||||
{
|
||||
boost::mutex::scoped_lock sl (mMutex);
|
||||
FeatureState* s = getCreateFeature (feature, true);
|
||||
|
||||
if (s->mEnabled)
|
||||
return false;
|
||||
|
||||
s->mEnabled = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Features::disableFeature (uint256 const& feature)
|
||||
{
|
||||
boost::mutex::scoped_lock sl (mMutex);
|
||||
FeatureState* s = getCreateFeature (feature, false);
|
||||
|
||||
if (!s || !s->mEnabled)
|
||||
return false;
|
||||
|
||||
s->mEnabled = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Features::isFeatureEnabled (uint256 const& feature)
|
||||
{
|
||||
boost::mutex::scoped_lock sl (mMutex);
|
||||
FeatureState* s = getCreateFeature (feature, false);
|
||||
return s && s->mEnabled;
|
||||
}
|
||||
|
||||
bool Features::isFeatureSupported (uint256 const& feature)
|
||||
{
|
||||
boost::mutex::scoped_lock sl (mMutex);
|
||||
FeatureState* s = getCreateFeature (feature, false);
|
||||
return s && s->mSupported;
|
||||
}
|
||||
|
||||
Features::featureList_t Features::getVetoedFeatures ()
|
||||
{
|
||||
featureList_t ret;
|
||||
boost::mutex::scoped_lock sl (mMutex);
|
||||
BOOST_FOREACH (const featureIt_t & it, mFeatureMap)
|
||||
{
|
||||
if (it.second.mVetoed)
|
||||
ret.insert (it.first);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
Features::featureList_t Features::getEnabledFeatures ()
|
||||
{
|
||||
featureList_t ret;
|
||||
boost::mutex::scoped_lock sl (mMutex);
|
||||
BOOST_FOREACH (const featureIt_t & it, mFeatureMap)
|
||||
{
|
||||
if (it.second.mEnabled)
|
||||
ret.insert (it.first);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Features::shouldEnable (uint32 closeTime, const FeatureState& fs)
|
||||
{
|
||||
if (fs.mVetoed || fs.mEnabled || !fs.mSupported || (fs.mLastMajority != mLastReport))
|
||||
return false;
|
||||
|
||||
if (fs.mFirstMajority == mFirstReport)
|
||||
{
|
||||
// had a majority when we first started the server, relaxed check
|
||||
// WRITEME
|
||||
}
|
||||
|
||||
// didn't have a majority when we first started the server, normal check
|
||||
return (fs.mLastMajority - fs.mFirstMajority) > mMajorityTime;
|
||||
|
||||
}
|
||||
|
||||
Features::featureList_t Features::getFeaturesToEnable (uint32 closeTime)
|
||||
{
|
||||
featureList_t ret;
|
||||
boost::mutex::scoped_lock sl (mMutex);
|
||||
|
||||
if (mLastReport != 0)
|
||||
{
|
||||
BOOST_FOREACH (const featureIt_t & it, mFeatureMap)
|
||||
{
|
||||
if (shouldEnable (closeTime, it.second))
|
||||
ret.insert (it.first);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Features::featureList_t Features::getDesiredFeatures ()
|
||||
{
|
||||
featureList_t ret;
|
||||
boost::mutex::scoped_lock sl (mMutex);
|
||||
BOOST_FOREACH (const featureIt_t & it, mFeatureMap)
|
||||
{
|
||||
if (it.second.mSupported && !it.second.mEnabled && !it.second.mVetoed)
|
||||
ret.insert (it.first);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Features::reportValidations (const FeatureSet& set)
|
||||
{
|
||||
if (set.mTrustedValidations == 0)
|
||||
return;
|
||||
|
||||
int threshold = (set.mTrustedValidations * mMajorityFraction) / 256;
|
||||
|
||||
typedef std::map<uint256, int>::value_type u256_int_pair;
|
||||
|
||||
boost::mutex::scoped_lock sl (mMutex);
|
||||
|
||||
if (mFirstReport == 0)
|
||||
mFirstReport = set.mCloseTime;
|
||||
|
||||
std::vector<uint256> changedFeatures;
|
||||
changedFeatures.resize (set.mVotes.size ());
|
||||
|
||||
BOOST_FOREACH (const u256_int_pair & it, set.mVotes)
|
||||
{
|
||||
FeatureState& state = mFeatureMap[it.first];
|
||||
WriteLog (lsDEBUG, Features) << "Feature " << it.first.GetHex () << " has " << it.second << " votes, needs " << threshold;
|
||||
|
||||
if (it.second >= threshold)
|
||||
{
|
||||
// we have a majority
|
||||
state.mLastMajority = set.mCloseTime;
|
||||
|
||||
if (state.mFirstMajority == 0)
|
||||
{
|
||||
WriteLog (lsWARNING, Features) << "Feature " << it.first << " attains a majority vote";
|
||||
state.mFirstMajority = set.mCloseTime;
|
||||
changedFeatures.push_back (it.first);
|
||||
}
|
||||
}
|
||||
else // we have no majority
|
||||
{
|
||||
if (state.mFirstMajority != 0)
|
||||
{
|
||||
WriteLog (lsWARNING, Features) << "Feature " << it.first << " loses majority vote";
|
||||
state.mFirstMajority = 0;
|
||||
state.mLastMajority = 0;
|
||||
changedFeatures.push_back (it.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
mLastReport = set.mCloseTime;
|
||||
|
||||
if (!changedFeatures.empty ())
|
||||
{
|
||||
ScopedLock sl (getApp().getWalletDB ()->getDBLock ());
|
||||
Database* db = getApp().getWalletDB ()->getDB ();
|
||||
|
||||
db->executeSQL ("BEGIN TRANSACTION;");
|
||||
BOOST_FOREACH (uint256 const & hash, changedFeatures)
|
||||
{
|
||||
FeatureState& fState = mFeatureMap[hash];
|
||||
db->executeSQL (boost::str (boost::format (
|
||||
"UPDATE Features SET FirstMajority = %d WHERE Hash = '%s';"
|
||||
) % fState.mFirstMajority % hash.GetHex ()));
|
||||
db->executeSQL (boost::str (boost::format (
|
||||
"UPDATE Features SET LastMajority = %d WHERE Hash = '%s';"
|
||||
) % fState.mLastMajority % hash.GetHex ()));
|
||||
}
|
||||
db->executeSQL ("END TRANSACTION;");
|
||||
changedFeatures.clear ();
|
||||
}
|
||||
}
|
||||
|
||||
void Features::setEnabledFeatures (const std::vector<uint256>& features)
|
||||
{
|
||||
boost::mutex::scoped_lock sl (mMutex);
|
||||
BOOST_FOREACH (featureIt_t & it, mFeatureMap)
|
||||
{
|
||||
it.second.mEnabled = false;
|
||||
}
|
||||
BOOST_FOREACH (uint256 const & it, features)
|
||||
{
|
||||
mFeatureMap[it].mEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Features::setSupportedFeatures (const std::vector<uint256>& features)
|
||||
{
|
||||
boost::mutex::scoped_lock sl (mMutex);
|
||||
BOOST_FOREACH (featureIt_t & it, mFeatureMap)
|
||||
{
|
||||
it.second.mSupported = false;
|
||||
}
|
||||
BOOST_FOREACH (uint256 const & it, features)
|
||||
{
|
||||
mFeatureMap[it].mSupported = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Features::doValidation (Ledger::ref lastClosedLedger, STObject& baseValidation)
|
||||
{
|
||||
featureList_t lFeatures = getDesiredFeatures ();
|
||||
|
||||
if (lFeatures.empty ())
|
||||
return;
|
||||
|
||||
STVector256 vFeatures (sfFeatures);
|
||||
BOOST_FOREACH (uint256 const & uFeature, lFeatures)
|
||||
{
|
||||
vFeatures.addValue (uFeature);
|
||||
}
|
||||
vFeatures.sort ();
|
||||
baseValidation.setFieldV256 (sfFeatures, vFeatures);
|
||||
}
|
||||
|
||||
void Features::doVoting (Ledger::ref lastClosedLedger, SHAMap::ref initialPosition)
|
||||
{
|
||||
featureList_t lFeatures = getFeaturesToEnable (lastClosedLedger->getCloseTimeNC ());
|
||||
|
||||
if (lFeatures.empty ())
|
||||
return;
|
||||
|
||||
BOOST_FOREACH (uint256 const & uFeature, lFeatures)
|
||||
{
|
||||
WriteLog (lsWARNING, Features) << "Voting for feature: " << uFeature;
|
||||
SerializedTransaction trans (ttFEATURE);
|
||||
trans.setFieldAccount (sfAccount, uint160 ());
|
||||
trans.setFieldH256 (sfFeature, uFeature);
|
||||
uint256 txID = trans.getTransactionID ();
|
||||
WriteLog (lsWARNING, Features) << "Vote ID: " << txID;
|
||||
|
||||
Serializer s;
|
||||
trans.add (s, true);
|
||||
|
||||
SHAMapItem::pointer tItem = boost::make_shared<SHAMapItem> (txID, s.peekData ());
|
||||
|
||||
if (!initialPosition->addGiveItem (tItem, true, false))
|
||||
{
|
||||
WriteLog (lsWARNING, Features) << "Ledger already had feature transaction";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Json::Value Features::getJson (int)
|
||||
{
|
||||
Json::Value ret (Json::objectValue);
|
||||
{
|
||||
boost::mutex::scoped_lock sl (mMutex);
|
||||
BOOST_FOREACH (const featureIt_t & it, mFeatureMap)
|
||||
{
|
||||
setJson (ret[it.first.GetHex ()] = Json::objectValue, it.second);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Features::setJson (Json::Value& v, const FeatureState& fs)
|
||||
{
|
||||
if (!fs.mFriendlyName.empty ())
|
||||
v["name"] = fs.mFriendlyName;
|
||||
|
||||
v["supported"] = fs.mSupported;
|
||||
v["vetoed"] = fs.mVetoed;
|
||||
|
||||
if (fs.mEnabled)
|
||||
v["enabled"] = true;
|
||||
else
|
||||
{
|
||||
v["enabled"] = false;
|
||||
|
||||
if (mLastReport != 0)
|
||||
{
|
||||
if (fs.mLastMajority == 0)
|
||||
{
|
||||
v["majority"] = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fs.mFirstMajority != 0)
|
||||
{
|
||||
if (fs.mFirstMajority == mFirstReport)
|
||||
v["majority_start"] = "start";
|
||||
else
|
||||
v["majority_start"] = fs.mFirstMajority;
|
||||
}
|
||||
|
||||
if (fs.mLastMajority != 0)
|
||||
{
|
||||
if (fs.mLastMajority == mLastReport)
|
||||
v["majority_until"] = "now";
|
||||
else
|
||||
v["majority_until"] = fs.mLastMajority;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fs.mVetoed)
|
||||
v["veto"] = true;
|
||||
}
|
||||
|
||||
Json::Value Features::getJson (uint256 const& feature)
|
||||
{
|
||||
Json::Value ret = Json::objectValue;
|
||||
boost::mutex::scoped_lock sl (mMutex);
|
||||
setJson (ret[feature.GetHex ()] = Json::objectValue, *getCreateFeature (feature, true));
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<typename INT> class VotableInteger
|
||||
{
|
||||
protected:
|
||||
INT mCurrent; // The current setting
|
||||
INT mTarget; // The setting we want
|
||||
std::map<INT, int> mVoteMap;
|
||||
|
||||
public:
|
||||
VotableInteger (INT current, INT target) : mCurrent (current), mTarget (target)
|
||||
{
|
||||
++mVoteMap[mTarget]; // Add our vote
|
||||
}
|
||||
|
||||
bool mayVote ()
|
||||
{
|
||||
return mCurrent != mTarget; // If we love the current setting, we will not vote
|
||||
}
|
||||
|
||||
void addVote (INT vote)
|
||||
{
|
||||
++mVoteMap[vote];
|
||||
}
|
||||
|
||||
void noVote ()
|
||||
{
|
||||
addVote (mCurrent);
|
||||
}
|
||||
|
||||
INT getVotes ()
|
||||
{
|
||||
INT ourVote = mCurrent;
|
||||
int weight = 0;
|
||||
|
||||
typedef typename std::map<INT, int>::value_type mapVType;
|
||||
BOOST_FOREACH (const mapVType & value, mVoteMap)
|
||||
{
|
||||
// Take most voted value between current and target, inclusive
|
||||
// FIXME: Should take best value that can get a significant majority
|
||||
if ((value.first <= std::max (mTarget, mCurrent)) &&
|
||||
(value.first >= std::min (mTarget, mCurrent)) &&
|
||||
(value.second > weight))
|
||||
{
|
||||
ourVote = value.first;
|
||||
weight = value.second;
|
||||
}
|
||||
}
|
||||
|
||||
return ourVote;
|
||||
}
|
||||
};
|
||||
|
||||
IFeatures* IFeatures::New (uint32 majorityTime, int majorityFraction)
|
||||
{
|
||||
return new Features (majorityTime, majorityFraction);
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
@@ -1,194 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
class Features;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class FeeVote : public IFeeVote
|
||||
{
|
||||
private:
|
||||
// VFALCO TODO rename template parameter (wtf, looks like a macro)
|
||||
template <typename INT>
|
||||
class VotableInteger
|
||||
{
|
||||
public:
|
||||
VotableInteger (INT current, INT target) : mCurrent (current), mTarget (target)
|
||||
{
|
||||
++mVoteMap[mTarget]; // Add our vote
|
||||
}
|
||||
|
||||
bool mayVote ()
|
||||
{
|
||||
return mCurrent != mTarget; // If we love the current setting, we will not vote
|
||||
}
|
||||
|
||||
void addVote (INT vote)
|
||||
{
|
||||
++mVoteMap[vote];
|
||||
}
|
||||
|
||||
void noVote ()
|
||||
{
|
||||
addVote (mCurrent);
|
||||
}
|
||||
|
||||
INT getVotes ()
|
||||
{
|
||||
INT ourVote = mCurrent;
|
||||
int weight = 0;
|
||||
|
||||
typedef typename std::map<INT, int>::value_type mapVType;
|
||||
BOOST_FOREACH (const mapVType & value, mVoteMap)
|
||||
{
|
||||
// Take most voted value between current and target, inclusive
|
||||
if ((value.first <= std::max (mTarget, mCurrent)) &&
|
||||
(value.first >= std::min (mTarget, mCurrent)) &&
|
||||
(value.second > weight))
|
||||
{
|
||||
ourVote = value.first;
|
||||
weight = value.second;
|
||||
}
|
||||
}
|
||||
|
||||
return ourVote;
|
||||
}
|
||||
|
||||
private:
|
||||
INT mCurrent; // The current setting
|
||||
INT mTarget; // The setting we want
|
||||
std::map<INT, int> mVoteMap;
|
||||
};
|
||||
public:
|
||||
FeeVote (uint64 targetBaseFee, uint32 targetReserveBase, uint32 targetReserveIncrement)
|
||||
: mTargetBaseFee (targetBaseFee)
|
||||
, mTargetReserveBase (targetReserveBase)
|
||||
, mTargetReserveIncrement (targetReserveIncrement)
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void doValidation (Ledger::ref lastClosedLedger, STObject& baseValidation)
|
||||
{
|
||||
if (lastClosedLedger->getBaseFee () != mTargetBaseFee)
|
||||
{
|
||||
WriteLog (lsINFO, Features) << "Voting for base fee of " << mTargetBaseFee;
|
||||
baseValidation.setFieldU64 (sfBaseFee, mTargetBaseFee);
|
||||
}
|
||||
|
||||
if (lastClosedLedger->getReserve (0) != mTargetReserveBase)
|
||||
{
|
||||
WriteLog (lsINFO, Features) << "Voting for base resrve of " << mTargetReserveBase;
|
||||
baseValidation.setFieldU32 (sfReserveBase, mTargetReserveBase);
|
||||
}
|
||||
|
||||
if (lastClosedLedger->getReserveInc () != mTargetReserveIncrement)
|
||||
{
|
||||
WriteLog (lsINFO, Features) << "Voting for reserve increment of " << mTargetReserveIncrement;
|
||||
baseValidation.setFieldU32 (sfReserveIncrement, mTargetReserveIncrement);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void doVoting (Ledger::ref lastClosedLedger, SHAMap::ref initialPosition)
|
||||
{
|
||||
// LCL must be flag ledger
|
||||
assert ((lastClosedLedger->getLedgerSeq () % 256) == 0);
|
||||
|
||||
VotableInteger<uint64> baseFeeVote (lastClosedLedger->getBaseFee (), mTargetBaseFee);
|
||||
VotableInteger<uint32> baseReserveVote (lastClosedLedger->getReserve (0), mTargetReserveBase);
|
||||
VotableInteger<uint32> incReserveVote (lastClosedLedger->getReserveInc (), mTargetReserveIncrement);
|
||||
|
||||
// get validations for ledger before flag
|
||||
ValidationSet set = getApp().getValidations ().getValidations (lastClosedLedger->getParentHash ());
|
||||
BOOST_FOREACH (ValidationSet::value_type const & value, set)
|
||||
{
|
||||
SerializedValidation const& val = *value.second;
|
||||
|
||||
if (val.isTrusted ())
|
||||
{
|
||||
if (val.isFieldPresent (sfBaseFee))
|
||||
{
|
||||
baseFeeVote.addVote (val.getFieldU64 (sfBaseFee));
|
||||
}
|
||||
else
|
||||
{
|
||||
baseFeeVote.noVote ();
|
||||
}
|
||||
|
||||
if (val.isFieldPresent (sfReserveBase))
|
||||
{
|
||||
baseReserveVote.addVote (val.getFieldU32 (sfReserveBase));
|
||||
}
|
||||
else
|
||||
{
|
||||
baseReserveVote.noVote ();
|
||||
}
|
||||
|
||||
if (val.isFieldPresent (sfReserveIncrement))
|
||||
{
|
||||
incReserveVote.addVote (val.getFieldU32 (sfReserveIncrement));
|
||||
}
|
||||
else
|
||||
{
|
||||
incReserveVote.noVote ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// choose our positions
|
||||
uint64 baseFee = baseFeeVote.getVotes ();
|
||||
uint32 baseReserve = baseReserveVote.getVotes ();
|
||||
uint32 incReserve = incReserveVote.getVotes ();
|
||||
|
||||
// add transactions to our position
|
||||
if ((baseFee != lastClosedLedger->getBaseFee ()) ||
|
||||
(baseReserve != lastClosedLedger->getReserve (0)) ||
|
||||
(incReserve != lastClosedLedger->getReserveInc ()))
|
||||
{
|
||||
WriteLog (lsWARNING, Features) << "We are voting for a fee change: " << baseFee << "/" << baseReserve << "/" << incReserve;
|
||||
|
||||
SerializedTransaction trans (ttFEE);
|
||||
trans.setFieldAccount (sfAccount, uint160 ());
|
||||
trans.setFieldU64 (sfBaseFee, baseFee);
|
||||
trans.setFieldU32 (sfReferenceFeeUnits, 10);
|
||||
trans.setFieldU32 (sfReserveBase, baseReserve);
|
||||
trans.setFieldU32 (sfReserveIncrement, incReserve);
|
||||
|
||||
uint256 txID = trans.getTransactionID ();
|
||||
|
||||
WriteLog (lsWARNING, Features) << "Vote: " << txID;
|
||||
|
||||
Serializer s;
|
||||
trans.add (s, true);
|
||||
|
||||
SHAMapItem::pointer tItem = boost::make_shared<SHAMapItem> (txID, s.peekData ());
|
||||
|
||||
if (!initialPosition->addGiveItem (tItem, true, false))
|
||||
{
|
||||
WriteLog (lsWARNING, Features) << "Ledger already had fee change";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
uint64 mTargetBaseFee;
|
||||
uint32 mTargetReserveBase;
|
||||
uint32 mTargetReserveIncrement;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
IFeeVote* IFeeVote::New (uint64 targetBaseFee,
|
||||
uint32 targetReserveBase,
|
||||
uint32 targetReserveIncrement)
|
||||
{
|
||||
return new FeeVote (targetBaseFee, targetReserveBase, targetReserveIncrement);
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
@@ -1,226 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
// VFALCO TODO Inline the function definitions
|
||||
class HashRouter : public IHashRouter
|
||||
{
|
||||
private:
|
||||
/** An entry in the routing table.
|
||||
*/
|
||||
class Entry : public CountedObject <Entry>
|
||||
{
|
||||
public:
|
||||
static char const* getCountedObjectName () { return "HashRouterEntry"; }
|
||||
|
||||
Entry ()
|
||||
: mFlags (0)
|
||||
{
|
||||
}
|
||||
|
||||
std::set <uint64> const& peekPeers () const
|
||||
{
|
||||
return mPeers;
|
||||
}
|
||||
|
||||
void addPeer (uint64 peer)
|
||||
{
|
||||
if (peer != 0)
|
||||
mPeers.insert (peer);
|
||||
}
|
||||
|
||||
bool hasPeer (uint64 peer) const
|
||||
{
|
||||
return mPeers.count (peer) > 0;
|
||||
}
|
||||
|
||||
int getFlags (void) const
|
||||
{
|
||||
return mFlags;
|
||||
}
|
||||
|
||||
bool hasFlag (int mask) const
|
||||
{
|
||||
return (mFlags & mask) != 0;
|
||||
}
|
||||
|
||||
void setFlag (int flagsToSet)
|
||||
{
|
||||
mFlags |= flagsToSet;
|
||||
}
|
||||
|
||||
void clearFlag (int flagsToClear)
|
||||
{
|
||||
mFlags &= ~flagsToClear;
|
||||
}
|
||||
|
||||
void swapSet (std::set <uint64>& other)
|
||||
{
|
||||
mPeers.swap (other);
|
||||
}
|
||||
|
||||
private:
|
||||
int mFlags;
|
||||
std::set <uint64> mPeers;
|
||||
};
|
||||
|
||||
public:
|
||||
explicit HashRouter (int holdTime)
|
||||
: mHoldTime (holdTime)
|
||||
{
|
||||
}
|
||||
|
||||
bool addSuppression (uint256 const& index);
|
||||
|
||||
bool addSuppressionPeer (uint256 const& index, uint64 peer);
|
||||
bool addSuppressionPeer (uint256 const& index, uint64 peer, int& flags);
|
||||
bool addSuppressionFlags (uint256 const& index, int flag);
|
||||
bool setFlag (uint256 const& index, int flag);
|
||||
int getFlags (uint256 const& index);
|
||||
|
||||
bool swapSet (uint256 const& index, std::set<uint64>& peers, int flag);
|
||||
|
||||
private:
|
||||
Entry getEntry (uint256 const& );
|
||||
|
||||
Entry& findCreateEntry (uint256 const& , bool& created);
|
||||
|
||||
boost::mutex mSuppressionMutex;
|
||||
|
||||
// Stores all suppressed hashes and their expiration time
|
||||
boost::unordered_map <uint256, Entry> mSuppressionMap;
|
||||
|
||||
// Stores all expiration times and the hashes indexed for them
|
||||
std::map< int, std::list<uint256> > mSuppressionTimes;
|
||||
|
||||
int mHoldTime;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
HashRouter::Entry& HashRouter::findCreateEntry (uint256 const& index, bool& created)
|
||||
{
|
||||
boost::unordered_map<uint256, Entry>::iterator fit = mSuppressionMap.find (index);
|
||||
|
||||
if (fit != mSuppressionMap.end ())
|
||||
{
|
||||
created = false;
|
||||
return fit->second;
|
||||
}
|
||||
|
||||
created = true;
|
||||
|
||||
int now = UptimeTimer::getInstance ().getElapsedSeconds ();
|
||||
int expireTime = now - mHoldTime;
|
||||
|
||||
// See if any supressions need to be expired
|
||||
std::map< int, std::list<uint256> >::iterator it = mSuppressionTimes.begin ();
|
||||
|
||||
if ((it != mSuppressionTimes.end ()) && (it->first <= expireTime))
|
||||
{
|
||||
BOOST_FOREACH (uint256 const & lit, it->second)
|
||||
mSuppressionMap.erase (lit);
|
||||
mSuppressionTimes.erase (it);
|
||||
}
|
||||
|
||||
mSuppressionTimes[now].push_back (index);
|
||||
return mSuppressionMap.emplace (index, Entry ()).first->second;
|
||||
}
|
||||
|
||||
bool HashRouter::addSuppression (uint256 const& index)
|
||||
{
|
||||
boost::mutex::scoped_lock sl (mSuppressionMutex);
|
||||
|
||||
bool created;
|
||||
findCreateEntry (index, created);
|
||||
return created;
|
||||
}
|
||||
|
||||
HashRouter::Entry HashRouter::getEntry (uint256 const& index)
|
||||
{
|
||||
boost::mutex::scoped_lock sl (mSuppressionMutex);
|
||||
|
||||
bool created;
|
||||
return findCreateEntry (index, created);
|
||||
}
|
||||
|
||||
bool HashRouter::addSuppressionPeer (uint256 const& index, uint64 peer)
|
||||
{
|
||||
boost::mutex::scoped_lock sl (mSuppressionMutex);
|
||||
|
||||
bool created;
|
||||
findCreateEntry (index, created).addPeer (peer);
|
||||
return created;
|
||||
}
|
||||
|
||||
bool HashRouter::addSuppressionPeer (uint256 const& index, uint64 peer, int& flags)
|
||||
{
|
||||
boost::mutex::scoped_lock sl (mSuppressionMutex);
|
||||
|
||||
bool created;
|
||||
Entry& s = findCreateEntry (index, created);
|
||||
s.addPeer (peer);
|
||||
flags = s.getFlags ();
|
||||
return created;
|
||||
}
|
||||
|
||||
int HashRouter::getFlags (uint256 const& index)
|
||||
{
|
||||
boost::mutex::scoped_lock sl (mSuppressionMutex);
|
||||
|
||||
bool created;
|
||||
return findCreateEntry (index, created).getFlags ();
|
||||
}
|
||||
|
||||
bool HashRouter::addSuppressionFlags (uint256 const& index, int flag)
|
||||
{
|
||||
boost::mutex::scoped_lock sl (mSuppressionMutex);
|
||||
|
||||
bool created;
|
||||
findCreateEntry (index, created).setFlag (flag);
|
||||
return created;
|
||||
}
|
||||
|
||||
bool HashRouter::setFlag (uint256 const& index, int flag)
|
||||
{
|
||||
// VFALCO NOTE Comments like this belong in the HEADER file,
|
||||
// and more importantly in a Javadoc comment so
|
||||
// they appear in the generated documentation.
|
||||
//
|
||||
// return: true = changed, false = unchanged
|
||||
assert (flag != 0);
|
||||
|
||||
boost::mutex::scoped_lock sl (mSuppressionMutex);
|
||||
|
||||
bool created;
|
||||
Entry& s = findCreateEntry (index, created);
|
||||
|
||||
if ((s.getFlags () & flag) == flag)
|
||||
return false;
|
||||
|
||||
s.setFlag (flag);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HashRouter::swapSet (uint256 const& index, std::set<uint64>& peers, int flag)
|
||||
{
|
||||
boost::mutex::scoped_lock sl (mSuppressionMutex);
|
||||
|
||||
bool created;
|
||||
Entry& s = findCreateEntry (index, created);
|
||||
|
||||
if ((s.getFlags () & flag) == flag)
|
||||
return false;
|
||||
|
||||
s.swapSet (peers);
|
||||
s.setFlag (flag);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
IHashRouter* IHashRouter::New (int holdTime)
|
||||
{
|
||||
return new HashRouter (holdTime);
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_IFEATURES_H
|
||||
#define RIPPLE_IFEATURES_H
|
||||
|
||||
class FeatureSet
|
||||
{
|
||||
// the status of all features requested in a given window
|
||||
public:
|
||||
uint32 mCloseTime;
|
||||
int mTrustedValidations; // number of trusted validations
|
||||
boost::unordered_map<uint256, int> mVotes; // yes votes by feature
|
||||
|
||||
FeatureSet (uint32 ct, int tv) : mCloseTime (ct), mTrustedValidations (tv)
|
||||
{
|
||||
;
|
||||
}
|
||||
void addVote (uint256 const& feature)
|
||||
{
|
||||
++mVotes[feature];
|
||||
}
|
||||
};
|
||||
|
||||
class FeatureState
|
||||
{
|
||||
public:
|
||||
bool mVetoed; // We don't want this feature enabled
|
||||
bool mEnabled;
|
||||
bool mSupported;
|
||||
bool mDefault; // Include in genesis ledger
|
||||
|
||||
uint32 mFirstMajority; // First time we saw a majority (close time)
|
||||
uint32 mLastMajority; // Most recent time we saw a majority (close time)
|
||||
|
||||
std::string mFriendlyName;
|
||||
|
||||
FeatureState ()
|
||||
: mVetoed (false), mEnabled (false), mSupported (false), mDefault (false),
|
||||
mFirstMajority (0), mLastMajority (0)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
void setVeto ()
|
||||
{
|
||||
mVetoed = true;
|
||||
}
|
||||
void setDefault ()
|
||||
{
|
||||
mDefault = true;
|
||||
}
|
||||
bool isDefault ()
|
||||
{
|
||||
return mDefault;
|
||||
}
|
||||
bool isSupported ()
|
||||
{
|
||||
return mSupported;
|
||||
}
|
||||
bool isVetoed ()
|
||||
{
|
||||
return mVetoed;
|
||||
}
|
||||
bool isEnabled ()
|
||||
{
|
||||
return mEnabled;
|
||||
}
|
||||
const std::string& getFiendlyName ()
|
||||
{
|
||||
return mFriendlyName;
|
||||
}
|
||||
void setFriendlyName (const std::string& n)
|
||||
{
|
||||
mFriendlyName = n;
|
||||
}
|
||||
};
|
||||
|
||||
/** Feature table interface.
|
||||
|
||||
The feature table stores the list of enabled and potential features.
|
||||
Individuals features are voted on by validators during the consensus
|
||||
process.
|
||||
*/
|
||||
class IFeatures
|
||||
{
|
||||
public:
|
||||
static IFeatures* New (uint32 majorityTime, int majorityFraction);
|
||||
|
||||
virtual ~IFeatures () { }
|
||||
|
||||
virtual void addInitialFeatures () = 0;
|
||||
|
||||
virtual FeatureState* addKnownFeature (const char* featureID, const char* friendlyName, bool veto) = 0;
|
||||
virtual uint256 getFeature (const std::string& name) = 0;
|
||||
|
||||
virtual bool vetoFeature (uint256 const& feature) = 0;
|
||||
virtual bool unVetoFeature (uint256 const& feature) = 0;
|
||||
|
||||
virtual bool enableFeature (uint256 const& feature) = 0;
|
||||
virtual bool disableFeature (uint256 const& feature) = 0;
|
||||
|
||||
virtual bool isFeatureEnabled (uint256 const& feature) = 0;
|
||||
virtual bool isFeatureSupported (uint256 const& feature) = 0;
|
||||
|
||||
virtual void setEnabledFeatures (const std::vector<uint256>& features) = 0;
|
||||
virtual void setSupportedFeatures (const std::vector<uint256>& features) = 0;
|
||||
|
||||
// VFALCO NOTE these can't possibly be used since featureList_t was/is private.
|
||||
/*
|
||||
featureList_t getVetoedFeatures() = 0;
|
||||
featureList_t getEnabledFeatures() = 0;
|
||||
featureList_t getFeaturesToEnable(uint32 closeTime) = 0; // gets features we would vote to enable
|
||||
featureList_t getDesiredFeatures() = 0; // features we support, do not veto, are not enabled
|
||||
*/
|
||||
|
||||
virtual void reportValidations (const FeatureSet&) = 0;
|
||||
|
||||
virtual Json::Value getJson (int) = 0;
|
||||
virtual Json::Value getJson (uint256 const& ) = 0;
|
||||
|
||||
virtual void doValidation (Ledger::ref lastClosedLedger, STObject& baseValidation) = 0;
|
||||
virtual void doVoting (Ledger::ref lastClosedLedger, SHAMap::ref initialPosition) = 0;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,44 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_IFEEVOTE_H
|
||||
#define RIPPLE_IFEEVOTE_H
|
||||
|
||||
/** Manager to process fee votes.
|
||||
*/
|
||||
class IFeeVote
|
||||
{
|
||||
public:
|
||||
/** Create a new fee vote manager.
|
||||
|
||||
@param targetBaseFee
|
||||
@param targetReserveBase
|
||||
@param targetReserveIncrement
|
||||
*/
|
||||
static IFeeVote* New (uint64 targetBaseFee,
|
||||
uint32 targetReserveBase,
|
||||
uint32 targetReserveIncrement);
|
||||
|
||||
virtual ~IFeeVote () { }
|
||||
|
||||
/** Add local fee preference to validation.
|
||||
|
||||
@param lastClosedLedger
|
||||
@param baseValidation
|
||||
*/
|
||||
virtual void doValidation (Ledger::ref lastClosedLedger,
|
||||
STObject& baseValidation) = 0;
|
||||
|
||||
/** Cast our local vote on the fee.
|
||||
|
||||
@param lastClosedLedger
|
||||
@param initialPosition
|
||||
*/
|
||||
virtual void doVoting (Ledger::ref lastClosedLedger,
|
||||
SHAMap::ref initialPosition) = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,68 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_HASHROUTER_RIPPLEHEADER
|
||||
#define RIPPLE_HASHROUTER_RIPPLEHEADER
|
||||
|
||||
// VFALCO NOTE Are these the flags?? Why aren't we using a packed struct?
|
||||
// VFALCO TODO convert these macros to int constants
|
||||
#define SF_RELAYED 0x01 // Has already been relayed to other nodes
|
||||
// VFALCO NOTE How can both bad and good be set on a hash?
|
||||
#define SF_BAD 0x02 // Signature/format is bad
|
||||
#define SF_SIGGOOD 0x04 // Signature is good
|
||||
#define SF_SAVED 0x08
|
||||
#define SF_RETRY 0x10 // Transaction can be retried
|
||||
#define SF_TRUSTED 0x20 // comes from trusted source
|
||||
|
||||
/** Routing table for objects identified by hash.
|
||||
|
||||
This table keeps track of which hashes have been received by which peers.
|
||||
It is used to manage the routing and broadcasting of messages in the peer
|
||||
to peer overlay.
|
||||
*/
|
||||
class IHashRouter
|
||||
{
|
||||
public:
|
||||
// VFALCO NOTE this preferred alternative to default parameters makes
|
||||
// behavior clear.
|
||||
//
|
||||
static inline int getDefaultHoldTime ()
|
||||
{
|
||||
return 300;
|
||||
}
|
||||
|
||||
// VFALCO TODO rename the parameter to entryHoldTimeInSeconds
|
||||
static IHashRouter* New (int holdTime);
|
||||
|
||||
virtual ~IHashRouter () { }
|
||||
|
||||
// VFALCO TODO Replace "Supression" terminology with something more semantically meaningful.
|
||||
virtual bool addSuppression (uint256 const& index) = 0;
|
||||
|
||||
virtual bool addSuppressionPeer (uint256 const& index, uint64 peer) = 0;
|
||||
|
||||
virtual bool addSuppressionPeer (uint256 const& index, uint64 peer, int& flags) = 0;
|
||||
|
||||
virtual bool addSuppressionFlags (uint256 const& index, int flag) = 0;
|
||||
|
||||
/** Set the flags on a hash.
|
||||
|
||||
@return `true` if the flags were changed.
|
||||
*/
|
||||
// VFALCO TODO Rename to setFlags since it works with multiple flags.
|
||||
virtual bool setFlag (uint256 const& index, int mask) = 0;
|
||||
|
||||
virtual int getFlags (uint256 const& index) = 0;
|
||||
|
||||
virtual bool swapSet (uint256 const& index, std::set<uint64>& peers, int flag) = 0;
|
||||
|
||||
// VFALCO TODO This appears to be unused!
|
||||
//
|
||||
// virtual Entry getEntry (uint256 const&) = 0;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
@@ -1,276 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_ILOADMANAGER_H
|
||||
#define RIPPLE_ILOADMANAGER_H
|
||||
|
||||
// types of load that can be placed on the server
|
||||
// VFALCO TODO replace LT_ with loadType in constants
|
||||
enum LoadType
|
||||
{
|
||||
// Bad things
|
||||
LT_InvalidRequest, // A request that we can immediately tell is invalid
|
||||
LT_RequestNoReply, // A request that we cannot satisfy
|
||||
LT_InvalidSignature, // An object whose signature we had to check and it failed
|
||||
LT_UnwantedData, // Data we have no use for
|
||||
LT_BadPoW, // Proof of work not valid
|
||||
LT_BadData, // Data we have to verify before rejecting
|
||||
|
||||
// RPC loads
|
||||
LT_RPCInvalid, // An RPC request that we can immediately tell is invalid.
|
||||
LT_RPCReference, // A default "reference" unspecified load
|
||||
LT_RPCException, // An RPC load that causes an exception
|
||||
LT_RPCBurden, // A particularly burdensome RPC load
|
||||
|
||||
// Good things
|
||||
LT_NewTrusted, // A new transaction/validation/proposal we trust
|
||||
LT_NewTransaction, // A new, valid transaction
|
||||
LT_NeededData, // Data we requested
|
||||
|
||||
// Requests
|
||||
LT_RequestData, // A request that is hard to satisfy, disk access
|
||||
LT_CheapQuery, // A query that is trivial, cached data
|
||||
|
||||
LT_MAX // MUST BE LAST
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Tracks the consumption of resources at an endpoint.
|
||||
|
||||
To prevent monopolization of server resources or attacks on servers,
|
||||
resource consumption is monitored at each endpoint. When consumption
|
||||
exceeds certain thresholds, costs are imposed. Costs include charging
|
||||
additional XRP for transactions, requiring a proof of work to be
|
||||
performed, or simply disconnecting the endpoint.
|
||||
|
||||
Currently, consumption endpoints include websocket connections used to
|
||||
service clients, and peer connections used to create the peer to peer
|
||||
overlay network implementing the Ripple protcool.
|
||||
|
||||
The current "balance" of a LoadSource represents resource consumption
|
||||
debt or credit. Debt is accrued when bad loads are imposed. Credit is
|
||||
granted when good loads are imposed. When the balance crosses heuristic
|
||||
thresholds, costs are increased on the endpoint.
|
||||
|
||||
The balance is represented as a unitless relative quantity.
|
||||
|
||||
@note Although RPC connections consume resources, they are transient and
|
||||
cannot be rate limited. It is advised not to expose RPC interfaces
|
||||
to the general public.
|
||||
*/
|
||||
class LoadSource
|
||||
{
|
||||
public:
|
||||
// VFALCO TODO Use these dispositions
|
||||
/*
|
||||
enum Disposition
|
||||
{
|
||||
none,
|
||||
shouldWarn,
|
||||
shouldDrop,
|
||||
};
|
||||
*/
|
||||
|
||||
/** Construct a load source.
|
||||
|
||||
Sources with admin privileges have relaxed or no restrictions
|
||||
on resource consumption.
|
||||
|
||||
@param admin `true` if the source should have admin privileges.
|
||||
*/
|
||||
// VFALCO TODO See who is constructing this with a parameter
|
||||
explicit LoadSource (bool admin)
|
||||
: mBalance (0)
|
||||
, mFlags (admin ? lsfPrivileged : 0)
|
||||
, mLastUpdate (UptimeTimer::getInstance ().getElapsedSeconds ())
|
||||
, mLastWarning (0)
|
||||
, mLogged (false)
|
||||
{
|
||||
}
|
||||
|
||||
/** Construct a load source with a given name.
|
||||
|
||||
The endpoint is considered non-privileged.
|
||||
*/
|
||||
explicit LoadSource (std::string const& name)
|
||||
: mName (name)
|
||||
, mBalance (0)
|
||||
, mFlags (0)
|
||||
, mLastUpdate (UptimeTimer::getInstance ().getElapsedSeconds ())
|
||||
, mLastWarning (0)
|
||||
, mLogged (false)
|
||||
{
|
||||
}
|
||||
|
||||
/** Change the name of the source.
|
||||
|
||||
An endpoint can be created before it's name is known. For example,
|
||||
on an incoming connection before the IP and port have been determined.
|
||||
*/
|
||||
// VFALCO TODO Figure out a way to construct the LoadSource object with
|
||||
// the proper name instead of renaming it later.
|
||||
//
|
||||
void rename (std::string const& name)
|
||||
{
|
||||
mName = name;
|
||||
}
|
||||
|
||||
/** Retrieve the name of this endpoint.
|
||||
*/
|
||||
std::string const& getName () const
|
||||
{
|
||||
return mName;
|
||||
}
|
||||
|
||||
/** Determine if this endpoint is privileged.
|
||||
*/
|
||||
bool isPrivileged () const
|
||||
{
|
||||
return (mFlags & lsfPrivileged) != 0;
|
||||
}
|
||||
|
||||
/** Grant the privileged attribute on this endpoint.
|
||||
*/
|
||||
void setPrivileged ()
|
||||
{
|
||||
mFlags |= lsfPrivileged;
|
||||
}
|
||||
|
||||
/** Retrieve the load debit or credit associated with the endpoint.
|
||||
|
||||
The balance is represented in a unitless relative quantity
|
||||
indicating the heuristically weighted amount of resource consumption.
|
||||
*/
|
||||
int getBalance () const
|
||||
{
|
||||
return mBalance;
|
||||
}
|
||||
|
||||
/** Returns true if the endpoint received a log warning.
|
||||
*/
|
||||
bool isLogged () const
|
||||
{
|
||||
return mLogged;
|
||||
}
|
||||
|
||||
/** Reset the flag indicating the endpoint received a log warning.
|
||||
*/
|
||||
void clearLogged ()
|
||||
{
|
||||
mLogged = false;
|
||||
}
|
||||
|
||||
/** Indicate that this endpoint is an outgoing connection.
|
||||
*/
|
||||
void setOutbound ()
|
||||
{
|
||||
mFlags |= lsfOutbound;
|
||||
}
|
||||
|
||||
/** Returns true if this endpoint is an outgoing connection.
|
||||
*/
|
||||
bool isOutbound () const
|
||||
{
|
||||
return (mFlags & lsfOutbound) != 0;
|
||||
}
|
||||
|
||||
private:
|
||||
// VFALCO Make this not a friend
|
||||
friend class LoadManager;
|
||||
|
||||
static const int lsfPrivileged = 1;
|
||||
static const int lsfOutbound = 2;
|
||||
|
||||
private:
|
||||
std::string mName;
|
||||
int mBalance;
|
||||
int mFlags;
|
||||
int mLastUpdate;
|
||||
int mLastWarning;
|
||||
bool mLogged;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Manages load sources.
|
||||
|
||||
This object creates an associated thread to maintain a clock.
|
||||
|
||||
When the server is overloaded by a particular peer it issues a warning
|
||||
first. This allows friendly peers to reduce their consumption of resources,
|
||||
or disconnect from the server.
|
||||
|
||||
The warning system is used instead of merely dropping, because hostile
|
||||
peers can just reconnect anyway.
|
||||
|
||||
@see LoadSource, LoadType
|
||||
*/
|
||||
class ILoadManager
|
||||
{
|
||||
public:
|
||||
/** Create a new manager.
|
||||
|
||||
The manager thread begins running immediately.
|
||||
|
||||
@note The thresholds for warnings and punishments are in
|
||||
the ctor-initializer
|
||||
*/
|
||||
static ILoadManager* New ();
|
||||
|
||||
/** Destroy the manager.
|
||||
|
||||
The destructor returns only after the thread has stopped.
|
||||
*/
|
||||
virtual ~ILoadManager () { }
|
||||
|
||||
/** Start the associated thread.
|
||||
|
||||
This is here to prevent the deadlock detector from activating during
|
||||
a lengthy program initialization.
|
||||
*/
|
||||
// VFALCO TODO Simplify the two stage initialization to one stage (construction).
|
||||
// NOTE In stand-alone mode the load manager thread isn't started
|
||||
virtual void startThread () = 0;
|
||||
|
||||
/** Turn on deadlock detection.
|
||||
|
||||
The deadlock detector begins in a disabled state. After this function
|
||||
is called, it will report deadlocks using a separate thread whenever
|
||||
the reset function is not called at least once per 10 seconds.
|
||||
|
||||
@see resetDeadlockDetector
|
||||
*/
|
||||
// VFALCO NOTE it seems that the deadlock detector has an "armed" state to prevent it
|
||||
// from going off during program startup if there's a lengthy initialization
|
||||
// operation taking place?
|
||||
//
|
||||
virtual void activateDeadlockDetector () = 0;
|
||||
|
||||
/** Reset the deadlock detection timer.
|
||||
|
||||
A dedicated thread monitors the deadlock timer, and if too much
|
||||
time passes it will produce log warnings.
|
||||
*/
|
||||
virtual void resetDeadlockDetector () = 0;
|
||||
|
||||
/** Update an endpoint to reflect an imposed load.
|
||||
|
||||
The balance of the endpoint is adjusted based on the heuristic cost
|
||||
of the indicated load.
|
||||
|
||||
@return `true` if the endpoint should be warned or punished.
|
||||
*/
|
||||
virtual bool applyLoadCharge (LoadSource& sourceToAdjust, LoadType loadToImpose) const = 0;
|
||||
|
||||
// VFALCO TODO Eliminate these two functions and just make applyLoadCharge()
|
||||
// return a LoadSource::Disposition
|
||||
//
|
||||
virtual bool shouldWarn (LoadSource&) const = 0;
|
||||
virtual bool shouldCutoff (LoadSource&) const = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,80 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_IPEERS_H_INCLUDED
|
||||
#define RIPPLE_IPEERS_H_INCLUDED
|
||||
|
||||
/** Manages the set of connected peers.
|
||||
*/
|
||||
class IPeers
|
||||
{
|
||||
public:
|
||||
static IPeers* New (boost::asio::io_service& io_service);
|
||||
|
||||
virtual ~IPeers () { }
|
||||
|
||||
// Begin enforcing connection policy.
|
||||
virtual void start () = 0;
|
||||
|
||||
// Send message to network.
|
||||
virtual int relayMessage (Peer* fromPeer, const PackedMessage::pointer& msg) = 0;
|
||||
virtual int relayMessageCluster (Peer* fromPeer, const PackedMessage::pointer& msg) = 0;
|
||||
virtual void relayMessageTo (const std::set<uint64>& fromPeers, const PackedMessage::pointer& msg) = 0;
|
||||
virtual void relayMessageBut (const std::set<uint64>& fromPeers, const PackedMessage::pointer& msg) = 0;
|
||||
|
||||
// Manual connection request.
|
||||
// Queue for immediate scanning.
|
||||
virtual void connectTo (const std::string& strIp, int iPort) = 0;
|
||||
|
||||
//
|
||||
// Peer connectivity notification.
|
||||
//
|
||||
virtual bool getTopNAddrs (int n, std::vector<std::string>& addrs) = 0;
|
||||
virtual bool savePeer (const std::string& strIp, int iPort, char code) = 0;
|
||||
|
||||
// We know peers node public key.
|
||||
// <-- bool: false=reject
|
||||
virtual bool peerConnected (Peer::ref peer, const RippleAddress& naPeer, const std::string& strIP, int iPort) = 0;
|
||||
|
||||
// No longer connected.
|
||||
virtual void peerDisconnected (Peer::ref peer, const RippleAddress& naPeer) = 0;
|
||||
|
||||
// As client accepted.
|
||||
virtual void peerVerified (Peer::ref peer) = 0;
|
||||
|
||||
// As client failed connect and be accepted.
|
||||
virtual void peerClosed (Peer::ref peer, const std::string& strIp, int iPort) = 0;
|
||||
|
||||
virtual int getPeerCount () = 0;
|
||||
virtual Json::Value getPeersJson () = 0;
|
||||
virtual std::vector<Peer::pointer> getPeerVector () = 0;
|
||||
|
||||
// Peer 64-bit ID function
|
||||
virtual uint64 assignPeerId () = 0;
|
||||
virtual Peer::pointer getPeerById (const uint64& id) = 0;
|
||||
virtual bool hasPeer (const uint64& id) = 0;
|
||||
|
||||
//
|
||||
// Scanning
|
||||
//
|
||||
|
||||
virtual void scanRefresh () = 0;
|
||||
|
||||
//
|
||||
// Connection policy
|
||||
//
|
||||
virtual void policyLowWater () = 0;
|
||||
virtual void policyEnforce () = 0; // VFALCO This and others can be made private
|
||||
|
||||
// configured connections
|
||||
virtual void makeConfigured () = 0;
|
||||
};
|
||||
|
||||
// VFALCO TODO Put this in some group of utilities
|
||||
extern void splitIpPort (const std::string& strIpPort, std::string& strIp, int& iPort);
|
||||
|
||||
#endif
|
||||
// vim:ts=4
|
||||
@@ -1,60 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_IPROOFOFWORKFACTORY_H
|
||||
#define RIPPLE_IPROOFOFWORKFACTORY_H
|
||||
|
||||
enum POWResult
|
||||
{
|
||||
powOK = 0,
|
||||
powREUSED = 1, // already submitted
|
||||
powBADNONCE = 2, // you didn't solve it
|
||||
powEXPIRED = 3, // time is up
|
||||
powCORRUPT = 4,
|
||||
powTOOEASY = 5, // the difficulty increased too much while you solved it
|
||||
};
|
||||
|
||||
// VFALCO TODO move this to the class as a static member and rename it
|
||||
bool powResultInfo (POWResult powCode, std::string& strToken, std::string& strHuman);
|
||||
|
||||
class IProofOfWorkFactory : LeakChecked <IProofOfWorkFactory>
|
||||
{
|
||||
public:
|
||||
typedef boost::bimap< boost::bimaps::multiset_of<time_t>, boost::bimaps::unordered_set_of<uint256> > powMap_t;
|
||||
typedef powMap_t::value_type powMap_vt;
|
||||
|
||||
public:
|
||||
static IProofOfWorkFactory* New ();
|
||||
|
||||
virtual ~IProofOfWorkFactory () { }
|
||||
|
||||
// VFALCO TODO which members can be const?
|
||||
|
||||
virtual ProofOfWork getProof () = 0;
|
||||
|
||||
virtual POWResult checkProof (const std::string& token, uint256 const& solution) = 0;
|
||||
|
||||
virtual uint64 getDifficulty () = 0;
|
||||
|
||||
virtual void setDifficulty (int i) = 0;
|
||||
|
||||
virtual void loadHigh () = 0;
|
||||
|
||||
virtual void loadLow () = 0;
|
||||
|
||||
virtual void sweep () = 0;
|
||||
|
||||
virtual uint256 const& getSecret () const = 0;
|
||||
|
||||
virtual void setSecret (uint256 const& secret) = 0;
|
||||
|
||||
public:
|
||||
static int getPowEntry (uint256 const& target, int iterations);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
// vim:ts=4
|
||||
@@ -1,48 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_IVALIDATIONS_H
|
||||
#define RIPPLE_IVALIDATIONS_H
|
||||
|
||||
// VFALCO TODO rename and move these typedefs into the IValidations interface
|
||||
typedef boost::unordered_map<uint160, SerializedValidation::pointer> ValidationSet;
|
||||
typedef std::pair<int, uint160> currentValidationCount; // nodes validating and highest node ID validating
|
||||
|
||||
class IValidations : LeakChecked <IValidations>
|
||||
{
|
||||
public:
|
||||
static IValidations* New ();
|
||||
|
||||
virtual ~IValidations () { }
|
||||
|
||||
virtual bool addValidation (SerializedValidation::ref, const std::string& source) = 0;
|
||||
|
||||
virtual ValidationSet getValidations (uint256 const& ledger) = 0;
|
||||
|
||||
virtual void getValidationCount (uint256 const& ledger, bool currentOnly, int& trusted, int& untrusted) = 0;
|
||||
virtual void getValidationTypes (uint256 const& ledger, int& full, int& partial) = 0;
|
||||
|
||||
virtual int getTrustedValidationCount (uint256 const& ledger) = 0;
|
||||
|
||||
virtual int getFeeAverage(uint256 const& ledger, uint64 ref, uint64& fee) = 0;
|
||||
|
||||
virtual int getNodesAfter (uint256 const& ledger) = 0;
|
||||
virtual int getLoadRatio (bool overLoaded) = 0;
|
||||
|
||||
// VFALCO TODO make a typedef for this ugly return value!
|
||||
virtual boost::unordered_map<uint256, currentValidationCount> getCurrentValidations (
|
||||
uint256 currentLedger, uint256 previousLedger) = 0;
|
||||
|
||||
virtual std::list <SerializedValidation::pointer> getCurrentTrustedValidations () = 0;
|
||||
|
||||
virtual void tune (int size, int age) = 0;
|
||||
|
||||
virtual void flush () = 0;
|
||||
|
||||
virtual void sweep () = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,73 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
// This is the primary interface into the "client" portion of the program.
|
||||
// Code that wants to do normal operations on the network such as
|
||||
// creating and monitoring accounts, creating transactions, and so on
|
||||
// should use this interface. The RPC code will primarily be a light wrapper
|
||||
// over this code.
|
||||
|
||||
// Eventually, it will check the node's operating mode (synched, unsynched,
|
||||
// etectera) and defer to the correct means of processing. The current
|
||||
// code assumes this node is synched (and will continue to do so until
|
||||
// there's a functional network.
|
||||
|
||||
// VFALCO TODO Figure out how to clean up these globals
|
||||
uint64 InfoSub::sSeq = 0;
|
||||
boost::mutex InfoSub::sSeqLock;
|
||||
|
||||
InfoSub::InfoSub ()
|
||||
{
|
||||
boost::mutex::scoped_lock sl (sSeqLock);
|
||||
mSeq = ++sSeq;
|
||||
}
|
||||
|
||||
InfoSub::~InfoSub ()
|
||||
{
|
||||
NetworkOPs& ops = getApp().getOPs ();
|
||||
ops.unsubTransactions (mSeq);
|
||||
ops.unsubRTTransactions (mSeq);
|
||||
ops.unsubLedger (mSeq);
|
||||
ops.unsubServer (mSeq);
|
||||
ops.unsubAccount (mSeq, mSubAccountInfo, true);
|
||||
ops.unsubAccount (mSeq, mSubAccountInfo, false);
|
||||
}
|
||||
|
||||
void InfoSub::send (const Json::Value& jvObj, const std::string& sObj, bool broadcast)
|
||||
{
|
||||
send (jvObj, broadcast);
|
||||
}
|
||||
|
||||
uint64 InfoSub::getSeq ()
|
||||
{
|
||||
return mSeq;
|
||||
}
|
||||
|
||||
void InfoSub::onSendEmpty ()
|
||||
{
|
||||
}
|
||||
|
||||
void InfoSub::insertSubAccountInfo (RippleAddress addr, uint32 uLedgerIndex)
|
||||
{
|
||||
boost::mutex::scoped_lock sl (mLockInfo);
|
||||
|
||||
mSubAccountInfo.insert (addr);
|
||||
}
|
||||
|
||||
void InfoSub::clearPathRequest ()
|
||||
{
|
||||
mPathRequest.reset ();
|
||||
}
|
||||
|
||||
void InfoSub::setPathRequest (const boost::shared_ptr<PathRequest>& req)
|
||||
{
|
||||
mPathRequest = req;
|
||||
}
|
||||
|
||||
const boost::shared_ptr<PathRequest>& InfoSub::getPathRequest ()
|
||||
{
|
||||
return mPathRequest;
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_INFOSUB_H
|
||||
#define RIPPLE_INFOSUB_H
|
||||
|
||||
// Operations that clients may wish to perform against the network
|
||||
// Master operational handler, server sequencer, network tracker
|
||||
|
||||
class PathRequest;
|
||||
|
||||
class InfoSub
|
||||
: public CountedObject <InfoSub>
|
||||
{
|
||||
public:
|
||||
static char const* getCountedObjectName () { return "InfoSub"; }
|
||||
|
||||
typedef boost::shared_ptr<InfoSub> pointer;
|
||||
|
||||
// VFALCO TODO Standardize on the names of weak / strong pointer typedefs.
|
||||
typedef boost::weak_ptr<InfoSub> wptr;
|
||||
|
||||
typedef const boost::shared_ptr<InfoSub>& ref;
|
||||
|
||||
public:
|
||||
InfoSub ();
|
||||
|
||||
virtual ~InfoSub ();
|
||||
|
||||
virtual void send (const Json::Value & jvObj, bool broadcast) = 0;
|
||||
|
||||
// VFALCO NOTE Why is this virtual?
|
||||
virtual void send (const Json::Value & jvObj, const std::string & sObj, bool broadcast);
|
||||
|
||||
uint64 getSeq ();
|
||||
|
||||
void onSendEmpty ();
|
||||
|
||||
void insertSubAccountInfo (RippleAddress addr, uint32 uLedgerIndex);
|
||||
|
||||
void clearPathRequest ();
|
||||
|
||||
void setPathRequest (const boost::shared_ptr<PathRequest>& req);
|
||||
|
||||
boost::shared_ptr <PathRequest> const& getPathRequest ();
|
||||
|
||||
protected:
|
||||
// VFALCO TODO make accessor for this member
|
||||
boost::mutex mLockInfo;
|
||||
|
||||
private:
|
||||
// VFALCO TODO Move these globals to class instance
|
||||
static uint64 sSeq;
|
||||
static boost::mutex sSeqLock;
|
||||
|
||||
boost::unordered_set <RippleAddress> mSubAccountInfo;
|
||||
boost::unordered_set <RippleAddress> mSubAccountTransaction;
|
||||
boost::shared_ptr <PathRequest> mPathRequest;
|
||||
|
||||
uint64 mSeq;
|
||||
};
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,160 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_LEDGERCONSENSUS_H
|
||||
#define RIPPLE_LEDGERCONSENSUS_H
|
||||
|
||||
/** Manager for achieving consensus on the next ledger.
|
||||
|
||||
This object is created when the consensus process starts, and
|
||||
is destroyed when the process is complete.
|
||||
*/
|
||||
class LedgerConsensus
|
||||
: public boost::enable_shared_from_this <LedgerConsensus>
|
||||
, public CountedObject <LedgerConsensus>
|
||||
{
|
||||
public:
|
||||
static char const* getCountedObjectName () { return "LedgerConsensus"; }
|
||||
|
||||
LedgerConsensus (LedgerHash const & prevLCLHash, Ledger::ref previousLedger, uint32 closeTime);
|
||||
|
||||
int startup ();
|
||||
|
||||
Json::Value getJson (bool full);
|
||||
|
||||
Ledger::ref peekPreviousLedger ()
|
||||
{
|
||||
return mPreviousLedger;
|
||||
}
|
||||
|
||||
uint256 getLCL ()
|
||||
{
|
||||
return mPrevLedgerHash;
|
||||
}
|
||||
|
||||
SHAMap::pointer getTransactionTree (uint256 const & hash, bool doAcquire);
|
||||
|
||||
TransactionAcquire::pointer getAcquiring (uint256 const & hash);
|
||||
|
||||
void mapComplete (uint256 const & hash, SHAMap::ref map, bool acquired);
|
||||
|
||||
bool stillNeedTXSet (uint256 const & hash);
|
||||
|
||||
void checkLCL ();
|
||||
|
||||
void handleLCL (uint256 const & lclHash);
|
||||
|
||||
void timerEntry ();
|
||||
|
||||
// state handlers
|
||||
void statePreClose ();
|
||||
void stateEstablish ();
|
||||
void stateCutoff ();
|
||||
void stateFinished ();
|
||||
void stateAccepted ();
|
||||
|
||||
bool haveConsensus (bool forReal);
|
||||
|
||||
bool peerPosition (LedgerProposal::ref);
|
||||
|
||||
bool peerHasSet (Peer::ref peer, uint256 const & set, protocol::TxSetStatus status);
|
||||
|
||||
SHAMapAddNode peerGaveNodes (Peer::ref peer, uint256 const & setHash,
|
||||
const std::list<SHAMapNode>& nodeIDs, const std::list< Blob >& nodeData);
|
||||
|
||||
bool isOurPubKey (const RippleAddress & k)
|
||||
{
|
||||
return k == mValPublic;
|
||||
}
|
||||
|
||||
// test/debug
|
||||
void simulate ();
|
||||
|
||||
private:
|
||||
// final accept logic
|
||||
void accept (SHAMap::ref txSet, LoadEvent::pointer);
|
||||
|
||||
void weHave (uint256 const & id, Peer::ref avoidPeer);
|
||||
void startAcquiring (TransactionAcquire::pointer);
|
||||
SHAMap::pointer find (uint256 const & hash);
|
||||
|
||||
void createDisputes (SHAMap::ref, SHAMap::ref);
|
||||
void addDisputedTransaction (uint256 const& , Blob const & transaction);
|
||||
void adjustCount (SHAMap::ref map, const std::vector<uint160>& peers);
|
||||
void propose ();
|
||||
|
||||
void addPosition (LedgerProposal&, bool ours);
|
||||
void removePosition (LedgerProposal&, bool ours);
|
||||
void sendHaveTxSet (uint256 const & set, bool direct);
|
||||
void applyTransactions (SHAMap::ref transactionSet, Ledger::ref targetLedger,
|
||||
Ledger::ref checkLedger, CanonicalTXSet & failedTransactions, bool openLgr);
|
||||
int applyTransaction (TransactionEngine & engine, SerializedTransaction::ref txn, Ledger::ref targetLedger,
|
||||
bool openLgr, bool retryAssured);
|
||||
|
||||
uint32 roundCloseTime (uint32 closeTime);
|
||||
|
||||
// manipulating our own position
|
||||
void statusChange (protocol::NodeEvent, Ledger & ledger);
|
||||
void takeInitialPosition (Ledger & initialLedger);
|
||||
void updateOurPositions ();
|
||||
void playbackProposals ();
|
||||
int getThreshold ();
|
||||
void closeLedger ();
|
||||
void checkOurValidation ();
|
||||
|
||||
void beginAccept (bool synchronous);
|
||||
void endConsensus ();
|
||||
|
||||
void addLoad (SerializedValidation::ref val);
|
||||
|
||||
private:
|
||||
// VFALCO TODO Rename these to look pretty
|
||||
enum LCState
|
||||
{
|
||||
lcsPRE_CLOSE, // We haven't closed our ledger yet, but others might have
|
||||
lcsESTABLISH, // Establishing consensus
|
||||
lcsFINISHED, // We have closed on a transaction set
|
||||
lcsACCEPTED, // We have accepted/validated a new last closed ledger
|
||||
};
|
||||
|
||||
LCState mState;
|
||||
uint32 mCloseTime; // The wall time this ledger closed
|
||||
uint256 mPrevLedgerHash, mNewLedgerHash;
|
||||
Ledger::pointer mPreviousLedger;
|
||||
InboundLedger::pointer mAcquiringLedger;
|
||||
LedgerProposal::pointer mOurPosition;
|
||||
RippleAddress mValPublic, mValPrivate;
|
||||
bool mProposing, mValidating, mHaveCorrectLCL, mConsensusFail;
|
||||
|
||||
int mCurrentMSeconds, mClosePercent, mCloseResolution;
|
||||
bool mHaveCloseTimeConsensus;
|
||||
|
||||
boost::posix_time::ptime mConsensusStartTime;
|
||||
int mPreviousProposers;
|
||||
int mPreviousMSeconds;
|
||||
|
||||
// Convergence tracking, trusted peers indexed by hash of public key
|
||||
boost::unordered_map<uint160, LedgerProposal::pointer> mPeerPositions;
|
||||
|
||||
// Transaction Sets, indexed by hash of transaction tree
|
||||
boost::unordered_map<uint256, SHAMap::pointer> mAcquired;
|
||||
boost::unordered_map<uint256, TransactionAcquire::pointer> mAcquiring;
|
||||
|
||||
// Peer sets
|
||||
boost::unordered_map<uint256, std::vector< boost::weak_ptr<Peer> > > mPeerData;
|
||||
|
||||
// Disputed transactions
|
||||
boost::unordered_map<uint256, DisputedTx::pointer> mDisputes;
|
||||
|
||||
// Close time estimates
|
||||
std::map<uint32, int> mCloseTimes;
|
||||
|
||||
// nodes that have bowed out of this consensus process
|
||||
boost::unordered_set<uint160> mDeadNodes;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
@@ -1,426 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
SETUP_LOG (LoadManager)
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class LoadManager
|
||||
: public ILoadManager
|
||||
, public beast::InterruptibleThread::EntryPoint
|
||||
{
|
||||
private:
|
||||
/* Entry mapping utilization to cost.
|
||||
|
||||
The cost is expressed as a unitless relative quantity. These
|
||||
mappings are statically loaded at startup with heuristic values.
|
||||
*/
|
||||
class Cost
|
||||
{
|
||||
public:
|
||||
// VFALCO TODO Eliminate this default ctor somehow
|
||||
Cost ()
|
||||
: m_loadType ()
|
||||
, m_cost (0)
|
||||
, m_resourceFlags (0)
|
||||
{
|
||||
}
|
||||
|
||||
Cost (LoadType loadType, int cost, int resourceFlags)
|
||||
: m_loadType (loadType)
|
||||
, m_cost (cost)
|
||||
, m_resourceFlags (resourceFlags)
|
||||
{
|
||||
}
|
||||
|
||||
LoadType getLoadType () const
|
||||
{
|
||||
return m_loadType;
|
||||
}
|
||||
|
||||
int getCost () const
|
||||
{
|
||||
return m_cost;
|
||||
}
|
||||
|
||||
int getResourceFlags () const
|
||||
{
|
||||
return m_resourceFlags;
|
||||
}
|
||||
|
||||
public:
|
||||
// VFALCO TODO Make these private and const
|
||||
LoadType m_loadType;
|
||||
int m_cost;
|
||||
int m_resourceFlags;
|
||||
};
|
||||
|
||||
public:
|
||||
LoadManager ()
|
||||
: m_thread ("loadmgr")
|
||||
, m_logThread ("loadmgr_log")
|
||||
, mCreditRate (100)
|
||||
, mCreditLimit (500)
|
||||
, mDebitWarn (-500)
|
||||
, mDebitLimit (-1000)
|
||||
, mArmed (false)
|
||||
, mDeadLock (0)
|
||||
, mCosts (LT_MAX)
|
||||
{
|
||||
m_logThread.start ();
|
||||
|
||||
/** Flags indicating the type of load.
|
||||
|
||||
Utilization may include any combination of:
|
||||
|
||||
- CPU
|
||||
- Storage space
|
||||
- Network transfer
|
||||
*/
|
||||
// VFALCO NOTE These flags are not used...
|
||||
enum
|
||||
{
|
||||
flagDisk = 1,
|
||||
flagCpu = 2,
|
||||
flagNet = 4
|
||||
};
|
||||
|
||||
// VFALCO TODO Replace this with a function that uses a simple switch statement...
|
||||
//
|
||||
addCost (Cost (LT_InvalidRequest, -10, flagCpu | flagNet));
|
||||
addCost (Cost (LT_RequestNoReply, -1, flagCpu | flagDisk));
|
||||
addCost (Cost (LT_InvalidSignature, -100, flagCpu));
|
||||
addCost (Cost (LT_UnwantedData, -5, flagCpu | flagNet));
|
||||
addCost (Cost (LT_BadData, -20, flagCpu));
|
||||
|
||||
addCost (Cost (LT_RPCInvalid, -10, flagCpu | flagNet));
|
||||
addCost (Cost (LT_RPCReference, -10, flagCpu | flagNet));
|
||||
addCost (Cost (LT_RPCException, -20, flagCpu | flagNet));
|
||||
addCost (Cost (LT_RPCBurden, -50, flagCpu | flagNet));
|
||||
|
||||
// VFALCO NOTE Why do these supposedly "good" load types still have a negative cost?
|
||||
//
|
||||
addCost (Cost (LT_NewTrusted, -10, 0));
|
||||
addCost (Cost (LT_NewTransaction, -2, 0));
|
||||
addCost (Cost (LT_NeededData, -10, 0));
|
||||
|
||||
addCost (Cost (LT_RequestData, -5, flagDisk | flagNet));
|
||||
addCost (Cost (LT_CheapQuery, -1, flagCpu));
|
||||
|
||||
UptimeTimer::getInstance ().beginManualUpdates ();
|
||||
}
|
||||
|
||||
private:
|
||||
~LoadManager ()
|
||||
{
|
||||
UptimeTimer::getInstance ().endManualUpdates ();
|
||||
|
||||
m_thread.interrupt ();
|
||||
}
|
||||
|
||||
void startThread ()
|
||||
{
|
||||
m_thread.start (this);
|
||||
}
|
||||
|
||||
void canonicalize (LoadSource& source, int now) const
|
||||
{
|
||||
if (source.mLastUpdate != now)
|
||||
{
|
||||
if (source.mLastUpdate < now)
|
||||
{
|
||||
source.mBalance += mCreditRate * (now - source.mLastUpdate);
|
||||
|
||||
if (source.mBalance > mCreditLimit)
|
||||
{
|
||||
source.mBalance = mCreditLimit;
|
||||
source.mLogged = false;
|
||||
}
|
||||
}
|
||||
|
||||
source.mLastUpdate = now;
|
||||
}
|
||||
}
|
||||
|
||||
bool shouldWarn (LoadSource& source) const
|
||||
{
|
||||
{
|
||||
boost::mutex::scoped_lock sl (mLock);
|
||||
|
||||
int now = UptimeTimer::getInstance ().getElapsedSeconds ();
|
||||
canonicalize (source, now);
|
||||
|
||||
if (source.isPrivileged () || (source.mBalance > mDebitWarn) || (source.mLastWarning == now))
|
||||
return false;
|
||||
|
||||
source.mLastWarning = now;
|
||||
}
|
||||
logWarning (source.getName ());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool shouldCutoff (LoadSource& source) const
|
||||
{
|
||||
{
|
||||
boost::mutex::scoped_lock sl (mLock);
|
||||
int now = UptimeTimer::getInstance ().getElapsedSeconds ();
|
||||
canonicalize (source, now);
|
||||
|
||||
if (source.isPrivileged () || (source.mBalance > mDebitLimit))
|
||||
return false;
|
||||
|
||||
if (source.mLogged)
|
||||
return true;
|
||||
|
||||
source.mLogged = true;
|
||||
}
|
||||
logDisconnect (source.getName ());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool applyLoadCharge (LoadSource& source, LoadType loadType) const
|
||||
{
|
||||
// FIXME: Scale by category
|
||||
Cost cost = mCosts[static_cast<int> (loadType)];
|
||||
|
||||
return adjust (source, cost.m_cost);
|
||||
}
|
||||
|
||||
bool adjust (LoadSource& source, int credits) const
|
||||
{
|
||||
// return: true = need to warn/cutoff
|
||||
|
||||
// We do it this way in case we want to add exponential decay later
|
||||
int now = UptimeTimer::getInstance ().getElapsedSeconds ();
|
||||
|
||||
boost::mutex::scoped_lock sl (mLock);
|
||||
canonicalize (source, now);
|
||||
source.mBalance += credits;
|
||||
|
||||
if (source.mBalance > mCreditLimit)
|
||||
source.mBalance = mCreditLimit;
|
||||
|
||||
if (source.isPrivileged ()) // privileged sources never warn/cutoff
|
||||
return false;
|
||||
|
||||
if ( (source.mBalance >= mDebitWarn) ||
|
||||
((source.mBalance >= mDebitLimit) && (source.mLastWarning == now)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void logWarning (const std::string& source) const
|
||||
{
|
||||
if (source.empty ())
|
||||
WriteLog (lsDEBUG, LoadManager) << "Load warning from empty source";
|
||||
else
|
||||
WriteLog (lsINFO, LoadManager) << "Load warning: " << source;
|
||||
}
|
||||
|
||||
void logDisconnect (const std::string& source) const
|
||||
{
|
||||
if (source.empty ())
|
||||
WriteLog (lsINFO, LoadManager) << "Disconnect for empty source";
|
||||
else
|
||||
WriteLog (lsWARNING, LoadManager) << "Disconnect for: " << source;
|
||||
}
|
||||
|
||||
// VFALCO TODO Implement this and stop accessing the vector directly
|
||||
//Cost const& getCost (LoadType loadType) const;
|
||||
int getCost (LoadType t) const
|
||||
{
|
||||
return mCosts [static_cast <int> (t)].getCost ();
|
||||
}
|
||||
|
||||
void resetDeadlockDetector ()
|
||||
{
|
||||
boost::mutex::scoped_lock sl (mLock);
|
||||
mDeadLock = UptimeTimer::getInstance ().getElapsedSeconds ();
|
||||
}
|
||||
|
||||
void activateDeadlockDetector ()
|
||||
{
|
||||
mArmed = true;
|
||||
}
|
||||
|
||||
static void logDeadlock (int dlTime)
|
||||
{
|
||||
WriteLog (lsWARNING, LoadManager) << "Server stalled for " << dlTime << " seconds.";
|
||||
|
||||
char const* fileName = getApp ().getMasterLock ().getFileName ();
|
||||
int lineNumber = getApp ().getMasterLock ().getLineNumber ();
|
||||
|
||||
WriteLog (lsWARNING, LoadManager) << "Master lock owned by " << File (fileName).getFileName ().toStdString () << ", line " << lineNumber;
|
||||
}
|
||||
|
||||
private:
|
||||
// VFALCO TODO These used to be public but are apparently not used. Find out why.
|
||||
/*
|
||||
int getCreditRate () const
|
||||
{
|
||||
boost::mutex::scoped_lock sl (mLock);
|
||||
return mCreditRate;
|
||||
}
|
||||
|
||||
int getCreditLimit () const
|
||||
{
|
||||
boost::mutex::scoped_lock sl (mLock);
|
||||
return mCreditLimit;
|
||||
}
|
||||
|
||||
int getDebitWarn () const
|
||||
{
|
||||
boost::mutex::scoped_lock sl (mLock);
|
||||
return mDebitWarn;
|
||||
}
|
||||
|
||||
int getDebitLimit () const
|
||||
{
|
||||
boost::mutex::scoped_lock sl (mLock);
|
||||
return mDebitLimit;
|
||||
}
|
||||
|
||||
void setCreditRate (int r)
|
||||
{
|
||||
boost::mutex::scoped_lock sl (mLock);
|
||||
mCreditRate = r;
|
||||
}
|
||||
|
||||
void setCreditLimit (int r)
|
||||
{
|
||||
boost::mutex::scoped_lock sl (mLock);
|
||||
mCreditLimit = r;
|
||||
}
|
||||
|
||||
void setDebitWarn (int r)
|
||||
{
|
||||
boost::mutex::scoped_lock sl (mLock);
|
||||
mDebitWarn = r;
|
||||
}
|
||||
|
||||
void setDebitLimit (int r)
|
||||
{
|
||||
boost::mutex::scoped_lock sl (mLock);
|
||||
mDebitLimit = r;
|
||||
}
|
||||
*/
|
||||
|
||||
private:
|
||||
void addCost (const Cost& c)
|
||||
{
|
||||
mCosts [static_cast <int> (c.getLoadType ())] = c;
|
||||
}
|
||||
|
||||
// VFALCO NOTE Where's the thread object? It's not a data member...
|
||||
//
|
||||
void threadRun ()
|
||||
{
|
||||
// VFALCO TODO replace this with a beast Time object?
|
||||
//
|
||||
// Initialize the clock to the current time.
|
||||
boost::posix_time::ptime t = boost::posix_time::microsec_clock::universal_time ();
|
||||
|
||||
while (! m_thread.interruptionPoint ())
|
||||
{
|
||||
{
|
||||
// VFALCO NOTE What is this lock protecting?
|
||||
boost::mutex::scoped_lock sl (mLock);
|
||||
|
||||
// VFALCO NOTE I think this is to reduce calls to the operating system
|
||||
// for retrieving the current time.
|
||||
//
|
||||
// TODO Instead of incrementing can't we just retrieve the current
|
||||
// time again?
|
||||
//
|
||||
// Manually update the timer.
|
||||
UptimeTimer::getInstance ().incrementElapsedTime ();
|
||||
|
||||
// Measure the amount of time we have been deadlocked, in seconds.
|
||||
//
|
||||
// VFALCO NOTE mDeadLock is a canary for detecting the condition.
|
||||
int const timeSpentDeadlocked = UptimeTimer::getInstance ().getElapsedSeconds () - mDeadLock;
|
||||
|
||||
// VFALCO NOTE I think that "armed" refers to the deadlock detector
|
||||
//
|
||||
if (mArmed && (timeSpentDeadlocked >= 10))
|
||||
{
|
||||
// Report the deadlocked condition every 10 seconds
|
||||
if ((timeSpentDeadlocked % 10) == 0)
|
||||
{
|
||||
// VFALCO TODO Replace this with a dedicated thread with call queue.
|
||||
//
|
||||
m_logThread.call (&logDeadlock, timeSpentDeadlocked);
|
||||
}
|
||||
|
||||
// If we go over 500 seconds spent deadlocked, it means that the
|
||||
// deadlock resolution code has failed, which qualifies as undefined
|
||||
// behavior.
|
||||
//
|
||||
assert (timeSpentDeadlocked < 500);
|
||||
}
|
||||
}
|
||||
|
||||
bool change;
|
||||
|
||||
// VFALCO TODO Eliminate the dependence on the Application object.
|
||||
// Choices include constructing with the job queue / feetracker.
|
||||
// Another option is using an observer pattern to invert the dependency.
|
||||
if (getApp().getJobQueue ().isOverloaded ())
|
||||
{
|
||||
WriteLog (lsINFO, LoadManager) << getApp().getJobQueue ().getJson (0);
|
||||
change = getApp().getFeeTrack ().raiseLocalFee ();
|
||||
}
|
||||
else
|
||||
{
|
||||
change = getApp().getFeeTrack ().lowerLocalFee ();
|
||||
}
|
||||
|
||||
if (change)
|
||||
{
|
||||
// VFALCO TODO replace this with a Listener / observer and subscribe in NetworkOPs or Application
|
||||
getApp().getOPs ().reportFeeChange ();
|
||||
}
|
||||
|
||||
t += boost::posix_time::seconds (1);
|
||||
boost::posix_time::time_duration when = t - boost::posix_time::microsec_clock::universal_time ();
|
||||
|
||||
if ((when.is_negative ()) || (when.total_seconds () > 1))
|
||||
{
|
||||
WriteLog (lsWARNING, LoadManager) << "time jump";
|
||||
t = boost::posix_time::microsec_clock::universal_time ();
|
||||
}
|
||||
else
|
||||
{
|
||||
boost::this_thread::sleep (when);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
beast::InterruptibleThread m_thread;
|
||||
beast::ThreadWithCallQueue m_logThread;
|
||||
|
||||
int mCreditRate; // credits gained/lost per second
|
||||
int mCreditLimit; // the most credits a source can have
|
||||
int mDebitWarn; // when a source drops below this, we warn
|
||||
int mDebitLimit; // when a source drops below this, we cut it off (should be negative)
|
||||
|
||||
bool mArmed;
|
||||
|
||||
int mDeadLock; // Detect server deadlocks
|
||||
|
||||
mutable boost::mutex mLock; // VFALCO TODO Replace with juce::Mutex and remove the mutable attribute
|
||||
|
||||
std::vector <Cost> mCosts;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
ILoadManager* ILoadManager::New ()
|
||||
{
|
||||
return new LoadManager;
|
||||
}
|
||||
@@ -1,170 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
LocalCredentials::LocalCredentials () : mDh512 (NULL), mDh1024 (NULL), mLedger (0)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
void LocalCredentials::start ()
|
||||
{
|
||||
// We need our node identity before we begin networking.
|
||||
// - Allows others to identify if they have connected multiple times.
|
||||
// - Determines our CAS routing and responsibilities.
|
||||
// - This is not our validation identity.
|
||||
if (!nodeIdentityLoad ())
|
||||
{
|
||||
nodeIdentityCreate ();
|
||||
|
||||
if (!nodeIdentityLoad ())
|
||||
throw std::runtime_error ("unable to retrieve new node identity.");
|
||||
}
|
||||
|
||||
if (!theConfig.QUIET)
|
||||
Log::out() << "NodeIdentity: " << mNodePublicKey.humanNodePublic ();
|
||||
|
||||
getApp().getUNL ().start ();
|
||||
}
|
||||
|
||||
// Retrieve network identity.
|
||||
bool LocalCredentials::nodeIdentityLoad ()
|
||||
{
|
||||
|
||||
Database* db = getApp().getWalletDB ()->getDB ();
|
||||
ScopedLock sl (getApp().getWalletDB ()->getDBLock ());
|
||||
bool bSuccess = false;
|
||||
|
||||
if (db->executeSQL ("SELECT * FROM NodeIdentity;") && db->startIterRows ())
|
||||
{
|
||||
std::string strPublicKey, strPrivateKey;
|
||||
|
||||
db->getStr ("PublicKey", strPublicKey);
|
||||
db->getStr ("PrivateKey", strPrivateKey);
|
||||
|
||||
mNodePublicKey.setNodePublic (strPublicKey);
|
||||
mNodePrivateKey.setNodePrivate (strPrivateKey);
|
||||
|
||||
mDh512 = DH_der_load (db->getStrBinary ("Dh512"));
|
||||
mDh1024 = DH_der_load (db->getStrBinary ("Dh1024"));
|
||||
|
||||
db->endIterRows ();
|
||||
bSuccess = true;
|
||||
}
|
||||
|
||||
if (theConfig.NODE_PUB.isValid () && theConfig.NODE_PRIV.isValid ())
|
||||
{
|
||||
mNodePublicKey = theConfig.NODE_PUB;
|
||||
mNodePrivateKey = theConfig.NODE_PRIV;
|
||||
}
|
||||
|
||||
return bSuccess;
|
||||
}
|
||||
|
||||
// Create and store a network identity.
|
||||
bool LocalCredentials::nodeIdentityCreate ()
|
||||
{
|
||||
if (!theConfig.QUIET)
|
||||
Log::out() << "NodeIdentity: Creating.";
|
||||
|
||||
//
|
||||
// Generate the public and private key
|
||||
//
|
||||
RippleAddress naSeed = RippleAddress::createSeedRandom ();
|
||||
RippleAddress naNodePublic = RippleAddress::createNodePublic (naSeed);
|
||||
RippleAddress naNodePrivate = RippleAddress::createNodePrivate (naSeed);
|
||||
|
||||
// Make new key.
|
||||
|
||||
#ifdef CREATE_NEW_DH_PARAMS
|
||||
std::string strDh512 = DH_der_gen (512);
|
||||
#else
|
||||
static const unsigned char dh512Param[] =
|
||||
{
|
||||
0x30, 0x46, 0x02, 0x41, 0x00, 0x98, 0x15, 0xd2, 0xd0, 0x08, 0x32, 0xda,
|
||||
0xaa, 0xac, 0xc4, 0x71, 0xa3, 0x1b, 0x11, 0xf0, 0x6c, 0x62, 0xb2, 0x35,
|
||||
0x8a, 0x10, 0x92, 0xc6, 0x0a, 0xa3, 0x84, 0x7e, 0xaf, 0x17, 0x29, 0x0b,
|
||||
0x70, 0xef, 0x07, 0x4f, 0xfc, 0x9d, 0x6d, 0x87, 0x99, 0x19, 0x09, 0x5b,
|
||||
0x6e, 0xdb, 0x57, 0x72, 0x4a, 0x7e, 0xcd, 0xaf, 0xbd, 0x3a, 0x97, 0x55,
|
||||
0x51, 0x77, 0x5a, 0x34, 0x7c, 0xe8, 0xc5, 0x71, 0x63, 0x02, 0x01, 0x02
|
||||
};
|
||||
std::string strDh512 (reinterpret_cast<const char*> (dh512Param), sizeof (dh512Param));
|
||||
#endif
|
||||
|
||||
|
||||
#if 1
|
||||
std::string strDh1024 = strDh512; // For testing and most cases 512 is fine.
|
||||
#else
|
||||
std::string strDh1024 = DH_der_gen (1024);
|
||||
#endif
|
||||
|
||||
//
|
||||
// Store the node information
|
||||
//
|
||||
Database* db = getApp().getWalletDB ()->getDB ();
|
||||
|
||||
ScopedLock sl (getApp().getWalletDB ()->getDBLock ());
|
||||
db->executeSQL (str (boost::format ("INSERT INTO NodeIdentity (PublicKey,PrivateKey,Dh512,Dh1024) VALUES ('%s','%s',%s,%s);")
|
||||
% naNodePublic.humanNodePublic ()
|
||||
% naNodePrivate.humanNodePrivate ()
|
||||
% sqlEscape (strDh512)
|
||||
% sqlEscape (strDh1024)));
|
||||
// XXX Check error result.
|
||||
|
||||
if (!theConfig.QUIET)
|
||||
Log::out() << "NodeIdentity: Created.";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LocalCredentials::dataDelete (const std::string& strKey)
|
||||
{
|
||||
Database* db = getApp().getRpcDB ()->getDB ();
|
||||
|
||||
ScopedLock sl (getApp().getRpcDB ()->getDBLock ());
|
||||
|
||||
return db->executeSQL (str (boost::format ("DELETE FROM RPCData WHERE Key=%s;")
|
||||
% sqlEscape (strKey)));
|
||||
}
|
||||
|
||||
bool LocalCredentials::dataFetch (const std::string& strKey, std::string& strValue)
|
||||
{
|
||||
Database* db = getApp().getRpcDB ()->getDB ();
|
||||
|
||||
ScopedLock sl (getApp().getRpcDB ()->getDBLock ());
|
||||
|
||||
bool bSuccess = false;
|
||||
|
||||
if (db->executeSQL (str (boost::format ("SELECT Value FROM RPCData WHERE Key=%s;")
|
||||
% sqlEscape (strKey))) && db->startIterRows ())
|
||||
{
|
||||
Blob vucData = db->getBinary ("Value");
|
||||
strValue.assign (vucData.begin (), vucData.end ());
|
||||
|
||||
db->endIterRows ();
|
||||
|
||||
bSuccess = true;
|
||||
}
|
||||
|
||||
return bSuccess;
|
||||
}
|
||||
|
||||
bool LocalCredentials::dataStore (const std::string& strKey, const std::string& strValue)
|
||||
{
|
||||
Database* db = getApp().getRpcDB ()->getDB ();
|
||||
|
||||
ScopedLock sl (getApp().getRpcDB ()->getDBLock ());
|
||||
|
||||
bool bSuccess = false;
|
||||
|
||||
return (db->executeSQL (str (boost::format ("REPLACE INTO RPCData (Key, Value) VALUES (%s,%s);")
|
||||
% sqlEscape (strKey)
|
||||
% sqlEscape (strValue)
|
||||
)));
|
||||
|
||||
return bSuccess;
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
@@ -1,67 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_LOCALCREDENTIALS_H
|
||||
#define RIPPLE_LOCALCREDENTIALS_H
|
||||
|
||||
/** Holds the cryptographic credentials identifying this instance of the server.
|
||||
*/
|
||||
class LocalCredentials : Uncopyable
|
||||
{
|
||||
public:
|
||||
LocalCredentials ();
|
||||
|
||||
// Begin processing.
|
||||
// - Maintain peer connectivity through validation and peer management.
|
||||
void start ();
|
||||
|
||||
RippleAddress const& getNodePublic () const
|
||||
{
|
||||
return mNodePublicKey;
|
||||
}
|
||||
|
||||
RippleAddress const& getNodePrivate () const
|
||||
{
|
||||
return mNodePrivateKey;
|
||||
}
|
||||
|
||||
DH* getDh512 () const
|
||||
{
|
||||
return DHparams_dup (mDh512);
|
||||
}
|
||||
|
||||
DH* getDh1024 () const
|
||||
{
|
||||
return DHparams_dup (mDh1024);
|
||||
}
|
||||
|
||||
// Local persistence of RPC clients
|
||||
bool dataDelete (std::string const& strKey);
|
||||
|
||||
// VFALCO NOTE why is strValue non-const?
|
||||
bool dataFetch (std::string const& strKey, std::string& strValue);
|
||||
bool dataStore (std::string const& strKey, std::string const& strValue);
|
||||
|
||||
private:
|
||||
LocalCredentials (LocalCredentials const&); // disallowed
|
||||
LocalCredentials& operator= (const LocalCredentials&); // disallowed
|
||||
|
||||
bool nodeIdentityLoad ();
|
||||
bool nodeIdentityCreate ();
|
||||
|
||||
private:
|
||||
boost::recursive_mutex mLock;
|
||||
|
||||
RippleAddress mNodePublicKey;
|
||||
RippleAddress mNodePrivateKey;
|
||||
DH* mDh512;
|
||||
DH* mDh1024;
|
||||
|
||||
LedgerIndex mLedger; // ledger we last synched to
|
||||
};
|
||||
|
||||
#endif
|
||||
// vim:ts=4
|
||||
@@ -1,450 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
namespace po = boost::program_options;
|
||||
|
||||
void setupServer ()
|
||||
{
|
||||
#ifdef RLIMIT_NOFILE
|
||||
struct rlimit rl;
|
||||
if (getrlimit(RLIMIT_NOFILE, &rl) == 0)
|
||||
{
|
||||
if (rl.rlim_cur != rl.rlim_max)
|
||||
{
|
||||
rl.rlim_cur = rl.rlim_max;
|
||||
setrlimit(RLIMIT_NOFILE, &rl);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
getApp().setup ();
|
||||
}
|
||||
|
||||
void startServer ()
|
||||
{
|
||||
//
|
||||
// Execute start up rpc commands.
|
||||
//
|
||||
if (theConfig.RPC_STARTUP.isArray ())
|
||||
{
|
||||
for (int i = 0; i != theConfig.RPC_STARTUP.size (); ++i)
|
||||
{
|
||||
const Json::Value& jvCommand = theConfig.RPC_STARTUP[i];
|
||||
|
||||
if (!theConfig.QUIET)
|
||||
Log::out() << "Startup RPC: " << jvCommand;
|
||||
|
||||
RPCHandler rhHandler (&getApp().getOPs ());
|
||||
|
||||
// VFALCO TODO Clean up this magic number
|
||||
LoadType loadType = LT_RPCReference;
|
||||
Json::Value jvResult = rhHandler.doCommand (jvCommand, RPCHandler::ADMIN, &loadType);
|
||||
|
||||
if (!theConfig.QUIET)
|
||||
Log::out() << "Result: " << jvResult;
|
||||
}
|
||||
}
|
||||
|
||||
getApp().run (); // Blocks till we get a stop RPC.
|
||||
}
|
||||
|
||||
void setupConfigForUnitTests (Config* config)
|
||||
{
|
||||
config->nodeDatabase = parseDelimitedKeyValueString ("type=memory");
|
||||
config->ephemeralNodeDatabase = StringPairArray ();
|
||||
config->importNodeDatabase = StringPairArray ();
|
||||
}
|
||||
|
||||
bool init_unit_test ()
|
||||
{
|
||||
setupConfigForUnitTests (&theConfig);
|
||||
|
||||
getApp ();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void printHelp (const po::options_description& desc)
|
||||
{
|
||||
using namespace std;
|
||||
|
||||
cerr << SYSTEM_NAME "d [options] <command> <params>" << endl;
|
||||
|
||||
cerr << desc << endl;
|
||||
|
||||
cerr << "Commands: " << endl;
|
||||
cerr << " account_info <account>|<nickname>|<seed>|<pass_phrase>|<key> [<ledger>] [strict]" << endl;
|
||||
cerr << " account_lines <account> <account>|\"\" [<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;
|
||||
#if ENABLE_INSECURE
|
||||
cerr << " data_delete <key>" << endl;
|
||||
cerr << " data_fetch <key>" << endl;
|
||||
cerr << " data_store <key> <value>" << endl;
|
||||
#endif
|
||||
cerr << " get_counts" << endl;
|
||||
cerr << " json <method> <json>" << endl;
|
||||
cerr << " ledger [<id>|current|closed|validated] [full]" << endl;
|
||||
cerr << " ledger_accept" << endl;
|
||||
cerr << " ledger_closed" << endl;
|
||||
cerr << " ledger_current" << endl;
|
||||
cerr << " ledger_header <ledger>" << endl;
|
||||
cerr << " logrotate " << endl;
|
||||
cerr << " peers" << endl;
|
||||
cerr << " proof_create [<difficulty>] [<secret>]" << endl;
|
||||
cerr << " proof_solve <token>" << endl;
|
||||
cerr << " proof_verify <token> <solution> [<difficulty>] [<secret>]" << endl;
|
||||
cerr << " random" << endl;
|
||||
cerr << " ripple ..." << endl;
|
||||
cerr << " ripple_path_find <json> [<ledger>]" << endl;
|
||||
// cerr << " send <seed> <paying_account> <account_id> <amount> [<currency>] [<send_max>] [<send_currency>]" << endl;
|
||||
cerr << " stop" << endl;
|
||||
cerr << " tx <id>" << endl;
|
||||
cerr << " unl_add <domain>|<public> [<comment>]" << endl;
|
||||
cerr << " unl_delete <domain>|<public_key>" << endl;
|
||||
cerr << " unl_list" << endl;
|
||||
cerr << " unl_load" << endl;
|
||||
cerr << " unl_network" << endl;
|
||||
cerr << " unl_reset" << endl;
|
||||
cerr << " validation_create [<seed>|<pass_phrase>|<key>]" << endl;
|
||||
cerr << " validation_seed [<seed>|<pass_phrase>|<key>]" << endl;
|
||||
cerr << " wallet_add <regular_seed> <paying_account> <master_seed> [<initial_funds>] [<account_annotation>]" << endl;
|
||||
cerr << " wallet_accounts <seed>" << endl;
|
||||
cerr << " wallet_claim <master_seed> <regular_seed> [<source_tag>] [<account_annotation>]" << endl;
|
||||
cerr << " wallet_seed [<seed>|<passphrase>|<passkey>]" << endl;
|
||||
cerr << " wallet_propose [<passphrase>]" << endl;
|
||||
|
||||
// Transaction helpers (that were removed):
|
||||
// cerr << " account_domain_set <seed> <paying_account> [<domain>]" << endl;
|
||||
// cerr << " account_email_set <seed> <paying_account> [<email_address>]" << endl;
|
||||
// cerr << " account_rate_set <seed> <paying_account> <rate>" << endl;
|
||||
// cerr << " account_wallet_set <seed> <paying_account> [<wallet_hash>]" << endl;
|
||||
// cerr << " nickname_info <nickname>" << endl;
|
||||
// cerr << " nickname_set <seed> <paying_account> <nickname> [<offer_minimum>] [<authorization>]" << endl;
|
||||
// cerr << " offer_create <seed> <paying_account> <taker_pays_amount> <taker_pays_currency> <taker_pays_issuer> <takers_gets_amount> <takers_gets_currency> <takers_gets_issuer> <expires> [passive]" << endl;
|
||||
// cerr << " offer_cancel <seed> <paying_account> <sequence>" << endl;
|
||||
// cerr << " password_fund <seed> <paying_account> [<account>]" << endl;
|
||||
// cerr << " password_set <master_seed> <regular_seed> [<account>]" << endl;
|
||||
// cerr << " trust_set <seed> <paying_account> <destination_account> <limit_amount> <currency> [<quality_in>] [<quality_out>]" << endl;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// OUr custom unit test runner
|
||||
class RippleUnitTests : public UnitTests
|
||||
{
|
||||
public:
|
||||
void logMessage (String const& message)
|
||||
{
|
||||
Log::out () << message.toStdString ();
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Run the Beast unit tests.
|
||||
*/
|
||||
static void runBeastUnitTests (std::string const& individualTest = "")
|
||||
{
|
||||
RippleUnitTests tr;
|
||||
|
||||
tr.setAssertOnFailure (false);
|
||||
tr.setPassesAreLogged (false);
|
||||
|
||||
if (individualTest.empty ())
|
||||
{
|
||||
tr.runAllTests ();
|
||||
}
|
||||
else
|
||||
{
|
||||
tr.runTest (individualTest.c_str ());
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Run the Boost unit tests.
|
||||
|
||||
@note These are deprecated. We want to migrate to using only
|
||||
the Beast unit testing framework. Please do not add more
|
||||
Boost based unit tests.
|
||||
*/
|
||||
// VFALCO NOTE What are argc and argv for?
|
||||
// Where does the boost unit test framework write its output?
|
||||
//
|
||||
static void runBoostUnitTests (int argc, char* argv [])
|
||||
{
|
||||
// DEPRECATED
|
||||
boost::unit_test::unit_test_main (init_unit_test, argc, argv);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
int rippleMain (int argc, char** argv)
|
||||
{
|
||||
//
|
||||
// These debug heap calls do nothing in release or non Visual Studio builds.
|
||||
//
|
||||
|
||||
// Checks the heap at every allocation and deallocation (slow).
|
||||
//
|
||||
//Debug::setAlwaysCheckHeap (false);
|
||||
|
||||
// Keeps freed memory blocks and fills them with a guard value.
|
||||
//
|
||||
//Debug::setHeapDelayedFree (false);
|
||||
|
||||
// At exit, reports all memory blocks which have not been freed.
|
||||
//
|
||||
#if 1
|
||||
Debug::setHeapReportLeaks (false);
|
||||
|
||||
#else
|
||||
// This is some temporary leak checking test code
|
||||
//
|
||||
Debug::setHeapReportLeaks (false);
|
||||
|
||||
//malloc (512); // Any leaks before this line in the output are from static initializations.
|
||||
|
||||
ThreadWithCallQueue t ("test");
|
||||
GlobalPagedFreeStore::getInstance ();
|
||||
t.start ();
|
||||
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
|
||||
setCallingThreadName ("main");
|
||||
int iResult = 0;
|
||||
po::variables_map vm; // Map of options.
|
||||
|
||||
String importDescription;
|
||||
{
|
||||
importDescription <<
|
||||
"Import an existing node database (specified in the " <<
|
||||
"[" << ConfigSection::importNodeDatabase () << "] configuration file section) "
|
||||
"into the current node database (specified in the " <<
|
||||
"[" << ConfigSection::nodeDatabase () << "] configuration file section). ";
|
||||
}
|
||||
|
||||
// VFALCO TODO Replace boost program options with something from Beast.
|
||||
//
|
||||
// Set up option parsing.
|
||||
//
|
||||
po::options_description desc ("General Options");
|
||||
desc.add_options ()
|
||||
("help,h", "Display this message.")
|
||||
("conf", po::value<std::string> (), "Specify the configuration file.")
|
||||
("rpc", "Perform rpc command (default).")
|
||||
("rpc_ip", po::value <std::string> (), "Specify the IP address for RPC command. Format: <ip-address>[':'<port-number>]")
|
||||
("rpc_port", po::value <int> (), "Specify the port number for RPC command.")
|
||||
("standalone,a", "Run with no peers.")
|
||||
("testnet,t", "Run in test net mode.")
|
||||
("unittest,u", "Perform unit tests.")
|
||||
("unittest2", po::value <std::string> ()->implicit_value (""), "Perform new unit tests.")
|
||||
("parameters", po::value< vector<string> > (), "Specify comma separated parameters.")
|
||||
("quiet,q", "Reduce diagnotics.")
|
||||
("verbose,v", "Verbose logging.")
|
||||
("load", "Load the current ledger from the local DB.")
|
||||
("replay","Replay a ledger close.")
|
||||
("ledger", po::value<std::string> (), "Load the specified ledger and start from .")
|
||||
("start", "Start from a fresh Ledger.")
|
||||
("net", "Get the initial ledger from the network.")
|
||||
("fg", "Run in the foreground.")
|
||||
("import", importDescription.toStdString ().c_str ())
|
||||
;
|
||||
|
||||
// Interpret positional arguments as --parameters.
|
||||
po::positional_options_description p;
|
||||
p.add ("parameters", -1);
|
||||
|
||||
// These must be added before the Application object is created
|
||||
NodeStore::addBackendFactory (KeyvaDBBackendFactory::getInstance ());
|
||||
NodeStore::addBackendFactory (LevelDBBackendFactory::getInstance ());
|
||||
NodeStore::addBackendFactory (MemoryBackendFactory::getInstance ());
|
||||
NodeStore::addBackendFactory (NullBackendFactory::getInstance ());
|
||||
NodeStore::addBackendFactory (SqliteBackendFactory::getInstance ());
|
||||
#if RIPPLE_HYPERLEVELDB_AVAILABLE
|
||||
NodeStore::addBackendFactory (HyperLevelDBBackendFactory::getInstance ());
|
||||
#endif
|
||||
#if RIPPLE_MDB_AVAILABLE
|
||||
NodeStore::addBackendFactory (MdbBackendFactory::getInstance ());
|
||||
#endif
|
||||
|
||||
if (! RandomNumbers::getInstance ().initialize ())
|
||||
{
|
||||
Log::out() << "Unable to add system entropy";
|
||||
iResult = 2;
|
||||
}
|
||||
|
||||
if (iResult)
|
||||
{
|
||||
nothing ();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Parse options, if no error.
|
||||
try
|
||||
{
|
||||
po::store (po::command_line_parser (argc, argv)
|
||||
.options (desc) // Parse options.
|
||||
.positional (p) // Remainder as --parameters.
|
||||
.run (),
|
||||
vm);
|
||||
po::notify (vm); // Invoke option notify functions.
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
iResult = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (iResult)
|
||||
{
|
||||
nothing ();
|
||||
}
|
||||
else if (vm.count ("help"))
|
||||
{
|
||||
iResult = 1;
|
||||
}
|
||||
|
||||
if (HaveSustain () &&
|
||||
!iResult && !vm.count ("parameters") && !vm.count ("fg") && !vm.count ("standalone") && !vm.count ("unittest"))
|
||||
{
|
||||
std::string logMe = DoSustain (theConfig.DEBUG_LOGFILE.string());
|
||||
|
||||
if (!logMe.empty ())
|
||||
Log (lsWARNING) << logMe;
|
||||
}
|
||||
|
||||
if (vm.count ("quiet"))
|
||||
{
|
||||
Log::setMinSeverity (lsFATAL, true);
|
||||
}
|
||||
else if (vm.count ("verbose"))
|
||||
{
|
||||
Log::setMinSeverity (lsTRACE, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log::setMinSeverity (lsINFO, true);
|
||||
}
|
||||
|
||||
// Run the unit tests if requested.
|
||||
//
|
||||
if (vm.count ("unittest"))
|
||||
{
|
||||
runBeastUnitTests ();
|
||||
|
||||
// DEPRECATED
|
||||
runBoostUnitTests (argc, argv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (vm.count ("unittest2"))
|
||||
{
|
||||
std::string const test = vm ["unittest2"].as <std::string> ();
|
||||
|
||||
runBeastUnitTests (test);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!iResult)
|
||||
{
|
||||
theConfig.setup (
|
||||
vm.count ("conf") ? vm["conf"].as<std::string> () : "", // Config file.
|
||||
!!vm.count ("testnet"), // Testnet flag.
|
||||
!!vm.count ("quiet")); // Quiet flag.
|
||||
|
||||
if (vm.count ("standalone"))
|
||||
{
|
||||
theConfig.RUN_STANDALONE = true;
|
||||
theConfig.LEDGER_HISTORY = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (vm.count ("start")) theConfig.START_UP = Config::FRESH;
|
||||
|
||||
// Handle a one-time import option
|
||||
//
|
||||
if (vm.count ("import"))
|
||||
{
|
||||
String const optionString (vm ["import"].as <std::string> ());
|
||||
|
||||
theConfig.importNodeDatabase = parseDelimitedKeyValueString (optionString);
|
||||
}
|
||||
|
||||
if (vm.count ("ledger"))
|
||||
{
|
||||
theConfig.START_LEDGER = vm["ledger"].as<std::string> ();
|
||||
if (vm.count("replay"))
|
||||
theConfig.START_UP = Config::REPLAY;
|
||||
else
|
||||
theConfig.START_UP = Config::LOAD;
|
||||
}
|
||||
else if (vm.count ("load"))
|
||||
{
|
||||
theConfig.START_UP = Config::LOAD;
|
||||
}
|
||||
else if (vm.count ("net"))
|
||||
{
|
||||
theConfig.START_UP = Config::NETWORK;
|
||||
|
||||
if (theConfig.VALIDATION_QUORUM < 2)
|
||||
theConfig.VALIDATION_QUORUM = 2;
|
||||
}
|
||||
|
||||
if (iResult == 0)
|
||||
{
|
||||
// These overrides must happen after the config file is loaded.
|
||||
|
||||
// Override the RPC destination IP address
|
||||
//
|
||||
if (vm.count ("rpc_ip"))
|
||||
{
|
||||
theConfig.setRpcIpAndOptionalPort (vm ["rpc_ip"].as <std::string> ());
|
||||
}
|
||||
|
||||
// Override the RPC destination port number
|
||||
//
|
||||
if (vm.count ("rpc_port"))
|
||||
{
|
||||
// VFALCO TODO This should be a short.
|
||||
theConfig.setRpcPort (vm ["rpc_port"].as <int> ());
|
||||
}
|
||||
}
|
||||
|
||||
if (iResult)
|
||||
{
|
||||
nothing ();
|
||||
}
|
||||
else if (!vm.count ("parameters"))
|
||||
{
|
||||
// No arguments. Run server.
|
||||
setupServer ();
|
||||
setCallingThreadName ("io");
|
||||
startServer ();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Have a RPC command.
|
||||
setCallingThreadName ("rpc");
|
||||
std::vector<std::string> vCmd = vm["parameters"].as<std::vector<std::string> > ();
|
||||
|
||||
iResult = commandLineRPC (vCmd);
|
||||
}
|
||||
|
||||
if (1 == iResult && !vm.count ("quiet"))
|
||||
printHelp (desc);
|
||||
|
||||
return iResult;
|
||||
}
|
||||
// vim:ts=4
|
||||
@@ -1,33 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
NicknameState::NicknameState (SerializedLedgerEntry::pointer ledgerEntry) :
|
||||
mLedgerEntry (ledgerEntry)
|
||||
{
|
||||
if (!mLedgerEntry || mLedgerEntry->getType () != ltNICKNAME) return;
|
||||
}
|
||||
|
||||
bool NicknameState::haveMinimumOffer () const
|
||||
{
|
||||
return mLedgerEntry->isFieldPresent (sfMinimumOffer);
|
||||
}
|
||||
|
||||
STAmount NicknameState::getMinimumOffer () const
|
||||
{
|
||||
return mLedgerEntry->isFieldPresent (sfMinimumOffer)
|
||||
? mLedgerEntry->getFieldAmount (sfMinimumOffer)
|
||||
: STAmount ();
|
||||
}
|
||||
|
||||
RippleAddress NicknameState::getAccountID () const
|
||||
{
|
||||
return mLedgerEntry->getFieldAccount (sfAccount);
|
||||
}
|
||||
|
||||
void NicknameState::addJson (Json::Value& val)
|
||||
{
|
||||
val = mLedgerEntry->getJson (0);
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
|
||||
#ifndef RIPPLE_NICKNAMESTATE_H
|
||||
#define RIPPLE_NICKNAMESTATE_H
|
||||
|
||||
//
|
||||
// State of a nickname node.
|
||||
// - Isolate ledger entry format.
|
||||
//
|
||||
|
||||
class NicknameState
|
||||
{
|
||||
public:
|
||||
typedef boost::shared_ptr <NicknameState> pointer;
|
||||
|
||||
public:
|
||||
explicit NicknameState (SerializedLedgerEntry::pointer ledgerEntry); // For accounts in a ledger
|
||||
|
||||
bool haveMinimumOffer () const;
|
||||
STAmount getMinimumOffer () const;
|
||||
RippleAddress getAccountID () const;
|
||||
|
||||
SerializedLedgerEntry::pointer getSLE ()
|
||||
{
|
||||
return mLedgerEntry;
|
||||
}
|
||||
const SerializedLedgerEntry& peekSLE () const
|
||||
{
|
||||
return *mLedgerEntry;
|
||||
}
|
||||
SerializedLedgerEntry& peekSLE ()
|
||||
{
|
||||
return *mLedgerEntry;
|
||||
}
|
||||
|
||||
Blob getRaw () const;
|
||||
void addJson (Json::Value& value);
|
||||
|
||||
private:
|
||||
SerializedLedgerEntry::pointer mLedgerEntry;
|
||||
};
|
||||
|
||||
#endif
|
||||
// vim:ts=4
|
||||
@@ -1,33 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
AccountItem::pointer Offer::makeItem (const uint160& , SerializedLedgerEntry::ref ledgerEntry)
|
||||
{
|
||||
if (!ledgerEntry || ledgerEntry->getType () != ltOFFER) return (AccountItem::pointer ());
|
||||
|
||||
Offer* offer = new Offer (ledgerEntry);
|
||||
return (AccountItem::pointer (offer));
|
||||
}
|
||||
|
||||
Offer::Offer (SerializedLedgerEntry::pointer ledgerEntry) : AccountItem (ledgerEntry),
|
||||
mAccount (mLedgerEntry->getFieldAccount (sfAccount)),
|
||||
mTakerGets (mLedgerEntry->getFieldAmount (sfTakerGets)),
|
||||
mTakerPays (mLedgerEntry->getFieldAmount (sfTakerPays)),
|
||||
mSeq (mLedgerEntry->getFieldU32 (sfSequence))
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
Json::Value Offer::getJson (int)
|
||||
{
|
||||
Json::Value ret (Json::objectValue);
|
||||
ret["account"] = mAccount.humanAccountID ();
|
||||
ret["taker_gets"] = getTakerGets ().getFullText ();
|
||||
ret["taker_pays"] = getTakerPays ().getFullText ();
|
||||
return ret;
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
@@ -1,55 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_OFFER_H
|
||||
#define RIPPLE_OFFER_H
|
||||
|
||||
class Offer : public AccountItem
|
||||
{
|
||||
public:
|
||||
Offer () {}
|
||||
|
||||
virtual ~Offer () {}
|
||||
|
||||
AccountItem::pointer makeItem (const uint160&, SerializedLedgerEntry::ref ledgerEntry);
|
||||
|
||||
LedgerEntryType getType ()
|
||||
{
|
||||
return (ltOFFER);
|
||||
}
|
||||
|
||||
const STAmount& getTakerPays ()
|
||||
{
|
||||
return (mTakerPays);
|
||||
}
|
||||
const STAmount& getTakerGets ()
|
||||
{
|
||||
return (mTakerGets);
|
||||
}
|
||||
const RippleAddress& getAccount ()
|
||||
{
|
||||
return (mAccount);
|
||||
}
|
||||
int getSeq ()
|
||||
{
|
||||
return (mSeq);
|
||||
}
|
||||
Json::Value getJson (int);
|
||||
|
||||
private:
|
||||
// For accounts in a ledger
|
||||
explicit Offer (SerializedLedgerEntry::pointer ledgerEntry);
|
||||
|
||||
private:
|
||||
RippleAddress mAccount;
|
||||
STAmount mTakerGets;
|
||||
STAmount mTakerPays;
|
||||
int mSeq;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
// vim:ts=4
|
||||
@@ -1,44 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
|
||||
OrderBook::OrderBook (uint256 const& index,
|
||||
uint160 const& currencyIn,
|
||||
uint160 const& currencyOut,
|
||||
uint160 const& issuerIn,
|
||||
uint160 const& issuerOut)
|
||||
: mBookBase (index)
|
||||
, mCurrencyIn (currencyIn)
|
||||
, mCurrencyOut (currencyOut)
|
||||
, mIssuerIn (issuerIn)
|
||||
, mIssuerOut (issuerOut)
|
||||
{
|
||||
}
|
||||
|
||||
uint256 const& OrderBook::getBookBase () const
|
||||
{
|
||||
return mBookBase;
|
||||
}
|
||||
|
||||
uint160 const& OrderBook::getCurrencyIn () const
|
||||
{
|
||||
return mCurrencyIn;
|
||||
}
|
||||
|
||||
uint160 const& OrderBook::getCurrencyOut () const
|
||||
{
|
||||
return mCurrencyOut;
|
||||
}
|
||||
|
||||
uint160 const& OrderBook::getIssuerIn () const
|
||||
{
|
||||
return mIssuerIn;
|
||||
}
|
||||
|
||||
uint160 const& OrderBook::getIssuerOut () const
|
||||
{
|
||||
return mIssuerOut;
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
|
||||
#ifndef RIPPLE_ORDERBOOK_H
|
||||
#define RIPPLE_ORDERBOOK_H
|
||||
|
||||
/** Describes a serialized ledger entry for an order book.
|
||||
*/
|
||||
class OrderBook : LeakChecked <OrderBook>
|
||||
{
|
||||
public:
|
||||
typedef boost::shared_ptr <OrderBook> pointer;
|
||||
|
||||
typedef boost::shared_ptr <OrderBook> const& ref;
|
||||
|
||||
public:
|
||||
/** Construct from a currency specification.
|
||||
|
||||
@param index ???
|
||||
@param currencyIn The base currency.
|
||||
@param currencyOut The destination currency.
|
||||
@param issuerIn The base issuer.
|
||||
@param issuerOut The destination issuer.
|
||||
*/
|
||||
// VFALCO NOTE what is the meaning of the index parameter?
|
||||
// VFALCO TODO group the issuer and currency parameters together.
|
||||
// VFALCO TODO give typedef names to uint256 / uint160 params
|
||||
OrderBook (uint256 const& index,
|
||||
uint160 const& currencyIn,
|
||||
uint160 const& currencyOut,
|
||||
uint160 const& issuerIn,
|
||||
uint160 const& issuerOut);
|
||||
|
||||
uint256 const& getBookBase () const;
|
||||
|
||||
uint160 const& getCurrencyIn () const;
|
||||
|
||||
uint160 const& getCurrencyOut () const;
|
||||
|
||||
uint160 const& getIssuerIn () const;
|
||||
|
||||
uint160 const& getIssuerOut () const;
|
||||
|
||||
|
||||
private:
|
||||
uint256 const mBookBase;
|
||||
uint160 const mCurrencyIn;
|
||||
uint160 const mCurrencyOut;
|
||||
uint160 const mIssuerIn;
|
||||
uint160 const mIssuerOut;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,361 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
SETUP_LOG (PathRequest)
|
||||
|
||||
// VFALCO TODO Move these globals into a PathRequests collection inteface
|
||||
boost::recursive_mutex PathRequest::sLock;
|
||||
std::set <PathRequest::wptr> PathRequest::sRequests;
|
||||
|
||||
PathRequest::PathRequest (const boost::shared_ptr<InfoSub>& subscriber)
|
||||
: wpSubscriber (subscriber)
|
||||
, jvStatus (Json::objectValue)
|
||||
, bValid (false)
|
||||
, bNew (true)
|
||||
{
|
||||
}
|
||||
|
||||
bool PathRequest::isValid ()
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl (mLock);
|
||||
return bValid;
|
||||
}
|
||||
|
||||
bool PathRequest::isNew ()
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl (mLock);
|
||||
return bNew;
|
||||
}
|
||||
|
||||
bool PathRequest::isValid (Ledger::ref lrLedger)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl (mLock);
|
||||
bValid = raSrcAccount.isSet () && raDstAccount.isSet () && saDstAmount.isPositive ();
|
||||
|
||||
if (bValid)
|
||||
{
|
||||
AccountState::pointer asSrc = getApp().getOPs ().getAccountState (lrLedger, raSrcAccount);
|
||||
|
||||
if (!asSrc)
|
||||
{
|
||||
// no source account
|
||||
bValid = false;
|
||||
jvStatus = rpcError (rpcSRC_ACT_NOT_FOUND);
|
||||
}
|
||||
else
|
||||
{
|
||||
AccountState::pointer asDst = getApp().getOPs ().getAccountState (lrLedger, raDstAccount);
|
||||
Json::Value jvDestCur;
|
||||
|
||||
if (!asDst)
|
||||
{
|
||||
// no destination account
|
||||
jvDestCur.append (Json::Value ("XRP"));
|
||||
|
||||
if (!saDstAmount.isNative ())
|
||||
{
|
||||
// only XRP can be send to a non-existent account
|
||||
bValid = false;
|
||||
jvStatus = rpcError (rpcACT_NOT_FOUND);
|
||||
}
|
||||
else if (saDstAmount < STAmount (lrLedger->getReserve (0)))
|
||||
{
|
||||
// payment must meet reserve
|
||||
bValid = false;
|
||||
jvStatus = rpcError (rpcDST_AMT_MALFORMED);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
boost::unordered_set<uint160> usDestCurrID = usAccountDestCurrencies (raDstAccount, lrLedger, true);
|
||||
BOOST_FOREACH (const uint160 & uCurrency, usDestCurrID)
|
||||
jvDestCur.append (STAmount::createHumanCurrency (uCurrency));
|
||||
jvStatus["destination_tag"] = (asDst->peekSLE ().getFlags () & lsfRequireDestTag) != 0;
|
||||
}
|
||||
|
||||
jvStatus["destination_currencies"] = jvDestCur;
|
||||
}
|
||||
}
|
||||
|
||||
jvStatus["ledger_hash"] = lrLedger->getHash ().GetHex ();
|
||||
jvStatus["ledger_index"] = lrLedger->getLedgerSeq ();
|
||||
return bValid;
|
||||
}
|
||||
|
||||
Json::Value PathRequest::doCreate (Ledger::ref lrLedger, const Json::Value& value)
|
||||
{
|
||||
assert (lrLedger->isClosed ());
|
||||
|
||||
Json::Value status;
|
||||
bool mValid;
|
||||
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl (mLock);
|
||||
|
||||
if (parseJson (value, true) != PFR_PJ_INVALID)
|
||||
{
|
||||
mValid = isValid (lrLedger);
|
||||
|
||||
if (mValid)
|
||||
{
|
||||
RippleLineCache::pointer cache = boost::make_shared<RippleLineCache> (lrLedger);
|
||||
doUpdate (cache, true);
|
||||
}
|
||||
}
|
||||
else
|
||||
mValid = false;
|
||||
}
|
||||
|
||||
if (mValid)
|
||||
{
|
||||
WriteLog (lsINFO, PathRequest) << "Request created: " << raSrcAccount.humanAccountID () <<
|
||||
" -> " << raDstAccount.humanAccountID ();
|
||||
WriteLog (lsINFO, PathRequest) << "Deliver: " << saDstAmount.getFullText ();
|
||||
|
||||
boost::recursive_mutex::scoped_lock sl (sLock);
|
||||
sRequests.insert (shared_from_this ());
|
||||
}
|
||||
|
||||
return jvStatus;
|
||||
}
|
||||
|
||||
int PathRequest::parseJson (const Json::Value& jvParams, bool complete)
|
||||
{
|
||||
int ret = PFR_PJ_NOCHANGE;
|
||||
|
||||
if (jvParams.isMember ("source_account"))
|
||||
{
|
||||
if (!raSrcAccount.setAccountID (jvParams["source_account"].asString ()))
|
||||
{
|
||||
jvStatus = rpcError (rpcSRC_ACT_MALFORMED);
|
||||
return PFR_PJ_INVALID;
|
||||
}
|
||||
}
|
||||
else if (complete)
|
||||
{
|
||||
jvStatus = rpcError (rpcSRC_ACT_MISSING);
|
||||
return PFR_PJ_INVALID;
|
||||
}
|
||||
|
||||
if (jvParams.isMember ("destination_account"))
|
||||
{
|
||||
if (!raDstAccount.setAccountID (jvParams["destination_account"].asString ()))
|
||||
{
|
||||
jvStatus = rpcError (rpcDST_ACT_MALFORMED);
|
||||
return PFR_PJ_INVALID;
|
||||
}
|
||||
}
|
||||
else if (complete)
|
||||
{
|
||||
jvStatus = rpcError (rpcDST_ACT_MISSING);
|
||||
return PFR_PJ_INVALID;
|
||||
}
|
||||
|
||||
if (jvParams.isMember ("destination_amount"))
|
||||
{
|
||||
if (!saDstAmount.bSetJson (jvParams["destination_amount"]) ||
|
||||
(saDstAmount.getCurrency ().isZero () && saDstAmount.getIssuer ().isNonZero ()) ||
|
||||
(saDstAmount.getCurrency () == CURRENCY_BAD) ||
|
||||
!saDstAmount.isPositive ())
|
||||
{
|
||||
jvStatus = rpcError (rpcDST_AMT_MALFORMED);
|
||||
return PFR_PJ_INVALID;
|
||||
}
|
||||
}
|
||||
else if (complete)
|
||||
{
|
||||
jvStatus = rpcError (rpcDST_ACT_MISSING);
|
||||
return PFR_PJ_INVALID;
|
||||
}
|
||||
|
||||
if (jvParams.isMember ("source_currencies"))
|
||||
{
|
||||
const Json::Value& jvSrcCur = jvParams["source_currencies"];
|
||||
|
||||
if (!jvSrcCur.isArray ())
|
||||
{
|
||||
jvStatus = rpcError (rpcSRC_CUR_MALFORMED);
|
||||
return PFR_PJ_INVALID;
|
||||
}
|
||||
|
||||
sciSourceCurrencies.clear ();
|
||||
|
||||
for (unsigned i = 0; i < jvSrcCur.size (); ++i)
|
||||
{
|
||||
const Json::Value& jvCur = jvSrcCur[i];
|
||||
uint160 uCur, uIss;
|
||||
|
||||
if (!jvCur.isObject() || !jvCur.isMember ("currency") || !STAmount::currencyFromString (uCur, jvCur["currency"].asString ()))
|
||||
{
|
||||
jvStatus = rpcError (rpcSRC_CUR_MALFORMED);
|
||||
return PFR_PJ_INVALID;
|
||||
}
|
||||
|
||||
if (jvCur.isMember ("issuer") && !STAmount::issuerFromString (uIss, jvCur["issuer"].asString ()))
|
||||
{
|
||||
jvStatus = rpcError (rpcSRC_ISR_MALFORMED);
|
||||
}
|
||||
|
||||
if (uCur.isZero () && uIss.isNonZero ())
|
||||
{
|
||||
jvStatus = rpcError (rpcSRC_CUR_MALFORMED);
|
||||
return PFR_PJ_INVALID;
|
||||
}
|
||||
|
||||
sciSourceCurrencies.insert (currIssuer_t (uCur, uIss));
|
||||
}
|
||||
}
|
||||
|
||||
if (jvParams.isMember ("id"))
|
||||
jvId = jvParams["id"];
|
||||
|
||||
return ret;
|
||||
}
|
||||
Json::Value PathRequest::doClose (const Json::Value&)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl (mLock);
|
||||
return jvStatus;
|
||||
}
|
||||
|
||||
Json::Value PathRequest::doStatus (const Json::Value&)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl (mLock);
|
||||
return jvStatus;
|
||||
}
|
||||
|
||||
bool PathRequest::doUpdate (RippleLineCache::ref cache, bool fast)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl (mLock);
|
||||
jvStatus = Json::objectValue;
|
||||
|
||||
if (!isValid (cache->getLedger ()))
|
||||
return false;
|
||||
|
||||
if (!fast)
|
||||
bNew = false;
|
||||
|
||||
std::set<currIssuer_t> sourceCurrencies (sciSourceCurrencies);
|
||||
|
||||
if (sourceCurrencies.empty ())
|
||||
{
|
||||
boost::unordered_set<uint160> usCurrencies =
|
||||
usAccountSourceCurrencies (raSrcAccount, cache->getLedger (), true);
|
||||
bool sameAccount = raSrcAccount == raDstAccount;
|
||||
BOOST_FOREACH (const uint160 & c, usCurrencies)
|
||||
{
|
||||
if (!sameAccount || (c != saDstAmount.getCurrency ()))
|
||||
{
|
||||
if (c.isZero ())
|
||||
sourceCurrencies.insert (std::make_pair (c, ACCOUNT_XRP));
|
||||
else
|
||||
sourceCurrencies.insert (std::make_pair (c, raSrcAccount.getAccountID ()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
jvStatus["source_account"] = raSrcAccount.humanAccountID ();
|
||||
jvStatus["destination_account"] = raDstAccount.humanAccountID ();
|
||||
jvStatus["destination_amount"] = saDstAmount.getJson (0);
|
||||
|
||||
if (!jvId.isNull ())
|
||||
jvStatus["id"] = jvId;
|
||||
|
||||
Json::Value jvArray = Json::arrayValue;
|
||||
|
||||
BOOST_FOREACH (const currIssuer_t & currIssuer, sourceCurrencies)
|
||||
{
|
||||
{
|
||||
STAmount test (currIssuer.first, currIssuer.second, 1);
|
||||
WriteLog (lsDEBUG, PathRequest) << "Trying to find paths: " << test.getFullText ();
|
||||
}
|
||||
bool valid;
|
||||
STPathSet spsPaths;
|
||||
Pathfinder pf (cache, raSrcAccount, raDstAccount,
|
||||
currIssuer.first, currIssuer.second, saDstAmount, valid);
|
||||
CondLog (!valid, lsINFO, PathRequest) << "PF request not valid";
|
||||
|
||||
if (valid && pf.findPaths (theConfig.PATH_SEARCH_SIZE - (fast ? 0 : 1), 3, spsPaths))
|
||||
{
|
||||
LedgerEntrySet lesSandbox (cache->getLedger (), tapNONE);
|
||||
std::vector<PathState::pointer> vpsExpanded;
|
||||
STAmount saMaxAmountAct;
|
||||
STAmount saDstAmountAct;
|
||||
STAmount saMaxAmount (currIssuer.first,
|
||||
currIssuer.second.isNonZero () ? currIssuer.second :
|
||||
(currIssuer.first.isZero () ? ACCOUNT_XRP : raSrcAccount.getAccountID ()), 1);
|
||||
saMaxAmount.negate ();
|
||||
WriteLog (lsDEBUG, PathRequest) << "Paths found, calling rippleCalc";
|
||||
TER terResult = RippleCalc::rippleCalc (lesSandbox, saMaxAmountAct, saDstAmountAct,
|
||||
vpsExpanded, saMaxAmount, saDstAmount, raDstAccount.getAccountID (), raSrcAccount.getAccountID (),
|
||||
spsPaths, false, false, false, true);
|
||||
|
||||
if (terResult == tesSUCCESS)
|
||||
{
|
||||
Json::Value jvEntry (Json::objectValue);
|
||||
jvEntry["source_amount"] = saMaxAmountAct.getJson (0);
|
||||
jvEntry["paths_computed"] = spsPaths.getJson (0);
|
||||
jvArray.append (jvEntry);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog (lsINFO, PathRequest) << "rippleCalc returns " << transHuman (terResult);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog (lsINFO, PathRequest) << "No paths found";
|
||||
}
|
||||
}
|
||||
jvStatus["alternatives"] = jvArray;
|
||||
return true;
|
||||
}
|
||||
|
||||
void PathRequest::updateAll (Ledger::ref ledger, bool newOnly)
|
||||
{
|
||||
std::set<wptr> requests;
|
||||
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl (sLock);
|
||||
requests = sRequests;
|
||||
}
|
||||
|
||||
if (requests.empty ())
|
||||
return;
|
||||
|
||||
RippleLineCache::pointer cache = boost::make_shared<RippleLineCache> (ledger);
|
||||
|
||||
BOOST_FOREACH (wref wRequest, requests)
|
||||
{
|
||||
bool remove = true;
|
||||
PathRequest::pointer pRequest = wRequest.lock ();
|
||||
|
||||
if (pRequest && (!newOnly || pRequest->isNew ()))
|
||||
{
|
||||
InfoSub::pointer ipSub = pRequest->wpSubscriber.lock ();
|
||||
|
||||
if (ipSub)
|
||||
{
|
||||
Json::Value update;
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl (pRequest->mLock);
|
||||
pRequest->doUpdate (cache, false);
|
||||
update = pRequest->jvStatus;
|
||||
}
|
||||
update["type"] = "path_find";
|
||||
ipSub->send (update, false);
|
||||
remove = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (remove)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl (sLock);
|
||||
sRequests.erase (wRequest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
@@ -1,72 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_PATHREQUEST_H
|
||||
#define RIPPLE_PATHREQUEST_H
|
||||
|
||||
// A pathfinding request submitted by a client
|
||||
// The request issuer must maintain a strong pointer
|
||||
|
||||
class RippleLineCache;
|
||||
|
||||
// Return values from parseJson <0 = invalid, >0 = valid
|
||||
#define PFR_PJ_INVALID -1
|
||||
#define PFR_PJ_NOCHANGE 0
|
||||
#define PFR_PJ_CHANGE 1
|
||||
|
||||
class PathRequest : public boost::enable_shared_from_this<PathRequest>
|
||||
{
|
||||
public:
|
||||
typedef boost::weak_ptr<PathRequest> wptr;
|
||||
typedef boost::shared_ptr<PathRequest> pointer;
|
||||
typedef const pointer& ref;
|
||||
typedef const wptr& wref;
|
||||
typedef std::pair<uint160, uint160> currIssuer_t;
|
||||
|
||||
public:
|
||||
// VFALCO TODO Break the cyclic dependency on InfoSub
|
||||
explicit PathRequest (boost::shared_ptr <InfoSub> const& subscriber);
|
||||
|
||||
bool isValid (const boost::shared_ptr<Ledger>&);
|
||||
bool isValid ();
|
||||
bool isNew ();
|
||||
Json::Value getStatus ();
|
||||
|
||||
Json::Value doCreate (const boost::shared_ptr<Ledger>&, const Json::Value&);
|
||||
Json::Value doClose (const Json::Value&);
|
||||
Json::Value doStatus (const Json::Value&);
|
||||
|
||||
bool doUpdate (const boost::shared_ptr<RippleLineCache>&, bool fast); // update jvStatus
|
||||
|
||||
static void updateAll (const boost::shared_ptr<Ledger>& ledger, bool newOnly);
|
||||
|
||||
private:
|
||||
boost::recursive_mutex mLock;
|
||||
boost::weak_ptr<InfoSub> wpSubscriber; // Who this request came from
|
||||
Json::Value jvId;
|
||||
Json::Value jvStatus; // Last result
|
||||
|
||||
// Client request parameters
|
||||
RippleAddress raSrcAccount;
|
||||
RippleAddress raDstAccount;
|
||||
STAmount saDstAmount;
|
||||
std::set<currIssuer_t> sciSourceCurrencies;
|
||||
std::vector<Json::Value> vjvBridges;
|
||||
|
||||
bool bValid;
|
||||
bool bNew;
|
||||
|
||||
// Track all requests
|
||||
static std::set<wptr> sRequests;
|
||||
static boost::recursive_mutex sLock;
|
||||
|
||||
void setValid ();
|
||||
int parseJson (const Json::Value&, bool complete);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
// vim:ts=4
|
||||
@@ -1,794 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
// TODO:
|
||||
// - Do automatic bridging via XRP.
|
||||
//
|
||||
// OPTIMIZE: When calculating path increment, note if increment consumes all liquidity. No need to revisit path in the future if
|
||||
// all liquidity is used.
|
||||
//
|
||||
|
||||
class RippleCalc; // for logging
|
||||
|
||||
std::size_t hash_value (const aciSource& asValue)
|
||||
{
|
||||
std::size_t seed = 0;
|
||||
|
||||
asValue.get<0> ().hash_combine (seed);
|
||||
asValue.get<1> ().hash_combine (seed);
|
||||
asValue.get<2> ().hash_combine (seed);
|
||||
|
||||
return seed;
|
||||
}
|
||||
|
||||
// Compare the non-calculated fields.
|
||||
bool PathState::Node::operator== (const Node& pnOther) const
|
||||
{
|
||||
return pnOther.uFlags == uFlags
|
||||
&& pnOther.uAccountID == uAccountID
|
||||
&& pnOther.uCurrencyID == uCurrencyID
|
||||
&& pnOther.uIssuerID == uIssuerID;
|
||||
}
|
||||
|
||||
// This is for debugging not end users. Output names can be changed without warning.
|
||||
Json::Value PathState::Node::getJson () const
|
||||
{
|
||||
Json::Value jvNode (Json::objectValue);
|
||||
Json::Value jvFlags (Json::arrayValue);
|
||||
|
||||
jvNode["type"] = uFlags;
|
||||
|
||||
if (isSetBit (uFlags, STPathElement::typeAccount) || !!uAccountID)
|
||||
jvFlags.append (!!isSetBit (uFlags, STPathElement::typeAccount) == !!uAccountID ? "account" : "-account");
|
||||
|
||||
if (isSetBit (uFlags, STPathElement::typeCurrency) || !!uCurrencyID)
|
||||
jvFlags.append (!!isSetBit (uFlags, STPathElement::typeCurrency) == !!uCurrencyID ? "currency" : "-currency");
|
||||
|
||||
if (isSetBit (uFlags, STPathElement::typeIssuer) || !!uIssuerID)
|
||||
jvFlags.append (!!isSetBit (uFlags, STPathElement::typeIssuer) == !!uIssuerID ? "issuer" : "-issuer");
|
||||
|
||||
jvNode["flags"] = jvFlags;
|
||||
|
||||
if (!!uAccountID)
|
||||
jvNode["account"] = RippleAddress::createHumanAccountID (uAccountID);
|
||||
|
||||
if (!!uCurrencyID)
|
||||
jvNode["currency"] = STAmount::createHumanCurrency (uCurrencyID);
|
||||
|
||||
if (!!uIssuerID)
|
||||
jvNode["issuer"] = RippleAddress::createHumanAccountID (uIssuerID);
|
||||
|
||||
if (saRevRedeem)
|
||||
jvNode["rev_redeem"] = saRevRedeem.getFullText ();
|
||||
|
||||
if (saRevIssue)
|
||||
jvNode["rev_issue"] = saRevIssue.getFullText ();
|
||||
|
||||
if (saRevDeliver)
|
||||
jvNode["rev_deliver"] = saRevDeliver.getFullText ();
|
||||
|
||||
if (saFwdRedeem)
|
||||
jvNode["fwd_redeem"] = saFwdRedeem.getFullText ();
|
||||
|
||||
if (saFwdIssue)
|
||||
jvNode["fwd_issue"] = saFwdIssue.getFullText ();
|
||||
|
||||
if (saFwdDeliver)
|
||||
jvNode["fwd_deliver"] = saFwdDeliver.getFullText ();
|
||||
|
||||
return jvNode;
|
||||
}
|
||||
|
||||
//
|
||||
// PathState implementation
|
||||
//
|
||||
|
||||
// Return true, iff lhs has less priority than rhs.
|
||||
bool PathState::lessPriority (PathState& lhs, PathState& rhs)
|
||||
{
|
||||
// First rank is quality.
|
||||
if (lhs.uQuality != rhs.uQuality)
|
||||
return lhs.uQuality > rhs.uQuality; // Bigger is worse.
|
||||
|
||||
// Second rank is best quantity.
|
||||
if (lhs.saOutPass != rhs.saOutPass)
|
||||
return lhs.saOutPass < rhs.saOutPass; // Smaller is worse.
|
||||
|
||||
// Third rank is path index.
|
||||
return lhs.mIndex > rhs.mIndex; // Bigger is worse.
|
||||
}
|
||||
|
||||
// Make sure last path node delivers to uAccountID: uCurrencyID from uIssuerID.
|
||||
//
|
||||
// If the unadded next node as specified by arguments would not work as is, then add the necessary nodes so it would work.
|
||||
//
|
||||
// Rules:
|
||||
// - Currencies must be converted via an offer.
|
||||
// - A node names it's output.
|
||||
// - A ripple nodes output issuer must be the node's account or the next node's account.
|
||||
// - Offers can only go directly to another offer if the currency and issuer are an exact match.
|
||||
// - Real issuers must be specified for non-XRP.
|
||||
TER PathState::pushImply (
|
||||
const uint160& uAccountID, // --> Delivering to this account.
|
||||
const uint160& uCurrencyID, // --> Delivering this currency.
|
||||
const uint160& uIssuerID) // --> Delivering this issuer.
|
||||
{
|
||||
const Node& pnPrv = vpnNodes.back ();
|
||||
TER terResult = tesSUCCESS;
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc) << "pushImply> "
|
||||
<< RippleAddress::createHumanAccountID (uAccountID)
|
||||
<< " " << STAmount::createHumanCurrency (uCurrencyID)
|
||||
<< " " << RippleAddress::createHumanAccountID (uIssuerID);
|
||||
|
||||
if (pnPrv.uCurrencyID != uCurrencyID)
|
||||
{
|
||||
// Currency is different, need to convert via an offer.
|
||||
|
||||
terResult = pushNode ( // Offer.
|
||||
!!uCurrencyID
|
||||
? STPathElement::typeCurrency | STPathElement::typeIssuer
|
||||
: STPathElement::typeCurrency,
|
||||
ACCOUNT_XRP, // Placeholder for offers.
|
||||
uCurrencyID, // The offer's output is what is now wanted.
|
||||
uIssuerID);
|
||||
}
|
||||
|
||||
const Node& pnBck = vpnNodes.back ();
|
||||
|
||||
// For ripple, non-XRP, ensure the issuer is on at least one side of the transaction.
|
||||
if (tesSUCCESS == terResult
|
||||
&& !!uCurrencyID // Not XRP.
|
||||
&& (pnBck.uAccountID != uIssuerID // Previous is not issuing own IOUs.
|
||||
&& uAccountID != uIssuerID)) // Current is not receiving own IOUs.
|
||||
{
|
||||
// Need to ripple through uIssuerID's account.
|
||||
|
||||
terResult = pushNode (
|
||||
STPathElement::typeAccount | STPathElement::typeCurrency | STPathElement::typeIssuer,
|
||||
uIssuerID, // Intermediate account is the needed issuer.
|
||||
uCurrencyID,
|
||||
uIssuerID);
|
||||
}
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc) << boost::str (boost::format ("pushImply< : %s") % transToken (terResult));
|
||||
|
||||
return terResult;
|
||||
}
|
||||
|
||||
// Append a node and insert before it any implied nodes.
|
||||
// Offers may go back to back.
|
||||
// <-- terResult: tesSUCCESS, temBAD_PATH, terNO_ACCOUNT, terNO_AUTH, terNO_LINE, tecPATH_DRY
|
||||
TER PathState::pushNode (
|
||||
const int iType,
|
||||
const uint160& uAccountID,
|
||||
const uint160& uCurrencyID,
|
||||
const uint160& uIssuerID)
|
||||
{
|
||||
Node pnCur;
|
||||
const bool bFirst = vpnNodes.empty ();
|
||||
const Node& pnPrv = bFirst ? Node () : vpnNodes.back ();
|
||||
// true, iff node is a ripple account. false, iff node is an offer node.
|
||||
const bool bAccount = isSetBit (iType, STPathElement::typeAccount);
|
||||
// true, iff currency supplied.
|
||||
// Currency is specified for the output of the current node.
|
||||
const bool bCurrency = isSetBit (iType, STPathElement::typeCurrency);
|
||||
// Issuer is specified for the output of the current node.
|
||||
const bool bIssuer = isSetBit (iType, STPathElement::typeIssuer);
|
||||
TER terResult = tesSUCCESS;
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc) << "pushNode> "
|
||||
<< iType
|
||||
<< ": " << (bAccount ? RippleAddress::createHumanAccountID (uAccountID) : "-")
|
||||
<< " " << (bCurrency ? STAmount::createHumanCurrency (uCurrencyID) : "-")
|
||||
<< "/" << (bIssuer ? RippleAddress::createHumanAccountID (uIssuerID) : "-");
|
||||
|
||||
pnCur.uFlags = iType;
|
||||
pnCur.uCurrencyID = bCurrency ? uCurrencyID : pnPrv.uCurrencyID;
|
||||
|
||||
if (iType & ~STPathElement::typeValidBits)
|
||||
{
|
||||
WriteLog (lsDEBUG, RippleCalc) << "pushNode: bad bits.";
|
||||
|
||||
terResult = temBAD_PATH;
|
||||
}
|
||||
else if (bIssuer && !pnCur.uCurrencyID)
|
||||
{
|
||||
WriteLog (lsDEBUG, RippleCalc) << "pushNode: issuer specified for XRP.";
|
||||
|
||||
terResult = temBAD_PATH;
|
||||
}
|
||||
else if (bIssuer && !uIssuerID)
|
||||
{
|
||||
WriteLog (lsDEBUG, RippleCalc) << "pushNode: specified bad issuer.";
|
||||
|
||||
terResult = temBAD_PATH;
|
||||
}
|
||||
else if (!bAccount && !bCurrency && !bIssuer)
|
||||
{
|
||||
WriteLog (lsDEBUG, RippleCalc) << "pushNode: offer must specify at least currency or issuer.";
|
||||
|
||||
terResult = temBAD_PATH;
|
||||
}
|
||||
else if (bAccount)
|
||||
{
|
||||
// Account link
|
||||
|
||||
pnCur.uAccountID = uAccountID;
|
||||
pnCur.uIssuerID = bIssuer
|
||||
? uIssuerID
|
||||
: !!pnCur.uCurrencyID
|
||||
? uAccountID
|
||||
: ACCOUNT_XRP;
|
||||
pnCur.saRevRedeem = STAmount (pnCur.uCurrencyID, uAccountID);
|
||||
pnCur.saRevIssue = STAmount (pnCur.uCurrencyID, uAccountID);
|
||||
pnCur.saRevDeliver = STAmount (pnCur.uCurrencyID, pnCur.uIssuerID);
|
||||
pnCur.saFwdDeliver = pnCur.saRevDeliver;
|
||||
|
||||
if (bFirst)
|
||||
{
|
||||
// The first node is always correct as is.
|
||||
|
||||
nothing ();
|
||||
}
|
||||
else if (!uAccountID)
|
||||
{
|
||||
WriteLog (lsDEBUG, RippleCalc) << "pushNode: specified bad account.";
|
||||
|
||||
terResult = temBAD_PATH;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add required intermediate nodes to deliver to current account.
|
||||
WriteLog (lsTRACE, RippleCalc) << "pushNode: imply for account.";
|
||||
|
||||
terResult = pushImply (
|
||||
pnCur.uAccountID, // Current account.
|
||||
pnCur.uCurrencyID, // Wanted currency.
|
||||
!!pnCur.uCurrencyID ? uAccountID : ACCOUNT_XRP); // Account as wanted issuer.
|
||||
|
||||
// Note: pnPrv may no longer be the immediately previous node.
|
||||
}
|
||||
|
||||
if (tesSUCCESS == terResult && !vpnNodes.empty ())
|
||||
{
|
||||
const Node& pnBck = vpnNodes.back ();
|
||||
bool bBckAccount = isSetBit (pnBck.uFlags, STPathElement::typeAccount);
|
||||
|
||||
if (bBckAccount)
|
||||
{
|
||||
SLE::pointer sleRippleState = lesEntries.entryCache (ltRIPPLE_STATE, Ledger::getRippleStateIndex (pnBck.uAccountID, pnCur.uAccountID, pnPrv.uCurrencyID));
|
||||
|
||||
if (!sleRippleState)
|
||||
{
|
||||
WriteLog (lsTRACE, RippleCalc) << "pushNode: No credit line between "
|
||||
<< RippleAddress::createHumanAccountID (pnBck.uAccountID)
|
||||
<< " and "
|
||||
<< RippleAddress::createHumanAccountID (pnCur.uAccountID)
|
||||
<< " for "
|
||||
<< STAmount::createHumanCurrency (pnCur.uCurrencyID)
|
||||
<< "." ;
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc) << getJson ();
|
||||
|
||||
terResult = terNO_LINE;
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog (lsTRACE, RippleCalc) << "pushNode: Credit line found between "
|
||||
<< RippleAddress::createHumanAccountID (pnBck.uAccountID)
|
||||
<< " and "
|
||||
<< RippleAddress::createHumanAccountID (pnCur.uAccountID)
|
||||
<< " for "
|
||||
<< STAmount::createHumanCurrency (pnCur.uCurrencyID)
|
||||
<< "." ;
|
||||
|
||||
SLE::pointer sleBck = lesEntries.entryCache (ltACCOUNT_ROOT, Ledger::getAccountRootIndex (pnBck.uAccountID));
|
||||
bool bHigh = pnBck.uAccountID > pnCur.uAccountID;
|
||||
|
||||
if (!sleBck)
|
||||
{
|
||||
WriteLog (lsWARNING, RippleCalc) << "pushNode: delay: can't receive IOUs from non-existent issuer: " << RippleAddress::createHumanAccountID (pnBck.uAccountID);
|
||||
|
||||
terResult = terNO_ACCOUNT;
|
||||
}
|
||||
else if ((isSetBit (sleBck->getFieldU32 (sfFlags), lsfRequireAuth)
|
||||
&& !isSetBit (sleRippleState->getFieldU32 (sfFlags), (bHigh ? lsfHighAuth : lsfLowAuth)))
|
||||
&& sleRippleState->getFieldAmount(sfBalance).isZero()) // CHECKME
|
||||
{
|
||||
WriteLog (lsWARNING, RippleCalc) << "pushNode: delay: can't receive IOUs from issuer without auth.";
|
||||
|
||||
terResult = terNO_AUTH;
|
||||
}
|
||||
|
||||
if (tesSUCCESS == terResult)
|
||||
{
|
||||
STAmount saOwed = lesEntries.rippleOwed (pnCur.uAccountID, pnBck.uAccountID, pnCur.uCurrencyID);
|
||||
STAmount saLimit;
|
||||
|
||||
if (!saOwed.isPositive ()
|
||||
&& -saOwed >= (saLimit = lesEntries.rippleLimit (pnCur.uAccountID, pnBck.uAccountID, pnCur.uCurrencyID)))
|
||||
{
|
||||
WriteLog (lsWARNING, RippleCalc) << boost::str (boost::format ("pushNode: dry: saOwed=%s saLimit=%s")
|
||||
% saOwed
|
||||
% saLimit);
|
||||
|
||||
terResult = tecPATH_DRY;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tesSUCCESS == terResult)
|
||||
{
|
||||
vpnNodes.push_back (pnCur);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Offer link
|
||||
// Offers bridge a change in currency & issuer or just a change in issuer.
|
||||
pnCur.uIssuerID = bIssuer
|
||||
? uIssuerID
|
||||
: !!pnCur.uCurrencyID
|
||||
? !!pnPrv.uIssuerID
|
||||
? pnPrv.uIssuerID // Default to previous issuer
|
||||
: pnPrv.uAccountID // Or previous account if no previous issuer.
|
||||
: ACCOUNT_XRP;
|
||||
pnCur.saRateMax = saZero;
|
||||
pnCur.saRevDeliver = STAmount (pnCur.uCurrencyID, pnCur.uIssuerID);
|
||||
pnCur.saFwdDeliver = pnCur.saRevDeliver;
|
||||
|
||||
if (!!pnCur.uCurrencyID != !!pnCur.uIssuerID)
|
||||
{
|
||||
WriteLog (lsDEBUG, RippleCalc) << "pushNode: currency is inconsistent with issuer.";
|
||||
|
||||
terResult = temBAD_PATH;
|
||||
}
|
||||
else if (!!pnPrv.uAccountID)
|
||||
{
|
||||
// Previous is an account.
|
||||
WriteLog (lsTRACE, RippleCalc) << "pushNode: imply for offer.";
|
||||
|
||||
// Insert intermediary issuer account if needed.
|
||||
terResult = pushImply (
|
||||
ACCOUNT_XRP, // Rippling, but offers don't have an account.
|
||||
pnPrv.uCurrencyID,
|
||||
pnPrv.uIssuerID);
|
||||
}
|
||||
|
||||
if (tesSUCCESS == terResult)
|
||||
{
|
||||
vpnNodes.push_back (pnCur);
|
||||
}
|
||||
}
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc) << boost::str (boost::format ("pushNode< : %s") % transToken (terResult));
|
||||
|
||||
return terResult;
|
||||
}
|
||||
|
||||
// Set to an expanded path.
|
||||
//
|
||||
// terStatus = tesSUCCESS, temBAD_PATH, terNO_LINE, terNO_ACCOUNT, terNO_AUTH, or temBAD_PATH_LOOP
|
||||
void PathState::setExpanded (
|
||||
const LedgerEntrySet& lesSource,
|
||||
const STPath& spSourcePath,
|
||||
const uint160& uReceiverID,
|
||||
const uint160& uSenderID
|
||||
)
|
||||
{
|
||||
uQuality = 1; // Mark path as active.
|
||||
|
||||
const uint160 uMaxCurrencyID = saInReq.getCurrency ();
|
||||
const uint160 uMaxIssuerID = saInReq.getIssuer ();
|
||||
|
||||
const uint160 uOutCurrencyID = saOutReq.getCurrency ();
|
||||
const uint160 uOutIssuerID = saOutReq.getIssuer ();
|
||||
const uint160 uSenderIssuerID = !!uMaxCurrencyID ? uSenderID : ACCOUNT_XRP; // Sender is always issuer for non-XRP.
|
||||
|
||||
WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("setExpanded> %s") % spSourcePath.getJson (0));
|
||||
|
||||
lesEntries = lesSource.duplicate ();
|
||||
|
||||
terStatus = tesSUCCESS;
|
||||
|
||||
// XRP with issuer is malformed.
|
||||
if ((!uMaxCurrencyID && !!uMaxIssuerID) || (!uOutCurrencyID && !!uOutIssuerID))
|
||||
terStatus = temBAD_PATH;
|
||||
|
||||
// Push sending node.
|
||||
// For non-XRP, issuer is always sending account.
|
||||
// - Trying to expand, not-compact.
|
||||
// - Every issuer will be traversed through.
|
||||
if (tesSUCCESS == terStatus)
|
||||
terStatus = pushNode (
|
||||
!!uMaxCurrencyID
|
||||
? STPathElement::typeAccount | STPathElement::typeCurrency | STPathElement::typeIssuer
|
||||
: STPathElement::typeAccount | STPathElement::typeCurrency,
|
||||
uSenderID,
|
||||
uMaxCurrencyID, // Max specifes the currency.
|
||||
uSenderIssuerID);
|
||||
|
||||
WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("setExpanded: pushed: account=%s currency=%s issuer=%s")
|
||||
% RippleAddress::createHumanAccountID (uSenderID)
|
||||
% STAmount::createHumanCurrency (uMaxCurrencyID)
|
||||
% RippleAddress::createHumanAccountID (uSenderIssuerID));
|
||||
|
||||
if (tesSUCCESS == terStatus
|
||||
&& uMaxIssuerID != uSenderIssuerID) // Issuer was not same as sender.
|
||||
{
|
||||
// May have an implied account node.
|
||||
// - If it was XRP, then issuers would have matched.
|
||||
|
||||
// Figure out next node properties for implied node.
|
||||
const uint160 uNxtCurrencyID = spSourcePath.size ()
|
||||
? spSourcePath.getElement (0).getCurrency () // Use next node.
|
||||
: uOutCurrencyID; // Use send.
|
||||
const uint160 uNxtAccountID = spSourcePath.size ()
|
||||
? spSourcePath.getElement (0).getAccountID ()
|
||||
: !!uOutCurrencyID
|
||||
? uOutIssuerID == uReceiverID
|
||||
? uReceiverID
|
||||
: uOutIssuerID // Use implied node.
|
||||
: ACCOUNT_XRP;
|
||||
|
||||
WriteLog (lsDEBUG, RippleCalc) << 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.
|
||||
if (!uNxtCurrencyID // Next is XRP, offer next. Must go through issuer.
|
||||
|| uMaxCurrencyID != uNxtCurrencyID // Next is different currency, offer next...
|
||||
|| uMaxIssuerID != uNxtAccountID) // Next is not implied issuer
|
||||
{
|
||||
WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("setExpanded: sender implied: account=%s currency=%s issuer=%s")
|
||||
% RippleAddress::createHumanAccountID (uMaxIssuerID)
|
||||
% STAmount::createHumanCurrency (uMaxCurrencyID)
|
||||
% RippleAddress::createHumanAccountID (uMaxIssuerID));
|
||||
// Add account implied by SendMax.
|
||||
terStatus = pushNode (
|
||||
!!uMaxCurrencyID
|
||||
? STPathElement::typeAccount | STPathElement::typeCurrency | STPathElement::typeIssuer
|
||||
: STPathElement::typeAccount | STPathElement::typeCurrency,
|
||||
uMaxIssuerID,
|
||||
uMaxCurrencyID,
|
||||
uMaxIssuerID);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_FOREACH (const STPathElement & speElement, spSourcePath)
|
||||
{
|
||||
if (tesSUCCESS == terStatus)
|
||||
{
|
||||
WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("setExpanded: element in path:"));
|
||||
terStatus = pushNode (speElement.getNodeType (), speElement.getAccountID (), speElement.getCurrency (), speElement.getIssuerID ());
|
||||
}
|
||||
}
|
||||
|
||||
const Node& pnPrv = vpnNodes.back ();
|
||||
|
||||
if (tesSUCCESS == terStatus
|
||||
&& !!uOutCurrencyID // Next is not XRP
|
||||
&& uOutIssuerID != uReceiverID // Out issuer is not receiver
|
||||
&& (pnPrv.uCurrencyID != uOutCurrencyID // Previous will be an offer.
|
||||
|| pnPrv.uAccountID != uOutIssuerID)) // Need the implied issuer.
|
||||
{
|
||||
// Add implied account.
|
||||
WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("setExpanded: receiver implied: account=%s currency=%s issuer=%s")
|
||||
% RippleAddress::createHumanAccountID (uOutIssuerID)
|
||||
% STAmount::createHumanCurrency (uOutCurrencyID)
|
||||
% RippleAddress::createHumanAccountID (uOutIssuerID));
|
||||
terStatus = pushNode (
|
||||
!!uOutCurrencyID
|
||||
? STPathElement::typeAccount | STPathElement::typeCurrency | STPathElement::typeIssuer
|
||||
: STPathElement::typeAccount | STPathElement::typeCurrency,
|
||||
uOutIssuerID,
|
||||
uOutCurrencyID,
|
||||
uOutIssuerID);
|
||||
}
|
||||
|
||||
if (tesSUCCESS == terStatus)
|
||||
{
|
||||
// Create receiver node.
|
||||
// Last node is always an account.
|
||||
|
||||
terStatus = pushNode (
|
||||
!!uOutCurrencyID
|
||||
? STPathElement::typeAccount | STPathElement::typeCurrency | STPathElement::typeIssuer
|
||||
: STPathElement::typeAccount | STPathElement::typeCurrency,
|
||||
uReceiverID, // Receive to output
|
||||
uOutCurrencyID, // Desired currency
|
||||
uReceiverID);
|
||||
}
|
||||
|
||||
if (tesSUCCESS == terStatus)
|
||||
{
|
||||
// Look for first mention of source in nodes and detect loops.
|
||||
// Note: The output is not allowed to be a source.
|
||||
|
||||
const unsigned int uNodes = vpnNodes.size ();
|
||||
|
||||
for (unsigned int uNode = 0; tesSUCCESS == terStatus && uNode != uNodes; ++uNode)
|
||||
{
|
||||
const Node& pnCur = vpnNodes[uNode];
|
||||
|
||||
if (!umForward.insert (std::make_pair (boost::make_tuple (pnCur.uAccountID, pnCur.uCurrencyID, pnCur.uIssuerID), uNode)).second)
|
||||
{
|
||||
// Failed to insert. Have a loop.
|
||||
WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("setExpanded: loop detected: %s")
|
||||
% getJson ());
|
||||
|
||||
terStatus = temBAD_PATH_LOOP;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("setExpanded: in=%s/%s out=%s/%s %s")
|
||||
% STAmount::createHumanCurrency (uMaxCurrencyID)
|
||||
% RippleAddress::createHumanAccountID (uMaxIssuerID)
|
||||
% STAmount::createHumanCurrency (uOutCurrencyID)
|
||||
% RippleAddress::createHumanAccountID (uOutIssuerID)
|
||||
% getJson ());
|
||||
}
|
||||
|
||||
// Set to a canonical path.
|
||||
// - Remove extra elements
|
||||
// - Assumes path is expanded.
|
||||
//
|
||||
// We do canonicalization to:
|
||||
// - Prevent waste in the ledger.
|
||||
// - Allow longer paths to be specified than would otherwise be allowed.
|
||||
//
|
||||
// Optimization theory:
|
||||
// - Can omit elements that the expansion routine derives.
|
||||
// - Can pack some elements into other elements.
|
||||
//
|
||||
// Rules:
|
||||
// - SendMax if not specified, defaults currency to send and if not sending XRP defaults issuer to sender.
|
||||
// - All paths start with the sender account.
|
||||
// - Currency and issuer is from SendMax.
|
||||
// - All paths end with the destination account.
|
||||
//
|
||||
// Optimization:
|
||||
// - An XRP output implies an offer node or destination node is next.
|
||||
// - A change in currency implies an offer node.
|
||||
// - A change in issuer...
|
||||
void PathState::setCanonical (
|
||||
const PathState& psExpanded
|
||||
)
|
||||
{
|
||||
assert (false);
|
||||
saInAct = psExpanded.saInAct;
|
||||
saOutAct = psExpanded.saOutAct;
|
||||
|
||||
const uint160 uMaxCurrencyID = saInAct.getCurrency ();
|
||||
const uint160 uMaxIssuerID = saInAct.getIssuer ();
|
||||
|
||||
const uint160 uOutCurrencyID = saOutAct.getCurrency ();
|
||||
const uint160 uOutIssuerID = saOutAct.getIssuer ();
|
||||
|
||||
unsigned int uNode = 0;
|
||||
|
||||
unsigned int uEnd = psExpanded.vpnNodes.size (); // The node, indexed by 0, not to include.
|
||||
|
||||
uint160 uDstAccountID = psExpanded.vpnNodes[uEnd].uAccountID; // FIXME: This can't be right
|
||||
|
||||
uint160 uAccountID = psExpanded.vpnNodes[0].uAccountID;
|
||||
uint160 uCurrencyID = uMaxCurrencyID;
|
||||
uint160 uIssuerID = uMaxIssuerID;
|
||||
|
||||
// Node 0 is a composite of the sending account and saInAct.
|
||||
++uNode; // skip node 0
|
||||
|
||||
// Last node is implied: Always skip last node
|
||||
--uEnd; // skip last node
|
||||
|
||||
// saInAct
|
||||
// - currency is always the same as vpnNodes[0].
|
||||
#if 1
|
||||
|
||||
if (uNode != uEnd && uMaxIssuerID != uAccountID)
|
||||
{
|
||||
// saInAct issuer is not the sender. This forces an implied node.
|
||||
// WriteLog (lsDEBUG, RippleCalc) << boost::str(boost::format("setCanonical: in diff: uNode=%d uEnd=%d") % uNode % uEnd);
|
||||
|
||||
// skip node 1
|
||||
|
||||
uIssuerID = psExpanded.vpnNodes[uNode].uIssuerID;
|
||||
|
||||
++uNode;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
if (uNode != uEnd)
|
||||
{
|
||||
// Have another node
|
||||
bool bKeep = false;
|
||||
|
||||
if (uMaxIssuerID != uAccountID)
|
||||
{
|
||||
}
|
||||
|
||||
if (uMaxCurrencyID) // Not sending XRP.
|
||||
{
|
||||
// Node 1 must be an account.
|
||||
|
||||
if (uMaxIssuerID != uAccountID)
|
||||
{
|
||||
// Node 1 is required to specify issuer.
|
||||
|
||||
bKeep = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Node 1 must be an account
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Node 1 must be an order book.
|
||||
|
||||
bKeep = true;
|
||||
}
|
||||
|
||||
if (bKeep)
|
||||
{
|
||||
uCurrencyID = psExpanded.vpnNodes[uNode].uCurrencyID;
|
||||
uIssuerID = psExpanded.vpnNodes[uNode].uIssuerID;
|
||||
++uNode; // Keep it.
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
if (uNode != uEnd && !!uOutCurrencyID && uOutIssuerID != uDstAccountID)
|
||||
{
|
||||
// WriteLog (lsDEBUG, RippleCalc) << boost::str(boost::format("setCanonical: out diff: uNode=%d uEnd=%d") % uNode % uEnd);
|
||||
// The next to last node is saOutAct if an issuer different from receiver is supplied.
|
||||
// The next to last node can be implied.
|
||||
|
||||
--uEnd;
|
||||
}
|
||||
|
||||
const Node& pnEnd = psExpanded.vpnNodes[uEnd];
|
||||
|
||||
if (uNode != uEnd
|
||||
&& !pnEnd.uAccountID && pnEnd.uCurrencyID == uOutCurrencyID && pnEnd.uIssuerID == uOutIssuerID)
|
||||
{
|
||||
// The current end node is an offer converting to saOutAct's currency and issuer and can be implied.
|
||||
// WriteLog (lsDEBUG, RippleCalc) << boost::str(boost::format("setCanonical: out offer: uNode=%d uEnd=%d") % uNode % uEnd);
|
||||
|
||||
--uEnd;
|
||||
}
|
||||
|
||||
// Do not include uEnd.
|
||||
for (; uNode != uEnd; ++uNode)
|
||||
{
|
||||
// WriteLog (lsDEBUG, RippleCalc) << boost::str(boost::format("setCanonical: loop: uNode=%d uEnd=%d") % uNode % uEnd);
|
||||
const Node& pnPrv = psExpanded.vpnNodes[uNode - 1];
|
||||
const Node& pnCur = psExpanded.vpnNodes[uNode];
|
||||
const Node& pnNxt = psExpanded.vpnNodes[uNode + 1];
|
||||
|
||||
const bool bCurAccount = isSetBit (pnCur.uFlags, STPathElement::typeAccount);
|
||||
|
||||
bool bSkip = false;
|
||||
|
||||
if (bCurAccount)
|
||||
{
|
||||
// Currently at an account.
|
||||
|
||||
// Output is non-XRP and issuer is account.
|
||||
if (!!pnCur.uCurrencyID && pnCur.uIssuerID == pnCur.uAccountID)
|
||||
{
|
||||
// Account issues itself.
|
||||
// XXX Not good enough. Previous account must mention it.
|
||||
|
||||
bSkip = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Currently at an offer.
|
||||
const bool bPrvAccount = isSetBit (pnPrv.uFlags, STPathElement::typeAccount);
|
||||
const bool bNxtAccount = isSetBit (pnNxt.uFlags, STPathElement::typeAccount);
|
||||
|
||||
if (bPrvAccount && bNxtAccount // Offer surrounded by accounts.
|
||||
&& pnPrv.uCurrencyID != pnNxt.uCurrencyID)
|
||||
{
|
||||
// Offer can be implied by currency change.
|
||||
// XXX What about issuer?
|
||||
|
||||
bSkip = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bSkip)
|
||||
{
|
||||
// Copy node
|
||||
Node pnNew;
|
||||
|
||||
bool bSetAccount = bCurAccount;
|
||||
bool bSetCurrency = uCurrencyID != pnCur.uCurrencyID;
|
||||
// XXX What if we need the next account because we want to skip it?
|
||||
bool bSetIssuer = !uCurrencyID && uIssuerID != pnCur.uIssuerID;
|
||||
|
||||
pnNew.uFlags = (bSetAccount ? STPathElement::typeAccount : 0)
|
||||
| (bSetCurrency ? STPathElement::typeCurrency : 0)
|
||||
| (bSetIssuer ? STPathElement::typeIssuer : 0);
|
||||
|
||||
if (bSetAccount)
|
||||
pnNew.uAccountID = pnCur.uAccountID;
|
||||
|
||||
if (bSetCurrency)
|
||||
{
|
||||
pnNew.uCurrencyID = pnCur.uCurrencyID;
|
||||
uCurrencyID = pnNew.uCurrencyID;
|
||||
}
|
||||
|
||||
if (bSetIssuer)
|
||||
pnNew.uIssuerID = pnCur.uIssuerID;
|
||||
|
||||
// XXX ^^^ What about setting uIssuerID?
|
||||
|
||||
if (bSetCurrency && !uCurrencyID)
|
||||
uIssuerID.zero ();
|
||||
|
||||
vpnNodes.push_back (pnNew);
|
||||
}
|
||||
}
|
||||
|
||||
WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("setCanonical: in=%s/%s out=%s/%s %s")
|
||||
% STAmount::createHumanCurrency (uMaxCurrencyID)
|
||||
% RippleAddress::createHumanAccountID (uMaxIssuerID)
|
||||
% STAmount::createHumanCurrency (uOutCurrencyID)
|
||||
% RippleAddress::createHumanAccountID (uOutIssuerID)
|
||||
% getJson ());
|
||||
}
|
||||
|
||||
// This is for debugging not end users. Output names can be changed without warning.
|
||||
Json::Value PathState::getJson () const
|
||||
{
|
||||
Json::Value jvPathState (Json::objectValue);
|
||||
Json::Value jvNodes (Json::arrayValue);
|
||||
|
||||
BOOST_FOREACH (const Node & pnNode, vpnNodes)
|
||||
{
|
||||
jvNodes.append (pnNode.getJson ());
|
||||
}
|
||||
|
||||
jvPathState["status"] = terStatus;
|
||||
jvPathState["index"] = mIndex;
|
||||
jvPathState["nodes"] = jvNodes;
|
||||
|
||||
if (saInReq)
|
||||
jvPathState["in_req"] = saInReq.getJson (0);
|
||||
|
||||
if (saInAct)
|
||||
jvPathState["in_act"] = saInAct.getJson (0);
|
||||
|
||||
if (saInPass)
|
||||
jvPathState["in_pass"] = saInPass.getJson (0);
|
||||
|
||||
if (saOutReq)
|
||||
jvPathState["out_req"] = saOutReq.getJson (0);
|
||||
|
||||
if (saOutAct)
|
||||
jvPathState["out_act"] = saOutAct.getJson (0);
|
||||
|
||||
if (saOutPass)
|
||||
jvPathState["out_pass"] = saOutPass.getJson (0);
|
||||
|
||||
if (uQuality)
|
||||
jvPathState["uQuality"] = boost::str (boost::format ("%d") % uQuality);
|
||||
|
||||
return jvPathState;
|
||||
}
|
||||
|
||||
@@ -1,164 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_PATHSTATE_H
|
||||
#define RIPPLE_PATHSTATE_H
|
||||
|
||||
// account id, currency id, issuer id :: node
|
||||
typedef boost::tuple <uint160, uint160, uint160> aciSource;
|
||||
typedef boost::unordered_map <aciSource, unsigned int> curIssuerNode; // Map of currency, issuer to node index.
|
||||
typedef boost::unordered_map <aciSource, unsigned int>::const_iterator curIssuerNodeConstIterator;
|
||||
|
||||
extern std::size_t hash_value (const aciSource& asValue);
|
||||
|
||||
// Holds a path state under incremental application.
|
||||
class PathState
|
||||
{
|
||||
public:
|
||||
class Node
|
||||
{
|
||||
public:
|
||||
bool operator == (Node const& pnOther) const;
|
||||
|
||||
Json::Value getJson () const;
|
||||
|
||||
public:
|
||||
uint16 uFlags; // --> From path.
|
||||
|
||||
uint160 uAccountID; // --> Accounts: Recieving/sending account.
|
||||
uint160 uCurrencyID; // --> Accounts: Receive and send, Offers: send.
|
||||
// --- For offer's next has currency out.
|
||||
uint160 uIssuerID; // --> Currency's issuer
|
||||
|
||||
STAmount saTransferRate; // Transfer rate for uIssuerID.
|
||||
|
||||
// Computed by Reverse.
|
||||
STAmount saRevRedeem; // <-- Amount to redeem to next.
|
||||
STAmount saRevIssue; // <-- Amount to issue to next limited by credit and outstanding IOUs.
|
||||
// Issue isn't used by offers.
|
||||
STAmount saRevDeliver; // <-- Amount to deliver to next regardless of fee.
|
||||
|
||||
// Computed by forward.
|
||||
STAmount saFwdRedeem; // <-- Amount node will redeem to next.
|
||||
STAmount saFwdIssue; // <-- Amount node will issue to next.
|
||||
// Issue isn't used by offers.
|
||||
STAmount saFwdDeliver; // <-- Amount to deliver to next regardless of fee.
|
||||
|
||||
// For offers:
|
||||
|
||||
STAmount saRateMax;
|
||||
|
||||
// Directory
|
||||
uint256 uDirectTip; // Current directory.
|
||||
uint256 uDirectEnd; // Next order book.
|
||||
bool bDirectAdvance; // Need to advance directory.
|
||||
SLE::pointer sleDirectDir;
|
||||
STAmount saOfrRate; // For correct ratio.
|
||||
|
||||
// PaymentNode
|
||||
bool bEntryAdvance; // Need to advance entry.
|
||||
unsigned int uEntry;
|
||||
uint256 uOfferIndex;
|
||||
SLE::pointer sleOffer;
|
||||
uint160 uOfrOwnerID;
|
||||
bool bFundsDirty; // Need to refresh saOfferFunds, saTakerPays, & saTakerGets.
|
||||
STAmount saOfferFunds;
|
||||
STAmount saTakerPays;
|
||||
STAmount saTakerGets;
|
||||
|
||||
};
|
||||
public:
|
||||
typedef boost::shared_ptr<PathState> pointer;
|
||||
typedef const boost::shared_ptr<PathState>& ref;
|
||||
|
||||
public:
|
||||
PathState* setIndex (const int iIndex)
|
||||
{
|
||||
mIndex = iIndex;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
int getIndex ()
|
||||
{
|
||||
return mIndex;
|
||||
};
|
||||
|
||||
PathState (
|
||||
const STAmount& saSend,
|
||||
const STAmount& saSendMax)
|
||||
: saInReq (saSendMax)
|
||||
, saOutReq (saSend)
|
||||
{
|
||||
}
|
||||
|
||||
PathState (const PathState& psSrc,
|
||||
bool bUnused)
|
||||
: saInReq (psSrc.saInReq)
|
||||
, saOutReq (psSrc.saOutReq)
|
||||
{
|
||||
}
|
||||
|
||||
void setExpanded (
|
||||
const LedgerEntrySet& lesSource,
|
||||
const STPath& spSourcePath,
|
||||
const uint160& uReceiverID,
|
||||
const uint160& uSenderID
|
||||
);
|
||||
|
||||
void setCanonical (
|
||||
const PathState& psExpanded
|
||||
);
|
||||
|
||||
Json::Value getJson () const;
|
||||
|
||||
#if 0
|
||||
static PathState::pointer createCanonical (
|
||||
PathState& ref pspExpanded
|
||||
)
|
||||
{
|
||||
PathState::pointer pspNew = boost::make_shared<PathState> (pspExpanded->saOutAct, pspExpanded->saInAct);
|
||||
|
||||
pspNew->setCanonical (pspExpanded);
|
||||
|
||||
return pspNew;
|
||||
}
|
||||
#endif
|
||||
static bool lessPriority (PathState& lhs, PathState& rhs);
|
||||
|
||||
public:
|
||||
TER terStatus;
|
||||
std::vector<Node> vpnNodes;
|
||||
|
||||
// When processing, don't want to complicate directory walking with deletion.
|
||||
std::vector<uint256> vUnfundedBecame; // Offers that became unfunded or were completely consumed.
|
||||
|
||||
// First time scanning foward, as part of path contruction, a funding source was mentioned for accounts. Source may only be
|
||||
// used there.
|
||||
curIssuerNode umForward; // Map of currency, issuer to node index.
|
||||
|
||||
// First time working in reverse a funding source was used.
|
||||
// Source may only be used there if not mentioned by an account.
|
||||
curIssuerNode umReverse; // Map of currency, issuer to node index.
|
||||
|
||||
LedgerEntrySet lesEntries;
|
||||
|
||||
int mIndex; // Index/rank amoung siblings.
|
||||
uint64 uQuality; // 0 = no quality/liquity left.
|
||||
const STAmount& saInReq; // --> Max amount to spend by sender.
|
||||
STAmount saInAct; // --> Amount spent by sender so far.
|
||||
STAmount saInPass; // <-- Amount spent by sender.
|
||||
const STAmount& saOutReq; // --> Amount to send.
|
||||
STAmount saOutAct; // --> Amount actually sent so far.
|
||||
STAmount saOutPass; // <-- Amount actually sent.
|
||||
bool bConsumed; // If true, use consumes full liquidity. False, may or may not.
|
||||
|
||||
private:
|
||||
TER pushNode (const int iType, const uint160& uAccountID, const uint160& uCurrencyID, const uint160& uIssuerID);
|
||||
TER pushImply (const uint160& uAccountID, const uint160& uCurrencyID, const uint160& uIssuerID);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,845 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
SETUP_LOG (Pathfinder)
|
||||
|
||||
/*
|
||||
we just need to find a succession of the highest quality paths there until we find enough width
|
||||
|
||||
Don't do branching within each path
|
||||
|
||||
We have a list of paths we are working on but how do we compare the ones that are terminating in a different currency?
|
||||
|
||||
Loops
|
||||
|
||||
TODO: what is a good way to come up with multiple paths?
|
||||
Maybe just change the sort criteria?
|
||||
first a low cost one and then a fat short one?
|
||||
|
||||
|
||||
OrderDB:
|
||||
getXRPOffers();
|
||||
|
||||
// return list of all orderbooks that want XRP
|
||||
// return list of all orderbooks that want IssuerID
|
||||
// return list of all orderbooks that want this issuerID and currencyID
|
||||
*/
|
||||
|
||||
/*
|
||||
Test sending to XRP
|
||||
Test XRP to XRP
|
||||
Test offer in middle
|
||||
Test XRP to USD
|
||||
Test USD to EUR
|
||||
*/
|
||||
|
||||
// we sort the options by:
|
||||
// cost of path
|
||||
// length of path
|
||||
// width of path
|
||||
// correct currency at the end
|
||||
|
||||
// quality, length, liquidity, index
|
||||
typedef boost::tuple<uint64, int, STAmount, unsigned int> path_LQ_t;
|
||||
|
||||
// Lower numbers have better quality. Sort higher quality first.
|
||||
static bool bQualityCmp (const path_LQ_t& a, const path_LQ_t& b)
|
||||
{
|
||||
// 1) Higher quality (lower cost) is better
|
||||
if (a.get<0> () != b.get<0> ())
|
||||
return a.get<0> () < b.get<0> ();
|
||||
|
||||
// 2) More liquidity (higher volume) is better
|
||||
if (a.get<2> () != b.get<2> ())
|
||||
return a.get<2> () > b.get<2> ();
|
||||
|
||||
// 3) Shorter paths are better
|
||||
if (a.get<1> () != b.get<1> ())
|
||||
return a.get<1> () < b.get<1> ();
|
||||
|
||||
// 4) Tie breaker
|
||||
return a.get<3> () > b.get<3> ();
|
||||
}
|
||||
|
||||
// Return true, if path is a default path with an element.
|
||||
// A path is a default path if it is implied via src, dst, send, and sendmax.
|
||||
bool Pathfinder::bDefaultPath (const STPath& spPath)
|
||||
{
|
||||
if (2 >= spPath.mPath.size ())
|
||||
{
|
||||
// Empty path is a default. Don't need to add it to return set.
|
||||
WriteLog (lsTRACE, Pathfinder) << "findPaths: empty path: direct";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!mPsDefault)
|
||||
{
|
||||
// No default path.
|
||||
// There might not be a direct credit line or there may be no implied nodes
|
||||
// in send and sendmax.
|
||||
|
||||
return false; // Didn't generate a default path. So can't match.
|
||||
}
|
||||
|
||||
PathState::pointer pspCurrent = boost::make_shared<PathState> (mDstAmount, mSrcAmount);
|
||||
|
||||
if (pspCurrent)
|
||||
{
|
||||
bool bDefault;
|
||||
LedgerEntrySet lesActive (mLedger, tapNONE);
|
||||
|
||||
WriteLog (lsTRACE, Pathfinder) << boost::str (boost::format ("bDefaultPath> mSrcAmount=%s mDstAmount=%s")
|
||||
% mSrcAmount.getFullText ()
|
||||
% mDstAmount.getFullText ());
|
||||
|
||||
// Expand the current path.
|
||||
pspCurrent->setExpanded (lesActive, spPath, mDstAccountID, mSrcAccountID);
|
||||
// XXX Need to report or act on errors returned in pspCurrent->terStatus.
|
||||
|
||||
// Determine if expanded current path is the default.
|
||||
// When path is a default (implied). Don't need to add it to return set.
|
||||
bDefault = pspCurrent->vpnNodes == mPsDefault->vpnNodes;
|
||||
|
||||
WriteLog (lsTRACE, Pathfinder) << "bDefaultPath: expanded path: " << pspCurrent->getJson ();
|
||||
WriteLog (lsTRACE, Pathfinder) << "bDefaultPath: source path: " << spPath.getJson (0);
|
||||
WriteLog (lsTRACE, Pathfinder) << "bDefaultPath: default path: " << mPsDefault->getJson ();
|
||||
|
||||
return bDefault;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
typedef std::pair<int, uint160> candidate_t;
|
||||
static bool candCmp (uint32 seq, const candidate_t& first, const candidate_t& second)
|
||||
{
|
||||
if (first.first < second.first)
|
||||
return false;
|
||||
|
||||
if (first.first > second.first)
|
||||
return true;
|
||||
|
||||
return (first.first ^ seq) < (second.first ^ seq);
|
||||
}
|
||||
|
||||
static int getEffectiveLength (const STPath& spPath)
|
||||
{
|
||||
// don't count exchanges to non-XRP currencies twice (only count the forced issuer account node)
|
||||
int length = 0;
|
||||
|
||||
for (std::vector<STPathElement>::const_iterator it = spPath.begin (); it != spPath.end (); ++it)
|
||||
{
|
||||
if (it->isAccount () || it->getCurrency ().isZero ())
|
||||
++length;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
Pathfinder::Pathfinder (RippleLineCache::ref cache,
|
||||
const RippleAddress& uSrcAccountID, const RippleAddress& uDstAccountID,
|
||||
const uint160& uSrcCurrencyID, const uint160& uSrcIssuerID, const STAmount& saDstAmount, bool& bValid)
|
||||
: mSrcAccountID (uSrcAccountID.getAccountID ()),
|
||||
mDstAccountID (uDstAccountID.getAccountID ()),
|
||||
mDstAmount (saDstAmount),
|
||||
mSrcCurrencyID (uSrcCurrencyID),
|
||||
mSrcIssuerID (uSrcIssuerID),
|
||||
mSrcAmount (uSrcCurrencyID, uSrcIssuerID, 1u, 0, true),
|
||||
mLedger (cache->getLedger ()), mRLCache (cache)
|
||||
{
|
||||
|
||||
if (((mSrcAccountID == mDstAccountID) && (mSrcCurrencyID == mDstAmount.getCurrency ())) || mDstAmount.isZero ())
|
||||
{
|
||||
// no need to send to same account with same currency, must send non-zero
|
||||
bValid = false;
|
||||
mLedger.reset ();
|
||||
return;
|
||||
}
|
||||
|
||||
bValid = true;
|
||||
|
||||
getApp().getOrderBookDB ().setup (mLedger);
|
||||
|
||||
m_loadEvent = getApp().getJobQueue ().getLoadEvent (jtPATH_FIND, "FindPath");
|
||||
|
||||
// Construct the default path for later comparison.
|
||||
|
||||
PathState::pointer psDefault = boost::make_shared<PathState> (mDstAmount, mSrcAmount);
|
||||
|
||||
if (psDefault)
|
||||
{
|
||||
// Build the default path.
|
||||
// Later, reject anything that expands to the default path as the default is sufficient.
|
||||
|
||||
LedgerEntrySet lesActive (mLedger, tapNONE);
|
||||
|
||||
WriteLog (lsTRACE, Pathfinder) << boost::str (boost::format ("Pathfinder> mSrcAmount=%s mDstAmount=%s")
|
||||
% mSrcAmount.getFullText ()
|
||||
% mDstAmount.getFullText ());
|
||||
|
||||
psDefault->setExpanded (lesActive, STPath (), mDstAccountID, mSrcAccountID);
|
||||
|
||||
if (tesSUCCESS == psDefault->terStatus)
|
||||
{
|
||||
// The default path works, remember it.
|
||||
WriteLog (lsTRACE, Pathfinder) << "Pathfinder: default path: " << psDefault->getJson ();
|
||||
|
||||
mPsDefault = psDefault;
|
||||
}
|
||||
else
|
||||
{
|
||||
// The default path doesn't work.
|
||||
WriteLog (lsTRACE, Pathfinder) << "Pathfinder: default path: NONE: " << transToken (psDefault->terStatus);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If possible, returns a single path.
|
||||
// --> iMaxSteps: Maximum nodes in paths to return.
|
||||
// --> iMaxPaths: Maximum number of paths to return.
|
||||
// <-- retPathSet: founds paths not including default paths.
|
||||
// Returns true if found paths.
|
||||
//
|
||||
// When generating a path set blindly, don't allow the empty path, it is implied by default.
|
||||
// When generating a path set for estimates, allow an empty path instead of no paths to indicate a path exists. The caller will
|
||||
// need to strip the empty path when submitting the transaction.
|
||||
//
|
||||
// Assumes rippling (not XRP to XRP)
|
||||
//
|
||||
// Leaves to the caller figuring out overall liquidity.
|
||||
// Optimization opportunity: For some simple cases, this routine has figured out the overall liquidity.
|
||||
bool Pathfinder::findPaths (const unsigned int iMaxSteps, const unsigned int iMaxPaths, STPathSet& spsDst)
|
||||
{
|
||||
bool bFound = false; // True, iff found a path.
|
||||
|
||||
WriteLog (lsTRACE, Pathfinder) << boost::str (boost::format ("findPaths> mSrcAccountID=%s mDstAccountID=%s mDstAmount=%s mSrcCurrencyID=%s mSrcIssuerID=%s")
|
||||
% RippleAddress::createHumanAccountID (mSrcAccountID)
|
||||
% RippleAddress::createHumanAccountID (mDstAccountID)
|
||||
% mDstAmount.getFullText ()
|
||||
% STAmount::createHumanCurrency (mSrcCurrencyID)
|
||||
% RippleAddress::createHumanAccountID (mSrcIssuerID)
|
||||
);
|
||||
|
||||
if (!mLedger)
|
||||
{
|
||||
WriteLog (lsDEBUG, Pathfinder) << "findPaths< no ledger";
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
LedgerEntrySet lesActive (mLedger, tapNONE);
|
||||
boost::unordered_map<uint160, AccountItems::pointer> aiMap;
|
||||
|
||||
SLE::pointer sleSrc = lesActive.entryCache (ltACCOUNT_ROOT, Ledger::getAccountRootIndex (mSrcAccountID));
|
||||
|
||||
if (!sleSrc)
|
||||
{
|
||||
WriteLog (lsDEBUG, Pathfinder) << boost::str (boost::format ("findPaths< no source"));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
SLE::pointer sleDst = lesActive.entryCache (ltACCOUNT_ROOT, Ledger::getAccountRootIndex (mDstAccountID));
|
||||
|
||||
if (!sleDst)
|
||||
{
|
||||
WriteLog (lsDEBUG, Pathfinder) << boost::str (boost::format ("findPaths< no dest"));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<STPath> vspResults;
|
||||
std::queue<STPath> qspExplore; // Path stubs to explore.
|
||||
|
||||
STPath spSeed;
|
||||
bool bForcedIssuer = !!mSrcCurrencyID && mSrcIssuerID != mSrcAccountID; // Source forced an issuer.
|
||||
|
||||
// The end is the cursor, start at the source account.
|
||||
STPathElement speEnd (mSrcAccountID,
|
||||
mSrcCurrencyID,
|
||||
!!mSrcCurrencyID
|
||||
? mSrcAccountID // Non-XRP, start with self as issuer.
|
||||
: ACCOUNT_XRP);
|
||||
|
||||
// Build a path of one element: the source.
|
||||
spSeed.addElement (speEnd);
|
||||
|
||||
if (bForcedIssuer)
|
||||
{
|
||||
// Add forced source issuer to seed, via issuer's account.
|
||||
STPathElement speIssuer (mSrcIssuerID, mSrcCurrencyID, mSrcIssuerID);
|
||||
|
||||
spSeed.addElement (speEnd);
|
||||
}
|
||||
|
||||
// Push the seed path to explore.
|
||||
qspExplore.push (spSeed);
|
||||
|
||||
while (qspExplore.size ()) // Have paths to explore?
|
||||
{
|
||||
STPath spPath = qspExplore.front ();
|
||||
|
||||
qspExplore.pop (); // Pop the first path from the queue.
|
||||
|
||||
speEnd = spPath.mPath.back (); // Get the last node from the path.
|
||||
|
||||
if (!speEnd.mCurrencyID // Tail output is XRP.
|
||||
&& !mDstAmount.getCurrency ()) // Which is dst currency.
|
||||
{
|
||||
// Done, cursor produces XRP and dest wants XRP.
|
||||
|
||||
// Remove implied source.
|
||||
spPath.mPath.erase (spPath.mPath.begin ());
|
||||
|
||||
if (bForcedIssuer)
|
||||
{
|
||||
// Remove implied source issuer.
|
||||
spPath.mPath.erase (spPath.mPath.begin ());
|
||||
}
|
||||
|
||||
if (spPath.size ())
|
||||
{
|
||||
// There is an actual path element.
|
||||
WriteLog (lsTRACE, Pathfinder) << "findPaths: adding path: " << spPath.getJson (0);
|
||||
|
||||
vspResults.push_back (spPath); // Potential result.
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog (lsWARNING, Pathfinder) << "findPaths: empty path: XRP->XRP";
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ShouldLog (lsTRACE, Pathfinder))
|
||||
{
|
||||
WriteLog (lsTRACE, Pathfinder) << 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 ()));
|
||||
|
||||
WriteLog (lsTRACE, Pathfinder) << "findPaths: finish? account: " << (speEnd.mAccountID == mDstAccountID);
|
||||
WriteLog (lsTRACE, Pathfinder) << "findPaths: finish? currency: " << (speEnd.mCurrencyID == mDstAmount.getCurrency ());
|
||||
WriteLog (lsTRACE, Pathfinder) << "findPaths: finish? issuer: "
|
||||
<< RippleAddress::createHumanAccountID (speEnd.mIssuerID)
|
||||
<< " / "
|
||||
<< RippleAddress::createHumanAccountID (mDstAmount.getIssuer ())
|
||||
<< " / "
|
||||
<< RippleAddress::createHumanAccountID (mDstAccountID);
|
||||
WriteLog (lsTRACE, Pathfinder) << "findPaths: finish? issuer is desired: " << (speEnd.mIssuerID == mDstAmount.getIssuer ());
|
||||
}
|
||||
|
||||
// YYY Allows going through self. Is this wanted?
|
||||
if (speEnd.mAccountID == mDstAccountID // Tail is destination account.
|
||||
&& speEnd.mCurrencyID == mDstAmount.getCurrency () // With correct output currency.
|
||||
&& ( speEnd.mIssuerID == mDstAccountID // Dest always accepts own issuer.
|
||||
|| mDstAmount.getIssuer () == mDstAccountID // Any issuer is good.
|
||||
|| mDstAmount.getIssuer () == speEnd.mIssuerID)) // The desired issuer.
|
||||
{
|
||||
// Done, found a path to the destination.
|
||||
// Cursor on the dest account with correct currency and issuer.
|
||||
|
||||
if (bDefaultPath (spPath))
|
||||
{
|
||||
WriteLog (lsTRACE, Pathfinder) << "findPaths: dropping: default path: " << spPath.getJson (0);
|
||||
|
||||
bFound = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Remove implied nodes.
|
||||
|
||||
spPath.mPath.erase (spPath.mPath.begin ());
|
||||
|
||||
if (bForcedIssuer)
|
||||
{
|
||||
// Remove implied source issuer.
|
||||
spPath.mPath.erase (spPath.mPath.begin ());
|
||||
}
|
||||
|
||||
spPath.mPath.erase (spPath.mPath.begin () + spPath.mPath.size () - 1);
|
||||
|
||||
vspResults.push_back (spPath); // Potential result.
|
||||
|
||||
WriteLog (lsDEBUG, Pathfinder) << "findPaths: adding path: " << spPath.getJson (0);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
bool bContinued = false; // True, if wasn't a dead end.
|
||||
|
||||
WriteLog (lsTRACE, Pathfinder) <<
|
||||
boost::str (boost::format ("findPaths: cursor: %s - %s/%s")
|
||||
% RippleAddress::createHumanAccountID (speEnd.mAccountID)
|
||||
% STAmount::createHumanCurrency (speEnd.mCurrencyID)
|
||||
% RippleAddress::createHumanAccountID (speEnd.mIssuerID));
|
||||
|
||||
int length = getEffectiveLength (spPath.mPath);
|
||||
|
||||
if (length >= iMaxSteps)
|
||||
{
|
||||
// Path is at maximum size. Don't want to add more.
|
||||
|
||||
WriteLog (lsTRACE, Pathfinder)
|
||||
<< boost::str (boost::format ("findPaths: dropping: path would exceed max steps"));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
bool isLast = (length == (iMaxSteps - 1));
|
||||
|
||||
if (!speEnd.mCurrencyID)
|
||||
{
|
||||
// Cursor is for XRP, continue with qualifying books: XRP -> non-XRP
|
||||
std::vector<OrderBook::pointer> xrpBooks;
|
||||
getApp().getOrderBookDB ().getBooksByTakerPays (ACCOUNT_XRP, CURRENCY_XRP, xrpBooks);
|
||||
BOOST_FOREACH (OrderBook::ref book, xrpBooks)
|
||||
{
|
||||
// New end is an order book with the currency and issuer.
|
||||
|
||||
if (!spPath.hasSeen (ACCOUNT_XRP, book->getCurrencyOut (), book->getIssuerOut ()) &&
|
||||
!matchesOrigin (book->getCurrencyOut (), book->getIssuerOut ()) &&
|
||||
(!isLast ||
|
||||
(book->getCurrencyOut () == mDstAmount.getCurrency () &&
|
||||
book->getIssuerOut () == mDstAccountID)))
|
||||
{
|
||||
// Not a order book already in path.
|
||||
STPath spNew (spPath);
|
||||
STPathElement speBook (ACCOUNT_XRP, book->getCurrencyOut (), book->getIssuerOut ());
|
||||
STPathElement speAccount (book->getIssuerOut (), book->getCurrencyOut (), book->getIssuerOut ());
|
||||
|
||||
spNew.mPath.push_back (speBook); // Add the order book.
|
||||
spNew.mPath.push_back (speAccount); // Add the account and currency
|
||||
|
||||
WriteLog (lsDEBUG, Pathfinder)
|
||||
<< boost::str (boost::format ("findPaths: XRP -> %s/%s")
|
||||
// % STAmount::createHumanCurrency(book->getCurrencyOut())
|
||||
// % RippleAddress::createHumanAccountID(book->getIssuerOut())
|
||||
% STAmount::createHumanCurrency (speBook.mCurrencyID)
|
||||
% RippleAddress::createHumanAccountID (speBook.mIssuerID));
|
||||
|
||||
qspExplore.push (spNew);
|
||||
|
||||
bContinued = true;
|
||||
}
|
||||
}
|
||||
|
||||
CondLog (!bContinued, lsDEBUG, Pathfinder)
|
||||
<< boost::str (boost::format ("findPaths: XRP -> dead end"));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Last element is for non-XRP, continue by adding ripple lines and order books.
|
||||
|
||||
// Create new paths for each outbound account not already in the path.
|
||||
|
||||
SLE::pointer sleEnd = lesActive.entryCache (ltACCOUNT_ROOT, Ledger::getAccountRootIndex (speEnd.mAccountID));
|
||||
|
||||
CondLog (!sleEnd, lsDEBUG, Pathfinder)
|
||||
<< boost::str (boost::format ("findPaths: tail: %s/%s : ")
|
||||
% RippleAddress::createHumanAccountID (speEnd.mAccountID)
|
||||
% RippleAddress::createHumanAccountID (speEnd.mIssuerID));
|
||||
|
||||
if (sleEnd)
|
||||
{
|
||||
// On a non-XRP account:
|
||||
// True, the cursor requires the next node to be authorized.
|
||||
bool bRequireAuth = isSetBit (sleEnd->getFieldU32 (sfFlags), lsfRequireAuth);
|
||||
bool dstCurrency = speEnd.mCurrencyID == mDstAmount.getCurrency ();
|
||||
|
||||
AccountItems& rippleLines (mRLCache->getRippleLines (speEnd.mAccountID));
|
||||
|
||||
std::vector< std::pair<int, uint160> > candidates;
|
||||
candidates.reserve (rippleLines.getItems ().size ());
|
||||
|
||||
BOOST_FOREACH (AccountItem::ref item, rippleLines.getItems ())
|
||||
{
|
||||
RippleState* rspEntry = (RippleState*) item.get ();
|
||||
const uint160& uPeerID = rspEntry->getAccountIDPeer ();
|
||||
|
||||
if (speEnd.mCurrencyID != rspEntry->getLimit ().getCurrency ())
|
||||
{
|
||||
// wrong currency
|
||||
nothing ();
|
||||
}
|
||||
else if (spPath.hasSeen (uPeerID, speEnd.mCurrencyID, uPeerID) ||
|
||||
((uPeerID == mSrcAccountID) && (uPeerID != mDstAccountID)))
|
||||
{
|
||||
// Peer is in path already. Ignore it to avoid a loop.
|
||||
WriteLog (lsTRACE, Pathfinder) <<
|
||||
boost::str (boost::format ("findPaths: SEEN: %s/%s -> %s/%s")
|
||||
% RippleAddress::createHumanAccountID (speEnd.mAccountID)
|
||||
% STAmount::createHumanCurrency (speEnd.mCurrencyID)
|
||||
% RippleAddress::createHumanAccountID (uPeerID)
|
||||
% STAmount::createHumanCurrency (speEnd.mCurrencyID));
|
||||
}
|
||||
else if (isLast && (!dstCurrency || (uPeerID != mDstAccountID)))
|
||||
{
|
||||
nothing ();
|
||||
}
|
||||
else if (!rspEntry->getBalance ().isPositive () // No IOUs to send.
|
||||
&& (!rspEntry->getLimitPeer () // Peer does not extend credit.
|
||||
|| -rspEntry->getBalance () >= rspEntry->getLimitPeer () // No credit left.
|
||||
|| (bRequireAuth && !rspEntry->getAuth ()))) // Not authorized to hold credit.
|
||||
{
|
||||
// Path has no credit left. Ignore it.
|
||||
WriteLog (lsTRACE, Pathfinder) <<
|
||||
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)
|
||||
% rspEntry->getBalance ().getFullText ()
|
||||
% rspEntry->getLimitPeer ().getFullText ()
|
||||
);
|
||||
}
|
||||
else if (dstCurrency && (uPeerID == mDstAccountID))
|
||||
{
|
||||
// never skip the destination node
|
||||
candidates.push_back (std::make_pair (1000000, uPeerID));
|
||||
}
|
||||
else
|
||||
{
|
||||
// save this candidate
|
||||
int out = getPathsOut (speEnd.mCurrencyID, uPeerID, dstCurrency, mDstAccountID);
|
||||
|
||||
if (out != 0)
|
||||
candidates.push_back (std::make_pair (out, uPeerID));
|
||||
else
|
||||
WriteLog(lsTRACE, Pathfinder) << "findPaths: " << RippleAddress::createHumanAccountID(uPeerID) << " has no paths out";
|
||||
}
|
||||
}
|
||||
|
||||
if (!candidates.empty ())
|
||||
{
|
||||
std::sort (candidates.begin (), candidates.end (),
|
||||
BIND_TYPE (candCmp, mLedger->getLedgerSeq (), P_1, P_2));
|
||||
|
||||
int count = candidates.size ();
|
||||
|
||||
if ((count > 10) && (speEnd.mAccountID != mSrcAccountID)) // try more paths from source
|
||||
count = 10;
|
||||
else if (count > 50)
|
||||
count = 50;
|
||||
|
||||
std::vector< std::pair<int, uint160> >::iterator it = candidates.begin ();
|
||||
|
||||
while (count-- != 0)
|
||||
{
|
||||
STPath spNew (spPath);
|
||||
STPathElement speNew (it->second, speEnd.mCurrencyID, it->second);
|
||||
|
||||
spNew.mPath.push_back (speNew);
|
||||
qspExplore.push (spNew);
|
||||
|
||||
bContinued = true;
|
||||
|
||||
WriteLog (lsTRACE, Pathfinder) <<
|
||||
boost::str (boost::format ("findPaths: push explore: %s/%s -> %s/%s")
|
||||
% STAmount::createHumanCurrency (speEnd.mCurrencyID)
|
||||
% RippleAddress::createHumanAccountID (speEnd.mAccountID)
|
||||
% STAmount::createHumanCurrency (speEnd.mCurrencyID)
|
||||
% RippleAddress::createHumanAccountID (it->second));
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// XXX Flip argument order to norm. (currency, issuer)
|
||||
std::vector<OrderBook::pointer> books;
|
||||
getApp().getOrderBookDB ().getBooksByTakerPays (speEnd.mIssuerID, speEnd.mCurrencyID, books);
|
||||
|
||||
BOOST_FOREACH (OrderBook::ref book, books)
|
||||
{
|
||||
if (!spPath.hasSeen (ACCOUNT_XRP, book->getCurrencyOut (), book->getIssuerOut ()) &&
|
||||
!matchesOrigin (book->getCurrencyOut (), book->getIssuerOut ()) &&
|
||||
(!isLast ||
|
||||
(book->getCurrencyOut () == mDstAmount.getCurrency () &&
|
||||
book->getIssuerOut () == mDstAccountID)))
|
||||
{
|
||||
// A book we haven't seen before. Add it.
|
||||
STPath spNew (spPath);
|
||||
STPathElement speBook (ACCOUNT_XRP, book->getCurrencyOut (), book->getIssuerOut (),
|
||||
book->getCurrencyIn () != book->getCurrencyOut ());
|
||||
|
||||
spNew.mPath.push_back (speBook); // Add the order book.
|
||||
|
||||
if (!!book->getCurrencyOut ())
|
||||
{
|
||||
// For non-XRP out, don't end on the book, add the issuing account.
|
||||
STPathElement speAccount (book->getIssuerOut (), book->getCurrencyOut (), book->getIssuerOut ());
|
||||
spNew.mPath.push_back (speAccount); // Add the account and currency
|
||||
}
|
||||
|
||||
qspExplore.push (spNew);
|
||||
|
||||
bContinued = true;
|
||||
|
||||
WriteLog (lsTRACE, Pathfinder) <<
|
||||
boost::str (boost::format ("findPaths: push book: %s/%s -> %s/%s")
|
||||
% STAmount::createHumanCurrency (speEnd.mCurrencyID)
|
||||
% RippleAddress::createHumanAccountID (speEnd.mIssuerID)
|
||||
% STAmount::createHumanCurrency (book->getCurrencyOut ())
|
||||
% RippleAddress::createHumanAccountID (book->getIssuerOut ()));
|
||||
}
|
||||
}
|
||||
|
||||
CondLog (!bContinued, lsTRACE, Pathfinder)
|
||||
<< boost::str (boost::format ("findPaths: dropping: non-XRP -> dead end"));
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int iLimit = std::min (iMaxPaths, (unsigned int) vspResults.size ());
|
||||
|
||||
// Only filter, sort, and limit if have non-default paths.
|
||||
if (iLimit)
|
||||
{
|
||||
std::vector<path_LQ_t> vMap;
|
||||
|
||||
// Build map of quality to entry.
|
||||
for (int i = vspResults.size (); i--;)
|
||||
{
|
||||
STAmount saMaxAmountAct;
|
||||
STAmount saDstAmountAct;
|
||||
std::vector<PathState::pointer> vpsExpanded;
|
||||
STPathSet spsPaths;
|
||||
STPath& spCurrent = vspResults[i];
|
||||
|
||||
spsPaths.addPath (spCurrent); // Just checking the current path.
|
||||
|
||||
TER terResult;
|
||||
|
||||
try
|
||||
{
|
||||
LedgerEntrySet lesSandbox (lesActive.duplicate ());
|
||||
|
||||
terResult = RippleCalc::rippleCalc (
|
||||
lesSandbox,
|
||||
saMaxAmountAct,
|
||||
saDstAmountAct,
|
||||
vpsExpanded,
|
||||
mSrcAmount, // --> amount to send max.
|
||||
mDstAmount, // --> amount to deliver.
|
||||
mDstAccountID,
|
||||
mSrcAccountID,
|
||||
spsPaths,
|
||||
true, // --> bPartialPayment: Allow, it might contribute.
|
||||
false, // --> bLimitQuality: Assume normal transaction.
|
||||
true, // --> bNoRippleDirect: Providing the only path.
|
||||
true); // --> bStandAlone: Don't need to delete unfundeds.
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
WriteLog (lsINFO, Pathfinder) << "findPaths: Caught throw: " << e.what ();
|
||||
|
||||
terResult = tefEXCEPTION;
|
||||
}
|
||||
|
||||
if (tesSUCCESS == terResult)
|
||||
{
|
||||
uint64 uQuality = STAmount::getRate (saDstAmountAct, saMaxAmountAct);
|
||||
|
||||
WriteLog (lsDEBUG, Pathfinder)
|
||||
<< boost::str (boost::format ("findPaths: quality: %d: %s")
|
||||
% uQuality
|
||||
% spCurrent.getJson (0));
|
||||
|
||||
vMap.push_back (path_LQ_t (uQuality, spCurrent.mPath.size (), saDstAmountAct, i));
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog (lsDEBUG, Pathfinder)
|
||||
<< boost::str (boost::format ("findPaths: dropping: %s: %s")
|
||||
% transToken (terResult)
|
||||
% spCurrent.getJson (0));
|
||||
}
|
||||
}
|
||||
|
||||
if (vMap.size ())
|
||||
{
|
||||
std::sort (vMap.begin (), vMap.end (), bQualityCmp); // Lower is better and should be first.
|
||||
|
||||
STAmount remaining = mDstAmount;
|
||||
|
||||
if (bFound)
|
||||
{
|
||||
// must subtract liquidity in default path from remaining amount
|
||||
try
|
||||
{
|
||||
STAmount saMaxAmountAct, saDstAmountAct;
|
||||
std::vector<PathState::pointer> vpsExpanded;
|
||||
LedgerEntrySet lesSandbox (lesActive.duplicate ());
|
||||
|
||||
TER result = RippleCalc::rippleCalc (
|
||||
lesSandbox,
|
||||
saMaxAmountAct,
|
||||
saDstAmountAct,
|
||||
vpsExpanded,
|
||||
mSrcAmount,
|
||||
mDstAmount,
|
||||
mDstAccountID,
|
||||
mSrcAccountID,
|
||||
STPathSet (),
|
||||
true, // allow partial payment
|
||||
false,
|
||||
false, // don't suppress default paths, that's the point
|
||||
true);
|
||||
|
||||
if (tesSUCCESS == result)
|
||||
{
|
||||
WriteLog (lsDEBUG, Pathfinder) << "Default path contributes: " << saDstAmountAct;
|
||||
remaining -= saDstAmountAct;
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog (lsDEBUG, Pathfinder) << "Default path fails: " << transToken (result);
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
WriteLog (lsDEBUG, Pathfinder) << "Default path causes exception";
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0, iPathsLeft = iMaxPaths; (iPathsLeft > 0) && (i < vMap.size ()); ++i)
|
||||
{
|
||||
path_LQ_t& lqt = vMap[i];
|
||||
|
||||
if ((iPathsLeft != 1) || (lqt.get<2> () >= remaining))
|
||||
{
|
||||
// last path must fill
|
||||
--iPathsLeft;
|
||||
remaining -= lqt.get<2> ();
|
||||
spsDst.addPath (vspResults[lqt.get<3> ()]);
|
||||
}
|
||||
else
|
||||
WriteLog (lsDEBUG, Pathfinder) << "Skipping a non-filling path: " << vspResults[lqt.get<3> ()].getJson (0);
|
||||
}
|
||||
|
||||
if (remaining.isPositive ())
|
||||
{
|
||||
bFound = false;
|
||||
WriteLog (lsINFO, Pathfinder) << "Paths could not send " << remaining << " of " << mDstAmount;
|
||||
}
|
||||
else
|
||||
bFound = true;
|
||||
|
||||
WriteLog (lsDEBUG, Pathfinder) << boost::str (boost::format ("findPaths: RESULTS: %s") % spsDst.getJson (0));
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog (lsDEBUG, Pathfinder) << boost::str (boost::format ("findPaths: RESULTS: non-defaults filtered away"));
|
||||
}
|
||||
}
|
||||
|
||||
WriteLog (lsDEBUG, Pathfinder) << boost::str (boost::format ("findPaths< bFound=%d") % bFound);
|
||||
|
||||
return bFound;
|
||||
}
|
||||
|
||||
boost::unordered_set<uint160> usAccountSourceCurrencies (const RippleAddress& raAccountID, Ledger::ref lrLedger,
|
||||
bool includeXRP)
|
||||
{
|
||||
boost::unordered_set<uint160> usCurrencies;
|
||||
|
||||
// YYY Only bother if they are above reserve
|
||||
if (includeXRP)
|
||||
usCurrencies.insert (uint160 (CURRENCY_XRP));
|
||||
|
||||
// List of ripple lines.
|
||||
AccountItems rippleLines (raAccountID.getAccountID (), lrLedger, AccountItem::pointer (new RippleState ()));
|
||||
|
||||
BOOST_FOREACH (AccountItem::ref item, rippleLines.getItems ())
|
||||
{
|
||||
RippleState* rspEntry = (RippleState*) item.get ();
|
||||
const STAmount& saBalance = rspEntry->getBalance ();
|
||||
|
||||
// Filter out non
|
||||
if (saBalance.isPositive () // Have IOUs to send.
|
||||
|| (rspEntry->getLimitPeer () // Peer extends credit.
|
||||
&& ((-saBalance) < rspEntry->getLimitPeer ()))) // Credit left.
|
||||
{
|
||||
usCurrencies.insert (saBalance.getCurrency ());
|
||||
}
|
||||
}
|
||||
|
||||
usCurrencies.erase (CURRENCY_BAD);
|
||||
return usCurrencies;
|
||||
}
|
||||
|
||||
boost::unordered_set<uint160> usAccountDestCurrencies (const RippleAddress& raAccountID, Ledger::ref lrLedger,
|
||||
bool includeXRP)
|
||||
{
|
||||
boost::unordered_set<uint160> usCurrencies;
|
||||
|
||||
if (includeXRP)
|
||||
usCurrencies.insert (uint160 (CURRENCY_XRP)); // Even if account doesn't exist
|
||||
|
||||
// List of ripple lines.
|
||||
AccountItems rippleLines (raAccountID.getAccountID (), lrLedger, AccountItem::pointer (new RippleState ()));
|
||||
|
||||
BOOST_FOREACH (AccountItem::ref item, rippleLines.getItems ())
|
||||
{
|
||||
RippleState* rspEntry = (RippleState*) item.get ();
|
||||
const STAmount& saBalance = rspEntry->getBalance ();
|
||||
|
||||
if (saBalance < rspEntry->getLimit ()) // Can take more
|
||||
usCurrencies.insert (saBalance.getCurrency ());
|
||||
}
|
||||
|
||||
usCurrencies.erase (CURRENCY_BAD);
|
||||
return usCurrencies;
|
||||
}
|
||||
|
||||
bool Pathfinder::matchesOrigin (const uint160& currency, const uint160& issuer)
|
||||
{
|
||||
return (currency == mSrcCurrencyID) && (issuer == mSrcIssuerID);
|
||||
}
|
||||
|
||||
int Pathfinder::getPathsOut (const uint160& currencyID, const uint160& accountID,
|
||||
bool isDstCurrency, const uint160& dstAccount)
|
||||
{
|
||||
#ifdef C11X
|
||||
std::pair<const uint160&, const uint160&> accountCurrency (currencyID, accountID);
|
||||
#else
|
||||
std::pair<uint160, uint160> accountCurrency (currencyID, accountID);
|
||||
#endif
|
||||
boost::unordered_map<std::pair<uint160, uint160>, int>::iterator it = mPOMap.find (accountCurrency);
|
||||
|
||||
if (it != mPOMap.end ())
|
||||
return it->second;
|
||||
|
||||
int aFlags = mLedger->getSLEi(Ledger::getAccountRootIndex(accountID))->getFieldU32(sfFlags);
|
||||
bool const bAuthRequired = (aFlags & lsfRequireAuth) != 0;
|
||||
|
||||
int count = 0;
|
||||
AccountItems& rippleLines (mRLCache->getRippleLines (accountID));
|
||||
BOOST_FOREACH (AccountItem::ref item, rippleLines.getItems ())
|
||||
{
|
||||
RippleState* rspEntry = (RippleState*) item.get ();
|
||||
|
||||
if (currencyID != rspEntry->getLimit ().getCurrency ())
|
||||
nothing ();
|
||||
else if (!rspEntry->getBalance ().isPositive () &&
|
||||
(!rspEntry->getLimitPeer ()
|
||||
|| -rspEntry->getBalance () >= rspEntry->getLimitPeer ()
|
||||
|| (bAuthRequired && !rspEntry->getAuth ())))
|
||||
nothing ();
|
||||
else if (isDstCurrency && (dstAccount == rspEntry->getAccountIDPeer ()))
|
||||
count += 10000; // count a path to the destination extra
|
||||
else
|
||||
++count;
|
||||
}
|
||||
mPOMap[accountCurrency] = count;
|
||||
return count;
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
@@ -1,93 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_PATHFINDER_H
|
||||
#define RIPPLE_PATHFINDER_H
|
||||
|
||||
// VFALCO TODO Remove this unused stuff?
|
||||
#if 0
|
||||
//
|
||||
// This is a very simple implementation. This can be made way better.
|
||||
// We are simply flooding from the start. And doing an exhaustive search of all paths under maxSearchSteps. An easy improvement would
|
||||
be to flood from both directions.
|
||||
//
|
||||
|
||||
class PathOption
|
||||
{
|
||||
public:
|
||||
typedef boost::shared_ptr<PathOption> pointer;
|
||||
typedef const boost::shared_ptr<PathOption>& ref;
|
||||
|
||||
STPath mPath;
|
||||
bool mCorrectCurrency; // for the sorting
|
||||
uint160 mCurrencyID; // what currency we currently have at the end of the path
|
||||
uint160 mCurrentAccount; // what account is at the end of the path
|
||||
int mTotalCost; // in send currency
|
||||
STAmount mMinWidth; // in dest currency
|
||||
float mQuality;
|
||||
|
||||
PathOption (uint160& srcAccount, uint160& srcCurrencyID, const uint160& dstCurrencyID);
|
||||
PathOption (PathOption::pointer other);
|
||||
};
|
||||
#endif
|
||||
|
||||
/** Calculates payment paths.
|
||||
|
||||
The @ref RippleCalc determines the quality of the found paths.
|
||||
|
||||
@see RippleCalc
|
||||
*/
|
||||
class Pathfinder
|
||||
{
|
||||
public:
|
||||
Pathfinder (RippleLineCache::ref cache,
|
||||
const RippleAddress& srcAccountID, const RippleAddress& dstAccountID,
|
||||
const uint160& srcCurrencyID, const uint160& srcIssuerID, const STAmount& dstAmount, bool& bValid);
|
||||
|
||||
bool findPaths (const unsigned int iMaxSteps, const unsigned int iMaxPaths, STPathSet& spsDst);
|
||||
|
||||
bool bDefaultPath (const STPath& spPath);
|
||||
|
||||
private:
|
||||
// void addOptions(PathOption::pointer tail);
|
||||
|
||||
// returns true if any building paths are now complete?
|
||||
bool checkComplete (STPathSet& retPathSet);
|
||||
|
||||
// void addPathOption(PathOption::pointer pathOption);
|
||||
|
||||
bool matchesOrigin (const uint160& currency, const uint160& issuer);
|
||||
|
||||
int getPathsOut (const uint160& currency, const uint160& accountID,
|
||||
bool isDestCurrency, const uint160& dest);
|
||||
|
||||
private:
|
||||
uint160 mSrcAccountID;
|
||||
uint160 mDstAccountID;
|
||||
STAmount mDstAmount;
|
||||
uint160 mSrcCurrencyID;
|
||||
uint160 mSrcIssuerID;
|
||||
STAmount mSrcAmount;
|
||||
|
||||
Ledger::pointer mLedger;
|
||||
PathState::pointer mPsDefault;
|
||||
LoadEvent::pointer m_loadEvent;
|
||||
RippleLineCache::pointer mRLCache;
|
||||
|
||||
boost::unordered_map<uint160, AccountItems::pointer> mRLMap;
|
||||
boost::unordered_map<std::pair<uint160, uint160>, int> mPOMap;
|
||||
|
||||
// std::list<PathOption::pointer> mBuildingPaths;
|
||||
// std::list<PathOption::pointer> mCompletePaths;
|
||||
};
|
||||
|
||||
boost::unordered_set<uint160> usAccountDestCurrencies (const RippleAddress& raAccountID, Ledger::ref lrLedger,
|
||||
bool includeXRP);
|
||||
|
||||
boost::unordered_set<uint160> usAccountSourceCurrencies (const RippleAddress& raAccountID, Ledger::ref lrLedger,
|
||||
bool includeXRP);
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,90 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_PEER_H_INCLUDED
|
||||
#define RIPPLE_PEER_H_INCLUDED
|
||||
|
||||
// VFALCO TODO Couldn't this be a struct?
|
||||
typedef std::pair <std::string, int> IPAndPortNumber;
|
||||
|
||||
/** Represents a peer connection in the overlay.
|
||||
*/
|
||||
class Peer
|
||||
: public boost::enable_shared_from_this <Peer>
|
||||
, LeakChecked <Peer>
|
||||
{
|
||||
public:
|
||||
typedef boost::shared_ptr <Peer> pointer;
|
||||
typedef pointer const& ref;
|
||||
|
||||
public:
|
||||
static pointer New (boost::asio::io_service& io_service,
|
||||
boost::asio::ssl::context& ctx,
|
||||
uint64 id,
|
||||
bool inbound);
|
||||
|
||||
// VFALCO TODO see if this and below can be private
|
||||
virtual void handleConnect (const boost::system::error_code& error,
|
||||
boost::asio::ip::tcp::resolver::iterator it) = 0;
|
||||
|
||||
virtual std::string const& getIP () = 0;
|
||||
|
||||
virtual std::string getDisplayName () = 0;
|
||||
|
||||
virtual int getPort () = 0;
|
||||
|
||||
virtual void setIpPort (const std::string& strIP, int iPort) = 0;
|
||||
|
||||
virtual boost::asio::ssl::stream<boost::asio::ip::tcp::socket>::lowest_layer_type& getSocket () = 0;
|
||||
|
||||
virtual void connect (const std::string& strIp, int iPort) = 0;
|
||||
|
||||
virtual void connected (const boost::system::error_code& error) = 0;
|
||||
|
||||
virtual void detach (const char*, bool onIOStrand) = 0;
|
||||
|
||||
virtual void sendPacket (const PackedMessage::pointer& packet, bool onStrand) = 0;
|
||||
|
||||
virtual void sendGetPeers () = 0;
|
||||
|
||||
virtual void applyLoadCharge (LoadType) = 0;
|
||||
|
||||
// VFALCO NOTE what's with this odd parameter passing? Why the static member?
|
||||
//
|
||||
/** Adjust this peer's load balance based on the type of load imposed.
|
||||
|
||||
@note Formerly named punishPeer
|
||||
*/
|
||||
static void applyLoadCharge (boost::weak_ptr <Peer>& peerTOCharge, LoadType loadThatWasImposed);
|
||||
|
||||
virtual Json::Value getJson () = 0;
|
||||
|
||||
virtual bool isConnected () const = 0;
|
||||
|
||||
virtual bool isInCluster () const = 0;
|
||||
|
||||
virtual bool isInbound () const = 0;
|
||||
|
||||
virtual bool isOutbound () const = 0;
|
||||
|
||||
virtual uint256 const& getClosedLedgerHash () const = 0;
|
||||
|
||||
virtual bool hasLedger (uint256 const& hash, uint32 seq) const = 0;
|
||||
|
||||
virtual bool hasTxSet (uint256 const& hash) const = 0;
|
||||
|
||||
virtual uint64 getPeerId () const = 0;
|
||||
|
||||
virtual const RippleAddress& getNodePublic () const = 0;
|
||||
|
||||
virtual void cycleStatus () = 0;
|
||||
|
||||
virtual bool hasProto (int version) = 0;
|
||||
|
||||
virtual bool hasRange (uint32 uMin, uint32 uMax) = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,108 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
class InboundLedger;
|
||||
|
||||
PeerSet::PeerSet (uint256 const& hash, int interval, bool txnData)
|
||||
: mHash (hash)
|
||||
, mTimerInterval (interval)
|
||||
, mTimeouts (0)
|
||||
, mComplete (false)
|
||||
, mFailed (false)
|
||||
, mProgress (true)
|
||||
, mAggressive (false)
|
||||
, mTxnData (txnData)
|
||||
, mTimer (getApp().getIOService ())
|
||||
{
|
||||
mLastAction = UptimeTimer::getInstance ().getElapsedSeconds ();
|
||||
assert ((mTimerInterval > 10) && (mTimerInterval < 30000));
|
||||
}
|
||||
|
||||
void PeerSet::peerHas (Peer::ref ptr)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl (mLock);
|
||||
|
||||
if (!mPeers.insert (std::make_pair (ptr->getPeerId (), 0)).second)
|
||||
return;
|
||||
|
||||
newPeer (ptr);
|
||||
}
|
||||
|
||||
void PeerSet::badPeer (Peer::ref ptr)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl (mLock);
|
||||
mPeers.erase (ptr->getPeerId ());
|
||||
}
|
||||
|
||||
void PeerSet::setTimer ()
|
||||
{
|
||||
mTimer.expires_from_now (boost::posix_time::milliseconds (mTimerInterval));
|
||||
mTimer.async_wait (boost::bind (&PeerSet::TimerEntry, pmDowncast (), boost::asio::placeholders::error));
|
||||
}
|
||||
|
||||
void PeerSet::invokeOnTimer ()
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl (mLock);
|
||||
|
||||
if (isDone ())
|
||||
return;
|
||||
|
||||
if (!mProgress)
|
||||
{
|
||||
++mTimeouts;
|
||||
WriteLog (lsWARNING, InboundLedger) << "Timeout(" << mTimeouts << ") pc=" << mPeers.size () << " acquiring " << mHash;
|
||||
onTimer (false, sl);
|
||||
}
|
||||
else
|
||||
{
|
||||
mProgress = false;
|
||||
onTimer (true, sl);
|
||||
}
|
||||
|
||||
if (!isDone ())
|
||||
setTimer ();
|
||||
}
|
||||
|
||||
void PeerSet::TimerEntry (boost::weak_ptr<PeerSet> wptr, const boost::system::error_code& result)
|
||||
{
|
||||
if (result == boost::asio::error::operation_aborted)
|
||||
return;
|
||||
|
||||
boost::shared_ptr<PeerSet> ptr = wptr.lock ();
|
||||
|
||||
if (ptr)
|
||||
{
|
||||
if (ptr->mTxnData)
|
||||
{
|
||||
getApp().getJobQueue ().addLimitJob (jtTXN_DATA, "timerEntry", 2,
|
||||
BIND_TYPE (&PeerSet::TimerJobEntry, P_1, ptr));
|
||||
}
|
||||
else
|
||||
{
|
||||
int jc = getApp().getJobQueue ().getJobCountTotal (jtLEDGER_DATA);
|
||||
|
||||
if (jc > 4)
|
||||
{
|
||||
WriteLog (lsDEBUG, InboundLedger) << "Deferring PeerSet timer due to load";
|
||||
ptr->setTimer ();
|
||||
}
|
||||
else
|
||||
getApp().getJobQueue ().addLimitJob (jtLEDGER_DATA, "timerEntry", 2,
|
||||
BIND_TYPE (&PeerSet::TimerJobEntry, P_1, ptr));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PeerSet::TimerJobEntry (Job&, boost::shared_ptr<PeerSet> ptr)
|
||||
{
|
||||
ptr->invokeOnTimer ();
|
||||
}
|
||||
|
||||
bool PeerSet::isActive ()
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl (mLock);
|
||||
return !isDone ();
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_PEERSET_H
|
||||
#define RIPPLE_PEERSET_H
|
||||
|
||||
/** A set of peers used to acquire data.
|
||||
|
||||
A peer set is used to acquire a ledger or a transaction set.
|
||||
*/
|
||||
class PeerSet : LeakChecked <PeerSet>
|
||||
{
|
||||
public:
|
||||
uint256 const& getHash () const
|
||||
{
|
||||
return mHash;
|
||||
}
|
||||
bool isComplete () const
|
||||
{
|
||||
return mComplete;
|
||||
}
|
||||
bool isFailed () const
|
||||
{
|
||||
return mFailed;
|
||||
}
|
||||
int getTimeouts () const
|
||||
{
|
||||
return mTimeouts;
|
||||
}
|
||||
|
||||
bool isActive ();
|
||||
void progress ()
|
||||
{
|
||||
mProgress = true;
|
||||
mAggressive = false;
|
||||
}
|
||||
bool isProgress ()
|
||||
{
|
||||
return mProgress;
|
||||
}
|
||||
void touch ()
|
||||
{
|
||||
mLastAction = UptimeTimer::getInstance ().getElapsedSeconds ();
|
||||
}
|
||||
int getLastAction ()
|
||||
{
|
||||
return mLastAction;
|
||||
}
|
||||
|
||||
void peerHas (Peer::ref);
|
||||
void badPeer (Peer::ref);
|
||||
void setTimer ();
|
||||
|
||||
int takePeerSetFrom (const PeerSet& s);
|
||||
int getPeerCount () const;
|
||||
virtual bool isDone () const
|
||||
{
|
||||
return mComplete || mFailed;
|
||||
}
|
||||
|
||||
private:
|
||||
static void TimerEntry (boost::weak_ptr<PeerSet>, const boost::system::error_code& result);
|
||||
static void TimerJobEntry (Job&, boost::shared_ptr<PeerSet>);
|
||||
|
||||
// VFALCO TODO try to make some of these private
|
||||
protected:
|
||||
PeerSet (uint256 const& hash, int interval, bool txnData);
|
||||
virtual ~PeerSet () { }
|
||||
|
||||
virtual void newPeer (Peer::ref) = 0;
|
||||
virtual void onTimer (bool progress, boost::recursive_mutex::scoped_lock&) = 0;
|
||||
virtual boost::weak_ptr<PeerSet> pmDowncast () = 0;
|
||||
|
||||
void setComplete ()
|
||||
{
|
||||
mComplete = true;
|
||||
}
|
||||
void setFailed ()
|
||||
{
|
||||
mFailed = true;
|
||||
}
|
||||
void invokeOnTimer ();
|
||||
|
||||
void sendRequest (const protocol::TMGetLedger& message);
|
||||
void sendRequest (const protocol::TMGetLedger& message, Peer::ref peer);
|
||||
|
||||
protected:
|
||||
uint256 mHash;
|
||||
int mTimerInterval;
|
||||
int mTimeouts;
|
||||
bool mComplete;
|
||||
bool mFailed;
|
||||
bool mProgress;
|
||||
bool mAggressive;
|
||||
bool mTxnData;
|
||||
int mLastAction;
|
||||
|
||||
|
||||
boost::recursive_mutex mLock;
|
||||
// VFALCO TODO move the responsibility for the timer to a higher level
|
||||
boost::asio::deadline_timer mTimer;
|
||||
|
||||
// VFALCO TODO Verify that these are used in the way that the names suggest.
|
||||
typedef uint64 PeerIdentifier;
|
||||
typedef int ReceivedChunkCount;
|
||||
boost::unordered_map <PeerIdentifier, ReceivedChunkCount> mPeers;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,898 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
class Peers
|
||||
: public IPeers
|
||||
, LeakChecked <Peers>
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
/** Frequency of policy enforcement.
|
||||
*/
|
||||
policyIntervalSeconds = 5
|
||||
};
|
||||
|
||||
explicit Peers (boost::asio::io_service& io_service)
|
||||
: mLastPeer (0)
|
||||
, mPhase (0)
|
||||
, mScanTimer (io_service)
|
||||
, mPolicyTimer (io_service)
|
||||
{
|
||||
}
|
||||
|
||||
// Begin enforcing connection policy.
|
||||
void start ();
|
||||
|
||||
// Send message to network.
|
||||
int relayMessage (Peer* fromPeer, const PackedMessage::pointer& msg);
|
||||
int relayMessageCluster (Peer* fromPeer, const PackedMessage::pointer& msg);
|
||||
void relayMessageTo (const std::set<uint64>& fromPeers, const PackedMessage::pointer& msg);
|
||||
void relayMessageBut (const std::set<uint64>& fromPeers, const PackedMessage::pointer& msg);
|
||||
|
||||
// Manual connection request.
|
||||
// Queue for immediate scanning.
|
||||
void connectTo (const std::string& strIp, int iPort);
|
||||
|
||||
//
|
||||
// Peer connectivity notification.
|
||||
//
|
||||
bool getTopNAddrs (int n, std::vector<std::string>& addrs);
|
||||
bool savePeer (const std::string& strIp, int iPort, char code);
|
||||
|
||||
// We know peers node public key.
|
||||
// <-- bool: false=reject
|
||||
bool peerConnected (Peer::ref peer, const RippleAddress& naPeer, const std::string& strIP, int iPort);
|
||||
|
||||
// No longer connected.
|
||||
void peerDisconnected (Peer::ref peer, const RippleAddress& naPeer);
|
||||
|
||||
// As client accepted.
|
||||
void peerVerified (Peer::ref peer);
|
||||
|
||||
// As client failed connect and be accepted.
|
||||
void peerClosed (Peer::ref peer, const std::string& strIp, int iPort);
|
||||
|
||||
int getPeerCount ();
|
||||
Json::Value getPeersJson ();
|
||||
std::vector<Peer::pointer> getPeerVector ();
|
||||
|
||||
// Peer 64-bit ID function
|
||||
uint64 assignPeerId ();
|
||||
Peer::pointer getPeerById (const uint64& id);
|
||||
bool hasPeer (const uint64& id);
|
||||
|
||||
//
|
||||
// Scanning
|
||||
//
|
||||
|
||||
void scanRefresh ();
|
||||
|
||||
//
|
||||
// Connection policy
|
||||
//
|
||||
void policyLowWater ();
|
||||
void policyEnforce ();
|
||||
|
||||
// configured connections
|
||||
void makeConfigured ();
|
||||
|
||||
private:
|
||||
boost::recursive_mutex mPeerLock;
|
||||
uint64 mLastPeer;
|
||||
int mPhase;
|
||||
|
||||
typedef std::pair<RippleAddress, Peer::pointer> naPeer;
|
||||
typedef std::pair<IPAndPortNumber, Peer::pointer> pipPeer;
|
||||
typedef std::map<IPAndPortNumber, Peer::pointer>::value_type vtPeer;
|
||||
|
||||
// Peers we are connecting with and non-thin peers we are connected to.
|
||||
// Only peers we know the connection ip for are listed.
|
||||
// We know the ip and port for:
|
||||
// - All outbound connections
|
||||
// - Some inbound connections (which we figured out).
|
||||
boost::unordered_map<IPAndPortNumber, Peer::pointer> mIpMap;
|
||||
|
||||
// Non-thin peers which we are connected to.
|
||||
// Peers we have the public key for.
|
||||
typedef boost::unordered_map<RippleAddress, Peer::pointer>::value_type vtConMap;
|
||||
boost::unordered_map<RippleAddress, Peer::pointer> mConnectedMap;
|
||||
|
||||
// Connections with have a 64-bit identifier
|
||||
boost::unordered_map<uint64, Peer::pointer> mPeerIdMap;
|
||||
|
||||
Peer::pointer mScanning;
|
||||
boost::asio::deadline_timer mScanTimer;
|
||||
std::string mScanIp;
|
||||
int mScanPort;
|
||||
|
||||
void scanHandler (const boost::system::error_code& ecResult);
|
||||
|
||||
boost::asio::deadline_timer mPolicyTimer;
|
||||
|
||||
void policyHandler (const boost::system::error_code& ecResult);
|
||||
|
||||
// Peers we are establishing a connection with as a client.
|
||||
// int miConnectStarting;
|
||||
|
||||
bool peerAvailable (std::string& strIp, int& iPort);
|
||||
bool peerScanSet (const std::string& strIp, int iPort);
|
||||
|
||||
Peer::pointer peerConnect (const std::string& strIp, int iPort);
|
||||
};
|
||||
|
||||
void splitIpPort (const std::string& strIpPort, std::string& strIp, int& iPort)
|
||||
{
|
||||
std::vector<std::string> vIpPort;
|
||||
boost::split (vIpPort, strIpPort, boost::is_any_of (" "));
|
||||
|
||||
strIp = vIpPort[0];
|
||||
iPort = boost::lexical_cast<int> (vIpPort[1]);
|
||||
}
|
||||
|
||||
void Peers::start ()
|
||||
{
|
||||
if (theConfig.RUN_STANDALONE)
|
||||
return;
|
||||
|
||||
// Start running policy.
|
||||
policyEnforce ();
|
||||
|
||||
// Start scanning.
|
||||
scanRefresh ();
|
||||
}
|
||||
|
||||
bool Peers::getTopNAddrs (int n, std::vector<std::string>& addrs)
|
||||
{
|
||||
// XXX Filter out other local addresses (like ipv6)
|
||||
Database* db = getApp().getWalletDB ()->getDB ();
|
||||
ScopedLock sl (getApp().getWalletDB ()->getDBLock ());
|
||||
|
||||
SQL_FOREACH (db, str (boost::format ("SELECT IpPort FROM PeerIps LIMIT %d") % n) )
|
||||
{
|
||||
std::string str;
|
||||
|
||||
db->getStr (0, str);
|
||||
|
||||
addrs.push_back (str);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Peers::savePeer (const std::string& strIp, int iPort, char code)
|
||||
{
|
||||
bool bNew = false;
|
||||
|
||||
Database* db = getApp().getWalletDB ()->getDB ();
|
||||
|
||||
std::string ipAndPort = sqlEscape (str (boost::format ("%s %d") % strIp % iPort));
|
||||
|
||||
ScopedLock sl (getApp().getWalletDB ()->getDBLock ());
|
||||
std::string sql = str (boost::format ("SELECT COUNT(*) FROM PeerIps WHERE IpPort=%s;") % ipAndPort);
|
||||
|
||||
if (db->executeSQL (sql) && db->startIterRows ())
|
||||
{
|
||||
if (!db->getInt (0))
|
||||
{
|
||||
db->executeSQL (str (boost::format ("INSERT INTO PeerIps (IpPort,Score,Source) values (%s,0,'%c');") % ipAndPort % code));
|
||||
bNew = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// We already had this peer.
|
||||
// We will eventually verify its address if it is possible.
|
||||
// YYY If it is vsInbound, then we might make verification immediate so we can connect back sooner if the connection
|
||||
// is lost.
|
||||
nothing ();
|
||||
}
|
||||
|
||||
db->endIterRows ();
|
||||
}
|
||||
else
|
||||
{
|
||||
Log::out() << "Error saving Peer";
|
||||
}
|
||||
|
||||
if (bNew)
|
||||
scanRefresh ();
|
||||
|
||||
return bNew;
|
||||
}
|
||||
|
||||
Peer::pointer Peers::getPeerById (const uint64& id)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl (mPeerLock);
|
||||
const boost::unordered_map<uint64, Peer::pointer>::iterator& it = mPeerIdMap.find (id);
|
||||
|
||||
if (it == mPeerIdMap.end ())
|
||||
return Peer::pointer ();
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
bool Peers::hasPeer (const uint64& id)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl (mPeerLock);
|
||||
return mPeerIdMap.find (id) != mPeerIdMap.end ();
|
||||
}
|
||||
|
||||
// An available peer is one we had no trouble connect to last time and that we are not currently knowingly connected or connecting
|
||||
// too.
|
||||
//
|
||||
// <-- true, if a peer is available to connect to
|
||||
bool Peers::peerAvailable (std::string& strIp, int& iPort)
|
||||
{
|
||||
Database* db = getApp().getWalletDB ()->getDB ();
|
||||
std::vector<std::string> vstrIpPort;
|
||||
|
||||
// Convert mIpMap (list of open connections) to a vector of "<ip> <port>".
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl (mPeerLock);
|
||||
|
||||
vstrIpPort.reserve (mIpMap.size ());
|
||||
|
||||
BOOST_FOREACH (const vtPeer & ipPeer, mIpMap)
|
||||
{
|
||||
const std::string& strIp = ipPeer.first.first;
|
||||
int iPort = ipPeer.first.second;
|
||||
|
||||
vstrIpPort.push_back (sqlEscape (str (boost::format ("%s %d") % strIp % iPort)));
|
||||
}
|
||||
}
|
||||
|
||||
// Get the first IpPort entry which is not in vector and which is not scheduled for scanning.
|
||||
std::string strIpPort;
|
||||
|
||||
{
|
||||
ScopedLock sl (getApp().getWalletDB ()->getDBLock ());
|
||||
|
||||
if (db->executeSQL (str (boost::format ("SELECT IpPort FROM PeerIps WHERE ScanNext IS NULL AND IpPort NOT IN (%s) LIMIT 1;")
|
||||
% strJoin (vstrIpPort.begin (), vstrIpPort.end (), ",")))
|
||||
&& db->startIterRows ())
|
||||
{
|
||||
strIpPort = db->getStrBinary ("IpPort");
|
||||
db->endIterRows ();
|
||||
}
|
||||
}
|
||||
|
||||
bool bAvailable = !strIpPort.empty ();
|
||||
|
||||
if (bAvailable)
|
||||
splitIpPort (strIpPort, strIp, iPort);
|
||||
|
||||
return bAvailable;
|
||||
}
|
||||
|
||||
// Make sure we have at least low water connections.
|
||||
void Peers::policyLowWater ()
|
||||
{
|
||||
std::string strIp;
|
||||
int iPort;
|
||||
|
||||
// Find an entry to connect to.
|
||||
if (getPeerCount () > theConfig.PEER_CONNECT_LOW_WATER)
|
||||
{
|
||||
// Above low water mark, don't need more connections.
|
||||
WriteLog (lsTRACE, Peers) << "Pool: Low water: sufficient connections: " << mConnectedMap.size () << "/" << theConfig.PEER_CONNECT_LOW_WATER;
|
||||
|
||||
nothing ();
|
||||
}
|
||||
|
||||
#if 0
|
||||
else if (miConnectStarting == theConfig.PEER_START_MAX)
|
||||
{
|
||||
// Too many connections starting to start another.
|
||||
nothing ();
|
||||
}
|
||||
|
||||
#endif
|
||||
else if (!peerAvailable (strIp, iPort))
|
||||
{
|
||||
// No more connections available to start.
|
||||
WriteLog (lsTRACE, Peers) << "Pool: Low water: no peers available.";
|
||||
|
||||
// XXX Might ask peers for more ips.
|
||||
nothing ();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Try to start connection.
|
||||
WriteLog (lsTRACE, Peers) << "Pool: Low water: start connection.";
|
||||
|
||||
if (!peerConnect (strIp, iPort))
|
||||
{
|
||||
WriteLog (lsINFO, Peers) << "Pool: Low water: already connected.";
|
||||
}
|
||||
|
||||
// Check if we need more.
|
||||
policyLowWater ();
|
||||
}
|
||||
}
|
||||
|
||||
void Peers::policyEnforce ()
|
||||
{
|
||||
// Cancel any in progress timer.
|
||||
(void) mPolicyTimer.cancel ();
|
||||
|
||||
// Enforce policies.
|
||||
policyLowWater ();
|
||||
|
||||
if (((++mPhase) % 12) == 0)
|
||||
{
|
||||
WriteLog (lsTRACE, Peers) << "Making configured connections";
|
||||
makeConfigured ();
|
||||
}
|
||||
|
||||
// Schedule next enforcement.
|
||||
mPolicyTimer.expires_at (boost::posix_time::second_clock::universal_time () + boost::posix_time::seconds (policyIntervalSeconds));
|
||||
mPolicyTimer.async_wait (BIND_TYPE (&Peers::policyHandler, this, P_1));
|
||||
}
|
||||
|
||||
void Peers::policyHandler (const boost::system::error_code& ecResult)
|
||||
{
|
||||
if (ecResult == boost::asio::error::operation_aborted)
|
||||
{
|
||||
nothing ();
|
||||
}
|
||||
else if (!ecResult)
|
||||
{
|
||||
policyEnforce ();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error ("Internal error: unexpected deadline error.");
|
||||
}
|
||||
}
|
||||
|
||||
// YYY: Should probably do this in the background.
|
||||
// YYY: Might end up sending to disconnected peer?
|
||||
int Peers::relayMessage (Peer* fromPeer, const PackedMessage::pointer& msg)
|
||||
{
|
||||
int sentTo = 0;
|
||||
std::vector<Peer::pointer> peerVector = getPeerVector ();
|
||||
BOOST_FOREACH (Peer::ref peer, peerVector)
|
||||
{
|
||||
if ((!fromPeer || ! (peer.get () == fromPeer)) && peer->isConnected ())
|
||||
{
|
||||
++sentTo;
|
||||
peer->sendPacket (msg, false);
|
||||
}
|
||||
}
|
||||
|
||||
return sentTo;
|
||||
}
|
||||
|
||||
int Peers::relayMessageCluster (Peer* fromPeer, const PackedMessage::pointer& msg)
|
||||
{
|
||||
int sentTo = 0;
|
||||
std::vector<Peer::pointer> peerVector = getPeerVector ();
|
||||
BOOST_FOREACH (Peer::ref peer, peerVector)
|
||||
{
|
||||
if ((!fromPeer || ! (peer.get () == fromPeer)) && peer->isConnected () && peer->isInCluster ())
|
||||
{
|
||||
++sentTo;
|
||||
peer->sendPacket (msg, false);
|
||||
}
|
||||
}
|
||||
|
||||
return sentTo;
|
||||
}
|
||||
|
||||
void Peers::relayMessageBut (const std::set<uint64>& fromPeers, const PackedMessage::pointer& msg)
|
||||
{
|
||||
// Relay message to all but the specified peers
|
||||
std::vector<Peer::pointer> peerVector = getPeerVector ();
|
||||
BOOST_FOREACH (Peer::ref peer, peerVector)
|
||||
{
|
||||
if (peer->isConnected () && (fromPeers.count (peer->getPeerId ()) == 0))
|
||||
peer->sendPacket (msg, false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Peers::relayMessageTo (const std::set<uint64>& fromPeers, const PackedMessage::pointer& msg)
|
||||
{
|
||||
// Relay message to the specified peers
|
||||
std::vector<Peer::pointer> peerVector = getPeerVector ();
|
||||
BOOST_FOREACH (Peer::ref peer, peerVector)
|
||||
{
|
||||
if (peer->isConnected () && (fromPeers.count (peer->getPeerId ()) != 0))
|
||||
peer->sendPacket (msg, false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Schedule a connection via scanning.
|
||||
//
|
||||
// Add or modify into PeerIps as a manual entry for immediate scanning.
|
||||
// Requires sane IP and port.
|
||||
void Peers::connectTo (const std::string& strIp, int iPort)
|
||||
{
|
||||
{
|
||||
Database* db = getApp().getWalletDB ()->getDB ();
|
||||
ScopedLock sl (getApp().getWalletDB ()->getDBLock ());
|
||||
|
||||
db->executeSQL (str (boost::format ("REPLACE INTO PeerIps (IpPort,Score,Source,ScanNext) values (%s,%d,'%c',0);")
|
||||
% sqlEscape (str (boost::format ("%s %d") % strIp % iPort))
|
||||
% getApp().getUNL ().iSourceScore (UniqueNodeList::vsManual)
|
||||
% char (UniqueNodeList::vsManual)));
|
||||
}
|
||||
|
||||
scanRefresh ();
|
||||
}
|
||||
|
||||
// Start a connection, if not already known connected or connecting.
|
||||
//
|
||||
// <-- true, if already connected.
|
||||
Peer::pointer Peers::peerConnect (const std::string& strIp, int iPort)
|
||||
{
|
||||
IPAndPortNumber pipPeer = make_pair (strIp, iPort);
|
||||
Peer::pointer ppResult;
|
||||
|
||||
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl (mPeerLock);
|
||||
|
||||
if (mIpMap.find (pipPeer) == mIpMap.end ())
|
||||
{
|
||||
ppResult = Peer::New (getApp().getIOService (),
|
||||
getApp().getPeerDoor ().getSSLContext (),
|
||||
++mLastPeer,
|
||||
false);
|
||||
|
||||
mIpMap[pipPeer] = ppResult;
|
||||
// ++miConnectStarting;
|
||||
}
|
||||
}
|
||||
|
||||
if (ppResult)
|
||||
{
|
||||
ppResult->connect (strIp, iPort);
|
||||
WriteLog (lsDEBUG, Peers) << "Pool: Connecting: " << strIp << " " << iPort;
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog (lsTRACE, Peers) << "Pool: Already connected: " << strIp << " " << iPort;
|
||||
}
|
||||
|
||||
return ppResult;
|
||||
}
|
||||
|
||||
// Returns information on verified peers.
|
||||
Json::Value Peers::getPeersJson ()
|
||||
{
|
||||
Json::Value ret (Json::arrayValue);
|
||||
std::vector<Peer::pointer> vppPeers = getPeerVector ();
|
||||
|
||||
BOOST_FOREACH (Peer::ref peer, vppPeers)
|
||||
{
|
||||
ret.append (peer->getJson ());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int Peers::getPeerCount ()
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl (mPeerLock);
|
||||
|
||||
return mConnectedMap.size ();
|
||||
}
|
||||
|
||||
std::vector<Peer::pointer> Peers::getPeerVector ()
|
||||
{
|
||||
std::vector<Peer::pointer> ret;
|
||||
|
||||
boost::recursive_mutex::scoped_lock sl (mPeerLock);
|
||||
|
||||
ret.reserve (mConnectedMap.size ());
|
||||
|
||||
BOOST_FOREACH (const vtConMap & pair, mConnectedMap)
|
||||
{
|
||||
assert (!!pair.second);
|
||||
ret.push_back (pair.second);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint64 Peers::assignPeerId ()
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl (mPeerLock);
|
||||
return ++mLastPeer;
|
||||
}
|
||||
|
||||
// Now know peer's node public key. Determine if we want to stay connected.
|
||||
// <-- bNew: false = redundant
|
||||
bool Peers::peerConnected (Peer::ref peer, const RippleAddress& naPeer,
|
||||
const std::string& strIP, int iPort)
|
||||
{
|
||||
bool bNew = false;
|
||||
|
||||
assert (!!peer);
|
||||
|
||||
if (naPeer == getApp().getLocalCredentials ().getNodePublic ())
|
||||
{
|
||||
WriteLog (lsINFO, Peers) << "Pool: Connected: self: " << addressToString (peer.get()) << ": " << naPeer.humanNodePublic () << " " << strIP << " " << iPort;
|
||||
}
|
||||
else
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl (mPeerLock);
|
||||
const boost::unordered_map<RippleAddress, Peer::pointer>::iterator& itCm = mConnectedMap.find (naPeer);
|
||||
|
||||
if (itCm == mConnectedMap.end ())
|
||||
{
|
||||
// New connection.
|
||||
//WriteLog (lsINFO, Peers) << "Pool: Connected: new: " << addressToString (peer.get()) << ": " << naPeer.humanNodePublic() << " " << strIP << " " << iPort;
|
||||
|
||||
mConnectedMap[naPeer] = peer;
|
||||
bNew = true;
|
||||
|
||||
assert (peer->getPeerId () != 0);
|
||||
mPeerIdMap.insert (std::make_pair (peer->getPeerId (), peer));
|
||||
}
|
||||
// Found in map, already connected.
|
||||
else if (!strIP.empty ())
|
||||
{
|
||||
// Was an outbound connection, we know IP and port.
|
||||
// Note in previous connection how to reconnect.
|
||||
if (itCm->second->getIP ().empty ())
|
||||
{
|
||||
// Old peer did not know it's IP.
|
||||
//WriteLog (lsINFO, Peers) << "Pool: Connected: redundant: outbound: " << addressToString (peer.get()) << " discovered: " << addressToString(itCm->second) << ": " << strIP << " " << iPort;
|
||||
|
||||
itCm->second->setIpPort (strIP, iPort);
|
||||
|
||||
// Add old connection to identified connection list.
|
||||
mIpMap[make_pair (strIP, iPort)] = itCm->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Old peer knew its IP. Do nothing.
|
||||
//WriteLog (lsINFO, Peers) << "Pool: Connected: redundant: outbound: rediscovered: " << addressToString (peer.get()) << " " << strIP << " " << iPort;
|
||||
|
||||
nothing ();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//WriteLog (lsINFO, Peers) << "Pool: Connected: redundant: inbound: " << addressToString (peer.get()) << " " << strIP << " " << iPort;
|
||||
|
||||
nothing ();
|
||||
}
|
||||
}
|
||||
|
||||
return bNew;
|
||||
}
|
||||
|
||||
// We maintain a map of public key to peer for connected and verified peers. Maintain it.
|
||||
void Peers::peerDisconnected (Peer::ref peer, const RippleAddress& naPeer)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl (mPeerLock);
|
||||
|
||||
if (naPeer.isValid ())
|
||||
{
|
||||
const boost::unordered_map<RippleAddress, Peer::pointer>::iterator& itCm = mConnectedMap.find (naPeer);
|
||||
|
||||
if (itCm == mConnectedMap.end ())
|
||||
{
|
||||
// Did not find it. Not already connecting or connected.
|
||||
WriteLog (lsWARNING, Peers) << "Pool: disconnected: Internal Error: mConnectedMap was inconsistent.";
|
||||
// XXX Maybe bad error, considering we have racing connections, may not so bad.
|
||||
}
|
||||
else if (itCm->second != peer)
|
||||
{
|
||||
WriteLog (lsWARNING, Peers) << "Pool: disconected: non canonical entry";
|
||||
|
||||
nothing ();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Found it. Delete it.
|
||||
mConnectedMap.erase (itCm);
|
||||
|
||||
//WriteLog (lsINFO, Peers) << "Pool: disconnected: " << naPeer.humanNodePublic() << " " << peer->getIP() << " " << peer->getPort();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//WriteLog (lsINFO, Peers) << "Pool: disconnected: anonymous: " << peer->getIP() << " " << peer->getPort();
|
||||
}
|
||||
|
||||
assert (peer->getPeerId () != 0);
|
||||
mPeerIdMap.erase (peer->getPeerId ());
|
||||
}
|
||||
|
||||
// Schedule for immediate scanning, if not already scheduled.
|
||||
//
|
||||
// <-- true, scanRefresh needed.
|
||||
bool Peers::peerScanSet (const std::string& strIp, int iPort)
|
||||
{
|
||||
std::string strIpPort = str (boost::format ("%s %d") % strIp % iPort);
|
||||
bool bScanDirty = false;
|
||||
|
||||
ScopedLock sl (getApp().getWalletDB ()->getDBLock ());
|
||||
Database* db = getApp().getWalletDB ()->getDB ();
|
||||
|
||||
if (db->executeSQL (str (boost::format ("SELECT ScanNext FROM PeerIps WHERE IpPort=%s;")
|
||||
% sqlEscape (strIpPort)))
|
||||
&& db->startIterRows ())
|
||||
{
|
||||
if (db->getNull ("ScanNext"))
|
||||
{
|
||||
// Non-scanning connection terminated. Schedule for scanning.
|
||||
int iInterval = theConfig.PEER_SCAN_INTERVAL_MIN;
|
||||
boost::posix_time::ptime tpNow = boost::posix_time::second_clock::universal_time ();
|
||||
boost::posix_time::ptime tpNext = tpNow + boost::posix_time::seconds (iInterval);
|
||||
|
||||
//WriteLog (lsINFO, Peers) << str(boost::format("Pool: Scan: schedule create: %s %s (next %s, delay=%d)")
|
||||
// % mScanIp % mScanPort % tpNext % (tpNext-tpNow).total_seconds());
|
||||
|
||||
db->executeSQL (str (boost::format ("UPDATE PeerIps SET ScanNext=%d,ScanInterval=%d WHERE IpPort=%s;")
|
||||
% iToSeconds (tpNext)
|
||||
% iInterval
|
||||
% sqlEscape (strIpPort)));
|
||||
|
||||
bScanDirty = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Scan connection terminated, already scheduled for retry.
|
||||
// boost::posix_time::ptime tpNow = boost::posix_time::second_clock::universal_time();
|
||||
// boost::posix_time::ptime tpNext = ptFromSeconds(db->getInt("ScanNext"));
|
||||
|
||||
//WriteLog (lsINFO, Peers) << str(boost::format("Pool: Scan: schedule exists: %s %s (next %s, delay=%d)")
|
||||
// % mScanIp % mScanPort % tpNext % (tpNext-tpNow).total_seconds());
|
||||
}
|
||||
|
||||
db->endIterRows ();
|
||||
}
|
||||
else
|
||||
{
|
||||
//WriteLog (lsWARNING, Peers) << "Pool: Scan: peer wasn't in PeerIps: " << strIp << " " << iPort;
|
||||
}
|
||||
|
||||
return bScanDirty;
|
||||
}
|
||||
|
||||
// --> strIp: not empty
|
||||
void Peers::peerClosed (Peer::ref peer, const std::string& strIp, int iPort)
|
||||
{
|
||||
IPAndPortNumber ipPeer = make_pair (strIp, iPort);
|
||||
bool bScanRefresh = false;
|
||||
|
||||
// If the connection was our scan, we are no longer scanning.
|
||||
if (mScanning && mScanning == peer)
|
||||
{
|
||||
//WriteLog (lsINFO, Peers) << "Pool: Scan: scan fail: " << strIp << " " << iPort;
|
||||
|
||||
mScanning.reset (); // No longer scanning.
|
||||
bScanRefresh = true; // Look for more to scan.
|
||||
}
|
||||
|
||||
// Determine if closed peer was redundant.
|
||||
bool bRedundant = true;
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl (mPeerLock);
|
||||
const boost::unordered_map<IPAndPortNumber, Peer::pointer>::iterator& itIp = mIpMap.find (ipPeer);
|
||||
|
||||
if (itIp == mIpMap.end ())
|
||||
{
|
||||
// Did not find it. Not already connecting or connected.
|
||||
WriteLog (lsWARNING, Peers) << "Pool: Closed: UNEXPECTED: " << addressToString (peer.get()) << ": " << strIp << " " << iPort;
|
||||
// XXX Internal error.
|
||||
}
|
||||
else if (mIpMap[ipPeer] == peer)
|
||||
{
|
||||
// We were the identified connection.
|
||||
//WriteLog (lsINFO, Peers) << "Pool: Closed: identified: " << addressToString (peer.get()) << ": " << strIp << " " << iPort;
|
||||
|
||||
// Delete our entry.
|
||||
mIpMap.erase (itIp);
|
||||
|
||||
bRedundant = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Found it. But, we were redundant.
|
||||
//WriteLog (lsINFO, Peers) << "Pool: Closed: redundant: " << addressToString (peer.get()) << ": " << strIp << " " << iPort;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bRedundant)
|
||||
{
|
||||
// If closed was not redundant schedule if not already scheduled.
|
||||
bScanRefresh = peerScanSet (ipPeer.first, ipPeer.second) || bScanRefresh;
|
||||
}
|
||||
|
||||
if (bScanRefresh)
|
||||
scanRefresh ();
|
||||
}
|
||||
|
||||
void Peers::peerVerified (Peer::ref peer)
|
||||
{
|
||||
if (mScanning && mScanning == peer)
|
||||
{
|
||||
// Scan completed successfully.
|
||||
std::string strIp = peer->getIP ();
|
||||
int iPort = peer->getPort ();
|
||||
|
||||
std::string strIpPort = str (boost::format ("%s %d") % strIp % iPort);
|
||||
|
||||
//WriteLog (lsINFO, Peers) << str(boost::format("Pool: Scan: connected: %s %s %s (scanned)") % addressToString (peer.get()) % strIp % iPort);
|
||||
|
||||
if (peer->getNodePublic () == getApp().getLocalCredentials ().getNodePublic ())
|
||||
{
|
||||
// Talking to ourself. We will just back off. This lets us maybe advertise our outside address.
|
||||
|
||||
nothing (); // Do nothing, leave scheduled scanning.
|
||||
}
|
||||
else
|
||||
{
|
||||
// Talking with a different peer.
|
||||
ScopedLock sl (getApp().getWalletDB ()->getDBLock ());
|
||||
Database* db = getApp().getWalletDB ()->getDB ();
|
||||
|
||||
db->executeSQL (boost::str (boost::format ("UPDATE PeerIps SET ScanNext=NULL,ScanInterval=0 WHERE IpPort=%s;")
|
||||
% sqlEscape (strIpPort)));
|
||||
// XXX Check error.
|
||||
}
|
||||
|
||||
mScanning.reset ();
|
||||
|
||||
scanRefresh (); // Continue scanning.
|
||||
}
|
||||
}
|
||||
|
||||
void Peers::scanHandler (const boost::system::error_code& ecResult)
|
||||
{
|
||||
if (ecResult == boost::asio::error::operation_aborted)
|
||||
{
|
||||
nothing ();
|
||||
}
|
||||
else if (!ecResult)
|
||||
{
|
||||
scanRefresh ();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error ("Internal error: unexpected deadline error.");
|
||||
}
|
||||
}
|
||||
|
||||
void Peers::makeConfigured ()
|
||||
{
|
||||
if (theConfig.RUN_STANDALONE)
|
||||
return;
|
||||
|
||||
BOOST_FOREACH (const std::string & strPeer, theConfig.IPS)
|
||||
{
|
||||
std::string strIP;
|
||||
int iPort;
|
||||
|
||||
if (parseIpPort (strPeer, strIP, iPort))
|
||||
peerConnect (strIP, iPort);
|
||||
}
|
||||
}
|
||||
|
||||
// Scan ips as per db entries.
|
||||
void Peers::scanRefresh ()
|
||||
{
|
||||
if (theConfig.RUN_STANDALONE)
|
||||
{
|
||||
nothing ();
|
||||
}
|
||||
else if (mScanning)
|
||||
{
|
||||
// Currently scanning, will scan again after completion.
|
||||
WriteLog (lsTRACE, Peers) << "Pool: Scan: already scanning";
|
||||
|
||||
nothing ();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Discover if there are entries that need scanning.
|
||||
boost::posix_time::ptime tpNext;
|
||||
boost::posix_time::ptime tpNow;
|
||||
std::string strIpPort;
|
||||
int iInterval;
|
||||
|
||||
{
|
||||
ScopedLock sl (getApp().getWalletDB ()->getDBLock ());
|
||||
Database* db = getApp().getWalletDB ()->getDB ();
|
||||
|
||||
if (db->executeSQL ("SELECT * FROM PeerIps INDEXED BY PeerScanIndex WHERE ScanNext NOT NULL ORDER BY ScanNext LIMIT 1;")
|
||||
&& db->startIterRows ())
|
||||
{
|
||||
// Have an entry to scan.
|
||||
int iNext = db->getInt ("ScanNext");
|
||||
|
||||
tpNext = ptFromSeconds (iNext);
|
||||
tpNow = boost::posix_time::second_clock::universal_time ();
|
||||
|
||||
db->getStr ("IpPort", strIpPort);
|
||||
iInterval = db->getInt ("ScanInterval");
|
||||
db->endIterRows ();
|
||||
}
|
||||
else
|
||||
{
|
||||
// No entries to scan.
|
||||
tpNow = boost::posix_time::ptime (boost::posix_time::not_a_date_time);
|
||||
}
|
||||
}
|
||||
|
||||
if (tpNow.is_not_a_date_time ())
|
||||
{
|
||||
//WriteLog (lsINFO, Peers) << "Pool: Scan: stop.";
|
||||
|
||||
(void) mScanTimer.cancel ();
|
||||
}
|
||||
else if (tpNext <= tpNow)
|
||||
{
|
||||
// Scan it.
|
||||
splitIpPort (strIpPort, mScanIp, mScanPort);
|
||||
|
||||
(void) mScanTimer.cancel ();
|
||||
|
||||
iInterval = std::max (iInterval, theConfig.PEER_SCAN_INTERVAL_MIN);
|
||||
|
||||
tpNext = tpNow + boost::posix_time::seconds (iInterval);
|
||||
|
||||
//WriteLog (lsINFO, Peers) << str(boost::format("Pool: Scan: Now: %s %s (next %s, delay=%d)")
|
||||
// % mScanIp % mScanPort % tpNext % (tpNext-tpNow).total_seconds());
|
||||
|
||||
iInterval *= 2;
|
||||
|
||||
{
|
||||
ScopedLock sl (getApp().getWalletDB ()->getDBLock ());
|
||||
Database* db = getApp().getWalletDB ()->getDB ();
|
||||
|
||||
db->executeSQL (boost::str (boost::format ("UPDATE PeerIps SET ScanNext=%d,ScanInterval=%d WHERE IpPort=%s;")
|
||||
% iToSeconds (tpNext)
|
||||
% iInterval
|
||||
% sqlEscape (strIpPort)));
|
||||
// XXX Check error.
|
||||
}
|
||||
|
||||
mScanning = peerConnect (mScanIp, mScanPort);
|
||||
|
||||
if (!mScanning)
|
||||
{
|
||||
// Already connected. Try again.
|
||||
scanRefresh ();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//WriteLog (lsINFO, Peers) << str(boost::format("Pool: Scan: Next: %s (next %s, delay=%d)")
|
||||
// % strIpPort % tpNext % (tpNext-tpNow).total_seconds());
|
||||
|
||||
mScanTimer.expires_at (tpNext);
|
||||
mScanTimer.async_wait (BIND_TYPE (&Peers::scanHandler, this, P_1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IPeers* IPeers::New (boost::asio::io_service& io_service)
|
||||
{
|
||||
return new Peers (io_service);
|
||||
}
|
||||
|
||||
#if 0
|
||||
bool Peers::isMessageKnown (PackedMessage::pointer msg)
|
||||
{
|
||||
for (unsigned int n = 0; n < mBroadcastMessages.size (); n++)
|
||||
{
|
||||
if (msg == mBroadcastMessages[n].first) return (false);
|
||||
}
|
||||
|
||||
return (false);
|
||||
}
|
||||
#endif
|
||||
|
||||
SETUP_LOG (Peers)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user