Move ./modules to ./src

This commit is contained in:
Vinnie Falco
2013-09-11 11:17:22 -07:00
parent 6d828ae476
commit 45eccf2ccf
386 changed files with 1673 additions and 1674 deletions

View File

@@ -1030,9 +1030,6 @@
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\modules\beast_core\beast_core.cpp">
<Filter>beast_core</Filter>
</ClCompile>
<ClCompile Include="..\..\modules\beast_core\containers\beast_AbstractFifo.cpp">
<Filter>beast_core\containers</Filter>
</ClCompile>
@@ -1312,9 +1309,6 @@
<ClCompile Include="..\..\modules\beast_core\diagnostic\beast_UnitTest.cpp">
<Filter>beast_core\diagnostic</Filter>
</ClCompile>
<ClCompile Include="..\..\modules\beast_crypto\beast_crypto.cpp">
<Filter>beast_crypto</Filter>
</ClCompile>
<ClCompile Include="..\..\modules\beast_crypto\math\beast_UnsignedInteger.cpp">
<Filter>beast_crypto\math</Filter>
</ClCompile>
@@ -1393,9 +1387,6 @@
<ClCompile Include="..\..\modules\beast_core\diagnostic\beast_SemanticVersion.cpp">
<Filter>beast_core\diagnostic</Filter>
</ClCompile>
<ClCompile Include="..\..\modules\beast_asio\beast_asio.cpp">
<Filter>beast_asio</Filter>
</ClCompile>
<ClCompile Include="..\..\modules\beast_core\threads\beast_ReadWriteMutex.cpp">
<Filter>beast_core\threads</Filter>
</ClCompile>
@@ -1543,6 +1534,15 @@
<ClCompile Include="..\..\modules\beast_asio\http\UniformResourceLocator.cpp">
<Filter>beast_asio\http</Filter>
</ClCompile>
<ClCompile Include="..\..\modules\beast_crypto\beast_crypto.cpp">
<Filter>beast_crypto</Filter>
</ClCompile>
<ClCompile Include="..\..\modules\beast_core\beast_core.cpp">
<Filter>beast_core</Filter>
</ClCompile>
<ClCompile Include="..\..\modules\beast_asio\beast_asio.cpp">
<Filter>beast_asio</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Text Include="..\..\TODO.txt" />

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

View 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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,161 @@
//------------------------------------------------------------------------------
/*
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;
boost::unordered_set<uint256> mCompares;
// Close time estimates
std::map<uint32, int> mCloseTimes;
// nodes that have bowed out of this consensus process
boost::unordered_set<uint160> mDeadNodes;
};
#endif

View File

@@ -0,0 +1,33 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
Contract::Contract ()
{
}
void Contract::executeCreate ()
{
}
void Contract::executeRemove ()
{
}
void Contract::executeFund ()
{
}
void Contract::executeAccept ()
{
//std::vector<char> code;
//Interpreter interpreter;
//interpreter.interpret(this,code);
}
// vim:ts=4

View File

@@ -0,0 +1,35 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
#ifndef CONTRACT_H
#define CONTRACT_H
/*
Encapsulates the SLE for a Contract
*/
class Contract
{
public:
Contract ();
uint160& getIssuer ();
uint160& getOwner ();
STAmount& getRippleEscrow ();
uint32 getEscrow ();
uint32 getBond ();
Script::Data getData (int index);
void executeCreate ();
void executeRemove ();
void executeFund ();
void executeAccept ();
};
#endif
// vim:ts=4

View File

@@ -0,0 +1,214 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
/*
We also need to charge for each op
*/
namespace Script
{
Interpreter::Interpreter ()
{
mContract = NULL;
mCode = NULL;
mInstructionPointer = 0;
mTotalFee = 0;
mInBlock = false;
mBlockSuccess = true;
mBlockJump = 0;
mFunctionTable.resize (NUM_OF_OPS);
mFunctionTable[INT_OP] = new IntOp ();
mFunctionTable[FLOAT_OP] = new FloatOp ();
mFunctionTable[UINT160_OP] = new Uint160Op ();
mFunctionTable[BOOL_OP] = new Uint160Op ();
mFunctionTable[PATH_OP] = new Uint160Op ();
mFunctionTable[ADD_OP] = new AddOp ();
mFunctionTable[SUB_OP] = new SubOp ();
mFunctionTable[MUL_OP] = new MulOp ();
mFunctionTable[DIV_OP] = new DivOp ();
mFunctionTable[MOD_OP] = new ModOp ();
mFunctionTable[GTR_OP] = new GtrOp ();
mFunctionTable[LESS_OP] = new LessOp ();
mFunctionTable[EQUAL_OP] = new SubOp ();
mFunctionTable[NOT_EQUAL_OP] = new SubOp ();
mFunctionTable[AND_OP] = new SubOp ();
mFunctionTable[OR_OP] = new SubOp ();
mFunctionTable[NOT_OP] = new SubOp ();
mFunctionTable[JUMP_OP] = new SubOp ();
mFunctionTable[JUMPIF_OP] = new SubOp ();
mFunctionTable[STOP_OP] = new SubOp ();
mFunctionTable[CANCEL_OP] = new SubOp ();
mFunctionTable[BLOCK_OP] = new SubOp ();
mFunctionTable[BLOCK_END_OP] = new SubOp ();
mFunctionTable[SEND_XRP_OP] = new SendXRPOp ();
/*
mFunctionTable[SEND_OP]=new SendOp();
mFunctionTable[REMOVE_CONTRACT_OP]=new SubOp();
mFunctionTable[FEE_OP]=new SubOp();
mFunctionTable[CHANGE_CONTRACT_OWNER_OP]=new SubOp();
mFunctionTable[STOP_REMOVE_OP]=new SubOp();
mFunctionTable[SET_DATA_OP]=new SubOp();
mFunctionTable[GET_DATA_OP]=new SubOp();
mFunctionTable[GET_NUM_DATA_OP]=new SubOp();
mFunctionTable[SET_REGISTER_OP]=new SubOp();
mFunctionTable[GET_REGISTER_OP]=new SubOp();
mFunctionTable[GET_ISSUER_ID_OP]=new SubOp();
mFunctionTable[GET_OWNER_ID_OP]=new SubOp();
mFunctionTable[GET_LEDGER_TIME_OP]=new SubOp();
mFunctionTable[GET_LEDGER_NUM_OP]=new SubOp();
mFunctionTable[GET_RAND_FLOAT_OP]=new SubOp();
mFunctionTable[GET_XRP_ESCROWED_OP]=new SubOp();
mFunctionTable[GET_RIPPLE_ESCROWED_OP]=new SubOp();
mFunctionTable[GET_RIPPLE_ESCROWED_CURRENCY_OP]=new SubOp();
mFunctionTable[GET_RIPPLE_ESCROWED_ISSUER]=new GetRippleEscrowedIssuerOp();
mFunctionTable[GET_ACCEPT_DATA_OP]=new AcceptDataOp();
mFunctionTable[GET_ACCEPTOR_ID_OP]=new GetAcceptorIDOp();
mFunctionTable[GET_CONTRACT_ID_OP]=new GetContractIDOp();
*/
}
Data::pointer Interpreter::popStack ()
{
if (mStack.size ())
{
Data::pointer item = mStack[mStack.size () - 1];
mStack.pop_back ();
return (item);
}
else
{
return (Data::pointer (new ErrorData ()));
}
}
void Interpreter::pushStack (Data::pointer data)
{
mStack.push_back (data);
}
// offset is where to jump to if the block fails
bool Interpreter::startBlock (int offset)
{
if (mInBlock) return (false); // can't nest blocks
mBlockSuccess = true;
mInBlock = true;
mBlockJump = offset + mInstructionPointer;
return (true);
}
bool Interpreter::endBlock ()
{
if (!mInBlock) return (false);
mInBlock = false;
mBlockJump = 0;
pushStack (Data::pointer (new BoolData (mBlockSuccess)));
return (true);
}
TER Interpreter::interpret (Contract* contract, const SerializedTransaction& txn, Blob& code)
{
mContract = contract;
mCode = &code;
mTotalFee = 0;
mInstructionPointer = 0;
while (mInstructionPointer < code.size ())
{
unsigned int fun = (*mCode)[mInstructionPointer];
mInstructionPointer++;
if (fun >= mFunctionTable.size ())
{
// TODO: log
return (temMALFORMED); // TODO: is this actually what we want to do?
}
mTotalFee += mFunctionTable[ fun ]->getFee (); // FIXME: You can't use fees this way, there's no consensus
if (mTotalFee > txn.getTransactionFee ().getNValue ())
{
// TODO: log
return (telINSUF_FEE_P);
}
else
{
if (!mFunctionTable[ fun ]->work (this))
{
// TODO: log
return (temMALFORMED); // TODO: is this actually what we want to do?
}
}
}
return (tesSUCCESS);
}
Data::pointer Interpreter::getIntData ()
{
int value = 0; // TODO
mInstructionPointer += 4;
return (Data::pointer (new IntData (value)));
}
Data::pointer Interpreter::getFloatData ()
{
float value = 0; // TODO
mInstructionPointer += 4;
return (Data::pointer (new FloatData (value)));
}
Data::pointer Interpreter::getUint160Data ()
{
uint160 value; // TODO
mInstructionPointer += 20;
return (Data::pointer (new Uint160Data (value)));
}
bool Interpreter::jumpTo (int offset)
{
mInstructionPointer += offset;
if ( (mInstructionPointer < 0) || (mInstructionPointer > mCode->size ()) )
{
mInstructionPointer -= offset;
return (false);
}
return (true);
}
void Interpreter::stop ()
{
mInstructionPointer = mCode->size ();
}
Data::pointer Interpreter::getContractData (int index)
{
return (Data::pointer (new ErrorData ()));
}
bool Interpreter::canSign (const uint160& signer)
{
return (true);
}
} // end namespace

View File

@@ -0,0 +1,86 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
#ifndef INTERPRETER_H
#define INTERPRETER_H
namespace Script
{
class Operation;
// Contracts are non typed have variable data types
class Interpreter
{
std::vector<Operation*> mFunctionTable;
std::vector<Data::pointer> mStack;
Contract* mContract;
Blob* mCode;
unsigned int mInstructionPointer;
int mTotalFee;
bool mInBlock;
int mBlockJump;
bool mBlockSuccess;
public:
enum { INT_OP = 1, FLOAT_OP, UINT160_OP, BOOL_OP, PATH_OP,
ADD_OP, SUB_OP, MUL_OP, DIV_OP, MOD_OP,
GTR_OP, LESS_OP, EQUAL_OP, NOT_EQUAL_OP,
AND_OP, OR_OP, NOT_OP,
JUMP_OP, JUMPIF_OP,
STOP_OP, CANCEL_OP,
BLOCK_OP, BLOCK_END_OP,
SEND_XRP_OP, SEND_OP, REMOVE_CONTRACT_OP, FEE_OP, CHANGE_CONTRACT_OWNER_OP,
STOP_REMOVE_OP,
SET_DATA_OP, GET_DATA_OP, GET_NUM_DATA_OP,
SET_REGISTER_OP, GET_REGISTER_OP,
GET_ISSUER_ID_OP, GET_OWNER_ID_OP, GET_LEDGER_TIME_OP, GET_LEDGER_NUM_OP, GET_RAND_FLOAT_OP,
GET_XRP_ESCROWED_OP, GET_RIPPLE_ESCROWED_OP, GET_RIPPLE_ESCROWED_CURRENCY_OP, GET_RIPPLE_ESCROWED_ISSUER,
GET_ACCEPT_DATA_OP, GET_ACCEPTOR_ID_OP, GET_CONTRACT_ID_OP,
NUM_OF_OPS
};
Interpreter ();
// returns a TransactionEngineResult
TER interpret (Contract* contract, const SerializedTransaction& txn, Blob& code);
void stop ();
bool canSign (const uint160& signer);
int getInstructionPointer ()
{
return (mInstructionPointer);
}
void setInstructionPointer (int n)
{
mInstructionPointer = n;
}
Data::pointer popStack ();
void pushStack (Data::pointer data);
bool jumpTo (int offset);
bool startBlock (int offset);
bool endBlock ();
Data::pointer getIntData ();
Data::pointer getFloatData ();
Data::pointer getUint160Data ();
Data::pointer getAcceptData (int index);
Data::pointer getContractData (int index);
};
} // end namespace
#endif

View File

@@ -0,0 +1,21 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
/*
We also need to charge for each op
*/
namespace Script
{
int Operation::getFee ()
{
return (getConfig ().FEE_CONTRACT_OPERATION);
}
}

View File

@@ -0,0 +1,367 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
#ifndef OPERATION_H
#define OPERATION_H
namespace Script
{
// Contracts are non typed have variable data types
class Operation
{
public:
// returns false if there was an error
virtual bool work (Interpreter* interpreter) = 0;
virtual int getFee ();
virtual ~Operation ()
{
;
}
};
// this is just an Int in the code
class IntOp : public Operation
{
public:
bool work (Interpreter* interpreter)
{
Data::pointer data = interpreter->getIntData ();
if (data->isInt32 ())
{
interpreter->pushStack ( data );
return (true);
}
return (false);
}
};
class FloatOp : public Operation
{
public:
bool work (Interpreter* interpreter)
{
Data::pointer data = interpreter->getFloatData ();
if (data->isFloat ())
{
interpreter->pushStack ( data );
return (true);
}
return (false);
}
};
class Uint160Op : public Operation
{
public:
bool work (Interpreter* interpreter)
{
Data::pointer data = interpreter->getUint160Data ();
if (data->isUint160 ())
{
interpreter->pushStack ( data );
return (true);
}
return (false);
}
};
class AddOp : public Operation
{
public:
bool work (Interpreter* interpreter)
{
Data::pointer data1 = interpreter->popStack ();
Data::pointer data2 = interpreter->popStack ();
if ( (data1->isInt32 () || data1->isFloat ()) &&
(data2->isInt32 () || data2->isFloat ()) )
{
if (data1->isFloat () || data2->isFloat ()) interpreter->pushStack (Data::pointer (new FloatData (data1->getFloat () + data2->getFloat ())));
else interpreter->pushStack (Data::pointer (new IntData (data1->getInt () + data2->getInt ())));
return (true);
}
else
{
return (false);
}
}
};
class SubOp : public Operation
{
public:
bool work (Interpreter* interpreter)
{
Data::pointer data1 = interpreter->popStack ();
Data::pointer data2 = interpreter->popStack ();
if ( (data1->isInt32 () || data1->isFloat ()) &&
(data2->isInt32 () || data2->isFloat ()) )
{
if (data1->isFloat () || data2->isFloat ()) interpreter->pushStack (Data::pointer (new FloatData (data1->getFloat () - data2->getFloat ())));
else interpreter->pushStack (Data::pointer (new IntData (data1->getInt () - data2->getInt ())));
return (true);
}
else
{
return (false);
}
}
};
class MulOp : public Operation
{
public:
bool work (Interpreter* interpreter)
{
Data::pointer data1 = interpreter->popStack ();
Data::pointer data2 = interpreter->popStack ();
if ( (data1->isInt32 () || data1->isFloat ()) &&
(data2->isInt32 () || data2->isFloat ()) )
{
if (data1->isFloat () || data2->isFloat ()) interpreter->pushStack (Data::pointer (new FloatData (data1->getFloat ()*data2->getFloat ())));
else interpreter->pushStack (Data::pointer (new IntData (data1->getInt ()*data2->getInt ())));
return (true);
}
else
{
return (false);
}
}
};
class DivOp : public Operation
{
public:
bool work (Interpreter* interpreter)
{
Data::pointer data1 = interpreter->popStack ();
Data::pointer data2 = interpreter->popStack ();
if ( (data1->isInt32 () || data1->isFloat ()) &&
(data2->isInt32 () || data2->isFloat ()) )
{
if (data1->isFloat () || data2->isFloat ()) interpreter->pushStack (Data::pointer (new FloatData (data1->getFloat () / data2->getFloat ())));
else interpreter->pushStack (Data::pointer (new IntData (data1->getInt () / data2->getInt ())));
return (true);
}
else
{
return (false);
}
}
};
class GtrOp : public Operation
{
public:
bool work (Interpreter* interpreter)
{
Data::pointer data1 = interpreter->popStack ();
Data::pointer data2 = interpreter->popStack ();
if ( (data1->isInt32 () || data1->isFloat ()) &&
(data2->isInt32 () || data2->isFloat ()) )
{
interpreter->pushStack (Data::pointer (new BoolData (data1->getFloat () > data2->getFloat ())));
return (true);
}
else
{
return (false);
}
}
};
class LessOp : public Operation
{
public:
bool work (Interpreter* interpreter)
{
Data::pointer data1 = interpreter->popStack ();
Data::pointer data2 = interpreter->popStack ();
if ( (data1->isInt32 () || data1->isFloat ()) &&
(data2->isInt32 () || data2->isFloat ()) )
{
interpreter->pushStack (Data::pointer (new FloatData (data1->getFloat () < data2->getFloat ())));
return (true);
}
else
{
return (false);
}
}
};
class ModOp : public Operation
{
public:
bool work (Interpreter* interpreter)
{
Data::pointer data1 = interpreter->popStack ();
Data::pointer data2 = interpreter->popStack ();
if ( data1->isInt32 () && data2->isInt32 () )
{
interpreter->pushStack (Data::pointer (new IntData (data1->getInt () % data2->getInt ())));
return (true);
}
else
{
return (false);
}
}
};
class StartBlockOp : public Operation
{
public:
bool work (Interpreter* interpreter)
{
Data::pointer offset = interpreter->getIntData ();
return (interpreter->startBlock (offset->getInt ()));
}
};
class EndBlockOp : public Operation
{
public:
bool work (Interpreter* interpreter)
{
return (interpreter->endBlock ());
}
};
class StopOp : public Operation
{
public:
bool work (Interpreter* interpreter)
{
interpreter->stop ();
return (true);
}
};
class AcceptDataOp : public Operation
{
public:
bool work (Interpreter* interpreter)
{
Data::pointer data = interpreter->popStack ();
if (data->isInt32 ())
{
interpreter->pushStack ( interpreter->getAcceptData (data->getInt ()) );
return (true);
}
return (false);
}
};
class JumpIfOp : public Operation
{
public:
bool work (Interpreter* interpreter)
{
Data::pointer offset = interpreter->getIntData ();
Data::pointer cond = interpreter->popStack ();
if (cond->isBool () && offset->isInt32 ())
{
if (cond->isTrue ())
{
return (interpreter->jumpTo (offset->getInt ()));
}
return (true);
}
return (false);
}
};
class JumpOp : public Operation
{
public:
bool work (Interpreter* interpreter)
{
Data::pointer offset = interpreter->getIntData ();
if (offset->isInt32 ())
{
return (interpreter->jumpTo (offset->getInt ()));
}
return (false);
}
};
class SendXRPOp : public Operation
{
public:
bool work (Interpreter* interpreter)
{
Data::pointer sourceID = interpreter->popStack ();
Data::pointer destID = interpreter->popStack ();
Data::pointer amount = interpreter->popStack ();
if (sourceID->isUint160 () && destID->isUint160 () && amount->isInt32 () && interpreter->canSign (sourceID->getUint160 ()))
{
// make sure:
// source is either, this contract, issuer, or acceptor
// TODO do the send
//interpreter->pushStack( send result);
return (true);
}
return (false);
}
};
class GetDataOp : public Operation
{
public:
bool work (Interpreter* interpreter)
{
Data::pointer index = interpreter->popStack ();
if (index->isInt32 ())
{
interpreter->pushStack ( interpreter->getContractData (index->getInt ()));
return (true);
}
return (false);
}
};
}
#endif
// vim:ts=4

View File

@@ -0,0 +1,5 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================

View File

@@ -0,0 +1,171 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
#ifndef SCRIPT_DATA_H
#define SCRIPT_DATA_H
namespace Script
{
class Data
{
public:
typedef boost::shared_ptr<Data> pointer;
virtual ~Data ()
{
;
}
virtual bool isInt32 ()
{
return (false);
}
virtual bool isFloat ()
{
return (false);
}
virtual bool isUint160 ()
{
return (false);
}
virtual bool isError ()
{
return (false);
}
virtual bool isTrue ()
{
return (false);
}
virtual bool isBool ()
{
return (false);
}
//virtual bool isBlockEnd(){ return(false); }
virtual int getInt ()
{
return (0);
}
virtual float getFloat ()
{
return (0);
}
virtual uint160 getUint160 ()
{
return (0);
}
//virtual bool isCurrency(){ return(false); }
};
class IntData : public Data
{
int mValue;
public:
IntData (int value)
{
mValue = value;
}
bool isInt32 ()
{
return (true);
}
int getInt ()
{
return (mValue);
}
float getFloat ()
{
return ((float)mValue);
}
bool isTrue ()
{
return (mValue != 0);
}
};
class FloatData : public Data
{
float mValue;
public:
FloatData (float value)
{
mValue = value;
}
bool isFloat ()
{
return (true);
}
float getFloat ()
{
return (mValue);
}
bool isTrue ()
{
return (mValue != 0);
}
};
class Uint160Data : public Data
{
uint160 mValue;
public:
Uint160Data (uint160 value) : mValue (value)
{
;
}
bool isUint160 ()
{
return (true);
}
uint160 getUint160 ()
{
return (mValue);
}
};
class BoolData : public Data
{
bool mValue;
public:
BoolData (bool value)
{
mValue = value;
}
bool isBool ()
{
return (true);
}
bool isTrue ()
{
return (mValue);
}
};
class ErrorData : public Data
{
public:
bool isError ()
{
return (true);
}
};
class BlockEndData : public Data
{
public:
bool isBlockEnd ()
{
return (true);
}
};
}
#endif
// vim:ts=4

View File

@@ -0,0 +1,343 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
// Transaction database holds transactions and public keys
const char* TxnDBInit[] =
{
"PRAGMA synchronous=NORMAL;",
"PRAGMA journal_mode=WAL;",
"PRAGMA journal_size_limit=1582080;",
#if (ULONG_MAX > UINT_MAX) && !defined (NO_SQLITE_MMAP)
"PRAGMA mmap_size=17179869184;",
#endif
"BEGIN TRANSACTION;",
"CREATE TABLE Transactions ( \
TransID CHARACTER(64) PRIMARY KEY, \
TransType CHARACTER(24), \
FromAcct CHARACTER(35), \
FromSeq BIGINT UNSIGNED, \
LedgerSeq BIGINT UNSIGNED, \
Status CHARACTER(1), \
RawTxn BLOB, \
TxnMeta BLOB \
);",
"CREATE INDEX TxLgrIndex ON \
Transactions(LedgerSeq);",
"CREATE TABLE AccountTransactions ( \
TransID CHARACTER(64), \
Account CHARACTER(64), \
LedgerSeq BIGINT UNSIGNED, \
TxnSeq INTEGER \
);",
"CREATE INDEX AcctTxIDIndex ON \
AccountTransactions(TransID);",
"CREATE INDEX AcctTxIndex ON \
AccountTransactions(Account, LedgerSeq, TxnSeq, TransID);",
"CREATE INDEX AcctLgrIndex ON \
AccountTransactions(LedgerSeq, Account, TransID);",
"END TRANSACTION;"
};
int TxnDBCount = NUMBER (TxnDBInit);
// Ledger database holds ledgers and ledger confirmations
const char* LedgerDBInit[] =
{
"PRAGMA synchronous=NORMAL;",
"PRAGMA journal_mode=WAL;",
"PRAGMA journal_size_limit=1582080;",
"BEGIN TRANSACTION;",
"CREATE TABLE Ledgers ( \
LedgerHash CHARACTER(64) PRIMARY KEY, \
LedgerSeq BIGINT UNSIGNED, \
PrevHash CHARACTER(64), \
TotalCoins BIGINT UNSIGNED, \
ClosingTime BIGINT UNSIGNED, \
PrevClosingTime BIGINT UNSIGNED, \
CloseTimeRes BIGINT UNSIGNED, \
CloseFlags BIGINT UNSIGNED, \
AccountSetHash CHARACTER(64), \
TransSetHash CHARACTER(64) \
);",
"CREATE INDEX SeqLedger ON Ledgers(LedgerSeq);",
"CREATE TABLE Validations ( \
LedgerHash CHARACTER(64), \
NodePubKey CHARACTER(56), \
SignTime BIGINT UNSIGNED, \
RawData BLOB \
);",
"CREATE INDEX ValidationsByHash ON \
Validations(LedgerHash);",
"CREATE INDEX ValidationsByTime ON \
Validations(SignTime);",
"END TRANSACTION;"
};
int LedgerDBCount = NUMBER (LedgerDBInit);
// RPC database holds persistent data for RPC clients.
const char* RpcDBInit[] =
{
// Local persistence of the RPC client
"CREATE TABLE RPCData ( \
Key TEXT PRIMARY Key, \
Value TEXT \
);",
};
int RpcDBCount = NUMBER (RpcDBInit);
// NodeIdentity database holds local accounts and trusted nodes
// VFALCO NOTE but its a table not a database, so...?
//
const char* WalletDBInit[] =
{
// Node identity must be persisted for CAS routing and responsibilities.
"BEGIN TRANSACTION;",
"CREATE TABLE NodeIdentity ( \
PublicKey CHARACTER(53), \
PrivateKey CHARACTER(52), \
Dh512 TEXT, \
Dh1024 TEXT \
);",
// Miscellaneous persistent information
// Integer: 1 : Used to simplify SQL.
// ScoreUpdated: when scores was last updated.
// FetchUpdated: when last fetch succeeded.
"CREATE TABLE Misc ( \
Magic INTEGER UNIQUE NOT NULL, \
ScoreUpdated DATETIME, \
FetchUpdated DATETIME \
);",
// Scoring and other information for domains.
//
// Domain:
// Domain source for https.
// PublicKey:
// Set if ever succeeded.
// XXX Use NULL in place of ""
// Source:
// 'M' = Manually added. : 1500
// 'V' = validators.txt : 1000
// 'W' = Web browsing. : 200
// 'R' = Referral : 0
// Next:
// Time of next fetch attempt.
// Scan:
// Time of last fetch attempt.
// Fetch:
// Time of last successful fetch.
// Sha256:
// Checksum of last fetch.
// Comment:
// User supplied comment.
// Table of Domains user has asked to trust.
"CREATE TABLE SeedDomains ( \
Domain TEXT PRIMARY KEY NOT NULL, \
PublicKey CHARACTER(53), \
Source CHARACTER(1) NOT NULL, \
Next DATETIME, \
Scan DATETIME, \
Fetch DATETIME, \
Sha256 CHARACTER[64], \
Comment TEXT \
);",
// Allow us to easily find the next SeedDomain to fetch.
"CREATE INDEX SeedDomainNext ON SeedDomains (Next);",
// Table of PublicKeys user has asked to trust.
// Fetches are made to the CAS. This gets the ripple.txt so even validators without a web server can publish a ripple.txt.
// Source:
// 'M' = Manually added. : 1500
// 'V' = validators.txt : 1000
// 'W' = Web browsing. : 200
// 'R' = Referral : 0
// Next:
// Time of next fetch attempt.
// Scan:
// Time of last fetch attempt.
// Fetch:
// Time of last successful fetch.
// Sha256:
// Checksum of last fetch.
// Comment:
// User supplied comment.
"CREATE TABLE SeedNodes ( \
PublicKey CHARACTER(53) PRIMARY KEY NOT NULL, \
Source CHARACTER(1) NOT NULL, \
Next DATETIME, \
Scan DATETIME, \
Fetch DATETIME, \
Sha256 CHARACTER[64], \
Comment TEXT \
);",
// Allow us to easily find the next SeedNode to fetch.
"CREATE INDEX SeedNodeNext ON SeedNodes (Next);",
// Nodes we trust to not grossly collude against us. Derived from SeedDomains, SeedNodes, and ValidatorReferrals.
//
// Score:
// Computed trust score. Higher is better.
// Seen:
// Last validation received.
"CREATE TABLE TrustedNodes ( \
PublicKey CHARACTER(53) PRIMARY KEY NOT NULL, \
Score INTEGER DEFAULT 0 NOT NULL, \
Seen DATETIME, \
Comment TEXT \
);",
// List of referrals.
// - There may be multiple sources for a Validator. The last source is used.
// Validator:
// Public key of referrer.
// Entry:
// Entry index in [validators] table.
// Referral:
// This is the form provided by the ripple.txt:
// - Public key for CAS based referral.
// - Domain for domain based referral.
// XXX Do garbage collection when validators have no references.
"CREATE TABLE ValidatorReferrals ( \
Validator CHARACTER(53) NOT NULL, \
Entry INTEGER NOT NULL, \
Referral TEXT NOT NULL, \
PRIMARY KEY (Validator,Entry) \
);",
// List of referrals from ripple.txt files.
// Validator:
// Public key of referree.
// Entry:
// Entry index in [validators] table.
// IP:
// IP of referred.
// Port:
// -1 = Default
// XXX Do garbage collection when ips have no references.
"CREATE TABLE IpReferrals ( \
Validator CHARACTER(53) NOT NULL, \
Entry INTEGER NOT NULL, \
IP TEXT NOT NULL, \
Port INTEGER NOT NULL DEFAULT -1, \
PRIMARY KEY (Validator,Entry) \
);",
// Table of IPs to contact the network.
// IP:
// IP address to contact.
// Port:
// Port to contact.
// -1 = Default
// Score:
// Computed trust score. Higher is better.
// Source:
// 'C' = Configuration file
// 'V' = Validation file
// 'M' = Manually added.
// 'I' = Inbound connection.
// 'T' = Told by other peer
// 'O' = Other.
// ScanNext:
// When to next scan. Null=not scanning.
// ScanInterval:
// Delay between scans.
"CREATE TABLE PeerIps ( \
IpPort TEXT NOT NULL PRIMARY KEY, \
Score INTEGER NOT NULL DEFAULT 0, \
Source CHARACTER(1) NOT NULL, \
ScanNext DATETIME DEFAULT 0, \
ScanInterval INTEGER NOT NULL DEFAULT 0 \
);",
"CREATE INDEX PeerScanIndex ON \
PeerIps(ScanNext);",
"CREATE TABLE Features ( \
Hash CHARACTER(64) PRIMARY KEY, \
FirstMajority BIGINT UNSIGNED, \
LastMajority BIGINT UNSIGNED \
);",
"END TRANSACTION;"
};
int WalletDBCount = NUMBER (WalletDBInit);
// Hash node database holds nodes indexed by hash
// VFALCO TODO Remove this since it looks unused
/*
int HashNodeDBCount = NUMBER (HashNodeDBInit);
*/
// Net node database holds nodes seen on the network
// XXX Not really used needs replacement.
/*
const char* NetNodeDBInit[] =
{
"CREATE TABLE KnownNodes ( \
Hanko CHARACTER(35) PRIMARY KEY, \
LastSeen TEXT, \
HaveContactInfo CHARACTER(1), \
ContactObject BLOB \
);"
};
int NetNodeDBCount = NUMBER (NetNodeDBInit);
*/
// This appears to be unused
/*
const char* PathFindDBInit[] =
{
"PRAGMA synchronous = OFF; ",
"DROP TABLE TrustLines; ",
"CREATE TABLE TrustLines { "
"To CHARACTER(40), " // Hex of account trusted
"By CHARACTER(40), " // Hex of account trusting
"Currency CHARACTER(80), " // Hex currency, hex issuer
"Use INTEGER, " // Use count
"Seq BIGINT UNSIGNED " // Sequence when use count was updated
"}; ",
"CREATE INDEX TLBy ON TrustLines(By, Currency, Use);",
"CREATE INDEX TLTo ON TrustLines(To, Currency, Use);",
"DROP TABLE Exchanges;",
"CREATE TABLE Exchanges { "
"From CHARACTER(80), "
"To CHARACTER(80), "
"Currency CHARACTER(80), "
"Use INTEGER, "
"Seq BIGINT UNSIGNED "
"}; ",
"CREATE INDEX ExBy ON Exchanges(By, Currency, Use);",
"CREATE INDEX ExTo ON Exchanges(To, Currency, Use);",
};
int PathFindDBCount = NUMBER (PathFindDBInit);
*/

View File

@@ -0,0 +1,22 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
#ifndef RIPPLE_DBINIT_H_INCLUDED
#define RIPPLE_DBINIT_H_INCLUDED
// VFALCO TODO Tidy these up into a class with functions and return types.
extern const char* RpcDBInit[];
extern const char* TxnDBInit[];
extern const char* LedgerDBInit[];
extern const char* WalletDBInit[];
// VFALCO TODO Figure out what these counts are for
extern int RpcDBCount;
extern int TxnDBCount;
extern int LedgerDBCount;
extern int WalletDBCount;
#endif

View File

