/** Returns the hash of the specified ledger. @param ledgerIndex The index of the desired ledger. @param referenceLedger [out] An optional known good subsequent ledger. @return The hash of the ledger. This will be all-bits-zero if not found. */ LedgerHash getHash( LedgerIndex const& ledgerIndex, std::shared_ptr<ReadView const>& referenceLedger) { LedgerHash ledgerHash; if (!referenceLedger || (referenceLedger->info().seq < ledgerIndex)) { referenceLedger = app_.getLedgerMaster().getValidatedLedger(); if (!referenceLedger) { JLOG (j_.warn()) << "No validated ledger"; return ledgerHash; // Nothing we can do. No validated ledger. } } if (referenceLedger->info().seq >= ledgerIndex) { // See if the hash for the ledger we need is in the reference ledger ledgerHash = getLedgerHash(referenceLedger, ledgerIndex); if (ledgerHash.isZero()) { // No. Try to get another ledger that might have the hash we // need: compute the index and hash of a ledger that will have // the hash we need. LedgerIndex refIndex = getCandidateLedger (ledgerIndex); LedgerHash refHash = getLedgerHash (referenceLedger, refIndex); bool const nonzero (refHash.isNonZero ()); assert (nonzero); if (nonzero) { // We found the hash and sequence of a better reference // ledger. referenceLedger = app_.getInboundLedgers().acquire( refHash, refIndex, InboundLedger::fcGENERIC); if (referenceLedger) ledgerHash = getLedgerHash( referenceLedger, ledgerIndex); } } } else JLOG (j_.warn()) << "Validated ledger is prior to target ledger"; return ledgerHash; }
bool RootStoppable::stopAsync (beast::Journal j) { bool alreadyCalled; { // Even though m_calledStop is atomic, we change its value under a // lock. This removes a small timing window that occurs if the // waiting thread is handling a spurious wakeup while m_calledStop // changes state. std::unique_lock<std::mutex> lock (m_); alreadyCalled = m_calledStop.exchange (true); } if (alreadyCalled) { if (auto stream = j.warn()) stream << "Stoppable::stop called again"; return false; } // Wait until all in-flight JobQueue Jobs are completed. using namespace std::chrono_literals; jobCounter_.join (m_name.c_str(), 1s, j); c_.notify_all(); stopAsyncRecursive(j); return true; }
bool shouldCloseLedger ( bool anyTransactions, int previousProposers, int proposersClosed, int proposersValidated, std::chrono::milliseconds previousTime, std::chrono::milliseconds currentTime, // Time since last ledger's close time std::chrono::milliseconds openTime, // Time waiting to close this ledger std::chrono::seconds idleInterval, beast::Journal j) { using namespace std::chrono_literals; if ((previousTime < -1s) || (previousTime > 10min) || (currentTime > 10min)) { // These are unexpected cases, we just close the ledger JLOG (j.warn()) << "shouldCloseLedger Trans=" << (anyTransactions ? "yes" : "no") << " Prop: " << previousProposers << "/" << proposersClosed << " Secs: " << currentTime.count() << " (last: " << previousTime.count() << ")"; return true; } if ((proposersClosed + proposersValidated) > (previousProposers / 2)) { // If more than half of the network has closed, we close JLOG (j.trace()) << "Others have closed"; return true; } if (!anyTransactions) { // Only close at the end of the idle interval return currentTime >= idleInterval; // normal idle } // Preserve minimum ledger open time if (openTime < LEDGER_MIN_CLOSE) { JLOG (j.debug()) << "Must wait minimum time before closing"; return false; } // Don't let this ledger close more than twice as fast as the previous // ledger reached consensus so that slower validators can slow down // the network if (openTime < (previousTime / 2)) { JLOG (j.debug()) << "Ledger has not been open long enough"; return false; } // Close the ledger return true; }
Ledger::Ledger ( LedgerInfo const& info, bool& loaded, Config const& config, Family& family, beast::Journal j) : mImmutable (true) , txMap_ (std::make_shared <SHAMap> (SHAMapType::TRANSACTION, info.txHash, family, SHAMap::version{getSHAMapV2(info) ? 2 : 1})) , stateMap_ (std::make_shared <SHAMap> (SHAMapType::STATE, info.accountHash, family, SHAMap::version{getSHAMapV2(info) ? 2 : 1})) , rules_ (config.features) , info_ (info) { loaded = true; if (info_.txHash.isNonZero () && !txMap_->fetchRoot (SHAMapHash{info_.txHash}, nullptr)) { loaded = false; JLOG (j.warn()) << "Don't have TX root for ledger"; } if (info_.accountHash.isNonZero () && !stateMap_->fetchRoot (SHAMapHash{info_.accountHash}, nullptr)) { loaded = false; JLOG (j.warn()) << "Don't have AS root for ledger"; } txMap_->setImmutable (); stateMap_->setImmutable (); if (! setup(config)) loaded = false; if (! loaded) { info_.hash = calculateLedgerHash(info_); family.missing_node (info_.hash); } }
ConsensusState checkConsensus ( int previousProposers, int currentProposers, int currentAgree, int currentFinished, std::chrono::milliseconds previousAgreeTime, std::chrono::milliseconds currentAgreeTime, bool proposing, beast::Journal j) { JLOG (j.trace()) << "checkConsensus: prop=" << currentProposers << "/" << previousProposers << " agree=" << currentAgree << " validated=" << currentFinished << " time=" << currentAgreeTime.count() << "/" << previousAgreeTime.count(); if (currentAgreeTime <= LEDGER_MIN_CONSENSUS) return ConsensusState::No; if (currentProposers < (previousProposers * 3 / 4)) { // Less than 3/4 of the last ledger's proposers are present; don't // rush: we may need more time. if (currentAgreeTime < (previousAgreeTime + LEDGER_MIN_CONSENSUS)) { JLOG (j.trace()) << "too fast, not enough proposers"; return ConsensusState::No; } } // Have we, together with the nodes on our UNL list, reached the threshold // to declare consensus? if (checkConsensusReached (currentAgree, currentProposers, proposing)) { JLOG (j.debug()) << "normal consensus"; return ConsensusState::Yes; } // Have sufficient nodes on our UNL list moved on and reached the threshold // to declare consensus? if (checkConsensusReached (currentFinished, currentProposers, false)) { JLOG (j.warn()) << "We see no consensus, but 80% of nodes have moved on"; return ConsensusState::MovedOn; } // no consensus yet JLOG (j.trace()) << "no consensus"; return ConsensusState::No; }
ApplyResult applyTransaction (Application& app, OpenView& view, STTx const& txn, bool retryAssured, ApplyFlags flags, beast::Journal j) { // Returns false if the transaction has need not be retried. if (retryAssured) flags = flags | tapRETRY; JLOG (j.debug()) << "TXN " << txn.getTransactionID () //<< (engine.view().open() ? " open" : " closed") // because of the optional in engine << (retryAssured ? "/retry" : "/final"); try { auto const result = apply(app, view, txn, flags, j); if (result.second) { JLOG (j.debug()) << "Transaction applied: " << transHuman (result.first); return ApplyResult::Success; } if (isTefFailure (result.first) || isTemMalformed (result.first) || isTelLocal (result.first)) { // failure JLOG (j.debug()) << "Transaction failure: " << transHuman (result.first); return ApplyResult::Fail; } JLOG (j.debug()) << "Transaction retry: " << transHuman (result.first); return ApplyResult::Retry; } catch (std::exception const&) { JLOG (j.warn()) << "Throws"; return ApplyResult::Fail; } }
void RootStoppable::stop (beast::Journal j) { // Must have a prior call to start() assert (m_started); { std::lock_guard<std::mutex> lock(m_); if (m_calledStop) { if (auto stream = j.warn()) stream << "Stoppable::stop called again"; return; } m_calledStop = true; c_.notify_all(); } stopAsync (j); stopRecursive (j); }
// VFALCO TODO This should return boost::optional<uint256> LedgerHash getLedgerHash( std::shared_ptr<ReadView const>& ledger, LedgerIndex index) { boost::optional<LedgerHash> hash; try { hash = hashOfSeq(*ledger, index, j_); } catch (SHAMapMissingNode &) { JLOG (j_.warn()) << "Node missing from ledger " << ledger->info().seq; app_.getInboundLedgers().acquire ( ledger->info().hash, ledger->info().seq, InboundLedger::fcGENERIC); } return hash ? *hash : zero; // kludge }
CanonicalTXSet applyTransactions( Application& app, SHAMap const& txns, OpenView& view, std::shared_ptr<Ledger> const& buildLCL, beast::Journal j) { CanonicalTXSet retriableTxs(txns.getHash().as_uint256()); for (auto const& item : txns) { if (buildLCL->txExists(item.key())) continue; // The transaction wasn't filtered // Add it to the set to be tried in canonical order JLOG(j.debug()) << "Processing candidate transaction: " << item.key(); try { retriableTxs.insert( std::make_shared<STTx const>(SerialIter{item.slice()})); } catch (std::exception const&) { JLOG(j.warn()) << "Txn " << item.key() << " throws"; } } bool certainRetry = true; // Attempt to apply all of the retriable transactions for (int pass = 0; pass < LEDGER_TOTAL_PASSES; ++pass) { JLOG(j.debug()) << "Pass: "******" Txns: " << retriableTxs.size() << (certainRetry ? " retriable" : " final"); int changes = 0; auto it = retriableTxs.begin(); while (it != retriableTxs.end()) { try { switch (applyTransaction( app, view, *it->second, certainRetry, tapNONE, j)) { case ApplyResult::Success: it = retriableTxs.erase(it); ++changes; break; case ApplyResult::Fail: it = retriableTxs.erase(it); break; case ApplyResult::Retry: ++it; } } catch (std::exception const&) { JLOG(j.warn()) << "Transaction throws"; it = retriableTxs.erase(it); } } JLOG(j.debug()) << "Pass: "******" finished " << changes << " changes"; // A non-retry pass made no changes if (!changes && !certainRetry) return retriableTxs; // Stop retriable passes if (!changes || (pass >= LEDGER_RETRY_PASSES)) certainRetry = false; } // If there are any transactions left, we must have // tried them in at least one final pass assert(retriableTxs.empty() || !certainRetry); return retriableTxs; }