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

This commit is contained in:
jed
2012-09-15 21:18:02 -07:00
63 changed files with 5373 additions and 4879 deletions

6
.gitignore vendored
View File

@@ -16,6 +16,10 @@
*.o
obj/*
bin/newcoind
newcoind
# Ignore locally installed node_modules
node_modules
# Ignore tmp directory.
tmp

Binary file not shown.

Binary file not shown.

View File

@@ -9,6 +9,7 @@
#include "Ledger.h"
#include "Serializer.h"
#include "Log.h"
AccountState::AccountState(const NewcoinAddress& naAccountID) : mAccountID(naAccountID), mValid(false)
{
@@ -59,11 +60,8 @@ void AccountState::addJson(Json::Value& val)
void AccountState::dump()
{
Json::Value j(Json::objectValue);
addJson(j);
Json::StyledStreamWriter ssw;
ssw.write(std::cerr, j);
Log(lsINFO) << j;
}
// vim:ts=4

View File

@@ -78,6 +78,7 @@ public:
NetworkOPs& getOPs() { return mNetOps; }
boost::asio::io_service& getIOService() { return mIOService; }
boost::asio::io_service& getAuxService() { return mAuxService; }
LedgerMaster& getMasterLedger() { return mMasterLedger; }
LedgerAcquireMaster& getMasterLedgerAcquire() { return mMasterLedgerAcquire; }

View File

@@ -14,6 +14,7 @@ const char *TxnDBInit[] = {
LedgerSeq BIGINT UNSIGNED, \
Status CHARACTER(1), \
RawTxn BLOB \
TxnMeta BLOB \
);",
"CREATE TABLE PubKeys ( \
ID CHARACTER(35) PRIMARY KEY, \

View File

@@ -154,7 +154,7 @@ void Ledger::setAccepted(uint32 closeTime, int closeResolution, bool correctClos
void Ledger::setAccepted()
{ // used when we acquired the ledger
assert(mClosed && (mCloseTime != 0) && (mCloseResolution != 0));
// FIXME assert(mClosed && (mCloseTime != 0) && (mCloseResolution != 0));
mCloseTime -= mCloseTime % mCloseResolution;
updateHash();
mAccepted = true;
@@ -211,35 +211,51 @@ RippleState::pointer Ledger::accessRippleState(const uint256& uNode)
return boost::make_shared<RippleState>(sle);
}
bool Ledger::addTransaction(const Transaction::pointer& trans)
bool Ledger::addTransaction(const uint256& txID, const Serializer& txn)
{ // low-level - just add to table
assert(!mAccepted);
assert(trans->getID().isNonZero());
Serializer s;
trans->getSTransaction()->add(s);
SHAMapItem::pointer item = boost::make_shared<SHAMapItem>(trans->getID(), s.peekData());
if (!mTransactionMap->addGiveItem(item, true, false)) // FIXME: TX metadata
SHAMapItem::pointer item = boost::make_shared<SHAMapItem>(txID, txn.peekData());
if (!mTransactionMap->addGiveItem(item, true, false))
return false;
return true;
}
bool Ledger::addTransaction(const uint256& txID, const Serializer& txn)
bool Ledger::addTransaction(const uint256& txID, const Serializer& txn, const Serializer& md)
{ // low-level - just add to table
SHAMapItem::pointer item = boost::make_shared<SHAMapItem>(txID, txn.peekData());
if (!mTransactionMap->addGiveItem(item, true, false)) // FIXME: TX metadata
Serializer s(txn.getDataLength() + md.getDataLength() + 64);
s.addVL(txn.peekData());
s.addVL(md.peekData());
SHAMapItem::pointer item = boost::make_shared<SHAMapItem>(txID, s.peekData());
if (!mTransactionMap->addGiveItem(item, true, true))
return false;
return true;
}
Transaction::pointer Ledger::getTransaction(const uint256& transID) const
{
SHAMapItem::pointer item = mTransactionMap->peekItem(transID);
SHAMapTreeNode::TNType type;
SHAMapItem::pointer item = mTransactionMap->peekItem(transID, type);
if (!item) return Transaction::pointer();
Transaction::pointer txn = theApp->getMasterTransaction().fetch(transID, false);
if (txn) return txn;
if (txn)
return txn;
if (type == SHAMapTreeNode::tnTRANSACTION_NM)
txn = Transaction::sharedTransaction(item->getData(), true);
else if (type == SHAMapTreeNode::tnTRANSACTION_MD)
{
std::vector<unsigned char> txnData;
int txnLength;
if (!item->peekSerializer().getVL(txnData, 0, txnLength))
return Transaction::pointer();
txn = Transaction::sharedTransaction(txnData, false);
}
else
{
assert(false);
return Transaction::pointer();
}
txn = Transaction::sharedTransaction(item->getData(), true);
if (txn->getStatus() == NEW)
txn->setStatus(mClosed ? COMMITTED : INCLUDED, mLedgerSeq);
@@ -247,6 +263,39 @@ Transaction::pointer Ledger::getTransaction(const uint256& transID) const
return txn;
}
bool Ledger::getTransaction(const uint256& txID, Transaction::pointer& txn, TransactionMetaSet::pointer& meta)
{
SHAMapTreeNode::TNType type;
SHAMapItem::pointer item = mTransactionMap->peekItem(txID, type);
if (!item)
return false;
if (type == SHAMapTreeNode::tnTRANSACTION_NM)
{ // in tree with no metadata
txn = theApp->getMasterTransaction().fetch(txID, false);
meta = TransactionMetaSet::pointer();
if (!txn)
txn = Transaction::sharedTransaction(item->getData(), true);
}
else if (type == SHAMapTreeNode::tnTRANSACTION_MD)
{ // in tree with metadata
SerializerIterator it(item->getData());
txn = theApp->getMasterTransaction().fetch(txID, false);
if (!txn)
txn = Transaction::sharedTransaction(it.getVL(), true);
else
it.getVL(); // skip transaction
meta = boost::make_shared<TransactionMetaSet>(mLedgerSeq, it.getVL());
}
else
return false;
if (txn->getStatus() == NEW)
txn->setStatus(mClosed ? COMMITTED : INCLUDED, mLedgerSeq);
theApp->getMasterTransaction().canonicalize(txn, false);
return true;
}
bool Ledger::unitTest()
{
return true;
@@ -440,14 +489,38 @@ void Ledger::addJson(Json::Value& ret, int options)
if (mTransactionMap && (full || ((options & LEDGER_JSON_DUMP_TXNS) != 0)))
{
Json::Value txns(Json::arrayValue);
for (SHAMapItem::pointer item = mTransactionMap->peekFirstItem(); !!item;
item = mTransactionMap->peekNextItem(item->getTag()))
SHAMapTreeNode::TNType type;
for (SHAMapItem::pointer item = mTransactionMap->peekFirstItem(type); !!item;
item = mTransactionMap->peekNextItem(item->getTag(), type))
{
if (full)
{
SerializerIterator sit(item->peekSerializer());
SerializedTransaction txn(sit);
txns.append(txn.getJson(0));
if (type == SHAMapTreeNode::tnTRANSACTION_NM)
{
SerializerIterator sit(item->peekSerializer());
SerializedTransaction txn(sit);
txns.append(txn.getJson(0));
}
else if (type == SHAMapTreeNode::tnTRANSACTION_MD)
{
SerializerIterator sit(item->peekSerializer());
Serializer sTxn(sit.getVL());
Serializer sMeta(sit.getVL());
SerializerIterator tsit(sTxn);
SerializedTransaction txn(tsit);
TransactionMetaSet meta(mLedgerSeq, sit.getVL());
Json::Value txJson = txn.getJson(0);
txJson["metaData"] = meta.getJson(0);
txns.append(txJson);
}
else
{
Json::Value error = Json::objectValue;
error[item->getTag().GetHex()] = type;
txns.append(error);
}
}
else txns.append(item->getTag().GetHex());
}

View File

@@ -11,6 +11,7 @@
#include "../json/value.h"
#include "Transaction.h"
#include "TransactionMeta.h"
#include "AccountState.h"
#include "RippleState.h"
#include "NicknameState.h"
@@ -56,7 +57,7 @@ public:
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
TR_TOOSMALL = 9, // amount is less than Tx fee
};
// ledger close flags
@@ -82,8 +83,6 @@ private:
protected:
bool addTransaction(const Transaction::pointer&);
bool addTransaction(const uint256& id, const Serializer& txn);
static Ledger::pointer getSQL(const std::string& sqlStatement);
@@ -152,8 +151,11 @@ public:
bool isAcquiringAS(void);
// Transaction Functions
bool addTransaction(const uint256& id, const Serializer& txn);
bool addTransaction(const uint256& id, const Serializer& txn, const Serializer& metaData);
bool hasTransaction(const uint256& TransID) const { return mTransactionMap->hasItem(TransID); }
Transaction::pointer getTransaction(const uint256& transID) const;
bool getTransaction(const uint256& transID, Transaction::pointer& txn, TransactionMetaSet::pointer& txMeta);
// high-level functions
AccountState::pointer getAccountState(const NewcoinAddress& acctID);

View File

@@ -84,9 +84,8 @@ void PeerSet::TimerEntry(boost::weak_ptr<PeerSet> wptr, const boost::system::err
if (result == boost::asio::error::operation_aborted)
return;
boost::shared_ptr<PeerSet> ptr = wptr.lock();
if (!ptr)
return;
ptr->invokeOnTimer();
if (ptr)
ptr->invokeOnTimer();
}
LedgerAcquire::LedgerAcquire(const uint256& hash) : PeerSet(hash, LEDGER_ACQUIRE_TIMEOUT),
@@ -109,7 +108,7 @@ void LedgerAcquire::onTimer()
boost::weak_ptr<PeerSet> LedgerAcquire::pmDowncast()
{
return boost::shared_polymorphic_downcast<PeerSet, LedgerAcquire>(shared_from_this());
return boost::shared_polymorphic_downcast<PeerSet>(shared_from_this());
}
void LedgerAcquire::done()

View File

@@ -78,6 +78,7 @@ protected:
public:
LedgerAcquire(const uint256& hash);
virtual ~LedgerAcquire() { ; }
bool isBase() const { return mHaveBase; }
bool isAcctStComplete() const { return mHaveState; }

View File

@@ -25,24 +25,26 @@ typedef std::pair<const uint256, LCTransaction::pointer> u256_lct_pair;
TransactionAcquire::TransactionAcquire(const uint256& hash) : PeerSet(hash, TX_ACQUIRE_TIMEOUT), mHaveRoot(false)
{
mMap = boost::make_shared<SHAMap>();
mMap->setSynching();
mMap = boost::make_shared<SHAMap>(hash);
}
void TransactionAcquire::done()
{
if (mFailed)
{
Log(lsWARNING) << "Failed to acqiure TXs " << mHash;
Log(lsWARNING) << "Failed to acquire TXs " << mHash;
theApp->getOPs().mapComplete(mHash, SHAMap::pointer());
}
else
{
mMap->setImmutable();
theApp->getOPs().mapComplete(mHash, mMap);
}
}
boost::weak_ptr<PeerSet> TransactionAcquire::pmDowncast()
{
return boost::shared_polymorphic_downcast<PeerSet, TransactionAcquire>(shared_from_this());
return boost::shared_polymorphic_downcast<PeerSet>(shared_from_this());
}
void TransactionAcquire::trigger(Peer::ref peer, bool timer)
@@ -109,7 +111,8 @@ bool TransactionAcquire::takeNodes(const std::list<SHAMapNode>& nodeIDs,
}
if (!mMap->addRootNode(getHash(), *nodeDatait, snfWIRE))
return false;
else mHaveRoot = true;
else
mHaveRoot = true;
}
else if (!mMap->addKnownNode(*nodeIDit, *nodeDatait, &sf))
return false;
@@ -174,37 +177,41 @@ void LCTransaction::unVote(const uint160& peer)
}
}
bool LCTransaction::updatePosition(int percentTime, bool proposing)
bool LCTransaction::updateVote(int percentTime, bool proposing)
{
if (mOurPosition && (mNays == 0))
if (mOurVote && (mNays == 0))
return false;
if (!mOurPosition && (mYays == 0))
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)
int weight = (mYays * 100 + (mOurPosition ? 100 : 0)) / (mNays + mYays + 1);
weight = (mYays * 100 + (mOurVote ? 100 : 0)) / (mNays + mYays + 1);
// 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 newPosition = weight > AV_LATE_CONSENSUS_PCT;
}
else // don't let us outweight a proposing node, just recognize consensus
else // don't let us outweigh a proposing node, just recognize consensus
{
weight = -1;
newPosition = mYays > mNays;
}
if (newPosition == mOurPosition)
if (newPosition == mOurVote)
{
#ifdef LC_DEBUG
Log(lsTRACE) << "No change (" << (mOurPosition ? "YES" : "NO") << ") : weight "
Log(lsTRACE) << "No change (" << (mOurVote ? "YES" : "NO") << ") : weight "
<< weight << ", percent " << percentTime;
#endif
return false;
}
mOurPosition = newPosition;
Log(lsTRACE) << "We now vote " << (mOurPosition ? "YES" : "NO") << " on " << mTransactionID;
mOurVote = newPosition;
Log(lsTRACE) << "We now vote " << (mOurVote ? "YES" : "NO") << " on " << mTransactionID;
return true;
}
@@ -333,6 +340,7 @@ void LedgerConsensus::handleLCL(const uint256& lclHash)
mPeerPositions.clear();
mDisputes.clear();
mDeadNodes.clear();
playbackProposals();
return;
}
@@ -357,9 +365,9 @@ void LedgerConsensus::takeInitialPosition(Ledger& initialLedger)
{
uint256 set = it.second->getCurrentHash();
if (found.insert(set).second)
{
boost::unordered_map<uint256, SHAMap::pointer>::iterator iit = mComplete.find(set);
if (iit != mComplete.end())
{ // OPTIMIZEME: Don't process the same set more than once
boost::unordered_map<uint256, SHAMap::pointer>::iterator iit = mAcquired.find(set);
if (iit != mAcquired.end())
createDisputes(initialSet, iit->second);
}
}
@@ -374,7 +382,7 @@ void LedgerConsensus::takeInitialPosition(Ledger& initialLedger)
propose();
}
void LedgerConsensus::createDisputes(const SHAMap::pointer& m1, const SHAMap::pointer& m2)
void LedgerConsensus::createDisputes(SHAMap::ref m1, SHAMap::ref m2)
{
SHAMap::SHAMapDiff differences;
m1->compare(m2, differences, 16384);
@@ -394,33 +402,36 @@ void LedgerConsensus::createDisputes(const SHAMap::pointer& m1, const SHAMap::po
}
}
void LedgerConsensus::mapComplete(const uint256& hash, const SHAMap::pointer& map, bool acquired)
void LedgerConsensus::mapComplete(const uint256& hash, SHAMap::ref map, bool acquired)
{
if (acquired)
Log(lsINFO) << "We have acquired TXS " << hash;
mAcquiring.erase(hash);
if (!map)
{ // this is an invalid/corrupt map
mComplete[hash] = map;
mAcquired[hash] = map;
mAcquiring.erase(hash);
Log(lsWARNING) << "A trusted node directed us to acquire an invalid TXN map";
return;
}
assert(hash == map->getHash());
if (mComplete.find(hash) != mComplete.end())
if (mAcquired.find(hash) != mAcquired.end())
return; // we already have this map
if (mOurPosition && (map->getHash() != mOurPosition->getCurrentHash()))
if (mOurPosition && (!mOurPosition->isBowOut()) && (hash != mOurPosition->getCurrentHash()))
{ // this could create disputed transactions
boost::unordered_map<uint256, SHAMap::pointer>::iterator it2 = mComplete.find(mOurPosition->getCurrentHash());
if (it2 != mComplete.end())
boost::unordered_map<uint256, SHAMap::pointer>::iterator it2 = mAcquired.find(mOurPosition->getCurrentHash());
if (it2 != mAcquired.end())
{
assert((it2->first == mOurPosition->getCurrentHash()) && it2->second);
createDisputes(it2->second, map);
}
else assert(false); // We don't have our own position?!
else
assert(false); // We don't have our own position?!
}
mComplete[map->getHash()] = map;
mAcquired[hash] = map;
mAcquiring.erase(hash);
// Adjust tracking for each peer that takes this position
std::vector<uint160> peers;
@@ -446,7 +457,7 @@ void LedgerConsensus::sendHaveTxSet(const uint256& hash, bool direct)
theApp->getConnectionPool().relayMessage(NULL, packet);
}
void LedgerConsensus::adjustCount(const SHAMap::pointer& map, const std::vector<uint160>& peers)
void LedgerConsensus::adjustCount(SHAMap::ref map, const std::vector<uint160>& peers)
{ // Adjust the counts on all disputed transactions based on the set of peers taking this position
BOOST_FOREACH(u256_lct_pair& it, mDisputes)
{
@@ -594,14 +605,15 @@ void LedgerConsensus::updateOurPositions()
BOOST_FOREACH(u256_lct_pair& it, mDisputes)
{
if (it.second->updatePosition(mClosePercent, mProposing))
if (it.second->updateVote(mClosePercent, mProposing))
{
if (!changes)
{
ourPosition = mComplete[mOurPosition->getCurrentHash()]->snapShot(true);
ourPosition = mAcquired[mOurPosition->getCurrentHash()]->snapShot(true);
assert(ourPosition);
changes = true;
}
if (it.second->getOurPosition()) // now a yes
if (it.second->getOurVote()) // now a yes
{
ourPosition->addItem(SHAMapItem(it.first, it.second->peekTransaction()), true, false);
// addedTx.push_back(it.first);
@@ -662,41 +674,53 @@ void LedgerConsensus::updateOurPositions()
((closeTime != (roundCloseTime(mOurPosition->getCloseTime()))) ||
(mOurPosition->isStale(ourCutoff))))
{ // close time changed or our position is stale
ourPosition = mComplete[mOurPosition->getCurrentHash()]->snapShot(true);
ourPosition = mAcquired[mOurPosition->getCurrentHash()]->snapShot(true);
assert(ourPosition);
changes = true;
}
if (changes)
{
uint256 newHash = ourPosition->getHash();
mOurPosition->changePosition(newHash, closeTime);
if (mProposing)
propose();
mapComplete(newHash, ourPosition, false);
Log(lsINFO) << "Position change: CTime " << closeTime << ", tx " << newHash;
if (mOurPosition->changePosition(newHash, closeTime))
{
if (mProposing)
propose();
mapComplete(newHash, ourPosition, false);
}
}
}
bool LedgerConsensus::haveConsensus()
{
{ // FIXME: Should check for a supermajority on each disputed transaction
// counting unacquired TX sets as disagreeing
int agree = 0, disagree = 0;
uint256 ourPosition = mOurPosition->getCurrentHash();
BOOST_FOREACH(u160_prop_pair& it, mPeerPositions)
{
if (it.second->getCurrentHash() == ourPosition)
++agree;
else
++disagree;
if (!it.second->isBowOut())
{
if (it.second->getCurrentHash() == ourPosition)
++agree;
else
++disagree;
}
}
int currentValidations = theApp->getValidations().getCurrentValidationCount(mPreviousLedger->getCloseTimeNC());
int currentValidations = theApp->getValidations().getNodesAfter(mPrevLedgerHash);
#ifdef LC_DEBUG
Log(lsINFO) << "Checking for TX consensus: agree=" << agree << ", disagree=" << disagree;
#endif
return ContinuousLedgerTiming::haveConsensus(mPreviousProposers, agree + disagree, agree, currentValidations,
mPreviousMSeconds, mCurrentMSeconds);
}
SHAMap::pointer LedgerConsensus::getTransactionTree(const uint256& hash, bool doAcquire)
{
boost::unordered_map<uint256, SHAMap::pointer>::iterator it = mComplete.find(hash);
if (it == mComplete.end())
boost::unordered_map<uint256, SHAMap::pointer>::iterator it = mAcquired.find(hash);
if (it == mAcquired.end())
{ // we have not completed acquiring this ledger
if (mState == lcsPRE_CLOSE)
@@ -780,10 +804,11 @@ void LedgerConsensus::addDisputedTransaction(const uint256& txID, const std::vec
bool ourPosition = false;
if (mOurPosition)
{
boost::unordered_map<uint256, SHAMap::pointer>::iterator mit = mComplete.find(mOurPosition->getCurrentHash());
if (mit != mComplete.end())
boost::unordered_map<uint256, SHAMap::pointer>::iterator mit = mAcquired.find(mOurPosition->getCurrentHash());
if (mit != mAcquired.end())
ourPosition = mit->second->hasItem(txID);
else assert(false); // We don't have our own position?
else
assert(false); // We don't have our own position?
}
LCTransaction::pointer txn = boost::make_shared<LCTransaction>(txID, tx, ourPosition);
@@ -792,8 +817,8 @@ void LedgerConsensus::addDisputedTransaction(const uint256& txID, const std::vec
BOOST_FOREACH(u160_prop_pair& pit, mPeerPositions)
{
boost::unordered_map<uint256, SHAMap::pointer>::const_iterator cit =
mComplete.find(pit.second->getCurrentHash());
if (cit != mComplete.end() && cit->second)
mAcquired.find(pit.second->getCurrentHash());
if (cit != mAcquired.end() && cit->second)
txn->setVote(pit.first, cit->second->hasItem(txID));
}
}
@@ -867,13 +892,15 @@ bool LedgerConsensus::peerGaveNodes(Peer::ref peer, const uint256& setHash,
const std::list<SHAMapNode>& nodeIDs, const std::list< std::vector<unsigned char> >& nodeData)
{
boost::unordered_map<uint256, TransactionAcquire::pointer>::iterator acq = mAcquiring.find(setHash);
if (acq == mAcquiring.end()) return false;
return acq->second->takeNodes(nodeIDs, nodeData, peer);
if (acq == mAcquiring.end())
return false;
TransactionAcquire::pointer set = acq->second; // We must keep the set around during the function
return set->takeNodes(nodeIDs, nodeData, peer);
}
void LedgerConsensus::beginAccept()
{
SHAMap::pointer consensusSet = mComplete[mOurPosition->getCurrentHash()];
SHAMap::pointer consensusSet = mAcquired[mOurPosition->getCurrentHash()];
if (!consensusSet)
{
Log(lsFATAL) << "We don't have a consensus set";
@@ -882,8 +909,7 @@ void LedgerConsensus::beginAccept()
}
theApp->getOPs().newLCL(mPeerPositions.size(), mCurrentMSeconds, mNewLedgerHash);
boost::thread thread(boost::bind(&LedgerConsensus::Saccept, shared_from_this(), consensusSet));
thread.detach();
theApp->getIOService().post(boost::bind(&LedgerConsensus::Saccept, shared_from_this(), consensusSet));
}
void LedgerConsensus::Saccept(boost::shared_ptr<LedgerConsensus> This, SHAMap::pointer txSet)
@@ -954,7 +980,7 @@ void LedgerConsensus::applyTransaction(TransactionEngine& engine, const Serializ
#endif
}
void LedgerConsensus::applyTransactions(const SHAMap::pointer& set, Ledger::ref applyLedger,
void LedgerConsensus::applyTransactions(SHAMap::ref set, Ledger::ref applyLedger,
Ledger::ref checkLedger, CanonicalTXSet& failedTransactions, bool openLgr)
{
TransactionEngineParams parms = openLgr ? tapOPEN_LEDGER : tapNONE;
@@ -1016,7 +1042,7 @@ uint32 LedgerConsensus::roundCloseTime(uint32 closeTime)
return closeTime - (closeTime % mCloseResolution);
}
void LedgerConsensus::accept(const SHAMap::pointer& set)
void LedgerConsensus::accept(SHAMap::ref set)
{
assert(set->getHash() == mOurPosition->getCurrentHash());
@@ -1070,7 +1096,7 @@ void LedgerConsensus::accept(const SHAMap::pointer& set)
TransactionEngine engine(newOL);
BOOST_FOREACH(u256_lct_pair& it, mDisputes)
{
if (!it.second->getOurPosition())
if (!it.second->getOurVote())
{ // we voted NO
try
{

View File

@@ -37,11 +37,11 @@ protected:
public:
TransactionAcquire(const uint256& hash);
virtual ~TransactionAcquire() { ; }
SHAMap::pointer getMap() { return mMap; }
bool takeNodes(const std::list<SHAMapNode>& IDs, const std::list< std::vector<unsigned char> >& data,
Peer::ref);
bool takeNodes(const std::list<SHAMapNode>& IDs, const std::list< std::vector<unsigned char> >& data, Peer::ref);
};
class LCTransaction
@@ -49,24 +49,24 @@ class LCTransaction
protected:
uint256 mTransactionID;
int mYays, mNays;
bool mOurPosition;
bool mOurVote;
Serializer transaction;
boost::unordered_map<uint160, bool> mVotes;
public:
typedef boost::shared_ptr<LCTransaction> pointer;
LCTransaction(const uint256 &txID, const std::vector<unsigned char>& tx, bool ourPosition) :
mTransactionID(txID), mYays(0), mNays(0), mOurPosition(ourPosition), transaction(tx) { ; }
LCTransaction(const uint256 &txID, const std::vector<unsigned char>& tx, bool ourVote) :
mTransactionID(txID), mYays(0), mNays(0), mOurVote(ourVote), transaction(tx) { ; }
const uint256& getTransactionID() const { return mTransactionID; }
bool getOurPosition() const { return mOurPosition; }
bool getOurVote() const { return mOurVote; }
Serializer& peekTransaction() { return transaction; }
void setVote(const uint160& peer, bool votesYes);
void unVote(const uint160& peer);
bool updatePosition(int percentTime, bool proposing);
bool updateVote(int percentTime, bool proposing);
};
enum LCState
@@ -100,7 +100,7 @@ protected:
boost::unordered_map<uint160, LedgerProposal::pointer> mPeerPositions;
// Transaction Sets, indexed by hash of transaction tree
boost::unordered_map<uint256, SHAMap::pointer> mComplete;
boost::unordered_map<uint256, SHAMap::pointer> mAcquired;
boost::unordered_map<uint256, TransactionAcquire::pointer> mAcquiring;
// Peer sets
@@ -120,21 +120,21 @@ protected:
// final accept logic
static void Saccept(boost::shared_ptr<LedgerConsensus> This, SHAMap::pointer txSet);
void accept(const SHAMap::pointer& txSet);
void accept(SHAMap::ref txSet);
void weHave(const uint256& id, Peer::ref avoidPeer);
void startAcquiring(const TransactionAcquire::pointer&);
SHAMap::pointer find(const uint256& hash);
void createDisputes(const SHAMap::pointer&, const SHAMap::pointer&);
void createDisputes(SHAMap::ref, SHAMap::ref);
void addDisputedTransaction(const uint256&, const std::vector<unsigned char>& transaction);
void adjustCount(const SHAMap::pointer& map, const std::vector<uint160>& peers);
void adjustCount(SHAMap::ref map, const std::vector<uint160>& peers);
void propose();
void addPosition(LedgerProposal&, bool ours);
void removePosition(LedgerProposal&, bool ours);
void sendHaveTxSet(const uint256& set, bool direct);
void applyTransactions(const SHAMap::pointer& transactionSet, Ledger::ref targetLedger,
void applyTransactions(SHAMap::ref transactionSet, Ledger::ref targetLedger,
Ledger::ref checkLedger, CanonicalTXSet& failedTransactions, bool openLgr);
void applyTransaction(TransactionEngine& engine, const SerializedTransaction::pointer& txn,
Ledger::ref targetLedger, CanonicalTXSet& failedTransactions, bool openLgr);
@@ -162,7 +162,7 @@ public:
SHAMap::pointer getTransactionTree(const uint256& hash, bool doAcquire);
TransactionAcquire::pointer getAcquiring(const uint256& hash);
void mapComplete(const uint256& hash, const SHAMap::pointer& map, bool acquired);
void mapComplete(const uint256& hash, SHAMap::ref map, bool acquired);
void checkLCL();
void handleLCL(const uint256& lclHash);

View File

@@ -1,7 +1,13 @@
#include "LedgerEntrySet.h"
#include <boost/make_shared.hpp>
#include "Log.h"
// Small for testing, should likely be 32 or 64.
#define DIR_NODE_MAX 2
void LedgerEntrySet::init(Ledger::ref ledger, const uint256& transactionID, uint32 ledgerID)
{
mEntries.clear();
@@ -251,6 +257,8 @@ Json::Value LedgerEntrySet::getJson(int) const
}
ret["nodes" ] = nodes;
ret["metaData"] = mSet.getJson(0);
return ret;
}
@@ -286,9 +294,13 @@ SLE::pointer LedgerEntrySet::getForMod(const uint256& node, Ledger::ref ledger,
bool LedgerEntrySet::threadTx(TransactionMetaNode& metaNode, const NewcoinAddress& threadTo, Ledger::ref ledger,
boost::unordered_map<uint256, SLE::pointer>& newMods)
{
Log(lsTRACE) << "Thread to " << threadTo.getAccountID();
SLE::pointer sle = getForMod(Ledger::getAccountRootIndex(threadTo.getAccountID()), ledger, newMods);
if (!sle)
{
assert(false);
return false;
}
return threadTx(metaNode, sle, ledger, newMods);
}
@@ -319,7 +331,7 @@ bool LedgerEntrySet::threadOwners(TransactionMetaNode& metaNode, SLE::ref node,
return false;
}
void LedgerEntrySet::calcRawMeta(Serializer& s, Ledger::ref origLedger)
void LedgerEntrySet::calcRawMeta(Serializer& s)
{ // calculate the raw meta data and return it. This must be called before the set is committed
// Entries modified only as a result of building the transaction metadata
@@ -344,21 +356,25 @@ void LedgerEntrySet::calcRawMeta(Serializer& s, Ledger::ref origLedger)
nType = TMNCreatedNode;
break;
default:
// ignore these
default: // ignore these
break;
}
if (nType == TMNEndOfMetadata)
continue;
SLE::pointer origNode = origLedger->getSLE(it->first);
SLE::pointer origNode = mLedger->getSLE(it->first);
if (origNode && (origNode->getType() == ltDIR_NODE)) // No metadata for dir nodes
continue;
SLE::pointer curNode = it->second.mEntry;
TransactionMetaNode &metaNode = mSet.getAffectedNode(it->first, nType);
if (nType == TMNDeletedNode)
{
threadOwners(metaNode, origNode, origLedger, newMod);
assert(origNode);
threadOwners(metaNode, origNode, mLedger, newMod);
if (origNode->getIFieldPresent(sfAmount))
{ // node has an amount, covers ripple state nodes
@@ -390,16 +406,20 @@ void LedgerEntrySet::calcRawMeta(Serializer& s, Ledger::ref origLedger)
}
if (nType == TMNCreatedNode) // if created, thread to owner(s)
threadOwners(metaNode, curNode, origLedger, newMod);
{
assert(!origNode);
threadOwners(metaNode, curNode, mLedger, newMod);
}
if ((nType == TMNCreatedNode) || (nType == TMNModifiedNode))
{
if (curNode->isThreadedType()) // always thread to self
threadTx(metaNode, curNode, origLedger, newMod);
threadTx(metaNode, curNode, mLedger, newMod);
}
if (nType == TMNModifiedNode)
{
assert(origNode);
if (origNode->getIFieldPresent(sfAmount))
{ // node has an amount, covers account root nodes and ripple nodes
STAmount amount = origNode->getIValueFieldAmount(sfAmount);
@@ -423,9 +443,697 @@ void LedgerEntrySet::calcRawMeta(Serializer& s, Ledger::ref origLedger)
// add any new modified nodes to the modification set
for (boost::unordered_map<uint256, SLE::pointer>::iterator it = newMod.begin(), end = newMod.end();
it != end; ++it)
entryCache(it->second);
entryModify(it->second);
#ifdef DEBUG
Log(lsINFO) << "Metadata:" << mSet.getJson(0);
#endif
mSet.addRaw(s);
}
// <-- uNodeDir: For deletion, present to make dirDelete efficient.
// --> uRootIndex: The index of the base of the directory. Nodes are based off of this.
// --> uLedgerIndex: Value to add to directory.
// We only append. This allow for things that watch append only structure to just monitor from the last node on ward.
// Within a node with no deletions order of elements is sequential. Otherwise, order of elements is random.
TER LedgerEntrySet::dirAdd(
uint64& uNodeDir,
const uint256& uRootIndex,
const uint256& uLedgerIndex)
{
SLE::pointer sleNode;
STVector256 svIndexes;
SLE::pointer sleRoot = entryCache(ltDIR_NODE, uRootIndex);
if (!sleRoot)
{
// No root, make it.
sleRoot = entryCreate(ltDIR_NODE, uRootIndex);
sleNode = sleRoot;
uNodeDir = 0;
}
else
{
uNodeDir = sleRoot->getIFieldU64(sfIndexPrevious); // Get index to last directory node.
if (uNodeDir)
{
// Try adding to last node.
sleNode = entryCache(ltDIR_NODE, Ledger::getDirNodeIndex(uRootIndex, uNodeDir));
assert(sleNode);
}
else
{
// Try adding to root. Didn't have a previous set to the last node.
sleNode = sleRoot;
}
svIndexes = sleNode->getIFieldV256(sfIndexes);
if (DIR_NODE_MAX != svIndexes.peekValue().size())
{
// Add to current node.
entryModify(sleNode);
}
// Add to new node.
else if (!++uNodeDir)
{
return terDIR_FULL;
}
else
{
// Have old last point to new node, if it was not root.
if (uNodeDir == 1)
{
// Previous node is root node.
sleRoot->setIFieldU64(sfIndexNext, uNodeDir);
}
else
{
// Previous node is not root node.
SLE::pointer slePrevious = entryCache(ltDIR_NODE, Ledger::getDirNodeIndex(uRootIndex, uNodeDir-1));
slePrevious->setIFieldU64(sfIndexNext, uNodeDir);
entryModify(slePrevious);
sleNode->setIFieldU64(sfIndexPrevious, uNodeDir-1);
}
// Have root point to new node.
sleRoot->setIFieldU64(sfIndexPrevious, uNodeDir);
entryModify(sleRoot);
// Create the new node.
sleNode = entryCreate(ltDIR_NODE, Ledger::getDirNodeIndex(uRootIndex, uNodeDir));
svIndexes = STVector256();
}
}
svIndexes.peekValue().push_back(uLedgerIndex); // Append entry.
sleNode->setIFieldV256(sfIndexes, svIndexes); // Save entry.
Log(lsINFO) << "dirAdd: creating: root: " << uRootIndex.ToString();
Log(lsINFO) << "dirAdd: appending: Entry: " << uLedgerIndex.ToString();
Log(lsINFO) << "dirAdd: appending: Node: " << strHex(uNodeDir);
// Log(lsINFO) << "dirAdd: appending: PREV: " << svIndexes.peekValue()[0].ToString();
return tesSUCCESS;
}
// Ledger must be in a state for this to work.
TER LedgerEntrySet::dirDelete(
const bool bKeepRoot, // --> True, if we never completely clean up, after we overflow the root node.
const uint64& uNodeDir, // --> Node containing entry.
const uint256& uRootIndex, // --> The index of the base of the directory. Nodes are based off of this.
const uint256& uLedgerIndex, // --> Value to add to directory.
const bool bStable) // --> True, not to change relative order of entries.
{
uint64 uNodeCur = uNodeDir;
SLE::pointer sleNode = entryCache(ltDIR_NODE, uNodeCur ? Ledger::getDirNodeIndex(uRootIndex, uNodeCur) : uRootIndex);
assert(sleNode);
if (!sleNode)
{
Log(lsWARNING) << "dirDelete: no such node";
return tefBAD_LEDGER;
}
STVector256 svIndexes = sleNode->getIFieldV256(sfIndexes);
std::vector<uint256>& vuiIndexes = svIndexes.peekValue();
std::vector<uint256>::iterator it;
it = std::find(vuiIndexes.begin(), vuiIndexes.end(), uLedgerIndex);
assert(vuiIndexes.end() != it);
if (vuiIndexes.end() == it)
{
assert(false);
Log(lsWARNING) << "dirDelete: no such entry";
return tefBAD_LEDGER;
}
// Remove the element.
if (vuiIndexes.size() > 1)
{
if (bStable)
{
vuiIndexes.erase(it);
}
else
{
*it = vuiIndexes[vuiIndexes.size()-1];
vuiIndexes.resize(vuiIndexes.size()-1);
}
}
else
{
vuiIndexes.clear();
}
sleNode->setIFieldV256(sfIndexes, svIndexes);
entryModify(sleNode);
if (vuiIndexes.empty())
{
// May be able to delete nodes.
uint64 uNodePrevious = sleNode->getIFieldU64(sfIndexPrevious);
uint64 uNodeNext = sleNode->getIFieldU64(sfIndexNext);
if (!uNodeCur)
{
// Just emptied root node.
if (!uNodePrevious)
{
// Never overflowed the root node. Delete it.
entryDelete(sleNode);
}
// Root overflowed.
else if (bKeepRoot)
{
// If root overflowed and not allowed to delete overflowed root node.
nothing();
}
else if (uNodePrevious != uNodeNext)
{
// Have more than 2 nodes. Can't delete root node.
nothing();
}
else
{
// Have only a root node and a last node.
SLE::pointer sleLast = entryCache(ltDIR_NODE, Ledger::getDirNodeIndex(uRootIndex, uNodeNext));
assert(sleLast);
if (sleLast->getIFieldV256(sfIndexes).peekValue().empty())
{
// Both nodes are empty.
entryDelete(sleNode); // Delete root.
entryDelete(sleLast); // Delete last.
}
else
{
// Have an entry, can't delete root node.
nothing();
}
}
}
// Just emptied a non-root node.
else if (uNodeNext)
{
// Not root and not last node. Can delete node.
SLE::pointer slePrevious = entryCache(ltDIR_NODE, uNodePrevious ? Ledger::getDirNodeIndex(uRootIndex, uNodePrevious) : uRootIndex);
assert(slePrevious);
SLE::pointer sleNext = entryCache(ltDIR_NODE, uNodeNext ? Ledger::getDirNodeIndex(uRootIndex, uNodeNext) : uRootIndex);
assert(slePrevious);
assert(sleNext);
if (!slePrevious)
{
Log(lsWARNING) << "dirDelete: previous node is missing";
return tefBAD_LEDGER;
}
if (!sleNext)
{
Log(lsWARNING) << "dirDelete: next node is missing";
return tefBAD_LEDGER;
}
// Fix previous to point to its new next.
slePrevious->setIFieldU64(sfIndexNext, uNodeNext);
entryModify(slePrevious);
// Fix next to point to its new previous.
sleNext->setIFieldU64(sfIndexPrevious, uNodePrevious);
entryModify(sleNext);
}
// Last node.
else if (bKeepRoot || uNodePrevious)
{
// Not allowed to delete last node as root was overflowed.
// Or, have pervious entries preventing complete delete.
nothing();
}
else
{
// Last and only node besides the root.
SLE::pointer sleRoot = entryCache(ltDIR_NODE, uRootIndex);
assert(sleRoot);
if (sleRoot->getIFieldV256(sfIndexes).peekValue().empty())
{
// Both nodes are empty.
entryDelete(sleRoot); // Delete root.
entryDelete(sleNode); // Delete last.
}
else
{
// Root has an entry, can't delete.
nothing();
}
}
}
return tesSUCCESS;
}
// Return the first entry and advance uDirEntry.
// <-- true, if had a next entry.
bool LedgerEntrySet::dirFirst(
const uint256& uRootIndex, // --> Root of directory.
SLE::pointer& sleNode, // <-- current node
unsigned int& uDirEntry, // <-- next entry
uint256& uEntryIndex) // <-- The entry, if available. Otherwise, zero.
{
sleNode = entryCache(ltDIR_NODE, uRootIndex);
uDirEntry = 0;
assert(sleNode); // We never probe for directories.
return LedgerEntrySet::dirNext(uRootIndex, sleNode, uDirEntry, uEntryIndex);
}
// Return the current entry and advance uDirEntry.
// <-- true, if had a next entry.
bool LedgerEntrySet::dirNext(
const uint256& uRootIndex, // --> Root of directory
SLE::pointer& sleNode, // <-> current node
unsigned int& uDirEntry, // <-> next entry
uint256& uEntryIndex) // <-- The entry, if available. Otherwise, zero.
{
STVector256 svIndexes = sleNode->getIFieldV256(sfIndexes);
std::vector<uint256>& vuiIndexes = svIndexes.peekValue();
if (uDirEntry == vuiIndexes.size())
{
uint64 uNodeNext = sleNode->getIFieldU64(sfIndexNext);
if (!uNodeNext)
{
uEntryIndex.zero();
return false;
}
else
{
sleNode = entryCache(ltDIR_NODE, Ledger::getDirNodeIndex(uRootIndex, uNodeNext));
uDirEntry = 0;
return dirNext(uRootIndex, sleNode, uDirEntry, uEntryIndex);
}
}
uEntryIndex = vuiIndexes[uDirEntry++];
Log(lsINFO) << boost::str(boost::format("dirNext: uDirEntry=%d uEntryIndex=%s") % uDirEntry % uEntryIndex);
return true;
}
TER LedgerEntrySet::offerDelete(const SLE::pointer& sleOffer, const uint256& uOfferIndex, const uint160& uOwnerID)
{
uint64 uOwnerNode = sleOffer->getIFieldU64(sfOwnerNode);
TER terResult = dirDelete(false, uOwnerNode, Ledger::getOwnerDirIndex(uOwnerID), uOfferIndex, false);
if (tesSUCCESS == terResult)
{
uint256 uDirectory = sleOffer->getIFieldH256(sfBookDirectory);
uint64 uBookNode = sleOffer->getIFieldU64(sfBookNode);
terResult = dirDelete(false, uBookNode, uDirectory, uOfferIndex, true);
}
entryDelete(sleOffer);
return terResult;
}
TER LedgerEntrySet::offerDelete(const uint256& uOfferIndex)
{
SLE::pointer sleOffer = entryCache(ltOFFER, uOfferIndex);
const uint160 uOwnerID = sleOffer->getIValueFieldAccount(sfAccount).getAccountID();
return offerDelete(sleOffer, uOfferIndex, uOwnerID);
}
// Returns amount owed by uToAccountID to uFromAccountID.
// <-- $owed/uCurrencyID/uToAccountID: positive: uFromAccountID holds IOUs., negative: uFromAccountID owes IOUs.
STAmount LedgerEntrySet::rippleOwed(const uint160& uToAccountID, const uint160& uFromAccountID, const uint160& uCurrencyID)
{
STAmount saBalance;
SLE::pointer sleRippleState = entryCache(ltRIPPLE_STATE, Ledger::getRippleStateIndex(uToAccountID, uFromAccountID, uCurrencyID));
if (sleRippleState)
{
saBalance = sleRippleState->getIValueFieldAmount(sfBalance);
if (uToAccountID < uFromAccountID)
saBalance.negate();
saBalance.setIssuer(uToAccountID);
}
else
{
Log(lsINFO) << "rippleOwed: No credit line between "
<< NewcoinAddress::createHumanAccountID(uFromAccountID)
<< " and "
<< NewcoinAddress::createHumanAccountID(uToAccountID)
<< " for "
<< STAmount::createHumanCurrency(uCurrencyID)
<< "." ;
assert(false);
}
return saBalance;
}
// Maximum amount of IOUs uToAccountID will hold from uFromAccountID.
// <-- $amount/uCurrencyID/uToAccountID.
STAmount LedgerEntrySet::rippleLimit(const uint160& uToAccountID, const uint160& uFromAccountID, const uint160& uCurrencyID)
{
STAmount saLimit;
SLE::pointer sleRippleState = entryCache(ltRIPPLE_STATE, Ledger::getRippleStateIndex(uToAccountID, uFromAccountID, uCurrencyID));
assert(sleRippleState);
if (sleRippleState)
{
saLimit = sleRippleState->getIValueFieldAmount(uToAccountID < uFromAccountID ? sfLowLimit : sfHighLimit);
saLimit.setIssuer(uToAccountID);
}
return saLimit;
}
uint32 LedgerEntrySet::rippleTransferRate(const uint160& uIssuerID)
{
SLE::pointer sleAccount = entryCache(ltACCOUNT_ROOT, Ledger::getAccountRootIndex(uIssuerID));
uint32 uQuality = sleAccount && sleAccount->getIFieldPresent(sfTransferRate)
? sleAccount->getIFieldU32(sfTransferRate)
: QUALITY_ONE;
Log(lsINFO) << boost::str(boost::format("rippleTransferRate: uIssuerID=%s account_exists=%d transfer_rate=%f")
% NewcoinAddress::createHumanAccountID(uIssuerID)
% !!sleAccount
% (uQuality/1000000000.0));
assert(sleAccount);
return uQuality;
}
// XXX Might not need this, might store in nodes on calc reverse.
uint32 LedgerEntrySet::rippleQualityIn(const uint160& uToAccountID, const uint160& uFromAccountID, const uint160& uCurrencyID, const SOE_Field sfLow, const SOE_Field sfHigh)
{
uint32 uQuality = QUALITY_ONE;
SLE::pointer sleRippleState;
if (uToAccountID == uFromAccountID)
{
nothing();
}
else
{
sleRippleState = entryCache(ltRIPPLE_STATE, Ledger::getRippleStateIndex(uToAccountID, uFromAccountID, uCurrencyID));
if (sleRippleState)
{
SOE_Field sfField = uToAccountID < uFromAccountID ? sfLow: sfHigh;
uQuality = sleRippleState->getIFieldPresent(sfField)
? sleRippleState->getIFieldU32(sfField)
: QUALITY_ONE;
if (!uQuality)
uQuality = 1; // Avoid divide by zero.
}
}
Log(lsINFO) << boost::str(boost::format("rippleQuality: %s uToAccountID=%s uFromAccountID=%s uCurrencyID=%s bLine=%d uQuality=%f")
% (sfLow == sfLowQualityIn ? "in" : "out")
% NewcoinAddress::createHumanAccountID(uToAccountID)
% NewcoinAddress::createHumanAccountID(uFromAccountID)
% STAmount::createHumanCurrency(uCurrencyID)
% !!sleRippleState
% (uQuality/1000000000.0));
assert(uToAccountID == uFromAccountID || !!sleRippleState);
return uQuality;
}
// Return how much of uIssuerID's uCurrencyID IOUs that uAccountID holds. May be negative.
// <-- IOU's uAccountID has of uIssuerID
STAmount LedgerEntrySet::rippleHolds(const uint160& uAccountID, const uint160& uCurrencyID, const uint160& uIssuerID)
{
STAmount saBalance;
SLE::pointer sleRippleState = entryCache(ltRIPPLE_STATE, Ledger::getRippleStateIndex(uAccountID, uIssuerID, uCurrencyID));
if (sleRippleState)
{
saBalance = sleRippleState->getIValueFieldAmount(sfBalance);
if (uAccountID > uIssuerID)
saBalance.negate(); // Put balance in uAccountID terms.
}
return saBalance;
}
// <-- saAmount: amount of uCurrencyID held by uAccountID. May be negative.
STAmount LedgerEntrySet::accountHolds(const uint160& uAccountID, const uint160& uCurrencyID, const uint160& uIssuerID)
{
STAmount saAmount;
if (!uCurrencyID)
{
SLE::pointer sleAccount = entryCache(ltACCOUNT_ROOT, Ledger::getAccountRootIndex(uAccountID));
saAmount = sleAccount->getIValueFieldAmount(sfBalance);
Log(lsINFO) << "accountHolds: stamps: " << saAmount.getText();
}
else
{
saAmount = rippleHolds(uAccountID, uCurrencyID, uIssuerID);
Log(lsINFO) << "accountHolds: "
<< saAmount.getFullText()
<< " : "
<< STAmount::createHumanCurrency(uCurrencyID)
<< "/"
<< NewcoinAddress::createHumanAccountID(uIssuerID);
}
return saAmount;
}
// Returns the funds available for uAccountID for a currency/issuer.
// Use when you need a default for rippling uAccountID's currency.
// --> saDefault/currency/issuer
// <-- saFunds: Funds available. May be negative.
// If the issuer is the same as uAccountID, funds are unlimited, use result is saDefault.
STAmount LedgerEntrySet::accountFunds(const uint160& uAccountID, const STAmount& saDefault)
{
STAmount saFunds;
Log(lsINFO) << "accountFunds: uAccountID="
<< NewcoinAddress::createHumanAccountID(uAccountID);
Log(lsINFO) << "accountFunds: saDefault.isNative()=" << saDefault.isNative();
Log(lsINFO) << "accountFunds: saDefault.getIssuer()="
<< NewcoinAddress::createHumanAccountID(saDefault.getIssuer());
if (!saDefault.isNative() && saDefault.getIssuer() == uAccountID)
{
saFunds = saDefault;
Log(lsINFO) << "accountFunds: offer funds: ripple self-funded: " << saFunds.getText();
}
else
{
saFunds = accountHolds(uAccountID, saDefault.getCurrency(), saDefault.getIssuer());
Log(lsINFO) << "accountFunds: offer funds: uAccountID ="
<< NewcoinAddress::createHumanAccountID(uAccountID)
<< " : "
<< saFunds.getText()
<< "/"
<< saDefault.getHumanCurrency()
<< "/"
<< NewcoinAddress::createHumanAccountID(saDefault.getIssuer());
}
return saFunds;
}
// Calculate transit fee.
STAmount LedgerEntrySet::rippleTransferFee(const uint160& uSenderID, const uint160& uReceiverID, const uint160& uIssuerID, const STAmount& saAmount)
{
STAmount saTransitFee;
if (uSenderID != uIssuerID && uReceiverID != uIssuerID)
{
uint32 uTransitRate = rippleTransferRate(uIssuerID);
if (QUALITY_ONE != uTransitRate)
{
STAmount saTransitRate(CURRENCY_ONE, uTransitRate, -9);
saTransitFee = STAmount::multiply(saAmount, saTransitRate, saAmount.getCurrency(), saAmount.getIssuer());
}
}
return saTransitFee;
}
// Direct send w/o fees: redeeming IOUs and/or sending own IOUs.
void LedgerEntrySet::rippleCredit(const uint160& uSenderID, const uint160& uReceiverID, const STAmount& saAmount, bool bCheckIssuer)
{
uint160 uIssuerID = saAmount.getIssuer();
assert(!bCheckIssuer || uSenderID == uIssuerID || uReceiverID == uIssuerID);
bool bFlipped = uSenderID > uReceiverID;
uint256 uIndex = Ledger::getRippleStateIndex(uSenderID, uReceiverID, saAmount.getCurrency());
SLE::pointer sleRippleState = entryCache(ltRIPPLE_STATE, uIndex);
if (!sleRippleState)
{
Log(lsINFO) << "rippleCredit: Creating ripple line: " << uIndex.ToString();
STAmount saBalance = saAmount;
sleRippleState = entryCreate(ltRIPPLE_STATE, uIndex);
if (!bFlipped)
saBalance.negate();
sleRippleState->setIFieldAmount(sfBalance, saBalance);
sleRippleState->setIFieldAccount(bFlipped ? sfHighID : sfLowID, uSenderID);
sleRippleState->setIFieldAccount(bFlipped ? sfLowID : sfHighID, uReceiverID);
}
else
{
STAmount saBalance = sleRippleState->getIValueFieldAmount(sfBalance);
if (!bFlipped)
saBalance.negate(); // Put balance in low terms.
saBalance += saAmount;
if (!bFlipped)
saBalance.negate();
sleRippleState->setIFieldAmount(sfBalance, saBalance);
entryModify(sleRippleState);
}
}
// Send regardless of limits.
// --> saAmount: Amount/currency/issuer for receiver to get.
// <-- saActual: Amount actually sent. Sender pay's fees.
STAmount LedgerEntrySet::rippleSend(const uint160& uSenderID, const uint160& uReceiverID, const STAmount& saAmount)
{
STAmount saActual;
const uint160 uIssuerID = saAmount.getIssuer();
assert(!!uSenderID && !!uReceiverID);
if (uSenderID == uIssuerID || uReceiverID == uIssuerID)
{
// Direct send: redeeming IOUs and/or sending own IOUs.
rippleCredit(uSenderID, uReceiverID, saAmount);
saActual = saAmount;
}
else
{
// Sending 3rd party IOUs: transit.
STAmount saTransitFee = rippleTransferFee(uSenderID, uReceiverID, uIssuerID, saAmount);
saActual = !saTransitFee ? saAmount : saAmount+saTransitFee;
saActual.setIssuer(uIssuerID); // XXX Make sure this done in + above.
rippleCredit(uIssuerID, uReceiverID, saAmount);
rippleCredit(uSenderID, uIssuerID, saActual);
}
return saActual;
}
void LedgerEntrySet::accountSend(const uint160& uSenderID, const uint160& uReceiverID, const STAmount& saAmount)
{
assert(!saAmount.isNegative());
if (!saAmount)
{
nothing();
}
else if (saAmount.isNative())
{
SLE::pointer sleSender = !!uSenderID
? entryCache(ltACCOUNT_ROOT, Ledger::getAccountRootIndex(uSenderID))
: SLE::pointer();
SLE::pointer sleReceiver = !!uReceiverID
? entryCache(ltACCOUNT_ROOT, Ledger::getAccountRootIndex(uReceiverID))
: SLE::pointer();
Log(lsINFO) << boost::str(boost::format("accountSend> %s (%s) -> %s (%s) : %s")
% NewcoinAddress::createHumanAccountID(uSenderID)
% (sleSender ? (sleSender->getIValueFieldAmount(sfBalance)).getFullText() : "-")
% NewcoinAddress::createHumanAccountID(uReceiverID)
% (sleReceiver ? (sleReceiver->getIValueFieldAmount(sfBalance)).getFullText() : "-")
% saAmount.getFullText());
if (sleSender)
{
sleSender->setIFieldAmount(sfBalance, sleSender->getIValueFieldAmount(sfBalance) - saAmount);
entryModify(sleSender);
}
if (sleReceiver)
{
sleReceiver->setIFieldAmount(sfBalance, sleReceiver->getIValueFieldAmount(sfBalance) + saAmount);
entryModify(sleReceiver);
}
Log(lsINFO) << boost::str(boost::format("accountSend< %s (%s) -> %s (%s) : %s")
% NewcoinAddress::createHumanAccountID(uSenderID)
% (sleSender ? (sleSender->getIValueFieldAmount(sfBalance)).getFullText() : "-")
% NewcoinAddress::createHumanAccountID(uReceiverID)
% (sleReceiver ? (sleReceiver->getIValueFieldAmount(sfBalance)).getFullText() : "-")
% saAmount.getFullText());
}
else
{
rippleSend(uSenderID, uReceiverID, saAmount);
}
}
// vim:ts=4

View File

@@ -6,6 +6,7 @@
#include "SerializedLedger.h"
#include "TransactionMeta.h"
#include "Ledger.h"
#include "TransactionErr.h"
enum LedgerEntryAction
{
@@ -16,7 +17,6 @@ enum LedgerEntryAction
taaCREATE, // Newly created.
};
class LedgerEntrySetEntry
{
public:
@@ -67,6 +67,9 @@ public:
void init(Ledger::ref ledger, const uint256& transactionID, uint32 ledgerID);
void clear();
Ledger::pointer& getLedger() { return mLedger; }
const Ledger::pointer& getLedgerRef() const { return mLedger; }
// basic entry functions
SLE::pointer getEntry(const uint256& index, LedgerEntryAction&);
LedgerEntryAction hasEntry(const uint256& index) const;
@@ -79,8 +82,45 @@ public:
SLE::pointer entryCreate(LedgerEntryType letType, const uint256& uIndex);
SLE::pointer entryCache(LedgerEntryType letType, const uint256& uIndex);
// Directory functions.
TER dirAdd(
uint64& uNodeDir, // Node of entry.
const uint256& uRootIndex,
const uint256& uLedgerIndex);
TER dirDelete(
const bool bKeepRoot,
const uint64& uNodeDir, // Node item is mentioned in.
const uint256& uRootIndex,
const uint256& uLedgerIndex, // Item being deleted
const bool bStable);
bool dirFirst(const uint256& uRootIndex, SLE::pointer& sleNode, unsigned int& uDirEntry, uint256& uEntryIndex);
bool dirNext(const uint256& uRootIndex, SLE::pointer& sleNode, unsigned int& uDirEntry, uint256& uEntryIndex);
// Offer functions.
TER offerDelete(const uint256& uOfferIndex);
TER offerDelete(const SLE::pointer& sleOffer, const uint256& uOfferIndex, const uint160& uOwnerID);
// Balance functions.
uint32 rippleTransferRate(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, const SOE_Field sfLow=sfLowQualityIn, const SOE_Field 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);
void rippleCredit(const uint160& uSenderID, const uint160& uReceiverID, const STAmount& saAmount, bool bCheckIssuer=true);
STAmount rippleSend(const uint160& uSenderID, const uint160& uReceiverID, const STAmount& saAmount);
STAmount accountHolds(const uint160& uAccountID, const uint160& uCurrencyID, const uint160& uIssuerID);
void accountSend(const uint160& uSenderID, const uint160& uReceiverID, const STAmount& saAmount);
STAmount accountFunds(const uint160& uAccountID, const STAmount& saDefault);
Json::Value getJson(int) const;
void calcRawMeta(Serializer&, Ledger::ref originalLedger);
void calcRawMeta(Serializer&);
// iterator functions
bool isEmpty() const { return mEntries.empty(); }

View File

@@ -80,8 +80,7 @@ Ledger::pointer LedgerMaster::closeLedger()
return closingLedger;
}
TER LedgerMaster::doTransaction(const SerializedTransaction& txn, uint32 targetLedger,
TransactionEngineParams params)
TER LedgerMaster::doTransaction(const SerializedTransaction& txn, TransactionEngineParams params)
{
TER result = mEngine.applyTransaction(txn, params);
theApp->getOPs().pubTransaction(mEngine.getLedger(), txn, result);

View File

@@ -45,8 +45,7 @@ public:
void runStandAlone() { mFinalizedLedger = mCurrentLedger; }
TER doTransaction(const SerializedTransaction& txn, uint32 targetLedger,
TransactionEngineParams params);
TER doTransaction(const SerializedTransaction& txn, TransactionEngineParams params);
void pushLedger(Ledger::ref newLedger);
void pushLedger(Ledger::ref newLCL, Ledger::ref newOL);

View File

@@ -54,17 +54,20 @@ bool LedgerProposal::checkSign(const std::string& signature, const uint256& sign
return mPublicKey.verifyNodePublic(signingHash, signature);
}
void LedgerProposal::changePosition(const uint256& newPosition, uint32 closeTime)
bool LedgerProposal::changePosition(const uint256& 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()
{
mCurrentHash = uint256();
mTime = boost::posix_time::second_clock::universal_time();
mProposeSeq = seqLeave;
}

View File

@@ -59,11 +59,12 @@ public:
void setSignature(const std::string& signature) { mSignature = signature; }
bool hasSignature() { return !mSignature.empty(); }
bool isPrevLedger(const uint256& 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; }
void changePosition(const uint256& newPosition, uint32 newCloseTime);
bool changePosition(const uint256& newPosition, uint32 newCloseTime);
void bowOut();
Json::Value getJson() const;
};

View File

@@ -28,6 +28,10 @@
// 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

View File

@@ -6,6 +6,9 @@
#include <boost/thread/recursive_mutex.hpp>
#include <boost/filesystem.hpp>
// Ensure that we don't get value.h without writer.h
#include "../json/json.h"
enum LogSeverity
{
lsTRACE = 0,

View File

@@ -93,7 +93,7 @@ Transaction::pointer NetworkOPs::submitTransaction(const Transaction::pointer& t
return tpTransNew;
}
Transaction::pointer NetworkOPs::processTransaction(Transaction::pointer trans, uint32 tgtLedger, Peer* source)
Transaction::pointer NetworkOPs::processTransaction(Transaction::pointer trans, Peer* source)
{
Transaction::pointer dbtx = theApp->getMasterTransaction().fetch(trans->getID(), true);
if (dbtx) return dbtx;
@@ -105,7 +105,7 @@ Transaction::pointer NetworkOPs::processTransaction(Transaction::pointer trans,
return trans;
}
TER r = mLedgerMaster->doTransaction(*trans->getSTransaction(), tgtLedger, tapOPEN_LEDGER);
TER r = mLedgerMaster->doTransaction(*trans->getSTransaction(), tapOPEN_LEDGER);
if (r == tefFAILURE) throw Fault(IO_ERROR);
if (r == terPRE_SEQ)
@@ -140,7 +140,6 @@ Transaction::pointer NetworkOPs::processTransaction(Transaction::pointer trans,
tx.set_rawtransaction(&s.getData().front(), s.getLength());
tx.set_status(newcoin::tsCURRENT);
tx.set_receivetimestamp(getNetworkTimeNC());
tx.set_ledgerindexpossible(trans->getLedger());
PackedMessage::pointer packet = boost::make_shared<PackedMessage>(tx, newcoin::mtTRANSACTION);
theApp->getConnectionPool().relayMessage(source, packet);
@@ -157,7 +156,6 @@ Transaction::pointer NetworkOPs::processTransaction(Transaction::pointer trans,
tx.set_rawtransaction(&s.getData().front(), s.getLength());
tx.set_status(newcoin::tsCURRENT);
tx.set_receivetimestamp(getNetworkTimeNC());
tx.set_ledgerindexpossible(tgtLedger);
PackedMessage::pointer packet = boost::make_shared<PackedMessage>(tx, newcoin::mtTRANSACTION);
theApp->getConnectionPool().relayMessage(source, packet);
}
@@ -213,7 +211,11 @@ SLE::pointer NetworkOPs::getGenerator(const uint256& uLedger, const uint160& uGe
{
LedgerStateParms qry = lepNONE;
return mLedgerMaster->getLedgerByHash(uLedger)->getGenerator(qry, uGeneratorID);
Ledger::pointer ledger = mLedgerMaster->getLedgerByHash(uLedger);
if (!ledger)
return SLE::pointer();
else
return ledger->getGenerator(qry, uGeneratorID);
}
//
@@ -714,7 +716,7 @@ bool NetworkOPs::hasTXSet(const boost::shared_ptr<Peer>& peer, const uint256& se
return mConsensus->peerHasSet(peer, set, status);
}
void NetworkOPs::mapComplete(const uint256& hash, const SHAMap::pointer& map)
void NetworkOPs::mapComplete(const uint256& hash, SHAMap::ref map)
{
if (mConsensus)
mConsensus->mapComplete(hash, map, true);

View File

@@ -116,8 +116,7 @@ public:
//
Transaction::pointer submitTransaction(const Transaction::pointer& tpTrans);
Transaction::pointer processTransaction(Transaction::pointer transaction, uint32 targetLedger = 0,
Peer* source = NULL);
Transaction::pointer processTransaction(Transaction::pointer transaction, Peer* source = NULL);
Transaction::pointer findTransactionByID(const uint256& transactionID);
int findTransactionsBySource(const uint256& uLedger, std::list<Transaction::pointer>&, const NewcoinAddress& sourceAccount,
uint32 minSeq, uint32 maxSeq);
@@ -172,7 +171,7 @@ public:
bool recvValidation(const SerializedValidation::pointer& val);
SHAMap::pointer getTXMap(const uint256& hash);
bool hasTXSet(const boost::shared_ptr<Peer>& peer, const uint256& set, newcoin::TxSetStatus status);
void mapComplete(const uint256& hash, const SHAMap::pointer& map);
void mapComplete(const uint256& hash, SHAMap::ref map);
// network state machine
void checkState(const boost::system::error_code& result);

View File

@@ -12,6 +12,8 @@ public:
virtual bool work(Interpreter* interpreter)=0;
virtual int getFee();
virtual ~Operation() { ; }
};
// this is just an Int in the code

View File

@@ -702,13 +702,7 @@ void Peer::recvTransaction(newcoin::TMTransaction& packet)
}
#endif
uint32 targetLedger = 0;
if (packet.has_ledgerindexfinal())
targetLedger = packet.ledgerindexfinal();
else if (packet.has_ledgerindexpossible())
targetLedger = packet.ledgerindexpossible();
tx = theApp->getOPs().processTransaction(tx, targetLedger, this);
tx = theApp->getOPs().processTransaction(tx, this);
if(tx->getStatus() != INCLUDED)
{ // transaction wasn't accepted into ledger

2400
src/RippleCalc.cpp Normal file

File diff suppressed because it is too large Load Diff

190
src/RippleCalc.h Normal file
View File

@@ -0,0 +1,190 @@
#ifndef __RIPPLE_CALC__
#define __RIPPLE_CALC__
#include <boost/unordered_set.hpp>
#include "LedgerEntrySet.h"
class PaymentNode {
protected:
friend class RippleCalc;
friend class PathState;
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; // XXX Should rate be sticky for forward too?
// Directory
uint256 uDirectTip; // Current directory.
uint256 uDirectEnd; // Next order book.
bool bDirectAdvance; // Need to advance directory.
SLE::pointer sleDirectDir;
STAmount saOfrRate; // For correct ratio.
// Node
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;
};
// 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
{
protected:
const Ledger::pointer& mLedger;
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);
public:
typedef boost::shared_ptr<PathState> pointer;
TER terStatus;
std::vector<PaymentNode> 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;
uint64 uQuality; // 0 = none.
STAmount saInReq; // Max amount to spend by sender
STAmount saInAct; // Amount spent by sender (calc output)
STAmount saOutReq; // Amount to send (calc input)
STAmount saOutAct; // Amount actually sent (calc output).
PathState(
const int iIndex,
const LedgerEntrySet& lesSource,
const STPath& spSourcePath,
const uint160& uReceiverID,
const uint160& uSenderID,
const STAmount& saSend,
const STAmount& saSendMax
);
Json::Value getJson() const;
static PathState::pointer createPathState(
const int iIndex,
const LedgerEntrySet& lesSource,
const STPath& spSourcePath,
const uint160& uReceiverID,
const uint160& uSenderID,
const STAmount& saSend,
const STAmount& saSendMax
)
{
return boost::make_shared<PathState>(iIndex, lesSource, spSourcePath, uReceiverID, uSenderID, saSend, saSendMax);
}
static bool lessPriority(const PathState::pointer& lhs, const PathState::pointer& rhs);
};
class RippleCalc
{
protected:
LedgerEntrySet& lesActive;
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.
PathState::pointer pathCreate(const STPath& spPath);
void pathNext(const PathState::pointer& pspCur, const int iPaths, const LedgerEntrySet& lesCheckpoint, LedgerEntrySet& lesCurrent);
TER calcNode(const unsigned int uIndex, const PathState::pointer& pspCur, const bool bMultiQuality);
TER calcNodeRev(const unsigned int uIndex, const PathState::pointer& pspCur, const bool bMultiQuality);
TER calcNodeFwd(const unsigned int uIndex, const PathState::pointer& pspCur, const bool bMultiQuality);
TER calcNodeOfferRev(const unsigned int uIndex, const PathState::pointer& pspCur, const bool bMultiQuality);
TER calcNodeOfferFwd(const unsigned int uIndex, const PathState::pointer& pspCur, const bool bMultiQuality);
TER calcNodeAccountRev(const unsigned int uIndex, const PathState::pointer& pspCur, const bool bMultiQuality);
TER calcNodeAccountFwd(const unsigned int uIndex, const PathState::pointer& pspCur, const bool bMultiQuality);
TER calcNodeAdvance(const unsigned int uIndex, const PathState::pointer& pspCur, const bool bMultiQuality, const bool bReverse);
TER calcNodeDeliverRev(
const unsigned int uIndex,
const PathState::pointer& pspCur,
const bool bMultiQuality,
const uint160& uOutAccountID,
const STAmount& saOutReq,
STAmount& saOutAct);
TER calcNodeDeliverFwd(
const unsigned int uIndex,
const PathState::pointer& pspCur,
const bool bMultiQuality,
const uint160& uInAccountID,
const STAmount& saInFunds,
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) : lesActive(lesNodes) { ; }
static TER rippleCalc(
LedgerEntrySet& lesActive,
STAmount& saMaxAmountAct,
STAmount& saDstAmountAct,
const STAmount& saDstAmountReq,
const STAmount& saMaxAmountReq,
const uint160& uDstAccountID,
const uint160& uSrcAccountID,
const STPathSet& spsPaths,
const bool bPartialPayment,
const bool bLimitQuality,
const bool bNoRippleDirect
);
};
#endif
// vim:ts=4

View File

@@ -39,14 +39,14 @@ std::size_t hash_value(const uint160& u)
}
SHAMap::SHAMap(uint32 seq) : mSeq(seq), mState(Modifying)
SHAMap::SHAMap(uint32 seq) : mSeq(seq), mState(smsModifying)
{
root = boost::make_shared<SHAMapTreeNode>(mSeq, SHAMapNode(0, uint256()));
root->makeInner();
mTNByID[*root] = root;
}
SHAMap::SHAMap(const uint256& hash) : mSeq(0), mState(Synching)
SHAMap::SHAMap(const uint256& hash) : mSeq(0), mState(smsSynching)
{ // FIXME: Need to acquire root node
root = boost::make_shared<SHAMapTreeNode>(mSeq, SHAMapNode(0, uint256()));
root->makeInner();
@@ -62,7 +62,7 @@ SHAMap::pointer SHAMap::snapShot(bool isMutable)
newMap.mTNByID = mTNByID;
newMap.root = root;
if (!isMutable)
newMap.mState = Immutable;
newMap.mState = smsImmutable;
return ret;
}
@@ -105,7 +105,7 @@ void SHAMap::dirtyUp(std::stack<SHAMapTreeNode::pointer>& stack, const uint256&
{ // walk the tree up from through the inner nodes to the root
// update linking hashes and add nodes to dirty list
assert((mState != Synching) && (mState != Immutable));
assert((mState != smsSynching) && (mState != smsImmutable));
while (!stack.empty())
{
@@ -238,7 +238,7 @@ SHAMapItem::SHAMapItem(const uint256& tag, const Serializer& data)
: mTag(tag), mData(data.peekData())
{ ; }
SHAMapItem::pointer SHAMap::firstBelow(SHAMapTreeNode* node)
SHAMapTreeNode* SHAMap::firstBelow(SHAMapTreeNode* node)
{
// Return the first item below this node
#ifdef ST_DEBUG
@@ -246,7 +246,7 @@ SHAMapItem::pointer SHAMap::firstBelow(SHAMapTreeNode* node)
#endif
do
{ // Walk down the tree
if (node->hasItem()) return node->peekItem();
if (node->hasItem()) return node;
bool foundNode = false;
for (int i = 0; i < 16; ++i)
@@ -261,11 +261,12 @@ SHAMapItem::pointer SHAMap::firstBelow(SHAMapTreeNode* node)
foundNode = true;
break;
}
if (!foundNode) return SHAMapItem::pointer();
if (!foundNode)
return NULL;
} while (true);
}
SHAMapItem::pointer SHAMap::lastBelow(SHAMapTreeNode* node)
SHAMapTreeNode* SHAMap::lastBelow(SHAMapTreeNode* node)
{
#ifdef DEBUG
std::cerr << "lastBelow(" << *node << ")" << std::endl;
@@ -273,7 +274,8 @@ SHAMapItem::pointer SHAMap::lastBelow(SHAMapTreeNode* node)
do
{ // Walk down the tree
if (node->hasItem()) return node->peekItem();
if (node->hasItem())
return node;
bool foundNode = false;
for (int i = 15; i >= 0; ++i)
@@ -283,7 +285,8 @@ SHAMapItem::pointer SHAMap::lastBelow(SHAMapTreeNode* node)
foundNode = true;
break;
}
if (!foundNode) return SHAMapItem::pointer();
if (!foundNode)
return NULL;
} while (true);
}
@@ -345,16 +348,39 @@ void SHAMap::eraseChildren(SHAMapTreeNode::pointer node)
SHAMapItem::pointer SHAMap::peekFirstItem()
{
boost::recursive_mutex::scoped_lock sl(mLock);
return firstBelow(root.get());
SHAMapTreeNode *node = firstBelow(root.get());
if (!node)
return SHAMapItem::pointer();
return node->peekItem();
}
SHAMapItem::pointer SHAMap::peekFirstItem(SHAMapTreeNode::TNType& type)
{
boost::recursive_mutex::scoped_lock sl(mLock);
SHAMapTreeNode *node = firstBelow(root.get());
if (!node)
return SHAMapItem::pointer();
type = node->getType();
return node->peekItem();
}
SHAMapItem::pointer SHAMap::peekLastItem()
{
boost::recursive_mutex::scoped_lock sl(mLock);
return lastBelow(root.get());
SHAMapTreeNode *node = lastBelow(root.get());
if (!node)
return SHAMapItem::pointer();
return node->peekItem();
}
SHAMapItem::pointer SHAMap::peekNextItem(const uint256& id)
{
SHAMapTreeNode::TNType type;
return peekNextItem(id, type);
}
SHAMapItem::pointer SHAMap::peekNextItem(const uint256& id, SHAMapTreeNode::TNType& type)
{ // Get a pointer to the next item in the tree after a given item - item must be in tree
boost::recursive_mutex::scoped_lock sl(mLock);
@@ -367,17 +393,24 @@ SHAMapItem::pointer SHAMap::peekNextItem(const uint256& id)
if (node->isLeaf())
{
if (node->peekItem()->getTag() > id)
return node->peekItem();
}
else for (int i = node->selectBranch(id) + 1; i < 16; ++i)
if (!node->isEmptyBranch(i))
{
node = getNode(node->getChildNodeID(i), node->getChildHash(i), false);
SHAMapItem::pointer item = firstBelow(node.get());
if (!item)
throw std::runtime_error("missing node");
return item;
type = node->getType();
return node->peekItem();
}
}
else
for (int i = node->selectBranch(id) + 1; i < 16; ++i)
if (!node->isEmptyBranch(i))
{
SHAMapTreeNode *firstNode = getNodePointer(node->getChildNodeID(i), node->getChildHash(i));
if (!firstNode)
throw std::runtime_error("missing node");
firstNode = firstBelow(firstNode);
if (!firstNode)
throw std::runtime_error("missing node");
type = firstNode->getType();
return firstNode->peekItem();
}
}
// must be last item
return SHAMapItem::pointer();
@@ -402,10 +435,10 @@ SHAMapItem::pointer SHAMap::peekPrevItem(const uint256& id)
if (!node->isEmptyBranch(i))
{
node = getNode(node->getChildNodeID(i), node->getChildHash(i), false);
SHAMapItem::pointer item = firstBelow(node.get());
SHAMapTreeNode* item = firstBelow(node.get());
if (!item)
throw std::runtime_error("missing node");
return item;
return item->peekItem();
}
}
// must be last item
@@ -421,6 +454,16 @@ SHAMapItem::pointer SHAMap::peekItem(const uint256& id)
return leaf->peekItem();
}
SHAMapItem::pointer SHAMap::peekItem(const uint256& id, SHAMapTreeNode::TNType& type)
{
boost::recursive_mutex::scoped_lock sl(mLock);
SHAMapTreeNode* leaf = walkToPointer(id);
if (!leaf)
return SHAMapItem::pointer();
type = leaf->getType();
return leaf->peekItem();
}
bool SHAMap::hasItem(const uint256& id)
{ // does the tree have an item with this ID
boost::recursive_mutex::scoped_lock sl(mLock);
@@ -432,7 +475,7 @@ bool SHAMap::hasItem(const uint256& id)
bool SHAMap::delItem(const uint256& id)
{ // delete the item with this ID
boost::recursive_mutex::scoped_lock sl(mLock);
assert(mState != Immutable);
assert(mState != smsImmutable);
std::stack<SHAMapTreeNode::pointer> stack = getStack(id, true, false);
if (stack.empty())
@@ -509,7 +552,7 @@ bool SHAMap::addGiveItem(const SHAMapItem::pointer& item, bool isTransaction, bo
(hasMeta ? SHAMapTreeNode::tnTRANSACTION_MD : SHAMapTreeNode::tnTRANSACTION_NM);
boost::recursive_mutex::scoped_lock sl(mLock);
assert(mState != Immutable);
assert(mState != smsImmutable);
std::stack<SHAMapTreeNode::pointer> stack = getStack(tag, true, false);
if (stack.empty())
@@ -601,7 +644,7 @@ bool SHAMap::updateGiveItem(const SHAMapItem::pointer& item, bool isTransaction,
uint256 tag = item->getTag();
boost::recursive_mutex::scoped_lock sl(mLock);
assert(mState != Immutable);
assert(mState != smsImmutable);
std::stack<SHAMapTreeNode::pointer> stack = getStack(tag, true, false);
if (stack.empty()) throw std::runtime_error("missing node");

View File

@@ -24,7 +24,8 @@ class SHAMap;
class SHAMapNode
{ // Identifies a node in a SHA256 hash
public:
typedef boost::shared_ptr<SHAMapNode> pointer;
typedef boost::shared_ptr<SHAMapNode> pointer;
typedef const boost::shared_ptr<SHAMapNode>& ref;
private:
static uint256 smMasks[65]; // AND with hash to get node id
@@ -216,11 +217,11 @@ public:
enum SHAMapState
{
Modifying = 0, // Objects can be added and removed (like an open ledger)
Immutable = 1, // Map cannot be changed (like a closed ledger)
Synching = 2, // Map's hash is locked in, valid nodes can be added (like a peer's closing ledger)
Floating = 3, // Map is free to change hash (like a synching open ledger)
Invalid = 4, // Map is known not to be valid (usually synching a corrupt ledger)
smsModifying = 0, // Objects can be added and removed (like an open ledger)
smsImmutable = 1, // Map cannot be changed (like a closed ledger)
smsSynching = 2, // Map's hash is locked in, valid nodes can be added (like a peer's closing ledger)
smsFloating = 3, // Map is free to change hash (like a synching open ledger)
smsInvalid = 4, // Map is known not to be valid (usually synching a corrupt ledger)
};
class SHAMapSyncFilter
@@ -228,9 +229,11 @@ class SHAMapSyncFilter
public:
SHAMapSyncFilter() { ; }
virtual ~SHAMapSyncFilter() { ; }
virtual void gotNode(const SHAMapNode& id, const uint256& nodeHash,
const std::vector<unsigned char>& nodeData, bool isLeaf)
{ ; }
virtual bool haveNode(const SHAMapNode& id, const uint256& nodeHash, std::vector<unsigned char>& nodeData)
{ return false; }
};
@@ -247,6 +250,7 @@ public:
{ ; }
virtual ~SHAMapMissingNode() throw()
{ ; }
const SHAMapNode& getNodeID() const { return mNodeID; }
const uint256& getNodeHash() const { return mNodeHash; }
};
@@ -255,6 +259,8 @@ class SHAMap
{
public:
typedef boost::shared_ptr<SHAMap> pointer;
typedef const boost::shared_ptr<SHAMap>& ref;
typedef std::map<uint256, std::pair<SHAMapItem::pointer, SHAMapItem::pointer> > SHAMapDiff;
private:
@@ -280,9 +286,9 @@ protected:
SHAMapTreeNode::pointer getNode(const SHAMapNode& id);
SHAMapTreeNode::pointer getNode(const SHAMapNode& id, const uint256& hash, bool modify);
SHAMapTreeNode* getNodePointer(const SHAMapNode& id, const uint256& hash);
SHAMapTreeNode* firstBelow(SHAMapTreeNode*);
SHAMapTreeNode* lastBelow(SHAMapTreeNode*);
SHAMapItem::pointer firstBelow(SHAMapTreeNode*);
SHAMapItem::pointer lastBelow(SHAMapTreeNode*);
SHAMapItem::pointer onlyBelow(SHAMapTreeNode*);
void eraseChildren(SHAMapTreeNode::pointer);
@@ -295,6 +301,8 @@ public:
SHAMap(uint32 seq = 0);
SHAMap(const uint256& hash);
~SHAMap() { mState = smsInvalid; }
// Returns a new map that's a snapshot of this one. Force CoW
SHAMap::pointer snapShot(bool isMutable);
@@ -318,11 +326,14 @@ public:
// save a copy if you only need a temporary
SHAMapItem::pointer peekItem(const uint256& id);
SHAMapItem::pointer peekItem(const uint256& id, SHAMapTreeNode::TNType& type);
// traverse functions
SHAMapItem::pointer peekFirstItem();
SHAMapItem::pointer peekFirstItem(SHAMapTreeNode::TNType& type);
SHAMapItem::pointer peekLastItem();
SHAMapItem::pointer peekNextItem(const uint256&);
SHAMapItem::pointer peekNextItem(const uint256&, SHAMapTreeNode::TNType& type);
SHAMapItem::pointer peekPrevItem(const uint256&);
// comparison/sync functions
@@ -337,17 +348,17 @@ public:
SHAMapSyncFilter* filter);
// status functions
void setImmutable(void) { assert(mState != Invalid); mState = Immutable; }
void clearImmutable(void) { mState = Modifying; }
bool isSynching(void) const { return (mState == Floating) || (mState == Synching); }
void setSynching(void) { mState = Synching; }
void setFloating(void) { mState = Floating; }
void clearSynching(void) { mState = Modifying; }
bool isValid(void) { return mState != Invalid; }
void setImmutable() { assert(mState != smsInvalid); mState = smsImmutable; }
void clearImmutable() { mState = smsModifying; }
bool isSynching() const { return (mState == smsFloating) || (mState == smsSynching); }
void setSynching() { mState = smsSynching; }
void setFloating() { mState = smsFloating; }
void clearSynching() { mState = smsModifying; }
bool isValid() { return mState != smsInvalid; }
// caution: otherMap must be accessed only by this function
// return value: true=successfully completed, false=too different
bool compare(const SHAMap::pointer& otherMap, SHAMapDiff& differences, int maxCount);
bool compare(SHAMap::ref otherMap, SHAMapDiff& differences, int maxCount);
void armDirty();
int flushDirty(int maxNodes, HashedObjectType t, uint32 seq);

View File

@@ -91,17 +91,22 @@ bool SHAMap::walkBranch(SHAMapTreeNode* node, SHAMapItem::pointer otherMapItem,
return true;
}
bool SHAMap::compare(const SHAMap::pointer& otherMap, SHAMapDiff& differences, int maxCount)
bool SHAMap::compare(SHAMap::ref otherMap, SHAMapDiff& differences, int maxCount)
{ // compare two hash trees, add up to maxCount differences to the difference table
// return value: true=complete table of differences given, false=too many differences
// throws on corrupt tables or missing nodes
// CAUTION: otherMap is not locked and must be immutable
assert(isValid() && otherMap && otherMap->isValid());
std::stack<SHAMapDiffNode> nodeStack; // track nodes we've pushed
ScopedLock sl(Lock());
if (getHash() == otherMap->getHash()) return true;
nodeStack.push(SHAMapDiffNode(SHAMapNode(), getHash(), otherMap->getHash()));
boost::recursive_mutex::scoped_lock sl(mLock);
if (getHash() == otherMap->getHash())
return true;
nodeStack.push(SHAMapDiffNode(SHAMapNode(), getHash(), otherMap->getHash()));
while (!nodeStack.empty())
{
SHAMapDiffNode dNode(nodeStack.top());
@@ -109,6 +114,11 @@ bool SHAMap::compare(const SHAMap::pointer& otherMap, SHAMapDiff& differences, i
SHAMapTreeNode* ourNode = getNodePointer(dNode.mNodeID, dNode.mOurHash);
SHAMapTreeNode* otherNode = otherMap->getNodePointer(dNode.mNodeID, dNode.mOtherHash);
if (!ourNode || !otherNode)
{
assert(false);
throw SHAMapMissingNode(dNode.mNodeID, uint256());
}
if (ourNode->isLeaf() && otherNode->isLeaf())
{ // two leaves
@@ -118,17 +128,20 @@ bool SHAMap::compare(const SHAMap::pointer& otherMap, SHAMapDiff& differences, i
{
differences.insert(std::make_pair(ourNode->getTag(),
std::make_pair(ourNode->getItem(), otherNode->getItem())));
if (--maxCount <= 0) return false;
if (--maxCount <= 0)
return false;
}
}
else
{
differences.insert(std::make_pair(ourNode->getTag(),
std::make_pair(ourNode->getItem(), SHAMapItem::pointer())));
if (--maxCount <= 0) return false;
if (--maxCount <= 0)
return false;
differences.insert(std::make_pair(otherNode->getTag(),
std::make_pair(SHAMapItem::pointer(), otherNode->getItem())));
if (--maxCount <= 0) return false;
if (--maxCount <= 0)
return false;
}
}
else if (ourNode->isInner() && otherNode->isLeaf())
@@ -164,7 +177,8 @@ bool SHAMap::compare(const SHAMap::pointer& otherMap, SHAMapDiff& differences, i
ourNode->getChildHash(i), otherNode->getChildHash(i)));
}
}
else assert(false);
else
assert(false);
}
return true;

View File

@@ -296,8 +296,9 @@ SHAMapTreeNode::SHAMapTreeNode(const SHAMapNode& id, const std::vector<unsigned
}
else if (prefix == sHP_TransactionNode)
{
uint256 txID; // WRITEME: Need code to extract transaction ID from TX+MD
assert(false);
uint256 txID;
s.get256(txID, s.getLength() - 32);
s.chop(32);
mItem = boost::make_shared<SHAMapItem>(txID, s.peekData());
mType = tnTRANSACTION_MD;
}

View File

@@ -9,6 +9,8 @@ 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); }

View File

@@ -102,7 +102,7 @@ bool SerializedLedgerEntry::thread(const uint256& txID, uint32 ledgerSeq, uint25
if (oldPrevTxID == txID)
return false;
prevTxID = oldPrevTxID;
prevLedgerID = getIFieldU32(sfLastTxnID);
prevLedgerID = getIFieldU32(sfLastTxnSeq);
assert(prevTxID != txID);
setIFieldH256(sfLastTxnID, txID);
setIFieldU32(sfLastTxnSeq, ledgerSeq);

View File

@@ -6,6 +6,8 @@
#include "../json/writer.h"
#include "Log.h"
std::auto_ptr<SerializedType> STObject::makeDefaultObject(SerializedTypeID id, const char *name)
{
switch(id)
@@ -713,8 +715,7 @@ void STObject::unitTest()
copy.setValueFieldU32(sfTest3, 1);
if (object1.getSerializer() == copy.getSerializer()) throw std::runtime_error("STObject error");
#ifdef DEBUG
Json::StyledStreamWriter ssw;
ssw.write(std::cerr, copy.getJson(0));
Log(lsDEBUG) << copy.getJson(0);
#endif
for (int i = 0; i < 1000; i++)

View File

@@ -106,7 +106,6 @@ enum SOE_Field
sfTakerGets,
sfTakerPays,
sfTarget,
sfTargetLedger,
sfTransferRate,
sfVersion,
sfWalletLocator,

View File

@@ -28,7 +28,7 @@ protected:
TransactionType mType;
STVariableLength mSignature;
STObject mMiddleTxn, mInnerTxn;
TransactionFormat* mFormat;
const TransactionFormat* mFormat;
SerializedTransaction* duplicate() const { return new SerializedTransaction(*this); }

View File

@@ -9,6 +9,9 @@
#include "NewcoinAddress.h"
#include "utils.h"
STAmount saZero(CURRENCY_ONE, ACCOUNT_ONE, 0);
STAmount saOne(CURRENCY_ONE, ACCOUNT_ONE, 1);
std::string SerializedType::getFullText() const
{
std::string ret;

View File

@@ -395,6 +395,9 @@ public:
Json::Value getJson(int) const;
};
extern STAmount saZero;
extern STAmount saOne;
class STHash128 : public SerializedType
{
protected:

View File

@@ -281,7 +281,6 @@ public:
STAmount getAmount() const { return mTransaction->getITFieldU64(sfAmount); }
STAmount getFee() const { return mTransaction->getTransactionFee(); }
uint32 getFromAccountSeq() const { return mTransaction->getSequence(); }
uint32 getSourceLedger() const { return mTransaction->getITFieldU32(sfTargetLedger); }
uint32 getIdent() const { return mTransaction->getITFieldU32(sfSourceTag); }
std::vector<unsigned char> getSignature() const { return mTransaction->getSignature(); }
uint32 getLedger() const { return mInLedger; }

1171
src/TransactionAction.cpp Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,6 @@
#ifndef __TRANSACTIONENGINE__
#define __TRANSACTIONENGINE__
#include <boost/tuple/tuple.hpp>
#include <boost/tuple/tuple_comparison.hpp>
#include <boost/unordered_set.hpp>
#include <boost/unordered_map.hpp>
@@ -10,120 +8,11 @@
#include "SerializedTransaction.h"
#include "SerializedLedger.h"
#include "LedgerEntrySet.h"
#include "TransactionErr.h"
// A TransactionEngine applies serialized transactions to a ledger
// It can also, verify signatures, verify fees, and give rejection reasons
enum TER // aka TransactionEngineResult
{
// Note: Range is stable. Exact numbers are currently unstable. Use tokens.
// -399 .. -300: L Local error (transaction fee inadequate, exceeds local limit)
// Only valid during non-consensus processing.
// Implications:
// - Not forwarded
// - No fee check
telLOCAL_ERROR = -399,
telBAD_PATH_COUNT,
telINSUF_FEE_P,
// -299 .. -200: M Malformed (bad signature)
// Causes:
// - Transaction corrupt.
// Implications:
// - Not applied
// - Not forwarded
// - Reject
// - Can not succeed in any imagined ledger.
temMALFORMED = -299,
temBAD_AMOUNT,
temBAD_AUTH_MASTER,
temBAD_EXPIRATION,
temBAD_ISSUER,
temBAD_OFFER,
temBAD_PATH,
temBAD_PATH_LOOP,
temBAD_PUBLISH,
temBAD_SET_ID,
temCREATEXNS,
temDST_IS_SRC,
temDST_NEEDED,
temINSUF_FEE_P,
temINVALID,
temREDUNDANT,
temRIPPLE_EMPTY,
temUNCERTAIN,
temUNKNOWN,
// -199 .. -100: F Failure (sequence number previously used)
// Causes:
// - Transaction cannot succeed because of ledger state.
// - Unexpected ledger state.
// - C++ exception.
// Implications:
// - Not applied
// - Not forwarded
// - Could succeed in an imagined ledger.
tefFAILURE = -199,
tefALREADY,
tefBAD_ADD_AUTH,
tefBAD_AUTH,
tefBAD_CLAIM_ID,
tefBAD_GEN_AUTH,
tefBAD_LEDGER,
tefCLAIMED,
tefCREATED,
tefEXCEPTION,
tefGEN_IN_USE,
tefPAST_SEQ,
// -99 .. -1: R Retry (sequence too high, no funds for txn fee, originating account non-existent)
// Causes:
// - Priror application of another, possibly non-existant, transaction could allow this transaction to succeed.
// Implications:
// - Not applied
// - Not forwarded
// - Might succeed later
// - Hold
terRETRY = -99,
terDIR_FULL,
terFUNDS_SPENT,
terINSUF_FEE_B,
terNO_ACCOUNT,
terNO_DST,
terNO_LINE,
terNO_LINE_NO_ZERO,
terOFFER_NOT_FOUND, // XXX If we check sequence first this could be hard failure.
terPRE_SEQ,
terSET_MISSING_DST,
terUNFUNDED,
// 0: S Success (success)
// Causes:
// - Success.
// Implications:
// - Applied
// - Forwarded
tesSUCCESS = 0,
// 100 .. P Partial success (SR) (ripple transaction with no good paths, pay to non-existent account)
// Causes:
// - Success, but does not achieve optimal result.
// Implications:
// - Applied
// - Forwarded
// Only allowed as a return code of appliedTransaction when !tapRetry. Otherwise, treated as terRETRY.
tepPARTIAL = 100,
tepPATH_DRY,
tepPATH_PARTIAL,
};
#define isTemMalformed(x) ((x) >= temMALFORMED && (x) < tefFAILURE)
#define isTefFailure(x) ((x) >= tefFAILURE && (x) < terRETRY)
#define isTepPartial(x) ((x) >= tepPATH_PARTIAL)
bool transResultInfo(TER terCode, std::string& strToken, std::string& strHuman);
enum TransactionEngineParams
{
tapNONE = 0x00,
@@ -138,127 +27,6 @@ enum TransactionEngineParams
// Transaction can be retried, soft failures allowed
};
class PaymentNode {
protected:
friend class TransactionEngine;
friend class PathState;
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; // XXX Should rate be sticky for forward too?
// Directory
uint256 uDirectTip; // Current directory.
uint256 uDirectEnd; // Next order book.
bool bDirectAdvance; // Need to advance directory.
SLE::pointer sleDirectDir;
STAmount saOfrRate; // For correct ratio.
// Node
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;
};
// 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
{
protected:
Ledger::pointer mLedger;
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);
public:
typedef boost::shared_ptr<PathState> pointer;
TER terStatus;
std::vector<PaymentNode> 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;
uint64 uQuality; // 0 = none.
STAmount saInReq; // Max amount to spend by sender
STAmount saInAct; // Amount spent by sender (calc output)
STAmount saOutReq; // Amount to send (calc input)
STAmount saOutAct; // Amount actually sent (calc output).
PathState(
Ledger::ref lpLedger,
const int iIndex,
const LedgerEntrySet& lesSource,
const STPath& spSourcePath,
const uint160& uReceiverID,
const uint160& uSenderID,
const STAmount& saSend,
const STAmount& saSendMax
);
Json::Value getJson() const;
static PathState::pointer createPathState(
Ledger::ref lpLedger,
const int iIndex,
const LedgerEntrySet& lesSource,
const STPath& spSourcePath,
const uint160& uReceiverID,
const uint160& uSenderID,
const STAmount& saSend,
const STAmount& saSendMax
)
{
return boost::make_shared<PathState>(lpLedger, iIndex, lesSource, spSourcePath, uReceiverID, uSenderID, saSend, saSendMax);
}
static bool lessPriority(const PathState::pointer& lhs, const PathState::pointer& rhs);
};
// One instance per ledger.
// Only one transaction applied at a time.
class TransactionEngine
@@ -266,21 +34,6 @@ class TransactionEngine
private:
LedgerEntrySet mNodes;
TER dirAdd(
uint64& uNodeDir, // Node of entry.
const uint256& uRootIndex,
const uint256& uLedgerIndex);
TER dirDelete(
const bool bKeepRoot,
const uint64& uNodeDir, // Node item is mentioned in.
const uint256& uRootIndex,
const uint256& uLedgerIndex, // Item being deleted
const bool bStable);
bool dirFirst(const uint256& uRootIndex, SLE::pointer& sleNode, unsigned int& uDirEntry, uint256& uEntryIndex);
bool dirNext(const uint256& uRootIndex, SLE::pointer& sleNode, unsigned int& uDirEntry, uint256& uEntryIndex);
TER setAuthorized(const SerializedTransaction& txn, bool bMustSetGenerator);
TER takeOffers(
@@ -299,72 +52,11 @@ protected:
uint160 mTxnAccountID;
SLE::pointer mTxnAccount;
// 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.
// When processing, don't want to complicate directory walking with deletion.
std::vector<uint256> mvUnfundedBecame; // Offers that became unfunded.
// If the transaction fails to meet some constraint, still need to delete unfunded offers.
boost::unordered_set<uint256> musUnfundedFound; // Offers that were found unfunded.
SLE::pointer entryCreate(LedgerEntryType type, const uint256& index) { return mNodes.entryCreate(type, index); }
SLE::pointer entryCache(LedgerEntryType type, const uint256& index) { return mNodes.entryCache(type, index); }
void entryDelete(SLE::ref sleEntry) { mNodes.entryDelete(sleEntry); }
void entryModify(SLE::ref sleEntry) { mNodes.entryModify(sleEntry); }
TER offerDelete(const uint256& uOfferIndex);
TER offerDelete(const SLE::pointer& sleOffer, const uint256& uOfferIndex, const uint160& uOwnerID);
uint32 rippleTransferRate(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, const SOE_Field sfLow=sfLowQualityIn, const SOE_Field 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);
void rippleCredit(const uint160& uSenderID, const uint160& uReceiverID, const STAmount& saAmount, bool bCheckIssuer=true);
STAmount rippleSend(const uint160& uSenderID, const uint160& uReceiverID, const STAmount& saAmount);
STAmount accountHolds(const uint160& uAccountID, const uint160& uCurrencyID, const uint160& uIssuerID);
void accountSend(const uint160& uSenderID, const uint160& uReceiverID, const STAmount& saAmount);
STAmount accountFunds(const uint160& uAccountID, const STAmount& saDefault);
PathState::pointer pathCreate(const STPath& spPath);
void pathNext(const PathState::pointer& pspCur, const int iPaths, const LedgerEntrySet& lesCheckpoint);
TER calcNode(const unsigned int uIndex, const PathState::pointer& pspCur, const bool bMultiQuality);
TER calcNodeRev(const unsigned int uIndex, const PathState::pointer& pspCur, const bool bMultiQuality);
TER calcNodeFwd(const unsigned int uIndex, const PathState::pointer& pspCur, const bool bMultiQuality);
TER calcNodeOfferRev(const unsigned int uIndex, const PathState::pointer& pspCur, const bool bMultiQuality);
TER calcNodeOfferFwd(const unsigned int uIndex, const PathState::pointer& pspCur, const bool bMultiQuality);
TER calcNodeAccountRev(const unsigned int uIndex, const PathState::pointer& pspCur, const bool bMultiQuality);
TER calcNodeAccountFwd(const unsigned int uIndex, const PathState::pointer& pspCur, const bool bMultiQuality);
TER calcNodeAdvance(const unsigned int uIndex, const PathState::pointer& pspCur, const bool bMultiQuality, const bool bReverse);
TER calcNodeDeliverRev(
const unsigned int uIndex,
const PathState::pointer& pspCur,
const bool bMultiQuality,
const uint160& uOutAccountID,
const STAmount& saOutReq,
STAmount& saOutAct);
TER calcNodeDeliverFwd(
const unsigned int uIndex,
const PathState::pointer& pspCur,
const bool bMultiQuality,
const uint160& uInAccountID,
const STAmount& saInFunds,
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);
void txnWrite();
TER doAccountSet(const SerializedTransaction& txn);
@@ -376,7 +68,7 @@ protected:
TER doNicknameSet(const SerializedTransaction& txn);
TER doPasswordFund(const SerializedTransaction& txn);
TER doPasswordSet(const SerializedTransaction& txn);
TER doPayment(const SerializedTransaction& txn);
TER doPayment(const SerializedTransaction& txn, const TransactionEngineParams params);
TER doWalletAdd(const SerializedTransaction& txn);
TER doContractAdd(const SerializedTransaction& txn);
TER doContractRemove(const SerializedTransaction& txn);

91
src/TransactionErr.cpp Normal file
View File

@@ -0,0 +1,91 @@
#include "TransactionErr.h"
#include "utils.h"
bool transResultInfo(TER terCode, std::string& strToken, std::string& strHuman)
{
static struct {
TER terCode;
const char* cpToken;
const char* cpHuman;
} transResultInfoA[] = {
{ tefALREADY, "tefALREADY", "The exact transaction was already in this ledger" },
{ tefBAD_ADD_AUTH, "tefBAD_ADD_AUTH", "Not authorized to add account." },
{ tefBAD_AUTH, "tefBAD_AUTH", "Transaction's public key is not authorized." },
{ tefBAD_CLAIM_ID, "tefBAD_CLAIM_ID", "Malformed." },
{ tefBAD_GEN_AUTH, "tefBAD_GEN_AUTH", "Not authorized to claim generator." },
{ tefBAD_LEDGER, "tefBAD_LEDGER", "Ledger in unexpected state." },
{ tefCLAIMED, "tefCLAIMED", "Can not claim a previously claimed account." },
{ tefEXCEPTION, "tefEXCEPTION", "Unexpected program state." },
{ tefCREATED, "tefCREATED", "Can't add an already created account." },
{ tefGEN_IN_USE, "tefGEN_IN_USE", "Generator already in use." },
{ tefPAST_SEQ, "tefPAST_SEQ", "This sequence number has already past" },
{ telBAD_PATH_COUNT, "telBAD_PATH_COUNT", "Malformed: too many paths." },
{ telINSUF_FEE_P, "telINSUF_FEE_P", "Fee insufficient." },
{ temBAD_AMOUNT, "temBAD_AMOUNT", "Can only send positive amounts." },
{ temBAD_AUTH_MASTER, "temBAD_AUTH_MASTER", "Auth for unclaimed account needs correct master key." },
{ temBAD_EXPIRATION, "temBAD_EXPIRATION", "Malformed." },
{ temBAD_ISSUER, "temBAD_ISSUER", "Malformed." },
{ temBAD_OFFER, "temBAD_OFFER", "Malformed." },
{ temBAD_PATH, "temBAD_PATH", "Malformed." },
{ temBAD_PATH_LOOP, "temBAD_PATH_LOOP", "Malformed." },
{ temBAD_PUBLISH, "temBAD_PUBLISH", "Malformed: bad publish." },
{ temBAD_SET_ID, "temBAD_SET_ID", "Malformed." },
{ temCREATEXNS, "temCREATEXNS", "Can not specify non XNS for Create." },
{ temDST_IS_SRC, "temDST_IS_SRC", "Destination may not be source." },
{ temDST_NEEDED, "temDST_NEEDED", "Destination not specified." },
{ temINSUF_FEE_P, "temINSUF_FEE_P", "Fee not allowed." },
{ temINVALID, "temINVALID", "The transaction is ill-formed" },
{ temREDUNDANT, "temREDUNDANT", "Sends same currency to self." },
{ temRIPPLE_EMPTY, "temRIPPLE_EMPTY", "PathSet with no paths." },
{ temUNCERTAIN, "temUNCERTAIN", "In process of determining result. Never returned." },
{ temUNKNOWN, "temUNKNOWN", "The transactions requires logic not implemented yet." },
{ tepPATH_DRY, "tepPATH_DRY", "Path could not send partial amount." },
{ tepPATH_PARTIAL, "tepPATH_PARTIAL", "Path could not send full amount." },
{ terDIR_FULL, "terDIR_FULL", "Can not add entry to full dir." },
{ terFUNDS_SPENT, "terFUNDS_SPENT", "Can't set password, password set funds already spent." },
{ terINSUF_FEE_B, "terINSUF_FEE_B", "Account balance can't pay fee." },
{ terNO_ACCOUNT, "terNO_ACCOUNT", "The source account does not exist." },
{ terNO_DST, "terNO_DST", "The destination does not exist" },
{ terNO_LINE, "terNO_LINE", "No such line." },
{ terNO_LINE_NO_ZERO, "terNO_LINE_NO_ZERO", "Can't zero non-existant line, destination might make it." },
{ terOFFER_NOT_FOUND, "terOFFER_NOT_FOUND", "Can not cancel offer." },
{ terPRE_SEQ, "terPRE_SEQ", "Missing/inapplicable prior transaction" },
{ terSET_MISSING_DST, "terSET_MISSING_DST", "Can't set password, destination missing." },
{ terUNFUNDED, "terUNFUNDED", "Source account had insufficient balance for transaction." },
{ tesSUCCESS, "tesSUCCESS", "The transaction was applied" },
};
int iIndex = NUMBER(transResultInfoA);
while (iIndex-- && transResultInfoA[iIndex].terCode != terCode)
;
if (iIndex >= 0)
{
strToken = transResultInfoA[iIndex].cpToken;
strHuman = transResultInfoA[iIndex].cpHuman;
}
return iIndex >= 0;
}
std::string transToken(TER terCode)
{
std::string strToken;
std::string strHuman;
return transResultInfo(terCode, strToken, strHuman) ? strToken : "-";
}
std::string transHuman(TER terCode)
{
std::string strToken;
std::string strHuman;
return transResultInfo(terCode, strToken, strHuman) ? strHuman : "-";
}

118
src/TransactionErr.h Normal file
View File

@@ -0,0 +1,118 @@
#ifndef _TRANSACTION_ERR_
#define _TRANSACTION_ERR_
#include <string>
enum TER // aka TransactionEngineResult
{
// Note: Range is stable. Exact numbers are currently unstable. Use tokens.
// -399 .. -300: L Local error (transaction fee inadequate, exceeds local limit)
// Only valid during non-consensus processing.
// Implications:
// - Not forwarded
// - No fee check
telLOCAL_ERROR = -399,
telBAD_PATH_COUNT,
telINSUF_FEE_P,
// -299 .. -200: M Malformed (bad signature)
// Causes:
// - Transaction corrupt.
// Implications:
// - Not applied
// - Not forwarded
// - Reject
// - Can not succeed in any imagined ledger.
temMALFORMED = -299,
temBAD_AMOUNT,
temBAD_AUTH_MASTER,
temBAD_EXPIRATION,
temBAD_ISSUER,
temBAD_OFFER,
temBAD_PATH,
temBAD_PATH_LOOP,
temBAD_PUBLISH,
temBAD_SET_ID,
temCREATEXNS,
temDST_IS_SRC,
temDST_NEEDED,
temINSUF_FEE_P,
temINVALID,
temREDUNDANT,
temRIPPLE_EMPTY,
temUNCERTAIN,
temUNKNOWN,
// -199 .. -100: F Failure (sequence number previously used)
// Causes:
// - Transaction cannot succeed because of ledger state.
// - Unexpected ledger state.
// - C++ exception.
// Implications:
// - Not applied
// - Not forwarded
// - Could succeed in an imagined ledger.
tefFAILURE = -199,
tefALREADY,
tefBAD_ADD_AUTH,
tefBAD_AUTH,
tefBAD_CLAIM_ID,
tefBAD_GEN_AUTH,
tefBAD_LEDGER,
tefCLAIMED,
tefCREATED,
tefEXCEPTION,
tefGEN_IN_USE,
tefPAST_SEQ,
// -99 .. -1: R Retry (sequence too high, no funds for txn fee, originating account non-existent)
// Causes:
// - Priror application of another, possibly non-existant, transaction could allow this transaction to succeed.
// Implications:
// - Not applied
// - Not forwarded
// - Might succeed later
// - Hold
terRETRY = -99,
terDIR_FULL,
terFUNDS_SPENT,
terINSUF_FEE_B,
terNO_ACCOUNT,
terNO_DST,
terNO_LINE,
terNO_LINE_NO_ZERO,
terOFFER_NOT_FOUND, // XXX If we check sequence first this could be hard failure.
terPRE_SEQ,
terSET_MISSING_DST,
terUNFUNDED,
// 0: S Success (success)
// Causes:
// - Success.
// Implications:
// - Applied
// - Forwarded
tesSUCCESS = 0,
// 100 .. P Partial success (SR) (ripple transaction with no good paths, pay to non-existent account)
// Causes:
// - Success, but does not achieve optimal result.
// Implications:
// - Applied
// - Forwarded
// Only allowed as a return code of appliedTransaction when !tapRetry. Otherwise, treated as terRETRY.
tepPARTIAL = 100,
tepPATH_DRY,
tepPATH_PARTIAL,
};
#define isTemMalformed(x) ((x) >= temMALFORMED && (x) < tefFAILURE)
#define isTefFailure(x) ((x) >= tefFAILURE && (x) < terRETRY)
#define isTepPartial(x) ((x) >= tepPATH_PARTIAL)
bool transResultInfo(TER terCode, std::string& strToken, std::string& strHuman);
std::string transToken(TER terCode);
std::string transHuman(TER terCode);
#endif

View File

@@ -160,11 +160,6 @@ TransactionMetaNode::TransactionMetaNode(int type, const uint256& node, Serializ
void TransactionMetaNode::addRaw(Serializer& s)
{
if (mEntries.empty())
{ // ack, an empty node
assert(false);
return;
}
s.add8(mType);
s.add256(mNode);
mEntries.sort();

View File

@@ -46,6 +46,7 @@ protected:
public:
TransactionMetaNodeEntry(int type) : mType(type) { ; }
virtual ~TransactionMetaNodeEntry() { ; }
int getType() const { return mType; }
virtual Json::Value getJson(int) const = 0;
@@ -167,6 +168,9 @@ public:
class TransactionMetaSet
{
public:
typedef boost::shared_ptr<TransactionMetaSet> pointer;
protected:
uint256 mTransactionID;
uint32 mLedger;

View File

@@ -116,19 +116,40 @@ int ValidationCollection::getTrustedValidationCount(const uint256& ledger)
return trusted;
}
int ValidationCollection::getCurrentValidationCount(uint32 afterTime)
{
int ValidationCollection::getNodesAfter(const uint256& ledger)
{ // Number of trusted nodes that have moved past this ledger
int count = 0;
boost::mutex::scoped_lock sl(mValidationLock);
for (boost::unordered_map<uint160, SerializedValidation::pointer>::iterator it = mCurrentValidations.begin(),
end = mCurrentValidations.end(); it != end; ++it)
{
if (it->second->isTrusted() && (it->second->getSignTime() > afterTime))
if (it->second->isTrusted() && it->second->isPreviousHash(ledger))
++count;
}
return count;
}
int ValidationCollection::getLoadRatio(bool overLoaded)
{ // how many trusted nodes are able to keep up, higher is better
int goodNodes = overLoaded ? 1 : 0;
int badNodes = overLoaded ? 0 : 1;
{
boost::mutex::scoped_lock sl(mValidationLock);
for (boost::unordered_map<uint160, SerializedValidation::pointer>::iterator it = mCurrentValidations.begin(),
end = mCurrentValidations.end(); it != end; ++it)
{
if (it->second->isTrusted())
{
if (it->second->isFull())
++goodNodes;
else
++badNodes;
}
}
}
return (goodNodes * 100) / (goodNodes + badNodes);
}
boost::unordered_map<uint256, int> ValidationCollection::getCurrentValidations(uint256 currentLedger)
{
uint32 cutoff = theApp->getOPs().getNetworkTimeNC() - LEDGER_VAL_INTERVAL;

View File

@@ -34,7 +34,9 @@ public:
void getValidationCount(const uint256& ledger, bool currentOnly, int& trusted, int& untrusted);
int getTrustedValidationCount(const uint256& ledger);
int getCurrentValidationCount(uint32 afterTime);
int getNodesAfter(const uint256& ledger);
int getLoadRatio(bool overLoaded);
boost::unordered_map<uint256, int> getCurrentValidations(uint256 currentLedger = uint256());

View File

@@ -5,7 +5,7 @@
//
#define SERVER_VERSION_MAJOR 0
#define SERVER_VERSION_MINOR 4
#define SERVER_VERSION_MINOR 5
#define SERVER_VERSION_SUB "-a"
#define SERVER_NAME "NewCoin"
@@ -16,11 +16,11 @@
// Version we prefer to speak:
#define PROTO_VERSION_MAJOR 0
#define PROTO_VERSION_MINOR 6
#define PROTO_VERSION_MINOR 7
// Version we will speak to:
#define MIN_PROTO_MAJOR 0
#define MIN_PROTO_MINOR 6
#define MIN_PROTO_MINOR 7
#define MAKE_VERSION_INT(maj,min) ((maj << 16) | min)
#define GET_VERSION_MAJOR(ver) (ver >> 16)

View File

@@ -27,7 +27,7 @@ private:
protected:
BN_CTX* pctx;
BN_CTX* operator=(BN_CTX* pnew) { return pctx = pnew; }
CAutoBN_CTX& operator=(BN_CTX* pnew) { pctx = pnew; return *this; }
public:
CAutoBN_CTX()

View File

@@ -69,9 +69,6 @@ message TMTransaction {
required bytes rawTransaction = 1;
required TransactionStatus status = 2;
optional uint64 receiveTimestamp = 3;
optional uint32 ledgerIndexPossible = 4; // the node may not know
optional uint32 ledgerIndexFinal = 5;
optional bytes conflictingTransaction = 6;
}

9
test/buster.js Normal file
View File

@@ -0,0 +1,9 @@
var config = module.exports;
config["Newcoin tests"] = {
rootPath: "../",
environment: "node",
tests: [
"test/*-test.js"
]
}

60
test/server.js Normal file
View File

@@ -0,0 +1,60 @@
// Manage test servers
// Provide servers
//
// Servers are created in tmp/server/$server
//
console.log("server.js>");
var config = require("./config.js");
var utils = require("./utils.js");
var fs = require("fs");
var path = require("path");
var util = require("util");
// var child = require("child");
var serverPath = function(name) {
return "tmp/server/" + name;
};
// Return a server's newcoind.cfg as string.
var serverConfig = function(name) {
var cfg = config.servers[name];
return Object.keys(cfg).map(function (o) {
return util.format("[%s]\n%s\n", o, cfg[o]);
}).join("");
};
var writeConfig = function(name, done) {
fs.writeFile(path.join(serverPath(name), "newcoind.cfg"), serverConfig(name), 'utf8', done);
};
var makeBase = function(name, done) {
var path = serverPath(name);
console.log("start> %s: %s", name, path);
// Reset the server directory, build it if needed.
utils.resetPath(path, parseInt('0777', 8), function (e) {
if (e) {
throw e;
}
else {
writeConfig(name, done);
}
});
console.log("start< %s", name);
};
var start = function(name, done) {
makeBase(name, done);
};
exports.start = start;
console.log("server.js<");
// vim:ts=4

18
test/standalone-test.js Normal file
View File

@@ -0,0 +1,18 @@
// console.log("standalone-test.js>");
var fs = require("fs");
var buster = require("buster");
var server = require("./server.js");
buster.testCase("Check standalone server startup", {
"Start": function (done) {
server.start("alpha", function(e) {
buster.refute(e);
done();
});
}
});
// console.log("standalone-test.js<");
// vim:ts=4

130
test/utils.js Normal file
View File

@@ -0,0 +1,130 @@
var fs = require("fs");
var path = require("path");
var filterErr = function(code, done) {
return function (e) {
done(e.code !== code ? e : undefined);
};
};
var throwErr = function(done) {
return function (e) {
if (e)
throw e;
done();
};
};
// apply function to elements of array. Return first true value to done or undefined.
var mapOr = function(func, array, done) {
if (array.length) {
func(array[array.length-1], function (v) {
if (v) {
done(v);
}
else {
array.length -= 1;
mapOr(func, array, done);
}
});
}
else {
done();
}
};
// Make a directory and sub-directories.
var mkPath = function(dirPath, mode, done) {
fs.mkdir(dirPath, mode, function (e) {
if (e && e.code === "EEXIST") {
// Already exists, done.
done();
}
else if (e.code === "ENOENT") {
// Missing sub dir.
mkPath(path.dirname(dirPath), mode, function(e) {
if (e) {
throw e;
}
else {
mkPath(dirPath, mode, done);
}
});
}
else {
throw e;
}
});
};
// Empty a directory.
var emptyPath = function(dirPath, done) {
fs.readdir(dirPath, function (err, files) {
if (err) {
done(err);
}
else {
mapOr(rmPath, files.map(function(f) { return path.join(dirPath, f); }), done);
}
});
};
// Remove path recursively.
var rmPath = function(dirPath, done) {
console.log("rmPath: %s", dirPath);
fs.lstat(dirPath, function (err, stats) {
if (err && err.code == "ENOENT") {
done();
}
if (err) {
done(err);
}
else if (stats.isDirectory()) {
emptyPath(dirPath, function (e) {
if (e) {
done(e);
}
else {
// console.log("rmdir: %s", dirPath); done();
fs.rmdir(dirPath, done);
}
});
}
else {
// console.log("unlink: %s", dirPath); done();
fs.unlink(dirPath, done);
}
});
};
// Create directory if needed and empty if needed.
var resetPath = function(dirPath, mode, done) {
mkPath(dirPath, mode, function (e) {
if (e) {
done(e);
}
else {
emptyPath(dirPath, done);
}
});
};
var trace = function(comment, func) {
return function() {
console.log("%s: %s", trace, arguments.toString);
func(arguments);
};
};
exports.emptyPath = emptyPath;
exports.mapOr = mapOr;
exports.mkPath = mkPath;
exports.resetPath = resetPath;
exports.rmPath = rmPath;
exports.trace = trace;
// vim:ts=4

View File

@@ -1,5 +0,0 @@
<config>
<PEER_PORT>4000</PEER_PORT>
<RPC_PORT>5001</RPC_PORT>
<NUMBER_CONNECTIONS>30</NUMBER_CONNECTIONS>
</config>

View File

@@ -1,4 +0,0 @@
<nodes>
<node ip="127.0.0.1" port="5000" last="0" />
<node ip="127.0.0.1" port="5005" last="0" />
</nodes>

View File

@@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<unl>
<node>
<name></name>
<url></url>
<hanko></hanko>
<date_added></date_added>
</node>
</unl>

View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<wallet>
<address>
<pubkey></pubkey>
<prikey></prikey>
</address>
</wallet>

View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<config>
<PEER_PORT>5000</PEER_PORT>
<RPC_PORT>5002</RPC_PORT>
<NUMBER_CONNECTIONS>30</NUMBER_CONNECTIONS>
</config>

View File

@@ -1,4 +0,0 @@
<nodes>
<node ip="127.0.0.1" port="5000" last="0" />
<node ip="127.0.0.1" port="5005" last="0" />
</nodes>

View File

@@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<unl>
<node>
<name></name>
<url></url>
<hanko></hanko>
<date_added></date_added>
</node>
</unl>

View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<wallet>
<address>
<pubkey></pubkey>
<prikey></prikey>
</address>
</wallet>

View File

@@ -1,6 +0,0 @@
REM copy C:\code\newcoin\Release\newcoin.exe C:\code\newcoin\tests\client1
REM copy C:\code\newcoin\Release\newcoin.exe C:\code\newcoin\tests\client2
copy d:\code\newcoin\Debug\newcoin.exe d:\code\newcoin\tests\client1
copy d:\code\newcoin\Debug\newcoin.exe d:\code\newcoin\tests\client2