std::unique_ptr<IMainChainStorage> createVectorMainChainStorage(const Currency& currency) { std::unique_ptr<IMainChainStorage> storage(new VectorMainChainStorage()); RawBlock genesis; genesis.block = toBinaryArray(currency.genesisBlock()); storage->pushBlock(genesis); return std::move(storage); }
FusionTransactionBuilder::FusionTransactionBuilder(const Currency& currency, uint64_t amount) : m_currency(currency), m_amount(amount), m_firstInput(0), m_firstOutput(0), m_fee(0), m_extraSize(0), m_inputCount(currency.fusionTxMinInputCount()) { }
std::string to_string(Currency const& currency) { static Currency const sIsoBits ("FFFFFFFFFFFFFFFFFFFFFFFF000000FFFFFFFFFF"); // Characters we are willing to allow in the ASCII representation of a // three-letter currency code. static std::string const allowed_characters = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789" "<>(){}[]|?!@#$%^&*"; if (currency == zero) return systemCurrencyCode(); if (currency == vbcCurrency()) return systemCurrencyCodeVBC(); if (currency == noCurrency()) return "1"; if ((currency & sIsoBits).isZero ()) { // The offset of the 3 character ISO code in the currency descriptor int const isoOffset = 12; std::string const iso( currency.data () + isoOffset, currency.data () + isoOffset + 3); // Specifying the system currency code using ISO-style representation // is not allowed. if ((iso != systemCurrencyCode()) && (iso != systemCurrencyCodeVBC()) && (iso.find_first_not_of (allowed_characters) == std::string::npos)) { return iso; } } return strHex (currency.begin (), currency.size ()); }
bool AllowTrustOpFrame::doCheckValid() { if (mAllowTrust.currency.type() != CURRENCY_TYPE_ALPHANUM) { innerResult().code(ALLOW_TRUST_MALFORMED); return false; } Currency ci; ci.type(CURRENCY_TYPE_ALPHANUM); ci.alphaNum().currencyCode = mAllowTrust.currency.currencyCode(); ci.alphaNum().issuer = getSourceID(); if (!isCurrencyValid(ci)) { innerResult().code(ALLOW_TRUST_MALFORMED); return false; } return true; }
void TransactionDialog::changeCurrency(QComboBox *control, int index) { Account* acc = accounts->at(index); QList<Currency*>* currency = acc->getCurrencyList(); control->clear(); QList<Currency*>::iterator i = currency->begin(); while(i != currency->end()) { Currency *cur = (*i); if (cur->isDisplay()) { control->addItem(cur->getShortName(), cur->getId()); } i++; } }
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; }
void TradeHandler::ValidateAndAcknowledgeTrade( AcceptCommit accept_commit) { uint160 accept_commit_hash = accept_commit.GetHash160(); uint160 commit_hash = accept_commit.order_commit_hash; OrderCommit commit = msgdata[commit_hash]["commit"]; AcceptOrder accept; accept = msgdata[commit.accept_order_hash]["accept_order"]; Order order = msgdata[accept.order_hash]["order"]; Currency currency = flexnode.currencies[order.currency]; vch_t payer_data, payee_data; if (order.side == ASK) { payer_data = order.auxiliary_data; payee_data = accept.auxiliary_data; } else { payee_data = order.auxiliary_data; payer_data = accept.auxiliary_data; } if (!currency.ValidateProposedFiatTransaction(accept_commit_hash, payer_data, payee_data, order.size)) { log_ << "Could not validate fiat transaction!\n"; return; } tradedata[accept_commit_hash]["ttp_validated"] = true; ThirdPartyTransactionAcknowledgement ack(accept_commit_hash); ack.Sign(); uint160 ack_hash = ack.GetHash160(); tradedata[accept_commit_hash]["acknowledgement"] = ack_hash; BroadcastMessage(ack); }
SLE::pointer getLedgerEntryRippleState(Ledger::pointer ledger, TestAccount const& account1, TestAccount const& account2, Currency currency) { auto uNodeIndex = getRippleStateIndex( account1.pk.getAccountID(), account2.pk.getAccountID(), to_currency(currency.getCurrency())); if (!uNodeIndex.isNonZero()) throw std::runtime_error( "!uNodeIndex.isNonZero()"); return ledger->getSLEi(uNodeIndex); }
bool to_currency(Currency& currency, std::string const& code) { if (code.empty () || !code.compare (systemCurrencyCode())) { currency = zero; return true; } if (code.empty() || !code.compare(systemCurrencyCodeVBC())) { currency = vbcCurrency(); return true; } static const int CURRENCY_CODE_LENGTH = 3; if (code.size () == CURRENCY_CODE_LENGTH) { Blob codeBlob (CURRENCY_CODE_LENGTH); std::transform (code.begin (), code.end (), codeBlob.begin (), ::toupper); Serializer s; s.addZeros (96 / 8); s.addRaw (codeBlob); s.addZeros (16 / 8); s.addZeros (24 / 8); s.get160 (currency, 0); return true; } if (40 == code.size ()) return currency.SetHex (code); return false; }
int PathRequest::parseJson (Json::Value const& jvParams, bool complete) { int ret = PFR_PJ_NOCHANGE; if (jvParams.isMember (jss::source_account)) { raSrcAccount = parseBase58<AccountID>( jvParams[jss::source_account].asString()); if (! raSrcAccount) { jvStatus = rpcError (rpcSRC_ACT_MALFORMED); return PFR_PJ_INVALID; } } else if (complete) { jvStatus = rpcError (rpcSRC_ACT_MISSING); return PFR_PJ_INVALID; } if (jvParams.isMember (jss::destination_account)) { raDstAccount = parseBase58<AccountID>( jvParams[jss::destination_account].asString()); if (! raDstAccount) { jvStatus = rpcError (rpcDST_ACT_MALFORMED); return PFR_PJ_INVALID; } } else if (complete) { jvStatus = rpcError (rpcDST_ACT_MISSING); return PFR_PJ_INVALID; } if (jvParams.isMember (jss::destination_amount)) { if (! amountFromJsonNoThrow ( saDstAmount, jvParams[jss::destination_amount]) || (saDstAmount.getCurrency ().isZero () && saDstAmount.getIssuer ().isNonZero ()) || (saDstAmount.getCurrency () == badCurrency ()) || saDstAmount <= zero) { jvStatus = rpcError (rpcDST_AMT_MALFORMED); return PFR_PJ_INVALID; } } else if (complete) { jvStatus = rpcError (rpcDST_ACT_MISSING); return PFR_PJ_INVALID; } if (jvParams.isMember (jss::source_currencies)) { Json::Value const& jvSrcCur = jvParams[jss::source_currencies]; if (!jvSrcCur.isArray ()) { jvStatus = rpcError (rpcSRC_CUR_MALFORMED); return PFR_PJ_INVALID; } sciSourceCurrencies.clear (); for (unsigned i = 0; i < jvSrcCur.size (); ++i) { Json::Value const& jvCur = jvSrcCur[i]; Currency uCur; AccountID uIss; if (!jvCur.isObject() || !jvCur.isMember (jss::currency) || !to_currency (uCur, jvCur[jss::currency].asString ())) { jvStatus = rpcError (rpcSRC_CUR_MALFORMED); return PFR_PJ_INVALID; } if (jvCur.isMember (jss::issuer) && !to_issuer (uIss, jvCur[jss::issuer].asString ())) { jvStatus = rpcError (rpcSRC_ISR_MALFORMED); } if (uCur.isZero () && uIss.isNonZero ()) { jvStatus = rpcError (rpcSRC_CUR_MALFORMED); return PFR_PJ_INVALID; } if (uCur.isNonZero() && uIss.isZero()) { uIss = *raSrcAccount; } sciSourceCurrencies.insert ({uCur, uIss}); } } if (jvParams.isMember ("id")) jvId = jvParams["id"]; return ret; }
void BudgetBalancer::processItem(LedgerBudget const& budget) { // if there is a budget period, process recorded items until we are within // range of this budget command advancePeriodToDate(budget.date()); // if the current period started before today, or if it already has recorded // commands, then end the period today and process what we have if (m_period.startDate() != budget.date() || m_numRecords != 0) { m_period = DateRange(m_period.startDate(), budget.date()); allocateCategories(); processRecords(); } // remove categories that are not in this budget command, or that have // changed, and allocate their funds to the available category auto categories = budget.categories(); for (auto it = m_categories.cbegin(); it != m_categories.cend(); ++it) { if (!categories.contains(it.key()) || categories[it.key()].type != it->type) { switch (it->type) { case LedgerBudget::Category::Type::GOAL: // nothing to do for goals break; case LedgerBudget::Category::Type::INCOME: // nothing to do for income type break; case LedgerBudget::Category::Type::RESERVE_AMOUNT: { Currency amount = m_reserveAmountAllocator.deallocate(it.key()); if (!amount.isZero()) { emit message(budget, QString("Reserve category '%1' was closed with " "a balance of %2. Those funds are " "available again.") .arg(it.key()) .arg(amount.toString())); } m_available += amount; break; } case LedgerBudget::Category::Type::RESERVE_PERCENT: { Currency amount = m_reservePercentAllocator.deallocate(it.key()); if (!amount.isZero()) { emit message(budget, QString("Reserve category '%1' was closed with " "a balance of %2. Those funds are " "available again.") .arg(it.key()) .arg(amount.toString())); } m_available += amount; break; } case LedgerBudget::Category::Type::ROUTINE: m_routineAllocator.deallocate(it.key()); break; } } } // configure new and changed budget categories for (auto it = categories.cbegin(); it != categories.cend(); ++it) { switch (it->type) { case LedgerBudget::Category::Type::GOAL: m_goalAllocator.budget(budget.date()); break; case LedgerBudget::Category::Type::INCOME: // nothing to do for income break; case LedgerBudget::Category::Type::RESERVE_AMOUNT: m_reserveAmountAllocator.budget(budget.date(), it.key(), it->amount, it->interval); break; case LedgerBudget::Category::Type::RESERVE_PERCENT: m_reservePercentAllocator.budget(it.key(), it->percentage); break; case LedgerBudget::Category::Type::ROUTINE: // nothing to do for routine expenses break; } } m_categories = categories; // reset the dates for the new period m_period = DateRange(budget.date(), budget.interval()); }
// 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; }
string ToString() { return CombineString(Currency.ToString(), DespoitType.ToString(), date::to_iso_string(Period.begin()), date::to_iso_string(Period.end()), date::to_iso_string(LastUpdateDate)); }
// This interface is deprecated. Json::Value doRipplePathFind (RPC::Context& context) { RPC::LegacyPathFind lpf (context.role == Role::ADMIN); if (!lpf.isOk ()) return rpcError (rpcTOO_BUSY); context.loadType = Resource::feeHighBurdenRPC; RippleAddress raSrc; RippleAddress raDst; STAmount saDstAmount; Ledger::pointer lpLedger; Json::Value jvResult; if (getConfig().RUN_STANDALONE || context.params.isMember(jss::ledger) || context.params.isMember(jss::ledger_index) || context.params.isMember(jss::ledger_hash)) { // The caller specified a ledger jvResult = RPC::lookupLedger ( context.params, lpLedger, context.netOps); if (!lpLedger) return jvResult; } if (!context.params.isMember ("source_account")) { jvResult = rpcError (rpcSRC_ACT_MISSING); } else if (!context.params["source_account"].isString () || !raSrc.setAccountID ( context.params["source_account"].asString ())) { jvResult = rpcError (rpcSRC_ACT_MALFORMED); } else if (!context.params.isMember ("destination_account")) { jvResult = rpcError (rpcDST_ACT_MISSING); } else if (!context.params["destination_account"].isString () || !raDst.setAccountID ( context.params["destination_account"].asString ())) { jvResult = rpcError (rpcDST_ACT_MALFORMED); } else if ( // Parse saDstAmount. !context.params.isMember ("destination_amount") || ! amountFromJsonNoThrow(saDstAmount, context.params["destination_amount"]) || saDstAmount <= zero || (!isXRP(saDstAmount.getCurrency ()) && (!saDstAmount.getIssuer () || noAccount() == saDstAmount.getIssuer ()))) { WriteLog (lsINFO, RPCHandler) << "Bad destination_amount."; jvResult = rpcError (rpcINVALID_PARAMS); } else if ( // Checks on source_currencies. context.params.isMember ("source_currencies") && (!context.params["source_currencies"].isArray () || !context.params["source_currencies"].size ()) // Don't allow empty currencies. ) { WriteLog (lsINFO, RPCHandler) << "Bad source_currencies."; jvResult = rpcError (rpcINVALID_PARAMS); } else { context.loadType = Resource::feeHighBurdenRPC; RippleLineCache::pointer cache; if (lpLedger) { // The caller specified a ledger lpLedger = std::make_shared<Ledger> (std::ref (*lpLedger), false); cache = std::make_shared<RippleLineCache>(lpLedger); } else { // The closed ledger is recent and any nodes made resident // have the best chance to persist lpLedger = context.netOps.getClosedLedger(); cache = getApp().getPathRequests().getLineCache(lpLedger, false); } Json::Value jvSrcCurrencies; if (context.params.isMember ("source_currencies")) { jvSrcCurrencies = context.params["source_currencies"]; } else { auto currencies = accountSourceCurrencies (raSrc, cache, true); jvSrcCurrencies = Json::Value (Json::arrayValue); for (auto const& uCurrency: currencies) { Json::Value jvCurrency (Json::objectValue); jvCurrency["currency"] = to_string(uCurrency); jvSrcCurrencies.append (jvCurrency); } } // Fill in currencies destination will accept Json::Value jvDestCur (Json::arrayValue); // TODO(tom): this could be optimized the same way that // PathRequest::doUpdate() is - if we don't obsolete this code first. auto usDestCurrID = accountDestCurrencies (raDst, cache, true); for (auto const& uCurrency: usDestCurrID) jvDestCur.append (to_string (uCurrency)); jvResult["destination_currencies"] = jvDestCur; jvResult["destination_account"] = raDst.humanAccountID (); Json::Value jvArray (Json::arrayValue); int level = getConfig().PATH_SEARCH_OLD; if ((getConfig().PATH_SEARCH_MAX > level) && !getApp().getFeeTrack().isLoadedLocal()) { ++level; } if (context.params.isMember("search_depth") && context.params["search_depth"].isIntegral()) { int rLev = context.params["search_depth"].asInt (); if ((rLev < level) || (context.role == Role::ADMIN)) level = rLev; } FindPaths fp ( cache, raSrc.getAccountID(), raDst.getAccountID(), saDstAmount, level, 4); // max paths for (unsigned int i = 0; i != jvSrcCurrencies.size (); ++i) { Json::Value jvSource = jvSrcCurrencies[i]; Currency uSrcCurrencyID; Account uSrcIssuerID; if (!jvSource.isObject ()) return rpcError (rpcINVALID_PARAMS); // Parse mandatory currency. if (!jvSource.isMember ("currency") || !to_currency ( uSrcCurrencyID, jvSource["currency"].asString ())) { WriteLog (lsINFO, RPCHandler) << "Bad currency."; return rpcError (rpcSRC_CUR_MALFORMED); } if (uSrcCurrencyID.isNonZero ()) uSrcIssuerID = raSrc.getAccountID (); // Parse optional issuer. if (jvSource.isMember ("issuer") && ((!jvSource["issuer"].isString () || !to_issuer (uSrcIssuerID, jvSource["issuer"].asString ())) || (uSrcIssuerID.isZero () != uSrcCurrencyID.isZero ()) || (noAccount() == uSrcIssuerID))) { WriteLog (lsINFO, RPCHandler) << "Bad issuer."; return rpcError (rpcSRC_ISR_MALFORMED); } STPathSet spsComputed; if (context.params.isMember("paths")) { Json::Value pathSet = Json::objectValue; pathSet["Paths"] = context.params["paths"]; STParsedJSONObject paths ("pathSet", pathSet); if (paths.object.get() == nullptr) return paths.error; else { spsComputed = paths.object.get()->getFieldPathSet (sfPaths); WriteLog (lsTRACE, RPCHandler) << "ripple_path_find: Paths: " << spsComputed.getJson (0); } } STPath fullLiquidityPath; auto valid = fp.findPathsForIssue ( {uSrcCurrencyID, uSrcIssuerID}, spsComputed, fullLiquidityPath); if (!valid) { WriteLog (lsWARNING, RPCHandler) << "ripple_path_find: No paths found."; } else { auto& issuer = isXRP (uSrcIssuerID) ? isXRP (uSrcCurrencyID) ? // Default to source account. xrpAccount() : Account (raSrc.getAccountID ()) : uSrcIssuerID; // Use specifed issuer. STAmount saMaxAmount ({uSrcCurrencyID, issuer}, 1); saMaxAmount.negate (); LedgerEntrySet lesSandbox (lpLedger, tapNONE); auto rc = path::RippleCalc::rippleCalculate ( lesSandbox, saMaxAmount, // --> Amount to send is unlimited // to get an estimate. saDstAmount, // --> Amount to deliver. raDst.getAccountID (), // --> Account to deliver to. raSrc.getAccountID (), // --> Account sending from. spsComputed); // --> Path set. WriteLog (lsWARNING, RPCHandler) << "ripple_path_find:" << " saMaxAmount=" << saMaxAmount << " saDstAmount=" << saDstAmount << " saMaxAmountAct=" << rc.actualAmountIn << " saDstAmountAct=" << rc.actualAmountOut; if (fullLiquidityPath.size() > 0 && (rc.result() == terNO_LINE || rc.result() == tecPATH_PARTIAL)) { WriteLog (lsDEBUG, PathRequest) << "Trying with an extra path element"; spsComputed.push_back (fullLiquidityPath); lesSandbox.clear (); rc = path::RippleCalc::rippleCalculate ( lesSandbox, saMaxAmount, // --> Amount to send is unlimited // to get an estimate. saDstAmount, // --> Amount to deliver. raDst.getAccountID (), // --> Account to deliver to. raSrc.getAccountID (), // --> Account sending from. spsComputed); // --> Path set. WriteLog (lsDEBUG, PathRequest) << "Extra path element gives " << transHuman (rc.result ()); } if (rc.result () == tesSUCCESS) { Json::Value jvEntry (Json::objectValue); STPathSet spsCanonical; // Reuse the expanded as it would need to be calcuated // anyway to produce the canonical. (At least unless we // make a direct canonical.) jvEntry["source_amount"] = rc.actualAmountIn.getJson (0); jvEntry["paths_canonical"] = Json::arrayValue; jvEntry["paths_computed"] = spsComputed.getJson (0); jvArray.append (jvEntry); } else { std::string strToken; std::string strHuman; transResultInfo (rc.result (), strToken, strHuman); WriteLog (lsDEBUG, RPCHandler) << "ripple_path_find: " << strToken << " " << strHuman << " " << spsComputed.getJson (0); } } } // Each alternative differs by source currency. jvResult["alternatives"] = jvArray; } WriteLog (lsDEBUG, RPCHandler) << boost::str (boost::format ("ripple_path_find< %s") % jvResult); return jvResult; }
string ToString() { return CombineString(Number, Name, Source, Currency.ToString(), time::to_iso_string(Time), Description); }
std::pair<bool, Json::Value> ripplePathFind(RippleLineCache::pointer const& cache, RippleAddress const& raSrc, RippleAddress const& raDst, STAmount const& saDstAmount, Ledger::pointer const& lpLedger, Json::Value const& jvSrcCurrencies, boost::optional<Json::Value> const& contextPaths, int const& level) { FindPaths fp( cache, raSrc.getAccountID(), raDst.getAccountID(), saDstAmount, level, 4); // max paths Json::Value jvArray(Json::arrayValue); for (unsigned int i = 0; i != jvSrcCurrencies.size(); ++i) { Json::Value jvSource = jvSrcCurrencies[i]; Currency uSrcCurrencyID; Account uSrcIssuerID; if (!jvSource.isObject()) return std::make_pair(false, rpcError(rpcINVALID_PARAMS)); // Parse mandatory currency. if (!jvSource.isMember(jss::currency) || !to_currency( uSrcCurrencyID, jvSource[jss::currency].asString())) { WriteLog(lsINFO, RPCHandler) << "Bad currency."; return std::make_pair(false, rpcError(rpcSRC_CUR_MALFORMED)); } if (uSrcCurrencyID.isNonZero()) uSrcIssuerID = raSrc.getAccountID(); // Parse optional issuer. if (jvSource.isMember(jss::issuer) && ((!jvSource[jss::issuer].isString() || !to_issuer(uSrcIssuerID, jvSource[jss::issuer].asString())) || (uSrcIssuerID.isZero() != uSrcCurrencyID.isZero()) || (noAccount() == uSrcIssuerID))) { WriteLog(lsINFO, RPCHandler) << "Bad issuer."; return std::make_pair(false, rpcError(rpcSRC_ISR_MALFORMED)); } STPathSet spsComputed; if (contextPaths) { Json::Value pathSet = Json::objectValue; pathSet[jss::Paths] = contextPaths.get(); STParsedJSONObject paths("pathSet", pathSet); if (paths.object.get() == nullptr) return std::make_pair(false, paths.error); else { spsComputed = paths.object.get()->getFieldPathSet(sfPaths); WriteLog(lsTRACE, RPCHandler) << "ripple_path_find: Paths: " << spsComputed.getJson(0); } } STPath fullLiquidityPath; auto valid = fp.findPathsForIssue( { uSrcCurrencyID, uSrcIssuerID }, spsComputed, fullLiquidityPath); if (!valid) { WriteLog(lsWARNING, RPCHandler) << "ripple_path_find: No paths found."; } else { auto& issuer = isXRP(uSrcIssuerID) ? isXRP(uSrcCurrencyID) ? // Default to source account. xrpAccount() : Account(raSrc.getAccountID()) : uSrcIssuerID; // Use specifed issuer. STAmount saMaxAmount({ uSrcCurrencyID, issuer }, 1); saMaxAmount.negate(); LedgerEntrySet lesSandbox(lpLedger, tapNONE); auto rc = path::RippleCalc::rippleCalculate( lesSandbox, saMaxAmount, // --> Amount to send is unlimited // to get an estimate. saDstAmount, // --> Amount to deliver. raDst.getAccountID(), // --> Account to deliver to. raSrc.getAccountID(), // --> Account sending from. spsComputed); // --> Path set. WriteLog(lsWARNING, RPCHandler) << "ripple_path_find:" << " saMaxAmount=" << saMaxAmount << " saDstAmount=" << saDstAmount << " saMaxAmountAct=" << rc.actualAmountIn << " saDstAmountAct=" << rc.actualAmountOut; if (fullLiquidityPath.size() > 0 && (rc.result() == terNO_LINE || rc.result() == tecPATH_PARTIAL)) { WriteLog(lsDEBUG, PathRequest) << "Trying with an extra path element"; spsComputed.push_back(fullLiquidityPath); lesSandbox.clear(); rc = path::RippleCalc::rippleCalculate( lesSandbox, saMaxAmount, // --> Amount to send is unlimited // to get an estimate. saDstAmount, // --> Amount to deliver. raDst.getAccountID(), // --> Account to deliver to. raSrc.getAccountID(), // --> Account sending from. spsComputed); // --> Path set. WriteLog(lsDEBUG, PathRequest) << "Extra path element gives " << transHuman(rc.result()); } if (rc.result() == tesSUCCESS) { Json::Value jvEntry(Json::objectValue); STPathSet spsCanonical; // Reuse the expanded as it would need to be calcuated // anyway to produce the canonical. (At least unless we // make a direct canonical.) jvEntry[jss::source_amount] = rc.actualAmountIn.getJson(0); jvEntry[jss::paths_canonical] = Json::arrayValue; jvEntry[jss::paths_computed] = spsComputed.getJson(0); jvArray.append(jvEntry); } else { std::string strToken; std::string strHuman; transResultInfo(rc.result(), strToken, strHuman); WriteLog(lsDEBUG, RPCHandler) << "ripple_path_find: " << strToken << " " << strHuman << " " << spsComputed.getJson(0); } } } return std::make_pair(true, jvArray); }
inline Money Money::rounded() const { return Money(currency_.rounding()(value_), currency_); }
int PathRequest::parseJson (const Json::Value& jvParams, bool complete) { int ret = PFR_PJ_NOCHANGE; if (jvParams.isMember ("source_account")) { if (!raSrcAccount.setAccountID (jvParams["source_account"].asString ())) { jvStatus = rpcError (rpcSRC_ACT_MALFORMED); return PFR_PJ_INVALID; } } else if (complete) { jvStatus = rpcError (rpcSRC_ACT_MISSING); return PFR_PJ_INVALID; } if (jvParams.isMember ("destination_account")) { if (!raDstAccount.setAccountID (jvParams["destination_account"].asString ())) { jvStatus = rpcError (rpcDST_ACT_MALFORMED); return PFR_PJ_INVALID; } } else if (complete) { jvStatus = rpcError (rpcDST_ACT_MISSING); return PFR_PJ_INVALID; } if (jvParams.isMember ("destination_amount")) { if (!saDstAmount.bSetJson (jvParams["destination_amount"]) || (saDstAmount.getCurrency ().isZero () && saDstAmount.getIssuer ().isNonZero ()) || (saDstAmount.getCurrency () == badCurrency()) || saDstAmount <= zero) { jvStatus = rpcError (rpcDST_AMT_MALFORMED); return PFR_PJ_INVALID; } } else if (complete) { jvStatus = rpcError (rpcDST_ACT_MISSING); return PFR_PJ_INVALID; } if (jvParams.isMember ("source_currencies")) { const Json::Value& jvSrcCur = jvParams["source_currencies"]; if (!jvSrcCur.isArray ()) { jvStatus = rpcError (rpcSRC_CUR_MALFORMED); return PFR_PJ_INVALID; } sciSourceCurrencies.clear (); for (unsigned i = 0; i < jvSrcCur.size (); ++i) { const Json::Value& jvCur = jvSrcCur[i]; Currency uCur; Account uIss; if (!jvCur.isObject() || !jvCur.isMember ("currency") || !to_currency (uCur, jvCur["currency"].asString ())) { jvStatus = rpcError (rpcSRC_CUR_MALFORMED); return PFR_PJ_INVALID; } if (jvCur.isMember ("issuer") && !to_issuer (uIss, jvCur["issuer"].asString ())) { jvStatus = rpcError (rpcSRC_ISR_MALFORMED); } if (uCur.isZero () && uIss.isNonZero ()) { jvStatus = rpcError (rpcSRC_CUR_MALFORMED); return PFR_PJ_INVALID; } sciSourceCurrencies.insert ({uCur, uIss}); } } if (jvParams.isMember ("id")) jvId = jvParams["id"]; return ret; }
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; Currency curB = mPathPayment.destCurrency; // update balances, walks backwards // build the full path to the destination, starting with sendCurrency std::vector<Currency> fullPath; fullPath.emplace_back(mPathPayment.sendCurrency); fullPath.insert(fullPath.end(), mPathPayment.path.begin(), mPathPayment.path.end()); // update last balance in the chain { if (curB.type() == CURRENCY_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; Currency 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() == CURRENCY_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; issuer = AccountFrame::loadAccount(curB.alphaNum().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; }
double EqonomizeValueEdit::fixup_sub(QString &input, QStringList &errors, bool &calculated) const { input = input.trimmed(); if(input.isEmpty()) { return 0.0; } if(o_currency) { input.remove(budget->monetary_group_separator); if(!budget->monetary_negative_sign.isEmpty()) input.replace(budget->monetary_negative_sign, "-"); if(!budget->monetary_positive_sign.isEmpty()) input.replace(budget->monetary_positive_sign, "+"); } else { input.remove(budget->group_separator); if(!budget->negative_sign.isEmpty()) input.replace(budget->negative_sign, "-"); if(!budget->positive_sign.isEmpty()) input.replace(budget->positive_sign, "+"); } input.replace(QLocale().negativeSign(), '-'); input.replace(QLocale().positiveSign(), '+'); int i = input.indexOf(')', 1); if(i < 1) { i = input.indexOf('(', 0); if(i == 0) { input.remove(0, 1); return fixup_sub(input, errors, calculated); } else if(i >= 0) { input += ')'; i = input.length() - 1; } else { i = -1; } } if(i >= 1) { int i2 = input.lastIndexOf('(', i - 1); if(i2 < 0 || i2 > i) { if(i == input.length() - 1) { input.chop(1); return fixup_sub(input, errors, calculated); } input.prepend('('); i++; i2 = 0; } if(i2 == 0 && i == input.length() - 1) { input.remove(0, 1); input.chop(1); return fixup_sub(input, errors, calculated); } if(i < input.length() - 1 && (input[i + 1].isNumber() || input[i + 1] == '(')) input.insert(i + 1, '*'); QString str = input.mid(i2 + 1, i - i2 - 1); double v = fixup_sub(str, errors, calculated); input.replace(i2, i - i2 + 1, o_currency ? o_currency->formatValue(0.5, decimals() + 2, false, false, true) : budget->formatValue(v, decimals() + 2)); if(i2 > 0 && (input[i2 - 1].isNumber() || input[i2 - 1] == ')')) input.insert(i2, '*'); calculated = true; return fixup_sub(input, errors, calculated); } i = input.indexOf(QRegExp("[-+]"), 1); if(i >= 1) { QStringList terms = input.split(QRegExp("[-+]")); i = 0; double v = 0.0; QList<bool> signs; signs << true; for(int terms_i = 0; terms_i < terms.size() - 1; terms_i++) { i += terms[terms_i].length(); if(input[i] == '-') signs << false; else signs << true; i++; } for(int terms_i = 0; terms_i < terms.size() - 1; terms_i++) { if(terms[terms_i].endsWith('*') || terms[terms_i].endsWith('/') || terms[terms_i].endsWith('^')) { if(!signs[terms_i + 1]) terms[terms_i] += '-'; else terms[terms_i] += '+'; terms[terms_i] += terms[terms_i + 1]; signs.removeAt(terms_i + 1); terms.removeAt(terms_i + 1); terms_i--; } } if(terms.size() > 1) { for(int terms_i = 0; terms_i < terms.size(); terms_i++) { if(terms[terms_i].isEmpty()) { if(!signs[terms_i] && terms_i + 1 < terms.size()) { signs[terms_i + 1] = !signs[terms_i + 1]; } } else { if(!signs[terms_i]) v -= fixup_sub(terms[terms_i], errors, calculated); else v += fixup_sub(terms[terms_i], errors, calculated); } } calculated = true; return v; } } if(input.indexOf("**") >= 0) input.replace("**", "^"); i = input.indexOf(QRegExp("[*/]"), 0); if(i >= 0) { QStringList terms = input.split(QRegExp("[*/]")); QChar c = '*'; i = 0; double v = 1.0; for(int terms_i = 0; terms_i < terms.size(); terms_i++) { if(terms[terms_i].isEmpty()) { if(c == '/') { errors << tr("Empty denominator."); } else { errors << tr("Empty factor."); } } else { i += terms[terms_i].length(); if(c == '/') { double den = fixup_sub(terms[terms_i], errors, calculated); if(den == 0.0) { errors << tr("Division by zero."); } else { v /= den; } } else { v *= fixup_sub(terms[terms_i], errors, calculated); } if(i < input.length()) c = input[i]; } i++; } calculated = true; return v; } i = input.indexOf(QLocale().percent()); if(i >= 0) { double v = 0.01; if(input.length() > 1) { if(i > 0 && i < input.length() - 1) { QString str = input.right(input.length() - 1 - i); input = input.left(i); i = 0; v = fixup_sub(str, errors, calculated) * v; } else if(i == input.length() - 1) { input = input.left(i); } else if(i == 0) { input = input.right(input.length() - 1); } v = fixup_sub(input, errors, calculated) * v; } calculated = true; return v; } if(o_currency) { QString reg_exp_str = "[\\d\\+\\-\\^"; reg_exp_str += '\\'; reg_exp_str += budget->monetary_decimal_separator; reg_exp_str += '\\'; reg_exp_str += budget->monetary_group_separator; if(budget->monetary_decimal_separator != "." && budget->monetary_group_separator != ".") { reg_exp_str += '\\'; reg_exp_str += '.'; } reg_exp_str += "]"; int i = input.indexOf(QRegExp(reg_exp_str)); if(i >= 1) { QString scur = input.left(i).trimmed(); Currency *cur = budget->findCurrency(scur); if(!cur && budget->defaultCurrency()->symbol(false) == scur) cur = budget->defaultCurrency(); if(!cur) cur = budget->findCurrencySymbol(scur, true); if(cur) { QString value = input.right(input.length() - i); double v = fixup_sub(value, errors, calculated); if(cur != o_currency) { v = cur->convertTo(v, o_currency); } calculated = true; return v; } errors << tr("Unknown or ambiguous currency, or unrecognized characters, in expression: %1.").arg(scur); } if(i >= 0) { i = input.lastIndexOf(QRegExp(reg_exp_str)); if(i >= 0 && i < input.length() - 1) { QString scur = input.right(input.length() - (i + 1)).trimmed(); Currency *cur = budget->findCurrency(scur); if(!cur && budget->defaultCurrency()->symbol(false) == scur) cur = budget->defaultCurrency(); if(!cur) cur = budget->findCurrencySymbol(scur, true); if(cur) { QString value = input.left(i + 1); double v = fixup_sub(value, errors, calculated); if(cur != o_currency) { v = cur->convertTo(v, o_currency); } calculated = true; return v; } errors << tr("Unknown or ambiguous currency, or unrecognized characters, in expression: %1.").arg(scur); } } else { Currency *cur = budget->findCurrency(input); if(!cur && budget->defaultCurrency()->symbol(false) == input) cur = budget->defaultCurrency(); if(!cur) cur = budget->findCurrencySymbol(input, true); if(cur) { double v = 1.0; if(cur != o_currency) { v = cur->convertTo(v, o_currency); } calculated = true; return v; } errors << tr("Unknown or ambiguous currency, or unrecognized characters, in expression: %1.").arg(input); } } i = input.indexOf('^', 0); if(i >= 0 && i != input.length() - 1) { QString base = input.left(i); if(base.isEmpty()) { errors << tr("Empty base."); } else { QString exp = input.right(input.length() - (i + 1)); double v; if(exp.isEmpty()) { errors << tr("Error"), tr("Empty exponent."); v = 1.0; } else { v = pow(fixup_sub(base, errors, calculated), fixup_sub(exp, errors, calculated)); } calculated = true; return v; } } if(!o_currency) { QString reg_exp_str = "[^\\d\\+\\-"; reg_exp_str += '\\'; reg_exp_str += budget->decimal_separator; reg_exp_str += '\\'; reg_exp_str += budget->group_separator; if(budget->decimal_separator != "." && budget->group_separator != ".") { reg_exp_str += '\\'; reg_exp_str += '.'; } reg_exp_str += "]"; i = input.indexOf(QRegExp(reg_exp_str)); if(i >= 0) { errors << tr("Unrecognized characters in expression."); } input.remove(budget->group_separator); input.replace(budget->decimal_separator, "."); return input.toDouble(); } input.remove(budget->monetary_group_separator); input.replace(budget->monetary_decimal_separator, "."); return input.toDouble(); }
int PathRequest::parseJson (Json::Value const& jvParams) { if (! jvParams.isMember(jss::source_account)) { jvStatus = rpcError(rpcSRC_ACT_MISSING); return PFR_PJ_INVALID; } if (! jvParams.isMember(jss::destination_account)) { jvStatus = rpcError(rpcDST_ACT_MISSING); return PFR_PJ_INVALID; } if (! jvParams.isMember(jss::destination_amount)) { jvStatus = rpcError(rpcDST_AMT_MISSING); return PFR_PJ_INVALID; } raSrcAccount = parseBase58<AccountID>( jvParams[jss::source_account].asString()); if (! raSrcAccount) { jvStatus = rpcError (rpcSRC_ACT_MALFORMED); return PFR_PJ_INVALID; } raDstAccount = parseBase58<AccountID>( jvParams[jss::destination_account].asString()); if (! raDstAccount) { jvStatus = rpcError (rpcDST_ACT_MALFORMED); return PFR_PJ_INVALID; } if (! amountFromJsonNoThrow ( saDstAmount, jvParams[jss::destination_amount])) { jvStatus = rpcError (rpcDST_AMT_MALFORMED); return PFR_PJ_INVALID; } convert_all_ = saDstAmount == STAmount(saDstAmount.issue(), 1u, 0, true); if ((saDstAmount.getCurrency ().isZero () && saDstAmount.getIssuer ().isNonZero ()) || (saDstAmount.getCurrency () == badCurrency ()) || (! convert_all_ && saDstAmount <= zero)) { jvStatus = rpcError (rpcDST_AMT_MALFORMED); return PFR_PJ_INVALID; } if (jvParams.isMember(jss::send_max)) { // Send_max requires destination amount to be -1. if (! convert_all_) { jvStatus = rpcError(rpcDST_AMT_MALFORMED); return PFR_PJ_INVALID; } saSendMax.emplace(); if (! amountFromJsonNoThrow( *saSendMax, jvParams[jss::send_max]) || (saSendMax->getCurrency().isZero() && saSendMax->getIssuer().isNonZero()) || (saSendMax->getCurrency() == badCurrency()) || (*saSendMax <= zero && *saSendMax != STAmount(saSendMax->issue(), 1u, 0, true))) { jvStatus = rpcError(rpcSENDMAX_MALFORMED); return PFR_PJ_INVALID; } } if (jvParams.isMember (jss::source_currencies)) { Json::Value const& jvSrcCurrencies = jvParams[jss::source_currencies]; if (! jvSrcCurrencies.isArray() || jvSrcCurrencies.size() == 0 || jvSrcCurrencies.size() > RPC::Tuning::max_src_cur) { jvStatus = rpcError (rpcSRC_CUR_MALFORMED); return PFR_PJ_INVALID; } sciSourceCurrencies.clear (); for (auto const& c : jvSrcCurrencies) { // Mandatory currency Currency srcCurrencyID; if (! c.isObject() || ! c.isMember(jss::currency) || ! to_currency(srcCurrencyID, c[jss::currency].asString())) { jvStatus = rpcError (rpcSRC_CUR_MALFORMED); return PFR_PJ_INVALID; } // Optional issuer AccountID srcIssuerID; if (c.isMember (jss::issuer) && (! c[jss::issuer].isString() || ! to_issuer(srcIssuerID, c[jss::issuer].asString()))) { jvStatus = rpcError (rpcSRC_ISR_MALFORMED); return PFR_PJ_INVALID; } if (srcCurrencyID.isZero()) { if (srcIssuerID.isNonZero()) { jvStatus = rpcError(rpcSRC_CUR_MALFORMED); return PFR_PJ_INVALID; } } else if (srcIssuerID.isZero()) { srcIssuerID = *raSrcAccount; } if (saSendMax) { // If the currencies don't match, ignore the source currency. if (srcCurrencyID == saSendMax->getCurrency()) { // If neither is the source and they are not equal, then the // source issuer is illegal. if (srcIssuerID != *raSrcAccount && saSendMax->getIssuer() != *raSrcAccount && srcIssuerID != saSendMax->getIssuer()) { jvStatus = rpcError (rpcSRC_ISR_MALFORMED); return PFR_PJ_INVALID; } // If both are the source, use the source. // Otherwise, use the one that's not the source. if (srcIssuerID != *raSrcAccount) { sciSourceCurrencies.insert( {srcCurrencyID, srcIssuerID}); } else if (saSendMax->getIssuer() != *raSrcAccount) { sciSourceCurrencies.insert( {srcCurrencyID, saSendMax->getIssuer()}); } else { sciSourceCurrencies.insert( {srcCurrencyID, *raSrcAccount}); } } } else { sciSourceCurrencies.insert({srcCurrencyID, srcIssuerID}); } } } if (jvParams.isMember ("id")) jvId = jvParams["id"]; return PFR_PJ_NOCHANGE; }
inline bool operator==(const Currency& c1, const Currency& c2) { return c1.name() == c2.name(); }