@@ -0,0 +1,193 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
Database::Database (const char* host)
: mNumCol (0)
{
mHost = host;
}
Database::~Database ()
{
}
bool Database::getNull (const char* colName)
{
int index;
if (getColNumber (colName, &index))
{
return getNull (index);
}
return true;
}
char* Database::getStr (const char* colName, std::string& retStr)
{
int index;
if (getColNumber (colName, &index))
{
return getStr (index, retStr);
}
return NULL;
}
int32 Database::getInt (const char* colName)
{
int index;
if (getColNumber (colName, &index))
{
return getInt (index);
}
return 0;
}
float Database::getFloat (const char* colName)
{
int index;
if (getColNumber (colName, &index))
{
return getFloat (index);
}
return 0;
}
bool Database::getBool (const char* colName)
{
int index;
if (getColNumber (colName, &index))
{
return getBool (index);
}
return 0;
}
int Database::getBinary (const char* colName, unsigned char* buf, int maxSize)
{
int index;
if (getColNumber (colName, &index))
{
return (getBinary (index, buf, maxSize));
}
return (0);
}
Blob Database::getBinary (const std::string& strColName)
{
int index;
if (getColNumber (strColName.c_str (), &index))
{
return getBinary (index);
}
return Blob ();
}
std::string Database::getStrBinary (const std::string& strColName)
{
// YYY Could eliminate a copy if getStrBinary was a template.
return strCopy (getBinary (strColName.c_str ()));
}
uint64 Database::getBigInt (const char* colName)
{
int index;
if (getColNumber (colName, &index))
{
return getBigInt (index);
}
return 0;
}
// returns false if can't find col
bool Database::getColNumber (const char* colName, int* retIndex)
{
for (unsigned int n = 0; n < mColNameTable.size (); n++)
{
if (strcmp (colName, mColNameTable[n].c_str ()) == 0)
{
*retIndex = n;
return (true);
}
}
return false;
}
#if 0
int Database::getSingleDBValueInt (const char* sql)
{
int ret;
if ( executeSQL (sql) && startIterRows ()
{
ret = getInt (0);
endIterRows ();
}
else
{
//theUI->statusMsg("ERROR with database: %s",sql);
ret = 0;
}
return (ret);
}
#endif
#if 0
float Database::getSingleDBValueFloat (const char* sql)
{
float ret;
if (executeSQL (sql) && startIterRows () && getNextRow ())
{
ret = getFloat (0);
endIterRows ();
}
else
{
//theUI->statusMsg("ERROR with database: %s",sql);
ret = 0;
}
return (ret);
}
#endif
#if 0
char* Database::getSingleDBValueStr (const char* sql, std::string& retStr)
{
char* ret;
if (executeSQL (sql) && startIterRows ())
{
ret = getStr (0, retStr);
endIterRows ();
}
else
{
//theUI->statusMsg("ERROR with database: %s",sql);
ret = 0;
}
return (ret);
}
#endif
// vim:ts=4

View File

@@ -0,0 +1,107 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
#ifndef RIPPLE_DATABASE_RIPPLEHEADER
#define RIPPLE_DATABASE_RIPPLEHEADER
// VFALCO Get rid of these macros
//
#define SQL_FOREACH(_db, _strQuery) \
if ((_db)->executeSQL(_strQuery)) \
for (bool _bMore = (_db)->startIterRows(); _bMore; _bMore = (_db)->getNextRow())
#define SQL_EXISTS(_db, _strQuery) \
((_db)->executeSQL(_strQuery) && (_db)->startIterRows())
/*
this maintains the connection to the database
*/
class SqliteDatabase;
class JobQueue;
class Database
{
public:
explicit Database (const char* host);
virtual ~Database ();
virtual void connect () = 0;
virtual void disconnect () = 0;
// returns true if the query went ok
virtual bool executeSQL (const char* sql, bool fail_okay = false) = 0;
bool executeSQL (std::string strSql, bool fail_okay = false)
{
return executeSQL (strSql.c_str (), fail_okay);
}
// returns false if there are no results
virtual bool startIterRows (bool finalize = true) = 0;
virtual void endIterRows () = 0;
// call this after you executeSQL
// will return false if there are no more rows
virtual bool getNextRow (bool finalize = true) = 0;
// get Data from the current row
bool getNull (const char* colName);
char* getStr (const char* colName, std::string& retStr);
std::string getStrBinary (const std::string& strColName);
int32 getInt (const char* colName);
float getFloat (const char* colName);
bool getBool (const char* colName);
// returns amount stored in buf
int getBinary (const char* colName, unsigned char* buf, int maxSize);
Blob getBinary (const std::string& strColName);
uint64 getBigInt (const char* colName);
virtual bool getNull (int colIndex) = 0;
virtual char* getStr (int colIndex, std::string& retStr) = 0;
virtual int32 getInt (int colIndex) = 0;
virtual float getFloat (int colIndex) = 0;
virtual bool getBool (int colIndex) = 0;
virtual int getBinary (int colIndex, unsigned char* buf, int maxSize) = 0;
virtual uint64 getBigInt (int colIndex) = 0;
virtual Blob getBinary (int colIndex) = 0;
// int getSingleDBValueInt(const char* sql);
// float getSingleDBValueFloat(const char* sql);
// char* getSingleDBValueStr(const char* sql, std::string& retStr);
virtual bool setupCheckpointing (JobQueue*)
{
return false;
}
virtual SqliteDatabase* getSqliteDB ()
{
return NULL;
}
virtual int getKBUsedAll ()
{
return -1;
}
virtual int getKBUsedDB ()
{
return -1;
}
protected:
bool getColNumber (const char* colName, int* retIndex);
int mNumCol;
std::string mHost;
std::vector <std::string> mColNameTable;
};
#endif
// vim:ts=4

View File

@@ -0,0 +1,33 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
int DatabaseCon::sCount = 0;
DatabaseCon::DatabaseCon (const std::string& strName, const char* initStrings[], int initCount)
{
++sCount;
// VFALCO TODO remove this dependency on the config by making it the caller's
// responsibility to pass in the path. Add a member function to Application
// or Config to compute this path.
//
boost::filesystem::path pPath = (getConfig ().RUN_STANDALONE &&
((getConfig ().START_UP != Config::LOAD) && (getConfig ().START_UP != Config::REPLAY)))
? "" // Use temporary files.
: (getConfig ().DATA_DIR / strName); // Use regular db files.
mDatabase = new SqliteDatabase (pPath.string ().c_str ());
mDatabase->connect ();
for (int i = 0; i < initCount; ++i)
mDatabase->executeSQL (initStrings[i], true);
}
DatabaseCon::~DatabaseCon ()
{
mDatabase->disconnect ();
delete mDatabase;
}

View File

@@ -0,0 +1,38 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
#ifndef RIPPLE_DATABASECON_H
#define RIPPLE_DATABASECON_H
// VFALCO NOTE This looks like a pointless class. Figure out
// what purpose it is really trying to serve and do it better.
class DatabaseCon : LeakChecked <DatabaseCon>
{
public:
DatabaseCon (const std::string& name, const char* initString[], int countInit);
~DatabaseCon ();
Database* getDB ()
{
return mDatabase;
}
DeprecatedRecursiveMutex& getDBLock ()
{
return mLock;
}
static int getCount ()
{
return sCount;
}
// VFALCO TODO change "protected" to "private" throughout the code
private:
Database* mDatabase;
DeprecatedRecursiveMutex mLock;
static int sCount;
};
#endif

View File

@@ -0,0 +1,450 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
SETUP_LOG (SqliteDatabase)
SqliteStatement::SqliteStatement (SqliteDatabase* db, const char* sql, bool aux)
{
assert (db);
sqlite3* conn = aux ? db->getAuxConnection () : db->peekConnection ();
int j = sqlite3_prepare_v2 (conn, sql, strlen (sql) + 1, &statement, NULL);
if (j != SQLITE_OK)
throw j;
}
SqliteStatement::SqliteStatement (SqliteDatabase* db, const std::string& sql, bool aux)
{
assert (db);
sqlite3* conn = aux ? db->getAuxConnection () : db->peekConnection ();
int j = sqlite3_prepare_v2 (conn, sql.c_str (), sql.size () + 1, &statement, NULL);
if (j != SQLITE_OK)
throw j;
}
SqliteStatement::~SqliteStatement ()
{
sqlite3_finalize (statement);
}
//------------------------------------------------------------------------------
SqliteDatabase::SqliteDatabase (const char* host)
: Database (host)
, m_walMutex (this, "SqliteDB", __FILE__, __LINE__)
, m_thread ("sqlitedb")
, mWalQ (NULL)
, walRunning (false)
{
m_thread.start ();
mConnection = NULL;
mAuxConnection = NULL;
mCurrentStmt = NULL;
}
void SqliteDatabase::connect ()
{
int rc = sqlite3_open (mHost.c_str (), &mConnection);
if (rc)
{
WriteLog (lsFATAL, SqliteDatabase) << "Can't open " << mHost << " " << rc;
sqlite3_close (mConnection);
assert ((rc != SQLITE_BUSY) && (rc != SQLITE_LOCKED));
}
}
sqlite3* SqliteDatabase::getAuxConnection ()
{
ScopedLockType sl (m_walMutex, __FILE__, __LINE__);
if (mAuxConnection == NULL)
{
int rc = sqlite3_open (mHost.c_str (), &mAuxConnection);
if (rc)
{
WriteLog (lsFATAL, SqliteDatabase) << "Can't aux open " << mHost << " " << rc;
assert ((rc != SQLITE_BUSY) && (rc != SQLITE_LOCKED));
if (mAuxConnection != NULL)
{
sqlite3_close (mConnection);
mAuxConnection = NULL;
}
}
}
return mAuxConnection;
}
void SqliteDatabase::disconnect ()
{
sqlite3_finalize (mCurrentStmt);
sqlite3_close (mConnection);
if (mAuxConnection != NULL)
sqlite3_close (mAuxConnection);
}
// returns true if the query went ok
bool SqliteDatabase::executeSQL (const char* sql, bool fail_ok)
{
#ifdef DEBUG_HANGING_LOCKS
assert (fail_ok || (mCurrentStmt == NULL));
#endif
sqlite3_finalize (mCurrentStmt);
int rc = sqlite3_prepare_v2 (mConnection, sql, -1, &mCurrentStmt, NULL);
if (SQLITE_OK != rc)
{
if (!fail_ok)
{
#ifdef BEAST_DEBUG
WriteLog (lsWARNING, SqliteDatabase) << "Perror:" << mHost << ": " << rc;
WriteLog (lsWARNING, SqliteDatabase) << "Statement: " << sql;
WriteLog (lsWARNING, SqliteDatabase) << "Error: " << sqlite3_errmsg (mConnection);
#endif
}
endIterRows ();
return false;
}
rc = sqlite3_step (mCurrentStmt);
if (rc == SQLITE_ROW)
{
mMoreRows = true;
}
else if (rc == SQLITE_DONE)
{
endIterRows ();
mMoreRows = false;
}
else
{
if ((rc != SQLITE_BUSY) && (rc != SQLITE_LOCKED))
{
WriteLog (lsFATAL, SqliteDatabase) << mHost << " returns error " << rc << ": " << sqlite3_errmsg (mConnection);
assert (false);
}
mMoreRows = false;
if (!fail_ok)
{
#ifdef BEAST_DEBUG
WriteLog (lsWARNING, SqliteDatabase) << "SQL Serror:" << mHost << ": " << rc;
WriteLog (lsWARNING, SqliteDatabase) << "Statement: " << sql;
WriteLog (lsWARNING, SqliteDatabase) << "Error: " << sqlite3_errmsg (mConnection);
#endif
}
endIterRows ();
return false;
}
return true;
}
// returns false if there are no results
bool SqliteDatabase::startIterRows (bool finalize)
{
mColNameTable.clear ();
mColNameTable.resize (sqlite3_column_count (mCurrentStmt));
for (unsigned n = 0; n < mColNameTable.size (); n++)
{
mColNameTable[n] = sqlite3_column_name (mCurrentStmt, n);
}
if (!mMoreRows && finalize)
endIterRows ();
return (mMoreRows);
}
void SqliteDatabase::endIterRows ()
{
sqlite3_finalize (mCurrentStmt);
mCurrentStmt = NULL;
}
// call this after you executeSQL
// will return false if there are no more rows
bool SqliteDatabase::getNextRow (bool finalize)
{
if (mMoreRows)
{
int rc = sqlite3_step (mCurrentStmt);
if (rc == SQLITE_ROW)
return (true);
assert ((rc != SQLITE_BUSY) && (rc != SQLITE_LOCKED));
CondLog ((rc != SQLITE_DONE), lsWARNING, SqliteDatabase) << "Rerror: " << mHost << ": " << rc;
}
if (finalize)
endIterRows ();
return false;
}
bool SqliteDatabase::getNull (int colIndex)
{
return (SQLITE_NULL == sqlite3_column_type (mCurrentStmt, colIndex));
}
char* SqliteDatabase::getStr (int colIndex, std::string& retStr)
{
const char* text = reinterpret_cast<const char*> (sqlite3_column_text (mCurrentStmt, colIndex));
retStr = (text == NULL) ? "" : text;
return const_cast<char*> (retStr.c_str ());
}
int32 SqliteDatabase::getInt (int colIndex)
{
return (sqlite3_column_int (mCurrentStmt, colIndex));
}
float SqliteDatabase::getFloat (int colIndex)
{
return (static_cast <float> (sqlite3_column_double (mCurrentStmt, colIndex)));
}
bool SqliteDatabase::getBool (int colIndex)
{
return (sqlite3_column_int (mCurrentStmt, colIndex) ? true : false);
}
int SqliteDatabase::getBinary (int colIndex, unsigned char* buf, int maxSize)
{
const void* blob = sqlite3_column_blob (mCurrentStmt, colIndex);
int size = sqlite3_column_bytes (mCurrentStmt, colIndex);
if (size < maxSize) maxSize = size;
memcpy (buf, blob, maxSize);
return (size);
}
Blob SqliteDatabase::getBinary (int colIndex)
{
const unsigned char* blob = reinterpret_cast<const unsigned char*> (sqlite3_column_blob (mCurrentStmt, colIndex));
size_t iSize = sqlite3_column_bytes (mCurrentStmt, colIndex);
Blob vucResult;
vucResult.resize (iSize);
std::copy (blob, blob + iSize, vucResult.begin ());
return vucResult;
}
uint64 SqliteDatabase::getBigInt (int colIndex)
{
return (sqlite3_column_int64 (mCurrentStmt, colIndex));
}
int SqliteDatabase::getKBUsedAll ()
{
return static_cast<int> (sqlite3_memory_used () / 1024);
}
int SqliteDatabase::getKBUsedDB ()
{
int cur = 0, hiw = 0;
sqlite3_db_status (mConnection, SQLITE_DBSTATUS_CACHE_USED, &cur, &hiw, 0);
return cur / 1024;
}
static int SqliteWALHook (void* s, sqlite3* dbCon, const char* dbName, int walSize)
{
(reinterpret_cast<SqliteDatabase*> (s))->doHook (dbName, walSize);
return SQLITE_OK;
}
bool SqliteDatabase::setupCheckpointing (JobQueue* q)
{
mWalQ = q;
sqlite3_wal_hook (mConnection, SqliteWALHook, this);
return true;
}
void SqliteDatabase::doHook (const char* db, int pages)
{
if (pages < 1000)
return;
{
ScopedLockType sl (m_walMutex, __FILE__, __LINE__);
if (walRunning)
return;
walRunning = true;
}
if (mWalQ)
{
mWalQ->addJob (jtWAL, std::string ("WAL:") + mHost, BIND_TYPE (&SqliteDatabase::runWal, this));
}
else
{
m_thread.call (&SqliteDatabase::runWal, this);
}
}
void SqliteDatabase::runWal ()
{
int log = 0, ckpt = 0;
int ret = sqlite3_wal_checkpoint_v2 (mConnection, NULL, SQLITE_CHECKPOINT_PASSIVE, &log, &ckpt);
if (ret != SQLITE_OK)
{
WriteLog ((ret == SQLITE_LOCKED) ? lsTRACE : lsWARNING, SqliteDatabase) << "WAL("
<< sqlite3_db_filename (mConnection, "main") << "): error " << ret;
}
else
WriteLog (lsTRACE, SqliteDatabase) << "WAL(" << sqlite3_db_filename (mConnection, "main") <<
"): frames=" << log << ", written=" << ckpt;
{
ScopedLockType sl (m_walMutex, __FILE__, __LINE__);
walRunning = false;
}
}
sqlite3_stmt* SqliteStatement::peekStatement ()
{
return statement;
}
int SqliteStatement::bind (int position, const void* data, int length)
{
return sqlite3_bind_blob (statement, position, data, length, SQLITE_TRANSIENT);
}
int SqliteStatement::bindStatic (int position, const void* data, int length)
{
return sqlite3_bind_blob (statement, position, data, length, SQLITE_STATIC);
}
int SqliteStatement::bindStatic (int position, Blob const& value)
{
return sqlite3_bind_blob (statement, position, &value.front (), value.size (), SQLITE_STATIC);
}
int SqliteStatement::bind (int position, uint32 value)
{
return sqlite3_bind_int64 (statement, position, static_cast<sqlite3_int64> (value));
}
int SqliteStatement::bind (int position, const std::string& value)
{
return sqlite3_bind_text (statement, position, value.data (), value.size (), SQLITE_TRANSIENT);
}
int SqliteStatement::bindStatic (int position, const std::string& value)
{
return sqlite3_bind_text (statement, position, value.data (), value.size (), SQLITE_STATIC);
}
int SqliteStatement::bind (int position)
{
return sqlite3_bind_null (statement, position);
}
int SqliteStatement::size (int column)
{
return sqlite3_column_bytes (statement, column);
}
const void* SqliteStatement::peekBlob (int column)
{
return sqlite3_column_blob (statement, column);
}
Blob SqliteStatement::getBlob (int column)
{
int size = sqlite3_column_bytes (statement, column);
Blob ret (size);
memcpy (& (ret.front ()), sqlite3_column_blob (statement, column), size);
return ret;
}
std::string SqliteStatement::getString (int column)
{
return reinterpret_cast<const char*> (sqlite3_column_text (statement, column));
}
const char* SqliteStatement::peekString (int column)
{
return reinterpret_cast<const char*> (sqlite3_column_text (statement, column));
}
uint32 SqliteStatement::getUInt32 (int column)
{
return static_cast<uint32> (sqlite3_column_int64 (statement, column));
}
int64 SqliteStatement::getInt64 (int column)
{
return sqlite3_column_int64 (statement, column);
}
int SqliteStatement::step ()
{
return sqlite3_step (statement);
}
int SqliteStatement::reset ()
{
return sqlite3_reset (statement);
}
bool SqliteStatement::isOk (int j)
{
return j == SQLITE_OK;
}
bool SqliteStatement::isDone (int j)
{
return j == SQLITE_DONE;
}
bool SqliteStatement::isRow (int j)
{
return j == SQLITE_ROW;
}
bool SqliteStatement::isError (int j)
{
switch (j)
{
case SQLITE_OK:
case SQLITE_ROW:
case SQLITE_DONE:
return true;
default:
return false;
}
}
std::string SqliteStatement::getError (int j)
{
return sqlite3_errstr (j);
}
// vim:ts=4

View File

@@ -0,0 +1,132 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
#ifndef RIPPLE_SQLITEDATABASE_RIPPLEHEADER
#define RIPPLE_SQLITEDATABASE_RIPPLEHEADER
class SqliteDatabase
: public Database
, LeakChecked <SqliteDatabase>
{
public:
explicit SqliteDatabase (char const* host);
void connect ();
void disconnect ();
// returns true if the query went ok
bool executeSQL (const char* sql, bool fail_okay);
// tells you how many rows were changed by an update or insert
int getNumRowsAffected ();
// returns false if there are no results
bool startIterRows (bool finalize);
void endIterRows ();
// call this after you executeSQL
// will return false if there are no more rows
bool getNextRow (bool finalize);
bool getNull (int colIndex);
char* getStr (int colIndex, std::string& retStr);
int32 getInt (int colIndex);
float getFloat (int colIndex);
bool getBool (int colIndex);
// returns amount stored in buf
int getBinary (int colIndex, unsigned char* buf, int maxSize);
Blob getBinary (int colIndex);
uint64 getBigInt (int colIndex);
sqlite3* peekConnection ()
{
return mConnection;
}
sqlite3* getAuxConnection ();
virtual bool setupCheckpointing (JobQueue*);
virtual SqliteDatabase* getSqliteDB ()
{
return this;
}
void runWal ();
void doHook (const char* db, int walSize);
int getKBUsedDB ();
int getKBUsedAll ();
private:
typedef RippleMutex LockType;
typedef LockType::ScopedLockType ScopedLockType;
LockType m_walMutex;
ThreadWithCallQueue m_thread;
sqlite3* mConnection;
// VFALCO TODO Why do we need an "aux" connection? Should just use a second SqliteDatabase object.
sqlite3* mAuxConnection;
sqlite3_stmt* mCurrentStmt;
bool mMoreRows;
JobQueue* mWalQ;
bool walRunning;
};
class SqliteStatement
{
private:
SqliteStatement (const SqliteStatement&); // no implementation
SqliteStatement& operator= (const SqliteStatement&); // no implementation
protected:
sqlite3_stmt* statement;
public:
// VFALCO TODO This is quite a convoluted interface. A mysterious "aux" connection?
// Why not just have two SqliteDatabase objects?
//
SqliteStatement (SqliteDatabase* db, const char* statement, bool aux = false);
SqliteStatement (SqliteDatabase* db, const std::string& statement, bool aux = false);
~SqliteStatement ();
sqlite3_stmt* peekStatement ();
// positions start at 1
int bind (int position, const void* data, int length);
int bindStatic (int position, const void* data, int length);
int bindStatic (int position, Blob const& value);
int bind (int position, const std::string& value);
int bindStatic (int position, const std::string& value);
int bind (int position, uint32 value);
int bind (int position);
// columns start at 0
int size (int column);
const void* peekBlob (int column);
Blob getBlob (int column);
std::string getString (int column);
const char* peekString (int column);
uint32 getUInt32 (int column);
int64 getInt64 (int column);
int step ();
int reset ();
// translate return values of step and reset
bool isOk (int);
bool isDone (int);
bool isRow (int);
bool isError (int);
std::string getError (int);
};
#endif
// vim:ts=4

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,505 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
#ifndef RIPPLE_LEDGER_H
#define RIPPLE_LEDGER_H
class Job;
enum LedgerStateParms
{
lepNONE = 0, // no special flags
// input flags
lepCREATE = 1, // Create if not present
// output flags
lepOKAY = 2, // success
lepMISSING = 4, // No node in that slot
lepWRONGTYPE = 8, // Node of different type there
lepCREATED = 16, // Node was created
lepERROR = 32, // error
};
#define LEDGER_JSON_DUMP_TXRP 0x10000000
#define LEDGER_JSON_DUMP_STATE 0x20000000
#define LEDGER_JSON_EXPAND 0x40000000
#define LEDGER_JSON_FULL 0x80000000
class SqliteStatement;
class LedgerBase
{
protected:
LedgerBase ();
// VFALCO TODO eliminate the need for friends
friend class TransactionEngine;
friend class Transactor;
typedef RippleRecursiveMutex LockType;
typedef LockType::ScopedLockType ScopedLockType;
LockType mLock;
};
// VFALCO TODO figure out exactly how this thing works.
// It seems like some ledger database is stored as a global, static in the
// class. But then what is the meaning of a Ledger object? Is this
// really two classes in one? StoreOfAllLedgers + SingleLedgerObject?
//
class Ledger
: public boost::enable_shared_from_this <Ledger>
, public LedgerBase
, public CountedObject <Ledger>
, public Uncopyable
{
public:
static char const* getCountedObjectName () { return "Ledger"; }
typedef boost::shared_ptr<Ledger> pointer;
typedef const boost::shared_ptr<Ledger>& ref;
enum TransResult
{
TR_ERROR = -1,
TR_SUCCESS = 0,
TR_NOTFOUND = 1,
TR_ALREADY = 2,
TR_BADTRANS = 3, // the transaction itself is corrupt
TR_BADACCT = 4, // one of the accounts is invalid
TR_INSUFF = 5, // the sending(apply)/receiving(remove) account is broke
TR_PASTASEQ = 6, // account is past this transaction
TR_PREASEQ = 7, // account is missing transactions before this
TR_BADLSEQ = 8, // ledger too early
TR_TOOSMALL = 9, // amount is less than Tx fee
};
// ledger close flags
static const uint32 sLCF_NoConsensusTime = 1;
public:
Ledger (const RippleAddress & masterID, uint64 startAmount); // used for the starting bootstrap ledger
Ledger (uint256 const & parentHash, uint256 const & transHash, uint256 const & accountHash,
uint64 totCoins, uint32 closeTime, uint32 parentCloseTime, int closeFlags, int closeResolution,
uint32 ledgerSeq, bool & loaded); // used for database ledgers
Ledger (Blob const & rawLedger, bool hasPrefix);
Ledger (const std::string & rawLedger, bool hasPrefix);
Ledger (bool dummy, Ledger & previous); // ledger after this one
Ledger (Ledger & target, bool isMutable); // snapshot
~Ledger ();
static Ledger::pointer getSQL (const std::string & sqlStatement);
static Ledger::pointer getSQL1 (SqliteStatement*);
static void getSQL2 (Ledger::ref);
static Ledger::pointer getLastFullLedger ();
static uint32 roundCloseTime (uint32 closeTime, uint32 closeResolution);
void updateHash ();
void setClosed ()
{
mClosed = true;
}
void setValidated()
{
mValidated = true;
}
void setAccepted (uint32 closeTime, int closeResolution, bool correctCloseTime);
void setAccepted ();
void setImmutable ();
bool isClosed ()
{
return mClosed;
}
bool isAccepted ()
{
return mAccepted;
}
bool isValidated ()
{
return mValidated;
}
bool isImmutable ()
{
return mImmutable;
}
bool isFixed ()
{
return mClosed || mImmutable;
}
void setFull ()
{
mTransactionMap->setLedgerSeq (mLedgerSeq);
mAccountStateMap->setLedgerSeq (mLedgerSeq);
}
// ledger signature operations
void addRaw (Serializer & s) const;
void setRaw (Serializer & s, bool hasPrefix);
uint256 getHash ();
uint256 const& getParentHash () const
{
return mParentHash;
}
uint256 const& getTransHash () const
{
return mTransHash;
}
uint256 const& getAccountHash () const
{
return mAccountHash;
}
uint64 getTotalCoins () const
{
return mTotCoins;
}
void destroyCoins (uint64 fee)
{
mTotCoins -= fee;
}
uint32 getCloseTimeNC () const
{
return mCloseTime;
}
uint32 getParentCloseTimeNC () const
{
return mParentCloseTime;
}
uint32 getLedgerSeq () const
{
return mLedgerSeq;
}
int getCloseResolution () const
{
return mCloseResolution;
}
bool getCloseAgree () const
{
return (mCloseFlags & sLCF_NoConsensusTime) == 0;
}
// close time functions
void setCloseTime (uint32 ct)
{
assert (!mImmutable);
mCloseTime = ct;
}
void setCloseTime (boost::posix_time::ptime);
boost::posix_time::ptime getCloseTime () const;
// low level functions
SHAMap::ref peekTransactionMap ()
{
return mTransactionMap;
}
SHAMap::ref peekAccountStateMap ()
{
return mAccountStateMap;
}
void dropCache ()
{
assert (isImmutable ());
if (mTransactionMap)
mTransactionMap->dropCache ();
if (mAccountStateMap)
mAccountStateMap->dropCache ();
}
// ledger sync functions
void setAcquiring (void);
bool isAcquiring (void);
bool isAcquiringTx (void);
bool isAcquiringAS (void);
// Transaction Functions
bool addTransaction (uint256 const & id, const Serializer & txn);
bool addTransaction (uint256 const & id, const Serializer & txn, const Serializer & metaData);
bool hasTransaction (uint256 const & TransID) const
{
return mTransactionMap->hasItem (TransID);
}
Transaction::pointer getTransaction (uint256 const & transID) const;
bool getTransaction (uint256 const & transID, Transaction::pointer & txn, TransactionMetaSet::pointer & txMeta);
bool getTransactionMeta (uint256 const & transID, TransactionMetaSet::pointer & txMeta);
bool getMetaHex (uint256 const & transID, std::string & hex);
static SerializedTransaction::pointer getSTransaction (SHAMapItem::ref, SHAMapTreeNode::TNType);
SerializedTransaction::pointer getSMTransaction (SHAMapItem::ref, SHAMapTreeNode::TNType,
TransactionMetaSet::pointer & txMeta);
// high-level functions
bool hasAccount (const RippleAddress & acctID);
AccountState::pointer getAccountState (const RippleAddress & acctID);
LedgerStateParms writeBack (LedgerStateParms parms, SLE::ref);
SLE::pointer getAccountRoot (const uint160 & accountID);
SLE::pointer getAccountRoot (const RippleAddress & naAccountID);
void updateSkipList ();
void visitAccountItems (const uint160 & acctID, FUNCTION_TYPE<void (SLE::ref)>);
// database functions (low-level)
static Ledger::pointer loadByIndex (uint32 ledgerIndex);
static Ledger::pointer loadByHash (uint256 const & ledgerHash);
static uint256 getHashByIndex (uint32 index);
static bool getHashesByIndex (uint32 index, uint256 & ledgerHash, uint256 & parentHash);
static std::map< uint32, std::pair<uint256, uint256> > getHashesByIndex (uint32 minSeq, uint32 maxSeq);
bool pendSaveValidated (bool isSynchronous, bool isCurrent);
// next/prev function
SLE::pointer getSLE (uint256 const & uHash); // SLE is mutable
SLE::pointer getSLEi (uint256 const & uHash); // SLE is immutable
// VFALCO NOTE These seem to let you walk the list of ledgers
//
uint256 getFirstLedgerIndex ();
uint256 getLastLedgerIndex ();
uint256 getNextLedgerIndex (uint256 const & uHash); // first node >hash
uint256 getNextLedgerIndex (uint256 const & uHash, uint256 const & uEnd); // first node >hash, <end
uint256 getPrevLedgerIndex (uint256 const & uHash); // last node <hash
uint256 getPrevLedgerIndex (uint256 const & uHash, uint256 const & uBegin); // last node <hash, >begin
// Ledger hash table function
static uint256 getLedgerHashIndex ();
static uint256 getLedgerHashIndex (uint32 desiredLedgerIndex);
static int getLedgerHashOffset (uint32 desiredLedgerIndex);
static int getLedgerHashOffset (uint32 desiredLedgerIndex, uint32 currentLedgerIndex);
uint256 getLedgerHash (uint32 ledgerIndex);
std::vector< std::pair<uint32, uint256> > getLedgerHashes ();
static uint256 getLedgerFeatureIndex ();
static uint256 getLedgerFeeIndex ();
std::vector<uint256> getLedgerFeatures ();
std::vector<uint256> getNeededTransactionHashes (int max, SHAMapSyncFilter * filter);
std::vector<uint256> getNeededAccountStateHashes (int max, SHAMapSyncFilter * filter);
// index calculation functions
static uint256 getAccountRootIndex (const uint160 & uAccountID);
static uint256 getAccountRootIndex (const RippleAddress & account)
{
return getAccountRootIndex (account.getAccountID ());
}
//
// Generator Map functions
//
SLE::pointer getGenerator (const uint160 & uGeneratorID);
static uint256 getGeneratorIndex (const uint160 & uGeneratorID);
//
// Nickname functions
//
static uint256 getNicknameHash (const std::string & strNickname)
{
Serializer s (strNickname);
return s.getSHA256 ();
}
NicknameState::pointer getNicknameState (uint256 const & uNickname);
NicknameState::pointer getNicknameState (const std::string & strNickname)
{
return getNicknameState (getNicknameHash (strNickname));
}
SLE::pointer getNickname (uint256 const & uNickname);
SLE::pointer getNickname (const std::string & strNickname)
{
return getNickname (getNicknameHash (strNickname));
}
static uint256 getNicknameIndex (uint256 const & uNickname);
//
// Order book functions
//
// Order book dirs have a base so we can use next to step through them in quality order.
static bool isValidBook (const uint160 & uTakerPaysCurrency, const uint160 & uTakerPaysIssuerID,
const uint160 & uTakerGetsCurrency, const uint160 & uTakerGetsIssuerID);
static uint256 getBookBase (const uint160 & uTakerPaysCurrency, const uint160 & uTakerPaysIssuerID,
const uint160 & uTakerGetsCurrency, const uint160 & uTakerGetsIssuerID);
//
// Offer functions
//
SLE::pointer getOffer (uint256 const & uIndex);
SLE::pointer getOffer (const uint160 & uAccountID, uint32 uSequence)
{
return getOffer (getOfferIndex (uAccountID, uSequence));
}
// The index of an offer.
static uint256 getOfferIndex (const uint160 & uAccountID, uint32 uSequence);
//
// Owner functions
//
// VFALCO NOTE This is a simple math operation that converts the account ID
// into a 256 bit object (I think....need to research this)
//
// All items controlled by an account are here: offers
static uint256 getOwnerDirIndex (const uint160 & uAccountID);
//
// Directory functions
// Directories are doubly linked lists of nodes.
// Given a directory root and and index compute the index of a node.
static uint256 getDirNodeIndex (uint256 const & uDirRoot, const uint64 uNodeIndex = 0);
static void ownerDirDescriber (SLE::ref, const uint160 & owner);
// Return a node: root or normal
SLE::pointer getDirNode (uint256 const & uNodeIndex);
//
// Quality
//
static uint256 getQualityIndex (uint256 const & uBase, const uint64 uNodeDir = 0);
static uint256 getQualityNext (uint256 const & uBase);
static uint64 getQuality (uint256 const & uBase);
static void qualityDirDescriber (SLE::ref,
const uint160 & uTakerPaysCurrency, const uint160 & uTakerPaysIssuer,
const uint160 & uTakerGetsCurrency, const uint160 & uTakerGetsIssuer,
const uint64 & uRate);
//
// Ripple functions : credit lines
//
// Index of node which is the ripple state between two accounts for a currency.
// VFALCO NOTE Rename these to make it clear they are simple functions that
// don't access global variables. e.g. "calculateKeyFromRippleStateAndAddress"
//
static uint256 getRippleStateIndex (const RippleAddress & naA, const RippleAddress & naB, const uint160 & uCurrency);
static uint256 getRippleStateIndex (const uint160 & uiA, const uint160 & uiB, const uint160 & uCurrency)
{
return getRippleStateIndex (RippleAddress::createAccountID (uiA), RippleAddress::createAccountID (uiB), uCurrency);
}
SLE::pointer getRippleState (uint256 const & uNode);
SLE::pointer getRippleState (const RippleAddress & naA, const RippleAddress & naB, const uint160 & uCurrency)
{
return getRippleState (getRippleStateIndex (naA, naB, uCurrency));
}
SLE::pointer getRippleState (const uint160 & uiA, const uint160 & uiB, const uint160 & uCurrency)
{
return getRippleState (getRippleStateIndex (RippleAddress::createAccountID (uiA), RippleAddress::createAccountID (uiB), uCurrency));
}
uint32 getReferenceFeeUnits ()
{
if (!mBaseFee) updateFees ();
return mReferenceFeeUnits;
}
uint64 getBaseFee ()
{
if (!mBaseFee) updateFees ();
return mBaseFee;
}
uint64 getReserve (int increments)
{
if (!mBaseFee) updateFees ();
return scaleFeeBase (static_cast<uint64> (increments) * mReserveIncrement + mReserveBase);
}
uint64 getReserveInc ()
{
if (!mBaseFee) updateFees ();
return mReserveIncrement;
}
uint64 scaleFeeBase (uint64 fee);
uint64 scaleFeeLoad (uint64 fee, bool bAdmin);
static std::set<uint32> getPendingSaves();
Json::Value getJson (int options);
void addJson (Json::Value&, int options);
bool walkLedger ();
bool assertSane ();
protected:
SLE::pointer getASNode (LedgerStateParms & parms, uint256 const & nodeID, LedgerEntryType let);
// returned SLE is immutable
SLE::pointer getASNodeI (uint256 const & nodeID, LedgerEntryType let);
void saveValidatedLedgerAsync(Job&, bool current)
{
saveValidatedLedger(current);
}
void saveValidatedLedger (bool current);
void updateFees ();
private:
void initializeFees ();
private:
// The basic Ledger structure, can be opened, closed, or synching
uint256 mHash;
uint256 mParentHash;
uint256 mTransHash;
uint256 mAccountHash;
uint64 mTotCoins;
uint32 mLedgerSeq;
uint32 mCloseTime; // when this ledger closed
uint32 mParentCloseTime; // when the previous ledger closed
int mCloseResolution; // the resolution for this ledger close time (2-120 seconds)
uint32 mCloseFlags; // flags indicating how this ledger close took place
bool mClosed, mValidated, mValidHash, mAccepted, mImmutable;
uint32 mReferenceFeeUnits; // Fee units for the reference transaction
uint32 mReserveBase, mReserveIncrement; // Reserve basse and increment in fee units
uint64 mBaseFee; // Ripple cost of the reference transaction
SHAMap::pointer mTransactionMap;
SHAMap::pointer mAccountStateMap;
typedef RippleMutex StaticLockType;
typedef StaticLockType::ScopedLockType StaticScopedLockType;
// ledgers not fully saved, validated ledger present but DB may not be correct yet
static StaticLockType sPendingSaveLock;
static std::set<uint32> sPendingSaves;
};
inline LedgerStateParms operator| (const LedgerStateParms& l1, const LedgerStateParms& l2)
{
return static_cast<LedgerStateParms> (static_cast<int> (l1) | static_cast<int> (l2));
}
inline LedgerStateParms operator& (const LedgerStateParms& l1, const LedgerStateParms& l2)
{
return static_cast<LedgerStateParms> (static_cast<int> (l1) & static_cast<int> (l2));
}
#endif

View File

@@ -0,0 +1,870 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
#define MIN_VALIDATION_RATIO 150 // 150/256ths of validations of previous ledger
#define MAX_LEDGER_GAP 100 // Don't catch up more than 100 ledgers (cannot exceed 256)
SETUP_LOG (LedgerMaster)
uint32 LedgerMaster::getCurrentLedgerIndex ()
{
return mCurrentLedger->getLedgerSeq ();
}
Ledger::ref LedgerMaster::getCurrentSnapshot ()
{
if (!mCurrentSnapshot || (mCurrentSnapshot->getHash () != mCurrentLedger->getHash ()))
mCurrentSnapshot = boost::make_shared<Ledger> (boost::ref (*mCurrentLedger), false);
assert (mCurrentSnapshot->isImmutable ());
return mCurrentSnapshot;
}
int LedgerMaster::getPublishedLedgerAge ()
{
ScopedLockType ml (mLock, __FILE__, __LINE__);
if (!mPubLedger)
{
WriteLog (lsDEBUG, LedgerMaster) << "No published ledger";
return 999999;
}
int64 ret = getApp().getOPs ().getCloseTimeNC ();
ret -= static_cast<int64> (mPubLedger->getCloseTimeNC ());
ret = std::max (0LL, ret);
WriteLog (lsTRACE, LedgerMaster) << "Published ledger age is " << ret;
return static_cast<int> (ret);
}
int LedgerMaster::getValidatedLedgerAge ()
{
ScopedLockType ml (mLock, __FILE__, __LINE__);
if (!mValidLedger)
{
WriteLog (lsDEBUG, LedgerMaster) << "No validated ledger";
return 999999;
}
int64 ret = getApp().getOPs ().getCloseTimeNC ();
ret -= static_cast<int64> (mValidLedger->getCloseTimeNC ());
ret = std::max (0LL, ret);
WriteLog (lsTRACE, LedgerMaster) << "Validated ledger age is " << ret;
return static_cast<int> (ret);
}
bool LedgerMaster::isCaughtUp(std::string& reason)
{
if (getPublishedLedgerAge() > 180)
{
reason = "No recently-published ledger";
return false;
}
ScopedLockType ml (mLock, __FILE__, __LINE__);
if (!mValidLedger || !mPubLedger)
{
reason = "No published ledger";
return false;
}
if (mValidLedger->getLedgerSeq() > (mPubLedger->getLedgerSeq() + 3))
{
reason = "Published ledger lags validated ledger";
return false;
}
return true;
}
void LedgerMaster::addHeldTransaction (Transaction::ref transaction)
{
// returns true if transaction was added
ScopedLockType ml (mLock, __FILE__, __LINE__);
mHeldTransactions.push_back (transaction->getSTransaction ());
}
void LedgerMaster::pushLedger (Ledger::pointer newLedger)
{
// Caller should already have properly assembled this ledger into "ready-to-close" form --
// all candidate transactions must already be applied
WriteLog (lsINFO, LedgerMaster) << "PushLedger: " << newLedger->getHash ();
{
ScopedLockType ml (mLock, __FILE__, __LINE__);
if (mClosedLedger)
{
mClosedLedger->setClosed ();
WriteLog (lsTRACE, LedgerMaster) << "Finalizes: " << mClosedLedger->getHash ();
}
mClosedLedger = mCurrentLedger;
mCurrentLedger = newLedger;
mEngine.setLedger (newLedger);
}
if (getConfig().RUN_STANDALONE)
{
setFullLedger(newLedger, true, false);
tryAdvance();
}
else
checkAccept(newLedger);
}
void LedgerMaster::pushLedger (Ledger::pointer newLCL, Ledger::pointer newOL)
{
assert (newLCL->isClosed () && newLCL->isAccepted ());
assert (!newOL->isClosed () && !newOL->isAccepted ());
{
ScopedLockType ml (mLock, __FILE__, __LINE__);
mClosedLedger = newLCL;
mCurrentLedger = newOL;
mEngine.setLedger (newOL);
}
if (getConfig().RUN_STANDALONE)
{
setFullLedger(newLCL, true, false);
tryAdvance();
}
else
{
mLedgerHistory.builtLedger (newLCL);
checkAccept (newLCL);
}
}
void LedgerMaster::switchLedgers (Ledger::pointer lastClosed, Ledger::pointer current)
{
assert (lastClosed && current);
{
ScopedLockType ml (mLock, __FILE__, __LINE__);
mClosedLedger = lastClosed;
mClosedLedger->setClosed ();
mClosedLedger->setAccepted ();
mCurrentLedger = current;
assert (!mCurrentLedger->isClosed ());
mEngine.setLedger (mCurrentLedger);
}
checkAccept (lastClosed);
}
void LedgerMaster::storeLedger (Ledger::pointer ledger)
{
mLedgerHistory.addLedger (ledger);
}
void LedgerMaster::forceValid (Ledger::pointer ledger)
{
ledger->setValidated();
setFullLedger(ledger, true, false);
}
Ledger::pointer LedgerMaster::closeLedger (bool recover)
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
Ledger::pointer closingLedger = mCurrentLedger;
if (recover)
{
int recovers = 0;
for (CanonicalTXSet::iterator it = mHeldTransactions.begin (), end = mHeldTransactions.end (); it != end; ++it)
{
try
{
TransactionEngineParams tepFlags = tapOPEN_LEDGER;
if (getApp().getHashRouter ().addSuppressionPeer (it->first.getTXID (), SF_SIGGOOD))
tepFlags = static_cast<TransactionEngineParams> (tepFlags | tapNO_CHECK_SIGN);
bool didApply;
mEngine.applyTransaction (*it->second, tepFlags, didApply);
if (didApply)
++recovers;
}
catch (...)
{
// CHECKME: We got a few of these
WriteLog (lsWARNING, LedgerMaster) << "Held transaction throws";
}
}
CondLog (recovers != 0, lsINFO, LedgerMaster) << "Recovered " << recovers << " held transactions";
// VFALCO TODO recreate the CanonicalTxSet object instead of resetting it
mHeldTransactions.reset (closingLedger->getHash ());
}
mCurrentLedger = boost::make_shared<Ledger> (boost::ref (*closingLedger), true);
mEngine.setLedger (mCurrentLedger);
return Ledger::pointer (new Ledger (*closingLedger, true));
}
TER LedgerMaster::doTransaction (SerializedTransaction::ref txn, TransactionEngineParams params, bool& didApply)
{
Ledger::pointer ledger;
TER result;
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
result = mEngine.applyTransaction (*txn, params, didApply);
ledger = mEngine.getLedger ();
}
if (didApply)
getApp().getOPs ().pubProposedTransaction (ledger, txn, result);
return result;
}
bool LedgerMaster::haveLedgerRange (uint32 from, uint32 to)
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
uint32 prevMissing = mCompleteLedgers.prevMissing (to + 1);
return (prevMissing == RangeSet::absent) || (prevMissing < from);
}
bool LedgerMaster::haveLedger (uint32 seq)
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
return mCompleteLedgers.hasValue (seq);
}
bool LedgerMaster::getFullValidatedRange (uint32& minVal, uint32& maxVal)
{ // Ledgers we have all the nodes for
ScopedLockType sl (mLock, __FILE__, __LINE__);
if (!mPubLedger)
return false;
maxVal = mPubLedger->getLedgerSeq ();
if (maxVal == 0)
return false;
minVal = mCompleteLedgers.prevMissing (maxVal);
if (minVal == RangeSet::absent)
minVal = maxVal;
else
++minVal;
return true;
}
bool LedgerMaster::getValidatedRange (uint32& minVal, uint32& maxVal)
{ // Ledgers we have all the nodes for and are indexed
ScopedLockType sl (mLock, __FILE__, __LINE__);
if (!mPubLedger)
return false;
maxVal = mPubLedger->getLedgerSeq ();
if (maxVal == 0)
return false;
minVal = mCompleteLedgers.prevMissing (maxVal);
if (minVal == RangeSet::absent)
minVal = maxVal;
else
++minVal;
// Remove from the validated range any ledger sequences that may not be
// fully updated in the database yet
std::set<uint32> sPendingSaves = Ledger::getPendingSaves();
if (!sPendingSaves.empty() && ((minVal != 0) || (maxVal != 0)))
{
// Ensure we shrink the tips as much as possible
// If we have 7-9 and 8,9 are invalid, we don't want to see the 8 and shrink to just 9
// because then we'll have nothing when we could have 7.
while (sPendingSaves.count(maxVal) > 0)
--maxVal;
while (sPendingSaves.count(minVal) > 0)
++minVal;
// Best effort for remaining exclusions
BOOST_FOREACH(uint32 v, sPendingSaves)
{
if ((v >= minVal) && (v <= maxVal))
{
if (v > ((minVal + maxVal) / 2))
maxVal = v - 1;
else
minVal = v + 1;
}
}
if (minVal > maxVal)
minVal = maxVal = 0;
}
return true;
}
void LedgerMaster::tryFill (Ledger::pointer ledger)
{
uint32 seq = ledger->getLedgerSeq ();
uint256 prevHash = ledger->getParentHash ();
std::map< uint32, std::pair<uint256, uint256> > ledgerHashes;
uint32 minHas = ledger->getLedgerSeq ();
uint32 maxHas = ledger->getLedgerSeq ();
while (seq > 0)
{
{
ScopedLockType ml (mLock, __FILE__, __LINE__);
minHas = seq;
--seq;
if (mCompleteLedgers.hasValue (seq))
break;
}
std::map< uint32, std::pair<uint256, uint256> >::iterator it = ledgerHashes.find (seq);
if (it == ledgerHashes.end ())
{
if (getApp().isShutdown ())
return;
{
ScopedLockType ml (mLock, __FILE__, __LINE__);
mCompleteLedgers.setRange (minHas, maxHas);
}
maxHas = minHas;
ledgerHashes = Ledger::getHashesByIndex ((seq < 500) ? 0 : (seq - 499), seq);
it = ledgerHashes.find (seq);
if (it == ledgerHashes.end ())
break;
}
if (it->second.first != prevHash)
break;
prevHash = it->second.second;
}
{
ScopedLockType ml (mLock, __FILE__, __LINE__);
mCompleteLedgers.setRange (minHas, maxHas);
mFillInProgress = 0;
tryAdvance();
}
}
void LedgerMaster::getFetchPack (Ledger::ref nextLedger)
{
Peer::pointer target;
int count = 0;
std::vector<Peer::pointer> peerList = getApp().getPeers ().getPeerVector ();
BOOST_FOREACH (const Peer::pointer & peer, peerList)
{
if (peer->hasRange (nextLedger->getLedgerSeq() - 1, nextLedger->getLedgerSeq()))
{
if (count++ == 0)
target = peer;
else if ((rand () % count) == 0)
target = peer;
}
}
if (target)
{
protocol::TMGetObjectByHash tmBH;
tmBH.set_query (true);
tmBH.set_type (protocol::TMGetObjectByHash::otFETCH_PACK);
tmBH.set_ledgerhash (nextLedger->getHash().begin (), 32);
PackedMessage::pointer packet = boost::make_shared<PackedMessage> (tmBH, protocol::mtGET_OBJECTS);
target->sendPacket (packet, false);
WriteLog (lsTRACE, LedgerMaster) << "Requested fetch pack for " << nextLedger->getLedgerSeq() - 1;
}
else
WriteLog (lsDEBUG, LedgerMaster) << "No peer for fetch pack";
}
bool LedgerMaster::shouldAcquire (uint32 currentLedger, uint32 ledgerHistory, uint32 candidateLedger)
{
bool ret;
if (candidateLedger >= currentLedger)
ret = true;
else
ret = (currentLedger - candidateLedger) <= ledgerHistory;
WriteLog (lsTRACE, LedgerMaster) << "Missing ledger " << candidateLedger << (ret ? " should" : " should NOT") << " be acquired";
return ret;
}
void LedgerMaster::fixMismatch (Ledger::ref ledger)
{
int invalidate = 0;
for (uint32 lSeq = ledger->getLedgerSeq () - 1; lSeq > 0; --lSeq)
if (mCompleteLedgers.hasValue (lSeq))
{
uint256 hash = ledger->getLedgerHash (lSeq);
if (hash.isNonZero ())
{
// try to close the seam
Ledger::pointer otherLedger = getLedgerBySeq (lSeq);
if (otherLedger && (otherLedger->getHash () == hash))
{
// we closed the seam
CondLog (invalidate != 0, lsWARNING, LedgerMaster) << "Match at " << lSeq << ", " <<
invalidate << " prior ledgers invalidated";
return;
}
}
mCompleteLedgers.clearValue (lSeq);
++invalidate;
}
// all prior ledgers invalidated
CondLog (invalidate != 0, lsWARNING, LedgerMaster) << "All " << invalidate << " prior ledgers invalidated";
}
void LedgerMaster::setFullLedger (Ledger::pointer ledger, bool isSynchronous, bool isCurrent)
{
// A new ledger has been accepted as part of the trusted chain
WriteLog (lsDEBUG, LedgerMaster) << "Ledger " << ledger->getLedgerSeq () << " accepted :" << ledger->getHash ();
assert (ledger->peekAccountStateMap ()->getHash ().isNonZero ());
ledger->setValidated();
mLedgerHistory.addLedger(ledger);
ScopedLockType ml (mLock, __FILE__, __LINE__);
mCompleteLedgers.setValue (ledger->getLedgerSeq ());
ledger->pendSaveValidated (isSynchronous, isCurrent);
if (!mValidLedger || (ledger->getLedgerSeq() > mValidLedger->getLedgerSeq()))
mValidLedger = ledger;
if (!mPubLedger)
mPubLedger = ledger;
if ((ledger->getLedgerSeq () != 0) && mCompleteLedgers.hasValue (ledger->getLedgerSeq () - 1))
{
// we think we have the previous ledger, double check
Ledger::pointer prevLedger = getLedgerBySeq (ledger->getLedgerSeq () - 1);
if (!prevLedger || (prevLedger->getHash () != ledger->getParentHash ()))
{
WriteLog (lsWARNING, LedgerMaster) << "Acquired ledger invalidates previous ledger: " <<
(prevLedger ? "hashMismatch" : "missingLedger");
fixMismatch (ledger);
}
}
}
void LedgerMaster::checkAccept (uint256 const& hash)
{
Ledger::pointer ledger = mLedgerHistory.getLedgerByHash (hash);
if (!ledger)
{
InboundLedger::pointer l = getApp().getInboundLedgers().findCreate(hash, 0, false);
if (l->isComplete() && !l->isFailed())
ledger = l->getLedger();
else
{
WriteLog (lsDEBUG, LedgerMaster) << "checkAccept triggers acquire " << hash.GetHex();
}
}
if (ledger)
checkAccept (ledger);
}
void LedgerMaster::checkAccept (Ledger::ref ledger)
{
// Can we advance the last fully-validated ledger? If so, can we publish?
ScopedLockType ml (mLock, __FILE__, __LINE__);
if (mValidLedger && (ledger->getLedgerSeq() <= mValidLedger->getLedgerSeq ()))
return;
int minVal = mMinValidations;
if (mLastValidateHash.isNonZero ())
{
int val = getApp().getValidations ().getTrustedValidationCount (mLastValidateHash);
val *= MIN_VALIDATION_RATIO;
val /= 256;
if (val > minVal)
minVal = val;
}
if (getConfig ().RUN_STANDALONE)
minVal = 0;
int tvc = getApp().getValidations().getTrustedValidationCount(ledger->getHash());
if (tvc < minVal) // nothing we can do
{
WriteLog (lsTRACE, LedgerMaster) << "Only " << tvc << " validations for " << ledger->getHash();
return;
}
WriteLog (lsINFO, LedgerMaster) << "Advancing accepted ledger to " << ledger->getLedgerSeq() << " with >= " << minVal << " validations";
mLastValidateHash = ledger->getHash();
mLastValidateSeq = ledger->getLedgerSeq();
ledger->setValidated();
mValidLedger = ledger;
if (!mPubLedger)
{
ledger->pendSaveValidated(true, true);
mPubLedger = ledger;
}
uint64 fee, fee2, ref;
ref = getApp().getFeeTrack().getLoadBase();
int count = getApp().getValidations().getFeeAverage(ledger->getHash(), ref, fee);
int count2 = getApp().getValidations().getFeeAverage(ledger->getParentHash(), ref, fee2);
if ((count + count2) == 0)
getApp().getFeeTrack().setRemoteFee(ref);
else
getApp().getFeeTrack().setRemoteFee(((fee * count) + (fee2 * count2)) / (count + count2));
tryAdvance ();
}
// Try to publish ledgers, acquire missing ledgers
void LedgerMaster::advanceThread()
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
assert (mValidLedger && mAdvanceThread);
WriteLog (lsTRACE, LedgerMaster) << "advanceThread<";
do
{
mAdvanceWork = false; // If there's work to do, we'll make progress
bool progress = false;
std::list<Ledger::pointer> pubLedgers = findNewLedgersToPublish (sl);
if (pubLedgers.empty())
{
if (!getConfig().RUN_STANDALONE && !getApp().getFeeTrack().isLoadedLocal() &&
(getApp().getJobQueue().getJobCount(jtPUBOLDLEDGER) < 10) &&
(mValidLedger->getLedgerSeq() == mPubLedger->getLedgerSeq()))
{ // We are in sync, so can acquire
uint32 missing = mCompleteLedgers.prevMissing(mPubLedger->getLedgerSeq());
WriteLog (lsTRACE, LedgerMaster) << "tryAdvance discovered missing " << missing;
if ((missing != RangeSet::absent) && (missing > 0) &&
shouldAcquire(mValidLedger->getLedgerSeq(), getConfig().LEDGER_HISTORY, missing) &&
((mFillInProgress == 0) || (missing > mFillInProgress)))
{
WriteLog (lsTRACE, LedgerMaster) << "advanceThread should acquire";
sl.unlock();
Ledger::pointer nextLedger = mLedgerHistory.getLedgerBySeq(missing + 1);
if (nextLedger)
{
assert (nextLedger->getLedgerSeq() == (missing + 1));
Ledger::pointer ledger = getLedgerByHash(nextLedger->getParentHash());
if (!ledger)
{
if (!getApp().getInboundLedgers().isFailure(nextLedger->getParentHash()))
{
InboundLedger::pointer acq =
getApp().getInboundLedgers().findCreate(nextLedger->getParentHash(),
nextLedger->getLedgerSeq() - 1, false);
if (acq->isComplete() && !acq->isFailed())
ledger = acq->getLedger();
else if ((missing > 40000) && getApp().getOPs().shouldFetchPack(missing))
{
WriteLog (lsTRACE, LedgerMaster) << "tryAdvance want fetch pack " << missing;
getFetchPack(nextLedger);
}
else
WriteLog (lsTRACE, LedgerMaster) << "tryAdvance no fetch pack for " << missing;
}
else
WriteLog (lsDEBUG, LedgerMaster) << "tryAdvance found failed acquire";
}
if (ledger)
{
assert(ledger->getLedgerSeq() == missing);
WriteLog (lsTRACE, LedgerMaster) << "tryAdvance acquired " << ledger->getLedgerSeq();
setFullLedger(ledger, false, false);
if ((mFillInProgress == 0) && (Ledger::getHashByIndex(ledger->getLedgerSeq() - 1) == ledger->getParentHash()))
{ // Previous ledger is in DB
sl.lock(__FILE__, __LINE__);
mFillInProgress = ledger->getLedgerSeq();
getApp().getJobQueue().addJob(jtADVANCE, "tryFill", BIND_TYPE (&LedgerMaster::tryFill, this, ledger));
sl.unlock();
}
progress = true;
}
else
{
for (int i = 0; i < getConfig().getSize(siLedgerFetch); ++i)
{
uint32 seq = missing - i;
uint256 hash = nextLedger->getLedgerHash(seq);
if (hash.isNonZero())
getApp().getInboundLedgers().findCreate(hash, seq, false);
}
}
}
else
{
WriteLog (lsFATAL, LedgerMaster) << "Unable to find ledger following prevMissing " << missing;
WriteLog (lsFATAL, LedgerMaster) << "Pub:" << mPubLedger->getLedgerSeq() << " Val:" << mValidLedger->getLedgerSeq();
WriteLog (lsFATAL, LedgerMaster) << "Ledgers: " << getApp().getLedgerMaster().getCompleteLedgers();
mCompleteLedgers.clearValue (missing + 1);
progress = true;
}
sl.lock(__FILE__, __LINE__);
if (mValidLedger->getLedgerSeq() != mPubLedger->getLedgerSeq())
{
WriteLog (lsDEBUG, LedgerMaster) << "tryAdvance found last valid changed";
progress = true;
}
}
}
else
WriteLog (lsTRACE, LedgerMaster) << "tryAdvance not fetching history";
}
else
{
WriteLog (lsTRACE, LedgerMaster) << "tryAdvance found " << pubLedgers.size() << " ledgers to publish";
BOOST_FOREACH(Ledger::ref ledger, pubLedgers)
{
sl.unlock();
WriteLog(lsDEBUG, LedgerMaster) << "tryAdvance publishing seq " << ledger->getLedgerSeq();
setFullLedger(ledger, true, true);
getApp().getOPs().pubLedger(ledger);
sl.lock(__FILE__, __LINE__);
mPubLedger = ledger;
progress = true;
}
getApp().getOPs().clearNeedNetworkLedger();
if (!mPathFindThread)
{
mPathFindThread = true;
getApp().getJobQueue ().addJob (jtUPDATE_PF, "updatePaths",
BIND_TYPE (&LedgerMaster::updatePaths, this));
}
}
if (progress)
mAdvanceWork = true;
} while (mAdvanceWork);
mAdvanceThread = false;
WriteLog (lsTRACE, LedgerMaster) << "advanceThread>";
}
std::list<Ledger::pointer> LedgerMaster::findNewLedgersToPublish(ScopedLockType& sl)
{
std::list<Ledger::pointer> ret;
WriteLog (lsTRACE, LedgerMaster) << "findNewLedgersToPublish<";
if (!mPubLedger)
{
WriteLog (lsINFO, LedgerMaster) << "First published ledger will be " << mValidLedger->getLedgerSeq();
ret.push_back (mValidLedger);
}
else if (mValidLedger->getLedgerSeq () > (mPubLedger->getLedgerSeq () + MAX_LEDGER_GAP))
{
WriteLog (lsWARNING, LedgerMaster) << "Gap in validated ledger stream " << mPubLedger->getLedgerSeq () << " - " <<
mValidLedger->getLedgerSeq () - 1;
ret.push_back (mValidLedger);
}
else if (mValidLedger->getLedgerSeq () > mPubLedger->getLedgerSeq ())
{
int acqCount = 0;
uint32 pubSeq = mPubLedger->getLedgerSeq() + 1; // Next sequence to publish
uint32 valSeq = mValidLedger->getLedgerSeq();
Ledger::pointer valLedger = mValidLedger;
sl.unlock();
for (uint32 seq = pubSeq; seq <= valSeq; ++seq)
{
WriteLog (lsTRACE, LedgerMaster) << "Trying to fetch/publish valid ledger " << seq;
Ledger::pointer ledger;
uint256 hash = valLedger->getLedgerHash (seq);
if (seq == valSeq)
{ // We need to publish the ledger we just fully validated
ledger = valLedger;
}
else
{
if (hash.isZero ())
{
WriteLog (lsFATAL, LedgerMaster) << "Ledger: " << valSeq << " does not have hash for " << seq;
assert (false);
}
ledger = mLedgerHistory.getLedgerByHash (hash);
}
if (!ledger && (++acqCount < 4))
{ // We can try to acquire the ledger we need
InboundLedger::pointer acq = getApp().getInboundLedgers ().findCreate (hash, seq, false);
if (!acq->isDone ())
{
nothing ();
}
else if (acq->isComplete () && !acq->isFailed ())
{
ledger = acq->getLedger();
}
else
{
WriteLog (lsWARNING, LedgerMaster) << "Failed to acquire a published ledger";
getApp().getInboundLedgers().dropLedger(hash);
acq = getApp().getInboundLedgers().findCreate(hash, seq, false);
if (acq->isComplete())
{
if (acq->isFailed())
getApp().getInboundLedgers().dropLedger(hash);
else
ledger = acq->getLedger();
}
}
}
if (ledger && (ledger->getLedgerSeq() == pubSeq))
{ // We acquired the next ledger we need to publish
ledger->setValidated();
ret.push_back (ledger);
++pubSeq;
}
}
sl.lock(__FILE__, __LINE__);
}
WriteLog (lsTRACE, LedgerMaster) << "findNewLedgersToPublish> " << ret.size();
return ret;
}
void LedgerMaster::tryAdvance()
{
ScopedLockType ml (mLock, __FILE__, __LINE__);
// Can't advance without at least one fully-valid ledger
mAdvanceWork = true;
if (!mAdvanceThread && mValidLedger)
{
mAdvanceThread = true;
getApp().getJobQueue ().addJob (jtADVANCE, "advanceLedger",
BIND_TYPE (&LedgerMaster::advanceThread, this));
}
}
uint256 LedgerMaster::getLedgerHash(uint32 desiredSeq, Ledger::ref knownGoodLedger)
{ // Get the hash of the valid ledger with a particular sequence, given a subsequent ledger known valid
assert(desiredSeq < knownGoodLedger->getLedgerSeq());
uint256 hash = knownGoodLedger->getLedgerHash(desiredSeq);
if (hash.isZero ())
{ // Not directly in the given ledger
uint32 seq = (desiredSeq + 255) % 256;
assert(seq < desiredSeq);
uint256 i = knownGoodLedger->getLedgerHash(seq);
if (i.isNonZero())
{
Ledger::pointer l = getLedgerByHash(i);
if (l)
{
hash = l->getLedgerHash(desiredSeq);
assert (hash.isNonZero());
}
}
else
assert(false);
}
return hash;
}
void LedgerMaster::updatePaths ()
{
Ledger::pointer lastLedger;
do
{
bool newOnly = true;
{
ScopedLockType ml (mLock, __FILE__, __LINE__);
if (!mPathLedger || (mPathLedger->getLedgerSeq() < mValidLedger->getLedgerSeq()))
{ // We have a new valid ledger since the last full pathfinding
newOnly = false;
mPathLedger = mValidLedger;
lastLedger = mPathLedger;
}
else if (mPathFindNewRequest)
{ // We have a new request but no new ledger
newOnly = true;
lastLedger = boost::make_shared<Ledger> (boost::ref (*mCurrentLedger), false);
}
else
{ // Nothing to do
mPathFindThread = false;
return;
}
mPathFindNewRequest = false;
}
// VFALCO TODO Fix this global variable
PathRequest::updateAll (lastLedger, newOnly);
}
while (1);
}
void LedgerMaster::newPathRequest ()
{
ScopedLockType ml (mLock, __FILE__, __LINE__);
mPathFindNewRequest = true;
if (!mPathFindThread)
{
mPathFindThread = true;
getApp().getJobQueue ().addJob (jtUPDATE_PF, "updatePaths",
BIND_TYPE (&LedgerMaster::updatePaths, this));
}
}
// vim:ts=4

