예제 #1
0
Ledger::pointer LedgerHistory::getLedgerBySeq (uint32 index)
{
    TaggedCache::ScopedLockType sl (mLedgersByHash.peekMutex (), __FILE__, __LINE__);
    std::map<uint32, uint256>::iterator it (mLedgersByIndex.find (index));

    if (it != mLedgersByIndex.end ())
    {
        uint256 hash = it->second;
        sl.unlock ();
        return getLedgerByHash (hash);
    }

    sl.unlock ();

    Ledger::pointer ret (Ledger::loadByIndex (index));

    if (!ret)
        return ret;

    assert (ret->getLedgerSeq () == index);

    sl.lock (__FILE__, __LINE__);
    assert (ret->isImmutable ());
    mLedgersByHash.canonicalize (ret->getHash (), ret);
    mLedgersByIndex[ret->getLedgerSeq ()] = ret->getHash ();
    return (ret->getLedgerSeq () == index) ? ret : Ledger::pointer ();
}
예제 #2
0
void
close_and_advance(Ledger::pointer& ledger, Ledger::pointer& LCL)
{
    std::shared_ptr<SHAMap> set = ledger->peekTransactionMap();
    CanonicalTXSet retriableTransactions(set->getHash());
    Ledger::pointer newLCL = std::make_shared<Ledger>(false, *LCL);
    // Set up to write SHAMap changes to our database,
    //   perform updates, extract changes
    applyTransactions(set, newLCL, newLCL, retriableTransactions, false);
    newLCL->updateSkipList();
    newLCL->setClosed();
    newLCL->peekAccountStateMap()->flushDirty(
        hotACCOUNT_NODE, newLCL->getLedgerSeq());
    newLCL->peekTransactionMap()->flushDirty(
        hotTRANSACTION_NODE, newLCL->getLedgerSeq());
    using namespace std::chrono;
    auto const epoch_offset = days(10957);  // 2000-01-01
    std::uint32_t closeTime = time_point_cast<seconds>  // now
        (system_clock::now() - epoch_offset).
        time_since_epoch().count();
    int closeResolution = seconds(LEDGER_TIME_ACCURACY).count();
    bool closeTimeCorrect = true;
    newLCL->setAccepted(closeTime, closeResolution, closeTimeCorrect);

    LCL = newLCL;
    ledger = std::make_shared<Ledger>(false, *LCL);
}
예제 #3
0
Ledger::pointer LedgerHistory::getLedgerBySeq (std::uint32_t index)
{
    {
        LedgersByHash::ScopedLockType sl (m_ledgers_by_hash.peekMutex ());
        std::map <std::uint32_t, uint256>::iterator it (mLedgersByIndex.find (index));

        if (it != mLedgersByIndex.end ())
        {
            uint256 hash = it->second;
            sl.unlock ();
            return getLedgerByHash (hash);
        }
    }

    Ledger::pointer ret (Ledger::loadByIndex (index));

    if (!ret)
        return ret;

    assert (ret->getLedgerSeq () == index);

    {
        // Add this ledger to the local tracking by index
        LedgersByHash::ScopedLockType sl (m_ledgers_by_hash.peekMutex ());

        assert (ret->isImmutable ());
        m_ledgers_by_hash.canonicalize (ret->getHash (), ret);
        mLedgersByIndex[ret->getLedgerSeq ()] = ret->getHash ();
        return (ret->getLedgerSeq () == index) ? ret : Ledger::pointer ();
    }
}
예제 #4
0
 // VFALCO TODO This should return boost::optional<uint256>
 LedgerHash getLedgerHash(Ledger::pointer ledger, LedgerIndex index)
 {
     boost::optional<LedgerHash> hash;
     try
     {
         hash = hashOfSeq(*ledger, index,
             getApp().getSLECache(), m_journal);
     }
     catch (SHAMapMissingNode &)
     {
         m_journal.warning <<
             "Node missing from ledger " << ledger->getLedgerSeq();
         getApp().getInboundLedgers().acquire (
             ledger->getHash(), ledger->getLedgerSeq(), InboundLedger::fcGENERIC);
     }
     return hash ? *hash : zero; // kludge
 }
