AccountID getIssuer(Asset const& asset) { return (asset.type() == ASSET_TYPE_CREDIT_ALPHANUM4 ? asset.alphaNum4().issuer : asset.alphaNum12().issuer); }
void processAsset(Asset& asset, AssetType assetType, std::string const& issuerStr, soci::indicator const& issuerIndicator, std::string const& assetCode, soci::indicator const& assetCodeIndicator) { asset.type(assetType); if (assetType != ASSET_TYPE_NATIVE) { if ((assetCodeIndicator != soci::i_ok) || (issuerIndicator != soci::i_ok)) { throw std::runtime_error("bad database state"); } if (assetType == ASSET_TYPE_CREDIT_ALPHANUM12) { asset.alphaNum12().issuer = KeyUtils::fromStrKey<PublicKey>(issuerStr); strToAssetCode(asset.alphaNum12().assetCode, assetCode); } else if (assetType == ASSET_TYPE_CREDIT_ALPHANUM4) { asset.alphaNum4().issuer = KeyUtils::fromStrKey<PublicKey>(issuerStr); strToAssetCode(asset.alphaNum4().assetCode, assetCode); } else { throw std::runtime_error("bad database state"); } } }
QDebug operator<<(QDebug debug, const Asset& asset) { debug.nospace() << "Asset{mnem=" << asset.mnem() // << ",display=" << asset.display() // << ",type=" << asset.type() // << '}'; return debug; }
// Note: This function is currently only used in AllowTrustOpFrame, which means // the asset parameter will never satisfy asset.type() == ASSET_TYPE_NATIVE. As // a consequence, I have not implemented that possibility so this function // throws in that case. std::vector<LedgerEntry> LedgerStateRoot::Impl::loadOffersByAccountAndAsset(AccountID const& accountID, Asset const& asset) const { std::string sql = "SELECT sellerid, offerid, " "sellingassettype, sellingassetcode, sellingissuer, " "buyingassettype, buyingassetcode, buyingissuer, " "amount, pricen, priced, flags, lastmodified " "FROM offers "; sql += " WHERE sellerid = :acc" " AND ((sellingassetcode = :code AND sellingissuer = :iss)" " OR (buyingassetcode = :code AND buyingissuer = :iss))"; std::string accountStr = KeyUtils::toStrKey(accountID); std::string assetCode; std::string assetIssuer; if (asset.type() == ASSET_TYPE_CREDIT_ALPHANUM4) { assetCodeToStr(asset.alphaNum4().assetCode, assetCode); assetIssuer = KeyUtils::toStrKey(asset.alphaNum4().issuer); } else if (asset.type() == ASSET_TYPE_CREDIT_ALPHANUM12) { assetCodeToStr(asset.alphaNum12().assetCode, assetCode); assetIssuer = KeyUtils::toStrKey(asset.alphaNum12().issuer); } else { throw std::runtime_error("Invalid asset type"); } auto prep = mDatabase.getPreparedStatement(sql); auto& st = prep.statement(); st.exchange(soci::use(accountStr, "acc")); st.exchange(soci::use(assetCode, "code")); st.exchange(soci::use(assetIssuer, "iss")); std::vector<LedgerEntry> offers; { auto timer = mDatabase.getSelectTimer("offer"); offers = loadOffers(prep); } return offers; }
TrustFrame::pointer TrustFrame::createIssuerFrame(Asset const& issuer) { pointer res = make_shared<TrustFrame>(); res->mIsIssuer = true; TrustLineEntry& tl = res->mEntry.data.trustLine(); if (issuer.type() == ASSET_TYPE_CREDIT_ALPHANUM4) { tl.accountID = issuer.alphaNum4().issuer; } else if (issuer.type() == ASSET_TYPE_CREDIT_ALPHANUM12) { tl.accountID = issuer.alphaNum12().issuer; } tl.flags |= AUTHORIZED_FLAG; tl.balance = INT64_MAX; tl.asset = issuer; tl.limit = INT64_MAX; return res; }
bool compareAsset(Asset const& first, Asset const& second) { if (first.type() != second.type()) return false; if (first.type() == ASSET_TYPE_NATIVE) return true; if (second.type() == ASSET_TYPE_CREDIT_ALPHANUM4) { if ((first.alphaNum4().issuer == second.alphaNum4().issuer) && (first.alphaNum4().assetCode == second.alphaNum4().assetCode)) return true; } if (second.type() == ASSET_TYPE_CREDIT_ALPHANUM12) { if ((first.alphaNum12().issuer == second.alphaNum12().issuer) && (first.alphaNum12().assetCode == second.alphaNum12().assetCode)) return true; } return false; }
bool AllowTrustOpFrame::doCheckValid(Application& app) { if (mAllowTrust.asset.type() == ASSET_TYPE_NATIVE) { app.getMetrics() .NewMeter({"op-allow-trust", "invalid", "malformed-non-alphanum"}, "operation") .Mark(); innerResult().code(ALLOW_TRUST_MALFORMED); 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(); } if (!isAssetValid(ci)) { app.getMetrics() .NewMeter({"op-allow-trust", "invalid", "malformed-invalid-asset"}, "operation") .Mark(); innerResult().code(ALLOW_TRUST_MALFORMED); return false; } return true; }
void OfferFrame::loadBestOffers(size_t numOffers, size_t offset, Asset const& selling, Asset const& buying, vector<OfferFrame::pointer>& retOffers, Database& db) { std::string sql = offerColumnSelector; std::string sellingAssetCode, sellingIssuerStrKey; std::string buyingAssetCode, buyingIssuerStrKey; bool useSellingAsset = false; bool useBuyingAsset = false; if (selling.type() == ASSET_TYPE_NATIVE) { sql += " WHERE sellingassettype = 0"; } else { if (selling.type() == ASSET_TYPE_CREDIT_ALPHANUM4) { assetCodeToStr(selling.alphaNum4().assetCode, sellingAssetCode); sellingIssuerStrKey = PubKeyUtils::toStrKey(selling.alphaNum4().issuer); } else if (selling.type() == ASSET_TYPE_CREDIT_ALPHANUM12) { assetCodeToStr(selling.alphaNum12().assetCode, sellingAssetCode); sellingIssuerStrKey = PubKeyUtils::toStrKey(selling.alphaNum12().issuer); } else { throw std::runtime_error("unknown asset type"); } useSellingAsset = true; sql += " WHERE sellingassetcode = :pcur AND sellingissuer = :pi"; } if (buying.type() == ASSET_TYPE_NATIVE) { sql += " AND buyingassettype = 0"; } else { if (buying.type() == ASSET_TYPE_CREDIT_ALPHANUM4) { assetCodeToStr(buying.alphaNum4().assetCode, buyingAssetCode); buyingIssuerStrKey = PubKeyUtils::toStrKey(buying.alphaNum4().issuer); } else if (buying.type() == ASSET_TYPE_CREDIT_ALPHANUM12) { assetCodeToStr(buying.alphaNum12().assetCode, buyingAssetCode); buyingIssuerStrKey = PubKeyUtils::toStrKey(buying.alphaNum12().issuer); } else { throw std::runtime_error("unknown asset type"); } useBuyingAsset = true; sql += " AND buyingassetcode = :gcur AND buyingissuer = :gi"; } // price is an approximation of the actual n/d (truncated math, 15 digits) // ordering by offerid gives precendence to older offers for fairness sql += " ORDER BY price, offerid LIMIT :n OFFSET :o"; auto prep = db.getPreparedStatement(sql); auto& st = prep.statement(); if (useSellingAsset) { st.exchange(use(sellingAssetCode)); st.exchange(use(sellingIssuerStrKey)); } if (useBuyingAsset) { st.exchange(use(buyingAssetCode)); st.exchange(use(buyingIssuerStrKey)); } st.exchange(use(numOffers)); st.exchange(use(offset)); auto timer = db.getSelectTimer("offer"); loadOffers(prep, [&retOffers](LedgerEntry const& of) { retOffers.emplace_back(make_shared<OfferFrame>(of)); }); }
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 isAssetValid(Asset const& cur) { if (cur.type() == ASSET_TYPE_NATIVE) return true; auto& loc = std::locale::classic(); if (cur.type() == ASSET_TYPE_CREDIT_ALPHANUM4) { auto const& code = cur.alphaNum4().assetCode; bool zeros = false; bool onechar = false; // at least one non zero character for (uint8_t b : code) { if (b == 0) { zeros = true; } else if (zeros) { // zeros can only be trailing return false; } else { if (b > 0x7F || !std::isalnum((char)b, loc)) { return false; } onechar = true; } } return onechar; } if (cur.type() == ASSET_TYPE_CREDIT_ALPHANUM12) { auto const& code = cur.alphaNum12().assetCode; bool zeros = false; int charcount = 0; // at least 5 non zero characters for (uint8_t b : code) { if (b == 0) { zeros = true; } else if (zeros) { // zeros can only be trailing return false; } else { if (b > 0x7F || !std::isalnum((char)b, loc)) { return false; } charcount++; } } return charcount > 4; } return false; }
bool PathPaymentOpFrame::doApply(Application& app, AbstractLedgerState& ls) { 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); bool doesSourceAccountExist = true; if (ls.loadHeader().current().ledgerVersion < 8) { doesSourceAccountExist = (bool)stellar::loadAccountWithoutRecord(ls, getSourceID()); } if (!bypassIssuerCheck) { if (!stellar::loadAccountWithoutRecord(ls, mPathPayment.destination)) { app.getMetrics() .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) { auto destination = stellar::loadAccount(ls, mPathPayment.destination); if (!addBalance(ls.loadHeader(), destination, curBReceived)) { app.getMetrics() .NewMeter({"op-path-payment", "invalid", "balance-overflow"}, "operation") .Mark(); innerResult().code(PATH_PAYMENT_MALFORMED); return false; } } else { if (!bypassIssuerCheck) { auto issuer = stellar::loadAccountWithoutRecord(ls, getIssuer(curB)); if (!issuer) { app.getMetrics() .NewMeter({"op-path-payment", "failure", "no-issuer"}, "operation") .Mark(); innerResult().code(PATH_PAYMENT_NO_ISSUER); innerResult().noIssuer() = curB; return false; } } auto destLine = stellar::loadTrustLine(ls, mPathPayment.destination, curB); if (!destLine) { app.getMetrics() .NewMeter({"op-path-payment", "failure", "no-trust"}, "operation") .Mark(); innerResult().code(PATH_PAYMENT_NO_TRUST); return false; } if (!destLine.isAuthorized()) { app.getMetrics() .NewMeter({"op-path-payment", "failure", "not-authorized"}, "operation") .Mark(); innerResult().code(PATH_PAYMENT_NOT_AUTHORIZED); return false; } if (!destLine.addBalance(ls.loadHeader(), curBReceived)) { app.getMetrics() .NewMeter({"op-path-payment", "failure", "line-full"}, "operation") .Mark(); innerResult().code(PATH_PAYMENT_LINE_FULL); return false; } } 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 (!stellar::loadAccountWithoutRecord(ls, getIssuer(curA))) { app.getMetrics() .NewMeter({"op-path-payment", "failure", "no-issuer"}, "operation") .Mark(); innerResult().code(PATH_PAYMENT_NO_ISSUER); innerResult().noIssuer() = curA; return false; } } // curA -> curB medida::MetricsRegistry& metrics = app.getMetrics(); std::vector<ClaimOfferAtom> offerTrail; ConvertResult r = convertWithOffers( ls, curA, INT64_MAX, curASent, curB, curBReceived, actualCurBReceived, true, [this, &metrics](LedgerStateEntry const& o) { auto const& offer = o.current().data.offer(); if (offer.sellerID == getSourceID()) { // we are crossing our own offer metrics .NewMeter( {"op-path-payment", "failure", "offer-cross-self"}, "operation") .Mark(); innerResult().code(PATH_PAYMENT_OFFER_CROSS_SELF); return OfferFilterResult::eStop; } return OfferFilterResult::eKeep; }, offerTrail); assert(curASent >= 0); switch (r) { case ConvertResult::eFilterStop: return false; case ConvertResult::eOK: if (curBReceived == actualCurBReceived) { break; } // fall through case ConvertResult::ePartial: app.getMetrics() .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(), offerTrail.begin(), offerTrail.end()); } // last step: we've reached the first account in the chain, update its // balance int64_t curBSent = curBReceived; if (curBSent > mPathPayment.sendMax) { // make sure not over the max app.getMetrics() .NewMeter({"op-path-payment", "failure", "over-send-max"}, "operation") .Mark(); innerResult().code(PATH_PAYMENT_OVER_SENDMAX); return false; } if (curB.type() == ASSET_TYPE_NATIVE) { auto header = ls.loadHeader(); LedgerStateEntry sourceAccount; if (header.current().ledgerVersion > 7) { sourceAccount = stellar::loadAccount(ls, getSourceID()); if (!sourceAccount) { app.getMetrics() .NewMeter({"op-path-payment", "invalid", "no-account"}, "operation") .Mark(); innerResult().code(PATH_PAYMENT_MALFORMED); return false; } } else { sourceAccount = loadSourceAccount(ls, header); } if (curBSent > getAvailableBalance(header, sourceAccount)) { // they don't have enough to send app.getMetrics() .NewMeter({"op-path-payment", "failure", "underfunded"}, "operation") .Mark(); innerResult().code(PATH_PAYMENT_UNDERFUNDED); return false; } if (!doesSourceAccountExist) { throw std::runtime_error("modifying account that does not exist"); } auto ok = addBalance(header, sourceAccount, -curBSent); assert(ok); } else { if (!bypassIssuerCheck) { auto issuer = stellar::loadAccountWithoutRecord(ls, getIssuer(curB)); if (!issuer) { app.getMetrics() .NewMeter({"op-path-payment", "failure", "no-issuer"}, "operation") .Mark(); innerResult().code(PATH_PAYMENT_NO_ISSUER); innerResult().noIssuer() = curB; return false; } } auto sourceLine = loadTrustLine(ls, getSourceID(), curB); if (!sourceLine) { app.getMetrics() .NewMeter({"op-path-payment", "failure", "src-no-trust"}, "operation") .Mark(); innerResult().code(PATH_PAYMENT_SRC_NO_TRUST); return false; } if (!sourceLine.isAuthorized()) { app.getMetrics() .NewMeter({"op-path-payment", "failure", "src-not-authorized"}, "operation") .Mark(); innerResult().code(PATH_PAYMENT_SRC_NOT_AUTHORIZED); return false; } if (!sourceLine.addBalance(ls.loadHeader(), -curBSent)) { app.getMetrics() .NewMeter({"op-path-payment", "failure", "underfunded"}, "operation") .Mark(); innerResult().code(PATH_PAYMENT_UNDERFUNDED); return false; } } app.getMetrics() .NewMeter({"op-path-payment", "success", "apply"}, "operation") .Mark(); return true; }
TrustFrame::pointer TrustFrame::loadTrustLine(AccountID const& accountID, Asset const& asset, Database& db) { if (asset.type() == ASSET_TYPE_NATIVE) { throw std::runtime_error("XLM TrustLine?"); } else { if (accountID == getIssuer(asset)) { return createIssuerFrame(asset); } } LedgerKey key; key.type(TRUSTLINE); key.trustLine().accountID = accountID; key.trustLine().asset = asset; if (cachedEntryExists(key, db)) { auto p = getCachedEntry(key, db); return p ? std::make_shared<TrustFrame>(*p) : nullptr; } std::string accStr, issuerStr, assetStr; accStr = PubKeyUtils::toStrKey(accountID); if (asset.type() == ASSET_TYPE_CREDIT_ALPHANUM4) { assetCodeToStr(asset.alphaNum4().assetCode, assetStr); issuerStr = PubKeyUtils::toStrKey(asset.alphaNum4().issuer); } else if (asset.type() == ASSET_TYPE_CREDIT_ALPHANUM12) { assetCodeToStr(asset.alphaNum12().assetCode, assetStr); issuerStr = PubKeyUtils::toStrKey(asset.alphaNum12().issuer); } auto query = std::string(trustLineColumnSelector); query += (" WHERE accountid = :id " " AND issuer = :issuer " " AND assetcode = :asset"); auto prep = db.getPreparedStatement(query); auto& st = prep.statement(); st.exchange(use(accStr)); st.exchange(use(issuerStr)); st.exchange(use(assetStr)); pointer retLine; auto timer = db.getSelectTimer("trust"); loadLines(prep, [&retLine](LedgerEntry const& trust) { retLine = make_shared<TrustFrame>(trust); }); if (retLine) { retLine->putCachedEntry(db); } else { putCachedEntry(key, nullptr, db); } return retLine; }
std::list<LedgerEntry>::const_iterator LedgerStateRoot::Impl::loadBestOffers(std::list<LedgerEntry>& offers, Asset const& buying, Asset const& selling, size_t numOffers, size_t offset) const { std::string sql = "SELECT sellerid, offerid, " "sellingassettype, sellingassetcode, sellingissuer, " "buyingassettype, buyingassetcode, buyingissuer, " "amount, pricen, priced, flags, lastmodified " "FROM offers "; std::string sellingAssetCode, sellingIssuerStrKey; if (selling.type() == ASSET_TYPE_NATIVE) { sql += " WHERE sellingassettype = 0 AND sellingissuer IS NULL"; } else { if (selling.type() == ASSET_TYPE_CREDIT_ALPHANUM4) { assetCodeToStr(selling.alphaNum4().assetCode, sellingAssetCode); sellingIssuerStrKey = KeyUtils::toStrKey(selling.alphaNum4().issuer); } else if (selling.type() == ASSET_TYPE_CREDIT_ALPHANUM12) { assetCodeToStr(selling.alphaNum12().assetCode, sellingAssetCode); sellingIssuerStrKey = KeyUtils::toStrKey(selling.alphaNum12().issuer); } else { throw std::runtime_error("unknown asset type"); } sql += " WHERE sellingassetcode = :sac AND sellingissuer = :si"; } std::string buyingAssetCode, buyingIssuerStrKey; if (buying.type() == ASSET_TYPE_NATIVE) { sql += " AND buyingassettype = 0 AND buyingissuer IS NULL"; } else { if (buying.type() == ASSET_TYPE_CREDIT_ALPHANUM4) { assetCodeToStr(buying.alphaNum4().assetCode, buyingAssetCode); buyingIssuerStrKey = KeyUtils::toStrKey(buying.alphaNum4().issuer); } else if (buying.type() == ASSET_TYPE_CREDIT_ALPHANUM12) { assetCodeToStr(buying.alphaNum12().assetCode, buyingAssetCode); buyingIssuerStrKey = KeyUtils::toStrKey(buying.alphaNum12().issuer); } else { throw std::runtime_error("unknown asset type"); } sql += " AND buyingassetcode = :bac AND buyingissuer = :bi"; } // price is an approximation of the actual n/d (truncated math, 15 digits) // ordering by offerid gives precendence to older offers for fairness sql += " ORDER BY price, offerid LIMIT :n OFFSET :o"; auto prep = mDatabase.getPreparedStatement(sql); auto& st = prep.statement(); if (selling.type() != ASSET_TYPE_NATIVE) { st.exchange(soci::use(sellingAssetCode, "sac")); st.exchange(soci::use(sellingIssuerStrKey, "si")); } if (buying.type() != ASSET_TYPE_NATIVE) { st.exchange(soci::use(buyingAssetCode, "bac")); st.exchange(soci::use(buyingIssuerStrKey, "bi")); } st.exchange(soci::use(numOffers, "n")); st.exchange(soci::use(offset, "o")); { auto timer = mDatabase.getSelectTimer("offer"); return loadOffers(prep, offers); } }
void OfferFrame::loadBestOffers(size_t numOffers, size_t offset, Asset const& selling, Asset const& buying, vector<OfferFrame::pointer>& retOffers, Database& db) { soci::session& session = db.getSession(); soci::details::prepare_temp_type sql = (session.prepare << offerColumnSelector); std::string sellingAssetCode, sellingIssuerStrKey; std::string buyingAssetCode, buyingIssuerStrKey; if (selling.type() == ASSET_TYPE_NATIVE) { sql << " WHERE sellingassettype=0"; } else { if(selling.type() == ASSET_TYPE_CREDIT_ALPHANUM4) { assetCodeToStr(selling.alphaNum4().assetCode, sellingAssetCode); sellingIssuerStrKey = PubKeyUtils::toStrKey(selling.alphaNum4().issuer); } else if(selling.type() == ASSET_TYPE_CREDIT_ALPHANUM12) { assetCodeToStr(selling.alphaNum12().assetCode, sellingAssetCode); sellingIssuerStrKey = PubKeyUtils::toStrKey(selling.alphaNum12().issuer); }else throw std::runtime_error("unknown asset type"); sql << " WHERE sellingassetcode=:pcur AND sellingissuer = :pi", use(sellingAssetCode), use(sellingIssuerStrKey); } if (buying.type() == ASSET_TYPE_NATIVE) { sql << " AND buyingassettype=0"; } else { if(buying.type() == ASSET_TYPE_CREDIT_ALPHANUM4) { assetCodeToStr(buying.alphaNum4().assetCode, buyingAssetCode); buyingIssuerStrKey = PubKeyUtils::toStrKey(buying.alphaNum4().issuer); } else if(buying.type() == ASSET_TYPE_CREDIT_ALPHANUM12) { assetCodeToStr(buying.alphaNum12().assetCode, buyingAssetCode); buyingIssuerStrKey = PubKeyUtils::toStrKey(buying.alphaNum12().issuer); }else throw std::runtime_error("unknown asset type"); sql << " AND buyingassetcode=:gcur AND buyingissuer = :gi", use(buyingAssetCode), use(buyingIssuerStrKey); } sql << " ORDER BY price,offerid LIMIT :n OFFSET :o", use(numOffers), use(offset); auto timer = db.getSelectTimer("offer"); loadOffers(sql, [&retOffers](LedgerEntry const& of) { retOffers.emplace_back(make_shared<OfferFrame>(of)); }); }
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; }
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; }