View File

@@ -0,0 +1,239 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
#ifndef RIPPLE_LEDGERMASTER_H
#define RIPPLE_LEDGERMASTER_H
// Tracks the current ledger and any ledgers in the process of closing
// Tracks ledger history
// Tracks held transactions
// VFALCO TODO Rename to Ledgers
// It sounds like this holds all the ledgers...
//
class LedgerMaster : LeakChecked <LedgerMaster>
{
public:
typedef FUNCTION_TYPE <void (Ledger::ref)> callback;
public:
typedef RippleRecursiveMutex LockType;
typedef LockType::ScopedLockType ScopedLockType;
LedgerMaster ()
: mLock (this, "LedgerMaster", __FILE__, __LINE__)
, mHeldTransactions (uint256 ())
, mMinValidations (0)
, mLastValidateSeq (0)
, mAdvanceThread (false)
, mAdvanceWork (false)
, mFillInProgress (0)
, mPathFindThread (false)
, mPathFindNewRequest (false)
{
}
~LedgerMaster ()
{
}
uint32 getCurrentLedgerIndex ();
LockType& peekMutex ()
{
return mLock;
}
// The current ledger is the ledger we believe new transactions should go in
Ledger::ref getCurrentLedger ()
{
return mCurrentLedger;
}
// An immutable snapshot of the current ledger
Ledger::ref getCurrentSnapshot ();
// The finalized ledger is the last closed/accepted ledger
Ledger::ref getClosedLedger ()
{
return mClosedLedger;
}
// The validated ledger is the last fully validated ledger
Ledger::ref getValidatedLedger ()
{
return mValidLedger;
}
// This is the last ledger we published to clients and can lag the validated ledger
Ledger::ref getPublishedLedger ()
{
return mPubLedger;
}
int getPublishedLedgerAge ();
int getValidatedLedgerAge ();
bool isCaughtUp(std::string& reason);
TER doTransaction (SerializedTransaction::ref txn, TransactionEngineParams params, bool& didApply);
int getMinValidations ()
{
return mMinValidations;
}
void setMinValidations (int v)
{
mMinValidations = v;
}
void pushLedger (Ledger::pointer newLedger);
void pushLedger (Ledger::pointer newLCL, Ledger::pointer newOL);
void storeLedger (Ledger::pointer);
void forceValid (Ledger::pointer);
void setFullLedger (Ledger::pointer ledger, bool isSynchronous, bool isCurrent);
void switchLedgers (Ledger::pointer lastClosed, Ledger::pointer newCurrent);
std::string getCompleteLedgers ()
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
return mCompleteLedgers.toString ();
}
Ledger::pointer closeLedger (bool recoverHeldTransactions);
uint256 getHashBySeq (uint32 index)
{
uint256 hash = mLedgerHistory.getLedgerHash (index);
if (hash.isNonZero ())
return hash;
return Ledger::getHashByIndex (index);
}
Ledger::pointer getLedgerBySeq (uint32 index)
{
if (mCurrentLedger && (mCurrentLedger->getLedgerSeq () == index))
return mCurrentLedger;
if (mClosedLedger && (mClosedLedger->getLedgerSeq () == index))
return mClosedLedger;
Ledger::pointer ret = mLedgerHistory.getLedgerBySeq (index);
if (ret)
return ret;
ScopedLockType ml (mLock, __FILE__, __LINE__);
mCompleteLedgers.clearValue (index);
return ret;
}
Ledger::pointer getLedgerByHash (uint256 const& hash)
{
if (hash.isZero ())
return boost::make_shared<Ledger> (boost::ref (*mCurrentLedger), false);
if (mCurrentLedger && (mCurrentLedger->getHash () == hash))
return boost::make_shared<Ledger> (boost::ref (*mCurrentLedger), false);
if (mClosedLedger && (mClosedLedger->getHash () == hash))
return mClosedLedger;
return mLedgerHistory.getLedgerByHash (hash);
}
void setLedgerRangePresent (uint32 minV, uint32 maxV)
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
mCompleteLedgers.setRange (minV, maxV);
}
uint256 getLedgerHash(uint32 desiredSeq, Ledger::ref knownGoodLedger);
void addHeldTransaction (Transaction::ref trans);
void fixMismatch (Ledger::ref ledger);
bool haveLedgerRange (uint32 from, uint32 to);
bool haveLedger (uint32 seq);
bool getValidatedRange (uint32& minVal, uint32& maxVal);
bool getFullValidatedRange (uint32& minVal, uint32& maxVal);
void tune (int size, int age)
{
mLedgerHistory.tune (size, age);
}
void sweep ()
{
mLedgerHistory.sweep ();
}
float getCacheHitRate ()
{
return mLedgerHistory.getCacheHitRate ();
}
void addValidateCallback (callback& c)
{
mOnValidate.push_back (c);
}
void checkAccept (Ledger::ref ledger);
void checkAccept (uint256 const& hash);
void tryAdvance ();
void newPathRequest ();
static bool shouldAcquire (uint32 currentLedgerID, uint32 ledgerHistory, uint32 targetLedger);
private:
std::list<Ledger::pointer> findNewLedgersToPublish(ScopedLockType& sl);
void applyFutureTransactions (uint32 ledgerIndex);
bool isValidTransaction (Transaction::ref trans);
bool isTransactionOnFutureList (Transaction::ref trans);
void getFetchPack (Ledger::ref have);
void tryFill (Ledger::pointer);
void advanceThread ();
void updatePaths ();
private:
LockType mLock;
TransactionEngine mEngine;
Ledger::pointer mCurrentLedger; // The ledger we are currently processiong
Ledger::pointer mCurrentSnapshot; // Snapshot of the current ledger
Ledger::pointer mClosedLedger; // The ledger that most recently closed
Ledger::pointer mValidLedger; // The highest-sequence ledger we have fully accepted
Ledger::pointer mPubLedger; // The last ledger we have published
Ledger::pointer mPathLedger; // The last ledger we did pathfinding against
LedgerHistory mLedgerHistory;
CanonicalTXSet mHeldTransactions;
RangeSet mCompleteLedgers;
int mMinValidations; // The minimum validations to publish a ledger
uint256 mLastValidateHash;
uint32 mLastValidateSeq;
std::list<callback> mOnValidate; // Called when a ledger has enough validations
std::list<Ledger::pointer> mPubLedgers; // List of ledgers to publish
bool mAdvanceThread; // Publish thread is running
bool mAdvanceWork; // Publish thread has work to do
int mFillInProgress;
bool mPathFindThread; // Pathfind thread is running
bool mPathFindNewLedger;
bool mPathFindNewRequest;
};
#endif
// vim:ts=4

View File

