mirror of
https://github.com/XRPLF/rippled.git
synced 2026-04-29 15:37:57 +00:00
Merge branch 'develop' of github.com:ripple/rippled into develop
This commit is contained in:
137
modules/ripple_app/consensus/ripple_DisputedTx.cpp
Normal file
137
modules/ripple_app/consensus/ripple_DisputedTx.cpp
Normal file
@@ -0,0 +1,137 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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;
|
||||
}
|
||||
|
||||
75
modules/ripple_app/consensus/ripple_DisputedTx.h
Normal file
75
modules/ripple_app/consensus/ripple_DisputedTx.h
Normal file
@@ -0,0 +1,75 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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
|
||||
1486
modules/ripple_app/consensus/ripple_LedgerConsensus.cpp
Normal file
1486
modules/ripple_app/consensus/ripple_LedgerConsensus.cpp
Normal file
File diff suppressed because it is too large
Load Diff
160
modules/ripple_app/consensus/ripple_LedgerConsensus.h
Normal file
160
modules/ripple_app/consensus/ripple_LedgerConsensus.h
Normal file
@@ -0,0 +1,160 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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
|
||||
178
modules/ripple_app/main/ParameterTable.cpp
Normal file
178
modules/ripple_app/main/ParameterTable.cpp
Normal file
@@ -0,0 +1,178 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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;
|
||||
}
|
||||
86
modules/ripple_app/main/ParameterTable.h
Normal file
86
modules/ripple_app/main/ParameterTable.h
Normal file
@@ -0,0 +1,86 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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
|
||||
994
modules/ripple_app/main/ripple_Application.cpp
Normal file
994
modules/ripple_app/main/ripple_Application.cpp
Normal file
@@ -0,0 +1,994 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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 ();
|
||||
}
|
||||
208
modules/ripple_app/main/ripple_Application.h
Normal file
208
modules/ripple_app/main/ripple_Application.h
Normal file
@@ -0,0 +1,208 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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
|
||||
276
modules/ripple_app/main/ripple_ILoadManager.h
Normal file
276
modules/ripple_app/main/ripple_ILoadManager.h
Normal file
@@ -0,0 +1,276 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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
|
||||
426
modules/ripple_app/main/ripple_LoadManager.cpp
Normal file
426
modules/ripple_app/main/ripple_LoadManager.cpp
Normal file
@@ -0,0 +1,426 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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;
|
||||
}
|
||||
170
modules/ripple_app/main/ripple_LocalCredentials.cpp
Normal file
170
modules/ripple_app/main/ripple_LocalCredentials.cpp
Normal file
@@ -0,0 +1,170 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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
|
||||
67
modules/ripple_app/main/ripple_LocalCredentials.h
Normal file
67
modules/ripple_app/main/ripple_LocalCredentials.h
Normal file
@@ -0,0 +1,67 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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
|
||||
450
modules/ripple_app/main/ripple_Main.cpp
Normal file
450
modules/ripple_app/main/ripple_Main.cpp
Normal file
@@ -0,0 +1,450 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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
|
||||
2416
modules/ripple_app/misc/NetworkOPs.cpp
Normal file
2416
modules/ripple_app/misc/NetworkOPs.cpp
Normal file
File diff suppressed because it is too large
Load Diff
444
modules/ripple_app/misc/NetworkOPs.h
Normal file
444
modules/ripple_app/misc/NetworkOPs.h
Normal file
@@ -0,0 +1,444 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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
|
||||
11
modules/ripple_app/misc/ripple_AccountItem.cpp
Normal file
11
modules/ripple_app/misc/ripple_AccountItem.cpp
Normal file
@@ -0,0 +1,11 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
AccountItem::AccountItem (SerializedLedgerEntry::ref ledger)
|
||||
: mLedgerEntry (ledger)
|
||||
{
|
||||
|
||||
}
|
||||
74
modules/ripple_app/misc/ripple_AccountItem.h
Normal file
74
modules/ripple_app/misc/ripple_AccountItem.h
Normal file
@@ -0,0 +1,74 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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
|
||||
68
modules/ripple_app/misc/ripple_AccountItems.cpp
Normal file
68
modules/ripple_app/misc/ripple_AccountItems.cpp
Normal file
@@ -0,0 +1,68 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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
|
||||
45
modules/ripple_app/misc/ripple_AccountItems.h
Normal file
45
modules/ripple_app/misc/ripple_AccountItems.h
Normal file
@@ -0,0 +1,45 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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
|
||||
69
modules/ripple_app/misc/ripple_AccountState.cpp
Normal file
69
modules/ripple_app/misc/ripple_AccountState.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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
|
||||
77
modules/ripple_app/misc/ripple_AccountState.h
Normal file
77
modules/ripple_app/misc/ripple_AccountState.h
Normal file
@@ -0,0 +1,77 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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
|
||||
78
modules/ripple_app/misc/ripple_CanonicalTXSet.cpp
Normal file
78
modules/ripple_app/misc/ripple_CanonicalTXSet.cpp
Normal file
@@ -0,0 +1,78 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
109
modules/ripple_app/misc/ripple_CanonicalTXSet.h
Normal file
109
modules/ripple_app/misc/ripple_CanonicalTXSet.h
Normal file
@@ -0,0 +1,109 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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
|
||||
541
modules/ripple_app/misc/ripple_Features.cpp
Normal file
541
modules/ripple_app/misc/ripple_Features.cpp
Normal file
@@ -0,0 +1,541 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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
|
||||
194
modules/ripple_app/misc/ripple_FeeVote.cpp
Normal file
194
modules/ripple_app/misc/ripple_FeeVote.cpp
Normal file
@@ -0,0 +1,194 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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
|
||||
226
modules/ripple_app/misc/ripple_HashRouter.cpp
Normal file
226
modules/ripple_app/misc/ripple_HashRouter.cpp
Normal file
@@ -0,0 +1,226 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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);
|
||||
}
|
||||
130
modules/ripple_app/misc/ripple_IFeatures.h
Normal file
130
modules/ripple_app/misc/ripple_IFeatures.h
Normal file
@@ -0,0 +1,130 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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
|
||||
44
modules/ripple_app/misc/ripple_IFeeVote.h
Normal file
44
modules/ripple_app/misc/ripple_IFeeVote.h
Normal file
@@ -0,0 +1,44 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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
|
||||
68
modules/ripple_app/misc/ripple_IHashRouter.h
Normal file
68
modules/ripple_app/misc/ripple_IHashRouter.h
Normal file
@@ -0,0 +1,68 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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
|
||||
60
modules/ripple_app/misc/ripple_IProofOfWorkFactory.h
Normal file
60
modules/ripple_app/misc/ripple_IProofOfWorkFactory.h
Normal file
@@ -0,0 +1,60 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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
|
||||
48
modules/ripple_app/misc/ripple_IValidations.h
Normal file
48
modules/ripple_app/misc/ripple_IValidations.h
Normal file
@@ -0,0 +1,48 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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
|
||||
73
modules/ripple_app/misc/ripple_InfoSub.cpp
Normal file
73
modules/ripple_app/misc/ripple_InfoSub.cpp
Normal file
@@ -0,0 +1,73 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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;
|
||||
}
|
||||
66
modules/ripple_app/misc/ripple_InfoSub.h
Normal file
66
modules/ripple_app/misc/ripple_InfoSub.h
Normal file
@@ -0,0 +1,66 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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
|
||||
33
modules/ripple_app/misc/ripple_NicknameState.cpp
Normal file
33
modules/ripple_app/misc/ripple_NicknameState.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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);
|
||||
}
|
||||
49
modules/ripple_app/misc/ripple_NicknameState.h
Normal file
49
modules/ripple_app/misc/ripple_NicknameState.h
Normal file
@@ -0,0 +1,49 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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
|
||||
33
modules/ripple_app/misc/ripple_Offer.cpp
Normal file
33
modules/ripple_app/misc/ripple_Offer.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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
|
||||
55
modules/ripple_app/misc/ripple_Offer.h
Normal file
55
modules/ripple_app/misc/ripple_Offer.h
Normal file
@@ -0,0 +1,55 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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
|
||||
44
modules/ripple_app/misc/ripple_OrderBook.cpp
Normal file
44
modules/ripple_app/misc/ripple_OrderBook.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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;
|
||||
}
|
||||
57
modules/ripple_app/misc/ripple_OrderBook.h
Normal file
57
modules/ripple_app/misc/ripple_OrderBook.h
Normal file
@@ -0,0 +1,57 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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
|
||||
181
modules/ripple_app/misc/ripple_ProofOfWork.cpp
Normal file
181
modules/ripple_app/misc/ripple_ProofOfWork.cpp
Normal file
@@ -0,0 +1,181 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
SETUP_LOG (ProofOfWork)
|
||||
|
||||
bool powResultInfo (POWResult powCode, std::string& strToken, std::string& strHuman)
|
||||
{
|
||||
static struct
|
||||
{
|
||||
POWResult powCode;
|
||||
const char* cpToken;
|
||||
const char* cpHuman;
|
||||
} powResultInfoA[] =
|
||||
{
|
||||
{ powREUSED, "powREUSED", "Proof-of-work has already been used." },
|
||||
{ powBADNONCE, "powBADNONCE", "The solution does not meet the required difficulty." },
|
||||
{ powEXPIRED, "powEXPIRED", "Token is expired." },
|
||||
{ powCORRUPT, "powCORRUPT", "Invalid token." },
|
||||
{ powTOOEASY, "powTOOEASY", "Difficulty has increased since token was issued." },
|
||||
|
||||
{ powOK, "powOK", "Valid proof-of-work." },
|
||||
};
|
||||
|
||||
int iIndex = NUMBER (powResultInfoA);
|
||||
|
||||
while (iIndex-- && powResultInfoA[iIndex].powCode != powCode)
|
||||
;
|
||||
|
||||
if (iIndex >= 0)
|
||||
{
|
||||
strToken = powResultInfoA[iIndex].cpToken;
|
||||
strHuman = powResultInfoA[iIndex].cpHuman;
|
||||
}
|
||||
|
||||
return iIndex >= 0;
|
||||
}
|
||||
|
||||
// VFALCO TODO Move these to a header because they are used by ripple_ProofOfWorkFactory.cpp
|
||||
const uint256 ProofOfWork::sMinTarget ("00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
|
||||
const int ProofOfWork::sMaxIterations (1 << 23);
|
||||
const int ProofOfWork::sMaxDifficulty (30);
|
||||
|
||||
ProofOfWork::ProofOfWork (const std::string& token,
|
||||
int iterations,
|
||||
uint256 const& challenge,
|
||||
uint256 const& target)
|
||||
: mToken (token)
|
||||
, mChallenge (challenge)
|
||||
, mTarget (target)
|
||||
, mIterations (iterations)
|
||||
{
|
||||
}
|
||||
|
||||
ProofOfWork::ProofOfWork (const std::string& token)
|
||||
{
|
||||
std::vector<std::string> fields;
|
||||
boost::split (fields, token, boost::algorithm::is_any_of ("-"));
|
||||
|
||||
if (fields.size () != 5)
|
||||
throw std::runtime_error ("invalid token");
|
||||
|
||||
mToken = token;
|
||||
mChallenge.SetHex (fields[0]);
|
||||
mTarget.SetHex (fields[1]);
|
||||
mIterations = lexical_cast_s<int> (fields[2]);
|
||||
}
|
||||
|
||||
bool ProofOfWork::isValid () const
|
||||
{
|
||||
if ((mIterations <= sMaxIterations) && (mTarget >= sMinTarget))
|
||||
return true;
|
||||
|
||||
WriteLog (lsWARNING, ProofOfWork) << "Invalid PoW: " << mIterations << ", " << mTarget;
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64 ProofOfWork::getDifficulty (uint256 const& target, int iterations)
|
||||
{
|
||||
// calculate the approximate number of hashes required to solve this proof of work
|
||||
if ((iterations > sMaxIterations) || (target < sMinTarget))
|
||||
{
|
||||
WriteLog (lsINFO, ProofOfWork) << "Iterations:" << iterations;
|
||||
WriteLog (lsINFO, ProofOfWork) << "MaxIterat: " << sMaxIterations;
|
||||
WriteLog (lsINFO, ProofOfWork) << "Target: " << target;
|
||||
WriteLog (lsINFO, ProofOfWork) << "MinTarget: " << sMinTarget;
|
||||
throw std::runtime_error ("invalid proof of work target/iteration");
|
||||
}
|
||||
|
||||
// more iterations means more hashes per iteration but also a larger final hash
|
||||
uint64 difficulty = iterations + (iterations / 8);
|
||||
|
||||
// Multiply the number of hashes needed by 256 for each leading zero byte in the difficulty
|
||||
const unsigned char* ptr = target.begin ();
|
||||
|
||||
while (*ptr == 0)
|
||||
{
|
||||
difficulty *= 256;
|
||||
++ptr;
|
||||
}
|
||||
|
||||
difficulty = (difficulty * 256) / (*ptr + 1);
|
||||
|
||||
return difficulty;
|
||||
}
|
||||
|
||||
static uint256 getSHA512Half (const std::vector<uint256>& vec)
|
||||
{
|
||||
return Serializer::getSHA512Half (vec.front ().begin (), vec.size () * (256 / 8));
|
||||
}
|
||||
|
||||
uint256 ProofOfWork::solve (int maxIterations) const
|
||||
{
|
||||
if (!isValid ())
|
||||
throw std::runtime_error ("invalid proof of work target/iteration");
|
||||
|
||||
uint256 nonce;
|
||||
RandomNumbers::getInstance ().fill (&nonce);
|
||||
|
||||
std::vector<uint256> buf2;
|
||||
buf2.resize (mIterations);
|
||||
|
||||
std::vector<uint256> buf1;
|
||||
buf1.resize (3);
|
||||
buf1[0] = mChallenge;
|
||||
|
||||
while (maxIterations > 0)
|
||||
{
|
||||
buf1[1] = nonce;
|
||||
buf1[2].zero ();
|
||||
|
||||
for (int i = (mIterations - 1); i >= 0; --i)
|
||||
{
|
||||
buf1[2] = getSHA512Half (buf1);
|
||||
buf2[i] = buf1[2];
|
||||
}
|
||||
|
||||
if (getSHA512Half (buf2) <= mTarget)
|
||||
return nonce;
|
||||
|
||||
++nonce;
|
||||
--maxIterations;
|
||||
}
|
||||
|
||||
return uint256 ();
|
||||
}
|
||||
|
||||
bool ProofOfWork::checkSolution (uint256 const& solution) const
|
||||
{
|
||||
if (mIterations > sMaxIterations)
|
||||
return false;
|
||||
|
||||
std::vector<uint256> buf1;
|
||||
buf1.push_back (mChallenge);
|
||||
buf1.push_back (solution);
|
||||
buf1.push_back (uint256 ());
|
||||
|
||||
std::vector<uint256> buf2;
|
||||
buf2.resize (mIterations);
|
||||
|
||||
for (int i = (mIterations - 1); i >= 0; --i)
|
||||
{
|
||||
buf1[2] = getSHA512Half (buf1);
|
||||
buf2[i] = buf1[2];
|
||||
}
|
||||
|
||||
return getSHA512Half (buf2) <= mTarget;
|
||||
}
|
||||
|
||||
bool ProofOfWork::validateToken (const std::string& strToken)
|
||||
{
|
||||
static boost::regex reToken ("[[:xdigit:]]{64}-[[:xdigit:]]{64}-[[:digit:]]+-[[:digit:]]+-[[:xdigit:]]{64}");
|
||||
boost::smatch smMatch;
|
||||
|
||||
return boost::regex_match (strToken, smMatch, reToken);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
60
modules/ripple_app/misc/ripple_ProofOfWork.h
Normal file
60
modules/ripple_app/misc/ripple_ProofOfWork.h
Normal file
@@ -0,0 +1,60 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_PROOFOFWORK_H
|
||||
#define RIPPLE_PROOFOFWORK_H
|
||||
|
||||
class ProofOfWork : LeakChecked <ProofOfWork>
|
||||
{
|
||||
public:
|
||||
static const int sMaxDifficulty;
|
||||
|
||||
typedef boost::shared_ptr <ProofOfWork> pointer;
|
||||
|
||||
ProofOfWork (const std::string& token,
|
||||
int iterations,
|
||||
uint256 const& challenge,
|
||||
uint256 const& target);
|
||||
|
||||
explicit ProofOfWork (const std::string& token);
|
||||
|
||||
bool isValid () const;
|
||||
|
||||
uint256 solve (int maxIterations = 2 * sMaxIterations) const;
|
||||
bool checkSolution (uint256 const& solution) const;
|
||||
|
||||
const std::string& getToken () const
|
||||
{
|
||||
return mToken;
|
||||
}
|
||||
uint256 const& getChallenge () const
|
||||
{
|
||||
return mChallenge;
|
||||
}
|
||||
|
||||
uint64 getDifficulty () const
|
||||
{
|
||||
return getDifficulty (mTarget, mIterations);
|
||||
}
|
||||
|
||||
// approximate number of hashes needed to solve
|
||||
static uint64 getDifficulty (uint256 const& target, int iterations);
|
||||
|
||||
static bool validateToken (const std::string& strToken);
|
||||
|
||||
private:
|
||||
std::string mToken;
|
||||
uint256 mChallenge;
|
||||
uint256 mTarget;
|
||||
int mIterations;
|
||||
|
||||
static const uint256 sMinTarget;
|
||||
static const int sMaxIterations;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
// vim:ts=4
|
||||
302
modules/ripple_app/misc/ripple_ProofOfWorkFactory.cpp
Normal file
302
modules/ripple_app/misc/ripple_ProofOfWorkFactory.cpp
Normal file
@@ -0,0 +1,302 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
ProofOfWorkFactory::ProofOfWorkFactory () : mValidTime (180)
|
||||
{
|
||||
setDifficulty (1);
|
||||
RandomNumbers::getInstance ().fillBytes (mSecret.begin (), mSecret.size ());
|
||||
}
|
||||
|
||||
ProofOfWork ProofOfWorkFactory::getProof ()
|
||||
{
|
||||
// challenge - target - iterations - time - validator
|
||||
static boost::format f ("%s-%s-%d-%d");
|
||||
|
||||
int now = static_cast<int> (time (NULL) / 4);
|
||||
|
||||
uint256 challenge;
|
||||
RandomNumbers::getInstance ().fillBytes (challenge.begin (), challenge.size ());
|
||||
|
||||
boost::mutex::scoped_lock sl (mLock);
|
||||
|
||||
std::string s = boost::str (boost::format (f) % challenge.GetHex () % mTarget.GetHex () % mIterations % now);
|
||||
std::string c = mSecret.GetHex () + s;
|
||||
s += "-" + Serializer::getSHA512Half (c).GetHex ();
|
||||
|
||||
return ProofOfWork (s, mIterations, challenge, mTarget);
|
||||
}
|
||||
|
||||
POWResult ProofOfWorkFactory::checkProof (const std::string& token, uint256 const& solution)
|
||||
{
|
||||
// challenge - target - iterations - time - validator
|
||||
|
||||
std::vector<std::string> fields;
|
||||
boost::split (fields, token, boost::algorithm::is_any_of ("-"));
|
||||
|
||||
if (fields.size () != 5)
|
||||
{
|
||||
WriteLog (lsDEBUG, ProofOfWork) << "PoW " << token << " is corrupt";
|
||||
return powCORRUPT;
|
||||
}
|
||||
|
||||
std::string v = mSecret.GetHex () + fields[0] + "-" + fields[1] + "-" + fields[2] + "-" + fields[3];
|
||||
|
||||
if (fields[4] != Serializer::getSHA512Half (v).GetHex ())
|
||||
{
|
||||
WriteLog (lsDEBUG, ProofOfWork) << "PoW " << token << " has a bad token";
|
||||
return powCORRUPT;
|
||||
}
|
||||
|
||||
uint256 challenge, target;
|
||||
challenge.SetHex (fields[0]);
|
||||
target.SetHex (fields[1]);
|
||||
|
||||
time_t t = lexical_cast_s<time_t> (fields[3]);
|
||||
time_t now = time (NULL);
|
||||
|
||||
int iterations = lexical_cast_s<int> (fields[2]);
|
||||
|
||||
{
|
||||
boost::mutex::scoped_lock sl (mLock);
|
||||
|
||||
if ((t * 4) > (now + mValidTime))
|
||||
{
|
||||
WriteLog (lsDEBUG, ProofOfWork) << "PoW " << token << " has expired";
|
||||
return powEXPIRED;
|
||||
}
|
||||
|
||||
if (((iterations != mIterations) || (target != mTarget)) && getPowEntry (target, iterations) < (mPowEntry - 2))
|
||||
{
|
||||
// difficulty has increased more than two times since PoW requested
|
||||
WriteLog (lsINFO, ProofOfWork) << "Difficulty has increased since PoW requested";
|
||||
return powTOOEASY;
|
||||
}
|
||||
}
|
||||
|
||||
ProofOfWork pow (token, iterations, challenge, target);
|
||||
|
||||
if (!pow.checkSolution (solution))
|
||||
{
|
||||
WriteLog (lsDEBUG, ProofOfWork) << "PoW " << token << " has a bad nonce";
|
||||
return powBADNONCE;
|
||||
}
|
||||
|
||||
{
|
||||
boost::mutex::scoped_lock sl (mLock);
|
||||
|
||||
if (!mSolvedChallenges.insert (powMap_vt (now, challenge)).second)
|
||||
{
|
||||
WriteLog (lsDEBUG, ProofOfWork) << "PoW " << token << " has been reused";
|
||||
return powREUSED;
|
||||
}
|
||||
}
|
||||
|
||||
return powOK;
|
||||
}
|
||||
|
||||
void ProofOfWorkFactory::sweep ()
|
||||
{
|
||||
time_t expire = time (NULL) - mValidTime;
|
||||
|
||||
boost::mutex::scoped_lock sl (mLock);
|
||||
|
||||
do
|
||||
{
|
||||
powMap_t::left_map::iterator it = mSolvedChallenges.left.begin ();
|
||||
|
||||
if (it == mSolvedChallenges.left.end ())
|
||||
return;
|
||||
|
||||
if (it->first >= expire)
|
||||
return;
|
||||
|
||||
mSolvedChallenges.left.erase (it);
|
||||
}
|
||||
while (1);
|
||||
}
|
||||
|
||||
void ProofOfWorkFactory::loadHigh ()
|
||||
{
|
||||
time_t now = time (NULL);
|
||||
|
||||
boost::mutex::scoped_lock sl (mLock);
|
||||
|
||||
if (mLastDifficultyChange == now)
|
||||
return;
|
||||
|
||||
if (mPowEntry == 30)
|
||||
return;
|
||||
|
||||
++mPowEntry;
|
||||
mLastDifficultyChange = now;
|
||||
}
|
||||
|
||||
void ProofOfWorkFactory::loadLow ()
|
||||
{
|
||||
time_t now = time (NULL);
|
||||
|
||||
boost::mutex::scoped_lock sl (mLock);
|
||||
|
||||
if (mLastDifficultyChange == now)
|
||||
return;
|
||||
|
||||
if (mPowEntry == 0)
|
||||
return;
|
||||
|
||||
--mPowEntry;
|
||||
mLastDifficultyChange = now;
|
||||
}
|
||||
|
||||
struct PowEntry
|
||||
{
|
||||
const char* target;
|
||||
int iterations;
|
||||
};
|
||||
|
||||
PowEntry PowEntries[ProofOfWork::sMaxDifficulty + 1] =
|
||||
{
|
||||
// target iterations hashes memory
|
||||
{ "0CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 65536 }, // 1451874, 2 MB
|
||||
{ "0CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 98304 }, // 2177811, 3 MB
|
||||
{ "07FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 98304 }, // 3538944, 3 MB
|
||||
{ "0CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 196608}, // 4355623, 6 MB
|
||||
|
||||
{ "07FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 131072}, // 4718592, 4 MB
|
||||
{ "0CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 262144}, // 5807497, 8 MB
|
||||
{ "07FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 196608}, // 7077888, 6 MB
|
||||
{ "07FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 262144}, // 9437184, 8 MB
|
||||
|
||||
{ "07FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 393216}, // 14155776, 12MB
|
||||
{ "03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 393216}, // 28311552, 12MB
|
||||
{ "00CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 262144}, // 92919965, 8 MB
|
||||
{ "00CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 393216}, // 139379948, 12MB
|
||||
|
||||
{ "007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 262144}, // 150994944, 8 MB
|
||||
{ "007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 393216}, // 226492416, 12MB
|
||||
{ "000CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 49152 }, // 278759896, 1.5MB
|
||||
{ "003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 262144}, // 301989888, 8 MB
|
||||
|
||||
{ "003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 393216}, // 452984832, 12MB
|
||||
{ "0007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 98304 }, // 905969664, 3 MB
|
||||
{ "000CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 196608}, // 1115039586, 6 MB
|
||||
{ "000CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 262144}, // 1486719448 8 MB
|
||||
|
||||
{ "000CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 393216}, // 2230079172 12MB
|
||||
{ "0007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 262144}, // 2415919104, 8 MB
|
||||
{ "0007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 393216}, // 3623878656, 12MB
|
||||
{ "0003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 393216}, // 7247757312, 12MB
|
||||
|
||||
{ "0000CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 262144}, // 23787511177, 8 MB
|
||||
{ "0000CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 393216}, // 35681266766, 12MB
|
||||
{ "00003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 131072}, // 38654705664, 4 MB
|
||||
{ "00007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 262144}, // 38654705664, 8 MB
|
||||
|
||||
{ "00003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 196608}, // 57982058496, 6 MB
|
||||
{ "00007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 393216}, // 57982058496, 12MB
|
||||
{ "00003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 262144}, // 77309411328, 8 MB
|
||||
};
|
||||
|
||||
int ProofOfWorkFactory::getPowEntry (uint256 const& target, int iterations)
|
||||
{
|
||||
for (int i = 0; i < 31; ++i)
|
||||
if (PowEntries[i].iterations == iterations)
|
||||
{
|
||||
uint256 t;
|
||||
t.SetHex (PowEntries[i].target);
|
||||
|
||||
if (t == target)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void ProofOfWorkFactory::setDifficulty (int i)
|
||||
{
|
||||
assert ((i >= 0) && (i <= ProofOfWork::sMaxDifficulty));
|
||||
time_t now = time (NULL);
|
||||
|
||||
boost::mutex::scoped_lock sl (mLock);
|
||||
mPowEntry = i;
|
||||
mIterations = PowEntries[i].iterations;
|
||||
mTarget.SetHex (PowEntries[i].target);
|
||||
mLastDifficultyChange = now;
|
||||
}
|
||||
|
||||
IProofOfWorkFactory* IProofOfWorkFactory::New ()
|
||||
{
|
||||
return new ProofOfWorkFactory;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class ProofOfWorkTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
ProofOfWorkTests () : UnitTest ("ProofOfWork", "ripple", UnitTest::runManual)
|
||||
{
|
||||
}
|
||||
|
||||
void runTest ()
|
||||
{
|
||||
using namespace ripple;
|
||||
|
||||
ProofOfWorkFactory gen;
|
||||
ProofOfWork pow = gen.getProof ();
|
||||
|
||||
String s;
|
||||
|
||||
s << "solve difficulty " << String (pow.getDifficulty ());
|
||||
beginTest ("solve");
|
||||
|
||||
uint256 solution = pow.solve (16777216);
|
||||
|
||||
expect (! solution.isZero (), "Should be solved");
|
||||
|
||||
expect (pow.checkSolution (solution), "Should be checked");
|
||||
|
||||
// Why is this emitted?
|
||||
//WriteLog (lsDEBUG, ProofOfWork) << "A bad nonce error is expected";
|
||||
|
||||
POWResult r = gen.checkProof (pow.getToken (), uint256 ());
|
||||
|
||||
expect (r == powBADNONCE, "Should show bad nonce for empty solution");
|
||||
|
||||
expect (gen.checkProof (pow.getToken (), solution) == powOK, "Solution should check with issuer");
|
||||
|
||||
//WriteLog (lsDEBUG, ProofOfWork) << "A reused nonce error is expected";
|
||||
|
||||
expect (gen.checkProof (pow.getToken (), solution) == powREUSED, "Reuse solution should be detected");
|
||||
|
||||
#ifdef SOLVE_POWS
|
||||
|
||||
for (int i = 0; i < 12; ++i)
|
||||
{
|
||||
gen.setDifficulty (i);
|
||||
ProofOfWork pow = gen.getProof ();
|
||||
WriteLog (lsINFO, ProofOfWork) << "Level: " << i << ", Estimated difficulty: " << pow.getDifficulty ();
|
||||
uint256 solution = pow.solve (131072);
|
||||
|
||||
if (solution.isZero ())
|
||||
{
|
||||
//WriteLog (lsINFO, ProofOfWork) << "Giving up";
|
||||
}
|
||||
else
|
||||
{
|
||||
//WriteLog (lsINFO, ProofOfWork) << "Solution found";
|
||||
|
||||
if (gen.checkProof (pow.getToken (), solution) != powOK)
|
||||
{
|
||||
//WriteLog (lsFATAL, ProofOfWork) << "Solution fails";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
static ProofOfWorkTests proofOfWorkTests;
|
||||
54
modules/ripple_app/misc/ripple_ProofOfWorkFactory.h
Normal file
54
modules/ripple_app/misc/ripple_ProofOfWorkFactory.h
Normal file
@@ -0,0 +1,54 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_PROOFOFWORKFACTORY_RIPPLEHEADER
|
||||
#define RIPPLE_PROOFOFWORKFACTORY_RIPPLEHEADER
|
||||
|
||||
// PRIVATE HEADER
|
||||
|
||||
class ProofOfWorkFactory
|
||||
: public IProofOfWorkFactory
|
||||
, LeakChecked <ProofOfWorkFactory>
|
||||
{
|
||||
public:
|
||||
ProofOfWorkFactory ();
|
||||
|
||||
ProofOfWork getProof ();
|
||||
POWResult checkProof (const std::string& token, uint256 const& solution);
|
||||
uint64 getDifficulty ()
|
||||
{
|
||||
return ProofOfWork::getDifficulty (mTarget, mIterations);
|
||||
}
|
||||
void setDifficulty (int i);
|
||||
|
||||
void loadHigh ();
|
||||
void loadLow ();
|
||||
void sweep (void);
|
||||
|
||||
uint256 const& getSecret () const
|
||||
{
|
||||
return mSecret;
|
||||
}
|
||||
void setSecret (uint256 const& secret)
|
||||
{
|
||||
mSecret = secret;
|
||||
}
|
||||
|
||||
static int getPowEntry (uint256 const& target, int iterations);
|
||||
|
||||
private:
|
||||
uint256 mSecret;
|
||||
int mIterations;
|
||||
uint256 mTarget;
|
||||
time_t mLastDifficultyChange;
|
||||
int mValidTime;
|
||||
int mPowEntry;
|
||||
|
||||
powMap_t mSolvedChallenges;
|
||||
boost::mutex mLock;
|
||||
};
|
||||
|
||||
#endif
|
||||
204
modules/ripple_app/misc/ripple_SerializedLedger.cpp
Normal file
204
modules/ripple_app/misc/ripple_SerializedLedger.cpp
Normal file
@@ -0,0 +1,204 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
struct SerializedLedgerLog; // for Log
|
||||
|
||||
SETUP_LOGN (SerializedLedgerLog,"SerializedLedger")
|
||||
|
||||
SerializedLedgerEntry::SerializedLedgerEntry (SerializerIterator& sit, uint256 const& index)
|
||||
: STObject (sfLedgerEntry), mIndex (index), mMutable (true)
|
||||
{
|
||||
set (sit);
|
||||
uint16 type = getFieldU16 (sfLedgerEntryType);
|
||||
|
||||
LedgerFormats::Item const* const item =
|
||||
LedgerFormats::getInstance()->findByType (static_cast <LedgerEntryType> (type));
|
||||
|
||||
if (item == nullptr)
|
||||
throw std::runtime_error ("invalid ledger entry type");
|
||||
|
||||
mType = item->getType ();
|
||||
|
||||
if (!setType (item->elements))
|
||||
throw std::runtime_error ("ledger entry not valid for type");
|
||||
}
|
||||
|
||||
SerializedLedgerEntry::SerializedLedgerEntry (const Serializer& s, uint256 const& index)
|
||||
: STObject (sfLedgerEntry), mIndex (index), mMutable (true)
|
||||
{
|
||||
SerializerIterator sit (const_cast<Serializer&> (s)); // we know 's' isn't going away
|
||||
set (sit);
|
||||
|
||||
uint16 type = getFieldU16 (sfLedgerEntryType);
|
||||
|
||||
LedgerFormats::Item const* const item =
|
||||
LedgerFormats::getInstance()->findByType (static_cast <LedgerEntryType> (type));
|
||||
|
||||
if (item == nullptr)
|
||||
throw std::runtime_error ("invalid ledger entry type");
|
||||
|
||||
mType = item->getType ();
|
||||
|
||||
if (!setType (item->elements))
|
||||
{
|
||||
WriteLog (lsWARNING, SerializedLedgerLog) << "Ledger entry not valid for type " << mFormat->getName ();
|
||||
WriteLog (lsWARNING, SerializedLedgerLog) << getJson (0);
|
||||
throw std::runtime_error ("ledger entry not valid for type");
|
||||
}
|
||||
}
|
||||
|
||||
SerializedLedgerEntry::SerializedLedgerEntry (LedgerEntryType type, uint256 const& index) :
|
||||
STObject (sfLedgerEntry), mIndex (index), mType (type), mMutable (true)
|
||||
{
|
||||
LedgerFormats::Item const* const item =
|
||||
LedgerFormats::getInstance()->findByType (type);
|
||||
|
||||
if (item != nullptr)
|
||||
{
|
||||
set (item->elements);
|
||||
|
||||
setFieldU16 (sfLedgerEntryType, static_cast <uint16> (item->getType ()));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error ("invalid ledger entry type");
|
||||
}
|
||||
}
|
||||
|
||||
SerializedLedgerEntry::pointer SerializedLedgerEntry::getMutable () const
|
||||
{
|
||||
SerializedLedgerEntry::pointer ret = boost::make_shared<SerializedLedgerEntry> (boost::cref (*this));
|
||||
ret->mMutable = true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string SerializedLedgerEntry::getFullText () const
|
||||
{
|
||||
std::string ret = "\"";
|
||||
ret += mIndex.GetHex ();
|
||||
ret += "\" = { ";
|
||||
ret += mFormat->getName ();
|
||||
ret += ", ";
|
||||
ret += STObject::getFullText ();
|
||||
ret += "}";
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string SerializedLedgerEntry::getText () const
|
||||
{
|
||||
return str (boost::format ("{ %s, %s }")
|
||||
% mIndex.GetHex ()
|
||||
% STObject::getText ());
|
||||
}
|
||||
|
||||
Json::Value SerializedLedgerEntry::getJson (int options) const
|
||||
{
|
||||
Json::Value ret (STObject::getJson (options));
|
||||
|
||||
ret["index"] = mIndex.GetHex ();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool SerializedLedgerEntry::isThreadedType ()
|
||||
{
|
||||
return getFieldIndex (sfPreviousTxnID) != -1;
|
||||
}
|
||||
|
||||
bool SerializedLedgerEntry::isThreaded ()
|
||||
{
|
||||
return isFieldPresent (sfPreviousTxnID);
|
||||
}
|
||||
|
||||
uint256 SerializedLedgerEntry::getThreadedTransaction ()
|
||||
{
|
||||
return getFieldH256 (sfPreviousTxnID);
|
||||
}
|
||||
|
||||
uint32 SerializedLedgerEntry::getThreadedLedger ()
|
||||
{
|
||||
return getFieldU32 (sfPreviousTxnLgrSeq);
|
||||
}
|
||||
|
||||
bool SerializedLedgerEntry::thread (uint256 const& txID, uint32 ledgerSeq, uint256& prevTxID, uint32& prevLedgerID)
|
||||
{
|
||||
uint256 oldPrevTxID = getFieldH256 (sfPreviousTxnID);
|
||||
WriteLog (lsTRACE, SerializedLedgerLog) << "Thread Tx:" << txID << " prev:" << oldPrevTxID;
|
||||
|
||||
if (oldPrevTxID == txID)
|
||||
{
|
||||
// this transaction is already threaded
|
||||
assert (getFieldU32 (sfPreviousTxnLgrSeq) == ledgerSeq);
|
||||
return false;
|
||||
}
|
||||
|
||||
prevTxID = oldPrevTxID;
|
||||
prevLedgerID = getFieldU32 (sfPreviousTxnLgrSeq);
|
||||
setFieldH256 (sfPreviousTxnID, txID);
|
||||
setFieldU32 (sfPreviousTxnLgrSeq, ledgerSeq);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SerializedLedgerEntry::hasOneOwner ()
|
||||
{
|
||||
return (mType != ltACCOUNT_ROOT) && (getFieldIndex (sfAccount) != -1);
|
||||
}
|
||||
|
||||
bool SerializedLedgerEntry::hasTwoOwners ()
|
||||
{
|
||||
return mType == ltRIPPLE_STATE;
|
||||
}
|
||||
|
||||
RippleAddress SerializedLedgerEntry::getOwner ()
|
||||
{
|
||||
return getFieldAccount (sfAccount);
|
||||
}
|
||||
|
||||
RippleAddress SerializedLedgerEntry::getFirstOwner ()
|
||||
{
|
||||
return RippleAddress::createAccountID (getFieldAmount (sfLowLimit).getIssuer ());
|
||||
}
|
||||
|
||||
RippleAddress SerializedLedgerEntry::getSecondOwner ()
|
||||
{
|
||||
return RippleAddress::createAccountID (getFieldAmount (sfHighLimit).getIssuer ());
|
||||
}
|
||||
|
||||
std::vector<uint256> SerializedLedgerEntry::getOwners ()
|
||||
{
|
||||
std::vector<uint256> owners;
|
||||
uint160 account;
|
||||
|
||||
for (int i = 0, fields = getCount (); i < fields; ++i)
|
||||
{
|
||||
SField::ref fc = getFieldSType (i);
|
||||
|
||||
if ((fc == sfAccount) || (fc == sfOwner))
|
||||
{
|
||||
const STAccount* entry = dynamic_cast<const STAccount*> (peekAtPIndex (i));
|
||||
|
||||
if ((entry != NULL) && entry->getValueH160 (account))
|
||||
owners.push_back (Ledger::getAccountRootIndex (account));
|
||||
}
|
||||
|
||||
if ((fc == sfLowLimit) || (fc == sfHighLimit))
|
||||
{
|
||||
const STAmount* entry = dynamic_cast<const STAmount*> (peekAtPIndex (i));
|
||||
|
||||
if ((entry != NULL))
|
||||
{
|
||||
uint160 issuer = entry->getIssuer ();
|
||||
|
||||
if (issuer.isNonZero ())
|
||||
owners.push_back (Ledger::getAccountRootIndex (issuer));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return owners;
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
108
modules/ripple_app/misc/ripple_SerializedLedger.h
Normal file
108
modules/ripple_app/misc/ripple_SerializedLedger.h
Normal file
@@ -0,0 +1,108 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_SERIALIZEDLEDGER_H
|
||||
#define RIPPLE_SERIALIZEDLEDGER_H
|
||||
|
||||
// VFALCO NOTE
|
||||
//
|
||||
// This looks like a central class for Ripple. Almost everything that
|
||||
// does anything of interest deals with SLE objects. Any documentation
|
||||
// effort should start with a complete description of this object and
|
||||
// all of its operations.
|
||||
//
|
||||
// It is derived from STObject so it inherits a lot of behavior from that.
|
||||
//
|
||||
// VFALCO TODO Rename the source file to match the class
|
||||
//
|
||||
// VFALCO TODO Can we rename this class to something shorter and more concise?
|
||||
//
|
||||
// Can we just call this LedgerEntry?
|
||||
//
|
||||
class SerializedLedgerEntry
|
||||
: public STObject
|
||||
, public CountedObject <SerializedLedgerEntry>
|
||||
{
|
||||
public:
|
||||
static char const* getCountedObjectName () { return "SerializedLedgerEntry"; }
|
||||
|
||||
typedef boost::shared_ptr<SerializedLedgerEntry> pointer;
|
||||
typedef const boost::shared_ptr<SerializedLedgerEntry>& ref;
|
||||
|
||||
public:
|
||||
SerializedLedgerEntry (const Serializer & s, uint256 const & index);
|
||||
SerializedLedgerEntry (SerializerIterator & sit, uint256 const & index);
|
||||
SerializedLedgerEntry (LedgerEntryType type, uint256 const & index);
|
||||
|
||||
SerializedTypeID getSType () const
|
||||
{
|
||||
return STI_LEDGERENTRY;
|
||||
}
|
||||
std::string getFullText () const;
|
||||
std::string getText () const;
|
||||
Json::Value getJson (int options) const;
|
||||
|
||||
uint256 const& getIndex () const
|
||||
{
|
||||
return mIndex;
|
||||
}
|
||||
void setIndex (uint256 const & i)
|
||||
{
|
||||
mIndex = i;
|
||||
}
|
||||
|
||||
void setImmutable ()
|
||||
{
|
||||
mMutable = false;
|
||||
}
|
||||
bool isMutable ()
|
||||
{
|
||||
return mMutable;
|
||||
}
|
||||
SerializedLedgerEntry::pointer getMutable () const;
|
||||
|
||||
LedgerEntryType getType () const
|
||||
{
|
||||
return mType;
|
||||
}
|
||||
uint16 getVersion () const
|
||||
{
|
||||
return getFieldU16 (sfLedgerEntryType);
|
||||
}
|
||||
LedgerFormats::Item const* getFormat ()
|
||||
{
|
||||
return mFormat;
|
||||
}
|
||||
|
||||
bool isThreadedType (); // is this a ledger entry that can be threaded
|
||||
bool isThreaded (); // is this ledger entry actually threaded
|
||||
bool hasOneOwner (); // This node has one other node that owns it (like nickname)
|
||||
bool hasTwoOwners (); // This node has two nodes that own it (like ripple balance)
|
||||
RippleAddress getOwner ();
|
||||
RippleAddress getFirstOwner ();
|
||||
RippleAddress getSecondOwner ();
|
||||
uint256 getThreadedTransaction ();
|
||||
uint32 getThreadedLedger ();
|
||||
bool thread (uint256 const & txID, uint32 ledgerSeq, uint256 & prevTxID, uint32 & prevLedgerID);
|
||||
std::vector<uint256> getOwners (); // nodes notified if this node is deleted
|
||||
|
||||
private:
|
||||
SerializedLedgerEntry* duplicate () const
|
||||
{
|
||||
return new SerializedLedgerEntry (*this);
|
||||
}
|
||||
|
||||
private:
|
||||
uint256 mIndex;
|
||||
LedgerEntryType mType;
|
||||
LedgerFormats::Item const* mFormat;
|
||||
bool mMutable;
|
||||
};
|
||||
|
||||
typedef SerializedLedgerEntry SLE;
|
||||
|
||||
#endif
|
||||
// vim:ts=4
|
||||
289
modules/ripple_app/misc/ripple_SerializedTransaction.cpp
Normal file
289
modules/ripple_app/misc/ripple_SerializedTransaction.cpp
Normal file
@@ -0,0 +1,289 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
SETUP_LOG (SerializedTransaction)
|
||||
|
||||
SerializedTransaction::SerializedTransaction (TxType type)
|
||||
: STObject (sfTransaction)
|
||||
, mType (type)
|
||||
, mSigGood (false)
|
||||
, mSigBad (false)
|
||||
{
|
||||
mFormat = TxFormats::getInstance()->findByType (type);
|
||||
|
||||
if (mFormat == nullptr)
|
||||
{
|
||||
WriteLog (lsWARNING, SerializedTransaction) << "Transaction type: " << type;
|
||||
throw std::runtime_error ("invalid transaction type");
|
||||
}
|
||||
|
||||
set (mFormat->elements);
|
||||
setFieldU16 (sfTransactionType, mFormat->getType ());
|
||||
}
|
||||
|
||||
SerializedTransaction::SerializedTransaction (STObject const& object)
|
||||
: STObject (object)
|
||||
, mSigGood (false)
|
||||
, mSigBad (false)
|
||||
{
|
||||
mType = static_cast <TxType> (getFieldU16 (sfTransactionType));
|
||||
|
||||
mFormat = TxFormats::getInstance()->findByType (mType);
|
||||
|
||||
if (!mFormat)
|
||||
{
|
||||
WriteLog (lsWARNING, SerializedTransaction) << "Transaction type: " << mType;
|
||||
throw std::runtime_error ("invalid transaction type");
|
||||
}
|
||||
|
||||
if (!setType (mFormat->elements))
|
||||
{
|
||||
throw std::runtime_error ("transaction not valid");
|
||||
}
|
||||
}
|
||||
|
||||
SerializedTransaction::SerializedTransaction (SerializerIterator& sit) : STObject (sfTransaction),
|
||||
mSigGood (false), mSigBad (false)
|
||||
{
|
||||
int length = sit.getBytesLeft ();
|
||||
|
||||
if ((length < Protocol::txMinSizeBytes) || (length > Protocol::txMaxSizeBytes))
|
||||
{
|
||||
Log (lsERROR) << "Transaction has invalid length: " << length;
|
||||
throw std::runtime_error ("Transaction length invalid");
|
||||
}
|
||||
|
||||
set (sit);
|
||||
mType = static_cast<TxType> (getFieldU16 (sfTransactionType));
|
||||
|
||||
mFormat = TxFormats::getInstance()->findByType (mType);
|
||||
|
||||
if (!mFormat)
|
||||
{
|
||||
WriteLog (lsWARNING, SerializedTransaction) << "Transaction type: " << mType;
|
||||
throw std::runtime_error ("invalid transaction type");
|
||||
}
|
||||
|
||||
if (!setType (mFormat->elements))
|
||||
{
|
||||
WriteLog (lsWARNING, SerializedTransaction) << "Transaction not legal for format";
|
||||
throw std::runtime_error ("transaction not valid");
|
||||
}
|
||||
}
|
||||
|
||||
std::string SerializedTransaction::getFullText () const
|
||||
{
|
||||
std::string ret = "\"";
|
||||
ret += getTransactionID ().GetHex ();
|
||||
ret += "\" = {";
|
||||
ret += STObject::getFullText ();
|
||||
ret += "}";
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string SerializedTransaction::getText () const
|
||||
{
|
||||
return STObject::getText ();
|
||||
}
|
||||
|
||||
std::vector<RippleAddress> SerializedTransaction::getMentionedAccounts () const
|
||||
{
|
||||
std::vector<RippleAddress> accounts;
|
||||
|
||||
BOOST_FOREACH (const SerializedType & it, peekData ())
|
||||
{
|
||||
const STAccount* sa = dynamic_cast<const STAccount*> (&it);
|
||||
|
||||
if (sa != NULL)
|
||||
{
|
||||
bool found = false;
|
||||
RippleAddress na = sa->getValueNCA ();
|
||||
BOOST_FOREACH (const RippleAddress & it, accounts)
|
||||
{
|
||||
if (it == na)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
accounts.push_back (na);
|
||||
}
|
||||
|
||||
const STAmount* sam = dynamic_cast<const STAmount*> (&it);
|
||||
|
||||
if (sam)
|
||||
{
|
||||
uint160 issuer = sam->getIssuer ();
|
||||
|
||||
if (issuer.isNonZero ())
|
||||
{
|
||||
RippleAddress na;
|
||||
na.setAccountID (issuer);
|
||||
bool found = false;
|
||||
BOOST_FOREACH (const RippleAddress & it, accounts)
|
||||
{
|
||||
if (it == na)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
accounts.push_back (na);
|
||||
}
|
||||
}
|
||||
}
|
||||
return accounts;
|
||||
}
|
||||
|
||||
uint256 SerializedTransaction::getSigningHash () const
|
||||
{
|
||||
return STObject::getSigningHash (theConfig.SIGN_TRANSACTION);
|
||||
}
|
||||
|
||||
uint256 SerializedTransaction::getTransactionID () const
|
||||
{
|
||||
// perhaps we should cache this
|
||||
return getHash (HashPrefix::transactionID);
|
||||
}
|
||||
|
||||
Blob SerializedTransaction::getSignature () const
|
||||
{
|
||||
try
|
||||
{
|
||||
return getFieldVL (sfTxnSignature);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return Blob ();
|
||||
}
|
||||
}
|
||||
|
||||
void SerializedTransaction::sign (const RippleAddress& naAccountPrivate)
|
||||
{
|
||||
Blob signature;
|
||||
naAccountPrivate.accountPrivateSign (getSigningHash (), signature);
|
||||
setFieldVL (sfTxnSignature, signature);
|
||||
}
|
||||
|
||||
bool SerializedTransaction::checkSign () const
|
||||
{
|
||||
if (mSigGood)
|
||||
return true;
|
||||
|
||||
if (mSigBad)
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
RippleAddress n;
|
||||
n.setAccountPublic (getFieldVL (sfSigningPubKey));
|
||||
|
||||
if (checkSign (n))
|
||||
{
|
||||
mSigGood = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
mSigBad = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SerializedTransaction::checkSign (const RippleAddress& naAccountPublic) const
|
||||
{
|
||||
try
|
||||
{
|
||||
return naAccountPublic.accountPublicVerify (getSigningHash (), getFieldVL (sfTxnSignature));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void SerializedTransaction::setSigningPubKey (const RippleAddress& naSignPubKey)
|
||||
{
|
||||
setFieldVL (sfSigningPubKey, naSignPubKey.getAccountPublic ());
|
||||
}
|
||||
|
||||
void SerializedTransaction::setSourceAccount (const RippleAddress& naSource)
|
||||
{
|
||||
setFieldAccount (sfAccount, naSource);
|
||||
}
|
||||
|
||||
Json::Value SerializedTransaction::getJson (int options, bool binary) const
|
||||
{
|
||||
if (binary)
|
||||
{
|
||||
Json::Value ret;
|
||||
Serializer s = STObject::getSerializer ();
|
||||
ret["tx"] = strHex (s.peekData ());
|
||||
ret["hash"] = getTransactionID ().GetHex ();
|
||||
return ret;
|
||||
}
|
||||
|
||||
Json::Value ret = STObject::getJson (0);
|
||||
ret["hash"] = getTransactionID ().GetHex ();
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string SerializedTransaction::getSQLValueHeader ()
|
||||
{
|
||||
return "(TransID, TransType, FromAcct, FromSeq, LedgerSeq, Status, RawTxn)";
|
||||
}
|
||||
|
||||
std::string SerializedTransaction::getMetaSQLValueHeader ()
|
||||
{
|
||||
return "(TransID, TransType, FromAcct, FromSeq, LedgerSeq, Status, RawTxn, TxnMeta)";
|
||||
}
|
||||
|
||||
std::string SerializedTransaction::getMetaSQLInsertReplaceHeader ()
|
||||
{
|
||||
return "INSERT OR REPLACE INTO Transactions " + getMetaSQLValueHeader () + " VALUES ";
|
||||
}
|
||||
|
||||
std::string SerializedTransaction::getSQL (uint32 inLedger, char status) const
|
||||
{
|
||||
Serializer s;
|
||||
add (s);
|
||||
return getSQL (s, inLedger, status);
|
||||
}
|
||||
|
||||
std::string SerializedTransaction::getMetaSQL (uint32 inLedger, const std::string& escapedMetaData) const
|
||||
{
|
||||
Serializer s;
|
||||
add (s);
|
||||
return getMetaSQL (s, inLedger, TXN_SQL_VALIDATED, escapedMetaData);
|
||||
}
|
||||
|
||||
std::string SerializedTransaction::getSQL (Serializer rawTxn, uint32 inLedger, char status) const
|
||||
{
|
||||
static boost::format bfTrans ("('%s', '%s', '%s', '%d', '%d', '%c', %s)");
|
||||
std::string rTxn = sqlEscape (rawTxn.peekData ());
|
||||
|
||||
return str (boost::format (bfTrans)
|
||||
% getTransactionID ().GetHex () % getTransactionType () % getSourceAccount ().humanAccountID ()
|
||||
% getSequence () % inLedger % status % rTxn);
|
||||
}
|
||||
|
||||
std::string SerializedTransaction::getMetaSQL (Serializer rawTxn, uint32 inLedger, char status,
|
||||
const std::string& escapedMetaData) const
|
||||
{
|
||||
static boost::format bfTrans ("('%s', '%s', '%s', '%d', '%d', '%c', %s, %s)");
|
||||
std::string rTxn = sqlEscape (rawTxn.peekData ());
|
||||
|
||||
return str (boost::format (bfTrans)
|
||||
% getTransactionID ().GetHex () % getTransactionType () % getSourceAccount ().humanAccountID ()
|
||||
% getSequence () % inLedger % status % rTxn % escapedMetaData);
|
||||
}
|
||||
141
modules/ripple_app/misc/ripple_SerializedTransaction.h
Normal file
141
modules/ripple_app/misc/ripple_SerializedTransaction.h
Normal file
@@ -0,0 +1,141 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_SERIALIZEDTRANSACTION_H
|
||||
#define RIPPLE_SERIALIZEDTRANSACTION_H
|
||||
|
||||
// VFALCO TODO eliminate these macros
|
||||
|
||||
#define TXN_SQL_NEW 'N'
|
||||
#define TXN_SQL_CONFLICT 'C'
|
||||
#define TXN_SQL_HELD 'H'
|
||||
#define TXN_SQL_VALIDATED 'V'
|
||||
#define TXN_SQL_INCLUDED 'I'
|
||||
#define TXN_SQL_UNKNOWN 'U'
|
||||
|
||||
class SerializedTransaction
|
||||
: public STObject
|
||||
, public CountedObject <SerializedTransaction>
|
||||
{
|
||||
public:
|
||||
static char const* getCountedObjectName () { return "SerializedTransaction"; }
|
||||
|
||||
typedef boost::shared_ptr<SerializedTransaction> pointer;
|
||||
typedef const boost::shared_ptr<SerializedTransaction>& ref;
|
||||
|
||||
public:
|
||||
SerializedTransaction (SerializerIterator & sit);
|
||||
SerializedTransaction (TxType type);
|
||||
SerializedTransaction (const STObject & object);
|
||||
|
||||
// STObject functions
|
||||
SerializedTypeID getSType () const
|
||||
{
|
||||
return STI_TRANSACTION;
|
||||
}
|
||||
std::string getFullText () const;
|
||||
std::string getText () const;
|
||||
|
||||
// outer transaction functions / signature functions
|
||||
Blob getSignature () const;
|
||||
void setSignature (Blob const & s)
|
||||
{
|
||||
setFieldVL (sfTxnSignature, s);
|
||||
}
|
||||
uint256 getSigningHash () const;
|
||||
|
||||
TxType getTxnType () const
|
||||
{
|
||||
return mType;
|
||||
}
|
||||
STAmount getTransactionFee () const
|
||||
{
|
||||
return getFieldAmount (sfFee);
|
||||
}
|
||||
void setTransactionFee (const STAmount & fee)
|
||||
{
|
||||
setFieldAmount (sfFee, fee);
|
||||
}
|
||||
|
||||
RippleAddress getSourceAccount () const
|
||||
{
|
||||
return getFieldAccount (sfAccount);
|
||||
}
|
||||
Blob getSigningPubKey () const
|
||||
{
|
||||
return getFieldVL (sfSigningPubKey);
|
||||
}
|
||||
void setSigningPubKey (const RippleAddress & naSignPubKey);
|
||||
void setSourceAccount (const RippleAddress & naSource);
|
||||
std::string getTransactionType () const
|
||||
{
|
||||
return mFormat->getName ();
|
||||
}
|
||||
|
||||
uint32 getSequence () const
|
||||
{
|
||||
return getFieldU32 (sfSequence);
|
||||
}
|
||||
void setSequence (uint32 seq)
|
||||
{
|
||||
return setFieldU32 (sfSequence, seq);
|
||||
}
|
||||
|
||||
std::vector<RippleAddress> getMentionedAccounts () const;
|
||||
|
||||
uint256 getTransactionID () const;
|
||||
|
||||
virtual Json::Value getJson (int options, bool binary = false) const;
|
||||
|
||||
void sign (const RippleAddress & naAccountPrivate);
|
||||
bool checkSign (const RippleAddress & naAccountPublic) const;
|
||||
bool checkSign () const;
|
||||
bool isKnownGood () const
|
||||
{
|
||||
return mSigGood;
|
||||
}
|
||||
bool isKnownBad () const
|
||||
{
|
||||
return mSigBad;
|
||||
}
|
||||
void setGood () const
|
||||
{
|
||||
mSigGood = true;
|
||||
}
|
||||
void setBad () const
|
||||
{
|
||||
mSigBad = true;
|
||||
}
|
||||
|
||||
// SQL Functions
|
||||
static std::string getSQLValueHeader ();
|
||||
static std::string getSQLInsertHeader ();
|
||||
static std::string getSQLInsertIgnoreHeader ();
|
||||
std::string getSQL (std::string & sql, uint32 inLedger, char status) const;
|
||||
std::string getSQL (uint32 inLedger, char status) const;
|
||||
std::string getSQL (Serializer rawTxn, uint32 inLedger, char status) const;
|
||||
|
||||
// SQL Functions with metadata
|
||||
static std::string getMetaSQLValueHeader ();
|
||||
static std::string getMetaSQLInsertReplaceHeader ();
|
||||
std::string getMetaSQL (uint32 inLedger, const std::string & escapedMetaData) const;
|
||||
std::string getMetaSQL (Serializer rawTxn, uint32 inLedger, char status, const std::string & escapedMetaData) const;
|
||||
|
||||
private:
|
||||
TxType mType;
|
||||
TxFormats::Item const* mFormat;
|
||||
|
||||
SerializedTransaction* duplicate () const
|
||||
{
|
||||
return new SerializedTransaction (*this);
|
||||
}
|
||||
|
||||
mutable bool mSigGood;
|
||||
mutable bool mSigBad;
|
||||
};
|
||||
|
||||
#endif
|
||||
// vim:ts=4
|
||||
@@ -0,0 +1,50 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
BOOST_AUTO_TEST_SUITE (SerializedTransactionTS)
|
||||
|
||||
BOOST_AUTO_TEST_CASE ( STrans_test )
|
||||
{
|
||||
using namespace ripple;
|
||||
|
||||
RippleAddress seed;
|
||||
seed.setSeedRandom ();
|
||||
RippleAddress generator = RippleAddress::createGeneratorPublic (seed);
|
||||
RippleAddress publicAcct = RippleAddress::createAccountPublic (generator, 1);
|
||||
RippleAddress privateAcct = RippleAddress::createAccountPrivate (generator, seed, 1);
|
||||
|
||||
SerializedTransaction j (ttACCOUNT_SET);
|
||||
j.setSourceAccount (publicAcct);
|
||||
j.setSigningPubKey (publicAcct);
|
||||
j.setFieldVL (sfMessageKey, publicAcct.getAccountPublic ());
|
||||
j.sign (privateAcct);
|
||||
|
||||
if (!j.checkSign ()) BOOST_FAIL ("Transaction fails signature test");
|
||||
|
||||
Serializer rawTxn;
|
||||
j.add (rawTxn);
|
||||
SerializerIterator sit (rawTxn);
|
||||
SerializedTransaction copy (sit);
|
||||
|
||||
if (copy != j)
|
||||
{
|
||||
Log (lsFATAL) << j.getJson (0);
|
||||
Log (lsFATAL) << copy.getJson (0);
|
||||
BOOST_FAIL ("Transaction fails serialize/deserialize test");
|
||||
}
|
||||
|
||||
UPTR_T<STObject> new_obj = STObject::parseJson (j.getJson (0), sfGeneric);
|
||||
|
||||
if (new_obj.get () == NULL) BOOST_FAIL ("Unable to build object from json");
|
||||
|
||||
if (STObject (j) != *new_obj)
|
||||
{
|
||||
Log (lsINFO) << "ORIG: " << j.getJson (0);
|
||||
Log (lsINFO) << "BUILT " << new_obj->getJson (0);
|
||||
BOOST_FAIL ("Built a different transaction");
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END ();
|
||||
444
modules/ripple_app/misc/ripple_Validations.cpp
Normal file
444
modules/ripple_app/misc/ripple_Validations.cpp
Normal file
@@ -0,0 +1,444 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
class Validations;
|
||||
|
||||
SETUP_LOG (Validations)
|
||||
|
||||
typedef std::map<uint160, SerializedValidation::pointer>::value_type u160_val_pair;
|
||||
typedef boost::shared_ptr<ValidationSet> VSpointer;
|
||||
|
||||
class Validations : public IValidations
|
||||
{
|
||||
private:
|
||||
boost::mutex mValidationLock;
|
||||
TaggedCache<uint256, ValidationSet, UptimeTimerAdapter> mValidations;
|
||||
boost::unordered_map<uint160, SerializedValidation::pointer> mCurrentValidations;
|
||||
std::vector<SerializedValidation::pointer> mStaleValidations;
|
||||
|
||||
bool mWriting;
|
||||
|
||||
private:
|
||||
boost::shared_ptr<ValidationSet> findCreateSet (uint256 const& ledgerHash)
|
||||
{
|
||||
VSpointer j = mValidations.fetch (ledgerHash);
|
||||
|
||||
if (!j)
|
||||
{
|
||||
j = boost::make_shared<ValidationSet> ();
|
||||
mValidations.canonicalize (ledgerHash, j);
|
||||
}
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
boost::shared_ptr<ValidationSet> findSet (uint256 const& ledgerHash)
|
||||
{
|
||||
return mValidations.fetch (ledgerHash);
|
||||
}
|
||||
|
||||
public:
|
||||
Validations () : mValidations ("Validations", 128, 600), mWriting (false)
|
||||
{
|
||||
mStaleValidations.reserve (512);
|
||||
}
|
||||
|
||||
private:
|
||||
bool addValidation (SerializedValidation::ref val, const std::string& source)
|
||||
{
|
||||
RippleAddress signer = val->getSignerPublic ();
|
||||
bool isCurrent = false;
|
||||
|
||||
if (getApp().getUNL ().nodeInUNL (signer) || val->isTrusted ())
|
||||
{
|
||||
val->setTrusted ();
|
||||
uint32 now = getApp().getOPs ().getCloseTimeNC ();
|
||||
uint32 valClose = val->getSignTime ();
|
||||
|
||||
if ((now > (valClose - LEDGER_EARLY_INTERVAL)) && (now < (valClose + LEDGER_VAL_INTERVAL)))
|
||||
isCurrent = true;
|
||||
else
|
||||
{
|
||||
WriteLog (lsWARNING, Validations) << "Received stale validation now=" << now << ", close=" << valClose;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog (lsDEBUG, Validations) << "Node " << signer.humanNodePublic () << " not in UNL st=" << val->getSignTime () <<
|
||||
", hash=" << val->getLedgerHash () << ", shash=" << val->getSigningHash () << " src=" << source;
|
||||
}
|
||||
|
||||
uint256 hash = val->getLedgerHash ();
|
||||
uint160 node = signer.getNodeID ();
|
||||
|
||||
{
|
||||
boost::mutex::scoped_lock sl (mValidationLock);
|
||||
|
||||
if (!findCreateSet (hash)->insert (std::make_pair (node, val)).second)
|
||||
return false;
|
||||
|
||||
if (isCurrent)
|
||||
{
|
||||
boost::unordered_map<uint160, SerializedValidation::pointer>::iterator it = mCurrentValidations.find (node);
|
||||
|
||||
if (it == mCurrentValidations.end ())
|
||||
mCurrentValidations.emplace (node, val);
|
||||
else if (!it->second)
|
||||
it->second = val;
|
||||
else if (val->getSignTime () > it->second->getSignTime ())
|
||||
{
|
||||
val->setPreviousHash (it->second->getLedgerHash ());
|
||||
mStaleValidations.push_back (it->second);
|
||||
it->second = val;
|
||||
condWrite ();
|
||||
}
|
||||
else
|
||||
isCurrent = false;
|
||||
}
|
||||
}
|
||||
|
||||
WriteLog (lsDEBUG, Validations) << "Val for " << hash << " from " << signer.humanNodePublic ()
|
||||
<< " added " << (val->isTrusted () ? "trusted/" : "UNtrusted/") << (isCurrent ? "current" : "stale");
|
||||
|
||||
if (val->isTrusted ())
|
||||
getApp().getLedgerMaster ().checkAccept (hash);
|
||||
|
||||
// FIXME: This never forwards untrusted validations
|
||||
return isCurrent;
|
||||
}
|
||||
|
||||
void tune (int size, int age)
|
||||
{
|
||||
mValidations.setTargetSize (size);
|
||||
mValidations.setTargetAge (age);
|
||||
}
|
||||
|
||||
ValidationSet getValidations (uint256 const& ledger)
|
||||
{
|
||||
{
|
||||
boost::mutex::scoped_lock sl (mValidationLock);
|
||||
VSpointer set = findSet (ledger);
|
||||
|
||||
if (set)
|
||||
return *set;
|
||||
}
|
||||
return ValidationSet ();
|
||||
}
|
||||
|
||||
void getValidationCount (uint256 const& ledger, bool currentOnly, int& trusted, int& untrusted)
|
||||
{
|
||||
trusted = untrusted = 0;
|
||||
boost::mutex::scoped_lock sl (mValidationLock);
|
||||
VSpointer set = findSet (ledger);
|
||||
|
||||
if (set)
|
||||
{
|
||||
uint32 now = getApp().getOPs ().getNetworkTimeNC ();
|
||||
BOOST_FOREACH (u160_val_pair & it, *set)
|
||||
{
|
||||
bool isTrusted = it.second->isTrusted ();
|
||||
|
||||
if (isTrusted && currentOnly)
|
||||
{
|
||||
uint32 closeTime = it.second->getSignTime ();
|
||||
|
||||
if ((now < (closeTime - LEDGER_EARLY_INTERVAL)) || (now > (closeTime + LEDGER_VAL_INTERVAL)))
|
||||
isTrusted = false;
|
||||
else
|
||||
{
|
||||
WriteLog (lsTRACE, Validations) << "VC: Untrusted due to time " << ledger;
|
||||
}
|
||||
}
|
||||
|
||||
if (isTrusted)
|
||||
++trusted;
|
||||
else
|
||||
++untrusted;
|
||||
}
|
||||
}
|
||||
|
||||
WriteLog (lsTRACE, Validations) << "VC: " << ledger << "t:" << trusted << " u:" << untrusted;
|
||||
}
|
||||
|
||||
void getValidationTypes (uint256 const& ledger, int& full, int& partial)
|
||||
{
|
||||
full = partial = 0;
|
||||
boost::mutex::scoped_lock sl (mValidationLock);
|
||||
VSpointer set = findSet (ledger);
|
||||
|
||||
if (set)
|
||||
{
|
||||
BOOST_FOREACH (u160_val_pair & it, *set)
|
||||
{
|
||||
if (it.second->isTrusted ())
|
||||
{
|
||||
if (it.second->isFull ())
|
||||
++full;
|
||||
else
|
||||
++partial;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WriteLog (lsTRACE, Validations) << "VC: " << ledger << "f:" << full << " p:" << partial;
|
||||
}
|
||||
|
||||
|
||||
int getTrustedValidationCount (uint256 const& ledger)
|
||||
{
|
||||
int trusted = 0;
|
||||
boost::mutex::scoped_lock sl (mValidationLock);
|
||||
VSpointer set = findSet (ledger);
|
||||
|
||||
if (set)
|
||||
{
|
||||
BOOST_FOREACH (u160_val_pair & it, *set)
|
||||
{
|
||||
if (it.second->isTrusted ())
|
||||
++trusted;
|
||||
}
|
||||
}
|
||||
|
||||
return trusted;
|
||||
}
|
||||
|
||||
int getFeeAverage (uint256 const& ledger, uint64 ref, uint64& fee)
|
||||
{
|
||||
int trusted = 0;
|
||||
fee = 0;
|
||||
|
||||
boost::mutex::scoped_lock sl (mValidationLock);
|
||||
VSpointer set = findSet (ledger);
|
||||
|
||||
if (set)
|
||||
{
|
||||
BOOST_FOREACH (u160_val_pair & it, *set)
|
||||
{
|
||||
if (it.second->isTrusted ())
|
||||
{
|
||||
++trusted;
|
||||
if (it.second->isFieldPresent(sfLoadFee))
|
||||
fee += it.second->getFieldU32(sfLoadFee);
|
||||
else
|
||||
fee += ref;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (trusted == 0)
|
||||
fee = ref;
|
||||
else
|
||||
fee /= trusted;
|
||||
return trusted;
|
||||
}
|
||||
|
||||
int getNodesAfter (uint256 const& ledger)
|
||||
{
|
||||
// Number of trusted nodes that have moved past this ledger
|
||||
int count = 0;
|
||||
boost::mutex::scoped_lock sl (mValidationLock);
|
||||
BOOST_FOREACH (u160_val_pair & it, mCurrentValidations)
|
||||
{
|
||||
if (it.second->isTrusted () && it.second->isPreviousHash (ledger))
|
||||
++count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
int getLoadRatio (bool overLoaded)
|
||||
{
|
||||
// how many trusted nodes are able to keep up, higher is better
|
||||
int goodNodes = overLoaded ? 1 : 0;
|
||||
int badNodes = overLoaded ? 0 : 1;
|
||||
{
|
||||
boost::mutex::scoped_lock sl (mValidationLock);
|
||||
BOOST_FOREACH (u160_val_pair & it, mCurrentValidations)
|
||||
{
|
||||
if (it.second->isTrusted ())
|
||||
{
|
||||
if (it.second->isFull ())
|
||||
++goodNodes;
|
||||
else
|
||||
++badNodes;
|
||||
}
|
||||
}
|
||||
}
|
||||
return (goodNodes * 100) / (goodNodes + badNodes);
|
||||
}
|
||||
|
||||
std::list<SerializedValidation::pointer> getCurrentTrustedValidations ()
|
||||
{
|
||||
uint32 cutoff = getApp().getOPs ().getNetworkTimeNC () - LEDGER_VAL_INTERVAL;
|
||||
|
||||
std::list<SerializedValidation::pointer> ret;
|
||||
|
||||
boost::mutex::scoped_lock sl (mValidationLock);
|
||||
boost::unordered_map<uint160, SerializedValidation::pointer>::iterator it = mCurrentValidations.begin ();
|
||||
|
||||
while (it != mCurrentValidations.end ())
|
||||
{
|
||||
if (!it->second) // contains no record
|
||||
it = mCurrentValidations.erase (it);
|
||||
else if (it->second->getSignTime () < cutoff)
|
||||
{
|
||||
// contains a stale record
|
||||
mStaleValidations.push_back (it->second);
|
||||
it->second.reset ();
|
||||
condWrite ();
|
||||
it = mCurrentValidations.erase (it);
|
||||
}
|
||||
else
|
||||
{
|
||||
// contains a live record
|
||||
if (it->second->isTrusted ())
|
||||
ret.push_back (it->second);
|
||||
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
boost::unordered_map<uint256, currentValidationCount>
|
||||
getCurrentValidations (uint256 currentLedger, uint256 priorLedger)
|
||||
{
|
||||
uint32 cutoff = getApp().getOPs ().getNetworkTimeNC () - LEDGER_VAL_INTERVAL;
|
||||
bool valCurrentLedger = currentLedger.isNonZero ();
|
||||
bool valPriorLedger = priorLedger.isNonZero ();
|
||||
|
||||
boost::unordered_map<uint256, currentValidationCount> ret;
|
||||
|
||||
boost::mutex::scoped_lock sl (mValidationLock);
|
||||
boost::unordered_map<uint160, SerializedValidation::pointer>::iterator it = mCurrentValidations.begin ();
|
||||
|
||||
while (it != mCurrentValidations.end ())
|
||||
{
|
||||
if (!it->second) // contains no record
|
||||
it = mCurrentValidations.erase (it);
|
||||
else if (it->second->getSignTime () < cutoff)
|
||||
{
|
||||
// contains a stale record
|
||||
mStaleValidations.push_back (it->second);
|
||||
it->second.reset ();
|
||||
condWrite ();
|
||||
it = mCurrentValidations.erase (it);
|
||||
}
|
||||
else
|
||||
{
|
||||
// contains a live record
|
||||
bool countPreferred = valCurrentLedger && (it->second->getLedgerHash () == currentLedger);
|
||||
|
||||
if (!countPreferred && // allow up to one ledger slip in either direction
|
||||
((valCurrentLedger && it->second->isPreviousHash (currentLedger)) ||
|
||||
(valPriorLedger && (it->second->getLedgerHash () == priorLedger))))
|
||||
{
|
||||
countPreferred = true;
|
||||
WriteLog (lsTRACE, Validations) << "Counting for " << currentLedger << " not " << it->second->getLedgerHash ();
|
||||
}
|
||||
|
||||
currentValidationCount& p = countPreferred ? ret[currentLedger] : ret[it->second->getLedgerHash ()];
|
||||
++ (p.first);
|
||||
uint160 ni = it->second->getNodeID ();
|
||||
|
||||
if (ni > p.second)
|
||||
p.second = ni;
|
||||
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void flush ()
|
||||
{
|
||||
bool anyNew = false;
|
||||
|
||||
WriteLog (lsINFO, Validations) << "Flushing validations";
|
||||
boost::mutex::scoped_lock sl (mValidationLock);
|
||||
BOOST_FOREACH (u160_val_pair & it, mCurrentValidations)
|
||||
{
|
||||
if (it.second)
|
||||
mStaleValidations.push_back (it.second);
|
||||
|
||||
anyNew = true;
|
||||
}
|
||||
mCurrentValidations.clear ();
|
||||
|
||||
if (anyNew)
|
||||
condWrite ();
|
||||
|
||||
while (mWriting)
|
||||
{
|
||||
sl.unlock ();
|
||||
boost::this_thread::sleep (boost::posix_time::milliseconds (100));
|
||||
sl.lock ();
|
||||
}
|
||||
|
||||
WriteLog (lsDEBUG, Validations) << "Validations flushed";
|
||||
}
|
||||
|
||||
void condWrite ()
|
||||
{
|
||||
if (mWriting)
|
||||
return;
|
||||
|
||||
mWriting = true;
|
||||
getApp().getJobQueue ().addJob (jtWRITE, "Validations::doWrite",
|
||||
BIND_TYPE (&Validations::doWrite, this, P_1));
|
||||
}
|
||||
|
||||
void doWrite (Job&)
|
||||
{
|
||||
LoadEvent::autoptr event (getApp().getJobQueue ().getLoadEventAP (jtDISK, "ValidationWrite"));
|
||||
boost::format insVal ("INSERT INTO Validations "
|
||||
"(LedgerHash,NodePubKey,SignTime,RawData) VALUES ('%s','%s','%u',%s);");
|
||||
|
||||
boost::mutex::scoped_lock sl (mValidationLock);
|
||||
assert (mWriting);
|
||||
|
||||
while (!mStaleValidations.empty ())
|
||||
{
|
||||
std::vector<SerializedValidation::pointer> vector;
|
||||
vector.reserve (512);
|
||||
mStaleValidations.swap (vector);
|
||||
sl.unlock ();
|
||||
{
|
||||
Database* db = getApp().getLedgerDB ()->getDB ();
|
||||
ScopedLock dbl (getApp().getLedgerDB ()->getDBLock ());
|
||||
|
||||
Serializer s (1024);
|
||||
db->executeSQL ("BEGIN TRANSACTION;");
|
||||
BOOST_FOREACH (SerializedValidation::ref it, vector)
|
||||
{
|
||||
s.erase ();
|
||||
it->add (s);
|
||||
db->executeSQL (boost::str (insVal % it->getLedgerHash ().GetHex ()
|
||||
% it->getSignerPublic ().humanNodePublic () % it->getSignTime ()
|
||||
% sqlEscape (s.peekData ())));
|
||||
}
|
||||
db->executeSQL ("END TRANSACTION;");
|
||||
}
|
||||
sl.lock ();
|
||||
}
|
||||
|
||||
mWriting = false;
|
||||
}
|
||||
|
||||
void sweep ()
|
||||
{
|
||||
boost::mutex::scoped_lock sl (mValidationLock);
|
||||
mValidations.sweep ();
|
||||
}
|
||||
};
|
||||
|
||||
IValidations* IValidations::New ()
|
||||
{
|
||||
return new Validations;
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
7
modules/ripple_app/network/WSConnection.cpp
Normal file
7
modules/ripple_app/network/WSConnection.cpp
Normal file
@@ -0,0 +1,7 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
SETUP_LOGN (WSConnectionLog,"WSConnection")
|
||||
284
modules/ripple_app/network/WSConnection.h
Normal file
284
modules/ripple_app/network/WSConnection.h
Normal file
@@ -0,0 +1,284 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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
|
||||
101
modules/ripple_app/network/WSDoor.cpp
Normal file
101
modules/ripple_app/network/WSDoor.cpp
Normal file
@@ -0,0 +1,101 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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 ();
|
||||
}
|
||||
}
|
||||
36
modules/ripple_app/network/WSDoor.h
Normal file
36
modules/ripple_app/network/WSDoor.h
Normal file
@@ -0,0 +1,36 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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
|
||||
7
modules/ripple_app/network/ripple_WSHandler.cpp
Normal file
7
modules/ripple_app/network/ripple_WSHandler.cpp
Normal file
@@ -0,0 +1,7 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
SETUP_LOGN (WSServerHandlerLog,"WSServerHandler")
|
||||
334
modules/ripple_app/network/ripple_WSHandler.h
Normal file
334
modules/ripple_app/network/ripple_WSHandler.h
Normal file
@@ -0,0 +1,334 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_WSHANDLER_H_INCLUDED
|
||||
#define RIPPLE_WSHANDLER_H_INCLUDED
|
||||
|
||||
extern bool serverOkay (std::string& reason);
|
||||
|
||||
template <typename endpoint_type>
|
||||
class WSConnection;
|
||||
|
||||
// CAUTION: on_* functions are called by the websocket code while holding a lock
|
||||
|
||||
struct WSServerHandlerLog;
|
||||
|
||||
// A single instance of this object is made.
|
||||
// This instance dispatches all events. There is no per connection persistence.
|
||||
|
||||
template <typename endpoint_type>
|
||||
class WSServerHandler
|
||||
: public endpoint_type::handler
|
||||
, LeakChecked <WSServerHandler <endpoint_type> >
|
||||
{
|
||||
public:
|
||||
typedef typename endpoint_type::handler::connection_ptr connection_ptr;
|
||||
typedef typename endpoint_type::handler::message_ptr message_ptr;
|
||||
typedef boost::shared_ptr< WSConnection<endpoint_type> > wsc_ptr;
|
||||
|
||||
// Private reasons to close.
|
||||
enum
|
||||
{
|
||||
crTooSlow = 4000, // Client is too slow.
|
||||
};
|
||||
|
||||
private:
|
||||
boost::shared_ptr<boost::asio::ssl::context> mCtx;
|
||||
|
||||
protected:
|
||||
boost::mutex mMapLock;
|
||||
// For each connection maintain an associated object to track subscriptions.
|
||||
boost::unordered_map<connection_ptr, boost::shared_ptr< WSConnection<endpoint_type> > > mMap;
|
||||
bool mPublic;
|
||||
|
||||
public:
|
||||
WSServerHandler (boost::shared_ptr<boost::asio::ssl::context> spCtx, bool bPublic) : mCtx (spCtx), mPublic (bPublic)
|
||||
{
|
||||
if (theConfig.WEBSOCKET_SECURE != 0)
|
||||
{
|
||||
basio::SslContext::initializeFromFile (
|
||||
*mCtx,
|
||||
theConfig.WEBSOCKET_SSL_KEY,
|
||||
theConfig.WEBSOCKET_SSL_CERT,
|
||||
theConfig.WEBSOCKET_SSL_CHAIN);
|
||||
}
|
||||
}
|
||||
|
||||
bool getPublic ()
|
||||
{
|
||||
return mPublic;
|
||||
};
|
||||
|
||||
boost::asio::ssl::context& getASIOContext ()
|
||||
{
|
||||
return *mCtx;
|
||||
}
|
||||
|
||||
static void ssend (connection_ptr cpClient, message_ptr mpMessage)
|
||||
{
|
||||
try
|
||||
{
|
||||
cpClient->send (mpMessage->get_payload (), mpMessage->get_opcode ());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
cpClient->close (websocketpp::close::status::value (crTooSlow), std::string ("Client is too slow."));
|
||||
}
|
||||
}
|
||||
|
||||
static void ssendb (connection_ptr cpClient, const std::string& strMessage, bool broadcast)
|
||||
{
|
||||
try
|
||||
{
|
||||
WriteLog (broadcast ? lsTRACE : lsDEBUG, WSServerHandlerLog) << "Ws:: Sending '" << strMessage << "'";
|
||||
|
||||
cpClient->send (strMessage);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
cpClient->close (websocketpp::close::status::value (crTooSlow), std::string ("Client is too slow."));
|
||||
}
|
||||
}
|
||||
|
||||
void send (connection_ptr cpClient, message_ptr mpMessage)
|
||||
{
|
||||
cpClient->get_strand ().post (BIND_TYPE (
|
||||
&WSServerHandler<endpoint_type>::ssend, cpClient, mpMessage));
|
||||
}
|
||||
|
||||
void send (connection_ptr cpClient, const std::string& strMessage, bool broadcast)
|
||||
{
|
||||
cpClient->get_strand ().post (BIND_TYPE (
|
||||
&WSServerHandler<endpoint_type>::ssendb, cpClient, strMessage, broadcast));
|
||||
}
|
||||
|
||||
void send (connection_ptr cpClient, const Json::Value& jvObj, bool broadcast)
|
||||
{
|
||||
Json::FastWriter jfwWriter;
|
||||
|
||||
// WriteLog (lsDEBUG, WSServerHandlerLog) << "Ws:: Object '" << jfwWriter.write(jvObj) << "'";
|
||||
|
||||
send (cpClient, jfwWriter.write (jvObj), broadcast);
|
||||
}
|
||||
|
||||
void pingTimer (connection_ptr cpClient)
|
||||
{
|
||||
wsc_ptr ptr;
|
||||
{
|
||||
boost::mutex::scoped_lock sl (mMapLock);
|
||||
typename boost::unordered_map<connection_ptr, wsc_ptr>::iterator it = mMap.find (cpClient);
|
||||
|
||||
if (it == mMap.end ())
|
||||
return;
|
||||
|
||||
ptr = it->second;
|
||||
}
|
||||
std::string data ("ping");
|
||||
|
||||
if (ptr->onPingTimer (data))
|
||||
{
|
||||
WriteLog (lsWARNING, WSServerHandlerLog) << "Connection pings out";
|
||||
cpClient->close (websocketpp::close::status::PROTOCOL_ERROR, "ping timeout");
|
||||
}
|
||||
else
|
||||
cpClient->ping (data);
|
||||
}
|
||||
|
||||
void on_send_empty (connection_ptr cpClient)
|
||||
{
|
||||
wsc_ptr ptr;
|
||||
{
|
||||
boost::mutex::scoped_lock sl (mMapLock);
|
||||
typename boost::unordered_map<connection_ptr, wsc_ptr>::iterator it = mMap.find (cpClient);
|
||||
|
||||
if (it == mMap.end ())
|
||||
return;
|
||||
|
||||
ptr = it->second;
|
||||
}
|
||||
|
||||
ptr->onSendEmpty ();
|
||||
}
|
||||
|
||||
void on_open (connection_ptr cpClient)
|
||||
{
|
||||
boost::mutex::scoped_lock sl (mMapLock);
|
||||
|
||||
try
|
||||
{
|
||||
mMap[cpClient] = boost::make_shared< WSConnection<endpoint_type> > (this, cpClient);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
void on_pong (connection_ptr cpClient, std::string data)
|
||||
{
|
||||
wsc_ptr ptr;
|
||||
{
|
||||
boost::mutex::scoped_lock sl (mMapLock);
|
||||
typename boost::unordered_map<connection_ptr, wsc_ptr>::iterator it = mMap.find (cpClient);
|
||||
|
||||
if (it == mMap.end ())
|
||||
return;
|
||||
|
||||
ptr = it->second;
|
||||
}
|
||||
ptr->onPong (data);
|
||||
}
|
||||
|
||||
void on_close (connection_ptr cpClient)
|
||||
{
|
||||
// we cannot destroy the connection while holding the map lock or we deadlock with pubLedger
|
||||
wsc_ptr ptr;
|
||||
{
|
||||
boost::mutex::scoped_lock sl (mMapLock);
|
||||
typename boost::unordered_map<connection_ptr, wsc_ptr>::iterator it = mMap.find (cpClient);
|
||||
|
||||
if (it == mMap.end ())
|
||||
return;
|
||||
|
||||
ptr = it->second; // prevent the WSConnection from being destroyed until we release the lock
|
||||
mMap.erase (it);
|
||||
}
|
||||
ptr->preDestroy (); // Must be done before we return
|
||||
|
||||
// Must be done without holding the websocket send lock
|
||||
getApp().getJobQueue ().addJob (jtCLIENT, "WSClient::destroy",
|
||||
BIND_TYPE (&WSConnection<endpoint_type>::destroy, ptr));
|
||||
}
|
||||
|
||||
void on_message (connection_ptr cpClient, message_ptr mpMessage)
|
||||
{
|
||||
wsc_ptr ptr;
|
||||
{
|
||||
boost::mutex::scoped_lock sl (mMapLock);
|
||||
typename boost::unordered_map<connection_ptr, wsc_ptr>::iterator it = mMap.find (cpClient);
|
||||
|
||||
if (it == mMap.end ())
|
||||
return;
|
||||
|
||||
ptr = it->second;
|
||||
}
|
||||
|
||||
bool bRejected, bRunQ;
|
||||
ptr->rcvMessage (mpMessage, bRejected, bRunQ);
|
||||
|
||||
if (bRejected)
|
||||
{
|
||||
try
|
||||
{
|
||||
WriteLog (lsDEBUG, WSServerHandlerLog) << "Ws:: Rejected("
|
||||
<< cpClient->get_socket ().lowest_layer ().remote_endpoint ().address ().to_string ()
|
||||
<< ") '" << mpMessage->get_payload () << "'";
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
if (bRunQ)
|
||||
getApp().getJobQueue ().addJob (jtCLIENT, "WSClient::command",
|
||||
BIND_TYPE (&WSServerHandler<endpoint_type>::do_messages, this, P_1, cpClient));
|
||||
}
|
||||
|
||||
void do_messages (Job& job, connection_ptr cpClient)
|
||||
{
|
||||
wsc_ptr ptr;
|
||||
{
|
||||
boost::mutex::scoped_lock sl (mMapLock);
|
||||
typename boost::unordered_map<connection_ptr, wsc_ptr>::iterator it = mMap.find (cpClient);
|
||||
|
||||
if (it == mMap.end ())
|
||||
return;
|
||||
|
||||
ptr = it->second;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 10; ++i)
|
||||
{
|
||||
message_ptr msg = ptr->getMessage ();
|
||||
|
||||
if (!msg)
|
||||
return;
|
||||
|
||||
do_message (job, cpClient, ptr, msg);
|
||||
}
|
||||
|
||||
getApp().getJobQueue ().addJob (jtCLIENT, "WSClient::more",
|
||||
BIND_TYPE (&WSServerHandler<endpoint_type>::do_messages, this, P_1, cpClient));
|
||||
}
|
||||
|
||||
void do_message (Job& job, const connection_ptr& cpClient, const wsc_ptr& conn, const message_ptr& mpMessage)
|
||||
{
|
||||
Json::Value jvRequest;
|
||||
Json::Reader jrReader;
|
||||
|
||||
try
|
||||
{
|
||||
WriteLog (lsDEBUG, WSServerHandlerLog) << "Ws:: Receiving("
|
||||
<< cpClient->get_socket ().lowest_layer ().remote_endpoint ().address ().to_string ()
|
||||
<< ") '" << mpMessage->get_payload () << "'";
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
|
||||
if (mpMessage->get_opcode () != websocketpp::frame::opcode::TEXT)
|
||||
{
|
||||
Json::Value jvResult (Json::objectValue);
|
||||
|
||||
jvResult["type"] = "error";
|
||||
jvResult["error"] = "wsTextRequired"; // We only accept text messages.
|
||||
|
||||
send (cpClient, jvResult, false);
|
||||
}
|
||||
else if (!jrReader.parse (mpMessage->get_payload (), jvRequest) || jvRequest.isNull () || !jvRequest.isObject ())
|
||||
{
|
||||
Json::Value jvResult (Json::objectValue);
|
||||
|
||||
jvResult["type"] = "error";
|
||||
jvResult["error"] = "jsonInvalid"; // Received invalid json.
|
||||
jvResult["value"] = mpMessage->get_payload ();
|
||||
|
||||
send (cpClient, jvResult, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (jvRequest.isMember ("command"))
|
||||
job.rename (std::string ("WSClient::") + jvRequest["command"].asString ());
|
||||
|
||||
send (cpClient, conn->invokeCommand (jvRequest), false);
|
||||
}
|
||||
}
|
||||
|
||||
boost::shared_ptr<boost::asio::ssl::context> on_tls_init ()
|
||||
{
|
||||
return mCtx;
|
||||
}
|
||||
|
||||
// Respond to http requests.
|
||||
bool http (connection_ptr cpClient)
|
||||
{
|
||||
std::string reason;
|
||||
|
||||
if (!serverOkay (reason))
|
||||
{
|
||||
cpClient->set_body (std::string ("<HTML><BODY>Server cannot accept clients: ") + reason + "</BODY></HTML>");
|
||||
return false;
|
||||
}
|
||||
|
||||
cpClient->set_body (
|
||||
"<!DOCTYPE html><html><head><title>" SYSTEM_NAME " Test</title></head>"
|
||||
"<body><h1>" SYSTEM_NAME " Test</h1><p>This page shows http(s) connectivity is working.</p></body></html>");
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
// vim:ts=4
|
||||
361
modules/ripple_app/paths/ripple_PathRequest.cpp
Normal file
361
modules/ripple_app/paths/ripple_PathRequest.cpp
Normal file
@@ -0,0 +1,361 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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
|
||||
72
modules/ripple_app/paths/ripple_PathRequest.h
Normal file
72
modules/ripple_app/paths/ripple_PathRequest.h
Normal file
@@ -0,0 +1,72 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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
|
||||
794
modules/ripple_app/paths/ripple_PathState.cpp
Normal file
794
modules/ripple_app/paths/ripple_PathState.cpp
Normal file
@@ -0,0 +1,794 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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;
|
||||
}
|
||||
|
||||
164
modules/ripple_app/paths/ripple_PathState.h
Normal file
164
modules/ripple_app/paths/ripple_PathState.h
Normal file
@@ -0,0 +1,164 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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
|
||||
845
modules/ripple_app/paths/ripple_Pathfinder.cpp
Normal file
845
modules/ripple_app/paths/ripple_Pathfinder.cpp
Normal file
@@ -0,0 +1,845 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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
|
||||
93
modules/ripple_app/paths/ripple_Pathfinder.h
Normal file
93
modules/ripple_app/paths/ripple_Pathfinder.h
Normal file
@@ -0,0 +1,93 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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
|
||||
2670
modules/ripple_app/paths/ripple_RippleCalc.cpp
Normal file
2670
modules/ripple_app/paths/ripple_RippleCalc.cpp
Normal file
File diff suppressed because it is too large
Load Diff
88
modules/ripple_app/paths/ripple_RippleCalc.h
Normal file
88
modules/ripple_app/paths/ripple_RippleCalc.h
Normal file
@@ -0,0 +1,88 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_RIPPLECALC_H
|
||||
#define RIPPLE_RIPPLECALC_H
|
||||
|
||||
/** Calculate the quality of a payment path.
|
||||
|
||||
The quality is a synonym for price. Specifically, the amount of
|
||||
input required to produce a given output along a specified path.
|
||||
*/
|
||||
// VFALCO TODO What's the difference between a RippleState versus PathState?
|
||||
//
|
||||
class RippleCalc
|
||||
{
|
||||
public:
|
||||
// First time working in reverse a funding source was mentioned. Source may only be used there.
|
||||
curIssuerNode mumSource; // Map of currency, issuer to node index.
|
||||
|
||||
// If the transaction fails to meet some constraint, still need to delete unfunded offers.
|
||||
boost::unordered_set<uint256> musUnfundedFound; // Offers that were found unfunded.
|
||||
|
||||
void pathNext (PathState::ref psrCur, const bool bMultiQuality, const LedgerEntrySet& lesCheckpoint, LedgerEntrySet& lesCurrent);
|
||||
TER calcNode (const unsigned int uNode, PathState& psCur, const bool bMultiQuality);
|
||||
TER calcNodeRev (const unsigned int uNode, PathState& psCur, const bool bMultiQuality);
|
||||
TER calcNodeFwd (const unsigned int uNode, PathState& psCur, const bool bMultiQuality);
|
||||
TER calcNodeOfferRev (const unsigned int uNode, PathState& psCur, const bool bMultiQuality);
|
||||
TER calcNodeOfferFwd (const unsigned int uNode, PathState& psCur, const bool bMultiQuality);
|
||||
TER calcNodeAccountRev (const unsigned int uNode, PathState& psCur, const bool bMultiQuality);
|
||||
TER calcNodeAccountFwd (const unsigned int uNode, PathState& psCur, const bool bMultiQuality);
|
||||
TER calcNodeAdvance (const unsigned int uNode, PathState& psCur, const bool bMultiQuality, const bool bReverse);
|
||||
TER calcNodeDeliverRev (
|
||||
const unsigned int uNode,
|
||||
PathState& psCur,
|
||||
const bool bMultiQuality,
|
||||
const uint160& uOutAccountID,
|
||||
const STAmount& saOutReq,
|
||||
STAmount& saOutAct);
|
||||
|
||||
TER calcNodeDeliverFwd (
|
||||
const unsigned int uNode,
|
||||
PathState& psCur,
|
||||
const bool bMultiQuality,
|
||||
const uint160& uInAccountID,
|
||||
const STAmount& saInReq,
|
||||
STAmount& saInAct,
|
||||
STAmount& saInFees);
|
||||
|
||||
void calcNodeRipple (const uint32 uQualityIn, const uint32 uQualityOut,
|
||||
const STAmount& saPrvReq, const STAmount& saCurReq,
|
||||
STAmount& saPrvAct, STAmount& saCurAct,
|
||||
uint64& uRateMax);
|
||||
|
||||
RippleCalc (LedgerEntrySet& lesNodes, const bool bOpenLedger)
|
||||
: lesActive (lesNodes), mOpenLedger (bOpenLedger)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
static TER rippleCalc (
|
||||
LedgerEntrySet& lesActive,
|
||||
STAmount& saMaxAmountAct,
|
||||
STAmount& saDstAmountAct,
|
||||
std::vector<PathState::pointer>& vpsExpanded,
|
||||
const STAmount& saDstAmountReq,
|
||||
const STAmount& saMaxAmountReq,
|
||||
const uint160& uDstAccountID,
|
||||
const uint160& uSrcAccountID,
|
||||
const STPathSet& spsPaths,
|
||||
const bool bPartialPayment,
|
||||
const bool bLimitQuality,
|
||||
const bool bNoRippleDirect,
|
||||
const bool bStandAlone, // --> True, not to affect accounts.
|
||||
const bool bOpenLedger = true // --> What kind of errors to return.
|
||||
);
|
||||
|
||||
static void setCanonical (STPathSet& spsDst, const std::vector<PathState::pointer>& vpsExpanded, bool bKeepDefault);
|
||||
|
||||
protected:
|
||||
LedgerEntrySet& lesActive;
|
||||
bool mOpenLedger;
|
||||
};
|
||||
|
||||
#endif
|
||||
// vim:ts=4
|
||||
18
modules/ripple_app/paths/ripple_RippleLineCache.cpp
Normal file
18
modules/ripple_app/paths/ripple_RippleLineCache.cpp
Normal file
@@ -0,0 +1,18 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
AccountItems& RippleLineCache::getRippleLines (const uint160& accountID)
|
||||
{
|
||||
boost::mutex::scoped_lock sl (mLock);
|
||||
|
||||
boost::unordered_map <uint160, AccountItems::pointer>::iterator it = mRLMap.find (accountID);
|
||||
|
||||
if (it == mRLMap.end ())
|
||||
it = mRLMap.insert (std::make_pair (accountID, boost::make_shared<AccountItems>
|
||||
(boost::cref (accountID), boost::cref (mLedger), AccountItem::pointer (new RippleState ())))).first;
|
||||
|
||||
return *it->second;
|
||||
}
|
||||
37
modules/ripple_app/paths/ripple_RippleLineCache.h
Normal file
37
modules/ripple_app/paths/ripple_RippleLineCache.h
Normal file
@@ -0,0 +1,37 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_RIPPLELINECACHE_H
|
||||
#define RIPPLE_RIPPLELINECACHE_H
|
||||
|
||||
// Used by Pathfinder
|
||||
class RippleLineCache
|
||||
{
|
||||
public:
|
||||
typedef boost::shared_ptr <RippleLineCache> pointer;
|
||||
typedef pointer const& ref;
|
||||
|
||||
explicit RippleLineCache (Ledger::ref l)
|
||||
: mLedger (l)
|
||||
{
|
||||
}
|
||||
|
||||
Ledger::ref getLedger () // VFALCO TODO const?
|
||||
{
|
||||
return mLedger;
|
||||
}
|
||||
|
||||
AccountItems& getRippleLines (const uint160& accountID);
|
||||
|
||||
private:
|
||||
boost::mutex mLock;
|
||||
|
||||
Ledger::pointer mLedger;
|
||||
|
||||
boost::unordered_map <uint160, AccountItems::pointer> mRLMap;
|
||||
};
|
||||
|
||||
#endif
|
||||
60
modules/ripple_app/paths/ripple_RippleState.cpp
Normal file
60
modules/ripple_app/paths/ripple_RippleState.cpp
Normal file
@@ -0,0 +1,60 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
AccountItem::pointer RippleState::makeItem (const uint160& accountID, SerializedLedgerEntry::ref ledgerEntry)
|
||||
{
|
||||
if (!ledgerEntry || ledgerEntry->getType () != ltRIPPLE_STATE)
|
||||
return AccountItem::pointer ();
|
||||
|
||||
RippleState* rs = new RippleState (ledgerEntry);
|
||||
rs->setViewAccount (accountID);
|
||||
|
||||
return AccountItem::pointer (rs);
|
||||
}
|
||||
|
||||
RippleState::RippleState (SerializedLedgerEntry::ref ledgerEntry) : AccountItem (ledgerEntry),
|
||||
mValid (false),
|
||||
mViewLowest (true),
|
||||
|
||||
mLowLimit (ledgerEntry->getFieldAmount (sfLowLimit)),
|
||||
mHighLimit (ledgerEntry->getFieldAmount (sfHighLimit)),
|
||||
|
||||
mLowID (mLowLimit.getIssuer ()),
|
||||
mHighID (mHighLimit.getIssuer ()),
|
||||
|
||||
mBalance (ledgerEntry->getFieldAmount (sfBalance))
|
||||
{
|
||||
mFlags = mLedgerEntry->getFieldU32 (sfFlags);
|
||||
|
||||
mLowQualityIn = mLedgerEntry->getFieldU32 (sfLowQualityIn);
|
||||
mLowQualityOut = mLedgerEntry->getFieldU32 (sfLowQualityOut);
|
||||
|
||||
mHighQualityIn = mLedgerEntry->getFieldU32 (sfHighQualityIn);
|
||||
mHighQualityOut = mLedgerEntry->getFieldU32 (sfHighQualityOut);
|
||||
|
||||
mValid = true;
|
||||
}
|
||||
|
||||
void RippleState::setViewAccount (const uint160& accountID)
|
||||
{
|
||||
bool bViewLowestNew = mLowID == accountID;
|
||||
|
||||
if (bViewLowestNew != mViewLowest)
|
||||
{
|
||||
mViewLowest = bViewLowestNew;
|
||||
mBalance.negate ();
|
||||
}
|
||||
}
|
||||
|
||||
Json::Value RippleState::getJson (int)
|
||||
{
|
||||
Json::Value ret (Json::objectValue);
|
||||
ret["low_id"] = mLowID.GetHex ();
|
||||
ret["high_id"] = mHighID.GetHex ();
|
||||
return ret;
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
122
modules/ripple_app/paths/ripple_RippleState.h
Normal file
122
modules/ripple_app/paths/ripple_RippleState.h
Normal file
@@ -0,0 +1,122 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_RIPPLESTATE_H
|
||||
#define RIPPLE_RIPPLESTATE_H
|
||||
|
||||
//
|
||||
// A ripple line's state.
|
||||
// - Isolate ledger entry format.
|
||||
//
|
||||
|
||||
class RippleState : public AccountItem
|
||||
{
|
||||
public:
|
||||
typedef boost::shared_ptr <RippleState> pointer;
|
||||
|
||||
public:
|
||||
RippleState () { }
|
||||
|
||||
virtual ~RippleState () { }
|
||||
|
||||
AccountItem::pointer makeItem (const uint160& accountID, SerializedLedgerEntry::ref ledgerEntry);
|
||||
|
||||
LedgerEntryType getType ()
|
||||
{
|
||||
return ltRIPPLE_STATE;
|
||||
}
|
||||
|
||||
void setViewAccount (const uint160& accountID);
|
||||
|
||||
const uint160& getAccountID () const
|
||||
{
|
||||
return mViewLowest ? mLowID : mHighID;
|
||||
}
|
||||
|
||||
const uint160& getAccountIDPeer () const
|
||||
{
|
||||
return !mViewLowest ? mLowID : mHighID;
|
||||
}
|
||||
|
||||
// True, Provided auth to peer.
|
||||
bool getAuth () const
|
||||
{
|
||||
return isSetBit (mFlags, mViewLowest ? lsfLowAuth : lsfHighAuth);
|
||||
}
|
||||
|
||||
bool getAuthPeer () const
|
||||
{
|
||||
return isSetBit (mFlags, !mViewLowest ? lsfLowAuth : lsfHighAuth);
|
||||
}
|
||||
|
||||
const STAmount& getBalance () const
|
||||
{
|
||||
return mBalance;
|
||||
}
|
||||
|
||||
const STAmount& getLimit () const
|
||||
{
|
||||
return mViewLowest ? mLowLimit : mHighLimit;
|
||||
}
|
||||
|
||||
const STAmount& getLimitPeer () const
|
||||
{
|
||||
return !mViewLowest ? mLowLimit : mHighLimit;
|
||||
}
|
||||
|
||||
uint32 getQualityIn () const
|
||||
{
|
||||
return ((uint32) (mViewLowest ? mLowQualityIn : mHighQualityIn));
|
||||
}
|
||||
|
||||
uint32 getQualityOut () const
|
||||
{
|
||||
return ((uint32) (mViewLowest ? mLowQualityOut : mHighQualityOut));
|
||||
}
|
||||
|
||||
SerializedLedgerEntry::pointer getSLE ()
|
||||
{
|
||||
return mLedgerEntry;
|
||||
}
|
||||
|
||||
const SerializedLedgerEntry& peekSLE () const
|
||||
{
|
||||
return *mLedgerEntry;
|
||||
}
|
||||
|
||||
SerializedLedgerEntry& peekSLE ()
|
||||
{
|
||||
return *mLedgerEntry;
|
||||
}
|
||||
|
||||
Json::Value getJson (int);
|
||||
|
||||
Blob getRaw () const;
|
||||
|
||||
private:
|
||||
explicit RippleState (SerializedLedgerEntry::ref ledgerEntry); // For accounts in a ledger
|
||||
|
||||
private:
|
||||
bool mValid;
|
||||
bool mViewLowest;
|
||||
|
||||
uint32 mFlags;
|
||||
|
||||
STAmount mLowLimit;
|
||||
STAmount mHighLimit;
|
||||
|
||||
uint160 mLowID;
|
||||
uint160 mHighID;
|
||||
|
||||
uint64 mLowQualityIn;
|
||||
uint64 mLowQualityOut;
|
||||
uint64 mHighQualityIn;
|
||||
uint64 mHighQualityOut;
|
||||
|
||||
STAmount mBalance;
|
||||
};
|
||||
#endif
|
||||
// vim:ts=4
|
||||
86
modules/ripple_app/peers/Peer.h
Normal file
86
modules/ripple_app/peers/Peer.h
Normal file
@@ -0,0 +1,86 @@
|
||||
#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
|
||||
107
modules/ripple_app/peers/PeerDoor.cpp
Normal file
107
modules/ripple_app/peers/PeerDoor.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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);
|
||||
}
|
||||
26
modules/ripple_app/peers/PeerDoor.h
Normal file
26
modules/ripple_app/peers/PeerDoor.h
Normal file
@@ -0,0 +1,26 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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
|
||||
58
modules/ripple_app/peers/ripple_ClusterNodeStatus.h
Normal file
58
modules/ripple_app/peers/ripple_ClusterNodeStatus.h
Normal file
@@ -0,0 +1,58 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_CLUSTERNODESTATUS_H_INCLUDED
|
||||
#define RIPPLE_CLUSTERNODESTATUS_H_INCLUDED
|
||||
|
||||
class ClusterNodeStatus
|
||||
{
|
||||
public:
|
||||
|
||||
ClusterNodeStatus() : mLoadFee(0), mReportTime(0)
|
||||
{ ; }
|
||||
|
||||
explicit 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
|
||||
80
modules/ripple_app/peers/ripple_IPeers.h
Normal file
80
modules/ripple_app/peers/ripple_IPeers.h
Normal file
@@ -0,0 +1,80 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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
|
||||
2478
modules/ripple_app/peers/ripple_Peer.cpp
Normal file
2478
modules/ripple_app/peers/ripple_Peer.cpp
Normal file
File diff suppressed because it is too large
Load Diff
90
modules/ripple_app/peers/ripple_Peer.h
Normal file
90
modules/ripple_app/peers/ripple_Peer.h
Normal file
@@ -0,0 +1,90 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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
|
||||
108
modules/ripple_app/peers/ripple_PeerSet.cpp
Normal file
108
modules/ripple_app/peers/ripple_PeerSet.cpp
Normal file
@@ -0,0 +1,108 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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 ();
|
||||
}
|
||||
112
modules/ripple_app/peers/ripple_PeerSet.h
Normal file
112
modules/ripple_app/peers/ripple_PeerSet.h
Normal file
@@ -0,0 +1,112 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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
|
||||
898
modules/ripple_app/peers/ripple_Peers.cpp
Normal file
898
modules/ripple_app/peers/ripple_Peers.cpp
Normal file
@@ -0,0 +1,898 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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)
|
||||
|
||||
2063
modules/ripple_app/peers/ripple_UniqueNodeList.cpp
Normal file
2063
modules/ripple_app/peers/ripple_UniqueNodeList.cpp
Normal file
File diff suppressed because it is too large
Load Diff
64
modules/ripple_app/peers/ripple_UniqueNodeList.h
Normal file
64
modules/ripple_app/peers/ripple_UniqueNodeList.h
Normal file
@@ -0,0 +1,64 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_UNIQUENODELIST_H_INCLUDED
|
||||
#define RIPPLE_UNIQUENODELIST_H_INCLUDED
|
||||
|
||||
class UniqueNodeList
|
||||
{
|
||||
public:
|
||||
enum ValidatorSource
|
||||
{
|
||||
vsConfig = 'C', // rippled.cfg
|
||||
vsInbound = 'I',
|
||||
vsManual = 'M',
|
||||
vsReferral = 'R',
|
||||
vsTold = 'T',
|
||||
vsValidator = 'V', // validators.txt
|
||||
vsWeb = 'W',
|
||||
};
|
||||
|
||||
// VFALCO TODO rename this to use the right coding style
|
||||
typedef long score;
|
||||
|
||||
public:
|
||||
// VFALCO TODO make this not use boost::asio...
|
||||
static UniqueNodeList* New ();
|
||||
|
||||
virtual ~UniqueNodeList () { }
|
||||
|
||||
// VFALCO TODO Roll this into the constructor so there is one less state.
|
||||
virtual void start () = 0;
|
||||
|
||||
// VFALCO TODO rename all these, the "node" prefix is redundant (lol)
|
||||
virtual void nodeAddPublic (const RippleAddress& naNodePublic, ValidatorSource vsWhy, const std::string& strComment) = 0;
|
||||
virtual void nodeAddDomain (std::string strDomain, ValidatorSource vsWhy, const std::string& strComment = "") = 0;
|
||||
virtual void nodeRemovePublic (const RippleAddress& naNodePublic) = 0;
|
||||
virtual void nodeRemoveDomain (std::string strDomain) = 0;
|
||||
virtual void nodeReset () = 0;
|
||||
|
||||
virtual void nodeScore () = 0;
|
||||
|
||||
virtual bool nodeInUNL (const RippleAddress& naNodePublic) = 0;
|
||||
virtual bool nodeInCluster (const RippleAddress& naNodePublic) = 0;
|
||||
virtual bool nodeInCluster (const RippleAddress& naNodePublic, std::string& name) = 0;
|
||||
virtual bool nodeUpdate (const RippleAddress& naNodePublic, ClusterNodeStatus const& cnsStatus) = 0;
|
||||
virtual std::map<RippleAddress, ClusterNodeStatus> getClusterStatus () = 0;
|
||||
virtual uint32 getClusterFee () = 0;
|
||||
virtual void addClusterStatus (Json::Value&) = 0;
|
||||
|
||||
virtual void nodeBootstrap () = 0;
|
||||
virtual bool nodeLoad (boost::filesystem::path pConfig) = 0;
|
||||
virtual void nodeNetwork () = 0;
|
||||
|
||||
virtual Json::Value getUnlJson () = 0;
|
||||
|
||||
virtual int iSourceScore (ValidatorSource vsWhy) = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
// vim:ts=4
|
||||
@@ -112,84 +112,84 @@ namespace ripple
|
||||
#include "node/ripple_NullBackendFactory.h"
|
||||
#include "node/ripple_SqliteBackendFactory.h"
|
||||
|
||||
#include "src/cpp/ripple/ripple_SHAMapItem.h"
|
||||
#include "src/cpp/ripple/ripple_SHAMapNode.h"
|
||||
#include "src/cpp/ripple/ripple_SHAMapTreeNode.h"
|
||||
#include "src/cpp/ripple/ripple_SHAMapMissingNode.h"
|
||||
#include "src/cpp/ripple/ripple_SHAMapSyncFilter.h"
|
||||
#include "src/cpp/ripple/ripple_SHAMapAddNode.h"
|
||||
#include "src/cpp/ripple/ripple_SHAMap.h"
|
||||
#include "src/cpp/ripple/ripple_SerializedTransaction.h"
|
||||
#include "src/cpp/ripple/ripple_SerializedLedger.h"
|
||||
#include "src/cpp/ripple/TransactionMeta.h"
|
||||
#include "src/cpp/ripple/Transaction.h"
|
||||
#include "src/cpp/ripple/ripple_AccountState.h"
|
||||
#include "src/cpp/ripple/ripple_NicknameState.h"
|
||||
#include "shamap/ripple_SHAMapItem.h"
|
||||
#include "shamap/ripple_SHAMapNode.h"
|
||||
#include "shamap/ripple_SHAMapTreeNode.h"
|
||||
#include "shamap/ripple_SHAMapMissingNode.h"
|
||||
#include "shamap/ripple_SHAMapSyncFilter.h"
|
||||
#include "shamap/ripple_SHAMapAddNode.h"
|
||||
#include "shamap/ripple_SHAMap.h"
|
||||
#include "misc/ripple_SerializedTransaction.h"
|
||||
#include "misc/ripple_SerializedLedger.h"
|
||||
#include "tx/TransactionMeta.h"
|
||||
#include "tx/Transaction.h"
|
||||
#include "misc/ripple_AccountState.h"
|
||||
#include "misc/ripple_NicknameState.h"
|
||||
#include "ledger/Ledger.h"
|
||||
#include "ledger/SerializedValidation.h"
|
||||
#include "src/cpp/ripple/ripple_ILoadManager.h"
|
||||
#include "src/cpp/ripple/ripple_ProofOfWork.h"
|
||||
#include "src/cpp/ripple/ripple_InfoSub.h"
|
||||
#include "src/cpp/ripple/ripple_OrderBook.h"
|
||||
#include "src/cpp/ripple/ripple_SHAMapSyncFilters.h"
|
||||
#include "src/cpp/ripple/ripple_IFeatures.h"
|
||||
#include "src/cpp/ripple/ripple_IFeeVote.h"
|
||||
#include "src/cpp/ripple/ripple_IHashRouter.h"
|
||||
#include "src/cpp/ripple/ripple_Peer.h" // VFALCO TODO Rename to IPeer
|
||||
#include "src/cpp/ripple/ripple_IPeers.h"
|
||||
#include "src/cpp/ripple/ripple_IProofOfWorkFactory.h"
|
||||
#include "src/cpp/ripple/ripple_ClusterNodeStatus.h"
|
||||
#include "src/cpp/ripple/ripple_UniqueNodeList.h"
|
||||
#include "src/cpp/ripple/ripple_IValidations.h"
|
||||
#include "src/cpp/ripple/ripple_PeerSet.h"
|
||||
#include "main/ripple_ILoadManager.h"
|
||||
#include "misc/ripple_ProofOfWork.h"
|
||||
#include "misc/ripple_InfoSub.h"
|
||||
#include "misc/ripple_OrderBook.h"
|
||||
#include "shamap/ripple_SHAMapSyncFilters.h"
|
||||
#include "misc/ripple_IFeatures.h"
|
||||
#include "misc/ripple_IFeeVote.h"
|
||||
#include "misc/ripple_IHashRouter.h"
|
||||
#include "peers/ripple_Peer.h" // VFALCO TODO Rename to IPeer
|
||||
#include "peers/ripple_IPeers.h"
|
||||
#include "misc/ripple_IProofOfWorkFactory.h"
|
||||
#include "peers/ripple_ClusterNodeStatus.h"
|
||||
#include "peers/ripple_UniqueNodeList.h"
|
||||
#include "misc/ripple_IValidations.h"
|
||||
#include "peers/ripple_PeerSet.h"
|
||||
#include "ledger/ripple_InboundLedger.h"
|
||||
#include "ledger/ripple_InboundLedgers.h"
|
||||
#include "src/cpp/ripple/ripple_AccountItem.h"
|
||||
#include "src/cpp/ripple/ripple_AccountItems.h"
|
||||
#include "misc/ripple_AccountItem.h"
|
||||
#include "misc/ripple_AccountItems.h"
|
||||
#include "ledger/ripple_AcceptedLedgerTx.h"
|
||||
#include "ledger/ripple_AcceptedLedger.h"
|
||||
#include "ledger/ripple_LedgerEntrySet.h"
|
||||
#include "src/cpp/ripple/TransactionEngine.h"
|
||||
#include "src/cpp/ripple/ripple_CanonicalTXSet.h"
|
||||
#include "tx/TransactionEngine.h"
|
||||
#include "misc/ripple_CanonicalTXSet.h"
|
||||
#include "ledger/ripple_LedgerHistory.h"
|
||||
#include "ledger/LedgerMaster.h"
|
||||
#include "ledger/LedgerProposal.h"
|
||||
#include "src/cpp/ripple/NetworkOPs.h"
|
||||
#include "src/cpp/ripple/TransactionMaster.h"
|
||||
#include "src/cpp/ripple/ripple_LocalCredentials.h"
|
||||
#include "src/cpp/ripple/WSDoor.h"
|
||||
#include "src/cpp/ripple/ripple_Application.h"
|
||||
#include "src/cpp/ripple/RPCHandler.h"
|
||||
#include "src/cpp/ripple/TransactionQueue.h"
|
||||
#include "misc/NetworkOPs.h"
|
||||
#include "tx/TransactionMaster.h"
|
||||
#include "main/ripple_LocalCredentials.h"
|
||||
#include "network/WSDoor.h"
|
||||
#include "main/ripple_Application.h"
|
||||
#include "rpc/RPCHandler.h"
|
||||
#include "tx/TransactionQueue.h"
|
||||
#include "ledger/OrderBookDB.h"
|
||||
#include "src/cpp/ripple/CallRPC.h"
|
||||
#include "src/cpp/ripple/Transactor.h"
|
||||
#include "src/cpp/ripple/ChangeTransactor.h"
|
||||
#include "src/cpp/ripple/ripple_TransactionAcquire.h"
|
||||
#include "src/cpp/ripple/ripple_DisputedTx.h"
|
||||
#include "src/cpp/ripple/ripple_LedgerConsensus.h"
|
||||
#include "rpc/CallRPC.h"
|
||||
#include "tx/Transactor.h"
|
||||
#include "tx/ChangeTransactor.h"
|
||||
#include "tx/ripple_TransactionAcquire.h"
|
||||
#include "consensus/ripple_DisputedTx.h"
|
||||
#include "consensus/ripple_LedgerConsensus.h"
|
||||
#include "ledger/LedgerTiming.h"
|
||||
#include "src/cpp/ripple/ripple_Offer.h"
|
||||
#include "src/cpp/ripple/OfferCancelTransactor.h"
|
||||
#include "src/cpp/ripple/OfferCreateTransactor.h"
|
||||
#include "src/cpp/ripple/ripple_PathRequest.h"
|
||||
#include "src/cpp/ripple/ParameterTable.h"
|
||||
#include "src/cpp/ripple/ripple_RippleLineCache.h"
|
||||
#include "src/cpp/ripple/ripple_PathState.h"
|
||||
#include "src/cpp/ripple/ripple_RippleCalc.h"
|
||||
#include "src/cpp/ripple/ripple_Pathfinder.h"
|
||||
#include "src/cpp/ripple/PaymentTransactor.h"
|
||||
#include "src/cpp/ripple/PeerDoor.h"
|
||||
#include "src/cpp/ripple/RPC.h"
|
||||
#include "src/cpp/ripple/RPCErr.h"
|
||||
#include "src/cpp/ripple/RPCSub.h"
|
||||
#include "src/cpp/ripple/RegularKeySetTransactor.h"
|
||||
#include "src/cpp/ripple/ripple_RippleState.h"
|
||||
#include "src/cpp/ripple/AccountSetTransactor.h"
|
||||
#include "src/cpp/ripple/TrustSetTransactor.h"
|
||||
#include "src/cpp/ripple/WSConnection.h"
|
||||
#include "src/cpp/ripple/ripple_WSHandler.h"
|
||||
#include "src/cpp/ripple/WalletAddTransactor.h"
|
||||
#include "misc/ripple_Offer.h"
|
||||
#include "tx/OfferCancelTransactor.h"
|
||||
#include "tx/OfferCreateTransactor.h"
|
||||
#include "paths/ripple_PathRequest.h"
|
||||
#include "main/ParameterTable.h"
|
||||
#include "paths/ripple_RippleLineCache.h"
|
||||
#include "paths/ripple_PathState.h"
|
||||
#include "paths/ripple_RippleCalc.h"
|
||||
#include "paths/ripple_Pathfinder.h"
|
||||
#include "tx/PaymentTransactor.h"
|
||||
#include "peers/PeerDoor.h"
|
||||
#include "rpc/RPC.h"
|
||||
#include "rpc/RPCErr.h"
|
||||
#include "rpc/RPCSub.h"
|
||||
#include "tx/RegularKeySetTransactor.h"
|
||||
#include "paths/ripple_RippleState.h"
|
||||
#include "tx/AccountSetTransactor.h"
|
||||
#include "tx/TrustSetTransactor.h"
|
||||
#include "network/WSConnection.h"
|
||||
#include "network/ripple_WSHandler.h"
|
||||
#include "tx/WalletAddTransactor.h"
|
||||
|
||||
#include "contracts/ripple_ScriptData.h"
|
||||
#include "contracts/ripple_Contract.h"
|
||||
@@ -200,7 +200,7 @@ namespace ripple
|
||||
#include "basics/ripple_BuildVersion.h" // private
|
||||
#include "basics/ripple_RPCServerHandler.h"
|
||||
|
||||
#include "src/cpp/ripple/RPCDoor.h" // needs RPCServer
|
||||
#include "rpc/RPCDoor.h" // needs RPCServer
|
||||
|
||||
}
|
||||
|
||||
@@ -257,13 +257,13 @@ static const uint64 tenTo17m1 = tenTo17 - 1;
|
||||
#include "node/ripple_SqliteBackendFactory.cpp"
|
||||
|
||||
#include "ledger/Ledger.cpp"
|
||||
#include "src/cpp/ripple/ripple_SHAMapDelta.cpp"
|
||||
#include "src/cpp/ripple/ripple_SHAMapNode.cpp"
|
||||
#include "src/cpp/ripple/ripple_SHAMapTreeNode.cpp"
|
||||
#include "shamap/ripple_SHAMapDelta.cpp"
|
||||
#include "shamap/ripple_SHAMapNode.cpp"
|
||||
#include "shamap/ripple_SHAMapTreeNode.cpp"
|
||||
|
||||
#include "src/cpp/ripple/ripple_AccountItems.cpp"
|
||||
#include "src/cpp/ripple/ripple_AccountState.cpp"
|
||||
#include "src/cpp/ripple/ChangeTransactor.cpp"
|
||||
#include "misc/ripple_AccountItems.cpp"
|
||||
#include "misc/ripple_AccountState.cpp"
|
||||
#include "tx/ChangeTransactor.cpp"
|
||||
|
||||
#include "contracts/ripple_Contract.cpp"
|
||||
#include "contracts/ripple_Operation.cpp"
|
||||
@@ -276,19 +276,19 @@ static const uint64 tenTo17m1 = tenTo17 - 1;
|
||||
|
||||
#if ! defined (RIPPLE_MAIN_PART) || RIPPLE_MAIN_PART == 2
|
||||
|
||||
#include "src/cpp/ripple/RPCHandler.cpp"
|
||||
#include "src/cpp/ripple/ripple_SHAMap.cpp" // Uses theApp
|
||||
#include "src/cpp/ripple/ripple_SHAMapItem.cpp"
|
||||
#include "src/cpp/ripple/ripple_SHAMapSync.cpp"
|
||||
#include "src/cpp/ripple/ripple_SHAMapMissingNode.cpp"
|
||||
#include "rpc/RPCHandler.cpp"
|
||||
#include "shamap/ripple_SHAMap.cpp" // Uses theApp
|
||||
#include "shamap/ripple_SHAMapItem.cpp"
|
||||
#include "shamap/ripple_SHAMapSync.cpp"
|
||||
#include "shamap/ripple_SHAMapMissingNode.cpp"
|
||||
|
||||
#include "src/cpp/ripple/ripple_AccountItem.cpp"
|
||||
#include "src/cpp/ripple/AccountSetTransactor.cpp"
|
||||
#include "src/cpp/ripple/ripple_CanonicalTXSet.cpp"
|
||||
#include "misc/ripple_AccountItem.cpp"
|
||||
#include "tx/AccountSetTransactor.cpp"
|
||||
#include "misc/ripple_CanonicalTXSet.cpp"
|
||||
#include "ledger/LedgerProposal.cpp"
|
||||
#include "src/cpp/ripple/ripple_LoadManager.cpp"
|
||||
#include "src/cpp/ripple/ripple_NicknameState.cpp"
|
||||
#include "src/cpp/ripple/OfferCancelTransactor.cpp"
|
||||
#include "main/ripple_LoadManager.cpp"
|
||||
#include "misc/ripple_NicknameState.cpp"
|
||||
#include "tx/OfferCancelTransactor.cpp"
|
||||
#include "ledger/OrderBookDB.cpp"
|
||||
|
||||
#include "data/ripple_Database.cpp"
|
||||
@@ -310,23 +310,23 @@ static DH* handleTmpDh (SSL* ssl, int is_export, int iKeyLength)
|
||||
return 512 == iKeyLength ? getApp().getLocalCredentials ().getDh512 () : getApp().getLocalCredentials ().getDh1024 ();
|
||||
}
|
||||
|
||||
#include "src/cpp/ripple/ripple_RippleCalc.cpp"
|
||||
#include "src/cpp/ripple/CallRPC.cpp"
|
||||
#include "src/cpp/ripple/ripple_PathState.cpp"
|
||||
#include "paths/ripple_RippleCalc.cpp"
|
||||
#include "paths/ripple_PathState.cpp"
|
||||
#include "rpc/CallRPC.cpp"
|
||||
|
||||
#include "src/cpp/ripple/ParameterTable.cpp"
|
||||
#include "src/cpp/ripple/PeerDoor.cpp"
|
||||
#include "src/cpp/ripple/ripple_RippleLineCache.cpp"
|
||||
#include "src/cpp/ripple/rpc.cpp"
|
||||
#include "src/cpp/ripple/RPCErr.cpp"
|
||||
#include "src/cpp/ripple/RPCSub.cpp"
|
||||
#include "main/ParameterTable.cpp"
|
||||
#include "peers/PeerDoor.cpp"
|
||||
#include "paths/ripple_RippleLineCache.cpp"
|
||||
#include "rpc/rpc.cpp"
|
||||
#include "rpc/RPCErr.cpp"
|
||||
#include "rpc/RPCSub.cpp"
|
||||
#include "ledger/SerializedValidation.cpp"
|
||||
#include "src/cpp/ripple/Transaction.cpp"
|
||||
#include "src/cpp/ripple/TransactionEngine.cpp"
|
||||
#include "src/cpp/ripple/TransactionMeta.cpp"
|
||||
#include "src/cpp/ripple/Transactor.cpp"
|
||||
#include "src/cpp/ripple/WSConnection.cpp"
|
||||
#include "src/cpp/ripple/WSDoor.cpp"
|
||||
#include "tx/Transaction.cpp"
|
||||
#include "tx/TransactionEngine.cpp"
|
||||
#include "tx/TransactionMeta.cpp"
|
||||
#include "tx/Transactor.cpp"
|
||||
#include "network/WSConnection.cpp"
|
||||
#include "network/WSDoor.cpp"
|
||||
|
||||
#endif
|
||||
|
||||
@@ -334,18 +334,18 @@ static DH* handleTmpDh (SSL* ssl, int is_export, int iKeyLength)
|
||||
|
||||
#if ! defined (RIPPLE_MAIN_PART) || RIPPLE_MAIN_PART == 4
|
||||
|
||||
#include "src/cpp/ripple/ripple_UniqueNodeList.cpp"
|
||||
#include "peers/ripple_UniqueNodeList.cpp"
|
||||
#include "ledger/ripple_InboundLedger.cpp"
|
||||
|
||||
#include "src/cpp/ripple/PaymentTransactor.cpp"
|
||||
#include "src/cpp/ripple/RegularKeySetTransactor.cpp"
|
||||
#include "src/cpp/ripple/ripple_RippleState.cpp"
|
||||
#include "src/cpp/ripple/RPCDoor.cpp"
|
||||
#include "src/cpp/ripple/TransactionCheck.cpp"
|
||||
#include "src/cpp/ripple/TransactionMaster.cpp"
|
||||
#include "src/cpp/ripple/TransactionQueue.cpp"
|
||||
#include "src/cpp/ripple/TrustSetTransactor.cpp"
|
||||
#include "src/cpp/ripple/ripple_WSHandler.cpp"
|
||||
#include "tx/PaymentTransactor.cpp"
|
||||
#include "tx/RegularKeySetTransactor.cpp"
|
||||
#include "paths/ripple_RippleState.cpp"
|
||||
#include "rpc/RPCDoor.cpp"
|
||||
#include "tx/TransactionCheck.cpp"
|
||||
#include "tx/TransactionMaster.cpp"
|
||||
#include "tx/TransactionQueue.cpp"
|
||||
#include "tx/TrustSetTransactor.cpp"
|
||||
#include "network/ripple_WSHandler.cpp"
|
||||
|
||||
#endif
|
||||
|
||||
@@ -353,14 +353,14 @@ static DH* handleTmpDh (SSL* ssl, int is_export, int iKeyLength)
|
||||
|
||||
#if ! defined (RIPPLE_MAIN_PART) || RIPPLE_MAIN_PART == 5
|
||||
|
||||
#include "src/cpp/ripple/ripple_Peer.cpp"
|
||||
#include "src/cpp/ripple/ripple_Application.cpp"
|
||||
#include "src/cpp/ripple/OfferCreateTransactor.cpp"
|
||||
#include "src/cpp/ripple/ripple_Validations.cpp"
|
||||
#include "peers/ripple_Peer.cpp"
|
||||
#include "main/ripple_Application.cpp"
|
||||
#include "tx/OfferCreateTransactor.cpp"
|
||||
#include "misc/ripple_Validations.cpp"
|
||||
|
||||
#include "src/cpp/ripple/WalletAddTransactor.cpp"
|
||||
#include "tx/WalletAddTransactor.cpp"
|
||||
#include "ledger/ripple_AcceptedLedgerTx.cpp"
|
||||
#include "src/cpp/ripple/ripple_FeeVote.cpp"
|
||||
#include "misc/ripple_FeeVote.cpp"
|
||||
#include "ledger/LedgerTiming.cpp"
|
||||
|
||||
#endif
|
||||
@@ -370,15 +370,15 @@ static DH* handleTmpDh (SSL* ssl, int is_export, int iKeyLength)
|
||||
#if ! defined (RIPPLE_MAIN_PART) || RIPPLE_MAIN_PART == 6
|
||||
|
||||
#include "ledger/ripple_LedgerEntrySet.cpp"
|
||||
#include "src/cpp/ripple/ripple_Pathfinder.cpp"
|
||||
#include "src/cpp/ripple/ripple_Features.cpp"
|
||||
#include "paths/ripple_Pathfinder.cpp"
|
||||
#include "misc/ripple_Features.cpp"
|
||||
|
||||
#include "src/cpp/ripple/ripple_LocalCredentials.cpp"
|
||||
#include "main/ripple_LocalCredentials.cpp"
|
||||
#include "ledger/ripple_AcceptedLedger.cpp"
|
||||
#include "src/cpp/ripple/ripple_DisputedTx.cpp"
|
||||
#include "src/cpp/ripple/ripple_HashRouter.cpp"
|
||||
#include "src/cpp/ripple/ripple_Main.cpp"
|
||||
#include "src/cpp/ripple/ripple_Offer.cpp"
|
||||
#include "consensus/ripple_DisputedTx.cpp"
|
||||
#include "misc/ripple_HashRouter.cpp"
|
||||
#include "main/ripple_Main.cpp"
|
||||
#include "misc/ripple_Offer.cpp"
|
||||
|
||||
#endif
|
||||
|
||||
@@ -386,14 +386,14 @@ static DH* handleTmpDh (SSL* ssl, int is_export, int iKeyLength)
|
||||
|
||||
#if ! defined (RIPPLE_MAIN_PART) || RIPPLE_MAIN_PART == 7
|
||||
|
||||
#include "src/cpp/ripple/NetworkOPs.cpp"
|
||||
#include "src/cpp/ripple/ripple_Peers.cpp"
|
||||
#include "misc/NetworkOPs.cpp"
|
||||
#include "peers/ripple_Peers.cpp"
|
||||
|
||||
#include "ledger/ripple_InboundLedgers.cpp"
|
||||
#include "ledger/ripple_LedgerHistory.cpp"
|
||||
#include "src/cpp/ripple/ripple_PathRequest.cpp"
|
||||
#include "src/cpp/ripple/ripple_SerializedLedger.cpp"
|
||||
#include "src/cpp/ripple/ripple_TransactionAcquire.cpp"
|
||||
#include "paths/ripple_PathRequest.cpp"
|
||||
#include "misc/ripple_SerializedLedger.cpp"
|
||||
#include "tx/ripple_TransactionAcquire.cpp"
|
||||
|
||||
#endif
|
||||
|
||||
@@ -401,18 +401,17 @@ static DH* handleTmpDh (SSL* ssl, int is_export, int iKeyLength)
|
||||
|
||||
#if ! defined (RIPPLE_MAIN_PART) || RIPPLE_MAIN_PART == 8
|
||||
|
||||
#include "src/cpp/ripple/ripple_LedgerConsensus.cpp"
|
||||
#include "consensus/ripple_LedgerConsensus.cpp"
|
||||
#include "ledger/LedgerMaster.cpp"
|
||||
#include "peers/ripple_PeerSet.cpp"
|
||||
#include "misc/ripple_InfoSub.cpp"
|
||||
#include "misc/ripple_OrderBook.cpp"
|
||||
#include "misc/ripple_ProofOfWork.cpp"
|
||||
#include "misc/ripple_ProofOfWorkFactory.h" // private
|
||||
#include "misc/ripple_ProofOfWorkFactory.cpp" // requires ProofOfWork.cpp for ProofOfWork::sMaxDifficulty
|
||||
#include "misc/ripple_SerializedTransaction.cpp"
|
||||
|
||||
#include "src/cpp/ripple/ripple_InfoSub.cpp"
|
||||
#include "src/cpp/ripple/ripple_OrderBook.cpp"
|
||||
#include "src/cpp/ripple/ripple_PeerSet.cpp"
|
||||
#include "src/cpp/ripple/ripple_ProofOfWork.cpp"
|
||||
#include "src/cpp/ripple/ripple_ProofOfWorkFactory.h" // private
|
||||
#include "src/cpp/ripple/ripple_ProofOfWorkFactory.cpp" // requires ProofOfWork.cpp for ProofOfWork::sMaxDifficulty
|
||||
#include "src/cpp/ripple/ripple_SerializedTransaction.cpp"
|
||||
|
||||
#include "src/cpp/ripple/ripple_SHAMapSyncFilters.cpp" // requires Application
|
||||
#include "shamap/ripple_SHAMapSyncFilters.cpp" // requires Application
|
||||
|
||||
#endif
|
||||
|
||||
@@ -431,9 +430,9 @@ static DH* handleTmpDh (SSL* ssl, int is_export, int iKeyLength)
|
||||
// VFALCO TODO Eliminate the need for boost for unit tests.
|
||||
//
|
||||
#include "ledger/LedgerUnitTests.cpp"
|
||||
#include "src/cpp/ripple/ripple_SHAMapUnitTests.cpp"
|
||||
#include "src/cpp/ripple/ripple_SHAMapSyncUnitTests.cpp"
|
||||
#include "src/cpp/ripple/ripple_SerializedTransactionUnitTests.cpp"
|
||||
#include "shamap/ripple_SHAMapUnitTests.cpp"
|
||||
#include "shamap/ripple_SHAMapSyncUnitTests.cpp"
|
||||
#include "misc/ripple_SerializedTransactionUnitTests.cpp"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
1064
modules/ripple_app/rpc/CallRPC.cpp
Normal file
1064
modules/ripple_app/rpc/CallRPC.cpp
Normal file
File diff suppressed because it is too large
Load Diff
73
modules/ripple_app/rpc/CallRPC.h
Normal file
73
modules/ripple_app/rpc/CallRPC.h
Normal file
@@ -0,0 +1,73 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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
|
||||
36
modules/ripple_app/rpc/RPC.h
Normal file
36
modules/ripple_app/rpc/RPC.h
Normal file
@@ -0,0 +1,36 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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
|
||||
101
modules/ripple_app/rpc/RPCDoor.cpp
Normal file
101
modules/ripple_app/rpc/RPCDoor.cpp
Normal file
@@ -0,0 +1,101 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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
|
||||
35
modules/ripple_app/rpc/RPCDoor.h
Normal file
35
modules/ripple_app/rpc/RPCDoor.h
Normal file
@@ -0,0 +1,35 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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
|
||||
106
modules/ripple_app/rpc/RPCErr.cpp
Normal file
106
modules/ripple_app/rpc/RPCErr.cpp
Normal file
@@ -0,0 +1,106 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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
|
||||
97
modules/ripple_app/rpc/RPCErr.h
Normal file
97
modules/ripple_app/rpc/RPCErr.h
Normal file
@@ -0,0 +1,97 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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
|
||||
3778
modules/ripple_app/rpc/RPCHandler.cpp
Normal file
3778
modules/ripple_app/rpc/RPCHandler.cpp
Normal file
File diff suppressed because it is too large
Load Diff
191
modules/ripple_app/rpc/RPCHandler.h
Normal file
191
modules/ripple_app/rpc/RPCHandler.h
Normal file
@@ -0,0 +1,191 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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
|
||||
119
modules/ripple_app/rpc/RPCSub.cpp
Normal file
119
modules/ripple_app/rpc/RPCSub.cpp
Normal file
@@ -0,0 +1,119 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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
|
||||
65
modules/ripple_app/rpc/RPCSub.h
Normal file
65
modules/ripple_app/rpc/RPCSub.h
Normal file
@@ -0,0 +1,65 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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
|
||||
281
modules/ripple_app/rpc/rpc.cpp
Normal file
281
modules/ripple_app/rpc/rpc.cpp
Normal file
@@ -0,0 +1,281 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
// Used for logging
|
||||
struct RPC;
|
||||
|
||||
SETUP_LOG (RPC)
|
||||
|
||||
unsigned int const gMaxHTTPHeaderSize = 0x02000000;
|
||||
|
||||
std::string gFormatStr ("v1");
|
||||
|
||||
// VFALCO TODO clean up this nonsense
|
||||
std::string FormatFullVersion ()
|
||||
{
|
||||
return (gFormatStr);
|
||||
}
|
||||
|
||||
|
||||
Json::Value JSONRPCError (int code, const std::string& message)
|
||||
{
|
||||
Json::Value error (Json::objectValue);
|
||||
|
||||
error["code"] = Json::Value (code);
|
||||
error["message"] = Json::Value (message);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
//
|
||||
// HTTP protocol
|
||||
//
|
||||
// This ain't Apache. We're just using HTTP header for the length field
|
||||
// and to be compatible with other JSON-RPC implementations.
|
||||
//
|
||||
|
||||
std::string createHTTPPost (const std::string& strHost, const std::string& strPath, const std::string& strMsg, const std::map<std::string, std::string>& mapRequestHeaders)
|
||||
{
|
||||
std::ostringstream s;
|
||||
|
||||
s << "POST "
|
||||
<< (strPath.empty () ? "/" : strPath)
|
||||
<< " HTTP/1.0\r\n"
|
||||
<< "User-Agent: " SYSTEM_NAME "-json-rpc/" << FormatFullVersion () << "\r\n"
|
||||
<< "Host: " << strHost << "\r\n"
|
||||
<< "Content-Type: application/json\r\n"
|
||||
<< "Content-Length: " << strMsg.size () << "\r\n"
|
||||
<< "Accept: application/json\r\n";
|
||||
|
||||
typedef std::map<std::string, std::string>::value_type HeaderType;
|
||||
|
||||
BOOST_FOREACH (const HeaderType & item, mapRequestHeaders)
|
||||
s << item.first << ": " << item.second << "\r\n";
|
||||
|
||||
s << "\r\n" << strMsg;
|
||||
|
||||
return s.str ();
|
||||
}
|
||||
|
||||
std::string rfc1123Time ()
|
||||
{
|
||||
char buffer[64];
|
||||
time_t now;
|
||||
time (&now);
|
||||
struct tm* now_gmt = gmtime (&now);
|
||||
std::string locale (setlocale (LC_TIME, NULL));
|
||||
setlocale (LC_TIME, "C"); // we want posix (aka "C") weekday/month strings
|
||||
strftime (buffer, sizeof (buffer), "%a, %d %b %Y %H:%M:%S +0000", now_gmt);
|
||||
setlocale (LC_TIME, locale.c_str ());
|
||||
return std::string (buffer);
|
||||
}
|
||||
|
||||
std::string HTTPReply (int nStatus, const std::string& strMsg)
|
||||
{
|
||||
WriteLog (lsTRACE, RPC) << "HTTP Reply " << nStatus << " " << strMsg;
|
||||
|
||||
if (nStatus == 401)
|
||||
return strprintf ("HTTP/1.0 401 Authorization Required\r\n"
|
||||
"Date: %s\r\n"
|
||||
"Server: " SYSTEM_NAME "-json-rpc/%s\r\n"
|
||||
"WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n"
|
||||
"Content-Type: text/html\r\n"
|
||||
"Content-Length: 296\r\n"
|
||||
"\r\n"
|
||||
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\r\n"
|
||||
"\"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd\">\r\n"
|
||||
"<HTML>\r\n"
|
||||
"<HEAD>\r\n"
|
||||
"<TITLE>Error</TITLE>\r\n"
|
||||
"<META HTTP-EQUIV='Content-Type' CONTENT='text/html; charset=ISO-8859-1'>\r\n"
|
||||
"</HEAD>\r\n"
|
||||
"<BODY><H1>401 Unauthorized.</H1></BODY>\r\n"
|
||||
"</HTML>\r\n", rfc1123Time ().c_str (), FormatFullVersion ().c_str ());
|
||||
|
||||
std::string strStatus;
|
||||
|
||||
if (nStatus == 200) strStatus = "OK";
|
||||
else if (nStatus == 400) strStatus = "Bad Request";
|
||||
else if (nStatus == 403) strStatus = "Forbidden";
|
||||
else if (nStatus == 404) strStatus = "Not Found";
|
||||
else if (nStatus == 500) strStatus = "Internal Server Error";
|
||||
|
||||
std::string access;
|
||||
|
||||
if (theConfig.RPC_ALLOW_REMOTE) access = "Access-Control-Allow-Origin: *\r\n";
|
||||
else access = "";
|
||||
|
||||
return strprintf (
|
||||
"HTTP/1.1 %d %s\r\n"
|
||||
"Date: %s\r\n"
|
||||
"Connection: Keep-Alive\r\n"
|
||||
"%s"
|
||||
"Content-Length: %d\r\n"
|
||||
"Content-Type: application/json; charset=UTF-8\r\n"
|
||||
"Server: " SYSTEM_NAME "-json-rpc/%s\r\n"
|
||||
"\r\n"
|
||||
"%s\r\n",
|
||||
nStatus,
|
||||
strStatus.c_str (),
|
||||
rfc1123Time ().c_str (),
|
||||
access.c_str (),
|
||||
strMsg.size () + 2,
|
||||
SERVER_VERSION,
|
||||
strMsg.c_str ());
|
||||
}
|
||||
|
||||
int ReadHTTPStatus (std::basic_istream<char>& stream)
|
||||
{
|
||||
std::string str;
|
||||
getline (stream, str);
|
||||
std::vector<std::string> vWords;
|
||||
boost::split (vWords, str, boost::is_any_of (" "));
|
||||
|
||||
if (vWords.size () < 2)
|
||||
return 500;
|
||||
|
||||
return atoi (vWords[1].c_str ());
|
||||
}
|
||||
|
||||
int ReadHTTPHeader (std::basic_istream<char>& stream, std::map<std::string, std::string>& mapHeadersRet)
|
||||
{
|
||||
int nLen = 0;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
std::string str;
|
||||
std::getline (stream, str);
|
||||
|
||||
if (str.empty () || str == "\r")
|
||||
break;
|
||||
|
||||
std::string::size_type nColon = str.find (":");
|
||||
|
||||
if (nColon != std::string::npos)
|
||||
{
|
||||
std::string strHeader = str.substr (0, nColon);
|
||||
boost::trim (strHeader);
|
||||
boost::to_lower (strHeader);
|
||||
std::string strValue = str.substr (nColon + 1);
|
||||
boost::trim (strValue);
|
||||
mapHeadersRet[strHeader] = strValue;
|
||||
|
||||
if (strHeader == "content-length")
|
||||
nLen = atoi (strValue.c_str ());
|
||||
}
|
||||
}
|
||||
|
||||
return nLen;
|
||||
}
|
||||
|
||||
int ReadHTTP (std::basic_istream<char>& stream, std::map<std::string, std::string>& mapHeadersRet,
|
||||
std::string& strMessageRet)
|
||||
{
|
||||
mapHeadersRet.clear ();
|
||||
strMessageRet = "";
|
||||
|
||||
// Read status
|
||||
int nStatus = ReadHTTPStatus (stream);
|
||||
|
||||
// Read header
|
||||
int nLen = ReadHTTPHeader (stream, mapHeadersRet);
|
||||
|
||||
if (nLen < 0 || nLen > gMaxHTTPHeaderSize)
|
||||
return 500;
|
||||
|
||||
// Read message
|
||||
if (nLen > 0)
|
||||
{
|
||||
std::vector<char> vch (nLen);
|
||||
stream.read (&vch[0], nLen);
|
||||
strMessageRet = std::string (vch.begin (), vch.end ());
|
||||
}
|
||||
|
||||
return nStatus;
|
||||
}
|
||||
|
||||
std::string DecodeBase64 (std::string s)
|
||||
{
|
||||
// FIXME: This performs badly
|
||||
BIO* b64, *bmem;
|
||||
|
||||
char* buffer = static_cast<char*> (calloc (s.size (), sizeof (char)));
|
||||
|
||||
b64 = BIO_new (BIO_f_base64 ());
|
||||
BIO_set_flags (b64, BIO_FLAGS_BASE64_NO_NL);
|
||||
bmem = BIO_new_mem_buf (const_cast<char*> (s.data ()), s.size ());
|
||||
bmem = BIO_push (b64, bmem);
|
||||
BIO_read (bmem, buffer, s.size ());
|
||||
BIO_free_all (bmem);
|
||||
|
||||
std::string result (buffer);
|
||||
free (buffer);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool HTTPAuthorized (const std::map<std::string, std::string>& mapHeaders)
|
||||
{
|
||||
std::map<std::string, std::string>::const_iterator it = mapHeaders.find ("authorization");
|
||||
|
||||
if ((it == mapHeaders.end ()) || (it->second.substr (0, 6) != "Basic "))
|
||||
return theConfig.RPC_USER.empty () && theConfig.RPC_PASSWORD.empty ();
|
||||
|
||||
std::string strUserPass64 = it->second.substr (6);
|
||||
boost::trim (strUserPass64);
|
||||
std::string strUserPass = DecodeBase64 (strUserPass64);
|
||||
std::string::size_type nColon = strUserPass.find (":");
|
||||
|
||||
if (nColon == std::string::npos)
|
||||
return false;
|
||||
|
||||
std::string strUser = strUserPass.substr (0, nColon);
|
||||
std::string strPassword = strUserPass.substr (nColon + 1);
|
||||
return (strUser == theConfig.RPC_USER) && (strPassword == theConfig.RPC_PASSWORD);
|
||||
}
|
||||
|
||||
//
|
||||
// JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility,
|
||||
// but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were
|
||||
// unspecified (HTTP errors and contents of 'error').
|
||||
//
|
||||
// 1.0 spec: http://json-rpc.org/wiki/specification
|
||||
// 1.2 spec: http://groups.google.com/group/json-rpc/web/json-rpc-over-http
|
||||
//
|
||||
|
||||
std::string JSONRPCRequest (const std::string& strMethod, const Json::Value& params, const Json::Value& id)
|
||||
{
|
||||
Json::Value request;
|
||||
request["method"] = strMethod;
|
||||
request["params"] = params;
|
||||
request["id"] = id;
|
||||
Json::FastWriter writer;
|
||||
return writer.write (request) + "\n";
|
||||
}
|
||||
|
||||
std::string JSONRPCReply (const Json::Value& result, const Json::Value& error, const Json::Value& id)
|
||||
{
|
||||
Json::Value reply (Json::objectValue);
|
||||
reply["result"] = result;
|
||||
//reply["error"]=error;
|
||||
//reply["id"]=id;
|
||||
Json::FastWriter writer;
|
||||
return writer.write (reply) + "\n";
|
||||
}
|
||||
|
||||
void ErrorReply (std::ostream& stream, const Json::Value& objError, const Json::Value& id)
|
||||
{
|
||||
// Send error reply from json-rpc error object
|
||||
int nStatus = 500;
|
||||
int code = objError["code"].asInt ();
|
||||
|
||||
if (code == -32600) nStatus = 400;
|
||||
else if (code == -32601) nStatus = 404;
|
||||
|
||||
std::string strReply = JSONRPCReply (Json::Value (), objError, id);
|
||||
stream << HTTPReply (nStatus, strReply) << std::flush;
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
1052
modules/ripple_app/shamap/ripple_SHAMap.cpp
Normal file
1052
modules/ripple_app/shamap/ripple_SHAMap.cpp
Normal file
File diff suppressed because it is too large
Load Diff
237
modules/ripple_app/shamap/ripple_SHAMap.h
Normal file
237
modules/ripple_app/shamap/ripple_SHAMap.h
Normal file
@@ -0,0 +1,237 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_SHAMAP_H
|
||||
#define RIPPLE_SHAMAP_H
|
||||
|
||||
enum SHAMapState
|
||||
{
|
||||
smsModifying = 0, // Objects can be added and removed (like an open ledger)
|
||||
smsImmutable = 1, // Map cannot be changed (like a closed ledger)
|
||||
smsSynching = 2, // Map's hash is locked in, valid nodes can be added (like a peer's closing ledger)
|
||||
smsFloating = 3, // Map is free to change hash (like a synching open ledger)
|
||||
smsInvalid = 4, // Map is known not to be valid (usually synching a corrupt ledger)
|
||||
};
|
||||
|
||||
class SHAMap
|
||||
: public CountedObject <SHAMap>
|
||||
{
|
||||
public:
|
||||
static char const* getCountedObjectName () { return "SHAMap"; }
|
||||
|
||||
typedef boost::shared_ptr<SHAMap> pointer;
|
||||
typedef const boost::shared_ptr<SHAMap>& ref;
|
||||
|
||||
typedef std::pair<SHAMapItem::pointer, SHAMapItem::pointer> DeltaItem;
|
||||
typedef std::map<uint256, DeltaItem> Delta;
|
||||
typedef boost::unordered_map<SHAMapNode, SHAMapTreeNode::pointer> DirtyMap;
|
||||
|
||||
public:
|
||||
// build new map
|
||||
explicit SHAMap (SHAMapType t, uint32 seq = 1);
|
||||
SHAMap (SHAMapType t, uint256 const & hash);
|
||||
|
||||
~SHAMap ()
|
||||
{
|
||||
mState = smsInvalid;
|
||||
}
|
||||
|
||||
// Returns a new map that's a snapshot of this one. Force CoW
|
||||
SHAMap::pointer snapShot (bool isMutable);
|
||||
|
||||
// Remove nodes from memory
|
||||
void dropCache ();
|
||||
|
||||
void setLedgerSeq (uint32 lseq)
|
||||
{
|
||||
mLedgerSeq = lseq;
|
||||
}
|
||||
|
||||
// hold the map stable across operations
|
||||
ScopedLock Lock () const
|
||||
{
|
||||
return ScopedLock (mLock);
|
||||
}
|
||||
|
||||
bool hasNode (const SHAMapNode & id);
|
||||
bool fetchRoot (uint256 const & hash, SHAMapSyncFilter * filter);
|
||||
|
||||
// normal hash access functions
|
||||
bool hasItem (uint256 const & id);
|
||||
bool delItem (uint256 const & id);
|
||||
bool addItem (const SHAMapItem & i, bool isTransaction, bool hasMeta);
|
||||
bool updateItem (const SHAMapItem & i, bool isTransaction, bool hasMeta);
|
||||
SHAMapItem getItem (uint256 const & id);
|
||||
uint256 getHash () const
|
||||
{
|
||||
return root->getNodeHash ();
|
||||
}
|
||||
uint256 getHash ()
|
||||
{
|
||||
return root->getNodeHash ();
|
||||
}
|
||||
|
||||
// save a copy if you have a temporary anyway
|
||||
bool updateGiveItem (SHAMapItem::ref, bool isTransaction, bool hasMeta);
|
||||
bool addGiveItem (SHAMapItem::ref, bool isTransaction, bool hasMeta);
|
||||
|
||||
// save a copy if you only need a temporary
|
||||
SHAMapItem::pointer peekItem (uint256 const & id);
|
||||
SHAMapItem::pointer peekItem (uint256 const & id, uint256 & hash);
|
||||
SHAMapItem::pointer peekItem (uint256 const & id, SHAMapTreeNode::TNType & type);
|
||||
|
||||
// traverse functions
|
||||
SHAMapItem::pointer peekFirstItem ();
|
||||
SHAMapItem::pointer peekFirstItem (SHAMapTreeNode::TNType & type);
|
||||
SHAMapItem::pointer peekLastItem ();
|
||||
SHAMapItem::pointer peekNextItem (uint256 const& );
|
||||
SHAMapItem::pointer peekNextItem (uint256 const& , SHAMapTreeNode::TNType & type);
|
||||
SHAMapItem::pointer peekPrevItem (uint256 const& );
|
||||
|
||||
// comparison/sync functions
|
||||
void getMissingNodes (std::vector<SHAMapNode>& nodeIDs, std::vector<uint256>& hashes, int max,
|
||||
SHAMapSyncFilter * filter);
|
||||
bool getNodeFat (const SHAMapNode & node, std::vector<SHAMapNode>& nodeIDs,
|
||||
std::list<Blob >& rawNode, bool fatRoot, bool fatLeaves);
|
||||
bool getRootNode (Serializer & s, SHANodeFormat format);
|
||||
std::vector<uint256> getNeededHashes (int max, SHAMapSyncFilter * filter);
|
||||
SHAMapAddNode addRootNode (uint256 const & hash, Blob const & rootNode, SHANodeFormat format,
|
||||
SHAMapSyncFilter * filter);
|
||||
SHAMapAddNode addRootNode (Blob const & rootNode, SHANodeFormat format,
|
||||
SHAMapSyncFilter * filter);
|
||||
SHAMapAddNode addKnownNode (const SHAMapNode & nodeID, Blob const & rawNode,
|
||||
SHAMapSyncFilter * filter);
|
||||
|
||||
// status functions
|
||||
void setImmutable ()
|
||||
{
|
||||
assert (mState != smsInvalid);
|
||||
mState = smsImmutable;
|
||||
}
|
||||
void clearImmutable ()
|
||||
{
|
||||
mState = smsModifying;
|
||||
}
|
||||
bool isSynching () const
|
||||
{
|
||||
return (mState == smsFloating) || (mState == smsSynching);
|
||||
}
|
||||
void setSynching ()
|
||||
{
|
||||
mState = smsSynching;
|
||||
}
|
||||
void setFloating ()
|
||||
{
|
||||
mState = smsFloating;
|
||||
}
|
||||
void clearSynching ()
|
||||
{
|
||||
mState = smsModifying;
|
||||
}
|
||||
bool isValid ()
|
||||
{
|
||||
return mState != smsInvalid;
|
||||
}
|
||||
|
||||
// caution: otherMap must be accessed only by this function
|
||||
// return value: true=successfully completed, false=too different
|
||||
bool compare (SHAMap::ref otherMap, Delta & differences, int maxCount);
|
||||
|
||||
int armDirty ();
|
||||
static int flushDirty (DirtyMap & dirtyMap, int maxNodes, NodeObjectType t, uint32 seq);
|
||||
boost::shared_ptr<DirtyMap> disarmDirty ();
|
||||
|
||||
void setSeq (uint32 seq)
|
||||
{
|
||||
mSeq = seq;
|
||||
}
|
||||
uint32 getSeq ()
|
||||
{
|
||||
return mSeq;
|
||||
}
|
||||
|
||||
// overloads for backed maps
|
||||
boost::shared_ptr<SHAMapTreeNode> fetchNodeExternal (const SHAMapNode & id, uint256 const & hash); // throws
|
||||
boost::shared_ptr<SHAMapTreeNode> fetchNodeExternalNT (const SHAMapNode & id, uint256 const & hash); // no throw
|
||||
|
||||
bool operator== (const SHAMap & s)
|
||||
{
|
||||
return getHash () == s.getHash ();
|
||||
}
|
||||
|
||||
// trusted path operations - prove a particular node is in a particular ledger
|
||||
std::list<Blob > getTrustedPath (uint256 const & index);
|
||||
static Blob checkTrustedPath (uint256 const & ledgerHash, uint256 const & leafIndex,
|
||||
const std::list<Blob >& path);
|
||||
|
||||
void walkMap (std::vector<SHAMapMissingNode>& missingNodes, int maxMissing);
|
||||
|
||||
bool getPath (uint256 const & index, std::vector< Blob >& nodes, SHANodeFormat format);
|
||||
|
||||
bool deepCompare (SHAMap & other);
|
||||
|
||||
virtual void dump (bool withHashes = false);
|
||||
|
||||
typedef std::pair <uint256, Blob> fetchPackEntry_t;
|
||||
|
||||
std::list<fetchPackEntry_t> getFetchPack (SHAMap * have, bool includeLeaves, int max);
|
||||
void getFetchPack (SHAMap * have, bool includeLeaves, int max, FUNCTION_TYPE<void (const uint256&, const Blob&)>);
|
||||
|
||||
static int getFullBelowSize ()
|
||||
{
|
||||
return fullBelowCache.getSize ();
|
||||
}
|
||||
static void sweep ()
|
||||
{
|
||||
fullBelowCache.sweep ();
|
||||
}
|
||||
|
||||
private:
|
||||
static KeyCache <uint256, UptimeTimerAdapter> fullBelowCache;
|
||||
|
||||
void dirtyUp (std::stack<SHAMapTreeNode::pointer>& stack, uint256 const & target, uint256 prevHash);
|
||||
std::stack<SHAMapTreeNode::pointer> getStack (uint256 const & id, bool include_nonmatching_leaf);
|
||||
SHAMapTreeNode::pointer walkTo (uint256 const & id, bool modify);
|
||||
SHAMapTreeNode* walkToPointer (uint256 const & id);
|
||||
SHAMapTreeNode::pointer checkCacheNode (const SHAMapNode&);
|
||||
void returnNode (SHAMapTreeNode::pointer&, bool modify);
|
||||
void trackNewNode (SHAMapTreeNode::pointer&);
|
||||
|
||||
SHAMapTreeNode::pointer getNode (const SHAMapNode & id);
|
||||
SHAMapTreeNode::pointer getNode (const SHAMapNode & id, uint256 const & hash, bool modify);
|
||||
SHAMapTreeNode* getNodePointer (const SHAMapNode & id, uint256 const & hash);
|
||||
SHAMapTreeNode* getNodePointerNT (const SHAMapNode & id, uint256 const & hash);
|
||||
SHAMapTreeNode* getNodePointer (const SHAMapNode & id, uint256 const & hash, SHAMapSyncFilter * filter);
|
||||
SHAMapTreeNode* getNodePointerNT (const SHAMapNode & id, uint256 const & hash, SHAMapSyncFilter * filter);
|
||||
SHAMapTreeNode* firstBelow (SHAMapTreeNode*);
|
||||
SHAMapTreeNode* lastBelow (SHAMapTreeNode*);
|
||||
|
||||
SHAMapItem::pointer onlyBelow (SHAMapTreeNode*);
|
||||
void eraseChildren (SHAMapTreeNode::pointer);
|
||||
void dropBelow (SHAMapTreeNode*);
|
||||
bool hasInnerNode (const SHAMapNode & nodeID, uint256 const & hash);
|
||||
bool hasLeafNode (uint256 const & tag, uint256 const & hash);
|
||||
|
||||
bool walkBranch (SHAMapTreeNode * node, SHAMapItem::ref otherMapItem, bool isFirstMap,
|
||||
Delta & differences, int & maxCount);
|
||||
|
||||
private:
|
||||
uint32 mSeq;
|
||||
uint32 mLedgerSeq; // sequence number of ledger this is part of
|
||||
mutable boost::recursive_mutex mLock;
|
||||
boost::unordered_map<SHAMapNode, SHAMapTreeNode::pointer> mTNByID;
|
||||
|
||||
boost::shared_ptr<DirtyMap> mDirtyNodes;
|
||||
|
||||
SHAMapTreeNode::pointer root;
|
||||
|
||||
SHAMapState mState;
|
||||
|
||||
SHAMapType mType;
|
||||
};
|
||||
|
||||
#endif
|
||||
// vim:ts=4
|
||||
88
modules/ripple_app/shamap/ripple_SHAMapAddNode.h
Normal file
88
modules/ripple_app/shamap/ripple_SHAMapAddNode.h
Normal file
@@ -0,0 +1,88 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_SHAMAPADDNODE_H
|
||||
#define RIPPLE_SHAMAPADDNODE_H
|
||||
|
||||
// results of adding nodes
|
||||
class SHAMapAddNode
|
||||
{
|
||||
public:
|
||||
SHAMapAddNode ()
|
||||
: mInvalid (false)
|
||||
, mUseful (false)
|
||||
{
|
||||
}
|
||||
|
||||
void setInvalid ()
|
||||
{
|
||||
mInvalid = true;
|
||||
}
|
||||
void setUseful ()
|
||||
{
|
||||
mUseful = true;
|
||||
}
|
||||
void reset ()
|
||||
{
|
||||
mInvalid = false;
|
||||
mUseful = false;
|
||||
}
|
||||
|
||||
bool isInvalid () const
|
||||
{
|
||||
return mInvalid;
|
||||
}
|
||||
bool isUseful () const
|
||||
{
|
||||
return mUseful;
|
||||
}
|
||||
|
||||
bool combine (SHAMapAddNode const& n)
|
||||
{
|
||||
// VFALCO NOTE What is the meaning of these lines?
|
||||
|
||||
if (n.mInvalid)
|
||||
{
|
||||
mInvalid = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (n.mUseful)
|
||||
mUseful = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
operator bool () const
|
||||
{
|
||||
return !mInvalid;
|
||||
}
|
||||
|
||||
static SHAMapAddNode okay ()
|
||||
{
|
||||
return SHAMapAddNode (false, false);
|
||||
}
|
||||
static SHAMapAddNode useful ()
|
||||
{
|
||||
return SHAMapAddNode (false, true);
|
||||
}
|
||||
static SHAMapAddNode invalid ()
|
||||
{
|
||||
return SHAMapAddNode (true, false);
|
||||
}
|
||||
|
||||
private:
|
||||
SHAMapAddNode (bool i, bool u)
|
||||
: mInvalid (i)
|
||||
, mUseful (u)
|
||||
{
|
||||
}
|
||||
|
||||
bool mInvalid;
|
||||
bool mUseful;
|
||||
};
|
||||
|
||||
#endif
|
||||
257
modules/ripple_app/shamap/ripple_SHAMapDelta.cpp
Normal file
257
modules/ripple_app/shamap/ripple_SHAMapDelta.cpp
Normal file
@@ -0,0 +1,257 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
// This code is used to compare another node's transaction tree
|
||||
// to our own. It returns a map containing all items that are different
|
||||
// between two SHA maps. It is optimized not to descend down tree
|
||||
// branches with the same branch hash. A limit can be passed so
|
||||
// that we will abort early if a node sends a map to us that
|
||||
// makes no sense at all. (And our sync algorithm will avoid
|
||||
// synchronizing matching brances too.)
|
||||
|
||||
class SHAMapDeltaNode
|
||||
{
|
||||
public:
|
||||
SHAMapNode mNodeID;
|
||||
uint256 mOurHash, mOtherHash;
|
||||
|
||||
SHAMapDeltaNode (const SHAMapNode& id, uint256 const& ourHash, uint256 const& otherHash) :
|
||||
mNodeID (id), mOurHash (ourHash), mOtherHash (otherHash)
|
||||
{
|
||||
;
|
||||
}
|
||||
};
|
||||
|
||||
bool SHAMap::walkBranch (SHAMapTreeNode* node, SHAMapItem::ref otherMapItem, bool isFirstMap,
|
||||
Delta& differences, int& maxCount)
|
||||
{
|
||||
// Walk a branch of a SHAMap that's matched by an empty branch or single item in the other map
|
||||
std::stack<SHAMapTreeNode*> nodeStack;
|
||||
nodeStack.push (node);
|
||||
|
||||
bool emptyBranch = !otherMapItem;
|
||||
|
||||
while (!nodeStack.empty ())
|
||||
{
|
||||
SHAMapTreeNode* node = nodeStack.top ();
|
||||
nodeStack.pop ();
|
||||
|
||||
if (node->isInner ())
|
||||
{
|
||||
// This is an inner node, add all non-empty branches
|
||||
for (int i = 0; i < 16; ++i)
|
||||
if (!node->isEmptyBranch (i))
|
||||
nodeStack.push (getNodePointer (node->getChildNodeID (i), node->getChildHash (i)));
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is a leaf node, process its item
|
||||
SHAMapItem::pointer item = node->getItem ();
|
||||
|
||||
if (!emptyBranch && (otherMapItem->getTag () < item->getTag ()))
|
||||
{
|
||||
// this item comes after the item from the other map, so add the other item
|
||||
if (isFirstMap) // this is first map, so other item is from second
|
||||
differences.insert (std::make_pair (otherMapItem->getTag (),
|
||||
std::make_pair (SHAMapItem::pointer (), otherMapItem)));
|
||||
else
|
||||
differences.insert (std::make_pair (otherMapItem->getTag (),
|
||||
std::make_pair (otherMapItem, SHAMapItem::pointer ())));
|
||||
|
||||
if (--maxCount <= 0)
|
||||
return false;
|
||||
|
||||
emptyBranch = true;
|
||||
}
|
||||
|
||||
if (emptyBranch || (item->getTag () != otherMapItem->getTag ()))
|
||||
{
|
||||
// unmatched
|
||||
if (isFirstMap)
|
||||
differences.insert (std::make_pair (item->getTag (), std::make_pair (item, SHAMapItem::pointer ())));
|
||||
else
|
||||
differences.insert (std::make_pair (item->getTag (), std::make_pair (SHAMapItem::pointer (), item)));
|
||||
|
||||
if (--maxCount <= 0)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (item->peekData () != otherMapItem->peekData ())
|
||||
{
|
||||
// non-matching items
|
||||
if (isFirstMap) differences.insert (std::make_pair (otherMapItem->getTag (),
|
||||
std::make_pair (item, otherMapItem)));
|
||||
else differences.insert (std::make_pair (otherMapItem->getTag (),
|
||||
std::make_pair (otherMapItem, item)));
|
||||
|
||||
if (--maxCount <= 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
emptyBranch = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!emptyBranch)
|
||||
{
|
||||
// otherMapItem was unmatched, must add
|
||||
if (isFirstMap) // this is first map, so other item is from second
|
||||
differences.insert (std::make_pair (otherMapItem->getTag (),
|
||||
std::make_pair (SHAMapItem::pointer (), otherMapItem)));
|
||||
else
|
||||
differences.insert (std::make_pair (otherMapItem->getTag (),
|
||||
std::make_pair (otherMapItem, SHAMapItem::pointer ())));
|
||||
|
||||
if (--maxCount <= 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SHAMap::compare (SHAMap::ref otherMap, Delta& differences, int maxCount)
|
||||
{
|
||||
// compare two hash trees, add up to maxCount differences to the difference table
|
||||
// return value: true=complete table of differences given, false=too many differences
|
||||
// throws on corrupt tables or missing nodes
|
||||
// CAUTION: otherMap is not locked and must be immutable
|
||||
|
||||
assert (isValid () && otherMap && otherMap->isValid ());
|
||||
|
||||
std::stack<SHAMapDeltaNode> nodeStack; // track nodes we've pushed
|
||||
|
||||
boost::recursive_mutex::scoped_lock sl (mLock);
|
||||
|
||||
if (getHash () == otherMap->getHash ())
|
||||
return true;
|
||||
|
||||
nodeStack.push (SHAMapDeltaNode (SHAMapNode (), getHash (), otherMap->getHash ()));
|
||||
|
||||
while (!nodeStack.empty ())
|
||||
{
|
||||
SHAMapDeltaNode dNode (nodeStack.top ());
|
||||
nodeStack.pop ();
|
||||
|
||||
SHAMapTreeNode* ourNode = getNodePointer (dNode.mNodeID, dNode.mOurHash);
|
||||
SHAMapTreeNode* otherNode = otherMap->getNodePointer (dNode.mNodeID, dNode.mOtherHash);
|
||||
|
||||
if (!ourNode || !otherNode)
|
||||
{
|
||||
assert (false);
|
||||
throw SHAMapMissingNode (mType, dNode.mNodeID, uint256 ());
|
||||
}
|
||||
|
||||
if (ourNode->isLeaf () && otherNode->isLeaf ())
|
||||
{
|
||||
// two leaves
|
||||
if (ourNode->getTag () == otherNode->getTag ())
|
||||
{
|
||||
if (ourNode->peekData () != otherNode->peekData ())
|
||||
{
|
||||
differences.insert (std::make_pair (ourNode->getTag (),
|
||||
std::make_pair (ourNode->getItem (), otherNode->getItem ())));
|
||||
|
||||
if (--maxCount <= 0)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
differences.insert (std::make_pair (ourNode->getTag (),
|
||||
std::make_pair (ourNode->getItem (), SHAMapItem::pointer ())));
|
||||
|
||||
if (--maxCount <= 0)
|
||||
return false;
|
||||
|
||||
differences.insert (std::make_pair (otherNode->getTag (),
|
||||
std::make_pair (SHAMapItem::pointer (), otherNode->getItem ())));
|
||||
|
||||
if (--maxCount <= 0)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (ourNode->isInner () && otherNode->isLeaf ())
|
||||
{
|
||||
if (!walkBranch (ourNode, otherNode->getItem (), true, differences, maxCount))
|
||||
return false;
|
||||
}
|
||||
else if (ourNode->isLeaf () && otherNode->isInner ())
|
||||
{
|
||||
if (!otherMap->walkBranch (otherNode, ourNode->getItem (), false, differences, maxCount))
|
||||
return false;
|
||||
}
|
||||
else if (ourNode->isInner () && otherNode->isInner ())
|
||||
{
|
||||
for (int i = 0; i < 16; ++i)
|
||||
if (ourNode->getChildHash (i) != otherNode->getChildHash (i))
|
||||
{
|
||||
if (otherNode->isEmptyBranch (i))
|
||||
{
|
||||
// We have a branch, the other tree does not
|
||||
SHAMapTreeNode* iNode = getNodePointer (ourNode->getChildNodeID (i), ourNode->getChildHash (i));
|
||||
|
||||
if (!walkBranch (iNode, SHAMapItem::pointer (), true, differences, maxCount))
|
||||
return false;
|
||||
}
|
||||
else if (ourNode->isEmptyBranch (i))
|
||||
{
|
||||
// The other tree has a branch, we do not
|
||||
SHAMapTreeNode* iNode =
|
||||
otherMap->getNodePointer (otherNode->getChildNodeID (i), otherNode->getChildHash (i));
|
||||
|
||||
if (!otherMap->walkBranch (iNode, SHAMapItem::pointer (), false, differences, maxCount))
|
||||
return false;
|
||||
}
|
||||
else // The two trees have different non-empty branches
|
||||
nodeStack.push (SHAMapDeltaNode (ourNode->getChildNodeID (i),
|
||||
ourNode->getChildHash (i), otherNode->getChildHash (i)));
|
||||
}
|
||||
}
|
||||
else
|
||||
assert (false);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SHAMap::walkMap (std::vector<SHAMapMissingNode>& missingNodes, int maxMissing)
|
||||
{
|
||||
std::stack<SHAMapTreeNode::pointer> nodeStack;
|
||||
|
||||
boost::recursive_mutex::scoped_lock sl (mLock);
|
||||
|
||||
if (!root->isInner ()) // root is only node, and we have it
|
||||
return;
|
||||
|
||||
nodeStack.push (root);
|
||||
|
||||
while (!nodeStack.empty ())
|
||||
{
|
||||
SHAMapTreeNode::pointer node = nodeStack.top ();
|
||||
nodeStack.pop ();
|
||||
|
||||
for (int i = 0; i < 16; ++i)
|
||||
if (!node->isEmptyBranch (i))
|
||||
{
|
||||
try
|
||||
{
|
||||
SHAMapTreeNode::pointer d = getNode (node->getChildNodeID (i), node->getChildHash (i), false);
|
||||
|
||||
if (d->isInner ())
|
||||
nodeStack.push (d);
|
||||
}
|
||||
catch (SHAMapMissingNode& n)
|
||||
{
|
||||
missingNodes.push_back (n);
|
||||
|
||||
if (--maxMissing <= 0)
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
19
modules/ripple_app/shamap/ripple_SHAMapItem.cpp
Normal file
19
modules/ripple_app/shamap/ripple_SHAMapItem.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
class SHAMap;
|
||||
|
||||
SHAMapItem::SHAMapItem (uint256 const& tag, Blob const& data)
|
||||
: mTag (tag)
|
||||
, mData (data)
|
||||
{
|
||||
}
|
||||
|
||||
SHAMapItem::SHAMapItem (uint256 const& tag, const Serializer& data)
|
||||
: mTag (tag)
|
||||
, mData (data.peekData ())
|
||||
{
|
||||
}
|
||||
116
modules/ripple_app/shamap/ripple_SHAMapItem.h
Normal file
116
modules/ripple_app/shamap/ripple_SHAMapItem.h
Normal file
@@ -0,0 +1,116 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_SHAMAPITEM_H
|
||||
#define RIPPLE_SHAMAPITEM_H
|
||||
|
||||
// an item stored in a SHAMap
|
||||
class SHAMapItem
|
||||
: public CountedObject <SHAMapItem>
|
||||
{
|
||||
public:
|
||||
static char const* getCountedObjectName () { return "SHAMapItem"; }
|
||||
|
||||
typedef boost::shared_ptr<SHAMapItem> pointer;
|
||||
typedef const boost::shared_ptr<SHAMapItem>& ref;
|
||||
|
||||
public:
|
||||
explicit SHAMapItem (uint256 const & tag) : mTag (tag)
|
||||
{
|
||||
;
|
||||
}
|
||||
explicit SHAMapItem (Blob const & data); // tag by hash
|
||||
SHAMapItem (uint256 const & tag, Blob const & data);
|
||||
SHAMapItem (uint256 const & tag, const Serializer & s);
|
||||
|
||||
uint256 const& getTag () const
|
||||
{
|
||||
return mTag;
|
||||
}
|
||||
Blob getData () const
|
||||
{
|
||||
return mData.getData ();
|
||||
}
|
||||
Blob const& peekData () const
|
||||
{
|
||||
return mData.peekData ();
|
||||
}
|
||||
Serializer& peekSerializer ()
|
||||
{
|
||||
return mData;
|
||||
}
|
||||
void addRaw (Blob & s) const
|
||||
{
|
||||
s.insert (s.end (), mData.begin (), mData.end ());
|
||||
}
|
||||
|
||||
void updateData (Blob const & data)
|
||||
{
|
||||
mData = data;
|
||||
}
|
||||
|
||||
bool operator== (const SHAMapItem & i) const
|
||||
{
|
||||
return mTag == i.mTag;
|
||||
}
|
||||
bool operator!= (const SHAMapItem & i) const
|
||||
{
|
||||
return mTag != i.mTag;
|
||||
}
|
||||
bool operator== (uint256 const & i) const
|
||||
{
|
||||
return mTag == i;
|
||||
}
|
||||
bool operator!= (uint256 const & i) const
|
||||
{
|
||||
return mTag != i;
|
||||
}
|
||||
|
||||
#if 0
|
||||
// This code is comment out because it is unused. It could work.
|
||||
bool operator< (const SHAMapItem & i) const
|
||||
{
|
||||
return mTag < i.mTag;
|
||||
}
|
||||
bool operator> (const SHAMapItem & i) const
|
||||
{
|
||||
return mTag > i.mTag;
|
||||
}
|
||||
bool operator<= (const SHAMapItem & i) const
|
||||
{
|
||||
return mTag <= i.mTag;
|
||||
}
|
||||
bool operator>= (const SHAMapItem & i) const
|
||||
{
|
||||
return mTag >= i.mTag;
|
||||
}
|
||||
|
||||
bool operator< (uint256 const & i) const
|
||||
{
|
||||
return mTag < i;
|
||||
}
|
||||
bool operator> (uint256 const & i) const
|
||||
{
|
||||
return mTag > i;
|
||||
}
|
||||
bool operator<= (uint256 const & i) const
|
||||
{
|
||||
return mTag <= i;
|
||||
}
|
||||
bool operator>= (uint256 const & i) const
|
||||
{
|
||||
return mTag >= i;
|
||||
}
|
||||
#endif
|
||||
|
||||
virtual void dump ();
|
||||
|
||||
private:
|
||||
uint256 mTag;
|
||||
Serializer mData;
|
||||
};
|
||||
|
||||
#endif
|
||||
26
modules/ripple_app/shamap/ripple_SHAMapMissingNode.cpp
Normal file
26
modules/ripple_app/shamap/ripple_SHAMapMissingNode.cpp
Normal file
@@ -0,0 +1,26 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
std::ostream& operator<< (std::ostream& out, const SHAMapMissingNode& mn)
|
||||
{
|
||||
switch (mn.getMapType ())
|
||||
{
|
||||
case smtTRANSACTION:
|
||||
out << "Missing/TXN(" << mn.getNodeID () << "/" << mn.getNodeHash () << ")";
|
||||
break;
|
||||
|
||||
case smtSTATE:
|
||||
out << "Missing/STA(" << mn.getNodeID () << "/" << mn.getNodeHash () << ")";
|
||||
break;
|
||||
|
||||
case smtFREE:
|
||||
default:
|
||||
out << "Missing/" << mn.getNodeID ();
|
||||
break;
|
||||
};
|
||||
|
||||
return out;
|
||||
}
|
||||
85
modules/ripple_app/shamap/ripple_SHAMapMissingNode.h
Normal file
85
modules/ripple_app/shamap/ripple_SHAMapMissingNode.h
Normal file
@@ -0,0 +1,85 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (c) 2011-2013, OpenCoin, Inc.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_SHAMAPMISSINGNODE_H
|
||||
#define RIPPLE_SHAMAPMISSINGNODE_H
|
||||
|
||||
enum SHAMapType
|
||||
{
|
||||
smtTRANSACTION = 1, // A tree of transactions
|
||||
smtSTATE = 2, // A tree of state nodes
|
||||
smtFREE = 3, // A tree not part of a ledger
|
||||
};
|
||||
|
||||
class SHAMapMissingNode : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
SHAMapMissingNode (SHAMapType t,
|
||||
SHAMapNode const& nodeID,
|
||||
uint256 const& nodeHash)
|
||||
: std::runtime_error ("SHAMapMissingNode")
|
||||
, mType (t)
|
||||
, mNodeID (nodeID)
|
||||
, mNodeHash (nodeHash)
|
||||
{
|
||||
}
|
||||
|
||||
SHAMapMissingNode (SHAMapType t,
|
||||
SHAMapNode const& nodeID,
|
||||
uint256 const& nodeHash,
|
||||
uint256 const& targetIndex)
|
||||
: std::runtime_error (nodeID.getString ())
|
||||
, mType (t)
|
||||
, mNodeID (nodeID)
|
||||
, mNodeHash (nodeHash)
|
||||
, mTargetIndex (targetIndex)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~SHAMapMissingNode () throw ()
|
||||
{
|
||||
}
|
||||
|
||||
void setTargetNode (uint256 const& tn)
|
||||
{
|
||||
mTargetIndex = tn;
|
||||
}
|
||||
|
||||
SHAMapType getMapType () const
|
||||
{
|
||||
return mType;
|
||||
}
|
||||
|
||||
SHAMapNode const& getNodeID () const
|
||||
{
|
||||
return mNodeID;
|
||||
}
|
||||
|
||||
uint256 const& getNodeHash () const
|
||||
{
|
||||
return mNodeHash;
|
||||
}
|
||||
|
||||
uint256 const& getTargetIndex () const
|
||||
{
|
||||
return mTargetIndex;
|
||||
}
|
||||
|
||||
bool hasTargetIndex () const
|
||||
{
|
||||
return !mTargetIndex.isZero ();
|
||||
}
|
||||
|
||||
private:
|
||||
SHAMapType mType;
|
||||
SHAMapNode mNodeID;
|
||||
uint256 mNodeHash;
|
||||
uint256 mTargetIndex;
|
||||
};
|
||||
|
||||
extern std::ostream& operator<< (std::ostream&, SHAMapMissingNode const&);
|
||||
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user