mirror of
https://github.com/XRPLF/rippled.git
synced 2026-06-07 02:36:47 +00:00
Merge branch 'master' of github.com:jedmccaleb/NewCoin
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -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.
@@ -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
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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, \
|
||||
|
||||
111
src/Ledger.cpp
111
src/Ledger.cpp
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -78,6 +78,7 @@ protected:
|
||||
|
||||
public:
|
||||
LedgerAcquire(const uint256& hash);
|
||||
virtual ~LedgerAcquire() { ; }
|
||||
|
||||
bool isBase() const { return mHaveBase; }
|
||||
bool isAcctStComplete() const { return mHaveState; }
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(); }
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -12,6 +12,8 @@ public:
|
||||
virtual bool work(Interpreter* interpreter)=0;
|
||||
|
||||
virtual int getFee();
|
||||
|
||||
virtual ~Operation() { ; }
|
||||
};
|
||||
|
||||
// this is just an Int in the code
|
||||
|
||||
@@ -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
2400
src/RippleCalc.cpp
Normal file
File diff suppressed because it is too large
Load Diff
190
src/RippleCalc.h
Normal file
190
src/RippleCalc.h
Normal 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
|
||||
@@ -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");
|
||||
|
||||
43
src/SHAMap.h
43
src/SHAMap.h
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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); }
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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++)
|
||||
|
||||
@@ -106,7 +106,6 @@ enum SOE_Field
|
||||
sfTakerGets,
|
||||
sfTakerPays,
|
||||
sfTarget,
|
||||
sfTargetLedger,
|
||||
sfTransferRate,
|
||||
sfVersion,
|
||||
sfWalletLocator,
|
||||
|
||||
@@ -28,7 +28,7 @@ protected:
|
||||
TransactionType mType;
|
||||
STVariableLength mSignature;
|
||||
STObject mMiddleTxn, mInnerTxn;
|
||||
TransactionFormat* mFormat;
|
||||
const TransactionFormat* mFormat;
|
||||
|
||||
SerializedTransaction* duplicate() const { return new SerializedTransaction(*this); }
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -395,6 +395,9 @@ public:
|
||||
Json::Value getJson(int) const;
|
||||
};
|
||||
|
||||
extern STAmount saZero;
|
||||
extern STAmount saOne;
|
||||
|
||||
class STHash128 : public SerializedType
|
||||
{
|
||||
protected:
|
||||
|
||||
@@ -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
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
@@ -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
91
src/TransactionErr.cpp
Normal 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
118
src/TransactionErr.h
Normal 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
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
9
test/buster.js
Normal 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
60
test/server.js
Normal 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
18
test/standalone-test.js
Normal 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
130
test/utils.js
Normal 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
|
||||
@@ -1,5 +0,0 @@
|
||||
<config>
|
||||
<PEER_PORT>4000</PEER_PORT>
|
||||
<RPC_PORT>5001</RPC_PORT>
|
||||
<NUMBER_CONNECTIONS>30</NUMBER_CONNECTIONS>
|
||||
</config>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<wallet>
|
||||
<address>
|
||||
<pubkey></pubkey>
|
||||
<prikey></prikey>
|
||||
</address>
|
||||
</wallet>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<wallet>
|
||||
<address>
|
||||
<pubkey></pubkey>
|
||||
<prikey></prikey>
|
||||
</address>
|
||||
</wallet>
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user