@@ -0,0 +1,103 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
LedgerProposal::LedgerProposal (uint256 const& pLgr, uint32 seq, uint256 const& tx, uint32 closeTime,
const RippleAddress& naPeerPublic, uint256 const& suppression) :
mPreviousLedger (pLgr), mCurrentHash (tx), mSuppression (suppression), mCloseTime (closeTime),
mProposeSeq (seq), mPublicKey (naPeerPublic)
{
// XXX Validate key.
// if (!mKey->SetPubKey(pubKey))
// throw std::runtime_error("Invalid public key in proposal");
mPeerID = mPublicKey.getNodeID ();
mTime = boost::posix_time::second_clock::universal_time ();
}
LedgerProposal::LedgerProposal (const RippleAddress& naPub, const RippleAddress& naPriv,
uint256 const& prevLgr, uint256 const& position, uint32 closeTime) :
mPreviousLedger (prevLgr), mCurrentHash (position), mCloseTime (closeTime), mProposeSeq (0),
mPublicKey (naPub), mPrivateKey (naPriv)
{
mPeerID = mPublicKey.getNodeID ();
mTime = boost::posix_time::second_clock::universal_time ();
}
LedgerProposal::LedgerProposal (uint256 const& prevLgr, uint256 const& position, uint32 closeTime) :
mPreviousLedger (prevLgr), mCurrentHash (position), mCloseTime (closeTime), mProposeSeq (0)
{
mTime = boost::posix_time::second_clock::universal_time ();
}
uint256 LedgerProposal::getSigningHash () const
{
Serializer s ((32 + 32 + 32 + 256 + 256) / 8);
s.add32 (getConfig ().SIGN_PROPOSAL);
s.add32 (mProposeSeq);
s.add32 (mCloseTime);
s.add256 (mPreviousLedger);
s.add256 (mCurrentHash);
return s.getSHA512Half ();
}
bool LedgerProposal::checkSign (const std::string& signature, uint256 const& signingHash)
{
return mPublicKey.verifyNodePublic (signingHash, signature);
}
bool LedgerProposal::changePosition (uint256 const& newPosition, uint32 closeTime)
{
if (mProposeSeq == seqLeave)
return false;
mCurrentHash = newPosition;
mCloseTime = closeTime;
mTime = boost::posix_time::second_clock::universal_time ();
++mProposeSeq;
return true;
}
void LedgerProposal::bowOut ()
{
mTime = boost::posix_time::second_clock::universal_time ();
mProposeSeq = seqLeave;
}
Blob LedgerProposal::sign (void)
{
Blob ret;
mPrivateKey.signNodePrivate (getSigningHash (), ret);
// XXX If this can fail, find out sooner.
// if (!mPrivateKey.signNodePrivate(getSigningHash(), ret))
// throw std::runtime_error("unable to sign proposal");
return ret;
}
Json::Value LedgerProposal::getJson () const
{
Json::Value ret = Json::objectValue;
ret["previous_ledger"] = mPreviousLedger.GetHex ();
if (mProposeSeq != seqLeave)
{
ret["transaction_hash"] = mCurrentHash.GetHex ();
ret["propose_seq"] = mProposeSeq;
}
ret["close_time"] = mCloseTime;
if (mPublicKey.isValid ())
ret["peer_id"] = mPublicKey.humanNodePublic ();
return ret;
}
// vim:ts=4

View File

@@ -0,0 +1,125 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
#ifndef __PROPOSELEDGER__
#define __PROPOSELEDGER__
class LedgerProposal
: public CountedObject <LedgerProposal>
{
public:
static char const* getCountedObjectName () { return "LedgerProposal"; }
static const uint32 seqLeave = 0xffffffff; // leaving the consensus process
typedef boost::shared_ptr<LedgerProposal> pointer;
typedef const pointer& ref;
// proposal from peer
LedgerProposal (uint256 const & prevLgr, uint32 proposeSeq, uint256 const & propose,
uint32 closeTime, const RippleAddress & naPeerPublic, uint256 const & suppress);
// our first proposal
LedgerProposal (const RippleAddress & pubKey, const RippleAddress & privKey,
uint256 const & prevLedger, uint256 const & position, uint32 closeTime);
// an unsigned "dummy" proposal for nodes not validating
LedgerProposal (uint256 const & prevLedger, uint256 const & position, uint32 closeTime);
uint256 getSigningHash () const;
bool checkSign (const std::string & signature, uint256 const & signingHash);
bool checkSign (const std::string & signature)
{
return checkSign (signature, getSigningHash ());
}
bool checkSign ()
{
return checkSign (mSignature, getSigningHash ());
}
const uint160& getPeerID () const
{
return mPeerID;
}
uint256 const& getCurrentHash () const
{
return mCurrentHash;
}
uint256 const& getPrevLedger () const
{
return mPreviousLedger;
}
uint256 const& getHashRouter () const
{
return mSuppression;
}
uint32 getProposeSeq () const
{
return mProposeSeq;
}
uint32 getCloseTime () const
{
return mCloseTime;
}
const RippleAddress& peekPublic () const
{
return mPublicKey;
}
Blob getPubKey () const
{
return mPublicKey.getNodePublic ();
}
Blob sign ();
void setPrevLedger (uint256 const & prevLedger)
{
mPreviousLedger = prevLedger;
}
void setSignature (const std::string & signature)
{
mSignature = signature;
}
bool hasSignature ()
{
return !mSignature.empty ();
}
bool isPrevLedger (uint256 const & pl)
{
return mPreviousLedger == pl;
}
bool isBowOut ()
{
return mProposeSeq == seqLeave;
}
const boost::posix_time::ptime getCreateTime ()
{
return mTime;
}
bool isStale (boost::posix_time::ptime cutoff)
{
return mTime <= cutoff;
}
bool changePosition (uint256 const & newPosition, uint32 newCloseTime);
void bowOut ();
Json::Value getJson () const;
private:
uint256 mPreviousLedger, mCurrentHash, mSuppression;
uint32 mCloseTime, mProposeSeq;
uint160 mPeerID;
RippleAddress mPublicKey;
RippleAddress mPrivateKey; // If ours
std::string mSignature; // set only if needed
boost::posix_time::ptime mTime;
};
#endif
// vim:ts=4

View File

@@ -0,0 +1,156 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
// VFALCO Should rename ContinuousLedgerTiming to LedgerTiming
struct LedgerTiming; // for Log
SETUP_LOG (LedgerTiming)
// NOTE: First and last times must be repeated
int ContinuousLedgerTiming::LedgerTimeResolution[] = { 10, 10, 20, 30, 60, 90, 120, 120 };
// Called when a ledger is open and no close is in progress -- when a transaction is received and no close
// is in process, or when a close completes. Returns the number of seconds the ledger should be be open.
bool ContinuousLedgerTiming::shouldClose (
bool anyTransactions,
int previousProposers, // proposers in the last closing
int proposersClosed, // proposers who have currently closed this ledgers
int proposersValidated, // proposers who have validated the last closed ledger
int previousMSeconds, // milliseconds the previous ledger took to reach consensus
int currentMSeconds, // milliseconds since the previous ledger closed
int openMSeconds, // milliseconds since the previous LCL was computed
int idleInterval) // network's desired idle interval
{
if ((previousMSeconds < -1000) || (previousMSeconds > 600000) ||
(currentMSeconds < -1000) || (currentMSeconds > 600000))
{
WriteLog (lsWARNING, LedgerTiming) <<
boost::str (boost::format ("CLC::shouldClose range Trans=%s, Prop: %d/%d, Secs: %d (last:%d)")
% (anyTransactions ? "yes" : "no") % previousProposers % proposersClosed
% currentMSeconds % previousMSeconds);
return true;
}
if (!anyTransactions)
{
// no transactions so far this interval
if (proposersClosed > (previousProposers / 4)) // did we miss a transaction?
{
WriteLog (lsTRACE, LedgerTiming) << "no transactions, many proposers: now (" << proposersClosed << " closed, "
<< previousProposers << " before)";
return true;
}
#if 0 // This false triggers on the genesis ledger
if (previousMSeconds > (1000 * (LEDGER_IDLE_INTERVAL + 2))) // the last ledger was very slow to close
{
WriteLog (lsTRACE, LedgerTiming) << "was slow to converge (p=" << (previousMSeconds) << ")";
if (previousMSeconds < 2000)
return previousMSeconds;
return previousMSeconds - 1000;
}
#endif
return currentMSeconds >= (idleInterval * 1000); // normal idle
}
if ((openMSeconds < LEDGER_MIN_CLOSE) && ((proposersClosed + proposersValidated) < (previousProposers / 2 )))
{
WriteLog (lsDEBUG, LedgerTiming) << "Must wait minimum time before closing";
return false;
}
if ((currentMSeconds < previousMSeconds) && ((proposersClosed + proposersValidated) < previousProposers))
{
WriteLog (lsDEBUG, LedgerTiming) << "We are waiting for more closes/validations";
return false;
}
return true; // this ledger should close now
}
// Returns whether we have a consensus or not. If so, we expect all honest nodes
// to already have everything they need to accept a consensus. Our vote is 'locked in'.
bool ContinuousLedgerTiming::haveConsensus (
int previousProposers, // proposers in the last closing (not including us)
int currentProposers, // proposers in this closing so far (not including us)
int currentAgree, // proposers who agree with us
int currentFinished, // proposers who have validated a ledger after this one
int previousAgreeTime, // how long it took to agree on the last ledger
int currentAgreeTime, // how long we've been trying to agree
bool forReal, // deciding whether to stop consensus process
bool& failed) // we can't reach a consensus
{
WriteLog (lsTRACE, LedgerTiming) << boost::str (boost::format ("CLC::haveConsensus: prop=%d/%d agree=%d validated=%d time=%d/%d%s") %
currentProposers % previousProposers % currentAgree % currentFinished % currentAgreeTime % previousAgreeTime %
(forReal ? "" : "X"));
if (currentAgreeTime <= LEDGER_MIN_CONSENSUS)
return false;
if (currentProposers < (previousProposers * 3 / 4))
{
// Less than 3/4 of the last ledger's proposers are present, we may need more time
if (currentAgreeTime < (previousAgreeTime + LEDGER_MIN_CONSENSUS))
{
CondLog (forReal, lsTRACE, LedgerTiming) << "too fast, not enough proposers";
return false;
}
}
// If 80% of current proposers (plus us) agree on a set, we have consensus
if (((currentAgree * 100 + 100) / (currentProposers + 1)) > 80)
{
CondLog (forReal, lsINFO, LedgerTiming) << "normal consensus";
failed = false;
return true;
}
// If 80% of the nodes on your UNL have moved on, you should declare consensus
if (((currentFinished * 100) / (currentProposers + 1)) > 80)
{
CondLog (forReal, lsWARNING, LedgerTiming) << "We see no consensus, but 80% of nodes have moved on";
failed = true;
return true;
}
// no consensus yet
CondLog (forReal, lsTRACE, LedgerTiming) << "no consensus";
return false;
}
int ContinuousLedgerTiming::getNextLedgerTimeResolution (int previousResolution, bool previousAgree, int ledgerSeq)
{
assert (ledgerSeq);
if ((!previousAgree) && ((ledgerSeq % LEDGER_RES_DECREASE) == 0))
{
// reduce resolution
int i = 1;
while (LedgerTimeResolution[i] != previousResolution)
++i;
return LedgerTimeResolution[i + 1];
}
if ((previousAgree) && ((ledgerSeq % LEDGER_RES_INCREASE) == 0))
{
// increase resolution
int i = 1;
while (LedgerTimeResolution[i] != previousResolution)
++i;
return LedgerTimeResolution[i - 1];
}
return previousResolution;
}

View File

@@ -0,0 +1,87 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
#ifndef __LEDGERTIMING__
#define __LEDGERTIMING__
// The number of seconds a ledger may remain idle before closing
# define LEDGER_IDLE_INTERVAL 15
// The number of seconds a validation remains current after its ledger's close time
// This is a safety to protect against very old validations and the time it takes to adjust
// the close time accuracy window
# define LEDGER_VAL_INTERVAL 300
// The number of seconds before a close time that we consider a validation acceptable
// This protects against extreme clock errors
# define LEDGER_EARLY_INTERVAL 180
// The number of milliseconds we wait minimum to ensure participation
# define LEDGER_MIN_CONSENSUS 2000
// The number of milliseconds we wait minimum to ensure others have computed the LCL
# define LEDGER_MIN_CLOSE 2000
// Initial resolution of ledger close time
# define LEDGER_TIME_ACCURACY 30
// How often to increase resolution
# define LEDGER_RES_INCREASE 8
// How often to decrease resolution
# define LEDGER_RES_DECREASE 1
// How often we check state or change positions (in milliseconds)
# define LEDGER_GRANULARITY 1000
// The percentage of active trusted validators that must be able to
// keep up with the network or we consider the network overloaded
# define LEDGER_NET_RATIO 70
// How long we consider a proposal fresh
# define PROPOSE_FRESHNESS 20
// How often we force generating a new proposal to keep ours fresh
# define PROPOSE_INTERVAL 12
// Avalanche tuning
# define AV_INIT_CONSENSUS_PCT 50 // percentage of nodes on our UNL that must vote yes
# define AV_MID_CONSENSUS_TIME 50 // percentage of previous close time before we advance
# define AV_MID_CONSENSUS_PCT 65 // percentage of nodes that most vote yes after advancing
# define AV_LATE_CONSENSUS_TIME 85 // percentage of previous close time before we advance
# define AV_LATE_CONSENSUS_PCT 70 // percentage of nodes that most vote yes after advancing
# define AV_STUCK_CONSENSUS_TIME 200
# define AV_STUCK_CONSENSUS_PCT 95
# define AV_CT_CONSENSUS_PCT 75
class ContinuousLedgerTiming
{
public:
static int LedgerTimeResolution[];
// Returns the number of seconds the ledger was or should be open
// Call when a consensus is reached and when any transaction is relayed to be added
static bool shouldClose (
bool anyTransactions,
int previousProposers, int proposersClosed, int proposerersValidated,
int previousMSeconds, int currentMSeconds, int openMSeconds,
int idleInterval);
static bool haveConsensus (
int previousProposers, int currentProposers,
int currentAgree, int currentClosed,
int previousAgreeTime, int currentAgreeTime,
bool forReal, bool& failed);
static int getNextLedgerTimeResolution (int previousResolution, bool previousAgree, int ledgerSeq);
};
#endif

View File

@@ -0,0 +1,264 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
SETUP_LOG (OrderBookDB)
OrderBookDB::OrderBookDB ()
: mLock (this, "OrderBookDB", __FILE__, __LINE__)
, mSeq (0)
{
}
void OrderBookDB::invalidate ()
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
mSeq = 0;
}
void OrderBookDB::setup (Ledger::ref ledger)
{
boost::unordered_set<uint256> mSeen;
ScopedLockType sl (mLock, __FILE__, __LINE__);
if ((mSeq != 0) && (ledger->getLedgerSeq () >= mSeq) && ((ledger->getLedgerSeq() - mSeq) < 10))
return;
mSeq = ledger->getLedgerSeq ();
LoadEvent::autoptr ev = getApp().getJobQueue ().getLoadEventAP (jtOB_SETUP, "OrderBookDB::setup");
mDestMap.clear ();
mSourceMap.clear ();
mXRPBooks.clear ();
WriteLog (lsDEBUG, OrderBookDB) << "OrderBookDB>";
// walk through the entire ledger looking for orderbook entries
int books = 0;
uint256 currentIndex = ledger->getFirstLedgerIndex ();
while (currentIndex.isNonZero ())
{
SLE::pointer entry = ledger->getSLEi (currentIndex);
if ((entry->getType () == ltDIR_NODE) && (entry->isFieldPresent (sfExchangeRate)) &&
(entry->getFieldH256 (sfRootIndex) == currentIndex))
{
const uint160& ci = entry->getFieldH160 (sfTakerPaysCurrency);
const uint160& co = entry->getFieldH160 (sfTakerGetsCurrency);
const uint160& ii = entry->getFieldH160 (sfTakerPaysIssuer);
const uint160& io = entry->getFieldH160 (sfTakerGetsIssuer);
uint256 index = Ledger::getBookBase (ci, ii, co, io);
if (mSeen.insert (index).second)
{
// VFALCO TODO Reduce the clunkiness of these parameter wrappers
OrderBook::pointer book = boost::make_shared<OrderBook> (boost::cref (index),
boost::cref (ci), boost::cref (co), boost::cref (ii), boost::cref (io));
mSourceMap[currencyIssuer_ct (ci, ii)].push_back (book);
mDestMap[currencyIssuer_ct (co, io)].push_back (book);
if (co.isZero())
mXRPBooks.insert(currencyIssuer_ct (ci, ii));
++books;
}
}
currentIndex = ledger->getNextLedgerIndex (currentIndex);
}
WriteLog (lsDEBUG, OrderBookDB) << "OrderBookDB< " << books << " books found";
}
// return list of all orderbooks that want this issuerID and currencyID
void OrderBookDB::getBooksByTakerPays (const uint160& issuerID, const uint160& currencyID,
std::vector<OrderBook::pointer>& bookRet)
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
boost::unordered_map< currencyIssuer_t, std::vector<OrderBook::pointer> >::const_iterator
it = mSourceMap.find (currencyIssuer_ct (currencyID, issuerID));
if (it != mSourceMap.end ())
bookRet = it->second;
else
bookRet.clear ();
}
bool OrderBookDB::isBookToXRP(const uint160& issuerID, const uint160& currencyID)
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
return mXRPBooks.count(currencyIssuer_ct(currencyID, issuerID)) > 0;
}
// return list of all orderbooks that give this issuerID and currencyID
void OrderBookDB::getBooksByTakerGets (const uint160& issuerID, const uint160& currencyID,
std::vector<OrderBook::pointer>& bookRet)
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
boost::unordered_map< currencyIssuer_t, std::vector<OrderBook::pointer> >::const_iterator
it = mDestMap.find (currencyIssuer_ct (currencyID, issuerID));
if (it != mDestMap.end ())
bookRet = it->second;
else
bookRet.clear ();
}
BookListeners::pointer OrderBookDB::makeBookListeners (const uint160& currencyPays, const uint160& currencyGets,
const uint160& issuerPays, const uint160& issuerGets)
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
BookListeners::pointer ret = getBookListeners (currencyPays, currencyGets, issuerPays, issuerGets);
if (!ret)
{
ret = boost::make_shared<BookListeners> ();
mListeners[issuerPays][issuerGets][currencyPays][currencyGets] = ret;
}
return ret;
}
BookListeners::pointer OrderBookDB::getBookListeners (const uint160& currencyPays, const uint160& currencyGets,
const uint160& issuerPays, const uint160& issuerGets)
{
BookListeners::pointer ret;
ScopedLockType sl (mLock, __FILE__, __LINE__);
std::map<uint160, std::map<uint160, std::map<uint160, std::map<uint160, BookListeners::pointer> > > >::iterator
it0 = mListeners.find (issuerPays);
if (it0 == mListeners.end ())
return ret;
std::map<uint160, std::map<uint160, std::map<uint160, BookListeners::pointer> > >::iterator
it1 = (*it0).second.find (issuerGets);
if (it1 == (*it0).second.end ())
return ret;
std::map<uint160, std::map<uint160, BookListeners::pointer> >::iterator it2 = (*it1).second.find (currencyPays);
if (it2 == (*it1).second.end ())
return ret;
std::map<uint160, BookListeners::pointer>::iterator it3 = (*it2).second.find (currencyGets);
if (it3 == (*it2).second.end ())
return ret;
return (*it3).second;
}
// Based on the meta, send the meta to the streams that are listening
// We need to determine which streams a given meta effects
void OrderBookDB::processTxn (Ledger::ref ledger, const AcceptedLedgerTx& alTx, Json::Value& jvObj)
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
if (alTx.getResult () == tesSUCCESS)
{
// check if this is an offer or an offer cancel or a payment that consumes an offer
//check to see what the meta looks like
BOOST_FOREACH (STObject & node, alTx.getMeta ()->getNodes ())
{
try
{
if (node.getFieldU16 (sfLedgerEntryType) == ltOFFER)
{
SField* field = NULL;
if (node.getFName () == sfModifiedNode)
{
field = &sfPreviousFields;
}
else if (node.getFName () == sfCreatedNode)
{
field = &sfNewFields;
}
else if (node.getFName () == sfDeletedNode)
{
field = &sfFinalFields;
}
if (field)
{
const STObject* data = dynamic_cast<const STObject*> (node.peekAtPField (*field));
if (data)
{
const STAmount& takerGets = data->getFieldAmount (sfTakerGets);
const uint160& currencyGets = takerGets.getCurrency ();
const uint160& issuerGets = takerGets.getIssuer ();
const STAmount& takerPays = data->getFieldAmount (sfTakerPays);
const uint160& currencyPays = takerPays.getCurrency ();
const uint160& issuerPays = takerPays.getIssuer ();
// determine the OrderBook
BookListeners::pointer book =
getBookListeners (currencyPays, currencyGets, issuerPays, issuerGets);
if (book)
book->publish (jvObj);
}
}
}
}
catch (...)
{
WriteLog (lsINFO, OrderBookDB) << "Fields not found in OrderBookDB::processTxn";
}
}
}
}
//------------------------------------------------------------------------------
BookListeners::BookListeners ()
: mLock (this, "BookListeners", __FILE__, __LINE__)
{
}
void BookListeners::addSubscriber (InfoSub::ref sub)
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
mListeners[sub->getSeq ()] = sub;
}
void BookListeners::removeSubscriber (uint64 seq)
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
mListeners.erase (seq);
}
void BookListeners::publish (Json::Value& jvObj)
{
Json::FastWriter jfwWriter;
std::string sObj = jfwWriter.write (jvObj);
ScopedLockType sl (mLock, __FILE__, __LINE__);
NetworkOPs::SubMapType::const_iterator it = mListeners.begin ();
while (it != mListeners.end ())
{
InfoSub::pointer p = it->second.lock ();
if (p)
{
p->send (jvObj, sObj, true);
++it;
}
else
it = mListeners.erase (it);
}
}
// vim:ts=4

View File

@@ -0,0 +1,82 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
#ifndef ORDERBOOK_DB_H
#define ORDERBOOK_DB_H
//
// XXX Eventually make this cached and just update it as transactions come in.
// But, for now it is probably faster to just generate it each time.
//
typedef std::pair<uint160, uint160> currencyIssuer_t;
#ifdef C11X
typedef std::pair<const uint160&, const uint160&> currencyIssuer_ct;
#else
typedef std::pair<uint160, uint160> currencyIssuer_ct; // C++ defect 106
#endif
class BookListeners
{
public:
typedef boost::shared_ptr<BookListeners> pointer;
BookListeners ();
void addSubscriber (InfoSub::ref sub);
void removeSubscriber (uint64 sub);
void publish (Json::Value& jvObj);
private:
typedef RippleRecursiveMutex LockType;
typedef LockType::ScopedLockType ScopedLockType;
LockType mLock;
boost::unordered_map<uint64, InfoSub::wptr> mListeners;
};
class OrderBookDB : LeakChecked <OrderBookDB>
{
public:
OrderBookDB ();
void setup (Ledger::ref ledger);
void invalidate ();
// return list of all orderbooks that want this issuerID and currencyID
void getBooksByTakerPays (const uint160& issuerID, const uint160& currencyID,
std::vector<OrderBook::pointer>& bookRet);
void getBooksByTakerGets (const uint160& issuerID, const uint160& currencyID,
std::vector<OrderBook::pointer>& bookRet);
bool isBookToXRP (const uint160& issuerID, const uint160& currencyID);
BookListeners::pointer getBookListeners (const uint160& currencyPays, const uint160& currencyGets,
const uint160& issuerPays, const uint160& issuerGets);
BookListeners::pointer makeBookListeners (const uint160& currencyPays, const uint160& currencyGets,
const uint160& issuerPays, const uint160& issuerGets);
// see if this txn effects any orderbook
void processTxn (Ledger::ref ledger, const AcceptedLedgerTx& alTx, Json::Value& jvObj);
private:
boost::unordered_map< currencyIssuer_t, std::vector<OrderBook::pointer> > mSourceMap; // by ci/ii
boost::unordered_map< currencyIssuer_t, std::vector<OrderBook::pointer> > mDestMap; // by co/io
boost::unordered_set< currencyIssuer_t > mXRPBooks; // does an order book to XRP exist
typedef RippleRecursiveMutex LockType;
typedef LockType::ScopedLockType ScopedLockType;
LockType mLock;
// issuerPays, issuerGets, currencyPays, currencyGets
std::map<uint160, std::map<uint160, std::map<uint160, std::map<uint160, BookListeners::pointer> > > > mListeners;
uint32 mSeq;
};
#endif
// vim:ts=4

View File

@@ -0,0 +1,144 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
SerializedValidation::SerializedValidation (SerializerIterator& sit, bool checkSignature)
: STObject (getFormat (), sit, sfValidation)
, mTrusted (false)
{
mNodeID = RippleAddress::createNodePublic (getFieldVL (sfSigningPubKey)).getNodeID ();
assert (mNodeID.isNonZero ());
if (checkSignature && !isValid ())
{
Log (lsTRACE) << "Invalid validation " << getJson (0);
throw std::runtime_error ("Invalid validation");
}
}
SerializedValidation::SerializedValidation (
uint256 const& ledgerHash, uint32 signTime,
const RippleAddress& raPub, bool isFull)
: STObject (getFormat (), sfValidation)
, mTrusted (false)
{
// Does not sign
setFieldH256 (sfLedgerHash, ledgerHash);
setFieldU32 (sfSigningTime, signTime);
setFieldVL (sfSigningPubKey, raPub.getNodePublic ());
mNodeID = raPub.getNodeID ();
assert (mNodeID.isNonZero ());
if (!isFull)
setFlag (kFullFlag);
}
void SerializedValidation::sign (const RippleAddress& raPriv)
{
uint256 signingHash;
sign (signingHash, raPriv);
}
void SerializedValidation::sign (uint256& signingHash, const RippleAddress& raPriv)
{
signingHash = getSigningHash ();
Blob signature;
raPriv.signNodePrivate (signingHash, signature);
setFieldVL (sfSignature, signature);
}
uint256 SerializedValidation::getSigningHash () const
{
return STObject::getSigningHash (getConfig ().SIGN_VALIDATION);
}
uint256 SerializedValidation::getLedgerHash () const
{
return getFieldH256 (sfLedgerHash);
}
uint32 SerializedValidation::getSignTime () const
{
return getFieldU32 (sfSigningTime);
}
uint32 SerializedValidation::getFlags () const
{
return getFieldU32 (sfFlags);
}
bool SerializedValidation::isValid () const
{
return isValid (getSigningHash ());
}
bool SerializedValidation::isValid (uint256 const& signingHash) const
{
try
{
RippleAddress raPublicKey = RippleAddress::createNodePublic (getFieldVL (sfSigningPubKey));
return raPublicKey.isValid () && raPublicKey.verifyNodePublic (signingHash, getFieldVL (sfSignature));
}
catch (...)
{
Log (lsINFO) << "exception validating validation";
return false;
}
}
RippleAddress SerializedValidation::getSignerPublic () const
{
RippleAddress a;
a.setNodePublic (getFieldVL (sfSigningPubKey));
return a;
}
bool SerializedValidation::isFull () const
{
return (getFlags () & kFullFlag) != 0;
}
Blob SerializedValidation::getSignature () const
{
return getFieldVL (sfSignature);
}
Blob SerializedValidation::getSigned () const
{
Serializer s;
add (s);
return s.peekData ();
}
SOTemplate const& SerializedValidation::getFormat ()
{
struct FormatHolder
{
SOTemplate format;
FormatHolder ()
{
format.push_back (SOElement (sfFlags, SOE_REQUIRED));
format.push_back (SOElement (sfLedgerHash, SOE_REQUIRED));
format.push_back (SOElement (sfLedgerSequence, SOE_OPTIONAL));
format.push_back (SOElement (sfCloseTime, SOE_OPTIONAL));
format.push_back (SOElement (sfLoadFee, SOE_OPTIONAL));
format.push_back (SOElement (sfFeatures, SOE_OPTIONAL));
format.push_back (SOElement (sfBaseFee, SOE_OPTIONAL));
format.push_back (SOElement (sfReserveBase, SOE_OPTIONAL));
format.push_back (SOElement (sfReserveIncrement, SOE_OPTIONAL));
format.push_back (SOElement (sfSigningTime, SOE_REQUIRED));
format.push_back (SOElement (sfSigningPubKey, SOE_REQUIRED));
format.push_back (SOElement (sfSignature, SOE_OPTIONAL));
}
};
static FormatHolder holder;
return holder.format;
}
// vim:ts=4

View File

@@ -0,0 +1,82 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
#ifndef RIPPLE_SERIALIZEDVALIDATION_H
#define RIPPLE_SERIALIZEDVALIDATION_H
class SerializedValidation
: public STObject
, public CountedObject <SerializedValidation>
{
public:
static char const* getCountedObjectName () { return "SerializedValidation"; }
typedef boost::shared_ptr<SerializedValidation> pointer;
typedef const boost::shared_ptr<SerializedValidation>& ref;
enum
{
kFullFlag = 0x1
};
// These throw if the object is not valid
SerializedValidation (SerializerIterator & sit, bool checkSignature = true);
// Does not sign the validation
SerializedValidation (uint256 const & ledgerHash, uint32 signTime, const RippleAddress & raPub, bool isFull);
uint256 getLedgerHash () const;
uint32 getSignTime () const;
uint32 getFlags () const;
RippleAddress getSignerPublic () const;
uint160 getNodeID () const
{
return mNodeID;
}
bool isValid () const;
bool isFull () const;
bool isTrusted () const
{
return mTrusted;
}
uint256 getSigningHash () const;
bool isValid (uint256 const& ) const;
void setTrusted ()
{
mTrusted = true;
}
Blob getSigned () const;
Blob getSignature () const;
void sign (uint256 & signingHash, const RippleAddress & raPrivate);
void sign (const RippleAddress & raPrivate);
// The validation this replaced
uint256 const& getPreviousHash ()
{
return mPreviousHash;
}
bool isPreviousHash (uint256 const & h) const
{
return mPreviousHash == h;
}
void setPreviousHash (uint256 const & h)
{
mPreviousHash = h;
}
private:
static SOTemplate const& getFormat ();
void setNode ();
uint256 mPreviousHash;
uint160 mNodeID;
bool mTrusted;
};
#endif
// vim:ts=4

View File

@@ -0,0 +1,46 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
TaggedCacheType <uint256, AcceptedLedger, UptimeTimerAdapter> AcceptedLedger::s_cache ("AcceptedLedger", 4, 60);
AcceptedLedger::AcceptedLedger (Ledger::ref ledger) : mLedger (ledger)
{
SHAMap& txSet = *ledger->peekTransactionMap ();
for (SHAMapItem::pointer item = txSet.peekFirstItem (); !!item; item = txSet.peekNextItem (item->getTag ()))
{
SerializerIterator sit (item->peekSerializer ());
insert (boost::make_shared<AcceptedLedgerTx> (ledger->getLedgerSeq (), boost::ref (sit)));
}
}
AcceptedLedger::pointer AcceptedLedger::makeAcceptedLedger (Ledger::ref ledger)
{
AcceptedLedger::pointer ret = s_cache.fetch (ledger->getHash ());
if (ret)
return ret;
ret = AcceptedLedger::pointer (new AcceptedLedger (ledger));
s_cache.canonicalize (ledger->getHash (), ret);
return ret;
}
void AcceptedLedger::insert (AcceptedLedgerTx::ref at)
{
assert (mMap.find (at->getIndex ()) == mMap.end ());
mMap.insert (std::make_pair (at->getIndex (), at));
}
AcceptedLedgerTx::pointer AcceptedLedger::getTxn (int i) const
{
map_t::const_iterator it = mMap.find (i);
if (it == mMap.end ())
return AcceptedLedgerTx::pointer ();
return it->second;
}

View File

@@ -0,0 +1,79 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
#ifndef RIPPLE_ACCEPTEDLEDGER_H
#define RIPPLE_ACCEPTEDLEDGER_H
/** A ledger that has become irrevocable.
An accepted ledger is a ledger that has a sufficient number of
validations to convince the local server that it is irrevocable.
The existence of an accepted ledger implies all preceding ledgers
are accepted.
*/
/* VFALCO TODO digest this terminology clarification:
Closed and accepted refer to ledgers that have not passed the
validation threshold yet. Once they pass the threshold, they are
"Validated". Closed just means its close time has passed and no
new transactions can get in. "Accepted" means we believe it to be
the result of the a consensus process (though haven't validated
it yet).
*/
class AcceptedLedger
{
public:
typedef boost::shared_ptr<AcceptedLedger> pointer;
typedef const pointer& ret;
typedef std::map<int, AcceptedLedgerTx::pointer> map_t; // Must be an ordered map!
typedef map_t::value_type value_type;
typedef map_t::const_iterator const_iterator;
public:
static pointer makeAcceptedLedger (Ledger::ref ledger);
static void sweep ()
{
s_cache.sweep ();
}
Ledger::ref getLedger () const
{
return mLedger;
}
const map_t& getMap () const
{
return mMap;
}
int getLedgerSeq () const
{
return mLedger->getLedgerSeq ();
}
int getTxnCount () const
{
return mMap.size ();
}
static float getCacheHitRate ()
{
return s_cache.getHitRate ();
}
AcceptedLedgerTx::pointer getTxn (int) const;
private:
explicit AcceptedLedger (Ledger::ref ledger);
void insert (AcceptedLedgerTx::ref);
private:
static TaggedCacheType <uint256, AcceptedLedger, UptimeTimerAdapter> s_cache;
Ledger::pointer mLedger;
map_t mMap;
};
#endif

View File

@@ -0,0 +1,61 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
AcceptedLedgerTx::AcceptedLedgerTx (uint32 seq, SerializerIterator& sit)
{
Serializer txnSer (sit.getVL ());
SerializerIterator txnIt (txnSer);
mTxn = boost::make_shared<SerializedTransaction> (boost::ref (txnIt));
mRawMeta = sit.getVL ();
mMeta = boost::make_shared<TransactionMetaSet> (mTxn->getTransactionID (), seq, mRawMeta);
mAffected = mMeta->getAffectedAccounts ();
mResult = mMeta->getResultTER ();
buildJson ();
}
AcceptedLedgerTx::AcceptedLedgerTx (SerializedTransaction::ref txn, TransactionMetaSet::ref met) :
mTxn (txn), mMeta (met), mAffected (met->getAffectedAccounts ())
{
mResult = mMeta->getResultTER ();
buildJson ();
}
AcceptedLedgerTx::AcceptedLedgerTx (SerializedTransaction::ref txn, TER result) :
mTxn (txn), mResult (result), mAffected (txn->getMentionedAccounts ())
{
buildJson ();
}
std::string AcceptedLedgerTx::getEscMeta () const
{
assert (!mRawMeta.empty ());
return sqlEscape (mRawMeta);
}
void AcceptedLedgerTx::buildJson ()
{
mJson = Json::objectValue;
mJson["transaction"] = mTxn->getJson (0);
if (mMeta)
{
mJson["meta"] = mMeta->getJson (0);
mJson["raw_meta"] = strHex (mRawMeta);
}
mJson["result"] = transHuman (mResult);
if (!mAffected.empty ())
{
Json::Value& affected = (mJson["affected"] = Json::arrayValue);
BOOST_FOREACH (const RippleAddress & ra, mAffected)
{
affected.append (ra.humanAccountID ());
}
}
}

View File

