diff --git a/src/Application.cpp b/src/Application.cpp index cc68d9cc39..532618bcaf 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -129,19 +129,23 @@ void Application::run() Log(lsINFO) << "Root master seed: " << rootSeedMaster.humanSeed(); Log(lsINFO) << "Root account: " << rootAddress.humanAccountID(); - Ledger::pointer firstLedger = boost::make_shared(rootAddress, SYSTEM_CURRENCY_START); - assert(!!firstLedger->getAccountState(rootAddress)); - firstLedger->updateHash(); - firstLedger->setClosed(); - firstLedger->setAccepted(); - mMasterLedger.pushLedger(firstLedger); + { + Ledger::pointer firstLedger = boost::make_shared(rootAddress, SYSTEM_CURRENCY_START); + assert(!!firstLedger->getAccountState(rootAddress)); + firstLedger->updateHash(); + firstLedger->setClosed(); + firstLedger->setAccepted(); + mMasterLedger.pushLedger(firstLedger); - Ledger::pointer secondLedger = boost::make_shared(true, boost::ref(*firstLedger)); - mMasterLedger.pushLedger(secondLedger); - assert(!!secondLedger->getAccountState(rootAddress)); - // temporary + Ledger::pointer secondLedger = boost::make_shared(true, boost::ref(*firstLedger)); + secondLedger->setClosed(); + secondLedger->setAccepted(); + mMasterLedger.pushLedger(secondLedger, boost::make_shared(true, boost::ref(*secondLedger))); + assert(!!secondLedger->getAccountState(rootAddress)); + mNetOps.setLastCloseNetTime(secondLedger->getCloseTimeNC()); + } - mNetOps.setStateTimer(0); + mNetOps.setStateTimer(); mIOService.run(); // This blocks diff --git a/src/BinaryFormats.h b/src/BinaryFormats.h deleted file mode 100644 index 86c1f69ad7..0000000000 --- a/src/BinaryFormats.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef __BINARYFORMATS__ -#define __BINARYFORMATS__ - -// ledger (note: fields after the timestamp are not part of the hash) -const int BLgSize=190; -const int BLgPIndex=0, BLgLIndex=4; // ledger index -const int BLgPTotCoins=4, BLgLTotCoins=8; // total native coins -const int BLgPPrevLg=12, BLgLPrevLg=32; // previous ledger hash -const int BLgPTxT=44, BLgLTxT=32; // transaction tree hash -const int BLgPAcT=76, BLgLPAct=32; // account state hash -const int BLgPClTs=108, BLgLClTs=8; // closing timestamp -const int BLgPNlIn=116, BLgLNlIn=2; -const int BLgPSig=118, BLgLSig=72; // signature - -#endif diff --git a/src/DBInit.cpp b/src/DBInit.cpp index 4f11e12ec3..c9c00ce7a6 100644 --- a/src/DBInit.cpp +++ b/src/DBInit.cpp @@ -43,7 +43,10 @@ const char *LedgerDBInit[] = { LedgerSeq BIGINT UNSIGNED, \ PrevHash CHARACTER(64), \ TotalCoins BIGINT UNSIGNED, \ - ClosingTime BIGINT UNSINGED, \ + ClosingTime BIGINT UNSIGNED, \ + PrevClosingTime BIGINT UNSIGNED, \ + CloseTimeRes BIGINT UNSIGNED, \ + CloseFlags, BIGINT UNSIGNED, \ AccountSetHash CHARACTER(64), \ TransSetHash CHARACTER(64) \ );", diff --git a/src/HashPrefixes.h b/src/HashPrefixes.h index 80c3d79685..832349ec64 100644 --- a/src/HashPrefixes.h +++ b/src/HashPrefixes.h @@ -3,25 +3,28 @@ #include "types.h" -// TXN +// TXN - Hash of transaction plus signature to give transaction ID const uint32 sHP_TransactionID = 0x54584E00; -// STX +// STX - Hash of inner transaction to sign const uint32 sHP_TransactionSign = 0x53545800; -// MLN +// TND - Hash of transaction plus metadata +const uint32 sHP_TransactionNode = 0x534E4400; + +// MLN - Hash of account state const uint32 sHP_LeafNode = 0x4D4C4E00; -// MIN +// MIN - Hash of inner node in tree const uint32 sHP_InnerNode = 0x4D494E00; -// LGR +// LGR - Hash of ledger master data for signing const uint32 sHP_Ledger = 0x4C575200; -// VAL +// VAL - Hash of validation for signing const uint32 sHP_Validation = 0x56414C00; -// PRP +// PRP - Hash of proposal for signing const uint32 sHP_Proposal = 0x50525000; #endif diff --git a/src/Ledger.cpp b/src/Ledger.cpp index 8e3db62ca4..de44f9f4ad 100644 --- a/src/Ledger.cpp +++ b/src/Ledger.cpp @@ -16,14 +16,14 @@ #include "Conversion.h" #include "BitcoinUtil.h" #include "Wallet.h" -#include "BinaryFormats.h" #include "LedgerTiming.h" #include "HashPrefixes.h" #include "Log.h" -Ledger::Ledger(const NewcoinAddress& masterID, uint64 startAmount) : mTotCoins(startAmount), - mCloseTime(0), mLedgerSeq(0), mLedgerInterval(LEDGER_INTERVAL), mClosed(false), mValidHash(false), - mAccepted(false), mImmutable(false), mTransactionMap(new SHAMap()), mAccountStateMap(new SHAMap()) +Ledger::Ledger(const NewcoinAddress& masterID, uint64 startAmount) : mTotCoins(startAmount), mLedgerSeq(0), + mCloseTime(0), mParentCloseTime(0), mCloseResolution(LEDGER_TIME_ACCURACY), mCloseFlags(0), + mClosed(false), mValidHash(false), mAccepted(false), mImmutable(false), + mTransactionMap(new SHAMap()), mAccountStateMap(new SHAMap()) { // special case: put coins in root account AccountState::pointer startAccount = boost::make_shared(masterID); @@ -37,16 +37,18 @@ Ledger::Ledger(const NewcoinAddress& masterID, uint64 startAmount) : mTotCoins(s } Ledger::Ledger(const uint256 &parentHash, const uint256 &transHash, const uint256 &accountHash, - uint64 totCoins, uint64 timeStamp, uint32 ledgerSeq) - : mParentHash(parentHash), mTransHash(transHash), mAccountHash(accountHash), - mTotCoins(totCoins), mCloseTime(timeStamp), mLedgerSeq(ledgerSeq), mLedgerInterval(LEDGER_INTERVAL), + uint64 totCoins, uint32 closeTime, uint32 parentCloseTime, int closeFlags, int closeResolution, uint32 ledgerSeq) + : mParentHash(parentHash), mTransHash(transHash), mAccountHash(accountHash), mTotCoins(totCoins), + mLedgerSeq(ledgerSeq), mCloseTime(closeTime), mParentCloseTime(parentCloseTime), + mCloseResolution(closeResolution), mCloseFlags(closeFlags), mClosed(false), mValidHash(false), mAccepted(false), mImmutable(false) { updateHash(); } -Ledger::Ledger(Ledger& ledger, bool isMutable) : mTotCoins(ledger.mTotCoins), - mLedgerSeq(ledger.mLedgerSeq), mLedgerInterval(ledger.mLedgerInterval), +Ledger::Ledger(Ledger& ledger, bool isMutable) : mTotCoins(ledger.mTotCoins), mLedgerSeq(ledger.mLedgerSeq), + mCloseTime(ledger.mCloseTime), mParentCloseTime(ledger.mParentCloseTime), + mCloseResolution(ledger.mCloseResolution), mCloseFlags(ledger.mCloseFlags), mClosed(ledger.mClosed), mValidHash(false), mAccepted(ledger.mAccepted), mImmutable(!isMutable), mTransactionMap(ledger.mTransactionMap->snapShot(isMutable)), mAccountStateMap(ledger.mAccountStateMap->snapShot(isMutable)) @@ -55,55 +57,37 @@ Ledger::Ledger(Ledger& ledger, bool isMutable) : mTotCoins(ledger.mTotCoins), } -Ledger::Ledger(bool, Ledger& prevLedger) : mTotCoins(prevLedger.mTotCoins), - mLedgerSeq(prevLedger.mLedgerSeq + 1), mLedgerInterval(prevLedger.mLedgerInterval), - mClosed(false), mValidHash(false), mAccepted(false), mImmutable(false), +Ledger::Ledger(bool dummy, Ledger& prevLedger) : + mTotCoins(prevLedger.mTotCoins), mLedgerSeq(prevLedger.mLedgerSeq + 1), + mParentCloseTime(prevLedger.mCloseTime), mCloseResolution(prevLedger.mCloseResolution), + mCloseFlags(0), mClosed(false), mValidHash(false), mAccepted(false), mImmutable(false), mTransactionMap(new SHAMap()), mAccountStateMap(prevLedger.mAccountStateMap->snapShot(true)) { // Create a new ledger that follows this one prevLedger.updateHash(); mParentHash = prevLedger.getHash(); assert(mParentHash.isNonZero()); - mCloseTime = prevLedger.getNextLedgerClose(); + + mCloseResolution = ContinuousLedgerTiming::getNextLedgerTimeResolution(prevLedger.mCloseResolution, + prevLedger.getCloseAgree(), mLedgerSeq); + if (prevLedger.mCloseTime == 0) + { + mCloseTime = theApp->getOPs().getNetworkTimeNC(); + mCloseTime -= (mCloseTime % mCloseResolution); + } + else + mCloseTime = prevLedger.mCloseTime + mCloseResolution; } -Ledger::Ledger(const std::vector& rawLedger) : mCloseTime(0), - mLedgerSeq(0), mClosed(false), mValidHash(false), mAccepted(false), mImmutable(true) +Ledger::Ledger(const std::vector& rawLedger) : + mClosed(false), mValidHash(false), mAccepted(false), mImmutable(true) { - Serializer s(rawLedger); - // 32seq, 64fee, 256phash, 256thash, 256ahash, 64ts - if (!s.get32(mLedgerSeq, BLgPIndex)) return; - if (!s.get64(mTotCoins, BLgPTotCoins)) return; - if (!s.get256(mParentHash, BLgPPrevLg)) return; - if (!s.get256(mTransHash, BLgPTxT)) return; - if (!s.get256(mAccountHash, BLgPAcT)) return; - if (!s.get64(mCloseTime, BLgPClTs)) return; - if (!s.get16(mLedgerInterval, BLgPNlIn)) return; - updateHash(); - if(mValidHash) - { - mTransactionMap = boost::make_shared(); - mAccountStateMap = boost::make_shared(); - } + setRaw(Serializer(rawLedger)); } -Ledger::Ledger(const std::string& rawLedger) : mCloseTime(0), - mLedgerSeq(0), mClosed(false), mValidHash(false), mAccepted(false), mImmutable(true) +Ledger::Ledger(const std::string& rawLedger) : + mClosed(false), mValidHash(false), mAccepted(false), mImmutable(true) { - Serializer s(rawLedger); - // 32seq, 64fee, 256phash, 256thash, 256ahash, 64ts - if (!s.get32(mLedgerSeq, BLgPIndex)) return; - if (!s.get64(mTotCoins, BLgPTotCoins)) return; - if (!s.get256(mParentHash, BLgPPrevLg)) return; - if (!s.get256(mTransHash, BLgPTxT)) return; - if (!s.get256(mAccountHash, BLgPAcT)) return; - if (!s.get64(mCloseTime, BLgPClTs)) return; - if (!s.get16(mLedgerInterval, BLgPNlIn)) return; - updateHash(); - if(mValidHash) - { - mTransactionMap = boost::make_shared(); - mAccountStateMap = boost::make_shared(); - } + setRaw(Serializer(rawLedger)); } void Ledger::updateHash() @@ -116,22 +100,64 @@ void Ledger::updateHash() else mAccountHash.zero(); } - Serializer s(116); + Serializer s(118); s.add32(sHP_Ledger); addRaw(s); mHash = s.getSHA512Half(); mValidHash = true; } -void Ledger::addRaw(Serializer &s) +void Ledger::setRaw(const Serializer &s) +{ + SerializerIterator sit(s); + mLedgerSeq = sit.get32(); + mTotCoins = sit.get64(); + mParentHash = sit.get256(); + mTransHash = sit.get256(); + mAccountHash = sit.get256(); + mParentCloseTime = sit.get32(); + mCloseTime = sit.get32(); + mCloseResolution = sit.get8(); + mCloseFlags = sit.get8(); + updateHash(); + if(mValidHash) + { + mTransactionMap = boost::make_shared(mTransHash); + mAccountStateMap = boost::make_shared(mAccountHash); + } +} + +void Ledger::addRaw(Serializer &s) const { s.add32(mLedgerSeq); s.add64(mTotCoins); s.add256(mParentHash); s.add256(mTransHash); s.add256(mAccountHash); - s.add64(mCloseTime); - s.add16(mLedgerInterval); + s.add32(mParentCloseTime); + s.add32(mCloseTime); + s.add8(mCloseResolution); + s.add8(mCloseFlags); +} + +void Ledger::setAccepted(uint32 closeTime, int closeResolution, bool correctCloseTime) +{ // used when we witnessed the consensus + assert(mClosed && !mAccepted); + mCloseTime = closeTime - (closeTime % closeResolution); + mCloseResolution = closeResolution; + mCloseFlags = correctCloseTime ? 0 : sLCF_NoConsensusTime; + updateHash(); + mAccepted = true; + mImmutable = true; +} + +void Ledger::setAccepted() +{ // used when we acquired the ledger + assert(mClosed && (mCloseResolution != 0) && (mCloseResolution != 0)); + mCloseTime -= mCloseTime % mCloseResolution; + updateHash(); + mAccepted = true; + mImmutable = true; } AccountState::pointer Ledger::getAccountState(const NewcoinAddress& accountID) @@ -191,14 +217,16 @@ bool Ledger::addTransaction(Transaction::pointer trans) Serializer s; trans->getSTransaction()->add(s); SHAMapItem::pointer item = boost::make_shared(trans->getID(), s.peekData()); - if (!mTransactionMap->addGiveItem(item, true)) return false; + if (!mTransactionMap->addGiveItem(item, true, false)) // FIXME: TX metadata + return false; return true; } 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)) return false; + if (!mTransactionMap->addGiveItem(item, true, false)) // FIXME: TX metadata + return false; return true; } @@ -241,28 +269,18 @@ void Ledger::saveAcceptedLedger(Ledger::pointer ledger) static boost::format AcctTransExists("SELECT LedgerSeq FROM AccountTransactions WHERE TransId = '%s';"); static boost::format transExists("SELECT Status FROM Transactions WHERE TransID = '%s';"); static boost::format updateTx("UPDATE Transactions SET LedgerSeq = %d, Status = '%c' WHERE TransID = '%s';"); - - std::string sql="INSERT INTO Ledgers " - "(LedgerHash,LedgerSeq,PrevHash,TotalCoins,ClosingTime,AccountSetHash,TransSetHash) VALUES ('"; - sql.append(ledger->getHash().GetHex()); - sql.append("','"); - sql.append(boost::lexical_cast(ledger->mLedgerSeq)); - sql.append("','"); - sql.append(ledger->mParentHash.GetHex()); - sql.append("','"); - sql.append(boost::lexical_cast(ledger->mTotCoins)); - sql.append("','"); - sql.append(boost::lexical_cast(ledger->mCloseTime)); - sql.append("','"); - sql.append(ledger->mAccountHash.GetHex()); - sql.append("','"); - sql.append(ledger->mTransHash.GetHex()); - sql.append("');"); + static boost::format addLedger("INSERT INTO Ledgers " + "(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(sql); + 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 while(ledger->mTransactionMap->flushDirty(256, TRANSACTION_NODE, ledger->mLedgerSeq)) @@ -331,8 +349,10 @@ void Ledger::saveAcceptedLedger(Ledger::pointer ledger) Ledger::pointer Ledger::getSQL(const std::string& sql) { uint256 ledgerHash, prevHash, accountHash, transHash; - uint64 totCoins, closingTime; - uint32 ledgerSeq; + uint64 totCoins; + uint32 closingTime, prevClosingTime, ledgerSeq; + int closeResolution; + unsigned closeFlags; std::string hash; { @@ -352,12 +372,16 @@ Ledger::pointer Ledger::getSQL(const std::string& sql) transHash.SetHex(hash); totCoins = db->getBigInt("TotalCoins"); closingTime = db->getBigInt("ClosingTime"); + prevClosingTime = db->getBigInt("PrevClosingTime"); + closeResolution = db->getBigInt("CloseTimeRes"); + closeFlags = db->getBigInt("CloseFlags"); ledgerSeq = db->getBigInt("LedgerSeq"); db->endIterRows(); } Ledger::pointer ret = - boost::make_shared(prevHash, transHash, accountHash, totCoins, closingTime, ledgerSeq); + boost::make_shared(prevHash, transHash, accountHash, totCoins, closingTime, prevClosingTime, + closeFlags, closeResolution, ledgerSeq); if (ret->getHash() != ledgerHash) { Json::StyledStreamWriter ssw; @@ -400,9 +424,16 @@ void Ledger::addJson(Json::Value& ret, int options) ledger["hash"] = mHash.GetHex(); ledger["transactionHash"] = mTransHash.GetHex(); ledger["accountHash"] = mAccountHash.GetHex(); - ledger["closed"] = true; + if (mClosed) ledger["closed"] = true; ledger["accepted"] = mAccepted; ledger["totalCoins"] = boost::lexical_cast(mTotCoins); + if ((mCloseFlags & sLCF_NoConsensusTime) != 0) + ledger["closeTimeEstimate"] = mCloseTime; + else + { + ledger["closeTime"] = mCloseTime; + ledger["closeTimeResolution"] = mCloseResolution; + } } else ledger["closed"] = false; if (mCloseTime != 0) @@ -473,23 +504,8 @@ boost::posix_time::ptime Ledger::getCloseTime() const void Ledger::setCloseTime(boost::posix_time::ptime ptm) { + assert(!mImmutable); mCloseTime = iToSeconds(ptm); } -uint64 Ledger::sGenesisClose = 0; - -uint64 Ledger::getNextLedgerClose() const -{ - if (mCloseTime == 0) - { - if (sGenesisClose == 0) - { - uint64 closeTime = theApp->getOPs().getNetworkTimeNC() + mLedgerInterval - 1; - sGenesisClose = closeTime - (closeTime % mLedgerInterval); - } - return sGenesisClose; - } - return mCloseTime + mLedgerInterval; -} - // vim:ts=4 diff --git a/src/Ledger.h b/src/Ledger.h index 0bf03f49b7..df2cc04abc 100644 --- a/src/Ledger.h +++ b/src/Ledger.h @@ -58,16 +58,18 @@ public: TR_TOOSMALL = 9, // amount is less than Tx fee }; + // ledger close flags + static const uint32 sLCF_NoConsensusTime = 1; private: - static uint64 sGenesisClose; uint256 mHash, mParentHash, mTransHash, mAccountHash; uint64 mTotCoins; - uint64 mCloseTime; // when this ledger closes - uint64 mParentCloseTime; uint32 mLedgerSeq; - uint16 mLedgerInterval; + uint32 mCloseTime; // when this ledger closed + uint32 mParentCloseTime; // when the previous ledger closed + int mCloseResolution; // the resolution for this ledger close time (2-120 seconds) + uint32 mCloseFlags; // flags indicating how this ledger close took place bool mClosed, mValidHash, mAccepted, mImmutable; SHAMap::pointer mTransactionMap, mAccountStateMap; @@ -84,21 +86,27 @@ protected: static Ledger::pointer getSQL(const std::string& sqlStatement); - SLE::pointer getASNode(LedgerStateParms& parms, const uint256& nodeID, - LedgerEntryType let); + SLE::pointer getASNode(LedgerStateParms& parms, const uint256& nodeID, LedgerEntryType let); public: Ledger(const NewcoinAddress& masterID, uint64 startAmount); // used for the starting bootstrap ledger + Ledger(const uint256 &parentHash, const uint256 &transHash, const uint256 &accountHash, - uint64 totCoins, uint64 timeStamp, uint32 ledgerSeq); // used for database ledgers + uint64 totCoins, uint32 closeTime, uint32 parentCloseTime, int closeFlags, int closeResolution, + uint32 ledgerSeq); // used for database ledgers + Ledger(const std::vector& rawLedger); + Ledger(const std::string& rawLedger); - Ledger(bool fromAccepted, Ledger& previous); // ledger after this one + + Ledger(bool dummy, Ledger& previous); // ledger after this one + Ledger(Ledger& target, bool isMutable); // snapshot void updateHash(); void setClosed() { mClosed = true; } - void setAccepted() { mAccepted = true; } + void setAccepted(uint32 closeTime, int closeResolution, bool correctCloseTime); + void setAccepted(); void setImmutable() { updateHash(); mImmutable = true; } bool isClosed() { return mClosed; } bool isAccepted() { return mAccepted; } @@ -111,7 +119,8 @@ public: void bumpSeq() { mClosed = true; mLedgerSeq++; } // ledger signature operations - void addRaw(Serializer &s); + void addRaw(Serializer &s) const; + void setRaw(const Serializer& s); uint256 getHash(); const uint256& getParentHash() const { return mParentHash; } @@ -119,15 +128,16 @@ public: const uint256& getAccountHash() const { return mAccountHash; } uint64 getTotalCoins() const { return mTotCoins; } void destroyCoins(uint64 fee) { mTotCoins -= fee; } - uint64 getCloseTimeNC() const { return mCloseTime; } - uint64 getParentCloseTimeNC() const { return mParentCloseTime; } + uint32 getCloseTimeNC() const { return mCloseTime; } + uint32 getParentCloseTimeNC() const { return mParentCloseTime; } uint32 getLedgerSeq() const { return mLedgerSeq; } - uint16 getInterval() const { return mLedgerInterval; } + int getCloseResolution() const { return mCloseResolution; } + bool getCloseAgree() const { return (mCloseFlags & sLCF_NoConsensusTime) == 0; } // close time functions - boost::posix_time::ptime getCloseTime() const; + void setCloseTime(uint32 ct) { assert(!mImmutable); mCloseTime = ct; } void setCloseTime(boost::posix_time::ptime); - uint64 getNextLedgerClose() const; + boost::posix_time::ptime getCloseTime() const; // low level functions SHAMap::pointer peekTransactionMap() { return mTransactionMap; } diff --git a/src/LedgerAcquire.cpp b/src/LedgerAcquire.cpp index 290c2afc7b..088c8c6638 100644 --- a/src/LedgerAcquire.cpp +++ b/src/LedgerAcquire.cpp @@ -12,6 +12,7 @@ #define LA_DEBUG #define LEDGER_ACQUIRE_TIMEOUT 2 +#define TRUST_NETWORK PeerSet::PeerSet(const uint256& hash, int interval) : mHash(hash), mTimerInterval(interval), mTimeouts(0), mComplete(false), mFailed(false), mProgress(true), mTimer(theApp->getIOService()) @@ -28,7 +29,8 @@ void PeerSet::peerHas(Peer::pointer ptr) it = mPeers.erase(it); else { - if (pr->samePeer(ptr)) return; // we already have this peer + if (pr->samePeer(ptr)) + return; // we already have this peer ++it; } } @@ -77,14 +79,16 @@ void PeerSet::invokeOnTimer() void PeerSet::TimerEntry(boost::weak_ptr wptr, const boost::system::error_code& result) { - if (result == boost::asio::error::operation_aborted) return; + if (result == boost::asio::error::operation_aborted) + return; boost::shared_ptr ptr = wptr.lock(); - if (!ptr) return; + if (!ptr) + return; ptr->invokeOnTimer(); } LedgerAcquire::LedgerAcquire(const uint256& hash) : PeerSet(hash, LEDGER_ACQUIRE_TIMEOUT), - mHaveBase(false), mHaveState(false), mHaveTransactions(false) + mHaveBase(false), mHaveState(false), mHaveTransactions(false), mAborted(false) { #ifdef LA_DEBUG Log(lsTRACE) << "Acquiring ledger " << mHash.GetHex(); @@ -109,7 +113,9 @@ void LedgerAcquire::done() mOnComplete.empty(); mLock.unlock(); - for (unsigned int i = 0; i < triggers.size(); ++i) + theApp->getMasterLedger().storeLedger(mLedger); + + for (int i = 0; i < triggers.size(); ++i) triggers[i](shared_from_this()); } @@ -122,6 +128,8 @@ void LedgerAcquire::addOnComplete(boost::function void LedgerAcquire::trigger(Peer::pointer peer) { + if (mAborted) + return; #ifdef LA_DEBUG if(peer) Log(lsTRACE) << "Trigger acquiring ledger " << mHash.GetHex() << " from " << peer->getIP(); else Log(lsTRACE) << "Trigger acquiring ledger " << mHash.GetHex(); @@ -299,6 +307,9 @@ bool LedgerAcquire::takeBase(const std::string& data) Log(lsWARNING) << "Acquire hash mismatch"; Log(lsWARNING) << mLedger->getHash().GetHex() << "!=" << mHash.GetHex(); mLedger = Ledger::pointer(); +#ifdef TRUST_NETWORK + assert(false); +#endif return false; } mHaveBase = true; @@ -337,7 +348,11 @@ bool LedgerAcquire::takeTxNode(const std::list& nodeIDs, if (!mLedger->peekTransactionMap()->isSynching()) { mHaveTransactions = true; - if (mHaveState) mComplete = true; + if (mHaveState) + { + mComplete = true; + done(); + } } progress(); return true; @@ -368,7 +383,11 @@ bool LedgerAcquire::takeAsNode(const std::list& nodeIDs, if (!mLedger->peekAccountStateMap()->isSynching()) { mHaveState = true; - if (mHaveTransactions) mComplete = true; + if (mHaveTransactions) + { + mComplete = true; + done(); + } } progress(); return true; @@ -388,6 +407,7 @@ bool LedgerAcquire::takeTxRootNode(const std::vector& data) LedgerAcquire::pointer LedgerAcquireMaster::findCreate(const uint256& hash) { + assert(hash.isNonZero()); boost::mutex::scoped_lock sl(mLock); LedgerAcquire::pointer& ptr = mLedgers[hash]; if (ptr) return ptr; @@ -399,6 +419,7 @@ LedgerAcquire::pointer LedgerAcquireMaster::findCreate(const uint256& hash) LedgerAcquire::pointer LedgerAcquireMaster::find(const uint256& hash) { + assert(hash.isNonZero()); boost::mutex::scoped_lock sl(mLock); std::map::iterator it = mLedgers.find(hash); if (it != mLedgers.end()) return it->second; @@ -407,13 +428,15 @@ LedgerAcquire::pointer LedgerAcquireMaster::find(const uint256& hash) bool LedgerAcquireMaster::hasLedger(const uint256& hash) { + assert(hash.isNonZero()); boost::mutex::scoped_lock sl(mLock); return mLedgers.find(hash) != mLedgers.end(); } void LedgerAcquireMaster::dropLedger(const uint256& hash) { - boost::mutex::scoped_lock sl(mLock); + assert(hash.isNonZero()); + boost::mutex::scoped_lock sl(mLock); mLedgers.erase(hash); } diff --git a/src/LedgerAcquire.h b/src/LedgerAcquire.h index ba96139372..62540172b3 100644 --- a/src/LedgerAcquire.h +++ b/src/LedgerAcquire.h @@ -65,7 +65,7 @@ public: protected: Ledger::pointer mLedger; - bool mHaveBase, mHaveState, mHaveTransactions; + bool mHaveBase, mHaveState, mHaveTransactions, mAborted; std::vector< boost::function > mOnComplete; @@ -83,6 +83,7 @@ public: bool isAcctStComplete() const { return mHaveState; } bool isTransComplete() const { return mHaveTransactions; } Ledger::pointer getLedger() { return mLedger; } + void abort() { mAborted = true; } void addOnComplete(boost::function); diff --git a/src/LedgerConsensus.cpp b/src/LedgerConsensus.cpp index 755b74c2c4..82ec0b42e5 100644 --- a/src/LedgerConsensus.cpp +++ b/src/LedgerConsensus.cpp @@ -155,7 +155,7 @@ void LCTransaction::setVote(const uint160& peer, bool votesYes) } } -bool LCTransaction::updatePosition(int seconds, bool proposing) +bool LCTransaction::updatePosition(int percentTime, bool proposing) { // this many seconds after close, should our position change if (mOurPosition && (mNays == 0)) return false; @@ -169,9 +169,9 @@ bool LCTransaction::updatePosition(int seconds, bool proposing) int weight = (mYays * 100 + (mOurPosition ? 100 : 0)) / (mNays + mYays + 1); // To prevent avalanche stalls, we increase the needed weight slightly over time - if (seconds <= LEDGER_ACCEL_CONVERGE) newPosition = weight > AV_MIN_CONSENSUS; - else if (seconds >= LEDGER_CONVERGE) newPosition = weight > AV_AVG_CONSENSUS; - else newPosition = weight > AV_MAX_CONSENSUS; + if (percentTime < AV_MID_CONSENSUS_TIME) newPosition = weight > AV_INIT_CONSENSUS_PCT; + else if (percentTime < AV_LATE_CONSENSUS_TIME) newPosition = weight > AV_MID_CONSENSUS_PCT; + else newPosition = weight > AV_LATE_CONSENSUS_PCT; } else // don't let us outweight a proposing node, just recognize consensus newPosition = mYays > mNays; @@ -189,26 +189,41 @@ bool LCTransaction::updatePosition(int seconds, bool proposing) return true; } -int LCTransaction::getAgreeLevel() -{ // how much do nodes agree with us - if (mOurPosition) return (mYays * 100 + 100) / (mYays + mNays + 1); - return (mNays * 100 + 100) / (mYays + mNays + 1); -} - LedgerConsensus::LedgerConsensus(const uint256& prevLCLHash, Ledger::pointer previousLedger, uint32 closeTime) - : mState(lcsPRE_CLOSE), mCloseTime(closeTime), mPrevLedgerHash(prevLCLHash), mPreviousLedger(previousLedger) + : mState(lcsPRE_CLOSE), mCloseTime(closeTime), mPrevLedgerHash(prevLCLHash), mPreviousLedger(previousLedger), + mCurrentSeconds(0), mClosePercent(0), mHaveCloseTimeConsensus(false) { mValSeed = theConfig.VALIDATION_SEED; Log(lsDEBUG) << "Creating consensus object"; Log(lsTRACE) << "LCL:" << previousLedger->getHash().GetHex() <<", ct=" << closeTime; - if (previousLedger->getHash() != prevLCLHash) + mPreviousProposers = theApp->getOPs().getPreviousProposers(); + mPreviousSeconds = theApp->getOPs().getPreviousSeconds(); + assert(mPreviousSeconds); + + mCloseResolution = ContinuousLedgerTiming::getNextLedgerTimeResolution( + previousLedger->getCloseResolution(), previousLedger->getCloseAgree(), previousLedger->getLedgerSeq() + 1); + + mHaveCorrectLCL = previousLedger->getHash() == prevLCLHash; + + if (!mHaveCorrectLCL) + { mHaveCorrectLCL = mProposing = mValidating = false; + mAcquiringLedger = theApp->getMasterLedgerAcquire().findCreate(prevLCLHash); + std::vector peerList = theApp->getConnectionPool().getPeerVector(); + for (std::vector::const_iterator it = peerList.begin(), end = peerList.end(); it != end; ++it) + if ((*it)->hasLedger(prevLCLHash)) + mAcquiringLedger->peerHas(*it); + } else if (mValSeed.isValid()) { - mValidating = true; + mHaveCorrectLCL = mValidating = true; mProposing = theApp->getOPs().getOperatingMode() == NetworkOPs::omFULL; } - else mProposing = mValidating = false; + else + { + mHaveCorrectLCL = true; + mProposing = mValidating = false; + } } void LedgerConsensus::takeInitialPosition(Ledger::pointer initialLedger) @@ -233,9 +248,9 @@ void LedgerConsensus::takeInitialPosition(Ledger::pointer initialLedger) if (mValidating) mOurPosition = boost::make_shared - (mValSeed, initialLedger->getParentHash(), txSet); + (mValSeed, initialLedger->getParentHash(), txSet, mCloseTime); else - mOurPosition = boost::make_shared(initialLedger->getParentHash(), txSet); + mOurPosition = boost::make_shared(initialLedger->getParentHash(), txSet, mCloseTime); mapComplete(txSet, initialSet, false); if (mProposing) propose(std::vector(), std::vector()); } @@ -342,84 +357,70 @@ void LedgerConsensus::statusChange(newcoin::NodeEvent event, Ledger::pointer led Log(lsINFO) << "send status change to peer"; } -void LedgerConsensus::abort() -{ - Log(lsWARNING) << "consensus aborted"; - mState = lcsABORTED; -} - int LedgerConsensus::startup() { - // create wobble ledger in case peers target transactions to it - theApp->getMasterLedger().beginWobble(); return 1; } -int LedgerConsensus::statePreClose(int secondsSinceClose) +void LedgerConsensus::statePreClose() { // it is shortly before ledger close time - if (secondsSinceClose >= 0) - { // it is time to close the ledger (swap default and wobble ledgers) - Log(lsINFO) << "Closing ledger"; - mState = lcsPOST_CLOSE; - theApp->getMasterLedger().closeTime(); - statusChange(newcoin::neCLOSING_LEDGER, mPreviousLedger); - } - return 1; -} + bool anyTransactions = theApp->getMasterLedger().getCurrentLedger()->peekTransactionMap()->getHash().isNonZero(); + int proposersClosed = mPeerPositions.size(); -int LedgerConsensus::statePostClose(int secondsSinceClose) -{ // we are in the transaction wobble time - if (secondsSinceClose > LEDGER_WOBBLE_TIME) - { - Log(lsINFO) << "Wobble is over, it's consensus time"; + int sinceClose = theApp->getOPs().getNetworkTimeNC() - theApp->getOPs().getLastCloseNetTime(); + + if (sinceClose >= ContinuousLedgerTiming::shouldClose(anyTransactions, mPreviousProposers, proposersClosed, + mPreviousSeconds, sinceClose)) + { // it is time to close the ledger (swap default and wobble ledgers) + Log(lsINFO) << "CLC:: closing ledger"; mState = lcsESTABLISH; - Ledger::pointer initial = theApp->getMasterLedger().endWobble(); + mConsensusStartTime = boost::posix_time::second_clock::universal_time(); + mCloseTime = theApp->getOPs().getNetworkTimeNC(); + theApp->getOPs().setLastCloseNetTime(mCloseTime); + statusChange(newcoin::neCLOSING_LEDGER, mPreviousLedger); + Ledger::pointer initial = theApp->getMasterLedger().closeLedger(); assert (initial->getParentHash() == mPreviousLedger->getHash()); takeInitialPosition(initial); } - return 1; } -int LedgerConsensus::stateEstablish(int secondsSinceClose) +void LedgerConsensus::stateEstablish() { // we are establishing consensus - if (mProposing) - updateOurPositions(secondsSinceClose); - if (secondsSinceClose > LEDGER_MAX_CONVERGE) + if (mCurrentSeconds < LEDGER_MIN_CONSENSUS) + return; + updateOurPositions(); + if (!mHaveCloseTimeConsensus) + { + Log(lsINFO) << "No close time consensus"; + } + else if (haveConsensus()) { Log(lsINFO) << "Converge cutoff"; - mState = lcsCUTOFF; - } - return 1; -} - -int LedgerConsensus::stateCutoff(int secondsSinceClose) -{ // we are making sure everyone else agrees - bool haveConsensus = updateOurPositions(secondsSinceClose); - if (haveConsensus || (secondsSinceClose > LEDGER_CONVERGE)) - { - Log(lsINFO) << "Consensus complete (" << haveConsensus << ")"; mState = lcsFINISHED; beginAccept(); } - return 1; } -int LedgerConsensus::stateFinished(int secondsSinceClose) +void LedgerConsensus::stateFinished() { // we are processing the finished ledger // logic of calculating next ledger advances us out of this state - return 1; + + // CHECKME: Should we count proposers that didn't converge to our consensus set? + int convergeTime = (boost::posix_time::second_clock::universal_time() - mConsensusStartTime).seconds(); + if (convergeTime <= 0) convergeTime = 1; + theApp->getOPs().newLCL(mPeerPositions.size(), convergeTime, mNewLedgerHash); } -int LedgerConsensus::stateAccepted(int secondsSinceClose) +void LedgerConsensus::stateAccepted() { // we have accepted a new ledger endConsensus(); - return 4; } -int LedgerConsensus::timerEntry() +void LedgerConsensus::timerEntry() { if (!mHaveCorrectLCL) { + Log(lsINFO) << "Checking for consensus ledger " << mPrevLedgerHash.GetHex(); Ledger::pointer consensus = theApp->getMasterLedger().getLedgerByHash(mPrevLedgerHash); if (consensus) { @@ -429,45 +430,42 @@ int LedgerConsensus::timerEntry() mPreviousLedger = consensus; mHaveCorrectLCL = true; } + else Log(lsINFO) << "We still don't have it"; } - int sinceClose = theApp->getOPs().getNetworkTimeNC() - mCloseTime; + mCurrentSeconds = (mCloseTime != 0) ? (theApp->getOPs().getNetworkTimeNC() - mCloseTime) : 0; + mClosePercent = mCurrentSeconds * 100 / mPreviousSeconds; switch (mState) { - case lcsPRE_CLOSE: return statePreClose(sinceClose); - case lcsPOST_CLOSE: return statePostClose(sinceClose); - case lcsESTABLISH: return stateEstablish(sinceClose); - case lcsCUTOFF: return stateCutoff(sinceClose); - case lcsFINISHED: return stateFinished(sinceClose); - case lcsACCEPTED: return stateAccepted(sinceClose); - case lcsABORTED: return 1; + case lcsPRE_CLOSE: statePreClose(); return; + case lcsESTABLISH: stateEstablish(); return; + case lcsFINISHED: stateFinished(); return; + case lcsACCEPTED: stateAccepted(); return; } assert(false); - return 1; } -bool LedgerConsensus::updateOurPositions(int sinceClose) -{ // returns true if the network has consensus +void LedgerConsensus::updateOurPositions() +{ + Log(lsINFO) << "Updating our positions"; bool changes = false; - bool stable = true; SHAMap::pointer ourPosition; std::vector addedTx, removedTx; for(boost::unordered_map::iterator it = mDisputes.begin(), end = mDisputes.end(); it != end; ++it) { - if (it->second->updatePosition(sinceClose, mProposing)) + if (it->second->updatePosition(mClosePercent, mProposing)) { if (!changes) { ourPosition = mComplete[mOurPosition->getCurrentHash()]->snapShot(true); changes = true; - stable = false; } if (it->second->getOurPosition()) // now a yes { - ourPosition->addItem(SHAMapItem(it->first, it->second->peekTransaction()), true); + ourPosition->addItem(SHAMapItem(it->first, it->second->peekTransaction()), true, false); addedTx.push_back(it->first); } else // now a no @@ -476,20 +474,60 @@ bool LedgerConsensus::updateOurPositions(int sinceClose) removedTx.push_back(it->first); } } - else if (it->second->getAgreeLevel() < AV_PCT_STOP) - stable = false; } + std::map closeTimes; + for (boost::unordered_map::iterator it = mPeerPositions.begin(), + end = mPeerPositions.end(); it != end; ++it) + ++closeTimes[it->second->getCloseTime() - (it->second->getCloseTime() % mCloseResolution)]; + ++closeTimes[mOurPosition->getCloseTime() - (mOurPosition->getCloseTime() % mCloseResolution)]; + + + int neededWeight; + if (mClosePercent < AV_MID_CONSENSUS_TIME) + neededWeight = AV_INIT_CONSENSUS_PCT; + else if (mClosePercent < AV_LATE_CONSENSUS_TIME) + neededWeight = AV_MID_CONSENSUS_PCT; + else neededWeight = AV_LATE_CONSENSUS_PCT; + + int thresh = mPeerPositions.size() * neededWeight / 100; + uint32 closeTime = 0; + for (std::map::iterator it = closeTimes.begin(), end = closeTimes.end(); it != end; ++it) + { + if (it->second > thresh) + { + mHaveCloseTimeConsensus = true; + closeTime = it->first; + } + } + if (closeTime != (mOurPosition->getCloseTime() - (mOurPosition->getCloseTime() % mCloseResolution))) + changes = true; + if (changes) { uint256 newHash = ourPosition->getHash(); - mOurPosition->changePosition(newHash); + mOurPosition->changePosition(newHash, closeTime); if (mProposing) propose(addedTx, removedTx); mapComplete(newHash, ourPosition, false); Log(lsINFO) << "We change our position to " << newHash.GetHex(); } +} - return stable; +bool LedgerConsensus::haveConsensus() +{ + int agree = 0, disagree = 0; + uint256 ourPosition = mOurPosition->getCurrentHash(); + for (boost::unordered_map::iterator it = mPeerPositions.begin(), + end = mPeerPositions.end(); it != end; ++it) + { + if (it->second->getCurrentHash() == ourPosition) + ++agree; + else + ++disagree; + } + int currentValidations = theApp->getValidations().getCurrentValidationCount(mPreviousLedger->getCloseTimeNC()); + return ContinuousLedgerTiming::haveConsensus(mPreviousProposers, agree + disagree, agree, currentValidations, + mPreviousSeconds, mCurrentSeconds); } SHAMap::pointer LedgerConsensus::getTransactionTree(const uint256& hash, bool doAcquire) @@ -546,6 +584,7 @@ void LedgerConsensus::propose(const std::vector& added, const std::vect newcoin::TMProposeSet prop; prop.set_currenttxhash(mOurPosition->getCurrentHash().begin(), 256 / 8); prop.set_proposeseq(mOurPosition->getProposeSeq()); + prop.set_closetime(mOurPosition->getCloseTime()); std::vector pubKey = mOurPosition->getPubKey(); std::vector sig = mOurPosition->sign(); @@ -586,6 +625,7 @@ void LedgerConsensus::addDisputedTransaction(const uint256& txID, const std::vec bool LedgerConsensus::peerPosition(LedgerProposal::pointer newPosition) { LedgerProposal::pointer& currentPosition = mPeerPositions[newPosition->getPeerID()]; + if (currentPosition) { assert(newPosition->getPeerID() == currentPosition->getPeerID()); @@ -598,6 +638,10 @@ bool LedgerConsensus::peerPosition(LedgerProposal::pointer newPosition) return true; } } + else if (newPosition->getProposeSeq() == 0) + { // new initial close time estimate + ++mCloseTimes[newPosition->getCloseTime()]; + } Log(lsINFO) << "Processing peer proposal " << newPosition->getProposeSeq() << "/" << newPosition->getCurrentHash().GetHex(); currentPosition = newPosition; @@ -666,7 +710,7 @@ void LedgerConsensus::applyTransaction(TransactionEngine& engine, SerializedTran try { #endif - TransactionEngineResult result = engine.applyTransaction(*txn, parms, 0); + TransactionEngineResult result = engine.applyTransaction(*txn, parms); if (result > 0) { Log(lsINFO) << " retry"; @@ -725,7 +769,7 @@ void LedgerConsensus::applyTransactions(SHAMap::pointer set, Ledger::pointer led { try { - TransactionEngineResult result = engine.applyTransaction(*it->second, parms, 0); + TransactionEngineResult result = engine.applyTransaction(*it->second, parms); if (result <= 0) { if (result == 0) ++successes; @@ -758,7 +802,16 @@ void LedgerConsensus::accept(SHAMap::pointer set) CanonicalTXSet failedTransactions(set->getHash()); applyTransactions(set, newLCL, failedTransactions, true); newLCL->setClosed(); - newLCL->setAccepted(); + + uint32 closeTime = mOurPosition->getCloseTime(); + bool closeTimeCorrect = true; + if (closeTime == 0) + { // we didn't agree + closeTimeCorrect = false; + closeTime = mPreviousLedger->getCloseTimeNC() + 1; + } + + newLCL->setAccepted(closeTime, mCloseResolution, closeTimeCorrect); newLCL->updateHash(); uint256 newLCLHash = newLCL->getHash(); Log(lsTRACE) << "newLCL " << newLCLHash.GetHex(); @@ -804,6 +857,7 @@ void LedgerConsensus::accept(SHAMap::pointer set) applyTransactions(theApp->getMasterLedger().getCurrentLedger()->peekTransactionMap(), newOL, failedTransactions, false); theApp->getMasterLedger().pushLedger(newLCL, newOL); + mNewLedgerHash = newLCL->getHash(); mState = lcsACCEPTED; sl.unlock(); diff --git a/src/LedgerConsensus.h b/src/LedgerConsensus.h index 8db236487e..235c670686 100644 --- a/src/LedgerConsensus.h +++ b/src/LedgerConsensus.h @@ -2,6 +2,7 @@ #define __LEDGER_CONSENSUS__ #include +#include #include #include @@ -62,32 +63,36 @@ public: void setVote(const uint160& peer, bool votesYes); - bool updatePosition(int timePassed, bool proposing); - int getAgreeLevel(); + bool updatePosition(int percentTime, bool proposing); }; enum LCState { lcsPRE_CLOSE, // We haven't closed our ledger yet, but others might have - lcsPOST_CLOSE, // Ledger closed, but wobble time lcsESTABLISH, // Establishing consensus - lcsCUTOFF, // Past the cutoff for consensus lcsFINISHED, // We have closed on a transaction set lcsACCEPTED, // We have accepted/validated a new last closed ledger - lcsABORTED // Abandoned }; class LedgerConsensus : public boost::enable_shared_from_this { protected: LCState mState; - uint32 mCloseTime; - uint256 mPrevLedgerHash; + uint32 mCloseTime; // The wall time this ledger closed + uint256 mPrevLedgerHash, mNewLedgerHash; Ledger::pointer mPreviousLedger; + LedgerAcquire::pointer mAcquiringLedger; LedgerProposal::pointer mOurPosition; NewcoinAddress mValSeed; bool mProposing, mValidating, mHaveCorrectLCL; + int mCurrentSeconds, mClosePercent, mCloseResolution; + bool mHaveCloseTimeConsensus; + + boost::posix_time::ptime mConsensusStartTime; + int mPreviousProposers; + int mPreviousSeconds; + // Convergence tracking, trusted peers indexed by hash of public key boost::unordered_map mPeerPositions; @@ -101,6 +106,9 @@ protected: // Disputed transactions boost::unordered_map mDisputes; + // Close time estimates + std::map mCloseTimes; + // final accept logic static void Saccept(boost::shared_ptr This, SHAMap::pointer txSet); void accept(SHAMap::pointer txSet); @@ -123,9 +131,9 @@ protected: CanonicalTXSet& failedTransactions, bool final); // manipulating our own position - void takeInitialPosition(Ledger::pointer initialLedger); - bool updateOurPositions(int sinceClose); void statusChange(newcoin::NodeEvent, Ledger::pointer ledger); + void takeInitialPosition(Ledger::pointer initialLedger); + void updateOurPositions(); int getThreshold(); void beginAccept(); void endConsensus(); @@ -142,16 +150,16 @@ public: TransactionAcquire::pointer getAcquiring(const uint256& hash); void mapComplete(const uint256& hash, SHAMap::pointer map, bool acquired); - void abort(); - int timerEntry(); + void timerEntry(); // state handlers - int statePreClose(int secondsSinceClose); - int statePostClose(int secondsSinceClose); - int stateEstablish(int secondsSinceClose); - int stateCutoff(int secondsSinceClose); - int stateFinished(int secondsSinceClose); - int stateAccepted(int secondsSinceClose); + void statePreClose(); + void stateEstablish(); + void stateCutoff(); + void stateFinished(); + void stateAccepted(); + + bool haveConsensus(); bool peerPosition(LedgerProposal::pointer); diff --git a/src/LedgerMaster.cpp b/src/LedgerMaster.cpp index 7d86d5922d..f5bb19646a 100644 --- a/src/LedgerMaster.cpp +++ b/src/LedgerMaster.cpp @@ -42,8 +42,8 @@ void LedgerMaster::pushLedger(Ledger::pointer newLCL, Ledger::pointer newOL) if (newLCL->isAccepted()) { - mLedgerHistory.addAcceptedLedger(mFinalizedLedger); - Log(lsINFO) << "StashAccepted: " << mFinalizedLedger->getHash().GetHex(); + mLedgerHistory.addAcceptedLedger(newLCL); + Log(lsINFO) << "StashAccepted: " << newLCL->getHash().GetHex(); } mFinalizedLedger = newLCL; @@ -54,6 +54,7 @@ void LedgerMaster::pushLedger(Ledger::pointer newLCL, Ledger::pointer newOL) void LedgerMaster::switchLedgers(Ledger::pointer lastClosed, Ledger::pointer current) { + assert(lastClosed && current); mFinalizedLedger = lastClosed; mFinalizedLedger->setClosed(); mFinalizedLedger->setAccepted(); @@ -63,40 +64,25 @@ void LedgerMaster::switchLedgers(Ledger::pointer lastClosed, Ledger::pointer cur mEngine.setLedger(mCurrentLedger); } -void LedgerMaster::beginWobble() +void LedgerMaster::storeLedger(Ledger::pointer ledger) { - boost::recursive_mutex::scoped_lock sl(mLock); - assert(!mWobbleLedger); - mWobbleLedger = boost::make_shared(boost::ref(*mCurrentLedger), true); - mEngine.setDefaultLedger(mCurrentLedger); - mEngine.setAlternateLedger(mWobbleLedger); + mLedgerHistory.addLedger(ledger); } -void LedgerMaster::closeTime() -{ // swap current and wobble ledgers - boost::recursive_mutex::scoped_lock sl(mLock); - assert(mCurrentLedger && mWobbleLedger); - std::swap(mCurrentLedger, mWobbleLedger); - mEngine.setDefaultLedger(mCurrentLedger); - mEngine.setAlternateLedger(mWobbleLedger); -} - -Ledger::pointer LedgerMaster::endWobble() +Ledger::pointer LedgerMaster::closeLedger() { boost::recursive_mutex::scoped_lock sl(mLock); - assert(mWobbleLedger && mCurrentLedger); - Ledger::pointer ret = mWobbleLedger; - mWobbleLedger = Ledger::pointer(); - mEngine.setAlternateLedger(Ledger::pointer()); - return ret; + Ledger::pointer closingLedger = mCurrentLedger; + mCurrentLedger = boost::make_shared(boost::ref(*closingLedger), true); + mEngine.setLedger(mCurrentLedger); + return closingLedger; } TransactionEngineResult LedgerMaster::doTransaction(const SerializedTransaction& txn, uint32 targetLedger, TransactionEngineParams params) { - Ledger::pointer ledger = mEngine.getTransactionLedger(targetLedger); - TransactionEngineResult result = mEngine.applyTransaction(txn, params, ledger); - theApp->getOPs().pubTransaction(ledger, txn, result); + TransactionEngineResult result = mEngine.applyTransaction(txn, params); + theApp->getOPs().pubTransaction(mEngine.getLedger(), txn, result); return result; } diff --git a/src/LedgerMaster.h b/src/LedgerMaster.h index f31d0869ed..9f9603f1bc 100644 --- a/src/LedgerMaster.h +++ b/src/LedgerMaster.h @@ -19,7 +19,6 @@ class LedgerMaster TransactionEngine mEngine; Ledger::pointer mCurrentLedger; // The ledger we are currently processiong - Ledger::pointer mWobbleLedger; // A ledger past its close time Ledger::pointer mFinalizedLedger; // The ledger that most recently closed LedgerHistory mLedgerHistory; @@ -41,9 +40,6 @@ public: // The current ledger is the ledger we believe new transactions should go in Ledger::pointer getCurrentLedger() { return mCurrentLedger; } - // The wobble ledger is a ledger that new transactions can go in if requested - Ledger::pointer getWobbleLedger() { return mWobbleLedger; } - // The finalized ledger is the last closed/accepted ledger Ledger::pointer getClosedLedger() { return mFinalizedLedger; } @@ -52,12 +48,11 @@ public: void pushLedger(Ledger::pointer newLedger); void pushLedger(Ledger::pointer newLCL, Ledger::pointer newOL); + void storeLedger(Ledger::pointer); void switchLedgers(Ledger::pointer lastClosed, Ledger::pointer newCurrent); - void closeTime(); - void beginWobble(); - Ledger::pointer endWobble(); + Ledger::pointer closeLedger(); Ledger::pointer getLedgerBySeq(uint32 index) { diff --git a/src/LedgerNode.cpp b/src/LedgerNode.cpp index 07f4edc5bd..cf9c8439ad 100644 --- a/src/LedgerNode.cpp +++ b/src/LedgerNode.cpp @@ -29,8 +29,7 @@ LedgerStateParms Ledger::writeBack(LedgerStateParms parms, SLE::pointer entry) if (create) { assert(!mAccountStateMap->hasItem(entry->getIndex())); - - if (!mAccountStateMap->addGiveItem(item, false)) + if(!mAccountStateMap->addGiveItem(item, false, false)) // FIXME: TX metadata { assert(false); return lepERROR; @@ -38,7 +37,7 @@ LedgerStateParms Ledger::writeBack(LedgerStateParms parms, SLE::pointer entry) return lepCREATED; } - if (!mAccountStateMap->updateGiveItem(item, false)) + if (!mAccountStateMap->updateGiveItem(item, false, false)) // FIXME: TX metadata { assert(false); return lepERROR; diff --git a/src/LedgerProposal.cpp b/src/LedgerProposal.cpp index 4de5d37f4f..55c7f0afc6 100644 --- a/src/LedgerProposal.cpp +++ b/src/LedgerProposal.cpp @@ -7,39 +7,41 @@ #include "Application.h" #include "HashPrefixes.h" -LedgerProposal::LedgerProposal(const uint256& pLgr, uint32 seq, const uint256& tx, const NewcoinAddress& naPeerPublic) : - mPreviousLedger(pLgr), mCurrentHash(tx), mProposeSeq(seq) +LedgerProposal::LedgerProposal(const uint256& pLgr, uint32 seq, const uint256& tx, uint32 closeTime, + const NewcoinAddress& naPeerPublic) : + mPreviousLedger(pLgr), mCurrentHash(tx), mCloseTime(closeTime), mProposeSeq(seq) { mPublicKey = naPeerPublic; // XXX Validate key. // if (!mKey->SetPubKey(pubKey)) // throw std::runtime_error("Invalid public key in proposal"); - mPreviousLedger = theApp->getMasterLedger().getClosedLedger()->getHash(); mPeerID = mPublicKey.getNodeID(); } -LedgerProposal::LedgerProposal(const NewcoinAddress& naSeed, const uint256& prevLgr, const uint256& position) : - mPreviousLedger(prevLgr), mCurrentHash(position), mProposeSeq(0) +LedgerProposal::LedgerProposal(const NewcoinAddress& naSeed, const uint256& prevLgr, + const uint256& position, uint32 closeTime) : + mPreviousLedger(prevLgr), mCurrentHash(position), mCloseTime(closeTime), mProposeSeq(0) { mPublicKey = NewcoinAddress::createNodePublic(naSeed); mPrivateKey = NewcoinAddress::createNodePrivate(naSeed); mPeerID = mPublicKey.getNodeID(); } -LedgerProposal::LedgerProposal(const uint256& prevLgr, const uint256& position) : - mPreviousLedger(prevLgr), mCurrentHash(position), mProposeSeq(0) +LedgerProposal::LedgerProposal(const uint256& prevLgr, const uint256& position, uint32 closeTime) : + mPreviousLedger(prevLgr), mCurrentHash(position), mCloseTime(closeTime), mProposeSeq(0) { ; } uint256 LedgerProposal::getSigningHash() const { - Serializer s(72); + Serializer s((32 + 32 + 32 + 256 + 256) / 8); s.add32(sHP_Proposal); s.add32(mProposeSeq); + s.add32(mCloseTime); s.add256(mPreviousLedger); s.add256(mCurrentHash); @@ -51,9 +53,10 @@ bool LedgerProposal::checkSign(const std::string& signature, const uint256& sign return mPublicKey.verifyNodePublic(signingHash, signature); } -void LedgerProposal::changePosition(const uint256& newPosition) +void LedgerProposal::changePosition(const uint256& newPosition, uint32 closeTime) { mCurrentHash = newPosition; + mCloseTime = closeTime; ++mProposeSeq; } diff --git a/src/LedgerProposal.h b/src/LedgerProposal.h index 6e924734ed..1490e9d5ee 100644 --- a/src/LedgerProposal.h +++ b/src/LedgerProposal.h @@ -13,7 +13,7 @@ class LedgerProposal protected: uint256 mPreviousLedger, mCurrentHash; - uint32 mProposeSeq; + uint32 mCloseTime, mProposeSeq; uint160 mPeerID; NewcoinAddress mPublicKey; @@ -25,13 +25,14 @@ public: // proposal from peer LedgerProposal(const uint256& prevLgr, uint32 proposeSeq, const uint256& propose, - const NewcoinAddress& naPeerPublic); + uint32 closeTime, const NewcoinAddress& naPeerPublic); // our first proposal - LedgerProposal(const NewcoinAddress& privKey, const uint256& prevLedger, const uint256& position); + LedgerProposal(const NewcoinAddress& privKey, const uint256& prevLedger, const uint256& position, + uint32 closeTime); - // an unsigned proposal - LedgerProposal(const uint256& prevLedger, const uint256& position); + // an unsigned "dummy" proposal for nodes not validating + LedgerProposal(const uint256& prevLedger, const uint256& position, uint32 closeTime); uint256 getSigningHash() const; bool checkSign(const std::string& signature, const uint256& signingHash); @@ -41,11 +42,12 @@ public: const uint256& getCurrentHash() const { return mCurrentHash; } const uint256& getPrevLedger() const { return mPreviousLedger; } uint32 getProposeSeq() const { return mProposeSeq; } + uint32 getCloseTime() const { return mCloseTime; } const NewcoinAddress& peekPublic() const { return mPublicKey; } std::vector getPubKey() const { return mPublicKey.getNodePublic(); } std::vector sign(); - void changePosition(const uint256& newPosition); + void changePosition(const uint256& newPosition, uint32 newCloseTime); }; #endif diff --git a/src/LedgerTiming.cpp b/src/LedgerTiming.cpp index 1425f85ac3..360ae2d341 100644 --- a/src/LedgerTiming.cpp +++ b/src/LedgerTiming.cpp @@ -1,34 +1,55 @@ #include "LedgerTiming.h" +#include -// Returns the number of seconds the ledger should be be open. -int ContinuousLedgerTiming::shouldClose( // How many: +#include + +#include "Log.h" + +// NOTE: First and last times must be repeated +int ContinuousLedgerTiming::LedgerTimeResolution[] = { 10, 10, 20, 30, 60, 90, 120, 120 }; + +// Called when a ledger is open and no close is in progress -- when a transaction is received and no close +// is in process, or when a close completes. Returns the number of seconds the ledger should be be open. +int ContinuousLedgerTiming::shouldClose( bool anyTransactions, - int previousProposers, // proposers there were in the last closing - int proposersClosed, // proposers who have currently closed their ledgers - int previousOpenSeconds, // seconds the previous ledger was open - int currentOpenSeconds) // seconds since the previous ledger closed + int previousProposers, // proposers in the last closing + int proposersClosed, // proposers who have currently closed this ledgers + int previousSeconds, // seconds the previous ledger took to reach consensus + int currentSeconds) // seconds since the previous ledger closed { + assert((previousSeconds > 0) && (previousSeconds < 600)); + assert((currentSeconds >= 0) && (currentSeconds < 600)); + +#if 0 + Log(lsTRACE) << boost::str(boost::format("CLC::shouldClose Trans=%s, Prop: %d/%d, Secs: %d (last:%d)") % + (anyTransactions ? "yes" : "no") % previousProposers % proposersClosed % currentSeconds % previousSeconds); +#endif + if (!anyTransactions) { // no transactions so far this interval if (proposersClosed > (previousProposers / 4)) // did we miss a transaction? - return currentOpenSeconds; - if (previousOpenSeconds > (LEDGER_IDLE_INTERVAL + 2)) // the last ledger was very slow to close - return previousOpenSeconds - 1; + { + Log(lsTRACE) << "no transactions, many proposers: now"; + return currentSeconds; + } + if (previousSeconds > (LEDGER_IDLE_INTERVAL + 2)) // the last ledger was very slow to close + { + Log(lsTRACE) << "slow to close"; + return previousSeconds - 1; + } return LEDGER_IDLE_INTERVAL; // normal idle } - if (previousOpenSeconds == LEDGER_IDLE_INTERVAL) // coming out of idle, close now - return currentOpenSeconds; + if (previousSeconds == LEDGER_IDLE_INTERVAL) // coming out of idle, close now + { + Log(lsTRACE) << "leaving idle, close now"; + return currentSeconds; + } - // If the network is slow, try to synchronize close times - if (previousOpenSeconds > 8) - return (currentOpenSeconds - currentOpenSeconds % 4); - else if (previousOpenSeconds > 4) - return (currentOpenSeconds - currentOpenSeconds % 2); - - return currentOpenSeconds; // this ledger should close now + Log(lsTRACE) << "close now"; + return currentSeconds; // this ledger should close now } // Returns whether we have a consensus or not. If so, we expect all honest nodes @@ -41,21 +62,59 @@ bool ContinuousLedgerTiming::haveConsensus( int previousAgreeTime, // how long it took to agree on the last ledger int currentAgreeTime) // how long we've been trying to agree { + Log(lsTRACE) << boost::str(boost::format("CLC::haveConsensus: prop=%d/%d agree=%d closed=%d time=%d/%d") % + currentProposers % previousProposers % currentAgree % currentClosed % currentAgreeTime % previousAgreeTime); + + if (currentAgreeTime <= LEDGER_MIN_CONSENSUS) + return false; + if (currentProposers < (previousProposers * 3 / 4)) - { // Less than 3/4 of the validators are present, slow down + { // Less than 3/4 of the last ledger's proposers are present, we may need more time if (currentAgreeTime < (previousAgreeTime + 2)) + { + Log(lsTRACE) << "too fast, not enough proposers"; return false; + } } // If 80% of current proposers (plus us) agree on a set, we have consensus - int agreeWeight = (currentAgree * 100 + 100) / (currentProposers + 1); - if (agreeWeight > 80) + if (((currentAgree * 100 + 100) / (currentProposers + 1)) > 80) + { + Log(lsTRACE) << "normal consensus"; return true; + } // If 50% of the nodes on your UNL (minus us) have closed, you should close - int closeWeight = (currentClosed * 100 - 100) / (currentProposers + 1); - if (closeWeight > 50) + if (((currentClosed * 100 - 100) / (currentProposers + 1)) > 50) + { + Log(lsTRACE) << "many closers"; return true; + } + // no consensus yet + Log(lsTRACE) << "no consensus"; return false; } + +int ContinuousLedgerTiming::getNextLedgerTimeResolution(int previousResolution, bool previousAgree, int ledgerSeq) +{ + assert(ledgerSeq); + assert(previousAgree); // TEMPORARY + if ((!previousAgree) && ((ledgerSeq % LEDGER_RES_DECREASE) == 0)) + { // reduce resolution + int i = 1; + while (LedgerTimeResolution[i] != previousResolution) + ++i; + return LedgerTimeResolution[i + 1]; + } + + if ((previousAgree) && ((ledgerSeq % LEDGER_RES_INCREASE) == 0)) + { // increase resolution + int i = 1; + while (LedgerTimeResolution[i] != previousResolution) + ++i; + return LedgerTimeResolution[i - 1]; + } + + return previousResolution; +} diff --git a/src/LedgerTiming.h b/src/LedgerTiming.h index 5431443282..2b580a6057 100644 --- a/src/LedgerTiming.h +++ b/src/LedgerTiming.h @@ -1,66 +1,53 @@ #ifndef __LEDGERTIMING__ #define __LEDGERTIMING__ -#define LEDGER_CLOSE_FAST -// #define LEDGER_CLOSE_SLOW - -#ifdef LEDGER_CLOSE_FAST - -// Time between one ledger close and the next ledger close -# define LEDGER_INTERVAL 30 - -// Time before we take a position -# define LEDGER_WOBBLE_TIME 1 - -// Time we acceleratet avalanche -# define LEDGER_ACCEL_CONVERGE 10 - -// Time we permit avalanche to finish -# define LEDGER_CONVERGE 14 - -// Maximum converge time -# define LEDGER_MAX_CONVERGE 20 - -#define AV_PCT_STOP 85 - -#endif - - - -// BEGIN LEDGER_CLOSE_CONTINUOUS - // The number of seconds a ledger may remain idle before closing # define LEDGER_IDLE_INTERVAL 15 -// How long we wait to transition from inactive to active -# define LEDGER_IDLE_SPIN_TIME 2 +// The number of seconds a validation remains current +# define LEDGER_MAX_INTERVAL 60 -// Avalance tuning (percent of UNL voting yes for us to vote yes) -#define AV_MIN_CONSENSUS 55 -#define AV_AVG_CONSENSUS 65 -#define AV_MAX_CONSENSUS 70 +// The number of seconds we wait minimum to ensure participation +# define LEDGER_MIN_CONSENSUS 2 + +// Initial resolution of ledger close time +# define LEDGER_TIME_ACCURACY 30 + +// How often to increase resolution +# define LEDGER_RES_INCREASE 8 + +// How often to decrease resolution +# define LEDGER_RES_DECREASE 1 + +// Avalanche tuning +#define AV_INIT_CONSENSUS_PCT 50 // percentage of nodes on our UNL that must vote yes + +#define AV_MID_CONSENSUS_TIME 50 // percentage of previous close time before we advance +#define AV_MID_CONSENSUS_PCT 65 // percentage of nodes that most vote yes after advancing + +#define AV_LATE_CONSENSUS_TIME 85 // percentage of previous close time before we advance +#define AV_LATE_CONSENSUS_PCT 70 // percentage of nodes that most vote yes after advancing class ContinuousLedgerTiming { public: + static int LedgerTimeResolution[]; + // Returns the number of seconds the ledger was or should be open // Call when a consensus is reached and when any transaction is relayed to be added static int shouldClose( bool anyTransactions, int previousProposers, int proposersClosed, - int previousOpenSeconds, int currentOpenSeconds); + int previousSeconds, int currentSeconds); static bool haveConsensus( int previousProposers, int currentProposers, int currentAgree, int currentClosed, int previousAgreeTime, int currentAgreeTime); + static int getNextLedgerTimeResolution(int previousResolution, bool previousAgree, int ledgerSeq); }; -// END LEDGER_CLOSE_CONTINUOUS - - - #endif diff --git a/src/NetworkOPs.cpp b/src/NetworkOPs.cpp index aed15e198e..53f77baacf 100644 --- a/src/NetworkOPs.cpp +++ b/src/NetworkOPs.cpp @@ -24,7 +24,8 @@ // there's a functional network. NetworkOPs::NetworkOPs(boost::asio::io_service& io_service, LedgerMaster* pLedgerMaster) : - mMode(omDISCONNECTED),mNetTimer(io_service), mLedgerMaster(pLedgerMaster) + mMode(omDISCONNECTED),mNetTimer(io_service), mLedgerMaster(pLedgerMaster), + mLastCloseProposers(0), mLastCloseConvergeTime(LEDGER_IDLE_INTERVAL) { } @@ -33,7 +34,7 @@ boost::posix_time::ptime NetworkOPs::getNetworkTimePT() return boost::posix_time::second_clock::universal_time(); } -uint64 NetworkOPs::getNetworkTimeNC() +uint32 NetworkOPs::getNetworkTimeNC() { return iToSeconds(getNetworkTimePT()); } @@ -220,10 +221,14 @@ NicknameState::pointer NetworkOPs::getNicknameState(const uint256& uLedger, cons // Json::Value NetworkOPs::getOwnerInfo(const uint256& uLedger, const NewcoinAddress& naAccount) +{ + return getOwnerInfo(mLedgerMaster->getLedgerByHash(uLedger), naAccount); +} + +Json::Value NetworkOPs::getOwnerInfo(Ledger::pointer lpLedger, const NewcoinAddress& naAccount) { Json::Value jvObjects(Json::arrayValue); - Ledger::pointer lpLedger = mLedgerMaster->getLedgerByHash(uLedger); uint256 uRootIndex = lpLedger->getOwnerDirIndex(naAccount.getAccountID()); LedgerStateParms lspNode = lepNONE; @@ -236,9 +241,9 @@ Json::Value NetworkOPs::getOwnerInfo(const uint256& uLedger, const NewcoinAddres do { STVector256 svIndexes = sleNode->getIFieldV256(sfIndexes); - std::vector& vuiIndexes = svIndexes.peekValue(); + const std::vector& vuiIndexes = svIndexes.peekValue(); - BOOST_FOREACH(uint256& uDirEntry, vuiIndexes) + BOOST_FOREACH(const uint256& uDirEntry, vuiIndexes) { LedgerStateParms lspOffer = lepNONE; SLE::pointer sleOffer = lpLedger->getOffer(lspOffer, uDirEntry); @@ -273,17 +278,9 @@ RippleState::pointer NetworkOPs::getRippleState(const uint256& uLedger, const ui // Other // -void NetworkOPs::setStateTimer(int sec) +void NetworkOPs::setStateTimer() { // set timer early if ledger is closing - if (!mConsensus && ((mMode == omFULL) || (mMode == omTRACKING))) - { - uint64 consensusTime = mLedgerMaster->getCurrentLedger()->getCloseTimeNC() - LEDGER_WOBBLE_TIME; - uint64 now = getNetworkTimeNC(); - - if (now >= consensusTime) sec = 1; - else if (sec > (consensusTime - now)) sec = (consensusTime - now); - } - mNetTimer.expires_from_now(boost::posix_time::seconds(sec)); + mNetTimer.expires_from_now(boost::posix_time::seconds(1)); mNetTimer.async_wait(boost::bind(&NetworkOPs::checkState, this, boost::asio::placeholders::error)); } @@ -320,7 +317,7 @@ void NetworkOPs::checkState(const boost::system::error_code& result) Log(lsWARNING) << "Node count (" << peerList.size() << ") has fallen below quorum (" << theConfig.NETWORK_QUORUM << ")."; } - setStateTimer(5); + setStateTimer(); return; } if (mMode == omDISCONNECTED) @@ -331,7 +328,8 @@ void NetworkOPs::checkState(const boost::system::error_code& result) if (mConsensus) { - setStateTimer(mConsensus->timerEntry()); + mConsensus->timerEntry(); + setStateTimer(); return; } @@ -339,6 +337,7 @@ void NetworkOPs::checkState(const boost::system::error_code& result) // If full or tracking, check only at wobble time! uint256 networkClosed; bool ledgerChange = checkLastClosedLedger(peerList, networkClosed); + assert(networkClosed.isNonZero()); // WRITEME: Unless we are in omFULL and in the process of doing a consensus, // we must count how many nodes share our LCL, how many nodes disagree with our LCL, @@ -361,7 +360,7 @@ void NetworkOPs::checkState(const boost::system::error_code& result) (theApp->getMasterLedger().getCurrentLedger()->getCloseTimeNC() + 4)) setMode(omFULL); else - Log(lsWARNING) << "Too late to go to full, will try in consensus window"; + Log(lsINFO) << "Will try to go to FULL in consensus window"; } if (mMode == omFULL) @@ -369,13 +368,11 @@ void NetworkOPs::checkState(const boost::system::error_code& result) // check if the ledger is bad enough to go to omTRACKING } - int secondsToClose = theApp->getMasterLedger().getCurrentLedger()->getCloseTimeNC() - - theApp->getOPs().getNetworkTimeNC(); - if ((!mConsensus) && (secondsToClose < LEDGER_WOBBLE_TIME)) // pre close wobble + if ((!mConsensus) && (mMode != omDISCONNECTED)) beginConsensus(networkClosed, theApp->getMasterLedger().getCurrentLedger()); if (mConsensus) - setStateTimer(mConsensus->timerEntry()); - else setStateTimer(4); + mConsensus->timerEntry(); + setStateTimer(); } bool NetworkOPs::checkLastClosedLedger(const std::vector& peerList, uint256& networkClosed) @@ -397,11 +394,11 @@ bool NetworkOPs::checkLastClosedLedger(const std::vector& peerLis } Ledger::pointer ourClosed = mLedgerMaster->getClosedLedger(); - uint256 closedLedger=0; - if(theConfig.LEDGER_CREATOR || ourClosed->getLedgerSeq() > 100) + uint256 closedLedger = ourClosed->getHash(); + ValidationCount& ourVC = ledgers[closedLedger]; + + if ((theConfig.LEDGER_CREATOR) && (mMode >= omTRACKING)) { - closedLedger = ourClosed->getHash(); - ValidationCount& ourVC = ledgers[closedLedger]; ++ourVC.nodesUsing; ourVC.highNode = theApp->getWallet().getNodePublic(); } @@ -445,25 +442,34 @@ bool NetworkOPs::checkLastClosedLedger(const std::vector& peerLis networkClosed = closedLedger; if (!switchLedgers) + { + if (mAcquiringLedger) + { + mAcquiringLedger->abort(); + theApp->getMasterLedgerAcquire().dropLedger(mAcquiringLedger->getHash()); + mAcquiringLedger = LedgerAcquire::pointer(); + } return false; + } Log(lsWARNING) << "We are not running on the consensus ledger"; Log(lsINFO) << "Our LCL " << ourClosed->getHash().GetHex(); Log(lsINFO) << "Net LCL " << closedLedger.GetHex(); - if ((mMode == omTRACKING) || (mMode == omFULL)) setMode(omCONNECTED); + if ((mMode == omTRACKING) || (mMode == omFULL)) + setMode(omCONNECTED); Ledger::pointer consensus = mLedgerMaster->getLedgerByHash(closedLedger); if (!consensus) { - Log(lsINFO) << "Acquiring consensus ledger"; - LedgerAcquire::pointer acq = theApp->getMasterLedgerAcquire().findCreate(closedLedger); - if (!acq || acq->isFailed()) + Log(lsINFO) << "Acquiring consensus ledger " << closedLedger.GetHex(); + LedgerAcquire::pointer mAcquiringLedger = theApp->getMasterLedgerAcquire().findCreate(closedLedger); + if (!mAcquiringLedger || mAcquiringLedger->isFailed()) { theApp->getMasterLedgerAcquire().dropLedger(closedLedger); Log(lsERROR) << "Network ledger cannot be acquired"; return true; } - if (!acq->isComplete()) + if (!mAcquiringLedger->isComplete()) { // add more peers int count = 0; std::vector peers=theApp->getConnectionPool().getPeerVector(); @@ -473,7 +479,7 @@ bool NetworkOPs::checkLastClosedLedger(const std::vector& peerLis if ((*it)->getClosedLedgerHash() == closedLedger) { ++count; - acq->peerHas(*it); + mAcquiringLedger->peerHas(*it); } } if (!count) @@ -482,12 +488,12 @@ bool NetworkOPs::checkLastClosedLedger(const std::vector& peerLis it != end; ++it) { if ((*it)->isConnected()) - acq->peerHas(*it); + mAcquiringLedger->peerHas(*it); } } return true; } - consensus = acq->getLedger(); + consensus = mAcquiringLedger->getLedger(); } // FIXME: If this rewinds the ledger sequence, or has the same sequence, we should update the status on @@ -502,12 +508,6 @@ void NetworkOPs::switchLastClosedLedger(Ledger::pointer newLedger) Log(lsERROR) << "ABNORMAL Switching last closed ledger to " << newLedger->getHash().GetHex(); - if (mConsensus) - { - mConsensus->abort(); - mConsensus = boost::shared_ptr(); - } - newLedger->setClosed(); Ledger::pointer openLedger = boost::make_shared(false, boost::ref(*newLedger)); mLedgerMaster->switchLedgers(newLedger, openLedger); @@ -526,7 +526,7 @@ void NetworkOPs::switchLastClosedLedger(Ledger::pointer newLedger) int NetworkOPs::beginConsensus(const uint256& networkClosed, Ledger::pointer closingLedger) { - Log(lsINFO) << "Ledger close time for ledger " << closingLedger->getLedgerSeq(); + Log(lsINFO) << "Consensus time for ledger " << closingLedger->getLedgerSeq(); Log(lsINFO) << " LCL is " << closingLedger->getParentHash().GetHex(); Ledger::pointer prevLedger = mLedgerMaster->getLedgerByHash(closingLedger->getParentHash()); @@ -540,19 +540,17 @@ int NetworkOPs::beginConsensus(const uint256& networkClosed, Ledger::pointer clo assert(closingLedger->getParentHash() == mLedgerMaster->getClosedLedger()->getHash()); // Create a consensus object to get consensus on this ledger - if (!!mConsensus) - mConsensus->abort(); + assert(!mConsensus); prevLedger->setImmutable(); mConsensus = boost::make_shared( - networkClosed, - prevLedger, theApp->getMasterLedger().getCurrentLedger()->getCloseTimeNC()); + networkClosed, prevLedger, theApp->getMasterLedger().getCurrentLedger()->getCloseTimeNC()); - Log(lsDEBUG) << "Pre-close time, initiating consensus engine"; + Log(lsDEBUG) << "Initiating consensus engine"; return mConsensus->startup(); } // <-- bool: true to relay -bool NetworkOPs::recvPropose(uint32 proposeSeq, const uint256& proposeHash, +bool NetworkOPs::recvPropose(uint32 proposeSeq, const uint256& proposeHash, uint32 closeTime, const std::string& pubKey, const std::string& signature) { // JED: does mConsensus need to be locked? @@ -561,7 +559,7 @@ bool NetworkOPs::recvPropose(uint32 proposeSeq, const uint256& proposeHash, // XXX Take a vuc for pubkey. // Get a preliminary hash to use to suppress duplicates - Serializer s; + Serializer s(128); s.add32(proposeSeq); s.add32(getCurrentLedgerID()); s.addRaw(pubKey); @@ -575,14 +573,14 @@ bool NetworkOPs::recvPropose(uint32 proposeSeq, const uint256& proposeHash, } if (!mConsensus) - { + { // FIXME: CLC Log(lsWARNING) << "Received proposal when full but not during consensus window"; return false; } NewcoinAddress naPeerPublic = NewcoinAddress::createNodePublic(strCopy(pubKey)); LedgerProposal::pointer proposal = - boost::make_shared(mConsensus->getLCL(), proposeSeq, proposeHash, naPeerPublic); + boost::make_shared(mConsensus->getLCL(), proposeSeq, proposeHash, closeTime, naPeerPublic); if (!proposal->checkSign(signature)) { // Note that if the LCL is different, the signature check will fail Log(lsWARNING) << "Ledger proposal fails signature check"; @@ -590,7 +588,6 @@ bool NetworkOPs::recvPropose(uint32 proposeSeq, const uint256& proposeHash, } // Is this node on our UNL? - // XXX Is this right? if (!theApp->getUNL().nodeInUNL(proposal->peekPublic())) { Log(lsINFO) << "Untrusted proposal: " << naPeerPublic.humanNodePublic() << " " << @@ -643,14 +640,8 @@ void NetworkOPs::endConsensus() void NetworkOPs::setMode(OperatingMode om) { if (mMode == om) return; - if (mMode == omFULL) - { - if (mConsensus) - { - mConsensus->abort(); - mConsensus = boost::shared_ptr(); - } - } + if ((om >= omCONNECTED) && (mMode == omDISCONNECTED)) + mConnectTime = boost::posix_time::second_clock::universal_time(); Log l((om < mMode) ? lsWARNING : lsINFO); if (om == omDISCONNECTED) l << "STATE->Disonnected"; else if (om == omCONNECTED) l << "STATE->Connected"; @@ -728,9 +719,23 @@ Json::Value NetworkOPs::getServerInfo() } // -// Monitoring:: publisher side +// Monitoring: publisher side // +Json::Value NetworkOPs::pubBootstrapAccountInfo(const Ledger::pointer& lpAccepted, const NewcoinAddress& naAccountID) +{ + Json::Value jvObj(Json::objectValue); + + jvObj["type"] = "accountInfoBootstrap"; + jvObj["account"] = naAccountID.humanAccountID(); + jvObj["owner"] = getOwnerInfo(lpAccepted, naAccountID); + jvObj["seq"] = lpAccepted->getLedgerSeq(); + jvObj["hash"] = lpAccepted->getHash().ToString(); + jvObj["time"] = Json::Value::UInt(lpAccepted->getCloseTimeNC()); + + return jvObj; +} + void NetworkOPs::pubAccountInfo(const NewcoinAddress& naAccountID, const Json::Value& jvObj) { boost::interprocess::sharable_lock sl(mMonitorLock); @@ -827,6 +832,24 @@ void NetworkOPs::pubLedger(const Ledger::pointer& lpAccepted) } } } + + // Publish bootsrap information for accounts. + { + boost::interprocess::scoped_lock sl(mMonitorLock); + + BOOST_FOREACH(const subInfoMapType::iterator::value_type& it, mBootAccountInfo) + { + Json::Value jvObj = pubBootstrapAccountInfo(lpAccepted, NewcoinAddress::createAccountID(it.first)); + + BOOST_FOREACH(InfoSub* ispListener, it.second) + { + ispListener->send(jvObj); + } + } + mBootAccountInfo.clear(); + } + + // XXX Publish delta information for accounts. } Json::Value NetworkOPs::transJson(const SerializedTransaction& stTxn, TransactionEngineResult terResult, const std::string& strStatus, int iSeq, const std::string& strType) @@ -917,7 +940,26 @@ void NetworkOPs::subAccountInfo(InfoSub* ispListener, const boost::unordered_set BOOST_FOREACH(const NewcoinAddress& naAccountID, vnaAccountIDs) { - subInfoMapType::iterator simIterator = mSubAccountInfo.find(naAccountID.getAccountID()); + // Register for bootstrap info. + subInfoMapType::iterator simIterator; + + simIterator = mBootAccountInfo.find(naAccountID.getAccountID()); + if (simIterator == mBootAccountInfo.end()) + { + // Not found + boost::unordered_set usisElement; + + usisElement.insert(ispListener); + mBootAccountInfo.insert(simIterator, make_pair(naAccountID.getAccountID(), usisElement)); + } + else + { + // Found + simIterator->second.insert(ispListener); + } + + // Register for messages. + simIterator = mSubAccountInfo.find(naAccountID.getAccountID()); if (simIterator == mSubAccountInfo.end()) { // Not found @@ -1009,6 +1051,15 @@ void NetworkOPs::unsubAccountTransaction(InfoSub* ispListener, const boost::unor } } +void NetworkOPs::newLCL(int proposers, int convergeTime, const uint256& ledgerHash) +{ + assert(convergeTime); + mLastCloseProposers = proposers; + mLastCloseConvergeTime = convergeTime; + mLastCloseHash = ledgerHash; +} + + #if 0 void NetworkOPs::subAccountChanges(InfoSub* ispListener, const uint256 uLedgerHash) { diff --git a/src/NetworkOPs.h b/src/NetworkOPs.h index 15f5b0355f..652be3fc12 100644 --- a/src/NetworkOPs.h +++ b/src/NetworkOPs.h @@ -6,6 +6,7 @@ #include "NicknameState.h" #include "RippleState.h" #include "SerializedValidation.h" +#include "LedgerAcquire.h" #include #include @@ -46,19 +47,27 @@ public: protected: OperatingMode mMode; + boost::posix_time::ptime mConnectTime; boost::asio::deadline_timer mNetTimer; boost::shared_ptr mConsensus; LedgerMaster* mLedgerMaster; + LedgerAcquire::pointer mAcquiringLedger; void setMode(OperatingMode); - typedef boost::unordered_map > subInfoMapType; + typedef boost::unordered_map > subInfoMapType; typedef boost::unordered_map >::value_type subInfoMapValue; - typedef boost::unordered_map >::iterator subInfoMapIterator; + typedef boost::unordered_map >::iterator subInfoMapIterator; + + // last ledger close + int mLastCloseProposers, mLastCloseConvergeTime; + uint256 mLastCloseHash; + uint32 mLastCloseNetTime; // XXX Split into more locks. boost::interprocess::interprocess_upgradable_mutex mMonitorLock; + subInfoMapType mBootAccountInfo; subInfoMapType mSubAccountInfo; subInfoMapType mSubAccountTransaction; boost::unordered_set mSubLedger; // ledger accepteds @@ -70,11 +79,13 @@ protected: void pubTransactionAll(const Ledger::pointer& lpCurrent, const SerializedTransaction& stTxn, TransactionEngineResult terResult, const char* pState); void pubTransactionAccounts(const Ledger::pointer& lpCurrent, const SerializedTransaction& stTxn, TransactionEngineResult terResult, const char* pState); + Json::Value pubBootstrapAccountInfo(const Ledger::pointer& lpAccepted, const NewcoinAddress& naAccountID); + public: NetworkOPs(boost::asio::io_service& io_service, LedgerMaster* pLedgerMaster); // network information - uint64 getNetworkTimeNC(); + uint32 getNetworkTimeNC(); boost::posix_time::ptime getNetworkTimePT(); uint32 getCurrentLedgerID(); OperatingMode getOperatingMode() { return mMode; } @@ -123,6 +134,7 @@ public: // Json::Value getOwnerInfo(const uint256& uLedger, const NewcoinAddress& naAccount); + Json::Value getOwnerInfo(Ledger::pointer lpLedger, const NewcoinAddress& naAccount); // // Ripple functions @@ -152,7 +164,7 @@ public: const std::vector& myNode, std::list< std::vector >& newNodes); // ledger proposal/close functions - bool recvPropose(uint32 proposeSeq, const uint256& proposeHash, + bool recvPropose(uint32 proposeSeq, const uint256& proposeHash, uint32 closeTime, const std::string& pubKey, const std::string& signature); bool gotTXData(boost::shared_ptr peer, const uint256& hash, const std::list& nodeIDs, const std::list< std::vector >& nodeData); @@ -167,7 +179,12 @@ public: bool checkLastClosedLedger(const std::vector&, uint256& networkClosed); int beginConsensus(const uint256& networkClosed, Ledger::pointer closingLedger); void endConsensus(); - void setStateTimer(int seconds); + void setStateTimer(); + void newLCL(int proposers, int convergeTime, const uint256& ledgerHash); + int getPreviousProposers() { return mLastCloseProposers; } + int getPreviousSeconds() { return mLastCloseConvergeTime; } + uint32 getLastCloseNetTime() { return mLastCloseNetTime; } + void setLastCloseNetTime(uint32 t) { mLastCloseNetTime = t; } Json::Value getServerInfo(); // client information retrieval functions diff --git a/src/Peer.cpp b/src/Peer.cpp index d92a0328ff..22994c3cf4 100644 --- a/src/Peer.cpp +++ b/src/Peer.cpp @@ -571,14 +571,15 @@ void Peer::recvHello(newcoin::TMHello& packet) // Cancel verification timeout. (void) mVerifyTimer.cancel(); - uint64 minTime = theApp->getOPs().getNetworkTimeNC() - 4; - uint64 maxTime = minTime + 8; + uint32 minTime = theApp->getOPs().getNetworkTimeNC() - 4; + uint32 maxTime = minTime + 8; if (packet.has_nettime() && ((packet.nettime() < minTime) || (packet.nettime() > maxTime))) { - Log(lsINFO) << "Recv(Hello): Disconnect: Clocks are too far off"; + Log(lsINFO) << "Recv(Hello): Disconnect: Clock is far off"; } - else if (packet.protoversionmin() < MAKE_VERSION_INT(MIN_PROTO_MAJOR, MIN_PROTO_MINOR)) + + if (packet.protoversionmin() < MAKE_VERSION_INT(MIN_PROTO_MAJOR, MIN_PROTO_MINOR)) { Log(lsINFO) << "Recv(Hello): Server requires protocol version " << GET_VERSION_MAJOR(packet.protoversion()) << "." << GET_VERSION_MINOR(packet.protoversion()) @@ -716,7 +717,8 @@ void Peer::recvPropose(newcoin::TMProposeSet& packet) uint256 currentTxHash; memcpy(currentTxHash.begin(), packet.currenttxhash().data(), 32); - if(theApp->getOPs().recvPropose(proposeSeq, currentTxHash, packet.nodepubkey(), packet.signature())) + if(theApp->getOPs().recvPropose(proposeSeq, currentTxHash, packet.closetime(), + packet.nodepubkey(), packet.signature())) { // FIXME: Not all nodes will want proposals PackedMessage::pointer message = boost::make_shared(packet, newcoin::mtPROPOSE_LEDGER); theApp->getConnectionPool().relayMessage(this, message); @@ -889,7 +891,7 @@ void Peer::recvStatus(newcoin::TMStatusChange& packet) if (packet.newevent() == newcoin::neLOST_SYNC) { - Log(lsTRACE) << "peer has lost sync" << getIP(); + Log(lsTRACE) << "peer has lost sync " << getIP(); mPreviousLedgerHash.zero(); mClosedLedgerHash.zero(); return; @@ -1111,6 +1113,11 @@ void Peer::recvLedger(newcoin::TMLedgerData& packet) punishPeer(PP_UNWANTED_DATA); } +bool Peer::hasLedger(const uint256& hash) const +{ + return (hash == mClosedLedgerHash) || (hash == mPreviousLedgerHash); +} + // Get session information we can sign to prevent man in the middle attack. // (both sides get the same information, neither side controls it) void Peer::getSessionCookie(std::string& strDst) diff --git a/src/Peer.h b/src/Peer.h index f06a8c3faa..13cf6b79bc 100644 --- a/src/Peer.h +++ b/src/Peer.h @@ -159,6 +159,7 @@ public: static PackedMessage::pointer createGetFullLedger(uint256& hash); uint256 getClosedLedgerHash() const { return mClosedLedgerHash; } + bool hasLedger(const uint256& hash) const; NewcoinAddress getNodePublic() const { return mNodePublic; } void cycleStatus() { mPreviousLedgerHash = mClosedLedgerHash; mClosedLedgerHash.zero(); } }; diff --git a/src/RPCServer.cpp b/src/RPCServer.cpp index d6857c4b67..005090bdb6 100644 --- a/src/RPCServer.cpp +++ b/src/RPCServer.cpp @@ -949,6 +949,7 @@ Json::Value RPCServer::doOfferCancel(const Json::Value ¶ms) } // owner_info || +// owner_info || [] Json::Value RPCServer::doOwnerInfo(const Json::Value& params) { std::string strIdent = params[0u].asString(); diff --git a/src/SHAMap.cpp b/src/SHAMap.cpp index cc27ed70b6..116cc470bf 100644 --- a/src/SHAMap.cpp +++ b/src/SHAMap.cpp @@ -46,6 +46,13 @@ SHAMap::SHAMap(uint32 seq) : mSeq(seq), mState(Modifying) mTNByID[*root] = root; } +SHAMap::SHAMap(const uint256& hash) : mSeq(0), mState(Synching) +{ // FIXME: Need to acquire root node + root = boost::make_shared(mSeq, SHAMapNode(0, uint256())); + root->makeInner(); + mTNByID[*root] = root; +} + SHAMap::pointer SHAMap::snapShot(bool isMutable) { // Return a new SHAMap that is an immutable snapshot of this one // Initially nodes are shared, but CoW is forced on both ledgers @@ -158,7 +165,7 @@ SHAMapTreeNode::pointer SHAMap::walkTo(const uint256& id, bool modify) SHAMapTreeNode* SHAMap::walkToPointer(const uint256& id) { - SHAMapTreeNode* inNode = &*root; + SHAMapTreeNode* inNode = root.get(); while (!inNode->isLeaf()) { int branch = inNode->selectBranch(id); @@ -201,12 +208,12 @@ SHAMapTreeNode* SHAMap::getNodePointer(const SHAMapNode& id, const uint256& hash { // fast, but you do not hold a reference boost::unordered_map::iterator it = mTNByID.find(id); if (it != mTNByID.end()) - return &*it->second; + return it->second.get(); SHAMapTreeNode::pointer node = fetchNodeExternal(id, hash); if (!mTNByID.insert(std::make_pair(id, node)).second) assert(false); - return &*node; + return node.get(); } void SHAMap::returnNode(SHAMapTreeNode::pointer& node, bool modify) @@ -338,13 +345,13 @@ void SHAMap::eraseChildren(SHAMapTreeNode::pointer node) SHAMapItem::pointer SHAMap::peekFirstItem() { boost::recursive_mutex::scoped_lock sl(mLock); - return firstBelow(&*root); + return firstBelow(root.get()); } SHAMapItem::pointer SHAMap::peekLastItem() { boost::recursive_mutex::scoped_lock sl(mLock); - return lastBelow(&*root); + return lastBelow(root.get()); } SHAMapItem::pointer SHAMap::peekNextItem(const uint256& id) @@ -366,7 +373,7 @@ SHAMapItem::pointer SHAMap::peekNextItem(const uint256& id) if(!node->isEmptyBranch(i)) { node = getNode(node->getChildNodeID(i), node->getChildHash(i), false); - SHAMapItem::pointer item = firstBelow(&*node); + SHAMapItem::pointer item = firstBelow(node.get()); if (!item) throw std::runtime_error("missing node"); return item; } @@ -394,7 +401,7 @@ SHAMapItem::pointer SHAMap::peekPrevItem(const uint256& id) if(!node->isEmptyBranch(i)) { node = getNode(node->getChildNodeID(i), node->getChildHash(i), false); - SHAMapItem::pointer item = firstBelow(&*node); + SHAMapItem::pointer item = firstBelow(node.get()); if (!item) throw std::runtime_error("missing node"); return item; } @@ -465,7 +472,7 @@ bool SHAMap::delItem(const uint256& id) } else if(bc==1) { // pull up on the thread - SHAMapItem::pointer item = onlyBelow(&*node); + SHAMapItem::pointer item = onlyBelow(node.get()); if(item) { eraseChildren(node); @@ -488,14 +495,15 @@ bool SHAMap::delItem(const uint256& id) return true; } -bool SHAMap::addGiveItem(SHAMapItem::pointer item, bool isTransaction) +bool SHAMap::addGiveItem(SHAMapItem::pointer item, bool isTransaction, bool hasMeta) { // add the specified item, does not update #ifdef ST_DEBUG std::cerr << "aGI " << item->getTag().GetHex() << std::endl; #endif uint256 tag = item->getTag(); - SHAMapTreeNode::TNType type = isTransaction ? SHAMapTreeNode::tnTRANSACTION : SHAMapTreeNode::tnACCOUNT_STATE; + SHAMapTreeNode::TNType type = !isTransaction ? SHAMapTreeNode::tnACCOUNT_STATE : + (hasMeta ? SHAMapTreeNode::tnTRANSACTION_MD : SHAMapTreeNode::tnTRANSACTION_NM); boost::recursive_mutex::scoped_lock sl(mLock); assert(mState != Immutable); @@ -580,12 +588,12 @@ bool SHAMap::addGiveItem(SHAMapItem::pointer item, bool isTransaction) return true; } -bool SHAMap::addItem(const SHAMapItem& i, bool isTransaction) +bool SHAMap::addItem(const SHAMapItem& i, bool isTransaction, bool hasMetaData) { - return addGiveItem(boost::make_shared(i), isTransaction); + return addGiveItem(boost::make_shared(i), isTransaction, hasMetaData); } -bool SHAMap::updateGiveItem(SHAMapItem::pointer item, bool isTransaction) +bool SHAMap::updateGiveItem(SHAMapItem::pointer item, bool isTransaction, bool hasMeta) { // can't change the tag but can change the hash uint256 tag = item->getTag(); @@ -605,7 +613,8 @@ bool SHAMap::updateGiveItem(SHAMapItem::pointer item, bool isTransaction) } returnNode(node, true); - if (!node->setItem(item, isTransaction ? SHAMapTreeNode::tnTRANSACTION : SHAMapTreeNode::tnACCOUNT_STATE)) + if (!node->setItem(item, !isTransaction ? SHAMapTreeNode::tnACCOUNT_STATE : + (hasMeta ? SHAMapTreeNode::tnTRANSACTION_MD : SHAMapTreeNode::tnTRANSACTION_NM))) { Log(lsWARNING) << "SHAMap setItem, no change"; return true; @@ -747,8 +756,8 @@ BOOST_AUTO_TEST_CASE( SHAMap_test ) SHAMap sMap; SHAMapItem i1(h1, IntToVUC(1)), i2(h2, IntToVUC(2)), i3(h3, IntToVUC(3)), i4(h4, IntToVUC(4)), i5(h5, IntToVUC(5)); - if(!sMap.addItem(i2, true)) BOOST_FAIL("no add"); - if(!sMap.addItem(i1, true)) BOOST_FAIL("no add"); + if(!sMap.addItem(i2, true, false)) BOOST_FAIL("no add"); + if(!sMap.addItem(i1, true, false)) BOOST_FAIL("no add"); SHAMapItem::pointer i; @@ -759,9 +768,9 @@ BOOST_AUTO_TEST_CASE( SHAMap_test ) i = sMap.peekNextItem(i->getTag()); if (i) BOOST_FAIL("bad traverse"); - sMap.addItem(i4, true); + sMap.addItem(i4, true, false); sMap.delItem(i2.getTag()); - sMap.addItem(i3, true); + sMap.addItem(i3, true, false); i = sMap.peekFirstItem(); if (!i || (*i != i1)) BOOST_FAIL("bad traverse"); diff --git a/src/SHAMap.h b/src/SHAMap.h index 08685a6b39..cbd1017180 100644 --- a/src/SHAMap.h +++ b/src/SHAMap.h @@ -61,7 +61,7 @@ public: bool operator!=(const uint256&) const; bool operator<=(const SHAMapNode&) const; bool operator>=(const SHAMapNode&) const; - bool isRoot() const { return mDepth==0; } + bool isRoot() const { return mDepth == 0; } virtual std::string getString() const; void dump() const; @@ -133,10 +133,11 @@ public: enum TNType { - tnERROR = 0, - tnINNER = 1, - tnTRANSACTION = 2, - tnACCOUNT_STATE = 3 + tnERROR = 0, + tnINNER = 1, + tnTRANSACTION_NM = 2, // transaction, no metadata + tnTRANSACTION_MD = 3, // transaction, with metadata + tnACCOUNT_STATE = 4 }; private: @@ -173,11 +174,13 @@ public: TNType getType() const { return mType; } // type functions - bool isLeaf() const { return (mType == tnTRANSACTION) || (mType == tnACCOUNT_STATE); } + bool isLeaf() const { return (mType == tnTRANSACTION_NM) || (mType == tnTRANSACTION_MD) || + (mType == tnACCOUNT_STATE); } bool isInner() const { return mType == tnINNER; } bool isValid() const { return mType != tnERROR; } - bool isTransaction() const { return mType != tnTRANSACTION; } - bool isAccountState() const { return mType != tnACCOUNT_STATE; } + bool isTransaction() const { return (mType == tnTRANSACTION_NM) || (mType == tnTRANSACTION_MD); } + bool hasMetaData() const { return mType == tnTRANSACTION_MD; } + bool isAccountState() const { return mType == tnACCOUNT_STATE; } // inner node functions bool isInnerNode() const { return !mItem; } @@ -287,6 +290,7 @@ public: // build new map SHAMap(uint32 seq = 0); + SHAMap(const uint256& hash); // Returns a new map that's a snapshot of this one. Force CoW SHAMap::pointer snapShot(bool isMutable); @@ -299,15 +303,15 @@ public: // normal hash access functions bool hasItem(const uint256& id); bool delItem(const uint256& id); - bool addItem(const SHAMapItem& i, bool isTransaction); - bool updateItem(const SHAMapItem& i, bool isTransaction); + bool addItem(const SHAMapItem& i, bool isTransaction, bool hasMeta); + bool updateItem(const SHAMapItem& i, bool isTransaction, bool hasMeta); SHAMapItem getItem(const uint256& id); uint256 getHash() const { return root->getNodeHash(); } uint256 getHash() { return root->getNodeHash(); } // save a copy if you have a temporary anyway - bool updateGiveItem(SHAMapItem::pointer, bool isTransaction); - bool addGiveItem(SHAMapItem::pointer, bool isTransaction); + bool updateGiveItem(SHAMapItem::pointer, bool isTransaction, bool hasMeta); + bool addGiveItem(SHAMapItem::pointer, bool isTransaction, bool hasMeta); // save a copy if you only need a temporary SHAMapItem::pointer peekItem(const uint256& id); diff --git a/src/SHAMapNodes.cpp b/src/SHAMapNodes.cpp index 32d9b65d80..95fdcb1f9d 100644 --- a/src/SHAMapNodes.cpp +++ b/src/SHAMapNodes.cpp @@ -197,7 +197,7 @@ SHAMapTreeNode::SHAMapTreeNode(const SHAMapNode& id, const std::vector 3)) + if ((type < 0) || (type > 4)) { #ifdef DEBUG std::cerr << "Invalid wire format node" << std::endl; @@ -210,14 +210,14 @@ SHAMapTreeNode::SHAMapTreeNode(const SHAMapNode& id, const std::vector(s.getPrefixHash(sHP_TransactionID), s.peekData()); - mType = tnTRANSACTION; + mType = tnTRANSACTION_NM; } else if (type == 1) { // account state if (len < (256 / 8)) throw std::runtime_error("short AS node"); uint256 u; - s.get256(u, len - 32); + s.get256(u, len - (256 / 8)); s.chop(256 / 8); if (u.isZero()) throw std::runtime_error("invalid AS node"); mItem = boost::make_shared(u, s.peekData()); @@ -242,6 +242,18 @@ SHAMapTreeNode::SHAMapTreeNode(const SHAMapNode& id, const std::vector(u, s.peekData()); + mType = tnTRANSACTION_MD; + } } if (format == STN_ARF_PREFIXED) @@ -258,8 +270,8 @@ SHAMapTreeNode::SHAMapTreeNode(const SHAMapNode& id, const std::vector(s.getSHA512Half(), s.peekData()); - mType = tnTRANSACTION; + mItem = boost::make_shared(Serializer::getSHA512Half(rawNode), s.peekData()); + mType = tnTRANSACTION_NM; } else if (prefix == sHP_LeafNode) { @@ -271,7 +283,7 @@ SHAMapTreeNode::SHAMapTreeNode(const SHAMapNode& id, const std::vector(u, s.peekData()); + mItem = boost::make_shared(u, s.peekData()); mType = tnACCOUNT_STATE; } else if (prefix == sHP_InnerNode) @@ -282,6 +294,13 @@ SHAMapTreeNode::SHAMapTreeNode(const SHAMapNode& id, const std::vector(txID, s.peekData()); + mType = tnTRANSACTION_MD; + } else { Log(lsINFO) << "Unknown node prefix " << std::hex << prefix << std::dec; @@ -310,16 +329,20 @@ bool SHAMapTreeNode::updateHash() } else if (mType == tnACCOUNT_STATE) { - Serializer s; + Serializer s((256 + 32) / 8 + mItem->peekData().size()); s.add32(sHP_LeafNode); mItem->addRaw(s); s.add256(mItem->getTag()); nh = s.getSHA512Half(); } - else if (mType == tnTRANSACTION) + else if (mType == tnTRANSACTION_NM) { nh = Serializer::getPrefixHash(sHP_TransactionID, mItem->peekData()); } + else if (mType == tnTRANSACTION_MD) + { + nh = Serializer::getPrefixHash(sHP_TransactionNode, mItem->peekData()); + } else assert(false); if (nh == mHash) return false; @@ -375,7 +398,7 @@ void SHAMapTreeNode::addRaw(Serializer& s, int format) s.add8(1); } } - else if (mType == tnTRANSACTION) + else if (mType == tnTRANSACTION_NM) { if (format == STN_ARF_PREFIXED) { @@ -388,7 +411,22 @@ void SHAMapTreeNode::addRaw(Serializer& s, int format) s.add8(0); } } - else assert(false); + else if (mType == tnTRANSACTION_MD) + { + if (format == STN_ARF_PREFIXED) + { + s.add32(sHP_TransactionNode); + mItem->addRaw(s); + } + else + { + mItem->addRaw(s); + s.add256(mItem->getTag()); + s.add8(4); + } + } + else + assert(false); } bool SHAMapTreeNode::setItem(SHAMapItem::pointer& i, TNType type) diff --git a/src/SHAMapSync.cpp b/src/SHAMapSync.cpp index a8d668cc61..ab230c79fb 100644 --- a/src/SHAMapSync.cpp +++ b/src/SHAMapSync.cpp @@ -362,7 +362,7 @@ static bool confuseMap(SHAMap &map, int count) { SHAMapItem::pointer item = makeRandomAS(); items.push_back(item->getTag()); - if (!map.addItem(*item, false)) + if (!map.addItem(*item, false, false)) { Log(lsFATAL) << "Unable to add item to map"; return false; @@ -423,7 +423,7 @@ BOOST_AUTO_TEST_CASE( SHAMapSync_test ) Log(lsTRACE) << "Adding random data"; int items = 10000; for (int i = 0; i < items; ++i) - source.addItem(*makeRandomAS(), false); + source.addItem(*makeRandomAS(), false, false); Log(lsTRACE) << "Adding items, then removing them"; if (!confuseMap(source, 500)) BOOST_FAIL("ConfuseMap"); diff --git a/src/SerializedValidation.cpp b/src/SerializedValidation.cpp index 15dca09117..f3b6799d6c 100644 --- a/src/SerializedValidation.cpp +++ b/src/SerializedValidation.cpp @@ -6,10 +6,8 @@ SOElement SerializedValidation::sValidationFormat[] = { { sfFlags, "Flags", STI_UINT32, SOE_FLAGS, 0 }, { sfLedgerHash, "LedgerHash", STI_HASH256, SOE_REQUIRED, 0 }, - { sfCloseTime, "CloseTime", STI_UINT64, SOE_REQUIRED, 0 }, + { sfCloseTime, "CloseTime", STI_UINT32, SOE_REQUIRED, 0 }, { sfSigningKey, "SigningKey", STI_VL, SOE_REQUIRED, 0 }, - { sfVersion, "Version", STI_UINT32, SOE_IFFLAG, 1 }, - { sfExtensions, "Extensions", STI_TL, SOE_IFFLAG, 0x01000000 }, { sfInvalid, NULL, STI_DONE, SOE_NEVER, -1 }, }; @@ -21,12 +19,12 @@ SerializedValidation::SerializedValidation(SerializerIterator& sit, bool checkSi if (checkSignature && !isValid()) throw std::runtime_error("Invalid validation"); } -SerializedValidation::SerializedValidation(const uint256& ledgerHash, uint64 closeTime, +SerializedValidation::SerializedValidation(const uint256& ledgerHash, uint32 closeTime, const NewcoinAddress& naSeed, bool isFull) : STObject(sValidationFormat), mSignature("Signature"), mTrusted(false) { setValueFieldH256(sfLedgerHash, ledgerHash); - setValueFieldU64(sfCloseTime, closeTime); + setValueFieldU32(sfCloseTime, closeTime); if (naSeed.isValid()) setValueFieldVL(sfSigningKey, NewcoinAddress::createNodePublic(naSeed).getNodePublic()); if (!isFull) setFlag(sFullFlag); @@ -52,9 +50,9 @@ uint256 SerializedValidation::getLedgerHash() const return getValueFieldH256(sfLedgerHash); } -uint64 SerializedValidation::getCloseTime() const +uint32 SerializedValidation::getCloseTime() const { - return getValueFieldU64(sfCloseTime); + return getValueFieldU32(sfCloseTime); } bool SerializedValidation::isValid() const diff --git a/src/SerializedValidation.h b/src/SerializedValidation.h index 85cfa0d840..dc5b7bbf0b 100644 --- a/src/SerializedValidation.h +++ b/src/SerializedValidation.h @@ -22,10 +22,10 @@ public: SerializedValidation(SerializerIterator& sit, bool checkSignature = true); SerializedValidation(const Serializer& s, bool checkSignature = true); - SerializedValidation(const uint256& ledgerHash, uint64 closeTime, const NewcoinAddress& naSeed, bool isFull); + SerializedValidation(const uint256& ledgerHash, uint32 closeTime, const NewcoinAddress& naSeed, bool isFull); uint256 getLedgerHash() const; - uint64 getCloseTime() const; + uint32 getCloseTime() const; NewcoinAddress getSignerPublic() const; bool isValid() const; bool isFull() const; diff --git a/src/Serializer.h b/src/Serializer.h index ab39a1c30d..5c687e2984 100644 --- a/src/Serializer.h +++ b/src/Serializer.h @@ -32,8 +32,8 @@ public: // assemble functions int add8(unsigned char byte); int add16(uint16); - int add32(uint32); // ledger indexes, account sequence - int add64(uint64); // timestamps, amounts + int add32(uint32); // ledger indexes, account sequence, timestamps + int add64(uint64); // native currency amounts int add128(const uint128&); // private key generators int add160(const uint160&); // account names, hankos int add256(const uint256&); // transaction and ledger hashes diff --git a/src/TransactionEngine.cpp b/src/TransactionEngine.cpp index 34ae6c6033..d7bb422676 100644 --- a/src/TransactionEngine.cpp +++ b/src/TransactionEngine.cpp @@ -1,6 +1,7 @@ // // XXX Should make sure all fields and are recognized on a transactions. // XXX Make sure fee is claimed for failed transactions. +// XXX Might uses an unordered set for vector. // #include "TransactionEngine.h" @@ -182,7 +183,7 @@ TransactionEngineResult TransactionEngine::dirAdd( if (uNodeDir) { - // Try adding to non-root. + // Try adding to last node. lspRoot = lepNONE; sleNode = mLedger->getDirNode(lspRoot, Ledger::getDirNodeIndex(uRootIndex, uNodeDir)); @@ -190,7 +191,7 @@ TransactionEngineResult TransactionEngine::dirAdd( } else { - // Try adding to root. Didn't have a previous set. + // Try adding to root. Didn't have a previous set to the last node. sleNode = sleRoot; } @@ -284,7 +285,7 @@ TransactionEngineResult TransactionEngine::dirDelete( { assert(false); - Log(lsWARNING) << "dirDelete: node not mentioned"; + Log(lsWARNING) << "dirDelete: no such entry"; return terBAD_LEDGER; } @@ -316,13 +317,13 @@ TransactionEngineResult TransactionEngine::dirDelete( // Root overflowed. else if (bKeepRoot) { - // Not allowed to delete root node, if it ever overflowed. + // If root overflowed and not allowed to delete overflowed root node. nothing(); } else if (uNodePrevious != uNodeNext) { - // Have multiple other nodes. Can't delete root node. + // Have more than 2 nodes. Can't delete root node. nothing(); } @@ -343,7 +344,7 @@ TransactionEngineResult TransactionEngine::dirDelete( } else { - // Have an entry, can't delete. + // Have an entry, can't delete root node. nothing(); } @@ -362,6 +363,7 @@ TransactionEngineResult TransactionEngine::dirDelete( LedgerStateParms lspNext = lepNONE; SLE::pointer sleNext = mLedger->getDirNode(lspNext, uNodeNext ? Ledger::getDirNodeIndex(uRootIndex, uNodeNext) : uRootIndex); + assert(slePrevious); assert(sleNext); if (!slePrevious) @@ -371,7 +373,6 @@ TransactionEngineResult TransactionEngine::dirDelete( return terBAD_LEDGER; } - assert(sleNext); if (!sleNext) { Log(lsWARNING) << "dirDelete: next node is missing"; @@ -475,18 +476,6 @@ TransactionEngineResult TransactionEngine::setAuthorized(const SerializedTransac return terSUCCESS; } -Ledger::pointer TransactionEngine::getTransactionLedger(uint32 targetLedger) -{ - Ledger::pointer ledger = mDefaultLedger; - if (mAlternateLedger && (targetLedger != 0) && - (targetLedger != mLedger->getLedgerSeq()) && (targetLedger == mAlternateLedger->getLedgerSeq())) - { - Log(lsINFO) << "Transaction goes into wobble ledger"; - ledger = mAlternateLedger; - } - return ledger; -} - bool TransactionEngine::entryExists(SLE::pointer sleEntry) { return mEntries.find(sleEntry) != mEntries.end(); @@ -519,7 +508,7 @@ void TransactionEngine::entryDelete(SLE::pointer sleEntry) case taaMODIFY: case taaNONE: - mEntries[sleEntry] = taaDELETE; // Upgrade + mEntries[sleEntry] = taaDELETE; // Upgrade. break; case taaDELETE: @@ -541,7 +530,7 @@ void TransactionEngine::entryModify(SLE::pointer sleEntry) break; case taaNONE: - mEntries[sleEntry] = taaMODIFY; // Upgrade + mEntries[sleEntry] = taaMODIFY; // Upgrade. break; case taaCREATE: @@ -565,7 +554,7 @@ void TransactionEngine::entryUnfunded(SLE::pointer sleEntry) break; case taaNONE: - mEntries[sleEntry] = taaUNFUNDED; // Upgrade + mEntries[sleEntry] = taaUNFUNDED; // Upgrade. break; case taaUNFUNDED: @@ -575,16 +564,10 @@ void TransactionEngine::entryUnfunded(SLE::pointer sleEntry) } TransactionEngineResult TransactionEngine::applyTransaction(const SerializedTransaction& txn, - TransactionEngineParams params, uint32 targetLedger) -{ - return applyTransaction(txn, params, getTransactionLedger(targetLedger)); -} - -TransactionEngineResult TransactionEngine::applyTransaction(const SerializedTransaction& txn, - TransactionEngineParams params, Ledger::pointer ledger) + TransactionEngineParams params) { Log(lsTRACE) << "applyTransaction>"; - mLedger = ledger; + assert(mLedger); mLedgerParentCloseTime = mLedger->getParentCloseTimeNC(); #ifdef DEBUG @@ -723,12 +706,7 @@ TransactionEngineResult TransactionEngine::applyTransaction(const SerializedTran } if (terSUCCESS != terResult) - { - // Avoid unnecessary locking. - mLedger = Ledger::pointer(); - return terResult; - } boost::recursive_mutex::scoped_lock sl(mLedger->mLock); @@ -1071,7 +1049,6 @@ TransactionEngineResult TransactionEngine::applyTransaction(const SerializedTran mLedger->destroyCoins(saPaid.getNValue()); } - mLedger = Ledger::pointer(); mEntries.clear(); return terResult; @@ -2234,13 +2211,14 @@ TransactionEngineResult TransactionEngine::doOfferCancel(const SerializedTransac Log(lsWARNING) << "doOfferCancel: uSequence=" << uSequence; uint64 uOwnerNode = sleOffer->getIFieldU64(sfOwnerNode); - uint64 uBookNode = sleOffer->getIFieldU64(sfBookNode); - uint256 uDirectory = sleOffer->getIFieldH256(sfDirectory); terResult = dirDelete(true, uOwnerNode, Ledger::getOwnerDirIndex(uSrcAccountID), uLedgerIndex); if (terSUCCESS == terResult) { + uint256 uDirectory = sleOffer->getIFieldH256(sfDirectory); + uint64 uBookNode = sleOffer->getIFieldU64(sfBookNode); + terResult = dirDelete(false, uBookNode, uDirectory, uLedgerIndex); } diff --git a/src/TransactionEngine.h b/src/TransactionEngine.h index 99f7444f5c..86d28a3657 100644 --- a/src/TransactionEngine.h +++ b/src/TransactionEngine.h @@ -155,7 +155,6 @@ private: STAmount& saTakerGot); protected: - Ledger::pointer mDefaultLedger, mAlternateLedger; Ledger::pointer mLedger; uint64 mLedgerParentCloseTime; @@ -189,20 +188,12 @@ protected: public: TransactionEngine() { ; } - TransactionEngine(Ledger::pointer ledger) : mDefaultLedger(ledger) { ; } + TransactionEngine(Ledger::pointer ledger) : mLedger(ledger) { ; } - Ledger::pointer getDefaultLedger() { return mDefaultLedger; } - void setDefaultLedger(Ledger::pointer ledger) { mDefaultLedger = ledger; } - Ledger::pointer getAlternateLedger() { return mAlternateLedger; } - void setAlternateLedger(Ledger::pointer ledger) { mAlternateLedger = ledger; } - void setLedger(Ledger::pointer ledger) { mDefaultLedger = ledger; - mAlternateLedger = Ledger::pointer(); } + Ledger::pointer getLedger() { return mLedger; } + void setLedger(Ledger::pointer ledger) { assert(ledger); mLedger = ledger; } - Ledger::pointer getTransactionLedger(uint32 targetLedger); - TransactionEngineResult applyTransaction(const SerializedTransaction&, TransactionEngineParams, - Ledger::pointer ledger); - TransactionEngineResult applyTransaction(const SerializedTransaction&, TransactionEngineParams, - uint32 targetLedger); + TransactionEngineResult applyTransaction(const SerializedTransaction&, TransactionEngineParams); }; inline TransactionEngineParams operator|(const TransactionEngineParams& l1, const TransactionEngineParams& l2) diff --git a/src/ValidationCollection.cpp b/src/ValidationCollection.cpp index 5b53e46f25..22bf0deb7f 100644 --- a/src/ValidationCollection.cpp +++ b/src/ValidationCollection.cpp @@ -11,9 +11,9 @@ bool ValidationCollection::addValidation(SerializedValidation::pointer val) if (theApp->getUNL().nodeInUNL(val->getSignerPublic())) { val->setTrusted(); - uint64 now = theApp->getOPs().getNetworkTimeNC(); - uint64 valClose = val->getCloseTime(); - if ((now > valClose) && (now < (valClose + LEDGER_INTERVAL))) + uint32 now = theApp->getOPs().getNetworkTimeNC(); + uint32 valClose = val->getCloseTime(); + if ((now > valClose) && (now < (valClose + LEDGER_MAX_INTERVAL))) isCurrent = true; else Log(lsWARNING) << "Received stale validation now=" << now << ", close=" << valClose; @@ -55,19 +55,19 @@ void ValidationCollection::getValidationCount(const uint256& ledger, bool curren trusted = untrusted = 0; boost::mutex::scoped_lock sl(mValidationLock); boost::unordered_map::iterator it = mValidations.find(ledger); - uint64 now = theApp->getOPs().getNetworkTimeNC(); + uint32 now = theApp->getOPs().getNetworkTimeNC(); if (it != mValidations.end()) { for (ValidationSet::iterator vit = it->second.begin(), end = it->second.end(); vit != end; ++vit) { - bool trusted = vit->second->isTrusted(); - if (trusted && currentOnly) + bool isTrusted = vit->second->isTrusted(); + if (isTrusted && currentOnly) { - uint64 closeTime = vit->second->getCloseTime(); - if ((now < closeTime) || (now > (closeTime + 2 * LEDGER_INTERVAL))) + uint32 closeTime = vit->second->getCloseTime(); + if ((now < closeTime) || (now > (closeTime + 2 * LEDGER_MAX_INTERVAL))) trusted = false; } - if (trusted) + if (isTrusted) ++trusted; else ++untrusted; @@ -75,9 +75,38 @@ void ValidationCollection::getValidationCount(const uint256& ledger, bool curren } } +int ValidationCollection::getTrustedValidationCount(const uint256& ledger) +{ + int trusted = 0; + boost::mutex::scoped_lock sl(mValidationLock); + for (boost::unordered_map::iterator it = mValidations.find(ledger), + end = mValidations.end(); it != end; ++it) + { + for (ValidationSet::iterator vit = it->second.begin(), end = it->second.end(); vit != end; ++vit) + { + if (vit->second->isTrusted()) + ++trusted; + } + } + return trusted; +} + +int ValidationCollection::getCurrentValidationCount(uint32 afterTime) +{ + int count = 0; + boost::mutex::scoped_lock sl(mValidationLock); + for (boost::unordered_map::iterator it = mCurrentValidations.begin(), + end = mCurrentValidations.end(); it != end; ++it) + { + if (it->second->isTrusted() && (it->second->getCloseTime() > afterTime)) + ++count; + } + return count; +} + boost::unordered_map ValidationCollection::getCurrentValidations() { - uint64 now = theApp->getOPs().getNetworkTimeNC(); + uint32 now = theApp->getOPs().getNetworkTimeNC(); boost::unordered_map ret; { @@ -85,14 +114,10 @@ boost::unordered_map ValidationCollection::getCurrentValidations() boost::unordered_map::iterator it = mCurrentValidations.begin(); while (it != mCurrentValidations.end()) { - if (now > (it->second->getCloseTime() + LEDGER_INTERVAL)) - { - Log(lsTRACE) << "Erasing validation for " << it->second->getLedgerHash().GetHex(); + if (now > (it->second->getCloseTime() + LEDGER_MAX_INTERVAL)) it = mCurrentValidations.erase(it); - } else { - Log(lsTRACE) << "Counting validation for " << it->second->getLedgerHash().GetHex(); ++ret[it->second->getLedgerHash()]; ++it; } diff --git a/src/ValidationCollection.h b/src/ValidationCollection.h index 7df9c91bb2..5c6feacfea 100644 --- a/src/ValidationCollection.h +++ b/src/ValidationCollection.h @@ -24,6 +24,8 @@ public: bool addValidation(SerializedValidation::pointer); ValidationSet getValidations(const uint256& ledger); void getValidationCount(const uint256& ledger, bool currentOnly, int& trusted, int& untrusted); + int getTrustedValidationCount(const uint256& ledger); + int getCurrentValidationCount(uint32 afterTime); boost::unordered_map getCurrentValidations(); }; diff --git a/src/Version.h b/src/Version.h index 0a9dc3bda4..8fd1805a62 100644 --- a/src/Version.h +++ b/src/Version.h @@ -4,11 +4,10 @@ // Versions // -// Version of this software: -#define SERVER_VERSION_MAJOR 0 -#define SERVER_VERSION_MINOR 1 -#define SERVER_VERSION_SUB "-a" -#define SERVER_NAME "NewCoin" +#define SERVER_VERSION_MAJOR 0 +#define SERVER_VERSION_MINOR 3 +#define SERVER_VERSION_SUB "-a" +#define SERVER_NAME "NewCoin" #define SV_STRINGIZE(x) SV_STRINGIZE2(x) #define SV_STRINGIZE2(x) #x @@ -17,11 +16,11 @@ // Version we prefer to speak: #define PROTO_VERSION_MAJOR 0 -#define PROTO_VERSION_MINOR 2 +#define PROTO_VERSION_MINOR 3 // Version we wil speak to: #define MIN_PROTO_MAJOR 0 -#define MIN_PROTO_MINOR 2 +#define MIN_PROTO_MINOR 3 #define MAKE_VERSION_INT(maj,min) ((maj << 16) | min) #define GET_VERSION_MAJOR(ver) (ver >> 16) diff --git a/src/main.cpp b/src/main.cpp index bbaeb190d1..ea77e3ef90 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -91,6 +91,7 @@ int main(int argc, char* argv[]) ("rpc", "Perform rpc command (default).") ("test,t", "Perform unit tests.") ("parameters", po::value< vector >(), "Specify comma separated parameters.") + ("verbose,v", "Increase log level") ; // Interpret positional arguments as --parameters. @@ -132,6 +133,11 @@ int main(int argc, char* argv[]) } } + if (vm.count("verbose")) + { + Log::setMinSeverity(lsTRACE); + } + if (!iResult) { theConfig.setup(vm.count("conf") ? vm["conf"].as() : ""); diff --git a/src/newcoin.proto b/src/newcoin.proto index d902073f4f..79f517468f 100644 --- a/src/newcoin.proto +++ b/src/newcoin.proto @@ -105,9 +105,10 @@ message TMProposeSet { required uint32 proposeSeq = 1; required bytes currentTxHash = 2; // the hash of the ledger we are proposing required bytes nodePubKey = 3; - required bytes signature = 4; // signature of above fields - repeated bytes addedTransactions = 5; // not required if number is large - repeated bytes removedTransactions = 6; // not required if number is large + required uint32 closeTime = 4; + required bytes signature = 5; // signature of above fields + repeated bytes addedTransactions = 6; // not required if number is large + repeated bytes removedTransactions = 7; // not required if number is large } enum TxSetStatus {