mirror of
https://github.com/Xahau/xahaud.git
synced 2025-12-06 17:27:52 +00:00
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:
committed by
Tom Ritchford
parent
bb7d68b3b9
commit
35a8ce2349
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
212
src/ripple/app/tests/Path_test.cpp
Normal file
212
src/ripple/app/tests/Path_test.cpp
Normal 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
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
39
src/ripple/rpc/impl/RipplePathFind.h
Normal file
39
src/ripple/rpc/impl/RipplePathFind.h
Normal 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
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user