@@ -0,0 +1,97 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
#ifndef RIPPLE_ACCEPTEDLEDGERTX_H
#define RIPPLE_ACCEPTEDLEDGERTX_H
//------------------------------------------------------------------------------
/**
A transaction that is in a closed ledger.
Description
An accepted ledger transaction contains additional information that the
server needs to tell clients about the transaction. For example,
- The transaction in JSON form
- Which accounts are affected
* This is used by InfoSub to report to clients
- Cached stuff
@code
@endcode
@see {uri}
@ingroup ripple_ledger
*/
class AcceptedLedgerTx
{
public:
typedef boost::shared_ptr <AcceptedLedgerTx> pointer;
typedef const pointer& ref;
public:
AcceptedLedgerTx (LedgerIndex ledgerSeq, SerializerIterator& sit);
AcceptedLedgerTx (SerializedTransaction::ref, TransactionMetaSet::ref);
AcceptedLedgerTx (SerializedTransaction::ref, TER result);
SerializedTransaction::ref getTxn () const
{
return mTxn;
}
TransactionMetaSet::ref getMeta () const
{
return mMeta;
}
std::vector <RippleAddress> const& getAffected () const
{
return mAffected;
}
TxID getTransactionID () const
{
return mTxn->getTransactionID ();
}
TxType getTxnType () const
{
return mTxn->getTxnType ();
}
TER getResult () const
{
return mResult;
}
uint32 getTxnSeq () const
{
return mMeta->getIndex ();
}
bool isApplied () const
{
return !!mMeta;
}
int getIndex () const
{
return mMeta ? mMeta->getIndex () : 0;
}
std::string getEscMeta () const;
Json::Value getJson () const
{
return mJson;
}
private:
SerializedTransaction::pointer mTxn;
TransactionMetaSet::pointer mMeta;
TER mResult;
std::vector <RippleAddress> mAffected;
Blob mRawMeta;
Json::Value mJson;
void buildJson ();
};
#endif

View File

@@ -0,0 +1,855 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
SETUP_LOG (InboundLedger)
// VFALCO TODO replace macros
#define LA_DEBUG
#define LEDGER_ACQUIRE_TIMEOUT 6000 // millisecond for each ledger timeout
#define LEDGER_TIMEOUT_COUNT 10 // how many timeouts before we giveup
#define LEDGER_TIMEOUT_AGGRESSIVE 6 // how many timeouts before we get aggressive
InboundLedger::InboundLedger (uint256 const& hash, uint32 seq)
: PeerSet (hash, LEDGER_ACQUIRE_TIMEOUT, false)
, mHaveBase (false)
, mHaveState (false)
, mHaveTransactions (false)
, mAborted (false)
, mSignaled (false)
, mByHash (true)
, mWaitCount (0)
, mSeq (seq)
{
#ifdef LA_DEBUG
WriteLog (lsTRACE, InboundLedger) << "Acquiring ledger " << mHash;
#endif
}
bool InboundLedger::checkLocal ()
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
if (!isDone () && tryLocal())
{
done();
return true;
}
return false;
}
InboundLedger::~InboundLedger ()
{
}
bool InboundLedger::tryLocal ()
{
// return value: true = no more work to do
if (!mHaveBase)
{
// Nothing we can do without the ledger base
NodeObject::pointer node = getApp().getNodeStore ().fetch (mHash);
if (!node)
{
Blob data;
if (!getApp().getOPs ().getFetchPack (mHash, data))
return false;
WriteLog (lsTRACE, InboundLedger) << "Ledger base found in fetch pack";
mLedger = boost::make_shared<Ledger> (data, true);
getApp().getNodeStore ().store (hotLEDGER, mLedger->getLedgerSeq (), data, mHash);
}
else
{
mLedger = boost::make_shared<Ledger> (strCopy (node->getData ()), true);
}
if (mLedger->getHash () != mHash)
{
// We know for a fact the ledger can never be acquired
WriteLog (lsWARNING, InboundLedger) << mHash << " cannot be a ledger";
mFailed = true;
return true;
}
mHaveBase = true;
}
if (!mHaveTransactions)
{
if (mLedger->getTransHash ().isZero ())
{
WriteLog (lsTRACE, InboundLedger) << "No TXNs to fetch";
mHaveTransactions = true;
}
else
{
TransactionStateSF filter (mLedger->getLedgerSeq ());
if (mLedger->peekTransactionMap ()->fetchRoot (mLedger->getTransHash (), &filter))
{
WriteLog (lsTRACE, InboundLedger) << "Got root txn map locally";
std::vector<uint256> h = mLedger->getNeededTransactionHashes (1, &filter);
if (h.empty ())
{
WriteLog (lsTRACE, InboundLedger) << "Had full txn map locally";
mHaveTransactions = true;
}
}
}
}
if (!mHaveState)
{
if (mLedger->getAccountHash ().isZero ())
{
WriteLog (lsFATAL, InboundLedger) << "We are acquiring a ledger with a zero account hash";
mHaveState = true;
}
else
{
AccountStateSF filter (mLedger->getLedgerSeq ());
if (mLedger->peekAccountStateMap ()->fetchRoot (mLedger->getAccountHash (), &filter))
{
WriteLog (lsTRACE, InboundLedger) << "Got root AS map locally";
std::vector<uint256> h = mLedger->getNeededAccountStateHashes (1, &filter);
if (h.empty ())
{
WriteLog (lsTRACE, InboundLedger) << "Had full AS map locally";
mHaveState = true;
}
}
}
}
if (mHaveTransactions && mHaveState)
{
WriteLog (lsDEBUG, InboundLedger) << "Had everything locally";
mComplete = true;
mLedger->setClosed ();
mLedger->setImmutable ();
}
return mComplete;
}
void InboundLedger::onTimer (bool wasProgress, ScopedLockType&)
{
mRecentTXNodes.clear ();
mRecentASNodes.clear ();
if (getTimeouts () > LEDGER_TIMEOUT_COUNT)
{
WriteLog (lsWARNING, InboundLedger) << "Too many timeouts( " << getTimeouts () << ") for ledger " << mHash;
setFailed ();
done ();
return;
}
if (!wasProgress)
{
if (isDone())
{
WriteLog (lsINFO, InboundLedger) << "Already done " << mHash;
return;
}
checkLocal();
if (isDone())
{
WriteLog (lsINFO, InboundLedger) << "Completed fetch " << mHash;
return;
}
mAggressive = true;
mByHash = true;
int pc = getPeerCount ();
WriteLog (lsDEBUG, InboundLedger) << "No progress(" << pc << ") for ledger " << mHash;
trigger (Peer::pointer ());
if (pc < 4)
addPeers ();
}
}
void InboundLedger::awaitData ()
{
++mWaitCount;
}
void InboundLedger::noAwaitData ()
{ // subtract one if mWaitCount is greater than zero
do
{
int j = mWaitCount.get();
if (j <= 0)
return;
if (mWaitCount.compareAndSetBool(j - 1, j))
return;
} while (1);
}
void InboundLedger::addPeers ()
{
std::vector<Peer::pointer> peerList = getApp().getPeers ().getPeerVector ();
int vSize = peerList.size ();
if (vSize == 0)
return;
// We traverse the peer list in random order so as not to favor any particular peer
int firstPeer = rand () % vSize;
int found = 0;
for (int i = 0; i < vSize; ++i)
{
Peer::ref peer = peerList[ (i + firstPeer) % vSize];
if (peer->hasLedger (getHash (), mSeq))
{
if (peerHas (peer) && (++found > 6))
break;
}
}
if (!found)
for (int i = 0; (i < 6) && (i < vSize); ++i)
peerHas (peerList[ (i + firstPeer) % vSize]);
}
boost::weak_ptr<PeerSet> InboundLedger::pmDowncast ()
{
return boost::dynamic_pointer_cast<PeerSet> (shared_from_this ());
}
static void LADispatch (
Job& job,
InboundLedger::pointer la,
std::vector< FUNCTION_TYPE<void (InboundLedger::pointer)> > trig)
{
if (la->isComplete() && !la->isFailed())
getApp().getLedgerMaster().checkAccept(la->getLedger());
getApp().getLedgerMaster().tryAdvance();
for (unsigned int i = 0; i < trig.size (); ++i)
trig[i] (la);
}
void InboundLedger::done ()
{
if (mSignaled)
return;
mSignaled = true;
touch ();
WriteLog (lsTRACE, InboundLedger) << "Done acquiring ledger " << mHash;
assert (isComplete () || isFailed ());
std::vector< FUNCTION_TYPE<void (InboundLedger::pointer)> > triggers;
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
triggers.swap (mOnComplete);
}
if (isComplete () && !isFailed () && mLedger)
{
mLedger->setClosed ();
mLedger->setImmutable ();
getApp().getLedgerMaster ().storeLedger (mLedger);
}
else
getApp().getInboundLedgers ().logFailure (mHash);
// We hold the PeerSet lock, so must dispatch
getApp().getJobQueue ().addJob (jtLEDGER_DATA, "triggers",
BIND_TYPE (LADispatch, P_1, shared_from_this (), triggers));
}
bool InboundLedger::addOnComplete (FUNCTION_TYPE<void (InboundLedger::pointer)> trigger)
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
if (isDone ())
return false;
mOnComplete.push_back (trigger);
return true;
}
void InboundLedger::trigger (Peer::ref peer)
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
if (isDone ())
{
WriteLog (lsDEBUG, InboundLedger) << "Trigger on ledger: " << mHash <<
(mAborted ? " aborted" : "") << (mComplete ? " completed" : "") << (mFailed ? " failed" : "");
return;
}
if ((mWaitCount.get() > 0) && peer)
{
WriteLog (lsTRACE, InboundLedger) << "Skipping peer";
return;
}
if (ShouldLog (lsTRACE, InboundLedger))
{
if (peer)
WriteLog (lsTRACE, InboundLedger) << "Trigger acquiring ledger " << mHash << " from " << peer->getIP ();
else
WriteLog (lsTRACE, InboundLedger) << "Trigger acquiring ledger " << mHash;
if (mComplete || mFailed)
WriteLog (lsTRACE, InboundLedger) << "complete=" << mComplete << " failed=" << mFailed;
else
WriteLog (lsTRACE, InboundLedger) << "base=" << mHaveBase << " tx=" << mHaveTransactions << " as=" << mHaveState;
}
if (!mHaveBase)
{
tryLocal ();
if (mFailed)
{
WriteLog (lsWARNING, InboundLedger) << " failed local for " << mHash;
return;
}
}
protocol::TMGetLedger tmGL;
tmGL.set_ledgerhash (mHash.begin (), mHash.size ());
if (getTimeouts () != 0)
{
tmGL.set_querytype (protocol::qtINDIRECT);
if (!isProgress () && !mFailed && mByHash && (getTimeouts () > LEDGER_TIMEOUT_AGGRESSIVE))
{
std::vector<neededHash_t> need = getNeededHashes ();
if (!need.empty ())
{
protocol::TMGetObjectByHash tmBH;
tmBH.set_query (true);
tmBH.set_ledgerhash (mHash.begin (), mHash.size ());
bool typeSet = false;
BOOST_FOREACH (neededHash_t & p, need)
{
WriteLog (lsWARNING, InboundLedger) << "Want: " << p.second;
if (!typeSet)
{
tmBH.set_type (p.first);
typeSet = true;
}
if (p.first == tmBH.type ())
{
protocol::TMIndexedObject* io = tmBH.add_objects ();
io->set_hash (p.second.begin (), p.second.size ());
}
}
PackedMessage::pointer packet = boost::make_shared<PackedMessage> (tmBH, protocol::mtGET_OBJECTS);
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
for (boost::unordered_map<uint64, int>::iterator it = mPeers.begin (), end = mPeers.end ();
it != end; ++it)
{
Peer::pointer iPeer = getApp().getPeers ().getPeerById (it->first);
if (iPeer)
{
mByHash = false;
iPeer->sendPacket (packet, false);
}
}
}
WriteLog (lsINFO, InboundLedger) << "Attempting by hash fetch for ledger " << mHash;
}
else
{
WriteLog (lsINFO, InboundLedger) << "getNeededHashes says acquire is complete";
mHaveBase = true;
mHaveTransactions = true;
mHaveState = true;
mComplete = true;
}
}
}
if (!mHaveBase && !mFailed)
{
tmGL.set_itype (protocol::liBASE);
WriteLog (lsTRACE, InboundLedger) << "Sending base request to " << (peer ? "selected peer" : "all peers");
sendRequest (tmGL, peer);
return;
}
if (mLedger)
tmGL.set_ledgerseq (mLedger->getLedgerSeq ());
if (mHaveBase && !mHaveTransactions && !mFailed)
{
assert (mLedger);
if (mLedger->peekTransactionMap ()->getHash ().isZero ())
{
// we need the root node
tmGL.set_itype (protocol::liTX_NODE);
* (tmGL.add_nodeids ()) = SHAMapNode ().getRawString ();
WriteLog (lsTRACE, InboundLedger) << "Sending TX root request to " << (peer ? "selected peer" : "all peers");
sendRequest (tmGL, peer);
return;
}
else
{
std::vector<SHAMapNode> nodeIDs;
std::vector<uint256> nodeHashes;
nodeIDs.reserve (256);
nodeHashes.reserve (256);
TransactionStateSF filter (mSeq);
mLedger->peekTransactionMap ()->getMissingNodes (nodeIDs, nodeHashes, 256, &filter);
if (nodeIDs.empty ())
{
if (!mLedger->peekTransactionMap ()->isValid ())
mFailed = true;
else
{
mHaveTransactions = true;
if (mHaveState)
mComplete = true;
}
}
else
{
if (!mAggressive)
filterNodes (nodeIDs, nodeHashes, mRecentTXNodes, 128, !isProgress ());
if (!nodeIDs.empty ())
{
tmGL.set_itype (protocol::liTX_NODE);
BOOST_FOREACH (SHAMapNode const& it, nodeIDs)
{
* (tmGL.add_nodeids ()) = it.getRawString ();
}
WriteLog (lsTRACE, InboundLedger) << "Sending TX node " << nodeIDs.size ()
<< " request to " << (peer ? "selected peer" : "all peers");
sendRequest (tmGL, peer);
return;
}
}
}
}
if (mHaveBase && !mHaveState && !mFailed)
{
assert (mLedger);
if (mLedger->peekAccountStateMap ()->getHash ().isZero ())
{
// we need the root node
tmGL.set_itype (protocol::liAS_NODE);
* (tmGL.add_nodeids ()) = SHAMapNode ().getRawString ();
WriteLog (lsTRACE, InboundLedger) << "Sending AS root request to " << (peer ? "selected peer" : "all peers");
sendRequest (tmGL, peer);
return;
}
else
{
std::vector<SHAMapNode> nodeIDs;
std::vector<uint256> nodeHashes;
nodeIDs.reserve (256);
nodeHashes.reserve (256);
AccountStateSF filter (mSeq);
mLedger->peekAccountStateMap ()->getMissingNodes (nodeIDs, nodeHashes, 256, &filter);
if (nodeIDs.empty ())
{
if (!mLedger->peekAccountStateMap ()->isValid ())
mFailed = true;
else
{
mHaveState = true;
if (mHaveTransactions)
mComplete = true;
}
}
else
{
if (!mAggressive)
filterNodes (nodeIDs, nodeHashes, mRecentASNodes, 128, !isProgress ());
if (!nodeIDs.empty ())
{
tmGL.set_itype (protocol::liAS_NODE);
BOOST_FOREACH (SHAMapNode const& it, nodeIDs)
{
* (tmGL.add_nodeids ()) = it.getRawString ();
}
WriteLog (lsTRACE, InboundLedger) << "Sending AS node " << nodeIDs.size ()
<< " request to " << (peer ? "selected peer" : "all peers");
CondLog (nodeIDs.size () == 1, lsTRACE, InboundLedger) << "AS node: " << nodeIDs[0];
sendRequest (tmGL, peer);
return;
}
}
}
}
if (mComplete || mFailed)
{
WriteLog (lsDEBUG, InboundLedger) << "Done:" << (mComplete ? " complete" : "") << (mFailed ? " failed " : " ")
<< mLedger->getLedgerSeq ();
sl.unlock ();
done ();
}
}
void InboundLedger::filterNodes (std::vector<SHAMapNode>& nodeIDs, std::vector<uint256>& nodeHashes,
std::set<SHAMapNode>& recentNodes, int max, bool aggressive)
{
// ask for new nodes in preference to ones we've already asked for
assert (nodeIDs.size () == nodeHashes.size ());
std::vector<bool> duplicates;
duplicates.reserve (nodeIDs.size ());
int dupCount = 0;
BOOST_FOREACH(SHAMapNode const& nodeID, nodeIDs)
{
if (recentNodes.count (nodeID) != 0)
{
duplicates.push_back (true);
++dupCount;
}
else
duplicates.push_back (false);
}
if (dupCount == nodeIDs.size ())
{
// all duplicates
if (!aggressive)
{
nodeIDs.clear ();
nodeHashes.clear ();
WriteLog (lsTRACE, InboundLedger) << "filterNodes: all are duplicates";
return;
}
}
else if (dupCount > 0)
{
// some, but not all, duplicates
int insertPoint = 0;
for (unsigned int i = 0; i < nodeIDs.size (); ++i)
if (!duplicates[i])
{
// Keep this node
if (insertPoint != i)
{
nodeIDs[insertPoint] = nodeIDs[i];
nodeHashes[insertPoint] = nodeHashes[i];
}
++insertPoint;
}
WriteLog (lsTRACE, InboundLedger) << "filterNodes " << nodeIDs.size () << " to " << insertPoint;
nodeIDs.resize (insertPoint);
nodeHashes.resize (insertPoint);
}
if (nodeIDs.size () > max)
{
nodeIDs.resize (max);
nodeHashes.resize (max);
}
BOOST_FOREACH (const SHAMapNode & n, nodeIDs)
{
recentNodes.insert (n);
}
}
bool InboundLedger::takeBase (const std::string& data) // data must not have hash prefix
{
// Return value: true=normal, false=bad data
#ifdef LA_DEBUG
WriteLog (lsTRACE, InboundLedger) << "got base acquiring ledger " << mHash;
#endif
ScopedLockType sl (mLock, __FILE__, __LINE__);
if (mComplete || mFailed || mHaveBase)
return true;
mLedger = boost::make_shared<Ledger> (data, false);
if (mLedger->getHash () != mHash)
{
WriteLog (lsWARNING, InboundLedger) << "Acquire hash mismatch";
WriteLog (lsWARNING, InboundLedger) << mLedger->getHash () << "!=" << mHash;
mLedger.reset ();
#ifdef TRUST_NETWORK
assert (false);
#endif
return false;
}
mHaveBase = true;
Serializer s (data.size () + 4);
s.add32 (HashPrefix::ledgerMaster);
s.addRaw (data);
getApp().getNodeStore ().store (hotLEDGER, mLedger->getLedgerSeq (), s.modData (), mHash);
progress ();
if (!mLedger->getTransHash ())
mHaveTransactions = true;
if (!mLedger->getAccountHash ())
mHaveState = true;
mLedger->setAcquiring ();
return true;
}
bool InboundLedger::takeTxNode (const std::list<SHAMapNode>& nodeIDs,
const std::list< Blob >& data, SHAMapAddNode& san)
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
if (!mHaveBase)
return false;
if (mHaveTransactions || mFailed)
return true;
std::list<SHAMapNode>::const_iterator nodeIDit = nodeIDs.begin ();
std::list< Blob >::const_iterator nodeDatait = data.begin ();
TransactionStateSF tFilter (mLedger->getLedgerSeq ());
while (nodeIDit != nodeIDs.end ())
{
if (nodeIDit->isRoot ())
{
if (!san.combine (mLedger->peekTransactionMap ()->addRootNode (mLedger->getTransHash (), *nodeDatait,
snfWIRE, &tFilter)))
return false;
}
else
{
if (!san.combine (mLedger->peekTransactionMap ()->addKnownNode (*nodeIDit, *nodeDatait, &tFilter)))
return false;
}
++nodeIDit;
++nodeDatait;
}
if (!mLedger->peekTransactionMap ()->isSynching ())
{
mHaveTransactions = true;
if (mHaveState)
{
mComplete = true;
done ();
}
}
progress ();
return true;
}
bool InboundLedger::takeAsNode (const std::list<SHAMapNode>& nodeIDs,
const std::list< Blob >& data, SHAMapAddNode& san)
{
WriteLog (lsTRACE, InboundLedger) << "got ASdata (" << nodeIDs.size () << ") acquiring ledger " << mHash;
CondLog (nodeIDs.size () == 1, lsTRACE, InboundLedger) << "got AS node: " << nodeIDs.front ();
ScopedLockType sl (mLock, __FILE__, __LINE__);
if (!mHaveBase)
{
WriteLog (lsWARNING, InboundLedger) << "Don't have ledger base";
return false;
}
if (mHaveState || mFailed)
return true;
std::list<SHAMapNode>::const_iterator nodeIDit = nodeIDs.begin ();
std::list< Blob >::const_iterator nodeDatait = data.begin ();
AccountStateSF tFilter (mLedger->getLedgerSeq ());
while (nodeIDit != nodeIDs.end ())
{
if (nodeIDit->isRoot ())
{
if (!san.combine (mLedger->peekAccountStateMap ()->addRootNode (mLedger->getAccountHash (),
*nodeDatait, snfWIRE, &tFilter)))
{
WriteLog (lsWARNING, InboundLedger) << "Bad ledger base";
return false;
}
}
else if (!san.combine (mLedger->peekAccountStateMap ()->addKnownNode (*nodeIDit, *nodeDatait, &tFilter)))
{
WriteLog (lsWARNING, InboundLedger) << "Unable to add AS node";
return false;
}
++nodeIDit;
++nodeDatait;
}
if (!mLedger->peekAccountStateMap ()->isSynching ())
{
mHaveState = true;
if (mHaveTransactions)
{
mComplete = true;
done ();
}
}
progress ();
return true;
}
bool InboundLedger::takeAsRootNode (Blob const& data, SHAMapAddNode& san)
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
if (mFailed || mHaveState)
return true;
if (!mHaveBase)
return false;
AccountStateSF tFilter (mLedger->getLedgerSeq ());
return san.combine (
mLedger->peekAccountStateMap ()->addRootNode (mLedger->getAccountHash (), data, snfWIRE, &tFilter));
}
bool InboundLedger::takeTxRootNode (Blob const& data, SHAMapAddNode& san)
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
if (mFailed || mHaveState)
return true;
if (!mHaveBase)
return false;
TransactionStateSF tFilter (mLedger->getLedgerSeq ());
return san.combine (
mLedger->peekTransactionMap ()->addRootNode (mLedger->getTransHash (), data, snfWIRE, &tFilter));
}
std::vector<InboundLedger::neededHash_t> InboundLedger::getNeededHashes ()
{
std::vector<neededHash_t> ret;
if (!mHaveBase)
{
ret.push_back (std::make_pair (protocol::TMGetObjectByHash::otLEDGER, mHash));
return ret;
}
if (!mHaveState)
{
AccountStateSF filter (mLedger->getLedgerSeq ());
std::vector<uint256> v = mLedger->getNeededAccountStateHashes (4, &filter);
BOOST_FOREACH (uint256 const & h, v)
{
ret.push_back (std::make_pair (protocol::TMGetObjectByHash::otSTATE_NODE, h));
}
}
if (!mHaveTransactions)
{
TransactionStateSF filter (mLedger->getLedgerSeq ());
std::vector<uint256> v = mLedger->getNeededTransactionHashes (4, &filter);
BOOST_FOREACH (uint256 const & h, v)
{
ret.push_back (std::make_pair (protocol::TMGetObjectByHash::otTRANSACTION_NODE, h));
}
}
return ret;
}
Json::Value InboundLedger::getJson (int)
{
Json::Value ret (Json::objectValue);
ScopedLockType sl (mLock, __FILE__, __LINE__);
ret["hash"] = mHash.GetHex ();
if (mComplete)
ret["complete"] = true;
if (mFailed)
ret["failed"] = true;
if (!mComplete && !mFailed)
ret["peers"] = static_cast<int>(mPeers.size());
ret["have_base"] = mHaveBase;
if (mHaveBase)
{
ret["have_state"] = mHaveState;
ret["have_transactions"] = mHaveTransactions;
}
if (mAborted)
ret["aborted"] = true;
ret["timeouts"] = getTimeouts ();
if (mHaveBase && !mHaveState)
{
Json::Value hv (Json::arrayValue);
std::vector<uint256> v = mLedger->getNeededAccountStateHashes (16, NULL);
BOOST_FOREACH (uint256 const & h, v)
{
hv.append (h.GetHex ());
}
ret["needed_state_hashes"] = hv;
}
if (mHaveBase && !mHaveTransactions)
{
Json::Value hv (Json::arrayValue);
std::vector<uint256> v = mLedger->getNeededTransactionHashes (16, NULL);
BOOST_FOREACH (uint256 const & h, v)
{
hv.append (h.GetHex ());
}
ret["needed_transaction_hashes"] = hv;
}
return ret;
}

View File

@@ -0,0 +1,113 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
#ifndef RIPPLE_INBOUNDLEDGER_H
#define RIPPLE_INBOUNDLEDGER_H
// VFALCO TODO Rename to InboundLedger
// A ledger we are trying to acquire
class InboundLedger
: public PeerSet
, public boost::enable_shared_from_this <InboundLedger>
, public CountedObject <InboundLedger>
{
public:
static char const* getCountedObjectName () { return "InboundLedger"; }
typedef boost::shared_ptr <InboundLedger> pointer;
public:
InboundLedger (uint256 const& hash, uint32 seq);
virtual ~InboundLedger ();
bool isBase () const
{
return mHaveBase;
}
bool isAcctStComplete () const
{
return mHaveState;
}
bool isTransComplete () const
{
return mHaveTransactions;
}
bool isDone () const
{
return mAborted || isComplete () || isFailed ();
}
Ledger::ref getLedger ()
{
return mLedger;
}
void abort ()
{
mAborted = true;
}
uint32 getSeq ()
{
return mSeq;
}
// VFALCO TODO Make this the Listener / Observer pattern
bool addOnComplete (FUNCTION_TYPE<void (InboundLedger::pointer)>);
bool takeBase (const std::string& data);
bool takeTxNode (const std::list<SHAMapNode>& IDs, const std::list<Blob >& data,
SHAMapAddNode&);
bool takeTxRootNode (Blob const& data, SHAMapAddNode&);
bool takeAsNode (const std::list<SHAMapNode>& IDs, const std::list<Blob >& data,
SHAMapAddNode&);
bool takeAsRootNode (Blob const& data, SHAMapAddNode&);
void trigger (Peer::ref);
bool tryLocal ();
void addPeers ();
void awaitData ();
void noAwaitData ();
bool checkLocal ();
typedef std::pair <protocol::TMGetObjectByHash::ObjectType, uint256> neededHash_t;
std::vector<neededHash_t> getNeededHashes ();
static void filterNodes (std::vector<SHAMapNode>& nodeIDs, std::vector<uint256>& nodeHashes,
std::set<SHAMapNode>& recentNodes, int max, bool aggressive);
Json::Value getJson (int);
private:
void done ();
void onTimer (bool progress, ScopedLockType& peerSetLock);
void newPeer (Peer::ref peer)
{
trigger (peer);
}
boost::weak_ptr <PeerSet> pmDowncast ();
private:
Ledger::pointer mLedger;
bool mHaveBase;
bool mHaveState;
bool mHaveTransactions;
bool mAborted;
bool mSignaled;
bool mByHash;
beast::Atomic<int> mWaitCount;
uint32 mSeq;
std::set <SHAMapNode> mRecentTXNodes;
std::set <SHAMapNode> mRecentASNodes;
std::vector <FUNCTION_TYPE <void (InboundLedger::pointer)> > mOnComplete;
};
#endif
// vim:ts=4

View File

@@ -0,0 +1,351 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
typedef std::pair<uint256, InboundLedger::pointer> u256_acq_pair;
InboundLedgers::InboundLedgers ()
: mLock (this, "InboundLedger", __FILE__, __LINE__)
, mRecentFailures ("LedgerAcquireRecentFailures", 0, kReacquireIntervalSeconds)
{
}
InboundLedger::pointer InboundLedgers::findCreate (uint256 const& hash, uint32 seq, bool couldBeNew)
{
assert (hash.isNonZero ());
InboundLedger::pointer ret;
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
boost::unordered_map<uint256, InboundLedger::pointer>::iterator it = mLedgers.find (hash);
if (it != mLedgers.end ())
{
ret = it->second;
// FIXME: Should set the sequence if it's not set
}
else
{
ret = boost::make_shared<InboundLedger> (hash, seq);
assert (ret);
mLedgers.insert (std::make_pair (hash, ret));
if (!ret->tryLocal())
{
ret->addPeers ();
ret->setTimer (); // Cannot call in constructor
}
else if (!ret->isFailed ())
{
WriteLog (lsDEBUG, InboundLedger) << "Acquiring ledger we already have locally: " << hash;
Ledger::pointer ledger = ret->getLedger ();
ledger->setClosed ();
ledger->setImmutable ();
getApp().getLedgerMaster ().storeLedger (ledger);
if (couldBeNew)
getApp().getLedgerMaster().checkAccept(ledger);
}
}
}
return ret;
}
InboundLedger::pointer InboundLedgers::find (uint256 const& hash)
{
assert (hash.isNonZero ());
InboundLedger::pointer ret;
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
boost::unordered_map<uint256, InboundLedger::pointer>::iterator it = mLedgers.find (hash);
if (it != mLedgers.end ())
{
ret = it->second;
}
}
return ret;
}
bool InboundLedgers::hasLedger (uint256 const& hash)
{
assert (hash.isNonZero ());
ScopedLockType sl (mLock, __FILE__, __LINE__);
return mLedgers.find (hash) != mLedgers.end ();
}
void InboundLedgers::dropLedger (uint256 const& hash)
{
assert (hash.isNonZero ());
ScopedLockType sl (mLock, __FILE__, __LINE__);
mLedgers.erase (hash);
}
bool InboundLedgers::awaitLedgerData (uint256 const& ledgerHash)
{
InboundLedger::pointer ledger = find (ledgerHash);
if (!ledger)
return false;
ledger->awaitData ();
return true;
}
/*
This gets called when
"We got some data from an inbound ledger"
inboundLedgerTrigger:
"What do we do with this partial data?"
Figures out what to do with the responses to our requests for information.
*/
// means "We got some data from an inbound ledger"
void InboundLedgers::gotLedgerData (Job&, uint256 hash,
boost::shared_ptr<protocol::TMLedgerData> packet_ptr, boost::weak_ptr<Peer> wPeer)
{
protocol::TMLedgerData& packet = *packet_ptr;
Peer::pointer peer = wPeer.lock ();
WriteLog (lsTRACE, InboundLedger) << "Got data (" << packet.nodes ().size () << ") for acquiring ledger: " << hash;
InboundLedger::pointer ledger = find (hash);
if (!ledger)
{
WriteLog (lsTRACE, InboundLedger) << "Got data for ledger we're not acquiring";
if (peer)
peer->applyLoadCharge (LT_InvalidRequest);
return;
}
ledger->noAwaitData ();
if (!peer)
return;
if (packet.type () == protocol::liBASE)
{
if (packet.nodes_size () < 1)
{
WriteLog (lsWARNING, InboundLedger) << "Got empty base data";
peer->applyLoadCharge (LT_InvalidRequest);
return;
}
if (!ledger->takeBase (packet.nodes (0).nodedata ()))
{
WriteLog (lsWARNING, InboundLedger) << "Got invalid base data";
peer->applyLoadCharge (LT_InvalidRequest);
return;
}
SHAMapAddNode san = SHAMapAddNode::useful ();
if ((packet.nodes ().size () > 1) && !ledger->takeAsRootNode (strCopy (packet.nodes (1).nodedata ()), san))
{
WriteLog (lsWARNING, InboundLedger) << "Included ASbase invalid";
}
if ((packet.nodes ().size () > 2) && !ledger->takeTxRootNode (strCopy (packet.nodes (2).nodedata ()), san))
{
WriteLog (lsWARNING, InboundLedger) << "Included TXbase invalid";
}
if (!san.isInvalid ())
{
ledger->progress ();
ledger->trigger (peer);
}
else
WriteLog (lsDEBUG, InboundLedger) << "Peer sends invalid base data";
return;
}
if ((packet.type () == protocol::liTX_NODE) || (packet.type () == protocol::liAS_NODE))
{
std::list<SHAMapNode> nodeIDs;
std::list< Blob > nodeData;
if (packet.nodes ().size () <= 0)
{
WriteLog (lsINFO, InboundLedger) << "Got response with no nodes";
peer->applyLoadCharge (LT_InvalidRequest);
return;
}
for (int i = 0; i < packet.nodes ().size (); ++i)
{
const protocol::TMLedgerNode& node = packet.nodes (i);
if (!node.has_nodeid () || !node.has_nodedata ())
{
WriteLog (lsWARNING, InboundLedger) << "Got bad node";
peer->applyLoadCharge (LT_InvalidRequest);
return;
}
nodeIDs.push_back (SHAMapNode (node.nodeid ().data (), node.nodeid ().size ()));
nodeData.push_back (Blob (node.nodedata ().begin (), node.nodedata ().end ()));
}
SHAMapAddNode ret;
if (packet.type () == protocol::liTX_NODE)
ledger->takeTxNode (nodeIDs, nodeData, ret);
else
ledger->takeAsNode (nodeIDs, nodeData, ret);
if (!ret.isInvalid ())
{
ledger->progress ();
ledger->trigger (peer);
}
else
WriteLog (lsDEBUG, InboundLedger) << "Peer sends invalid node data";
return;
}
WriteLog (lsWARNING, InboundLedger) << "Not sure what ledger data we got";
peer->applyLoadCharge (LT_InvalidRequest);
}
void InboundLedgers::sweep ()
{
mRecentFailures.sweep ();
int const now = UptimeTimer::getInstance ().getElapsedSeconds ();
// Make a list of things to sweep, while holding the lock
std::vector <MapType::mapped_type> stuffToSweep;
std::size_t total;
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
MapType::iterator it (mLedgers.begin ());
total = mLedgers.size ();
stuffToSweep.reserve (total);
while (it != mLedgers.end ())
{
if (it->second->getLastAction () > now)
{
it->second->touch ();
++it;
}
else if ((it->second->getLastAction () + 60) < now)
{
stuffToSweep.push_back (it->second);
// shouldn't cause the actual final delete
// since we are holding a reference in the vector.
it = mLedgers.erase (it);
}
else
{
++it;
}
}
}
WriteLog (lsDEBUG, InboundLedger) <<
"Sweeped " << stuffToSweep.size () <<
" out of " << total << " inbound ledgers.";
}
int InboundLedgers::getFetchCount (int& timeoutCount)
{
timeoutCount = 0;
int ret = 0;
std::vector<u256_acq_pair> inboundLedgers;
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
inboundLedgers.reserve(mLedgers.size());
BOOST_FOREACH (const u256_acq_pair & it, mLedgers)
{
inboundLedgers.push_back(it);
}
}
BOOST_FOREACH (const u256_acq_pair & it, inboundLedgers)
{
if (it.second->isActive ())
{
++ret;
timeoutCount += it.second->getTimeouts ();
}
}
return ret;
}
void InboundLedgers::gotFetchPack (Job&)
{
std::vector<InboundLedger::pointer> acquires;
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
acquires.reserve (mLedgers.size ());
BOOST_FOREACH (const u256_acq_pair & it, mLedgers)
{
assert (it.second);
acquires.push_back (it.second);
}
}
BOOST_FOREACH (const InboundLedger::pointer & acquire, acquires)
{
acquire->checkLocal ();
}
}
void InboundLedgers::clearFailures ()
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
mRecentFailures.clear();
mLedgers.clear();
}
Json::Value InboundLedgers::getInfo()
{
Json::Value ret(Json::objectValue);
std::vector<u256_acq_pair> acquires;
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
acquires.reserve (mLedgers.size ());
BOOST_FOREACH (const u256_acq_pair & it, mLedgers)
{
assert (it.second);
acquires.push_back (it);
}
}
BOOST_FOREACH (const u256_acq_pair& it, acquires)
{
uint32 seq = it.second->getSeq();
if (seq > 1)
ret[lexicalCastThrow <std::string>(seq)] = it.second->getJson(0);
else
ret[it.first.GetHex()] = it.second->getJson(0);
}
return ret;
}
// vim:ts=4

