Pathfinding unit tests:

* Refactor ripple path find to be more testable.
* Reimplements the first 4 tests from `tests\path-test.js`
* Verify balances in Ledger test.
This commit is contained in:
Edward Hennis
2015-02-19 19:03:52 -05:00
committed by Tom Ritchford
parent bb7d68b3b9
commit 35a8ce2349
10 changed files with 941 additions and 272 deletions

View File

@@ -1942,6 +1942,10 @@
</ClCompile>
<ClInclude Include="..\..\src\ripple\app\tests\common_ledger.h">
</ClInclude>
<ClCompile Include="..\..\src\ripple\app\tests\Path_test.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple\app\transactors\CancelOffer.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
@@ -3319,6 +3323,8 @@
</ClCompile>
<ClInclude Include="..\..\src\ripple\rpc\impl\ParseAccountIds.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\rpc\impl\RipplePathFind.h">
</ClInclude>
<ClCompile Include="..\..\src\ripple\rpc\impl\RPCHandler.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile>

View File

@@ -2535,6 +2535,9 @@
<ClInclude Include="..\..\src\ripple\app\tests\common_ledger.h">
<Filter>ripple\app\tests</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ripple\app\tests\Path_test.cpp">
<Filter>ripple\app\tests</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple\app\transactors\CancelOffer.cpp">
<Filter>ripple\app\transactors</Filter>
</ClCompile>
@@ -3888,6 +3891,9 @@
<ClInclude Include="..\..\src\ripple\rpc\impl\ParseAccountIds.h">
<Filter>ripple\rpc\impl</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\rpc\impl\RipplePathFind.h">
<Filter>ripple\rpc\impl</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ripple\rpc\impl\RPCHandler.cpp">
<Filter>ripple\rpc\impl</Filter>
</ClCompile>

View File

@@ -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<Ledger>(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<Ledger>(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<Ledger>(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<Ledger>(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<Ledger>(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<Ledger> (false, *LCL);
Ledger::pointer LCL;
Ledger::pointer ledger;
std::tie(LCL, ledger) = createGenesisLedger (100000 * xrp, master);
auto gw1 = createAccount ("gw1", keyType);

View File

@@ -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 <ripple/app/tests/common_ledger.h>
#include <ripple/crypto/KeyType.h>
#include <ripple/json/json_writer.h>
#include <ripple/basics/TestSuite.h>
// 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

View File

@@ -20,26 +20,92 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include <ripple/app/tests/common_ledger.h>
#include <ripple/protocol/RippleAddress.h>
#include <ripple/protocol/Indexes.h>
#include <ripple/app/paths/FindPaths.h>
#include <ripple/rpc/impl/RipplePathFind.h>
#include <ripple/json/json_writer.h>
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<Ledger::pointer, Ledger::pointer>
createGenesisLedger(std::uint64_t start_amount_drops, TestAccount const& master)
{
initializePathfinding();
Ledger::pointer ledger = std::make_shared<Ledger>(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<Ledger>(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<std::string, TestAccount>
createAndFundAccounts(TestAccount& from, std::vector<std::string> passphrases,
KeyType keyType, std::uint64_t amountDrops,
Ledger::pointer const& ledger, bool sign)
{
std::map<std::string, TestAccount> 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<std::string, TestAccount>
createAndFundAccountsWithFlags(TestAccount& from,
std::vector<std::string> 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<TestAccount>& 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<RippleLineCache>(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<SHAMap> 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<Ledger>(false, *LCL);
}
Json::Value findPath(Ledger::pointer ledger, TestAccount const& src,
TestAccount const& dest, std::vector<Currency> srcCurrencies,
Amount const& dstAmount, beast::abstract_ostream& log,
boost::optional<Json::Value> contextPaths)
{
int const level = 8;
auto cache = std::make_shared<RippleLineCache>(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");
}
}
}

View File

@@ -28,11 +28,14 @@
#include <ripple/basics/seconds_clock.h>
#include <ripple/crypto/KeyType.h>
#include <ripple/json/json_value.h>
#include <ripple/protocol/JsonFields.h>
#include <ripple/protocol/Indexes.h>
#include <ripple/protocol/RippleAddress.h>
#include <ripple/protocol/STParsedJSON.h>
#include <ripple/protocol/TxFlags.h>
#include <ripple/protocol/STLedgerEntry.h>
#include <beast/unit_test/suite.h>
#include <beast/streams/abstract_ostream.h>
#include <chrono>
#include <string>
@@ -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<Ledger::pointer, Ledger::pointer>
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<std::string, TestAccount>
createAndFundAccounts(TestAccount& from, std::vector<std::string> passphrases,
KeyType keyType, std::uint64_t amountDrops,
Ledger::pointer const& ledger, bool sign = true);
std::map<std::string, TestAccount>
createAndFundAccountsWithFlags(TestAccount& from,
std::vector<std::string> 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<TestAccount>& accounts, Ledger::pointer const& ledger,
const std::uint32_t flags, bool sign = true);
template<class key_t>
void
setAllAccountFlags(std::map<key_t, TestAccount>& 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<Currency> srcCurrencies,
Amount const& dstAmount, beast::abstract_ostream& log,
boost::optional<Json::Value> 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

View File

@@ -46,6 +46,24 @@ public:
}
template <class S, class T>
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 <class Collection>
bool expectCollectionEquals (
Collection const& actual, Collection const& expected,

View File

@@ -18,6 +18,7 @@
//==============================================================================
#include <BeastConfig.h>
#include <ripple/rpc/impl/RipplePathFind.h>
#include <ripple/app/paths/AccountCurrencies.h>
#include <ripple/app/paths/FindPaths.h>
#include <ripple/app/paths/RippleCalc.h>
@@ -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<Json::Value>(context.params[jss::paths]) :
boost::optional<Json::Value>(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<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);
}
} // ripple

View File

@@ -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 <ripple/app/paths/RippleLineCache.h>
#include <ripple/app/ledger/Ledger.h>
namespace ripple {
class RippleAddress;
Json::Value
buildSrcCurrencies(RippleAddress const& raSrc, RippleLineCache::pointer const& cache);
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);
}
#endif

View File

@@ -24,3 +24,4 @@
#include <ripple/app/tests/common_ledger.cpp>
#include <ripple/app/ledger/tests/Ledger_test.cpp>
#include <ripple/app/tests/Path_test.cpp>