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