コード例 #1
0
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;
    }
}
コード例 #2
0
void
TxSetFrame::surgePricingFilter(LedgerManager const& lm)
{
    size_t max = lm.getMaxTxSetSize();
    if (mTransactions.size() > max)
    { // surge pricing in effect!
        CLOG(WARNING, "Herder")
            << "surge pricing in effect! " << mTransactions.size();

        // determine the fee ratio for each account
        map<AccountID, double> accountFeeMap;
        for (auto& tx : mTransactions)
        {
            double r = tx->getFeeRatio(lm);
            double now = accountFeeMap[tx->getSourceID()];
            if (now == 0)
                accountFeeMap[tx->getSourceID()] = r;
            else if (r < now)
                accountFeeMap[tx->getSourceID()] = r;
        }

        // sort tx by amount of fee they have paid
        // remove the bottom that aren't paying enough
        std::vector<TransactionFramePtr> tempList = mTransactions;
        std::sort(tempList.begin(), tempList.end(), SurgeSorter(accountFeeMap));

        for (auto iter = tempList.begin() + max; iter != tempList.end(); iter++)
        {
            removeTx(*iter);
        }
    }
}
コード例 #3
0
bool
CreateAccountOpFrame::doApply(LedgerDelta& delta, LedgerManager& ledgerManager)
{
    AccountFrame destAccount;

    Database& db = ledgerManager.getDatabase();

    if (!AccountFrame::loadAccount(mCreateAccount.destination, destAccount, db))
    {
        if (mCreateAccount.startingBalance < ledgerManager.getMinBalance(0))
        { // not over the minBalance to make an account
            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
                innerResult().code(CREATE_ACCOUNT_UNDERFUNDED);
                return false;
            }

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

            destAccount.getAccount().accountID = mCreateAccount.destination;
            destAccount.getAccount().seqNum =
                delta.getHeaderFrame().getStartingSequenceNumber();
            destAccount.getAccount().balance = mCreateAccount.startingBalance;

            destAccount.storeAdd(delta, db);

            innerResult().code(CREATE_ACCOUNT_SUCCESS);
            return true;
        }
    }
    else
    {
        innerResult().code(CREATE_ACCOUNT_ALREADY_EXIST);
        return false;
    }
}
コード例 #4
0
bool
ManageDataOpFrame::doApply(Application& app,
                            LedgerDelta& delta, LedgerManager& ledgerManager)
{
    Database& db = ledgerManager.getDatabase();
    
    auto dataFrame = DataFrame::loadData(mSourceAccount->getID(), mManageData.dataName, db);

    if(mManageData.dataValue)
    {
        if(!dataFrame)
        {  // create a new data entry
            
            if(!mSourceAccount->addNumEntries(1, ledgerManager))
            {
                app.getMetrics().NewMeter({ "op-manage-data", "invalid", "low reserve" },
                    "operation").Mark();
                innerResult().code(MANAGE_DATA_LOW_RESERVE);
                return false;
            }

            dataFrame= std::make_shared<DataFrame>();
            dataFrame->getData().accountID= mSourceAccount->getID();
            dataFrame->getData().dataName = mManageData.dataName;
            dataFrame->getData().dataValue = *mManageData.dataValue;

            dataFrame->storeAdd(delta, db);
            mSourceAccount->storeChange(delta, db);
        } else
        {  // modify an existing entry
            dataFrame->getData().dataValue = *mManageData.dataValue;
            dataFrame->storeChange(delta, db);
        }
    } else
    {   // delete an existing piece of data
        
        if(!dataFrame)
        {
            app.getMetrics().NewMeter({ "op-manage-data", "invalid", "not-found" },
                "operation").Mark();
            innerResult().code(MANAGE_DATA_NAME_NOT_FOUND);
            return false;
        }

        mSourceAccount->addNumEntries(-1, ledgerManager);
        mSourceAccount->storeChange(delta, db);
        dataFrame->storeDelete(delta, db);
    }


    innerResult().code(MANAGE_DATA_SUCCESS);

    app.getMetrics().NewMeter({"op-manage-data", "success", "apply"}, "operation")
        .Mark();
    return true;
}
コード例 #5
0
void
TransactionFrame::storeTransaction(LedgerManager& ledgerManager,
                                   LedgerDelta const& delta, int txindex,
                                   SHA256& resultHasher) const
{
    auto txBytes(xdr::xdr_to_opaque(mEnvelope));
    auto txResultBytes(xdr::xdr_to_opaque(getResultPair()));

    resultHasher.add(txResultBytes);

    std::string txBody = base64::encode(
        reinterpret_cast<const unsigned char*>(txBytes.data()), txBytes.size());

    std::string txResult = base64::encode(
        reinterpret_cast<const unsigned char*>(txResultBytes.data()),
        txResultBytes.size());

    xdr::opaque_vec<> txMeta(delta.getTransactionMeta());

    std::string meta = base64::encode(
        reinterpret_cast<const unsigned char*>(txMeta.data()), txMeta.size());

    string txIDString(binToHex(getContentsHash()));

    auto timer = ledgerManager.getDatabase().getInsertTimer("txhistory");
    soci::statement st =
        (ledgerManager.getDatabase().getSession().prepare
             << "INSERT INTO txhistory (txid, ledgerseq, txindex, txbody, "
                "txresult, txmeta) VALUES "
                "(:id,:seq,:txindex,:txb,:txres,:meta)",
         soci::use(txIDString),
         soci::use(ledgerManager.getCurrentLedgerHeader().ledgerSeq),
         soci::use(txindex), soci::use(txBody), soci::use(txResult),
         soci::use(meta));

    st.execute(true);

    if (st.get_affected_rows() != 1)
    {
        throw std::runtime_error("Could not update data in SQL");
    }
}
コード例 #6
0
bool
PaymentOpFrame::doApply(LedgerDelta& delta, LedgerManager& ledgerManager)
{
    AccountFrame destAccount;

    // if sending to self directly, just mark as success
    if (mPayment.destination == getSourceID() && mPayment.path.empty())
    {
        innerResult().code(PAYMENT_SUCCESS);
        return true;
    }

    Database& db = ledgerManager.getDatabase();

    if (!AccountFrame::loadAccount(mPayment.destination, destAccount, db))
    { // this tx is creating an account
        if (mPayment.currency.type() == CURRENCY_TYPE_NATIVE)
        {
            if (mPayment.amount < ledgerManager.getMinBalance(0))
            { // not over the minBalance to make an account
                innerResult().code(PAYMENT_LOW_RESERVE);
                return false;
            }
            else
            {
                destAccount.getAccount().accountID = mPayment.destination;
                destAccount.getAccount().seqNum =
                    delta.getHeaderFrame().getStartingSequenceNumber();
                destAccount.getAccount().balance = 0;

                destAccount.storeAdd(delta, db);
            }
        }
        else
        { // trying to send credit to an unmade account
            innerResult().code(PAYMENT_NO_DESTINATION);
            return false;
        }
    }

    return sendNoCreate(destAccount, delta, ledgerManager);
}
コード例 #7
0
// make sure the deleted Account hasn't issued credit
// make sure we aren't holding any credit
// make sure the we delete all the offers
// make sure the we delete all the trustlines
// move the XLM to the new account
bool
MergeOpFrame::doApply(LedgerDelta& delta, LedgerManager& ledgerManager)
{
    AccountFrame::pointer otherAccount;
    Database& db = ledgerManager.getDatabase();

    otherAccount = AccountFrame::loadAccount(mOperation.body.destination(), db);

    if (!otherAccount)
    {
        innerResult().code(ACCOUNT_MERGE_NO_ACCOUNT);
        return false;
    }

    if (TrustFrame::hasIssued(getSourceID(), db))
    {
        innerResult().code(ACCOUNT_MERGE_CREDIT_HELD);
        return false;
    }

    std::vector<TrustFrame::pointer> lines;
    TrustFrame::loadLines(getSourceID(), lines, db);
    for(auto &l : lines)
    {
        if(l->getBalance() > 0)
        {
            innerResult().code(ACCOUNT_MERGE_HAS_CREDIT);
            return false;
        }
    }

    // delete offers
    std::vector<OfferFrame::pointer> offers;
    OfferFrame::loadOffers(getSourceID(), offers, db);
    for (auto& offer : offers)
    {
        offer->storeDelete(delta, db);
    }

    // delete trust lines
    for (auto& l : lines)
    {
        l->storeDelete(delta, db);
    }

    otherAccount->getAccount().balance += mSourceAccount->getAccount().balance;
    otherAccount->storeChange(delta, db);
    mSourceAccount->storeDelete(delta, db);

    innerResult().code(ACCOUNT_MERGE_SUCCESS);
    return true;
}
コード例 #8
0
ファイル: AccountFrame.cpp プロジェクト: soitun/stellar-core
int64_t
AccountFrame::getBalanceAboveReserve(LedgerManager const& lm) const
{
    int64_t avail =
        getBalance() - lm.getMinBalance(mAccountEntry.numSubEntries);
    if (avail < 0)
    {
        // nothing can leave this account if below the reserve
        // (this can happen if the reserve is raised)
        avail = 0;
    }
    return avail;
}
コード例 #9
0
// returns true if successfully updated,
// false if balance is not sufficient
bool
AccountFrame::addNumEntries(int count, LedgerManager const& lm)
{
    int newEntriesCount = mAccountEntry.numSubEntries + count;
    if (newEntriesCount < 0)
    {
        throw std::runtime_error("invalid account state");
    }
    if (getBalance() < lm.getMinBalance(newEntriesCount))
    {
        // balance too low
        return false;
    }
    mAccountEntry.numSubEntries = newEntriesCount;
    return true;
}
コード例 #10
0
void
LedgerHeaderFrame::storeInsert(LedgerManager& ledgerManager) const
{
    if (!isValid(mHeader))
    {
        throw std::runtime_error("invalid ledger header (insert)");
    }

    getHash();

    string hash(binToHex(mHash)),
        prevHash(binToHex(mHeader.previousLedgerHash)),
        bucketListHash(binToHex(mHeader.bucketListHash));

    auto headerBytes(xdr::xdr_to_opaque(mHeader));

    std::string headerEncoded;
    headerEncoded = decoder::encode_b64(headerBytes);

    auto& db = ledgerManager.getDatabase();

    // note: columns other than "data" are there to faciliate lookup/processing
    auto prep = db.getPreparedStatement(
        "INSERT INTO ledgerheaders "
        "(ledgerhash, prevhash, bucketlisthash, ledgerseq, closetime, data) "
        "VALUES "
        "(:h,        :ph,      :blh,            :seq,     :ct,       :data)");
    auto& st = prep.statement();
    st.exchange(use(hash));
    st.exchange(use(prevHash));
    st.exchange(use(bucketListHash));
    st.exchange(use(mHeader.ledgerSeq));
    st.exchange(use(mHeader.scpValue.closeTime));
    st.exchange(use(headerEncoded));
    st.define_and_bind();
    {
        auto timer = db.getInsertTimer("ledger-header");
        st.execute(true);
    }
    if (st.get_affected_rows() != 1)
    {
        throw std::runtime_error("Could not update data in SQL");
    }
}
コード例 #11
0
void
TransactionFrame::prepareResult(LedgerDelta& delta,
                                LedgerManager& ledgerManager)
{
    Database& db = ledgerManager.getDatabase();
    int64_t fee = getResult().feeCharged;

    if (fee > 0)
    {
        int64_t avail = mSigningAccount->getAccount().balance;
        if (avail < fee)
        {
            // take all their balance to be safe
            fee = avail;
        }
        mSigningAccount->setSeqNum(mEnvelope.tx.seqNum);
        mSigningAccount->getAccount().balance -= fee;
        delta.getHeader().feePool += fee;

        mSigningAccount->storeChange(delta, db);
    }
}
コード例 #12
0
bool
AllowTrustOpFrame::doApply(LedgerDelta& delta, LedgerManager& ledgerManager)
{
    if (!(mSourceAccount->getAccount().flags & AUTH_REQUIRED_FLAG))
    { // this account doesn't require authorization to hold credit
        innerResult().code(ALLOW_TRUST_TRUST_NOT_REQUIRED);
        return false;
    }

    if (!(mSourceAccount->getAccount().flags & AUTH_REVOCABLE_FLAG) &&
        !mAllowTrust.authorize)
    {
        innerResult().code(ALLOW_TRUST_CANT_REVOKE);
        return false;
    }

    Currency ci;
    ci.type(CURRENCY_TYPE_ALPHANUM);
    ci.alphaNum().currencyCode = mAllowTrust.currency.currencyCode();
    ci.alphaNum().issuer = getSourceID();

    Database& db = ledgerManager.getDatabase();
    TrustFrame::pointer trustLine;
    trustLine = TrustFrame::loadTrustLine(mAllowTrust.trustor, ci, db);

    if (!trustLine)
    {
        innerResult().code(ALLOW_TRUST_NO_TRUST_LINE);
        return false;
    }

    innerResult().code(ALLOW_TRUST_SUCCESS);

    trustLine->setAuthorized(mAllowTrust.authorize);

    trustLine->storeChange(delta, db);

    return true;
}
コード例 #13
0
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;
}
コード例 #14
0
// 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;
}
コード例 #15
0
bool
PaymentOpFrame::doApply(LedgerDelta& delta, LedgerManager& ledgerManager)
{
    AccountFrame destination;

    // if sending to self directly, just mark as success
    if (mPayment.destination == getSourceID())
    {
        innerResult().code(PAYMENT_SUCCESS);
        return true;
    }

    Database& db = ledgerManager.getDatabase();

    if (!AccountFrame::loadAccount(mPayment.destination, destination, db))
    {
        innerResult().code(PAYMENT_NO_DESTINATION);
        return false;
    }

    // build a pathPaymentOp
    Operation op;
    op.sourceAccount = mOperation.sourceAccount;
    op.body.type(PATH_PAYMENT);
    PathPaymentOp& ppOp = op.body.pathPaymentOp();
    ppOp.sendCurrency = mPayment.currency;
    ppOp.destCurrency = mPayment.currency;

    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() || !ppayment.doApply(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:
            res = PAYMENT_UNDERFUNDED;
            break;
        case PATH_PAYMENT_NO_DESTINATION:
            res = PAYMENT_NO_DESTINATION;
            break;
        case PATH_PAYMENT_NO_TRUST:
            res = PAYMENT_NO_TRUST;
            break;
        case PATH_PAYMENT_NOT_AUTHORIZED:
            res = PAYMENT_NOT_AUTHORIZED;
            break;
        case PATH_PAYMENT_LINE_FULL:
            res = PAYMENT_LINE_FULL;
            break;
        default:
            throw std::runtime_error("Unexpected error code from pathPayment");
        }
        innerResult().code(res);
        return false;
    }

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

    innerResult().code(PAYMENT_SUCCESS);

    return true;
}
コード例 #16
0
bool
AllowTrustOpFrame::doApply(Application& app, LedgerDelta& delta,
                           LedgerManager& ledgerManager)
{
    if (ledgerManager.getCurrentLedgerVersion() > 2)
    {
        if (mAllowTrust.trustor == getSourceID())
        { // since version 3 it is not
            // allowed to use ALLOW_TRUST on
            // self
            app.getMetrics()
                .NewMeter({"op-allow-trust", "failure", "trust-self"},
                          "operation")
                .Mark();
            innerResult().code(ALLOW_TRUST_SELF_NOT_ALLOWED);
            return false;
        }
    }

    if (!(mSourceAccount->getAccount().flags & AUTH_REQUIRED_FLAG))
    { // this account doesn't require authorization to
        // hold credit
        app.getMetrics()
            .NewMeter({"op-allow-trust", "failure", "not-required"},
                      "operation")
            .Mark();
        innerResult().code(ALLOW_TRUST_TRUST_NOT_REQUIRED);
        return false;
    }

    if (!(mSourceAccount->getAccount().flags & AUTH_REVOCABLE_FLAG) &&
        !mAllowTrust.authorize)
    {
        app.getMetrics()
            .NewMeter({"op-allow-trust", "failure", "cant-revoke"}, "operation")
            .Mark();
        innerResult().code(ALLOW_TRUST_CANT_REVOKE);
        return false;
    }

    Asset ci;
    ci.type(mAllowTrust.asset.type());
    if (mAllowTrust.asset.type() == ASSET_TYPE_CREDIT_ALPHANUM4)
    {
        ci.alphaNum4().assetCode = mAllowTrust.asset.assetCode4();
        ci.alphaNum4().issuer = getSourceID();
    }
    else if (mAllowTrust.asset.type() == ASSET_TYPE_CREDIT_ALPHANUM12)
    {
        ci.alphaNum12().assetCode = mAllowTrust.asset.assetCode12();
        ci.alphaNum12().issuer = getSourceID();
    }

    Database& db = ledgerManager.getDatabase();
    TrustFrame::pointer trustLine;
    trustLine = TrustFrame::loadTrustLine(mAllowTrust.trustor, ci, db, &delta);

    if (!trustLine)
    {
        app.getMetrics()
            .NewMeter({"op-allow-trust", "failure", "no-trust-line"},
                      "operation")
            .Mark();
        innerResult().code(ALLOW_TRUST_NO_TRUST_LINE);
        return false;
    }

    app.getMetrics()
        .NewMeter({"op-allow-trust", "success", "apply"}, "operation")
        .Mark();
    innerResult().code(ALLOW_TRUST_SUCCESS);

    trustLine->setAuthorized(mAllowTrust.authorize);

    trustLine->storeChange(delta, db);

    return true;
}
コード例 #17
0
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;
    }
}
コード例 #18
0
ファイル: AccountFrame.cpp プロジェクト: soitun/stellar-core
int64_t
AccountFrame::getMinimumBalance(LedgerManager const& lm) const
{
    return lm.getMinBalance(mAccountEntry.numSubEntries);
}
コード例 #19
0
// work backward to determine how much they need to send to get the
// specified amount of currency to the recipient
bool
PaymentOpFrame::sendNoCreate(AccountFrame& destination, LedgerDelta& delta,
                             LedgerManager& ledgerManager)
{
    Database& db = ledgerManager.getDatabase();

    bool multi_mode = mPayment.path.size() != 0;
    if (multi_mode)
    {
        innerResult().code(PAYMENT_SUCCESS_MULTI);
    }
    else
    {
        innerResult().code(PAYMENT_SUCCESS);
    }

    // tracks the last amount that was traded
    int64_t curBReceived = mPayment.amount;
    Currency curB = mPayment.currency;

    // update balances, walks backwards

    // update last balance in the chain
    {

        if (curB.type() == CURRENCY_TYPE_NATIVE)
        {
            destination.getAccount().balance += curBReceived;
            destination.storeChange(delta, db);
        }
        else
        {
            TrustFrame destLine;

            if (!TrustFrame::loadTrustLine(destination.getID(), curB, destLine,
                                           db))
            {
                innerResult().code(PAYMENT_NO_TRUST);
                return false;
            }

            if (!destLine.isAuthorized())
            {
                innerResult().code(PAYMENT_NOT_AUTHORIZED);
                return false;
            }

            if (!destLine.addBalance(curBReceived))
            {
                innerResult().code(PAYMENT_LINE_FULL);
                return false;
            }

            destLine.storeChange(delta, db);
        }

        if (multi_mode)
        {
            innerResult().multi().last =
                SimplePaymentResult(destination.getID(), curB, curBReceived);
        }
    }

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

            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:
                innerResult().code(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().multi().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 > mPayment.sendMax)
    { // make sure not over the max
        innerResult().code(PAYMENT_OVER_SENDMAX);
        return false;
    }

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

        if (mSourceAccount->getAccount().balance < (minBalance + curBSent))
        { // they don't have enough to send
            innerResult().code(PAYMENT_UNDERFUNDED);
            return false;
        }

        mSourceAccount->getAccount().balance -= curBSent;
        mSourceAccount->storeChange(delta, db);
    }
    else
    {
        AccountFrame issuer;
        if (!AccountFrame::loadAccount(curB.alphaNum().issuer, issuer, db))
        {
            throw std::runtime_error("sendCredit Issuer not found");
        }

        TrustFrame sourceLineFrame;
        if (!TrustFrame::loadTrustLine(getSourceID(), curB, sourceLineFrame,
                                       db))
        {
            innerResult().code(PAYMENT_UNDERFUNDED);
            return false;
        }

        if (!sourceLineFrame.addBalance(-curBSent))
        {
            innerResult().code(PAYMENT_UNDERFUNDED);
            return false;
        }

        sourceLineFrame.storeChange(delta, db);
    }

    return true;
}
コード例 #20
0
bool
SetOptionsOpFrame::doApply(Application& app, LedgerDelta& delta,
                           LedgerManager& ledgerManager)
{
    Database& db = ledgerManager.getDatabase();
    AccountEntry& account = mSourceAccount->getAccount();

    if (mSetOptions.inflationDest)
    {
        AccountFrame::pointer inflationAccount;
        AccountID inflationID = *mSetOptions.inflationDest;
        inflationAccount = AccountFrame::loadAccount(delta, inflationID, db);
        if (!inflationAccount)
        {
            app.getMetrics()
                .NewMeter({"op-set-options", "failure", "invalid-inflation"},
                          "operation")
                .Mark();
            innerResult().code(SET_OPTIONS_INVALID_INFLATION);
            return false;
        }
        account.inflationDest.activate() = inflationID;
    }

    if (mSetOptions.clearFlags)
    {
        if ((*mSetOptions.clearFlags & allAccountAuthFlags) &&
            mSourceAccount->isImmutableAuth())
        {
            app.getMetrics()
                .NewMeter({"op-set-options", "failure", "cant-change"},
                          "operation")
                .Mark();
            innerResult().code(SET_OPTIONS_CANT_CHANGE);
            return false;
        }
        account.flags = account.flags & ~*mSetOptions.clearFlags;
    }

    if (mSetOptions.setFlags)
    {
        if ((*mSetOptions.setFlags & allAccountAuthFlags) &&
            mSourceAccount->isImmutableAuth())
        {
            app.getMetrics()
                .NewMeter({"op-set-options", "failure", "cant-change"},
                          "operation")
                .Mark();
            innerResult().code(SET_OPTIONS_CANT_CHANGE);
            return false;
        }
        account.flags = account.flags | *mSetOptions.setFlags;
    }

    if (mSetOptions.homeDomain)
    {
        account.homeDomain = *mSetOptions.homeDomain;
    }

    if (mSetOptions.masterWeight)
    {
        account.thresholds[THRESHOLD_MASTER_WEIGHT] =
            *mSetOptions.masterWeight & UINT8_MAX;
    }

    if (mSetOptions.lowThreshold)
    {
        account.thresholds[THRESHOLD_LOW] =
            *mSetOptions.lowThreshold & UINT8_MAX;
    }

    if (mSetOptions.medThreshold)
    {
        account.thresholds[THRESHOLD_MED] =
            *mSetOptions.medThreshold & UINT8_MAX;
    }

    if (mSetOptions.highThreshold)
    {
        account.thresholds[THRESHOLD_HIGH] =
            *mSetOptions.highThreshold & UINT8_MAX;
    }

    if (mSetOptions.signer)
    {
        auto& signers = account.signers;
        if (mSetOptions.signer->weight)
        { // add or change signer
            bool found = false;
            for (auto& oldSigner : signers)
            {
                if (oldSigner.key == mSetOptions.signer->key)
                {
                    oldSigner.weight = mSetOptions.signer->weight;
                    found = true;
                }
            }
            if (!found)
            {
                if (signers.size() == signers.max_size())
                {
                    app.getMetrics()
                        .NewMeter(
                            {"op-set-options", "failure", "too-many-signers"},
                            "operation")
                        .Mark();
                    innerResult().code(SET_OPTIONS_TOO_MANY_SIGNERS);
                    return false;
                }
                if (!mSourceAccount->addNumEntries(1, ledgerManager))
                {
                    app.getMetrics()
                        .NewMeter({"op-set-options", "failure", "low-reserve"},
                                  "operation")
                        .Mark();
                    innerResult().code(SET_OPTIONS_LOW_RESERVE);
                    return false;
                }
                signers.push_back(*mSetOptions.signer);
            }
        }
        else
        { // delete signer
            auto it = signers.begin();
            while (it != signers.end())
            {
                Signer& oldSigner = *it;
                if (oldSigner.key == mSetOptions.signer->key)
                {
                    it = signers.erase(it);
                    mSourceAccount->addNumEntries(-1, ledgerManager);
                }
                else
                {
                    it++;
                }
            }
        }
        mSourceAccount->setUpdateSigners();
    }

    app.getMetrics()
        .NewMeter({"op-set-options", "success", "apply"}, "operation")
        .Mark();
    innerResult().code(SET_OPTIONS_SUCCESS);
    mSourceAccount->storeChange(delta, db);
    return true;
}
コード例 #21
0
ファイル: InflationOpFrame.cpp プロジェクト: rtu/network
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;
}
コード例 #22
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 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;
}
コード例 #23
0
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;
}