View File

@@ -0,0 +1,74 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
#ifndef RIPPLE_INBOUNDLEDGERS_H
#define RIPPLE_INBOUNDLEDGERS_H
/** Manages the lifetime of inbound ledgers.
@see InboundLedger
*/
// VFALCO TODO Rename to InboundLedgers
// VFALCO TODO Create abstract interface
class InboundLedgers : LeakChecked <InboundLedger>
{
public:
// How long before we try again to acquire the same ledger
static const int kReacquireIntervalSeconds = 300;
InboundLedgers ();
// VFALCO TODO Should this be called findOrAdd ?
//
InboundLedger::pointer findCreate (uint256 const& hash, uint32 seq, bool bCouldBeNew);
InboundLedger::pointer find (uint256 const& hash);
bool hasLedger (LedgerHash const& ledgerHash);
void dropLedger (LedgerHash const& ledgerHash);
bool awaitLedgerData (LedgerHash const& ledgerHash);
// VFALCO TODO Why is hash passed by value?
// VFALCO TODO Remove the dependency on the Peer object.
//
void gotLedgerData (Job&,
LedgerHash hash,
boost::shared_ptr <protocol::TMLedgerData> packet,
boost::weak_ptr<Peer> peer);
int getFetchCount (int& timeoutCount);
void logFailure (uint256 const& h)
{
mRecentFailures.add (h);
}
bool isFailure (uint256 const& h)
{
return mRecentFailures.isPresent (h, false);
}
void clearFailures();
Json::Value getInfo();
void gotFetchPack (Job&);
void sweep ();
private:
typedef boost::unordered_map <uint256, InboundLedger::pointer> MapType;
typedef RippleMutex LockType;
typedef LockType::ScopedLockType ScopedLockType;
LockType mLock;
MapType mLedgers;
KeyCache <uint256, UptimeTimerAdapter> mRecentFailures;
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,271 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
#ifndef RIPPLE_LEDGERENTRYSET_H
#define RIPPLE_LEDGERENTRYSET_H
enum TransactionEngineParams
{
tapNONE = 0x00,
tapNO_CHECK_SIGN = 0x01, // Signature already checked
tapOPEN_LEDGER = 0x10, // Transaction is running against an open ledger
// true = failures are not forwarded, check transaction fee
// false = debit ledger for consumed funds
tapRETRY = 0x20, // This is not the transaction's last pass
// Transaction can be retried, soft failures allowed
tapADMIN = 0x400, // Transaction came from a privileged source
};
enum LedgerEntryAction
{
taaNONE,
taaCACHED, // Unmodified.
taaMODIFY, // Modifed, must have previously been taaCACHED.
taaDELETE, // Delete, must have previously been taaDELETE or taaMODIFY.
taaCREATE, // Newly created.
};
class LedgerEntrySetEntry
: public CountedObject <LedgerEntrySetEntry>
{
public:
static char const* getCountedObjectName () { return "LedgerEntrySetEntry"; }
SLE::pointer mEntry;
LedgerEntryAction mAction;
int mSeq;
LedgerEntrySetEntry (SLE::ref e, LedgerEntryAction a, int s)
: mEntry (e)
, mAction (a)
, mSeq (s)
{
}
};
/** An LES is a LedgerEntrySet.
It's a view into a ledger used while a transaction is processing.
The transaction manipulates the LES rather than the ledger
(because it's cheaper, can be checkpointed, and so on). When the
transaction finishes, the LES is committed into the ledger to make
the modifications. The transaction metadata is built from the LES too.
*/
class LedgerEntrySet
: public CountedObject <LedgerEntrySet>
{
public:
static char const* getCountedObjectName () { return "LedgerEntrySet"; }
LedgerEntrySet (Ledger::ref ledger, TransactionEngineParams tep, bool immutable = false) :
mLedger (ledger), mParams (tep), mSeq (0), mImmutable (immutable)
{
}
LedgerEntrySet () : mParams (tapNONE), mSeq (0), mImmutable (false)
{
}
// set functions
void setImmutable ()
{
mImmutable = true;
}
bool isImmutable () const
{
return mImmutable;
}
LedgerEntrySet duplicate () const; // Make a duplicate of this set
void setTo (const LedgerEntrySet&); // Set this set to have the same contents as another
void swapWith (LedgerEntrySet&); // Swap the contents of two sets
void invalidate ()
{
mLedger.reset ();
}
bool isValid () const
{
return mLedger != nullptr;
}
int getSeq () const
{
return mSeq;
}
TransactionEngineParams getParams () const
{
return mParams;
}
void bumpSeq ()
{
++mSeq;
}
void init (Ledger::ref ledger, uint256 const & transactionID, uint32 ledgerID, TransactionEngineParams params);
void clear ();
Ledger::pointer& getLedger ()
{
return mLedger;
}
Ledger::ref getLedgerRef () const
{
return mLedger;
}
// basic entry functions
SLE::pointer getEntry (uint256 const & index, LedgerEntryAction&);
LedgerEntryAction hasEntry (uint256 const & index) const;
void entryCache (SLE::ref); // Add this entry to the cache
void entryCreate (SLE::ref); // This entry will be created
void entryDelete (SLE::ref); // This entry will be deleted
void entryModify (SLE::ref); // This entry will be modified
bool hasChanges (); // True if LES has any changes
// higher-level ledger functions
SLE::pointer entryCreate (LedgerEntryType letType, uint256 const & uIndex);
SLE::pointer entryCache (LedgerEntryType letType, uint256 const & uIndex);
// Directory functions.
TER dirAdd (
uint64 & uNodeDir, // Node of entry.
uint256 const & uRootIndex,
uint256 const & uLedgerIndex,
FUNCTION_TYPE<void (SLE::ref)> fDescriber);
TER dirDelete (
const bool bKeepRoot,
const uint64 & uNodeDir, // Node item is mentioned in.
uint256 const & uRootIndex,
uint256 const & uLedgerIndex, // Item being deleted
const bool bStable,
const bool bSoft);
bool dirFirst (uint256 const & uRootIndex, SLE::pointer & sleNode, unsigned int & uDirEntry, uint256 & uEntryIndex);
bool dirNext (uint256 const & uRootIndex, SLE::pointer & sleNode, unsigned int & uDirEntry, uint256 & uEntryIndex);
TER dirCount (uint256 const & uDirIndex, uint32 & uCount);
uint256 getNextLedgerIndex (uint256 const & uHash);
uint256 getNextLedgerIndex (uint256 const & uHash, uint256 const & uEnd);
void ownerCountAdjust (const uint160 & uOwnerID, int iAmount, SLE::ref sleAccountRoot = SLE::pointer ());
// Offer functions.
TER offerDelete (uint256 const & uOfferIndex);
TER offerDelete (SLE::ref sleOffer, uint256 const & uOfferIndex, const uint160 & uOwnerID);
// Balance functions.
uint32 rippleTransferRate (const uint160 & uIssuerID);
uint32 rippleTransferRate (const uint160 & uSenderID, const uint160 & uReceiverID, const uint160 & uIssuerID);
STAmount rippleOwed (const uint160 & uToAccountID, const uint160 & uFromAccountID, const uint160 & uCurrencyID);
STAmount rippleLimit (const uint160 & uToAccountID, const uint160 & uFromAccountID, const uint160 & uCurrencyID);
uint32 rippleQualityIn (const uint160 & uToAccountID, const uint160 & uFromAccountID, const uint160 & uCurrencyID,
SField::ref sfLow = sfLowQualityIn, SField::ref sfHigh = sfHighQualityIn);
uint32 rippleQualityOut (const uint160 & uToAccountID, const uint160 & uFromAccountID, const uint160 & uCurrencyID)
{
return rippleQualityIn (uToAccountID, uFromAccountID, uCurrencyID, sfLowQualityOut, sfHighQualityOut);
}
STAmount rippleHolds (const uint160 & uAccountID, const uint160 & uCurrencyID, const uint160 & uIssuerID);
STAmount rippleTransferFee (const uint160 & uSenderID, const uint160 & uReceiverID, const uint160 & uIssuerID, const STAmount & saAmount);
TER rippleCredit (const uint160 & uSenderID, const uint160 & uReceiverID, const STAmount & saAmount, bool bCheckIssuer = true);
TER rippleSend (const uint160 & uSenderID, const uint160 & uReceiverID, const STAmount & saAmount, STAmount & saActual);
STAmount accountHolds (const uint160 & uAccountID, const uint160 & uCurrencyID, const uint160 & uIssuerID);
STAmount accountFunds (const uint160 & uAccountID, const STAmount & saDefault);
TER accountSend (const uint160 & uSenderID, const uint160 & uReceiverID, const STAmount & saAmount);
TER trustCreate (
const bool bSrcHigh,
const uint160 & uSrcAccountID,
const uint160 & uDstAccountID,
uint256 const & uIndex,
SLE::ref sleAccount,
const bool bAuth,
const STAmount & saSrcBalance,
const STAmount & saSrcLimit,
const uint32 uSrcQualityIn = 0,
const uint32 uSrcQualityOut = 0);
TER trustDelete (SLE::ref sleRippleState, const uint160 & uLowAccountID, const uint160 & uHighAccountID);
Json::Value getJson (int) const;
void calcRawMeta (Serializer&, TER result, uint32 index);
// iterator functions
typedef std::map<uint256, LedgerEntrySetEntry>::iterator iterator;
typedef std::map<uint256, LedgerEntrySetEntry>::const_iterator const_iterator;
bool isEmpty () const
{
return mEntries.empty ();
}
std::map<uint256, LedgerEntrySetEntry>::const_iterator begin () const
{
return mEntries.begin ();
}
std::map<uint256, LedgerEntrySetEntry>::const_iterator end () const
{
return mEntries.end ();
}
std::map<uint256, LedgerEntrySetEntry>::iterator begin ()
{
return mEntries.begin ();
}
std::map<uint256, LedgerEntrySetEntry>::iterator end ()
{
return mEntries.end ();
}
static bool intersect (const LedgerEntrySet & lesLeft, const LedgerEntrySet & lesRight);
private:
Ledger::pointer mLedger;
std::map<uint256, LedgerEntrySetEntry> mEntries; // cannot be unordered!
TransactionMetaSet mSet;
TransactionEngineParams mParams;
int mSeq;
bool mImmutable;
LedgerEntrySet (Ledger::ref ledger, const std::map<uint256, LedgerEntrySetEntry>& e,
const TransactionMetaSet & s, int m) :
mLedger (ledger), mEntries (e), mSet (s), mParams (tapNONE), mSeq (m), mImmutable (false)
{
;
}
SLE::pointer getForMod (uint256 const & node, Ledger::ref ledger,
boost::unordered_map<uint256, SLE::pointer>& newMods);
bool threadTx (const RippleAddress & threadTo, Ledger::ref ledger,
boost::unordered_map<uint256, SLE::pointer>& newMods);
bool threadTx (SLE::ref threadTo, Ledger::ref ledger, boost::unordered_map<uint256, SLE::pointer>& newMods);
bool threadOwners (SLE::ref node, Ledger::ref ledger, boost::unordered_map<uint256, SLE::pointer>& newMods);
};
inline LedgerEntrySet::iterator range_begin (LedgerEntrySet& x)
{
return x.begin ();
}
inline LedgerEntrySet::iterator range_end (LedgerEntrySet& x)
{
return x.end ();
}
#endif

View File

@@ -0,0 +1,182 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
// VFALCO TODO replace macros
#ifndef CACHED_LEDGER_NUM
#define CACHED_LEDGER_NUM 96
#endif
#ifndef CACHED_LEDGER_AGE
#define CACHED_LEDGER_AGE 120
#endif
// FIXME: Need to clean up ledgers by index at some point
LedgerHistory::LedgerHistory ()
: mLedgersByHash ("LedgerCache", CACHED_LEDGER_NUM, CACHED_LEDGER_AGE)
, mConsensusValidated ("ConsensusValidated", 64, 300)
{
;
}
void LedgerHistory::addLedger (Ledger::pointer ledger)
{
assert (ledger && ledger->isImmutable ());
assert (ledger->peekAccountStateMap ()->getHash ().isNonZero ());
TaggedCache::ScopedLockType sl (mLedgersByHash.peekMutex (), __FILE__, __LINE__);
mLedgersByHash.canonicalize (ledger->getHash(), ledger, true);
if (ledger->isValidated())
mLedgersByIndex[ledger->getLedgerSeq()] = ledger->getHash();
}
uint256 LedgerHistory::getLedgerHash (uint32 index)
{
TaggedCache::ScopedLockType sl (mLedgersByHash.peekMutex (), __FILE__, __LINE__);
std::map<uint32, uint256>::iterator it (mLedgersByIndex.find (index));
if (it != mLedgersByIndex.end ())
return it->second;
sl.unlock ();
return uint256 ();
}
Ledger::pointer LedgerHistory::getLedgerBySeq (uint32 index)
{
TaggedCache::ScopedLockType sl (mLedgersByHash.peekMutex (), __FILE__, __LINE__);
std::map<uint32, uint256>::iterator it (mLedgersByIndex.find (index));
if (it != mLedgersByIndex.end ())
{
uint256 hash = it->second;
sl.unlock ();
return getLedgerByHash (hash);
}
sl.unlock ();
Ledger::pointer ret (Ledger::loadByIndex (index));
if (!ret)
return ret;
assert (ret->getLedgerSeq () == index);
sl.lock (__FILE__, __LINE__);
assert (ret->isImmutable ());
mLedgersByHash.canonicalize (ret->getHash (), ret);
mLedgersByIndex[ret->getLedgerSeq ()] = ret->getHash ();
return (ret->getLedgerSeq () == index) ? ret : Ledger::pointer ();
}
Ledger::pointer LedgerHistory::getLedgerByHash (uint256 const& hash)
{
Ledger::pointer ret = mLedgersByHash.fetch (hash);
if (ret)
{
assert (ret->isImmutable ());
assert (ret->getHash () == hash); // FIXME: We seem to be getting these
return ret;
}
ret = Ledger::loadByHash (hash);
if (!ret)
return ret;
assert (ret->isImmutable ());
assert (ret->getHash () == hash);
mLedgersByHash.canonicalize (ret->getHash (), ret);
assert (ret->getHash () == hash);
return ret;
}
Ledger::pointer LedgerHistory::canonicalizeLedger (Ledger::pointer ledger, bool save)
{
assert (ledger->isImmutable ());
uint256 h (ledger->getHash ());
if (!save)
{
// return input ledger if not in map, otherwise, return corresponding map ledger
Ledger::pointer ret = mLedgersByHash.fetch (h);
if (ret)
return ret;
return ledger;
}
// save input ledger in map if not in map, otherwise return corresponding map ledger
TaggedCache::ScopedLockType sl (mLedgersByHash.peekMutex (), __FILE__, __LINE__);
mLedgersByHash.canonicalize (h, ledger);
if (ledger->isValidated ())
mLedgersByIndex[ledger->getLedgerSeq ()] = ledger->getHash ();
return ledger;
}
void LedgerHistory::builtLedger (Ledger::ref ledger)
{
LedgerIndex index = ledger->getLedgerSeq();
LedgerHash hash = ledger->getHash();
assert (!hash.isZero());
TaggedCache::ScopedLockType sl(mConsensusValidated.peekMutex(), __FILE__, __LINE__);
boost::shared_ptr< std::pair< LedgerHash, LedgerHash > > entry = boost::make_shared<std::pair< LedgerHash, LedgerHash >>();
mConsensusValidated.canonicalize(index, entry, false);
if (entry->first != hash)
{
if (entry->first.isNonZero() && (entry->first != hash))
{
WriteLog (lsERROR, LedgerMaster) << "MISMATCH: seq=" << index << " built:" << entry->first << " then:" << hash;
}
if (entry->second.isNonZero() && (entry->second != hash))
{
WriteLog (lsERROR, LedgerMaster) << "MISMATCH: seq=" << index << " validated:" << entry->second << " accepted:" << hash;
}
entry->first = hash;
}
}
void LedgerHistory::validatedLedger (Ledger::ref ledger)
{
LedgerIndex index = ledger->getLedgerSeq();
LedgerHash hash = ledger->getHash();
assert (!hash.isZero());
TaggedCache::ScopedLockType sl(mConsensusValidated.peekMutex(), __FILE__, __LINE__);
boost::shared_ptr< std::pair< LedgerHash, LedgerHash > > entry = boost::make_shared<std::pair< LedgerHash, LedgerHash >>();
mConsensusValidated.canonicalize(index, entry, false);
if (entry->second != hash)
{
if (entry->second.isNonZero() && (entry->second != hash))
{
WriteLog (lsERROR, LedgerMaster) << "MISMATCH: seq=" << index << " validated:" << entry->second << " then:" << hash;
}
if (entry->first.isNonZero() && (entry->first != hash))
{
WriteLog (lsERROR, LedgerMaster) << "MISMATCH: seq=" << index << " built:" << entry->first << " validated:" << hash;
}
entry->second = hash;
}
}
void LedgerHistory::tune (int size, int age)
{
mLedgersByHash.setTargetSize (size);
mLedgersByHash.setTargetAge (age);
}
// vim:ts=4

View File

@@ -0,0 +1,52 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
#ifndef RIPPLE_LEDGERHISTORY_H
#define RIPPLE_LEDGERHISTORY_H
// VFALCO TODO Rename to OldLedgers ?
class LedgerHistory : LeakChecked <LedgerHistory>
{
public:
LedgerHistory ();
void addLedger (Ledger::pointer ledger);
float getCacheHitRate ()
{
return mLedgersByHash.getHitRate ();
}
Ledger::pointer getLedgerBySeq (LedgerIndex ledgerIndex);
// VFALCO NOTE shouldn't this call the function above?
LedgerHash getLedgerHash (LedgerIndex ledgerIndex);
Ledger::pointer getLedgerByHash (LedgerHash const& ledgerHash);
Ledger::pointer canonicalizeLedger (Ledger::pointer ledger, bool cache);
void tune (int size, int age);
void sweep ()
{
mLedgersByHash.sweep ();
mConsensusValidated.sweep ();
}
void builtLedger (Ledger::ref);
void validatedLedger (Ledger::ref);
private:
TaggedCacheType <LedgerHash, Ledger, UptimeTimerAdapter> mLedgersByHash;
TaggedCacheType <LedgerIndex, std::pair< LedgerHash, LedgerHash >, UptimeTimerAdapter> mConsensusValidated;
// Maps ledger indexes to the corresponding hash.
std::map <LedgerIndex, LedgerHash> mLedgersByIndex; // validated ledgers
};
#endif

View 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
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 = lexicalCastThrow <int> (value.asString ());
}
catch (...)
{
}
}
error = Json::objectValue;
error["error"] = "Cannot convert to integer";
error["value"] = value;
return false;
}

View 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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,123 @@
//------------------------------------------------------------------------------
/*
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 Peers;
class UniqueNodeList;
class Validators;
class NodeStore;
class JobQueue;
class InboundLedgers;
class LedgerMaster;
class LoadManager;
class NetworkOPs;
class OrderBookDB;
class ProofOfWorkFactory;
class SerializedLedgerEntry;
class TransactionMaster;
class TxQueue;
class LocalCredentials;
class DatabaseCon;
typedef TaggedCacheType <uint256, Blob , UptimeTimerAdapter> NodeCache;
typedef TaggedCacheType <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
*/
typedef RippleRecursiveMutex LockType;
typedef LockType::ScopedLockType ScopedLockType;
virtual LockType& 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 LoadManager& getLoadManager () = 0;
virtual Peers& getPeers () = 0;
virtual ProofOfWorkFactory& getProofOfWorkFactory () = 0;
virtual UniqueNodeList& getUNL () = 0;
virtual Validations& 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 TransactionMaster& getMasterTransaction () = 0;
virtual TxQueue& getTxQueue () = 0;
virtual LocalCredentials& getLocalCredentials () = 0;
virtual DatabaseCon* getRpcDB () = 0;
virtual DatabaseCon* getTxnDB () = 0;
virtual DatabaseCon* getLedgerDB () = 0;
/** Retrieve the "wallet database"
It looks like this is used to store the unique node list.
*/
// VFALCO TODO Rename, document this
// NOTE This will be replaced by class Validators
//
virtual DatabaseCon* getWalletDB () = 0;
virtual bool getSystemTimeOffset (int& offset) = 0;
virtual bool isShutdown () = 0;
virtual bool running () = 0;
virtual void setup () = 0;
virtual void run () = 0;
virtual void stop () = 0;
virtual void sweep () = 0;
};
extern Application& getApp ();
#endif

View File

@@ -0,0 +1,50 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
#ifdef TWICE
#error die
#endif
#define TWICE
FatalErrorReporter::FatalErrorReporter ()
{
FatalError::setReporter (*this);
}
FatalErrorReporter::~FatalErrorReporter ()
{
FatalError::resetReporter (*this);
}
void FatalErrorReporter::reportMessage (String& formattedMessage)
{
Log::out() << formattedMessage.toRawUTF8 ();
}
//------------------------------------------------------------------------------
class FatalErrorReporterTests : public UnitTest
{
public:
FatalErrorReporterTests () : UnitTest ("FatalErrorReporter", "ripple", runManual)
{
}
void runTest ()
{
beginTestCase ("report");
FatalErrorReporter reporter;
// We don't really expect the program to run after this
// but the unit test is here so you can manually test it.
FatalError ("The unit test intentionally failed", __FILE__, __LINE__);
}
};
static FatalErrorReporterTests fatalErrorReporterTests;

View File

@@ -0,0 +1,30 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
#ifndef RIPPLE_FATALERRORREPORTER_H_INCLUDED
#define RIPPLE_FATALERRORREPORTER_H_INCLUDED
/** FatalError reporter.
This writes the details to standard error and the log. The reporter is
installed for the lifetime of the object so typically you would put this
at the top of main().
An alternative is to make it a global variable but for this to cover all
possible cases, there can be no other global variables with non trivial
constructors that can report a fatal error. Also, the Log would need
to be guaranteed to be set up for this handler to work.
*/
class FatalErrorReporter : public FatalError::Reporter
{
public:
FatalErrorReporter ();
~FatalErrorReporter ();
void reportMessage (String& formattedMessage);
};
#endif

View File

@@ -0,0 +1,437 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
SETUP_LOG (LoadManager)
//------------------------------------------------------------------------------
class LoadManagerImp
: public LoadManager
, 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:
LoadManagerImp ()
: mLock (this, "LoadManagerImp", __FILE__, __LINE__)
, 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:
~LoadManagerImp ()
{
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
{
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
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
{
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
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 ();
ScopedLockType sl (mLock, __FILE__, __LINE__);
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 ()
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
mDeadLock = UptimeTimer::getInstance ().getElapsedSeconds ();
}
void activateDeadlockDetector ()
{
mArmed = true;
}
static void logDeadlock (int dlTime)
{
WriteLog (lsWARNING, LoadManager) << "Server stalled for " << dlTime << " seconds.";
#if RIPPLE_TRACK_MUTEXES
StringArray report;
TrackedMutex::generateGlobalBlockedReport (report);
if (report.size () > 0)
{
report.insert (0, String::empty);
report.insert (-1, String::empty);
Log::print (report);
}
#endif
}
private:
// VFALCO TODO These used to be public but are apparently not used. Find out why.
/*
int getCreditRate () const
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
return mCreditRate;
}
int getCreditLimit () const
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
return mCreditLimit;
}
int getDebitWarn () const
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
return mDebitWarn;
}
int getDebitLimit () const
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
return mDebitLimit;
}
void setCreditRate (int r)
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
mCreditRate = r;
}
void setCreditLimit (int r)
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
mCreditLimit = r;
}
void setDebitWarn (int r)
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
mDebitWarn = r;
}
void setDebitLimit (int r)
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
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?
ScopedLockType sl (mLock, __FILE__, __LINE__);
// 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
//
int const reportingIntervalSeconds = 10;
if (mArmed && (timeSpentDeadlocked >= reportingIntervalSeconds))
{
// Report the deadlocked condition every 10 seconds
if ((timeSpentDeadlocked % reportingIntervalSeconds) == 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:
typedef RippleMutex LockType;
typedef LockType::ScopedLockType ScopedLockType;
LockType mLock;
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
std::vector <Cost> mCosts;
};
//------------------------------------------------------------------------------
LoadManager* LoadManager::New ()
{
ScopedPointer <LoadManager> object (new LoadManagerImp);
return object.release ();
}

View File

@@ -0,0 +1,87 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
#ifndef RIPPLE_LOADMANAGER_H_INCLUDEd
#define RIPPLE_LOADMANAGER_H_INCLUDEd
/** 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 LoadManager
{
public:
/** Create a new manager.
The manager thread begins running immediately.
@note The thresholds for warnings and punishments are in
the ctor-initializer
*/
static LoadManager* New ();
/** Destroy the manager.
The destructor returns only after the thread has stopped.
*/
virtual ~LoadManager () { }
/** Start the associated thread.
This is here to prevent the deadlock detector from activating during
a lengthy program initialization.
*/
// VFALCO TODO Simplify the two stage initialization to one stage (construction).
// NOTE In stand-alone mode the load manager thread isn't started
virtual void startThread () = 0;
/** Turn on deadlock detection.
The deadlock detector begins in a disabled state. After this function
is called, it will report deadlocks using a separate thread whenever
the reset function is not called at least once per 10 seconds.
@see resetDeadlockDetector
*/
// VFALCO NOTE it seems that the deadlock detector has an "armed" state to prevent it
// from going off during program startup if there's a lengthy initialization
// operation taking place?
//
virtual void activateDeadlockDetector () = 0;
/** Reset the deadlock detection timer.
A dedicated thread monitors the deadlock timer, and if too much
time passes it will produce log warnings.
*/
virtual void resetDeadlockDetector () = 0;
/** Update an endpoint to reflect an imposed load.
The balance of the endpoint is adjusted based on the heuristic cost
of the indicated load.
@return `true` if the endpoint should be warned or punished.
*/
virtual bool applyLoadCharge (LoadSource& sourceToAdjust, LoadType loadToImpose) const = 0;
// VFALCO TODO Eliminate these two functions and just make applyLoadCharge()
// return a LoadSource::Disposition
//
virtual bool shouldWarn (LoadSource&) const = 0;
virtual bool shouldCutoff (LoadSource&) const = 0;
};
#endif

View File

@@ -0,0 +1,156 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
LocalCredentials::LocalCredentials ()
: 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 (!getConfig ().QUIET)
Log::out() << "NodeIdentity: " << mNodePublicKey.humanNodePublic ();
getApp().getUNL ().start ();
}
// Retrieve network identity.
bool LocalCredentials::nodeIdentityLoad ()
{
Database* db = getApp().getWalletDB ()->getDB ();
DeprecatedScopedLock 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);
db->endIterRows ();
bSuccess = true;
}
if (getConfig ().NODE_PUB.isValid () && getConfig ().NODE_PRIV.isValid ())
{
mNodePublicKey = getConfig ().NODE_PUB;
mNodePrivateKey = getConfig ().NODE_PRIV;
}
return bSuccess;
}
// Create and store a network identity.
bool LocalCredentials::nodeIdentityCreate ()
{
if (!getConfig ().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
std::string strDh512 (RippleSSLContext::getRawDHParams (512));
#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 ();
DeprecatedScopedLock 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 (!getConfig ().QUIET)
Log::out() << "NodeIdentity: Created.";
return true;
}
bool LocalCredentials::dataDelete (const std::string& strKey)
{
Database* db = getApp().getRpcDB ()->getDB ();
DeprecatedScopedLock 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 ();
DeprecatedScopedLock 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 ();
DeprecatedScopedLock sl (getApp().getRpcDB ()->getDBLock ());
bool bSuccess = false;
return (db->executeSQL (str (boost::format ("REPLACE INTO RPCData (Key, Value) VALUES (%s,%s);")
% sqlEscape (strKey)
% sqlEscape (strValue)
)));
return bSuccess;
}
// vim:ts=4

View File

@@ -0,0 +1,55 @@
//------------------------------------------------------------------------------
/*
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 : public 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;
}
// 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;
LedgerIndex mLedger; // ledger we last synched to
};
#endif
// vim:ts=4

View File

@@ -0,0 +1,476 @@
//------------------------------------------------------------------------------
/*
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 (getConfig ().RPC_STARTUP.isArray ())
{
for (int i = 0; i != getConfig ().RPC_STARTUP.size (); ++i)
{
const Json::Value& jvCommand = getConfig ().RPC_STARTUP[i];
if (!getConfig ().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, Config::ADMIN, &loadType);
if (!getConfig ().QUIET)
Log::out() << "Result: " << jvResult;
}
}
getApp().run (); // Blocks till we get a stop RPC.
}
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:
explicit RippleUnitTests (bool shouldLog)
: m_shouldLog (shouldLog)
{
// VFALCO NOTE It sucks that we have to do this but some
// code demands the Application object exists.
//
// TODO To find out who, just change the #if 1 to #if 0
#if 1
setupConfigForUnitTests (&getConfig ());
getApp ();
#endif
setAssertOnFailure (false);
}
void logMessage (String const& message)
{
if (m_shouldLog)
{
#if BEAST_MSVC
if (beast_isRunningUnderDebugger ())
{
Logger::outputDebugString (message);
}
else
{
std::cout << message.toStdString () << std::endl;
}
#else
std::cout << message.toStdString () << std::endl;
#endif
}
}
private:
void setupConfigForUnitTests (Config* config)
{
config->nodeDatabase = parseDelimitedKeyValueString ("type=memory");
config->ephemeralNodeDatabase = StringPairArray ();
config->importNodeDatabase = StringPairArray ();
}
private:
bool const m_shouldLog;
};
static int runUnitTests (String const& match, String const& format)
{
bool const shouldLog = format != "junit";
if (format != "junit" && format != "text" && format != "")
{
String s;
s << "Warning, unknown unittest-format='" << format << "'";
Log::out () << s.toStdString ();
}
RippleUnitTests tr (shouldLog);
tr.runSelectedTests (match);
if (format == "junit")
{
UnitTestUtilities::JUnitXMLFormatter f (tr);
String const s = f.createDocumentString ();
std::cout << s.toStdString ();
}
else
{
UnitTests::Results const& r (tr.getResults ());
String s;
s << "Summary: " <<
String (r.suites.size ()) << " suites, " <<
String (r.cases) << " cases, " <<
String (r.tests) << " tests, " <<
String (r.failures) << " failure" << ((r.failures != 1) ? "s" : "") << ".";
tr.logMessage (s);
}
return tr.anyTestsFailed () ? EXIT_FAILURE : EXIT_SUCCESS;
}
//------------------------------------------------------------------------------
int RippleMain::run (int argc, char const* const* argv)
{
FatalErrorReporter reporter;
//
// 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 RIPPLE_DUMP_LEAKS_ON_EXIT
Debug::setHeapReportLeaks (true);
#else
Debug::setHeapReportLeaks (false);
#endif
#if 0
// 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", po::value <std::string> ()->implicit_value (""), "Perform unit tests.")
("unittest-format", po::value <std::string> ()->implicit_value ("text"), "Format unit test output. Choices are 'text', 'junit'")
("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);
// NOTE: These must be added before the
// Application object is created.
//
NodeStore::addAvailableBackends ();
// VFALCO NOTE SqliteBackendFactory is here because it has
// dependencies like SqliteDatabase and DatabaseCon
//
NodeStore::addBackendFactory (SqliteBackendFactory::getInstance ());
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;
}
// Use a watchdog process unless we're invoking a stand alone type of mode
//
if (HaveSustain ()
&& !iResult
&& !vm.count ("parameters")
&& !vm.count ("fg")
&& !vm.count ("standalone")
&& !vm.count ("unittest"))
{
std::string logMe = DoSustain (getConfig ().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.
// The unit tests will exit the application with an appropriate return code.
//
if (vm.count ("unittest"))
{
String format;
if (vm.count ("unittest-format"))
format = vm ["unittest-format"].as <std::string> ();
return runUnitTests (vm ["unittest"].as <std::string> (), format);
}
if (!iResult)
{
getConfig ().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"))
{
getConfig ().RUN_STANDALONE = true;
getConfig ().LEDGER_HISTORY = 0;
}
}
if (vm.count ("start")) getConfig ().START_UP = Config::FRESH;
// Handle a one-time import option
//
if (vm.count ("import"))
{
String const optionString (vm ["import"].as <std::string> ());
getConfig ().importNodeDatabase = parseDelimitedKeyValueString (optionString);
}
if (vm.count ("ledger"))
{
getConfig ().START_LEDGER = vm["ledger"].as<std::string> ();
if (vm.count("replay"))
getConfig ().START_UP = Config::REPLAY;
else
getConfig ().START_UP = Config::LOAD;
}
else if (vm.count ("load"))
{
getConfig ().START_UP = Config::LOAD;
}
else if (vm.count ("net"))
{
getConfig ().START_UP = Config::NETWORK;
if (getConfig ().VALIDATION_QUORUM < 2)
getConfig ().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"))
{
getConfig ().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.
getConfig ().setRpcPort (vm ["rpc_port"].as <int> ());
}
}
if (iResult == 0)
{
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 = RPCCall::fromCommandLine (vCmd);
}
}
if (1 == iResult && !vm.count ("quiet"))
printHelp (desc);
return iResult;
}

View File

@@ -0,0 +1,16 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
#ifndef RIPPLE_RIPPLEMAIN_H_INCLUDED
#define RIPPLE_RIPPLEMAIN_H_INCLUDED
class RippleMain : public Main
{
public:
int run (int argc, char const* const* argv);
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,295 @@
//------------------------------------------------------------------------------
/*
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;
// 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.
//
/** Provides server functionality for clients.
Clients include backend applications, local commands, and connected
clients. This class acts as a proxy, fulfilling the command with local
data if possible, or asking the network and returning the results if
needed.
A backend application or local client can trust a local instance of
rippled / NetworkOPs. However, client software connecting to non-local
instances of rippled will need to be hardened to protect against hostile
or unreliable servers.
*/
class NetworkOPs : public InfoSub::Source
{
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
};
// VFALCO TODO Fix OrderBookDB to not need this unrelated type.
//
typedef boost::unordered_map <uint64, InfoSub::wptr> SubMapType;
public:
// VFALCO TODO Make LedgerMaster a SharedPtr or a reference.
//
static NetworkOPs* New (LedgerMaster& ledgerMaster);
virtual ~NetworkOPs () { }
//--------------------------------------------------------------------------
//
// Network information
//
// Our best estimate of wall time in seconds from 1/1/2000
virtual uint32 getNetworkTimeNC () = 0;
// Our best estimate of current ledger close time
virtual uint32 getCloseTimeNC () = 0;
// Use *only* to timestamp our own validation
virtual uint32 getValidationTimeNC () = 0;
virtual void closeTimeOffset (int) = 0;
virtual boost::posix_time::ptime getNetworkTimePT () = 0;
virtual uint32 getLedgerID (uint256 const& hash) = 0;
virtual uint32 getCurrentLedgerID () = 0;
virtual OperatingMode getOperatingMode () = 0;
virtual std::string strOperatingMode () = 0;
virtual Ledger::ref getClosedLedger () = 0;
virtual Ledger::ref getValidatedLedger () = 0;
virtual Ledger::ref getPublishedLedger () = 0;
virtual Ledger::ref getCurrentLedger () = 0;
virtual Ledger::ref getCurrentSnapshot () = 0;
virtual Ledger::pointer getLedgerByHash (uint256 const& hash) = 0;
virtual Ledger::pointer getLedgerBySeq (const uint32 seq) = 0;
virtual void missingNodeInLedger (const uint32 seq) = 0;
virtual uint256 getClosedLedgerHash () = 0;
// Do we have this inclusive range of ledgers in our database
virtual bool haveLedgerRange (uint32 from, uint32 to) = 0;
virtual bool haveLedger (uint32 seq) = 0;
virtual uint32 getValidatedSeq () = 0;
virtual bool isValidated (uint32 seq) = 0;
virtual bool isValidated (uint32 seq, uint256 const& hash) = 0;
virtual bool isValidated (Ledger::ref l) = 0;
virtual bool getValidatedRange (uint32& minVal, uint32& maxVal) = 0;
virtual bool getFullValidatedRange (uint32& minVal, uint32& maxVal) = 0;
virtual SerializedValidation::ref getLastValidation () = 0;
virtual void setLastValidation (SerializedValidation::ref v) = 0;
virtual SLE::pointer getSLE (Ledger::pointer lpLedger, uint256 const& uHash) = 0;
virtual SLE::pointer getSLEi (Ledger::pointer lpLedger, uint256 const& uHash) = 0;
//--------------------------------------------------------------------------
//
// Transaction processing
//
// must complete immediately
// VFALCO TODO Make this a TxCallback structure
typedef FUNCTION_TYPE<void (Transaction::pointer, TER)> stCallback;
virtual void submitTransaction (Job&, SerializedTransaction::pointer,
stCallback callback = stCallback ()) = 0;
virtual Transaction::pointer submitTransactionSync (Transaction::ref tpTrans,
bool bAdmin, bool bFailHard, bool bSubmit) = 0;
virtual void runTransactionQueue () = 0;
virtual Transaction::pointer processTransaction (Transaction::pointer,
bool bAdmin, bool bFailHard, stCallback) = 0;
virtual Transaction::pointer processTransaction (Transaction::pointer transaction,
bool bAdmin, bool bFailHard) = 0;
virtual Transaction::pointer findTransactionByID (uint256 const& transactionID) = 0;
virtual int findTransactionsByDestination (std::list<Transaction::pointer>&,
const RippleAddress& destinationAccount, uint32 startLedgerSeq,
uint32 endLedgerSeq, int maxTransactions) = 0;
//--------------------------------------------------------------------------
//
// Account functions
//
virtual AccountState::pointer getAccountState (Ledger::ref lrLedger,
const RippleAddress& accountID) = 0;
virtual SLE::pointer getGenerator (Ledger::ref lrLedger,
uint160 const& uGeneratorID) = 0;
//--------------------------------------------------------------------------
//
// Directory functions
//
virtual STVector256 getDirNodeInfo (Ledger::ref lrLedger,
uint256 const& uRootIndex, uint64& uNodePrevious, uint64& uNodeNext) = 0;
//--------------------------------------------------------------------------
//
// Owner functions
//
virtual Json::Value getOwnerInfo (Ledger::pointer lpLedger,
const RippleAddress& naAccount) = 0;
//--------------------------------------------------------------------------
//
// Book functions
//
virtual 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) = 0;
//--------------------------------------------------------------------------
// ledger proposal/close functions
virtual void processTrustedProposal (LedgerProposal::pointer proposal,
boost::shared_ptr<protocol::TMProposeSet> set, RippleAddress nodePublic,
uint256 checkLedger, bool sigGood) = 0;
virtual SHAMapAddNode gotTXData (const boost::shared_ptr<Peer>& peer,
uint256 const& hash, const std::list<SHAMapNode>& nodeIDs,
const std::list< Blob >& nodeData) = 0;
virtual bool recvValidation (SerializedValidation::ref val,
const std::string& source) = 0;
virtual void takePosition (int seq, SHAMap::ref position) = 0;
virtual SHAMap::pointer getTXMap (uint256 const& hash) = 0;
virtual bool hasTXSet (const boost::shared_ptr<Peer>& peer,
uint256 const& set, protocol::TxSetStatus status) = 0;
virtual void mapComplete (uint256 const& hash, SHAMap::ref map) = 0;
virtual bool stillNeedTXSet (uint256 const& hash) = 0;
// Fetch packs
virtual void makeFetchPack (Job&, boost::weak_ptr<Peer> peer,
boost::shared_ptr<protocol::TMGetObjectByHash> request,
Ledger::pointer wantLedger, Ledger::pointer haveLedger, uint32 uUptime) = 0;
virtual bool shouldFetchPack (uint32 seq) = 0;
virtual void gotFetchPack (bool progress, uint32 seq) = 0;
virtual void addFetchPack (uint256 const& hash, boost::shared_ptr< Blob >& data) = 0;
virtual bool getFetchPack (uint256 const& hash, Blob& data) = 0;
virtual int getFetchSize () = 0;
virtual void sweepFetchPack () = 0;
// network state machine
virtual void endConsensus (bool correctLCL) = 0;
virtual void setStandAlone () = 0;
virtual void setStateTimer () = 0;
virtual void newLCL (int proposers, int convergeTime, uint256 const& ledgerHash) = 0;
// VFALCO TODO rename to setNeedNetworkLedger
virtual void needNetworkLedger () = 0;
virtual void clearNeedNetworkLedger () = 0;
virtual bool isNeedNetworkLedger () = 0;
virtual bool isFull () = 0;
virtual void setProposing (bool isProposing, bool isValidating) = 0;
virtual bool isProposing () = 0;
virtual bool isValidating () = 0;
virtual bool isFeatureBlocked () = 0;
virtual void setFeatureBlocked () = 0;
virtual void consensusViewChange () = 0;
virtual int getPreviousProposers () = 0;
virtual int getPreviousConvergeTime () = 0;
virtual uint32 getLastCloseTime () = 0;
virtual void setLastCloseTime (uint32 t) = 0;
virtual Json::Value getConsensusInfo () = 0;
virtual Json::Value getServerInfo (bool human, bool admin) = 0;
virtual void clearLedgerFetch () = 0;
virtual Json::Value getLedgerFetchInfo () = 0;
virtual uint32 acceptLedger () = 0;
typedef boost::unordered_map <uint160, std::list<LedgerProposal::pointer> > Proposals;
virtual Proposals& peekStoredProposals () = 0;
virtual void storeProposal (LedgerProposal::ref proposal,
const RippleAddress& peerPublic) = 0;
virtual uint256 getConsensusLCL () = 0;
virtual void reportFeeChange () = 0;
//Helper function to generate SQL query to get transactions
virtual 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) = 0;
// client information retrieval functions
typedef std::vector< std::pair<Transaction::pointer, TransactionMetaSet::pointer> > AccountTxs;
virtual AccountTxs getAccountTxs (const RippleAddress& account,
int32 minLedger, int32 maxLedger, bool descending, uint32 offset,
int limit, bool bAdmin) = 0;
typedef std::vector< std::pair<Transaction::pointer, TransactionMetaSet::pointer> > TxsAccount;
virtual TxsAccount getTxsAccount (const RippleAddress& account,
int32 minLedger, int32 maxLedger, bool forward, Json::Value& token,
int limit, bool bAdmin) = 0;
typedef boost::tuple<std::string, std::string, uint32> txnMetaLedgerType;
typedef std::vector<txnMetaLedgerType> MetaTxsList;
virtual MetaTxsList getAccountTxsB (const RippleAddress& account,
int32 minLedger, int32 maxLedger, bool descending,
uint32 offset, int limit, bool bAdmin) = 0;
virtual MetaTxsList getTxsAccountB (const RippleAddress& account,
int32 minLedger, int32 maxLedger, bool forward,
Json::Value& token, int limit, bool bAdmin) = 0;
virtual std::vector<RippleAddress> getLedgerAffectedAccounts (uint32 ledgerSeq) = 0;
virtual uint32 countAccountTxs (const RippleAddress& account,
int32 minLedger, int32 maxLedger) = 0;
//--------------------------------------------------------------------------
//
// Monitoring: publisher side
//
virtual void pubLedger (Ledger::ref lpAccepted) = 0;
virtual void pubProposedTransaction (Ledger::ref lpCurrent,
SerializedTransaction::ref stTxn, TER terResult) = 0;
};
#endif

