mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
Move tests to app/tests
This commit is contained in:
committed by
Nik Bougalis
parent
c3da2e1f03
commit
8aafebbb75
251
src/ripple/app/tests/AccountTxPaging.test.cpp
Normal file
251
src/ripple/app/tests/AccountTxPaging.test.cpp
Normal file
@@ -0,0 +1,251 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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/core/DatabaseCon.h>
|
||||
#include <ripple/app/misc/impl/AccountTxPaging.h>
|
||||
#include <ripple/protocol/types.h>
|
||||
#include <beast/cxx14/memory.h> // <memory>
|
||||
#include <beast/unit_test/suite.h>
|
||||
#include <cstdlib>
|
||||
#include <vector>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
struct AccountTxPaging_test : beast::unit_test::suite
|
||||
{
|
||||
std::unique_ptr<DatabaseCon> db_;
|
||||
NetworkOPs::AccountTxs txs_;
|
||||
AccountID account_;
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
std::string data_path;
|
||||
|
||||
if (auto const fixtures = std::getenv("TEST_FIXTURES"))
|
||||
data_path = fixtures;
|
||||
|
||||
if (data_path.empty ())
|
||||
{
|
||||
fail("The 'TEST_FIXTURES' environment variable is empty.");
|
||||
return;
|
||||
}
|
||||
|
||||
DatabaseCon::Setup dbConf;
|
||||
dbConf.dataDir = data_path + "/";
|
||||
|
||||
db_ = std::make_unique <DatabaseCon> (
|
||||
dbConf, "account-tx-transactions.db", nullptr, 0);
|
||||
|
||||
account_ = *parseBase58<AccountID>(
|
||||
"rfu6L5p3azwPzQZsbTafuVk884N9YoKvVG");
|
||||
|
||||
testAccountTxPaging();
|
||||
}
|
||||
|
||||
void
|
||||
checkToken (Json::Value const& token, int ledger, int sequence)
|
||||
{
|
||||
expect (token.isMember ("ledger"));
|
||||
expect (token["ledger"].asInt() == ledger);
|
||||
expect (token.isMember ("seq"));
|
||||
expect (token["seq"].asInt () == sequence);
|
||||
}
|
||||
|
||||
void
|
||||
checkTransaction (NetworkOPs::AccountTx const& tx, int ledger, int index)
|
||||
{
|
||||
expect (tx.second->getLgrSeq () == ledger);
|
||||
expect (tx.second->getIndex () == index);
|
||||
}
|
||||
|
||||
std::size_t
|
||||
next (
|
||||
int limit,
|
||||
bool forward,
|
||||
Json::Value& token,
|
||||
std::int32_t minLedger,
|
||||
std::int32_t maxLedger)
|
||||
{
|
||||
txs_.clear();
|
||||
|
||||
std::int32_t const page_length = 200;
|
||||
bool const admin = true;
|
||||
|
||||
auto& txs = txs_;
|
||||
|
||||
auto bound = [&txs](
|
||||
std::uint32_t ledger_index,
|
||||
std::string const& status,
|
||||
Blob const& rawTxn,
|
||||
Blob const& rawMeta)
|
||||
{
|
||||
convertBlobsToTxResult (txs, ledger_index, status, rawTxn, rawMeta);
|
||||
};
|
||||
|
||||
accountTxPage(*db_, [](std::uint32_t){}, bound, account_, minLedger,
|
||||
maxLedger, forward, token, limit, admin, page_length);
|
||||
|
||||
return txs_.size();
|
||||
}
|
||||
|
||||
void
|
||||
testAccountTxPaging ()
|
||||
{
|
||||
using namespace std::placeholders;
|
||||
|
||||
bool const forward = true;
|
||||
|
||||
std::int32_t min_ledger;
|
||||
std::int32_t max_ledger;
|
||||
Json::Value token;
|
||||
int limit;
|
||||
|
||||
// the supplied account-tx-transactions.db contains contains
|
||||
// transactions with the following ledger/sequence pairs.
|
||||
// 3|5
|
||||
// 4|4
|
||||
// 4|10
|
||||
// 5|4
|
||||
// 5|7
|
||||
// 6|1
|
||||
// 6|5
|
||||
// 6|6
|
||||
// 6|7
|
||||
// 6|8
|
||||
// 6|9
|
||||
// 6|10
|
||||
// 6|11
|
||||
|
||||
min_ledger = 2;
|
||||
max_ledger = 5;
|
||||
|
||||
{
|
||||
limit = 2;
|
||||
|
||||
expect (next(limit, forward, token, min_ledger, max_ledger) == 2);
|
||||
checkTransaction (txs_[0], 3, 5);
|
||||
checkTransaction (txs_[1], 4, 4);
|
||||
checkToken (token, 4, 10);
|
||||
|
||||
expect (next(limit, forward, token, min_ledger, max_ledger) == 2);
|
||||
checkTransaction (txs_[0], 4, 10);
|
||||
checkTransaction (txs_[1], 5, 4);
|
||||
checkToken (token, 5, 7);
|
||||
|
||||
expect (next(limit, forward, token, min_ledger, max_ledger) == 1);
|
||||
checkTransaction (txs_[0], 5, 7);
|
||||
|
||||
expect(! token["ledger"]);
|
||||
expect(! token["seq"]);
|
||||
}
|
||||
|
||||
token = Json::nullValue;
|
||||
|
||||
min_ledger = 3;
|
||||
max_ledger = 9;
|
||||
|
||||
{
|
||||
limit = 1;
|
||||
|
||||
expect (next(limit, forward, token, min_ledger, max_ledger) == 1);
|
||||
checkTransaction (txs_[0], 3, 5);
|
||||
checkToken (token, 4, 4);
|
||||
|
||||
expect(next(limit, forward, token, min_ledger, max_ledger) == 1);
|
||||
checkTransaction (txs_[0], 4, 4);
|
||||
checkToken (token, 4, 10);
|
||||
|
||||
expect (next(limit, forward, token, min_ledger, max_ledger) == 1);
|
||||
checkTransaction (txs_[0], 4, 10);
|
||||
checkToken (token, 5, 4);
|
||||
}
|
||||
|
||||
{
|
||||
limit = 3;
|
||||
|
||||
expect (next(limit, forward, token, min_ledger, max_ledger) == 3);
|
||||
checkTransaction (txs_[0], 5, 4);
|
||||
checkTransaction (txs_[1], 5, 7);
|
||||
checkTransaction (txs_[2], 6, 1);
|
||||
checkToken (token, 6, 5);
|
||||
|
||||
expect (next(limit, forward, token, min_ledger, max_ledger) == 3);
|
||||
checkTransaction (txs_[0], 6, 5);
|
||||
checkTransaction (txs_[1], 6, 6);
|
||||
checkTransaction (txs_[2], 6, 7);
|
||||
checkToken (token, 6, 8);
|
||||
|
||||
expect (next(limit, forward, token, min_ledger, max_ledger) == 3);
|
||||
checkTransaction (txs_[0], 6, 8);
|
||||
checkTransaction (txs_[1], 6, 9);
|
||||
checkTransaction (txs_[2], 6, 10);
|
||||
checkToken (token, 6, 11);
|
||||
|
||||
expect(next(limit, forward, token, min_ledger, max_ledger) == 1);
|
||||
checkTransaction (txs_[0], 6, 11);
|
||||
|
||||
expect(! token["ledger"]);
|
||||
expect(! token["seq"]);
|
||||
}
|
||||
|
||||
token = Json::nullValue;
|
||||
|
||||
{
|
||||
limit = 2;
|
||||
|
||||
expect (next(limit, ! forward, token, min_ledger, max_ledger) == 2);
|
||||
checkTransaction (txs_[0], 6, 11);
|
||||
checkTransaction (txs_[1], 6, 10);
|
||||
checkToken (token, 6, 9);
|
||||
|
||||
expect(next(limit, ! forward, token, min_ledger, max_ledger) == 2);
|
||||
checkTransaction (txs_[0], 6, 9);
|
||||
checkTransaction (txs_[1], 6, 8);
|
||||
checkToken (token, 6, 7);
|
||||
}
|
||||
|
||||
{
|
||||
limit = 3;
|
||||
|
||||
expect (next(limit, ! forward, token, min_ledger, max_ledger) == 3);
|
||||
checkTransaction (txs_[0], 6, 7);
|
||||
checkTransaction (txs_[1], 6, 6);
|
||||
checkTransaction (txs_[2], 6, 5);
|
||||
checkToken (token, 6, 1);
|
||||
|
||||
expect (next(limit, ! forward, token, min_ledger, max_ledger) == 3);
|
||||
checkTransaction (txs_[0], 6, 1);
|
||||
checkTransaction (txs_[1], 5, 7);
|
||||
checkTransaction (txs_[2], 5, 4);
|
||||
checkToken (token, 4, 10);
|
||||
|
||||
expect (next(limit, ! forward, token, min_ledger, max_ledger) == 3);
|
||||
checkTransaction (txs_[0], 4, 10);
|
||||
checkTransaction (txs_[1], 4, 4);
|
||||
checkTransaction (txs_[2], 3, 5);
|
||||
}
|
||||
|
||||
expect (! token["ledger"]);
|
||||
expect (! token["seq"]);
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE_MANUAL(AccountTxPaging,app,ripple);
|
||||
|
||||
}
|
||||
793
src/ripple/app/tests/AmendmentTable.test.cpp
Normal file
793
src/ripple/app/tests/AmendmentTable.test.cpp
Normal file
@@ -0,0 +1,793 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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 <BeastConfig.h>
|
||||
|
||||
#include <ripple/app/misc/AmendmentTable.h>
|
||||
#include <ripple/basics/BasicConfig.h>
|
||||
#include <ripple/basics/chrono.h>
|
||||
#include <ripple/basics/Log.h>
|
||||
#include <ripple/core/ConfigSections.h>
|
||||
#include <ripple/protocol/TxFlags.h>
|
||||
#include <beast/unit_test/suite.h>
|
||||
|
||||
namespace ripple
|
||||
{
|
||||
|
||||
class AmendmentTable_test final : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
using StringPairVec = std::vector<std::pair<std::string, std::string>>;
|
||||
|
||||
private:
|
||||
enum class TablePopulationAlgo
|
||||
{
|
||||
addInitial,
|
||||
addKnown
|
||||
};
|
||||
|
||||
// 204/256 about 80% (we round down because the implementation rounds up)
|
||||
static int const majorityFraction{204};
|
||||
|
||||
static void populateTable (AmendmentTable& table,
|
||||
std::vector<std::string> const& configLines)
|
||||
{
|
||||
Section section (SECTION_AMENDMENTS);
|
||||
section.append (configLines);
|
||||
table.addInitial (section);
|
||||
}
|
||||
|
||||
static std::vector<AmendmentName> getAmendmentNames (
|
||||
StringPairVec const& amendmentPairs)
|
||||
{
|
||||
std::vector<AmendmentName> amendmentNames;
|
||||
amendmentNames.reserve (amendmentPairs.size ());
|
||||
for (auto const& i : amendmentPairs)
|
||||
{
|
||||
amendmentNames.emplace_back (i.first, i.second);
|
||||
}
|
||||
return amendmentNames;
|
||||
}
|
||||
|
||||
std::vector<AmendmentName> populateTable (
|
||||
AmendmentTable& table,
|
||||
StringPairVec const& amendmentPairs,
|
||||
TablePopulationAlgo populationAlgo = TablePopulationAlgo::addKnown)
|
||||
{
|
||||
std::vector<AmendmentName> const amendmentNames (
|
||||
getAmendmentNames (amendmentPairs));
|
||||
switch (populationAlgo)
|
||||
{
|
||||
case TablePopulationAlgo::addKnown:
|
||||
for (auto const& i : amendmentNames)
|
||||
{
|
||||
table.addKnown (i);
|
||||
}
|
||||
break;
|
||||
case TablePopulationAlgo::addInitial:
|
||||
{
|
||||
std::vector<std::string> configLines;
|
||||
configLines.reserve (amendmentPairs.size ());
|
||||
for (auto const& i : amendmentPairs)
|
||||
{
|
||||
configLines.emplace_back (i.first + " " + i.second);
|
||||
}
|
||||
populateTable (table, configLines);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
fail ("Error in test case logic");
|
||||
}
|
||||
|
||||
return amendmentNames;
|
||||
}
|
||||
|
||||
static std::unique_ptr< AmendmentTable >
|
||||
makeTable (int w)
|
||||
{
|
||||
return make_AmendmentTable (
|
||||
weeks (w),
|
||||
majorityFraction,
|
||||
deprecatedLogs().journal("TestAmendmentTable"));
|
||||
};
|
||||
|
||||
// Create the amendments by string pairs instead of AmendmentNames
|
||||
// as this helps test the AmendmentNames class
|
||||
StringPairVec const m_knownAmendmentPairs;
|
||||
StringPairVec const m_unknownAmendmentPairs;
|
||||
|
||||
public:
|
||||
AmendmentTable_test ()
|
||||
: m_knownAmendmentPairs (
|
||||
{{"a49f90e7cddbcadfed8fc89ec4d02011", "Known1"},
|
||||
{"ca956ccabf25151a16d773171c485423", "Known2"},
|
||||
{"60dcd528f057711c5d26b57be28e23df", "Known3"},
|
||||
{"da956ccabf25151a16d773171c485423", "Known4"},
|
||||
{"70dcd528f057711c5d26b57be28e23df", "Known5"},
|
||||
{"70dcd528f057711c5d26b57be28e23d0", "Known6"}})
|
||||
, m_unknownAmendmentPairs (
|
||||
{{"a9f90e7cddbcadfed8fc89ec4d02011c", "Unknown1"},
|
||||
{"c956ccabf25151a16d773171c485423b", "Unknown2"},
|
||||
{"6dcd528f057711c5d26b57be28e23dfa", "Unknown3"}})
|
||||
{
|
||||
}
|
||||
|
||||
void testGet ()
|
||||
{
|
||||
testcase ("get");
|
||||
auto table (makeTable (2));
|
||||
std::vector<AmendmentName> const amendmentNames (
|
||||
populateTable (*table, m_knownAmendmentPairs));
|
||||
std::vector<AmendmentName> const unknownAmendmentNames (
|
||||
getAmendmentNames (m_unknownAmendmentPairs));
|
||||
for (auto const& i : amendmentNames)
|
||||
{
|
||||
expect (table->get (i.friendlyName ()) == i.id ());
|
||||
}
|
||||
|
||||
for (auto const& i : unknownAmendmentNames)
|
||||
{
|
||||
expect (table->get (i.friendlyName ()) == uint256 ());
|
||||
}
|
||||
}
|
||||
|
||||
void testAddInitialAddKnown ()
|
||||
{
|
||||
testcase ("addInitialAddKnown");
|
||||
|
||||
for (auto tablePopulationAlgo :
|
||||
{TablePopulationAlgo::addInitial, TablePopulationAlgo::addKnown})
|
||||
{
|
||||
{
|
||||
// test that the amendments we add are enabled and amendments we
|
||||
// didn't add are not enabled
|
||||
|
||||
auto table (makeTable (2));
|
||||
std::vector<AmendmentName> const amendmentNames (populateTable (
|
||||
*table, m_knownAmendmentPairs, tablePopulationAlgo));
|
||||
std::vector<AmendmentName> const unknownAmendmentNames (
|
||||
getAmendmentNames (m_unknownAmendmentPairs));
|
||||
|
||||
for (auto const& i : amendmentNames)
|
||||
{
|
||||
expect (table->isSupported (i.id ()));
|
||||
if (tablePopulationAlgo == TablePopulationAlgo::addInitial)
|
||||
expect (table->isEnabled (i.id ()));
|
||||
}
|
||||
|
||||
for (auto const& i : unknownAmendmentNames)
|
||||
{
|
||||
expect (!table->isSupported (i.id ()));
|
||||
expect (!table->isEnabled (i.id ()));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// check that we throw an exception on bad hex pairs
|
||||
StringPairVec const badHexPairs (
|
||||
{{"a9f90e7cddbcadfedm8fc89ec4d02011c", "BadHex1"},
|
||||
{"c956ccabf25151a16d77T3171c485423b", "BadHex2"},
|
||||
{"6dcd528f057711c5d2Z6b57be28e23dfa", "BadHex3"}});
|
||||
|
||||
// make sure each element throws
|
||||
for (auto const& i : badHexPairs)
|
||||
{
|
||||
StringPairVec v ({i});
|
||||
auto table (makeTable (2));
|
||||
try
|
||||
{
|
||||
populateTable (*table, v, tablePopulationAlgo);
|
||||
// line above should throw
|
||||
fail ("didn't throw");
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
pass ();
|
||||
}
|
||||
try
|
||||
{
|
||||
populateTable (
|
||||
*table, badHexPairs, tablePopulationAlgo);
|
||||
// line above should throw
|
||||
fail ("didn't throw");
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
pass ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// check that we thow on bad num tokens
|
||||
std::vector<std::string> const badNumTokensConfigLines (
|
||||
{"19f6d",
|
||||
"19fd6 bad friendly name"
|
||||
"9876 one two"});
|
||||
|
||||
// make sure each element throws
|
||||
for (auto const& i : badNumTokensConfigLines)
|
||||
{
|
||||
std::vector<std::string> v ({i});
|
||||
auto table (makeTable (2));
|
||||
try
|
||||
{
|
||||
populateTable (*table, v);
|
||||
// line above should throw
|
||||
fail ("didn't throw");
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
pass ();
|
||||
}
|
||||
try
|
||||
{
|
||||
populateTable (*table, badNumTokensConfigLines);
|
||||
// line above should throw
|
||||
fail ("didn't throw");
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
pass ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void testEnable ()
|
||||
{
|
||||
testcase ("enable");
|
||||
auto table (makeTable (2));
|
||||
std::vector<AmendmentName> const amendmentNames (
|
||||
populateTable (*table, m_knownAmendmentPairs));
|
||||
{
|
||||
// enable/disable tests
|
||||
for (auto const& i : amendmentNames)
|
||||
{
|
||||
auto id (i.id ());
|
||||
table->enable (id);
|
||||
expect (table->isEnabled (id));
|
||||
table->disable (id);
|
||||
expect (!table->isEnabled (id));
|
||||
table->enable (id);
|
||||
expect (table->isEnabled (id));
|
||||
}
|
||||
|
||||
std::vector<uint256> toEnable;
|
||||
for (auto const& i : amendmentNames)
|
||||
{
|
||||
auto id (i.id ());
|
||||
toEnable.emplace_back (id);
|
||||
table->disable (id);
|
||||
expect (!table->isEnabled (id));
|
||||
}
|
||||
table->setEnabled (toEnable);
|
||||
for (auto const& i : toEnable)
|
||||
{
|
||||
expect (table->isEnabled (i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
using ATSetter =
|
||||
void (AmendmentTable::*)(const std::vector<uint256>& amendments);
|
||||
using ATGetter = bool (AmendmentTable::*)(uint256 const& amendment);
|
||||
void testVectorSetUnset (ATSetter setter, ATGetter getter)
|
||||
{
|
||||
auto table (makeTable (2));
|
||||
// make pointer to ref syntax a little nicer
|
||||
auto& tableRef (*table);
|
||||
std::vector<AmendmentName> const amendmentNames (
|
||||
populateTable (tableRef, m_knownAmendmentPairs));
|
||||
|
||||
// they should all be set
|
||||
for (auto const& i : amendmentNames)
|
||||
{
|
||||
expect ((tableRef.*getter)(i.id ())); // i.e. "isSupported"
|
||||
}
|
||||
|
||||
{
|
||||
// only set every other amendment
|
||||
std::vector<uint256> toSet;
|
||||
toSet.reserve (amendmentNames.size ());
|
||||
for (int i = 0; i < amendmentNames.size (); ++i)
|
||||
{
|
||||
if (i % 2)
|
||||
{
|
||||
toSet.emplace_back (amendmentNames[i].id ());
|
||||
}
|
||||
}
|
||||
(tableRef.*setter)(toSet);
|
||||
for (int i = 0; i < amendmentNames.size (); ++i)
|
||||
{
|
||||
bool const shouldBeSet = i % 2;
|
||||
expect (shouldBeSet ==
|
||||
(tableRef.*getter)(
|
||||
amendmentNames[i].id ())); // i.e. "isSupported"
|
||||
}
|
||||
}
|
||||
}
|
||||
void testSupported ()
|
||||
{
|
||||
testcase ("supported");
|
||||
testVectorSetUnset (&AmendmentTable::setSupported,
|
||||
&AmendmentTable::isSupported);
|
||||
}
|
||||
void testEnabled ()
|
||||
{
|
||||
testcase ("enabled");
|
||||
testVectorSetUnset (&AmendmentTable::setEnabled,
|
||||
&AmendmentTable::isEnabled);
|
||||
}
|
||||
void testSupportedEnabled ()
|
||||
{
|
||||
// Check that supported/enabled aren't the same thing
|
||||
testcase ("supportedEnabled");
|
||||
auto table (makeTable (2));
|
||||
|
||||
std::vector<AmendmentName> const amendmentNames (
|
||||
populateTable (*table, m_knownAmendmentPairs));
|
||||
|
||||
{
|
||||
// support every even amendment
|
||||
// enable every odd amendment
|
||||
std::vector<uint256> toSupport;
|
||||
toSupport.reserve (amendmentNames.size ());
|
||||
std::vector<uint256> toEnable;
|
||||
toEnable.reserve (amendmentNames.size ());
|
||||
for (int i = 0; i < amendmentNames.size (); ++i)
|
||||
{
|
||||
if (i % 2)
|
||||
{
|
||||
toSupport.emplace_back (amendmentNames[i].id ());
|
||||
}
|
||||
else
|
||||
{
|
||||
toEnable.emplace_back (amendmentNames[i].id ());
|
||||
}
|
||||
}
|
||||
table->setEnabled (toEnable);
|
||||
table->setSupported (toSupport);
|
||||
for (int i = 0; i < amendmentNames.size (); ++i)
|
||||
{
|
||||
bool const shouldBeSupported = i % 2;
|
||||
bool const shouldBeEnabled = !(i % 2);
|
||||
expect (shouldBeEnabled ==
|
||||
(table->isEnabled (amendmentNames[i].id ())));
|
||||
expect (shouldBeSupported ==
|
||||
(table->isSupported (amendmentNames[i].id ())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector <RippleAddress> makeValidators (int num)
|
||||
{
|
||||
std::vector <RippleAddress> ret;
|
||||
ret.reserve (num);
|
||||
for (int i = 0; i < num; ++i)
|
||||
ret.push_back (RippleAddress::createNodePublic (
|
||||
RippleAddress::createSeedRandom ()));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static std::uint32_t weekTime (int w)
|
||||
{
|
||||
return w * (7*24*60*60);
|
||||
}
|
||||
|
||||
// Execute a pretend consensus round for a flag ledger
|
||||
void doRound
|
||||
( AmendmentTable& table
|
||||
, int week
|
||||
, std::vector <RippleAddress> const& validators
|
||||
, std::vector <std::pair <uint256, int> > const& votes
|
||||
, std::vector <uint256>& ourVotes
|
||||
, enabledAmendments_t& enabled
|
||||
, majorityAmendments_t& majority)
|
||||
{
|
||||
// Do a round at the specified time
|
||||
// Returns the amendments we voted for
|
||||
|
||||
// Parameters:
|
||||
// table: Our table of known and vetoed amendments
|
||||
// validators: The addreses of validators we trust
|
||||
// votes: Amendments and the number of validators who vote for them
|
||||
// ourVotes: The amendments we vote for in our validation
|
||||
// enabled: In/out enabled amendments
|
||||
// majority: In/our majority amendments (and when they got a majority)
|
||||
|
||||
std::uint32_t const roundTime = weekTime (week);
|
||||
|
||||
// Build validations
|
||||
ValidationSet validations;
|
||||
validations.reserve (validators.size ());
|
||||
|
||||
int i = 0;
|
||||
for (auto const& val : validators)
|
||||
{
|
||||
STValidation::pointer v =
|
||||
std::make_shared <STValidation>
|
||||
(uint256(), roundTime, val, true);
|
||||
|
||||
++i;
|
||||
STVector256 field (sfAmendments);
|
||||
|
||||
for (auto const& amendment : votes)
|
||||
{
|
||||
if ((256 * i) < (validators.size() * amendment.second))
|
||||
{
|
||||
// We vote yes on this amendment
|
||||
field.push_back (amendment.first);
|
||||
}
|
||||
}
|
||||
if (!field.empty ())
|
||||
v->setFieldV256 (sfAmendments, field);
|
||||
|
||||
v->setTrusted();
|
||||
validations [val.getNodeID()] = v;
|
||||
}
|
||||
|
||||
ourVotes = table.doValidation (enabled);
|
||||
|
||||
auto actions = table.doVoting (roundTime, enabled, majority, validations);
|
||||
for (auto const& action : actions)
|
||||
{
|
||||
// This code assumes other validators do as we do
|
||||
|
||||
auto const& hash = action.first;
|
||||
switch (action.second)
|
||||
{
|
||||
case 0:
|
||||
// amendment goes from majority to enabled
|
||||
if (enabled.find (hash) != enabled.end ())
|
||||
throw std::runtime_error ("enabling already enabled");
|
||||
if (majority.find (hash) == majority.end ())
|
||||
throw std::runtime_error ("enabling without majority");
|
||||
enabled.insert (hash);
|
||||
majority.erase (hash);
|
||||
break;
|
||||
|
||||
case tfGotMajority:
|
||||
if (majority.find (hash) != majority.end ())
|
||||
throw std::runtime_error ("got majority while having majority");
|
||||
majority[hash] = roundTime;
|
||||
break;
|
||||
|
||||
case tfLostMajority:
|
||||
if (majority.find (hash) == majority.end ())
|
||||
throw std::runtime_error ("lost majority without majority");
|
||||
majority.erase (hash);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert (false);
|
||||
throw std::runtime_error ("unknown action");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No vote on unknown amendment
|
||||
void testNoUnknown ()
|
||||
{
|
||||
testcase ("voteNoUnknown");
|
||||
|
||||
auto table (makeTable (2));
|
||||
|
||||
auto const validators = makeValidators (10);
|
||||
|
||||
uint256 testAmendment;
|
||||
testAmendment.SetHex("6dcd528f057711c5d26b57be28e23dfa");
|
||||
|
||||
std::vector <std::pair <uint256, int>> votes;
|
||||
std::vector <uint256> ourVotes;
|
||||
enabledAmendments_t enabled;
|
||||
majorityAmendments_t majority;
|
||||
|
||||
doRound (*table, 1,
|
||||
validators,
|
||||
votes,
|
||||
ourVotes,
|
||||
enabled,
|
||||
majority);
|
||||
expect (ourVotes.empty(), "Voted with nothing to vote on");
|
||||
expect (enabled.empty(), "Enabled amendment for no reason");
|
||||
expect (majority.empty(), "Majority found for no reason");
|
||||
|
||||
votes.emplace_back (testAmendment, 256);
|
||||
|
||||
doRound (*table, 2,
|
||||
validators,
|
||||
votes,
|
||||
ourVotes,
|
||||
enabled,
|
||||
majority);
|
||||
expect (ourVotes.empty(), "Voted on unknown because others did");
|
||||
expect (enabled.empty(), "Enabled amendment for no reason");
|
||||
|
||||
majority[testAmendment] = weekTime(1);
|
||||
|
||||
// Note that the simulation code assumes others behave as we do,
|
||||
// so the amendment won't get enabled
|
||||
doRound (*table, 5,
|
||||
validators,
|
||||
votes,
|
||||
ourVotes,
|
||||
enabled,
|
||||
majority);
|
||||
expect (ourVotes.empty(), "Voted on unknown because it had majority");
|
||||
expect (enabled.empty(), "Pseudo-transaction from nowhere");
|
||||
}
|
||||
|
||||
// No vote on vetoed amendment
|
||||
void testNoVetoed ()
|
||||
{
|
||||
testcase ("voteNoVetoed");
|
||||
|
||||
auto table (makeTable (2));
|
||||
|
||||
auto const validators = makeValidators (10);
|
||||
|
||||
uint256 testAmendment;
|
||||
testAmendment.SetHex("6dcd528f057711c5d26b57be28e23dfa");
|
||||
table->veto(testAmendment);
|
||||
|
||||
std::vector <std::pair <uint256, int>> votes;
|
||||
std::vector <uint256> ourVotes;
|
||||
enabledAmendments_t enabled;
|
||||
majorityAmendments_t majority;
|
||||
|
||||
doRound (*table, 1,
|
||||
validators,
|
||||
votes,
|
||||
ourVotes,
|
||||
enabled,
|
||||
majority);
|
||||
expect (ourVotes.empty(), "Voted with nothing to vote on");
|
||||
expect (enabled.empty(), "Enabled amendment for no reason");
|
||||
expect (majority.empty(), "Majority found for no reason");
|
||||
|
||||
votes.emplace_back (testAmendment, 256);
|
||||
|
||||
doRound (*table, 2,
|
||||
validators,
|
||||
votes,
|
||||
ourVotes,
|
||||
enabled,
|
||||
majority);
|
||||
expect (ourVotes.empty(), "Voted on vetoed amendment because others did");
|
||||
expect (enabled.empty(), "Enabled amendment for no reason");
|
||||
|
||||
majority[testAmendment] = weekTime(1);
|
||||
|
||||
doRound (*table, 5,
|
||||
validators,
|
||||
votes,
|
||||
ourVotes,
|
||||
enabled,
|
||||
majority);
|
||||
expect (ourVotes.empty(), "Voted on vetoed because it had majority");
|
||||
expect (enabled.empty(), "Enabled amendment for no reason");
|
||||
}
|
||||
|
||||
// Vote on and enable known, not-enabled amendment
|
||||
void testVoteEnable ()
|
||||
{
|
||||
testcase ("voteEnable");
|
||||
|
||||
auto table (makeTable (2));
|
||||
auto const amendmentNames (
|
||||
populateTable (*table, m_knownAmendmentPairs));
|
||||
|
||||
auto const validators = makeValidators (10);
|
||||
|
||||
std::vector <std::pair <uint256, int>> votes;
|
||||
std::vector <uint256> ourVotes;
|
||||
enabledAmendments_t enabled;
|
||||
majorityAmendments_t majority;
|
||||
|
||||
// Week 1: We should vote for all known amendments not enabled
|
||||
doRound (*table, 1,
|
||||
validators,
|
||||
votes,
|
||||
ourVotes,
|
||||
enabled,
|
||||
majority);
|
||||
expect (ourVotes.size() == amendmentNames.size(), "Did not vote");
|
||||
expect (enabled.empty(), "Enabled amendment for no reason");
|
||||
for (auto const& i : amendmentNames)
|
||||
expect(majority.find(i.id()) == majority.end(), "majority detected for no reaosn");
|
||||
|
||||
// Now, everyone votes for this feature
|
||||
for (auto const& i : amendmentNames)
|
||||
votes.emplace_back (i.id(), 256);
|
||||
|
||||
// Week 2: We should recognize a majority
|
||||
doRound (*table, 2,
|
||||
validators,
|
||||
votes,
|
||||
ourVotes,
|
||||
enabled,
|
||||
majority);
|
||||
expect (ourVotes.size() == amendmentNames.size(), "Did not vote");
|
||||
expect (enabled.empty(), "Enabled amendment for no reason");
|
||||
for (auto const& i : amendmentNames)
|
||||
expect (majority[i.id()] == weekTime(2), "majority not detected");
|
||||
|
||||
// Week 5: We should enable the amendment
|
||||
doRound (*table, 5,
|
||||
validators,
|
||||
votes,
|
||||
ourVotes,
|
||||
enabled,
|
||||
majority);
|
||||
expect (enabled.size() == amendmentNames.size(), "Did not enable");
|
||||
|
||||
// Week 6: We should remove it from our votes and from having a majority
|
||||
doRound (*table, 6,
|
||||
validators,
|
||||
votes,
|
||||
ourVotes,
|
||||
enabled,
|
||||
majority);
|
||||
expect (enabled.size() == amendmentNames.size(), "Disabled");
|
||||
expect (ourVotes.empty(), "Voted after enabling");
|
||||
for (auto const& i : amendmentNames)
|
||||
expect(majority.find(i.id()) == majority.end(), "majority not removed");
|
||||
}
|
||||
|
||||
// Detect majority at 80%, enable later
|
||||
void testDetectMajority ()
|
||||
{
|
||||
testcase ("detectMajority");
|
||||
auto table (makeTable (2));
|
||||
|
||||
uint256 testAmendment;
|
||||
testAmendment.SetHex("6dcd528f057711c5d26b57be28e23dfa");
|
||||
table->addKnown({testAmendment, "testAmendment"});
|
||||
|
||||
auto const validators = makeValidators (16);
|
||||
|
||||
enabledAmendments_t enabled;
|
||||
majorityAmendments_t majority;
|
||||
|
||||
for (int i = 0; i <= 17; ++i)
|
||||
{
|
||||
std::vector <std::pair <uint256, int>> votes;
|
||||
std::vector <uint256> ourVotes;
|
||||
|
||||
if ((i > 0) && (i < 17))
|
||||
votes.emplace_back (testAmendment, i * 16);
|
||||
|
||||
doRound (*table, i,
|
||||
validators, votes, ourVotes, enabled, majority);
|
||||
|
||||
if (i < 14)
|
||||
{
|
||||
// rounds 0-13
|
||||
// We are voting yes, not enabled, no majority
|
||||
expect (!ourVotes.empty(), "We aren't voting");
|
||||
expect (enabled.empty(), "Enabled too early");
|
||||
expect (majority.empty(), "Majority too early");
|
||||
}
|
||||
else if (i < 16)
|
||||
{
|
||||
// rounds 14 and 15
|
||||
// We have a majority, not enabled, keep voting
|
||||
expect (!ourVotes.empty(), "We stopped voting");
|
||||
expect (!majority.empty(), "Failed to detect majority");
|
||||
expect (enabled.empty(), "Enabled too early");
|
||||
}
|
||||
else if (i == 16) // round 16
|
||||
{
|
||||
// round 16
|
||||
// enable, keep voting, remove from majority
|
||||
expect (!ourVotes.empty(), "We stopped voting");
|
||||
expect (majority.empty(), "Failed to remove from majority");
|
||||
expect (!enabled.empty(), "Did not enable");
|
||||
}
|
||||
else
|
||||
{
|
||||
// round 17
|
||||
// Done, we should be enabled and not voting
|
||||
expect (ourVotes.empty(), "We did not stop voting");
|
||||
expect (majority.empty(), "Failed to revove from majority");
|
||||
expect (!enabled.empty(), "Did not enable");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Detect loss of majority
|
||||
void testLostMajority ()
|
||||
{
|
||||
testcase ("lostMajority");
|
||||
|
||||
auto table (makeTable (8));
|
||||
|
||||
uint256 testAmendment;
|
||||
testAmendment.SetHex("6dcd528f057711c5d26b57be28e23dfa");
|
||||
table->addKnown({testAmendment, "testAmendment"});
|
||||
|
||||
auto const validators = makeValidators (16);
|
||||
|
||||
enabledAmendments_t enabled;
|
||||
majorityAmendments_t majority;
|
||||
|
||||
{
|
||||
// establish majority
|
||||
std::vector <std::pair <uint256, int>> votes;
|
||||
std::vector <uint256> ourVotes;
|
||||
|
||||
votes.emplace_back (testAmendment, 250);
|
||||
|
||||
doRound (*table, 1,
|
||||
validators, votes, ourVotes, enabled, majority);
|
||||
|
||||
expect (enabled.empty(), "Enabled for no reason");
|
||||
expect (!majority.empty(), "Failed to detect majority");
|
||||
}
|
||||
|
||||
for (int i = 1; i < 16; ++i)
|
||||
{
|
||||
std::vector <std::pair <uint256, int>> votes;
|
||||
std::vector <uint256> ourVotes;
|
||||
|
||||
// Gradually reduce support
|
||||
votes.emplace_back (testAmendment, 256 - i * 8);
|
||||
|
||||
doRound (*table, i + 1,
|
||||
validators, votes, ourVotes, enabled, majority);
|
||||
|
||||
if (i < 6)
|
||||
{
|
||||
// rounds 1 to 5
|
||||
// We are voting yes, not enabled, majority
|
||||
expect (!ourVotes.empty(), "We aren't voting");
|
||||
expect (enabled.empty(), "Enabled for no reason");
|
||||
expect (!majority.empty(), "Lost majority too early");
|
||||
}
|
||||
else
|
||||
{
|
||||
// rounds 6 to 15
|
||||
// No majority, not enabled, keep voting
|
||||
expect (!ourVotes.empty(), "We stopped voting");
|
||||
expect (majority.empty(), "Failed to detect loss of majority");
|
||||
expect (enabled.empty(), "Enabled errneously");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void run ()
|
||||
{
|
||||
testGet ();
|
||||
testAddInitialAddKnown ();
|
||||
testEnable ();
|
||||
testSupported ();
|
||||
testSupportedEnabled ();
|
||||
testNoUnknown ();
|
||||
testNoVetoed ();
|
||||
testVoteEnable ();
|
||||
testDetectMajority ();
|
||||
testLostMajority ();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE (AmendmentTable, app, ripple);
|
||||
|
||||
} // ripple
|
||||
120
src/ripple/app/tests/DeliverMin.test.cpp
Normal file
120
src/ripple/app/tests/DeliverMin.test.cpp
Normal file
@@ -0,0 +1,120 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012-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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <BeastConfig.h>
|
||||
#include <ripple/test/jtx.h>
|
||||
#include <beast/unit_test/suite.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
class DeliverMin_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
void
|
||||
test_convert_all_of_an_asset()
|
||||
{
|
||||
testcase("Convert all of an asset using DeliverMin");
|
||||
|
||||
using namespace jtx;
|
||||
auto const gw = Account("gateway");
|
||||
auto const USD = gw["USD"];
|
||||
|
||||
{
|
||||
Env env(*this);
|
||||
env.fund(XRP(10000), "alice", "bob", "carol", gw);
|
||||
env.trust(USD(100), "alice", "bob", "carol");
|
||||
env(pay("alice", "bob", USD(10)), delivermin(USD(10)), ter(temBAD_AMOUNT));
|
||||
env(pay("alice", "bob", USD(10)), delivermin(USD(-5)),
|
||||
txflags(tfPartialPayment), ter(temBAD_AMOUNT));
|
||||
env(pay("alice", "bob", USD(10)), delivermin(XRP(5)),
|
||||
txflags(tfPartialPayment), ter(temBAD_AMOUNT));
|
||||
env(pay("alice", "bob", USD(10)),
|
||||
delivermin(Account("carol")["USD"](5)),
|
||||
txflags(tfPartialPayment), ter(temBAD_AMOUNT));
|
||||
env(pay("alice", "bob", USD(10)), delivermin(USD(15)),
|
||||
txflags(tfPartialPayment), ter(temBAD_AMOUNT));
|
||||
env(pay(gw, "carol", USD(50)));
|
||||
env(offer("carol", XRP(5), USD(5)));
|
||||
env(pay("alice", "bob", USD(10)), paths(XRP),
|
||||
delivermin(USD(7)), txflags(tfPartialPayment),
|
||||
sendmax(XRP(5)), ter(tecPATH_PARTIAL));
|
||||
env.require(balance("alice", XRP(9999.99999)));
|
||||
env.require(balance("bob", XRP(10000)));
|
||||
}
|
||||
|
||||
{
|
||||
Env env(*this);
|
||||
env.fund(XRP(10000), "alice", "bob", gw);
|
||||
env.trust(USD(1000), "alice", "bob");
|
||||
env(pay(gw, "bob", USD(100)));
|
||||
env(offer("bob", XRP(100), USD(100)));
|
||||
env(pay("alice", "alice", USD(10000)), paths(XRP),
|
||||
delivermin(USD(100)), txflags(tfPartialPayment),
|
||||
sendmax(XRP(100)));
|
||||
env.require(balance("alice", USD(100)));
|
||||
}
|
||||
|
||||
{
|
||||
Env env(*this);
|
||||
env.fund(XRP(10000), "alice", "bob", "carol", gw);
|
||||
env.trust(USD(1000), "bob", "carol");
|
||||
env(pay(gw, "bob", USD(200)));
|
||||
env(offer("bob", XRP(100), USD(100)));
|
||||
env(offer("bob", XRP(1000), USD(100)));
|
||||
env(offer("bob", XRP(10000), USD(100)));
|
||||
env(pay("alice", "carol", USD(10000)), paths(XRP),
|
||||
delivermin(USD(200)), txflags(tfPartialPayment),
|
||||
sendmax(XRP(1000)), ter(tecPATH_PARTIAL));
|
||||
env(pay("alice", "carol", USD(10000)), paths(XRP),
|
||||
delivermin(USD(200)), txflags(tfPartialPayment),
|
||||
sendmax(XRP(1100)));
|
||||
env.require(balance("bob", USD(0)));
|
||||
env.require(balance("carol", USD(200)));
|
||||
}
|
||||
|
||||
{
|
||||
Env env(*this);
|
||||
env.fund(XRP(10000), "alice", "bob", "carol", "dan", gw);
|
||||
env.trust(USD(1000), "bob", "carol", "dan");
|
||||
env(pay(gw, "bob", USD(100)));
|
||||
env(pay(gw, "dan", USD(100)));
|
||||
env(offer("bob", XRP(100), USD(100)));
|
||||
env(offer("bob", XRP(1000), USD(100)));
|
||||
env(offer("dan", XRP(100), USD(100)));
|
||||
env(pay("alice", "carol", USD(10000)), paths(XRP),
|
||||
delivermin(USD(200)), txflags(tfPartialPayment),
|
||||
sendmax(XRP(200)));
|
||||
env.require(balance("bob", USD(0)));
|
||||
env.require(balance("carol", USD(200)));
|
||||
env.require(balance("dan", USD(0)));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
run()
|
||||
{
|
||||
test_convert_all_of_an_asset();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(DeliverMin,app,ripple)
|
||||
|
||||
} // test
|
||||
} // ripple
|
||||
534
src/ripple/app/tests/MultiSign.test.cpp
Normal file
534
src/ripple/app/tests/MultiSign.test.cpp
Normal file
@@ -0,0 +1,534 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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 <BeastConfig.h>
|
||||
#include <ripple/protocol/JsonFields.h> // jss:: definitions
|
||||
#include <ripple/test/jtx.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
class MultiSign_test : public beast::unit_test::suite
|
||||
{
|
||||
// Unfunded accounts to use for phantom signing.
|
||||
jtx::Account const bogie {"bogie", KeyType::secp256k1};
|
||||
jtx::Account const demon {"demon", KeyType::ed25519};
|
||||
jtx::Account const ghost {"ghost", KeyType::secp256k1};
|
||||
jtx::Account const haunt {"haunt", KeyType::ed25519};
|
||||
jtx::Account const jinni {"jinni", KeyType::secp256k1};
|
||||
jtx::Account const phase {"phase", KeyType::ed25519};
|
||||
jtx::Account const shade {"shade", KeyType::secp256k1};
|
||||
jtx::Account const spook {"spook", KeyType::ed25519};
|
||||
|
||||
public:
|
||||
void test_noReserve()
|
||||
{
|
||||
using namespace jtx;
|
||||
Env env(*this);
|
||||
Account const alice {"alice", KeyType::secp256k1};
|
||||
|
||||
// Pay alice enough to meet the initial reserve, but not enough to
|
||||
// meet the reserve for a SignerListSet.
|
||||
env.fund(XRP(200), alice);
|
||||
env.close();
|
||||
env.require (owners (alice, 0));
|
||||
|
||||
{
|
||||
// Attach a signer list to alice. Should fail.
|
||||
Json::Value smallSigners = signers(alice, 1, { { bogie, 1 } });
|
||||
env(smallSigners, ter(tecINSUFFICIENT_RESERVE));
|
||||
env.close();
|
||||
env.require (owners (alice, 0));
|
||||
|
||||
// Fund alice enough to set the signer list, then attach signers.
|
||||
env(pay(env.master, alice, XRP(151)));
|
||||
env.close();
|
||||
env(smallSigners);
|
||||
env.close();
|
||||
env.require (owners (alice, 3));
|
||||
}
|
||||
{
|
||||
// Replace with the biggest possible signer list. Should fail.
|
||||
Json::Value bigSigners = signers(alice, 1, {
|
||||
{ bogie, 1 }, { demon, 1 }, { ghost, 1 }, { haunt, 1 },
|
||||
{ jinni, 1 }, { phase, 1 }, { shade, 1 }, { spook, 1 }});
|
||||
env(bigSigners, ter(tecINSUFFICIENT_RESERVE));
|
||||
env.close();
|
||||
env.require (owners (alice, 3));
|
||||
|
||||
// Fund alice and succeed.
|
||||
env(pay(env.master, alice, XRP(350)));
|
||||
env.close();
|
||||
env(bigSigners);
|
||||
env.close();
|
||||
env.require (owners (alice, 10));
|
||||
}
|
||||
// Remove alice's signer list and get the owner count back.
|
||||
env(signers(alice, jtx::none));
|
||||
env.close();
|
||||
env.require (owners (alice, 0));
|
||||
}
|
||||
|
||||
void test_signerListSet()
|
||||
{
|
||||
using namespace jtx;
|
||||
Env env(*this);
|
||||
Account const alice {"alice", KeyType::ed25519};
|
||||
env.fund(XRP(1000), alice);
|
||||
|
||||
// Add alice as a multisigner for herself. Should fail.
|
||||
env(signers(alice, 1, { { alice, 1} }), ter (temBAD_SIGNER));
|
||||
|
||||
// Add a signer with a weight of zero. Should fail.
|
||||
env(signers(alice, 1, { { bogie, 0} }), ter (temBAD_WEIGHT));
|
||||
|
||||
// Add a signer where the weight is too big. Should fail since
|
||||
// the weight field is only 16 bits. The jtx framework can't do
|
||||
// this kind of test, so it's commented out.
|
||||
// env(signers(alice, 1, { { bogie, 0x10000} }), ter (temBAD_WEIGHT));
|
||||
|
||||
// Add the same signer twice. Should fail.
|
||||
env(signers(alice, 1, {
|
||||
{ bogie, 1 }, { demon, 1 }, { ghost, 1 }, { haunt, 1 },
|
||||
{ jinni, 1 }, { phase, 1 }, { demon, 1 }, { spook, 1 }}),
|
||||
ter(temBAD_SIGNER));
|
||||
|
||||
// Set a quorum of zero. Should fail.
|
||||
env(signers(alice, 0, { { bogie, 1} }), ter (temMALFORMED));
|
||||
|
||||
// Make a signer list where the quorum can't be met. Should fail.
|
||||
env(signers(alice, 9, {
|
||||
{ bogie, 1 }, { demon, 1 }, { ghost, 1 }, { haunt, 1 },
|
||||
{ jinni, 1 }, { phase, 1 }, { shade, 1 }, { spook, 1 }}),
|
||||
ter(temBAD_QUORUM));
|
||||
|
||||
// Make a signer list that's too big. Should fail.
|
||||
Account const spare ("spare", KeyType::secp256k1);
|
||||
env(signers(alice, 1, {
|
||||
{ bogie, 1 }, { demon, 1 }, { ghost, 1 }, { haunt, 1 },
|
||||
{ jinni, 1 }, { phase, 1 }, { shade, 1 }, { spook, 1 },
|
||||
{ spare, 1 }}),
|
||||
ter(temMALFORMED));
|
||||
|
||||
env.close();
|
||||
env.require (owners (alice, 0));
|
||||
}
|
||||
|
||||
void test_phantomSigners()
|
||||
{
|
||||
using namespace jtx;
|
||||
Env env(*this);
|
||||
Account const alice {"alice", KeyType::ed25519};
|
||||
env.fund(XRP(1000), alice);
|
||||
env.close();
|
||||
|
||||
// Attach phantom signers to alice and use them for a transaction.
|
||||
env(signers(alice, 1, {{bogie, 1}, {demon, 1}}));
|
||||
env.close();
|
||||
env.require (owners (alice, 4));
|
||||
|
||||
// This should work.
|
||||
std::uint32_t aliceSeq = env.seq (alice);
|
||||
env(noop(alice), msig(bogie, demon));
|
||||
env.close();
|
||||
expect (env.seq(alice) == aliceSeq + 1);
|
||||
|
||||
// Either signer alone should work.
|
||||
aliceSeq = env.seq (alice);
|
||||
env(noop(alice), msig(bogie));
|
||||
env.close();
|
||||
expect (env.seq(alice) == aliceSeq + 1);
|
||||
|
||||
aliceSeq = env.seq (alice);
|
||||
env(noop(alice), msig(demon));
|
||||
env.close();
|
||||
expect (env.seq(alice) == aliceSeq + 1);
|
||||
|
||||
// Duplicate signers should fail.
|
||||
aliceSeq = env.seq (alice);
|
||||
env(noop(alice), msig(demon, demon), ter(temINVALID));
|
||||
env.close();
|
||||
expect (env.seq(alice) == aliceSeq);
|
||||
|
||||
// A non-signer should fail.
|
||||
aliceSeq = env.seq (alice);
|
||||
env(noop(alice), msig(bogie, spook), ter(tefBAD_SIGNATURE));
|
||||
env.close();
|
||||
expect (env.seq(alice) == aliceSeq);
|
||||
|
||||
// Multisign, but leave a nonempty sfSigners. Should fail.
|
||||
{
|
||||
aliceSeq = env.seq (alice);
|
||||
Json::Value multiSig = env.json (noop (alice), msig(bogie));
|
||||
|
||||
env (env.jt (multiSig), ter (temINVALID));
|
||||
env.close();
|
||||
expect (env.seq(alice) == aliceSeq);
|
||||
}
|
||||
|
||||
// Don't meet the quorum. Should fail.
|
||||
env(signers(alice, 2, {{bogie, 1}, {demon, 1}}));
|
||||
aliceSeq = env.seq (alice);
|
||||
env(noop(alice), msig(bogie), ter(tefBAD_QUORUM));
|
||||
env.close();
|
||||
expect (env.seq(alice) == aliceSeq);
|
||||
|
||||
// Meet the quorum. Should succeed.
|
||||
aliceSeq = env.seq (alice);
|
||||
env(noop(alice), msig(bogie, demon));
|
||||
env.close();
|
||||
expect (env.seq(alice) == aliceSeq + 1);
|
||||
}
|
||||
|
||||
void test_misorderedSigners()
|
||||
{
|
||||
using namespace jtx;
|
||||
Env env(*this);
|
||||
Account const alice {"alice", KeyType::ed25519};
|
||||
env.fund(XRP(1000), alice);
|
||||
env.close();
|
||||
|
||||
// The signatures in a transaction must be submitted in sorted order.
|
||||
// Make sure the transaction fails if they are not.
|
||||
env(signers(alice, 1, {{bogie, 1}, {demon, 1}}));
|
||||
env.close();
|
||||
env.require (owners(alice, 4));
|
||||
|
||||
msig phantoms {bogie, demon};
|
||||
std::reverse (phantoms.signers.begin(), phantoms.signers.end());
|
||||
std::uint32_t const aliceSeq = env.seq (alice);
|
||||
env(noop(alice), phantoms, ter(temINVALID));
|
||||
env.close();
|
||||
expect (env.seq(alice) == aliceSeq);
|
||||
}
|
||||
|
||||
void test_masterSigners()
|
||||
{
|
||||
using namespace jtx;
|
||||
Env env(*this);
|
||||
Account const alice {"alice", KeyType::ed25519};
|
||||
Account const becky {"becky", KeyType::secp256k1};
|
||||
Account const cheri {"cheri", KeyType::ed25519};
|
||||
env.fund(XRP(1000), alice, becky, cheri);
|
||||
env.close();
|
||||
|
||||
// For a different situation, give alice a regular key but don't use it.
|
||||
Account const alie {"alie", KeyType::secp256k1};
|
||||
env(regkey (alice, alie));
|
||||
env.close();
|
||||
std::uint32_t aliceSeq = env.seq (alice);
|
||||
env(noop(alice), sig(alice));
|
||||
env(noop(alice), sig(alie));
|
||||
env.close();
|
||||
expect (env.seq(alice) == aliceSeq + 2);
|
||||
|
||||
//Attach signers to alice
|
||||
env(signers(alice, 4, {{becky, 3}, {cheri, 4}}), sig (alice));
|
||||
env.close();
|
||||
env.require (owners (alice, 4));
|
||||
|
||||
// Attempt a multisigned transaction that meets the quorum.
|
||||
aliceSeq = env.seq (alice);
|
||||
env(noop(alice), msig(cheri));
|
||||
env.close();
|
||||
expect (env.seq(alice) == aliceSeq + 1);
|
||||
|
||||
// If we don't meet the quorum the transaction should fail.
|
||||
aliceSeq = env.seq (alice);
|
||||
env(noop(alice), msig(becky), ter(tefBAD_QUORUM));
|
||||
env.close();
|
||||
expect (env.seq(alice) == aliceSeq);
|
||||
|
||||
// Give becky and cheri regular keys.
|
||||
Account const beck {"beck", KeyType::ed25519};
|
||||
env(regkey (becky, beck));
|
||||
Account const cher {"cher", KeyType::ed25519};
|
||||
env(regkey (cheri, cher));
|
||||
env.close();
|
||||
|
||||
// becky's and cheri's master keys should still work.
|
||||
aliceSeq = env.seq (alice);
|
||||
env(noop(alice), msig(becky, cheri));
|
||||
env.close();
|
||||
expect (env.seq(alice) == aliceSeq + 1);
|
||||
}
|
||||
|
||||
void test_regularSigners()
|
||||
{
|
||||
using namespace jtx;
|
||||
Env env(*this);
|
||||
Account const alice {"alice", KeyType::secp256k1};
|
||||
Account const becky {"becky", KeyType::ed25519};
|
||||
Account const cheri {"cheri", KeyType::secp256k1};
|
||||
env.fund(XRP(1000), alice, becky, cheri);
|
||||
env.close();
|
||||
|
||||
// Attach signers to alice.
|
||||
env(signers(alice, 1, {{becky, 1}, {cheri, 1}}), sig (alice));
|
||||
|
||||
// Give everyone regular keys.
|
||||
Account const alie {"alie", KeyType::ed25519};
|
||||
env(regkey (alice, alie));
|
||||
Account const beck {"beck", KeyType::secp256k1};
|
||||
env(regkey (becky, beck));
|
||||
Account const cher {"cher", KeyType::ed25519};
|
||||
env(regkey (cheri, cher));
|
||||
env.close();
|
||||
|
||||
// Disable cheri's master key to mix things up.
|
||||
env(fset (cheri, asfDisableMaster), sig(cheri));
|
||||
env.close();
|
||||
|
||||
// Attempt a multisigned transaction that meets the quorum.
|
||||
std::uint32_t aliceSeq = env.seq (alice);
|
||||
env(noop(alice), msig(msig::Reg{cheri, cher}));
|
||||
env.close();
|
||||
expect (env.seq(alice) == aliceSeq + 1);
|
||||
|
||||
// cheri should not be able to multisign using her master key.
|
||||
aliceSeq = env.seq (alice);
|
||||
env(noop(alice), msig(cheri), ter(tefMASTER_DISABLED));
|
||||
env.close();
|
||||
expect (env.seq(alice) == aliceSeq);
|
||||
|
||||
// becky should be able to multisign using either of her keys.
|
||||
aliceSeq = env.seq (alice);
|
||||
env(noop(alice), msig(becky));
|
||||
env.close();
|
||||
expect (env.seq(alice) == aliceSeq + 1);
|
||||
|
||||
aliceSeq = env.seq (alice);
|
||||
env(noop(alice), msig(msig::Reg{becky, beck}));
|
||||
env.close();
|
||||
expect (env.seq(alice) == aliceSeq + 1);
|
||||
|
||||
// Both becky and cheri should be able to sign using regular keys.
|
||||
aliceSeq = env.seq (alice);
|
||||
env(noop(alice),
|
||||
msig(msig::Reg{becky, beck}, msig::Reg{cheri, cher}));
|
||||
env.close();
|
||||
expect (env.seq(alice) == aliceSeq + 1);
|
||||
|
||||
}
|
||||
|
||||
void test_heterogeneousSigners()
|
||||
{
|
||||
using namespace jtx;
|
||||
Env env(*this);
|
||||
Account const alice {"alice", KeyType::secp256k1};
|
||||
Account const becky {"becky", KeyType::ed25519};
|
||||
Account const cheri {"cheri", KeyType::secp256k1};
|
||||
Account const daria {"daria", KeyType::ed25519};
|
||||
env.fund(XRP(1000), alice, becky, cheri, daria);
|
||||
env.close();
|
||||
|
||||
// alice uses a regular key with the master disabled.
|
||||
Account const alie {"alie", KeyType::secp256k1};
|
||||
env(regkey (alice, alie));
|
||||
env(fset (alice, asfDisableMaster), sig(alice));
|
||||
|
||||
// becky is master only without a regular key.
|
||||
|
||||
// cheri has a regular key, but leaves the master key enabled.
|
||||
Account const cher {"cher", KeyType::secp256k1};
|
||||
env(regkey (cheri, cher));
|
||||
|
||||
// daria has a regular key and disables her master key.
|
||||
Account const dari {"dari", KeyType::ed25519};
|
||||
env(regkey (daria, dari));
|
||||
env(fset (daria, asfDisableMaster), sig(daria));
|
||||
env.close();
|
||||
|
||||
// Attach signers to alice.
|
||||
env(signers(alice, 1,
|
||||
{{becky, 1}, {cheri, 1}, {daria, 1}, {jinni, 1}}), sig (alie));
|
||||
env.close();
|
||||
env.require (owners (alice, 6));
|
||||
|
||||
// Each type of signer should succeed individually.
|
||||
std::uint32_t aliceSeq = env.seq (alice);
|
||||
env(noop(alice), msig(becky));
|
||||
env.close();
|
||||
expect (env.seq(alice) == aliceSeq + 1);
|
||||
|
||||
aliceSeq = env.seq (alice);
|
||||
env(noop(alice), msig(cheri));
|
||||
env.close();
|
||||
expect (env.seq(alice) == aliceSeq + 1);
|
||||
|
||||
aliceSeq = env.seq (alice);
|
||||
env(noop(alice), msig(msig::Reg{cheri, cher}));
|
||||
env.close();
|
||||
expect (env.seq(alice) == aliceSeq + 1);
|
||||
|
||||
aliceSeq = env.seq (alice);
|
||||
env(noop(alice), msig(msig::Reg{daria, dari}));
|
||||
env.close();
|
||||
expect (env.seq(alice) == aliceSeq + 1);
|
||||
|
||||
aliceSeq = env.seq (alice);
|
||||
env(noop(alice), msig(jinni));
|
||||
env.close();
|
||||
expect (env.seq(alice) == aliceSeq + 1);
|
||||
|
||||
// Should also work if all signers sign.
|
||||
aliceSeq = env.seq (alice);
|
||||
env(noop(alice),
|
||||
msig(becky, msig::Reg{cheri, cher}, msig::Reg{daria, dari}, jinni));
|
||||
env.close();
|
||||
expect (env.seq(alice) == aliceSeq + 1);
|
||||
|
||||
// Require all signers to sign.
|
||||
env(signers(alice, 0x3FFFC, {{becky, 0xFFFF},
|
||||
{cheri, 0xFFFF}, {daria, 0xFFFF}, {jinni, 0xFFFF}}), sig (alie));
|
||||
env.close();
|
||||
env.require (owners (alice, 6));
|
||||
|
||||
aliceSeq = env.seq (alice);
|
||||
env(noop(alice),
|
||||
msig(becky, msig::Reg{cheri, cher}, msig::Reg{daria, dari}, jinni));
|
||||
env.close();
|
||||
expect (env.seq(alice) == aliceSeq + 1);
|
||||
|
||||
// Try cheri with both key types.
|
||||
aliceSeq = env.seq (alice);
|
||||
env(noop(alice), msig(becky, cheri, msig::Reg{daria, dari}, jinni));
|
||||
env.close();
|
||||
expect (env.seq(alice) == aliceSeq + 1);
|
||||
|
||||
// Makes sure the maximum allowed number of signers works.
|
||||
env(signers(alice, 0x7FFF8, {{becky, 0xFFFF}, {cheri, 0xFFFF},
|
||||
{daria, 0xFFFF}, {haunt, 0xFFFF}, {jinni, 0xFFFF},
|
||||
{phase, 0xFFFF}, {shade, 0xFFFF}, {spook, 0xFFFF}}), sig (alie));
|
||||
env.close();
|
||||
env.require (owners (alice, 10));
|
||||
|
||||
aliceSeq = env.seq (alice);
|
||||
env(noop(alice), msig(becky, msig::Reg{cheri, cher},
|
||||
msig::Reg{daria, dari}, haunt, jinni, phase, shade, spook));
|
||||
env.close();
|
||||
expect (env.seq(alice) == aliceSeq + 1);
|
||||
|
||||
// One signer short should fail.
|
||||
aliceSeq = env.seq (alice);
|
||||
env(noop(alice), msig(becky, cheri, haunt, jinni, phase, shade, spook),
|
||||
ter (tefBAD_QUORUM));
|
||||
env.close();
|
||||
expect (env.seq(alice) == aliceSeq);
|
||||
|
||||
// Remove alice's signer list and get the owner count back.
|
||||
env(signers(alice, jtx::none), sig(alie));
|
||||
env.close();
|
||||
env.require (owners (alice, 0));
|
||||
}
|
||||
|
||||
// See if every kind of transaction can be successfully multi-signed.
|
||||
void test_txTypes()
|
||||
{
|
||||
using namespace jtx;
|
||||
Env env(*this);
|
||||
Account const alice {"alice", KeyType::secp256k1};
|
||||
Account const becky {"becky", KeyType::ed25519};
|
||||
Account const zelda {"zelda", KeyType::secp256k1};
|
||||
Account const gw {"gw"};
|
||||
auto const USD = gw["USD"];
|
||||
env.fund(XRP(1000), alice, becky, zelda, gw);
|
||||
env.close();
|
||||
|
||||
// alice uses a regular key with the master disabled.
|
||||
Account const alie {"alie", KeyType::secp256k1};
|
||||
env(regkey (alice, alie));
|
||||
env(fset (alice, asfDisableMaster), sig(alice));
|
||||
|
||||
// Attach signers to alice.
|
||||
env(signers(alice, 2, {{becky, 1}, {bogie, 1}}), sig (alie));
|
||||
env.close();
|
||||
env.require (owners (alice, 4));
|
||||
|
||||
// Multisign a ttPAYMENT.
|
||||
std::uint32_t aliceSeq = env.seq (alice);
|
||||
env(pay(alice, env.master, XRP(1)), msig(becky, bogie));
|
||||
env.close();
|
||||
expect (env.seq(alice) == aliceSeq + 1);
|
||||
|
||||
// Multisign a ttACCOUNT_SET.
|
||||
aliceSeq = env.seq (alice);
|
||||
env(noop(alice), msig(becky, bogie));
|
||||
env.close();
|
||||
expect (env.seq(alice) == aliceSeq + 1);
|
||||
|
||||
// Multisign a ttREGULAR_KEY_SET.
|
||||
aliceSeq = env.seq (alice);
|
||||
Account const ace {"ace", KeyType::secp256k1};
|
||||
env(regkey (alice, ace), msig(becky, bogie));
|
||||
env.close();
|
||||
expect (env.seq(alice) == aliceSeq + 1);
|
||||
|
||||
// Multisign a ttTRUST_SET
|
||||
env(trust("alice", USD(100)),
|
||||
msig(becky, bogie), require (lines("alice", 1)));
|
||||
env.close();
|
||||
env.require (owners (alice, 5));
|
||||
|
||||
// Multisign a ttOFFER_CREATE transaction.
|
||||
env(pay(gw, alice, USD(50)));
|
||||
env.close();
|
||||
env.require(balance(alice, USD(50)));
|
||||
env.require(balance(gw, alice["USD"](-50)));
|
||||
|
||||
std::uint32_t const offerSeq = env.seq (alice);
|
||||
env(offer(alice, XRP(50), USD(50)), msig (becky, bogie));
|
||||
env.close();
|
||||
env.require(owners(alice, 6));
|
||||
|
||||
// Now multisign a ttOFFER_CANCEL canceling the offer we just created.
|
||||
{
|
||||
aliceSeq = env.seq (alice);
|
||||
Json::Value cancelOffer;
|
||||
cancelOffer[jss::Account] = alice.human();
|
||||
cancelOffer[jss::OfferSequence] = offerSeq;
|
||||
cancelOffer[jss::TransactionType] = "OfferCancel";
|
||||
env (cancelOffer, seq (aliceSeq), msig (becky, bogie));
|
||||
env.close();
|
||||
expect (env.seq(alice) == aliceSeq + 1);
|
||||
env.require(owners(alice, 5));
|
||||
}
|
||||
|
||||
// Multisign a ttSIGNER_LIST_SET.
|
||||
env(signers(alice, 3, {{becky, 1}, {bogie, 1}, {demon, 1}}),
|
||||
msig (becky, bogie));
|
||||
env.close();
|
||||
env.require (owners (alice, 6));
|
||||
}
|
||||
|
||||
void run() override
|
||||
{
|
||||
test_noReserve();
|
||||
test_signerListSet();
|
||||
test_phantomSigners();
|
||||
test_misorderedSigners();
|
||||
test_masterSigners();
|
||||
test_regularSigners();
|
||||
test_heterogeneousSigners();
|
||||
test_txTypes();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(MultiSign, app, ripple);
|
||||
|
||||
} // test
|
||||
} // ripple
|
||||
156
src/ripple/app/tests/Offer.test.cpp
Normal file
156
src/ripple/app/tests/Offer.test.cpp
Normal file
@@ -0,0 +1,156 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012-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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <BeastConfig.h>
|
||||
#include <ripple/app/main/Application.h>
|
||||
#include <ripple/app/ledger/Ledger.h>
|
||||
#include <ripple/basics/Log.h>
|
||||
#include <ripple/test/jtx.h>
|
||||
#include <ripple/test/jtx/Account.h>
|
||||
#include <ripple/ledger/tests/PathSet.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
class Offer_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
void testRmFundedOffer ()
|
||||
{
|
||||
// We need at least two paths. One at good quality and one at bad quality.
|
||||
// The bad quality path needs two offer books in a row. Each offer book
|
||||
// should have two offers at the same quality, the offers should be
|
||||
// completely consumed, and the payment should should require both offers to
|
||||
// be satisified. The first offer must be "taker gets" XRP. Old, broken
|
||||
// would remove the first "taker gets" xrp offer, even though the offer is
|
||||
// still funded and not used for the payment.
|
||||
|
||||
// Mon Aug 17 11:00:00am PDT
|
||||
static NetClock::time_point const switchoverTime (
|
||||
std::chrono::seconds (493149600));
|
||||
|
||||
for(int i=0; i<2; ++i)
|
||||
{
|
||||
using namespace jtx;
|
||||
Env env (*this);
|
||||
|
||||
auto const tp = switchoverTime + std::chrono::seconds (i);
|
||||
bool const enableFix = tp > switchoverTime;
|
||||
expect (enableFix == bool(i));
|
||||
|
||||
// ledger close times have a dynamic resolution depending on network
|
||||
// conditions it appears the resolution in test is 10 seconds
|
||||
env.close (tp);
|
||||
|
||||
NetClock::time_point const pct (
|
||||
std::chrono::seconds (env.open ()->info ().parentCloseTime));
|
||||
if (enableFix)
|
||||
expect (pct > switchoverTime);
|
||||
else
|
||||
expect (pct <= switchoverTime);
|
||||
|
||||
auto const gw = Account ("gateway");
|
||||
auto const USD = gw["USD"];
|
||||
auto const BTC = gw["BTC"];
|
||||
Account const alice ("alice");
|
||||
Account const bob ("bob");
|
||||
Account const carol ("carol");
|
||||
|
||||
env.fund (XRP (10000), alice, bob, carol, gw);
|
||||
env.trust (USD (1000), alice, bob, carol);
|
||||
env.trust (BTC (1000), alice, bob, carol);
|
||||
|
||||
env (pay (gw, alice, BTC (1000)));
|
||||
|
||||
env (pay (gw, carol, USD (1000)));
|
||||
env (pay (gw, carol, BTC (1000)));
|
||||
|
||||
// Must be two offers at the same quality
|
||||
// "taker gets" must be XRP
|
||||
// (Different amounts so I can distinguish the offers)
|
||||
env (offer (carol, BTC (49), XRP (49)));
|
||||
env (offer (carol, BTC (51), XRP (51)));
|
||||
|
||||
// Offers for the poor quality path
|
||||
// Must be two offers at the same quality
|
||||
env (offer (carol, XRP (50), USD (50)));
|
||||
env (offer (carol, XRP (50), USD (50)));
|
||||
|
||||
// Offers for the good quality path
|
||||
env (offer (carol, BTC (1), USD (100)));
|
||||
|
||||
PathSet paths (Path (XRP, USD), Path (USD));
|
||||
|
||||
env (pay ("alice", "bob", USD (100)), json (paths.json ()),
|
||||
sendmax (BTC (1000)), txflags (tfPartialPayment));
|
||||
|
||||
require (balance ("bob", USD (100)));
|
||||
if (enableFix)
|
||||
expect (!isOffer (env, "carol", BTC (1), USD (100)) &&
|
||||
isOffer (env, "carol", BTC (49), XRP (49)));
|
||||
else
|
||||
expect (!isOffer (env, "carol", BTC (1), USD (100)) &&
|
||||
!isOffer (env, "carol", BTC (49), XRP (49)));
|
||||
}
|
||||
}
|
||||
void testCanceledOffer ()
|
||||
{
|
||||
using namespace jtx;
|
||||
Env env (*this);
|
||||
auto const gw = Account ("gateway");
|
||||
auto const USD = gw["USD"];
|
||||
|
||||
env.fund (XRP (10000), "alice", gw);
|
||||
env.trust (USD (100), "alice");
|
||||
|
||||
env (pay (gw, "alice", USD (50)));
|
||||
|
||||
auto const firstOfferSeq = env.seq ("alice");
|
||||
Json::StaticString const osKey ("OfferSequence");
|
||||
|
||||
env (offer ("alice", XRP (500), USD (100)),
|
||||
require (offers ("alice", 1)));
|
||||
|
||||
expect (isOffer (env, "alice", XRP (500), USD (100)));
|
||||
|
||||
// cancel the offer above and replace it with a new offer
|
||||
env (offer ("alice", XRP (300), USD (100)), json (osKey, firstOfferSeq),
|
||||
require (offers ("alice", 1)));
|
||||
|
||||
expect (isOffer (env, "alice", XRP (300), USD (100)) &&
|
||||
!isOffer (env, "alice", XRP (500), USD (100)));
|
||||
|
||||
// Test canceling non-existant offer.
|
||||
env (offer ("alice", XRP (400), USD (200)), json (osKey, firstOfferSeq),
|
||||
require (offers ("alice", 2)));
|
||||
|
||||
expect (isOffer (env, "alice", XRP (300), USD (100)) &&
|
||||
isOffer (env, "alice", XRP (400), USD (200)));
|
||||
}
|
||||
void run ()
|
||||
{
|
||||
testCanceledOffer ();
|
||||
testRmFundedOffer ();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE (Offer, tx, ripple)
|
||||
|
||||
} // test
|
||||
} // ripple
|
||||
44
src/ripple/app/tests/OfferStream.test.cpp
Normal file
44
src/ripple/app/tests/OfferStream.test.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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 <BeastConfig.h>
|
||||
#include <ripple/app/tx/impl/OfferStream.h>
|
||||
#include <beast/unit_test/suite.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
class OfferStream_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
void
|
||||
test()
|
||||
{
|
||||
pass();
|
||||
}
|
||||
|
||||
void
|
||||
run()
|
||||
{
|
||||
test();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE_MANUAL(OfferStream,tx,ripple);
|
||||
|
||||
}
|
||||
590
src/ripple/app/tests/Path_test.cpp
Normal file
590
src/ripple/app/tests/Path_test.cpp
Normal file
@@ -0,0 +1,590 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012-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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <BeastConfig.h>
|
||||
#include <ripple/test/jtx.h>
|
||||
#include <ripple/json/json_reader.h>
|
||||
#include <ripple/json/to_string.h>
|
||||
#include <ripple/protocol/TxFlags.h>
|
||||
#include <ripple/rpc/RipplePathFind.h>
|
||||
#include <ripple/basics/Log.h>
|
||||
#include <beast/unit_test/suite.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
using namespace jtx;
|
||||
|
||||
class Path_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
Json::Value
|
||||
findPath (std::shared_ptr<ReadView const> const& view,
|
||||
Account const& src, Account const& dest,
|
||||
std::vector<Issue> const& srcIssues,
|
||||
STAmount const& saDstAmount)
|
||||
{
|
||||
auto jvSrcCurrencies = Json::Value(Json::arrayValue);
|
||||
for (auto const& i : srcIssues)
|
||||
{
|
||||
STAmount const a = STAmount(i, 0);
|
||||
jvSrcCurrencies.append(a.getJson(0));
|
||||
}
|
||||
|
||||
int const level = 8;
|
||||
auto result = ripplePathFind(
|
||||
std::make_shared<RippleLineCache>(view),
|
||||
src.id(), dest.id(), saDstAmount,
|
||||
jvSrcCurrencies, boost::none, level);
|
||||
if(!result.first)
|
||||
throw std::runtime_error(
|
||||
"Path_test::findPath: ripplePathFind find failed");
|
||||
|
||||
return result.second;
|
||||
}
|
||||
|
||||
void
|
||||
test_no_direct_path_no_intermediary_no_alternatives()
|
||||
{
|
||||
testcase("no direct path no intermediary no alternatives");
|
||||
Env env(*this);
|
||||
env.fund(XRP(10000), "alice", "bob");
|
||||
|
||||
auto const alternatives = findPath(env.open(), "alice", "bob",
|
||||
{Account("alice")["USD"]}, Account("bob")["USD"](5));
|
||||
expect(alternatives.size() == 0);
|
||||
}
|
||||
|
||||
void
|
||||
test_direct_path_no_intermediary()
|
||||
{
|
||||
testcase("direct path no intermediary");
|
||||
Env env(*this);
|
||||
env.fund(XRP(10000), "alice", "bob");
|
||||
env.trust(Account("alice")["USD"](700), "bob");
|
||||
|
||||
auto const alternatives = findPath(env.open(), "alice", "bob",
|
||||
{Account("alice")["USD"]}, Account("bob")["USD"](5));
|
||||
Json::Value jv;
|
||||
Json::Reader().parse(R"([{
|
||||
"paths_canonical" : [],
|
||||
"paths_computed" : [],
|
||||
"source_amount" :
|
||||
{
|
||||
"currency" : "USD",
|
||||
"issuer" : "rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn",
|
||||
"value" : "5"
|
||||
}
|
||||
}])", jv);
|
||||
expect(jv == alternatives);
|
||||
}
|
||||
|
||||
void
|
||||
test_payment_auto_path_find()
|
||||
{
|
||||
testcase("payment auto path find");
|
||||
Env env(*this);
|
||||
auto const gw = Account("gateway");
|
||||
auto const USD = gw["USD"];
|
||||
env.fund(XRP(10000), "alice", "bob", gw);
|
||||
env.trust(USD(600), "alice");
|
||||
env.trust(USD(700), "bob");
|
||||
env(pay(gw, "alice", USD(70)));
|
||||
env(pay("alice", "bob", USD(24)));
|
||||
env.require(balance("alice", USD(46)));
|
||||
env.require(balance(gw, Account("alice")["USD"](-46)));
|
||||
env.require(balance("bob", USD(24)));
|
||||
env.require(balance(gw, Account("bob")["USD"](-24)));
|
||||
}
|
||||
|
||||
void
|
||||
test_path_find()
|
||||
{
|
||||
testcase("path find");
|
||||
Env env(*this);
|
||||
auto const gw = Account("gateway");
|
||||
auto const USD = gw["USD"];
|
||||
env.fund(XRP(10000), "alice", "bob", gw);
|
||||
env.trust(USD(600), "alice");
|
||||
env.trust(USD(700), "bob");
|
||||
env(pay(gw, "alice", USD(70)));
|
||||
env(pay(gw, "bob", USD(50)));
|
||||
|
||||
auto const alternatives = findPath(env.open(), "alice", "bob",
|
||||
{USD}, Account("bob")["USD"](5));
|
||||
Json::Value jv;
|
||||
Json::Reader().parse(R"([{
|
||||
"paths_canonical" : [],
|
||||
"paths_computed" : [],
|
||||
"source_amount" :
|
||||
{
|
||||
"currency" : "USD",
|
||||
"issuer" : "r9QxhA9RghPZBbUchA9HkrmLKaWvkLXU29",
|
||||
"value" : "5"
|
||||
}
|
||||
}])", jv);
|
||||
expect(jv == alternatives);
|
||||
}
|
||||
|
||||
void
|
||||
test_path_find_consume_all()
|
||||
{
|
||||
testcase("path find consume all");
|
||||
Env env(*this);
|
||||
auto const gw = Account("gateway");
|
||||
auto const USD = gw["USD"];
|
||||
env.fund(XRP(10000), "alice", "bob", gw);
|
||||
env.trust(Account("alice")["USD"](600), gw);
|
||||
env.trust(USD(700), "bob");
|
||||
|
||||
auto const alternatives = findPath(env.open(), "alice", "bob",
|
||||
{USD}, Account("bob")["USD"](1));
|
||||
Json::Value jv;
|
||||
Json::Reader().parse(R"([{
|
||||
"paths_canonical" : [],
|
||||
"paths_computed" : [],
|
||||
"source_amount" :
|
||||
{
|
||||
"currency" : "USD",
|
||||
"issuer" : "r9QxhA9RghPZBbUchA9HkrmLKaWvkLXU29",
|
||||
"value" : "1"
|
||||
}
|
||||
}])", jv);
|
||||
expect(jv == alternatives);
|
||||
}
|
||||
|
||||
void
|
||||
test_alternative_path_consume_both()
|
||||
{
|
||||
testcase("alternative path consume both");
|
||||
Env env(*this);
|
||||
auto const gw = Account("gateway");
|
||||
auto const USD = gw["USD"];
|
||||
auto const gw2 = Account("gateway2");
|
||||
auto const gw2_USD = gw2["USD"];
|
||||
env.fund(XRP(10000), "alice", "bob", gw, gw2);
|
||||
env.trust(USD(600), "alice");
|
||||
env.trust(gw2_USD(800), "alice");
|
||||
env.trust(USD(700), "bob");
|
||||
env.trust(gw2_USD(900), "bob");
|
||||
env(pay(gw, "alice", USD(70)));
|
||||
env(pay(gw2, "alice", gw2_USD(70)));
|
||||
env(pay("alice", "bob", Account("bob")["USD"](140)),
|
||||
paths(Account("alice")["USD"]));
|
||||
env.require(balance("alice", USD(0)));
|
||||
env.require(balance("alice", gw2_USD(0)));
|
||||
env.require(balance("bob", USD(70)));
|
||||
env.require(balance("bob", gw2_USD(70)));
|
||||
env.require(balance(gw, Account("alice")["USD"](0)));
|
||||
env.require(balance(gw, Account("bob")["USD"](-70)));
|
||||
env.require(balance(gw2, Account("alice")["USD"](0)));
|
||||
env.require(balance(gw2, Account("bob")["USD"](-70)));
|
||||
}
|
||||
|
||||
void
|
||||
test_alternative_paths_consume_best_transfer()
|
||||
{
|
||||
testcase("alternative paths consume best transfer");
|
||||
Env env(*this);
|
||||
auto const gw = Account("gateway");
|
||||
auto const USD = gw["USD"];
|
||||
auto const gw2 = Account("gateway2");
|
||||
auto const gw2_USD = gw2["USD"];
|
||||
env.fund(XRP(10000), "alice", "bob", gw, gw2);
|
||||
env(rate(gw2, 1.1));
|
||||
env.trust(USD(600), "alice");
|
||||
env.trust(gw2_USD(800), "alice");
|
||||
env.trust(USD(700), "bob");
|
||||
env.trust(gw2_USD(900), "bob");
|
||||
env(pay(gw, "alice", USD(70)));
|
||||
env(pay(gw2, "alice", gw2_USD(70)));
|
||||
env(pay("alice", "bob", USD(70)));
|
||||
env.require(balance("alice", USD(0)));
|
||||
env.require(balance("alice", gw2_USD(70)));
|
||||
env.require(balance("bob", USD(70)));
|
||||
env.require(balance("bob", gw2_USD(0)));
|
||||
env.require(balance(gw, Account("alice")["USD"](0)));
|
||||
env.require(balance(gw, Account("bob")["USD"](-70)));
|
||||
env.require(balance(gw2, Account("alice")["USD"](-70)));
|
||||
env.require(balance(gw2, Account("bob")["USD"](0)));
|
||||
}
|
||||
|
||||
void
|
||||
test_alternative_paths_consume_best_transfer_first()
|
||||
{
|
||||
testcase("alternative paths - consume best transfer first");
|
||||
Env env(*this);
|
||||
auto const gw = Account("gateway");
|
||||
auto const USD = gw["USD"];
|
||||
auto const gw2 = Account("gateway2");
|
||||
auto const gw2_USD = gw2["USD"];
|
||||
env.fund(XRP(10000), "alice", "bob", gw, gw2);
|
||||
env(rate(gw2, 1.1));
|
||||
env.trust(USD(600), "alice");
|
||||
env.trust(gw2_USD(800), "alice");
|
||||
env.trust(USD(700), "bob");
|
||||
env.trust(gw2_USD(900), "bob");
|
||||
env(pay(gw, "alice", USD(70)));
|
||||
env(pay(gw2, "alice", gw2_USD(70)));
|
||||
env(pay("alice", "bob", Account("bob")["USD"](77)),
|
||||
sendmax(Account("alice")["USD"](100)),
|
||||
paths(Account("alice")["USD"]));
|
||||
env.require(balance("alice", USD(0)));
|
||||
env.require(balance("alice", gw2_USD(62.3)));
|
||||
env.require(balance("bob", USD(70)));
|
||||
env.require(balance("bob", gw2_USD(7)));
|
||||
env.require(balance(gw, Account("alice")["USD"](0)));
|
||||
env.require(balance(gw, Account("bob")["USD"](-70)));
|
||||
env.require(balance(gw2, Account("alice")["USD"](-62.3)));
|
||||
env.require(balance(gw2, Account("bob")["USD"](-7)));
|
||||
}
|
||||
|
||||
void
|
||||
test_alternative_paths_limit_returned_paths_to_best_quality()
|
||||
{
|
||||
testcase("alternative paths - limit returned paths to best quality");
|
||||
Env env(*this);
|
||||
auto const gw = Account("gateway");
|
||||
auto const USD = gw["USD"];
|
||||
auto const gw2 = Account("gateway2");
|
||||
auto const gw2_USD = gw2["USD"];
|
||||
env.fund(XRP(10000), "alice", "bob", "carol", "dan", gw, gw2);
|
||||
env(rate("carol", 1.1));
|
||||
env.trust(Account("carol")["USD"](800), "alice", "bob");
|
||||
env.trust(Account("dan")["USD"](800), "alice", "bob");
|
||||
env.trust(USD(800), "alice", "bob");
|
||||
env.trust(gw2_USD(800), "alice", "bob");
|
||||
env.trust(Account("alice")["USD"](800), "dan");
|
||||
env.trust(Account("bob")["USD"](800), "dan");
|
||||
env(pay(gw2, "alice", gw2_USD(100)));
|
||||
env(pay("carol", "alice", Account("carol")["USD"](100)));
|
||||
env(pay(gw, "alice", USD(100)));
|
||||
|
||||
auto const alternatives = findPath(env.open(), "alice", "bob",
|
||||
{USD}, Account("bob")["USD"](5));
|
||||
Json::Value jv;
|
||||
Json::Reader().parse(R"([{
|
||||
"paths_canonical" : [],
|
||||
"paths_computed" : [],
|
||||
"source_amount" :
|
||||
{
|
||||
"currency" : "USD",
|
||||
"issuer" : "r9QxhA9RghPZBbUchA9HkrmLKaWvkLXU29",
|
||||
"value" : "5"
|
||||
}
|
||||
}])", jv);
|
||||
expect(jv == alternatives);
|
||||
}
|
||||
|
||||
void
|
||||
test_issues_path_negative_issue()
|
||||
{
|
||||
testcase("path negative: Issue #5");
|
||||
Env env(*this);
|
||||
env.fund(XRP(10000), "alice", "bob", "carol", "dan");
|
||||
env.trust(Account("bob")["USD"](100), "alice", "carol", "dan");
|
||||
env.trust(Account("alice")["USD"](100), "dan");
|
||||
env.trust(Account("carol")["USD"](100), "dan");
|
||||
env(pay("bob", "carol", Account("bob")["USD"](75)));
|
||||
env.require(balance("bob", Account("carol")["USD"](-75)));
|
||||
env.require(balance("carol", Account("bob")["USD"](75)));
|
||||
|
||||
auto alternatives = findPath(env.open(), "alice", "bob",
|
||||
{Account("alice")["USD"]}, Account("bob")["USD"](25));
|
||||
expect(alternatives.size() == 0);
|
||||
|
||||
env(pay("alice", "bob", Account("alice")["USD"](25)),
|
||||
ter(tecPATH_DRY));
|
||||
|
||||
alternatives = findPath(env.open(), "alice", "bob",
|
||||
{Account("alice")["USD"]}, Account("alice")["USD"](25));
|
||||
expect(alternatives.size() == 0);
|
||||
|
||||
env.require(balance("alice", Account("bob")["USD"](0)));
|
||||
env.require(balance("alice", Account("dan")["USD"](0)));
|
||||
env.require(balance("bob", Account("alice")["USD"](0)));
|
||||
env.require(balance("bob", Account("carol")["USD"](-75)));
|
||||
env.require(balance("bob", Account("dan")["USD"](0)));
|
||||
env.require(balance("carol", Account("bob")["USD"](75)));
|
||||
env.require(balance("carol", Account("dan")["USD"](0)));
|
||||
env.require(balance("dan", Account("alice")["USD"](0)));
|
||||
env.require(balance("dan", Account("bob")["USD"](0)));
|
||||
env.require(balance("dan", Account("carol")["USD"](0)));
|
||||
}
|
||||
|
||||
// alice -- limit 40 --> bob
|
||||
// alice --> carol --> dan --> bob
|
||||
// Balance of 100 USD Bob - Balance of 37 USD -> Rod
|
||||
void
|
||||
test_issues_path_negative_ripple_client_issue_23_smaller()
|
||||
{
|
||||
testcase("path negative: ripple-client issue #23: smaller");
|
||||
Env env(*this);
|
||||
env.fund(XRP(10000), "alice", "bob", "carol", "dan");
|
||||
env.trust(Account("alice")["USD"](40), "bob");
|
||||
env.trust(Account("dan")["USD"](20), "bob");
|
||||
env.trust(Account("alice")["USD"](20), "carol");
|
||||
env.trust(Account("carol")["USD"](20), "dan");
|
||||
env(pay("alice", "bob", Account("bob")["USD"](55)),
|
||||
paths(Account("alice")["USD"]));
|
||||
env.require(balance("bob", Account("alice")["USD"](40)));
|
||||
env.require(balance("bob", Account("dan")["USD"](15)));
|
||||
}
|
||||
|
||||
// alice -120 USD-> edward -25 USD-> bob
|
||||
// alice -25 USD-> carol -75 USD -> dan -100 USD-> bob
|
||||
void
|
||||
test_issues_path_negative_ripple_client_issue_23_larger()
|
||||
{
|
||||
testcase("path negative: ripple-client issue #23: larger");
|
||||
Env env(*this);
|
||||
env.fund(XRP(10000), "alice", "bob", "carol", "dan", "edward");
|
||||
env.trust(Account("alice")["USD"](120), "edward");
|
||||
env.trust(Account("edward")["USD"](25), "bob");
|
||||
env.trust(Account("dan")["USD"](100), "bob");
|
||||
env.trust(Account("alice")["USD"](25), "carol");
|
||||
env.trust(Account("carol")["USD"](75), "dan");
|
||||
env(pay("alice", "bob", Account("bob")["USD"](50)),
|
||||
paths(Account("alice")["USD"]));
|
||||
env.require(balance("alice", Account("edward")["USD"](-25)));
|
||||
env.require(balance("alice", Account("carol")["USD"](-25)));
|
||||
env.require(balance("bob", Account("edward")["USD"](25)));
|
||||
env.require(balance("bob", Account("dan")["USD"](25)));
|
||||
env.require(balance("carol", Account("alice")["USD"](25)));
|
||||
env.require(balance("carol", Account("dan")["USD"](-25)));
|
||||
env.require(balance("dan", Account("carol")["USD"](25)));
|
||||
env.require(balance("dan", Account("bob")["USD"](-25)));
|
||||
}
|
||||
|
||||
// carol holds gateway AUD, sells gateway AUD for XRP
|
||||
// bob will hold gateway AUD
|
||||
// alice pays bob gateway AUD using XRP
|
||||
void
|
||||
test_via_offers_via_gateway()
|
||||
{
|
||||
testcase("via gateway");
|
||||
Env env(*this);
|
||||
auto const gw = Account("gateway");
|
||||
auto const AUD = gw["AUD"];
|
||||
env.fund(XRP(10000), "alice", "bob", "carol", gw);
|
||||
env(rate(gw, 1.1));
|
||||
env.trust(AUD(100), "bob");
|
||||
env.trust(AUD(100), "carol");
|
||||
env(pay(gw, "carol", AUD(50)));
|
||||
env(offer("carol", XRP(50), AUD(50)));
|
||||
env(pay("alice", "bob", AUD(10)), sendmax(XRP(100)), paths(XRP));
|
||||
env.require(balance("bob", AUD(10)));
|
||||
env.require(balance("carol", AUD(39)));
|
||||
|
||||
auto const alternatives = findPath(env.open(), "alice", "bob",
|
||||
{Account("alice")["USD"]}, Account("bob")["USD"](25));
|
||||
expect(alternatives.size() == 0);
|
||||
}
|
||||
|
||||
void
|
||||
test_indirect_paths_path_find()
|
||||
{
|
||||
testcase("path find");
|
||||
Env env(*this);
|
||||
env.fund(XRP(10000), "alice", "bob", "carol");
|
||||
env.trust(Account("alice")["USD"](1000), "bob");
|
||||
env.trust(Account("bob")["USD"](1000), "carol");
|
||||
|
||||
auto const alternatives = findPath(env.open(), "alice", "carol",
|
||||
{Account("alice")["USD"]}, Account("carol")["USD"](5));
|
||||
Json::Value jv;
|
||||
Json::Reader().parse(R"([{
|
||||
"paths_canonical" : [],
|
||||
"paths_computed" :
|
||||
[
|
||||
[
|
||||
{
|
||||
"account" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
|
||||
"type" : 1,
|
||||
"type_hex" : "0000000000000001"
|
||||
}
|
||||
]
|
||||
],
|
||||
"source_amount" :
|
||||
{
|
||||
"currency" : "USD",
|
||||
"issuer" : "rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn",
|
||||
"value" : "5"
|
||||
}
|
||||
}])", jv);
|
||||
expect(jv == alternatives);
|
||||
}
|
||||
|
||||
void
|
||||
test_quality_paths_quality_set_and_test()
|
||||
{
|
||||
testcase("quality set and test");
|
||||
Env env(*this);
|
||||
env.fund(XRP(10000), "alice", "bob");
|
||||
env(trust("bob", Account("alice")["USD"](1000)),
|
||||
json("{\"" + sfQualityIn.fieldName + "\": 2000}"),
|
||||
json("{\"" + sfQualityOut.fieldName + "\": 1400000000}"));
|
||||
|
||||
Json::Value jv;
|
||||
Json::Reader().parse(R"({
|
||||
"Balance" : {
|
||||
"currency" : "USD",
|
||||
"issuer" : "rrrrrrrrrrrrrrrrrrrrBZbvji",
|
||||
"value" : "0"
|
||||
},
|
||||
"Flags" : 131072,
|
||||
"HighLimit" : {
|
||||
"currency" : "USD",
|
||||
"issuer" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
|
||||
"value" : "1000"
|
||||
},
|
||||
"HighNode" : "0000000000000000",
|
||||
"HighQualityIn" : 2000,
|
||||
"HighQualityOut" : 1400000000,
|
||||
"LedgerEntryType" : "RippleState",
|
||||
"LowLimit" : {
|
||||
"currency" : "USD",
|
||||
"issuer" : "rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn",
|
||||
"value" : "0"
|
||||
},
|
||||
"LowNode" : "0000000000000000"
|
||||
})", jv);
|
||||
|
||||
auto const jv_l = env.le(keylet::line(Account("bob").id(),
|
||||
Account("alice")["USD"].issue()))->getJson(0);
|
||||
for (auto it = jv.begin(); it != jv.end(); ++it)
|
||||
expect(*it == jv_l[it.memberName()]);
|
||||
}
|
||||
|
||||
void
|
||||
test_trust_auto_clear_trust_normal_clear()
|
||||
{
|
||||
testcase("trust normal clear");
|
||||
Env env(*this);
|
||||
env.fund(XRP(10000), "alice", "bob");
|
||||
env.trust(Account("bob")["USD"](1000), "alice");
|
||||
env.trust(Account("alice")["USD"](1000), "bob");
|
||||
|
||||
Json::Value jv;
|
||||
Json::Reader().parse(R"({
|
||||
"Balance" : {
|
||||
"currency" : "USD",
|
||||
"issuer" : "rrrrrrrrrrrrrrrrrrrrBZbvji",
|
||||
"value" : "0"
|
||||
},
|
||||
"Flags" : 196608,
|
||||
"HighLimit" : {
|
||||
"currency" : "USD",
|
||||
"issuer" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
|
||||
"value" : "1000"
|
||||
},
|
||||
"HighNode" : "0000000000000000",
|
||||
"LedgerEntryType" : "RippleState",
|
||||
"LowLimit" : {
|
||||
"currency" : "USD",
|
||||
"issuer" : "rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn",
|
||||
"value" : "1000"
|
||||
},
|
||||
"LowNode" : "0000000000000000"
|
||||
})", jv);
|
||||
|
||||
auto const jv_l = env.le(keylet::line(Account("bob").id(),
|
||||
Account("alice")["USD"].issue()))->getJson(0);
|
||||
for (auto it = jv.begin(); it != jv.end(); ++it)
|
||||
expect(*it == jv_l[it.memberName()]);
|
||||
|
||||
env.trust(Account("bob")["USD"](0), "alice");
|
||||
env.trust(Account("alice")["USD"](0), "bob");
|
||||
expect(env.le(keylet::line(Account("bob").id(),
|
||||
Account("alice")["USD"].issue())) == nullptr);
|
||||
}
|
||||
|
||||
void
|
||||
test_trust_auto_clear_trust_auto_clear()
|
||||
{
|
||||
testcase("trust auto clear");
|
||||
Env env(*this);
|
||||
env.fund(XRP(10000), "alice", "bob");
|
||||
env.trust(Account("bob")["USD"](1000), "alice");
|
||||
env(pay("bob", "alice", Account("bob")["USD"](50)));
|
||||
env.trust(Account("bob")["USD"](0), "alice");
|
||||
|
||||
Json::Value jv;
|
||||
Json::Reader().parse(R"({
|
||||
"Balance" :
|
||||
{
|
||||
"currency" : "USD",
|
||||
"issuer" : "rrrrrrrrrrrrrrrrrrrrBZbvji",
|
||||
"value" : "50"
|
||||
},
|
||||
"Flags" : 65536,
|
||||
"HighLimit" :
|
||||
{
|
||||
"currency" : "USD",
|
||||
"issuer" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
|
||||
"value" : "0"
|
||||
},
|
||||
"HighNode" : "0000000000000000",
|
||||
"LedgerEntryType" : "RippleState",
|
||||
"LowLimit" :
|
||||
{
|
||||
"currency" : "USD",
|
||||
"issuer" : "rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn",
|
||||
"value" : "0"
|
||||
},
|
||||
"LowNode" : "0000000000000000"
|
||||
})", jv);
|
||||
|
||||
auto const jv_l = env.le(keylet::line(Account("alice").id(),
|
||||
Account("bob")["USD"].issue()))->getJson(0);
|
||||
for (auto it = jv.begin(); it != jv.end(); ++it)
|
||||
expect(*it == jv_l[it.memberName()]);
|
||||
|
||||
env(pay("alice", "bob", Account("alice")["USD"](50)));
|
||||
expect(env.le(keylet::line(Account("alice").id(),
|
||||
Account("bob")["USD"].issue())) == nullptr);
|
||||
}
|
||||
|
||||
void
|
||||
run()
|
||||
{
|
||||
test_no_direct_path_no_intermediary_no_alternatives();
|
||||
test_direct_path_no_intermediary();
|
||||
test_payment_auto_path_find();
|
||||
test_path_find();
|
||||
test_path_find_consume_all();
|
||||
test_alternative_path_consume_both();
|
||||
test_alternative_paths_consume_best_transfer();
|
||||
test_alternative_paths_consume_best_transfer_first();
|
||||
test_alternative_paths_limit_returned_paths_to_best_quality();
|
||||
test_issues_path_negative_issue();
|
||||
test_issues_path_negative_ripple_client_issue_23_smaller();
|
||||
test_issues_path_negative_ripple_client_issue_23_larger();
|
||||
test_via_offers_via_gateway();
|
||||
test_indirect_paths_path_find();
|
||||
test_quality_paths_quality_set_and_test();
|
||||
test_trust_auto_clear_trust_normal_clear();
|
||||
test_trust_auto_clear_trust_auto_clear();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(Path,app,ripple)
|
||||
|
||||
} // test
|
||||
} // ripple
|
||||
360
src/ripple/app/tests/Taker.test.cpp
Normal file
360
src/ripple/app/tests/Taker.test.cpp
Normal file
@@ -0,0 +1,360 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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/tx/impl/Taker.h>
|
||||
#include <beast/unit_test/suite.h>
|
||||
#include <beast/module/core/text/LexicalCast.h>
|
||||
#include <beast/cxx14/type_traits.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
class Taker_test : public beast::unit_test::suite
|
||||
{
|
||||
static bool const Buy = false;
|
||||
static bool const Sell = true;
|
||||
|
||||
class TestTaker
|
||||
: public BasicTaker
|
||||
{
|
||||
STAmount funds_;
|
||||
STAmount cross_funds;
|
||||
|
||||
public:
|
||||
TestTaker (
|
||||
CrossType cross_type, Amounts const& amount, Quality const& quality,
|
||||
STAmount const& funds, std::uint32_t flags, std::uint32_t rate_in,
|
||||
std::uint32_t rate_out)
|
||||
: BasicTaker (cross_type, AccountID(0x4701), amount, quality, flags, rate_in, rate_out)
|
||||
, funds_ (funds)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
set_funds (STAmount const& funds)
|
||||
{
|
||||
cross_funds = funds;
|
||||
}
|
||||
|
||||
STAmount
|
||||
get_funds (AccountID const& owner, STAmount const& funds) const
|
||||
{
|
||||
if (owner == account ())
|
||||
return funds_;
|
||||
|
||||
return cross_funds;
|
||||
}
|
||||
|
||||
Amounts
|
||||
cross (Amounts offer, Quality quality)
|
||||
{
|
||||
if (reject (quality))
|
||||
return Amounts (offer.in.zeroed (), offer.out.zeroed ());
|
||||
|
||||
// we need to emulate "unfunded offers" behavior
|
||||
if (get_funds (AccountID (0x4702), offer.out) == zero)
|
||||
return Amounts (offer.in.zeroed (), offer.out.zeroed ());
|
||||
|
||||
if (done ())
|
||||
return Amounts (offer.in.zeroed (), offer.out.zeroed ());
|
||||
|
||||
auto result = do_cross (offer, quality, AccountID (0x4702));
|
||||
|
||||
funds_ -= result.order.in;
|
||||
|
||||
return result.order;
|
||||
}
|
||||
|
||||
std::pair<Amounts, Amounts>
|
||||
cross (Amounts offer1, Quality quality1, Amounts offer2, Quality quality2)
|
||||
{
|
||||
/* check if composed quality should be rejected */
|
||||
Quality const quality (composed_quality (quality1, quality2));
|
||||
|
||||
if (reject (quality))
|
||||
return std::make_pair(
|
||||
Amounts { offer1.in.zeroed (), offer1.out.zeroed () },
|
||||
Amounts { offer2.in.zeroed (), offer2.out.zeroed () });
|
||||
|
||||
if (done ())
|
||||
return std::make_pair(
|
||||
Amounts { offer1.in.zeroed (), offer1.out.zeroed () },
|
||||
Amounts { offer2.in.zeroed (), offer2.out.zeroed () });
|
||||
|
||||
auto result = do_cross (
|
||||
offer1, quality1, AccountID (0x4703),
|
||||
offer2, quality2, AccountID (0x4704));
|
||||
|
||||
return std::make_pair (result.first.order, result.second.order);
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
Issue const& usd () const
|
||||
{
|
||||
static Issue const issue (
|
||||
Currency (0x5553440000000000), AccountID (0x4985601));
|
||||
return issue;
|
||||
}
|
||||
|
||||
Issue const& eur () const
|
||||
{
|
||||
static Issue const issue (
|
||||
Currency (0x4555520000000000), AccountID (0x4985602));
|
||||
return issue;
|
||||
}
|
||||
|
||||
Issue const& xrp () const
|
||||
{
|
||||
static Issue const issue (
|
||||
xrpCurrency (), xrpAccount ());
|
||||
return issue;
|
||||
}
|
||||
|
||||
STAmount parse_amount (std::string const& amount, Issue const& issue)
|
||||
{
|
||||
return amountFromString (issue, amount);
|
||||
}
|
||||
|
||||
Amounts parse_amounts (
|
||||
std::string const& amount_in, Issue const& issue_in,
|
||||
std::string const& amount_out, Issue const& issue_out)
|
||||
{
|
||||
STAmount const in (parse_amount (amount_in, issue_in));
|
||||
STAmount const out (parse_amount (amount_out, issue_out));
|
||||
|
||||
return { in, out };
|
||||
}
|
||||
|
||||
struct cross_attempt_offer
|
||||
{
|
||||
cross_attempt_offer (std::string const& in_,
|
||||
std::string const &out_)
|
||||
: in (in_)
|
||||
, out (out_)
|
||||
{
|
||||
}
|
||||
|
||||
std::string in;
|
||||
std::string out;
|
||||
};
|
||||
|
||||
private:
|
||||
std::string
|
||||
format_amount (STAmount const& amount)
|
||||
{
|
||||
std::string txt = amount.getText ();
|
||||
txt += "/";
|
||||
txt += to_string (amount.issue().currency);
|
||||
return txt;
|
||||
}
|
||||
|
||||
void
|
||||
attempt (
|
||||
bool sell,
|
||||
std::string name,
|
||||
Quality taker_quality,
|
||||
cross_attempt_offer const offer,
|
||||
std::string const funds,
|
||||
Quality cross_quality,
|
||||
cross_attempt_offer const cross,
|
||||
std::string const cross_funds,
|
||||
cross_attempt_offer const flow,
|
||||
Issue const& issue_in,
|
||||
Issue const& issue_out,
|
||||
std::uint32_t rate_in = QUALITY_ONE,
|
||||
std::uint32_t rate_out = QUALITY_ONE)
|
||||
{
|
||||
Amounts taker_offer (parse_amounts (
|
||||
offer.in, issue_in,
|
||||
offer.out, issue_out));
|
||||
|
||||
Amounts cross_offer (parse_amounts (
|
||||
cross.in, issue_in,
|
||||
cross.out, issue_out));
|
||||
|
||||
CrossType cross_type;
|
||||
|
||||
if (isXRP (issue_out))
|
||||
cross_type = CrossType::IouToXrp;
|
||||
else if (isXRP (issue_in))
|
||||
cross_type = CrossType::XrpToIou;
|
||||
else
|
||||
cross_type = CrossType::IouToIou;
|
||||
|
||||
// FIXME: We are always invoking the IOU-to-IOU taker. We should select
|
||||
// the correct type dynamically.
|
||||
TestTaker taker (cross_type, taker_offer, taker_quality,
|
||||
parse_amount (funds, issue_in), sell ? tfSell : 0,
|
||||
rate_in, rate_out);
|
||||
|
||||
taker.set_funds (parse_amount (cross_funds, issue_out));
|
||||
|
||||
auto result = taker.cross (cross_offer, cross_quality);
|
||||
|
||||
Amounts const expected (parse_amounts (
|
||||
flow.in, issue_in,
|
||||
flow.out, issue_out));
|
||||
|
||||
expect (expected == result, name + (sell ? " (s)" : " (b)"));
|
||||
|
||||
if (expected != result)
|
||||
{
|
||||
log <<
|
||||
"Expected: " << format_amount (expected.in) <<
|
||||
" : " << format_amount (expected.out);
|
||||
log <<
|
||||
" Actual: " << format_amount (result.in) <<
|
||||
" : " << format_amount (result.out);
|
||||
}
|
||||
}
|
||||
|
||||
Quality get_quality(std::string in, std::string out)
|
||||
{
|
||||
return Quality (parse_amounts (in, xrp(), out, xrp ()));
|
||||
}
|
||||
|
||||
public:
|
||||
// Notation for clamp scenario descriptions:
|
||||
//
|
||||
// IN:OUT (with the last in the list being limiting factor)
|
||||
// N = Nothing
|
||||
// T = Taker Offer Balance
|
||||
// A = Taker Account Balance
|
||||
// B = Owner Account Balance
|
||||
//
|
||||
// (s) = sell semantics: taker wants unlimited output
|
||||
// (b) = buy semantics: taker wants a limited amount out
|
||||
|
||||
// NIKB TODO: Augment TestTaker so currencies and rates can be specified
|
||||
// once without need for repetition.
|
||||
void
|
||||
test_xrp_to_iou ()
|
||||
{
|
||||
testcase ("XRP Quantization: input");
|
||||
|
||||
Quality q1 = get_quality ("1", "1");
|
||||
|
||||
// TAKER OWNER
|
||||
// QUAL OFFER FUNDS QUAL OFFER FUNDS EXPECTED
|
||||
// XRP USD
|
||||
attempt (Sell, "N:N", q1, { "2", "2" }, "2", q1, { "2", "2" }, "2", { "2", "2" }, xrp(), usd());
|
||||
attempt (Sell, "N:B", q1, { "2", "2" }, "2", q1, { "2", "2" }, "1.8", { "1", "1.8" }, xrp(), usd());
|
||||
attempt (Buy, "N:T", q1, { "1", "1" }, "2", q1, { "2", "2" }, "2", { "1", "1" }, xrp(), usd());
|
||||
attempt (Buy, "N:BT", q1, { "1", "1" }, "2", q1, { "2", "2" }, "1.8", { "1", "1" }, xrp(), usd());
|
||||
attempt (Buy, "N:TB", q1, { "1", "1" }, "2", q1, { "2", "2" }, "0.8", { "0", "0.8" }, xrp(), usd());
|
||||
|
||||
attempt (Sell, "T:N", q1, { "1", "1" }, "2", q1, { "2", "2" }, "2", { "1", "1" }, xrp(), usd());
|
||||
attempt (Sell, "T:B", q1, { "1", "1" }, "2", q1, { "2", "2" }, "1.8", { "1", "1.8" }, xrp(), usd());
|
||||
attempt (Buy, "T:T", q1, { "1", "1" }, "2", q1, { "2", "2" }, "2", { "1", "1" }, xrp(), usd());
|
||||
attempt (Buy, "T:BT", q1, { "1", "1" }, "2", q1, { "2", "2" }, "1.8", { "1", "1" }, xrp(), usd());
|
||||
attempt (Buy, "T:TB", q1, { "1", "1" }, "2", q1, { "2", "2" }, "0.8", { "0", "0.8" }, xrp(), usd());
|
||||
|
||||
attempt (Sell, "A:N", q1, { "2", "2" }, "1", q1, { "2", "2" }, "2", { "1", "1" }, xrp(), usd());
|
||||
attempt (Sell, "A:B", q1, { "2", "2" }, "1", q1, { "2", "2" }, "1.8", { "1", "1.8" }, xrp(), usd());
|
||||
attempt (Buy, "A:T", q1, { "2", "2" }, "1", q1, { "3", "3" }, "3", { "1", "1" }, xrp(), usd());
|
||||
attempt (Buy, "A:BT", q1, { "2", "2" }, "1", q1, { "3", "3" }, "2.4", { "1", "1" }, xrp(), usd());
|
||||
attempt (Buy, "A:TB", q1, { "2", "2" }, "1", q1, { "3", "3" }, "0.8", { "0", "0.8" }, xrp(), usd());
|
||||
|
||||
attempt (Sell, "TA:N", q1, { "2", "2" }, "1", q1, { "2", "2" }, "2", { "1", "1" }, xrp(), usd());
|
||||
attempt (Sell, "TA:B", q1, { "2", "2" }, "1", q1, { "3", "3" }, "1.8", { "1", "1.8" }, xrp(), usd());
|
||||
attempt (Buy, "TA:T", q1, { "2", "2" }, "1", q1, { "3", "3" }, "3", { "1", "1" }, xrp(), usd());
|
||||
attempt (Buy, "TA:BT", q1, { "2", "2" }, "1", q1, { "3", "3" }, "1.8", { "1", "1.8" }, xrp(), usd());
|
||||
attempt (Buy, "TA:TB", q1, { "2", "2" }, "1", q1, { "3", "3" }, "1.8", { "1", "1.8" }, xrp(), usd());
|
||||
|
||||
attempt (Sell, "AT:N", q1, { "2", "2" }, "1", q1, { "3", "3" }, "3", { "1", "1" }, xrp(), usd());
|
||||
attempt (Sell, "AT:B", q1, { "2", "2" }, "1", q1, { "3", "3" }, "1.8", { "1", "1.8" }, xrp(), usd());
|
||||
attempt (Buy, "AT:T", q1, { "2", "2" }, "1", q1, { "3", "3" }, "3", { "1", "1" }, xrp(), usd());
|
||||
attempt (Buy, "AT:BT", q1, { "2", "2" }, "1", q1, { "3", "3" }, "1.8", { "1", "1.8" }, xrp(), usd());
|
||||
attempt (Buy, "AT:TB", q1, { "2", "2" }, "1", q1, { "3", "3" }, "0.8", { "0", "0.8" }, xrp(), usd());
|
||||
}
|
||||
|
||||
void
|
||||
test_iou_to_xrp ()
|
||||
{
|
||||
testcase ("XRP Quantization: output");
|
||||
|
||||
Quality q1 = get_quality ("1", "1");
|
||||
|
||||
// TAKER OWNER
|
||||
// QUAL OFFER FUNDS QUAL OFFER FUNDS EXPECTED
|
||||
// USD XRP
|
||||
attempt (Sell, "N:N", q1, { "3", "3" }, "3", q1, { "3", "3" }, "3", { "3", "3" }, usd(), xrp());
|
||||
attempt (Sell, "N:B", q1, { "3", "3" }, "3", q1, { "3", "3" }, "2", { "2", "2" }, usd(), xrp());
|
||||
attempt (Buy, "N:T", q1, { "3", "3" }, "2.5", q1, { "5", "5" }, "5", { "2.5", "2" }, usd(), xrp());
|
||||
attempt (Buy, "N:BT", q1, { "3", "3" }, "1.5", q1, { "5", "5" }, "4", { "1.5", "1" }, usd(), xrp());
|
||||
attempt (Buy, "N:TB", q1, { "3", "3" }, "2.2", q1, { "5", "5" }, "1", { "1", "1" }, usd(), xrp());
|
||||
|
||||
attempt (Sell, "T:N", q1, { "1", "1" }, "2", q1, { "2", "2" }, "2", { "1", "1" }, usd(), xrp());
|
||||
attempt (Sell, "T:B", q1, { "2", "2" }, "2", q1, { "3", "3" }, "1", { "1", "1" }, usd(), xrp());
|
||||
attempt (Buy, "T:T", q1, { "1", "1" }, "2", q1, { "2", "2" }, "2", { "1", "1" }, usd(), xrp());
|
||||
attempt (Buy, "T:BT", q1, { "1", "1" }, "2", q1, { "3", "3" }, "2", { "1", "1" }, usd(), xrp());
|
||||
attempt (Buy, "T:TB", q1, { "2", "2" }, "2", q1, { "3", "3" }, "1", { "1", "1" }, usd(), xrp());
|
||||
|
||||
attempt (Sell, "A:N", q1, { "2", "2" }, "1.5", q1, { "2", "2" }, "2", { "1.5", "1" }, usd(), xrp());
|
||||
attempt (Sell, "A:B", q1, { "2", "2" }, "1.8", q1, { "3", "3" }, "2", { "1.8", "1" }, usd(), xrp());
|
||||
attempt (Buy, "A:T", q1, { "2", "2" }, "1.2", q1, { "3", "3" }, "3", { "1.2", "1" }, usd(), xrp());
|
||||
attempt (Buy, "A:BT", q1, { "2", "2" }, "1.5", q1, { "4", "4" }, "3", { "1.5", "1" }, usd(), xrp());
|
||||
attempt (Buy, "A:TB", q1, { "2", "2" }, "1.5", q1, { "4", "4" }, "1", { "1", "1" }, usd(), xrp());
|
||||
|
||||
attempt (Sell, "TA:N", q1, { "2", "2" }, "1.5", q1, { "2", "2" }, "2", { "1.5", "1" }, usd(), xrp());
|
||||
attempt (Sell, "TA:B", q1, { "2", "2" }, "1.5", q1, { "3", "3" }, "1", { "1", "1" }, usd(), xrp());
|
||||
attempt (Buy, "TA:T", q1, { "2", "2" }, "1.5", q1, { "3", "3" }, "3", { "1.5", "1" }, usd(), xrp());
|
||||
attempt (Buy, "TA:BT", q1, { "2", "2" }, "1.8", q1, { "4", "4" }, "3", { "1.8", "1" }, usd(), xrp());
|
||||
attempt (Buy, "TA:TB", q1, { "2", "2" }, "1.2", q1, { "3", "3" }, "1", { "1", "1" }, usd(), xrp());
|
||||
|
||||
attempt (Sell, "AT:N", q1, { "2", "2" }, "2.5", q1, { "4", "4" }, "4", { "2", "2" }, usd(), xrp());
|
||||
attempt (Sell, "AT:B", q1, { "2", "2" }, "2.5", q1, { "3", "3" }, "1", { "1", "1" }, usd(), xrp());
|
||||
attempt (Buy, "AT:T", q1, { "2", "2" }, "2.5", q1, { "3", "3" }, "3", { "2", "2" }, usd(), xrp());
|
||||
attempt (Buy, "AT:BT", q1, { "2", "2" }, "2.5", q1, { "4", "4" }, "3", { "2", "2" }, usd(), xrp());
|
||||
attempt (Buy, "AT:TB", q1, { "2", "2" }, "2.5", q1, { "3", "3" }, "1", { "1", "1" }, usd(), xrp());
|
||||
}
|
||||
|
||||
void
|
||||
test_iou_to_iou ()
|
||||
{
|
||||
testcase ("IOU to IOU");
|
||||
|
||||
Quality q1 = get_quality ("1", "1");
|
||||
|
||||
// Highly exaggerated 50% transfer rate for the input and output:
|
||||
std::uint32_t rate = QUALITY_ONE + (QUALITY_ONE / 2);
|
||||
|
||||
// TAKER OWNER
|
||||
// QUAL OFFER FUNDS QUAL OFFER FUNDS EXPECTED
|
||||
// EUR USD
|
||||
attempt (Sell, "N:N", q1, { "2", "2" }, "10", q1, { "2", "2" }, "10", { "2", "2" }, eur(), usd(), rate, rate);
|
||||
attempt (Sell, "N:B", q1, { "4", "4" }, "10", q1, { "4", "4" }, "4", { "2.666666666666666", "2.666666666666666" }, eur(), usd(), rate, rate);
|
||||
attempt (Buy, "N:T", q1, { "1", "1" }, "10", q1, { "2", "2" }, "10", { "1", "1" }, eur(), usd(), rate, rate);
|
||||
attempt (Buy, "N:BT", q1, { "2", "2" }, "10", q1, { "6", "6" }, "5", { "2", "2" }, eur(), usd(), rate, rate);
|
||||
attempt (Buy, "N:TB", q1, { "2", "2" }, "2", q1, { "6", "6" }, "1", { "0.6666666666666667", "0.6666666666666667" }, eur(), usd(), rate, rate);
|
||||
attempt (Sell, "A:N", q1, { "2", "2" }, "2.5", q1, { "2", "2" }, "10", { "1.666666666666666", "1.666666666666666" }, eur(), usd(), rate, rate);
|
||||
}
|
||||
|
||||
void
|
||||
run()
|
||||
{
|
||||
test_xrp_to_iou ();
|
||||
test_iou_to_xrp ();
|
||||
test_iou_to_iou ();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(Taker,tx,ripple);
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user