//------------------------------------------------------------------------------ /* Copyright (c) 2011-2013, OpenCoin, Inc. */ //============================================================================== // VFALCO Should rename ContinuousLedgerTiming to LedgerTiming struct LedgerTiming; // for Log SETUP_LOG (LedgerTiming) // 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. bool ContinuousLedgerTiming::shouldClose ( bool anyTransactions, int previousProposers, // proposers in the last closing int proposersClosed, // proposers who have currently closed this ledgers int proposersValidated, // proposers who have validated the last closed ledger int previousMSeconds, // milliseconds the previous ledger took to reach consensus int currentMSeconds, // milliseconds since the previous ledger closed int openMSeconds, // milliseconds since the previous LCL was computed int idleInterval) // network's desired idle interval { if ((previousMSeconds < -1000) || (previousMSeconds > 600000) || (currentMSeconds < -1000) || (currentMSeconds > 600000)) { WriteLog (lsWARNING, LedgerTiming) << boost::str (boost::format ("CLC::shouldClose range Trans=%s, Prop: %d/%d, Secs: %d (last:%d)") % (anyTransactions ? "yes" : "no") % previousProposers % proposersClosed % currentMSeconds % previousMSeconds); return true; } if (!anyTransactions) { // no transactions so far this interval if (proposersClosed > (previousProposers / 4)) // did we miss a transaction? { WriteLog (lsTRACE, LedgerTiming) << "no transactions, many proposers: now (" << proposersClosed << " closed, " << previousProposers << " before)"; return true; } #if 0 // This false triggers on the genesis ledger if (previousMSeconds > (1000 * (LEDGER_IDLE_INTERVAL + 2))) // the last ledger was very slow to close { WriteLog (lsTRACE, LedgerTiming) << "was slow to converge (p=" << (previousMSeconds) << ")"; if (previousMSeconds < 2000) return previousMSeconds; return previousMSeconds - 1000; } #endif return currentMSeconds >= (idleInterval * 1000); // normal idle } if ((openMSeconds < LEDGER_MIN_CLOSE) && ((proposersClosed + proposersValidated) < (previousProposers / 2 ))) { WriteLog (lsDEBUG, LedgerTiming) << "Must wait minimum time before closing"; return false; } if ((currentMSeconds < previousMSeconds) && ((proposersClosed + proposersValidated) < previousProposers)) { WriteLog (lsDEBUG, LedgerTiming) << "We are waiting for more closes/validations"; return false; } return true; // this ledger should close now } // Returns whether we have a consensus or not. If so, we expect all honest nodes // to already have everything they need to accept a consensus. Our vote is 'locked in'. bool ContinuousLedgerTiming::haveConsensus ( int previousProposers, // proposers in the last closing (not including us) int currentProposers, // proposers in this closing so far (not including us) int currentAgree, // proposers who agree with us int currentFinished, // proposers who have validated a ledger after this one int previousAgreeTime, // how long it took to agree on the last ledger int currentAgreeTime, // how long we've been trying to agree bool forReal, // deciding whether to stop consensus process bool& failed) // we can't reach a consensus { WriteLog (lsTRACE, LedgerTiming) << boost::str (boost::format ("CLC::haveConsensus: prop=%d/%d agree=%d validated=%d time=%d/%d%s") % currentProposers % previousProposers % currentAgree % currentFinished % currentAgreeTime % previousAgreeTime % (forReal ? "" : "X")); if (currentAgreeTime <= LEDGER_MIN_CONSENSUS) return false; if (currentProposers < (previousProposers * 3 / 4)) { // Less than 3/4 of the last ledger's proposers are present, we may need more time if (currentAgreeTime < (previousAgreeTime + LEDGER_MIN_CONSENSUS)) { CondLog (forReal, lsTRACE, LedgerTiming) << "too fast, not enough proposers"; return false; } } // If 80% of current proposers (plus us) agree on a set, we have consensus if (((currentAgree * 100 + 100) / (currentProposers + 1)) > 80) { CondLog (forReal, lsINFO, LedgerTiming) << "normal consensus"; failed = false; return true; } // If 80% of the nodes on your UNL have moved on, you should declare consensus if (((currentFinished * 100) / (currentProposers + 1)) > 80) { CondLog (forReal, lsWARNING, LedgerTiming) << "We see no consensus, but 80% of nodes have moved on"; failed = true; return true; } // no consensus yet CondLog (forReal, lsTRACE, LedgerTiming) << "no consensus"; return false; } int ContinuousLedgerTiming::getNextLedgerTimeResolution (int previousResolution, bool previousAgree, int ledgerSeq) { assert (ledgerSeq); 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; }