View File

@@ -0,0 +1,20 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
#ifndef RIPPLE_POWRESULT_H_INCLUDED
#define RIPPLE_POWRESULT_H_INCLUDED
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
};
#endif

View File

@@ -0,0 +1,179 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
SETUP_LOG (ProofOfWork)
// VFALCO TODO Move these to a header
const uint256 ProofOfWork::sMinTarget ("00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
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 = lexicalCast <int> (fields[2]);
}
bool ProofOfWork::isValid () const
{
if ((mIterations <= kMaxIterations) && (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 > kMaxIterations) || (target < sMinTarget))
{
WriteLog (lsINFO, ProofOfWork) << "Iterations:" << iterations;
WriteLog (lsINFO, ProofOfWork) << "MaxIterat: " << kMaxIterations;
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 > kMaxIterations)
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);
}
//------------------------------------------------------------------------------
bool ProofOfWork::calcResultInfo (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;
}

View File

@@ -0,0 +1,65 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
#ifndef RIPPLE_PROOFOFWORK_H
#define RIPPLE_PROOFOFWORK_H
class ProofOfWork : LeakChecked <ProofOfWork>
{
public:
enum
{
kMaxIterations = (1 << 23)
};
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 * kMaxIterations) 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);
static bool calcResultInfo (PowResult powCode, std::string& strToken, std::string& strHuman);
private:
std::string mToken;
uint256 mChallenge;
uint256 mTarget;
int mIterations;
static const uint256 sMinTarget;
static const int maxIterations;
};
#endif
// vim:ts=4

View File

@@ -0,0 +1,411 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
class ProofOfWorkFactoryImp
: public ProofOfWorkFactory
, public LeakChecked <ProofOfWorkFactoryImp>
{
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;
//--------------------------------------------------------------------------
ProofOfWorkFactoryImp ()
: mLock (this, "PoWFactory", __FILE__, __LINE__)
, mValidTime (180)
{
setDifficulty (1);
RandomNumbers::getInstance ().fillBytes (mSecret.begin (), mSecret.size ());
}
//--------------------------------------------------------------------------
enum
{
numPowEntries = kMaxDifficulty + 1
};
struct PowEntry
{
const char* target;
int iterations;
};
typedef std::vector <PowEntry> PowEntries;
static PowEntries const& getPowEntries ()
{
struct StaticPowEntries
{
StaticPowEntries ()
{
// VFALCO TODO Make this array know its own size.
//
PowEntry entries [numPowEntries] =
{
// 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
};
data.reserve (numPowEntries);
for (int i = 0; i < numPowEntries; ++i)
data.push_back (entries [i]);
}
std::vector <PowEntry> data;
};
static StaticPowEntries list;
return list.data;
}
//--------------------------------------------------------------------------
static int getPowEntry (uint256 const& target, int iterations)
{
PowEntries const& entries (getPowEntries ());
for (int i = 0; i < numPowEntries; ++i)
{
if (entries [i].iterations == iterations)
{
uint256 t;
t.SetHex (entries [i].target);
if (t == target)
return i;
}
}
return -1;
}
//--------------------------------------------------------------------------
ProofOfWork 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 ());
ScopedLockType sl (mLock, __FILE__, __LINE__);
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 checkProof (const std::string& token, uint256 const& solution)
{
// VFALCO COmmented this out because Dave said it wasn't used
// and also we dont have the lexicalCast from a vector of strings to a time_t
// 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;
#if 0
// Broken with lexicalCast<> changes
t = lexicalCast <time_t> (fields[3]);
#else
t = static_cast <time_t> (lexicalCast <uint64> (fields [3]));
#endif
time_t now = time (NULL);
int iterations = lexicalCast <int> (fields[2]);
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
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;
}
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
if (!mSolvedChallenges.insert (powMap_vt (now, challenge)).second)
{
WriteLog (lsDEBUG, ProofOfWork) << "PoW " << token << " has been reused";
return powREUSED;
}
}
return powOK;
}
//--------------------------------------------------------------------------
void sweep ()
{
time_t expire = time (NULL) - mValidTime;
ScopedLockType sl (mLock, __FILE__, __LINE__);
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 loadHigh ()
{
time_t now = time (NULL);
ScopedLockType sl (mLock, __FILE__, __LINE__);
if (mLastDifficultyChange == now)
return;
if (mPowEntry == 30)
return;
++mPowEntry;
mLastDifficultyChange = now;
}
//--------------------------------------------------------------------------
void loadLow ()
{
time_t now = time (NULL);
ScopedLockType sl (mLock, __FILE__, __LINE__);
if (mLastDifficultyChange == now)
return;
if (mPowEntry == 0)
return;
--mPowEntry;
mLastDifficultyChange = now;
}
//--------------------------------------------------------------------------
void setDifficulty (int i)
{
assert ((i >= 0) && (i <= kMaxDifficulty));
time_t now = time (NULL);
ScopedLockType sl (mLock, __FILE__, __LINE__);
mPowEntry = i;
PowEntries const& entries (getPowEntries ());
mIterations = entries [i].iterations;
mTarget.SetHex (entries [i].target);
mLastDifficultyChange = now;
}
//--------------------------------------------------------------------------
uint64 getDifficulty ()
{
return ProofOfWork::getDifficulty (mTarget, mIterations);
}
uint256 const& getSecret () const
{
return mSecret;
}
void setSecret (uint256 const& secret)
{
mSecret = secret;
}
private:
typedef RippleMutex LockType;
typedef LockType::ScopedLockType ScopedLockType;
LockType mLock;
uint256 mSecret;
int mIterations;
uint256 mTarget;
time_t mLastDifficultyChange;
int mValidTime;
int mPowEntry;
powMap_t mSolvedChallenges;
};
//------------------------------------------------------------------------------
ProofOfWorkFactory* ProofOfWorkFactory::New ()
{
ScopedPointer <ProofOfWorkFactory> object (new ProofOfWorkFactoryImp);
return object.release ();
}
//------------------------------------------------------------------------------
class ProofOfWorkTests : public UnitTest
{
public:
ProofOfWorkTests () : UnitTest ("ProofOfWork", "ripple", runManual)
{
}
void runTest ()
{
using namespace ripple;
ProofOfWorkFactoryImp gen;
ProofOfWork pow = gen.getProof ();
String s;
s << "solve difficulty " << String (pow.getDifficulty ());
beginTestCase ("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;

View File

@@ -0,0 +1,41 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
#ifndef RIPPLE_PROOFOFWORKFACTORY_H_INCLUDED
#define RIPPLE_PROOFOFWORKFACTORY_H_INCLUDED
class ProofOfWorkFactory
{
public:
enum
{
kMaxDifficulty = 30,
};
static ProofOfWorkFactory* New ();
virtual ~ProofOfWorkFactory () { }
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;
};
#endif

View File

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

View 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

View 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

View 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

View 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

View 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

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

View 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

View File

@@ -0,0 +1,544 @@
//------------------------------------------------------------------------------
/*
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;
typedef RippleMutex LockType;
typedef LockType::ScopedLockType ScopedLockType;
LockType mLock;
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)
: mLock (this, "Features", __FILE__, __LINE__)
, 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 ("';");
DeprecatedScopedLock 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)
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
FeatureState* s = getCreateFeature (feature, true);
if (s->mVetoed)
return false;
s->mVetoed = true;
return true;
}
bool Features::unVetoFeature (uint256 const& feature)
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
FeatureState* s = getCreateFeature (feature, false);
if (!s || !s->mVetoed)
return false;
s->mVetoed = false;
return true;
}
bool Features::enableFeature (uint256 const& feature)
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
FeatureState* s = getCreateFeature (feature, true);
if (s->mEnabled)
return false;
s->mEnabled = true;
return true;
}
bool Features::disableFeature (uint256 const& feature)
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
FeatureState* s = getCreateFeature (feature, false);
if (!s || !s->mEnabled)
return false;
s->mEnabled = false;
return true;
}
bool Features::isFeatureEnabled (uint256 const& feature)
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
FeatureState* s = getCreateFeature (feature, false);
return s && s->mEnabled;
}
bool Features::isFeatureSupported (uint256 const& feature)
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
FeatureState* s = getCreateFeature (feature, false);
return s && s->mSupported;
}
Features::featureList_t Features::getVetoedFeatures ()
{
featureList_t ret;
ScopedLockType sl (mLock, __FILE__, __LINE__);
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;
ScopedLockType sl (mLock, __FILE__, __LINE__);
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;
ScopedLockType sl (mLock, __FILE__, __LINE__);
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;
ScopedLockType sl (mLock, __FILE__, __LINE__);
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;
ScopedLockType sl (mLock, __FILE__, __LINE__);
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 ())
{
DeprecatedScopedLock 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)
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
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)
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
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);
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
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;
ScopedLockType sl (mLock, __FILE__, __LINE__);
setJson (ret[feature.GetHex ()] = Json::objectValue, *getCreateFeature (feature, true));
return ret;
}
template<typename INT> class VotableInteger
{
protected:
INT mCurrent; // The current setting
INT mTarget; // The setting we want
std::map<INT, int> mVoteMap;
public:
VotableInteger (INT current, INT target) : mCurrent (current), mTarget (target)
{
++mVoteMap[mTarget]; // Add our vote
}
bool mayVote ()
{
return mCurrent != mTarget; // If we love the current setting, we will not vote
}
void addVote (INT vote)
{
++mVoteMap[vote];
}
void noVote ()
{
addVote (mCurrent);
}
INT getVotes ()
{
INT ourVote = mCurrent;
int weight = 0;
typedef typename std::map<INT, int>::value_type mapVType;
BOOST_FOREACH (const mapVType & value, mVoteMap)
{
// Take most voted value between current and target, inclusive
// FIXME: Should take best value that can get a significant majority
if ((value.first <= std::max (mTarget, mCurrent)) &&
(value.first >= std::min (mTarget, mCurrent)) &&
(value.second > weight))
{
ourVote = value.first;
weight = value.second;
}
}
return ourVote;
}
};
IFeatures* IFeatures::New (uint32 majorityTime, int majorityFraction)
{
return new Features (majorityTime, majorityFraction);
}
// vim:ts=4

View File

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

View File

@@ -0,0 +1,229 @@
//------------------------------------------------------------------------------
/*
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)
: mLock (this, "HashRouter", __FILE__, __LINE__)
, 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);
typedef RippleMutex LockType;
typedef LockType::ScopedLockType ScopedLockType;
LockType mLock;
// 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)
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
bool created;
findCreateEntry (index, created);
return created;
}
HashRouter::Entry HashRouter::getEntry (uint256 const& index)
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
bool created;
return findCreateEntry (index, created);
}
bool HashRouter::addSuppressionPeer (uint256 const& index, uint64 peer)
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
bool created;
findCreateEntry (index, created).addPeer (peer);
return created;
}
bool HashRouter::addSuppressionPeer (uint256 const& index, uint64 peer, int& flags)
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
bool created;
Entry& s = findCreateEntry (index, created);
s.addPeer (peer);
flags = s.getFlags ();
return created;
}
int HashRouter::getFlags (uint256 const& index)
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
bool created;
return findCreateEntry (index, created).getFlags ();
}
bool HashRouter::addSuppressionFlags (uint256 const& index, int flag)
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
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);
ScopedLockType sl (mLock, __FILE__, __LINE__);
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)
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
bool created;
Entry& s = findCreateEntry (index, created);
if ((s.getFlags () & flag) == flag)
return false;
s.swapSet (peers);
s.setFlag (flag);
return true;
}
IHashRouter* IHashRouter::New (int holdTime)
{
return new HashRouter (holdTime);
}

View File

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

View 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

View 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

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

View 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

View 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

View 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

View File

@@ -0,0 +1,19 @@
//------------------------------------------------------------------------------
/*
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)
{
}

View File

@@ -0,0 +1,72 @@
//------------------------------------------------------------------------------
/*
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
{
return mBookBase;
}
uint160 const& getCurrencyIn () const
{
return mCurrencyIn;
}
uint160 const& getCurrencyOut () const
{
return mCurrencyOut;
}
uint160 const& getIssuerIn () const
{
return mIssuerIn;
}
uint160 const& getIssuerOut () const
{
return mIssuerOut;
}
private:
uint256 const mBookBase;
uint160 const mCurrencyIn;
uint160 const mCurrencyOut;
uint160 const mIssuerIn;
uint160 const mIssuerOut;
};
#endif

View 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

View 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

View File

@@ -0,0 +1,351 @@
//------------------------------------------------------------------------------
/*
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 (getConfig ().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);
}
//------------------------------------------------------------------------------
class SerializedTransactionTests : public UnitTest
{
public:
SerializedTransactionTests () : UnitTest ("SerializedTransaction", "ripple")
{
}
void runTest ()
{
beginTestCase ("tx");
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);
unexpected (!j.checkSign (), "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);
fail ("Transaction fails serialize/deserialize test");
}
else
{
pass ();
}
UPTR_T<STObject> new_obj = STObject::parseJson (j.getJson (0), sfGeneric);
if (new_obj.get () == NULL) 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);
fail ("Built a different transaction");
}
else
{
pass ();
}
}
};
static SerializedTransactionTests serializedTransactionTests;

View 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

View File

@@ -0,0 +1,451 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
class ValidationsImp;
SETUP_LOG (Validations)
typedef std::map<uint160, SerializedValidation::pointer>::value_type u160_val_pair;
typedef boost::shared_ptr<ValidationSet> VSpointer;
class ValidationsImp : public Validations
{
private:
typedef RippleMutex LockType;
typedef LockType::ScopedLockType ScopedLockType;
typedef LockType::ScopedUnlockType ScopedUnlockType;
LockType mLock;
TaggedCacheType<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:
ValidationsImp ()
: mLock (this, "Validations", __FILE__, __LINE__)
, 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 ();
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
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)
{
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
VSpointer set = findSet (ledger);
if (set)
return *set;
}
return ValidationSet ();
}
void getValidationCount (uint256 const& ledger, bool currentOnly, int& trusted, int& untrusted)
{
trusted = untrusted = 0;
ScopedLockType sl (mLock, __FILE__, __LINE__);
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;
ScopedLockType sl (mLock, __FILE__, __LINE__);
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;
ScopedLockType sl (mLock, __FILE__, __LINE__);
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;
ScopedLockType sl (mLock, __FILE__, __LINE__);
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;
ScopedLockType sl (mLock, __FILE__, __LINE__);
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;
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
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;
ScopedLockType sl (mLock, __FILE__, __LINE__);
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;
ScopedLockType sl (mLock, __FILE__, __LINE__);
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";
ScopedLockType sl (mLock, __FILE__, __LINE__);
BOOST_FOREACH (u160_val_pair & it, mCurrentValidations)
{
if (it.second)
mStaleValidations.push_back (it.second);
anyNew = true;
}
mCurrentValidations.clear ();
if (anyNew)
condWrite ();
while (mWriting)
{
ScopedUnlockType sul (mLock, __FILE__, __LINE__);
boost::this_thread::sleep (boost::posix_time::milliseconds (100));
}
WriteLog (lsDEBUG, Validations) << "Validations flushed";
}
void condWrite ()
{
if (mWriting)
return;
mWriting = true;
getApp().getJobQueue ().addJob (jtWRITE, "Validations::doWrite",
BIND_TYPE (&ValidationsImp::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);");
ScopedLockType sl (mLock, __FILE__, __LINE__);
assert (mWriting);
while (!mStaleValidations.empty ())
{
std::vector<SerializedValidation::pointer> vector;
vector.reserve (512);
mStaleValidations.swap (vector);
{
ScopedUnlockType sul (mLock, __FILE__, __LINE__);
{
Database* db = getApp().getLedgerDB ()->getDB ();
DeprecatedScopedLock 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;");
}
}
}
mWriting = false;
}
void sweep ()
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
mValidations.sweep ();
}
};
Validations* Validations::New ()
{
return new ValidationsImp;
}
// vim:ts=4

View File

@@ -0,0 +1,48 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
#ifndef RIPPLE_VALIDATIONS_H_INCLUDED
#define RIPPLE_VALIDATIONS_H_INCLUDED
// VFALCO TODO rename and move these typedefs into the Validations interface
typedef boost::unordered_map<uint160, SerializedValidation::pointer> ValidationSet;
typedef std::pair<int, uint160> currentValidationCount; // nodes validating and highest node ID validating
class Validations : LeakChecked <Validations>
{
public:
static Validations* New ();
virtual ~Validations () { }
virtual bool addValidation (SerializedValidation::ref, const std::string& source) = 0;
virtual ValidationSet getValidations (uint256 const& ledger) = 0;
virtual void getValidationCount (uint256 const& ledger, bool currentOnly, int& trusted, int& untrusted) = 0;
virtual void getValidationTypes (uint256 const& ledger, int& full, int& partial) = 0;
virtual int getTrustedValidationCount (uint256 const& ledger) = 0;
virtual int getFeeAverage(uint256 const& ledger, uint64 ref, uint64& fee) = 0;
virtual int getNodesAfter (uint256 const& ledger) = 0;
virtual int getLoadRatio (bool overLoaded) = 0;
// VFALCO TODO make a typedef for this ugly return value!
virtual boost::unordered_map<uint256, currentValidationCount> getCurrentValidations (
uint256 currentLedger, uint256 previousLedger) = 0;
virtual std::list <SerializedValidation::pointer> getCurrentTrustedValidations () = 0;
virtual void tune (int size, int age) = 0;
virtual void flush () = 0;
virtual void sweep () = 0;
};
#endif

View File

@@ -0,0 +1,238 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
static const char* s_nodeStoreDBInit [] =
{
"PRAGMA synchronous=NORMAL;",
"PRAGMA journal_mode=WAL;",
"PRAGMA journal_size_limit=1582080;",
#if (ULONG_MAX > UINT_MAX) && !defined (NO_SQLITE_MMAP)
"PRAGMA mmap_size=171798691840;",
#endif
"BEGIN TRANSACTION;",
"CREATE TABLE CommittedObjects ( \
Hash CHARACTER(64) PRIMARY KEY, \
ObjType CHAR(1) NOT NULL, \
LedgerIndex BIGINT UNSIGNED, \
Object BLOB \
);",
"END TRANSACTION;"
};
static int s_nodeStoreDBCount = NUMBER (s_nodeStoreDBInit);
//------------------------------------------------------------------------------
class SqliteBackendFactory::Backend : public NodeStore::Backend
{
public:
Backend (size_t keyBytes, std::string const& path)
: m_keyBytes (keyBytes)
, m_name (path)
, m_db (new DatabaseCon(path, s_nodeStoreDBInit, s_nodeStoreDBCount))
{
String s;
// VFALCO TODO Remove this dependency on theConfig
//
s << "PRAGMA cache_size=-" << String (getConfig ().getSize(siHashNodeDBCache) * 1024);
m_db->getDB()->executeSQL (s.toStdString ().c_str ());
}
~Backend()
{
}
std::string getName()
{
return m_name;
}
//--------------------------------------------------------------------------
Status fetch (void const* key, NodeObject::Ptr* pObject)
{
Status result = ok;
pObject->reset ();
{
DeprecatedScopedLock sl (m_db->getDBLock());
uint256 const hash (uint256::fromVoid (key));
static SqliteStatement pSt (m_db->getDB()->getSqliteDB(),
"SELECT ObjType,LedgerIndex,Object FROM CommittedObjects WHERE Hash = ?;");
pSt.bind (1, hash.GetHex());
if (pSt.isRow (pSt.step()))
{
// VFALCO NOTE This is unfortunately needed,
// the DatabaseCon creates the blob?
Blob data (pSt.getBlob (2));
*pObject = NodeObject::createObject (
getTypeFromString (pSt.peekString (0)),
pSt.getUInt32 (1),
data,
hash);
}
else
{
result = notFound;
}
pSt.reset();
}
return result;
}
void store (NodeObject::ref object)
{
NodeStore::Batch batch;
batch.push_back (object);
storeBatch (batch);
}
void storeBatch (NodeStore::Batch const& batch)
{
// VFALCO TODO Rewrite this to use Beast::db
DeprecatedScopedLock sl (m_db->getDBLock());
static SqliteStatement pStB (m_db->getDB()->getSqliteDB(), "BEGIN TRANSACTION;");
static SqliteStatement pStE (m_db->getDB()->getSqliteDB(), "END TRANSACTION;");
static SqliteStatement pSt (m_db->getDB()->getSqliteDB(),
"INSERT OR IGNORE INTO CommittedObjects "
"(Hash,ObjType,LedgerIndex,Object) VALUES (?, ?, ?, ?);");
pStB.step();
pStB.reset();
BOOST_FOREACH (NodeObject::Ptr const& object, batch)
{
doBind (pSt, object);
pSt.step();
pSt.reset();
}
pStE.step();
pStE.reset();
}
void visitAll (VisitCallback& callback)
{
// No lock needed as per the visitAll() API
uint256 hash;
static SqliteStatement pSt(m_db->getDB()->getSqliteDB(),
"SELECT ObjType,LedgerIndex,Object,Hash FROM CommittedObjects;");
while (pSt.isRow (pSt.step()))
{
hash.SetHexExact(pSt.getString(3));
// VFALCO NOTE This is unfortunately needed,
// the DatabaseCon creates the blob?
Blob data (pSt.getBlob (2));
NodeObject::Ptr const object (NodeObject::createObject (
getTypeFromString (pSt.peekString (0)),
pSt.getUInt32 (1),
data,
hash));
callback.visitObject (object);
}
pSt.reset ();
}
int getWriteLoad ()
{
return 0;
}
//--------------------------------------------------------------------------
void doBind (SqliteStatement& statement, NodeObject::ref object)
{
char const* type;
switch (object->getType())
{
case hotLEDGER: type = "L"; break;
case hotTRANSACTION: type = "T"; break;
case hotACCOUNT_NODE: type = "A"; break;
case hotTRANSACTION_NODE: type = "N"; break;
default: type = "U";
}
statement.bind(1, object->getHash().GetHex());
statement.bind(2, type);
statement.bind(3, object->getIndex());
statement.bindStatic(4, object->getData());
}
NodeObjectType getTypeFromString (std::string const& s)
{
NodeObjectType type = hotUNKNOWN;
if (!s.empty ())
{
switch (s [0])
{
case 'L': type = hotLEDGER; break;
case 'T': type = hotTRANSACTION; break;
case 'A': type = hotACCOUNT_NODE; break;
case 'N': type = hotTRANSACTION_NODE; break;
}
}
return type;
}
private:
size_t const m_keyBytes;
std::string const m_name;
ScopedPointer <DatabaseCon> m_db;
};
//------------------------------------------------------------------------------
SqliteBackendFactory::SqliteBackendFactory ()
{
}
SqliteBackendFactory::~SqliteBackendFactory ()
{
}
SqliteBackendFactory& SqliteBackendFactory::getInstance ()
{
static SqliteBackendFactory instance;
return instance;
}
String SqliteBackendFactory::getName () const
{
return "Sqlite";
}
NodeStore::Backend* SqliteBackendFactory::createInstance (
size_t keyBytes,
StringPairArray const& keyValues,
NodeStore::Scheduler& scheduler)
{
return new Backend (keyBytes, keyValues ["path"].toStdString ());
}

View File

@@ -0,0 +1,32 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
#ifndef RIPPLE_SQLITEBACKENDFACTORY_H_INCLUDED
#define RIPPLE_SQLITEBACKENDFACTORY_H_INCLUDED
/** Factory to produce SQLite backends for the NodeStore.
@see NodeStore
*/
class SqliteBackendFactory : public NodeStore::BackendFactory
{
private:
class Backend;
SqliteBackendFactory ();
~SqliteBackendFactory ();
public:
static SqliteBackendFactory& getInstance ();
String getName () const;
NodeStore::Backend* createInstance (size_t keyBytes,
StringPairArray const& keyValues,
NodeStore::Scheduler& scheduler);
};
#endif

View File

@@ -0,0 +1,411 @@
//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
SETUP_LOG (PathRequest)
// VFALCO TODO Move these globals into a PathRequests collection inteface
PathRequest::StaticLockType PathRequest::sLock ("PathRequest", __FILE__, __LINE__);
std::set <PathRequest::wptr> PathRequest::sRequests;
PathRequest::PathRequest (const boost::shared_ptr<InfoSub>& subscriber)
: mLock (this, "PathRequest", __FILE__, __LINE__)
, wpSubscriber (subscriber)
, jvStatus (Json::objectValue)
, bValid (false)
, bNew (true)
, iLastLevel (0)
, bLastSuccess (false)
{
}
bool PathRequest::isValid ()
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
return bValid;
}
bool PathRequest::isNew ()
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
return bNew;
}
bool PathRequest::isValid (Ledger::ref lrLedger)
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
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;
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
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 ();
StaticScopedLockType sl (sLock, __FILE__, __LINE__);
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&)
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
return jvStatus;
}
Json::Value PathRequest::doStatus (const Json::Value&)
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
return jvStatus;
}
bool PathRequest::doUpdate (RippleLineCache::ref cache, bool fast)
{
ScopedLockType sl (mLock, __FILE__, __LINE__);
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;
int iLevel = iLastLevel;
bool loaded = getApp().getFeeTrack().isLoadedLocal();
if (iLevel == 0)
{ // first pass
if (loaded)
iLevel = getConfig().PATH_SEARCH_FAST;
else if (!fast)
iLevel = getConfig().PATH_SEARCH_OLD;
else if (getConfig().PATH_SEARCH < getConfig().PATH_SEARCH_MAX)
iLevel = getConfig().PATH_SEARCH + 1; // start with an extra boost
else
iLevel = getConfig().PATH_SEARCH;
}
else if ((iLevel == getConfig().PATH_SEARCH_FAST) && !fast)
{ // leaving fast pathfinding
iLevel = getConfig().PATH_SEARCH;
if (loaded && (iLevel > getConfig().PATH_SEARCH_FAST))
--iLevel;
else if (!loaded && (iLevel < getConfig().PATH_SEARCH))
++iLevel;
}
else if (bLastSuccess)
{ // decrement, if possible
if ((iLevel > getConfig().PATH_SEARCH) || (loaded && (iLevel > getConfig().PATH_SEARCH_FAST)))
--iLevel;
}
else
{ // adjust as needed
if (!loaded && (iLevel < getConfig().PATH_SEARCH_MAX))
++iLevel;
if (loaded && (iLevel > getConfig().PATH_SEARCH_FAST))
--iLevel;
}
bool found = false;
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 = mContext[currIssuer];
Pathfinder pf (cache, raSrcAccount, raDstAccount,
currIssuer.first, currIssuer.second, saDstAmount, valid);
CondLog (!valid, lsINFO, PathRequest) << "PF request not valid";
if (valid && pf.findPaths (iLevel, 4, 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);
found = true;
jvArray.append (jvEntry);
}
else
{
WriteLog (lsINFO, PathRequest) << "rippleCalc returns " << transHuman (terResult);
}
}
else
{
WriteLog (lsINFO, PathRequest) << "No paths found";
}
}
iLastLevel = iLevel;
bLastSuccess = found;
jvStatus["alternatives"] = jvArray;
return true;
}
void PathRequest::updateAll (Ledger::ref ledger, bool newOnly)
{
std::set<wptr> requests;
{
StaticScopedLockType sl (sLock, __FILE__, __LINE__);
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)
{
if (newOnly && !pRequest->isNew ())
remove = false;
else
{
InfoSub::pointer ipSub = pRequest->wpSubscriber.lock ();
if (ipSub)
{
Json::Value update;
{
ScopedLockType sl (pRequest->mLock, __FILE__, __LINE__);
pRequest->doUpdate (cache, false);
update = pRequest->jvStatus;
}
update["type"] = "path_find";
ipSub->send (update, false);
remove = false;
}
}
}
if (remove)
{
StaticScopedLockType sl (sLock, __FILE__, __LINE__);
sRequests.erase (wRequest);
}
}
}
// vim:ts=4

