mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
Merge branch 'master' of github.com:jedmccaleb/NewCoin
This commit is contained in:
@@ -24,20 +24,18 @@ void AccountItems::fillItems(const uint160& accountID, Ledger::ref ledger)
|
||||
uint256 rootIndex = Ledger::getOwnerDirIndex(accountID);
|
||||
uint256 currentIndex = rootIndex;
|
||||
|
||||
LedgerStateParms lspNode = lepNONE;
|
||||
|
||||
while (1)
|
||||
{
|
||||
SLE::pointer ownerDir = ledger->getDirNode(lspNode, currentIndex);
|
||||
SLE::pointer ownerDir = ledger->getDirNode(currentIndex);
|
||||
if (!ownerDir) return;
|
||||
|
||||
STVector256 svOwnerNodes = ownerDir->getFieldV256(sfIndexes);
|
||||
|
||||
BOOST_FOREACH(uint256& uNode, svOwnerNodes.peekValue())
|
||||
{
|
||||
SLE::pointer sleCur = ledger->getSLE(uNode);
|
||||
SLE::pointer sleCur = ledger->getSLEi(uNode);
|
||||
|
||||
AccountItem::pointer item=mOfType->makeItem(accountID, sleCur);
|
||||
AccountItem::pointer item = mOfType->makeItem(accountID, sleCur);
|
||||
if(item)
|
||||
{
|
||||
mItems.push_back(item);
|
||||
|
||||
@@ -41,7 +41,7 @@ DatabaseCon::~DatabaseCon()
|
||||
|
||||
Application::Application() :
|
||||
mIOWork(mIOService), mAuxWork(mAuxService), mUNL(mIOService), mNetOps(mIOService, &mLedgerMaster),
|
||||
mTempNodeCache("NodeCache", 16384, 90), mHashedObjectStore(16384, 300),
|
||||
mTempNodeCache("NodeCache", 16384, 90), mHashedObjectStore(16384, 300), mSLECache("LedgerEntryCache", 4096, 120),
|
||||
mSNTPClient(mAuxService), mRPCHandler(&mNetOps), mFeeTrack(),
|
||||
mRpcDB(NULL), mTxnDB(NULL), mLedgerDB(NULL), mWalletDB(NULL), mHashNodeDB(NULL), mNetNodeDB(NULL),
|
||||
mConnectionPool(mIOService), mPeerDoor(NULL), mRPCDoor(NULL), mWSPublicDoor(NULL), mWSPrivateDoor(NULL),
|
||||
@@ -301,6 +301,7 @@ void Application::sweep()
|
||||
mTempNodeCache.sweep();
|
||||
mValidations.sweep();
|
||||
getMasterLedgerAcquire().sweep();
|
||||
mSLECache.sweep();
|
||||
mSweepTimer.expires_from_now(boost::posix_time::seconds(theConfig.getSize(siSweepInterval)));
|
||||
mSweepTimer.async_wait(boost::bind(&Application::sweep, this));
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
class RPCDoor;
|
||||
class PeerDoor;
|
||||
typedef TaggedCache< uint256, std::vector<unsigned char> > NodeCache;
|
||||
typedef TaggedCache< uint256, SLE > SLECache;
|
||||
|
||||
class DatabaseCon
|
||||
{
|
||||
@@ -60,6 +61,7 @@ class Application
|
||||
ValidationCollection mValidations;
|
||||
SuppressionTable mSuppressions;
|
||||
HashedObjectStore mHashedObjectStore;
|
||||
SLECache mSLECache;
|
||||
SNTPClient mSNTPClient;
|
||||
JobQueue mJobQueue;
|
||||
RPCHandler mRPCHandler;
|
||||
@@ -118,6 +120,7 @@ public:
|
||||
TXQueue& getTxnQueue() { return mTxnQueue; }
|
||||
PeerDoor& getPeerDoor() { return *mPeerDoor; }
|
||||
OrderBookDB& getOrderBookDB() { return mOrderBookDB; }
|
||||
SLECache& getSLECache() { return mSLECache; }
|
||||
|
||||
|
||||
bool isNew(const uint256& s) { return mSuppressions.addSuppression(s); }
|
||||
|
||||
@@ -483,7 +483,7 @@ int Config::getSize(SizedItemName item)
|
||||
{
|
||||
SizedItem sizeTable[] = {
|
||||
{ siSweepInterval, { 10, 30, 60, 90, 90 } },
|
||||
{ siLedgerFetch, { 2, 4, 5, 6, 6 } },
|
||||
{ siLedgerFetch, { 2, 2, 3, 4, 5 } },
|
||||
{ siValidationsSize, { 256, 256, 512, 1024, 1024 } },
|
||||
{ siValidationsAge, { 500, 500, 500, 500, 500 } },
|
||||
{ siNodeCacheSize, { 8192, 32768, 131072, 1048576, 0 } },
|
||||
|
||||
@@ -98,7 +98,7 @@ bool ConnectionPool::savePeer(const std::string& strIp, int iPort, char code)
|
||||
|
||||
Peer::pointer ConnectionPool::getPeerById(const uint64& id)
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mPeerLock);
|
||||
boost::recursive_mutex::scoped_lock sl(mPeerLock);
|
||||
const boost::unordered_map<uint64, Peer::pointer>::iterator& it = mPeerIdMap.find(id);
|
||||
if (it == mPeerIdMap.end())
|
||||
return Peer::pointer();
|
||||
@@ -107,7 +107,7 @@ Peer::pointer ConnectionPool::getPeerById(const uint64& id)
|
||||
|
||||
bool ConnectionPool::hasPeer(const uint64& id)
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mPeerLock);
|
||||
boost::recursive_mutex::scoped_lock sl(mPeerLock);
|
||||
return mPeerIdMap.find(id) != mPeerIdMap.end();
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ bool ConnectionPool::peerAvailable(std::string& strIp, int& iPort)
|
||||
|
||||
// Convert mIpMap (list of open connections) to a vector of "<ip> <port>".
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mPeerLock);
|
||||
boost::recursive_mutex::scoped_lock sl(mPeerLock);
|
||||
|
||||
vstrIpPort.reserve(mIpMap.size());
|
||||
|
||||
@@ -235,7 +235,7 @@ void ConnectionPool::policyHandler(const boost::system::error_code& ecResult)
|
||||
int ConnectionPool::relayMessage(Peer* fromPeer, const PackedMessage::pointer& msg)
|
||||
{
|
||||
int sentTo = 0;
|
||||
boost::mutex::scoped_lock sl(mPeerLock);
|
||||
boost::recursive_mutex::scoped_lock sl(mPeerLock);
|
||||
|
||||
BOOST_FOREACH(const vtConMap& pair, mConnectedMap)
|
||||
{
|
||||
@@ -254,7 +254,7 @@ int ConnectionPool::relayMessage(Peer* fromPeer, const PackedMessage::pointer& m
|
||||
|
||||
void ConnectionPool::relayMessageBut(const std::set<uint64>& fromPeers, const PackedMessage::pointer& msg)
|
||||
{ // Relay message to all but the specified peers
|
||||
boost::mutex::scoped_lock sl(mPeerLock);
|
||||
boost::recursive_mutex::scoped_lock sl(mPeerLock);
|
||||
|
||||
BOOST_FOREACH(const vtConMap& pair, mConnectedMap)
|
||||
{
|
||||
@@ -267,7 +267,7 @@ void ConnectionPool::relayMessageBut(const std::set<uint64>& fromPeers, const Pa
|
||||
|
||||
void ConnectionPool::relayMessageTo(const std::set<uint64>& fromPeers, const PackedMessage::pointer& msg)
|
||||
{ // Relay message to the specified peers
|
||||
boost::mutex::scoped_lock sl(mPeerLock);
|
||||
boost::recursive_mutex::scoped_lock sl(mPeerLock);
|
||||
|
||||
BOOST_FOREACH(const uint64& peerID, fromPeers)
|
||||
{
|
||||
@@ -305,34 +305,22 @@ Peer::pointer ConnectionPool::peerConnect(const std::string& strIp, int iPort)
|
||||
ipPort pipPeer = make_pair(strIp, iPort);
|
||||
Peer::pointer ppResult;
|
||||
|
||||
boost::unordered_map<ipPort, Peer::pointer>::iterator it;
|
||||
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mPeerLock);
|
||||
|
||||
if ((it = mIpMap.find(pipPeer)) == mIpMap.end())
|
||||
boost::recursive_mutex::scoped_lock sl(mPeerLock);
|
||||
if (mIpMap.find(pipPeer) == mIpMap.end())
|
||||
{
|
||||
Peer::pointer ppNew(Peer::create(theApp->getIOService(), theApp->getPeerDoor().getSSLContext(),
|
||||
++mLastPeer, false));
|
||||
ppResult = Peer::create(theApp->getIOService(), theApp->getPeerDoor().getSSLContext(),
|
||||
++mLastPeer, false);
|
||||
|
||||
// Did not find it. Not already connecting or connected.
|
||||
ppNew->connect(strIp, iPort);
|
||||
|
||||
mIpMap[pipPeer] = ppNew;
|
||||
|
||||
ppResult = ppNew;
|
||||
mIpMap[pipPeer] = ppResult;
|
||||
// ++miConnectStarting;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Found it. Already connected.
|
||||
|
||||
nothing();
|
||||
}
|
||||
}
|
||||
|
||||
if (ppResult)
|
||||
{
|
||||
ppResult->connect(strIp, iPort);
|
||||
//cLog(lsINFO) << "Pool: Connecting: " << ADDRESS_SHARED(ppResult) << ": " << strIp << " " << iPort;
|
||||
}
|
||||
else
|
||||
@@ -359,7 +347,7 @@ Json::Value ConnectionPool::getPeersJson()
|
||||
|
||||
int ConnectionPool::getPeerCount()
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mPeerLock);
|
||||
boost::recursive_mutex::scoped_lock sl(mPeerLock);
|
||||
|
||||
return mConnectedMap.size();
|
||||
}
|
||||
@@ -368,7 +356,7 @@ std::vector<Peer::pointer> ConnectionPool::getPeerVector()
|
||||
{
|
||||
std::vector<Peer::pointer> ret;
|
||||
|
||||
boost::mutex::scoped_lock sl(mPeerLock);
|
||||
boost::recursive_mutex::scoped_lock sl(mPeerLock);
|
||||
|
||||
ret.reserve(mConnectedMap.size());
|
||||
|
||||
@@ -383,7 +371,7 @@ std::vector<Peer::pointer> ConnectionPool::getPeerVector()
|
||||
|
||||
uint64 ConnectionPool::assignPeerId()
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mPeerLock);
|
||||
boost::recursive_mutex::scoped_lock sl(mPeerLock);
|
||||
return ++mLastPeer;
|
||||
}
|
||||
|
||||
@@ -402,7 +390,7 @@ bool ConnectionPool::peerConnected(Peer::ref peer, const RippleAddress& naPeer,
|
||||
}
|
||||
else
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mPeerLock);
|
||||
boost::recursive_mutex::scoped_lock sl(mPeerLock);
|
||||
const boost::unordered_map<RippleAddress, Peer::pointer>::iterator& itCm = mConnectedMap.find(naPeer);
|
||||
|
||||
if (itCm == mConnectedMap.end())
|
||||
@@ -453,7 +441,7 @@ bool ConnectionPool::peerConnected(Peer::ref peer, const RippleAddress& naPeer,
|
||||
// We maintain a map of public key to peer for connected and verified peers. Maintain it.
|
||||
void ConnectionPool::peerDisconnected(Peer::ref peer, const RippleAddress& naPeer)
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mPeerLock);
|
||||
boost::recursive_mutex::scoped_lock sl(mPeerLock);
|
||||
|
||||
if (naPeer.isValid())
|
||||
{
|
||||
@@ -556,7 +544,7 @@ void ConnectionPool::peerClosed(Peer::ref peer, const std::string& strIp, int iP
|
||||
// Determine if closed peer was redundant.
|
||||
bool bRedundant = true;
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mPeerLock);
|
||||
boost::recursive_mutex::scoped_lock sl(mPeerLock);
|
||||
const boost::unordered_map<ipPort, Peer::pointer>::iterator& itIp = mIpMap.find(ipPeer);
|
||||
|
||||
if (itIp == mIpMap.end())
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
class ConnectionPool
|
||||
{
|
||||
private:
|
||||
boost::mutex mPeerLock;
|
||||
uint64 mLastPeer;
|
||||
boost::recursive_mutex mPeerLock;
|
||||
uint64 mLastPeer;
|
||||
|
||||
typedef std::pair<RippleAddress, Peer::pointer> naPeer;
|
||||
typedef std::pair<ipPort, Peer::pointer> pipPeer;
|
||||
|
||||
@@ -23,7 +23,7 @@ JobQueue::JobQueue() : mLastJob(0), mThreadCount(0), mShuttingDown(false)
|
||||
mJobLoads[jtTRANSACTION_l].setTargetLatency(100, 500);
|
||||
mJobLoads[jtPROPOSAL_t].setTargetLatency(100, 500);
|
||||
|
||||
mJobLoads[jtCLIENT].setTargetLatency(250, 1000);
|
||||
mJobLoads[jtCLIENT].setTargetLatency(2000, 5000);
|
||||
mJobLoads[jtPEER].setTargetLatency(200, 1250);
|
||||
mJobLoads[jtDISK].setTargetLatency(500, 1000);
|
||||
mJobLoads[jtRPC].setTargetLatency(250, 750);
|
||||
|
||||
@@ -200,8 +200,8 @@ AccountState::pointer Ledger::getAccountState(const RippleAddress& accountID)
|
||||
// std::cerr << "Ledger:getAccountState(" << accountID.humanAccountID() << ")" << std::endl;
|
||||
#endif
|
||||
|
||||
SHAMapItem::pointer item = mAccountStateMap->peekItem(Ledger::getAccountRootIndex(accountID));
|
||||
if (!item)
|
||||
SLE::pointer sle = getSLEi(Ledger::getAccountRootIndex(accountID));
|
||||
if (!sle)
|
||||
{
|
||||
cLog(lsDEBUG) << boost::str(boost::format("Ledger:getAccountState: not found: %s: %s")
|
||||
% accountID.humanAccountID()
|
||||
@@ -210,8 +210,6 @@ AccountState::pointer Ledger::getAccountState(const RippleAddress& accountID)
|
||||
return AccountState::pointer();
|
||||
}
|
||||
|
||||
SerializedLedgerEntry::pointer sle =
|
||||
boost::make_shared<SerializedLedgerEntry>(item->peekSerializer(), item->getTag());
|
||||
if (sle->getType() != ltACCOUNT_ROOT)
|
||||
return AccountState::pointer();
|
||||
|
||||
@@ -395,7 +393,7 @@ void Ledger::saveAcceptedLedger(bool fromConsensus, LoadEvent::pointer event)
|
||||
static boost::format transExists("SELECT Status FROM Transactions WHERE TransID = '%s';");
|
||||
static boost::format
|
||||
updateTx("UPDATE Transactions SET LedgerSeq = %d, Status = '%c', TxnMeta = %s WHERE TransID = '%s';");
|
||||
static boost::format addLedger("INSERT INTO Ledgers "
|
||||
static boost::format addLedger("INSERT OR REPLACE INTO Ledgers "
|
||||
"(LedgerHash,LedgerSeq,PrevHash,TotalCoins,ClosingTime,PrevClosingTime,CloseTimeRes,CloseFlags,"
|
||||
"AccountSetHash,TransSetHash) VALUES ('%s','%u','%s','%s','%u','%u','%d','%u','%s','%s');");
|
||||
|
||||
@@ -819,6 +817,7 @@ Json::Value Ledger::getJson(int options)
|
||||
{
|
||||
Json::Value txns(Json::arrayValue);
|
||||
SHAMapTreeNode::TNType type;
|
||||
ScopedLock l(mTransactionMap->Lock());
|
||||
for (SHAMapItem::pointer item = mTransactionMap->peekFirstItem(type); !!item;
|
||||
item = mTransactionMap->peekNextItem(item->getTag(), type))
|
||||
{
|
||||
@@ -858,6 +857,7 @@ Json::Value Ledger::getJson(int options)
|
||||
if (mAccountStateMap && (bFull || ((options & LEDGER_JSON_DUMP_STATE) != 0)))
|
||||
{
|
||||
Json::Value state(Json::arrayValue);
|
||||
ScopedLock l(mAccountStateMap->Lock());
|
||||
for (SHAMapItem::pointer item = mAccountStateMap->peekFirstItem(); !!item;
|
||||
item = mAccountStateMap->peekNextItem(item->getTag()))
|
||||
{
|
||||
@@ -954,6 +954,26 @@ SLE::pointer Ledger::getSLE(const uint256& uHash)
|
||||
return boost::make_shared<SLE>(node->peekSerializer(), node->getTag());
|
||||
}
|
||||
|
||||
SLE::pointer Ledger::getSLEi(const uint256& uId)
|
||||
{
|
||||
uint256 hash;
|
||||
|
||||
ScopedLock sl(mAccountStateMap->Lock());
|
||||
|
||||
SHAMapItem::pointer node = mAccountStateMap->peekItem(uId, hash);
|
||||
if (!node)
|
||||
return SLE::pointer();
|
||||
|
||||
SLE::pointer ret = theApp->getSLECache().fetch(hash);
|
||||
if (!ret)
|
||||
{
|
||||
ret = boost::make_shared<SLE>(node->peekSerializer(), node->getTag());
|
||||
ret->setImmutable();
|
||||
theApp->getSLECache().canonicalize(hash, ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint256 Ledger::getFirstLedgerIndex()
|
||||
{
|
||||
SHAMapItem::pointer node = mAccountStateMap->peekFirstItem();
|
||||
@@ -994,6 +1014,14 @@ uint256 Ledger::getPrevLedgerIndex(const uint256& uHash, const uint256& uBegin)
|
||||
return node->getTag();
|
||||
}
|
||||
|
||||
SLE::pointer Ledger::getASNodeI(const uint256& nodeID, LedgerEntryType let)
|
||||
{
|
||||
SLE::pointer node = getSLEi(nodeID);
|
||||
if (node && (node->getType() != let))
|
||||
node.reset();
|
||||
return node;
|
||||
}
|
||||
|
||||
SLE::pointer Ledger::getASNode(LedgerStateParms& parms, const uint256& nodeID,
|
||||
LedgerEntryType let )
|
||||
{
|
||||
@@ -1030,43 +1058,39 @@ SLE::pointer Ledger::getASNode(LedgerStateParms& parms, const uint256& nodeID,
|
||||
|
||||
SLE::pointer Ledger::getAccountRoot(const uint160& accountID)
|
||||
{
|
||||
LedgerStateParms qry = lepNONE;
|
||||
|
||||
return getASNode(qry, getAccountRootIndex(accountID), ltACCOUNT_ROOT);
|
||||
return getASNodeI(getAccountRootIndex(accountID), ltACCOUNT_ROOT);
|
||||
}
|
||||
|
||||
SLE::pointer Ledger::getAccountRoot(const RippleAddress& naAccountID)
|
||||
{
|
||||
LedgerStateParms qry = lepNONE;
|
||||
|
||||
return getASNode(qry, getAccountRootIndex(naAccountID.getAccountID()), ltACCOUNT_ROOT);
|
||||
return getASNodeI(getAccountRootIndex(naAccountID.getAccountID()), ltACCOUNT_ROOT);
|
||||
}
|
||||
|
||||
//
|
||||
// Directory
|
||||
//
|
||||
|
||||
SLE::pointer Ledger::getDirNode(LedgerStateParms& parms, const uint256& uNodeIndex)
|
||||
SLE::pointer Ledger::getDirNode(const uint256& uNodeIndex)
|
||||
{
|
||||
return getASNode(parms, uNodeIndex, ltDIR_NODE);
|
||||
return getASNodeI(uNodeIndex, ltDIR_NODE);
|
||||
}
|
||||
|
||||
//
|
||||
// Generator Map
|
||||
//
|
||||
|
||||
SLE::pointer Ledger::getGenerator(LedgerStateParms& parms, const uint160& uGeneratorID)
|
||||
SLE::pointer Ledger::getGenerator(const uint160& uGeneratorID)
|
||||
{
|
||||
return getASNode(parms, getGeneratorIndex(uGeneratorID), ltGENERATOR_MAP);
|
||||
return getASNodeI(getGeneratorIndex(uGeneratorID), ltGENERATOR_MAP);
|
||||
}
|
||||
|
||||
//
|
||||
// Nickname
|
||||
//
|
||||
|
||||
SLE::pointer Ledger::getNickname(LedgerStateParms& parms, const uint256& uNickname)
|
||||
SLE::pointer Ledger::getNickname(const uint256& uNickname)
|
||||
{
|
||||
return getASNode(parms, uNickname, ltNICKNAME);
|
||||
return getASNodeI(uNickname, ltNICKNAME);
|
||||
}
|
||||
|
||||
//
|
||||
@@ -1074,18 +1098,18 @@ SLE::pointer Ledger::getNickname(LedgerStateParms& parms, const uint256& uNickna
|
||||
//
|
||||
|
||||
|
||||
SLE::pointer Ledger::getOffer(LedgerStateParms& parms, const uint256& uIndex)
|
||||
SLE::pointer Ledger::getOffer(const uint256& uIndex)
|
||||
{
|
||||
return getASNode(parms, uIndex, ltOFFER);
|
||||
return getASNodeI(uIndex, ltOFFER);
|
||||
}
|
||||
|
||||
//
|
||||
// Ripple State
|
||||
//
|
||||
|
||||
SLE::pointer Ledger::getRippleState(LedgerStateParms& parms, const uint256& uNode)
|
||||
SLE::pointer Ledger::getRippleState(const uint256& uNode)
|
||||
{
|
||||
return getASNode(parms, uNode, ltRIPPLE_STATE);
|
||||
return getASNodeI(uNode, ltRIPPLE_STATE);
|
||||
}
|
||||
|
||||
// For an entry put in the 64 bit index or quality.
|
||||
@@ -1179,7 +1203,7 @@ uint256 Ledger::getLedgerHash(uint32 ledgerIndex)
|
||||
int diff = mLedgerSeq - ledgerIndex;
|
||||
if (diff <= 256)
|
||||
{
|
||||
SLE::pointer hashIndex = getSLE(getLedgerHashIndex());
|
||||
SLE::pointer hashIndex = getSLEi(getLedgerHashIndex());
|
||||
if (hashIndex)
|
||||
{
|
||||
assert(hashIndex->getFieldU32(sfLastLedgerSequence) == (mLedgerSeq - 1));
|
||||
@@ -1199,7 +1223,7 @@ uint256 Ledger::getLedgerHash(uint32 ledgerIndex)
|
||||
}
|
||||
|
||||
// in skiplist
|
||||
SLE::pointer hashIndex = getSLE(getLedgerHashIndex(ledgerIndex));
|
||||
SLE::pointer hashIndex = getSLEi(getLedgerHashIndex(ledgerIndex));
|
||||
if (hashIndex)
|
||||
{
|
||||
int lastSeq = hashIndex->getFieldU32(sfLastLedgerSequence);
|
||||
@@ -1219,7 +1243,7 @@ uint256 Ledger::getLedgerHash(uint32 ledgerIndex)
|
||||
std::vector< std::pair<uint32, uint256> > Ledger::getLedgerHashes()
|
||||
{
|
||||
std::vector< std::pair<uint32, uint256> > ret;
|
||||
SLE::pointer hashIndex = getSLE(getLedgerHashIndex());
|
||||
SLE::pointer hashIndex = getSLEi(getLedgerHashIndex());
|
||||
if (hashIndex)
|
||||
{
|
||||
STVector256 vec = hashIndex->getFieldV256(sfHashes);
|
||||
|
||||
@@ -97,6 +97,9 @@ private:
|
||||
protected:
|
||||
SLE::pointer getASNode(LedgerStateParms& parms, const uint256& nodeID, LedgerEntryType let);
|
||||
|
||||
// returned SLE is immutable
|
||||
SLE::pointer getASNodeI(const uint256& nodeID, LedgerEntryType let);
|
||||
|
||||
static void incPendingSaves();
|
||||
static void decPendingSaves();
|
||||
void saveAcceptedLedger(bool fromConsensus, LoadEvent::pointer);
|
||||
@@ -201,7 +204,8 @@ public:
|
||||
void pendSave(bool fromConsensus);
|
||||
|
||||
// next/prev function
|
||||
SLE::pointer getSLE(const uint256& uHash);
|
||||
SLE::pointer getSLE(const uint256& uHash); // SLE is mutable
|
||||
SLE::pointer getSLEi(const uint256& uHash); // SLE is immutable
|
||||
uint256 getFirstLedgerIndex();
|
||||
uint256 getLastLedgerIndex();
|
||||
uint256 getNextLedgerIndex(const uint256& uHash); // first node >hash
|
||||
@@ -230,7 +234,7 @@ public:
|
||||
// Generator Map functions
|
||||
//
|
||||
|
||||
SLE::pointer getGenerator(LedgerStateParms& parms, const uint160& uGeneratorID);
|
||||
SLE::pointer getGenerator(const uint160& uGeneratorID);
|
||||
|
||||
static uint256 getGeneratorIndex(const uint160& uGeneratorID);
|
||||
|
||||
@@ -245,9 +249,8 @@ public:
|
||||
NicknameState::pointer getNicknameState(const std::string& strNickname)
|
||||
{ return getNicknameState(getNicknameHash(strNickname)); }
|
||||
|
||||
SLE::pointer getNickname(LedgerStateParms& parms, const uint256& uNickname);
|
||||
SLE::pointer getNickname(LedgerStateParms& parms, const std::string& strNickname)
|
||||
{ return getNickname(parms, getNicknameHash(strNickname)); }
|
||||
SLE::pointer getNickname(const uint256& uNickname);
|
||||
SLE::pointer getNickname(const std::string& strNickname) { return getNickname(getNicknameHash(strNickname)); }
|
||||
|
||||
static uint256 getNicknameIndex(const uint256& uNickname);
|
||||
|
||||
@@ -263,16 +266,10 @@ public:
|
||||
// Offer functions
|
||||
//
|
||||
|
||||
SLE::pointer getOffer(LedgerStateParms& parms, const uint256& uIndex);
|
||||
SLE::pointer getOffer(const uint256& uIndex);
|
||||
|
||||
SLE::pointer getOffer(const uint256& uIndex)
|
||||
{
|
||||
LedgerStateParms qry = lepNONE;
|
||||
return getOffer(qry, uIndex);
|
||||
}
|
||||
|
||||
SLE::pointer getOffer(LedgerStateParms& parms, const uint160& uAccountID, uint32 uSequence)
|
||||
{ return getOffer(parms, getOfferIndex(uAccountID, uSequence)); }
|
||||
SLE::pointer getOffer(const uint160& uAccountID, uint32 uSequence)
|
||||
{ return getOffer(getOfferIndex(uAccountID, uSequence)); }
|
||||
|
||||
// The index of an offer.
|
||||
static uint256 getOfferIndex(const uint160& uAccountID, uint32 uSequence);
|
||||
@@ -293,7 +290,7 @@ public:
|
||||
static void ownerDirDescriber(SLE::ref, const uint160& owner);
|
||||
|
||||
// Return a node: root or normal
|
||||
SLE::pointer getDirNode(LedgerStateParms& parms, const uint256& uNodeIndex);
|
||||
SLE::pointer getDirNode(const uint256& uNodeIndex);
|
||||
|
||||
//
|
||||
// Quality
|
||||
@@ -316,13 +313,7 @@ public:
|
||||
static uint256 getRippleStateIndex(const uint160& uiA, const uint160& uiB, const uint160& uCurrency)
|
||||
{ return getRippleStateIndex(RippleAddress::createAccountID(uiA), RippleAddress::createAccountID(uiB), uCurrency); }
|
||||
|
||||
SLE::pointer getRippleState(LedgerStateParms& parms, const uint256& uNode);
|
||||
|
||||
SLE::pointer getRippleState(const uint256& uNode)
|
||||
{
|
||||
LedgerStateParms qry = lepNONE;
|
||||
return getRippleState(qry, uNode);
|
||||
}
|
||||
SLE::pointer getRippleState(const uint256& uNode);
|
||||
|
||||
SLE::pointer getRippleState(const RippleAddress& naA, const RippleAddress& naB, const uint160& uCurrency)
|
||||
{ return getRippleState(getRippleStateIndex(naA, naB, uCurrency)); }
|
||||
|
||||
@@ -71,6 +71,8 @@ void PeerSet::TimerEntry(boost::weak_ptr<PeerSet> wptr, const boost::system::err
|
||||
{
|
||||
if (result == boost::asio::error::operation_aborted)
|
||||
return;
|
||||
|
||||
ScopedLock sl(theApp->getMasterLock());
|
||||
boost::shared_ptr<PeerSet> ptr = wptr.lock();
|
||||
if (ptr)
|
||||
ptr->invokeOnTimer();
|
||||
@@ -920,15 +922,21 @@ void LedgerAcquireMaster::sweep()
|
||||
}
|
||||
}
|
||||
|
||||
int LedgerAcquireMaster::getFetchCount()
|
||||
int LedgerAcquireMaster::getFetchCount(int& timeoutCount)
|
||||
{
|
||||
timeoutCount = 0;
|
||||
int ret = 0;
|
||||
{
|
||||
typedef std::pair<uint256, LedgerAcquire::pointer> u256_acq_pair;
|
||||
boost::mutex::scoped_lock sl(mLock);
|
||||
BOOST_FOREACH(const u256_acq_pair& it, mLedgers)
|
||||
{
|
||||
if (it.second->isActive())
|
||||
{
|
||||
++ret;
|
||||
timeoutCount += it.second->getTimeouts();
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@ public:
|
||||
void dropLedger(const uint256& ledgerHash);
|
||||
SMAddNode gotLedgerData(ripple::TMLedgerData& packet, Peer::ref);
|
||||
|
||||
int getFetchCount();
|
||||
int getFetchCount(int& timeoutCount);
|
||||
void logFailure(const uint256& h) { mRecentFailures.add(h); }
|
||||
bool isFailure(const uint256& h) { return mRecentFailures.isPresent(h, false); }
|
||||
|
||||
|
||||
@@ -107,6 +107,7 @@ LedgerEntryAction LedgerEntrySet::hasEntry(const uint256& index) const
|
||||
|
||||
void LedgerEntrySet::entryCache(SLE::ref sle)
|
||||
{
|
||||
assert(sle->isMutable());
|
||||
std::map<uint256, LedgerEntrySetEntry>::iterator it = mEntries.find(sle->getIndex());
|
||||
if (it == mEntries.end())
|
||||
{
|
||||
@@ -128,6 +129,7 @@ void LedgerEntrySet::entryCache(SLE::ref sle)
|
||||
|
||||
void LedgerEntrySet::entryCreate(SLE::ref sle)
|
||||
{
|
||||
assert(sle->isMutable());
|
||||
std::map<uint256, LedgerEntrySetEntry>::iterator it = mEntries.find(sle->getIndex());
|
||||
if (it == mEntries.end())
|
||||
{
|
||||
@@ -161,6 +163,7 @@ void LedgerEntrySet::entryCreate(SLE::ref sle)
|
||||
|
||||
void LedgerEntrySet::entryModify(SLE::ref sle)
|
||||
{
|
||||
assert(sle->isMutable());
|
||||
std::map<uint256, LedgerEntrySetEntry>::iterator it = mEntries.find(sle->getIndex());
|
||||
if (it == mEntries.end())
|
||||
{
|
||||
@@ -193,6 +196,7 @@ void LedgerEntrySet::entryModify(SLE::ref sle)
|
||||
|
||||
void LedgerEntrySet::entryDelete(SLE::ref sle)
|
||||
{
|
||||
assert(sle->isMutable());
|
||||
std::map<uint256, LedgerEntrySetEntry>::iterator it = mEntries.find(sle->getIndex());
|
||||
if (it == mEntries.end())
|
||||
{
|
||||
@@ -399,7 +403,7 @@ void LedgerEntrySet::calcRawMeta(Serializer& s, TER result, uint32 index)
|
||||
if (type == &sfGeneric)
|
||||
continue;
|
||||
|
||||
SLE::pointer origNode = mLedger->getSLE(it.first);
|
||||
SLE::pointer origNode = mLedger->getSLEi(it.first);
|
||||
SLE::pointer curNode = it.second.mEntry;
|
||||
|
||||
if ((type == &sfModifiedNode) && (*curNode == *origNode))
|
||||
|
||||
@@ -180,7 +180,10 @@ bool LedgerMaster::acquireMissingLedger(Ledger::ref origLedger, const uint256& l
|
||||
}
|
||||
|
||||
if (theApp->getMasterLedgerAcquire().isFailure(ledgerHash))
|
||||
{
|
||||
cLog(lsTRACE) << "Already failed to acquire " << ledgerSeq;
|
||||
return false;
|
||||
}
|
||||
|
||||
mMissingLedger = theApp->getMasterLedgerAcquire().findCreate(ledgerHash);
|
||||
if (mMissingLedger->isComplete())
|
||||
@@ -203,20 +206,28 @@ bool LedgerMaster::acquireMissingLedger(Ledger::ref origLedger, const uint256& l
|
||||
theApp->getIOService().post(boost::bind(&LedgerMaster::missingAcquireComplete, this, mMissingLedger));
|
||||
}
|
||||
|
||||
int fetch = theConfig.getSize(siLedgerFetch);
|
||||
if (theApp->getMasterLedgerAcquire().getFetchCount() < fetch)
|
||||
{
|
||||
int count = 0;
|
||||
typedef std::pair<uint32, uint256> u_pair;
|
||||
int fetchMax = theConfig.getSize(siLedgerFetch);
|
||||
int timeoutCount;
|
||||
int fetchCount = theApp->getMasterLedgerAcquire().getFetchCount(timeoutCount);
|
||||
|
||||
std::vector<u_pair> vec = origLedger->getLedgerHashes();
|
||||
BOOST_REVERSE_FOREACH(const u_pair& it, vec)
|
||||
if (fetchCount < fetchMax)
|
||||
{
|
||||
if (timeoutCount > 2)
|
||||
{
|
||||
if ((count < fetch) && (it.first < ledgerSeq) &&
|
||||
!mCompleteLedgers.hasValue(it.first) && !theApp->getMasterLedgerAcquire().find(it.second))
|
||||
cLog(lsDEBUG) << "Not acquiring due to timeouts";
|
||||
}
|
||||
else
|
||||
{
|
||||
typedef std::pair<uint32, uint256> u_pair;
|
||||
std::vector<u_pair> vec = origLedger->getLedgerHashes();
|
||||
BOOST_REVERSE_FOREACH(const u_pair& it, vec)
|
||||
{
|
||||
++count;
|
||||
theApp->getMasterLedgerAcquire().findCreate(it.second);
|
||||
if ((fetchCount < fetchMax) && (it.first < ledgerSeq) &&
|
||||
!mCompleteLedgers.hasValue(it.first) && !theApp->getMasterLedgerAcquire().find(it.second))
|
||||
{
|
||||
++fetchCount;
|
||||
theApp->getMasterLedgerAcquire().findCreate(it.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -249,7 +260,7 @@ bool LedgerMaster::shouldAcquire(uint32 currentLedger, uint32 ledgerHistory, uin
|
||||
if (candidateLedger >= currentLedger)
|
||||
ret = true;
|
||||
else ret = (currentLedger - candidateLedger) <= ledgerHistory;
|
||||
cLog(lsTRACE) << "Missing ledger " << candidateLedger << (ret ? " will" : " will NOT") << " be acquired";
|
||||
cLog(lsTRACE) << "Missing ledger " << candidateLedger << (ret ? " should" : " should NOT") << " be acquired";
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -362,7 +373,10 @@ void LedgerMaster::setFullLedger(Ledger::ref ledger)
|
||||
if (!mCompleteLedgers.hasValue(ledger->getLedgerSeq() - 1))
|
||||
{
|
||||
if (!shouldAcquire(mCurrentLedger->getLedgerSeq(), theConfig.LEDGER_HISTORY, ledger->getLedgerSeq() - 1))
|
||||
{
|
||||
cLog(lsTRACE) << "Don't need any ledgers";
|
||||
return;
|
||||
}
|
||||
cLog(lsDEBUG) << "We need the ledger before the ledger we just accepted: " << ledger->getLedgerSeq() - 1;
|
||||
acquireMissingLedger(ledger, ledger->getParentHash(), ledger->getLedgerSeq() - 1);
|
||||
}
|
||||
@@ -387,6 +401,8 @@ void LedgerMaster::setFullLedger(Ledger::ref ledger)
|
||||
cLog(lsWARNING) << "We have a gap we can't fix: " << prevMissing + 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
cLog(lsTRACE) << "Shouldn't acquire";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -266,7 +266,7 @@ bool LoadFeeTrack::lowerLocalFee()
|
||||
boost::mutex::scoped_lock sl(mLock);
|
||||
uint32 origFee = mLocalTxnLoadFee;
|
||||
|
||||
mLocalTxnLoadFee -= (mLocalTxnLoadFee / lftFeeDecFraction ); // reduce by 1/16th
|
||||
mLocalTxnLoadFee -= (mLocalTxnLoadFee / lftFeeDecFraction ); // reduce by 1/4
|
||||
|
||||
if (mLocalTxnLoadFee < lftNormalFee)
|
||||
mLocalTxnLoadFee = lftNormalFee;
|
||||
|
||||
@@ -134,7 +134,7 @@ protected:
|
||||
|
||||
static const int lftNormalFee = 256; // 256 is the minimum/normal load factor
|
||||
static const int lftFeeIncFraction = 16; // increase fee by 1/16
|
||||
static const int lftFeeDecFraction = 16; // decrease fee by 1/16
|
||||
static const int lftFeeDecFraction = 4; // decrease fee by 1/4
|
||||
static const int lftFeeMax = lftNormalFee * 1000000;
|
||||
|
||||
uint32 mLocalTxnLoadFee; // Scale factor, lftNormalFee = normal fee
|
||||
|
||||
@@ -455,12 +455,9 @@ AccountState::pointer NetworkOPs::getAccountState(Ledger::ref lrLedger, const Ri
|
||||
|
||||
SLE::pointer NetworkOPs::getGenerator(Ledger::ref lrLedger, const uint160& uGeneratorID)
|
||||
{
|
||||
LedgerStateParms qry = lepNONE;
|
||||
|
||||
if (!lrLedger)
|
||||
return SLE::pointer();
|
||||
else
|
||||
return lrLedger->getGenerator(qry, uGeneratorID);
|
||||
return lrLedger->getGenerator(uGeneratorID);
|
||||
}
|
||||
|
||||
//
|
||||
@@ -475,8 +472,7 @@ STVector256 NetworkOPs::getDirNodeInfo(
|
||||
uint64& uNodeNext)
|
||||
{
|
||||
STVector256 svIndexes;
|
||||
LedgerStateParms lspNode = lepNONE;
|
||||
SLE::pointer sleNode = lrLedger->getDirNode(lspNode, uNodeIndex);
|
||||
SLE::pointer sleNode = lrLedger->getDirNode(uNodeIndex);
|
||||
|
||||
if (sleNode)
|
||||
{
|
||||
@@ -524,8 +520,7 @@ Json::Value NetworkOPs::getOwnerInfo(Ledger::pointer lpLedger, const RippleAddre
|
||||
|
||||
uint256 uRootIndex = lpLedger->getOwnerDirIndex(naAccount.getAccountID());
|
||||
|
||||
LedgerStateParms lspNode = lepNONE;
|
||||
SLE::pointer sleNode = lpLedger->getDirNode(lspNode, uRootIndex);
|
||||
SLE::pointer sleNode = lpLedger->getDirNode(uRootIndex);
|
||||
|
||||
if (sleNode)
|
||||
{
|
||||
@@ -538,7 +533,7 @@ Json::Value NetworkOPs::getOwnerInfo(Ledger::pointer lpLedger, const RippleAddre
|
||||
|
||||
BOOST_FOREACH(const uint256& uDirEntry, vuiIndexes)
|
||||
{
|
||||
SLE::pointer sleCur = lpLedger->getSLE(uDirEntry);
|
||||
SLE::pointer sleCur = lpLedger->getSLEi(uDirEntry);
|
||||
|
||||
switch (sleCur->getType())
|
||||
{
|
||||
@@ -569,9 +564,7 @@ Json::Value NetworkOPs::getOwnerInfo(Ledger::pointer lpLedger, const RippleAddre
|
||||
uNodeDir = sleNode->getFieldU64(sfIndexNext);
|
||||
if (uNodeDir)
|
||||
{
|
||||
lspNode = lepNONE;
|
||||
sleNode = lpLedger->getDirNode(lspNode, Ledger::getDirNodeIndex(uRootIndex, uNodeDir));
|
||||
|
||||
sleNode = lpLedger->getDirNode(Ledger::getDirNodeIndex(uRootIndex, uNodeDir));
|
||||
assert(sleNode);
|
||||
}
|
||||
} while (uNodeDir);
|
||||
@@ -1070,7 +1063,7 @@ std::vector< std::pair<Transaction::pointer, TransactionMetaSet::pointer> >
|
||||
|
||||
std::string sql =
|
||||
str(boost::format("SELECT LedgerSeq,Status,RawTxn,TxnMeta FROM Transactions where TransID in (SELECT TransID from AccountTransactions "
|
||||
" WHERE Account = '%s' AND LedgerSeq <= '%d' AND LedgerSeq >= '%d' LIMIT 1000) ORDER BY LedgerSeq;")
|
||||
" WHERE Account = '%s' AND LedgerSeq <= '%d' AND LedgerSeq >= '%d' LIMIT 200) ORDER BY LedgerSeq DESC;")
|
||||
% account.humanAccountID() % maxLedger % minLedger);
|
||||
|
||||
{
|
||||
@@ -1092,7 +1085,7 @@ std::vector< std::pair<Transaction::pointer, TransactionMetaSet::pointer> >
|
||||
}else rawMeta.resize(metaSize);
|
||||
|
||||
TransactionMetaSet::pointer meta= boost::make_shared<TransactionMetaSet>(txn->getID(), txn->getLedger(), rawMeta.getData());
|
||||
ret.push_back(std::pair<Transaction::ref, TransactionMetaSet::ref>(txn,meta));
|
||||
ret.push_back(std::pair<Transaction::pointer, TransactionMetaSet::pointer>(txn,meta));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1212,7 +1205,16 @@ Json::Value NetworkOPs::getServerInfo(bool human, bool admin)
|
||||
static_cast<double>(Json::UInt(lpClosed->getReserve(0) * baseFee / baseRef)) / SYSTEM_CURRENCY_PARTS;
|
||||
l["reserve_inc_xrp"] =
|
||||
static_cast<double>(Json::UInt(lpClosed->getReserveInc() * baseFee / baseRef)) / SYSTEM_CURRENCY_PARTS;
|
||||
l["age"] = Json::UInt(getCloseTimeNC() - lpClosed->getCloseTimeNC());
|
||||
|
||||
uint32 closeTime = getCloseTimeNC();
|
||||
uint32 lCloseTime = lpClosed->getCloseTimeNC();
|
||||
|
||||
if (lCloseTime <= closeTime)
|
||||
{
|
||||
uint32 age = closeTime - lCloseTime;
|
||||
if (age < 1000000)
|
||||
l["age"] = Json::UInt(age);
|
||||
}
|
||||
}
|
||||
info["closed_ledger"] = l;
|
||||
}
|
||||
|
||||
@@ -165,6 +165,7 @@ public:
|
||||
void setLastValidation(SerializedValidation::ref v) { mLastValidation = v; }
|
||||
|
||||
SLE::pointer getSLE(Ledger::pointer lpLedger, const uint256& uHash) { return lpLedger->getSLE(uHash); }
|
||||
SLE::pointer getSLEi(Ledger::pointer lpLedger, const uint256& uHash) { return lpLedger->getSLEi(uHash); }
|
||||
|
||||
//
|
||||
// Transaction operations
|
||||
|
||||
@@ -39,7 +39,7 @@ void OrderBookDB::setup(Ledger::ref ledger)
|
||||
|
||||
while (currentIndex.isNonZero())
|
||||
{
|
||||
SLE::pointer entry=ledger->getSLE(currentIndex);
|
||||
SLE::pointer entry=ledger->getSLEi(currentIndex);
|
||||
|
||||
OrderBook::pointer book = OrderBook::newOrderBook(entry);
|
||||
if (book)
|
||||
|
||||
@@ -24,11 +24,14 @@ DECLARE_INSTANCE(Peer);
|
||||
// Node has this long to verify its identity from connection accepted or connection attempt.
|
||||
#define NODE_VERIFY_SECONDS 15
|
||||
|
||||
// Idle nodes are probed this often
|
||||
#define NODE_IDLE_SECONDS 120
|
||||
|
||||
Peer::Peer(boost::asio::io_service& io_service, boost::asio::ssl::context& ctx, uint64 peerID, bool inbound) :
|
||||
mInbound(inbound),
|
||||
mHelloed(false),
|
||||
mDetaching(false),
|
||||
mActive(true),
|
||||
mActive(2),
|
||||
mCluster(false),
|
||||
mPeerId(peerID),
|
||||
mSocketSsl(io_service, ctx),
|
||||
@@ -123,6 +126,33 @@ void Peer::detach(const char *rsn)
|
||||
}
|
||||
}
|
||||
|
||||
void Peer::handlePingTimer(const boost::system::error_code& ecResult)
|
||||
{
|
||||
if (ecResult || mDetaching)
|
||||
return;
|
||||
|
||||
if (mActive == 1)
|
||||
{ // ping out
|
||||
detach("pto");
|
||||
return;
|
||||
}
|
||||
|
||||
if (mActive == 0)
|
||||
{ // idle->pingsent
|
||||
mActive = 1;
|
||||
ripple::TMPing packet;
|
||||
packet.set_type(ripple::TMPing::ptPING);
|
||||
sendPacket(boost::make_shared<PackedMessage>(packet, ripple::mtPING));
|
||||
}
|
||||
else // active->idle
|
||||
mActive = 0;
|
||||
|
||||
mActivityTimer.expires_from_now(boost::posix_time::seconds(NODE_IDLE_SECONDS));
|
||||
mActivityTimer.async_wait(boost::bind(&Peer::handlePingTimer, shared_from_this(),
|
||||
boost::asio::placeholders::error));
|
||||
}
|
||||
|
||||
|
||||
void Peer::handleVerifyTimer(const boost::system::error_code& ecResult)
|
||||
{
|
||||
if (ecResult == boost::asio::error::operation_aborted)
|
||||
@@ -631,8 +661,10 @@ void Peer::recvHello(ripple::TMHello& packet)
|
||||
{
|
||||
bool bDetach = true;
|
||||
|
||||
// Cancel verification timeout. - FIXME Start ping/pong timer
|
||||
(void) mActivityTimer.cancel();
|
||||
mActivityTimer.expires_from_now(boost::posix_time::seconds(NODE_IDLE_SECONDS));
|
||||
mActivityTimer.async_wait(boost::bind(&Peer::handlePingTimer, shared_from_this(),
|
||||
boost::asio::placeholders::error));
|
||||
|
||||
uint32 ourTime = theApp->getOPs().getNetworkTimeNC();
|
||||
uint32 minTime = ourTime - 20;
|
||||
@@ -1197,7 +1229,7 @@ void Peer::recvPing(ripple::TMPing& packet)
|
||||
}
|
||||
else if (packet.type() == ripple::TMPing::ptPONG)
|
||||
{
|
||||
mActive = true;
|
||||
mActive = 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ private:
|
||||
bool mClientConnect; // In process of connecting as client.
|
||||
bool mHelloed; // True, if hello accepted.
|
||||
bool mDetaching; // True, if detaching.
|
||||
bool mActive;
|
||||
int mActive; // 0=idle, 1=pingsent, 2=active
|
||||
bool mCluster; // Node in our cluster
|
||||
RippleAddress mNodePublic; // Node public key of peer.
|
||||
std::string mNodeName;
|
||||
@@ -58,6 +58,7 @@ private:
|
||||
|
||||
void handleStart(const boost::system::error_code& ecResult);
|
||||
void handleVerifyTimer(const boost::system::error_code& ecResult);
|
||||
void handlePingTimer(const boost::system::error_code& ecResult);
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
@@ -217,7 +217,7 @@ Json::Value RPCHandler::transactionSign(Json::Value jvRequest, bool bSubmit)
|
||||
if (!txJSON.isMember("Flags")) txJSON["Flags"] = 0;
|
||||
|
||||
Ledger::pointer lpCurrent = mNetOps->getCurrentLedger();
|
||||
SLE::pointer sleAccountRoot = mNetOps->getSLE(lpCurrent, Ledger::getAccountRootIndex(raSrcAddressID.getAccountID()));
|
||||
SLE::pointer sleAccountRoot = mNetOps->getSLEi(lpCurrent, Ledger::getAccountRootIndex(raSrcAddressID.getAccountID()));
|
||||
|
||||
if (!sleAccountRoot)
|
||||
{
|
||||
@@ -899,7 +899,7 @@ Json::Value RPCHandler::doAccountLines(Json::Value jvRequest)
|
||||
if (!lpLedger)
|
||||
return jvResult;
|
||||
|
||||
ScopedUnlock su(theApp->getMasterLock(), lpLedger->isFixed());
|
||||
ScopedUnlock su(theApp->getMasterLock());
|
||||
|
||||
if (!jvRequest.isMember("account"))
|
||||
return rpcError(rpcINVALID_PARAMS);
|
||||
@@ -980,7 +980,7 @@ Json::Value RPCHandler::doAccountOffers(Json::Value jvRequest)
|
||||
if (!lpLedger)
|
||||
return jvResult;
|
||||
|
||||
ScopedUnlock su(theApp->getMasterLock(), lpLedger->isClosed() || lpLedger->isImmutable());
|
||||
ScopedUnlock su(theApp->getMasterLock());
|
||||
|
||||
if (!jvRequest.isMember("account"))
|
||||
return rpcError(rpcINVALID_PARAMS);
|
||||
@@ -1526,6 +1526,7 @@ Json::Value RPCHandler::doLedger(Json::Value jvRequest)
|
||||
|
||||
Json::Value ret(Json::objectValue);
|
||||
|
||||
ScopedUnlock(theApp->getMasterLock());
|
||||
ledger->addJson(ret, full ? LEDGER_JSON_FULL : 0);
|
||||
|
||||
return ret;
|
||||
@@ -2147,7 +2148,7 @@ Json::Value RPCHandler::lookupLedger(Json::Value jvRequest, Ledger::pointer& lpL
|
||||
if (-3 == iLedgerIndex)
|
||||
{ // Last fully-validated ledger
|
||||
lpLedger = mNetOps->getValidatedLedger();
|
||||
iLedgerIndex = lpLedger->getLedgerSeq();
|
||||
iLedgerIndex = lpLedger->getLedgerSeq();
|
||||
}
|
||||
|
||||
if (iLedgerIndex <= 0)
|
||||
@@ -2364,7 +2365,7 @@ Json::Value RPCHandler::doLedgerEntry(Json::Value jvRequest)
|
||||
|
||||
if (!!uNodeIndex)
|
||||
{
|
||||
SLE::pointer sleNode = mNetOps->getSLE(lpLedger, uNodeIndex);
|
||||
SLE::pointer sleNode = mNetOps->getSLEi(lpLedger, uNodeIndex);
|
||||
|
||||
if (!sleNode)
|
||||
{
|
||||
@@ -2833,7 +2834,8 @@ Json::Value RPCHandler::doCommand(const Json::Value& jvRequest, int iRole)
|
||||
return rpcError(rpcNO_PERMISSION);
|
||||
}
|
||||
|
||||
// XXX Need the master lock for getOperatingMode
|
||||
boost::recursive_mutex::scoped_lock sl(theApp->getMasterLock());
|
||||
|
||||
if (commandsA[i].iOptions & optNetwork
|
||||
&& mNetOps->getOperatingMode() != NetworkOPs::omTRACKING
|
||||
&& mNetOps->getOperatingMode() != NetworkOPs::omFULL)
|
||||
@@ -2842,7 +2844,6 @@ Json::Value RPCHandler::doCommand(const Json::Value& jvRequest, int iRole)
|
||||
}
|
||||
// XXX Should verify we have a current ledger.
|
||||
|
||||
boost::recursive_mutex::scoped_lock sl(theApp->getMasterLock());
|
||||
if ((commandsA[i].iOptions & optCurrent) && false)
|
||||
{
|
||||
return rpcError(rpcNO_CURRENT);
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/functional/hash.hpp>
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include <boost/thread/mutex.hpp>
|
||||
#include <boost/unordered_map.hpp>
|
||||
|
||||
#include <openssl/rand.h>
|
||||
|
||||
@@ -293,6 +295,9 @@ uint160 RippleAddress::getAccountID() const
|
||||
}
|
||||
}
|
||||
|
||||
static boost::mutex rncLock;
|
||||
static boost::unordered_map< std::vector<unsigned char>, std::string > rncMap;
|
||||
|
||||
std::string RippleAddress::humanAccountID() const
|
||||
{
|
||||
switch (nVersion) {
|
||||
@@ -300,7 +305,13 @@ std::string RippleAddress::humanAccountID() const
|
||||
throw std::runtime_error("unset source - humanAccountID");
|
||||
|
||||
case VER_ACCOUNT_ID:
|
||||
return ToString();
|
||||
{
|
||||
boost::mutex::scoped_lock sl(rncLock);
|
||||
boost::unordered_map< std::vector<unsigned char>, std::string >::iterator it = rncMap.find(vchData);
|
||||
if (it != rncMap.end())
|
||||
return it->second;
|
||||
return rncMap[vchData] = ToString();
|
||||
}
|
||||
|
||||
case VER_ACCOUNT_PUBLIC:
|
||||
{
|
||||
|
||||
@@ -485,6 +485,17 @@ SHAMapItem::pointer SHAMap::peekItem(const uint256& id, SHAMapTreeNode::TNType&
|
||||
return leaf->peekItem();
|
||||
}
|
||||
|
||||
SHAMapItem::pointer SHAMap::peekItem(const uint256& id, uint256& hash)
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
SHAMapTreeNode* leaf = walkToPointer(id);
|
||||
if (!leaf)
|
||||
return no_item;
|
||||
hash = leaf->getNodeHash();
|
||||
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);
|
||||
|
||||
@@ -406,6 +406,7 @@ public:
|
||||
|
||||
// save a copy if you only need a temporary
|
||||
SHAMapItem::pointer peekItem(const uint256& id);
|
||||
SHAMapItem::pointer peekItem(const uint256& id, uint256& hash);
|
||||
SHAMapItem::pointer peekItem(const uint256& id, SHAMapTreeNode::TNType& type);
|
||||
|
||||
// traverse functions
|
||||
|
||||
@@ -9,7 +9,7 @@ DECLARE_INSTANCE(SerializedLedgerEntry)
|
||||
SETUP_LOG();
|
||||
|
||||
SerializedLedgerEntry::SerializedLedgerEntry(SerializerIterator& sit, const uint256& index)
|
||||
: STObject(sfLedgerEntry), mIndex(index)
|
||||
: STObject(sfLedgerEntry), mIndex(index), mMutable(true)
|
||||
{
|
||||
set(sit);
|
||||
uint16 type = getFieldU16(sfLedgerEntryType);
|
||||
@@ -22,7 +22,7 @@ SerializedLedgerEntry::SerializedLedgerEntry(SerializerIterator& sit, const uint
|
||||
}
|
||||
|
||||
SerializedLedgerEntry::SerializedLedgerEntry(const Serializer& s, const uint256& index)
|
||||
: STObject(sfLedgerEntry), mIndex(index)
|
||||
: STObject(sfLedgerEntry), mIndex(index), mMutable(true)
|
||||
{
|
||||
SerializerIterator sit(const_cast<Serializer&>(s)); // we know 's' isn't going away
|
||||
set(sit);
|
||||
@@ -41,7 +41,7 @@ SerializedLedgerEntry::SerializedLedgerEntry(const Serializer& s, const uint256&
|
||||
}
|
||||
|
||||
SerializedLedgerEntry::SerializedLedgerEntry(LedgerEntryType type, const uint256& index) :
|
||||
STObject(sfLedgerEntry), mIndex(index), mType(type)
|
||||
STObject(sfLedgerEntry), mIndex(index), mType(type), mMutable(true)
|
||||
{
|
||||
mFormat = LedgerEntryFormat::getLgrFormat(type);
|
||||
if (mFormat == NULL) throw std::runtime_error("invalid ledger entry type");
|
||||
@@ -49,6 +49,13 @@ SerializedLedgerEntry::SerializedLedgerEntry(LedgerEntryType type, const uint256
|
||||
setFieldU16(sfLedgerEntryType, static_cast<uint16>(mFormat->t_type));
|
||||
}
|
||||
|
||||
SerializedLedgerEntry::pointer SerializedLedgerEntry::getMutable() const
|
||||
{
|
||||
SerializedLedgerEntry::pointer ret = boost::make_shared<SerializedLedgerEntry>(boost::ref(*this));
|
||||
ret->mMutable = true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string SerializedLedgerEntry::getFullText() const
|
||||
{
|
||||
std::string ret = "\"";
|
||||
|
||||
@@ -18,6 +18,7 @@ protected:
|
||||
uint256 mIndex;
|
||||
LedgerEntryType mType;
|
||||
const LedgerEntryFormat* mFormat;
|
||||
bool mMutable;
|
||||
|
||||
SerializedLedgerEntry* duplicate() const { return new SerializedLedgerEntry(*this); }
|
||||
|
||||
@@ -34,6 +35,10 @@ public:
|
||||
const uint256& getIndex() const { return mIndex; }
|
||||
void setIndex(const uint256& i) { mIndex = i; }
|
||||
|
||||
void setImmutable() { mMutable = false; }
|
||||
bool isMutable() { return mMutable; }
|
||||
SerializedLedgerEntry::pointer getMutable() const;
|
||||
|
||||
LedgerEntryType getType() const { return mType; }
|
||||
uint16 getVersion() const { return getFieldU16(sfLedgerEntryType); }
|
||||
const LedgerEntryFormat* getFormat() { return mFormat; }
|
||||
|
||||
@@ -147,16 +147,18 @@ void STObject::set(const std::vector<SOElement::ref>& type)
|
||||
|
||||
bool STObject::setType(const std::vector<SOElement::ref> &type)
|
||||
{
|
||||
boost::ptr_vector<SerializedType> newData;
|
||||
boost::ptr_vector<SerializedType> newData(type.size());
|
||||
bool valid = true;
|
||||
|
||||
mType.clear();
|
||||
mType.reserve(type.size());
|
||||
|
||||
BOOST_FOREACH(SOElement::ref elem, type)
|
||||
{
|
||||
bool match = false;
|
||||
for (boost::ptr_vector<SerializedType>::iterator it = mData.begin(); it != mData.end(); ++it)
|
||||
if (it->getFName() == elem->e_field)
|
||||
{
|
||||
{ // matching entry, move to new vector
|
||||
match = true;
|
||||
newData.push_back(mData.release(it).release());
|
||||
if ((elem->flags == SOE_DEFAULT) && it->isDefault())
|
||||
@@ -169,7 +171,7 @@ bool STObject::setType(const std::vector<SOElement::ref> &type)
|
||||
}
|
||||
|
||||
if (!match)
|
||||
{
|
||||
{ // no match found
|
||||
if (elem->flags == SOE_REQUIRED)
|
||||
{
|
||||
cLog(lsWARNING) << "setType( " << getFName().getName() << ") invalid missing "
|
||||
@@ -181,18 +183,17 @@ bool STObject::setType(const std::vector<SOElement::ref> &type)
|
||||
|
||||
mType.push_back(elem);
|
||||
}
|
||||
if (mData.size() != 0)
|
||||
{
|
||||
BOOST_FOREACH(const SerializedType& t, mData)
|
||||
|
||||
BOOST_FOREACH(const SerializedType& t, mData)
|
||||
{ // Anything left over must be discardable
|
||||
if (!t.getFName().isDiscardable())
|
||||
{
|
||||
if (!t.getFName().isDiscardable())
|
||||
{
|
||||
cLog(lsWARNING) << "setType( " << getFName().getName() << ") invalid leftover "
|
||||
<< t.getFName().getName();
|
||||
valid = false;
|
||||
}
|
||||
cLog(lsWARNING) << "setType( " << getFName().getName() << ") invalid leftover "
|
||||
<< t.getFName().getName();
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
mData.swap(newData);
|
||||
return valid;
|
||||
}
|
||||
|
||||
@@ -282,22 +282,23 @@ STAccount* STAccount::construct(SerializerIterator& u, SField::ref name)
|
||||
STVector256* STVector256::construct(SerializerIterator& u, SField::ref name)
|
||||
{
|
||||
std::vector<unsigned char> data = u.getVL();
|
||||
std::vector<uint256> value;
|
||||
|
||||
std::auto_ptr<STVector256> vec(new STVector256(name));
|
||||
|
||||
int count = data.size() / (256 / 8);
|
||||
value.reserve(count);
|
||||
vec->mValue.reserve(count);
|
||||
|
||||
unsigned int uStart = 0;
|
||||
for (unsigned int i = 0; i != count; i++)
|
||||
{
|
||||
unsigned int uEnd = uStart+(256/8);
|
||||
|
||||
value.push_back(uint256(std::vector<unsigned char>(data.begin()+uStart, data.begin()+(uStart+32))));
|
||||
|
||||
// This next line could be optimized to construct a default uint256 in the vector and then copy into it
|
||||
vec->mValue.push_back(uint256(std::vector<unsigned char>(data.begin()+uStart, data.begin()+uEnd)));
|
||||
uStart = uEnd;
|
||||
}
|
||||
|
||||
return new STVector256(name, value);
|
||||
return vec.release();
|
||||
}
|
||||
|
||||
void STVector256::add(Serializer& s) const
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "../json/value.h"
|
||||
|
||||
#include <boost/weak_ptr.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
|
||||
#include "WSDoor.h"
|
||||
#include "Application.h"
|
||||
@@ -17,7 +18,7 @@
|
||||
DEFINE_INSTANCE(WebSocketConnection);
|
||||
|
||||
#ifndef WEBSOCKET_PING_FREQUENCY
|
||||
#define WEBSOCKET_PING_FREQUENCY 120
|
||||
#define WEBSOCKET_PING_FREQUENCY (5*60)
|
||||
#endif
|
||||
|
||||
template <typename endpoint_type>
|
||||
@@ -53,7 +54,7 @@ public:
|
||||
|
||||
WSConnection(WSServerHandler<endpoint_type>* wshpHandler, const connection_ptr& cpConnection)
|
||||
: mHandler(wshpHandler), mConnection(cpConnection), mNetwork(theApp->getOPs()),
|
||||
mPingTimer(theApp->getAuxService()), mPinged(false)
|
||||
mPingTimer(cpConnection->get_io_service()), mPinged(false)
|
||||
{
|
||||
mRemoteIP = cpConnection->get_socket().lowest_layer().remote_endpoint().address().to_string();
|
||||
cLog(lsDEBUG) << "Websocket connection from " << mRemoteIP;
|
||||
@@ -62,6 +63,7 @@ public:
|
||||
|
||||
void preDestroy()
|
||||
{ // sever connection
|
||||
mPingTimer.cancel();
|
||||
mConnection.reset();
|
||||
}
|
||||
|
||||
@@ -143,10 +145,10 @@ public:
|
||||
bool onPingTimer()
|
||||
{
|
||||
if (mPinged)
|
||||
return true;
|
||||
return true; // causes connection to close
|
||||
mPinged = true;
|
||||
setPingTimer();
|
||||
return false;
|
||||
return false; // causes ping to be sent
|
||||
}
|
||||
|
||||
void onPong()
|
||||
@@ -154,8 +156,11 @@ public:
|
||||
mPinged = false;
|
||||
}
|
||||
|
||||
static void pingTimer(weak_connection_ptr c, WSServerHandler<endpoint_type>* h)
|
||||
static void pingTimer(weak_connection_ptr c, WSServerHandler<endpoint_type>* h, const boost::system::error_code& e)
|
||||
{
|
||||
if (e)
|
||||
return;
|
||||
|
||||
connection_ptr ptr = c.lock();
|
||||
if (ptr)
|
||||
h->pingTimer(ptr);
|
||||
@@ -164,10 +169,10 @@ public:
|
||||
void setPingTimer()
|
||||
{
|
||||
mPingTimer.expires_from_now(boost::posix_time::seconds(WEBSOCKET_PING_FREQUENCY));
|
||||
mPingTimer.async_wait(boost::bind(&WSConnection<endpoint_type>::pingTimer, mConnection, mHandler));
|
||||
mPingTimer.async_wait(boost::bind(
|
||||
&WSConnection<endpoint_type>::pingTimer, mConnection, mHandler, boost::asio::placeholders::error));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
// vim:ts=4
|
||||
|
||||
@@ -176,7 +176,7 @@ protected:
|
||||
memset(&vchData[0], 0, vchData.size());
|
||||
}
|
||||
|
||||
void SetData(int nVersionIn, std::vector<unsigned char> vchDataIn)
|
||||
void SetData(int nVersionIn, const std::vector<unsigned char>& vchDataIn)
|
||||
{
|
||||
nVersion = nVersionIn;
|
||||
vchData = vchDataIn;
|
||||
@@ -186,7 +186,7 @@ protected:
|
||||
{
|
||||
nVersion = nVersionIn;
|
||||
vchData.resize(nSize);
|
||||
if (!vchData.empty())
|
||||
if (nSize)
|
||||
memcpy(&vchData[0], pdata, nSize);
|
||||
}
|
||||
|
||||
|
||||
104
src/js/orderbook.js
Normal file
104
src/js/orderbook.js
Normal file
@@ -0,0 +1,104 @@
|
||||
// Routines for working with an orderbook.
|
||||
//
|
||||
// Events:
|
||||
|
||||
// var network = require("./network.js");
|
||||
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var Amount = require('./amount').Amount;
|
||||
var UInt160 = require('./uint160').UInt160;
|
||||
var Currency = require('./currency').Currency;
|
||||
|
||||
var extend = require('extend');
|
||||
|
||||
var OrderBook = function (remote,
|
||||
currency_out, issuer_out,
|
||||
currency_in, issuer_in) {
|
||||
var self = this;
|
||||
|
||||
this._remote = remote;
|
||||
this._currency_out = currency_out;
|
||||
this._issuer_out = issuer_out;
|
||||
this._currency_in = currency_in;
|
||||
this._issuer_in = issuer_in;
|
||||
|
||||
this._subs = 0;
|
||||
|
||||
// Ledger entry object
|
||||
// Important: This must never be overwritten, only extend()-ed
|
||||
this._entry = {};
|
||||
|
||||
this.on('newListener', function (type, listener) {
|
||||
if (OrderBook.subscribe_events.indexOf(type) !== -1) {
|
||||
if (!self._subs && 'open' === self._remote._online_state) {
|
||||
self._remote.request_subscribe()
|
||||
.books([self.to_json()])
|
||||
.request();
|
||||
}
|
||||
self._subs += 1;
|
||||
}
|
||||
});
|
||||
|
||||
this.on('removeListener', function (type, listener) {
|
||||
if (OrderBook.subscribe_events.indexOf(type) !== -1) {
|
||||
self._subs -= 1;
|
||||
|
||||
if (!self._subs && 'open' === self._remote._online_state) {
|
||||
self._remote.request_unsubscribe()
|
||||
.books([self.to_json()])
|
||||
.request();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this._remote.on('connect', function () {
|
||||
if (self._subs) {
|
||||
self._remote.request_subscribe()
|
||||
.books([self.to_json()])
|
||||
.request();
|
||||
}
|
||||
});
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
OrderBook.prototype = new EventEmitter;
|
||||
|
||||
/**
|
||||
* List of events that require a remote subscription to the orderbook.
|
||||
*/
|
||||
OrderBook.subscribe_events = ['transaction'];
|
||||
|
||||
OrderBook.prototype.to_json = function ()
|
||||
{
|
||||
var json = {
|
||||
"CurrencyOut": this._currency_out,
|
||||
"CurrencyIn": this._currency_in
|
||||
};
|
||||
|
||||
if (json["CurrencyOut"] !== "XRP") json["IssuerOut"] = this._issuer_out;
|
||||
if (json["CurrencyIn"] !== "XRP") json["IssuerIn"] = this._issuer_in;
|
||||
|
||||
return json;
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether the OrderBook is valid.
|
||||
*
|
||||
* Note: This only checks whether the parameters (currencies and issuer) are
|
||||
* syntactically valid. It does not check anything against the ledger.
|
||||
*/
|
||||
OrderBook.prototype.is_valid = function ()
|
||||
{
|
||||
return (
|
||||
Currency.is_valid(this._currency_in) &&
|
||||
(this._currency_in !== "XRP" && UInt160.is_valid(this._issuer_in)) &&
|
||||
Currency.is_valid(this._currency_out) &&
|
||||
(this._currency_out !== "XRP" && UInt160.is_valid(this._issuer_out)) &&
|
||||
!(this._currency_in === "XRP" && this._currency_out === "XRP")
|
||||
);
|
||||
};
|
||||
|
||||
exports.OrderBook = OrderBook;
|
||||
|
||||
// vim:sw=2:sts=2:ts=8:et
|
||||
@@ -22,6 +22,7 @@ var UInt160 = require('./amount').UInt160;
|
||||
var Transaction = require('./transaction').Transaction;
|
||||
var Account = require('./account').Account;
|
||||
var Meta = require('./meta').Meta;
|
||||
var OrderBook = require('./orderbook').OrderBook;
|
||||
|
||||
var utils = require('./utils');
|
||||
var config = require('./config');
|
||||
@@ -181,6 +182,31 @@ Request.prototype.rt_accounts = function (accounts) {
|
||||
return this.accounts(accounts, true);
|
||||
};
|
||||
|
||||
Request.prototype.books = function (books) {
|
||||
var procBooks = [];
|
||||
|
||||
for (var i = 0, l = books.length; i < l; i++) {
|
||||
var book = books[i];
|
||||
|
||||
var json = {
|
||||
"CurrencyOut": Currency.json_rewrite(book["CurrencyOut"]),
|
||||
"CurrencyIn": Currency.json_rewrite(book["CurrencyIn"])
|
||||
};
|
||||
|
||||
if (json["CurrencyOut"] !== "XRP") {
|
||||
json["IssuerOut"] = UInt160.json_rewrite(book["IssuerOut"]);
|
||||
}
|
||||
if (json["CurrencyIn"] !== "XRP") {
|
||||
json["IssuerIn"] = UInt160.json_rewrite(book["IssuerIn"]);
|
||||
}
|
||||
|
||||
procBooks.push(json);
|
||||
}
|
||||
this.message.books = procBooks;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
//
|
||||
// Remote - access to a remote Ripple server via websocket.
|
||||
//
|
||||
@@ -1036,6 +1062,15 @@ Remote.prototype.account = function (accountId) {
|
||||
return this._accounts[account.to_json()] = account;
|
||||
};
|
||||
|
||||
Remote.prototype.book = function (currency_out, issuer_out,
|
||||
currency_in, issuer_in) {
|
||||
var book = new OrderBook(this,
|
||||
currency_out, issuer_out,
|
||||
currency_in, issuer_in);
|
||||
|
||||
return book;
|
||||
}
|
||||
|
||||
// Return the next account sequence if possible.
|
||||
// <-- undefined or Sequence
|
||||
Remote.prototype.account_seq = function (account, advance) {
|
||||
|
||||
@@ -218,6 +218,11 @@ buster.testCase("Amount", {
|
||||
"Divide EUR by XRP, neg, <1" : function () {
|
||||
buster.assert.equals("-0.05/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", Amount.from_json("-100/EUR/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh").divide(Amount.from_json("2000")).to_text_full());
|
||||
}
|
||||
},
|
||||
"Amount comparisons" : {
|
||||
"10 USD != 100 USD" : function () {
|
||||
buster.refute(Amount.from_json("10/USD/rNDKeo9RrCiRdfsMG8AdoZvNZxHASGzbZL").equals(Amount.from_json("100/USD/rNDKeo9RrCiRdfsMG8AdoZvNZxHASGzbZL")));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user