예제 #5
0
    /** 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,
        Ledger::pointer& referenceLedger)
    {
        LedgerHash ledgerHash;

        if (!referenceLedger || (referenceLedger->getLedgerSeq() < ledgerIndex))
        {
            referenceLedger = getApp().getLedgerMaster().getValidatedLedger();
            if (!referenceLedger)
            {
                m_journal.warning << "No validated ledger";
                return ledgerHash; // Nothing we can do. No validated ledger.
            }
        }

        if (referenceLedger->getLedgerSeq() >= 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 =
                        getApp().getInboundLedgers().acquire(
                            refHash, refIndex, InboundLedger::fcGENERIC);
                    if (referenceLedger)
                        ledgerHash = getLedgerHash(referenceLedger, ledgerIndex);
                }
            }
        }
        else
            m_journal.warning << "Validated ledger is prior to target ledger";

        return ledgerHash;
    }
예제 #6
0
bool LedgerMaster::acquireMissingLedger(Ledger::ref origLedger, const uint256& ledgerHash, uint32 ledgerSeq)
{ // return: false = already gave up recently
	if (mTooFast)
		return true;

	Ledger::pointer ledger = mLedgerHistory.getLedgerBySeq(ledgerSeq);
	if (ledger && (Ledger::getHashByIndex(ledgerSeq) == ledgerHash))
	{
		cLog(lsTRACE) << "Ledger hash found in database";
		mTooFast = true;
		theApp->getJobQueue().addJob(jtPUBOLDLEDGER, boost::bind(&LedgerMaster::asyncAccept, this, ledger));
		return true;
	}

	if (theApp->getMasterLedgerAcquire().isFailure(ledgerHash))
		return false;

	mMissingLedger = theApp->getMasterLedgerAcquire().findCreate(ledgerHash);
	if (mMissingLedger->isComplete())
	{
		Ledger::pointer lgr = mMissingLedger->getLedger();
		if (lgr && (lgr->getLedgerSeq() == ledgerSeq))
			missingAcquireComplete(mMissingLedger);
		mMissingLedger.reset();
		return true;
	}
	else if (mMissingLedger->isDone())
	{
		mMissingLedger.reset();
		return false;
	}
	mMissingSeq = ledgerSeq;
	if (mMissingLedger->setAccept())
	{
		if (!mMissingLedger->addOnComplete(boost::bind(&LedgerMaster::missingAcquireComplete, this, _1)))
			theApp->getIOService().post(boost::bind(&LedgerMaster::missingAcquireComplete, this, mMissingLedger));
	}

	int fetch = theConfig.getSize(siLedgerFetch);
	if (theApp->getMasterLedgerAcquire().getFetchCount() < fetch)
	{
		int count = 0;
		typedef std::pair<uint32, uint256> u_pair;

		std::vector<u_pair> vec = origLedger->getLedgerHashes();
		BOOST_REVERSE_FOREACH(const u_pair& it, vec)
		{
			if ((count < fetch) && (it.first < ledgerSeq) &&
				!mCompleteLedgers.hasValue(it.first) && !theApp->getMasterLedgerAcquire().find(it.second))
			{
				++count;
				theApp->getMasterLedgerAcquire().findCreate(it.second);
			}
		}
	}
예제 #7
0
void LedgerHistory::addLedger (Ledger::pointer ledger, bool validated)
{
    assert (ledger && ledger->isImmutable ());
    assert (ledger->peekAccountStateMap ()->getHash ().isNonZero ());

    TaggedCache::ScopedLockType sl (mLedgersByHash.peekMutex (), __FILE__, __LINE__);

    mLedgersByHash.canonicalize (ledger->getHash(), ledger, true);
    if (validated)
        mLedgersByIndex[ledger->getLedgerSeq()] = ledger->getHash();
}
예제 #8
0
// The previous version of the lookupLedger command would accept the
// "ledger_index" argument as a string and silently treat it as a request to
// return the current ledger which, while not strictly wrong, could cause a lot
// of confusion.
//
// The code now robustly validates the input and ensures that the only possible
// values for the "ledger_index" parameter are the index of a ledger passed as
// an integer or one of the strings "current", "closed" or "validated".
// Additionally, the code ensures that the value passed in "ledger_hash" is a
// string and a valid hash. Invalid values will return an appropriate error
// code.
//
// In the absence of the "ledger_hash" or "ledger_index" parameters, the code
// assumes that "ledger_index" has the value "current".
//
// Returns a Json::objectValue.  If there was an error, it will be in that
// return value.  Otherwise, the object contains the field "validated" and
// optionally the fields "ledger_hash", "ledger_index" and
// "ledger_current_index", if they are defined.
Status lookupLedger (
    Json::Value const& params,
    Ledger::pointer& ledger,
    NetworkOPs& netOps,
    Json::Value& jsonResult)
{
    if (auto status = ledgerFromRequest (params, ledger, netOps))
        return status;

    if (ledger->isClosed ())
    {
        jsonResult[jss::ledger_hash] = to_string (ledger->getHash());
        jsonResult[jss::ledger_index] = ledger->getLedgerSeq();
    }
    else
    {
        jsonResult[jss::ledger_current_index] = ledger->getLedgerSeq();
    }
    jsonResult[jss::validated] = isValidated (*ledger);
    return Status::OK;
}
예제 #9
0
bool LedgerHistory::addLedger (Ledger::pointer ledger, bool validated)
{
    assert (ledger && ledger->isImmutable ());
    assert (ledger->peekAccountStateMap ()->getHash ().isNonZero ());

    LedgersByHash::ScopedLockType sl (m_ledgers_by_hash.peekMutex ());

    const bool alreadyHad = m_ledgers_by_hash.canonicalize (ledger->getHash(), ledger, true);
    if (validated)
        mLedgersByIndex[ledger->getLedgerSeq()] = ledger->getHash();

    return alreadyHad;
}
예제 #10
0
/** Get the current RippleLineCache, updating it if necessary.
    Get the correct ledger to use.
*/
RippleLineCache::pointer PathRequests::getLineCache (Ledger::pointer& ledger, bool authoritative)
{
    ScopedLockType sl (mLock, __FILE__, __LINE__);

    uint32 lineSeq = mLineCache ? mLineCache->getLedger()->getLedgerSeq() : 0;
    uint32 lgrSeq = ledger->getLedgerSeq();

    if ( (lineSeq == 0) ||                                 // no ledger
         (authoritative && (lgrSeq > lineSeq)) ||          // newer authoritative ledger
         (authoritative && ((lgrSeq + 8)  < lineSeq)) ||   // we jumped way back for some reason
         (lgrSeq > (lineSeq + 8)))                         // we jumped way forward for some reason
    {
        ledger = boost::make_shared<Ledger>(*ledger, false); // Take a snapshot of the ledger
        mLineCache = boost::make_shared<RippleLineCache> (ledger);
    }
    else
    {
        ledger = mLineCache->getLedger();
    }
    return mLineCache;
}
예제 #11
0
void LedgerMaster::asyncAccept(Ledger::pointer ledger)
{
	uint32 seq = ledger->getLedgerSeq();
	uint256 prevHash = ledger->getParentHash();

	while (seq > 0)
	{
		{
			boost::recursive_mutex::scoped_lock ml(mLock);
			mCompleteLedgers.setValue(seq);
			--seq;
			if (mCompleteLedgers.hasValue(seq))
				break;
		}

		uint256 tHash, pHash;
		if (!Ledger::getHashesByIndex(seq, tHash, pHash) || (tHash != prevHash))
			break;
		prevHash = pHash;
	}

	resumeAcquiring();
}
예제 #12
0
// {
//   account: account,
//   ledger_index_min: ledger_index  // optional, defaults to earliest
//   ledger_index_max: ledger_index, // optional, defaults to latest
//   binary: boolean,                // optional, defaults to false
//   forward: boolean,               // optional, defaults to false
//   limit: integer,                 // optional
//   marker: opaque                  // optional, resume previous query
// }
Json::Value RPCHandler::doAccountTx (Json::Value params, Resource::Charge& loadType, Application::ScopedLockType& masterLockHolder)
{
    masterLockHolder.unlock ();

    RippleAddress   raAccount;
    int             limit       = params.isMember (jss::limit) ? params[jss::limit].asUInt () : -1;
    bool            bBinary     = params.isMember ("binary") && params["binary"].asBool ();
    bool            bForward    = params.isMember ("forward") && params["forward"].asBool ();
    std::uint32_t   uLedgerMin;
    std::uint32_t   uLedgerMax;
    std::uint32_t   uValidatedMin;
    std::uint32_t   uValidatedMax;
    bool            bValidated  = mNetOps->getValidatedRange (uValidatedMin, uValidatedMax);

    if (!bValidated)
    {
        // Don't have a validated ledger range.
        return rpcError (rpcLGR_IDXS_INVALID);
    }

    if (!params.isMember ("account"))
        return rpcError (rpcINVALID_PARAMS);

    if (!raAccount.setAccountID (params["account"].asString ()))
        return rpcError (rpcACT_MALFORMED);

    loadType = Resource::feeMediumBurdenRPC;

    if (params.isMember ("ledger_index_min") || params.isMember ("ledger_index_max"))
    {
        std::int64_t iLedgerMin  = params.isMember ("ledger_index_min") ? params["ledger_index_min"].asInt () : -1;
        std::int64_t iLedgerMax  = params.isMember ("ledger_index_max") ? params["ledger_index_max"].asInt () : -1;


        uLedgerMin  = iLedgerMin == -1 ? 0 : iLedgerMin;
        uLedgerMax  = iLedgerMax == -1 ? uValidatedMax : iLedgerMax;

        if (uLedgerMax < uLedgerMin)
        {
            return rpcError (rpcLGR_IDXS_INVALID);
        }
    }
    else
    {
        Ledger::pointer l;
        Json::Value ret = RPC::lookupLedger (params, l, *mNetOps);

        if (!l)
            return ret;

		uLedgerMin = 0;
		uLedgerMax = l->getLedgerSeq ();
    }

    Json::Value resumeToken;

    if (params.isMember(jss::marker))
    {
         resumeToken = params[jss::marker];
    }

#ifndef BEAST_DEBUG

    try
    {
#endif
        Json::Value ret (Json::objectValue);

        ret["account"] = raAccount.humanAccountID ();
        Json::Value& jvTxns = (ret["transactions"] = Json::arrayValue);

        if (bBinary)
        {
            std::vector<NetworkOPs::txnMetaLedgerType> txns =
                mNetOps->getTxsAccountB (raAccount, uLedgerMin, uLedgerMax, bForward, resumeToken, limit, mRole == Config::ADMIN);

            for (std::vector<NetworkOPs::txnMetaLedgerType>::const_iterator it = txns.begin (), end = txns.end ();
                    it != end; ++it)
            {
                Json::Value& jvObj = jvTxns.append (Json::objectValue);

                std::uint32_t uLedgerIndex = std::get<2> (*it);
                jvObj["tx_blob"]           = std::get<0> (*it);
                jvObj["meta"]              = std::get<1> (*it);
                jvObj["ledger_index"]      = uLedgerIndex;
                jvObj[jss::validated]      = bValidated && uValidatedMin <= uLedgerIndex && uValidatedMax >= uLedgerIndex;

            }
        }
        else
        {
            std::vector< std::pair<Transaction::pointer, TransactionMetaSet::pointer> > txns =
                 mNetOps->getTxsAccount (raAccount, uLedgerMin, uLedgerMax, bForward, resumeToken, limit, mRole == Config::ADMIN);

            for (std::vector< std::pair<Transaction::pointer, TransactionMetaSet::pointer> >::iterator it = txns.begin (), end = txns.end (); it != end; ++it)
            {
                Json::Value&    jvObj = jvTxns.append (Json::objectValue);

                if (it->first)
                    jvObj[jss::tx]          = it->first->getJson (1);

                if (it->second)
                {
                    std::uint32_t uLedgerIndex = it->second->getLgrSeq ();

                    jvObj[jss::meta]        = it->second->getJson (0);
                    jvObj[jss::validated]   = bValidated && uValidatedMin <= uLedgerIndex && uValidatedMax >= uLedgerIndex;
                }

            }
        }

        //Add information about the original query
        ret[jss::ledger_index_min] = uLedgerMin;
        ret[jss::ledger_index_max] = uLedgerMax;
        if (params.isMember (jss::limit))
            ret[jss::limit]        = limit;
        if (!resumeToken.isNull())
            ret[jss::marker] = resumeToken;

        return ret;
#ifndef BEAST_DEBUG
    }
    catch (...)
    {
        return rpcError (rpcINTERNAL);
    }

#endif
}
예제 #13
0
void PathRequests::updateAll (Ledger::ref inLedger, CancelCallback shouldCancel)
{
    std::vector<PathRequest::wptr> requests;

    LoadEvent::autoptr event (getApp().getJobQueue().getLoadEventAP(jtPATH_FIND, "PathRequest::updateAll"));

    // Get the ledger and cache we should be using
    Ledger::pointer ledger = inLedger;
    RippleLineCache::pointer cache;
    {
        ScopedLockType sl (mLock);
        requests = mRequests;
        cache = getLineCache (ledger, true);
    }

    bool newRequests = getApp().getLedgerMaster().isNewPathRequest();
    bool mustBreak = false;

    mJournal.trace << "updateAll seq=" << ledger->getLedgerSeq() << ", " <<
                   requests.size() << " requests";
    int processed = 0, removed = 0;

    do
    {

        BOOST_FOREACH (PathRequest::wref wRequest, requests)
        {
            if (shouldCancel())
                break;

            bool remove = true;
            PathRequest::pointer pRequest = wRequest.lock ();

            if (pRequest)
            {
                if (!pRequest->needsUpdate (newRequests, ledger->getLedgerSeq ()))
                    remove = false;
                else
                {
                    InfoSub::pointer ipSub = pRequest->getSubscriber ();
                    if (ipSub)
                    {
                        ipSub->getConsumer ().charge (Resource::feePathFindUpdate);
                        if (!ipSub->getConsumer ().warn ())
                        {
                            Json::Value update = pRequest->doUpdate (cache, false);
                            pRequest->updateComplete ();
                            update["type"] = "path_find";
                            ipSub->send (update, false);
                            remove = false;
                            ++processed;
                        }
                    }
                }
            }

            if (remove)
            {
                PathRequest::pointer pRequest = wRequest.lock ();

                ScopedLockType sl (mLock);

                // Remove any dangling weak pointers or weak pointers that refer to this path request.
                std::vector<PathRequest::wptr>::iterator it = mRequests.begin();
                while (it != mRequests.end())
                {
                    PathRequest::pointer itRequest = it->lock ();
                    if (!itRequest || (itRequest == pRequest))
                    {
                        ++removed;
                        it = mRequests.erase (it);
                    }
                    else
                        ++it;
                }
            }

            mustBreak = !newRequests && getApp().getLedgerMaster().isNewPathRequest();
            if (mustBreak) // We weren't handling new requests and then there was a new request
                break;

        }

        if (mustBreak)
        {   // a new request came in while we were working
            newRequests = true;
        }
        else if (newRequests)
        {   // we only did new requests, so we always need a last pass
            newRequests = getApp().getLedgerMaster().isNewPathRequest();
        }
        else
        {   // check if there are any new requests, otherwise we are done
            newRequests = getApp().getLedgerMaster().isNewPathRequest();
            if (!newRequests) // We did a full pass and there are no new requests
                return;
        }

        {
            // Get the latest requests, cache, and ledger for next pass
            ScopedLockType sl (mLock);

            if (mRequests.empty())
                break;
            requests = mRequests;

            cache = getLineCache (ledger, false);
        }

    }
    while (!shouldCancel ());

    mJournal.debug << "updateAll complete " << processed << " process and " <<
                   removed << " removed";
}
예제 #14
0
/**
 * Instantiate an application and replay a ledger history out
 * of the dump file `filename`.
 */