View File

@@ -0,0 +1,82 @@
//------------------------------------------------------------------------------
/*
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:
void setValid ();
int parseJson (const Json::Value&, bool complete);
typedef RippleRecursiveMutex LockType;
typedef LockType::ScopedLockType ScopedLockType;
LockType 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;
std::map<currIssuer_t, STPathSet> mContext;
bool bValid;
bool bNew;
int iLastLevel;
bool bLastSuccess;
// Track all requests
static std::set<wptr> sRequests;
typedef RippleRecursiveMutex StaticLockType;
typedef LockType::ScopedLockType StaticScopedLockType;
static StaticLockType sLock;
};
#endif
// vim:ts=4

View 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 (lsTRACE, 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 (lsTRACE, RippleCalc) << boost::str (boost::format ("setExpanded: element in path:"));
terStatus = pushNode (speElement.getNodeType (), speElement.getAccountID (), speElement.getCurrency (), speElement.getIssuerID ());
}
}
const Node& pnPrv = vpnNodes.back ();
if (tesSUCCESS == terStatus
&& !!uOutCurrencyID // Next is not XRP
&& uOutIssuerID != uReceiverID // Out issuer is not receiver
&& (pnPrv.uCurrencyID != uOutCurrencyID // Previous will be an offer.
|| pnPrv.uAccountID != uOutIssuerID)) // Need the implied issuer.
{
// Add implied account.
WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("setExpanded: receiver implied: account=%s currency=%s issuer=%s")
% RippleAddress::createHumanAccountID (uOutIssuerID)
% STAmount::createHumanCurrency (uOutCurrencyID)
% RippleAddress::createHumanAccountID (uOutIssuerID));
terStatus = pushNode (
!!uOutCurrencyID
? STPathElement::typeAccount | STPathElement::typeCurrency | STPathElement::typeIssuer
: STPathElement::typeAccount | STPathElement::typeCurrency,
uOutIssuerID,
uOutCurrencyID,
uOutIssuerID);
}
if (tesSUCCESS == terStatus)
{
// Create receiver node.
// Last node is always an account.
terStatus = pushNode (
!!uOutCurrencyID
? STPathElement::typeAccount | STPathElement::typeCurrency | STPathElement::typeIssuer
: STPathElement::typeAccount | STPathElement::typeCurrency,
uReceiverID, // Receive to output
uOutCurrencyID, // Desired currency
uReceiverID);
}
if (tesSUCCESS == terStatus)
{
// Look for first mention of source in nodes and detect loops.
// Note: The output is not allowed to be a source.
const unsigned int uNodes = vpnNodes.size ();
for (unsigned int uNode = 0; tesSUCCESS == terStatus && uNode != uNodes; ++uNode)
{
const Node& pnCur = vpnNodes[uNode];
if (!umForward.insert (std::make_pair (boost::make_tuple (pnCur.uAccountID, pnCur.uCurrencyID, pnCur.uIssuerID), uNode)).second)
{
// Failed to insert. Have a loop.
WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("setExpanded: loop detected: %s")
% getJson ());
terStatus = temBAD_PATH_LOOP;
}
}
}
WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("setExpanded: in=%s/%s out=%s/%s %s")
% STAmount::createHumanCurrency (uMaxCurrencyID)
% RippleAddress::createHumanAccountID (uMaxIssuerID)
% STAmount::createHumanCurrency (uOutCurrencyID)
% RippleAddress::createHumanAccountID (uOutIssuerID)
% getJson ());
}
// Set to a canonical path.
// - Remove extra elements
// - Assumes path is expanded.
//
// We do canonicalization to:
// - Prevent waste in the ledger.
// - Allow longer paths to be specified than would otherwise be allowed.
//
// Optimization theory:
// - Can omit elements that the expansion routine derives.
// - Can pack some elements into other elements.
//
// Rules:
// - SendMax if not specified, defaults currency to send and if not sending XRP defaults issuer to sender.
// - All paths start with the sender account.
// - Currency and issuer is from SendMax.
// - All paths end with the destination account.
//
// Optimization:
// - An XRP output implies an offer node or destination node is next.
// - A change in currency implies an offer node.
// - A change in issuer...
void PathState::setCanonical (
const PathState& psExpanded
)
{
assert (false);
saInAct = psExpanded.saInAct;
saOutAct = psExpanded.saOutAct;
const uint160 uMaxCurrencyID = saInAct.getCurrency ();
const uint160 uMaxIssuerID = saInAct.getIssuer ();
const uint160 uOutCurrencyID = saOutAct.getCurrency ();
const uint160 uOutIssuerID = saOutAct.getIssuer ();
unsigned int uNode = 0;
unsigned int uEnd = psExpanded.vpnNodes.size (); // The node, indexed by 0, not to include.
uint160 uDstAccountID = psExpanded.vpnNodes[uEnd].uAccountID; // FIXME: This can't be right
uint160 uAccountID = psExpanded.vpnNodes[0].uAccountID;
uint160 uCurrencyID = uMaxCurrencyID;
uint160 uIssuerID = uMaxIssuerID;
// Node 0 is a composite of the sending account and saInAct.
++uNode; // skip node 0
// Last node is implied: Always skip last node
--uEnd; // skip last node
// saInAct
// - currency is always the same as vpnNodes[0].
#if 1
if (uNode != uEnd && uMaxIssuerID != uAccountID)
{
// saInAct issuer is not the sender. This forces an implied node.
// WriteLog (lsDEBUG, RippleCalc) << boost::str(boost::format("setCanonical: in diff: uNode=%d uEnd=%d") % uNode % uEnd);
// skip node 1
uIssuerID = psExpanded.vpnNodes[uNode].uIssuerID;
++uNode;
}
#else
if (uNode != uEnd)
{
// Have another node
bool bKeep = false;
if (uMaxIssuerID != uAccountID)
{
}
if (uMaxCurrencyID) // Not sending XRP.
{
// Node 1 must be an account.
if (uMaxIssuerID != uAccountID)
{
// Node 1 is required to specify issuer.
bKeep = true;
}
else
{
// Node 1 must be an account
}
}
else
{
// Node 1 must be an order book.
bKeep = true;
}
if (bKeep)
{
uCurrencyID = psExpanded.vpnNodes[uNode].uCurrencyID;
uIssuerID = psExpanded.vpnNodes[uNode].uIssuerID;
++uNode; // Keep it.
}
}
#endif
if (uNode != uEnd && !!uOutCurrencyID && uOutIssuerID != uDstAccountID)
{
// WriteLog (lsDEBUG, RippleCalc) << boost::str(boost::format("setCanonical: out diff: uNode=%d uEnd=%d") % uNode % uEnd);
// The next to last node is saOutAct if an issuer different from receiver is supplied.
// The next to last node can be implied.
--uEnd;
}
const Node& pnEnd = psExpanded.vpnNodes[uEnd];
if (uNode != uEnd
&& !pnEnd.uAccountID && pnEnd.uCurrencyID == uOutCurrencyID && pnEnd.uIssuerID == uOutIssuerID)
{
// The current end node is an offer converting to saOutAct's currency and issuer and can be implied.
// WriteLog (lsDEBUG, RippleCalc) << boost::str(boost::format("setCanonical: out offer: uNode=%d uEnd=%d") % uNode % uEnd);
--uEnd;
}
// Do not include uEnd.
for (; uNode != uEnd; ++uNode)
{
// WriteLog (lsDEBUG, RippleCalc) << boost::str(boost::format("setCanonical: loop: uNode=%d uEnd=%d") % uNode % uEnd);
const Node& pnPrv = psExpanded.vpnNodes[uNode - 1];
const Node& pnCur = psExpanded.vpnNodes[uNode];
const Node& pnNxt = psExpanded.vpnNodes[uNode + 1];
const bool bCurAccount = isSetBit (pnCur.uFlags, STPathElement::typeAccount);
bool bSkip = false;
if (bCurAccount)
{
// Currently at an account.
// Output is non-XRP and issuer is account.
if (!!pnCur.uCurrencyID && pnCur.uIssuerID == pnCur.uAccountID)
{
// Account issues itself.
// XXX Not good enough. Previous account must mention it.
bSkip = true;
}
}
else
{
// Currently at an offer.
const bool bPrvAccount = isSetBit (pnPrv.uFlags, STPathElement::typeAccount);
const bool bNxtAccount = isSetBit (pnNxt.uFlags, STPathElement::typeAccount);
if (bPrvAccount && bNxtAccount // Offer surrounded by accounts.
&& pnPrv.uCurrencyID != pnNxt.uCurrencyID)
{
// Offer can be implied by currency change.
// XXX What about issuer?
bSkip = true;
}
}
if (!bSkip)
{
// Copy node
Node pnNew;
bool bSetAccount = bCurAccount;
bool bSetCurrency = uCurrencyID != pnCur.uCurrencyID;
// XXX What if we need the next account because we want to skip it?
bool bSetIssuer = !uCurrencyID && uIssuerID != pnCur.uIssuerID;
pnNew.uFlags = (bSetAccount ? STPathElement::typeAccount : 0)
| (bSetCurrency ? STPathElement::typeCurrency : 0)
| (bSetIssuer ? STPathElement::typeIssuer : 0);
if (bSetAccount)
pnNew.uAccountID = pnCur.uAccountID;
if (bSetCurrency)
{
pnNew.uCurrencyID = pnCur.uCurrencyID;
uCurrencyID = pnNew.uCurrencyID;
}
if (bSetIssuer)
pnNew.uIssuerID = pnCur.uIssuerID;
// XXX ^^^ What about setting uIssuerID?
if (bSetCurrency && !uCurrencyID)
uIssuerID.zero ();
vpnNodes.push_back (pnNew);
}
}
WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("setCanonical: in=%s/%s out=%s/%s %s")
% STAmount::createHumanCurrency (uMaxCurrencyID)
% RippleAddress::createHumanAccountID (uMaxIssuerID)
% STAmount::createHumanCurrency (uOutCurrencyID)
% RippleAddress::createHumanAccountID (uOutIssuerID)
% getJson ());
}
// This is for debugging not end users. Output names can be changed without warning.
Json::Value PathState::getJson () const
{
Json::Value jvPathState (Json::objectValue);
Json::Value jvNodes (Json::arrayValue);
BOOST_FOREACH (const Node & pnNode, vpnNodes)
{
jvNodes.append (pnNode.getJson ());
}
jvPathState["status"] = terStatus;
jvPathState["index"] = mIndex;
jvPathState["nodes"] = jvNodes;
if (saInReq)
jvPathState["in_req"] = saInReq.getJson (0);
if (saInAct)
jvPathState["in_act"] = saInAct.getJson (0);
if (saInPass)
jvPathState["in_pass"] = saInPass.getJson (0);
if (saOutReq)
jvPathState["out_req"] = saOutReq.getJson (0);
if (saOutAct)
jvPathState["out_act"] = saOutAct.getJson (0);
if (saOutPass)
jvPathState["out_pass"] = saOutPass.getJson (0);
if (uQuality)
jvPathState["uQuality"] = boost::str (boost::format ("%d") % uQuality);
return jvPathState;
}

View File

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

View File

@@ -0,0 +1,854 @@
//------------------------------------------------------------------------------
/*
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> ();
}
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);
}
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;
// FIXME: This is not right
getApp().getOrderBookDB ().setup (mLedger);
m_loadEvent = getApp().getJobQueue ().getLoadEvent (jtPATH_FIND, "FindPath");
bool bIssuer = mSrcCurrencyID.isNonZero() && mSrcIssuerID.isNonZero() && (mSrcIssuerID != mSrcAccountID);
mSource = STPathElement( // Where does an empty path start?
bIssuer ? mSrcIssuerID : mSrcAccountID, // On the source account or issuer account
mSrcCurrencyID, // In the source currency
mSrcCurrencyID.isZero() ? uint160() : (bIssuer ? mSrcIssuerID : mSrcAccountID));
}
bool Pathfinder::findPaths (int iLevel, const unsigned int iMaxPaths, STPathSet& pathsOut)
{ // pathsOut contains only non-default paths without source or destiation
// On input, pathsOut contains any paths you want to ensure are included if still good
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;
}
bool bSrcXrp = mSrcCurrencyID.isZero();
bool bDstXrp = mDstAmount.getCurrency().isZero();
SLE::pointer sleSrc = mLedger->getSLEi(Ledger::getAccountRootIndex(mSrcAccountID));
if (!sleSrc)
return false;
SLE::pointer sleDest = mLedger->getSLEi(Ledger::getAccountRootIndex(mDstAccountID));
if (!sleDest && (!bDstXrp || (mDstAmount < mLedger->getReserve(0))))
return false;
PaymentType paymentType;
if (bSrcXrp && bDstXrp)
{ // XRP -> XRP
WriteLog (lsDEBUG, Pathfinder) << "XRP to XRP payment";
paymentType = pt_XRP_to_XRP;
}
else if (bSrcXrp)
{ // XRP -> non-XRP
WriteLog (lsDEBUG, Pathfinder) << "XRP to non-XRP payment";
paymentType = pt_XRP_to_nonXRP;
}
else if (bDstXrp)
{ // non-XRP -> XRP
WriteLog (lsDEBUG, Pathfinder) << "non-XRP to XRP payment";
paymentType = pt_nonXRP_to_XRP;
}
else if (mSrcCurrencyID == mDstAmount.getCurrency())
{ // non-XRP -> non-XRP - Same currency
WriteLog (lsDEBUG, Pathfinder) << "non-XRP to non-XRP - same currency";
paymentType = pt_nonXRP_to_same;
}
else
{ // non-XRP to non-XRP - Different currency
WriteLog (lsDEBUG, Pathfinder) << "non-XRP to non-XRP - cross currency";
paymentType = pt_nonXRP_to_nonXRP;
}
BOOST_FOREACH(CostedPath_t const& costedPath, mPathTable[paymentType])
{
if (costedPath.first <= iLevel)
{
getPaths(costedPath.second);
}
}
WriteLog (lsDEBUG, Pathfinder) << mCompletePaths.size() << " complete paths found";
BOOST_FOREACH(const STPath& path, pathsOut)
{ // make sure no paths were lost
bool found = false;
BOOST_FOREACH(const STPath& ePath, mCompletePaths)
{
if (ePath == path)
{
found = true;
break;
}
}
if (!found)
mCompletePaths.addPath(path);
}
WriteLog (lsDEBUG, Pathfinder) << mCompletePaths.size() << " paths to filter";
if (mCompletePaths.size() > iMaxPaths)
pathsOut = filterPaths(iMaxPaths);
else
pathsOut = mCompletePaths;
return true; // Even if we find no paths, default paths may work, and we don't check them currently
}
STPathSet Pathfinder::filterPaths(int iMaxPaths)
{
if (mCompletePaths.size() <= iMaxPaths)
return mCompletePaths;
STAmount remaining = mDstAmount;
// must subtract liquidity in default path from remaining amount
try
{
STAmount saMaxAmountAct, saDstAmountAct;
std::vector<PathState::pointer> vpsExpanded;
LedgerEntrySet lesSandbox (mLedger, tapNONE);
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";
}
std::vector<path_LQ_t> vMap;
// Build map of quality to entry.
for (int i = mCompletePaths.size (); i--;)
{
STAmount saMaxAmountAct;
STAmount saDstAmountAct;
std::vector<PathState::pointer> vpsExpanded;
STPathSet spsPaths;
STPath& spCurrent = mCompletePaths[i];
spsPaths.addPath (spCurrent); // Just checking the current path.
TER terResult;
try
{
LedgerEntrySet lesSandbox (mLedger, tapNONE);
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));
}
}
STPathSet spsDst;
if (vMap.size())
{
std::sort (vMap.begin (), vMap.end (), bQualityCmp); // Lower is better and should be first.
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 (mCompletePaths[lqt.get<3> ()]);
}
else
WriteLog (lsDEBUG, Pathfinder) << "Skipping a non-filling path: " << mCompletePaths[lqt.get<3> ()].getJson (0);
}
if (remaining.isPositive ())
{
WriteLog (lsINFO, Pathfinder) << "Paths could not send " << remaining << " of " << mDstAmount;
}
else
{
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"));
}
return spsDst;
}
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)
{
if (currency != mSrcCurrencyID)
return false;
if (currency.isZero())
return true;
return (issuer == mSrcIssuerID) || (issuer == mSrcAccountID);
}
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;
}
void Pathfinder::addLink(
const STPathSet& currentPaths, // The paths to build from
STPathSet& incompletePaths, // The set of partial paths we add to
int addFlags)
{
WriteLog (lsDEBUG, Pathfinder) << "addLink< on " << currentPaths.size() << " source(s), flags=" << addFlags;
BOOST_FOREACH(const STPath& path, currentPaths)
{
addLink(path, incompletePaths, addFlags);
}
}
STPathSet& Pathfinder::getPaths(PathType_t const& type, bool addComplete)
{
std::map< PathType_t, STPathSet >::iterator it = mPaths.find(type);
// We already have these paths
if (it != mPaths.end())
return it->second;
// The type is empty
if (type.empty())
return mPaths[type];
NodeType toAdd = type.back();
PathType_t pathType(type);
pathType.pop_back();
STPathSet pathsIn = getPaths(pathType, false);
STPathSet& pathsOut = mPaths[type];
WriteLog (lsDEBUG, Pathfinder)
<< "getPaths< adding onto '"
<< pathTypeToString(pathType) << "' to get '"
<< pathTypeToString(type) << "'";
int cp = mCompletePaths.size();
switch (toAdd)
{
case nt_SOURCE:
{ // source is an empty path
assert(pathsOut.isEmpty());
pathsOut.addPath(STPath());
}
break;
case nt_ACCOUNTS:
addLink(pathsIn, pathsOut, afADD_ACCOUNTS);
break;
case nt_BOOKS:
addLink(pathsIn, pathsOut, afADD_BOOKS);
break;
case nt_XRP_BOOK:
addLink(pathsIn, pathsOut, afADD_BOOKS | afOB_XRP);
break;
case nt_DEST_BOOK:
addLink(pathsIn, pathsOut, afADD_BOOKS | afOB_LAST);
break;
case nt_DESTINATION:
// FIXME: What if a different issuer was specified on the destination amount
addLink(pathsIn, pathsOut, afADD_ACCOUNTS | afAC_LAST);
break;
}
CondLog (mCompletePaths.size() != cp, lsDEBUG, Pathfinder)
<< (mCompletePaths.size() - cp)
<< " complete paths added";
WriteLog (lsDEBUG, Pathfinder) << "getPaths> " << pathsOut.size() << " partial paths found";
return pathsOut;
}
void Pathfinder::addLink(
const STPath& currentPath, // The path to build from
STPathSet& incompletePaths, // The set of partial paths we add to
int addFlags)
{
STPathElement const& pathEnd = currentPath.isEmpty() ? mSource : currentPath.mPath.back ();
uint160 const& uEndCurrency = pathEnd.mCurrencyID;
uint160 const& uEndIssuer = pathEnd.mIssuerID;
uint160 const& uEndAccount = pathEnd.mAccountID;
bool const bOnXRP = uEndCurrency.isZero();
WriteLog (lsTRACE, Pathfinder) << "addLink< flags=" << addFlags << " onXRP=" << bOnXRP;
WriteLog (lsTRACE, Pathfinder) << currentPath.getJson(0);
if (addFlags & afADD_ACCOUNTS)
{ // add accounts
if (bOnXRP)
{
if (mDstAmount.isNative() && !currentPath.isEmpty())
{ // non-default path to XRP destination
WriteLog (lsTRACE, Pathfinder) << "complete path found ax: " << currentPath.getJson(0);
mCompletePaths.addUniquePath(currentPath);
}
}
else
{ // search for accounts to add
SLE::pointer sleEnd = mLedger->getSLEi(Ledger::getAccountRootIndex(uEndAccount));
if (sleEnd)
{
bool const bRequireAuth = isSetBit(sleEnd->getFieldU32(sfFlags), lsfRequireAuth);
bool const bIsEndCurrency = (uEndCurrency == mDstAmount.getCurrency());
AccountItems& rippleLines(mRLCache->getRippleLines(uEndAccount));
std::vector< std::pair<int, uint160> > candidates;
candidates.reserve(rippleLines.getItems().size());
BOOST_FOREACH(AccountItem::ref item, rippleLines.getItems())
{
RippleState const& rspEntry = * reinterpret_cast<RippleState const *>(item.get());
uint160 const& acctID = rspEntry.getAccountIDPeer();
if ((uEndCurrency == rspEntry.getLimit().getCurrency()) &&
!currentPath.hasSeen(acctID, uEndCurrency, acctID))
{ // path is for correct currency and has not been seen
if (!rspEntry.getBalance().isPositive()
&& (!rspEntry.getLimitPeer()
|| -rspEntry.getBalance() >= rspEntry.getLimitPeer()
|| (bRequireAuth && !rspEntry.getAuth())))
{
// path has no credit
}
else if (acctID == mDstAccountID)
{ // destination is always worth trying
if (uEndCurrency == mDstAmount.getCurrency())
{ // this is a complete path
if (!currentPath.isEmpty())
{
WriteLog (lsTRACE, Pathfinder) << "complete path found ae: " << currentPath.getJson(0);
mCompletePaths.addUniquePath(currentPath);
}
}
else if ((addFlags & afAC_LAST) == 0)
{ // this is a high-priority candidate
candidates.push_back(std::make_pair(100000, acctID));
}
}
else if (acctID == mSrcAccountID)
{
// going back to the source is bad
}
else if ((addFlags & afAC_LAST) == 0)
{ // save this candidate
int out = getPathsOut(uEndCurrency, acctID, bIsEndCurrency, mDstAccountID);
if (out)
candidates.push_back(std::make_pair(out, acctID));
}
}
}
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) && (uEndAccount != mSrcAccountID)) // allow more paths from source
count = 10;
else if (count > 50)
count = 50;
std::vector< std::pair<int, uint160> >::const_iterator it = candidates.begin();
while (count-- != 0)
{ // Add accounts to incompletePaths
incompletePaths.assembleAdd(currentPath, STPathElement(STPathElement::typeAccount, it->second, uEndCurrency, it->second));
++it;
}
}
}
else
{
WriteLog(lsWARNING, Pathfinder) << "Path ends on non-existent issuer";
}
}
}
if (addFlags & afADD_BOOKS)
{ // add order books
if (addFlags & afOB_XRP)
{ // to XRP only
if (!bOnXRP && getApp().getOrderBookDB().isBookToXRP(uEndIssuer, uEndCurrency))
{
incompletePaths.assembleAdd(currentPath, STPathElement(STPathElement::typeCurrency, ACCOUNT_XRP, CURRENCY_XRP, ACCOUNT_XRP));
}
}
else
{
bool bDestOnly = (addFlags & afOB_LAST) != 0;
std::vector<OrderBook::pointer> books;
getApp().getOrderBookDB().getBooksByTakerPays(uEndIssuer, uEndCurrency, books);
WriteLog (lsTRACE, Pathfinder) << books.size() << " books found from this currency/issuer";
BOOST_FOREACH(OrderBook::ref book, books)
{
if (!currentPath.hasSeen (ACCOUNT_XRP, book->getCurrencyOut(), book->getIssuerOut()) &&
!matchesOrigin(book->getCurrencyOut(), book->getIssuerOut()) &&
(!bDestOnly || (book->getCurrencyOut() == mDstAmount.getCurrency())))
{
STPath newPath(currentPath);
if (book->getCurrencyOut().isZero())
{ // to XRP
// add the order book itself
newPath.addElement(STPathElement(STPathElement::typeCurrency, ACCOUNT_XRP, CURRENCY_XRP, ACCOUNT_XRP));
if (mDstAmount.getCurrency().isZero())
{ // destination is XRP, add account and path is complete
WriteLog (lsTRACE, Pathfinder) << "complete path found bx: " << currentPath.getJson(0);
mCompletePaths.addUniquePath(newPath);
}
else
incompletePaths.addPath(newPath);
}
else if (!currentPath.hasSeen(book->getIssuerOut(), book->getCurrencyOut(), book->getIssuerOut()))
{ // Don't want the book if we've already seen the issuer
// add the order book itself
newPath.addElement(STPathElement(STPathElement::typeCurrency | STPathElement::typeIssuer,
ACCOUNT_XRP, book->getCurrencyOut(), book->getIssuerOut()));
if ((book->getIssuerOut() == mDstAccountID) && book->getCurrencyOut() == mDstAmount.getCurrency())
{ // with the destination account, this path is complete
WriteLog (lsTRACE, Pathfinder) << "complete path found ba: " << currentPath.getJson(0);
mCompletePaths.addUniquePath(newPath);
}
else
{ // add issuer's account, path still incomplete
incompletePaths.assembleAdd(newPath,
STPathElement(STPathElement::typeAccount,
book->getIssuerOut(), book->getCurrencyOut(), book->getIssuerOut()));
}
}
}
}
}
}
}
std::map<Pathfinder::PaymentType, Pathfinder::CostedPathList_t> Pathfinder::mPathTable;
Pathfinder::PathType_t Pathfinder::makePath(char const *string)
{
PathType_t ret;
while (true)
{
switch (*string++)
{
case 's': // source
ret.push_back(nt_SOURCE);
break;
case 'a': // accounts
ret.push_back(nt_ACCOUNTS);
break;
case 'b': // books
ret.push_back(nt_BOOKS);
break;
case 'x': // xrp book
ret.push_back(nt_XRP_BOOK);
break;
case 'f': // book to final currency
ret.push_back(nt_DEST_BOOK);
break;
case 'd': // destination (with account, if required and not already present)
ret.push_back(nt_DESTINATION);
break;
case 0:
return ret;
}
}
}
std::string Pathfinder::pathTypeToString(PathType_t const& type)
{
std::string ret;
BOOST_FOREACH(NodeType const& node, type)
{
switch (node)
{
case nt_SOURCE:
ret.append("s");
break;
case nt_ACCOUNTS:
ret.append("a");
break;
case nt_BOOKS:
ret.append("b");
break;
case nt_XRP_BOOK:
ret.append("x");
break;
case nt_DEST_BOOK:
ret.append("f");
break;
case nt_DESTINATION:
ret.append("d");
break;
}
}
return ret;
}
// Costs:
// 0 = minimum to make some payments possible
// 1 = include trivial paths to make common cases work
// 4 = normal fast search level
// 7 = normal slow search level
// 10 = most agressive
void Pathfinder::initPathTable()
{ // CAUTION: Do not include rules that build default paths
{ // XRP to XRP
CostedPathList_t& list = mPathTable[pt_XRP_to_XRP];
list.push_back(CostedPath_t(8, makePath("sbxd"))); // source -> book -> book_to_XRP -> destination
list.push_back(CostedPath_t(9, makePath("sbaxd"))); // source -> book -> gateway -> to_XRP ->destination
}
{ // XRP to non-XRP
CostedPathList_t& list = mPathTable[pt_XRP_to_nonXRP];
list.push_back(CostedPath_t(0, makePath("sfd"))); // source -> book -> gateway
list.push_back(CostedPath_t(3, makePath("sfad"))); // source -> book -> account -> destination
list.push_back(CostedPath_t(5, makePath("sfaad"))); // source -> book -> account -> account -> destination
list.push_back(CostedPath_t(6, makePath("sbfd"))); // source -> book -> book -> destination
list.push_back(CostedPath_t(8, makePath("sbafd"))); // source -> book -> account -> book -> destination
list.push_back(CostedPath_t(9, makePath("sbfad"))); // source -> book -> book -> account -> destination
list.push_back(CostedPath_t(10, makePath("sbafad")));
}
{ // non-XRP to XRP
CostedPathList_t& list = mPathTable[pt_nonXRP_to_XRP];
list.push_back(CostedPath_t(0, makePath("sxd"))); // gateway buys XRP
list.push_back(CostedPath_t(1, makePath("saxd"))); // source -> gateway -> book(XRP) -> dest
list.push_back(CostedPath_t(6, makePath("saaxd")));
list.push_back(CostedPath_t(7, makePath("sbxd")));
list.push_back(CostedPath_t(8, makePath("sabxd")));
list.push_back(CostedPath_t(9, makePath("sabaxd")));
}
{ // non-XRP to non-XRP (same currency)
CostedPathList_t& list = mPathTable[pt_nonXRP_to_same];
list.push_back(CostedPath_t(1, makePath("sad"))); // source -> gateway -> destination
list.push_back(CostedPath_t(1, makePath("sfd"))); // source -> book -> destination
list.push_back(CostedPath_t(4, makePath("safd"))); // source -> gateway -> book -> destination
list.push_back(CostedPath_t(4, makePath("sfad")));
list.push_back(CostedPath_t(5, makePath("saad")));
list.push_back(CostedPath_t(5, makePath("sxfd")));
list.push_back(CostedPath_t(6, makePath("sxfad")));
list.push_back(CostedPath_t(6, makePath("safad")));
list.push_back(CostedPath_t(6, makePath("saxfd"))); // source -> gateway -> book to XRP -> book -> destination
list.push_back(CostedPath_t(6, makePath("saxfad")));
list.push_back(CostedPath_t(8, makePath("saaad")));
}
{ // non-XRP to non-XRP (different currency)
CostedPathList_t& list = mPathTable[pt_nonXRP_to_nonXRP];
list.push_back(CostedPath_t(1, makePath("sfad")));
list.push_back(CostedPath_t(1, makePath("safd")));
list.push_back(CostedPath_t(3, makePath("safad")));
list.push_back(CostedPath_t(4, makePath("sxfd")));
list.push_back(CostedPath_t(5, makePath("saxfd")));
list.push_back(CostedPath_t(6, makePath("saxfad")));
list.push_back(CostedPath_t(7, makePath("saafd")));
list.push_back(CostedPath_t(8, makePath("saafad")));
list.push_back(CostedPath_t(9, makePath("safaad")));
}
}
// vim:ts=4

View File

@@ -0,0 +1,129 @@
//------------------------------------------------------------------------------
/*
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);
static void initPathTable();
bool findPaths (int iLevel, const unsigned int iMaxPaths, STPathSet& spsDst);
private:
enum PaymentType
{
pt_XRP_to_XRP,
pt_XRP_to_nonXRP,
pt_nonXRP_to_XRP,
pt_nonXRP_to_same,
pt_nonXRP_to_nonXRP
};
enum NodeType
{
nt_SOURCE, // The source account with an issuer account, if required
nt_ACCOUNTS, // Accounts that connect from this source/currency
nt_BOOKS, // Order books that connect to this currency
nt_XRP_BOOK, // The order book from this currency to XRP
nt_DEST_BOOK, // The order book to the destination currency/issuer
nt_DESTINATION // The destination account only
};
typedef std::vector<NodeType> PathType_t;
typedef std::pair<int, PathType_t> CostedPath_t;
typedef std::vector<CostedPath_t> CostedPathList_t;
// returns true if any building paths are now complete?
bool checkComplete (STPathSet& retPathSet);
static std::string pathTypeToString(PathType_t const&);
bool matchesOrigin (const uint160& currency, const uint160& issuer);
int getPathsOut (const uint160& currency, const uint160& accountID,
bool isDestCurrency, const uint160& dest);
void addLink(const STPath& currentPath, STPathSet& incompletePaths, int addFlags);
void addLink(const STPathSet& currentPaths, STPathSet& incompletePaths, int addFlags);
STPathSet& getPaths(const PathType_t& type, bool addComplete = true);
STPathSet filterPaths(int iMaxPaths);
// Our main table of paths
static std::map<PaymentType, CostedPathList_t> mPathTable;
static PathType_t makePath(char const*);
uint160 mSrcAccountID;
uint160 mDstAccountID;
STAmount mDstAmount;
uint160 mSrcCurrencyID;
uint160 mSrcIssuerID;
STAmount mSrcAmount;
Ledger::pointer mLedger;
LoadEvent::pointer m_loadEvent;
RippleLineCache::pointer mRLCache;
STPathElement mSource;
STPathSet mCompletePaths;
std::map< PathType_t, STPathSet > mPaths;
boost::unordered_map<uint160, AccountItems::pointer> mRLMap;
boost::unordered_map<std::pair<uint160, uint160>, int> mPOMap;
static const uint32 afADD_ACCOUNTS = 0x001; // Add ripple paths
static const uint32 afADD_BOOKS = 0x002; // Add order books
static const uint32 afOB_XRP = 0x010; // Add order book to XRP only
static const uint32 afOB_LAST = 0x040; // Must link to destination currency
static const uint32 afAC_LAST = 0x080; // Destination account only
};
boost::unordered_set<uint160> usAccountDestCurrencies (const RippleAddress& raAccountID, Ledger::ref lrLedger,
bool includeXRP);
boost::unordered_set<uint160> usAccountSourceCurrencies (const RippleAddress& raAccountID, Ledger::ref lrLedger,
bool includeXRP);
#endif

File diff suppressed because it is too large Load Diff

View File

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

Some files were not shown because too many files have changed in this diff Show More