bool
PathPaymentOpFrame::doCheckValid(medida::MetricsRegistry& metrics)
{
    if (mPathPayment.destAmount <= 0 || mPathPayment.sendMax <= 0)
    {
        metrics.NewMeter({"op-path-payment", "invalid", "malformed-amounts"},
                         "operation").Mark();
        innerResult().code(PATH_PAYMENT_MALFORMED);
        return false;
    }
    if (!isAssetValid(mPathPayment.sendAsset) ||
        !isAssetValid(mPathPayment.destAsset))
    {
        metrics.NewMeter({"op-path-payment", "invalid", "malformed-currencies"},
                         "operation").Mark();
        innerResult().code(PATH_PAYMENT_MALFORMED);
        return false;
    }
    auto const& p = mPathPayment.path;
    if (!std::all_of(p.begin(), p.end(), isAssetValid))
    {
        metrics.NewMeter({"op-path-payment", "invalid", "malformed-currencies"},
                         "operation").Mark();
        innerResult().code(PATH_PAYMENT_MALFORMED);
        return false;
    }
    return true;
}
// makes sure the currencies are different
bool
ManageOfferOpFrame::doCheckValid(medida::MetricsRegistry& metrics)
{
    Asset const& sheep = mManageOffer.selling;
    Asset const& wheat = mManageOffer.buying;

    if (!isAssetValid(sheep) || !isAssetValid(wheat))
    {
        metrics.NewMeter({"op-manage-offer", "invalid", "invalid-asset"},
                         "operation").Mark();
        innerResult().code(MANAGE_OFFER_MALFORMED);
        return false;
    }
    if (compareAsset(sheep, wheat))
    {
        metrics.NewMeter({"op-manage-offer", "invalid", "equal-currencies"},
                         "operation").Mark();
        innerResult().code(MANAGE_OFFER_MALFORMED);
        return false;
    }
    if (mManageOffer.amount < 0 || mManageOffer.price.d <= 0 ||
        mManageOffer.price.n <= 0)
    {
        metrics.NewMeter(
                    {"op-manage-offer", "invalid", "negative-or-zero-values"},
                    "operation").Mark();
        innerResult().code(MANAGE_OFFER_MALFORMED);
        return false;
    }

    return true;
}
bool
CreateAccountOpFrame::doApply(medida::MetricsRegistry& metrics,
                              LedgerDelta& delta, LedgerManager& ledgerManager)
{
    AccountFrame::pointer destAccount;

    Database& db = ledgerManager.getDatabase();

    destAccount = AccountFrame::loadAccount(mCreateAccount.destination, db);
    if (!destAccount)
    {
        if (mCreateAccount.startingBalance < ledgerManager.getMinBalance(0))
        { // not over the minBalance to make an account
            metrics.NewMeter({"op-create-account", "failure", "low-reserve"},
                             "operation").Mark();
            innerResult().code(CREATE_ACCOUNT_LOW_RESERVE);
            return false;
        }
        else
        {
            int64_t minBalance =
                mSourceAccount->getMinimumBalance(ledgerManager);

            if (mSourceAccount->getAccount().balance <
                (minBalance + mCreateAccount.startingBalance))
            { // they don't have enough to send
                metrics.NewMeter({"op-create-account", "failure", "underfunded"},
                                 "operation").Mark();
                innerResult().code(CREATE_ACCOUNT_UNDERFUNDED);
                return false;
            }

            mSourceAccount->getAccount().balance -=
                mCreateAccount.startingBalance;
            mSourceAccount->storeChange(delta, db);

            destAccount = make_shared<AccountFrame>(mCreateAccount.destination);
            destAccount->getAccount().seqNum =
                delta.getHeaderFrame().getStartingSequenceNumber();
            destAccount->getAccount().balance = mCreateAccount.startingBalance;

            destAccount->storeAdd(delta, db);

            metrics.NewMeter({"op-create-account", "success", "apply"},
                             "operation").Mark();
            innerResult().code(CREATE_ACCOUNT_SUCCESS);
            return true;
        }
    }
    else
    {
        metrics.NewMeter({"op-create-account", "failure", "already-exist"},
                         "operation").Mark();
        innerResult().code(CREATE_ACCOUNT_ALREADY_EXIST);
        return false;
    }
}
// make sure these issuers exist and you can hold the ask asset
bool
ManageOfferOpFrame::checkOfferValid(medida::MetricsRegistry& metrics,
                                    Database& db)
{
    Asset const& sheep = mManageOffer.selling;
    Asset const& wheat = mManageOffer.buying;

    if (sheep.type() != ASSET_TYPE_NATIVE)
    {
        mSheepLineA = TrustFrame::loadTrustLine(getSourceID(), sheep, db);
        if (!mSheepLineA)
        { // we don't have what we are trying to sell
            metrics.NewMeter(
                        {"op-manage-offer", "invalid", "underfunded-absent"},
                        "operation").Mark();
            innerResult().code(MANAGE_OFFER_SELL_NO_TRUST);
            return false;
        }
        if (mSheepLineA->getBalance() == 0)
        {
            metrics.NewMeter(
                        {"op-manage-offer", "invalid", "underfunded-absent"},
                        "operation").Mark();
            innerResult().code(MANAGE_OFFER_UNDERFUNDED);
            return false;
        }
        if (!mSheepLineA->isAuthorized())
        {
            metrics.NewMeter({"op-manage-offer", "invalid", "not-authorized"},
                             "operation").Mark();
            // we are not authorized to sell
            innerResult().code(MANAGE_OFFER_SELL_NOT_AUTHORIZED);
            return false;
        }
    }

    if (wheat.type() != ASSET_TYPE_NATIVE)
    {
        mWheatLineA = TrustFrame::loadTrustLine(getSourceID(), wheat, db);
        if (!mWheatLineA)
        { // we can't hold what we are trying to buy
            metrics.NewMeter({"op-manage-offer", "invalid", "no-trust"},
                             "operation").Mark();
            innerResult().code(MANAGE_OFFER_BUY_NO_TRUST);
            return false;
        }

        if (!mWheatLineA->isAuthorized())
        { // we are not authorized to hold what we are trying to buy
            metrics.NewMeter({"op-manage-offer", "invalid", "not-authorized"},
                             "operation").Mark();
            innerResult().code(MANAGE_OFFER_BUY_NOT_AUTHORIZED);
            return false;
        }
    }
    return true;
}
bool
PaymentOpFrame::doCheckValid(medida::MetricsRegistry& metrics)
{
    if (mPayment.amount <= 0)
    {
        metrics.NewMeter({"op-payment", "invalid", "malformed-negative-amount"},
                         "operation").Mark();
        innerResult().code(PAYMENT_MALFORMED);
        return false;
    }
    if (!isAssetValid(mPayment.asset))
    {
        metrics.NewMeter({"op-payment", "invalid", "malformed-invalid-asset"},
                         "operation").Mark();
        innerResult().code(PAYMENT_MALFORMED);
        return false;
    }
    return true;
}
bool
ChangeTrustOpFrame::doCheckValid(medida::MetricsRegistry& metrics)
{
    if (mChangeTrust.limit < 0)
    {
        metrics.NewMeter(
                    {"op-change-trust", "invalid", "malformed-negative-limit"},
                    "operation").Mark();
        innerResult().code(CHANGE_TRUST_MALFORMED);
        return false;
    }
    if (!isAssetValid(mChangeTrust.line))
    {
        metrics.NewMeter(
                    {"op-change-trust", "invalid", "malformed-invalid-asset"},
                    "operation").Mark();
        innerResult().code(CHANGE_TRUST_MALFORMED);
        return false;
    }
    return true;
}
bool
CreateAccountOpFrame::doCheckValid(medida::MetricsRegistry& metrics)
{
    if (mCreateAccount.startingBalance <= 0)
    {
        metrics.NewMeter({"op-create-account", "invalid",
                          "malformed-negative-balance"},
                         "operation").Mark();
        innerResult().code(CREATE_ACCOUNT_MALFORMED);
        return false;
    }

    if (mCreateAccount.destination == getSourceID())
    {
        metrics.NewMeter({"op-create-account", "invalid",
                          "malformed-destination-equals-source"},
                         "operation").Mark();
        innerResult().code(CREATE_ACCOUNT_MALFORMED);
        return false;
    }

    return true;
}
LoadGenerator::TxMetrics::TxMetrics(medida::MetricsRegistry& m)
    : mAccountCreated(m.NewMeter({"loadgen", "account", "created"}, "account"))
    , mPayment(m.NewMeter({"loadgen", "payment", "any"}, "payment"))
    , mNativePayment(m.NewMeter({"loadgen", "payment", "native"}, "payment"))
    , mTxnAttempted(m.NewMeter({"loadgen", "txn", "attempted"}, "txn"))
    , mTxnRejected(m.NewMeter({"loadgen", "txn", "rejected"}, "txn"))
    , mTxnBytes(m.NewMeter({"loadgen", "txn", "bytes"}, "txn"))
{
}
// make sure these issuers exist and you can hold the ask asset
bool
ManageOfferOpFrame::checkOfferValid(medida::MetricsRegistry& metrics,
                                    Database& db, LedgerDelta& delta)
{
    Asset const& sheep = mManageOffer.selling;
    Asset const& wheat = mManageOffer.buying;

    if (mManageOffer.amount == 0)
    {
        // don't bother loading trust lines as we're deleting the offer
        return true;
    }

    if (sheep.type() != ASSET_TYPE_NATIVE)
    {
        auto tlI =
            TrustFrame::loadTrustLineIssuer(getSourceID(), sheep, db, delta);
        mSheepLineA = tlI.first;
        if (!tlI.second)
        {
            metrics.NewMeter({"op-manage-offer", "invalid", "sell-no-issuer"},
                             "operation").Mark();
            innerResult().code(MANAGE_OFFER_SELL_NO_ISSUER);
            return false;
        }
        if (!mSheepLineA)
        { // we don't have what we are trying to sell
            metrics.NewMeter({"op-manage-offer", "invalid", "sell-no-trust"},
                             "operation").Mark();
            innerResult().code(MANAGE_OFFER_SELL_NO_TRUST);
            return false;
        }
        if (mSheepLineA->getBalance() == 0)
        {
            metrics.NewMeter({"op-manage-offer", "invalid", "underfunded"},
                             "operation").Mark();
            innerResult().code(MANAGE_OFFER_UNDERFUNDED);
            return false;
        }
        if (!mSheepLineA->isAuthorized())
        {
            metrics.NewMeter(
                        {"op-manage-offer", "invalid", "sell-not-authorized"},
                        "operation").Mark();
            // we are not authorized to sell
            innerResult().code(MANAGE_OFFER_SELL_NOT_AUTHORIZED);
            return false;
        }
    }

    if (wheat.type() != ASSET_TYPE_NATIVE)
    {
        auto tlI =
            TrustFrame::loadTrustLineIssuer(getSourceID(), wheat, db, delta);
        mWheatLineA = tlI.first;
        if (!tlI.second)
        {
            metrics.NewMeter({"op-manage-offer", "invalid", "buy-no-issuer"},
                             "operation").Mark();
            innerResult().code(MANAGE_OFFER_BUY_NO_ISSUER);
            return false;
        }
        if (!mWheatLineA)
        { // we can't hold what we are trying to buy
            metrics.NewMeter({"op-manage-offer", "invalid", "buy-no-trust"},
                             "operation").Mark();
            innerResult().code(MANAGE_OFFER_BUY_NO_TRUST);
            return false;
        }
        if (!mWheatLineA->isAuthorized())
        { // we are not authorized to hold what we are trying to buy
            metrics.NewMeter(
                        {"op-manage-offer", "invalid", "buy-not-authorized"},
                        "operation").Mark();
            innerResult().code(MANAGE_OFFER_BUY_NOT_AUTHORIZED);
            return false;
        }
    }
    return true;
}
// you are selling sheep for wheat
// need to check the counter offers selling wheat for sheep
// see if this is modifying an old offer
// see if this offer crosses any existing offers
bool
ManageOfferOpFrame::doApply(medida::MetricsRegistry& metrics,
                            LedgerDelta& delta, LedgerManager& ledgerManager)
{
    Database& db = ledgerManager.getDatabase();

    if (!checkOfferValid(metrics, db, delta))
    {
        return false;
    }

    Asset const& sheep = mManageOffer.selling;
    Asset const& wheat = mManageOffer.buying;

    bool creatingNewOffer = false;
    uint64_t offerID = mManageOffer.offerID;

    if (offerID)
    { // modifying an old offer
        mSellSheepOffer =
            OfferFrame::loadOffer(getSourceID(), offerID, db, &delta);

        if (!mSellSheepOffer)
        {
            metrics.NewMeter({"op-manage-offer", "invalid", "not-found"},
                             "operation").Mark();
            innerResult().code(MANAGE_OFFER_NOT_FOUND);
            return false;
        }

        // rebuild offer based off the manage offer
        mSellSheepOffer->getOffer() = buildOffer(
            getSourceID(), mManageOffer, mSellSheepOffer->getOffer().flags);
        mPassive = mSellSheepOffer->getFlags() & PASSIVE_FLAG;
    }
    else
    { // creating a new Offer
        creatingNewOffer = true;
        LedgerEntry le;
        le.data.type(OFFER);
        le.data.offer() = buildOffer(getSourceID(), mManageOffer,
                                     mPassive ? PASSIVE_FLAG : 0);
        mSellSheepOffer = std::make_shared<OfferFrame>(le);
    }

    int64_t maxSheepSend = mSellSheepOffer->getAmount();

    int64_t maxAmountOfSheepCanSell;

    innerResult().code(MANAGE_OFFER_SUCCESS);

    soci::transaction sqlTx(db.getSession());
    LedgerDelta tempDelta(delta);

    if (mManageOffer.amount == 0)
    {
        mSellSheepOffer->getOffer().amount = 0;
    }
    else
    {
        if (sheep.type() == ASSET_TYPE_NATIVE)
        {
            maxAmountOfSheepCanSell =
                mSourceAccount->getBalanceAboveReserve(ledgerManager);
        }
        else
        {
            maxAmountOfSheepCanSell = mSheepLineA->getBalance();
        }

        // the maximum is defined by how much wheat it can receive
        int64_t maxWheatCanSell;
        if (wheat.type() == ASSET_TYPE_NATIVE)
        {
            maxWheatCanSell = INT64_MAX;
        }
        else
        {
            maxWheatCanSell = mWheatLineA->getMaxAmountReceive();
            if (maxWheatCanSell == 0)
            {
                metrics.NewMeter({"op-manage-offer", "invalid", "line-full"},
                                 "operation").Mark();
                innerResult().code(MANAGE_OFFER_LINE_FULL);
                return false;
            }
        }

        Price const& sheepPrice = mSellSheepOffer->getPrice();

        {
            int64_t maxSheepBasedOnWheat;
            if (!bigDivide(maxSheepBasedOnWheat, maxWheatCanSell, sheepPrice.d,
                           sheepPrice.n))
            {
                maxSheepBasedOnWheat = INT64_MAX;
            }

            if (maxAmountOfSheepCanSell > maxSheepBasedOnWheat)
            {
                maxAmountOfSheepCanSell = maxSheepBasedOnWheat;
            }
        }

        // amount of sheep for sale is the lesser of amount we can sell and
        // amount
        // put in the offer
        if (maxAmountOfSheepCanSell < maxSheepSend)
        {
            maxSheepSend = maxAmountOfSheepCanSell;
        }

        int64_t sheepSent, wheatReceived;

        OfferExchange oe(tempDelta, ledgerManager);

        const Price maxWheatPrice(sheepPrice.d, sheepPrice.n);

        OfferExchange::ConvertResult r = oe.convertWithOffers(
            sheep, maxSheepSend, sheepSent, wheat, maxWheatCanSell,
            wheatReceived, [this, &maxWheatPrice](OfferFrame const& o)
            {
                if (o.getOfferID() == mSellSheepOffer->getOfferID())
                {
                    // don't let the offer cross itself when updating it
                    return OfferExchange::eSkip;
                }
                if ((mPassive && (o.getPrice() >= maxWheatPrice)) ||
                    (o.getPrice() > maxWheatPrice))
                {
                    return OfferExchange::eStop;
                }
                if (o.getSellerID() == getSourceID())
                {
                    // we are crossing our own offer
                    innerResult().code(MANAGE_OFFER_CROSS_SELF);
                    return OfferExchange::eStop;
                }
                return OfferExchange::eKeep;
            });

        switch (r)
        {
        case OfferExchange::eOK:
        case OfferExchange::ePartial:
            break;
        case OfferExchange::eFilterStop:
            if (innerResult().code() != MANAGE_OFFER_SUCCESS)
            {
                return false;
            }
            break;
        }

        // updates the result with the offers that got taken on the way

        for (auto const& oatom : oe.getOfferTrail())
        {
            innerResult().success().offersClaimed.push_back(oatom);
        }

        if (wheatReceived > 0)
        {
            // it's OK to use mSourceAccount, mWheatLineA and mSheepLineA
            // here as OfferExchange won't cross offers from source account
            if (wheat.type() == ASSET_TYPE_NATIVE)
            {
                mSourceAccount->getAccount().balance += wheatReceived;
                mSourceAccount->storeChange(delta, db);
            }
            else
            {
                if (!mWheatLineA->addBalance(wheatReceived))
                {
                    // this would indicate a bug in OfferExchange
                    throw std::runtime_error("offer claimed over limit");
                }

                mWheatLineA->storeChange(delta, db);
            }

            if (sheep.type() == ASSET_TYPE_NATIVE)
            {
                mSourceAccount->getAccount().balance -= sheepSent;
                mSourceAccount->storeChange(delta, db);
            }
            else
            {
                if (!mSheepLineA->addBalance(-sheepSent))
                {
                    // this would indicate a bug in OfferExchange
                    throw std::runtime_error("offer sold more than balance");
                }
                mSheepLineA->storeChange(delta, db);
            }
        }

        // recomputes the amount of sheep for sale
        mSellSheepOffer->getOffer().amount = maxSheepSend - sheepSent;
    }

    if (mSellSheepOffer->getOffer().amount > 0)
    { // we still have sheep to sell so leave an offer

        if (creatingNewOffer)
        {
            // make sure we don't allow us to add offers when we don't have
            // the minbalance
            if (!mSourceAccount->addNumEntries(1, ledgerManager))
            {
                metrics.NewMeter({"op-manage-offer", "invalid", "low reserve"},
                                 "operation").Mark();
                innerResult().code(MANAGE_OFFER_LOW_RESERVE);
                return false;
            }
            mSellSheepOffer->mEntry.data.offer().offerID =
                tempDelta.getHeaderFrame().generateID();
            innerResult().success().offer.effect(MANAGE_OFFER_CREATED);
            mSellSheepOffer->storeAdd(tempDelta, db);
            mSourceAccount->storeChange(tempDelta, db);
        }
        else
        {
            innerResult().success().offer.effect(MANAGE_OFFER_UPDATED);
            mSellSheepOffer->storeChange(tempDelta, db);
        }
        innerResult().success().offer.offer() = mSellSheepOffer->getOffer();
    }
    else
    {
        innerResult().success().offer.effect(MANAGE_OFFER_DELETED);

        if (!creatingNewOffer)
        {
            mSellSheepOffer->storeDelete(tempDelta, db);
            mSourceAccount->addNumEntries(-1, ledgerManager);
            mSourceAccount->storeChange(tempDelta, db);
        }
    }

    sqlTx.commit();
    tempDelta.commit();

    metrics.NewMeter({"op-create-offer", "success", "apply"}, "operation")
        .Mark();
    return true;
}
bool
ChangeTrustOpFrame::doApply(medida::MetricsRegistry& metrics,
                            LedgerDelta& delta, LedgerManager& ledgerManager)
{
    Database& db = ledgerManager.getDatabase();

    auto tlI = TrustFrame::loadTrustLineIssuer(getSourceID(), mChangeTrust.line,
                                               db, delta);

    auto& trustLine = tlI.first;
    auto& issuer = tlI.second;

    if (trustLine)
    { // we are modifying an old trustline

        if (mChangeTrust.limit < trustLine->getBalance())
        { // Can't drop the limit below the balance you are holding with them
            metrics.NewMeter({"op-change-trust", "failure", "invalid-limit"},
                             "operation").Mark();
            innerResult().code(CHANGE_TRUST_INVALID_LIMIT);
            return false;
        }

        if (mChangeTrust.limit == 0)
        {
            // line gets deleted
            trustLine->storeDelete(delta, db);
            mSourceAccount->addNumEntries(-1, ledgerManager);
            mSourceAccount->storeChange(delta, db);
        }
        else
        {
            if (!issuer)
            {
                metrics.NewMeter({"op-change-trust", "failure", "no-issuer"},
                                 "operation").Mark();
                innerResult().code(CHANGE_TRUST_NO_ISSUER);
                return false;
            }
            trustLine->getTrustLine().limit = mChangeTrust.limit;
            trustLine->storeChange(delta, db);
        }
        metrics.NewMeter({"op-change-trust", "success", "apply"}, "operation")
            .Mark();
        innerResult().code(CHANGE_TRUST_SUCCESS);
        return true;
    }
    else
    { // new trust line
        if (mChangeTrust.limit == 0)
        {
            metrics.NewMeter({"op-change-trust", "failure", "invalid-limit"},
                             "operation").Mark();
            innerResult().code(CHANGE_TRUST_INVALID_LIMIT);
            return false;
        }
        if (!issuer)
        {
            metrics.NewMeter({"op-change-trust", "failure", "no-issuer"},
                             "operation").Mark();
            innerResult().code(CHANGE_TRUST_NO_ISSUER);
            return false;
        }
        trustLine = std::make_shared<TrustFrame>();
        auto& tl = trustLine->getTrustLine();
        tl.accountID = getSourceID();
        tl.asset = mChangeTrust.line;
        tl.limit = mChangeTrust.limit;
        tl.balance = 0;
        trustLine->setAuthorized(!issuer->isAuthRequired());

        if (!mSourceAccount->addNumEntries(1, ledgerManager))
        {
            metrics.NewMeter({"op-change-trust", "failure", "low-reserve"},
                             "operation").Mark();
            innerResult().code(CHANGE_TRUST_LOW_RESERVE);
            return false;
        }

        mSourceAccount->storeChange(delta, db);
        trustLine->storeAdd(delta, db);

        metrics.NewMeter({"op-change-trust", "success", "apply"}, "operation")
            .Mark();
        innerResult().code(CHANGE_TRUST_SUCCESS);
        return true;
    }
}
Exemple #12
0
bool
InflationOpFrame::doApply(medida::MetricsRegistry& metrics, LedgerDelta& delta,
                          LedgerManager& ledgerManager)
{
    LedgerDelta inflationDelta(delta);

    auto& lcl = inflationDelta.getHeader();

    time_t closeTime = lcl.scpValue.closeTime;
    uint32_t seq = lcl.inflationSeq;

    time_t inflationTime = (INFLATION_START_TIME + seq * INFLATION_FREQUENCY);
    if (closeTime < inflationTime)
    {
        metrics.NewMeter({"op-inflation", "failure", "not-time"}, "operation")
            .Mark();
        innerResult().code(INFLATION_NOT_TIME);
        return false;
    }

    /*
    Inflation is calculated using the following

    1. calculate tally of votes based on "inflationDest" set on each account
    2. take the top accounts (by vote) that get at least .05% of the vote
    3. If no accounts are over this threshold then the extra goes back to the 
       inflation pool
    */

    int64_t totalVotes = lcl.totalCoins;
    int64_t minBalance =
        bigDivide(totalVotes, INFLATION_WIN_MIN_PERCENT, TRILLION);

    std::vector<AccountFrame::InflationVotes> winners;
    auto& db = ledgerManager.getDatabase();

    AccountFrame::processForInflation(
        [&](AccountFrame::InflationVotes const& votes)
        {
            if (votes.mVotes >= minBalance)
            {
                winners.push_back(votes);
                return true;
            }
            return false;
        },
        INFLATION_NUM_WINNERS, db);

    int64 amountToDole =
        bigDivide(lcl.totalCoins, INFLATION_RATE_TRILLIONTHS, TRILLION);
    amountToDole += lcl.feePool;

    lcl.feePool = 0;
    lcl.inflationSeq++;

    // now credit each account
    innerResult().code(INFLATION_SUCCESS);
    auto& payouts = innerResult().payouts();

    int64 leftAfterDole = amountToDole;

   
    for (auto const& w : winners)
    {
        AccountFrame::pointer winner;

        int64 toDoleThisWinner =
            bigDivide(amountToDole, w.mVotes, totalVotes);

        if (toDoleThisWinner == 0)
            continue;

        winner = AccountFrame::loadAccount(w.mInflationDest, db);

        if (winner)
        {
            leftAfterDole -= toDoleThisWinner;
            lcl.totalCoins += toDoleThisWinner;
            winner->getAccount().balance += toDoleThisWinner;
            winner->storeChange(inflationDelta, db);
            payouts.emplace_back(w.mInflationDest, toDoleThisWinner);
        }
    }
   
    // put back in fee pool as unclaimed funds
    lcl.feePool += leftAfterDole;
    

    inflationDelta.commit();

    metrics.NewMeter({"op-inflation", "success", "apply"}, "operation").Mark();
    return true;
}
bool
PathPaymentOpFrame::doApply(medida::MetricsRegistry& metrics,
                            LedgerDelta& delta, LedgerManager& ledgerManager)
{
    Database& db = ledgerManager.getDatabase();

    innerResult().code(PATH_PAYMENT_SUCCESS);

    // tracks the last amount that was traded
    int64_t curBReceived = mPathPayment.destAmount;
    Asset curB = mPathPayment.destAsset;

    // update balances, walks backwards

    // build the full path to the destination, starting with sendAsset
    std::vector<Asset> fullPath;
    fullPath.emplace_back(mPathPayment.sendAsset);
    fullPath.insert(fullPath.end(), mPathPayment.path.begin(),
                    mPathPayment.path.end());

    bool bypassIssuerCheck = false;

    // if the payment doesn't involve intermediate accounts
    // and the destination is the issuer we don't bother
    // checking if the destination account even exist
    // so that it's always possible to send credits back to its issuer
    bypassIssuerCheck = (curB.type() != ASSET_TYPE_NATIVE) &&
                        (fullPath.size() == 1) &&
                        (mPathPayment.sendAsset == mPathPayment.destAsset) &&
                        (getIssuer(curB) == mPathPayment.destination);

    AccountFrame::pointer destination;

    if (!bypassIssuerCheck)
    {
        destination = AccountFrame::loadAccount(mPathPayment.destination, db);

        if (!destination)
        {
            metrics.NewMeter({"op-path-payment", "failure", "no-destination"},
                             "operation").Mark();
            innerResult().code(PATH_PAYMENT_NO_DESTINATION);
            return false;
        }
    }

    // update last balance in the chain
    if (curB.type() == ASSET_TYPE_NATIVE)
    {
        destination->getAccount().balance += curBReceived;
        destination->storeChange(delta, db);
    }
    else
    {
        TrustFrame::pointer destLine;

        if (bypassIssuerCheck)
        {
            destLine =
                TrustFrame::loadTrustLine(mPathPayment.destination, curB, db);
        }
        else
        {
            auto tlI = TrustFrame::loadTrustLineIssuer(mPathPayment.destination,
                                                       curB, db);
            if (!tlI.second)
            {
                metrics.NewMeter({"op-path-payment", "failure", "no-issuer"},
                                 "operation").Mark();
                innerResult().code(PATH_PAYMENT_NO_ISSUER);
                innerResult().noIssuer() = curB;
                return false;
            }
            destLine = tlI.first;
        }

        if (!destLine)
        {
            metrics.NewMeter({"op-path-payment", "failure", "no-trust"},
                             "operation").Mark();
            innerResult().code(PATH_PAYMENT_NO_TRUST);
            return false;
        }

        if (!destLine->isAuthorized())
        {
            metrics.NewMeter({"op-path-payment", "failure", "not-authorized"},
                             "operation").Mark();
            innerResult().code(PATH_PAYMENT_NOT_AUTHORIZED);
            return false;
        }

        if (!destLine->addBalance(curBReceived))
        {
            metrics.NewMeter({"op-path-payment", "failure", "line-full"},
                             "operation").Mark();
            innerResult().code(PATH_PAYMENT_LINE_FULL);
            return false;
        }

        destLine->storeChange(delta, db);
    }

    innerResult().success().last =
        SimplePaymentResult(mPathPayment.destination, curB, curBReceived);

    // now, walk the path backwards
    for (int i = (int)fullPath.size() - 1; i >= 0; i--)
    {
        int64_t curASent, actualCurBReceived;
        Asset const& curA = fullPath[i];

        if (curA == curB)
        {
            continue;
        }

        if (curA.type() != ASSET_TYPE_NATIVE)
        {
            if (!AccountFrame::loadAccount(getIssuer(curA), db))
            {
                metrics.NewMeter({"op-path-payment", "failure", "no-issuer"},
                                 "operation").Mark();
                innerResult().code(PATH_PAYMENT_NO_ISSUER);
                innerResult().noIssuer() = curA;
                return false;
            }
        }

        OfferExchange oe(delta, ledgerManager);

        // curA -> curB
        OfferExchange::ConvertResult r = oe.convertWithOffers(
            curA, INT64_MAX, curASent, curB, curBReceived, actualCurBReceived,
            [this, &metrics](OfferFrame const& o)
            {
                if (o.getSellerID() == getSourceID())
                {
                    // we are crossing our own offer, potentially invalidating
                    // mSourceAccount (balance or numSubEntries)
                    metrics.NewMeter({"op-path-payment", "failure",
                                      "offer-cross-self"},
                                     "operation").Mark();
                    innerResult().code(PATH_PAYMENT_OFFER_CROSS_SELF);
                    return OfferExchange::eStop;
                }
                return OfferExchange::eKeep;
            });
        switch (r)
        {
        case OfferExchange::eFilterStop:
            return false;
        case OfferExchange::eOK:
            if (curBReceived == actualCurBReceived)
            {
                break;
            }
        // fall through
        case OfferExchange::ePartial:
            metrics.NewMeter({"op-path-payment", "failure", "too-few-offers"},
                             "operation").Mark();
            innerResult().code(PATH_PAYMENT_TOO_FEW_OFFERS);
            return false;
        }
        assert(curBReceived == actualCurBReceived);
        curBReceived = curASent; // next round, we need to send enough
        curB = curA;

        // add offers that got taken on the way
        // insert in front to match the path's order
        auto& offers = innerResult().success().offers;
        offers.insert(offers.begin(), oe.getOfferTrail().begin(),
                      oe.getOfferTrail().end());
    }

    // last step: we've reached the first account in the chain, update its
    // balance

    int64_t curBSent;

    curBSent = curBReceived;

    if (curBSent > mPathPayment.sendMax)
    { // make sure not over the max
        metrics.NewMeter({"op-path-payment", "failure", "over-send-max"},
                         "operation").Mark();
        innerResult().code(PATH_PAYMENT_OVER_SENDMAX);
        return false;
    }

    if (curB.type() == ASSET_TYPE_NATIVE)
    {
        int64_t minBalance = mSourceAccount->getMinimumBalance(ledgerManager);

        if ((mSourceAccount->getAccount().balance - curBSent) < minBalance)
        { // they don't have enough to send
            metrics.NewMeter({"op-path-payment", "failure", "underfunded"},
                             "operation").Mark();
            innerResult().code(PATH_PAYMENT_UNDERFUNDED);
            return false;
        }

        mSourceAccount->getAccount().balance -= curBSent;
        mSourceAccount->storeChange(delta, db);
    }
    else
    {
        TrustFrame::pointer sourceLineFrame;
        if (bypassIssuerCheck)
        {
            sourceLineFrame =
                TrustFrame::loadTrustLine(getSourceID(), curB, db);
        }
        else
        {
            auto tlI = TrustFrame::loadTrustLineIssuer(getSourceID(), curB, db);

            if (!tlI.second)
            {
                metrics.NewMeter({"op-path-payment", "failure", "no-issuer"},
                                 "operation").Mark();
                innerResult().code(PATH_PAYMENT_NO_ISSUER);
                innerResult().noIssuer() = curB;
                return false;
            }
            sourceLineFrame = tlI.first;
        }

        if (!sourceLineFrame)
        {
            metrics.NewMeter({"op-path-payment", "failure", "src-no-trust"},
                             "operation").Mark();
            innerResult().code(PATH_PAYMENT_SRC_NO_TRUST);
            return false;
        }

        if (!sourceLineFrame->isAuthorized())
        {
            metrics.NewMeter(
                        {"op-path-payment", "failure", "src-not-authorized"},
                        "operation").Mark();
            innerResult().code(PATH_PAYMENT_SRC_NOT_AUTHORIZED);
            return false;
        }

        if (!sourceLineFrame->addBalance(-curBSent))
        {
            metrics.NewMeter({"op-path-payment", "failure", "underfunded"},
                             "operation").Mark();
            innerResult().code(PATH_PAYMENT_UNDERFUNDED);
            return false;
        }

        sourceLineFrame->storeChange(delta, db);
    }

    metrics.NewMeter({"op-path-payment", "success", "apply"}, "operation")
        .Mark();

    return true;
}
bool
PathPaymentOpFrame::doApply(medida::MetricsRegistry& metrics,
                            LedgerDelta& delta, LedgerManager& ledgerManager)
{
    AccountFrame::pointer destination;

    Database& db = ledgerManager.getDatabase();

    destination = AccountFrame::loadAccount(mPathPayment.destination, db);

    if (!destination)
    {
        metrics.NewMeter({"op-path-payment", "failure", "no-destination"},
                         "operation").Mark();
        innerResult().code(PATH_PAYMENT_NO_DESTINATION);
        return false;
    }

    innerResult().code(PATH_PAYMENT_SUCCESS);

    // tracks the last amount that was traded
    int64_t curBReceived = mPathPayment.destAmount;
    Asset curB = mPathPayment.destAsset;

    // update balances, walks backwards

    // build the full path to the destination, starting with sendAsset
    std::vector<Asset> fullPath;
    fullPath.emplace_back(mPathPayment.sendAsset);
    fullPath.insert(fullPath.end(), mPathPayment.path.begin(),
                    mPathPayment.path.end());

    // update last balance in the chain
    {
        if (curB.type() == ASSET_TYPE_NATIVE)
        {
            destination->getAccount().balance += curBReceived;
            destination->storeChange(delta, db);
        }
        else
        {
            TrustFrame::pointer destLine;

            destLine =
                TrustFrame::loadTrustLine(destination->getID(), curB, db);
            if (!destLine)
            {
                metrics.NewMeter({"op-path-payment", "failure", "no-trust"},
                                 "operation").Mark();
                innerResult().code(PATH_PAYMENT_NO_TRUST);
                return false;
            }

            if (!destLine->isAuthorized())
            {
                metrics.NewMeter(
                            {"op-path-payment", "failure", "not-authorized"},
                            "operation").Mark();
                innerResult().code(PATH_PAYMENT_NOT_AUTHORIZED);
                return false;
            }

            if (!destLine->addBalance(curBReceived))
            {
                metrics.NewMeter({"op-path-payment", "failure", "line-full"},
                                 "operation").Mark();
                innerResult().code(PATH_PAYMENT_LINE_FULL);
                return false;
            }

            destLine->storeChange(delta, db);
        }

        innerResult().success().last =
            SimplePaymentResult(destination->getID(), curB, curBReceived);
    }

    // now, walk the path backwards
    for (int i = (int)fullPath.size() - 1; i >= 0; i--)
    {
        int64_t curASent, actualCurBReceived;
        Asset const& curA = fullPath[i];

        if (curA == curB)
        {
            continue;
        }

        OfferExchange oe(delta, ledgerManager);

        // curA -> curB
        OfferExchange::ConvertResult r =
            oe.convertWithOffers(curA, INT64_MAX, curASent, curB, curBReceived,
                                 actualCurBReceived, nullptr);
        switch (r)
        {
        case OfferExchange::eFilterStop:
            assert(false); // no filter -> should not happen
            break;
        case OfferExchange::eOK:
            if (curBReceived == actualCurBReceived)
            {
                break;
            }
        // fall through
        case OfferExchange::ePartial:
            metrics.NewMeter({"op-path-payment", "failure", "too-few-offers"},
                             "operation").Mark();
            innerResult().code(PATH_PAYMENT_TOO_FEW_OFFERS);
            return false;
        }
        assert(curBReceived == actualCurBReceived);
        curBReceived = curASent; // next round, we need to send enough
        curB = curA;

        // add offers that got taken on the way
        // insert in front to match the path's order
        auto& offers = innerResult().success().offers;
        offers.insert(offers.begin(), oe.getOfferTrail().begin(),
                      oe.getOfferTrail().end());
    }

    // last step: we've reached the first account in the chain, update its
    // balance

    int64_t curBSent;

    curBSent = curBReceived;

    if (curBSent > mPathPayment.sendMax)
    { // make sure not over the max
        metrics.NewMeter({"op-path-payment", "failure", "over-send-max"},
                         "operation").Mark();
        innerResult().code(PATH_PAYMENT_OVER_SENDMAX);
        return false;
    }

    if (curB.type() == ASSET_TYPE_NATIVE)
    {
        int64_t minBalance = mSourceAccount->getMinimumBalance(ledgerManager);

        if ((mSourceAccount->getAccount().balance - curBSent) < minBalance)
        { // they don't have enough to send
            metrics.NewMeter({"op-path-payment", "failure", "underfunded"},
                             "operation").Mark();
            innerResult().code(PATH_PAYMENT_UNDERFUNDED);
            return false;
        }

        mSourceAccount->getAccount().balance -= curBSent;
        mSourceAccount->storeChange(delta, db);
    }
    else
    {
        AccountFrame::pointer issuer;
        if(curB.type()==ASSET_TYPE_CREDIT_ALPHANUM4)
            issuer = AccountFrame::loadAccount(curB.alphaNum4().issuer, db);
        else if(curB.type()==ASSET_TYPE_CREDIT_ALPHANUM12)
            issuer = AccountFrame::loadAccount(curB.alphaNum12().issuer, db);

        if (!issuer)
        {
            metrics.NewMeter({"op-path-payment", "failure", "no-issuer"},
                             "operation").Mark();
            throw std::runtime_error("sendCredit Issuer not found");
        }

        TrustFrame::pointer sourceLineFrame;
        sourceLineFrame = TrustFrame::loadTrustLine(getSourceID(), curB, db);
        if (!sourceLineFrame)
        {
            metrics.NewMeter({"op-path-payment", "failure", "src-no-trust"},
                             "operation").Mark();
            innerResult().code(PATH_PAYMENT_SRC_NO_TRUST);
            return false;
        }

        if (!sourceLineFrame->isAuthorized())
        {
            metrics.NewMeter(
                        {"op-path-payment", "failure", "src-not-authorized"},
                        "operation").Mark();
            innerResult().code(PATH_PAYMENT_SRC_NOT_AUTHORIZED);
            return false;
        }

        if (!sourceLineFrame->addBalance(-curBSent))
        {
            metrics.NewMeter({"op-path-payment", "failure", "underfunded"},
                             "operation").Mark();
            innerResult().code(PATH_PAYMENT_UNDERFUNDED);
            return false;
        }

        sourceLineFrame->storeChange(delta, db);
    }

    metrics.NewMeter({"op-path-payment", "success", "apply"}, "operation")
        .Mark();

    return true;
}
bool
InflationOpFrame::doApply(medida::MetricsRegistry& metrics, LedgerDelta& delta,
                          LedgerManager& ledgerManager)
{
    LedgerDelta inflationDelta(delta);

    auto& lcl = inflationDelta.getHeader();

    time_t closeTime = lcl.scpValue.closeTime;
    uint32_t seq = lcl.inflationSeq;

    time_t inflationTime = (INFLATION_START_TIME + seq * INFLATION_FREQUENCY);
    if (closeTime < inflationTime)
    {
        metrics.NewMeter({"op-inflation", "failure", "not-time"}, "operation")
            .Mark();
        innerResult().code(INFLATION_NOT_TIME);
        return false;
    }

    /*
    Inflation is calculated using the following

    1. calculate tally of votes based on "inflationDest" set on each account
    2. take the top accounts (by vote) that exceed 1.5%
        (INFLATION_WIN_MIN_PERCENT) of votes,
        up to 50 accounts (INFLATION_NUM_WINNERS)
    exception:
    if no account crosses the INFLATION_WIN_MIN_PERCENT, the top 50 is used
    3. share the coins between those accounts proportionally to the number
        of votes they got.
    */

    int64_t totalVotes = 0;
    bool first = true;
    int64_t minBalance =
        bigDivide(lcl.totalCoins, INFLATION_WIN_MIN_PERCENT, TRILLION);

    std::vector<AccountFrame::InflationVotes> winners;
    auto& db = ledgerManager.getDatabase();

    AccountFrame::processForInflation(
        [&](AccountFrame::InflationVotes const& votes)
        {
            if (first && votes.mVotes < minBalance)
            {
                // need to take the entire set if nobody crossed the threshold
                minBalance = 0;
            }

            first = false;

            bool res;

            if (votes.mVotes >= minBalance)
            {
                totalVotes += votes.mVotes;
                winners.push_back(votes);
                res = true;
            }
            else
            {
                res = false;
            }

            return res;
        },
        INFLATION_NUM_WINNERS, db);

    int64 amountToDole =
        bigDivide(lcl.totalCoins, INFLATION_RATE_TRILLIONTHS, TRILLION);
    amountToDole += lcl.feePool;

    lcl.feePool = 0;
    lcl.inflationSeq++;

    // now credit each account
    innerResult().code(INFLATION_SUCCESS);
    auto& payouts = innerResult().payouts();

    if (totalVotes != 0)
    {
        for (auto const& w : winners)
        {
            AccountFrame::pointer winner;

            int64 toDoleThisWinner =
                bigDivide(amountToDole, w.mVotes, totalVotes);

            if (toDoleThisWinner == 0)
                continue;

            winner = AccountFrame::loadAccount(w.mInflationDest, db);

            if (winner)
            {
                lcl.totalCoins += toDoleThisWinner;
                winner->getAccount().balance += toDoleThisWinner;
                winner->storeChange(inflationDelta, db);
                payouts.emplace_back(w.mInflationDest, toDoleThisWinner);
            }
            else
            {
                // put back in fee pool as unclaimed funds
                lcl.feePool += toDoleThisWinner;
            }
        }
    }
    else
    {
        // put back in fee pool as unclaimed funds
        lcl.feePool += amountToDole;
    }

    inflationDelta.commit();

    metrics.NewMeter({"op-inflation", "success", "apply"}, "operation").Mark();
    return true;
}
bool
PaymentOpFrame::doApply(medida::MetricsRegistry& metrics, LedgerDelta& delta,
                        LedgerManager& ledgerManager)
{
    // if sending to self directly, just mark as success
    if (mPayment.destination == getSourceID())
    {
        metrics.NewMeter({"op-payment", "success", "apply"}, "operation")
            .Mark();
        innerResult().code(PAYMENT_SUCCESS);
        return true;
    }

    // build a pathPaymentOp
    Operation op;
    op.sourceAccount = mOperation.sourceAccount;
    op.body.type(PATH_PAYMENT);
    PathPaymentOp& ppOp = op.body.pathPaymentOp();
    ppOp.sendAsset = mPayment.asset;
    ppOp.destAsset = mPayment.asset;

    ppOp.destAmount = mPayment.amount;
    ppOp.sendMax = mPayment.amount;

    ppOp.destination = mPayment.destination;

    OperationResult opRes;
    opRes.code(opINNER);
    opRes.tr().type(PATH_PAYMENT);
    PathPaymentOpFrame ppayment(op, opRes, mParentTx);
    ppayment.setSourceAccountPtr(mSourceAccount);

    if (!ppayment.doCheckValid(metrics) ||
        !ppayment.doApply(metrics, delta, ledgerManager))
    {
        if (ppayment.getResultCode() != opINNER)
        {
            throw std::runtime_error("Unexpected error code from pathPayment");
        }
        PaymentResultCode res;

        switch (PathPaymentOpFrame::getInnerCode(ppayment.getResult()))
        {
        case PATH_PAYMENT_UNDERFUNDED:
            metrics.NewMeter({"op-payment", "failure", "underfunded"},
                             "operation").Mark();
            res = PAYMENT_UNDERFUNDED;
            break;
        case PATH_PAYMENT_SRC_NOT_AUTHORIZED:
            metrics.NewMeter({"op-payment", "failure", "src-not-authorized"},
                             "operation").Mark();
            res = PAYMENT_SRC_NOT_AUTHORIZED;
            break;
        case PATH_PAYMENT_SRC_NO_TRUST:
            metrics.NewMeter({"op-payment", "failure", "src-no-trust"},
                             "operation").Mark();
            res = PAYMENT_SRC_NO_TRUST;
            break;
        case PATH_PAYMENT_NO_DESTINATION:
            metrics.NewMeter({"op-payment", "failure", "no-destination"},
                             "operation").Mark();
            res = PAYMENT_NO_DESTINATION;
            break;
        case PATH_PAYMENT_NO_TRUST:
            metrics.NewMeter({"op-payment", "failure", "no-trust"}, "operation")
                .Mark();
            res = PAYMENT_NO_TRUST;
            break;
        case PATH_PAYMENT_NOT_AUTHORIZED:
            metrics.NewMeter({"op-payment", "failure", "not-authorized"},
                             "operation").Mark();
            res = PAYMENT_NOT_AUTHORIZED;
            break;
        case PATH_PAYMENT_LINE_FULL:
            metrics.NewMeter({"op-payment", "failure", "line-full"},
                             "operation").Mark();
            res = PAYMENT_LINE_FULL;
            break;
        case PATH_PAYMENT_NO_ISSUER:
            metrics.NewMeter({"op-payment", "failure", "no-issuer"},
                             "operation").Mark();
            res = PAYMENT_NO_ISSUER;
            break;
        default:
            throw std::runtime_error("Unexpected error code from pathPayment");
        }
        innerResult().code(res);
        return false;
    }

    assert(PathPaymentOpFrame::getInnerCode(ppayment.getResult()) ==
           PATH_PAYMENT_SUCCESS);

    metrics.NewMeter({"op-payment", "success", "apply"}, "operation").Mark();
    innerResult().code(PAYMENT_SUCCESS);

    return true;
}
Exemple #17
0
void
checkDBAgainstBuckets(medida::MetricsRegistry& metrics,
                      BucketManager& bucketManager, Database& db,
                      BucketList& bl)
{
    CLOG(INFO, "Bucket") << "CheckDB starting";
    auto execTimer =
        metrics.NewTimer({"bucket", "checkdb", "execute"}).TimeScope();

    // Step 1: Collect all buckets to merge.
    std::vector<std::shared_ptr<Bucket>> buckets;
    for (size_t i = 0; i < BucketList::kNumLevels; ++i)
    {
        CLOG(INFO, "Bucket") << "CheckDB collecting buckets from level " << i;
        auto& level = bl.getLevel(i);
        auto& next = level.getNext();
        if (next.isLive())
        {
            CLOG(INFO, "Bucket") << "CheckDB resolving future bucket on level "
                                 << i;
            buckets.push_back(next.resolve());
        }
        buckets.push_back(level.getCurr());
        buckets.push_back(level.getSnap());
    }

    if (buckets.empty())
    {
        CLOG(INFO, "Bucket") << "CheckDB found no buckets, returning";
        return;
    }

    // Step 2: merge all buckets into a single super-bucket.
    auto i = buckets.begin();
    assert(i != buckets.end());
    std::shared_ptr<Bucket> superBucket = *i;
    while (++i != buckets.end())
    {
        auto mergeTimer =
            metrics.NewTimer({"bucket", "checkdb", "merge"}).TimeScope();
        assert(superBucket);
        assert(*i);
        superBucket = Bucket::merge(bucketManager, *i, superBucket);
        assert(superBucket);
    }

    CLOG(INFO, "Bucket") << "CheckDB starting object comparison";

    // Step 3: scan the superbucket, checking each object against the DB and
    // counting objects along the way.
    uint64_t nAccounts = 0, nTrustLines = 0, nOffers = 0;
    {
        auto& meter = metrics.NewMeter({"bucket", "checkdb", "object-compare"},
                                       "comparison");
        auto compareTimer =
            metrics.NewTimer({"bucket", "checkdb", "compare"}).TimeScope();
        for (Bucket::InputIterator iter(superBucket); iter; ++iter)
        {
            meter.Mark();
            auto& e = *iter;
            if (e.type() == LIVEENTRY)
            {
                switch (e.liveEntry().data.type())
                {
                case ACCOUNT:
                    ++nAccounts;
                    break;
                case TRUSTLINE:
                    ++nTrustLines;
                    break;
                case OFFER:
                    ++nOffers;
                    break;
                }
                EntryFrame::checkAgainstDatabase(e.liveEntry(), db);
                if (meter.count() % 100 == 0)
                {
                    CLOG(INFO, "Bucket") << "CheckDB compared " << meter.count()
                                         << " objects";
                }
            }
        }
    }

    // Step 4: confirm size of datasets matches size of datasets in DB.
    soci::session& sess = db.getSession();
    compareSizes("account", AccountFrame::countObjects(sess), nAccounts);
    compareSizes("trustline", TrustFrame::countObjects(sess), nTrustLines);
    compareSizes("offer", OfferFrame::countObjects(sess), nOffers);
}