mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
Don't include unit test sources in code coverage (RIPD-1132):
Most files containing unit test code are moved to src/test. JTx and the test client code are not yet moved.
This commit is contained in:
175
src/test/rpc/AccountInfo_test.cpp
Normal file
175
src/test/rpc/AccountInfo_test.cpp
Normal file
@@ -0,0 +1,175 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2016 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/protocol/JsonFields.h> // jss:: definitions
|
||||
#include <ripple/protocol/Feature.h>
|
||||
#include <ripple/test/jtx.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
class AccountInfo_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
|
||||
void testErrors()
|
||||
{
|
||||
using namespace jtx;
|
||||
Env env(*this);
|
||||
{
|
||||
// account_info with no account.
|
||||
auto const info = env.rpc ("json", "account_info", "{ }");
|
||||
BEAST_EXPECT(info[jss::result][jss::error_message] ==
|
||||
"Missing field 'account'.");
|
||||
}
|
||||
{
|
||||
// account_info with a malformed account sting.
|
||||
auto const info = env.rpc ("json", "account_info", "{\"account\": "
|
||||
"\"n94JNrQYkDrpt62bbSR7nVEhdyAvcJXRAsjEkFYyqRkh9SUTYEqV\"}");
|
||||
BEAST_EXPECT(info[jss::result][jss::error_message] ==
|
||||
"Disallowed seed.");
|
||||
}
|
||||
{
|
||||
// account_info with an account that's not in the ledger.
|
||||
Account const bogie {"bogie"};
|
||||
auto const info = env.rpc ("json", "account_info",
|
||||
std::string ("{ ") + "\"account\": \"" + bogie.human() + "\"}");
|
||||
BEAST_EXPECT(info[jss::result][jss::error_message] ==
|
||||
"Account not found.");
|
||||
}
|
||||
}
|
||||
|
||||
// Test the "signer_lists" argument in account_info.
|
||||
void testSignerLists()
|
||||
{
|
||||
using namespace jtx;
|
||||
Env env(*this, features(featureMultiSign));
|
||||
Account const alice {"alice"};
|
||||
env.fund(XRP(1000), alice);
|
||||
|
||||
auto const withoutSigners = std::string ("{ ") +
|
||||
"\"account\": \"" + alice.human() + "\"}";
|
||||
|
||||
auto const withSigners = std::string ("{ ") +
|
||||
"\"account\": \"" + alice.human() + "\", " +
|
||||
"\"signer_lists\": true }";
|
||||
|
||||
// Alice has no SignerList yet.
|
||||
{
|
||||
// account_info without the "signer_lists" argument.
|
||||
auto const info = env.rpc ("json", "account_info", withoutSigners);
|
||||
BEAST_EXPECT(info.isMember(jss::result) &&
|
||||
info[jss::result].isMember(jss::account_data));
|
||||
BEAST_EXPECT(! info[jss::result][jss::account_data].
|
||||
isMember (jss::signer_lists));
|
||||
}
|
||||
{
|
||||
// account_info with the "signer_lists" argument.
|
||||
auto const info = env.rpc ("json", "account_info", withSigners);
|
||||
BEAST_EXPECT(info.isMember(jss::result) &&
|
||||
info[jss::result].isMember(jss::account_data));
|
||||
auto const& data = info[jss::result][jss::account_data];
|
||||
BEAST_EXPECT(data.isMember (jss::signer_lists));
|
||||
auto const& signerLists = data[jss::signer_lists];
|
||||
BEAST_EXPECT(signerLists.isArray());
|
||||
BEAST_EXPECT(signerLists.size() == 0);
|
||||
}
|
||||
|
||||
// Give alice a SignerList.
|
||||
Account const bogie {"bogie"};
|
||||
|
||||
Json::Value const smallSigners = signers(alice, 2, { { bogie, 3 } });
|
||||
env(smallSigners);
|
||||
{
|
||||
// account_info without the "signer_lists" argument.
|
||||
auto const info = env.rpc ("json", "account_info", withoutSigners);
|
||||
BEAST_EXPECT(info.isMember(jss::result) &&
|
||||
info[jss::result].isMember(jss::account_data));
|
||||
BEAST_EXPECT(! info[jss::result][jss::account_data].
|
||||
isMember (jss::signer_lists));
|
||||
}
|
||||
{
|
||||
// account_info with the "signer_lists" argument.
|
||||
auto const info = env.rpc ("json", "account_info", withSigners);
|
||||
BEAST_EXPECT(info.isMember(jss::result) &&
|
||||
info[jss::result].isMember(jss::account_data));
|
||||
auto const& data = info[jss::result][jss::account_data];
|
||||
BEAST_EXPECT(data.isMember (jss::signer_lists));
|
||||
auto const& signerLists = data[jss::signer_lists];
|
||||
BEAST_EXPECT(signerLists.isArray());
|
||||
BEAST_EXPECT(signerLists.size() == 1);
|
||||
auto const& signers = signerLists[0u];
|
||||
BEAST_EXPECT(signers.isObject());
|
||||
BEAST_EXPECT(signers[sfSignerQuorum.jsonName] == 2);
|
||||
auto const& signerEntries = signers[sfSignerEntries.jsonName];
|
||||
BEAST_EXPECT(signerEntries.size() == 1);
|
||||
auto const& entry0 = signerEntries[0u][sfSignerEntry.jsonName];
|
||||
BEAST_EXPECT(entry0[sfSignerWeight.jsonName] == 3);
|
||||
}
|
||||
|
||||
// Give alice a big signer list
|
||||
Account const demon {"demon"};
|
||||
Account const ghost {"ghost"};
|
||||
Account const haunt {"haunt"};
|
||||
Account const jinni {"jinni"};
|
||||
Account const phase {"phase"};
|
||||
Account const shade {"shade"};
|
||||
Account const spook {"spook"};
|
||||
|
||||
Json::Value const bigSigners = signers(alice, 4, {
|
||||
{bogie, 1}, {demon, 1}, {ghost, 1}, {haunt, 1},
|
||||
{jinni, 1}, {phase, 1}, {shade, 1}, {spook, 1}, });
|
||||
env(bigSigners);
|
||||
{
|
||||
// account_info with the "signer_lists" argument.
|
||||
auto const info = env.rpc ("json", "account_info", withSigners);
|
||||
BEAST_EXPECT(info.isMember(jss::result) &&
|
||||
info[jss::result].isMember(jss::account_data));
|
||||
auto const& data = info[jss::result][jss::account_data];
|
||||
BEAST_EXPECT(data.isMember (jss::signer_lists));
|
||||
auto const& signerLists = data[jss::signer_lists];
|
||||
BEAST_EXPECT(signerLists.isArray());
|
||||
BEAST_EXPECT(signerLists.size() == 1);
|
||||
auto const& signers = signerLists[0u];
|
||||
BEAST_EXPECT(signers.isObject());
|
||||
BEAST_EXPECT(signers[sfSignerQuorum.jsonName] == 4);
|
||||
auto const& signerEntries = signers[sfSignerEntries.jsonName];
|
||||
BEAST_EXPECT(signerEntries.size() == 8);
|
||||
for (unsigned i = 0u; i < 8; ++i)
|
||||
{
|
||||
auto const& entry = signerEntries[i][sfSignerEntry.jsonName];
|
||||
BEAST_EXPECT(entry.size() == 2);
|
||||
BEAST_EXPECT(entry.isMember(sfAccount.jsonName));
|
||||
BEAST_EXPECT(entry[sfSignerWeight.jsonName] == 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
testErrors();
|
||||
testSignerLists();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(AccountInfo,app,ripple);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
360
src/test/rpc/AccountLinesRPC_test.cpp
Normal file
360
src/test/rpc/AccountLinesRPC_test.cpp
Normal file
@@ -0,0 +1,360 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2016 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/ErrorCodes.h>
|
||||
#include <ripple/protocol/JsonFields.h>
|
||||
#include <ripple/protocol/TxFlags.h>
|
||||
#include <ripple/test/jtx.h>
|
||||
#include <ripple/beast/unit_test.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
namespace RPC {
|
||||
|
||||
class AccountLinesRPC_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
void testAccountLines()
|
||||
{
|
||||
using namespace test::jtx;
|
||||
Env env(*this);
|
||||
{
|
||||
// account_lines with no account.
|
||||
auto const lines = env.rpc ("json", "account_lines", "{ }");
|
||||
BEAST_EXPECT(lines[jss::result][jss::error_message] ==
|
||||
RPC::missing_field_error(jss::account)[jss::error_message]);
|
||||
}
|
||||
{
|
||||
// account_lines with a malformed account.
|
||||
auto const lines = env.rpc ("json", "account_lines",
|
||||
R"({"account": )"
|
||||
R"("n9MJkEKHDhy5eTLuHUQeAAjo382frHNbFK4C8hcwN4nwM2SrLdBj"})");
|
||||
BEAST_EXPECT(lines[jss::result][jss::error_message] ==
|
||||
RPC::make_error(rpcBAD_SEED)[jss::error_message]);
|
||||
}
|
||||
Account const alice {"alice"};
|
||||
{
|
||||
// account_lines on an unfunded account.
|
||||
auto const lines = env.rpc ("json", "account_lines",
|
||||
R"({"account": ")" + alice.human() + R"("})");
|
||||
BEAST_EXPECT(lines[jss::result][jss::error_message] ==
|
||||
RPC::make_error(rpcACT_NOT_FOUND)[jss::error_message]);
|
||||
}
|
||||
env.fund(XRP(10000), alice);
|
||||
env.close();
|
||||
LedgerInfo const ledger3Info = env.closed()->info();
|
||||
BEAST_EXPECT(ledger3Info.seq == 3);
|
||||
|
||||
{
|
||||
// alice is funded but has no lines. An empty array is returned.
|
||||
auto const lines = env.rpc ("json", "account_lines",
|
||||
R"({"account": ")" + alice.human() + R"("})");
|
||||
BEAST_EXPECT(lines[jss::result][jss::lines].isArray());
|
||||
BEAST_EXPECT(lines[jss::result][jss::lines].size() == 0);
|
||||
}
|
||||
{
|
||||
// Specify a ledger that doesn't exist.
|
||||
auto const lines = env.rpc ("json", "account_lines",
|
||||
R"({"account": ")" + alice.human() + R"(", )"
|
||||
R"("ledger_index": "nonsense"})");
|
||||
BEAST_EXPECT(lines[jss::result][jss::error_message] ==
|
||||
"ledgerIndexMalformed");
|
||||
}
|
||||
{
|
||||
// Specify a different ledger that doesn't exist.
|
||||
auto const lines = env.rpc ("json", "account_lines",
|
||||
R"({"account": ")" + alice.human() + R"(", )"
|
||||
R"("ledger_index": 50000})");
|
||||
BEAST_EXPECT(lines[jss::result][jss::error_message] ==
|
||||
"ledgerNotFound");
|
||||
}
|
||||
// Create trust lines to share with alice.
|
||||
Account const gw1 {"gw1"};
|
||||
env.fund(XRP(10000), gw1);
|
||||
std::vector<IOU> gw1Currencies;
|
||||
|
||||
for (char c = 0; c <= ('Z' - 'A'); ++c)
|
||||
{
|
||||
// gw1 currencies have names "YAA" -> "YAZ".
|
||||
gw1Currencies.push_back(
|
||||
gw1[std::string("YA") + static_cast<char>('A' + c)]);
|
||||
IOU const& gw1Currency = gw1Currencies.back();
|
||||
|
||||
// Establish trust lines.
|
||||
env(trust(alice, gw1Currency(100 + c)));
|
||||
env(pay(gw1, alice, gw1Currency(50 + c)));
|
||||
}
|
||||
env.close();
|
||||
LedgerInfo const ledger4Info = env.closed()->info();
|
||||
BEAST_EXPECT(ledger4Info.seq == 4);
|
||||
|
||||
// Add another set of trust lines in another ledger so we can see
|
||||
// differences in historic ledgers.
|
||||
Account const gw2 {"gw2"};
|
||||
env.fund(XRP(10000), gw2);
|
||||
|
||||
// gw2 requires authorization.
|
||||
env(fset(gw2, asfRequireAuth));
|
||||
env.close();
|
||||
std::vector<IOU> gw2Currencies;
|
||||
|
||||
for (char c = 0; c <= ('Z' - 'A'); ++c)
|
||||
{
|
||||
// gw2 currencies have names "ZAA" -> "ZAZ".
|
||||
gw2Currencies.push_back(
|
||||
gw2[std::string("ZA") + static_cast<char>('A' + c)]);
|
||||
IOU const& gw2Currency = gw2Currencies.back();
|
||||
|
||||
// Establish trust lines.
|
||||
env(trust(alice, gw2Currency(200 + c)));
|
||||
env(trust(gw2, gw2Currency(0), alice, tfSetfAuth));
|
||||
env.close();
|
||||
env(pay(gw2, alice, gw2Currency(100 + c)));
|
||||
env.close();
|
||||
|
||||
// Set flags on gw2 trust lines so we can look for them.
|
||||
env(trust(alice, gw2Currency(0), gw2, tfSetNoRipple | tfSetFreeze));
|
||||
}
|
||||
env.close();
|
||||
LedgerInfo const ledger58Info = env.closed()->info();
|
||||
BEAST_EXPECT(ledger58Info.seq == 58);
|
||||
|
||||
// A re-usable test for historic ledgers.
|
||||
auto testAccountLinesHistory =
|
||||
[this, &env](Account const& account, LedgerInfo const& info, int count)
|
||||
{
|
||||
// Get account_lines by ledger index.
|
||||
auto const linesSeq = env.rpc ("json", "account_lines",
|
||||
R"({"account": ")" + account.human() + R"(", )"
|
||||
R"("ledger_index": )" + std::to_string(info.seq) + "}");
|
||||
BEAST_EXPECT(linesSeq[jss::result][jss::lines].isArray());
|
||||
BEAST_EXPECT(linesSeq[jss::result][jss::lines].size() == count);
|
||||
|
||||
// Get account_lines by ledger hash.
|
||||
auto const linesHash = env.rpc ("json", "account_lines",
|
||||
R"({"account": ")" + account.human() + R"(", )"
|
||||
R"("ledger_hash": ")" + to_string(info.hash) + R"("})");
|
||||
BEAST_EXPECT(linesHash[jss::result][jss::lines].isArray());
|
||||
BEAST_EXPECT(linesHash[jss::result][jss::lines].size() == count);
|
||||
};
|
||||
|
||||
// Alice should have no trust lines in ledger 3.
|
||||
testAccountLinesHistory (alice, ledger3Info, 0);
|
||||
|
||||
// Alice should have 26 trust lines in ledger 4.
|
||||
testAccountLinesHistory (alice, ledger4Info, 26);
|
||||
|
||||
// Alice should have 52 trust lines in ledger 58.
|
||||
testAccountLinesHistory (alice, ledger58Info, 52);
|
||||
|
||||
{
|
||||
// Surprisingly, it's valid to specify both index and hash, in
|
||||
// which case the hash wins.
|
||||
auto const lines = env.rpc ("json", "account_lines",
|
||||
R"({"account": ")" + alice.human() + R"(", )"
|
||||
R"("ledger_hash": ")" + to_string(ledger4Info.hash) + R"("})"
|
||||
R"("ledger_index": )" + std::to_string(ledger58Info.seq) + "}");
|
||||
BEAST_EXPECT(lines[jss::result][jss::lines].isArray());
|
||||
BEAST_EXPECT(lines[jss::result][jss::lines].size() == 26);
|
||||
}
|
||||
{
|
||||
// alice should have 52 trust lines in the current ledger.
|
||||
auto const lines = env.rpc ("json", "account_lines",
|
||||
R"({"account": ")" + alice.human() + R"("})");
|
||||
BEAST_EXPECT(lines[jss::result][jss::lines].isArray());
|
||||
BEAST_EXPECT(lines[jss::result][jss::lines].size() == 52);
|
||||
}
|
||||
{
|
||||
// alice should have 26 trust lines with gw1.
|
||||
auto const lines = env.rpc ("json", "account_lines",
|
||||
R"({"account": ")" + alice.human() + R"(", )"
|
||||
R"("peer": ")" + gw1.human() + R"("})");
|
||||
BEAST_EXPECT(lines[jss::result][jss::lines].isArray());
|
||||
BEAST_EXPECT(lines[jss::result][jss::lines].size() == 26);
|
||||
}
|
||||
{
|
||||
// Use a malformed peer.
|
||||
auto const lines = env.rpc ("json", "account_lines",
|
||||
R"({"account": ")" + alice.human() + R"(", )"
|
||||
R"("peer": )"
|
||||
R"("n9MJkEKHDhy5eTLuHUQeAAjo382frHNbFK4C8hcwN4nwM2SrLdBj"})");
|
||||
BEAST_EXPECT(lines[jss::result][jss::error_message] ==
|
||||
RPC::make_error(rpcBAD_SEED)[jss::error_message]);
|
||||
}
|
||||
{
|
||||
// A negative limit should fail.
|
||||
auto const lines = env.rpc ("json", "account_lines",
|
||||
R"({"account": ")" + alice.human() + R"(", )"
|
||||
R"("limit": -1})");
|
||||
BEAST_EXPECT(lines[jss::result][jss::error_message] ==
|
||||
RPC::expected_field_message(jss::limit, "unsigned integer"));
|
||||
}
|
||||
{
|
||||
// Limit the response to 1 trust line.
|
||||
auto const linesA = env.rpc ("json", "account_lines",
|
||||
R"({"account": ")" + alice.human() + R"(", )"
|
||||
R"("limit": 1})");
|
||||
BEAST_EXPECT(linesA[jss::result][jss::lines].isArray());
|
||||
BEAST_EXPECT(linesA[jss::result][jss::lines].size() == 1);
|
||||
|
||||
// Pick up from where the marker left off. We should get 51.
|
||||
auto marker = linesA[jss::result][jss::marker].asString();
|
||||
auto const linesB = env.rpc ("json", "account_lines",
|
||||
R"({"account": ")" + alice.human() + R"(", )"
|
||||
R"("marker": ")" + marker + R"("})");
|
||||
BEAST_EXPECT(linesB[jss::result][jss::lines].isArray());
|
||||
BEAST_EXPECT(linesB[jss::result][jss::lines].size() == 51);
|
||||
|
||||
// Go again from where the marker left off, but set a limit of 3.
|
||||
auto const linesC = env.rpc ("json", "account_lines",
|
||||
R"({"account": ")" + alice.human() + R"(", )"
|
||||
R"("limit": 3, )"
|
||||
R"("marker": ")" + marker + R"("})");
|
||||
BEAST_EXPECT(linesC[jss::result][jss::lines].isArray());
|
||||
BEAST_EXPECT(linesC[jss::result][jss::lines].size() == 3);
|
||||
|
||||
// Mess with the marker so it becomes bad and check for the error.
|
||||
marker[5] = marker[5] == '7' ? '8' : '7';
|
||||
auto const linesD = env.rpc ("json", "account_lines",
|
||||
R"({"account": ")" + alice.human() + R"(", )"
|
||||
R"("marker": ")" + marker + R"("})");
|
||||
BEAST_EXPECT(linesD[jss::result][jss::error_message] ==
|
||||
RPC::make_error(rpcINVALID_PARAMS)[jss::error_message]);
|
||||
}
|
||||
{
|
||||
// A non-string marker should also fail.
|
||||
auto const lines = env.rpc ("json", "account_lines",
|
||||
R"({"account": ")" + alice.human() + R"(", )"
|
||||
R"("marker": true})");
|
||||
BEAST_EXPECT(lines[jss::result][jss::error_message] ==
|
||||
RPC::expected_field_message(jss::marker, "string"));
|
||||
}
|
||||
{
|
||||
// Check that the flags we expect from alice to gw2 are present.
|
||||
auto const lines = env.rpc ("json", "account_lines",
|
||||
R"({"account": ")" + alice.human() + R"(", )"
|
||||
R"("limit": 1, )"
|
||||
R"("peer": ")" + gw2.human() + R"("})");
|
||||
auto const& line = lines[jss::result][jss::lines][0u];
|
||||
BEAST_EXPECT(line[jss::freeze].asBool() == true);
|
||||
BEAST_EXPECT(line[jss::no_ripple].asBool() == true);
|
||||
BEAST_EXPECT(line[jss::peer_authorized].asBool() == true);
|
||||
}
|
||||
{
|
||||
// Check that the flags we expect from gw2 to alice are present.
|
||||
auto const linesA = env.rpc ("json", "account_lines",
|
||||
R"({"account": ")" + gw2.human() + R"(", )"
|
||||
R"("limit": 1, )"
|
||||
R"("peer": ")" + alice.human() + R"("})");
|
||||
auto const& lineA = linesA[jss::result][jss::lines][0u];
|
||||
BEAST_EXPECT(lineA[jss::freeze_peer].asBool() == true);
|
||||
BEAST_EXPECT(lineA[jss::no_ripple_peer].asBool() == true);
|
||||
BEAST_EXPECT(lineA[jss::authorized].asBool() == true);
|
||||
|
||||
// Continue from the returned marker to make sure that works.
|
||||
BEAST_EXPECT(linesA[jss::result].isMember(jss::marker));
|
||||
auto const marker = linesA[jss::result][jss::marker].asString();
|
||||
auto const linesB = env.rpc ("json", "account_lines",
|
||||
R"({"account": ")" + gw2.human() + R"(", )"
|
||||
R"("limit": 25, )"
|
||||
R"("marker": ")" + marker + R"(", )"
|
||||
R"("peer": ")" + alice.human() + R"("})");
|
||||
BEAST_EXPECT(linesB[jss::result][jss::lines].isArray());
|
||||
BEAST_EXPECT(linesB[jss::result][jss::lines].size() == 25);
|
||||
BEAST_EXPECT(! linesB[jss::result].isMember(jss::marker));
|
||||
}
|
||||
}
|
||||
|
||||
void testAccountLineDelete()
|
||||
{
|
||||
using namespace test::jtx;
|
||||
Env env(*this);
|
||||
|
||||
// The goal here is to observe account_lines marker behavior if the
|
||||
// entry pointed at by a returned marker is removed from the ledger.
|
||||
//
|
||||
// It isn't easy to explicitly delete a trust line, so we do so in a
|
||||
// round-about fashion. It takes 4 actors:
|
||||
// o Gateway gw1 issues USD
|
||||
// o alice offers to buy 100 USD for 100 XRP.
|
||||
// o becky offers to sell 100 USD for 100 XRP.
|
||||
// There will now be an inferred trustline between alice and gw1.
|
||||
// o alice pays her 100 USD to cheri.
|
||||
// alice should now have no USD and no trustline to gw1.
|
||||
Account const alice {"alice"};
|
||||
Account const becky {"becky"};
|
||||
Account const cheri {"cheri"};
|
||||
Account const gw1 {"gw1"};
|
||||
Account const gw2 {"gw2"};
|
||||
env.fund(XRP(10000), alice, becky, cheri, gw1, gw2);
|
||||
env.close();
|
||||
|
||||
auto const USD = gw1["USD"];
|
||||
auto const EUR = gw2["EUR"];
|
||||
env(trust(alice, EUR(200)));
|
||||
env(trust(becky, USD(200)));
|
||||
env(trust(cheri, USD(200)));
|
||||
env.close();
|
||||
|
||||
// becky gets 100 USD from gw1.
|
||||
env(pay(gw1, becky, USD(100)));
|
||||
env.close();
|
||||
|
||||
// alice offers to buy 100 USD for 100 XRP.
|
||||
env(offer(alice, USD(100), XRP(100)));
|
||||
env.close();
|
||||
|
||||
// becky offers to buy 100 XRP for 100 USD.
|
||||
env(offer(becky, XRP(100), USD(100)));
|
||||
env.close();
|
||||
|
||||
// Get account_lines for alice. Limit at 1, so we get a marker.
|
||||
auto const linesBeg = env.rpc ("json", "account_lines",
|
||||
R"({"account": ")" + alice.human() + R"(", )"
|
||||
R"("limit": 1})");
|
||||
BEAST_EXPECT(linesBeg[jss::result][jss::lines][0u][jss::currency] == "EUR");
|
||||
BEAST_EXPECT(linesBeg[jss::result].isMember(jss::marker));
|
||||
|
||||
// alice pays 100 USD to cheri.
|
||||
env(pay(alice, cheri, USD(100)));
|
||||
env.close();
|
||||
|
||||
// Since alice paid all her USD to cheri, alice should no longer
|
||||
// have a trust line to gw1. So the old marker should now be invalid.
|
||||
auto const linesEnd = env.rpc ("json", "account_lines",
|
||||
R"({"account": ")" + alice.human() + R"(", )"
|
||||
R"("marker": ")" +
|
||||
linesBeg[jss::result][jss::marker].asString() + R"("})");
|
||||
BEAST_EXPECT(linesEnd[jss::result][jss::error_message] ==
|
||||
RPC::make_error(rpcINVALID_PARAMS)[jss::error_message]);
|
||||
}
|
||||
|
||||
void run ()
|
||||
{
|
||||
testAccountLines();
|
||||
testAccountLineDelete();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(AccountLinesRPC,app,ripple);
|
||||
|
||||
} // RPC
|
||||
} // ripple
|
||||
|
||||
337
src/test/rpc/AccountObjects_test.cpp
Normal file
337
src/test/rpc/AccountObjects_test.cpp
Normal file
@@ -0,0 +1,337 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2016 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/protocol/JsonFields.h>
|
||||
#include <ripple/json/json_value.h>
|
||||
#include <ripple/json/to_string.h>
|
||||
#include <ripple/json/json_reader.h>
|
||||
#include <ripple/test/jtx.h>
|
||||
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
static char const* bobs_account_objects[] = {
|
||||
R"json(
|
||||
{
|
||||
"Balance": {
|
||||
"currency": "USD",
|
||||
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
|
||||
"value": "-1000"
|
||||
},
|
||||
"Flags": 131072,
|
||||
"HighLimit": {
|
||||
"currency": "USD",
|
||||
"issuer": "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
|
||||
"value": "1000"
|
||||
},
|
||||
"HighNode": "0000000000000000",
|
||||
"LedgerEntryType": "RippleState",
|
||||
"LowLimit": {
|
||||
"currency": "USD",
|
||||
"issuer": "r32rQHyesiTtdWFU7UJVtff4nCR5SHCbJW",
|
||||
"value": "0"
|
||||
},
|
||||
"LowNode": "0000000000000000",
|
||||
"index":
|
||||
"D89BC239086183EB9458C396E643795C1134963E6550E682A190A5F021766D43"
|
||||
})json"
|
||||
,
|
||||
R"json(
|
||||
{
|
||||
"Balance": {
|
||||
"currency": "USD",
|
||||
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
|
||||
"value": "-1000"
|
||||
},
|
||||
"Flags": 131072,
|
||||
"HighLimit": {
|
||||
"currency": "USD",
|
||||
"issuer": "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
|
||||
"value": "1000"
|
||||
},
|
||||
"HighNode": "0000000000000000",
|
||||
"LedgerEntryType": "RippleState",
|
||||
"LowLimit": {
|
||||
"currency": "USD",
|
||||
"issuer": "r9cZvwKU3zzuZK9JFovGg1JC5n7QiqNL8L",
|
||||
"value": "0"
|
||||
},
|
||||
"LowNode": "0000000000000000",
|
||||
"index":
|
||||
"D13183BCFFC9AAC9F96AEBB5F66E4A652AD1F5D10273AEB615478302BEBFD4A4"
|
||||
})json"
|
||||
,
|
||||
R"json(
|
||||
{
|
||||
"Account": "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
|
||||
"BookDirectory":
|
||||
"50AD0A9E54D2B381288D535EB724E4275FFBF41580D28A925D038D7EA4C68000",
|
||||
"BookNode": "0000000000000000",
|
||||
"Flags": 65536,
|
||||
"LedgerEntryType": "Offer",
|
||||
"OwnerNode": "0000000000000000",
|
||||
"Sequence": 4,
|
||||
"TakerGets": {
|
||||
"currency": "USD",
|
||||
"issuer": "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
|
||||
"value": "1"
|
||||
},
|
||||
"TakerPays": "100000000",
|
||||
"index":
|
||||
"A984D036A0E562433A8377CA57D1A1E056E58C0D04818F8DFD3A1AA3F217DD82"
|
||||
})json"
|
||||
,
|
||||
R"json(
|
||||
{
|
||||
"Account": "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
|
||||
"BookDirectory":
|
||||
"B025997A323F5C3E03DDF1334471F5984ABDE31C59D463525D038D7EA4C68000",
|
||||
"BookNode": "0000000000000000",
|
||||
"Flags": 65536,
|
||||
"LedgerEntryType": "Offer",
|
||||
"OwnerNode": "0000000000000000",
|
||||
"Sequence": 5,
|
||||
"TakerGets": {
|
||||
"currency": "USD",
|
||||
"issuer" : "r32rQHyesiTtdWFU7UJVtff4nCR5SHCbJW",
|
||||
"value" : "1"
|
||||
},
|
||||
"TakerPays" : "100000000",
|
||||
"index" :
|
||||
"CAFE32332D752387B01083B60CC63069BA4A969C9730836929F841450F6A718E"
|
||||
}
|
||||
)json"
|
||||
};
|
||||
|
||||
class AccountObjects_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
void testErrors()
|
||||
{
|
||||
testcase("error cases");
|
||||
|
||||
using namespace jtx;
|
||||
Env env(*this);
|
||||
|
||||
// test error on no account
|
||||
{
|
||||
auto resp = env.rpc("account_objects");
|
||||
BEAST_EXPECT( resp[jss::result][jss::error_message] ==
|
||||
"Missing field 'account'.");
|
||||
}
|
||||
// test error on malformed account string.
|
||||
{
|
||||
Json::Value params;
|
||||
params[jss::account] = "n94JNrQYkDrpt62bbSR7nVEhdyAvcJXRAsjEkFYyqRkh9SUTYEqV";
|
||||
auto resp = env.rpc("json", "account_objects", to_string(params));
|
||||
BEAST_EXPECT( resp[jss::result][jss::error_message] == "Disallowed seed.");
|
||||
}
|
||||
// test error on account that's not in the ledger.
|
||||
{
|
||||
Json::Value params;
|
||||
params[jss::account] = Account{ "bogie" }.human();
|
||||
auto resp = env.rpc("json", "account_objects", to_string(params));
|
||||
BEAST_EXPECT( resp[jss::result][jss::error_message] == "Account not found.");
|
||||
}
|
||||
Account const bob{ "bob" };
|
||||
// test error on large ledger_index.
|
||||
{
|
||||
Json::Value params;
|
||||
params[jss::account] = bob.human();
|
||||
params[jss::ledger_index] = 10;
|
||||
auto resp = env.rpc("json", "account_objects", to_string(params));
|
||||
BEAST_EXPECT( resp[jss::result][jss::error_message] == "ledgerNotFound");
|
||||
}
|
||||
|
||||
env.fund(XRP(1000), bob);
|
||||
// test error on type param not a string
|
||||
{
|
||||
Json::Value params;
|
||||
params[jss::account] = bob.human();
|
||||
params[jss::type] = 10;
|
||||
auto resp = env.rpc("json", "account_objects", to_string(params));
|
||||
BEAST_EXPECT( resp[jss::result][jss::error_message] ==
|
||||
"Invalid field 'type', not string.");
|
||||
}
|
||||
// test error on type param not a valid type
|
||||
{
|
||||
Json::Value params;
|
||||
params[jss::account] = bob.human();
|
||||
params[jss::type] = "expedited";
|
||||
auto resp = env.rpc("json", "account_objects", to_string(params));
|
||||
BEAST_EXPECT( resp[jss::result][jss::error_message] == "Invalid field 'type'.");
|
||||
}
|
||||
// test error on limit -ve
|
||||
{
|
||||
Json::Value params;
|
||||
params[jss::account] = bob.human();
|
||||
params[jss::limit] = -1;
|
||||
auto resp = env.rpc("json", "account_objects", to_string(params));
|
||||
BEAST_EXPECT( resp[jss::result][jss::error_message] ==
|
||||
"Invalid field 'limit', not unsigned integer.");
|
||||
}
|
||||
// test errors on marker
|
||||
{
|
||||
Account const gw{ "G" };
|
||||
env.fund(XRP(1000), gw);
|
||||
auto const USD = gw["USD"];
|
||||
env.trust(USD(1000), bob);
|
||||
env(pay(gw, bob, XRP(1)));
|
||||
env(offer(bob, XRP(100), bob["USD"](1)), txflags(tfPassive));
|
||||
|
||||
Json::Value params;
|
||||
params[jss::account] = bob.human();
|
||||
params[jss::limit] = 1;
|
||||
auto resp = env.rpc("json", "account_objects", to_string(params));
|
||||
|
||||
auto resume_marker = resp[jss::result][jss::marker];
|
||||
std::string mark = to_string(resume_marker);
|
||||
params[jss::marker] = 10;
|
||||
resp = env.rpc("json", "account_objects", to_string(params));
|
||||
BEAST_EXPECT( resp[jss::result][jss::error_message] == "Invalid field 'marker', not string.");
|
||||
|
||||
params[jss::marker] = "This is a string with no comma";
|
||||
resp = env.rpc("json", "account_objects", to_string(params));
|
||||
BEAST_EXPECT( resp[jss::result][jss::error_message] == "Invalid field 'marker'.");
|
||||
|
||||
params[jss::marker] = "This string has a comma, but is not hex";
|
||||
resp = env.rpc("json", "account_objects", to_string(params));
|
||||
BEAST_EXPECT( resp[jss::result][jss::error_message] == "Invalid field 'marker'.");
|
||||
|
||||
params[jss::marker] = std::string(&mark[1U], 64);
|
||||
resp = env.rpc("json", "account_objects", to_string(params));
|
||||
BEAST_EXPECT( resp[jss::result][jss::error_message] == "Invalid field 'marker'.");
|
||||
|
||||
params[jss::marker] = std::string(&mark[1U], 65);
|
||||
resp = env.rpc("json", "account_objects", to_string(params));
|
||||
BEAST_EXPECT( resp[jss::result][jss::error_message] == "Invalid field 'marker'.");
|
||||
|
||||
params[jss::marker] = std::string(&mark[1U], 65) + "not hex";
|
||||
resp = env.rpc("json", "account_objects", to_string(params));
|
||||
BEAST_EXPECT( resp[jss::result][jss::error_message] == "Invalid field 'marker'.");
|
||||
|
||||
// Should this be an error?
|
||||
// A hex digit is absent from the end of marker.
|
||||
// No account objects returned.
|
||||
params[jss::marker] = std::string(&mark[1U], 128);
|
||||
resp = env.rpc("json", "account_objects", to_string(params));
|
||||
BEAST_EXPECT( resp[jss::result][jss::account_objects].size() == 0);
|
||||
}
|
||||
}
|
||||
|
||||
void testUnsteppedThenStepped()
|
||||
{
|
||||
testcase("unsteppedThenStepped");
|
||||
|
||||
using namespace jtx;
|
||||
Env env(*this);
|
||||
|
||||
Account const gw1{ "G1" };
|
||||
Account const gw2{ "G2" };
|
||||
Account const bob{ "bob" };
|
||||
|
||||
auto const USD1 = gw1["USD"];
|
||||
auto const USD2 = gw2["USD"];
|
||||
|
||||
env.fund(XRP(1000), gw1, gw2, bob);
|
||||
env.trust(USD1(1000), bob);
|
||||
env.trust(USD2(1000), bob);
|
||||
|
||||
env(pay(gw1, bob, USD1(1000)));
|
||||
env(pay(gw2, bob, USD2(1000)));
|
||||
|
||||
env(offer(bob, XRP(100), bob["USD"](1)),txflags(tfPassive));
|
||||
env(offer(bob, XRP(100), USD1(1)), txflags(tfPassive));
|
||||
|
||||
Json::Value bobj[4];
|
||||
for (int i = 0; i < 4; ++i)
|
||||
Json::Reader{}.parse(bobs_account_objects[i], bobj[i]);
|
||||
|
||||
// test 'unstepped'
|
||||
// i.e. request account objects without explicit limit/marker paging
|
||||
{
|
||||
Json::Value params;
|
||||
params[jss::account] = bob.human();
|
||||
auto resp = env.rpc("json", "account_objects", to_string(params));
|
||||
BEAST_EXPECT( !resp.isMember(jss::marker));
|
||||
|
||||
BEAST_EXPECT( resp[jss::result][jss::account_objects].size() == 4);
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
auto& aobj = resp[jss::result][jss::account_objects][i];
|
||||
aobj.removeMember("PreviousTxnID");
|
||||
aobj.removeMember("PreviousTxnLgrSeq");
|
||||
|
||||
BEAST_EXPECT( aobj == bobj[i]);
|
||||
}
|
||||
}
|
||||
// test request with type parameter as filter, unstepped
|
||||
{
|
||||
Json::Value params;
|
||||
params[jss::account] = bob.human();
|
||||
params[jss::type] = "state";
|
||||
auto resp = env.rpc("json", "account_objects", to_string(params));
|
||||
BEAST_EXPECT( !resp.isMember(jss::marker));
|
||||
|
||||
BEAST_EXPECT( resp[jss::result][jss::account_objects].size() == 2);
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
auto& aobj = resp[jss::result][jss::account_objects][i];
|
||||
aobj.removeMember("PreviousTxnID");
|
||||
aobj.removeMember("PreviousTxnLgrSeq");
|
||||
|
||||
BEAST_EXPECT( aobj == bobj[i]);
|
||||
}
|
||||
}
|
||||
// test stepped one-at-a-time with limit=1, resume from prev marker
|
||||
{
|
||||
Json::Value params;
|
||||
params[jss::account] = bob.human();
|
||||
params[jss::limit] = 1;
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
auto resp = env.rpc("json", "account_objects", to_string(params));
|
||||
auto& aobjs = resp[jss::result][jss::account_objects];
|
||||
BEAST_EXPECT( aobjs.size() == 1);
|
||||
auto& aobj = aobjs[0U];
|
||||
if (i < 3) BEAST_EXPECT( resp[jss::result][jss::limit] == 1);
|
||||
|
||||
aobj.removeMember("PreviousTxnID");
|
||||
aobj.removeMember("PreviousTxnLgrSeq");
|
||||
BEAST_EXPECT( aobj == bobj[i]);
|
||||
|
||||
auto resume_marker = resp[jss::result][jss::marker];
|
||||
params[jss::marker] = resume_marker;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void run() override
|
||||
{
|
||||
testErrors();
|
||||
testUnsteppedThenStepped();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(AccountObjects,app,ripple);
|
||||
|
||||
}
|
||||
}
|
||||
300
src/test/rpc/AccountOffers_test.cpp
Normal file
300
src/test/rpc/AccountOffers_test.cpp
Normal file
@@ -0,0 +1,300 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2016 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/protocol/JsonFields.h>
|
||||
#include <ripple/test/jtx.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
class AccountOffers_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
static
|
||||
std::unique_ptr<Config>
|
||||
makeConfig(bool setup_admin)
|
||||
{
|
||||
auto p = std::make_unique<Config>();
|
||||
setupConfigForUnitTests(*p);
|
||||
// the default config has admin active
|
||||
// ...we remove them if setup_admin is false
|
||||
if (! setup_admin)
|
||||
{
|
||||
(*p)["port_rpc"].set("admin","");
|
||||
(*p)["port_ws"].set("admin","");
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
// test helper
|
||||
static bool checkArraySize(Json::Value const& val, unsigned int size)
|
||||
{
|
||||
return val.isArray() &&
|
||||
val.size() == size;
|
||||
}
|
||||
|
||||
// test helper
|
||||
static bool checkMarker(Json::Value const& val)
|
||||
{
|
||||
return val.isMember(jss::marker) &&
|
||||
val[jss::marker].isString() &&
|
||||
val[jss::marker].asString().size() > 0;
|
||||
}
|
||||
|
||||
void testNonAdminMinLimit()
|
||||
{
|
||||
using namespace jtx;
|
||||
Env env(*this, makeConfig(false));
|
||||
Account const gw ("G1");
|
||||
auto const USD_gw = gw["USD"];
|
||||
Account const bob ("bob");
|
||||
auto const USD_bob = bob["USD"];
|
||||
|
||||
env.fund(XRP(10000), gw, bob);
|
||||
env.trust(USD_gw(1000), bob);
|
||||
|
||||
// this is to provide some USD from gw in the
|
||||
// bob account so that it can rightly
|
||||
// make offers that give those USDs
|
||||
env (pay (gw, bob, USD_gw (10)));
|
||||
unsigned const offer_count = 12u;
|
||||
for (auto i = 0u; i < offer_count; i++)
|
||||
{
|
||||
Json::Value jvo = offer(bob, XRP(100 + i), USD_gw(1));
|
||||
jvo[sfExpiration.fieldName] = 10000000u;
|
||||
env(jvo);
|
||||
}
|
||||
|
||||
// make non-limited RPC call
|
||||
auto const jro_nl = env.rpc("account_offers", bob.human())[jss::result][jss::offers];
|
||||
BEAST_EXPECT(checkArraySize(jro_nl, offer_count));
|
||||
|
||||
// now make a low-limit query, should get "corrected"
|
||||
// to a min of 10 results with a marker set since there
|
||||
// are more than 10 total
|
||||
Json::Value jvParams;
|
||||
jvParams[jss::account] = bob.human();
|
||||
jvParams[jss::limit] = 1u;
|
||||
auto const jrr_l = env.rpc ("json", "account_offers", jvParams.toStyledString())[jss::result];
|
||||
auto const& jro_l = jrr_l[jss::offers];
|
||||
BEAST_EXPECT(checkMarker(jrr_l));
|
||||
BEAST_EXPECT(checkArraySize(jro_l, 10u));
|
||||
}
|
||||
|
||||
void testSequential(bool as_admin)
|
||||
{
|
||||
using namespace jtx;
|
||||
Env env(*this, makeConfig(as_admin));
|
||||
Account const gw ("G1");
|
||||
auto const USD_gw = gw["USD"];
|
||||
Account const bob ("bob");
|
||||
auto const USD_bob = bob["USD"];
|
||||
|
||||
env.fund(XRP(10000), gw, bob);
|
||||
env.trust(USD_gw(1000), bob);
|
||||
|
||||
// this is to provide some USD from gw in the
|
||||
// bob account so that it can rightly
|
||||
// make offers that give those USDs
|
||||
env (pay (gw, bob, USD_gw (10)));
|
||||
|
||||
env(offer(bob, XRP(100), USD_bob(1)));
|
||||
env(offer(bob, XRP(100), USD_gw(1)));
|
||||
env(offer(bob, XRP(10), USD_gw(2)));
|
||||
|
||||
// make the RPC call
|
||||
auto const jro = env.rpc("account_offers", bob.human())[jss::result][jss::offers];
|
||||
if(BEAST_EXPECT(checkArraySize(jro, 3u)))
|
||||
{
|
||||
BEAST_EXPECT(jro[0u][jss::quality] == "100000000");
|
||||
BEAST_EXPECT(jro[0u][jss::taker_gets][jss::currency] == "USD");
|
||||
BEAST_EXPECT(jro[0u][jss::taker_gets][jss::issuer] == bob.human());
|
||||
BEAST_EXPECT(jro[0u][jss::taker_gets][jss::value] == "1");
|
||||
BEAST_EXPECT(jro[0u][jss::taker_pays] == "100000000");
|
||||
|
||||
BEAST_EXPECT(jro[1u][jss::quality] == "100000000");
|
||||
BEAST_EXPECT(jro[1u][jss::taker_gets][jss::currency] == "USD");
|
||||
BEAST_EXPECT(jro[1u][jss::taker_gets][jss::issuer] == gw.human());
|
||||
BEAST_EXPECT(jro[1u][jss::taker_gets][jss::value] == "1");
|
||||
BEAST_EXPECT(jro[1u][jss::taker_pays] == "100000000");
|
||||
|
||||
BEAST_EXPECT(jro[2u][jss::quality] == "5000000");
|
||||
BEAST_EXPECT(jro[2u][jss::taker_gets][jss::currency] == "USD");
|
||||
BEAST_EXPECT(jro[2u][jss::taker_gets][jss::issuer] == gw.human());
|
||||
BEAST_EXPECT(jro[2u][jss::taker_gets][jss::value] == "2");
|
||||
BEAST_EXPECT(jro[2u][jss::taker_pays] == "10000000");
|
||||
}
|
||||
|
||||
{
|
||||
// now make a limit (= 1) query for the same data
|
||||
Json::Value jvParams;
|
||||
jvParams[jss::account] = bob.human();
|
||||
jvParams[jss::limit] = 1u;
|
||||
auto const jrr_l_1 = env.rpc ("json", "account_offers", jvParams.toStyledString())[jss::result];
|
||||
auto const& jro_l_1 = jrr_l_1[jss::offers];
|
||||
// there is a difference in the validation of the limit param
|
||||
// between admin and non-admin requests. with admin requests, the
|
||||
// limit parameter is NOT subject to sane defaults, but with a
|
||||
// non-admin there are pre-configured limit ranges applied. That's
|
||||
// why we have different BEAST_EXPECT()s here for the two scenarios
|
||||
BEAST_EXPECT(checkArraySize(jro_l_1, as_admin ? 1u : 3u));
|
||||
BEAST_EXPECT(as_admin ? checkMarker(jrr_l_1) : (! jrr_l_1.isMember(jss::marker)));
|
||||
if (as_admin)
|
||||
{
|
||||
BEAST_EXPECT(jro[0u] == jro_l_1[0u]);
|
||||
|
||||
// second item...with previous marker passed
|
||||
jvParams[jss::marker] = jrr_l_1[jss::marker];
|
||||
auto const jrr_l_2 = env.rpc ("json", "account_offers", jvParams.toStyledString())[jss::result];
|
||||
auto const& jro_l_2 = jrr_l_2[jss::offers];
|
||||
BEAST_EXPECT(checkMarker(jrr_l_2));
|
||||
BEAST_EXPECT(checkArraySize(jro_l_2, 1u));
|
||||
BEAST_EXPECT(jro[1u] == jro_l_2[0u]);
|
||||
|
||||
// last item...with previous marker passed
|
||||
jvParams[jss::marker] = jrr_l_2[jss::marker];
|
||||
auto const jrr_l_3 = env.rpc ("json", "account_offers", jvParams.toStyledString())[jss::result];
|
||||
auto const& jro_l_3 = jrr_l_3[jss::offers];
|
||||
BEAST_EXPECT(! jrr_l_3.isMember(jss::marker));
|
||||
BEAST_EXPECT(checkArraySize(jro_l_3, 1u));
|
||||
BEAST_EXPECT(jro[2u] == jro_l_3[0u]);
|
||||
}
|
||||
else
|
||||
{
|
||||
BEAST_EXPECT(jro == jro_l_1);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// now make a limit (= 0) query for the same data
|
||||
// since we operate on the admin port, the limit
|
||||
// value of 0 is not adjusted into tuned ranges for admin requests so
|
||||
// we literally get 0 elements in that case. For non-admin
|
||||
// requests, we get limit defaults applied thus all our results
|
||||
// come back (we are below the min results limit)
|
||||
Json::Value jvParams;
|
||||
jvParams[jss::account] = bob.human();
|
||||
jvParams[jss::limit] = 0u;
|
||||
auto const jrr = env.rpc ("json", "account_offers", jvParams.toStyledString())[jss::result];
|
||||
auto const& jro = jrr[jss::offers];
|
||||
BEAST_EXPECT(checkArraySize(jro, as_admin ? 0u : 3u));
|
||||
BEAST_EXPECT(as_admin ? checkMarker(jrr) : (! jrr.isMember(jss::marker)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void testBadInput()
|
||||
{
|
||||
using namespace jtx;
|
||||
Env env(*this);
|
||||
Account const gw ("G1");
|
||||
auto const USD_gw = gw["USD"];
|
||||
Account const bob ("bob");
|
||||
auto const USD_bob = bob["USD"];
|
||||
|
||||
env.fund(XRP(10000), gw, bob);
|
||||
env.trust(USD_gw(1000), bob);
|
||||
|
||||
{
|
||||
// no account field
|
||||
auto const jrr = env.rpc ("account_offers")[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::status] == "error");
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Missing field 'account'.");
|
||||
}
|
||||
|
||||
{
|
||||
// empty string account
|
||||
Json::Value jvParams;
|
||||
jvParams[jss::account] = "";
|
||||
auto const jrr = env.rpc ("json", "account_offers", jvParams.toStyledString())[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "badSeed");
|
||||
BEAST_EXPECT(jrr[jss::status] == "error");
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Disallowed seed.");
|
||||
}
|
||||
|
||||
{
|
||||
// bogus account value
|
||||
auto const jrr = env.rpc ("account_offers", "rNOT_AN_ACCOUNT")[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "actNotFound");
|
||||
BEAST_EXPECT(jrr[jss::status] == "error");
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Account not found.");
|
||||
}
|
||||
|
||||
{
|
||||
// bad limit
|
||||
Json::Value jvParams;
|
||||
jvParams[jss::account] = bob.human();
|
||||
jvParams[jss::limit] = "0"; // NOT an integer
|
||||
auto const jrr = env.rpc ("json", "account_offers", jvParams.toStyledString())[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::status] == "error");
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'limit', not unsigned integer.");
|
||||
}
|
||||
|
||||
{
|
||||
// invalid marker
|
||||
Json::Value jvParams;
|
||||
jvParams[jss::account] = bob.human();
|
||||
jvParams[jss::marker] = "NOT_A_MARKER";
|
||||
auto const jrr = env.rpc ("json", "account_offers", jvParams.toStyledString())[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::status] == "error");
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Invalid parameters.");
|
||||
}
|
||||
|
||||
{
|
||||
// invalid marker - not a string
|
||||
Json::Value jvParams;
|
||||
jvParams[jss::account] = bob.human();
|
||||
jvParams[jss::marker] = 1;
|
||||
auto const jrr = env.rpc ("json", "account_offers", jvParams.toStyledString())[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::status] == "error");
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'marker', not string.");
|
||||
}
|
||||
|
||||
{
|
||||
// ask for a bad ledger index
|
||||
Json::Value jvParams;
|
||||
jvParams[jss::account] = bob.human();
|
||||
jvParams[jss::ledger_index] = 10u;
|
||||
auto const jrr = env.rpc ("json", "account_offers", jvParams.toStyledString())[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "lgrNotFound");
|
||||
BEAST_EXPECT(jrr[jss::status] == "error");
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "ledgerNotFound");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
testSequential(true);
|
||||
testSequential(false);
|
||||
testBadInput();
|
||||
testNonAdminMinLimit();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(AccountOffers,app,ripple);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
317
src/test/rpc/AccountSet_test.cpp
Normal file
317
src/test/rpc/AccountSet_test.cpp
Normal file
@@ -0,0 +1,317 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2016 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/protocol/JsonFields.h>
|
||||
#include <ripple/protocol/Feature.h>
|
||||
#include <ripple/test/jtx.h>
|
||||
#include <ripple/basics/StringUtilities.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
class AccountSet_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
|
||||
void testNullAccountSet()
|
||||
{
|
||||
using namespace test::jtx;
|
||||
Env env(*this);
|
||||
Account const alice ("alice");
|
||||
env.fund(XRP(10000), noripple(alice));
|
||||
//ask for the ledger entry - account root, to check its flags
|
||||
auto const jrr = env.le(alice);
|
||||
BEAST_EXPECT((*env.le(alice))[ sfFlags ] == 0u);
|
||||
}
|
||||
|
||||
void testSetAndReset(unsigned int flag_val, std::string const& label)
|
||||
{
|
||||
using namespace test::jtx;
|
||||
Env env(*this);
|
||||
Account const alice ("alice");
|
||||
env.fund(XRP(10000), noripple(alice));
|
||||
env.memoize("eric");
|
||||
env(regkey(alice, "eric"));
|
||||
|
||||
unsigned int orig_flags = (*env.le(alice))[ sfFlags ];
|
||||
|
||||
env.require(nflags(alice, flag_val));
|
||||
env(fset(alice, flag_val), sig(alice));
|
||||
env.require(flags(alice, flag_val));
|
||||
env(fclear(alice, flag_val));
|
||||
env.require(nflags(alice, flag_val));
|
||||
uint32 now_flags = (*env.le(alice))[ sfFlags ];
|
||||
BEAST_EXPECT(now_flags == orig_flags);
|
||||
}
|
||||
|
||||
void testSetAndResetAccountTxnID()
|
||||
{
|
||||
using namespace test::jtx;
|
||||
Env env(*this);
|
||||
Account const alice ("alice");
|
||||
env.fund(XRP(10000), noripple(alice));
|
||||
|
||||
unsigned int orig_flags = (*env.le(alice))[ sfFlags ];
|
||||
|
||||
// asfAccountTxnID is special and not actually set as a flag,
|
||||
// so we check the field presence instead
|
||||
BEAST_EXPECT(! env.le(alice)->isFieldPresent(sfAccountTxnID));
|
||||
env(fset(alice, asfAccountTxnID), sig(alice));
|
||||
BEAST_EXPECT(env.le(alice)->isFieldPresent(sfAccountTxnID));
|
||||
env(fclear(alice, asfAccountTxnID));
|
||||
BEAST_EXPECT(! env.le(alice)->isFieldPresent(sfAccountTxnID));
|
||||
uint32 now_flags = (*env.le(alice))[ sfFlags ];
|
||||
BEAST_EXPECT(now_flags == orig_flags);
|
||||
}
|
||||
|
||||
void testSetNoFreeze()
|
||||
{
|
||||
using namespace test::jtx;
|
||||
Env env(*this);
|
||||
Account const alice ("alice");
|
||||
env.fund(XRP(10000), noripple(alice));
|
||||
env.memoize("eric");
|
||||
env(regkey(alice, "eric"));
|
||||
|
||||
env.require(nflags(alice, asfNoFreeze));
|
||||
env(fset(alice, asfNoFreeze), sig("eric"), ter(tecNEED_MASTER_KEY));
|
||||
env(fset(alice, asfNoFreeze), sig(alice));
|
||||
env.require(flags(alice, asfNoFreeze));
|
||||
env(fclear(alice, asfNoFreeze), sig(alice));
|
||||
// verify flag is still set (clear does not clear in this case)
|
||||
env.require(flags(alice, asfNoFreeze));
|
||||
}
|
||||
|
||||
void testDomain()
|
||||
{
|
||||
using namespace test::jtx;
|
||||
Env env(*this);
|
||||
Account const alice ("alice");
|
||||
env.fund(XRP(10000), alice);
|
||||
auto jt = noop(alice);
|
||||
// The Domain field is represented as the hex string of the lowercase
|
||||
// ASCII of the domain. For example, the domain example.com would be
|
||||
// represented as "6578616d706c652e636f6d".
|
||||
//
|
||||
// To remove the Domain field from an account, send an AccountSet with
|
||||
// the Domain set to an empty string.
|
||||
std::string const domain = "example.com";
|
||||
jt[sfDomain.fieldName] = strHex(domain);
|
||||
env(jt);
|
||||
BEAST_EXPECT((*env.le(alice))[ sfDomain ] == makeSlice(domain));
|
||||
|
||||
jt[sfDomain.fieldName] = "";
|
||||
env(jt);
|
||||
BEAST_EXPECT(! env.le(alice)->isFieldPresent(sfDomain));
|
||||
|
||||
// The upper limit on the length is 256 bytes
|
||||
// (defined as DOMAIN_BYTES_MAX in SetAccount)
|
||||
// test the edge cases: 255, 256, 257.
|
||||
std::size_t const maxLength = 256;
|
||||
for (std::size_t len = maxLength - 1; len <= maxLength + 1; ++len)
|
||||
{
|
||||
std::string domain2 =
|
||||
std::string(len - domain.length() - 1, 'a') + "." + domain;
|
||||
|
||||
BEAST_EXPECT (domain2.length() == len);
|
||||
|
||||
jt[sfDomain.fieldName] = strHex(domain2);
|
||||
|
||||
if (len <= maxLength)
|
||||
{
|
||||
env(jt);
|
||||
BEAST_EXPECT((*env.le(alice))[ sfDomain ] == makeSlice(domain2));
|
||||
}
|
||||
else
|
||||
{
|
||||
env(jt, ter(telBAD_DOMAIN));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void testMessageKey()
|
||||
{
|
||||
using namespace test::jtx;
|
||||
Env env(*this);
|
||||
Account const alice ("alice");
|
||||
env.fund(XRP(10000), alice);
|
||||
auto jt = noop(alice);
|
||||
|
||||
auto const rkp = randomKeyPair(KeyType::ed25519);
|
||||
jt[sfMessageKey.fieldName] = strHex(rkp.first.slice());
|
||||
env(jt);
|
||||
BEAST_EXPECT(strHex((*env.le(alice))[ sfMessageKey ]) == strHex(rkp.first.slice()));
|
||||
|
||||
jt[sfMessageKey.fieldName] = "";
|
||||
env(jt);
|
||||
BEAST_EXPECT(! env.le(alice)->isFieldPresent(sfMessageKey));
|
||||
|
||||
jt[sfMessageKey.fieldName] = strHex("NOT_REALLY_A_PUBKEY");
|
||||
env(jt, ter(telBAD_PUBLIC_KEY));
|
||||
}
|
||||
|
||||
void testWalletID()
|
||||
{
|
||||
using namespace test::jtx;
|
||||
Env env(*this);
|
||||
Account const alice ("alice");
|
||||
env.fund(XRP(10000), alice);
|
||||
auto jt = noop(alice);
|
||||
|
||||
uint256 somehash = from_hex_text<uint256>("9633ec8af54f16b5286db1d7b519ef49eefc050c0c8ac4384f1d88acd1bfdf05");
|
||||
jt[sfWalletLocator.fieldName] = to_string(somehash);
|
||||
env(jt);
|
||||
BEAST_EXPECT((*env.le(alice))[ sfWalletLocator ] == somehash);
|
||||
|
||||
jt[sfWalletLocator.fieldName] = "";
|
||||
env(jt);
|
||||
BEAST_EXPECT(! env.le(alice)->isFieldPresent(sfWalletLocator));
|
||||
}
|
||||
|
||||
void testEmailHash()
|
||||
{
|
||||
using namespace test::jtx;
|
||||
Env env(*this);
|
||||
Account const alice ("alice");
|
||||
env.fund(XRP(10000), alice);
|
||||
auto jt = noop(alice);
|
||||
|
||||
uint128 somehash = from_hex_text<uint128>("fff680681c2f5e6095324e2e08838f221a72ab4f");
|
||||
jt[sfEmailHash.fieldName] = to_string(somehash);
|
||||
env(jt);
|
||||
BEAST_EXPECT((*env.le(alice))[ sfEmailHash ] == somehash);
|
||||
|
||||
jt[sfEmailHash.fieldName] = "";
|
||||
env(jt);
|
||||
BEAST_EXPECT(! env.le(alice)->isFieldPresent(sfEmailHash));
|
||||
}
|
||||
|
||||
void testTransferRate()
|
||||
{
|
||||
using namespace test::jtx;
|
||||
Env env(*this);
|
||||
Account const alice ("alice");
|
||||
env.fund(XRP(10000), alice);
|
||||
auto jt = noop(alice);
|
||||
|
||||
uint32 xfer_rate = 2000000000;
|
||||
jt[sfTransferRate.fieldName] = xfer_rate;
|
||||
env(jt);
|
||||
BEAST_EXPECT((*env.le(alice))[ sfTransferRate ] == xfer_rate);
|
||||
|
||||
jt[sfTransferRate.fieldName] = 0u;
|
||||
env(jt);
|
||||
BEAST_EXPECT(! env.le(alice)->isFieldPresent(sfTransferRate));
|
||||
|
||||
// set a bad value (< QUALITY_ONE)
|
||||
jt[sfTransferRate.fieldName] = 10u;
|
||||
env(jt, ter(temBAD_TRANSFER_RATE));
|
||||
}
|
||||
|
||||
void testBadInputs()
|
||||
{
|
||||
using namespace test::jtx;
|
||||
Env env(*this);
|
||||
Account const alice ("alice");
|
||||
env.fund(XRP(10000), alice);
|
||||
|
||||
auto jt = fset(alice, asfDisallowXRP);
|
||||
jt[jss::ClearFlag] = asfDisallowXRP;
|
||||
env(jt, ter(temINVALID_FLAG));
|
||||
|
||||
jt = fset(alice, asfRequireAuth);
|
||||
jt[jss::ClearFlag] = asfRequireAuth;
|
||||
env(jt, ter(temINVALID_FLAG));
|
||||
|
||||
jt = fset(alice, asfRequireDest);
|
||||
jt[jss::ClearFlag] = asfRequireDest;
|
||||
env(jt, ter(temINVALID_FLAG));
|
||||
|
||||
jt = fset(alice, asfDisallowXRP);
|
||||
jt[sfFlags.fieldName] = tfAllowXRP;
|
||||
env(jt, ter(temINVALID_FLAG));
|
||||
|
||||
jt = fset(alice, asfRequireAuth);
|
||||
jt[sfFlags.fieldName] = tfOptionalAuth;
|
||||
env(jt, ter(temINVALID_FLAG));
|
||||
|
||||
jt = fset(alice, asfRequireDest);
|
||||
jt[sfFlags.fieldName] = tfOptionalDestTag;
|
||||
env(jt, ter(temINVALID_FLAG));
|
||||
|
||||
jt = fset(alice, asfRequireDest);
|
||||
jt[sfFlags.fieldName] = tfAccountSetMask;
|
||||
env(jt, ter(temINVALID_FLAG));
|
||||
|
||||
env(fset (alice, asfDisableMaster),
|
||||
sig(alice), ter(tecNO_REGULAR_KEY));
|
||||
}
|
||||
|
||||
void testRequireAuthWithDir()
|
||||
{
|
||||
using namespace test::jtx;
|
||||
Env env(*this, features(featureMultiSign));
|
||||
Account const alice ("alice");
|
||||
Account const bob ("bob");
|
||||
|
||||
env.fund(XRP(10000), alice);
|
||||
env.close();
|
||||
|
||||
// alice should have an empty directory.
|
||||
BEAST_EXPECT(dirIsEmpty (*env.closed(), keylet::ownerDir(alice)));
|
||||
|
||||
// Give alice a signer list, then there will be stuff in the directory.
|
||||
env(signers(alice, 1, { { bob, 1} }));
|
||||
env.close();
|
||||
BEAST_EXPECT(! dirIsEmpty (*env.closed(), keylet::ownerDir(alice)));
|
||||
|
||||
env(fset (alice, asfRequireAuth), ter(tecOWNERS));
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
testNullAccountSet();
|
||||
for(auto const& flag_set : std::vector<std::pair<unsigned int, std::string>>({
|
||||
{asfRequireDest, "RequireDestTag"},
|
||||
{asfRequireAuth, "RequireAuth"},
|
||||
{asfDisallowXRP, "DisallowXRP"},
|
||||
{asfGlobalFreeze, "GlobalFreeze"},
|
||||
{asfDisableMaster, "DisableMaster"},
|
||||
{asfDefaultRipple, "DefaultRipple"}
|
||||
}))
|
||||
{
|
||||
testSetAndReset(flag_set.first, flag_set.second);
|
||||
}
|
||||
testSetAndResetAccountTxnID();
|
||||
testSetNoFreeze();
|
||||
testDomain();
|
||||
testMessageKey();
|
||||
testWalletID();
|
||||
testEmailHash();
|
||||
testBadInputs();
|
||||
testRequireAuthWithDir();
|
||||
testTransferRate();
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(AccountSet,app,ripple);
|
||||
|
||||
}
|
||||
|
||||
823
src/test/rpc/Book_test.cpp
Normal file
823
src/test/rpc/Book_test.cpp
Normal file
@@ -0,0 +1,823 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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>
|
||||
#include <ripple/test/WSClient.h>
|
||||
#include <ripple/test/jtx.h>
|
||||
#include <ripple/beast/unit_test.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
class Book_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
void
|
||||
testOneSideEmptyBook()
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
using namespace jtx;
|
||||
Env env(*this);
|
||||
env.fund(XRP(10000), "alice");
|
||||
auto USD = Account("alice")["USD"];
|
||||
auto wsc = makeWSClient(env.app().config());
|
||||
Json::Value books;
|
||||
|
||||
{
|
||||
// RPC subscribe to books stream
|
||||
books[jss::books] = Json::arrayValue;
|
||||
{
|
||||
auto &j = books[jss::books].append(Json::objectValue);
|
||||
j[jss::snapshot] = true;
|
||||
j[jss::taker_gets][jss::currency] = "XRP";
|
||||
j[jss::taker_pays][jss::currency] = "USD";
|
||||
j[jss::taker_pays][jss::issuer] = Account("alice").human();
|
||||
}
|
||||
|
||||
auto jv = wsc->invoke("subscribe", books);
|
||||
BEAST_EXPECT(jv[jss::status] == "success");
|
||||
BEAST_EXPECT(jv[jss::result].isMember(jss::offers) &&
|
||||
jv[jss::result][jss::offers].size() == 0);
|
||||
BEAST_EXPECT(! jv[jss::result].isMember(jss::asks));
|
||||
BEAST_EXPECT(! jv[jss::result].isMember(jss::bids));
|
||||
}
|
||||
|
||||
{
|
||||
// Create an ask: TakerPays 700, TakerGets 100/USD
|
||||
env(offer("alice", XRP(700), USD(100)),
|
||||
require(owners("alice", 1)));
|
||||
env.close();
|
||||
|
||||
// Check stream update
|
||||
BEAST_EXPECT(wsc->findMsg(5s,
|
||||
[&](auto const& jv)
|
||||
{
|
||||
auto const& t = jv[jss::transaction];
|
||||
return t[jss::TransactionType] == "OfferCreate" &&
|
||||
t[jss::TakerGets] == USD(100).value().getJson(0) &&
|
||||
t[jss::TakerPays] == XRP(700).value().getJson(0);
|
||||
}));
|
||||
}
|
||||
|
||||
{
|
||||
// Create a bid: TakerPays 100/USD, TakerGets 75
|
||||
env(offer("alice", USD(100), XRP(75)),
|
||||
require(owners("alice", 2)));
|
||||
env.close();
|
||||
BEAST_EXPECT(! wsc->getMsg(10ms));
|
||||
}
|
||||
|
||||
// RPC unsubscribe
|
||||
BEAST_EXPECT(wsc->invoke("unsubscribe",
|
||||
books)[jss::status] == "success");
|
||||
}
|
||||
|
||||
void
|
||||
testOneSideOffersInBook()
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
using namespace jtx;
|
||||
Env env(*this);
|
||||
env.fund(XRP(10000), "alice");
|
||||
auto USD = Account("alice")["USD"];
|
||||
auto wsc = makeWSClient(env.app().config());
|
||||
Json::Value books;
|
||||
|
||||
// Create an ask: TakerPays 500, TakerGets 100/USD
|
||||
env(offer("alice", XRP(500), USD(100)),
|
||||
require(owners("alice", 1)));
|
||||
|
||||
// Create a bid: TakerPays 100/USD, TakerGets 200
|
||||
env(offer("alice", USD(100), XRP(200)),
|
||||
require(owners("alice", 2)));
|
||||
env.close();
|
||||
|
||||
{
|
||||
// RPC subscribe to books stream
|
||||
books[jss::books] = Json::arrayValue;
|
||||
{
|
||||
auto &j = books[jss::books].append(Json::objectValue);
|
||||
j[jss::snapshot] = true;
|
||||
j[jss::taker_gets][jss::currency] = "XRP";
|
||||
j[jss::taker_pays][jss::currency] = "USD";
|
||||
j[jss::taker_pays][jss::issuer] = Account("alice").human();
|
||||
}
|
||||
|
||||
auto jv = wsc->invoke("subscribe", books);
|
||||
BEAST_EXPECT(jv[jss::status] == "success");
|
||||
BEAST_EXPECT(jv[jss::result].isMember(jss::offers) &&
|
||||
jv[jss::result][jss::offers].size() == 1);
|
||||
BEAST_EXPECT(jv[jss::result][jss::offers][0u][jss::TakerGets] ==
|
||||
XRP(200).value().getJson(0));
|
||||
BEAST_EXPECT(jv[jss::result][jss::offers][0u][jss::TakerPays] ==
|
||||
USD(100).value().getJson(0));
|
||||
BEAST_EXPECT(! jv[jss::result].isMember(jss::asks));
|
||||
BEAST_EXPECT(! jv[jss::result].isMember(jss::bids));
|
||||
}
|
||||
|
||||
{
|
||||
// Create an ask: TakerPays 700, TakerGets 100/USD
|
||||
env(offer("alice", XRP(700), USD(100)),
|
||||
require(owners("alice", 3)));
|
||||
env.close();
|
||||
|
||||
// Check stream update
|
||||
BEAST_EXPECT(wsc->findMsg(5s,
|
||||
[&](auto const& jv)
|
||||
{
|
||||
auto const& t = jv[jss::transaction];
|
||||
return t[jss::TransactionType] == "OfferCreate" &&
|
||||
t[jss::TakerGets] == USD(100).value().getJson(0) &&
|
||||
t[jss::TakerPays] == XRP(700).value().getJson(0);
|
||||
}));
|
||||
}
|
||||
|
||||
{
|
||||
// Create a bid: TakerPays 100/USD, TakerGets 75
|
||||
env(offer("alice", USD(100), XRP(75)),
|
||||
require(owners("alice", 4)));
|
||||
env.close();
|
||||
BEAST_EXPECT(! wsc->getMsg(10ms));
|
||||
}
|
||||
|
||||
// RPC unsubscribe
|
||||
BEAST_EXPECT(wsc->invoke("unsubscribe",
|
||||
books)[jss::status] == "success");
|
||||
}
|
||||
|
||||
void
|
||||
testBothSidesEmptyBook()
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
using namespace jtx;
|
||||
Env env(*this);
|
||||
env.fund(XRP(10000), "alice");
|
||||
auto USD = Account("alice")["USD"];
|
||||
auto wsc = makeWSClient(env.app().config());
|
||||
Json::Value books;
|
||||
|
||||
{
|
||||
// RPC subscribe to books stream
|
||||
books[jss::books] = Json::arrayValue;
|
||||
{
|
||||
auto &j = books[jss::books].append(Json::objectValue);
|
||||
j[jss::snapshot] = true;
|
||||
j[jss::both] = true;
|
||||
j[jss::taker_gets][jss::currency] = "XRP";
|
||||
j[jss::taker_pays][jss::currency] = "USD";
|
||||
j[jss::taker_pays][jss::issuer] = Account("alice").human();
|
||||
}
|
||||
|
||||
auto jv = wsc->invoke("subscribe", books);
|
||||
BEAST_EXPECT(jv[jss::status] == "success");
|
||||
BEAST_EXPECT(jv[jss::result].isMember(jss::asks) &&
|
||||
jv[jss::result][jss::asks].size() == 0);
|
||||
BEAST_EXPECT(jv[jss::result].isMember(jss::bids) &&
|
||||
jv[jss::result][jss::bids].size() == 0);
|
||||
BEAST_EXPECT(! jv[jss::result].isMember(jss::offers));
|
||||
}
|
||||
|
||||
{
|
||||
// Create an ask: TakerPays 700, TakerGets 100/USD
|
||||
env(offer("alice", XRP(700), USD(100)),
|
||||
require(owners("alice", 1)));
|
||||
env.close();
|
||||
|
||||
// Check stream update
|
||||
BEAST_EXPECT(wsc->findMsg(5s,
|
||||
[&](auto const& jv)
|
||||
{
|
||||
auto const& t = jv[jss::transaction];
|
||||
return t[jss::TransactionType] == "OfferCreate" &&
|
||||
t[jss::TakerGets] == USD(100).value().getJson(0) &&
|
||||
t[jss::TakerPays] == XRP(700).value().getJson(0);
|
||||
}));
|
||||
}
|
||||
|
||||
{
|
||||
// Create a bid: TakerPays 100/USD, TakerGets 75
|
||||
env(offer("alice", USD(100), XRP(75)),
|
||||
require(owners("alice", 2)));
|
||||
env.close();
|
||||
|
||||
// Check stream update
|
||||
BEAST_EXPECT(wsc->findMsg(5s,
|
||||
[&](auto const& jv)
|
||||
{
|
||||
auto const& t = jv[jss::transaction];
|
||||
return t[jss::TransactionType] == "OfferCreate" &&
|
||||
t[jss::TakerGets] == XRP(75).value().getJson(0) &&
|
||||
t[jss::TakerPays] == USD(100).value().getJson(0);
|
||||
}));
|
||||
}
|
||||
|
||||
// RPC unsubscribe
|
||||
BEAST_EXPECT(wsc->invoke("unsubscribe",
|
||||
books)[jss::status] == "success");
|
||||
}
|
||||
|
||||
void
|
||||
testBothSidesOffersInBook()
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
using namespace jtx;
|
||||
Env env(*this);
|
||||
env.fund(XRP(10000), "alice");
|
||||
auto USD = Account("alice")["USD"];
|
||||
auto wsc = makeWSClient(env.app().config());
|
||||
Json::Value books;
|
||||
|
||||
// Create an ask: TakerPays 500, TakerGets 100/USD
|
||||
env(offer("alice", XRP(500), USD(100)),
|
||||
require(owners("alice", 1)));
|
||||
|
||||
// Create a bid: TakerPays 100/USD, TakerGets 200
|
||||
env(offer("alice", USD(100), XRP(200)),
|
||||
require(owners("alice", 2)));
|
||||
env.close();
|
||||
|
||||
{
|
||||
// RPC subscribe to books stream
|
||||
books[jss::books] = Json::arrayValue;
|
||||
{
|
||||
auto &j = books[jss::books].append(Json::objectValue);
|
||||
j[jss::snapshot] = true;
|
||||
j[jss::both] = true;
|
||||
j[jss::taker_gets][jss::currency] = "XRP";
|
||||
j[jss::taker_pays][jss::currency] = "USD";
|
||||
j[jss::taker_pays][jss::issuer] = Account("alice").human();
|
||||
}
|
||||
|
||||
auto jv = wsc->invoke("subscribe", books);
|
||||
BEAST_EXPECT(jv[jss::status] == "success");
|
||||
BEAST_EXPECT(jv[jss::result].isMember(jss::asks) &&
|
||||
jv[jss::result][jss::asks].size() == 1);
|
||||
BEAST_EXPECT(jv[jss::result].isMember(jss::bids) &&
|
||||
jv[jss::result][jss::bids].size() == 1);
|
||||
BEAST_EXPECT(jv[jss::result][jss::asks][0u][jss::TakerGets] ==
|
||||
USD(100).value().getJson(0));
|
||||
BEAST_EXPECT(jv[jss::result][jss::asks][0u][jss::TakerPays] ==
|
||||
XRP(500).value().getJson(0));
|
||||
BEAST_EXPECT(jv[jss::result][jss::bids][0u][jss::TakerGets] ==
|
||||
XRP(200).value().getJson(0));
|
||||
BEAST_EXPECT(jv[jss::result][jss::bids][0u][jss::TakerPays] ==
|
||||
USD(100).value().getJson(0));
|
||||
BEAST_EXPECT(! jv[jss::result].isMember(jss::offers));
|
||||
}
|
||||
|
||||
{
|
||||
// Create an ask: TakerPays 700, TakerGets 100/USD
|
||||
env(offer("alice", XRP(700), USD(100)),
|
||||
require(owners("alice", 3)));
|
||||
env.close();
|
||||
|
||||
// Check stream update
|
||||
BEAST_EXPECT(wsc->findMsg(5s,
|
||||
[&](auto const& jv)
|
||||
{
|
||||
auto const& t = jv[jss::transaction];
|
||||
return t[jss::TransactionType] == "OfferCreate" &&
|
||||
t[jss::TakerGets] == USD(100).value().getJson(0) &&
|
||||
t[jss::TakerPays] == XRP(700).value().getJson(0);
|
||||
}));
|
||||
}
|
||||
|
||||
{
|
||||
// Create a bid: TakerPays 100/USD, TakerGets 75
|
||||
env(offer("alice", USD(100), XRP(75)),
|
||||
require(owners("alice", 4)));
|
||||
env.close();
|
||||
|
||||
// Check stream update
|
||||
BEAST_EXPECT(wsc->findMsg(5s,
|
||||
[&](auto const& jv)
|
||||
{
|
||||
auto const& t = jv[jss::transaction];
|
||||
return t[jss::TransactionType] == "OfferCreate" &&
|
||||
t[jss::TakerGets] == XRP(75).value().getJson(0) &&
|
||||
t[jss::TakerPays] == USD(100).value().getJson(0);
|
||||
}));
|
||||
}
|
||||
|
||||
// RPC unsubscribe
|
||||
BEAST_EXPECT(wsc->invoke("unsubscribe",
|
||||
books)[jss::status] == "success");
|
||||
}
|
||||
|
||||
void
|
||||
testMultipleBooksOneSideEmptyBook()
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
using namespace jtx;
|
||||
Env env(*this);
|
||||
env.fund(XRP(10000), "alice");
|
||||
auto USD = Account("alice")["USD"];
|
||||
auto CNY = Account("alice")["CNY"];
|
||||
auto JPY = Account("alice")["JPY"];
|
||||
auto wsc = makeWSClient(env.app().config());
|
||||
Json::Value books;
|
||||
|
||||
{
|
||||
// RPC subscribe to books stream
|
||||
books[jss::books] = Json::arrayValue;
|
||||
{
|
||||
auto &j = books[jss::books].append(Json::objectValue);
|
||||
j[jss::snapshot] = true;
|
||||
j[jss::taker_gets][jss::currency] = "XRP";
|
||||
j[jss::taker_pays][jss::currency] = "USD";
|
||||
j[jss::taker_pays][jss::issuer] = Account("alice").human();
|
||||
}
|
||||
{
|
||||
auto &j = books[jss::books].append(Json::objectValue);
|
||||
j[jss::snapshot] = true;
|
||||
j[jss::taker_gets][jss::currency] = "CNY";
|
||||
j[jss::taker_gets][jss::issuer] = Account("alice").human();
|
||||
j[jss::taker_pays][jss::currency] = "JPY";
|
||||
j[jss::taker_pays][jss::issuer] = Account("alice").human();
|
||||
}
|
||||
|
||||
auto jv = wsc->invoke("subscribe", books);
|
||||
BEAST_EXPECT(jv[jss::status] == "success");
|
||||
BEAST_EXPECT(jv[jss::result].isMember(jss::offers) &&
|
||||
jv[jss::result][jss::offers].size() == 0);
|
||||
BEAST_EXPECT(! jv[jss::result].isMember(jss::asks));
|
||||
BEAST_EXPECT(! jv[jss::result].isMember(jss::bids));
|
||||
}
|
||||
|
||||
{
|
||||
// Create an ask: TakerPays 700, TakerGets 100/USD
|
||||
env(offer("alice", XRP(700), USD(100)),
|
||||
require(owners("alice", 1)));
|
||||
env.close();
|
||||
|
||||
// Check stream update
|
||||
BEAST_EXPECT(wsc->findMsg(5s,
|
||||
[&](auto const& jv)
|
||||
{
|
||||
auto const& t = jv[jss::transaction];
|
||||
return t[jss::TransactionType] == "OfferCreate" &&
|
||||
t[jss::TakerGets] == USD(100).value().getJson(0) &&
|
||||
t[jss::TakerPays] == XRP(700).value().getJson(0);
|
||||
}));
|
||||
}
|
||||
|
||||
{
|
||||
// Create a bid: TakerPays 100/USD, TakerGets 75
|
||||
env(offer("alice", USD(100), XRP(75)),
|
||||
require(owners("alice", 2)));
|
||||
env.close();
|
||||
BEAST_EXPECT(! wsc->getMsg(10ms));
|
||||
}
|
||||
|
||||
{
|
||||
// Create an ask: TakerPays 700/CNY, TakerGets 100/JPY
|
||||
env(offer("alice", CNY(700), JPY(100)),
|
||||
require(owners("alice", 3)));
|
||||
env.close();
|
||||
|
||||
// Check stream update
|
||||
BEAST_EXPECT(wsc->findMsg(5s,
|
||||
[&](auto const& jv)
|
||||
{
|
||||
auto const& t = jv[jss::transaction];
|
||||
return t[jss::TransactionType] == "OfferCreate" &&
|
||||
t[jss::TakerGets] == JPY(100).value().getJson(0) &&
|
||||
t[jss::TakerPays] == CNY(700).value().getJson(0);
|
||||
}));
|
||||
}
|
||||
|
||||
{
|
||||
// Create a bid: TakerPays 100/JPY, TakerGets 75/CNY
|
||||
env(offer("alice", JPY(100), CNY(75)),
|
||||
require(owners("alice", 4)));
|
||||
env.close();
|
||||
BEAST_EXPECT(! wsc->getMsg(10ms));
|
||||
}
|
||||
|
||||
// RPC unsubscribe
|
||||
BEAST_EXPECT(wsc->invoke("unsubscribe",
|
||||
books)[jss::status] == "success");
|
||||
}
|
||||
|
||||
void
|
||||
testMultipleBooksOneSideOffersInBook()
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
using namespace jtx;
|
||||
Env env(*this);
|
||||
env.fund(XRP(10000), "alice");
|
||||
auto USD = Account("alice")["USD"];
|
||||
auto CNY = Account("alice")["CNY"];
|
||||
auto JPY = Account("alice")["JPY"];
|
||||
auto wsc = makeWSClient(env.app().config());
|
||||
Json::Value books;
|
||||
|
||||
// Create an ask: TakerPays 500, TakerGets 100/USD
|
||||
env(offer("alice", XRP(500), USD(100)),
|
||||
require(owners("alice", 1)));
|
||||
|
||||
// Create an ask: TakerPays 500/CNY, TakerGets 100/JPY
|
||||
env(offer("alice", CNY(500), JPY(100)),
|
||||
require(owners("alice", 2)));
|
||||
|
||||
// Create a bid: TakerPays 100/USD, TakerGets 200
|
||||
env(offer("alice", USD(100), XRP(200)),
|
||||
require(owners("alice", 3)));
|
||||
|
||||
// Create a bid: TakerPays 100/JPY, TakerGets 200/CNY
|
||||
env(offer("alice", JPY(100), CNY(200)),
|
||||
require(owners("alice", 4)));
|
||||
env.close();
|
||||
|
||||
{
|
||||
// RPC subscribe to books stream
|
||||
books[jss::books] = Json::arrayValue;
|
||||
{
|
||||
auto &j = books[jss::books].append(Json::objectValue);
|
||||
j[jss::snapshot] = true;
|
||||
j[jss::taker_gets][jss::currency] = "XRP";
|
||||
j[jss::taker_pays][jss::currency] = "USD";
|
||||
j[jss::taker_pays][jss::issuer] = Account("alice").human();
|
||||
}
|
||||
{
|
||||
auto &j = books[jss::books].append(Json::objectValue);
|
||||
j[jss::snapshot] = true;
|
||||
j[jss::taker_gets][jss::currency] = "CNY";
|
||||
j[jss::taker_gets][jss::issuer] = Account("alice").human();
|
||||
j[jss::taker_pays][jss::currency] = "JPY";
|
||||
j[jss::taker_pays][jss::issuer] = Account("alice").human();
|
||||
}
|
||||
|
||||
auto jv = wsc->invoke("subscribe", books);
|
||||
BEAST_EXPECT(jv[jss::status] == "success");
|
||||
BEAST_EXPECT(jv[jss::result].isMember(jss::offers) &&
|
||||
jv[jss::result][jss::offers].size() == 2);
|
||||
BEAST_EXPECT(jv[jss::result][jss::offers][0u][jss::TakerGets] ==
|
||||
XRP(200).value().getJson(0));
|
||||
BEAST_EXPECT(jv[jss::result][jss::offers][0u][jss::TakerPays] ==
|
||||
USD(100).value().getJson(0));
|
||||
BEAST_EXPECT(jv[jss::result][jss::offers][1u][jss::TakerGets] ==
|
||||
CNY(200).value().getJson(0));
|
||||
BEAST_EXPECT(jv[jss::result][jss::offers][1u][jss::TakerPays] ==
|
||||
JPY(100).value().getJson(0));
|
||||
BEAST_EXPECT(! jv[jss::result].isMember(jss::asks));
|
||||
BEAST_EXPECT(! jv[jss::result].isMember(jss::bids));
|
||||
}
|
||||
|
||||
{
|
||||
// Create an ask: TakerPays 700, TakerGets 100/USD
|
||||
env(offer("alice", XRP(700), USD(100)),
|
||||
require(owners("alice", 5)));
|
||||
env.close();
|
||||
|
||||
// Check stream update
|
||||
BEAST_EXPECT(wsc->findMsg(5s,
|
||||
[&](auto const& jv)
|
||||
{
|
||||
auto const& t = jv[jss::transaction];
|
||||
return t[jss::TransactionType] == "OfferCreate" &&
|
||||
t[jss::TakerGets] == USD(100).value().getJson(0) &&
|
||||
t[jss::TakerPays] == XRP(700).value().getJson(0);
|
||||
}));
|
||||
}
|
||||
|
||||
{
|
||||
// Create a bid: TakerPays 100/USD, TakerGets 75
|
||||
env(offer("alice", USD(100), XRP(75)),
|
||||
require(owners("alice", 6)));
|
||||
env.close();
|
||||
BEAST_EXPECT(! wsc->getMsg(10ms));
|
||||
}
|
||||
|
||||
{
|
||||
// Create an ask: TakerPays 700/CNY, TakerGets 100/JPY
|
||||
env(offer("alice", CNY(700), JPY(100)),
|
||||
require(owners("alice", 7)));
|
||||
env.close();
|
||||
|
||||
// Check stream update
|
||||
BEAST_EXPECT(wsc->findMsg(5s,
|
||||
[&](auto const& jv)
|
||||
{
|
||||
auto const& t = jv[jss::transaction];
|
||||
return t[jss::TransactionType] == "OfferCreate" &&
|
||||
t[jss::TakerGets] == JPY(100).value().getJson(0) &&
|
||||
t[jss::TakerPays] == CNY(700).value().getJson(0);
|
||||
}));
|
||||
}
|
||||
|
||||
{
|
||||
// Create a bid: TakerPays 100/JPY, TakerGets 75/CNY
|
||||
env(offer("alice", JPY(100), CNY(75)),
|
||||
require(owners("alice", 8)));
|
||||
env.close();
|
||||
BEAST_EXPECT(! wsc->getMsg(10ms));
|
||||
}
|
||||
|
||||
// RPC unsubscribe
|
||||
BEAST_EXPECT(wsc->invoke("unsubscribe",
|
||||
books)[jss::status] == "success");
|
||||
}
|
||||
|
||||
void
|
||||
testMultipleBooksBothSidesEmptyBook()
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
using namespace jtx;
|
||||
Env env(*this);
|
||||
env.fund(XRP(10000), "alice");
|
||||
auto USD = Account("alice")["USD"];
|
||||
auto CNY = Account("alice")["CNY"];
|
||||
auto JPY = Account("alice")["JPY"];
|
||||
auto wsc = makeWSClient(env.app().config());
|
||||
Json::Value books;
|
||||
|
||||
{
|
||||
// RPC subscribe to books stream
|
||||
books[jss::books] = Json::arrayValue;
|
||||
{
|
||||
auto &j = books[jss::books].append(Json::objectValue);
|
||||
j[jss::snapshot] = true;
|
||||
j[jss::both] = true;
|
||||
j[jss::taker_gets][jss::currency] = "XRP";
|
||||
j[jss::taker_pays][jss::currency] = "USD";
|
||||
j[jss::taker_pays][jss::issuer] = Account("alice").human();
|
||||
}
|
||||
{
|
||||
auto &j = books[jss::books].append(Json::objectValue);
|
||||
j[jss::snapshot] = true;
|
||||
j[jss::both] = true;
|
||||
j[jss::taker_gets][jss::currency] = "CNY";
|
||||
j[jss::taker_gets][jss::issuer] = Account("alice").human();
|
||||
j[jss::taker_pays][jss::currency] = "JPY";
|
||||
j[jss::taker_pays][jss::issuer] = Account("alice").human();
|
||||
}
|
||||
|
||||
auto jv = wsc->invoke("subscribe", books);
|
||||
BEAST_EXPECT(jv[jss::status] == "success");
|
||||
BEAST_EXPECT(jv[jss::result].isMember(jss::asks) &&
|
||||
jv[jss::result][jss::asks].size() == 0);
|
||||
BEAST_EXPECT(jv[jss::result].isMember(jss::bids) &&
|
||||
jv[jss::result][jss::bids].size() == 0);
|
||||
BEAST_EXPECT(! jv[jss::result].isMember(jss::offers));
|
||||
}
|
||||
|
||||
{
|
||||
// Create an ask: TakerPays 700, TakerGets 100/USD
|
||||
env(offer("alice", XRP(700), USD(100)),
|
||||
require(owners("alice", 1)));
|
||||
env.close();
|
||||
|
||||
// Check stream update
|
||||
BEAST_EXPECT(wsc->findMsg(5s,
|
||||
[&](auto const& jv)
|
||||
{
|
||||
auto const& t = jv[jss::transaction];
|
||||
return t[jss::TransactionType] == "OfferCreate" &&
|
||||
t[jss::TakerGets] == USD(100).value().getJson(0) &&
|
||||
t[jss::TakerPays] == XRP(700).value().getJson(0);
|
||||
}));
|
||||
}
|
||||
|
||||
{
|
||||
// Create a bid: TakerPays 100/USD, TakerGets 75
|
||||
env(offer("alice", USD(100), XRP(75)),
|
||||
require(owners("alice", 2)));
|
||||
env.close();
|
||||
|
||||
// Check stream update
|
||||
BEAST_EXPECT(wsc->findMsg(5s,
|
||||
[&](auto const& jv)
|
||||
{
|
||||
auto const& t = jv[jss::transaction];
|
||||
return t[jss::TransactionType] == "OfferCreate" &&
|
||||
t[jss::TakerGets] == XRP(75).value().getJson(0) &&
|
||||
t[jss::TakerPays] == USD(100).value().getJson(0);
|
||||
}));
|
||||
}
|
||||
|
||||
{
|
||||
// Create an ask: TakerPays 700/CNY, TakerGets 100/JPY
|
||||
env(offer("alice", CNY(700), JPY(100)),
|
||||
require(owners("alice", 3)));
|
||||
env.close();
|
||||
|
||||
// Check stream update
|
||||
BEAST_EXPECT(wsc->findMsg(5s,
|
||||
[&](auto const& jv)
|
||||
{
|
||||
auto const& t = jv[jss::transaction];
|
||||
return t[jss::TransactionType] == "OfferCreate" &&
|
||||
t[jss::TakerGets] == JPY(100).value().getJson(0) &&
|
||||
t[jss::TakerPays] == CNY(700).value().getJson(0);
|
||||
}));
|
||||
}
|
||||
|
||||
{
|
||||
// Create a bid: TakerPays 100/JPY, TakerGets 75/CNY
|
||||
env(offer("alice", JPY(100), CNY(75)),
|
||||
require(owners("alice", 4)));
|
||||
env.close();
|
||||
|
||||
// Check stream update
|
||||
BEAST_EXPECT(wsc->findMsg(5s,
|
||||
[&](auto const& jv)
|
||||
{
|
||||
auto const& t = jv[jss::transaction];
|
||||
return t[jss::TransactionType] == "OfferCreate" &&
|
||||
t[jss::TakerGets] == CNY(75).value().getJson(0) &&
|
||||
t[jss::TakerPays] == JPY(100).value().getJson(0);
|
||||
}));
|
||||
}
|
||||
|
||||
// RPC unsubscribe
|
||||
BEAST_EXPECT(wsc->invoke("unsubscribe",
|
||||
books)[jss::status] == "success");
|
||||
}
|
||||
|
||||
void
|
||||
testMultipleBooksBothSidesOffersInBook()
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
using namespace jtx;
|
||||
Env env(*this);
|
||||
env.fund(XRP(10000), "alice");
|
||||
auto USD = Account("alice")["USD"];
|
||||
auto CNY = Account("alice")["CNY"];
|
||||
auto JPY = Account("alice")["JPY"];
|
||||
auto wsc = makeWSClient(env.app().config());
|
||||
Json::Value books;
|
||||
|
||||
// Create an ask: TakerPays 500, TakerGets 100/USD
|
||||
env(offer("alice", XRP(500), USD(100)),
|
||||
require(owners("alice", 1)));
|
||||
|
||||
// Create an ask: TakerPays 500/CNY, TakerGets 100/JPY
|
||||
env(offer("alice", CNY(500), JPY(100)),
|
||||
require(owners("alice", 2)));
|
||||
|
||||
// Create a bid: TakerPays 100/USD, TakerGets 200
|
||||
env(offer("alice", USD(100), XRP(200)),
|
||||
require(owners("alice", 3)));
|
||||
|
||||
// Create a bid: TakerPays 100/JPY, TakerGets 200/CNY
|
||||
env(offer("alice", JPY(100), CNY(200)),
|
||||
require(owners("alice", 4)));
|
||||
env.close();
|
||||
|
||||
{
|
||||
// RPC subscribe to books stream
|
||||
books[jss::books] = Json::arrayValue;
|
||||
{
|
||||
auto &j = books[jss::books].append(Json::objectValue);
|
||||
j[jss::snapshot] = true;
|
||||
j[jss::both] = true;
|
||||
j[jss::taker_gets][jss::currency] = "XRP";
|
||||
j[jss::taker_pays][jss::currency] = "USD";
|
||||
j[jss::taker_pays][jss::issuer] = Account("alice").human();
|
||||
}
|
||||
// RPC subscribe to books stream
|
||||
{
|
||||
auto &j = books[jss::books].append(Json::objectValue);
|
||||
j[jss::snapshot] = true;
|
||||
j[jss::both] = true;
|
||||
j[jss::taker_gets][jss::currency] = "CNY";
|
||||
j[jss::taker_gets][jss::issuer] = Account("alice").human();
|
||||
j[jss::taker_pays][jss::currency] = "JPY";
|
||||
j[jss::taker_pays][jss::issuer] = Account("alice").human();
|
||||
}
|
||||
|
||||
auto jv = wsc->invoke("subscribe", books);
|
||||
BEAST_EXPECT(jv[jss::status] == "success");
|
||||
BEAST_EXPECT(jv[jss::result].isMember(jss::asks) &&
|
||||
jv[jss::result][jss::asks].size() == 2);
|
||||
BEAST_EXPECT(jv[jss::result].isMember(jss::bids) &&
|
||||
jv[jss::result][jss::bids].size() == 2);
|
||||
BEAST_EXPECT(jv[jss::result][jss::asks][0u][jss::TakerGets] ==
|
||||
USD(100).value().getJson(0));
|
||||
BEAST_EXPECT(jv[jss::result][jss::asks][0u][jss::TakerPays] ==
|
||||
XRP(500).value().getJson(0));
|
||||
BEAST_EXPECT(jv[jss::result][jss::asks][1u][jss::TakerGets] ==
|
||||
JPY(100).value().getJson(0));
|
||||
BEAST_EXPECT(jv[jss::result][jss::asks][1u][jss::TakerPays] ==
|
||||
CNY(500).value().getJson(0));
|
||||
BEAST_EXPECT(jv[jss::result][jss::bids][0u][jss::TakerGets] ==
|
||||
XRP(200).value().getJson(0));
|
||||
BEAST_EXPECT(jv[jss::result][jss::bids][0u][jss::TakerPays] ==
|
||||
USD(100).value().getJson(0));
|
||||
BEAST_EXPECT(jv[jss::result][jss::bids][1u][jss::TakerGets] ==
|
||||
CNY(200).value().getJson(0));
|
||||
BEAST_EXPECT(jv[jss::result][jss::bids][1u][jss::TakerPays] ==
|
||||
JPY(100).value().getJson(0));
|
||||
BEAST_EXPECT(! jv[jss::result].isMember(jss::offers));
|
||||
}
|
||||
|
||||
{
|
||||
// Create an ask: TakerPays 700, TakerGets 100/USD
|
||||
env(offer("alice", XRP(700), USD(100)),
|
||||
require(owners("alice", 5)));
|
||||
env.close();
|
||||
|
||||
// Check stream update
|
||||
BEAST_EXPECT(wsc->findMsg(5s,
|
||||
[&](auto const& jv)
|
||||
{
|
||||
auto const& t = jv[jss::transaction];
|
||||
return t[jss::TransactionType] == "OfferCreate" &&
|
||||
t[jss::TakerGets] == USD(100).value().getJson(0) &&
|
||||
t[jss::TakerPays] == XRP(700).value().getJson(0);
|
||||
}));
|
||||
}
|
||||
|
||||
{
|
||||
// Create a bid: TakerPays 100/USD, TakerGets 75
|
||||
env(offer("alice", USD(100), XRP(75)),
|
||||
require(owners("alice", 6)));
|
||||
env.close();
|
||||
|
||||
// Check stream update
|
||||
BEAST_EXPECT(wsc->findMsg(5s,
|
||||
[&](auto const& jv)
|
||||
{
|
||||
auto const& t = jv[jss::transaction];
|
||||
return t[jss::TransactionType] == "OfferCreate" &&
|
||||
t[jss::TakerGets] == XRP(75).value().getJson(0) &&
|
||||
t[jss::TakerPays] == USD(100).value().getJson(0);
|
||||
}));
|
||||
}
|
||||
|
||||
{
|
||||
// Create an ask: TakerPays 700/CNY, TakerGets 100/JPY
|
||||
env(offer("alice", CNY(700), JPY(100)),
|
||||
require(owners("alice", 7)));
|
||||
env.close();
|
||||
|
||||
// Check stream update
|
||||
BEAST_EXPECT(wsc->findMsg(5s,
|
||||
[&](auto const& jv)
|
||||
{
|
||||
auto const& t = jv[jss::transaction];
|
||||
return t[jss::TransactionType] == "OfferCreate" &&
|
||||
t[jss::TakerGets] == JPY(100).value().getJson(0) &&
|
||||
t[jss::TakerPays] == CNY(700).value().getJson(0);
|
||||
}));
|
||||
}
|
||||
|
||||
{
|
||||
// Create a bid: TakerPays 100/JPY, TakerGets 75/CNY
|
||||
env(offer("alice", JPY(100), CNY(75)),
|
||||
require(owners("alice", 8)));
|
||||
env.close();
|
||||
|
||||
// Check stream update
|
||||
BEAST_EXPECT(wsc->findMsg(5s,
|
||||
[&](auto const& jv)
|
||||
{
|
||||
auto const& t = jv[jss::transaction];
|
||||
return t[jss::TransactionType] == "OfferCreate" &&
|
||||
t[jss::TakerGets] == CNY(75).value().getJson(0) &&
|
||||
t[jss::TakerPays] == JPY(100).value().getJson(0);
|
||||
}));
|
||||
}
|
||||
|
||||
// RPC unsubscribe
|
||||
BEAST_EXPECT(wsc->invoke("unsubscribe",
|
||||
books)[jss::status] == "success");
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testOneSideEmptyBook();
|
||||
testOneSideOffersInBook();
|
||||
|
||||
testBothSidesEmptyBook();
|
||||
testBothSidesOffersInBook();
|
||||
|
||||
testMultipleBooksOneSideEmptyBook();
|
||||
testMultipleBooksOneSideOffersInBook();
|
||||
|
||||
testMultipleBooksBothSidesEmptyBook();
|
||||
testMultipleBooksBothSidesOffersInBook();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(Book,app,ripple);
|
||||
|
||||
} // test
|
||||
} // ripple
|
||||
156
src/test/rpc/GatewayBalances_test.cpp
Normal file
156
src/test/rpc/GatewayBalances_test.cpp
Normal file
@@ -0,0 +1,156 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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>
|
||||
#include <ripple/test/WSClient.h>
|
||||
#include <ripple/test/jtx.h>
|
||||
#include <ripple/beast/unit_test.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
class GatewayBalances_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
|
||||
void
|
||||
testGWB()
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
using namespace jtx;
|
||||
Env env(*this);
|
||||
|
||||
// Gateway account and assets
|
||||
Account const alice {"alice"};
|
||||
env.fund(XRP(10000), "alice");
|
||||
auto USD = alice["USD"];
|
||||
auto CNY = alice["CNY"];
|
||||
auto JPY = alice["JPY"];
|
||||
|
||||
// Create a hotwallet
|
||||
Account const hw {"hw"};
|
||||
env.fund(XRP(10000), "hw");
|
||||
env(trust(hw, USD(10000)));
|
||||
env(trust(hw, JPY(10000)));
|
||||
env(pay(alice, hw, USD(5000)));
|
||||
env(pay(alice, hw, JPY(5000)));
|
||||
|
||||
// Create some clients
|
||||
Account const bob {"bob"};
|
||||
env.fund(XRP(10000), "bob");
|
||||
env(trust(bob, USD(100)));
|
||||
env(trust(bob, CNY(100)));
|
||||
env(pay(alice, bob, USD(50)));
|
||||
|
||||
Account const charley {"charley"};
|
||||
env.fund(XRP(10000), "charley");
|
||||
env(trust(charley, CNY(500)));
|
||||
env(trust(charley, JPY(500)));
|
||||
env(pay(alice, charley, CNY(250)));
|
||||
env(pay(alice, charley, JPY(250)));
|
||||
|
||||
Account const dave {"dave"};
|
||||
env.fund(XRP(10000), "dave");
|
||||
env(trust(dave, CNY(100)));
|
||||
env(pay(alice, dave, CNY(30)));
|
||||
|
||||
// give the gateway an asset
|
||||
env(trust(alice, charley["USD"](50)));
|
||||
env(pay(charley, alice, USD(10)));
|
||||
|
||||
// freeze dave
|
||||
env(trust(alice, dave["CNY"](0), dave, tfSetFreeze));
|
||||
|
||||
env.close();
|
||||
|
||||
auto wsc = makeWSClient(env.app().config());
|
||||
|
||||
Json::Value qry;
|
||||
qry[jss::account] = alice.human();
|
||||
qry[jss::hotwallet] = hw.human();
|
||||
|
||||
auto jv = wsc->invoke("gateway_balances", qry);
|
||||
expect(jv[jss::status] == "success");
|
||||
|
||||
auto const& result = jv[jss::result];
|
||||
expect(result[jss::account] == alice.human());
|
||||
expect(result[jss::status] == "success");
|
||||
|
||||
{
|
||||
auto const& balances = result[jss::balances];
|
||||
expect (balances.isObject(), "balances is not an object");
|
||||
expect (balances.size() == 1, "balances size is not 1");
|
||||
|
||||
auto const& hwBalance = balances[hw.human()];
|
||||
expect (hwBalance.isArray(), "hwBalance is not an array");
|
||||
expect (hwBalance.size() == 2);
|
||||
auto c1 = hwBalance[0u][jss::currency];
|
||||
auto c2 = hwBalance[1u][jss::currency];
|
||||
expect (c1 == "USD" || c2 == "USD");
|
||||
expect (c1 == "JPY" || c2 == "JPY");
|
||||
expect (hwBalance[0u][jss::value] == "5000" &&
|
||||
hwBalance[1u][jss::value] == "5000");
|
||||
}
|
||||
|
||||
{
|
||||
auto const& fBalances = result[jss::frozen_balances];
|
||||
expect (fBalances.isObject());
|
||||
expect (fBalances.size() == 1);
|
||||
|
||||
auto const& fBal = fBalances[dave.human()];
|
||||
expect (fBal.isArray());
|
||||
expect (fBal.size() == 1);
|
||||
expect (fBal[0u].isObject());
|
||||
expect (fBal[0u][jss::currency] == "CNY");
|
||||
expect (fBal[0u][jss::value] == "30");
|
||||
}
|
||||
|
||||
{
|
||||
auto const& assets = result[jss::assets];
|
||||
expect (assets.isObject(), "assets it not an object");
|
||||
expect (assets.size() == 1, "assets size is not 1");
|
||||
|
||||
auto const& cAssets = assets[charley.human()];
|
||||
expect (cAssets.isArray());
|
||||
expect (cAssets.size() == 1);
|
||||
expect (cAssets[0u][jss::currency] == "USD");
|
||||
expect (cAssets[0u][jss::value] == "10");
|
||||
}
|
||||
|
||||
{
|
||||
auto const& obligations = result[jss::obligations];
|
||||
expect (obligations.isObject(), "obligations is not an object");
|
||||
expect (obligations.size() == 3);
|
||||
expect (obligations["CNY"] == "250");
|
||||
expect (obligations["JPY"] == "250");
|
||||
expect (obligations["USD"] == "50");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testGWB();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(GatewayBalances,app,ripple);
|
||||
|
||||
} // test
|
||||
} // ripple
|
||||
2241
src/test/rpc/JSONRPC_test.cpp
Normal file
2241
src/test/rpc/JSONRPC_test.cpp
Normal file
File diff suppressed because it is too large
Load Diff
697
src/test/rpc/KeyGeneration_test.cpp
Normal file
697
src/test/rpc/KeyGeneration_test.cpp
Normal file
@@ -0,0 +1,697 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2015 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <BeastConfig.h>
|
||||
#include <ripple/basics/StringUtilities.h>
|
||||
#include <ripple/basics/TestSuite.h>
|
||||
#include <ripple/json/json_value.h>
|
||||
#include <ripple/json/json_writer.h>
|
||||
#include <ripple/protocol/ErrorCodes.h>
|
||||
#include <ripple/protocol/JsonFields.h>
|
||||
#include <ripple/rpc/handlers/WalletPropose.h>
|
||||
#include <ripple/rpc/impl/RPCHelpers.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
namespace RPC {
|
||||
|
||||
struct key_strings
|
||||
{
|
||||
char const* account_id;
|
||||
char const* master_key;
|
||||
char const* master_seed;
|
||||
char const* master_seed_hex;
|
||||
char const* public_key;
|
||||
char const* public_key_hex;
|
||||
char const* secret_key_hex;
|
||||
};
|
||||
|
||||
namespace common {
|
||||
static char const* passphrase = "REINDEER FLOTILLA";
|
||||
static char const* master_key = "SCAT BERN ISLE FOR ROIL BUS SOAK AQUA FREE FOR DRAM BRIG";
|
||||
static char const* master_seed = "snMwVWs2hZzfDUF3p2tHZ3EgmyhFs";
|
||||
static char const* master_seed_hex = "BE6A670A19B209E112146D0A7ED2AAD7";
|
||||
}
|
||||
|
||||
static key_strings const secp256k1_strings =
|
||||
{
|
||||
"r4Vtj2jrfmTVZGfSP3gH9hQPMqFPQFin8f",
|
||||
common::master_key,
|
||||
common::master_seed,
|
||||
common::master_seed_hex,
|
||||
"aBQxK2YFNqzmAaXNczYcjqDjfiKkLsJUizsr1UBf44RCF8FHdrmX",
|
||||
"038AAE247B2344B1837FBED8F57389C8C11774510A3F7D784F2A09F0CB6843236C",
|
||||
"1949ECD889EA71324BC7A30C8E81F4E93CB73EE19D59E9082111E78CC3DDABC2",
|
||||
};
|
||||
|
||||
static key_strings const ed25519_strings =
|
||||
{
|
||||
"r4qV6xTXerqaZav3MJfSY79ynmc1BSBev1",
|
||||
common::master_key,
|
||||
common::master_seed,
|
||||
common::master_seed_hex,
|
||||
"aKEQmgLMyZPMruJFejUuedp169LgW6DbJt1rej1DJ5hWUMH4pHJ7",
|
||||
"ED54C3F5BEDA8BD588B203D23A27398FAD9D20F88A974007D6994659CD7273FE1D",
|
||||
"77AAED2698D56D6676323629160F4EEF21CFD9EE3D0745CC78FA291461F98278",
|
||||
};
|
||||
|
||||
class WalletPropose_test : public ripple::TestSuite
|
||||
{
|
||||
public:
|
||||
void testRandomWallet(boost::optional<std::string> const& keyType)
|
||||
{
|
||||
Json::Value params;
|
||||
if (keyType)
|
||||
params[jss::key_type] = *keyType;
|
||||
Json::Value result = walletPropose (params);
|
||||
|
||||
BEAST_EXPECT(! contains_error (result));
|
||||
BEAST_EXPECT(result.isMember (jss::account_id));
|
||||
BEAST_EXPECT(result.isMember (jss::master_key));
|
||||
BEAST_EXPECT(result.isMember (jss::master_seed));
|
||||
BEAST_EXPECT(result.isMember (jss::master_seed_hex));
|
||||
BEAST_EXPECT(result.isMember (jss::public_key));
|
||||
BEAST_EXPECT(result.isMember (jss::public_key_hex));
|
||||
BEAST_EXPECT(result.isMember (jss::key_type));
|
||||
|
||||
expectEquals (result[jss::key_type],
|
||||
params.isMember (jss::key_type) ? params[jss::key_type]
|
||||
: "secp256k1");
|
||||
|
||||
std::string seed = result[jss::master_seed].asString();
|
||||
|
||||
result = walletPropose (params);
|
||||
|
||||
// We asked for two random seeds, so they shouldn't match.
|
||||
BEAST_EXPECT(result[jss::master_seed].asString() != seed);
|
||||
}
|
||||
|
||||
void testSecretWallet (Json::Value const& params, key_strings const& s)
|
||||
{
|
||||
Json::Value result = walletPropose (params);
|
||||
|
||||
BEAST_EXPECT(! contains_error (result));
|
||||
expectEquals (result[jss::account_id], s.account_id);
|
||||
expectEquals (result[jss::master_key], s.master_key);
|
||||
expectEquals (result[jss::master_seed], s.master_seed);
|
||||
expectEquals (result[jss::master_seed_hex], s.master_seed_hex);
|
||||
expectEquals (result[jss::public_key], s.public_key);
|
||||
expectEquals (result[jss::public_key_hex], s.public_key_hex);
|
||||
expectEquals (result[jss::key_type],
|
||||
params.isMember (jss::key_type) ? params[jss::key_type]
|
||||
: "secp256k1");
|
||||
}
|
||||
|
||||
void testSeed (boost::optional<std::string> const& keyType,
|
||||
key_strings const& strings)
|
||||
{
|
||||
testcase ("seed");
|
||||
|
||||
Json::Value params;
|
||||
if (keyType)
|
||||
params[jss::key_type] = *keyType;
|
||||
params[jss::seed] = strings.master_seed;
|
||||
|
||||
testSecretWallet (params, strings);
|
||||
}
|
||||
|
||||
void testSeedHex (boost::optional<std::string> const& keyType,
|
||||
key_strings const& strings)
|
||||
{
|
||||
testcase ("seed_hex");
|
||||
|
||||
Json::Value params;
|
||||
if (keyType)
|
||||
params[jss::key_type] = *keyType;
|
||||
params[jss::seed_hex] = strings.master_seed_hex;
|
||||
|
||||
testSecretWallet (params, strings);
|
||||
}
|
||||
|
||||
void testLegacyPassphrase (char const* value,
|
||||
boost::optional<std::string> const& keyType,
|
||||
key_strings const& strings)
|
||||
{
|
||||
Json::Value params;
|
||||
if (keyType)
|
||||
params[jss::key_type] = *keyType;
|
||||
params[jss::passphrase] = value;
|
||||
|
||||
testSecretWallet (params, strings);
|
||||
}
|
||||
|
||||
void testLegacyPassphrase(boost::optional<std::string> const& keyType,
|
||||
key_strings const& strings)
|
||||
{
|
||||
testcase ("passphrase");
|
||||
|
||||
testLegacyPassphrase (common::passphrase, keyType, strings);
|
||||
testLegacyPassphrase (strings.master_key, keyType, strings);
|
||||
testLegacyPassphrase (strings.master_seed, keyType, strings);
|
||||
testLegacyPassphrase (strings.master_seed_hex, keyType, strings);
|
||||
}
|
||||
|
||||
void testKeyType (boost::optional<std::string> const& keyType,
|
||||
key_strings const& strings)
|
||||
{
|
||||
testcase (keyType ? *keyType : "no key_type");
|
||||
|
||||
testRandomWallet (keyType);
|
||||
testSeed (keyType, strings);
|
||||
testSeedHex (keyType, strings);
|
||||
testLegacyPassphrase (keyType, strings);
|
||||
|
||||
Json::Value params;
|
||||
if (keyType)
|
||||
params[jss::key_type] = *keyType;
|
||||
params[jss::seed] = strings.master_seed;
|
||||
params[jss::seed_hex] = strings.master_seed_hex;
|
||||
|
||||
// Secret fields are mutually exclusive.
|
||||
BEAST_EXPECT(contains_error (walletPropose (params)));
|
||||
}
|
||||
|
||||
void testBadInput ()
|
||||
{
|
||||
testcase ("Bad inputs");
|
||||
|
||||
// Passing non-strings where strings are required
|
||||
{
|
||||
Json::Value params;
|
||||
params[jss::key_type] = "secp256k1";
|
||||
params[jss::passphrase] = 20160506;
|
||||
auto result = walletPropose (params);
|
||||
BEAST_EXPECT(contains_error (result));
|
||||
BEAST_EXPECT(result[jss::error_message] ==
|
||||
"Invalid field 'passphrase', not string.");
|
||||
}
|
||||
|
||||
{
|
||||
Json::Value params;
|
||||
params[jss::key_type] = "secp256k1";
|
||||
params[jss::seed] = Json::objectValue;
|
||||
auto result = walletPropose (params);
|
||||
BEAST_EXPECT(contains_error (result));
|
||||
BEAST_EXPECT(result[jss::error_message] ==
|
||||
"Invalid field 'seed', not string.");
|
||||
}
|
||||
|
||||
{
|
||||
Json::Value params;
|
||||
params[jss::key_type] = "ed25519";
|
||||
params[jss::seed_hex] = Json::arrayValue;
|
||||
auto result = walletPropose (params);
|
||||
BEAST_EXPECT(contains_error (result));
|
||||
BEAST_EXPECT(result[jss::error_message] ==
|
||||
"Invalid field 'seed_hex', not string.");
|
||||
}
|
||||
|
||||
// Specifying multiple items at once
|
||||
{
|
||||
Json::Value params;
|
||||
params[jss::key_type] = "secp256k1";
|
||||
params[jss::passphrase] = common::master_key;
|
||||
params[jss::seed_hex] = common::master_seed_hex;
|
||||
params[jss::seed] = common::master_seed;
|
||||
auto result = walletPropose (params);
|
||||
BEAST_EXPECT(contains_error (result));
|
||||
BEAST_EXPECT(result[jss::error_message] ==
|
||||
"Exactly one of the following must be specified: passphrase, seed or seed_hex");
|
||||
}
|
||||
|
||||
// Specifying bad key types:
|
||||
{
|
||||
Json::Value params;
|
||||
params[jss::key_type] = "prime256v1";
|
||||
params[jss::passphrase] = common::master_key;
|
||||
auto result = walletPropose (params);
|
||||
BEAST_EXPECT(contains_error (result));
|
||||
BEAST_EXPECT(result[jss::error_message] ==
|
||||
"Invalid parameters.");
|
||||
}
|
||||
|
||||
{
|
||||
Json::Value params;
|
||||
params[jss::key_type] = Json::objectValue;
|
||||
params[jss::seed_hex] = common::master_seed_hex;
|
||||
auto result = walletPropose (params);
|
||||
BEAST_EXPECT(contains_error (result));
|
||||
BEAST_EXPECT(result[jss::error_message] ==
|
||||
"Invalid field 'key_type', not string.");
|
||||
}
|
||||
|
||||
{
|
||||
Json::Value params;
|
||||
params[jss::key_type] = Json::arrayValue;
|
||||
params[jss::seed] = common::master_seed;
|
||||
auto result = walletPropose (params);
|
||||
BEAST_EXPECT(contains_error (result));
|
||||
BEAST_EXPECT(result[jss::error_message] ==
|
||||
"Invalid field 'key_type', not string.");
|
||||
}
|
||||
}
|
||||
|
||||
void testKeypairForSignature (
|
||||
boost::optional<std::string> keyType,
|
||||
key_strings const& strings)
|
||||
{
|
||||
testcase ("keypairForSignature - " +
|
||||
(keyType ? *keyType : "no key_type"));
|
||||
|
||||
auto const publicKey = parseBase58<PublicKey>(
|
||||
TokenType::TOKEN_ACCOUNT_PUBLIC, strings.public_key);
|
||||
BEAST_EXPECT(publicKey);
|
||||
|
||||
if (!keyType)
|
||||
{
|
||||
{
|
||||
Json::Value params;
|
||||
Json::Value error;
|
||||
params[jss::secret] = strings.master_seed;
|
||||
|
||||
auto ret = keypairForSignature (params, error);
|
||||
BEAST_EXPECT(! contains_error (error));
|
||||
BEAST_EXPECT(ret.first.size() != 0);
|
||||
BEAST_EXPECT(ret.first == publicKey);
|
||||
}
|
||||
|
||||
{
|
||||
Json::Value params;
|
||||
Json::Value error;
|
||||
params[jss::secret] = strings.master_seed_hex;
|
||||
|
||||
auto ret = keypairForSignature (params, error);
|
||||
BEAST_EXPECT(! contains_error (error));
|
||||
BEAST_EXPECT(ret.first.size() != 0);
|
||||
BEAST_EXPECT(ret.first == publicKey);
|
||||
}
|
||||
|
||||
{
|
||||
Json::Value params;
|
||||
Json::Value error;
|
||||
params[jss::secret] = strings.master_key;
|
||||
|
||||
auto ret = keypairForSignature (params, error);
|
||||
BEAST_EXPECT(! contains_error (error));
|
||||
BEAST_EXPECT(ret.first.size() != 0);
|
||||
BEAST_EXPECT(ret.first == publicKey);
|
||||
}
|
||||
|
||||
keyType.emplace ("secp256k1");
|
||||
}
|
||||
|
||||
{
|
||||
Json::Value params;
|
||||
Json::Value error;
|
||||
|
||||
params[jss::key_type] = *keyType;
|
||||
params[jss::seed] = strings.master_seed;
|
||||
|
||||
auto ret = keypairForSignature (params, error);
|
||||
BEAST_EXPECT(! contains_error (error));
|
||||
BEAST_EXPECT(ret.first.size() != 0);
|
||||
BEAST_EXPECT(ret.first == publicKey);
|
||||
}
|
||||
|
||||
{
|
||||
Json::Value params;
|
||||
Json::Value error;
|
||||
|
||||
params[jss::key_type] = *keyType;
|
||||
params[jss::seed_hex] = strings.master_seed_hex;
|
||||
|
||||
auto ret = keypairForSignature (params, error);
|
||||
BEAST_EXPECT(! contains_error (error));
|
||||
BEAST_EXPECT(ret.first.size() != 0);
|
||||
BEAST_EXPECT(ret.first == publicKey);
|
||||
}
|
||||
|
||||
{
|
||||
Json::Value params;
|
||||
Json::Value error;
|
||||
|
||||
params[jss::key_type] = *keyType;
|
||||
params[jss::passphrase] = strings.master_key;
|
||||
|
||||
auto ret = keypairForSignature (params, error);
|
||||
BEAST_EXPECT(! contains_error (error));
|
||||
BEAST_EXPECT(ret.first.size() != 0);
|
||||
BEAST_EXPECT(ret.first == publicKey);
|
||||
}
|
||||
}
|
||||
|
||||
void testKeypairForSignatureErrors()
|
||||
{
|
||||
// Specify invalid "secret"
|
||||
{
|
||||
Json::Value params;
|
||||
Json::Value error;
|
||||
params[jss::secret] = 314159265;
|
||||
auto ret = keypairForSignature (params, error);
|
||||
BEAST_EXPECT(contains_error (error));
|
||||
BEAST_EXPECT(error[jss::error_message] ==
|
||||
"Invalid field 'secret', not string.");
|
||||
BEAST_EXPECT(ret.first.size() == 0);
|
||||
}
|
||||
|
||||
{
|
||||
Json::Value params;
|
||||
Json::Value error;
|
||||
params[jss::secret] = Json::arrayValue;
|
||||
params[jss::secret].append ("array:0");
|
||||
|
||||
auto ret = keypairForSignature (params, error);
|
||||
BEAST_EXPECT(contains_error (error));
|
||||
BEAST_EXPECT(error[jss::error_message] ==
|
||||
"Invalid field 'secret', not string.");
|
||||
BEAST_EXPECT(ret.first.size() == 0);
|
||||
}
|
||||
|
||||
{
|
||||
Json::Value params;
|
||||
Json::Value error;
|
||||
params[jss::secret] = Json::objectValue;
|
||||
params[jss::secret]["string"] = "string";
|
||||
params[jss::secret]["number"] = 702;
|
||||
|
||||
auto ret = keypairForSignature (params, error);
|
||||
BEAST_EXPECT(contains_error (error));
|
||||
BEAST_EXPECT(ret.first.size() == 0);
|
||||
BEAST_EXPECT(error[jss::error_message] ==
|
||||
"Invalid field 'secret', not string.");
|
||||
}
|
||||
|
||||
// Specify "secret" and "key_type"
|
||||
{
|
||||
Json::Value params;
|
||||
Json::Value error;
|
||||
params[jss::key_type] = "ed25519";
|
||||
params[jss::secret] = common::master_seed;
|
||||
|
||||
auto ret = keypairForSignature (params, error);
|
||||
BEAST_EXPECT(contains_error (error));
|
||||
BEAST_EXPECT(error[jss::error_message] ==
|
||||
"The secret field is not allowed if key_type is used.");
|
||||
BEAST_EXPECT(ret.first.size() == 0);
|
||||
}
|
||||
|
||||
// Specify unknown or bad "key_type"
|
||||
{
|
||||
Json::Value params;
|
||||
Json::Value error;
|
||||
params[jss::key_type] = "prime256v1";
|
||||
params[jss::passphrase] = common::master_key;
|
||||
|
||||
auto ret = keypairForSignature (params, error);
|
||||
BEAST_EXPECT(contains_error (error));
|
||||
BEAST_EXPECT(error[jss::error_message] ==
|
||||
"Invalid field 'key_type'.");
|
||||
BEAST_EXPECT(ret.first.size() == 0);
|
||||
}
|
||||
|
||||
{
|
||||
Json::Value params;
|
||||
Json::Value error;
|
||||
params[jss::key_type] = Json::objectValue;
|
||||
params[jss::seed_hex] = common::master_seed_hex;
|
||||
|
||||
auto ret = keypairForSignature (params, error);
|
||||
BEAST_EXPECT(contains_error (error));
|
||||
BEAST_EXPECT(error[jss::error_message] ==
|
||||
"Invalid field 'key_type', not string.");
|
||||
BEAST_EXPECT(ret.first.size() == 0);
|
||||
}
|
||||
|
||||
{
|
||||
Json::Value params;
|
||||
Json::Value error;
|
||||
params[jss::key_type] = Json::arrayValue;
|
||||
params[jss::seed] = common::master_seed;
|
||||
|
||||
auto ret = keypairForSignature (params, error);
|
||||
BEAST_EXPECT(contains_error (error));
|
||||
BEAST_EXPECT(error[jss::error_message] ==
|
||||
"Invalid field 'key_type', not string.");
|
||||
BEAST_EXPECT(ret.first.size() == 0);
|
||||
}
|
||||
|
||||
// Specify non-string passphrase
|
||||
{ // not a passphrase: number
|
||||
Json::Value params;
|
||||
Json::Value error;
|
||||
params[jss::key_type] = "secp256k1";
|
||||
params[jss::passphrase] = 1234567890;
|
||||
|
||||
auto ret = keypairForSignature (params, error);
|
||||
BEAST_EXPECT(contains_error (error));
|
||||
BEAST_EXPECT(error[jss::error_message] ==
|
||||
"Invalid field 'passphrase', not string.");
|
||||
BEAST_EXPECT(ret.first.size() == 0);
|
||||
}
|
||||
|
||||
{ // not a passphrase: object
|
||||
Json::Value params;
|
||||
Json::Value error;
|
||||
params[jss::key_type] = "secp256k1";
|
||||
params[jss::passphrase] = Json::objectValue;
|
||||
|
||||
auto ret = keypairForSignature (params, error);
|
||||
BEAST_EXPECT(contains_error (error));
|
||||
BEAST_EXPECT(error[jss::error_message] ==
|
||||
"Invalid field 'passphrase', not string.");
|
||||
BEAST_EXPECT(ret.first.size() == 0);
|
||||
}
|
||||
|
||||
{ // not a passphrase: array
|
||||
Json::Value params;
|
||||
Json::Value error;
|
||||
params[jss::key_type] = "secp256k1";
|
||||
params[jss::passphrase] = Json::arrayValue;
|
||||
|
||||
auto ret = keypairForSignature (params, error);
|
||||
BEAST_EXPECT(contains_error (error));
|
||||
BEAST_EXPECT(error[jss::error_message] ==
|
||||
"Invalid field 'passphrase', not string.");
|
||||
BEAST_EXPECT(ret.first.size() == 0);
|
||||
}
|
||||
|
||||
{ // not a passphrase: empty string
|
||||
Json::Value params;
|
||||
Json::Value error;
|
||||
params[jss::key_type] = "secp256k1";
|
||||
params[jss::passphrase] = "";
|
||||
|
||||
auto ret = keypairForSignature (params, error);
|
||||
BEAST_EXPECT(contains_error (error));
|
||||
BEAST_EXPECT(error[jss::error_message] ==
|
||||
"Disallowed seed.");
|
||||
BEAST_EXPECT(ret.first.size() == 0);
|
||||
}
|
||||
|
||||
|
||||
// Specify non-string or invalid seed
|
||||
{ // not a seed: number
|
||||
Json::Value params;
|
||||
Json::Value error;
|
||||
params[jss::key_type] = "secp256k1";
|
||||
params[jss::seed] = 443556;
|
||||
|
||||
auto ret = keypairForSignature (params, error);
|
||||
BEAST_EXPECT(contains_error (error));
|
||||
BEAST_EXPECT(error[jss::error_message] ==
|
||||
"Invalid field 'seed', not string.");
|
||||
BEAST_EXPECT(ret.first.size() == 0);
|
||||
}
|
||||
|
||||
{ // not a string: object
|
||||
Json::Value params;
|
||||
Json::Value error;
|
||||
params[jss::key_type] = "secp256k1";
|
||||
params[jss::seed] = Json::objectValue;
|
||||
|
||||
auto ret = keypairForSignature (params, error);
|
||||
BEAST_EXPECT(contains_error (error));
|
||||
BEAST_EXPECT(error[jss::error_message] ==
|
||||
"Invalid field 'seed', not string.");
|
||||
BEAST_EXPECT(ret.first.size() == 0);
|
||||
}
|
||||
|
||||
{ // not a string: array
|
||||
Json::Value params;
|
||||
Json::Value error;
|
||||
params[jss::key_type] = "secp256k1";
|
||||
params[jss::seed] = Json::arrayValue;
|
||||
|
||||
auto ret = keypairForSignature (params, error);
|
||||
BEAST_EXPECT(contains_error (error));
|
||||
BEAST_EXPECT(error[jss::error_message] ==
|
||||
"Invalid field 'seed', not string.");
|
||||
BEAST_EXPECT(ret.first.size() == 0);
|
||||
}
|
||||
|
||||
{ // not a seed: empty
|
||||
Json::Value params;
|
||||
Json::Value error;
|
||||
params[jss::key_type] = "secp256k1";
|
||||
params[jss::seed] = "";
|
||||
|
||||
auto ret = keypairForSignature (params, error);
|
||||
BEAST_EXPECT(contains_error (error));
|
||||
BEAST_EXPECT(error[jss::error_message] ==
|
||||
"Disallowed seed.");
|
||||
BEAST_EXPECT(ret.first.size() == 0);
|
||||
}
|
||||
|
||||
{ // not a seed: invalid characters
|
||||
Json::Value params;
|
||||
Json::Value error;
|
||||
params[jss::key_type] = "secp256k1";
|
||||
params[jss::seed] = "s M V s h z D F p t Z E m h s";
|
||||
|
||||
auto ret = keypairForSignature (params, error);
|
||||
BEAST_EXPECT(contains_error (error));
|
||||
BEAST_EXPECT(error[jss::error_message] ==
|
||||
"Disallowed seed.");
|
||||
BEAST_EXPECT(ret.first.size() == 0);
|
||||
}
|
||||
|
||||
{ // not a seed: random string
|
||||
Json::Value params;
|
||||
Json::Value error;
|
||||
params[jss::key_type] = "secp256k1";
|
||||
params[jss::seed] = "pnnjkbnobnml43679nbvjdsklnbjs";
|
||||
|
||||
auto ret = keypairForSignature (params, error);
|
||||
BEAST_EXPECT(contains_error (error));
|
||||
BEAST_EXPECT(error[jss::error_message] ==
|
||||
"Disallowed seed.");
|
||||
BEAST_EXPECT(ret.first.size() == 0);
|
||||
}
|
||||
|
||||
// Specify non-string or invalid seed_hex
|
||||
{ // not a string: number
|
||||
Json::Value params;
|
||||
Json::Value error;
|
||||
params[jss::key_type] = "secp256k1";
|
||||
params[jss::seed_hex] = 443556;
|
||||
|
||||
auto ret = keypairForSignature (params, error);
|
||||
BEAST_EXPECT(contains_error (error));
|
||||
BEAST_EXPECT(error[jss::error_message] ==
|
||||
"Invalid field 'seed_hex', not string.");
|
||||
BEAST_EXPECT(ret.first.size() == 0);
|
||||
}
|
||||
|
||||
{ // not a string: object
|
||||
Json::Value params;
|
||||
Json::Value error;
|
||||
params[jss::key_type] = "secp256k1";
|
||||
params[jss::seed_hex] = Json::objectValue;
|
||||
|
||||
auto ret = keypairForSignature (params, error);
|
||||
BEAST_EXPECT(contains_error (error));
|
||||
BEAST_EXPECT(error[jss::error_message] ==
|
||||
"Invalid field 'seed_hex', not string.");
|
||||
BEAST_EXPECT(ret.first.size() == 0);
|
||||
}
|
||||
|
||||
{ // not a string: array
|
||||
Json::Value params;
|
||||
Json::Value error;
|
||||
params[jss::key_type] = "secp256k1";
|
||||
params[jss::seed_hex] = Json::arrayValue;
|
||||
|
||||
auto ret = keypairForSignature (params, error);
|
||||
BEAST_EXPECT(contains_error (error));
|
||||
BEAST_EXPECT(error[jss::error_message] ==
|
||||
"Invalid field 'seed_hex', not string.");
|
||||
BEAST_EXPECT(ret.first.size() == 0);
|
||||
}
|
||||
|
||||
{ // empty
|
||||
Json::Value params;
|
||||
Json::Value error;
|
||||
params[jss::key_type] = "secp256k1";
|
||||
params[jss::seed_hex] = "";
|
||||
|
||||
auto ret = keypairForSignature (params, error);
|
||||
BEAST_EXPECT(contains_error (error));
|
||||
BEAST_EXPECT(error[jss::error_message] ==
|
||||
"Disallowed seed.");
|
||||
BEAST_EXPECT(ret.first.size() == 0);
|
||||
}
|
||||
|
||||
{ // short
|
||||
Json::Value params;
|
||||
Json::Value error;
|
||||
params[jss::key_type] = "secp256k1";
|
||||
params[jss::seed_hex] = "A670A19B";
|
||||
|
||||
auto ret = keypairForSignature (params, error);
|
||||
BEAST_EXPECT(contains_error (error));
|
||||
BEAST_EXPECT(error[jss::error_message] ==
|
||||
"Disallowed seed.");
|
||||
BEAST_EXPECT(ret.first.size() == 0);
|
||||
}
|
||||
|
||||
{ // not hex
|
||||
Json::Value params;
|
||||
Json::Value error;
|
||||
params[jss::key_type] = "secp256k1";
|
||||
params[jss::seed_hex] = common::passphrase;
|
||||
|
||||
auto ret = keypairForSignature (params, error);
|
||||
BEAST_EXPECT(contains_error (error));
|
||||
BEAST_EXPECT(error[jss::error_message] ==
|
||||
"Disallowed seed.");
|
||||
BEAST_EXPECT(ret.first.size() == 0);
|
||||
}
|
||||
|
||||
{ // overlong
|
||||
Json::Value params;
|
||||
Json::Value error;
|
||||
params[jss::key_type] = "secp256k1";
|
||||
params[jss::seed_hex] = "BE6A670A19B209E112146D0A7ED2AAD72567D0FC913";
|
||||
|
||||
auto ret = keypairForSignature (params, error);
|
||||
BEAST_EXPECT(contains_error (error));
|
||||
BEAST_EXPECT(error[jss::error_message] ==
|
||||
"Disallowed seed.");
|
||||
BEAST_EXPECT(ret.first.size() == 0);
|
||||
}
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
testKeyType (boost::none, secp256k1_strings);
|
||||
testKeyType (std::string("secp256k1"), secp256k1_strings);
|
||||
testKeyType (std::string("ed25519"), ed25519_strings);
|
||||
testBadInput ();
|
||||
|
||||
testKeypairForSignature (boost::none, secp256k1_strings);
|
||||
testKeypairForSignature (std::string("secp256k1"), secp256k1_strings);
|
||||
testKeypairForSignature (std::string("ed25519"), ed25519_strings);
|
||||
testKeypairForSignatureErrors ();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(WalletPropose,ripple_basics,ripple);
|
||||
|
||||
} // RPC
|
||||
} // ripple
|
||||
237
src/test/rpc/LedgerData_test.cpp
Normal file
237
src/test/rpc/LedgerData_test.cpp
Normal file
@@ -0,0 +1,237 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2016 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/protocol/JsonFields.h>
|
||||
#include <ripple/test/jtx.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
class LedgerData_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
|
||||
static
|
||||
std::unique_ptr<Config>
|
||||
makeConfig(bool setup_admin)
|
||||
{
|
||||
auto p = std::make_unique<Config>();
|
||||
test::setupConfigForUnitTests(*p);
|
||||
// the default config has admin active
|
||||
// ...we remove them if setup_admin is false
|
||||
if (! setup_admin)
|
||||
{
|
||||
(*p)["port_rpc"].set("admin","");
|
||||
(*p)["port_ws"].set("admin","");
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
// test helper
|
||||
static bool checkArraySize(Json::Value const& val, unsigned int size)
|
||||
{
|
||||
return val.isArray() &&
|
||||
val.size() == size;
|
||||
}
|
||||
|
||||
// test helper
|
||||
static bool checkMarker(Json::Value const& val)
|
||||
{
|
||||
return val.isMember(jss::marker) &&
|
||||
val[jss::marker].isString() &&
|
||||
val[jss::marker].asString().size() > 0;
|
||||
}
|
||||
|
||||
void testCurrentLedgerToLimits(bool as_admin)
|
||||
{
|
||||
using namespace test::jtx;
|
||||
Env env {*this, makeConfig(as_admin)};
|
||||
Account const gw {"gateway"};
|
||||
auto const USD = gw["USD"];
|
||||
env.fund(XRP(100000), gw);
|
||||
|
||||
int const max_limit = 256; //would be 2048 for binary requests, no need to test that here
|
||||
|
||||
for (auto i = 0; i < max_limit + 10; i++)
|
||||
{
|
||||
Account const bob {std::string("bob") + std::to_string(i)};
|
||||
env.fund(XRP(1000), bob);
|
||||
}
|
||||
env.close();
|
||||
|
||||
// no limit specified
|
||||
// with no limit specified, we get the max_limit if the total number of
|
||||
// accounts is greater than max, which it is here
|
||||
Json::Value jvParams;
|
||||
jvParams[jss::ledger_index] = "current";
|
||||
jvParams[jss::binary] = false;
|
||||
auto const jrr = env.rpc ( "json", "ledger_data", jvParams.toStyledString() ) [jss::result];
|
||||
BEAST_EXPECT(
|
||||
jrr[jss::ledger_current_index].isIntegral() &&
|
||||
jrr[jss::ledger_current_index].asInt() > 0 );
|
||||
BEAST_EXPECT( checkMarker(jrr) );
|
||||
BEAST_EXPECT( checkArraySize(jrr[jss::state], max_limit) );
|
||||
|
||||
// check limits values around the max_limit (+/- 1)
|
||||
for (auto delta = -1; delta <= 1; delta++)
|
||||
{
|
||||
jvParams[jss::limit] = max_limit + delta;
|
||||
auto const jrr = env.rpc ( "json", "ledger_data", jvParams.toStyledString() ) [jss::result];
|
||||
BEAST_EXPECT(
|
||||
checkArraySize( jrr[jss::state],
|
||||
(delta > 0 && !as_admin) ? max_limit : max_limit + delta ));
|
||||
}
|
||||
}
|
||||
|
||||
void testCurrentLedgerBinary()
|
||||
{
|
||||
using namespace test::jtx;
|
||||
Env env { *this, makeConfig(false) };
|
||||
Account const gw { "gateway" };
|
||||
auto const USD = gw["USD"];
|
||||
env.fund(XRP(100000), gw);
|
||||
|
||||
int const num_accounts = 10;
|
||||
|
||||
for (auto i = 0; i < num_accounts; i++)
|
||||
{
|
||||
Account const bob { std::string("bob") + std::to_string(i) };
|
||||
env.fund(XRP(1000), bob);
|
||||
}
|
||||
env.close();
|
||||
|
||||
// no limit specified
|
||||
// with no limit specified, we should get all of our fund entries
|
||||
// plus three more related to the gateway setup
|
||||
Json::Value jvParams;
|
||||
jvParams[jss::ledger_index] = "current";
|
||||
jvParams[jss::binary] = true;
|
||||
auto const jrr = env.rpc ( "json", "ledger_data", jvParams.toStyledString() ) [jss::result];
|
||||
BEAST_EXPECT(
|
||||
jrr[jss::ledger_current_index].isIntegral() &&
|
||||
jrr[jss::ledger_current_index].asInt() > 0);
|
||||
BEAST_EXPECT( ! jrr.isMember(jss::marker) );
|
||||
BEAST_EXPECT( checkArraySize(jrr[jss::state], num_accounts + 3) );
|
||||
}
|
||||
|
||||
void testBadInput()
|
||||
{
|
||||
using namespace test::jtx;
|
||||
Env env { *this };
|
||||
Account const gw { "gateway" };
|
||||
auto const USD = gw["USD"];
|
||||
Account const bob { "bob" };
|
||||
|
||||
env.fund(XRP(10000), gw, bob);
|
||||
env.trust(USD(1000), bob);
|
||||
|
||||
{
|
||||
// bad limit
|
||||
Json::Value jvParams;
|
||||
jvParams[jss::limit] = "0"; // NOT an integer
|
||||
auto const jrr = env.rpc ( "json", "ledger_data", jvParams.toStyledString() ) [jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::status] == "error");
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'limit', not integer.");
|
||||
}
|
||||
|
||||
{
|
||||
// invalid marker
|
||||
Json::Value jvParams;
|
||||
jvParams[jss::marker] = "NOT_A_MARKER";
|
||||
auto const jrr = env.rpc ( "json", "ledger_data", jvParams.toStyledString() ) [jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::status] == "error");
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'marker', not valid.");
|
||||
}
|
||||
|
||||
{
|
||||
// invalid marker - not a string
|
||||
Json::Value jvParams;
|
||||
jvParams[jss::marker] = 1;
|
||||
auto const jrr = env.rpc ( "json", "ledger_data", jvParams.toStyledString() ) [jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(jrr[jss::status] == "error");
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "Invalid field 'marker', not valid.");
|
||||
}
|
||||
|
||||
{
|
||||
// ask for a bad ledger index
|
||||
Json::Value jvParams;
|
||||
jvParams[jss::ledger_index] = 10u;
|
||||
auto const jrr = env.rpc ( "json", "ledger_data", jvParams.toStyledString() ) [jss::result];
|
||||
BEAST_EXPECT(jrr[jss::error] == "lgrNotFound");
|
||||
BEAST_EXPECT(jrr[jss::status] == "error");
|
||||
BEAST_EXPECT(jrr[jss::error_message] == "ledgerNotFound");
|
||||
}
|
||||
}
|
||||
|
||||
void testMarkerFollow()
|
||||
{
|
||||
using namespace test::jtx;
|
||||
Env env { *this, makeConfig(false) };
|
||||
Account const gw { "gateway" };
|
||||
auto const USD = gw["USD"];
|
||||
env.fund(XRP(100000), gw);
|
||||
|
||||
int const num_accounts = 20;
|
||||
|
||||
for (auto i = 0; i < num_accounts; i++)
|
||||
{
|
||||
Account const bob { std::string("bob") + std::to_string(i) };
|
||||
env.fund(XRP(1000), bob);
|
||||
}
|
||||
env.close();
|
||||
|
||||
// no limit specified
|
||||
// with no limit specified, we should get all of our fund entries
|
||||
// plus three more related to the gateway setup
|
||||
Json::Value jvParams;
|
||||
jvParams[jss::ledger_index] = "current";
|
||||
jvParams[jss::binary] = false;
|
||||
auto jrr = env.rpc ( "json", "ledger_data", jvParams.toStyledString() ) [jss::result];
|
||||
auto const total_count = jrr[jss::state].size();
|
||||
|
||||
// now make request with a limit and loop until we get all
|
||||
jvParams[jss::limit] = 5;
|
||||
jrr = env.rpc ( "json", "ledger_data", jvParams.toStyledString() ) [jss::result];
|
||||
BEAST_EXPECT( checkMarker(jrr) );
|
||||
auto running_total = jrr[jss::state].size();
|
||||
while ( jrr.isMember(jss::marker) )
|
||||
{
|
||||
jvParams[jss::marker] = jrr[jss::marker];
|
||||
jrr = env.rpc ( "json", "ledger_data", jvParams.toStyledString() ) [jss::result];
|
||||
running_total += jrr[jss::state].size();
|
||||
}
|
||||
BEAST_EXPECT( running_total == total_count );
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
testCurrentLedgerToLimits(true);
|
||||
testCurrentLedgerToLimits(false);
|
||||
testCurrentLedgerBinary();
|
||||
testBadInput();
|
||||
testMarkerFollow();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(LedgerData,app,ripple);
|
||||
|
||||
}
|
||||
317
src/test/rpc/LedgerRequestRPC_test.cpp
Normal file
317
src/test/rpc/LedgerRequestRPC_test.cpp
Normal file
@@ -0,0 +1,317 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012-2016 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/ErrorCodes.h>
|
||||
#include <ripple/protocol/JsonFields.h>
|
||||
#include <ripple/test/jtx.h>
|
||||
#include <ripple/beast/unit_test.h>
|
||||
#include <ripple/app/ledger/LedgerMaster.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
namespace RPC {
|
||||
|
||||
class LedgerRequestRPC_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
|
||||
static
|
||||
std::unique_ptr<Config>
|
||||
makeNonAdminConfig()
|
||||
{
|
||||
auto p = std::make_unique<Config>();
|
||||
test::setupConfigForUnitTests(*p);
|
||||
(*p)["port_rpc"].set("admin","");
|
||||
(*p)["port_ws"].set("admin","");
|
||||
return p;
|
||||
}
|
||||
|
||||
void testLedgerRequest()
|
||||
{
|
||||
using namespace test::jtx;
|
||||
|
||||
Env env(*this);
|
||||
|
||||
env.close();
|
||||
env.close();
|
||||
BEAST_EXPECT(env.current()->info().seq == 5);
|
||||
|
||||
{
|
||||
// arbitrary text is converted to 0.
|
||||
auto const result = env.rpc("ledger_request", "arbitrary_text");
|
||||
BEAST_EXPECT(RPC::contains_error(result[jss::result]) &&
|
||||
result[jss::result][jss::error_message] ==
|
||||
"Ledger index too small");
|
||||
}
|
||||
|
||||
{
|
||||
auto const result = env.rpc("ledger_request", "-1");
|
||||
BEAST_EXPECT(RPC::contains_error(result[jss::result]) &&
|
||||
result[jss::result][jss::error_message] ==
|
||||
"Ledger index too small");
|
||||
}
|
||||
|
||||
{
|
||||
auto const result = env.rpc("ledger_request", "0");
|
||||
BEAST_EXPECT(RPC::contains_error(result[jss::result]) &&
|
||||
result[jss::result][jss::error_message] ==
|
||||
"Ledger index too small");
|
||||
}
|
||||
|
||||
{
|
||||
auto const result = env.rpc("ledger_request", "1");
|
||||
BEAST_EXPECT(!RPC::contains_error(result[jss::result]) &&
|
||||
result[jss::result][jss::ledger_index] == 1 &&
|
||||
result[jss::result].isMember(jss::ledger));
|
||||
BEAST_EXPECT(result[jss::result][jss::ledger].
|
||||
isMember(jss::ledger_hash) &&
|
||||
result[jss::result][jss::ledger]
|
||||
[jss::ledger_hash].isString());
|
||||
}
|
||||
|
||||
{
|
||||
auto const result = env.rpc("ledger_request", "2");
|
||||
BEAST_EXPECT(!RPC::contains_error(result[jss::result]) &&
|
||||
result[jss::result][jss::ledger_index] == 2 &&
|
||||
result[jss::result].isMember(jss::ledger));
|
||||
BEAST_EXPECT(result[jss::result][jss::ledger].
|
||||
isMember(jss::ledger_hash) &&
|
||||
result[jss::result][jss::ledger]
|
||||
[jss::ledger_hash].isString());
|
||||
}
|
||||
|
||||
{
|
||||
auto const result = env.rpc("ledger_request", "3");
|
||||
BEAST_EXPECT(!RPC::contains_error(result[jss::result]) &&
|
||||
result[jss::result][jss::ledger_index] == 3 &&
|
||||
result[jss::result].isMember(jss::ledger));
|
||||
BEAST_EXPECT(result[jss::result][jss::ledger].
|
||||
isMember(jss::ledger_hash) &&
|
||||
result[jss::result][jss::ledger]
|
||||
[jss::ledger_hash].isString());
|
||||
|
||||
auto const ledgerHash = result[jss::result]
|
||||
[jss::ledger][jss::ledger_hash].asString();
|
||||
|
||||
{
|
||||
// Intentionally shadow `result` here to avoid reuing it.
|
||||
auto const result = env.rpc("ledger_request", ledgerHash);
|
||||
BEAST_EXPECT(!RPC::contains_error(result[jss::result]) &&
|
||||
result[jss::result][jss::ledger_index] == 3 &&
|
||||
result[jss::result].isMember(jss::ledger));
|
||||
BEAST_EXPECT(result[jss::result][jss::ledger].
|
||||
isMember(jss::ledger_hash) &&
|
||||
result[jss::result][jss::ledger]
|
||||
[jss::ledger_hash] == ledgerHash);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
std::string ledgerHash(64, 'q');
|
||||
|
||||
auto const result = env.rpc("ledger_request", ledgerHash);
|
||||
|
||||
BEAST_EXPECT(RPC::contains_error(result[jss::result]) &&
|
||||
result[jss::result][jss::error_message] ==
|
||||
"Invalid field 'ledger_hash'.");
|
||||
}
|
||||
|
||||
{
|
||||
std::string ledgerHash(64, '1');
|
||||
|
||||
auto const result = env.rpc("ledger_request", ledgerHash);
|
||||
|
||||
BEAST_EXPECT(!RPC::contains_error(result[jss::result]) &&
|
||||
result[jss::result][jss::have_header] == false);
|
||||
}
|
||||
|
||||
{
|
||||
auto const result = env.rpc("ledger_request", "4");
|
||||
BEAST_EXPECT(RPC::contains_error(result[jss::result]) &&
|
||||
result[jss::result][jss::error_message] ==
|
||||
"Ledger index too large");
|
||||
}
|
||||
|
||||
{
|
||||
auto const result = env.rpc("ledger_request", "5");
|
||||
BEAST_EXPECT(RPC::contains_error(result[jss::result]) &&
|
||||
result[jss::result][jss::error_message] ==
|
||||
"Ledger index too large");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void testEvolution()
|
||||
{
|
||||
using namespace test::jtx;
|
||||
Env env { *this };
|
||||
Account const gw { "gateway" };
|
||||
auto const USD = gw["USD"];
|
||||
env.fund(XRP(100000), gw);
|
||||
env.close();
|
||||
|
||||
env.memoize("bob");
|
||||
env.fund(XRP(1000), "bob");
|
||||
env.close();
|
||||
|
||||
env.memoize("alice");
|
||||
env.fund(XRP(1000), "alice");
|
||||
env.close();
|
||||
|
||||
env.memoize("carol");
|
||||
env.fund(XRP(1000), "carol");
|
||||
env.close();
|
||||
|
||||
auto result = env.rpc ( "ledger_request", "1" ) [jss::result];
|
||||
BEAST_EXPECT(result[jss::ledger][jss::ledger_index] == "1");
|
||||
BEAST_EXPECT(result[jss::ledger][jss::total_coins] == "100000000000000000");
|
||||
BEAST_EXPECT(result[jss::ledger][jss::closed] == true);
|
||||
BEAST_EXPECT(result[jss::ledger][jss::ledger_hash] == "AB868A6CFEEC779C2FF845C0AF00A642259986AF40C01976A7F842B6918936C7");
|
||||
BEAST_EXPECT(result[jss::ledger][jss::parent_hash] == "0000000000000000000000000000000000000000000000000000000000000000");
|
||||
BEAST_EXPECT(result[jss::ledger][jss::account_hash] == "A21ED30C04C88046FC61DB9DC19375EEDBD365FD8C17286F27127DF804E9CAA6");
|
||||
BEAST_EXPECT(result[jss::ledger][jss::transaction_hash] == "0000000000000000000000000000000000000000000000000000000000000000");
|
||||
|
||||
result = env.rpc ( "ledger_request", "2" ) [jss::result];
|
||||
BEAST_EXPECT(result[jss::ledger][jss::ledger_index] == "2");
|
||||
BEAST_EXPECT(result[jss::ledger][jss::total_coins] == "100000000000000000");
|
||||
BEAST_EXPECT(result[jss::ledger][jss::closed] == true);
|
||||
BEAST_EXPECT(result[jss::ledger][jss::ledger_hash] == "8AEDBB96643962F1D40F01E25632ABB3C56C9F04B0231EE4B18248B90173D189");
|
||||
BEAST_EXPECT(result[jss::ledger][jss::parent_hash] == "AB868A6CFEEC779C2FF845C0AF00A642259986AF40C01976A7F842B6918936C7");
|
||||
BEAST_EXPECT(result[jss::ledger][jss::account_hash] == "183D5235C7C1FB5AE67AD2F6CC3B28F5FB86E8C4F89DB50DD85641A96470534E");
|
||||
BEAST_EXPECT(result[jss::ledger][jss::transaction_hash] == "0000000000000000000000000000000000000000000000000000000000000000");
|
||||
|
||||
result = env.rpc ( "ledger_request", "3" ) [jss::result];
|
||||
BEAST_EXPECT(result[jss::ledger][jss::ledger_index] == "3");
|
||||
BEAST_EXPECT(result[jss::ledger][jss::total_coins] == "99999999999999980");
|
||||
BEAST_EXPECT(result[jss::ledger][jss::closed] == true);
|
||||
BEAST_EXPECT(result[jss::ledger][jss::ledger_hash] == "D2EE1E2A7288AAD43D6FA8AD8007FD1A95646F365EF3A1AD608A03258F11CF18");
|
||||
BEAST_EXPECT(result[jss::ledger][jss::parent_hash] == "8AEDBB96643962F1D40F01E25632ABB3C56C9F04B0231EE4B18248B90173D189");
|
||||
BEAST_EXPECT(result[jss::ledger][jss::account_hash] == "22565DC00D1A30F2C15871714E512976EF476281E5E87FF63D3E129C9069F4F4");
|
||||
BEAST_EXPECT(result[jss::ledger][jss::transaction_hash] == "0213EC486C058B3942FBE3DAC6839949A5C5B02B8B4244C8998EFDF04DBD8222");
|
||||
|
||||
result = env.rpc ( "ledger_request", "4" ) [jss::result];
|
||||
BEAST_EXPECT(result[jss::ledger][jss::ledger_index] == "4");
|
||||
BEAST_EXPECT(result[jss::ledger][jss::total_coins] == "99999999999999960");
|
||||
BEAST_EXPECT(result[jss::ledger][jss::closed] == true);
|
||||
BEAST_EXPECT(result[jss::ledger][jss::ledger_hash] == "8F9032390CDD4C9D7A5B216AFDA3B525A3B39D7589C69D90D4C6BCA4619DD33C");
|
||||
BEAST_EXPECT(result[jss::ledger][jss::parent_hash] == "D2EE1E2A7288AAD43D6FA8AD8007FD1A95646F365EF3A1AD608A03258F11CF18");
|
||||
BEAST_EXPECT(result[jss::ledger][jss::account_hash] == "C3335CA14E712CB28F2A7C09BEB9A24BF30BBFA5528F156C19F6665D7A588FEA");
|
||||
BEAST_EXPECT(result[jss::ledger][jss::transaction_hash] == "3CBDB8F42E04333E1642166BFB93AC9A7E1C6C067092CD5D881D6F3AB3D67E76");
|
||||
|
||||
result = env.rpc ( "ledger_request", "5" ) [jss::result];
|
||||
BEAST_EXPECT(result[jss::ledger][jss::ledger_index] == "5");
|
||||
BEAST_EXPECT(result[jss::ledger][jss::total_coins] == "99999999999999940");
|
||||
BEAST_EXPECT(result[jss::ledger][jss::closed] == true);
|
||||
BEAST_EXPECT(result[jss::ledger][jss::ledger_hash] == "3EDEB201735867A8EEECBC79A75902C05A7E3F192E4C12E02E67BFDDE5566CCE");
|
||||
BEAST_EXPECT(result[jss::ledger][jss::parent_hash] == "8F9032390CDD4C9D7A5B216AFDA3B525A3B39D7589C69D90D4C6BCA4619DD33C");
|
||||
BEAST_EXPECT(result[jss::ledger][jss::account_hash] == "7C77B1E9EB86410D84EE0CD50716AAA21192F19CF533194AD705798895248212");
|
||||
BEAST_EXPECT(result[jss::ledger][jss::transaction_hash] == "C3D086CD6BDB9E97AD1D513B2C049EF2840BD21D0B3E22D84EBBB89B6D2EF59D");
|
||||
|
||||
result = env.rpc ( "ledger_request", "6" ) [jss::result];
|
||||
BEAST_EXPECT(result[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(result[jss::status] == "error");
|
||||
BEAST_EXPECT(result[jss::error_message] == "Ledger index too large");
|
||||
}
|
||||
|
||||
void testBadInput()
|
||||
{
|
||||
using namespace test::jtx;
|
||||
Env env { *this };
|
||||
Account const gw { "gateway" };
|
||||
auto const USD = gw["USD"];
|
||||
env.fund(XRP(100000), gw);
|
||||
env.close();
|
||||
|
||||
Json::Value jvParams;
|
||||
jvParams[jss::ledger_hash] = "AB868A6CFEEC779C2FF845C0AF00A642259986AF40C01976A7F842B6918936C7";
|
||||
jvParams[jss::ledger_index] = "1";
|
||||
auto result = env.rpc ("json", "ledger_request", jvParams.toStyledString()) [jss::result];
|
||||
BEAST_EXPECT(result[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(result[jss::status] == "error");
|
||||
BEAST_EXPECT(result[jss::error_message] == "Exactly one of ledger_hash and ledger_index can be set.");
|
||||
|
||||
// the purpose in this test is to force the ledger expiration/out of
|
||||
// date check to trigger
|
||||
env.timeKeeper().adjustCloseTime(weeks{3});
|
||||
result = env.rpc ( "ledger_request", "1" ) [jss::result];
|
||||
BEAST_EXPECT(result[jss::error] == "noCurrent");
|
||||
BEAST_EXPECT(result[jss::status] == "error");
|
||||
BEAST_EXPECT(result[jss::error_message] == "Current ledger is unavailable.");
|
||||
|
||||
}
|
||||
|
||||
void testMoreThan256Closed()
|
||||
{
|
||||
using namespace test::jtx;
|
||||
Env env {*this};
|
||||
Account const gw {"gateway"};
|
||||
env.app().getLedgerMaster().tune(0, 3600);
|
||||
auto const USD = gw["USD"];
|
||||
env.fund(XRP(100000), gw);
|
||||
|
||||
int const max_limit = 256;
|
||||
|
||||
for (auto i = 0; i < max_limit + 10; i++)
|
||||
{
|
||||
Account const bob {std::string("bob") + std::to_string(i)};
|
||||
env.fund(XRP(1000), bob);
|
||||
env.close();
|
||||
}
|
||||
|
||||
auto result = env.rpc ( "ledger_request", "1" ) [jss::result];
|
||||
BEAST_EXPECT(result[jss::ledger][jss::ledger_index] == "1");
|
||||
BEAST_EXPECT(result[jss::ledger][jss::total_coins] == "100000000000000000");
|
||||
BEAST_EXPECT(result[jss::ledger][jss::closed] == true);
|
||||
BEAST_EXPECT(result[jss::ledger][jss::ledger_hash] == "AB868A6CFEEC779C2FF845C0AF00A642259986AF40C01976A7F842B6918936C7");
|
||||
BEAST_EXPECT(result[jss::ledger][jss::parent_hash] == "0000000000000000000000000000000000000000000000000000000000000000");
|
||||
BEAST_EXPECT(result[jss::ledger][jss::account_hash] == "A21ED30C04C88046FC61DB9DC19375EEDBD365FD8C17286F27127DF804E9CAA6");
|
||||
BEAST_EXPECT(result[jss::ledger][jss::transaction_hash] == "0000000000000000000000000000000000000000000000000000000000000000");
|
||||
}
|
||||
|
||||
void testNonAdmin()
|
||||
{
|
||||
using namespace test::jtx;
|
||||
Env env { *this, makeNonAdminConfig() };
|
||||
Account const gw { "gateway" };
|
||||
auto const USD = gw["USD"];
|
||||
env.fund(XRP(100000), gw);
|
||||
env.close();
|
||||
|
||||
auto const result = env.rpc ( "ledger_request", "1" ) [jss::result];
|
||||
BEAST_EXPECT(result[jss::error] == "noPermission");
|
||||
BEAST_EXPECT(result[jss::status] == "error");
|
||||
BEAST_EXPECT(result[jss::error_message] == "You don't have permission for this command.");
|
||||
}
|
||||
|
||||
void run ()
|
||||
{
|
||||
testLedgerRequest();
|
||||
testEvolution();
|
||||
testBadInput();
|
||||
testMoreThan256Closed();
|
||||
testNonAdmin();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(LedgerRequestRPC,app,ripple);
|
||||
|
||||
} // RPC
|
||||
} // ripple
|
||||
|
||||
321
src/test/rpc/RobustTransaction_test.cpp
Normal file
321
src/test/rpc/RobustTransaction_test.cpp
Normal file
@@ -0,0 +1,321 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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/core/JobQueue.h>
|
||||
#include <ripple/protocol/JsonFields.h>
|
||||
#include <ripple/test/jtx.h>
|
||||
#include <ripple/test/WSClient.h>
|
||||
#include <ripple/beast/unit_test.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
class RobustTransaction_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
void
|
||||
testSequenceRealignment()
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
using namespace jtx;
|
||||
Env env(*this);
|
||||
env.fund(XRP(10000), "alice", "bob");
|
||||
env.close();
|
||||
auto wsc = makeWSClient(env.app().config());
|
||||
|
||||
{
|
||||
// RPC subscribe to transactions stream
|
||||
Json::Value jv;
|
||||
jv[jss::streams] = Json::arrayValue;
|
||||
jv[jss::streams].append("transactions");
|
||||
jv = wsc->invoke("subscribe", jv);
|
||||
BEAST_EXPECT(jv[jss::status] == "success");
|
||||
}
|
||||
|
||||
{
|
||||
// Submit past ledger sequence transaction
|
||||
Json::Value payment;
|
||||
payment[jss::secret] = toBase58(generateSeed("alice"));
|
||||
payment[jss::tx_json] = pay("alice", "bob", XRP(1));
|
||||
payment[jss::tx_json][sfLastLedgerSequence.fieldName] = 1;
|
||||
auto jv = wsc->invoke("submit", payment);
|
||||
BEAST_EXPECT(jv[jss::result][jss::engine_result] ==
|
||||
"tefMAX_LEDGER");
|
||||
|
||||
// Submit past sequence transaction
|
||||
payment[jss::tx_json] = pay("alice", "bob", XRP(1));
|
||||
payment[jss::tx_json][sfSequence.fieldName] =
|
||||
env.seq("alice") - 1;
|
||||
jv = wsc->invoke("submit", payment);
|
||||
BEAST_EXPECT(jv[jss::result][jss::engine_result] ==
|
||||
"tefPAST_SEQ");
|
||||
|
||||
// Submit future sequence transaction
|
||||
payment[jss::tx_json][sfSequence.fieldName] =
|
||||
env.seq("alice") + 1;
|
||||
jv = wsc->invoke("submit", payment);
|
||||
BEAST_EXPECT(jv[jss::result][jss::engine_result] ==
|
||||
"terPRE_SEQ");
|
||||
|
||||
// Submit transaction to bridge the sequence gap
|
||||
payment[jss::tx_json][sfSequence.fieldName] =
|
||||
env.seq("alice");
|
||||
jv = wsc->invoke("submit", payment);
|
||||
BEAST_EXPECT(jv[jss::result][jss::engine_result] ==
|
||||
"tesSUCCESS");
|
||||
|
||||
// Wait for the jobqueue to process everything
|
||||
env.app().getJobQueue().rendezvous();
|
||||
|
||||
// Finalize transactions
|
||||
jv = wsc->invoke("ledger_accept");
|
||||
BEAST_EXPECT(jv[jss::result].isMember(
|
||||
jss::ledger_current_index));
|
||||
}
|
||||
|
||||
{
|
||||
// Check balances
|
||||
BEAST_EXPECT(wsc->findMsg(5s,
|
||||
[&](auto const& jv)
|
||||
{
|
||||
auto const& ff = jv[jss::meta]["AffectedNodes"]
|
||||
[1u]["ModifiedNode"]["FinalFields"];
|
||||
return ff[jss::Account] == Account("bob").human() &&
|
||||
ff["Balance"] == "10001000000";
|
||||
}));
|
||||
|
||||
BEAST_EXPECT(wsc->findMsg(5s,
|
||||
[&](auto const& jv)
|
||||
{
|
||||
auto const& ff = jv[jss::meta]["AffectedNodes"]
|
||||
[1u]["ModifiedNode"]["FinalFields"];
|
||||
return ff[jss::Account] == Account("bob").human() &&
|
||||
ff["Balance"] == "10002000000";
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Submit a normal payment. Client disconnects after the proposed
|
||||
transaction result is received.
|
||||
|
||||
Client reconnects in the future. During this time it is presumed that the
|
||||
transaction should have succeeded.
|
||||
|
||||
Upon reconnection, recent account transaction history is loaded.
|
||||
The submitted transaction should be detected, and the transaction should
|
||||
ultimately succeed.
|
||||
*/
|
||||
void
|
||||
testReconnect()
|
||||
{
|
||||
using namespace jtx;
|
||||
Env env(*this);
|
||||
env.fund(XRP(10000), "alice", "bob");
|
||||
env.close();
|
||||
auto wsc = makeWSClient(env.app().config());
|
||||
|
||||
{
|
||||
// Submit normal payment
|
||||
Json::Value jv;
|
||||
jv[jss::secret] = toBase58(generateSeed("alice"));
|
||||
jv[jss::tx_json] = pay("alice", "bob", XRP(1));
|
||||
jv = wsc->invoke("submit", jv);
|
||||
BEAST_EXPECT(jv[jss::result][jss::engine_result] ==
|
||||
"tesSUCCESS");
|
||||
|
||||
// Disconnect
|
||||
wsc.reset();
|
||||
|
||||
// Server finalizes transaction
|
||||
env.close();
|
||||
}
|
||||
|
||||
{
|
||||
// RPC account_tx
|
||||
Json::Value jv;
|
||||
jv[jss::account] = Account("bob").human();
|
||||
jv[jss::ledger_index_min] = -1;
|
||||
jv[jss::ledger_index_max] = -1;
|
||||
wsc = makeWSClient(env.app().config());
|
||||
jv = wsc->invoke("account_tx", jv);
|
||||
|
||||
// Check balance
|
||||
auto ff = jv[jss::result][jss::transactions][0u][jss::meta]
|
||||
["AffectedNodes"][1u]["ModifiedNode"]["FinalFields"];
|
||||
BEAST_EXPECT(ff[jss::Account] ==
|
||||
Account("bob").human());
|
||||
BEAST_EXPECT(ff["Balance"] == "10001000000");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testReconnectAfterWait()
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
using namespace jtx;
|
||||
Env env(*this);
|
||||
env.fund(XRP(10000), "alice", "bob");
|
||||
env.close();
|
||||
auto wsc = makeWSClient(env.app().config());
|
||||
|
||||
{
|
||||
// Submit normal payment
|
||||
Json::Value jv;
|
||||
jv[jss::secret] = toBase58(generateSeed("alice"));
|
||||
jv[jss::tx_json] = pay("alice", "bob", XRP(1));
|
||||
jv = wsc->invoke("submit", jv);
|
||||
BEAST_EXPECT(jv[jss::result][jss::engine_result] ==
|
||||
"tesSUCCESS");
|
||||
|
||||
// Finalize transaction
|
||||
jv = wsc->invoke("ledger_accept");
|
||||
BEAST_EXPECT(jv[jss::result].isMember(
|
||||
jss::ledger_current_index));
|
||||
|
||||
// Wait for the jobqueue to process everything
|
||||
env.app().getJobQueue().rendezvous();
|
||||
}
|
||||
|
||||
{
|
||||
// RPC subscribe to ledger stream
|
||||
Json::Value jv;
|
||||
jv[jss::streams] = Json::arrayValue;
|
||||
jv[jss::streams].append("ledger");
|
||||
jv = wsc->invoke("subscribe", jv);
|
||||
BEAST_EXPECT(jv[jss::status] == "success");
|
||||
|
||||
// Close ledgers
|
||||
for(auto i = 0; i < 8; ++i)
|
||||
{
|
||||
BEAST_EXPECT(wsc->invoke("ledger_accept")[jss::result].
|
||||
isMember(jss::ledger_current_index));
|
||||
|
||||
// Wait for the jobqueue to process everything
|
||||
env.app().getJobQueue().rendezvous();
|
||||
|
||||
BEAST_EXPECT(wsc->findMsg(5s,
|
||||
[&](auto const& jv)
|
||||
{
|
||||
return jv[jss::type] == "ledgerClosed";
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Disconnect, reconnect
|
||||
wsc = makeWSClient(env.app().config());
|
||||
|
||||
// RPC subscribe to ledger stream
|
||||
Json::Value jv;
|
||||
jv[jss::streams] = Json::arrayValue;
|
||||
jv[jss::streams].append("ledger");
|
||||
jv = wsc->invoke("subscribe", jv);
|
||||
BEAST_EXPECT(jv[jss::status] == "success");
|
||||
|
||||
// Close ledgers
|
||||
for (auto i = 0; i < 2; ++i)
|
||||
{
|
||||
BEAST_EXPECT(wsc->invoke("ledger_accept")[jss::result].
|
||||
isMember(jss::ledger_current_index));
|
||||
|
||||
// Wait for the jobqueue to process everything
|
||||
env.app().getJobQueue().rendezvous();
|
||||
|
||||
BEAST_EXPECT(wsc->findMsg(5s,
|
||||
[&](auto const& jv)
|
||||
{
|
||||
return jv[jss::type] == "ledgerClosed";
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// RPC account_tx
|
||||
Json::Value jv;
|
||||
jv[jss::account] = Account("bob").human();
|
||||
jv[jss::ledger_index_min] = -1;
|
||||
jv[jss::ledger_index_max] = -1;
|
||||
wsc = makeWSClient(env.app().config());
|
||||
jv = wsc->invoke("account_tx", jv);
|
||||
|
||||
// Check balance
|
||||
auto ff = jv[jss::result][jss::transactions][0u][jss::meta]
|
||||
["AffectedNodes"][1u]["ModifiedNode"]["FinalFields"];
|
||||
BEAST_EXPECT(ff[jss::Account] ==
|
||||
Account("bob").human());
|
||||
BEAST_EXPECT(ff["Balance"] == "10001000000");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testAccountsProposed()
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
using namespace jtx;
|
||||
Env env(*this);
|
||||
env.fund(XRP(10000), "alice");
|
||||
env.close();
|
||||
auto wsc = makeWSClient(env.app().config());
|
||||
|
||||
{
|
||||
// RPC subscribe to accounts_proposed stream
|
||||
Json::Value jv;
|
||||
jv[jss::accounts_proposed] = Json::arrayValue;
|
||||
jv[jss::accounts_proposed].append(
|
||||
Account("alice").human());
|
||||
jv = wsc->invoke("subscribe", jv);
|
||||
BEAST_EXPECT(jv[jss::status] == "success");
|
||||
}
|
||||
|
||||
{
|
||||
// Submit account_set transaction
|
||||
Json::Value jv;
|
||||
jv[jss::secret] = toBase58(generateSeed("alice"));
|
||||
jv[jss::tx_json] = fset("alice", 0);
|
||||
jv[jss::tx_json][jss::Fee] = 10;
|
||||
jv = wsc->invoke("submit", jv);
|
||||
BEAST_EXPECT(jv[jss::result][jss::engine_result] ==
|
||||
"tesSUCCESS");
|
||||
}
|
||||
|
||||
{
|
||||
// Check stream update
|
||||
BEAST_EXPECT(wsc->findMsg(5s,
|
||||
[&](auto const& jv)
|
||||
{
|
||||
return jv[jss::transaction][jss::TransactionType] ==
|
||||
"AccountSet";
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testSequenceRealignment();
|
||||
testReconnect();
|
||||
testReconnectAfterWait();
|
||||
testAccountsProposed();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(RobustTransaction,app,ripple);
|
||||
|
||||
} // test
|
||||
} // ripple
|
||||
108
src/test/rpc/ServerInfo_test.cpp
Normal file
108
src/test/rpc/ServerInfo_test.cpp
Normal file
@@ -0,0 +1,108 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012-2016 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>
|
||||
#include <ripple/test/jtx.h>
|
||||
#include <ripple/beast/unit_test.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
namespace test {
|
||||
|
||||
namespace validator {
|
||||
static auto const seed = "ss7t3J9dYentEFgKdPA3q6eyxtrLB";
|
||||
static auto const master_key =
|
||||
"nHU4LxxrSQsRTKy5uZbX95eYowoamUEPCcWraxoiCNbtDaUr1V34";
|
||||
static auto const signing_key =
|
||||
"n9LHPLA36SBky1YjbaVEApQQ3s9XcpazCgfAG7jsqBb1ugDAosbm";
|
||||
// Format manifest string to test trim()
|
||||
static auto const manifest =
|
||||
" JAAAAAFxIe2FwblmJwz4pVYXHLJSzSBgIK7mpQuHNQ88CxW\n"
|
||||
" \tjIN7q4nMhAuUTyasIhvj2KPfNRbmmIBnqNUzidgkKb244eP \n"
|
||||
"\t794ZpMdkC+8l5n3R/CHP6SAwhYDOaqub0Cs2NjjewBnp1mf\n"
|
||||
"\t 23rhAzdcjRuWzm0IT12eduZ0DwcF5Ng8rAelaYP1iT93ScE\t \t";
|
||||
static auto sequence = 1;
|
||||
}
|
||||
|
||||
class ServerInfo_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
static
|
||||
std::unique_ptr<Config>
|
||||
makeValidatorConfig()
|
||||
{
|
||||
auto p = std::make_unique<Config>();
|
||||
boost::format toLoad(R"rippleConfig(
|
||||
[validation_manifest]
|
||||
%1%
|
||||
|
||||
[validation_seed]
|
||||
%2%
|
||||
)rippleConfig");
|
||||
|
||||
p->loadFromString (boost::str (
|
||||
toLoad % validator::manifest % validator::seed));
|
||||
|
||||
setupConfigForUnitTests(*p);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
void testServerInfo()
|
||||
{
|
||||
using namespace test::jtx;
|
||||
|
||||
{
|
||||
Env env(*this);
|
||||
auto const result = env.rpc("server_info", "1");
|
||||
BEAST_EXPECT(!result[jss::result].isMember (jss::error));
|
||||
BEAST_EXPECT(result[jss::status] == "success");
|
||||
BEAST_EXPECT(result[jss::result].isMember(jss::info));
|
||||
}
|
||||
{
|
||||
Env env(*this, makeValidatorConfig());
|
||||
auto const result = env.rpc("server_info", "1");
|
||||
BEAST_EXPECT(!result[jss::result].isMember (jss::error));
|
||||
BEAST_EXPECT(result[jss::status] == "success");
|
||||
BEAST_EXPECT(result[jss::result].isMember(jss::info));
|
||||
BEAST_EXPECT(result[jss::result][jss::info]
|
||||
[jss::pubkey_validator] == validator::signing_key);
|
||||
BEAST_EXPECT(result[jss::result][jss::info].isMember(
|
||||
jss::validation_manifest));
|
||||
BEAST_EXPECT(result[jss::result][jss::info][jss::validation_manifest]
|
||||
[jss::master_key] == validator::master_key);
|
||||
BEAST_EXPECT(result[jss::result][jss::info][jss::validation_manifest]
|
||||
[jss::signing_key] == validator::signing_key);
|
||||
BEAST_EXPECT(result[jss::result][jss::info][jss::validation_manifest]
|
||||
[jss::seq] == validator::sequence);
|
||||
}
|
||||
}
|
||||
|
||||
void run ()
|
||||
{
|
||||
testServerInfo ();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(ServerInfo,app,ripple);
|
||||
|
||||
} // test
|
||||
} // ripple
|
||||
|
||||
215
src/test/rpc/Status_test.cpp
Normal file
215
src/test/rpc/Status_test.cpp
Normal file
@@ -0,0 +1,215 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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/rpc/Status.h>
|
||||
#include <ripple/basics/contract.h>
|
||||
#include <ripple/beast/unit_test.h>
|
||||
#include <algorithm>
|
||||
|
||||
namespace ripple {
|
||||
namespace RPC {
|
||||
|
||||
class codeString_test : public beast::unit_test::suite
|
||||
{
|
||||
private:
|
||||
template <typename Type>
|
||||
std::string codeString (Type t)
|
||||
{
|
||||
return Status (t).codeString();
|
||||
}
|
||||
|
||||
void test_OK ()
|
||||
{
|
||||
testcase ("OK");
|
||||
{
|
||||
auto s = codeString (Status ());
|
||||
expect (s.empty(), "String for OK status");
|
||||
}
|
||||
|
||||
{
|
||||
auto s = codeString (Status::OK);
|
||||
expect (s.empty(), "String for OK status");
|
||||
}
|
||||
|
||||
{
|
||||
auto s = codeString (0);
|
||||
expect (s.empty(), "String for 0 status");
|
||||
}
|
||||
|
||||
{
|
||||
auto s = codeString (tesSUCCESS);
|
||||
expect (s.empty(), "String for tesSUCCESS");
|
||||
}
|
||||
|
||||
{
|
||||
auto s = codeString (rpcSUCCESS);
|
||||
expect (s.empty(), "String for rpcSUCCESS");
|
||||
}
|
||||
}
|
||||
|
||||
void test_error ()
|
||||
{
|
||||
testcase ("error");
|
||||
{
|
||||
auto s = codeString (23);
|
||||
expect (s == "23", s);
|
||||
}
|
||||
|
||||
{
|
||||
auto s = codeString (temBAD_AMOUNT);
|
||||
expect (s == "temBAD_AMOUNT: Can only send positive amounts.", s);
|
||||
}
|
||||
|
||||
{
|
||||
auto s = codeString (rpcBAD_SYNTAX);
|
||||
expect (s == "badSyntax: Syntax error.", s);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
void run()
|
||||
{
|
||||
test_OK ();
|
||||
test_error ();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE (codeString, Status, RPC);
|
||||
|
||||
class fillJson_test : public beast::unit_test::suite
|
||||
{
|
||||
private:
|
||||
Json::Value value_;
|
||||
|
||||
template <typename Type>
|
||||
void fillJson (Type t)
|
||||
{
|
||||
value_.clear ();
|
||||
Status (t).fillJson (value_);
|
||||
}
|
||||
|
||||
void test_OK ()
|
||||
{
|
||||
testcase ("OK");
|
||||
fillJson (Status ());
|
||||
expect (! value_, "Value for empty status");
|
||||
|
||||
fillJson (0);
|
||||
expect (! value_, "Value for 0 status");
|
||||
|
||||
fillJson (Status::OK);
|
||||
expect (! value_, "Value for OK status");
|
||||
|
||||
fillJson (tesSUCCESS);
|
||||
expect (! value_, "Value for tesSUCCESS");
|
||||
|
||||
fillJson (rpcSUCCESS);
|
||||
expect (! value_, "Value for rpcSUCCESS");
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
void expectFill (
|
||||
std::string const& label,
|
||||
Type status,
|
||||
Status::Strings messages,
|
||||
std::string const& message)
|
||||
{
|
||||
value_.clear ();
|
||||
fillJson (Status (status, messages));
|
||||
|
||||
auto prefix = label + ": ";
|
||||
expect (bool (value_), prefix + "No value");
|
||||
|
||||
auto error = value_[jss::error];
|
||||
expect (bool (error), prefix + "No error.");
|
||||
|
||||
auto code = error[jss::code].asInt();
|
||||
expect (status == code, prefix + "Wrong status " +
|
||||
std::to_string (code) + " != " + std::to_string (status));
|
||||
|
||||
auto m = error[jss::message].asString ();
|
||||
expect (m == message, m + " != " + message);
|
||||
|
||||
auto d = error[jss::data];
|
||||
size_t s1 = d.size(), s2 = messages.size();
|
||||
expect (s1 == s2, prefix + "Data sizes differ " +
|
||||
std::to_string (s1) + " != " + std::to_string (s2));
|
||||
for (auto i = 0; i < std::min (s1, s2); ++i)
|
||||
{
|
||||
auto ds = d[i].asString();
|
||||
expect (ds == messages[i], prefix + ds + " != " + messages[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void test_error ()
|
||||
{
|
||||
testcase ("error");
|
||||
expectFill (
|
||||
"temBAD_AMOUNT",
|
||||
temBAD_AMOUNT,
|
||||
{},
|
||||
"temBAD_AMOUNT: Can only send positive amounts.");
|
||||
|
||||
expectFill (
|
||||
"rpcBAD_SYNTAX",
|
||||
rpcBAD_SYNTAX,
|
||||
{"An error.", "Another error."},
|
||||
"badSyntax: Syntax error.");
|
||||
|
||||
expectFill (
|
||||
"integer message",
|
||||
23,
|
||||
{"Stuff."},
|
||||
"23");
|
||||
}
|
||||
|
||||
void test_throw ()
|
||||
{
|
||||
testcase ("throw");
|
||||
try
|
||||
{
|
||||
Throw<Status> (Status(temBAD_PATH, { "path=sdcdfd" }));
|
||||
}
|
||||
catch (Status const& s)
|
||||
{
|
||||
expect (s.toTER () == temBAD_PATH, "temBAD_PATH wasn't thrown");
|
||||
auto msgs = s.messages ();
|
||||
expect (msgs.size () == 1, "Wrong number of messages");
|
||||
expect (msgs[0] == "path=sdcdfd", msgs[0]);
|
||||
}
|
||||
catch (std::exception const&)
|
||||
{
|
||||
expect (false, "Didn't catch a Status");
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
void run()
|
||||
{
|
||||
test_OK ();
|
||||
test_error ();
|
||||
test_throw ();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE (fillJson, Status, RPC);
|
||||
|
||||
} // namespace RPC
|
||||
} // ripple
|
||||
342
src/test/rpc/Subscribe_test.cpp
Normal file
342
src/test/rpc/Subscribe_test.cpp
Normal file
@@ -0,0 +1,342 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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/LoadFeeTrack.h>
|
||||
#include <ripple/app/misc/NetworkOPs.h>
|
||||
#include <ripple/protocol/JsonFields.h>
|
||||
#include <ripple/test/WSClient.h>
|
||||
#include <ripple/test/jtx.h>
|
||||
#include <ripple/beast/unit_test.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
class Subscribe_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
void testServer()
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
using namespace jtx;
|
||||
Env env(*this);
|
||||
auto wsc = makeWSClient(env.app().config());
|
||||
Json::Value stream;
|
||||
|
||||
{
|
||||
// RPC subscribe to server stream
|
||||
stream[jss::streams] = Json::arrayValue;
|
||||
stream[jss::streams].append("server");
|
||||
auto jv = wsc->invoke("subscribe", stream);
|
||||
BEAST_EXPECT(jv[jss::status] == "success");
|
||||
}
|
||||
|
||||
{
|
||||
// Raise fee to cause an update
|
||||
auto& feeTrack = env.app().getFeeTrack();
|
||||
for(int i = 0; i < 5; ++i)
|
||||
feeTrack.raiseLocalFee();
|
||||
env.app().getOPs().reportFeeChange();
|
||||
|
||||
// Check stream update
|
||||
BEAST_EXPECT(wsc->findMsg(5s,
|
||||
[&](auto const& jv)
|
||||
{
|
||||
return jv[jss::type] == "serverStatus";
|
||||
}));
|
||||
}
|
||||
|
||||
{
|
||||
// RPC unsubscribe
|
||||
auto jv = wsc->invoke("unsubscribe", stream);
|
||||
BEAST_EXPECT(jv[jss::status] == "success");
|
||||
}
|
||||
|
||||
{
|
||||
// Raise fee to cause an update
|
||||
auto& feeTrack = env.app().getFeeTrack();
|
||||
for (int i = 0; i < 5; ++i)
|
||||
feeTrack.raiseLocalFee();
|
||||
env.app().getOPs().reportFeeChange();
|
||||
|
||||
// Check stream update
|
||||
BEAST_EXPECT(! wsc->getMsg(10ms));
|
||||
}
|
||||
}
|
||||
|
||||
void testLedger()
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
using namespace jtx;
|
||||
Env env(*this);
|
||||
auto wsc = makeWSClient(env.app().config());
|
||||
Json::Value stream;
|
||||
|
||||
{
|
||||
// RPC subscribe to ledger stream
|
||||
stream[jss::streams] = Json::arrayValue;
|
||||
stream[jss::streams].append("ledger");
|
||||
auto jv = wsc->invoke("subscribe", stream);
|
||||
BEAST_EXPECT(jv[jss::result][jss::ledger_index] == 2);
|
||||
}
|
||||
|
||||
{
|
||||
// Accept a ledger
|
||||
env.close();
|
||||
|
||||
// Check stream update
|
||||
BEAST_EXPECT(wsc->findMsg(5s,
|
||||
[&](auto const& jv)
|
||||
{
|
||||
return jv[jss::ledger_index] == 3;
|
||||
}));
|
||||
}
|
||||
|
||||
{
|
||||
// Accept another ledger
|
||||
env.close();
|
||||
|
||||
// Check stream update
|
||||
BEAST_EXPECT(wsc->findMsg(5s,
|
||||
[&](auto const& jv)
|
||||
{
|
||||
return jv[jss::ledger_index] == 4;
|
||||
}));
|
||||
}
|
||||
|
||||
// RPC unsubscribe
|
||||
auto jv = wsc->invoke("unsubscribe", stream);
|
||||
BEAST_EXPECT(jv[jss::status] == "success");
|
||||
}
|
||||
|
||||
void testTransactions()
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
using namespace jtx;
|
||||
Env env(*this);
|
||||
auto wsc = makeWSClient(env.app().config());
|
||||
Json::Value stream;
|
||||
|
||||
{
|
||||
// RPC subscribe to transactions stream
|
||||
stream[jss::streams] = Json::arrayValue;
|
||||
stream[jss::streams].append("transactions");
|
||||
auto jv = wsc->invoke("subscribe", stream);
|
||||
BEAST_EXPECT(jv[jss::status] == "success");
|
||||
}
|
||||
|
||||
{
|
||||
env.fund(XRP(10000), "alice");
|
||||
env.close();
|
||||
|
||||
// Check stream update for payment transaction
|
||||
BEAST_EXPECT(wsc->findMsg(5s,
|
||||
[&](auto const& jv)
|
||||
{
|
||||
return jv[jss::meta]["AffectedNodes"][1u]
|
||||
["CreatedNode"]["NewFields"][jss::Account] ==
|
||||
Account("alice").human();
|
||||
}));
|
||||
|
||||
// Check stream update for accountset transaction
|
||||
BEAST_EXPECT(wsc->findMsg(5s,
|
||||
[&](auto const& jv)
|
||||
{
|
||||
return jv[jss::meta]["AffectedNodes"][0u]
|
||||
["ModifiedNode"]["FinalFields"][jss::Account] ==
|
||||
Account("alice").human();
|
||||
}));
|
||||
|
||||
env.fund(XRP(10000), "bob");
|
||||
env.close();
|
||||
|
||||
// Check stream update for payment transaction
|
||||
BEAST_EXPECT(wsc->findMsg(5s,
|
||||
[&](auto const& jv)
|
||||
{
|
||||
return jv[jss::meta]["AffectedNodes"][1u]
|
||||
["CreatedNode"]["NewFields"][jss::Account] ==
|
||||
Account("bob").human();
|
||||
}));
|
||||
|
||||
// Check stream update for accountset transaction
|
||||
BEAST_EXPECT(wsc->findMsg(5s,
|
||||
[&](auto const& jv)
|
||||
{
|
||||
return jv[jss::meta]["AffectedNodes"][0u]
|
||||
["ModifiedNode"]["FinalFields"][jss::Account] ==
|
||||
Account("bob").human();
|
||||
}));
|
||||
}
|
||||
|
||||
{
|
||||
// RPC unsubscribe
|
||||
auto jv = wsc->invoke("unsubscribe", stream);
|
||||
BEAST_EXPECT(jv[jss::status] == "success");
|
||||
}
|
||||
|
||||
{
|
||||
// RPC subscribe to accounts stream
|
||||
stream = Json::objectValue;
|
||||
stream[jss::accounts] = Json::arrayValue;
|
||||
stream[jss::accounts].append(Account("alice").human());
|
||||
auto jv = wsc->invoke("subscribe", stream);
|
||||
BEAST_EXPECT(jv[jss::status] == "success");
|
||||
}
|
||||
|
||||
{
|
||||
// Transaction that does not affect stream
|
||||
env.fund(XRP(10000), "carol");
|
||||
env.close();
|
||||
BEAST_EXPECT(! wsc->getMsg(10ms));
|
||||
|
||||
// Transactions concerning alice
|
||||
env.trust(Account("bob")["USD"](100), "alice");
|
||||
env.close();
|
||||
|
||||
// Check stream updates
|
||||
BEAST_EXPECT(wsc->findMsg(5s,
|
||||
[&](auto const& jv)
|
||||
{
|
||||
return jv[jss::meta]["AffectedNodes"][1u]
|
||||
["ModifiedNode"]["FinalFields"][jss::Account] ==
|
||||
Account("alice").human();
|
||||
}));
|
||||
|
||||
BEAST_EXPECT(wsc->findMsg(5s,
|
||||
[&](auto const& jv)
|
||||
{
|
||||
return jv[jss::meta]["AffectedNodes"][1u]
|
||||
["CreatedNode"]["NewFields"]["LowLimit"]
|
||||
[jss::issuer] == Account("alice").human();
|
||||
}));
|
||||
}
|
||||
|
||||
// RPC unsubscribe
|
||||
auto jv = wsc->invoke("unsubscribe", stream);
|
||||
BEAST_EXPECT(jv[jss::status] == "success");
|
||||
}
|
||||
|
||||
void testManifests()
|
||||
{
|
||||
using namespace jtx;
|
||||
Env env(*this);
|
||||
auto wsc = makeWSClient(env.app().config());
|
||||
Json::Value stream;
|
||||
|
||||
{
|
||||
// RPC subscribe to manifests stream
|
||||
stream[jss::streams] = Json::arrayValue;
|
||||
stream[jss::streams].append("manifests");
|
||||
auto jv = wsc->invoke("subscribe", stream);
|
||||
BEAST_EXPECT(jv[jss::status] == "success");
|
||||
}
|
||||
|
||||
// RPC unsubscribe
|
||||
auto jv = wsc->invoke("unsubscribe", stream);
|
||||
BEAST_EXPECT(jv[jss::status] == "success");
|
||||
}
|
||||
|
||||
static
|
||||
std::unique_ptr<Config>
|
||||
makeValidatorConfig(
|
||||
std::string const& valPrivateKey, std::string const& valPublicKey)
|
||||
{
|
||||
auto p = std::make_unique<Config>();
|
||||
setupConfigForUnitTests(*p);
|
||||
|
||||
// If the config has valid validation keys then we run as a validator.
|
||||
auto const sk = parseBase58<SecretKey>(
|
||||
TOKEN_NODE_PRIVATE,
|
||||
valPrivateKey);
|
||||
if (!sk)
|
||||
Throw<std::runtime_error> ("Invalid validation private key");
|
||||
p->VALIDATION_PRIV = *sk;
|
||||
|
||||
auto const pk = parseBase58<PublicKey>(
|
||||
TOKEN_NODE_PUBLIC,
|
||||
valPublicKey);
|
||||
if (!pk)
|
||||
Throw<std::runtime_error> ("Invalid validation public key");
|
||||
p->VALIDATION_PUB = *pk;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
void testValidations()
|
||||
{
|
||||
using namespace jtx;
|
||||
|
||||
// Public key must be derived from the private key
|
||||
const std::string valPrivateKey =
|
||||
"paEdUCVVCNnv4aYBepid9Xh3NaAr9xWRw2vh351piFJrxQwvExd";
|
||||
const std::string valPublicKey =
|
||||
"n9MvFGjgv1kYkm7bLbb2QUwSqgzrQkYMYHXtrzN8W28Jfp2mVihq";
|
||||
Env env(*this, makeValidatorConfig(valPrivateKey, valPublicKey));
|
||||
auto wsc = makeWSClient(env.app().config());
|
||||
Json::Value stream;
|
||||
|
||||
{
|
||||
// RPC subscribe to validations stream
|
||||
stream[jss::streams] = Json::arrayValue;
|
||||
stream[jss::streams].append("validations");
|
||||
auto jv = wsc->invoke("subscribe", stream);
|
||||
BEAST_EXPECT(jv[jss::status] == "success");
|
||||
}
|
||||
|
||||
{
|
||||
// Accept a ledger
|
||||
env.close();
|
||||
|
||||
// Check stream update
|
||||
BEAST_EXPECT(wsc->findMsg(5s,
|
||||
[&](auto const& jv)
|
||||
{
|
||||
return jv[jss::type] == "validationReceived" &&
|
||||
jv[jss::validation_public_key] == valPublicKey &&
|
||||
jv[jss::ledger_hash] ==
|
||||
to_string(env.closed()->info().hash) &&
|
||||
jv[jss::ledger_index] ==
|
||||
to_string(env.closed()->info().seq) &&
|
||||
jv[jss::flags] ==
|
||||
(vfFullyCanonicalSig | STValidation::kFullFlag) &&
|
||||
jv[jss::full] == true &&
|
||||
!jv.isMember(jss::load_fee) &&
|
||||
jv[jss::signature] &&
|
||||
jv[jss::signing_time];
|
||||
}));
|
||||
}
|
||||
|
||||
// RPC unsubscribe
|
||||
auto jv = wsc->invoke("unsubscribe", stream);
|
||||
BEAST_EXPECT(jv[jss::status] == "success");
|
||||
}
|
||||
|
||||
void run() override
|
||||
{
|
||||
testServer();
|
||||
testLedger();
|
||||
testTransactions();
|
||||
testManifests();
|
||||
testValidations();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(Subscribe,app,ripple);
|
||||
|
||||
} // test
|
||||
} // ripple
|
||||
Reference in New Issue
Block a user