From 58523b7c1bf68dbd6df94d615f66b09f2316ce2b Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sun, 14 Oct 2012 18:25:29 -0700 Subject: [PATCH 01/38] Add 'get' functions to TMNEThread --- src/TransactionMeta.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/TransactionMeta.h b/src/TransactionMeta.h index e3ac3b6cd..b329fdb61 100644 --- a/src/TransactionMeta.h +++ b/src/TransactionMeta.h @@ -81,6 +81,9 @@ public: virtual void addRaw(Serializer&) const; virtual Json::Value getJson(int) const; + const uint256& getPrevTxID() const { return mPrevTxID; } + uint32 getPrevLgr() const { return mPrevLgrSeq; } + protected: virtual TransactionMetaNodeEntry* duplicate(void) const { return new TMNEThread(*this); } virtual int compare(const TransactionMetaNodeEntry&) const; @@ -175,7 +178,7 @@ public: protected: uint256 mTransactionID; uint32 mLedger; - std::map mNodes; + std::map mNodes; // must be an ordered set public: TransactionMetaSet() : mLedger(0) { ; } From 0e6ea3bb4a94d575fccc7ccaa02c1ac5d5b69408 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sun, 14 Oct 2012 18:25:50 -0700 Subject: [PATCH 02/38] Double check TMNEThread operations. --- src/TransactionMeta.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/TransactionMeta.cpp b/src/TransactionMeta.cpp index 3f687897c..3e0cd4035 100644 --- a/src/TransactionMeta.cpp +++ b/src/TransactionMeta.cpp @@ -195,7 +195,11 @@ bool TransactionMetaNode::thread(const uint256& prevTx, uint32 prevLgr) { BOOST_FOREACH(TransactionMetaNodeEntry& it, mEntries) if (it.getType() == TMSThread) + { + TMNEThread* a = dynamic_cast(&it); + assert(a && (a->getPrevTxID() == prevTx) && (a->getPrevLgr() == prevLgr)); return false; + } addNode(new TMNEThread(prevTx, prevLgr)); return true; } @@ -267,8 +271,7 @@ TransactionMetaSet::TransactionMetaSet(uint32 ledger, const std::vector::iterator it = mNodes.begin(), end = mNodes.end(); - it != end; ++it) + for (std::map::iterator it = mNodes.begin(), end = mNodes.end(); it != end; ++it) it->second.addRaw(s); s.add8(TMNEndOfMetadata); } From bd4ef3598b7350d789dbfd6d8802826c4b7c95a1 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sun, 14 Oct 2012 18:26:38 -0700 Subject: [PATCH 03/38] Clean up saveAcceptedLedger. Add some warning logs. Add 'getSTransaction'. --- src/Ledger.cpp | 155 +++++++++++++++++++++++++++++-------------------- src/Ledger.h | 3 +- 2 files changed, 93 insertions(+), 65 deletions(-) diff --git a/src/Ledger.cpp b/src/Ledger.cpp index cda250cf2..1c3bfb524 100644 --- a/src/Ledger.cpp +++ b/src/Ledger.cpp @@ -225,18 +225,24 @@ bool Ledger::addTransaction(const uint256& txID, const Serializer& txn) { // low-level - just add to table SHAMapItem::pointer item = boost::make_shared(txID, txn.peekData()); if (!mTransactionMap->addGiveItem(item, true, false)) + { + cLog(lsWARNING) << "Attempt to add transaction to ledger that already had it"; return false; + } return true; } bool Ledger::addTransaction(const uint256& txID, const Serializer& txn, const Serializer& md) { // low-level - just add to table - Serializer s(txn.getDataLength() + md.getDataLength() + 64); + Serializer s(txn.getDataLength() + md.getDataLength() + 16); s.addVL(txn.peekData()); s.addVL(md.peekData()); SHAMapItem::pointer item = boost::make_shared(txID, s.peekData()); if (!mTransactionMap->addGiveItem(item, true, true)) + { + cLog(lsFATAL) << "Attempt to add transaction+MD to ledger that already had it"; return false; + } return true; } @@ -273,6 +279,22 @@ Transaction::pointer Ledger::getTransaction(const uint256& transID) const return txn; } +SerializedTransaction::pointer Ledger::getSTransaction(SHAMapItem::ref item, SHAMapTreeNode::TNType type) +{ + SerializerIterator sit(item->peekSerializer()); + + if (type == SHAMapTreeNode::tnTRANSACTION_NM) + return boost::make_shared(boost::ref(sit)); + else if (type == SHAMapTreeNode::tnTRANSACTION_MD) + { + Serializer sTxn(sit.getVL()); + SerializerIterator tSit(sTxn); + return boost::make_shared(boost::ref(tSit)); + } + + return SerializedTransaction::pointer(); +} + bool Ledger::getTransaction(const uint256& txID, Transaction::pointer& txn, TransactionMetaSet::pointer& meta) { SHAMapTreeNode::TNType type; @@ -285,7 +307,7 @@ bool Ledger::getTransaction(const uint256& txID, Transaction::pointer& txn, Tran txn = theApp->getMasterTransaction().fetch(txID, false); meta = TransactionMetaSet::pointer(); if (!txn) - txn = Transaction::sharedTransaction(item->getData(), true); + txn = Transaction::sharedTransaction(item->peekData(), true); } else if (type == SHAMapTreeNode::tnTRANSACTION_MD) { // in tree with metadata @@ -317,8 +339,8 @@ uint256 Ledger::getHash() return(mHash); } -void Ledger::saveAcceptedLedger(Ledger::ref ledger) -{ +void Ledger::saveAcceptedLedger() +{ // can be called in a different thread static boost::format ledgerExists("SELECT LedgerSeq FROM Ledgers where LedgerSeq = %d;"); static boost::format deleteLedger("DELETE FROM Ledgers WHERE LedgerSeq = %d;"); static boost::format AcctTransExists("SELECT LedgerSeq FROM AccountTransactions WHERE TransId = '%s';"); @@ -328,78 +350,83 @@ void Ledger::saveAcceptedLedger(Ledger::ref ledger) "(LedgerHash,LedgerSeq,PrevHash,TotalCoins,ClosingTime,PrevClosingTime,CloseTimeRes,CloseFlags," "AccountSetHash,TransSetHash) VALUES ('%s','%u','%s','%s','%u','%u','%d','%u','%s','%s');"); - ScopedLock sl(theApp->getLedgerDB()->getDBLock()); - if (SQL_EXISTS(theApp->getLedgerDB()->getDB(), boost::str(ledgerExists % ledger->mLedgerSeq))) - theApp->getLedgerDB()->getDB()->executeSQL(boost::str(deleteLedger % ledger->mLedgerSeq)); - theApp->getLedgerDB()->getDB()->executeSQL(boost::str(addLedger % - ledger->getHash().GetHex() % ledger->mLedgerSeq % ledger->mParentHash.GetHex() % - boost::lexical_cast(ledger->mTotCoins) % ledger->mCloseTime % ledger->mParentCloseTime % - ledger->mCloseResolution % ledger->mCloseFlags % - ledger->mAccountHash.GetHex() % ledger->mTransHash.GetHex())); - - // write out dirty nodes - int fc; - while ((fc = ledger->mTransactionMap->flushDirty(256, hotTRANSACTION_NODE, ledger->mLedgerSeq)) > 0) - { cLog(lsINFO) << "Flushed " << fc << " dirty transaction nodes"; } - while ((fc = ledger->mAccountStateMap->flushDirty(256, hotACCOUNT_NODE, ledger->mLedgerSeq)) > 0) - { cLog(lsINFO) << "Flushed " << fc << " dirty state nodes"; } - ledger->disarmDirty(); - - SHAMap& txSet = *ledger->peekTransactionMap(); - Database *db = theApp->getTxnDB()->getDB(); - ScopedLock dbLock = theApp->getTxnDB()->getDBLock(); - db->executeSQL("BEGIN TRANSACTION;"); - for (SHAMapItem::pointer item = txSet.peekFirstItem(); !!item; item = txSet.peekNextItem(item->getTag())) { - SerializedTransaction::pointer txn = theApp->getMasterTransaction().fetch(item, false, ledger->mLedgerSeq); + ScopedLock sl(theApp->getLedgerDB()->getDBLock()); + if (SQL_EXISTS(theApp->getLedgerDB()->getDB(), boost::str(ledgerExists % mLedgerSeq))) + theApp->getLedgerDB()->getDB()->executeSQL(boost::str(deleteLedger % mLedgerSeq)); + theApp->getLedgerDB()->getDB()->executeSQL(boost::str(addLedger % + getHash().GetHex() % mLedgerSeq % mParentHash.GetHex() % + boost::lexical_cast(mTotCoins) % mCloseTime % mParentCloseTime % + mCloseResolution % mCloseFlags % + mAccountHash.GetHex() % mTransHash.GetHex())); - // Make sure transaction is in AccountTransactions. - if (!SQL_EXISTS(db, boost::str(AcctTransExists % item->getTag().GetHex()))) + // write out dirty nodes + int fc; + while ((fc = mTransactionMap->flushDirty(256, hotTRANSACTION_NODE, mLedgerSeq)) > 0) + { cLog(lsINFO) << "Flushed " << fc << " dirty transaction nodes"; } + while ((fc = mAccountStateMap->flushDirty(256, hotACCOUNT_NODE, mLedgerSeq)) > 0) + { cLog(lsINFO) << "Flushed " << fc << " dirty state nodes"; } + disarmDirty(); + + SHAMap& txSet = *peekTransactionMap(); + Database *db = theApp->getTxnDB()->getDB(); + ScopedLock dbLock = theApp->getTxnDB()->getDBLock(); + db->executeSQL("BEGIN TRANSACTION;"); + SHAMapTreeNode::TNType type; + for (SHAMapItem::pointer item = txSet.peekFirstItem(type); !!item; + item = txSet.peekNextItem(item->getTag(), type)) { - // Transaction not in AccountTransactions - std::vector accts = txn->getAffectedAccounts(); + SerializedTransaction::pointer txn = getSTransaction(item, type); + assert(txn); - std::string sql = "INSERT INTO AccountTransactions (TransID, Account, LedgerSeq) VALUES "; - bool first = true; - for (std::vector::iterator it = accts.begin(), end = accts.end(); it != end; ++it) + // Make sure transaction is in AccountTransactions. + if (!SQL_EXISTS(db, boost::str(AcctTransExists % item->getTag().GetHex()))) { - if (!first) - sql += ", ('"; - else + // Transaction not in AccountTransactions + std::vector accts = txn->getAffectedAccounts(); + + std::string sql = "INSERT INTO AccountTransactions (TransID, Account, LedgerSeq) VALUES "; + bool first = true; + for (std::vector::iterator it = accts.begin(), end = accts.end(); it != end; ++it) { - sql += "('"; - first = false; + if (!first) + sql += ", ('"; + else + { + sql += "('"; + first = false; + } + sql += txn->getTransactionID().GetHex(); + sql += "','"; + sql += it->humanAccountID(); + sql += "',"; + sql += boost::lexical_cast(getLedgerSeq()); + sql += ")"; } - sql += txn->getTransactionID().GetHex(); - sql += "','"; - sql += it->humanAccountID(); - sql += "',"; - sql += boost::lexical_cast(ledger->getLedgerSeq()); - sql += ")"; + sql += ";"; + Log(lsTRACE) << "ActTx: " << sql; + db->executeSQL(sql); // may already be in there } - sql += ";"; - Log(lsTRACE) << "ActTx: " << sql; - db->executeSQL(sql); // may already be in there - } - if (SQL_EXISTS(db, boost::str(transExists % txn->getTransactionID().GetHex()))) - { - // In Transactions, update LedgerSeq and Status. - db->executeSQL(boost::str(updateTx - % ledger->getLedgerSeq() - % TXN_SQL_VALIDATED - % txn->getTransactionID().GetHex())); - } - else - { - // Not in Transactions, insert the whole thing.. - db->executeSQL( - txn->getSQLInsertHeader() + txn->getSQL(ledger->getLedgerSeq(), TXN_SQL_VALIDATED) + ";"); + if (SQL_EXISTS(db, boost::str(transExists % txn->getTransactionID().GetHex()))) + { + // In Transactions, update LedgerSeq and Status. + db->executeSQL(boost::str(updateTx + % getLedgerSeq() + % TXN_SQL_VALIDATED + % txn->getTransactionID().GetHex())); + } + else + { + // Not in Transactions, insert the whole thing.. + db->executeSQL( + txn->getSQLInsertHeader() + txn->getSQL(getLedgerSeq(), TXN_SQL_VALIDATED) + ";"); + } } + db->executeSQL("COMMIT TRANSACTION;"); } - db->executeSQL("COMMIT TRANSACTION;"); - theApp->getOPs().pubLedger(ledger); + theApp->getOPs().pubLedger(shared_from_this()); } Ledger::pointer Ledger::getSQL(const std::string& sql) diff --git a/src/Ledger.h b/src/Ledger.h index bf362fb6c..d8dcae482 100644 --- a/src/Ledger.h +++ b/src/Ledger.h @@ -149,6 +149,7 @@ public: 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); + static SerializedTransaction::pointer getSTransaction(SHAMapItem::ref, SHAMapTreeNode::TNType); // high-level functions AccountState::pointer getAccountState(const NewcoinAddress& acctID); @@ -157,7 +158,7 @@ public: SLE::pointer getAccountRoot(const NewcoinAddress& naAccountID); // database functions - static void saveAcceptedLedger(Ledger::ref); + void saveAcceptedLedger(); static Ledger::pointer loadByIndex(uint32 ledgerIndex); static Ledger::pointer loadByHash(const uint256& ledgerHash); From 50ce067d04f0e1e94c4987c963edee1fc3f5a767 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sun, 14 Oct 2012 18:27:19 -0700 Subject: [PATCH 04/38] Whitespace changes. --- src/LedgerEntrySet.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/LedgerEntrySet.cpp b/src/LedgerEntrySet.cpp index b1b4ed750..e804f33c2 100644 --- a/src/LedgerEntrySet.cpp +++ b/src/LedgerEntrySet.cpp @@ -310,7 +310,8 @@ bool LedgerEntrySet::threadTx(const NewcoinAddress& threadTo, Ledger::ref ledger return threadTx(sle, ledger, newMods); } -bool LedgerEntrySet::threadTx(SLE::ref threadTo, Ledger::ref ledger, boost::unordered_map& newMods) +bool LedgerEntrySet::threadTx(SLE::ref threadTo, Ledger::ref ledger, + boost::unordered_map& newMods) { // node = the node that was modified/deleted/created // threadTo = the node that needs to know uint256 prevTxID; @@ -323,7 +324,8 @@ bool LedgerEntrySet::threadTx(SLE::ref threadTo, Ledger::ref ledger, boost::unor return false; } -bool LedgerEntrySet::threadOwners(SLE::ref node, Ledger::ref ledger, boost::unordered_map& newMods) +bool LedgerEntrySet::threadOwners(SLE::ref node, Ledger::ref ledger, + boost::unordered_map& newMods) { // thread new or modified node to owner or owners if (node->hasOneOwner()) // thread to owner's account { From 41e105eddd82c7e6e7502ea7308450ab1d544a2c Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sun, 14 Oct 2012 18:27:26 -0700 Subject: [PATCH 05/38] Whitespace changes. --- src/LedgerHistory.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/LedgerHistory.cpp b/src/LedgerHistory.cpp index 6dd3a9c4a..2c1563d63 100644 --- a/src/LedgerHistory.cpp +++ b/src/LedgerHistory.cpp @@ -36,6 +36,7 @@ void LedgerHistory::addAcceptedLedger(Ledger::pointer ledger) assert(ledger->isAccepted()); assert(ledger->isImmutable()); mLedgersByIndex.insert(std::make_pair(ledger->getLedgerSeq(), ledger)); + boost::thread thread(boost::bind(&Ledger::saveAcceptedLedger, ledger)); thread.detach(); } From 879a763fe2e18acc1558db265bc6212b1327b547 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sun, 14 Oct 2012 18:27:42 -0700 Subject: [PATCH 06/38] Cleanup. --- src/SerializedLedger.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/SerializedLedger.cpp b/src/SerializedLedger.cpp index acc58da3c..1e29a99c2 100644 --- a/src/SerializedLedger.cpp +++ b/src/SerializedLedger.cpp @@ -98,10 +98,12 @@ bool SerializedLedgerEntry::thread(const uint256& txID, uint32 ledgerSeq, uint25 uint256 oldPrevTxID = getFieldH256(sfLastTxnID); Log(lsTRACE) << "Thread Tx:" << txID << " prev:" << oldPrevTxID; if (oldPrevTxID == txID) + { // this transaction is already threaded + assert(getFieldU32(sfLastTxnSeq) == ledgerSeq); return false; + } prevTxID = oldPrevTxID; prevLedgerID = getFieldU32(sfLastTxnSeq); - assert(prevTxID != txID); setFieldH256(sfLastTxnID, txID); setFieldU32(sfLastTxnSeq, ledgerSeq); return true; From 9ff0ab174bffe62a39168f72d7878b1d8dcd67b2 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sun, 14 Oct 2012 18:28:01 -0700 Subject: [PATCH 07/38] Log warnings in various deserialization failure cases. --- src/Serializer.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/Serializer.cpp b/src/Serializer.cpp index 78067feeb..efce6138c 100644 --- a/src/Serializer.cpp +++ b/src/Serializer.cpp @@ -9,6 +9,9 @@ #include #include "key.h" +#include "Log.h" + +SETUP_LOG(); int Serializer::addZeros(size_t uBytes) { @@ -185,7 +188,10 @@ int Serializer::addFieldID(int type, int name) bool Serializer::getFieldID(int& type, int& name, int offset) const { if (!get8(type, offset)) + { + cLog(lsWARNING) << "gFID: unable to get type"; return false; + } name = type & 15; type >>= 4; if (type == 0) @@ -193,14 +199,20 @@ bool Serializer::getFieldID(int& type, int& name, int offset) const if (!get8(type, ++offset)) return false; if ((type == 0) || (type < 16)) + { + cLog(lsWARNING) << "gFID: uncommon type out of range " << type; return false; + } } if (name == 0) { // uncommon name if (!get8(name, ++offset)) return false; if ((name == 0) || (name < 16)) + { + cLog(lsWARNING) << "gFID: uncommon name out of range " << name; return false; + } } return true; } From 5e445994563704c91fe9f8a09329f341aed79ecf Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sun, 14 Oct 2012 18:28:47 -0700 Subject: [PATCH 08/38] Switch to new conditional logging. --- src/SerializedObject.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/SerializedObject.cpp b/src/SerializedObject.cpp index 4aa7d067b..678b1add3 100644 --- a/src/SerializedObject.cpp +++ b/src/SerializedObject.cpp @@ -13,6 +13,8 @@ #include "TransactionFormats.h" #include "SerializedTransaction.h" +SETUP_LOG(); + std::auto_ptr STObject::makeDefaultObject(SerializedTypeID id, SField::ref name) { assert((id == STI_NOTPRESENT) || (id == name.fieldType)); @@ -154,7 +156,7 @@ bool STObject::setType(const std::vector &type) { if (elem->flags != SOE_OPTIONAL) { - Log(lsWARNING) << "setType !valid missing " << elem->e_field.fieldName; + cLog(lsWARNING) << "setType !valid missing " << elem->e_field.fieldName; valid = false; } newData.push_back(makeNonPresentObject(elem->e_field)); @@ -168,7 +170,7 @@ bool STObject::setType(const std::vector &type) { if (!t.getFName().isDiscardable()) { - Log(lsWARNING) << "setType !valid leftover: " << t.getFName().getName(); + cLog(lsWARNING) << "setType !valid leftover: " << t.getFName().getName(); valid = false; } } @@ -214,7 +216,7 @@ bool STObject::set(SerializerIterator& sit, int depth) SField::ref fn = SField::getField(type, field); if (fn.isInvalid()) { - Log(lsWARNING) << "Unknown field: field_type=" << type << ", field_name=" << field; + cLog(lsWARNING) << "Unknown field: field_type=" << type << ", field_name=" << field; throw std::runtime_error("Unknown field"); } giveObject(makeDeserializedObject(fn.fieldType, fn, sit, depth + 1)); @@ -853,7 +855,7 @@ STArray* STArray::construct(SerializerIterator& sit, SField::ref field) SField::ref fn = SField::getField(type, field); if (fn.isInvalid()) { - Log(lsTRACE) << "Unknown field: " << type << "/" << field; + cLog(lsTRACE) << "Unknown field: " << type << "/" << field; throw std::runtime_error("Unknown field"); } @@ -1097,7 +1099,7 @@ std::auto_ptr STObject::parseJson(const Json::Value& object, SField::r NewcoinAddress a; if (!a.setAccountID(strValue)) { - Log(lsINFO) << "Invalid acccount JSON: " << fieldName << ": " << strValue; + cLog(lsINFO) << "Invalid acccount JSON: " << fieldName << ": " << strValue; throw std::runtime_error("Account invalid"); } data.push_back(new STAccount(field, a.getAccountID())); @@ -1163,8 +1165,8 @@ BOOST_AUTO_TEST_CASE( FieldManipulation_test ) if (object1.getSerializer() == object2.getSerializer()) { - Log(lsINFO) << "O1: " << object1.getJson(0); - Log(lsINFO) << "O2: " << object2.getJson(0); + cLog(lsINFO) << "O1: " << object1.getJson(0); + cLog(lsINFO) << "O2: " << object2.getJson(0); BOOST_FAIL("STObject error 4"); } object1.makeFieldAbsent(sfTestH256); From 319c6fbe54637385254b4adf96f953b325973a09 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sun, 14 Oct 2012 21:32:04 -0700 Subject: [PATCH 09/38] Output the hash of a node we can't find when we log it. --- src/SHAMapNodes.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/SHAMapNodes.cpp b/src/SHAMapNodes.cpp index 859b47382..4cc47f649 100644 --- a/src/SHAMapNodes.cpp +++ b/src/SHAMapNodes.cpp @@ -513,9 +513,9 @@ bool SHAMapTreeNode::setChildHash(int m, const uint256 &hash) std::ostream& operator<<(std::ostream& out, const SHAMapMissingNode& mn) { if (mn.getMapType() == smtTRANSACTION) - out << "Missing/TXN(" << mn.getNodeID() << ")"; + out << "Missing/TXN(" << mn.getNodeID() << "/" << mn.getNodeHash() << ")"; else if (mn.getMapType() == smtSTATE) - out << "Missing/STA(" << mn.getNodeID() << ")"; + out << "Missing/STA(" << mn.getNodeID() << "/" << mn.getNodeHash() << ")"; else out << "Missing/" << mn.getNodeID(); return out; From f286621d178f7acb700b6030a58271faa02e2917 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sun, 14 Oct 2012 21:32:23 -0700 Subject: [PATCH 10/38] Add 'walkLedger' function. --- src/Ledger.cpp | 8 +++++++- src/Ledger.h | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Ledger.cpp b/src/Ledger.cpp index 1c3bfb524..ae4e18d8b 100644 --- a/src/Ledger.cpp +++ b/src/Ledger.cpp @@ -957,6 +957,12 @@ uint256 Ledger::getRippleStateIndex(const NewcoinAddress& naA, const NewcoinAddr return s.getSHA512Half(); } - +bool Ledger::walkLedger() +{ + std::vector missingNodes; + mAccountStateMap->walkMap(missingNodes, 1); + mTransactionMap->walkMap(missingNodes, 1); + return missingNodes.empty(); +} // vim:ts=4 diff --git a/src/Ledger.h b/src/Ledger.h index d8dcae482..0e7eaf060 100644 --- a/src/Ledger.h +++ b/src/Ledger.h @@ -280,6 +280,8 @@ public: void addJson(Json::Value&, int options); + bool walkLedger(); + static bool unitTest(); }; From 9f97307f999ff4aa4527ac3f7266ad70f9812664 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sun, 14 Oct 2012 21:32:38 -0700 Subject: [PATCH 11/38] Add 'walkMap' function. --- src/SHAMap.h | 2 ++ src/SHAMapDiff.cpp | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/src/SHAMap.h b/src/SHAMap.h index f144b5ca9..079ddf0c0 100644 --- a/src/SHAMap.h +++ b/src/SHAMap.h @@ -400,6 +400,8 @@ public: static std::vector checkTrustedPath(const uint256& ledgerHash, const uint256& leafIndex, const std::list >& path); + void walkMap(std::vector& missingNodes, int maxMissing); + bool deepCompare(SHAMap& other); virtual void dump(bool withHashes = false); }; diff --git a/src/SHAMapDiff.cpp b/src/SHAMapDiff.cpp index d0e78e8f9..05619df21 100644 --- a/src/SHAMapDiff.cpp +++ b/src/SHAMapDiff.cpp @@ -186,3 +186,38 @@ bool SHAMap::compare(SHAMap::ref otherMap, SHAMapDiff& differences, int maxCount return true; } + +void SHAMap::walkMap(std::vector& missingNodes, int maxMissing) +{ + std::stack nodeStack; + + boost::recursive_mutex::scoped_lock sl(mLock); + + if (!root->isInner()) // root is only node, and we have it + return; + + nodeStack.push(root); + + while (!nodeStack.empty()) + { + SHAMapTreeNode::pointer node = nodeStack.top(); + nodeStack.pop(); + + for (int i = 0; i < 16; ++i) + if (!node->isEmptyBranch(i)) + { + try + { + SHAMapTreeNode::pointer d = getNode(node->getChildNodeID(i), node->getChildHash(i), false); + if (d->isInner()) + nodeStack.push(d); + } + catch (SHAMapMissingNode& n) + { + missingNodes.push_back(n); + if (--maxMissing <= 0) + return; + } + } + } +} From 032ce72d009bf3e61963d002a6227d80d0fa1faa Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sun, 14 Oct 2012 21:32:52 -0700 Subject: [PATCH 12/38] Improve logging. Walk the initial ledger. --- src/Application.cpp | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/Application.cpp b/src/Application.cpp index 236f46ebf..72fe6ca1b 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -17,6 +17,8 @@ #include #include +SETUP_LOG(); + Application* theApp = NULL; DatabaseCon::DatabaseCon(const std::string& strName, const char *initStrings[], int initCount) @@ -56,7 +58,7 @@ void Application::stop() mValidations.flush(); mAuxService.stop(); - Log(lsINFO) << "Stopped: " << mIOService.stopped(); + cLog(lsINFO) << "Stopped: " << mIOService.stopped(); } static void InitDB(DatabaseCon** dbCon, const char *fileName, const char *dbInit[], int dbCount) @@ -90,12 +92,12 @@ void Application::run() if (theConfig.START_UP == Config::FRESH) { - Log(lsINFO) << "Starting new Ledger"; + cLog(lsINFO) << "Starting new Ledger"; startNewLedger(); } else if (theConfig.START_UP == Config::LOAD) { - Log(lsINFO) << "Loading Old Ledger"; + cLog(lsINFO) << "Loading Old Ledger"; loadOldLedger(); } else if (theConfig.START_UP == Config::NETWORK) @@ -155,7 +157,7 @@ void Application::run() if (theConfig.RUN_STANDALONE) { - Log(lsWARNING) << "Running in standalone mode"; + cLog(lsWARNING) << "Running in standalone mode"; mNetOps.setStandAlone(); } else @@ -185,8 +187,8 @@ void Application::startNewLedger() NewcoinAddress rootAddress = NewcoinAddress::createAccountPublic(rootGeneratorMaster, 0); // Print enough information to be able to claim root account. - Log(lsINFO) << "Root master seed: " << rootSeedMaster.humanSeed(); - Log(lsINFO) << "Root account: " << rootAddress.humanAccountID(); + cLog(lsINFO) << "Root master seed: " << rootSeedMaster.humanSeed(); + cLog(lsINFO) << "Root account: " << rootAddress.humanAccountID(); { Ledger::pointer firstLedger = boost::make_shared(rootAddress, SYSTEM_CURRENCY_START); @@ -218,13 +220,21 @@ void Application::loadOldLedger() } lastLedger->setClosed(); + cLog(lsINFO) << "Loading ledger " << lastLedger->getHash() << " seq:" << lastLedger->getLedgerSeq(); + + if (!lastLedger->walkLedger()) + { + cLog(lsFATAL) << "Ledger is missing nodes."; + exit(-1); + } + Ledger::pointer openLedger = boost::make_shared(false, boost::ref(*lastLedger)); mMasterLedger.switchLedgers(lastLedger, openLedger); mNetOps.setLastCloseTime(lastLedger->getCloseTimeNC()); } catch (SHAMapMissingNode& mn) { - Log(lsFATAL) << "Cannot load ledger. " << mn; + cLog(lsFATAL) << "Cannot load ledger. " << mn; exit(-1); } } From 573c601273650bfa6249cbf609e412aefd919901 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sun, 14 Oct 2012 21:33:03 -0700 Subject: [PATCH 13/38] Extra debug --- src/SHAMap.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/SHAMap.cpp b/src/SHAMap.cpp index 09877fa40..5da32ce2f 100644 --- a/src/SHAMap.cpp +++ b/src/SHAMap.cpp @@ -686,7 +686,10 @@ void SHAMapItem::dump() SHAMapTreeNode::pointer SHAMap::fetchNodeExternal(const SHAMapNode& id, const uint256& hash) { if (!theApp->running()) + { + cLog(lsTRACE) << "Trying to fetch external node with application not running"; throw SHAMapMissingNode(mType, id, hash); + } HashedObject::pointer obj(theApp->getHashedObjectStore().retrieve(hash)); if (!obj) @@ -713,6 +716,7 @@ SHAMapTreeNode::pointer SHAMap::fetchNodeExternal(const SHAMapNode& id, const ui void SHAMap::fetchRoot(const uint256& hash) { + cLog(lsTRACE) << "Trying to fetch root SHAMap node " << hash; root = fetchNodeExternal(SHAMapNode(), hash); root->makeInner(); mTNByID[*root] = root; From b3268529545a6ad8ff6ea3725cfbec8f4c62b4db Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sun, 14 Oct 2012 22:40:48 -0700 Subject: [PATCH 14/38] Return true from canonicalize even if we refreshed the data. --- src/TaggedCache.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TaggedCache.h b/src/TaggedCache.h index 62a9e699d..cf1b55bab 100644 --- a/src/TaggedCache.h +++ b/src/TaggedCache.h @@ -169,7 +169,7 @@ bool TaggedCache::canonicalize(const key_type& key, boost::shared { // in map, but expired. Update in map, insert in cache mit->second = data; mCache.insert(std::make_pair(key, std::make_pair(time(NULL), data))); - return false; + return true; } // in map and cache, canonicalize From de5321e51c8dfd5259bdd93327725d3d3eca581b Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sun, 14 Oct 2012 23:03:23 -0700 Subject: [PATCH 15/38] Cleanups. --- src/SHAMapNodes.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/SHAMapNodes.cpp b/src/SHAMapNodes.cpp index 4cc47f649..db3568675 100644 --- a/src/SHAMapNodes.cpp +++ b/src/SHAMapNodes.cpp @@ -347,9 +347,11 @@ bool SHAMapTreeNode::updateHash() { nh = Serializer::getPrefixHash(sHP_TransactionNode, mItem->peekData()); } - else assert(false); + else + assert(false); - if (nh == mHash) return false; + if (nh == mHash) + return false; mHash = nh; return true; } @@ -357,10 +359,12 @@ bool SHAMapTreeNode::updateHash() void SHAMapTreeNode::addRaw(Serializer& s, SHANodeFormat format) { assert((format == snfPREFIX) || (format == snfWIRE)); - if (mType == tnERROR) throw std::runtime_error("invalid I node type"); + if (mType == tnERROR) + throw std::runtime_error("invalid I node type"); if (mType == tnINNER) { + assert(getBranchCount() != 0); if (format == snfPREFIX) { s.add32(sHP_InnerNode); From 9e84bb38a8ca9f2e3ce9d7045f74634685657f1a Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sun, 14 Oct 2012 23:14:17 -0700 Subject: [PATCH 16/38] Add new 'isEmpty' function to find empty inner nodes. --- src/SHAMap.h | 1 + src/SHAMapNodes.cpp | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/SHAMap.h b/src/SHAMap.h index 079ddf0c0..6d579a918 100644 --- a/src/SHAMap.h +++ b/src/SHAMap.h @@ -195,6 +195,7 @@ public: bool isInnerNode() const { return !mItem; } bool setChildHash(int m, const uint256& hash); bool isEmptyBranch(int m) const { return !mHashes[m]; } + bool isEmpty() const; int getBranchCount() const; void makeInner(); const uint256& getChildHash(int m) const diff --git a/src/SHAMapNodes.cpp b/src/SHAMapNodes.cpp index db3568675..12f4c956e 100644 --- a/src/SHAMapNodes.cpp +++ b/src/SHAMapNodes.cpp @@ -364,7 +364,7 @@ void SHAMapTreeNode::addRaw(Serializer& s, SHANodeFormat format) if (mType == tnINNER) { - assert(getBranchCount() != 0); + assert(!isEmpty()); if (format == snfPREFIX) { s.add32(sHP_InnerNode); @@ -453,6 +453,14 @@ SHAMapItem::pointer SHAMapTreeNode::getItem() const return boost::make_shared(*mItem); } +bool SHAMapTreeNode::isEmpty() const +{ + assert(isInner()); + for (int i = 0; i < 16; ++i) + if (mHashes[i].isNonZero()) return false; + return true; +} + int SHAMapTreeNode::getBranchCount() const { assert(isInner()); From 1005313ed841638b8e36d61f7fd3f61741708389 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sun, 14 Oct 2012 23:25:15 -0700 Subject: [PATCH 17/38] Make sure to write the root node of the genesis ledger to the database. --- src/Ledger.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Ledger.cpp b/src/Ledger.cpp index ae4e18d8b..c5000e068 100644 --- a/src/Ledger.cpp +++ b/src/Ledger.cpp @@ -31,11 +31,12 @@ Ledger::Ledger(const NewcoinAddress& masterID, uint64 startAmount) : mTotCoins(s AccountState::pointer startAccount = boost::make_shared(masterID); startAccount->peekSLE().setFieldAmount(sfBalance, startAmount); startAccount->peekSLE().setFieldU32(sfSequence, 1); + cLog(lsTRACE) << "root account: " << startAccount->peekSLE().getJson(0); + + mAccountStateMap->armDirty(); writeBack(lepCREATE, startAccount->getSLE()); -#if 0 - std::cerr << "Root account:"; - startAccount->dump(); -#endif + mAccountStateMap->flushDirty(256, hotACCOUNT_NODE, mLedgerSeq); + mAccountStateMap->disarmDirty(); } Ledger::Ledger(const uint256 &parentHash, const uint256 &transHash, const uint256 &accountHash, From ce24ae1ef9f7b35eb7c69d0f535fcdb0f30b0a5a Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sun, 14 Oct 2012 23:25:46 -0700 Subject: [PATCH 18/38] Don't store the temp root node (that represents an empty map) to the db. Improve fetchRoot logging. --- src/SHAMap.cpp | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/SHAMap.cpp b/src/SHAMap.cpp index 5da32ce2f..1bd61d5c6 100644 --- a/src/SHAMap.cpp +++ b/src/SHAMap.cpp @@ -230,11 +230,16 @@ void SHAMap::returnNode(SHAMapTreeNode::pointer& node, bool modify) assert(node->getSeq() <= mSeq); if (node && modify && (node->getSeq() != mSeq)) { // have a CoW - if (mDirtyNodes) (*mDirtyNodes)[*node] = node; + if (mDirtyNodes) + { // don't save an empty root + if (!node->isRoot() || !node->isEmpty()) + (*mDirtyNodes)[*node] = node; + } node = boost::make_shared(*node, mSeq); assert(node->isValid()); mTNByID[*node] = node; - if (node->isRoot()) root = node; + if (node->isRoot()) + root = node; } } @@ -716,7 +721,15 @@ SHAMapTreeNode::pointer SHAMap::fetchNodeExternal(const SHAMapNode& id, const ui void SHAMap::fetchRoot(const uint256& hash) { - cLog(lsTRACE) << "Trying to fetch root SHAMap node " << hash; + if (sLog(lsTRACE)) + { + if (mType == smtTRANSACTION) + Log(lsTRACE) << "Fetch root TXN node " << hash; + else if (mType == smtSTATE) + Log(lsTRACE) << "Fetch root STATE node " << hash; + else + Log(lsTRACE) << "Fetch root SHAMap node " << hash; + } root = fetchNodeExternal(SHAMapNode(), hash); root->makeInner(); mTNByID[*root] = root; From 1c9573a97234442f4096847fb985c0273a40fc8c Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Sun, 14 Oct 2012 23:26:32 -0700 Subject: [PATCH 19/38] Clean up the writer thread. --- src/HashedObject.cpp | 73 +++++++++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 31 deletions(-) diff --git a/src/HashedObject.cpp b/src/HashedObject.cpp index 25bff6626..381df79df 100644 --- a/src/HashedObject.cpp +++ b/src/HashedObject.cpp @@ -21,7 +21,11 @@ bool HashedObjectStore::store(HashedObjectType type, uint32 index, const std::vector& data, const uint256& hash) { // return: false=already in cache, true = added to cache assert(hash == Serializer::getSHA512Half(data)); - if (!theApp->getHashNodeDB()) return true; + if (!theApp->getHashNodeDB()) + { + cLog(lsTRACE) << "HOS: no db"; + return true; + } if (mCache.touch(hash)) { cLog(lsTRACE) << "HOS: " << hash << " store: incache"; @@ -48,42 +52,49 @@ void HashedObjectStore::bulkWrite() std::vector< boost::shared_ptr > set; set.reserve(128); + do { - boost::recursive_mutex::scoped_lock sl(mWriteMutex); - mWriteSet.swap(set); - mWritePending = false; - } - cLog(lsINFO) << "HOS: BulkWrite " << set.size(); - - static boost::format fExists("SELECT ObjType FROM CommittedObjects WHERE Hash = '%s';"); - static boost::format - fAdd("INSERT INTO CommittedObjects (Hash,ObjType,LedgerIndex,Object) VALUES ('%s','%c','%u',%s);"); - - Database* db = theApp->getHashNodeDB()->getDB(); - ScopedLock sl = theApp->getHashNodeDB()->getDBLock(); - - db->executeSQL("BEGIN TRANSACTION;"); - - BOOST_FOREACH(const boost::shared_ptr& it, set) - { - if (!SQL_EXISTS(db, boost::str(fExists % it->getHash().GetHex()))) { - char type; - switch(it->getType()) + boost::recursive_mutex::scoped_lock sl(mWriteMutex); + mWriteSet.swap(set); + if (set.empty()) { - case hotLEDGER: type= 'L'; break; - case hotTRANSACTION: type = 'T'; break; - case hotACCOUNT_NODE: type = 'A'; break; - case hotTRANSACTION_NODE: type = 'N'; break; - default: type = 'U'; + mWritePending = false; + return; } - std::string rawData; - db->escape(&(it->getData().front()), it->getData().size(), rawData); - db->executeSQL(boost::str(fAdd % it->getHash().GetHex() % type % it->getIndex() % rawData )); } - } + cLog(lsINFO) << "HOS: BulkWrite " << set.size(); - db->executeSQL("END TRANSACTION;"); + static boost::format fExists("SELECT ObjType FROM CommittedObjects WHERE Hash = '%s';"); + static boost::format + fAdd("INSERT INTO CommittedObjects (Hash,ObjType,LedgerIndex,Object) VALUES ('%s','%c','%u',%s);"); + + Database* db = theApp->getHashNodeDB()->getDB(); + ScopedLock sl = theApp->getHashNodeDB()->getDBLock(); + + db->executeSQL("BEGIN TRANSACTION;"); + + BOOST_FOREACH(const boost::shared_ptr& it, set) + { + if (!SQL_EXISTS(db, boost::str(fExists % it->getHash().GetHex()))) + { + char type; + switch(it->getType()) + { + case hotLEDGER: type = 'L'; break; + case hotTRANSACTION: type = 'T'; break; + case hotACCOUNT_NODE: type = 'A'; break; + case hotTRANSACTION_NODE: type = 'N'; break; + default: type = 'U'; + } + std::string rawData; + db->escape(&(it->getData().front()), it->getData().size(), rawData); + db->executeSQL(boost::str(fAdd % it->getHash().GetHex() % type % it->getIndex() % rawData )); + } + } + + db->executeSQL("END TRANSACTION;"); + } while(1); } HashedObject::pointer HashedObjectStore::retrieve(const uint256& hash) From 353bf532e454e059d8b85e5ff4e40c86486a38f0 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Mon, 15 Oct 2012 00:45:10 -0700 Subject: [PATCH 20/38] Remove two FIXME's that have been fied. --- src/Ledger.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ledger.cpp b/src/Ledger.cpp index c5000e068..3af3c8838 100644 --- a/src/Ledger.cpp +++ b/src/Ledger.cpp @@ -641,7 +641,7 @@ LedgerStateParms Ledger::writeBack(LedgerStateParms parms, SLE::ref entry) if (create) { assert(!mAccountStateMap->hasItem(entry->getIndex())); - if(!mAccountStateMap->addGiveItem(item, false, false)) // FIXME: TX metadata + if(!mAccountStateMap->addGiveItem(item, false, false)) { assert(false); return lepERROR; @@ -649,7 +649,7 @@ LedgerStateParms Ledger::writeBack(LedgerStateParms parms, SLE::ref entry) return lepCREATED; } - if (!mAccountStateMap->updateGiveItem(item, false, false)) // FIXME: TX metadata + if (!mAccountStateMap->updateGiveItem(item, false, false)) { assert(false); return lepERROR; From 3872cbbca0fdb9bd06ed146c4e29b548374126d4 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Mon, 15 Oct 2012 00:45:57 -0700 Subject: [PATCH 21/38] Fix the dirty node tracking logic --- src/SHAMap.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/SHAMap.cpp b/src/SHAMap.cpp index 1bd61d5c6..2b6460405 100644 --- a/src/SHAMap.cpp +++ b/src/SHAMap.cpp @@ -230,12 +230,9 @@ void SHAMap::returnNode(SHAMapTreeNode::pointer& node, bool modify) assert(node->getSeq() <= mSeq); if (node && modify && (node->getSeq() != mSeq)) { // have a CoW - if (mDirtyNodes) - { // don't save an empty root - if (!node->isRoot() || !node->isEmpty()) - (*mDirtyNodes)[*node] = node; - } node = boost::make_shared(*node, mSeq); + if (mDirtyNodes) + (*mDirtyNodes)[*node] = node; assert(node->isValid()); mTNByID[*node] = node; if (node->isRoot()) @@ -554,7 +551,7 @@ bool SHAMap::delItem(const uint256& id) return true; } -bool SHAMap::addGiveItem(const SHAMapItem::pointer& item, bool isTransaction, bool hasMeta) +bool SHAMap::addGiveItem(SHAMapItem::ref item, bool isTransaction, bool hasMeta) { // add the specified item, does not update #ifdef ST_DEBUG std::cerr << "aGI " << item->getTag() << std::endl; @@ -652,7 +649,7 @@ bool SHAMap::addItem(const SHAMapItem& i, bool isTransaction, bool hasMetaData) return addGiveItem(boost::make_shared(i), isTransaction, hasMetaData); } -bool SHAMap::updateGiveItem(const SHAMapItem::pointer& item, bool isTransaction, bool hasMeta) +bool SHAMap::updateGiveItem(SHAMapItem::ref item, bool isTransaction, bool hasMeta) { // can't change the tag but can change the hash uint256 tag = item->getTag(); From db0d9447cd18db2fc799a3537ec347bdc46e7bb7 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Mon, 15 Oct 2012 00:46:08 -0700 Subject: [PATCH 22/38] Write out objects sooner so we're more likely to have them after a crash. --- src/HashedObject.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/HashedObject.cpp b/src/HashedObject.cpp index 381df79df..bfa3d7ee1 100644 --- a/src/HashedObject.cpp +++ b/src/HashedObject.cpp @@ -35,15 +35,18 @@ bool HashedObjectStore::store(HashedObjectType type, uint32 index, HashedObject::pointer object = boost::make_shared(type, index, data, hash); if (!mCache.canonicalize(hash, object)) { +// cLog(lsTRACE) << "Queuing write for " << hash; boost::recursive_mutex::scoped_lock sl(mWriteMutex); mWriteSet.push_back(object); - if (!mWritePending && (mWriteSet.size() >= 64)) + if (!mWritePending) { mWritePending = true; boost::thread t(boost::bind(&HashedObjectStore::bulkWrite, this)); t.detach(); } } +// else +// cLog(lsTRACE) << "HOS: already had " << hash; return true; } From d273ac6e76113fc39f7142f9243ca98d0b7f2d39 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Mon, 15 Oct 2012 03:04:56 -0700 Subject: [PATCH 23/38] Fix a bug that could prevent a close time consensus from being recognized. --- src/LedgerConsensus.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/LedgerConsensus.cpp b/src/LedgerConsensus.cpp index e8e9e0685..31e64bffb 100644 --- a/src/LedgerConsensus.cpp +++ b/src/LedgerConsensus.cpp @@ -58,7 +58,6 @@ void TransactionAcquire::trigger(Peer::ref peer, bool timer) } if (!mHaveRoot) { - cLog(lsINFO) << "have no root"; ripple::TMGetLedger tmGL; tmGL.set_ledgerhash(mHash.begin(), mHash.size()); tmGL.set_itype(ripple::liTS_CANDIDATE); @@ -675,7 +674,7 @@ void LedgerConsensus::updateOurPositions() for (std::map::iterator it = closeTimes.begin(), end = closeTimes.end(); it != end; ++it) { cLog(lsINFO) << "CCTime: " << it->first << " has " << it->second << ", " << thresh << " required"; - if (it->second > thresh) + if (it->second >= thresh) { cLog(lsINFO) << "Close time consensus reached: " << it->first; mHaveCloseTimeConsensus = true; From 357245b5954c28e84bec1c89467680995428945b Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Mon, 15 Oct 2012 03:08:42 -0700 Subject: [PATCH 24/38] Fix a bug that would cause a write spinout. --- src/HashedObject.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/HashedObject.cpp b/src/HashedObject.cpp index bfa3d7ee1..ca0becbef 100644 --- a/src/HashedObject.cpp +++ b/src/HashedObject.cpp @@ -52,11 +52,10 @@ bool HashedObjectStore::store(HashedObjectType type, uint32 index, void HashedObjectStore::bulkWrite() { - std::vector< boost::shared_ptr > set; - set.reserve(128); - - do { + std::vector< boost::shared_ptr > set; + set.reserve(128); + { boost::recursive_mutex::scoped_lock sl(mWriteMutex); mWriteSet.swap(set); @@ -66,7 +65,6 @@ void HashedObjectStore::bulkWrite() return; } } - cLog(lsINFO) << "HOS: BulkWrite " << set.size(); static boost::format fExists("SELECT ObjType FROM CommittedObjects WHERE Hash = '%s';"); static boost::format @@ -125,7 +123,7 @@ HashedObject::pointer HashedObjectStore::retrieve(const uint256& hash) if (!db->executeSQL(sql) || !db->startIterRows()) { - cLog(lsTRACE) << "HOS: " << hash << " fetch: not in db"; +// cLog(lsTRACE) << "HOS: " << hash << " fetch: not in db"; return HashedObject::pointer(); } From 633cf60836048c6e140210ab6e2c4bf6ee24c920 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Mon, 15 Oct 2012 03:13:42 -0700 Subject: [PATCH 25/38] Remove unneeded debug --- src/SHAMap.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SHAMap.cpp b/src/SHAMap.cpp index 2b6460405..ddcb11bb0 100644 --- a/src/SHAMap.cpp +++ b/src/SHAMap.cpp @@ -696,7 +696,7 @@ SHAMapTreeNode::pointer SHAMap::fetchNodeExternal(const SHAMapNode& id, const ui HashedObject::pointer obj(theApp->getHashedObjectStore().retrieve(hash)); if (!obj) { - Log(lsTRACE) << "fetchNodeExternal: missing " << hash; +// Log(lsTRACE) << "fetchNodeExternal: missing " << hash; throw SHAMapMissingNode(mType, id, hash); } assert(Serializer::getSHA512Half(obj->getData()) == hash); From 030910594884a6108ac6951b548fb08cc9c91533 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Mon, 15 Oct 2012 03:13:51 -0700 Subject: [PATCH 26/38] Cleanup --- src/HashedObject.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/HashedObject.cpp b/src/HashedObject.cpp index ca0becbef..41126fd1a 100644 --- a/src/HashedObject.cpp +++ b/src/HashedObject.cpp @@ -52,8 +52,10 @@ bool HashedObjectStore::store(HashedObjectType type, uint32 index, void HashedObjectStore::bulkWrite() { + std::vector< boost::shared_ptr > set; + while (1) { - std::vector< boost::shared_ptr > set; + set.clear(); set.reserve(128); { @@ -65,6 +67,7 @@ void HashedObjectStore::bulkWrite() return; } } + cLog(lsINFO) << "HOS: writing " << set.size(); static boost::format fExists("SELECT ObjType FROM CommittedObjects WHERE Hash = '%s';"); static boost::format @@ -95,7 +98,7 @@ void HashedObjectStore::bulkWrite() } db->executeSQL("END TRANSACTION;"); - } while(1); + } } HashedObject::pointer HashedObjectStore::retrieve(const uint256& hash) From 33017a35a8c048cac90ae3a4479697d5f58a109d Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Mon, 15 Oct 2012 03:47:23 -0700 Subject: [PATCH 27/38] Extra sanity checks. --- src/Peer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Peer.cpp b/src/Peer.cpp index 8fdeea74b..05a85da62 100644 --- a/src/Peer.cpp +++ b/src/Peer.cpp @@ -1056,7 +1056,7 @@ void Peer::recvGetLedger(ripple::TMGetLedger& packet) { // new-style root request cLog(lsINFO) << "Ledger root w/map roots request"; SHAMap::pointer map = ledger->peekAccountStateMap(); - if (map) + if (map && map->getHash().isNonZero()) { // return account state root node if possible Serializer rootNode(768); if (map->getRootNode(rootNode, snfWIRE)) @@ -1065,7 +1065,7 @@ void Peer::recvGetLedger(ripple::TMGetLedger& packet) if (ledger->getTransHash().isNonZero()) { map = ledger->peekTransactionMap(); - if (map) + if (map && map->getHash().isNonZero()) { rootNode.resize(0); if (map->getRootNode(rootNode, snfWIRE)) From 1214be1f22f04b09e445280867cda7f78ecc48bd Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Mon, 15 Oct 2012 03:48:31 -0700 Subject: [PATCH 28/38] Remove some chatty debug. --- src/HashedObject.cpp | 2 +- src/SHAMap.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/HashedObject.cpp b/src/HashedObject.cpp index 41126fd1a..837a518a6 100644 --- a/src/HashedObject.cpp +++ b/src/HashedObject.cpp @@ -67,7 +67,7 @@ void HashedObjectStore::bulkWrite() return; } } - cLog(lsINFO) << "HOS: writing " << set.size(); +// cLog(lsINFO) << "HOS: writing " << set.size(); static boost::format fExists("SELECT ObjType FROM CommittedObjects WHERE Hash = '%s';"); static boost::format diff --git a/src/SHAMap.cpp b/src/SHAMap.cpp index ddcb11bb0..b1596c390 100644 --- a/src/SHAMap.cpp +++ b/src/SHAMap.cpp @@ -749,8 +749,8 @@ int SHAMap::flushDirty(int maxNodes, HashedObjectType t, uint32 seq) boost::unordered_map::iterator it = dirtyNodes.begin(); while (it != dirtyNodes.end()) { - tLog(mType == smtTRANSACTION, lsDEBUG) << "TX node write " << it->first; - tLog(mType == smtSTATE, lsDEBUG) << "STATE node write " << it->first; +// tLog(mType == smtTRANSACTION, lsDEBUG) << "TX node write " << it->first; +// tLog(mType == smtSTATE, lsDEBUG) << "STATE node write " << it->first; s.erase(); it->second->addRaw(s, snfPREFIX); theApp->getHashedObjectStore().store(t, seq, s.peekData(), s.getSHA512Half()); From 65395ea971a52c85e1a72382836f7e4f4e1ca092 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Mon, 15 Oct 2012 03:48:45 -0700 Subject: [PATCH 29/38] Add some sanity checks. --- src/Application.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Application.cpp b/src/Application.cpp index 72fe6ca1b..59f80e2e4 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -222,12 +222,22 @@ void Application::loadOldLedger() cLog(lsINFO) << "Loading ledger " << lastLedger->getHash() << " seq:" << lastLedger->getLedgerSeq(); + if (lastLedger->getAccountHash().isZero()) + { + cLog(lsFATAL) << "Ledger is empty."; + assert(false); + exit(-1); + } + if (!lastLedger->walkLedger()) { cLog(lsFATAL) << "Ledger is missing nodes."; exit(-1); } + assert(lastLedger->getAccountHash() == lastLedger->peekAccountStateMap()->getHash()); + assert(lastLedger->getTransHash() == lastLedger->peekTransactionMap()->getHash()); + Ledger::pointer openLedger = boost::make_shared(false, boost::ref(*lastLedger)); mMasterLedger.switchLedgers(lastLedger, openLedger); mNetOps.setLastCloseTime(lastLedger->getCloseTimeNC()); From 83a2b0d88420faddcf5c773c18608562edf135ba Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Mon, 15 Oct 2012 03:50:14 -0700 Subject: [PATCH 30/38] Cleanups. Extra asserts. --- src/Ledger.cpp | 24 ++++++++++++++++++++---- src/Ledger.h | 1 + 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/Ledger.cpp b/src/Ledger.cpp index 3af3c8838..4626eb691 100644 --- a/src/Ledger.cpp +++ b/src/Ledger.cpp @@ -336,8 +336,9 @@ bool Ledger::unitTest() uint256 Ledger::getHash() { - if(!mValidHash) updateHash(); - return(mHash); + if (!mValidHash) + updateHash(); + return mHash; } void Ledger::saveAcceptedLedger() @@ -351,6 +352,15 @@ void Ledger::saveAcceptedLedger() "(LedgerHash,LedgerSeq,PrevHash,TotalCoins,ClosingTime,PrevClosingTime,CloseTimeRes,CloseFlags," "AccountSetHash,TransSetHash) VALUES ('%s','%u','%s','%s','%u','%u','%d','%u','%s','%s');"); + if (!getAccountHash().isNonZero()) + { + cLog(lsFATAL) << "AH is zero: " << getJson(0); + assert(false); + } + + assert (getAccountHash() == mAccountStateMap->getHash()); + assert (getTransHash() == mTransactionMap->getHash()); + { ScopedLock sl(theApp->getLedgerDB()->getDBLock()); if (SQL_EXISTS(theApp->getLedgerDB()->getDB(), boost::str(ledgerExists % mLedgerSeq))) @@ -477,6 +487,7 @@ Ledger::pointer Ledger::getSQL(const std::string& sql) assert(false); return Ledger::pointer(); } + Log(lsDEBUG) << "Loaded ledger: " << ledgerHash; return ret; } @@ -497,6 +508,11 @@ Ledger::pointer Ledger::loadByHash(const uint256& ledgerHash) } void Ledger::addJson(Json::Value& ret, int options) +{ + ret["ledger"] = getJson(options); +} + +Json::Value Ledger::getJson(int options) { Json::Value ledger(Json::objectValue); @@ -582,8 +598,8 @@ void Ledger::addJson(Json::Value& ret, int options) } ledger["accountState"] = state; } - ledger["seqNum"]=boost::lexical_cast(mLedgerSeq); - ret["ledger"] = ledger; + ledger["seqNum"] = boost::lexical_cast(mLedgerSeq); + return ledger; } void Ledger::setAcquiring(void) diff --git a/src/Ledger.h b/src/Ledger.h index 0e7eaf060..a467fab2e 100644 --- a/src/Ledger.h +++ b/src/Ledger.h @@ -278,6 +278,7 @@ public: SLE::pointer getRippleState(const uint160& uiA, const uint160& uiB, const uint160& uCurrency) { return getRippleState(getRippleStateIndex(NewcoinAddress::createAccountID(uiA), NewcoinAddress::createAccountID(uiB), uCurrency)); } + Json::Value getJson(int options); void addJson(Json::Value&, int options); bool walkLedger(); From bc040e0b9113f4bc01ac3723c5a49879e78f1cb6 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Mon, 15 Oct 2012 04:12:20 -0700 Subject: [PATCH 31/38] Root node is not always an inner node. --- src/SHAMap.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/SHAMap.cpp b/src/SHAMap.cpp index b1596c390..890075215 100644 --- a/src/SHAMap.cpp +++ b/src/SHAMap.cpp @@ -704,9 +704,7 @@ SHAMapTreeNode::pointer SHAMap::fetchNodeExternal(const SHAMapNode& id, const ui try { SHAMapTreeNode::pointer ret = boost::make_shared(id, obj->getData(), mSeq, snfPREFIX); -#ifdef DEBUG assert((ret->getNodeHash() == hash) && (id == *ret)); -#endif return ret; } catch (...) @@ -728,8 +726,8 @@ void SHAMap::fetchRoot(const uint256& hash) Log(lsTRACE) << "Fetch root SHAMap node " << hash; } root = fetchNodeExternal(SHAMapNode(), hash); - root->makeInner(); mTNByID[*root] = root; + assert(root->getNodeHash() == hash); } void SHAMap::armDirty() From 863b082e320fdca94d2aba85a8b1092ad944b87f Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Mon, 15 Oct 2012 04:12:34 -0700 Subject: [PATCH 32/38] Move the logging a bit earlier. --- src/LedgerConsensus.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/LedgerConsensus.cpp b/src/LedgerConsensus.cpp index 31e64bffb..334fe883a 100644 --- a/src/LedgerConsensus.cpp +++ b/src/LedgerConsensus.cpp @@ -1113,6 +1113,15 @@ void LedgerConsensus::accept(SHAMap::ref set) newLCL->setAccepted(closeTime, mCloseResolution, closeTimeCorrect); newLCL->updateHash(); uint256 newLCLHash = newLCL->getHash(); + + if (sLog(lsTRACE)) + { + Log(lsTRACE) << "newLCL"; + Json::Value p; + newLCL->addJson(p, LEDGER_JSON_DUMP_TXNS | LEDGER_JSON_DUMP_STATE); + Log(lsTRACE) << p; + } + statusChange(ripple::neACCEPTED_LEDGER, *newLCL); if (mValidating) { @@ -1180,13 +1189,6 @@ void LedgerConsensus::accept(SHAMap::ref set) theApp->getOPs().closeTimeOffset(offset); } - if (sLog(lsTRACE)) - { - Log(lsTRACE) << "newLCL"; - Json::Value p; - newLCL->addJson(p, LEDGER_JSON_DUMP_TXNS | LEDGER_JSON_DUMP_STATE); - Log(lsTRACE) << p; - } } void LedgerConsensus::endConsensus() From 862b2c5aef9d636174213b778070469108819737 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Mon, 15 Oct 2012 04:12:48 -0700 Subject: [PATCH 33/38] Cleanups. --- src/SHAMapNodes.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/SHAMapNodes.cpp b/src/SHAMapNodes.cpp index 12f4c956e..6c02b79a3 100644 --- a/src/SHAMapNodes.cpp +++ b/src/SHAMapNodes.cpp @@ -278,6 +278,8 @@ SHAMapTreeNode::SHAMapTreeNode(const SHAMapNode& id, const std::vector Date: Mon, 15 Oct 2012 04:13:15 -0700 Subject: [PATCH 34/38] Extra sanity checks. --- src/Ledger.cpp | 24 ++++++++++++++++++++++-- src/Ledger.h | 1 + 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/Ledger.cpp b/src/Ledger.cpp index 4626eb691..9bfafeb46 100644 --- a/src/Ledger.cpp +++ b/src/Ledger.cpp @@ -977,9 +977,29 @@ uint256 Ledger::getRippleStateIndex(const NewcoinAddress& naA, const NewcoinAddr bool Ledger::walkLedger() { std::vector missingNodes; - mAccountStateMap->walkMap(missingNodes, 1); - mTransactionMap->walkMap(missingNodes, 1); + mAccountStateMap->walkMap(missingNodes, 6); + if (sLog(lsINFO) && !missingNodes.empty()) + { + Log(lsINFO) << missingNodes.size() << " missing account nodes"; + Log(lsINFO) << "First: " << missingNodes[0]; + } + mTransactionMap->walkMap(missingNodes, 6); return missingNodes.empty(); } +bool Ledger::assertSane() +{ + if (mHash.isNonZero() && mAccountHash.isNonZero() && mAccountStateMap && mTransactionMap && + (mAccountHash == mAccountStateMap->getHash()) && (mTransHash == mTransactionMap->getHash())) + return true; + + Log(lsFATAL) << "ledger is not sane"; + Json::Value j = getJson(0); + j["accountTreeHash"] = mAccountHash.GetHex(); + j["transTreeHash"] = mTransHash.GetHex(); + + assert(false); + return false; +} + // vim:ts=4 diff --git a/src/Ledger.h b/src/Ledger.h index a467fab2e..0c201d12f 100644 --- a/src/Ledger.h +++ b/src/Ledger.h @@ -282,6 +282,7 @@ public: void addJson(Json::Value&, int options); bool walkLedger(); + bool assertSane(); static bool unitTest(); }; From debe1b47410348b06bb988a6352cb92362d7e084 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Mon, 15 Oct 2012 04:13:26 -0700 Subject: [PATCH 35/38] Check ledger sanity. --- src/Application.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Application.cpp b/src/Application.cpp index 59f80e2e4..ec56e6b91 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -235,8 +235,11 @@ void Application::loadOldLedger() exit(-1); } - assert(lastLedger->getAccountHash() == lastLedger->peekAccountStateMap()->getHash()); - assert(lastLedger->getTransHash() == lastLedger->peekTransactionMap()->getHash()); + if (!lastLedger->assertSane()) + { + cLog(lsFATAL) << "Ledger is not sane."; + exit(-1); + } Ledger::pointer openLedger = boost::make_shared(false, boost::ref(*lastLedger)); mMasterLedger.switchLedgers(lastLedger, openLedger); From b9ec8e2addefb033751843d6b58e12683b2eb2a5 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Mon, 15 Oct 2012 08:56:15 -0700 Subject: [PATCH 36/38] Slight logging improvements. --- src/Ledger.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Ledger.cpp b/src/Ledger.cpp index 9bfafeb46..b21958e58 100644 --- a/src/Ledger.cpp +++ b/src/Ledger.cpp @@ -977,13 +977,13 @@ uint256 Ledger::getRippleStateIndex(const NewcoinAddress& naA, const NewcoinAddr bool Ledger::walkLedger() { std::vector missingNodes; - mAccountStateMap->walkMap(missingNodes, 6); + mAccountStateMap->walkMap(missingNodes, 32); if (sLog(lsINFO) && !missingNodes.empty()) { - Log(lsINFO) << missingNodes.size() << " missing account nodes"; + Log(lsINFO) << missingNodes.size() << " missing account node(s)"; Log(lsINFO) << "First: " << missingNodes[0]; } - mTransactionMap->walkMap(missingNodes, 6); + mTransactionMap->walkMap(missingNodes, 32); return missingNodes.empty(); } From 194f61d4e24d0666f45f246a15e6081982a68756 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Mon, 15 Oct 2012 08:56:25 -0700 Subject: [PATCH 37/38] Hopefully a fix for the "stuck in neverending consensus" bug Jed reported. --- src/LedgerConsensus.cpp | 73 +++++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 35 deletions(-) diff --git a/src/LedgerConsensus.cpp b/src/LedgerConsensus.cpp index 334fe883a..83dcbefef 100644 --- a/src/LedgerConsensus.cpp +++ b/src/LedgerConsensus.cpp @@ -300,37 +300,9 @@ void LedgerConsensus::checkLCL() void LedgerConsensus::handleLCL(const uint256& lclHash) { - mPrevLedgerHash = lclHash; - if (mPreviousLedger->getHash() == mPrevLedgerHash) - return; - - Ledger::pointer newLCL = theApp->getMasterLedger().getLedgerByHash(lclHash); - if (newLCL) - mPreviousLedger = newLCL; - else if (mAcquiringLedger && (mAcquiringLedger->getHash() == mPrevLedgerHash)) - return; - else - { - cLog(lsWARNING) << "Need consensus ledger " << mPrevLedgerHash; - - mAcquiringLedger = theApp->getMasterLedgerAcquire().findCreate(mPrevLedgerHash); - std::vector peerList = theApp->getConnectionPool().getPeerVector(); - - bool found = false; - BOOST_FOREACH(Peer::ref peer, peerList) - { - if (peer->hasLedger(mPrevLedgerHash)) - { - found = true; - mAcquiringLedger->peerHas(peer); - } - } - - if (!found) - { - BOOST_FOREACH(Peer::ref peer, peerList) - mAcquiringLedger->peerHas(peer); - } + if (mPrevLedgerHash != lclHash) + { // first time switching to this ledger + mPrevLedgerHash = lclHash; if (mHaveCorrectLCL && mProposing && mOurPosition) { @@ -338,15 +310,47 @@ void LedgerConsensus::handleLCL(const uint256& lclHash) mOurPosition->bowOut(); propose(); } - mHaveCorrectLCL = false; mProposing = false; mValidating = false; - mCloseTimes.clear(); mPeerPositions.clear(); + mPeerData.clear(); mDisputes.clear(); + mCloseTimes.clear(); mDeadNodes.clear(); playbackProposals(); - return; + } + + if (mPreviousLedger->getHash() != mPrevLedgerHash) + { // we need to switch the ledger we're working from + Ledger::pointer newLCL = theApp->getMasterLedger().getLedgerByHash(lclHash); + if (newLCL) + mPreviousLedger = newLCL; + else if (!mAcquiringLedger || (mAcquiringLedger->getHash() != mPrevLedgerHash)) + { // need to start acquiring the correct consensus LCL + cLog(lsWARNING) << "Need consensus ledger " << mPrevLedgerHash; + + mAcquiringLedger = theApp->getMasterLedgerAcquire().findCreate(mPrevLedgerHash); + std::vector peerList = theApp->getConnectionPool().getPeerVector(); + + bool found = false; + BOOST_FOREACH(Peer::ref peer, peerList) + { + if (peer->hasLedger(mPrevLedgerHash)) + { + found = true; + mAcquiringLedger->peerHas(peer); + } + } + + if (!found) + { + BOOST_FOREACH(Peer::ref peer, peerList) + mAcquiringLedger->peerHas(peer); + } + + mHaveCorrectLCL = false; + return; + } } cLog(lsINFO) << "Acquired the consensus ledger " << mPrevLedgerHash; @@ -355,7 +359,6 @@ void LedgerConsensus::handleLCL(const uint256& lclHash) mCloseResolution = ContinuousLedgerTiming::getNextLedgerTimeResolution( mPreviousLedger->getCloseResolution(), mPreviousLedger->getCloseAgree(), mPreviousLedger->getLedgerSeq() + 1); - playbackProposals(); } void LedgerConsensus::takeInitialPosition(Ledger& initialLedger) From de71645351df6178253f3c6f5c87e91955e820ac Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Mon, 15 Oct 2012 10:35:57 -0700 Subject: [PATCH 38/38] Log whether we actually couldn't find the ledger or if the sequence was incorrect. --- src/Peer.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Peer.cpp b/src/Peer.cpp index 05a85da62..99558dd84 100644 --- a/src/Peer.cpp +++ b/src/Peer.cpp @@ -1035,7 +1035,10 @@ void Peer::recvGetLedger(ripple::TMGetLedger& packet) if ((!ledger) || (packet.has_ledgerseq() && (packet.ledgerseq() != ledger->getLedgerSeq()))) { punishPeer(PP_UNKNOWN_REQUEST); - cLog(lsWARNING) << "Can't find the ledger they want"; + if (ledger) + cLog(lsWARNING) << "Ledger has wrong sequence"; + else + cLog(lsWARNING) << "Can't find the ledger they want"; return; }