diff --git a/Builds/VisualStudio2013/RippleD.vcxproj b/Builds/VisualStudio2013/RippleD.vcxproj
index c256ef5b31..38b48abe15 100644
--- a/Builds/VisualStudio2013/RippleD.vcxproj
+++ b/Builds/VisualStudio2013/RippleD.vcxproj
@@ -1942,6 +1942,10 @@
+
+ True
+ True
+
True
True
@@ -3319,6 +3323,8 @@
+
+
True
diff --git a/Builds/VisualStudio2013/RippleD.vcxproj.filters b/Builds/VisualStudio2013/RippleD.vcxproj.filters
index 97145598b1..dae82c8920 100644
--- a/Builds/VisualStudio2013/RippleD.vcxproj.filters
+++ b/Builds/VisualStudio2013/RippleD.vcxproj.filters
@@ -2535,6 +2535,9 @@
ripple\app\tests
+
+ ripple\app\tests
+
ripple\app\transactors
@@ -3888,6 +3891,9 @@
ripple\rpc\impl
+
+ ripple\rpc\impl
+
ripple\rpc\impl
diff --git a/src/ripple/app/ledger/tests/Ledger_test.cpp b/src/ripple/app/ledger/tests/Ledger_test.cpp
index bab7833e12..3282eb9eaa 100644
--- a/src/ripple/app/ledger/tests/Ledger_test.cpp
+++ b/src/ripple/app/ledger/tests/Ledger_test.cpp
@@ -29,10 +29,10 @@ class Ledger_test : public beast::unit_test::suite
std::uint64_t const xrp = std::mega::num;
auto master = createAccount ("masterpassphrase", keyType);
-
- Ledger::pointer LCL = createGenesisLedger(100000*xrp, master);
-
- Ledger::pointer ledger = std::make_shared(false, *LCL);
+
+ Ledger::pointer LCL;
+ Ledger::pointer ledger;
+ std::tie(LCL, ledger) = createGenesisLedger(100000*xrp, master);
// User accounts
auto gw1 = createAccount ("gw1", keyType);
@@ -44,48 +44,48 @@ class Ledger_test : public beast::unit_test::suite
auto mark = createAccount ("mark", keyType);
// Fund gw1, gw2, gw3, alice, mark from master
- makeAndApplyPayment(master, gw1, 5000 * xrp, ledger, sign);
- makeAndApplyPayment(master, gw2, 4000 * xrp, ledger, sign);
- makeAndApplyPayment(master, gw3, 3000 * xrp, ledger, sign);
- makeAndApplyPayment(master, alice, 2000 * xrp, ledger, sign);
- makeAndApplyPayment(master, mark, 1000 * xrp, ledger, sign);
+ pay(master, gw1, 5000 * xrp, ledger, sign);
+ pay(master, gw2, 4000 * xrp, ledger, sign);
+ pay(master, gw3, 3000 * xrp, ledger, sign);
+ pay(master, alice, 2000 * xrp, ledger, sign);
+ pay(master, mark, 1000 * xrp, ledger, sign);
- LCL = close_and_advance(ledger, LCL);
- ledger = std::make_shared(false, *LCL);
+ close_and_advance(ledger, LCL);
// alice trusts FOO/gw1
- makeTrustSet (alice, gw1, "FOO", 1, ledger, sign);
+ trust (alice, gw1, "FOO", 1, ledger, sign);
// mark trusts FOO/gw2
- makeTrustSet (mark, gw2, "FOO", 1, ledger, sign);
+ trust (mark, gw2, "FOO", 1, ledger, sign);
// mark trusts FOO/gw3
- makeTrustSet (mark, gw3, "FOO", 1, ledger, sign);
+ trust (mark, gw3, "FOO", 1, ledger, sign);
// gw2 pays mark with FOO
- makeAndApplyPayment(gw2, mark, "FOO", ".1", ledger, sign);
+ pay(gw2, mark, "FOO", "0.1", ledger, sign);
// gw3 pays mark with FOO
- makeAndApplyPayment(gw3, mark, "FOO", ".2", ledger, sign);
+ pay(gw3, mark, "FOO", "0.2", ledger, sign);
// gw1 pays alice with FOO
- makeAndApplyPayment(gw1, alice, "FOO", ".3", ledger, sign);
+ pay(gw1, alice, "FOO", "0.3", ledger, sign);
- LCL = close_and_advance(ledger, LCL);
- ledger = std::make_shared(false, *LCL);
+ verifyBalance(ledger, mark, Amount(0.1, "FOO", gw2));
+ verifyBalance(ledger, mark, Amount(0.2, "FOO", gw3));
+ verifyBalance(ledger, alice, Amount(0.3, "FOO", gw1));
+
+ close_and_advance(ledger, LCL);
createOffer (mark, Amount (1, "FOO", gw1), Amount (1, "FOO", gw2), ledger, sign);
createOffer (mark, Amount (1, "FOO", gw2), Amount (1, "FOO", gw3), ledger, sign);
cancelOffer (mark, ledger, sign);
freezeAccount (alice, ledger, sign);
- LCL = close_and_advance(ledger, LCL);
- ledger = std::make_shared(false, *LCL);
+ close_and_advance(ledger, LCL);
- makeAndApplyPayment(alice, mark, 1 * xrp, ledger, sign);
+ pay(alice, mark, 1 * xrp, ledger, sign);
- LCL = close_and_advance(ledger, LCL);
- ledger = std::make_shared(false, *LCL);
+ close_and_advance(ledger, LCL);
pass ();
}
@@ -96,9 +96,9 @@ class Ledger_test : public beast::unit_test::suite
auto master = createAccount ("masterpassphrase", keyType);
- Ledger::pointer LCL = createGenesisLedger (100000 * xrp, master);
-
- Ledger::pointer ledger = std::make_shared (false, *LCL);
+ Ledger::pointer LCL;
+ Ledger::pointer ledger;
+ std::tie(LCL, ledger) = createGenesisLedger (100000 * xrp, master);
auto gw1 = createAccount ("gw1", keyType);
diff --git a/src/ripple/app/tests/Path_test.cpp b/src/ripple/app/tests/Path_test.cpp
new file mode 100644
index 0000000000..049cf98af4
--- /dev/null
+++ b/src/ripple/app/tests/Path_test.cpp
@@ -0,0 +1,212 @@
+//------------------------------------------------------------------------------
+/*
+This file is part of rippled: https://github.com/ripple/rippled
+Copyright (c) 2012, 2013 Ripple Labs Inc.
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+//==============================================================================
+
+#include
+#include
+#include
+#include
+
+// probably going to be moved to common_ledger.h
+
+namespace ripple {
+namespace test {
+
+class Path_test : public TestSuite
+{
+ void
+ test_no_direct_path_no_intermediary_no_alternatives()
+ {
+ testcase("no direct path no intermediary no alternatives");
+ std::uint64_t const xrp = std::mega::num;
+
+ auto master = createAccount("masterpassphrase", KeyType::ed25519);
+
+ Ledger::pointer LCL;
+ Ledger::pointer ledger;
+ std::tie(LCL, ledger) = createGenesisLedger(100000 * xrp, master);
+
+ auto accounts = createAndFundAccounts(master, { "alice", "bob" },
+ KeyType::ed25519, 10000 * xrp, ledger);
+ auto& alice = accounts["alice"];
+ auto& bob = accounts["bob"];
+ expectNotEquals(alice.pk.humanAccountID(), master.pk.humanAccountID());
+
+ auto alternatives = findPath(ledger, alice, bob, { Currency("USD") },
+ Amount(5, "USD", alice), log.stream());
+ log << "ripplePathFind alternatives: " << alternatives;
+
+ expectEquals(alternatives.size(), 0);
+ }
+
+ void
+ test_direct_path_no_intermediary()
+ {
+ testcase("direct path no intermediary");
+ std::uint64_t const xrp = std::mega::num;
+
+ auto master = createAccount("masterpassphrase", KeyType::ed25519);
+
+ Ledger::pointer LCL;
+ Ledger::pointer ledger;
+ std::tie(LCL, ledger) = createGenesisLedger(100000 * xrp, master);
+
+ // Create accounts
+ auto accounts = createAndFundAccounts(master, { "alice", "bob" },
+ KeyType::ed25519, 10000 * xrp, ledger);
+ auto& alice = accounts["alice"];
+ auto& bob = accounts["bob"];
+ expectNotEquals(alice.pk.humanAccountID(), master.pk.humanAccountID());
+
+ // Set credit limit
+ trust(bob, alice, "USD", 700, ledger);
+
+ // Find path from alice to bob
+ auto alternatives = findPath(ledger, alice, bob, { Currency("USD") },
+ Amount(5, "USD", bob), log.stream());
+ log << "ripplePathFind alternatives: " << alternatives;
+
+ expectEquals(alternatives.size(), 1);
+ auto alt = alternatives[0u];
+ expectEquals(alt[jss::paths_canonical].size(), 0);
+ expectEquals(alt[jss::paths_computed].size(), 0);
+ auto srcAmount = alt[jss::source_amount];
+ expectEquals(srcAmount[jss::currency], "USD");
+ expectEquals(srcAmount[jss::value], "5");
+ expectEquals(srcAmount[jss::issuer], alice.pk.humanAccountID());
+ }
+
+ void
+ test_payment_auto_path_find_using_build_path()
+ {
+ testcase("payment auto path find (using build_path)");
+
+ std::uint64_t const xrp = std::mega::num;
+
+ auto master = createAccount("masterpassphrase", KeyType::ed25519);
+
+ Ledger::pointer LCL;
+ Ledger::pointer ledger;
+ std::tie(LCL, ledger) = createGenesisLedger(100000 * xrp, master);
+
+ // Create accounts
+ auto accounts = createAndFundAccountsWithFlags(master, { "alice", "bob", "mtgox" },
+ KeyType::ed25519, 10000 * xrp, ledger, LCL, asfDefaultRipple);
+ auto& alice = accounts["alice"];
+ auto& bob = accounts["bob"];
+ auto& mtgox = accounts["mtgox"];
+ expectNotEquals(alice.pk.humanAccountID(), master.pk.humanAccountID());
+
+ // Set credit limits
+ trust(alice, mtgox, "USD", 600, ledger);
+ trust(bob, mtgox, "USD", 700, ledger);
+
+ // Distribute funds.
+ pay(mtgox, alice, "USD", "70", ledger);
+
+ verifyBalance(ledger, alice, Amount(70, "USD", mtgox));
+
+ // Payment with path.
+ payWithPath(alice, bob, "USD", "24", ledger);
+
+ // Verify balances
+ verifyBalance(ledger, alice, Amount(46, "USD", mtgox));
+ verifyBalance(ledger, mtgox, Amount(-46, "USD", alice));
+ verifyBalance(ledger, mtgox, Amount(-24, "USD", bob));
+ verifyBalance(ledger, bob, Amount(24, "USD", mtgox));
+ }
+
+ void
+ test_path_find()
+ {
+ testcase("path find");
+
+ std::uint64_t const xrp = std::mega::num;
+
+ auto master = createAccount("masterpassphrase", KeyType::ed25519);
+
+ Ledger::pointer LCL;
+ Ledger::pointer ledger;
+ std::tie(LCL, ledger) = createGenesisLedger(100000 * xrp, master);
+
+ // Create accounts
+ auto accounts = createAndFundAccountsWithFlags(master, { "alice", "bob", "mtgox" },
+ KeyType::ed25519, 10000 * xrp, ledger, LCL, asfDefaultRipple);
+ auto& alice = accounts["alice"];
+ auto& bob = accounts["bob"];
+ auto& mtgox = accounts["mtgox"];
+ expectNotEquals(alice.pk.humanAccountID(), master.pk.humanAccountID());
+
+ // Set credit limits
+ trust(alice, mtgox, "USD", 600, ledger);
+ trust(bob, mtgox, "USD", 700, ledger);
+
+ // Distribute funds.
+ pay(mtgox, alice, "USD", "70", ledger);
+ pay(mtgox, bob, "USD", "50", ledger);
+
+ verifyBalance(ledger, alice, Amount(70, "USD", mtgox));
+ verifyBalance(ledger, bob, Amount(50, "USD", mtgox));
+
+ // Find path from alice to mtgox
+ auto alternatives = findPath(ledger, alice, bob, { Currency("USD") },
+ Amount(5, "USD", mtgox), log.stream());
+ log << "Path find alternatives: " << alternatives;
+ expectEquals(alternatives.size(), 1);
+ auto alt = alternatives[0u];
+ expectEquals(alt["paths_canonical"].size(), 0);
+ expectEquals(alt["paths_computed"].size(), 0);
+ /*
+ // This block represents results old behavior. I'd like (me or someone else) to
+ // eventually write a test that returns data like this, and then this code can
+ // be moved/reused.
+
+ expectEquals(alt["paths_computed"].size(), 1);
+ auto computedPaths = alt["paths_computed"];
+ expectEquals(computedPaths.size(), 1);
+ auto computedPath = computedPaths[0u];
+ expectEquals(computedPath.size(), 1);
+ auto computedPathStep = computedPath[0u];
+ expectEquals(computedPathStep[jss::account],
+ mtgox.pk.humanAccountID());
+ expectEquals(computedPathStep[jss::type], 1);
+ expectEquals(computedPathStep[jss::type_hex],
+ "0000000000000001");
+ */
+ auto srcAmount = alt[jss::source_amount];
+ expectEquals(srcAmount[jss::currency], "USD");
+ expectEquals(srcAmount[jss::value], "5");
+ expectEquals(srcAmount[jss::issuer],
+ alice.pk.humanAccountID());
+ }
+
+
+public:
+ void run()
+ {
+ test_no_direct_path_no_intermediary_no_alternatives();
+ test_direct_path_no_intermediary();
+ test_payment_auto_path_find_using_build_path();
+ test_path_find();
+ }
+};
+
+BEAST_DEFINE_TESTSUITE(Path, app, ripple);
+
+} // test
+} // ripple
diff --git a/src/ripple/app/tests/common_ledger.cpp b/src/ripple/app/tests/common_ledger.cpp
index 58559436b1..f2bc27670e 100644
--- a/src/ripple/app/tests/common_ledger.cpp
+++ b/src/ripple/app/tests/common_ledger.cpp
@@ -20,26 +20,92 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include
#include
+#include
+#include
+#include
+#include
+
namespace ripple {
namespace test {
-Amount::Amount(double value_, std::string currency_, TestAccount issuer_)
- : value(value_)
- , currency(currency_)
- , issuer(issuer_)
+Json::Value
+TestJson::getJson() const
+{
+ Json::Value tx_json;
+ getJson(tx_json);
+ return tx_json;
+}
+
+Currency::Currency(std::string currency)
+ : currency_(currency)
{
}
-Json::Value
-Amount::getJson() const
+void
+Currency::getJson(Json::Value& tx_json) const
{
- Json::Value tx_json;
- tx_json["currency"] = currency;
- tx_json["issuer"] = issuer.pk.humanAccountID();
- tx_json["value"] = std::to_string(value);
- return tx_json;
+ tx_json[jss::currency] = currency_;
}
+std::string
+Currency::getCurrency() const
+{
+ return currency_;
+}
+
+
+Issuer::Issuer(TestAccount issuer)
+ :issuer_(issuer)
+{
+}
+
+void
+Issuer::getJson(Json::Value& tx_json) const
+{
+ tx_json[jss::issuer] = issuer_.pk.humanAccountID();
+}
+
+TestAccount const&
+Issuer::getAccount() const
+{
+ return issuer_;
+}
+
+
+Amount::Amount(double value, std::string currency, TestAccount issuer)
+ : value_(value)
+ , currency_(Currency(currency))
+ , issuer_(Issuer(issuer))
+{
+}
+
+void
+Amount::getJson(Json::Value& tx_json) const
+{
+ currency_.getJson(tx_json);
+ issuer_.getJson(tx_json);
+ tx_json[jss::value] = std::to_string(value_);
+}
+
+double
+Amount::getValue() const
+{
+ return value_;
+}
+
+TestAccount const&
+Amount::getIssuer() const
+{
+ return issuer_.getAccount();
+}
+
+Currency const&
+Amount::getCurrency() const
+{
+ return currency_;
+}
+
+
// Helper function to parse a transaction in Json, sign it with account,
// and return it as a STTx
STTx
@@ -75,9 +141,10 @@ applyTransaction(Ledger::pointer const& ledger, STTx const& tx, bool check)
// Create genesis ledger from a start amount in drops, and the public
// master RippleAddress
-Ledger::pointer
+std::pair
createGenesisLedger(std::uint64_t start_amount_drops, TestAccount const& master)
{
+ initializePathfinding();
Ledger::pointer ledger = std::make_shared(master.pk,
start_amount_drops);
ledger->updateHash();
@@ -85,7 +152,7 @@ createGenesisLedger(std::uint64_t start_amount_drops, TestAccount const& master)
if (!ledger->assertSane())
throw std::runtime_error(
"! ledger->assertSane()");
- return ledger;
+ return std::make_pair(std::move(ledger), std::make_shared(false, *ledger));
}
// Create an account represented by public RippleAddress and private
@@ -105,50 +172,125 @@ createAccount(std::string const& passphrase, KeyType keyType)
};
}
-void
-freezeAccount(TestAccount& account, Ledger::pointer const& ledger, bool sign)
+TestAccount
+createAndFundAccount(TestAccount& from, std::string const& passphrase,
+ KeyType keyType, std::uint64_t amountDrops,
+ Ledger::pointer const& ledger, bool sign)
+{
+ auto to = createAccount(passphrase, keyType);
+ pay(from, to, amountDrops, ledger, sign);
+ return to;
+}
+
+std::map
+createAndFundAccounts(TestAccount& from, std::vector passphrases,
+ KeyType keyType, std::uint64_t amountDrops,
+ Ledger::pointer const& ledger, bool sign)
+{
+ std::map accounts;
+ for (auto const& passphrase : passphrases)
+ {
+ auto to = createAndFundAccount(from, passphrase, keyType,
+ amountDrops, ledger, sign);
+ accounts.emplace(passphrase, std::move(to));
+ }
+ return accounts;
+}
+
+std::map
+createAndFundAccountsWithFlags(TestAccount& from,
+ std::vector passphrases,
+ KeyType keyType, std::uint64_t amountDrops,
+ Ledger::pointer& ledger,
+ Ledger::pointer& LCL,
+ const std::uint32_t flags, bool sign)
+{
+ auto accounts = createAndFundAccounts(from,
+ passphrases, keyType, amountDrops, ledger, sign);
+ close_and_advance(ledger, LCL);
+ setAllAccountFlags(accounts, ledger, flags);
+ close_and_advance(ledger, LCL);
+ return accounts;
+}
+
+Json::Value
+getCommonTransactionJson(TestAccount& account)
{
Json::Value tx_json;
- tx_json["TransactionType"] = "AccountSet";
- tx_json["Fee"] = std::to_string(10);
- tx_json["Account"] = account.pk.humanAccountID();
- tx_json["SetFlag"] = asfGlobalFreeze;
- tx_json["Sequence"] = ++account.sequence;
+ tx_json[jss::Account] = account.pk.humanAccountID();
+ tx_json[jss::Fee] = std::to_string(10);
+ tx_json[jss::Sequence] = ++account.sequence;
+ return tx_json;
+}
+
+void
+setAccountFlags(TestAccount& account, Ledger::pointer const& ledger,
+ const std::uint32_t flags, bool sign)
+{
+ Json::Value tx_json = getCommonTransactionJson(account);
+ tx_json[jss::TransactionType] = "AccountSet";
+ tx_json[jss::SetFlag] = flags;
STTx tx = parseTransaction(account, tx_json, sign);
applyTransaction(ledger, tx, sign);
}
void
-unfreezeAccount(TestAccount& account, Ledger::pointer const& ledger, bool sign)
+setAllAccountFlags(std::vector& accounts, Ledger::pointer const& ledger,
+const std::uint32_t flags, bool sign)
{
- Json::Value tx_json;
- tx_json["TransactionType"] = "AccountSet";
- tx_json["Fee"] = std::to_string(10);
- tx_json["Account"] = account.pk.humanAccountID();
- tx_json["ClearFlag"] = asfGlobalFreeze;
- tx_json["Sequence"] = ++account.sequence;
+ for (auto& account : accounts)
+ {
+ setAccountFlags(account, ledger, flags, sign);
+ }
+}
+
+void
+clearAccountFlags(TestAccount& account, Ledger::pointer const& ledger,
+ const std::uint32_t flags, bool sign)
+{
+ Json::Value tx_json = getCommonTransactionJson(account);
+ tx_json[jss::TransactionType] = "AccountSet";
+ tx_json[jss::ClearFlag] = flags;
STTx tx = parseTransaction(account, tx_json, sign);
applyTransaction(ledger, tx, sign);
}
+void
+freezeAccount(TestAccount& account, Ledger::pointer const& ledger, bool sign)
+{
+ setAccountFlags(account, ledger, asfGlobalFreeze, sign);
+}
+
+void
+unfreezeAccount(TestAccount& account, Ledger::pointer const& ledger, bool sign)
+{
+ clearAccountFlags(account, ledger, asfGlobalFreeze, sign);
+}
+
+Json::Value
+getPaymentJson(TestAccount& from, TestAccount const& to,
+ Json::Value amountJson)
+{
+ Json::Value tx_json = getCommonTransactionJson(from);
+ tx_json[jss::Amount] = amountJson;
+ tx_json[jss::Destination] = to.pk.humanAccountID();
+ tx_json[jss::TransactionType] = "Payment";
+ tx_json[jss::Flags] = tfUniversal;
+ return tx_json;
+}
+
STTx
getPaymentTx(TestAccount& from, TestAccount const& to,
std::uint64_t amountDrops,
bool sign)
{
- Json::Value tx_json;
- tx_json["Account"] = from.pk.humanAccountID();
- tx_json["Amount"] = std::to_string(amountDrops);
- tx_json["Destination"] = to.pk.humanAccountID();
- tx_json["TransactionType"] = "Payment";
- tx_json["Fee"] = std::to_string(10);
- tx_json["Sequence"] = ++from.sequence;
- tx_json["Flags"] = tfUniversal;
+ Json::Value tx_json = getPaymentJson(from, to,
+ std::to_string(amountDrops));
return parseTransaction(from, tx_json, sign);
}
STTx
-makeAndApplyPayment(TestAccount& from, TestAccount const& to,
+pay(TestAccount& from, TestAccount const& to,
std::uint64_t amountDrops,
Ledger::pointer const& ledger, bool sign)
{
@@ -162,19 +304,13 @@ getPaymentTx(TestAccount& from, TestAccount const& to,
std::string const& currency, std::string const& amount,
bool sign)
{
- Json::Value tx_json;
- tx_json["Account"] = from.pk.humanAccountID();
- tx_json["Amount"] = Amount(std::stod(amount), currency, to).getJson();
- tx_json["Destination"] = to.pk.humanAccountID();
- tx_json["TransactionType"] = "Payment";
- tx_json["Fee"] = std::to_string(10);
- tx_json["Sequence"] = ++from.sequence;
- tx_json["Flags"] = tfUniversal;
+ Json::Value tx_json = getPaymentJson(from, to,
+ Amount(std::stod(amount), currency, to).getJson());
return parseTransaction(from, tx_json, sign);
}
STTx
-makeAndApplyPayment(TestAccount& from, TestAccount const& to,
+pay(TestAccount& from, TestAccount const& to,
std::string const& currency, std::string const& amount,
Ledger::pointer const& ledger, bool sign)
{
@@ -183,17 +319,55 @@ makeAndApplyPayment(TestAccount& from, TestAccount const& to,
return tx;
}
+STTx
+getPaymentTxWithPath(TestAccount& from, TestAccount const& to,
+std::string const& currency, std::string const& amount,
+Ledger::pointer const& ledger, bool sign)
+{
+ auto amountJson = Amount(std::stod(amount), currency, to).getJson();
+ Json::Value tx_json = getPaymentJson(from, to, amountJson);
+
+ // Find path. Note that the sign command can do this transparently
+ // with the "build_path" field, but we don't have that here.
+ auto cache = std::make_shared(ledger);
+ STPathSet pathSet;
+ STPath fullLiquidityPath;
+ auto stDstAmount = amountFromJson(sfGeneric, amountJson);
+ Issue srcIssue = Issue(stDstAmount.getCurrency(), from.pk.getAccountID());
+
+ auto found = findPathsForOneIssuer(cache, from.pk.getAccountID(), to.pk.getAccountID(),
+ srcIssue, stDstAmount, 7, 4, pathSet, fullLiquidityPath);
+ if (!found)
+ throw std::runtime_error(
+ "!found");
+ if (pathSet.isDefault())
+ throw std::runtime_error(
+ "pathSet.isDefault()");
+
+ tx_json[jss::Paths] = pathSet.getJson(0);
+
+ return parseTransaction(from, tx_json, sign);
+}
+
+STTx
+payWithPath(TestAccount& from, TestAccount const& to,
+ std::string const& currency, std::string const& amount,
+ Ledger::pointer const& ledger, bool sign)
+{
+ auto tx = getPaymentTxWithPath(from, to, currency, amount, ledger, sign);
+ applyTransaction(ledger, tx, sign);
+ return tx;
+}
+
+
void
createOffer(TestAccount& from, Amount const& in, Amount const& out,
Ledger::pointer ledger, bool sign)
{
- Json::Value tx_json;
- tx_json["TransactionType"] = "OfferCreate";
- tx_json["Fee"] = std::to_string(10);
- tx_json["Account"] = from.pk.humanAccountID();
- tx_json["TakerPays"] = in.getJson();
- tx_json["TakerGets"] = out.getJson();
- tx_json["Sequence"] = ++from.sequence;
+ Json::Value tx_json = getCommonTransactionJson(from);
+ tx_json[jss::TransactionType] = "OfferCreate";
+ tx_json[jss::TakerPays] = in.getJson();
+ tx_json[jss::TakerGets] = out.getJson();
STTx tx = parseTransaction(from, tx_json, sign);
applyTransaction(ledger, tx, sign);
}
@@ -203,37 +377,32 @@ createOffer(TestAccount& from, Amount const& in, Amount const& out,
void
cancelOffer(TestAccount& from, Ledger::pointer ledger, bool sign)
{
- Json::Value tx_json;
- tx_json["TransactionType"] = "OfferCancel";
- tx_json["Fee"] = std::to_string(10);
- tx_json["Account"] = from.pk.humanAccountID();
- tx_json["OfferSequence"] = from.sequence;
- tx_json["Sequence"] = ++from.sequence;
+ auto seq = from.sequence;
+ Json::Value tx_json = getCommonTransactionJson(from);
+ tx_json[jss::TransactionType] = "OfferCancel";
+ tx_json[jss::OfferSequence] = seq;
STTx tx = parseTransaction(from, tx_json, sign);
applyTransaction(ledger, tx, sign);
}
void
-makeTrustSet(TestAccount& from, TestAccount const& issuer,
+trust(TestAccount& from, TestAccount const& issuer,
std::string const& currency, double amount,
Ledger::pointer const& ledger, bool sign)
{
- Json::Value tx_json;
- tx_json["Account"] = from.pk.humanAccountID();
- Json::Value& limitAmount = tx_json["LimitAmount"];
- limitAmount["currency"] = currency;
- limitAmount["issuer"] = issuer.pk.humanAccountID();
- limitAmount["value"] = std::to_string(amount);
- tx_json["TransactionType"] = "TrustSet";
- tx_json["Fee"] = std::to_string(10);
- tx_json["Sequence"] = ++from.sequence;
- tx_json["Flags"] = tfClearNoRipple;
+ Json::Value tx_json = getCommonTransactionJson(from);
+ Json::Value& limitAmount = tx_json[jss::LimitAmount];
+ limitAmount[jss::currency] = currency;
+ limitAmount[jss::issuer] = issuer.pk.humanAccountID();
+ limitAmount[jss::value] = std::to_string(amount);
+ tx_json[jss::TransactionType] = "TrustSet";
+ tx_json[jss::Flags] = 0; // tfClearNoRipple;
STTx tx = parseTransaction(from, tx_json, sign);
applyTransaction(ledger, tx, sign);
}
-Ledger::pointer
-close_and_advance(Ledger::pointer ledger, Ledger::pointer LCL)
+void
+close_and_advance(Ledger::pointer& ledger, Ledger::pointer& LCL)
{
std::shared_ptr set = ledger->peekTransactionMap();
CanonicalTXSet retriableTransactions(set->getHash());
@@ -255,9 +424,80 @@ close_and_advance(Ledger::pointer ledger, Ledger::pointer LCL)
int closeResolution = seconds(LEDGER_TIME_ACCURACY).count();
bool closeTimeCorrect = true;
newLCL->setAccepted(closeTime, closeResolution, closeTimeCorrect);
- return newLCL;
+
+ LCL = newLCL;
+ ledger = std::make_shared(false, *LCL);
}
+Json::Value findPath(Ledger::pointer ledger, TestAccount const& src,
+ TestAccount const& dest, std::vector srcCurrencies,
+ Amount const& dstAmount, beast::abstract_ostream& log,
+ boost::optional contextPaths)
+{
+ int const level = 8;
+
+ auto cache = std::make_shared(ledger);
+
+ STAmount saDstAmount;
+ if (!amountFromJsonNoThrow(saDstAmount, dstAmount.getJson()))
+ throw std::runtime_error(
+ "!amountFromJsonNoThrow(saDstAmount, dstAmount.getJson())");
+ log << "Dst amount: " << saDstAmount;
+
+ auto jvSrcCurrencies = Json::Value(Json::arrayValue);
+ for (auto const& srcCurrency : srcCurrencies)
+ {
+ jvSrcCurrencies.append(srcCurrency.getJson());
+ }
+ log << "Source currencies: " << jvSrcCurrencies;
+
+ auto result = ripplePathFind(cache, src.pk, dest.pk, saDstAmount,
+ ledger, jvSrcCurrencies, contextPaths, level);
+ if(!result.first)
+ throw std::runtime_error(
+ "ripplePathFind find failed");
+
+ return result.second;
+}
+
+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);
+}
+
+void
+verifyBalance(Ledger::pointer ledger, TestAccount const& account,
+ Amount const& amount)
+{
+ auto sle = getLedgerEntryRippleState(ledger, account,
+ amount.getIssuer(), amount.getCurrency());
+ if (!sle)
+ throw std::runtime_error(
+ "!sle");
+ STAmount amountReq;
+ amountFromJsonNoThrow(amountReq, amount.getJson());
+
+ auto high = sle->getFieldAmount(sfHighLimit);
+ auto balance = sle->getFieldAmount(sfBalance);
+ if (high.getIssuer() == account.pk.getAccountID())
+ {
+ balance.negate();
+ }
+ if (balance != amountReq)
+ throw std::runtime_error(
+ "balance != amountReq");
+}
}
}
\ No newline at end of file
diff --git a/src/ripple/app/tests/common_ledger.h b/src/ripple/app/tests/common_ledger.h
index 4b9933b752..bb443e3031 100644
--- a/src/ripple/app/tests/common_ledger.h
+++ b/src/ripple/app/tests/common_ledger.h
@@ -28,11 +28,14 @@
#include
#include
#include
+#include
#include
#include
#include
#include
+#include
#include
+#include
#include
#include
@@ -46,17 +49,69 @@ struct TestAccount
unsigned sequence;
};
-struct Amount
+struct TestJson
{
- Amount(double value_, std::string currency_, TestAccount issuer_);
-
Json::Value
getJson() const;
+ virtual void
+ getJson(Json::Value& tx_json) const = 0;
+};
+
+struct Currency : TestJson
+{
+ Currency(std::string currency);
+
+ void
+ getJson(Json::Value& tx_json) const override;
+
+ std::string
+ getCurrency() const;
+
+ using TestJson::getJson;
+
private:
- double value;
- std::string currency;
- TestAccount issuer;
+ std::string currency_;
+};
+
+struct Issuer : TestJson
+{
+ Issuer(TestAccount issuer);
+
+ void
+ getJson(Json::Value& tx_json) const override;
+
+ TestAccount const&
+ getAccount() const;
+
+ using TestJson::getJson;
+
+private:
+ TestAccount issuer_;
+};
+
+struct Amount : TestJson
+{
+ Amount(double value, std::string currency, TestAccount issuer);
+
+ void
+ getJson(Json::Value& tx_json) const override;
+
+ double
+ getValue() const;
+
+ TestAccount const&
+ getIssuer() const;
+
+ Currency const&
+ getCurrency() const;
+
+ using TestJson::getJson;
+
+private:
+ double value_;
+ Currency currency_;
+ Issuer issuer_;
};
// Helper function to parse a transaction in Json, sign it with account,
@@ -70,7 +125,7 @@ applyTransaction(Ledger::pointer const& ledger, STTx const& tx, bool check = tru
// Create genesis ledger from a start amount in drops, and the public
// master RippleAddress
-Ledger::pointer
+std::pair
createGenesisLedger(std::uint64_t start_amount_drops, TestAccount const& master);
// Create an account represented by public RippleAddress and private
@@ -78,6 +133,47 @@ createGenesisLedger(std::uint64_t start_amount_drops, TestAccount const& master)
TestAccount
createAccount(std::string const& passphrase, KeyType keyType);
+TestAccount
+createAndFundAccount(TestAccount& from, std::string const& passphrase,
+ KeyType keyType, std::uint64_t amountDrops,
+ Ledger::pointer const& ledger, bool sign = true);
+
+std::map
+createAndFundAccounts(TestAccount& from, std::vector passphrases,
+ KeyType keyType, std::uint64_t amountDrops,
+ Ledger::pointer const& ledger, bool sign = true);
+
+std::map
+createAndFundAccountsWithFlags(TestAccount& from,
+ std::vector passphrases,
+ KeyType keyType, std::uint64_t amountDrops,
+ Ledger::pointer& ledger,
+ Ledger::pointer& LCL,
+ const std::uint32_t flags, bool sign = true);
+
+void
+setAccountFlags(TestAccount& account, Ledger::pointer const& ledger,
+ const std::uint32_t flags, bool sign = true);
+
+void
+setAllAccountFlags(std::vector& accounts, Ledger::pointer const& ledger,
+ const std::uint32_t flags, bool sign = true);
+
+template
+void
+setAllAccountFlags(std::map& accounts, Ledger::pointer const& ledger,
+const std::uint32_t flags, bool sign = true)
+{
+ for (auto& pair : accounts)
+ {
+ setAccountFlags(pair.second, ledger, flags, sign);
+ }
+}
+
+void
+clearAccountFlags(TestAccount& account, Ledger::pointer const& ledger,
+ const std::uint32_t flags, bool sign = true);
+
void
freezeAccount(TestAccount& account, Ledger::pointer const& ledger, bool sign = true);
@@ -90,7 +186,7 @@ getPaymentTx(TestAccount& from, TestAccount const& to,
bool sign = true);
STTx
-makeAndApplyPayment(TestAccount& from, TestAccount const& to,
+pay(TestAccount& from, TestAccount const& to,
std::uint64_t amountDrops,
Ledger::pointer const& ledger, bool sign = true);
@@ -100,10 +196,20 @@ getPaymentTx(TestAccount& from, TestAccount const& to,
bool sign = true);
STTx
-makeAndApplyPayment(TestAccount& from, TestAccount const& to,
+pay(TestAccount& from, TestAccount const& to,
std::string const& currency, std::string const& amount,
Ledger::pointer const& ledger, bool sign = true);
+STTx
+getPaymentTxWithPath(TestAccount& from, TestAccount const& to,
+ std::string const& currency, std::string const& amount,
+ Ledger::pointer const& ledger, bool sign = true);
+
+STTx
+payWithPath(TestAccount& from, TestAccount const& to,
+ std::string const& currency, std::string const& amount,
+ Ledger::pointer const& ledger, bool sign = true);
+
void
createOffer(TestAccount& from, Amount const& in, Amount const& out,
Ledger::pointer ledger, bool sign = true);
@@ -114,12 +220,25 @@ void
cancelOffer(TestAccount& from, Ledger::pointer ledger, bool sign = true);
void
-makeTrustSet(TestAccount& from, TestAccount const& issuer,
+trust(TestAccount& from, TestAccount const& issuer,
std::string const& currency, double amount,
Ledger::pointer const& ledger, bool sign = true);
-Ledger::pointer
-close_and_advance(Ledger::pointer ledger, Ledger::pointer LCL);
+void
+close_and_advance(Ledger::pointer& ledger, Ledger::pointer& LCL);
+
+Json::Value findPath(Ledger::pointer ledger, TestAccount const& src,
+ TestAccount const& dest, std::vector srcCurrencies,
+ Amount const& dstAmount, beast::abstract_ostream& log,
+ boost::optional contextPaths = boost::none);
+
+SLE::pointer
+get_ledger_entry_ripple_state(Ledger::pointer ledger,
+ RippleAddress account1, RippleAddress account2,
+ Currency currency);
+
+void
+verifyBalance(Ledger::pointer ledger, TestAccount const& account, Amount const& amount);
} // test
} // ripple
diff --git a/src/ripple/basics/TestSuite.h b/src/ripple/basics/TestSuite.h
index 4896614f32..e3b1f8fb3a 100644
--- a/src/ripple/basics/TestSuite.h
+++ b/src/ripple/basics/TestSuite.h
@@ -46,6 +46,24 @@ public:
}
+ template
+ bool expectNotEquals(S actual, T expected, std::string const& message = "")
+ {
+ if (actual == expected)
+ {
+ std::stringstream ss;
+ if (!message.empty())
+ ss << message << "\n";
+ ss << "Actual: " << actual << "\n"
+ << "Expected anything but: " << expected;
+ fail(ss.str());
+ return false;
+ }
+ pass();
+ return true;
+
+ }
+
template
bool expectCollectionEquals (
Collection const& actual, Collection const& expected,
diff --git a/src/ripple/rpc/handlers/RipplePathFind.cpp b/src/ripple/rpc/handlers/RipplePathFind.cpp
index f7723542f5..9b255d330b 100644
--- a/src/ripple/rpc/handlers/RipplePathFind.cpp
+++ b/src/ripple/rpc/handlers/RipplePathFind.cpp
@@ -18,6 +18,7 @@
//==============================================================================
#include
+#include
#include
#include
#include
@@ -126,15 +127,7 @@ Json::Value doRipplePathFind (RPC::Context& context)
}
else
{
- auto currencies = accountSourceCurrencies (raSrc, cache, true);
- jvSrcCurrencies = Json::Value (Json::arrayValue);
-
- for (auto const& uCurrency: currencies)
- {
- Json::Value jvCurrency (Json::objectValue);
- jvCurrency[jss::currency] = to_string(uCurrency);
- jvSrcCurrencies.append (jvCurrency);
- }
+ jvSrcCurrencies = buildSrcCurrencies(raSrc, cache);
}
// Fill in currencies destination will accept
@@ -149,8 +142,6 @@ Json::Value doRipplePathFind (RPC::Context& context)
jvResult[jss::destination_currencies] = jvDestCur;
jvResult[jss::destination_account] = raDst.humanAccountID ();
- Json::Value jvArray (Json::arrayValue);
-
int level = getConfig().PATH_SEARCH_OLD;
if ((getConfig().PATH_SEARCH_MAX > level)
&& !getApp().getFeeTrack().isLoadedLocal())
@@ -166,158 +157,16 @@ Json::Value doRipplePathFind (RPC::Context& context)
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 (jss::currency)
- || !to_currency (
- uSrcCurrencyID, jvSource[jss::currency].asString ()))
- {
- WriteLog (lsINFO, RPCHandler) << "Bad currency.";
-
- return 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 rpcError (rpcSRC_ISR_MALFORMED);
- }
-
- STPathSet spsComputed;
- if (context.params.isMember(jss::paths))
- {
- Json::Value pathSet = Json::objectValue;
- pathSet[jss::Paths] = context.params[jss::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[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);
- }
- }
- }
+ auto contextPaths = context.params.isMember(jss::paths) ?
+ boost::optional(context.params[jss::paths]) :
+ boost::optional(boost::none);
+ auto pathFindResult = ripplePathFind(cache, raSrc, raDst, saDstAmount,
+ lpLedger, jvSrcCurrencies, contextPaths, level);
+ if (!pathFindResult.first)
+ return pathFindResult.second;
// Each alternative differs by source currency.
- jvResult[jss::alternatives] = jvArray;
+ jvResult[jss::alternatives] = pathFindResult.second;
}
WriteLog (lsDEBUG, RPCHandler)
@@ -327,4 +176,183 @@ Json::Value doRipplePathFind (RPC::Context& context)
return jvResult;
}
+Json::Value
+buildSrcCurrencies(RippleAddress const& raSrc, RippleLineCache::pointer const& cache)
+{
+ auto currencies = accountSourceCurrencies(raSrc, cache, true);
+ auto jvSrcCurrencies = Json::Value(Json::arrayValue);
+
+ for (auto const& uCurrency : currencies)
+ {
+ Json::Value jvCurrency(Json::objectValue);
+ jvCurrency[jss::currency] = to_string(uCurrency);
+ jvSrcCurrencies.append(jvCurrency);
+ }
+
+ return jvSrcCurrencies;
+}
+
+std::pair
+ripplePathFind(RippleLineCache::pointer const& cache,
+ RippleAddress const& raSrc, RippleAddress const& raDst,
+ STAmount const& saDstAmount, Ledger::pointer const& lpLedger,
+ Json::Value const& jvSrcCurrencies,
+ boost::optional 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);
+}
+
} // ripple
diff --git a/src/ripple/rpc/impl/RipplePathFind.h b/src/ripple/rpc/impl/RipplePathFind.h
new file mode 100644
index 0000000000..b2a56245f8
--- /dev/null
+++ b/src/ripple/rpc/impl/RipplePathFind.h
@@ -0,0 +1,39 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of rippled: https://github.com/ripple/rippled
+ Copyright (c) 2015 Ripple Labs Inc.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+//==============================================================================
+
+#ifndef RIPPLE_RPC_RIPPLEPATHFIND_H_INCLUDED
+#define RIPPLE_RPC_RIPPLEPATHFIND_H_INCLUDED
+
+#include
+#include
+
+namespace ripple {
+
+class RippleAddress;
+
+Json::Value
+buildSrcCurrencies(RippleAddress const& raSrc, RippleLineCache::pointer const& cache);
+
+std::pair
+ripplePathFind(RippleLineCache::pointer const& cache, RippleAddress const& raSrc, RippleAddress const& raDst,
+ STAmount const& saDstAmount, Ledger::pointer const& lpLedger, Json::Value const& jvSrcCurrencies, boost::optional const& contextPaths, int const& level);
+
+}
+
+#endif
\ No newline at end of file
diff --git a/src/ripple/unity/app3.cpp b/src/ripple/unity/app3.cpp
index ba6b58c254..2528ca9adb 100644
--- a/src/ripple/unity/app3.cpp
+++ b/src/ripple/unity/app3.cpp
@@ -24,3 +24,4 @@
#include
#include
+#include