void
LedgerDump::loadTransactions (std::string const& filename)
{
    std::ifstream in (filename);
    require ((bool)in, "opening file");

    std::unique_ptr <Application> app (make_Application ());
    app->setup ();
    auto &lm = app->getLedgerMaster ();
    WriteLog (lsINFO, LedgerDump) << "Loading ledgers from " << filename;

    auto nTxs = 0;

    // app->setup() when called with START_UP == Config::FRESH calls
    // ApplicationImp::startNewLedger(). Unfortunately it starts the new
    // ledger at the wrong timestamp, so we call it again once we've read
    // the first ledger we're going to apply. However, it's worth
    // understanding that startNewLedger() establishes 3 ledgers:
    //
    // Ledger 0 is the genesis ledger, it's not a real ledger, just a
    //          number.
    //
    // Ledger 1 is the root-account deposit ledger, with a single pile of
    //          currency owned by a single account generated by the seed
    //          "masterpassword".
    //
    // Ledger 2 is created and closed immediately on start, not sure why.
    //
    // Ledger 3 is a new ledger chained to #2 and left open in
    //          ledgermaster.
    //
    // Ledger 3 is where replay should be starting, so (once we call
    // startNewLedger() again) we pull out ledger #2 and use it as the
    // parent of the new ledger 3 we're replaying, and throw away the #3
    // that was made by startNewLedger().

    Ledger::pointer parentLedger;

    while (in)
    {
        if ((gLedgerSeq & 0xfff) == 0) {
            Job j;
            app->doSweep (j);
        }

        Json::Value j = loadJsonRecord (in);

        Ledger::pointer deserializedLedger;
        SHAMap::pointer txSet;
        std::vector<uint256> txOrder;
        std::tie (deserializedLedger, txSet, txOrder) =
            loadLedgerAndTransactionsFromJSON (*app, j);

        if (!parentLedger)
        {
            if (getConfig ().START_LEDGER.empty ())
            {
                require (deserializedLedger->getLedgerSeq () == 3,
                         "Initial ledger isn't seq #3");

                // On first iteration, restart the app's view of the ledger
                // history at the same instant as the parent close time of the
                // first ledger (ledger #3).
                app->startNewLedger (deserializedLedger->getParentCloseTimeNC ());
                parentLedger = lm.getClosedLedger ();
                require (parentLedger->getLedgerSeq () == 2,
                         "Initial ledger parent isn't seq #2");
            }
            else
            {
                // We're being invoked with --ledger, which is where we
                // will start replay from.
                require (app->loadOldLedger (getConfig ().START_LEDGER, false),
                         "Reloading old ledger failed.");
                parentLedger = lm.getClosedLedger ();
            }

            auto const parentSeq = parentLedger->getLedgerSeq ();
            auto seq = j["seq"].asUInt ();
            while (parentSeq + 1 > seq)
            {
                // Fast-scan JSON records until we hit the right one.
                WriteLog (lsINFO, LedgerDump) << "scanning past ledger: "
                                              << seq;
                j = loadJsonRecord (in);
                seq = j["seq"].asUInt ();
                if (parentSeq + 1 <= seq)
                {
                    require (parentSeq + 1 == seq,
                             "Missing ledgers between loaded and replay-start");
                    std::tie (deserializedLedger, txSet, txOrder) =
                        loadLedgerAndTransactionsFromJSON (*app, j);
                }
            }
            gLedgerSeq = parentSeq;
            require(parentLedger->getLedgerSeq () + 1 ==
                    deserializedLedger->getLedgerSeq (),
                    "Mismatched ledger-sequence prefix.");
        }

        Ledger::pointer currentLedger =
            boost::make_shared<Ledger> (true, *parentLedger);
        currentLedger->setCloseTime (deserializedLedger->getCloseTimeNC ());
        currentLedger->setCloseFlags (deserializedLedger->getCloseFlags ());
        currentLedger->setParentHash (deserializedLedger->getParentHash ());

        WriteLog (lsINFO, LedgerDump) << "loading ledger: "
                                      << currentLedger->getLedgerSeq();

        if (ShouldLog (lsTRACE, LedgerDump))
        {
            WriteLog (lsTRACE, LedgerDump) << "expecting next ledger:";
            WriteLog (lsTRACE, LedgerDump) << deserializedLedger->getJson (0);
            WriteLog (lsTRACE, LedgerDump) << "synthetic next ledger:";
            WriteLog (lsTRACE, LedgerDump) << currentLedger->getJson (0);
        }

        gLedgerSeq++;

        // Apply transactions, transitioning from one ledger state to next.
        WriteLog (lsDEBUG, LedgerDump)
            << "Applying set of " << txOrder.size() << " transactions";
        CanonicalTXSet retriableTransactions (txSet->getHash ());
        std::set<uint256> failedTransactions;
        LedgerConsensus::applyTransactions (txSet, currentLedger, currentLedger,
                                            retriableTransactions, failedTransactions,
                                            false, txOrder);

        require (retriableTransactions.empty (), "failed retriable tx set is not empty");
        require (failedTransactions.empty (), "failed tx set is not empty");

        currentLedger->updateSkipList ();
        currentLedger->setClosed ();
        currentLedger->setCloseTime (deserializedLedger->getCloseTimeNC ());

        int asf = currentLedger->peekAccountStateMap ()->flushDirty (
            hotACCOUNT_NODE, currentLedger->getLedgerSeq());
        int tmf = currentLedger->peekTransactionMap ()->flushDirty (
            hotTRANSACTION_NODE, currentLedger->getLedgerSeq());
        nTxs += tmf;

        WriteLog (lsDEBUG, LedgerDump) << "Flushed " << asf << " account "
                                  << "and " << tmf << "transaction nodes";

        // Finalize with the LedgerMaster.
        currentLedger->setAccepted ();
        bool alreadyHadLedger = lm.storeLedger (currentLedger);
        assert (! alreadyHadLedger);
        lm.pushLedger (currentLedger);

        WriteLog (lsTRACE, LedgerDump) << "parent ledger:";
        traceLedgerContents (*parentLedger);
        WriteLog (lsTRACE, LedgerDump) << "current ledger:";
        traceLedgerContents (*currentLedger);

        try
        {
            checkLedgersEqual (*deserializedLedger, *currentLedger);
        }
        catch (...)
        {
            WriteLog (lsINFO, LedgerDump) << "bad ledger:";
            std::cerr << currentLedger->getJson (LEDGER_JSON_FULL);
            throw;
        }
        parentLedger = currentLedger;
    }

    WriteLog (lsINFO, LedgerDump) << "Loaded "
                             << gLedgerSeq << "ledgers, "
                             << nTxs << " transactions from "
                             << filename;
    exit (0);
}
예제 #15
0
void LedgerHistory::handleMismatch (LedgerHash const& built, LedgerHash  const& valid)
{
    assert (built != valid);
    ++mismatch_counter_;

    Ledger::pointer builtLedger = getLedgerByHash (built);
    Ledger::pointer validLedger = getLedgerByHash (valid);

    if (builtLedger && validLedger)
    {
        assert (builtLedger->getLedgerSeq() == validLedger->getLedgerSeq());

        // Determine the mismatch reason
        // Distinguish Byzantine failure from transaction processing difference

        if (builtLedger->getParentHash() != validLedger->getParentHash())
        {
            // Disagreement over prior ledger indicates sync issue
            WriteLog (lsERROR, LedgerMaster) << "MISMATCH on prior ledger";
        }
        else if (builtLedger->getCloseTimeNC() != validLedger->getCloseTimeNC())
        {
            // Disagreement over close time indicates Byzantine failure
            WriteLog (lsERROR, LedgerMaster) << "MISMATCH on close time";
        }
        else
        {
            std::vector <uint256> builtTx, validTx;
            builtLedger->peekTransactionMap()->visitLeaves(
                std::bind (&addLeaf, std::ref (builtTx), std::placeholders::_1));
            validLedger->peekTransactionMap()->visitLeaves(
                std::bind (&addLeaf, std::ref (validTx), std::placeholders::_1));
            std::sort (builtTx.begin(), builtTx.end());
            std::sort (validTx.begin(), validTx.end());

            if (builtTx == validTx)
            {
                // Disagreement with same prior ledger, close time, and transactions
                // indicates a transaction processing difference
                WriteLog (lsERROR, LedgerMaster) <<
                    "MISMATCH with same " << builtTx.size() << " tx";
            }
            else
            {
                std::vector <uint256> notBuilt, notValid;
                std::set_difference (
                    validTx.begin(), validTx.end(),
                    builtTx.begin(), builtTx.end(),
                    std::inserter (notBuilt, notBuilt.begin()));
                std::set_difference (
                    builtTx.begin(), builtTx.end(),
                    validTx.begin(), validTx.end(),
                    std::inserter (notValid, notValid.begin()));

                // This can be either a disagreement over the consensus
                // set or difference in which transactions were rejected
                // as invalid

                WriteLog (lsERROR, LedgerMaster) << "MISMATCH tx differ "
                    << builtTx.size() << " built, " << validTx.size() << " valid";
                for (auto const& t : notBuilt)
                {
                    WriteLog (lsERROR, LedgerMaster) << "MISMATCH built without " << t;
                }
                for (auto const& t : notValid)
                {
                    WriteLog (lsERROR, LedgerMaster) << "MISMATCH valid without " << t;
                }
            }
        }
    }
    else
        WriteLog (lsERROR, LedgerMaster) << "MISMATCH cannot be analyzed";
}
예제 #16
0
// {
//   account: account,
//   ledger_index_min: ledger_index,
//   ledger_index_max: ledger_index,
//   binary: boolean,              // optional, defaults to false
//   count: boolean,               // optional, defaults to false
//   descending: boolean,          // optional, defaults to false
//   offset: integer,              // optional, defaults to 0
//   limit: integer                // optional
// }
Json::Value doAccountTxOld (RPC::Context& context)
{
    RippleAddress   raAccount;
    std::uint32_t offset
            = context.params_.isMember ("offset")
            ? context.params_["offset"].asUInt () : 0;
    int limit = context.params_.isMember ("limit")
            ? context.params_["limit"].asUInt () : -1;
    bool bBinary = context.params_.isMember ("binary")
            && context.params_["binary"].asBool ();
    bool bDescending = context.params_.isMember ("descending")
            && context.params_["descending"].asBool ();
    bool bCount = context.params_.isMember ("count")
            && context.params_["count"].asBool ();
    std::uint32_t   uLedgerMin;
    std::uint32_t   uLedgerMax;
    std::uint32_t   uValidatedMin;
    std::uint32_t   uValidatedMax;
    bool bValidated  = context.netOps_.getValidatedRange (
        uValidatedMin, uValidatedMax);

    if (!context.params_.isMember ("account"))
        return rpcError (rpcINVALID_PARAMS);

    if (!raAccount.setAccountID (context.params_["account"].asString ()))
        return rpcError (rpcACT_MALFORMED);

    if (offset > 3000)
        return rpcError (rpcATX_DEPRECATED);

    context.loadType_ = Resource::feeHighBurdenRPC;

    // DEPRECATED
    if (context.params_.isMember ("ledger_min"))
    {
        context.params_["ledger_index_min"]   = context.params_["ledger_min"];
        bDescending = true;
    }

    // DEPRECATED
    if (context.params_.isMember ("ledger_max"))
    {
        context.params_["ledger_index_max"]   = context.params_["ledger_max"];
        bDescending = true;
    }

    if (context.params_.isMember ("ledger_index_min")
        || context.params_.isMember ("ledger_index_max"))
    {
        std::int64_t iLedgerMin  = context.params_.isMember ("ledger_index_min")
                ? context.params_["ledger_index_min"].asInt () : -1;
        std::int64_t iLedgerMax  = context.params_.isMember ("ledger_index_max")
                ? context.params_["ledger_index_max"].asInt () : -1;

        if (!bValidated && (iLedgerMin == -1 || iLedgerMax == -1))
        {
            // Don't have a validated ledger range.
            return rpcError (rpcLGR_IDXS_INVALID);
        }

        uLedgerMin  = iLedgerMin == -1 ? uValidatedMin : iLedgerMin;
        uLedgerMax  = iLedgerMax == -1 ? uValidatedMax : iLedgerMax;

        if (uLedgerMax < uLedgerMin)
        {
            return rpcError (rpcLGR_IDXS_INVALID);
        }
    }
    else
    {
        Ledger::pointer l;
        Json::Value ret = RPC::lookupLedger (context.params_, l, context.netOps_);

        if (!l)
            return ret;

        uLedgerMin = uLedgerMax = l->getLedgerSeq ();
    }

    int count = 0;

#ifndef BEAST_DEBUG

    try
    {
#endif

        Json::Value ret (Json::objectValue);

        ret["account"] = raAccount.humanAccountID ();
        Json::Value& jvTxns = (ret["transactions"] = Json::arrayValue);

        if (bBinary)
        {
            auto txns = context.netOps_.getAccountTxsB (
                raAccount, uLedgerMin, uLedgerMax, bDescending, offset, limit,
                context.role_ == Config::ADMIN);

            for (auto it = txns.begin (), end = txns.end (); it != end; ++it)
            {
                ++count;
                Json::Value& jvObj = jvTxns.append (Json::objectValue);

                std::uint32_t  uLedgerIndex = std::get<2> (*it);
                jvObj["tx_blob"]            = std::get<0> (*it);
                jvObj["meta"]               = std::get<1> (*it);
                jvObj["ledger_index"]       = uLedgerIndex;
                jvObj["validated"]
                        = bValidated
                        && uValidatedMin <= uLedgerIndex
                        && uValidatedMax >= uLedgerIndex;

            }
        }
        else
        {
            auto txns = context.netOps_.getAccountTxs (
                raAccount, uLedgerMin, uLedgerMax, bDescending, offset, limit,
                context.role_ == Config::ADMIN);

            for (auto it = txns.begin (), end = txns.end (); it != end; ++it)
            {
                ++count;
                Json::Value&    jvObj = jvTxns.append (Json::objectValue);

                if (it->first)
                    jvObj["tx"]             = it->first->getJson (1);

                if (it->second)
                {
                    std::uint32_t uLedgerIndex = it->second->getLgrSeq ();

                    jvObj["meta"]           = it->second->getJson (0);
                    jvObj["validated"]
                            = bValidated
                            && uValidatedMin <= uLedgerIndex
                            && uValidatedMax >= uLedgerIndex;
                }

            }
        }

        //Add information about the original query
        ret["ledger_index_min"] = uLedgerMin;
        ret["ledger_index_max"] = uLedgerMax;
        ret["validated"]
                = bValidated
                && uValidatedMin <= uLedgerMin
                && uValidatedMax >= uLedgerMax;
        ret["offset"]           = offset;

        // We no longer return the full count but only the count of returned
        // transactions. Computing this count was two expensive and this API is
        // deprecated anyway.
        if (bCount)
            ret["count"]        = count;

        if (context.params_.isMember ("limit"))
            ret["limit"]        = limit;


        return ret;
#ifndef BEAST_DEBUG
    }
    catch (...)
    {
        return rpcError (rpcINTERNAL);
    }

#endif
}
예제 #17
0
// Get state nodes from a ledger
//   Inputs:
//     limit:        integer, maximum number of entries
//     marker:       opaque, resume point
//     binary:       boolean, format
//   Outputs:
//     ledger_hash:  chosen ledger's hash
//     ledger_index: chosen ledger's index
//     state:        array of state nodes
//     marker:       resume point, if any
Json::Value doLedgerData (RPC::Context& context)
{
    context.lock_.unlock ();

    int const BINARY_PAGE_LENGTH = 256;
    int const JSON_PAGE_LENGTH = 2048;

    Ledger::pointer lpLedger;

    Json::Value jvResult = RPC::lookupLedger (context.params_, lpLedger, context.netOps_);
    if (!lpLedger)
        return jvResult;

    uint256 resumePoint;
    if (context.params_.isMember ("marker"))
    {
        Json::Value const& jMarker = context.params_["marker"];
        if (!jMarker.isString ())
            return RPC::expected_field_error ("marker", "valid");
        if (!resumePoint.SetHex (jMarker.asString ()))
            return RPC::expected_field_error ("marker", "valid");
    }

    bool isBinary = false;
    if (context.params_.isMember ("binary"))
    {
        Json::Value const& jBinary = context.params_["binary"];
        if (!jBinary.isBool ())
            return RPC::expected_field_error ("binary", "bool");
        isBinary = jBinary.asBool ();
    }

    int limit = -1;
    int maxLimit = isBinary ? BINARY_PAGE_LENGTH : JSON_PAGE_LENGTH;

    if (context.params_.isMember ("limit"))
    {
        Json::Value const& jLimit = context.params_["limit"];
        if (!jLimit.isIntegral ())
            return RPC::expected_field_error ("limit", "integer");

        limit = jLimit.asInt ();
    }

    if ((limit < 0) || ((limit > maxLimit) && (context.role_ != Config::ADMIN)))
        limit = maxLimit;

    Json::Value jvReply = Json::objectValue;

    jvReply["ledger_hash"] = to_string (lpLedger->getHash());
    jvReply["ledger_index"] = beast::lexicalCastThrow <std::string> (lpLedger->getLedgerSeq ());

    Json::Value& nodes = (jvReply["state"] = Json::arrayValue);
    SHAMap& map = *(lpLedger->peekAccountStateMap ());

    for (;;)
    {
       SHAMapItem::pointer item = map.peekNextItem (resumePoint);
       if (!item)
           break;
       resumePoint = item->getTag();

       if (limit-- <= 0)
       {
           --resumePoint;
           jvReply["marker"] = to_string (resumePoint);
           break;
       }

       if (isBinary)
       {
           Json::Value& entry = nodes.append (Json::objectValue);
           entry["data"] = strHex (item->peekData().begin(), item->peekData().size());
           entry["index"] = to_string (item->getTag ());
       }
       else
       {
           SLE sle (item->peekSerializer(), item->getTag ());
           Json::Value& entry = nodes.append (sle.getJson (0));
           entry["index"] = to_string (item->getTag ());
       }
    }

    return jvReply;
}
예제 #18
0
// {
//   account: account,
//   ledger_index_min: ledger_index  // optional, defaults to earliest
//   ledger_index_max: ledger_index, // optional, defaults to latest
//   binary: boolean,                // optional, defaults to false
//   forward: boolean,               // optional, defaults to false
//   limit: integer,                 // optional
//   marker: opaque                  // optional, resume previous query
// }
Json::Value doAccountTx (RPC::Context& context)
{
    auto& params = context.params_;

    RippleAddress   raAccount;
    int limit = params.isMember (jss::limit) ?
            params[jss::limit].asUInt () : -1;
    bool bBinary = params.isMember ("binary") && params["binary"].asBool ();
    bool bForward = params.isMember ("forward") && params["forward"].asBool ();
    std::uint32_t   uLedgerMin;
    std::uint32_t   uLedgerMax;
    std::uint32_t   uValidatedMin;
    std::uint32_t   uValidatedMax;
    bool bValidated = context.netOps_.getValidatedRange (
        uValidatedMin, uValidatedMax);

    if (!bValidated)
    {
        // Don't have a validated ledger range.
        return rpcError (rpcLGR_IDXS_INVALID);
    }

    if (!params.isMember ("account"))
        return rpcError (rpcINVALID_PARAMS);

    if (!raAccount.setAccountID (params["account"].asString ()))
        return rpcError (rpcACT_MALFORMED);

    context.loadType_ = Resource::feeMediumBurdenRPC;

    if (params.isMember ("ledger_index_min") ||
        params.isMember ("ledger_index_max"))
    {
        std::int64_t iLedgerMin  = params.isMember ("ledger_index_min")
                ? params["ledger_index_min"].asInt () : -1;
        std::int64_t iLedgerMax  = params.isMember ("ledger_index_max")
                ? params["ledger_index_max"].asInt () : -1;

        uLedgerMin  = iLedgerMin == -1 ? uValidatedMin : iLedgerMin;
        uLedgerMax  = iLedgerMax == -1 ? uValidatedMax : iLedgerMax;

        if (uLedgerMax < uLedgerMin)
            return rpcError (rpcLGR_IDXS_INVALID);
    }
    else
    {
        Ledger::pointer l;
        Json::Value ret = RPC::lookupLedger (params, l, context.netOps_);

        if (!l)
            return ret;

        uLedgerMin = uLedgerMax = l->getLedgerSeq ();
    }

    Json::Value resumeToken;

    if (params.isMember(jss::marker))
         resumeToken = params[jss::marker];

#ifndef BEAST_DEBUG

    try
    {
#endif
        Json::Value ret (Json::objectValue);

        ret["account"] = raAccount.humanAccountID ();
        Json::Value& jvTxns = (ret["transactions"] = Json::arrayValue);

        if (bBinary)
        {
            auto txns = context.netOps_.getTxsAccountB (
                raAccount, uLedgerMin, uLedgerMax, bForward, resumeToken, limit,
                context.role_ == Config::ADMIN);

            for (auto& it: txns)
            {
                Json::Value& jvObj = jvTxns.append (Json::objectValue);

                std::uint32_t uLedgerIndex = std::get<2> (it);
                jvObj["tx_blob"] = std::get<0> (it);
                jvObj["meta"] = std::get<1> (it);
                jvObj["ledger_index"] = uLedgerIndex;
                jvObj[jss::validated]
                        = bValidated
                        && uValidatedMin <= uLedgerIndex
                        && uValidatedMax >= uLedgerIndex;

            }
        }
        else
        {
            auto txns = context.netOps_.getTxsAccount (
                raAccount, uLedgerMin, uLedgerMax, bForward, resumeToken, limit,
                context.role_ == Config::ADMIN);

            for (auto& it: txns)
            {
                Json::Value& jvObj = jvTxns.append (Json::objectValue);

                if (it.first)
                    jvObj[jss::tx] = it.first->getJson (1);

                if (it.second)
                {
                    std::uint32_t uLedgerIndex = it.second->getLgrSeq ();

                    jvObj[jss::meta] = it.second->getJson (0);
                    jvObj[jss::validated]
                            = bValidated
                            && uValidatedMin <= uLedgerIndex
                            && uValidatedMax >= uLedgerIndex;
                }

            }
        }

        //Add information about the original query
        ret[jss::ledger_index_min] = uLedgerMin;
        ret[jss::ledger_index_max] = uLedgerMax;
        if (params.isMember (jss::limit))
            ret[jss::limit]        = limit;
        if (!resumeToken.isNull())
            ret[jss::marker] = resumeToken;

        return ret;
#ifndef BEAST_DEBUG
    }
    catch (...)
    {
        return rpcError (rpcINTERNAL);
    }

#endif
}
예제 #19
0
bool PathRequest::isValid (RippleLineCache::ref crCache)
{
    ScopedLockType sl (mLock);
    bValid = raSrcAccount.isSet () && raDstAccount.isSet () && saDstAmount > zero;
    Ledger::pointer lrLedger = crCache->getLedger ();

    if (bValid)
    {
        AccountState::pointer asSrc = getApp().getOPs ().getAccountState (crCache->getLedger(), raSrcAccount);

        if (!asSrc)
        {
            // no source account
            bValid = false;
            jvStatus = rpcError (rpcSRC_ACT_NOT_FOUND);
        }
        else
        {
            AccountState::pointer asDst = getApp().getOPs ().getAccountState (lrLedger, raDstAccount);

            Json::Value& jvDestCur = (jvStatus["destination_currencies"] = Json::arrayValue);

            if (!asDst)
            {
                // no destination account
                jvDestCur.append (Json::Value ("XRP"));

                if (!saDstAmount.isNative ())
                {
                    // only XRP can be send to a non-existent account
                    bValid = false;
                    jvStatus = rpcError (rpcACT_NOT_FOUND);
                }
                else if (saDstAmount < STAmount (lrLedger->getReserve (0)))
                {
                    // payment must meet reserve
                    bValid = false;
                    jvStatus = rpcError (rpcDST_AMT_MALFORMED);
                }
            }
            else
            {
                bool const disallowXRP (
                    asDst->peekSLE ().getFlags() & lsfDisallowXRP);

                CurrencySet usDestCurrID =
                    usAccountDestCurrencies (raDstAccount, crCache, !disallowXRP);

                for (auto const& currency : usDestCurrID)
                    jvDestCur.append (to_string (currency));

                jvStatus["destination_tag"] = (asDst->peekSLE ().getFlags () & lsfRequireDestTag) != 0;
            }
        }
    }

    if (bValid)
    {
        jvStatus["ledger_hash"] = to_string (lrLedger->getHash ());
        jvStatus["ledger_index"] = lrLedger->getLedgerSeq ();
    }
    return bValid;
}