Move all remaining files into modules

This commit is contained in:
Vinnie Falco
2013-07-23 18:41:10 -07:00
parent bf3d3b7535
commit 58ee1b2019
150 changed files with 1112 additions and 1117 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,7 +0,0 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
SETUP_LOGN (WSConnectionLog,"WSConnection")

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,11 +0,0 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
AccountItem::AccountItem (SerializedLedgerEntry::ref ledger)
: mLedgerEntry (ledger)
{
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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