Compare commits

...

2 Commits

Author SHA1 Message Date
JCW
ac55c8725c Fix unit tests 2025-07-02 11:14:06 +01:00
Jingchen
793768a7ba Migrate some tests 2025-06-27 14:02:36 +01:00
17 changed files with 2508 additions and 3107 deletions

View File

@@ -1,74 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/XRPLF/rippled/
Copyright (c) 2023 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 <xrpl/beast/unit_test.h>
#include <xrpl/beast/unit_test/suite.h>
#include <xrpl/protocol/ApiVersion.h>
#include <array>
#include <cstdint>
#include <limits>
#include <optional>
#include <type_traits>
#include <utility>
namespace ripple {
namespace test {
struct ApiVersion_test : beast::unit_test::suite
{
void
run() override
{
{
testcase("API versions invariants");
static_assert(
RPC::apiMinimumSupportedVersion <=
RPC::apiMaximumSupportedVersion);
static_assert(
RPC::apiMinimumSupportedVersion <= RPC::apiMaximumValidVersion);
static_assert(
RPC::apiMaximumSupportedVersion <= RPC::apiMaximumValidVersion);
static_assert(RPC::apiBetaVersion <= RPC::apiMaximumValidVersion);
BEAST_EXPECT(true);
}
{
// Update when we change versions
testcase("API versions");
static_assert(RPC::apiMinimumSupportedVersion >= 1);
static_assert(RPC::apiMinimumSupportedVersion < 2);
static_assert(RPC::apiMaximumSupportedVersion >= 2);
static_assert(RPC::apiMaximumSupportedVersion < 3);
static_assert(RPC::apiMaximumValidVersion >= 3);
static_assert(RPC::apiMaximumValidVersion < 4);
static_assert(RPC::apiBetaVersion >= 3);
static_assert(RPC::apiBetaVersion < 4);
BEAST_EXPECT(true);
}
}
};
BEAST_DEFINE_TESTSUITE(ApiVersion, protocol, ripple);
} // namespace test
} // namespace ripple

View File

@@ -1,118 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2020 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 <xrpl/beast/unit_test.h>
#include <xrpl/protocol/BuildInfo.h>
namespace ripple {
class BuildInfo_test : public beast::unit_test::suite
{
public:
void
testEncodeSoftwareVersion()
{
testcase("EncodeSoftwareVersion");
auto encodedVersion = BuildInfo::encodeSoftwareVersion("1.2.3-b7");
// the first two bytes identify the particular implementation, 0x183B
BEAST_EXPECT(
(encodedVersion & 0xFFFF'0000'0000'0000LLU) ==
0x183B'0000'0000'0000LLU);
// the next three bytes: major version, minor version, patch version,
// 0x010203
BEAST_EXPECT(
(encodedVersion & 0x0000'FFFF'FF00'0000LLU) ==
0x0000'0102'0300'0000LLU);
// the next two bits:
{
// 01 if a beta
BEAST_EXPECT(
(encodedVersion & 0x0000'0000'00C0'0000LLU) >> 22 == 0b01);
// 10 if an RC
encodedVersion = BuildInfo::encodeSoftwareVersion("1.2.4-rc7");
BEAST_EXPECT(
(encodedVersion & 0x0000'0000'00C0'0000LLU) >> 22 == 0b10);
// 11 if neither an RC nor a beta
encodedVersion = BuildInfo::encodeSoftwareVersion("1.2.5");
BEAST_EXPECT(
(encodedVersion & 0x0000'0000'00C0'0000LLU) >> 22 == 0b11);
}
// the next six bits: rc/beta number (1-63)
encodedVersion = BuildInfo::encodeSoftwareVersion("1.2.6-b63");
BEAST_EXPECT((encodedVersion & 0x0000'0000'003F'0000LLU) >> 16 == 63);
// the last two bytes are zeros
BEAST_EXPECT((encodedVersion & 0x0000'0000'0000'FFFFLLU) == 0);
// Test some version strings with wrong formats:
// no rc/beta number
encodedVersion = BuildInfo::encodeSoftwareVersion("1.2.3-b");
BEAST_EXPECT((encodedVersion & 0x0000'0000'00FF'0000LLU) == 0);
// rc/beta number out of range
encodedVersion = BuildInfo::encodeSoftwareVersion("1.2.3-b64");
BEAST_EXPECT((encodedVersion & 0x0000'0000'00FF'0000LLU) == 0);
// Check that the rc/beta number of a release is 0:
encodedVersion = BuildInfo::encodeSoftwareVersion("1.2.6");
BEAST_EXPECT((encodedVersion & 0x0000'0000'003F'0000LLU) == 0);
}
void
testIsRippledVersion()
{
testcase("IsRippledVersion");
auto vFF = 0xFFFF'FFFF'FFFF'FFFFLLU;
BEAST_EXPECT(!BuildInfo::isRippledVersion(vFF));
auto vRippled = 0x183B'0000'0000'0000LLU;
BEAST_EXPECT(BuildInfo::isRippledVersion(vRippled));
}
void
testIsNewerVersion()
{
testcase("IsNewerVersion");
auto vFF = 0xFFFF'FFFF'FFFF'FFFFLLU;
BEAST_EXPECT(!BuildInfo::isNewerVersion(vFF));
auto v159 = BuildInfo::encodeSoftwareVersion("1.5.9");
BEAST_EXPECT(!BuildInfo::isNewerVersion(v159));
auto vCurrent = BuildInfo::getEncodedVersion();
BEAST_EXPECT(!BuildInfo::isNewerVersion(vCurrent));
auto vMax = BuildInfo::encodeSoftwareVersion("255.255.255");
BEAST_EXPECT(BuildInfo::isNewerVersion(vMax));
}
void
run() override
{
testEncodeSoftwareVersion();
testIsRippledVersion();
testIsNewerVersion();
}
};
BEAST_DEFINE_TESTSUITE(BuildInfo, protocol, ripple);
} // namespace ripple

View File

@@ -1,198 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012-2017 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 <test/jtx.h>
#include <xrpl/protocol/Feature.h>
#include <functional>
#include <vector>
namespace ripple {
class Hooks_test : public beast::unit_test::suite
{
/**
* This unit test was requested here:
* https://github.com/ripple/rippled/pull/4089#issuecomment-1050274539
* These are tests that exercise facilities that are reserved for when Hooks
* is merged in the future.
**/
void
testHookFields()
{
testcase("Test Hooks fields");
using namespace test::jtx;
std::vector<std::reference_wrapper<SField const>> fields_to_test = {
sfHookResult,
sfHookStateChangeCount,
sfHookEmitCount,
sfHookExecutionIndex,
sfHookApiVersion,
sfHookStateCount,
sfEmitGeneration,
sfHookOn,
sfHookInstructionCount,
sfEmitBurden,
sfHookReturnCode,
sfReferenceCount,
sfEmitParentTxnID,
sfEmitNonce,
sfEmitHookHash,
sfHookStateKey,
sfHookHash,
sfHookNamespace,
sfHookSetTxnID,
sfHookStateData,
sfHookReturnString,
sfHookParameterName,
sfHookParameterValue,
sfEmitCallback,
sfHookAccount,
sfEmittedTxn,
sfHook,
sfHookDefinition,
sfHookParameter,
sfHookGrant,
sfEmitDetails,
sfHookExecutions,
sfHookExecution,
sfHookParameters,
sfHooks,
sfHookGrants};
for (auto const& rf : fields_to_test)
{
SField const& f = rf.get();
STObject dummy{sfGeneric};
BEAST_EXPECT(!dummy.isFieldPresent(f));
switch (f.fieldType)
{
case STI_UINT8: {
dummy.setFieldU8(f, 0);
BEAST_EXPECT(dummy.getFieldU8(f) == 0);
dummy.setFieldU8(f, 255);
BEAST_EXPECT(dummy.getFieldU8(f) == 255);
BEAST_EXPECT(dummy.isFieldPresent(f));
break;
}
case STI_UINT16: {
dummy.setFieldU16(f, 0);
BEAST_EXPECT(dummy.getFieldU16(f) == 0);
dummy.setFieldU16(f, 0xFFFFU);
BEAST_EXPECT(dummy.getFieldU16(f) == 0xFFFFU);
BEAST_EXPECT(dummy.isFieldPresent(f));
break;
}
case STI_UINT32: {
dummy.setFieldU32(f, 0);
BEAST_EXPECT(dummy.getFieldU32(f) == 0);
dummy.setFieldU32(f, 0xFFFFFFFFU);
BEAST_EXPECT(dummy.getFieldU32(f) == 0xFFFFFFFFU);
BEAST_EXPECT(dummy.isFieldPresent(f));
break;
}
case STI_UINT64: {
dummy.setFieldU64(f, 0);
BEAST_EXPECT(dummy.getFieldU64(f) == 0);
dummy.setFieldU64(f, 0xFFFFFFFFFFFFFFFFU);
BEAST_EXPECT(dummy.getFieldU64(f) == 0xFFFFFFFFFFFFFFFFU);
BEAST_EXPECT(dummy.isFieldPresent(f));
break;
}
case STI_UINT256: {
uint256 u = uint256::fromVoid(
"DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBE"
"EFDEADBEEF");
dummy.setFieldH256(f, u);
BEAST_EXPECT(dummy.getFieldH256(f) == u);
BEAST_EXPECT(dummy.isFieldPresent(f));
break;
}
case STI_VL: {
std::vector<uint8_t> v{1, 2, 3};
dummy.setFieldVL(f, v);
BEAST_EXPECT(dummy.getFieldVL(f) == v);
BEAST_EXPECT(dummy.isFieldPresent(f));
break;
}
case STI_ACCOUNT: {
AccountID id = *parseBase58<AccountID>(
"rwfSjJNK2YQuN64bSWn7T2eY9FJAyAPYJT");
dummy.setAccountID(f, id);
BEAST_EXPECT(dummy.getAccountID(f) == id);
BEAST_EXPECT(dummy.isFieldPresent(f));
break;
}
case STI_OBJECT: {
dummy.emplace_back(STObject{f});
BEAST_EXPECT(dummy.getField(f).getFName() == f);
BEAST_EXPECT(dummy.isFieldPresent(f));
break;
}
case STI_ARRAY: {
STArray dummy2{f, 2};
dummy2.push_back(STObject{sfGeneric});
dummy2.push_back(STObject{sfGeneric});
dummy.setFieldArray(f, dummy2);
BEAST_EXPECT(dummy.getFieldArray(f) == dummy2);
BEAST_EXPECT(dummy.isFieldPresent(f));
break;
}
default:
BEAST_EXPECT(false);
}
}
}
public:
void
run() override
{
using namespace test::jtx;
testHookFields();
}
};
BEAST_DEFINE_TESTSUITE(Hooks, protocol, ripple);
} // namespace ripple

View File

@@ -1,983 +0,0 @@
//------------------------------------------------------------------------------
/*
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 <xrpl/basics/UnorderedContainers.h>
#include <xrpl/beast/unit_test.h>
#include <xrpl/protocol/Book.h>
#include <xrpl/protocol/Issue.h>
#include <sys/types.h>
#include <map>
#include <optional>
#include <set>
#include <typeinfo>
#include <unordered_set>
#if BEAST_MSVC
#define STL_SET_HAS_EMPLACE 1
#else
#define STL_SET_HAS_EMPLACE 0
#endif
#ifndef RIPPLE_ASSETS_ENABLE_STD_HASH
#if BEAST_MAC || BEAST_IOS
#define RIPPLE_ASSETS_ENABLE_STD_HASH 0
#else
#define RIPPLE_ASSETS_ENABLE_STD_HASH 1
#endif
#endif
namespace ripple {
class Issue_test : public beast::unit_test::suite
{
public:
using Domain = uint256;
// Comparison, hash tests for uint60 (via base_uint)
template <typename Unsigned>
void
testUnsigned()
{
Unsigned const u1(1);
Unsigned const u2(2);
Unsigned const u3(3);
BEAST_EXPECT(u1 != u2);
BEAST_EXPECT(u1 < u2);
BEAST_EXPECT(u1 <= u2);
BEAST_EXPECT(u2 <= u2);
BEAST_EXPECT(u2 == u2);
BEAST_EXPECT(u2 >= u2);
BEAST_EXPECT(u3 >= u2);
BEAST_EXPECT(u3 > u2);
std::hash<Unsigned> hash;
BEAST_EXPECT(hash(u1) == hash(u1));
BEAST_EXPECT(hash(u2) == hash(u2));
BEAST_EXPECT(hash(u3) == hash(u3));
BEAST_EXPECT(hash(u1) != hash(u2));
BEAST_EXPECT(hash(u1) != hash(u3));
BEAST_EXPECT(hash(u2) != hash(u3));
}
//--------------------------------------------------------------------------
// Comparison, hash tests for Issue
template <class Issue>
void
testIssue()
{
Currency const c1(1);
AccountID const i1(1);
Currency const c2(2);
AccountID const i2(2);
Currency const c3(3);
AccountID const i3(3);
BEAST_EXPECT(Issue(c1, i1) != Issue(c2, i1));
BEAST_EXPECT(Issue(c1, i1) < Issue(c2, i1));
BEAST_EXPECT(Issue(c1, i1) <= Issue(c2, i1));
BEAST_EXPECT(Issue(c2, i1) <= Issue(c2, i1));
BEAST_EXPECT(Issue(c2, i1) == Issue(c2, i1));
BEAST_EXPECT(Issue(c2, i1) >= Issue(c2, i1));
BEAST_EXPECT(Issue(c3, i1) >= Issue(c2, i1));
BEAST_EXPECT(Issue(c3, i1) > Issue(c2, i1));
BEAST_EXPECT(Issue(c1, i1) != Issue(c1, i2));
BEAST_EXPECT(Issue(c1, i1) < Issue(c1, i2));
BEAST_EXPECT(Issue(c1, i1) <= Issue(c1, i2));
BEAST_EXPECT(Issue(c1, i2) <= Issue(c1, i2));
BEAST_EXPECT(Issue(c1, i2) == Issue(c1, i2));
BEAST_EXPECT(Issue(c1, i2) >= Issue(c1, i2));
BEAST_EXPECT(Issue(c1, i3) >= Issue(c1, i2));
BEAST_EXPECT(Issue(c1, i3) > Issue(c1, i2));
std::hash<Issue> hash;
BEAST_EXPECT(hash(Issue(c1, i1)) == hash(Issue(c1, i1)));
BEAST_EXPECT(hash(Issue(c1, i2)) == hash(Issue(c1, i2)));
BEAST_EXPECT(hash(Issue(c1, i3)) == hash(Issue(c1, i3)));
BEAST_EXPECT(hash(Issue(c2, i1)) == hash(Issue(c2, i1)));
BEAST_EXPECT(hash(Issue(c2, i2)) == hash(Issue(c2, i2)));
BEAST_EXPECT(hash(Issue(c2, i3)) == hash(Issue(c2, i3)));
BEAST_EXPECT(hash(Issue(c3, i1)) == hash(Issue(c3, i1)));
BEAST_EXPECT(hash(Issue(c3, i2)) == hash(Issue(c3, i2)));
BEAST_EXPECT(hash(Issue(c3, i3)) == hash(Issue(c3, i3)));
BEAST_EXPECT(hash(Issue(c1, i1)) != hash(Issue(c1, i2)));
BEAST_EXPECT(hash(Issue(c1, i1)) != hash(Issue(c1, i3)));
BEAST_EXPECT(hash(Issue(c1, i1)) != hash(Issue(c2, i1)));
BEAST_EXPECT(hash(Issue(c1, i1)) != hash(Issue(c2, i2)));
BEAST_EXPECT(hash(Issue(c1, i1)) != hash(Issue(c2, i3)));
BEAST_EXPECT(hash(Issue(c1, i1)) != hash(Issue(c3, i1)));
BEAST_EXPECT(hash(Issue(c1, i1)) != hash(Issue(c3, i2)));
BEAST_EXPECT(hash(Issue(c1, i1)) != hash(Issue(c3, i3)));
}
template <class Set>
void
testIssueSet()
{
Currency const c1(1);
AccountID const i1(1);
Currency const c2(2);
AccountID const i2(2);
Issue const a1(c1, i1);
Issue const a2(c2, i2);
{
Set c;
c.insert(a1);
if (!BEAST_EXPECT(c.size() == 1))
return;
c.insert(a2);
if (!BEAST_EXPECT(c.size() == 2))
return;
if (!BEAST_EXPECT(c.erase(Issue(c1, i2)) == 0))
return;
if (!BEAST_EXPECT(c.erase(Issue(c1, i1)) == 1))
return;
if (!BEAST_EXPECT(c.erase(Issue(c2, i2)) == 1))
return;
if (!BEAST_EXPECT(c.empty()))
return;
}
{
Set c;
c.insert(a1);
if (!BEAST_EXPECT(c.size() == 1))
return;
c.insert(a2);
if (!BEAST_EXPECT(c.size() == 2))
return;
if (!BEAST_EXPECT(c.erase(Issue(c1, i2)) == 0))
return;
if (!BEAST_EXPECT(c.erase(Issue(c1, i1)) == 1))
return;
if (!BEAST_EXPECT(c.erase(Issue(c2, i2)) == 1))
return;
if (!BEAST_EXPECT(c.empty()))
return;
#if STL_SET_HAS_EMPLACE
c.emplace(c1, i1);
if (!BEAST_EXPECT(c.size() == 1))
return;
c.emplace(c2, i2);
if (!BEAST_EXPECT(c.size() == 2))
return;
#endif
}
}
template <class Map>
void
testIssueMap()
{
Currency const c1(1);
AccountID const i1(1);
Currency const c2(2);
AccountID const i2(2);
Issue const a1(c1, i1);
Issue const a2(c2, i2);
{
Map c;
c.insert(std::make_pair(a1, 1));
if (!BEAST_EXPECT(c.size() == 1))
return;
c.insert(std::make_pair(a2, 2));
if (!BEAST_EXPECT(c.size() == 2))
return;
if (!BEAST_EXPECT(c.erase(Issue(c1, i2)) == 0))
return;
if (!BEAST_EXPECT(c.erase(Issue(c1, i1)) == 1))
return;
if (!BEAST_EXPECT(c.erase(Issue(c2, i2)) == 1))
return;
if (!BEAST_EXPECT(c.empty()))
return;
}
{
Map c;
c.insert(std::make_pair(a1, 1));
if (!BEAST_EXPECT(c.size() == 1))
return;
c.insert(std::make_pair(a2, 2));
if (!BEAST_EXPECT(c.size() == 2))
return;
if (!BEAST_EXPECT(c.erase(Issue(c1, i2)) == 0))
return;
if (!BEAST_EXPECT(c.erase(Issue(c1, i1)) == 1))
return;
if (!BEAST_EXPECT(c.erase(Issue(c2, i2)) == 1))
return;
if (!BEAST_EXPECT(c.empty()))
return;
}
}
template <class Set>
void
testIssueDomainSet()
{
Currency const c1(1);
AccountID const i1(1);
Currency const c2(2);
AccountID const i2(2);
Issue const a1(c1, i1);
Issue const a2(c2, i2);
uint256 const domain1{1};
uint256 const domain2{2};
Set c;
c.insert(std::make_pair(a1, domain1));
if (!BEAST_EXPECT(c.size() == 1))
return;
c.insert(std::make_pair(a2, domain1));
if (!BEAST_EXPECT(c.size() == 2))
return;
c.insert(std::make_pair(a2, domain2));
if (!BEAST_EXPECT(c.size() == 3))
return;
if (!BEAST_EXPECT(c.erase(std::make_pair(Issue(c1, i2), domain1)) == 0))
return;
if (!BEAST_EXPECT(c.erase(std::make_pair(a1, domain1)) == 1))
return;
if (!BEAST_EXPECT(c.erase(std::make_pair(a2, domain1)) == 1))
return;
if (!BEAST_EXPECT(c.erase(std::make_pair(a2, domain2)) == 1))
return;
if (!BEAST_EXPECT(c.empty()))
return;
}
template <class Map>
void
testIssueDomainMap()
{
Currency const c1(1);
AccountID const i1(1);
Currency const c2(2);
AccountID const i2(2);
Issue const a1(c1, i1);
Issue const a2(c2, i2);
uint256 const domain1{1};
uint256 const domain2{2};
Map c;
c.insert(std::make_pair(std::make_pair(a1, domain1), 1));
if (!BEAST_EXPECT(c.size() == 1))
return;
c.insert(std::make_pair(std::make_pair(a2, domain1), 2));
if (!BEAST_EXPECT(c.size() == 2))
return;
c.insert(std::make_pair(std::make_pair(a2, domain2), 2));
if (!BEAST_EXPECT(c.size() == 3))
return;
if (!BEAST_EXPECT(c.erase(std::make_pair(Issue(c1, i2), domain1)) == 0))
return;
if (!BEAST_EXPECT(c.erase(std::make_pair(a1, domain1)) == 1))
return;
if (!BEAST_EXPECT(c.erase(std::make_pair(a2, domain1)) == 1))
return;
if (!BEAST_EXPECT(c.erase(std::make_pair(a2, domain2)) == 1))
return;
if (!BEAST_EXPECT(c.empty()))
return;
}
void
testIssueDomainSets()
{
testcase("std::set <std::pair<Issue, Domain>>");
testIssueDomainSet<std::set<std::pair<Issue, Domain>>>();
testcase("std::set <std::pair<Issue, Domain>>");
testIssueDomainSet<std::set<std::pair<Issue, Domain>>>();
testcase("hash_set <std::pair<Issue, Domain>>");
testIssueDomainSet<hash_set<std::pair<Issue, Domain>>>();
testcase("hash_set <std::pair<Issue, Domain>>");
testIssueDomainSet<hash_set<std::pair<Issue, Domain>>>();
}
void
testIssueDomainMaps()
{
testcase("std::map <std::pair<Issue, Domain>, int>");
testIssueDomainMap<std::map<std::pair<Issue, Domain>, int>>();
testcase("std::map <std::pair<Issue, Domain>, int>");
testIssueDomainMap<std::map<std::pair<Issue, Domain>, int>>();
#if RIPPLE_ASSETS_ENABLE_STD_HASH
testcase("hash_map <std::pair<Issue, Domain>, int>");
testIssueDomainMap<hash_map<std::pair<Issue, Domain>, int>>();
testcase("hash_map <std::pair<Issue, Domain>, int>");
testIssueDomainMap<hash_map<std::pair<Issue, Domain>, int>>();
testcase("hardened_hash_map <std::pair<Issue, Domain>, int>");
testIssueDomainMap<hardened_hash_map<std::pair<Issue, Domain>, int>>();
testcase("hardened_hash_map <std::pair<Issue, Domain>, int>");
testIssueDomainMap<hardened_hash_map<std::pair<Issue, Domain>, int>>();
#endif
}
void
testIssueSets()
{
testcase("std::set <Issue>");
testIssueSet<std::set<Issue>>();
testcase("std::set <Issue>");
testIssueSet<std::set<Issue>>();
#if RIPPLE_ASSETS_ENABLE_STD_HASH
testcase("std::unordered_set <Issue>");
testIssueSet<std::unordered_set<Issue>>();
testcase("std::unordered_set <Issue>");
testIssueSet<std::unordered_set<Issue>>();
#endif
testcase("hash_set <Issue>");
testIssueSet<hash_set<Issue>>();
testcase("hash_set <Issue>");
testIssueSet<hash_set<Issue>>();
}
void
testIssueMaps()
{
testcase("std::map <Issue, int>");
testIssueMap<std::map<Issue, int>>();
testcase("std::map <Issue, int>");
testIssueMap<std::map<Issue, int>>();
#if RIPPLE_ASSETS_ENABLE_STD_HASH
testcase("std::unordered_map <Issue, int>");
testIssueMap<std::unordered_map<Issue, int>>();
testcase("std::unordered_map <Issue, int>");
testIssueMap<std::unordered_map<Issue, int>>();
testcase("hash_map <Issue, int>");
testIssueMap<hash_map<Issue, int>>();
testcase("hash_map <Issue, int>");
testIssueMap<hash_map<Issue, int>>();
#endif
}
//--------------------------------------------------------------------------
// Comparison, hash tests for Book
template <class Book>
void
testBook()
{
Currency const c1(1);
AccountID const i1(1);
Currency const c2(2);
AccountID const i2(2);
Currency const c3(3);
AccountID const i3(3);
Issue a1(c1, i1);
Issue a2(c1, i2);
Issue a3(c2, i2);
Issue a4(c3, i2);
uint256 const domain1{1};
uint256 const domain2{2};
// Books without domains
BEAST_EXPECT(Book(a1, a2, std::nullopt) != Book(a2, a3, std::nullopt));
BEAST_EXPECT(Book(a1, a2, std::nullopt) < Book(a2, a3, std::nullopt));
BEAST_EXPECT(Book(a1, a2, std::nullopt) <= Book(a2, a3, std::nullopt));
BEAST_EXPECT(Book(a2, a3, std::nullopt) <= Book(a2, a3, std::nullopt));
BEAST_EXPECT(Book(a2, a3, std::nullopt) == Book(a2, a3, std::nullopt));
BEAST_EXPECT(Book(a2, a3, std::nullopt) >= Book(a2, a3, std::nullopt));
BEAST_EXPECT(Book(a3, a4, std::nullopt) >= Book(a2, a3, std::nullopt));
BEAST_EXPECT(Book(a3, a4, std::nullopt) > Book(a2, a3, std::nullopt));
// test domain books
{
// Books with different domains
BEAST_EXPECT(Book(a2, a3, domain1) != Book(a2, a3, domain2));
BEAST_EXPECT(Book(a2, a3, domain1) < Book(a2, a3, domain2));
BEAST_EXPECT(Book(a2, a3, domain2) > Book(a2, a3, domain1));
// One Book has a domain, the other does not
BEAST_EXPECT(Book(a2, a3, domain1) != Book(a2, a3, std::nullopt));
BEAST_EXPECT(Book(a2, a3, std::nullopt) < Book(a2, a3, domain1));
BEAST_EXPECT(Book(a2, a3, domain1) > Book(a2, a3, std::nullopt));
// Both Books have the same domain
BEAST_EXPECT(Book(a2, a3, domain1) == Book(a2, a3, domain1));
BEAST_EXPECT(Book(a2, a3, domain2) == Book(a2, a3, domain2));
BEAST_EXPECT(
Book(a2, a3, std::nullopt) == Book(a2, a3, std::nullopt));
// Both Books have no domain
BEAST_EXPECT(
Book(a2, a3, std::nullopt) == Book(a2, a3, std::nullopt));
// Testing comparisons with >= and <=
// When comparing books with domain1 vs domain2
BEAST_EXPECT(Book(a2, a3, domain1) <= Book(a2, a3, domain2));
BEAST_EXPECT(Book(a2, a3, domain2) >= Book(a2, a3, domain1));
BEAST_EXPECT(Book(a2, a3, domain1) >= Book(a2, a3, domain1));
BEAST_EXPECT(Book(a2, a3, domain2) <= Book(a2, a3, domain2));
// One Book has domain1 and the other has no domain
BEAST_EXPECT(Book(a2, a3, domain1) > Book(a2, a3, std::nullopt));
BEAST_EXPECT(Book(a2, a3, std::nullopt) < Book(a2, a3, domain1));
// One Book has domain2 and the other has no domain
BEAST_EXPECT(Book(a2, a3, domain2) > Book(a2, a3, std::nullopt));
BEAST_EXPECT(Book(a2, a3, std::nullopt) < Book(a2, a3, domain2));
// Comparing two Books with no domains
BEAST_EXPECT(
Book(a2, a3, std::nullopt) <= Book(a2, a3, std::nullopt));
BEAST_EXPECT(
Book(a2, a3, std::nullopt) >= Book(a2, a3, std::nullopt));
// Test case where domain1 is less than domain2
BEAST_EXPECT(Book(a2, a3, domain1) <= Book(a2, a3, domain2));
BEAST_EXPECT(Book(a2, a3, domain2) >= Book(a2, a3, domain1));
// Test case where domain2 is equal to domain1
BEAST_EXPECT(Book(a2, a3, domain1) >= Book(a2, a3, domain1));
BEAST_EXPECT(Book(a2, a3, domain1) <= Book(a2, a3, domain1));
// More test cases involving a4 (with domain2)
// Comparing Book with domain2 (a4) to a Book with domain1
BEAST_EXPECT(Book(a2, a3, domain1) < Book(a3, a4, domain2));
BEAST_EXPECT(Book(a3, a4, domain2) > Book(a2, a3, domain1));
// Comparing Book with domain2 (a4) to a Book with no domain
BEAST_EXPECT(Book(a3, a4, domain2) > Book(a2, a3, std::nullopt));
BEAST_EXPECT(Book(a2, a3, std::nullopt) < Book(a3, a4, domain2));
// Comparing Book with domain2 (a4) to a Book with the same domain
BEAST_EXPECT(Book(a3, a4, domain2) == Book(a3, a4, domain2));
// Comparing Book with domain2 (a4) to a Book with domain1
BEAST_EXPECT(Book(a2, a3, domain1) < Book(a3, a4, domain2));
BEAST_EXPECT(Book(a3, a4, domain2) > Book(a2, a3, domain1));
}
std::hash<Book> hash;
// log << std::hex << hash (Book (a1, a2));
// log << std::hex << hash (Book (a1, a2));
//
// log << std::hex << hash (Book (a1, a3));
// log << std::hex << hash (Book (a1, a3));
//
// log << std::hex << hash (Book (a1, a4));
// log << std::hex << hash (Book (a1, a4));
//
// log << std::hex << hash (Book (a2, a3));
// log << std::hex << hash (Book (a2, a3));
//
// log << std::hex << hash (Book (a2, a4));
// log << std::hex << hash (Book (a2, a4));
//
// log << std::hex << hash (Book (a3, a4));
// log << std::hex << hash (Book (a3, a4));
BEAST_EXPECT(
hash(Book(a1, a2, std::nullopt)) ==
hash(Book(a1, a2, std::nullopt)));
BEAST_EXPECT(
hash(Book(a1, a3, std::nullopt)) ==
hash(Book(a1, a3, std::nullopt)));
BEAST_EXPECT(
hash(Book(a1, a4, std::nullopt)) ==
hash(Book(a1, a4, std::nullopt)));
BEAST_EXPECT(
hash(Book(a2, a3, std::nullopt)) ==
hash(Book(a2, a3, std::nullopt)));
BEAST_EXPECT(
hash(Book(a2, a4, std::nullopt)) ==
hash(Book(a2, a4, std::nullopt)));
BEAST_EXPECT(
hash(Book(a3, a4, std::nullopt)) ==
hash(Book(a3, a4, std::nullopt)));
BEAST_EXPECT(
hash(Book(a1, a2, std::nullopt)) !=
hash(Book(a1, a3, std::nullopt)));
BEAST_EXPECT(
hash(Book(a1, a2, std::nullopt)) !=
hash(Book(a1, a4, std::nullopt)));
BEAST_EXPECT(
hash(Book(a1, a2, std::nullopt)) !=
hash(Book(a2, a3, std::nullopt)));
BEAST_EXPECT(
hash(Book(a1, a2, std::nullopt)) !=
hash(Book(a2, a4, std::nullopt)));
BEAST_EXPECT(
hash(Book(a1, a2, std::nullopt)) !=
hash(Book(a3, a4, std::nullopt)));
// Books with domain
BEAST_EXPECT(
hash(Book(a1, a2, domain1)) == hash(Book(a1, a2, domain1)));
BEAST_EXPECT(
hash(Book(a1, a3, domain1)) == hash(Book(a1, a3, domain1)));
BEAST_EXPECT(
hash(Book(a1, a4, domain1)) == hash(Book(a1, a4, domain1)));
BEAST_EXPECT(
hash(Book(a2, a3, domain1)) == hash(Book(a2, a3, domain1)));
BEAST_EXPECT(
hash(Book(a2, a4, domain1)) == hash(Book(a2, a4, domain1)));
BEAST_EXPECT(
hash(Book(a3, a4, domain1)) == hash(Book(a3, a4, domain1)));
BEAST_EXPECT(
hash(Book(a1, a2, std::nullopt)) ==
hash(Book(a1, a2, std::nullopt)));
// Comparing Books with domain1 vs no domain
BEAST_EXPECT(
hash(Book(a1, a2, std::nullopt)) != hash(Book(a1, a2, domain1)));
BEAST_EXPECT(
hash(Book(a1, a3, std::nullopt)) != hash(Book(a1, a3, domain1)));
BEAST_EXPECT(
hash(Book(a1, a4, std::nullopt)) != hash(Book(a1, a4, domain1)));
BEAST_EXPECT(
hash(Book(a2, a3, std::nullopt)) != hash(Book(a2, a3, domain1)));
BEAST_EXPECT(
hash(Book(a2, a4, std::nullopt)) != hash(Book(a2, a4, domain1)));
BEAST_EXPECT(
hash(Book(a3, a4, std::nullopt)) != hash(Book(a3, a4, domain1)));
// Books with domain1 but different Issues
BEAST_EXPECT(
hash(Book(a1, a2, domain1)) != hash(Book(a1, a3, domain1)));
BEAST_EXPECT(
hash(Book(a1, a2, domain1)) != hash(Book(a1, a4, domain1)));
BEAST_EXPECT(
hash(Book(a2, a3, domain1)) != hash(Book(a2, a4, domain1)));
BEAST_EXPECT(
hash(Book(a1, a2, domain1)) != hash(Book(a2, a3, domain1)));
BEAST_EXPECT(
hash(Book(a2, a4, domain1)) != hash(Book(a3, a4, domain1)));
BEAST_EXPECT(
hash(Book(a3, a4, domain1)) != hash(Book(a1, a4, domain1)));
// Books with domain1 and domain2
BEAST_EXPECT(
hash(Book(a1, a2, domain1)) != hash(Book(a1, a2, domain2)));
BEAST_EXPECT(
hash(Book(a1, a3, domain1)) != hash(Book(a1, a3, domain2)));
BEAST_EXPECT(
hash(Book(a1, a4, domain1)) != hash(Book(a1, a4, domain2)));
BEAST_EXPECT(
hash(Book(a2, a3, domain1)) != hash(Book(a2, a3, domain2)));
BEAST_EXPECT(
hash(Book(a2, a4, domain1)) != hash(Book(a2, a4, domain2)));
BEAST_EXPECT(
hash(Book(a3, a4, domain1)) != hash(Book(a3, a4, domain2)));
}
//--------------------------------------------------------------------------
template <class Set>
void
testBookSet()
{
Currency const c1(1);
AccountID const i1(1);
Currency const c2(2);
AccountID const i2(2);
Issue const a1(c1, i1);
Issue const a2(c2, i2);
Book const b1(a1, a2, std::nullopt);
Book const b2(a2, a1, std::nullopt);
uint256 const domain1{1};
uint256 const domain2{2};
Book const b1_d1(a1, a2, domain1);
Book const b2_d1(a2, a1, domain1);
Book const b1_d2(a1, a2, domain2);
Book const b2_d2(a2, a1, domain2);
{
Set c;
c.insert(b1);
if (!BEAST_EXPECT(c.size() == 1))
return;
c.insert(b2);
if (!BEAST_EXPECT(c.size() == 2))
return;
if (!BEAST_EXPECT(c.erase(Book(a1, a1, std::nullopt)) == 0))
return;
if (!BEAST_EXPECT(c.erase(Book(a1, a2, std::nullopt)) == 1))
return;
if (!BEAST_EXPECT(c.erase(Book(a2, a1, std::nullopt)) == 1))
return;
if (!BEAST_EXPECT(c.empty()))
return;
}
{
Set c;
c.insert(b1);
if (!BEAST_EXPECT(c.size() == 1))
return;
c.insert(b2);
if (!BEAST_EXPECT(c.size() == 2))
return;
if (!BEAST_EXPECT(c.erase(Book(a1, a1, std::nullopt)) == 0))
return;
if (!BEAST_EXPECT(c.erase(Book(a1, a2, std::nullopt)) == 1))
return;
if (!BEAST_EXPECT(c.erase(Book(a2, a1, std::nullopt)) == 1))
return;
if (!BEAST_EXPECT(c.empty()))
return;
#if STL_SET_HAS_EMPLACE
c.emplace(a1, a2);
if (!BEAST_EXPECT(c.size() == 1))
return;
c.emplace(a2, a1);
if (!BEAST_EXPECT(c.size() == 2))
return;
#endif
}
{
Set c;
c.insert(b1_d1);
if (!BEAST_EXPECT(c.size() == 1))
return;
c.insert(b2_d1);
if (!BEAST_EXPECT(c.size() == 2))
return;
c.insert(b1_d2);
if (!BEAST_EXPECT(c.size() == 3))
return;
c.insert(b2_d2);
if (!BEAST_EXPECT(c.size() == 4))
return;
// Try removing non-existent elements
if (!BEAST_EXPECT(c.erase(Book(a2, a2, domain1)) == 0))
return;
if (!BEAST_EXPECT(c.erase(Book(a1, a2, domain1)) == 1))
return;
if (!BEAST_EXPECT(c.erase(Book(a2, a1, domain1)) == 1))
return;
if (!BEAST_EXPECT(c.size() == 2))
return;
if (!BEAST_EXPECT(c.erase(Book(a1, a2, domain2)) == 1))
return;
if (!BEAST_EXPECT(c.erase(Book(a2, a1, domain2)) == 1))
return;
if (!BEAST_EXPECT(c.empty()))
return;
}
{
Set c;
c.insert(b1);
c.insert(b2);
c.insert(b1_d1);
c.insert(b2_d1);
if (!BEAST_EXPECT(c.size() == 4))
return;
if (!BEAST_EXPECT(c.erase(Book(a1, a2, std::nullopt)) == 1))
return;
if (!BEAST_EXPECT(c.erase(Book(a2, a1, std::nullopt)) == 1))
return;
if (!BEAST_EXPECT(c.size() == 2))
return;
if (!BEAST_EXPECT(c.erase(Book(a1, a2, domain1)) == 1))
return;
if (!BEAST_EXPECT(c.erase(Book(a2, a1, domain1)) == 1))
return;
if (!BEAST_EXPECT(c.empty()))
return;
}
}
template <class Map>
void
testBookMap()
{
Currency const c1(1);
AccountID const i1(1);
Currency const c2(2);
AccountID const i2(2);
Issue const a1(c1, i1);
Issue const a2(c2, i2);
Book const b1(a1, a2, std::nullopt);
Book const b2(a2, a1, std::nullopt);
uint256 const domain1{1};
uint256 const domain2{2};
Book const b1_d1(a1, a2, domain1);
Book const b2_d1(a2, a1, domain1);
Book const b1_d2(a1, a2, domain2);
Book const b2_d2(a2, a1, domain2);
// typename Map::value_type value_type;
// std::pair <Book const, int> value_type;
{
Map c;
// c.insert (value_type (b1, 1));
c.insert(std::make_pair(b1, 1));
if (!BEAST_EXPECT(c.size() == 1))
return;
// c.insert (value_type (b2, 2));
c.insert(std::make_pair(b2, 1));
if (!BEAST_EXPECT(c.size() == 2))
return;
if (!BEAST_EXPECT(c.erase(Book(a1, a1, std::nullopt)) == 0))
return;
if (!BEAST_EXPECT(c.erase(Book(a1, a2, std::nullopt)) == 1))
return;
if (!BEAST_EXPECT(c.erase(Book(a2, a1, std::nullopt)) == 1))
return;
if (!BEAST_EXPECT(c.empty()))
return;
}
{
Map c;
// c.insert (value_type (b1, 1));
c.insert(std::make_pair(b1, 1));
if (!BEAST_EXPECT(c.size() == 1))
return;
// c.insert (value_type (b2, 2));
c.insert(std::make_pair(b2, 1));
if (!BEAST_EXPECT(c.size() == 2))
return;
if (!BEAST_EXPECT(c.erase(Book(a1, a1, std::nullopt)) == 0))
return;
if (!BEAST_EXPECT(c.erase(Book(a1, a2, std::nullopt)) == 1))
return;
if (!BEAST_EXPECT(c.erase(Book(a2, a1, std::nullopt)) == 1))
return;
if (!BEAST_EXPECT(c.empty()))
return;
}
{
Map c;
c.insert(std::make_pair(b1_d1, 10));
if (!BEAST_EXPECT(c.size() == 1))
return;
c.insert(std::make_pair(b2_d1, 20));
if (!BEAST_EXPECT(c.size() == 2))
return;
c.insert(std::make_pair(b1_d2, 30));
if (!BEAST_EXPECT(c.size() == 3))
return;
c.insert(std::make_pair(b2_d2, 40));
if (!BEAST_EXPECT(c.size() == 4))
return;
// Try removing non-existent elements
if (!BEAST_EXPECT(c.erase(Book(a2, a2, domain1)) == 0))
return;
if (!BEAST_EXPECT(c.erase(Book(a1, a2, domain1)) == 1))
return;
if (!BEAST_EXPECT(c.erase(Book(a2, a1, domain1)) == 1))
return;
if (!BEAST_EXPECT(c.size() == 2))
return;
if (!BEAST_EXPECT(c.erase(Book(a1, a2, domain2)) == 1))
return;
if (!BEAST_EXPECT(c.erase(Book(a2, a1, domain2)) == 1))
return;
if (!BEAST_EXPECT(c.empty()))
return;
}
{
Map c;
c.insert(std::make_pair(b1, 1));
c.insert(std::make_pair(b2, 2));
c.insert(std::make_pair(b1_d1, 3));
c.insert(std::make_pair(b2_d1, 4));
if (!BEAST_EXPECT(c.size() == 4))
return;
// Try removing non-existent elements
if (!BEAST_EXPECT(c.erase(Book(a1, a1, domain1)) == 0))
return;
if (!BEAST_EXPECT(c.erase(Book(a2, a2, domain2)) == 0))
return;
if (!BEAST_EXPECT(c.erase(Book(a1, a2, std::nullopt)) == 1))
return;
if (!BEAST_EXPECT(c.erase(Book(a2, a1, std::nullopt)) == 1))
return;
if (!BEAST_EXPECT(c.size() == 2))
return;
if (!BEAST_EXPECT(c.erase(Book(a1, a2, domain1)) == 1))
return;
if (!BEAST_EXPECT(c.erase(Book(a2, a1, domain1)) == 1))
return;
if (!BEAST_EXPECT(c.empty()))
return;
}
}
void
testBookSets()
{
testcase("std::set <Book>");
testBookSet<std::set<Book>>();
testcase("std::set <Book>");
testBookSet<std::set<Book>>();
#if RIPPLE_ASSETS_ENABLE_STD_HASH
testcase("std::unordered_set <Book>");
testBookSet<std::unordered_set<Book>>();
testcase("std::unordered_set <Book>");
testBookSet<std::unordered_set<Book>>();
#endif
testcase("hash_set <Book>");
testBookSet<hash_set<Book>>();
testcase("hash_set <Book>");
testBookSet<hash_set<Book>>();
}
void
testBookMaps()
{
testcase("std::map <Book, int>");
testBookMap<std::map<Book, int>>();
testcase("std::map <Book, int>");
testBookMap<std::map<Book, int>>();
#if RIPPLE_ASSETS_ENABLE_STD_HASH
testcase("std::unordered_map <Book, int>");
testBookMap<std::unordered_map<Book, int>>();
testcase("std::unordered_map <Book, int>");
testBookMap<std::unordered_map<Book, int>>();
testcase("hash_map <Book, int>");
testBookMap<hash_map<Book, int>>();
testcase("hash_map <Book, int>");
testBookMap<hash_map<Book, int>>();
#endif
}
//--------------------------------------------------------------------------
void
run() override
{
testcase("Currency");
testUnsigned<Currency>();
testcase("AccountID");
testUnsigned<AccountID>();
// ---
testcase("Issue");
testIssue<Issue>();
testcase("Issue");
testIssue<Issue>();
testIssueSets();
testIssueMaps();
// ---
testcase("Book");
testBook<Book>();
testcase("Book");
testBook<Book>();
testBookSets();
testBookMaps();
// ---
testIssueDomainSets();
testIssueDomainMaps();
}
};
BEAST_DEFINE_TESTSUITE(Issue, protocol, ripple);
} // namespace ripple

View File

@@ -1,140 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2022 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 <test/jtx.h>
#include <xrpl/basics/strHex.h>
namespace ripple {
class Memo_test : public beast::unit_test::suite
{
public:
void
testMemos()
{
testcase("Test memos");
using namespace test::jtx;
Account alice{"alice"};
Env env(*this);
env.fund(XRP(10000), alice);
env.close();
// Lambda that returns a valid JTx with a memo that we can hack up.
// This is the basis for building tests of invalid states.
auto makeJtxWithMemo = [&env, &alice]() {
JTx example = noop(alice);
memo const exampleMemo{"tic", "tac", "toe"};
exampleMemo(env, example);
return example;
};
// A valid memo.
env(makeJtxWithMemo());
env.close();
{
// Make sure that too big a memo is flagged as invalid.
JTx memoSize = makeJtxWithMemo();
memoSize.jv[sfMemos.jsonName][0u][sfMemo.jsonName]
[sfMemoData.jsonName] = std::string(2020, '0');
env(memoSize,
rpc("invalidTransaction",
"fails local checks: The memo exceeds the maximum allowed "
"size."));
// This memo is just barely small enough.
memoSize.jv[sfMemos.jsonName][0u][sfMemo.jsonName]
[sfMemoData.jsonName] = std::string(2018, '1');
env(memoSize);
}
{
// Put a non-Memo in the Memos array.
JTx memoNonMemo = noop(alice);
auto& jv = memoNonMemo.jv;
auto& ma = jv[sfMemos.jsonName];
auto& mi = ma[ma.size()];
auto& m = mi[sfCreatedNode.jsonName]; // CreatedNode in Memos
m[sfMemoData.jsonName] = "3030303030";
env(memoNonMemo,
rpc("invalidTransaction",
"fails local checks: A memo array may contain only Memo "
"objects."));
}
{
// Put an invalid field in a Memo object.
JTx memoExtra = makeJtxWithMemo();
memoExtra
.jv[sfMemos.jsonName][0u][sfMemo.jsonName][sfFlags.jsonName] =
13;
env(memoExtra,
rpc("invalidTransaction",
"fails local checks: A memo may contain only MemoType, "
"MemoData or MemoFormat fields."));
}
{
// Put a character that is not allowed in a URL in a MemoType field.
JTx memoBadChar = makeJtxWithMemo();
memoBadChar.jv[sfMemos.jsonName][0u][sfMemo.jsonName]
[sfMemoType.jsonName] =
strHex(std::string_view("ONE<INFINITY"));
env(memoBadChar,
rpc("invalidTransaction",
"fails local checks: The MemoType and MemoFormat fields "
"may only contain characters that are allowed in URLs "
"under RFC 3986."));
}
{
// Put a character that is not allowed in a URL in a MemoData field.
// That's okay.
JTx memoLegitChar = makeJtxWithMemo();
memoLegitChar.jv[sfMemos.jsonName][0u][sfMemo.jsonName]
[sfMemoData.jsonName] =
strHex(std::string_view("ONE<INFINITY"));
env(memoLegitChar);
}
{
// Put a character that is not allowed in a URL in a MemoFormat.
JTx memoBadChar = makeJtxWithMemo();
memoBadChar.jv[sfMemos.jsonName][0u][sfMemo.jsonName]
[sfMemoFormat.jsonName] =
strHex(std::string_view("NoBraces{}InURL"));
env(memoBadChar,
rpc("invalidTransaction",
"fails local checks: The MemoType and MemoFormat fields "
"may only contain characters that are allowed in URLs "
"under RFC 3986."));
}
}
//--------------------------------------------------------------------------
void
run() override
{
testMemos();
}
};
BEAST_DEFINE_TESTSUITE(Memo, ripple_data, ripple);
} // namespace ripple

File diff suppressed because it is too large Load Diff

View File

@@ -1,474 +0,0 @@
//------------------------------------------------------------------------------
/*
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 <xrpl/beast/unit_test.h>
#include <xrpl/protocol/PublicKey.h>
#include <xrpl/protocol/SecretKey.h>
#include <vector>
namespace ripple {
class PublicKey_test : public beast::unit_test::suite
{
public:
using blob = std::vector<std::uint8_t>;
template <class FwdIter, class Container>
static void
hex_to_binary(FwdIter first, FwdIter last, Container& out)
{
struct Table
{
int val[256];
Table()
{
std::fill(val, val + 256, 0);
for (int i = 0; i < 10; ++i)
val['0' + i] = i;
for (int i = 0; i < 6; ++i)
{
val['A' + i] = 10 + i;
val['a' + i] = 10 + i;
}
}
int
operator[](int i)
{
return val[i];
}
};
static Table lut;
out.reserve(std::distance(first, last) / 2);
while (first != last)
{
auto const hi(lut[(*first++)]);
auto const lo(lut[(*first++)]);
out.push_back((hi * 16) + lo);
}
}
blob
sig(std::string const& hex)
{
blob b;
hex_to_binary(hex.begin(), hex.end(), b);
return b;
}
bool
check(std::optional<ECDSACanonicality> answer, std::string const& s)
{
return ecdsaCanonicality(makeSlice(sig(s))) == answer;
}
void
testCanonical()
{
testcase("Canonical");
// Fully canonical
BEAST_EXPECT(check(
ECDSACanonicality::fullyCanonical,
"3045"
"022100FF478110D1D4294471EC76E0157540C2181F47DEBD25D7F9E7DDCCCD47EE"
"E905"
"0220078F07CDAE6C240855D084AD91D1479609533C147C93B0AEF19BC9724D003F"
"28"));
BEAST_EXPECT(check(
ECDSACanonicality::fullyCanonical,
"3045"
"0221009218248292F1762D8A51BE80F8A7F2CD288D810CE781D5955700DA1684DF"
"1D2D"
"022041A1EE1746BFD72C9760CC93A7AAA8047D52C8833A03A20EAAE92EA19717B4"
"54"));
BEAST_EXPECT(check(
ECDSACanonicality::fullyCanonical,
"3044"
"02206A9E43775F73B6D1EC420E4DDD222A80D4C6DF5D1BEECC431A91B63C928B75"
"81"
"022023E9CC2D61DDA6F73EAA6BCB12688BEB0F434769276B3127E4044ED895C9D9"
"6B"));
BEAST_EXPECT(check(
ECDSACanonicality::fullyCanonical,
"3044"
"022056E720007221F3CD4EFBB6352741D8E5A0968D48D8D032C2FBC4F6304AD1D0"
"4E"
"02201F39EB392C20D7801C3E8D81D487E742FA84A1665E923225BD6323847C7187"
"9F"));
BEAST_EXPECT(check(
ECDSACanonicality::fullyCanonical,
"3045"
"022100FDFD5AD05518CEA0017A2DCB5C4DF61E7C73B6D3A38E7AE93210A1564E8C"
"2F12"
"0220214FF061CCC123C81D0BB9D0EDEA04CD40D96BF1425D311DA62A7096BB18EA"
"18"));
// Canonical but not fully canonical
BEAST_EXPECT(check(
ECDSACanonicality::canonical,
"3046"
"022100F477B3FA6F31C7CB3A0D1AD94A231FDD24B8D78862EE334CEA7CD08F6CBC"
"0A1B"
"022100928E6BCF1ED2684679730C5414AEC48FD62282B090041C41453C1D064AF5"
"97A1"));
BEAST_EXPECT(check(
ECDSACanonicality::canonical,
"3045"
"022063E7C7CA93CB2400E413A342C027D00665F8BAB9C22EF0A7B8AE3AAF092230"
"B6"
"0221008F2E8BB7D09521ABBC277717B14B93170AE6465C5A1B36561099319C4BEB"
"254C"));
BEAST_EXPECT(check(
ECDSACanonicality::canonical,
"3046"
"02210099DCA1188663DDEA506A06A7B20C2B7D8C26AFF41DECE69D6C5F7C967D32"
"625F"
"022100897658A6B1F9EEE5D140D7A332DA0BD73BB98974EA53F6201B01C1B594F2"
"86EA"));
BEAST_EXPECT(check(
ECDSACanonicality::canonical,
"3045"
"02200855DE366E4E323AA2CE2A25674401A7D11F72EC432770D07F7B57DF7387AE"
"C0"
"022100DA4C6ADDEA14888858DE2AC5B91ED9050D6972BB388DEF582628CEE32869"
"AE35"));
// valid
BEAST_EXPECT(check(
ECDSACanonicality::fullyCanonical,
"3006"
"020101"
"020102"));
BEAST_EXPECT(check(
ECDSACanonicality::fullyCanonical,
"3044"
"02203932c892e2e550f3af8ee4ce9c215a87f9bb831dcac87b2838e2c2eaa891df"
"0c"
"022030b61dd36543125d56b9f9f3a1f53189e5af33cdda8d77a5209aec03978fa0"
"01"));
BEAST_EXPECT(check(
ECDSACanonicality::canonical,
"3045"
"0220076045be6f9eca28ff1ec606b833d0b87e70b2a630f5e3a496b110967a40f9"
"0a"
"0221008fffd599910eefe00bc803c688eca1d2ba7f6b180620eaa03488e6585db6"
"ba01"));
BEAST_EXPECT(check(
ECDSACanonicality::canonical,
"3046"
"022100876045be6f9eca28ff1ec606b833d0b87e70b2a630f5e3a496b110967a40"
"f90a"
"0221008fffd599910eefe00bc803c688c2eca1d2ba7f6b180620eaa03488e6585d"
"b6ba"));
BEAST_EXPECT(check(
std::nullopt,
"3005"
"0201FF"
"0200"));
BEAST_EXPECT(check(
std::nullopt,
"3006"
"020101"
"020202"));
BEAST_EXPECT(check(
std::nullopt,
"3006"
"020701"
"020102"));
BEAST_EXPECT(check(
std::nullopt,
"3006"
"020401"
"020102"));
BEAST_EXPECT(check(
std::nullopt,
"3006"
"020501"
"020102"));
BEAST_EXPECT(check(
std::nullopt,
"3006"
"020201"
"020102"));
BEAST_EXPECT(check(
std::nullopt,
"3006"
"020301"
"020202"));
BEAST_EXPECT(check(
std::nullopt,
"3006"
"020401"
"020202"));
BEAST_EXPECT(check(
std::nullopt,
"3047"
"0221005990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba"
"6105"
"022200002d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e56"
"6695ed"));
BEAST_EXPECT(check(
std::nullopt,
"3144"
"02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
"05"
"02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695"
"ed"));
BEAST_EXPECT(check(
std::nullopt,
"3045"
"02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
"05"
"02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695"
"ed"));
BEAST_EXPECT(check(
std::nullopt,
"301F"
"01205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1"));
BEAST_EXPECT(check(
std::nullopt,
"3045"
"02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
"05"
"02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695"
"ed00"));
BEAST_EXPECT(check(
std::nullopt,
"3044"
"01205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
"05"
"02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695"
"ed"));
BEAST_EXPECT(check(
std::nullopt,
"3024"
"0200"
"02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695"
"ed"));
BEAST_EXPECT(check(
std::nullopt,
"3044"
"02208990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
"05"
"02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695"
"ed"));
BEAST_EXPECT(check(
std::nullopt,
"3045"
"0221005990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba"
"6105"
"02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695"
"ed"));
BEAST_EXPECT(check(
std::nullopt,
"3044"
"02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
"05012"
"02d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695e"
"d"));
BEAST_EXPECT(check(
std::nullopt,
"3024"
"02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
"05"
"0200"));
BEAST_EXPECT(check(
std::nullopt,
"3044"
"02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
"05"
"0220fd5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695"
"ed"));
BEAST_EXPECT(check(
std::nullopt,
"3045"
"02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
"05"
"0221002d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e5666"
"95ed"));
}
void
testBase58(KeyType keyType)
{
// Try converting short, long and malformed data
BEAST_EXPECT(!parseBase58<PublicKey>(TokenType::NodePublic, ""));
BEAST_EXPECT(!parseBase58<PublicKey>(TokenType::NodePublic, " "));
BEAST_EXPECT(
!parseBase58<PublicKey>(TokenType::NodePublic, "!ty89234gh45"));
auto const good = toBase58(
TokenType::NodePublic, derivePublicKey(keyType, randomSecretKey()));
// Short (non-empty) strings
{
auto s = good;
// Remove all characters from the string in random order:
std::hash<std::string> r;
while (!s.empty())
{
s.erase(r(s) % s.size(), 1);
BEAST_EXPECT(!parseBase58<PublicKey>(TokenType::NodePublic, s));
}
}
// Long strings
for (std::size_t i = 1; i != 16; i++)
{
auto s = good;
s.resize(s.size() + i, s[i % s.size()]);
BEAST_EXPECT(!parseBase58<PublicKey>(TokenType::NodePublic, s));
}
// Strings with invalid Base58 characters
for (auto c : std::string("0IOl"))
{
for (std::size_t i = 0; i != good.size(); ++i)
{
auto s = good;
s[i % s.size()] = c;
BEAST_EXPECT(!parseBase58<PublicKey>(TokenType::NodePublic, s));
}
}
// Strings with incorrect prefix
{
auto s = good;
for (auto c : std::string("apsrJqtv7"))
{
s[0] = c;
BEAST_EXPECT(!parseBase58<PublicKey>(TokenType::NodePublic, s));
}
}
// Try some random secret keys
std::vector<PublicKey> keys;
keys.reserve(32);
for (std::size_t i = 0; i != keys.capacity(); ++i)
keys.emplace_back(derivePublicKey(keyType, randomSecretKey()));
BEAST_EXPECT(keys.size() == 32);
for (std::size_t i = 0; i != keys.size(); ++i)
{
auto const si = toBase58(TokenType::NodePublic, keys[i]);
BEAST_EXPECT(!si.empty());
auto const ski = parseBase58<PublicKey>(TokenType::NodePublic, si);
BEAST_EXPECT(ski && (keys[i] == *ski));
for (std::size_t j = i; j != keys.size(); ++j)
{
BEAST_EXPECT((keys[i] == keys[j]) == (i == j));
auto const sj = toBase58(TokenType::NodePublic, keys[j]);
BEAST_EXPECT((si == sj) == (i == j));
auto const skj =
parseBase58<PublicKey>(TokenType::NodePublic, sj);
BEAST_EXPECT(skj && (keys[j] == *skj));
BEAST_EXPECT((*ski == *skj) == (i == j));
}
}
}
void
testBase58()
{
testcase("Base58: secp256k1");
{
auto const pk1 = derivePublicKey(
KeyType::secp256k1,
generateSecretKey(
KeyType::secp256k1, generateSeed("masterpassphrase")));
auto const pk2 = parseBase58<PublicKey>(
TokenType::NodePublic,
"n94a1u4jAz288pZLtw6yFWVbi89YamiC6JBXPVUj5zmExe5fTVg9");
BEAST_EXPECT(pk2);
BEAST_EXPECT(pk1 == *pk2);
}
testBase58(KeyType::secp256k1);
testcase("Base58: ed25519");
{
auto const pk1 = derivePublicKey(
KeyType::ed25519,
generateSecretKey(
KeyType::ed25519, generateSeed("masterpassphrase")));
auto const pk2 = parseBase58<PublicKey>(
TokenType::NodePublic,
"nHUeeJCSY2dM71oxM8Cgjouf5ekTuev2mwDpc374aLMxzDLXNmjf");
BEAST_EXPECT(pk2);
BEAST_EXPECT(pk1 == *pk2);
}
testBase58(KeyType::ed25519);
}
void
testMiscOperations()
{
testcase("Miscellaneous operations");
auto const pk1 = derivePublicKey(
KeyType::secp256k1,
generateSecretKey(
KeyType::secp256k1, generateSeed("masterpassphrase")));
PublicKey pk2(pk1);
BEAST_EXPECT(pk1 == pk2);
BEAST_EXPECT(pk2 == pk1);
PublicKey pk3 = derivePublicKey(
KeyType::secp256k1,
generateSecretKey(
KeyType::secp256k1, generateSeed("arbitraryPassPhrase")));
// Testing the copy assignment operation of PublicKey class
pk3 = pk2;
BEAST_EXPECT(pk3 == pk2);
BEAST_EXPECT(pk1 == pk3);
}
void
run() override
{
testBase58();
testCanonical();
testMiscOperations();
}
};
BEAST_DEFINE_TESTSUITE(PublicKey, protocol, ripple);
} // namespace ripple

View File

@@ -12,3 +12,5 @@ xrpl_add_test(basics)
target_link_libraries(xrpl.test.basics PRIVATE xrpl.imports.test)
xrpl_add_test(crypto)
target_link_libraries(xrpl.test.crypto PRIVATE xrpl.imports.test)
xrpl_add_test(protocol)
target_link_libraries(xrpl.test.protocol PRIVATE xrpl.imports.test)

View File

@@ -0,0 +1,56 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/XRPLF/rippled/
Copyright (c) 2023 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 <xrpl/protocol/ApiVersion.h>
#include <doctest/doctest.h>
using namespace ripple;
TEST_SUITE_BEGIN("ApiVersion");
TEST_CASE("API versions invariants")
{
static_assert(
RPC::apiMinimumSupportedVersion <= RPC::apiMaximumSupportedVersion);
static_assert(
RPC::apiMinimumSupportedVersion <= RPC::apiMaximumValidVersion);
static_assert(
RPC::apiMaximumSupportedVersion <= RPC::apiMaximumValidVersion);
static_assert(RPC::apiBetaVersion <= RPC::apiMaximumValidVersion);
CHECK(true);
}
TEST_CASE("API versions")
{
// Update when we change versions
static_assert(RPC::apiMinimumSupportedVersion >= 1);
static_assert(RPC::apiMinimumSupportedVersion < 2);
static_assert(RPC::apiMaximumSupportedVersion >= 2);
static_assert(RPC::apiMaximumSupportedVersion < 3);
static_assert(RPC::apiMaximumValidVersion >= 3);
static_assert(RPC::apiMaximumValidVersion < 4);
static_assert(RPC::apiBetaVersion >= 3);
static_assert(RPC::apiBetaVersion < 4);
CHECK(true);
}
TEST_SUITE_END();

View File

@@ -0,0 +1,98 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2020 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 <xrpl/protocol/BuildInfo.h>
#include <doctest/doctest.h>
using namespace ripple;
TEST_SUITE_BEGIN("BuildInfo");
TEST_CASE("EncodeSoftwareVersion")
{
auto encodedVersion = BuildInfo::encodeSoftwareVersion("1.2.3-b7");
// the first two bytes identify the particular implementation, 0x183B
CHECK(
(encodedVersion & 0xFFFF'0000'0000'0000LLU) ==
0x183B'0000'0000'0000LLU);
// the next three bytes: major version, minor version, patch version,
// 0x010203
CHECK(
(encodedVersion & 0x0000'FFFF'FF00'0000LLU) ==
0x0000'0102'0300'0000LLU);
// the next two bits:
{
// 01 if a beta
CHECK((encodedVersion & 0x0000'0000'00C0'0000LLU) >> 22 == 0b01);
// 10 if an RC
encodedVersion = BuildInfo::encodeSoftwareVersion("1.2.4-rc7");
CHECK((encodedVersion & 0x0000'0000'00C0'0000LLU) >> 22 == 0b10);
// 11 if neither an RC nor a beta
encodedVersion = BuildInfo::encodeSoftwareVersion("1.2.5");
CHECK((encodedVersion & 0x0000'0000'00C0'0000LLU) >> 22 == 0b11);
}
// the next six bits: rc/beta number (1-63)
encodedVersion = BuildInfo::encodeSoftwareVersion("1.2.6-b63");
CHECK((encodedVersion & 0x0000'0000'003F'0000LLU) >> 16 == 63);
// the last two bytes are zeros
CHECK((encodedVersion & 0x0000'0000'0000'FFFFLLU) == 0);
// Test some version strings with wrong formats:
// no rc/beta number
encodedVersion = BuildInfo::encodeSoftwareVersion("1.2.3-b");
CHECK((encodedVersion & 0x0000'0000'00FF'0000LLU) == 0);
// rc/beta number out of range
encodedVersion = BuildInfo::encodeSoftwareVersion("1.2.3-b64");
CHECK((encodedVersion & 0x0000'0000'00FF'0000LLU) == 0);
// Check that the rc/beta number of a release is 0:
encodedVersion = BuildInfo::encodeSoftwareVersion("1.2.6");
CHECK((encodedVersion & 0x0000'0000'003F'0000LLU) == 0);
}
TEST_CASE("IsRippledVersion")
{
auto vFF = 0xFFFF'FFFF'FFFF'FFFFLLU;
CHECK(!BuildInfo::isRippledVersion(vFF));
auto vRippled = 0x183B'0000'0000'0000LLU;
CHECK(BuildInfo::isRippledVersion(vRippled));
}
TEST_CASE("IsNewerVersion")
{
auto vFF = 0xFFFF'FFFF'FFFF'FFFFLLU;
CHECK(!BuildInfo::isNewerVersion(vFF));
auto v159 = BuildInfo::encodeSoftwareVersion("1.5.9");
CHECK(!BuildInfo::isNewerVersion(v159));
auto vCurrent = BuildInfo::getEncodedVersion();
CHECK(!BuildInfo::isNewerVersion(vCurrent));
auto vMax = BuildInfo::encodeSoftwareVersion("255.255.255");
CHECK(BuildInfo::isNewerVersion(vMax));
}
TEST_SUITE_END();

View File

@@ -0,0 +1,179 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012-2017 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 <test/jtx.h>
#include <xrpl/protocol/Feature.h>
#include <doctest/doctest.h>
#include <functional>
#include <vector>
using namespace ripple;
TEST_SUITE_BEGIN("Hooks");
TEST_CASE("Test Hooks fields")
{
using namespace test::jtx;
std::vector<std::reference_wrapper<SField const>> fields_to_test = {
sfHookResult,
sfHookStateChangeCount,
sfHookEmitCount,
sfHookExecutionIndex,
sfHookApiVersion,
sfHookStateCount,
sfEmitGeneration,
sfHookOn,
sfHookInstructionCount,
sfEmitBurden,
sfHookReturnCode,
sfReferenceCount,
sfEmitParentTxnID,
sfEmitNonce,
sfEmitHookHash,
sfHookStateKey,
sfHookHash,
sfHookNamespace,
sfHookSetTxnID,
sfHookStateData,
sfHookReturnString,
sfHookParameterName,
sfHookParameterValue,
sfEmitCallback,
sfHookAccount,
sfEmittedTxn,
sfHook,
sfHookDefinition,
sfHookParameter,
sfHookGrant,
sfEmitDetails,
sfHookExecutions,
sfHookExecution,
sfHookParameters,
sfHooks,
sfHookGrants};
for (auto const& rf : fields_to_test)
{
SField const& f = rf.get();
STObject dummy{sfGeneric};
CHECK(!dummy.isFieldPresent(f));
switch (f.fieldType)
{
case STI_UINT8: {
dummy.setFieldU8(f, 0);
CHECK(dummy.getFieldU8(f) == 0);
dummy.setFieldU8(f, 255);
CHECK(dummy.getFieldU8(f) == 255);
CHECK(dummy.isFieldPresent(f));
break;
}
case STI_UINT16: {
dummy.setFieldU16(f, 0);
CHECK(dummy.getFieldU16(f) == 0);
dummy.setFieldU16(f, 0xFFFFU);
CHECK(dummy.getFieldU16(f) == 0xFFFFU);
CHECK(dummy.isFieldPresent(f));
break;
}
case STI_UINT32: {
dummy.setFieldU32(f, 0);
CHECK(dummy.getFieldU32(f) == 0);
dummy.setFieldU32(f, 0xFFFFFFFFU);
CHECK(dummy.getFieldU32(f) == 0xFFFFFFFFU);
CHECK(dummy.isFieldPresent(f));
break;
}
case STI_UINT64: {
dummy.setFieldU64(f, 0);
CHECK(dummy.getFieldU64(f) == 0);
dummy.setFieldU64(f, 0xFFFFFFFFFFFFFFFFU);
CHECK(dummy.getFieldU64(f) == 0xFFFFFFFFFFFFFFFFU);
CHECK(dummy.isFieldPresent(f));
break;
}
case STI_UINT256: {
uint256 u = uint256::fromVoid(
"DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDE"
"ADBEEF");
dummy.setFieldH256(f, u);
CHECK(dummy.getFieldH256(f) == u);
CHECK(dummy.isFieldPresent(f));
break;
}
case STI_VL: {
std::vector<uint8_t> v{1, 2, 3};
dummy.setFieldVL(f, v);
CHECK(dummy.getFieldVL(f) == v);
CHECK(dummy.isFieldPresent(f));
break;
}
case STI_ACCOUNT: {
AccountID id = *parseBase58<AccountID>(
"rwfSjJNK2YQuN64bSWn7T2eY9FJAyAPYJT");
dummy.setAccountID(f, id);
CHECK(dummy.getAccountID(f) == id);
CHECK(dummy.isFieldPresent(f));
break;
}
case STI_OBJECT: {
dummy.emplace_back(STObject{f});
CHECK(dummy.getField(f).getFName() == f);
CHECK(dummy.isFieldPresent(f));
break;
}
case STI_ARRAY: {
STArray dummy2{f, 2};
dummy2.push_back(STObject{sfGeneric});
dummy2.push_back(STObject{sfGeneric});
dummy.setFieldArray(f, dummy2);
CHECK(dummy.getFieldArray(f) == dummy2);
CHECK(dummy.isFieldPresent(f));
break;
}
default:
CHECK(false);
}
}
}
TEST_SUITE_END();

View File

@@ -17,15 +17,14 @@
*/
//==============================================================================
#include <test/jtx.h>
#include <xrpl/basics/contract.h>
#include <xrpl/beast/unit_test.h>
#include <xrpl/json/json_reader.h> // Json::Reader
#include <xrpl/protocol/ErrorCodes.h> // RPC::containsError
#include <xrpl/protocol/STParsedJSON.h> // STParsedJSONObject
#include <xrpl/json/json_reader.h>
#include <xrpl/protocol/ErrorCodes.h>
#include <xrpl/protocol/STParsedJSON.h>
namespace ripple {
#include <doctest/doctest.h>
using namespace ripple;
namespace InnerObjectFormatsUnitTestDetail {
@@ -36,7 +35,6 @@ struct TestJSONTxt
};
static TestJSONTxt const testArray[] = {
// Valid SignerEntry
{R"({
"Account" : "rDg53Haik2475DJx8bjMDSDPj4VX7htaMd",
@@ -61,7 +59,6 @@ static TestJSONTxt const testArray[] = {
"TransactionType" : "SignerListSet"
})",
false},
// SignerEntry missing Account
{R"({
"Account" : "rDg53Haik2475DJx8bjMDSDPj4VX7htaMd",
@@ -85,7 +82,6 @@ static TestJSONTxt const testArray[] = {
"TransactionType" : "SignerListSet"
})",
true},
// SignerEntry missing SignerWeight
{R"({
"Account" : "rDg53Haik2475DJx8bjMDSDPj4VX7htaMd",
@@ -109,7 +105,6 @@ static TestJSONTxt const testArray[] = {
"TransactionType" : "SignerListSet"
})",
true},
// SignerEntry with unexpected Amount
{R"({
"Account" : "rDg53Haik2475DJx8bjMDSDPj4VX7htaMd",
@@ -135,7 +130,6 @@ static TestJSONTxt const testArray[] = {
"TransactionType" : "SignerListSet"
})",
true},
// SignerEntry with no Account and unexpected Amount
{R"({
"Account" : "rDg53Haik2475DJx8bjMDSDPj4VX7htaMd",
@@ -160,47 +154,36 @@ static TestJSONTxt const testArray[] = {
"TransactionType" : "SignerListSet"
})",
true},
};
} // namespace InnerObjectFormatsUnitTestDetail
class InnerObjectFormatsParsedJSON_test : public beast::unit_test::suite
TEST_SUITE_BEGIN("InnerObjectFormatsParsedJSON");
TEST_CASE("InnerObjectFormatsParsedJSON")
{
public:
void
run() override
using namespace InnerObjectFormatsUnitTestDetail;
for (auto const& test : testArray)
{
using namespace InnerObjectFormatsUnitTestDetail;
// Instantiate a jtx::Env so debugLog writes are exercised.
test::jtx::Env env(*this);
for (auto const& test : testArray)
Json::Value req;
Json::Reader().parse(test.txt, req);
if (RPC::contains_error(req))
{
Json::Value req;
Json::Reader().parse(test.txt, req);
if (RPC::contains_error(req))
{
Throw<std::runtime_error>(
"Internal InnerObjectFormatsParsedJSON error. Bad JSON.");
}
STParsedJSONObject parsed("request", req);
bool const noObj = !parsed.object.has_value();
if (noObj == test.expectFail)
{
pass();
}
else
{
std::string errStr("Unexpected STParsedJSON result on:\n");
errStr += test.txt;
fail(errStr);
}
Throw<std::runtime_error>(
"Internal InnerObjectFormatsParsedJSON error. Bad JSON.");
}
STParsedJSONObject parsed("request", req);
bool const noObj = !parsed.object.has_value();
if (noObj == test.expectFail)
CHECK(true);
else
{
std::string errStr("Unexpected STParsedJSON result on:\n");
errStr += test.txt;
FAIL(errStr);
}
}
};
}
BEAST_DEFINE_TESTSUITE(InnerObjectFormatsParsedJSON, ripple_app, ripple);
} // namespace ripple
TEST_SUITE_END();

View File

@@ -0,0 +1,860 @@
//------------------------------------------------------------------------------
/*
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 <xrpl/basics/UnorderedContainers.h>
#include <doctest/doctest.h>
#include <xrpl/protocol/Book.h>
#include <xrpl/protocol/Issue.h>
#include <sys/types.h>
#include <map>
#include <optional>
#include <set>
#include <typeinfo>
#include <unordered_set>
#if BEAST_MSVC
#define STL_SET_HAS_EMPLACE 1
#else
#define STL_SET_HAS_EMPLACE 0
#endif
#ifndef RIPPLE_ASSETS_ENABLE_STD_HASH
#if BEAST_MAC || BEAST_IOS
#define RIPPLE_ASSETS_ENABLE_STD_HASH 0
#else
#define RIPPLE_ASSETS_ENABLE_STD_HASH 1
#endif
#endif
namespace ripple {
namespace {
using Domain = uint256;
// Comparison, hash tests for uint60 (via base_uint)
template <typename Unsigned>
void
testUnsigned()
{
Unsigned const u1(1);
Unsigned const u2(2);
Unsigned const u3(3);
CHECK(u1 != u2);
CHECK(u1 < u2);
CHECK(u1 <= u2);
CHECK(u2 <= u2);
CHECK(u2 == u2);
CHECK(u2 >= u2);
CHECK(u3 >= u2);
CHECK(u3 > u2);
std::hash<Unsigned> hash;
CHECK(hash(u1) == hash(u1));
CHECK(hash(u2) == hash(u2));
CHECK(hash(u3) == hash(u3));
CHECK(hash(u1) != hash(u2));
CHECK(hash(u1) != hash(u3));
CHECK(hash(u2) != hash(u3));
}
//--------------------------------------------------------------------------
// Comparison, hash tests for Issue
template <class Issue>
void
testIssue()
{
Currency const c1(1);
AccountID const i1(1);
Currency const c2(2);
AccountID const i2(2);
Currency const c3(3);
AccountID const i3(3);
CHECK(Issue(c1, i1) != Issue(c2, i1));
CHECK(Issue(c1, i1) < Issue(c2, i1));
CHECK(Issue(c1, i1) <= Issue(c2, i1));
CHECK(Issue(c2, i1) <= Issue(c2, i1));
CHECK(Issue(c2, i1) == Issue(c2, i1));
CHECK(Issue(c2, i1) >= Issue(c2, i1));
CHECK(Issue(c3, i1) >= Issue(c2, i1));
CHECK(Issue(c3, i1) > Issue(c2, i1));
CHECK(Issue(c1, i1) != Issue(c1, i2));
CHECK(Issue(c1, i1) < Issue(c1, i2));
CHECK(Issue(c1, i1) <= Issue(c1, i2));
CHECK(Issue(c1, i2) <= Issue(c1, i2));
CHECK(Issue(c1, i2) == Issue(c1, i2));
CHECK(Issue(c1, i2) >= Issue(c1, i2));
CHECK(Issue(c1, i3) >= Issue(c1, i2));
CHECK(Issue(c1, i3) > Issue(c1, i2));
std::hash<Issue> hash;
CHECK(hash(Issue(c1, i1)) == hash(Issue(c1, i1)));
CHECK(hash(Issue(c1, i2)) == hash(Issue(c1, i2)));
CHECK(hash(Issue(c1, i3)) == hash(Issue(c1, i3)));
CHECK(hash(Issue(c2, i1)) == hash(Issue(c2, i1)));
CHECK(hash(Issue(c2, i2)) == hash(Issue(c2, i2)));
CHECK(hash(Issue(c2, i3)) == hash(Issue(c2, i3)));
CHECK(hash(Issue(c3, i1)) == hash(Issue(c3, i1)));
CHECK(hash(Issue(c3, i2)) == hash(Issue(c3, i2)));
CHECK(hash(Issue(c3, i3)) == hash(Issue(c3, i3)));
CHECK(hash(Issue(c1, i1)) != hash(Issue(c1, i2)));
CHECK(hash(Issue(c1, i1)) != hash(Issue(c1, i3)));
CHECK(hash(Issue(c1, i1)) != hash(Issue(c2, i1)));
CHECK(hash(Issue(c1, i1)) != hash(Issue(c2, i2)));
CHECK(hash(Issue(c1, i1)) != hash(Issue(c2, i3)));
CHECK(hash(Issue(c1, i1)) != hash(Issue(c3, i1)));
CHECK(hash(Issue(c1, i1)) != hash(Issue(c3, i2)));
CHECK(hash(Issue(c1, i1)) != hash(Issue(c3, i3)));
}
template <class Set>
void
testIssueSet()
{
Currency const c1(1);
AccountID const i1(1);
Currency const c2(2);
AccountID const i2(2);
Issue const a1(c1, i1);
Issue const a2(c2, i2);
{
Set c;
c.insert(a1);
CHECK(c.size() == 1);
c.insert(a2);
CHECK(c.size() == 2);
CHECK(c.erase(Issue(c1, i2)) == 0);
CHECK(c.erase(Issue(c1, i1)) == 1);
CHECK(c.erase(Issue(c2, i2)) == 1);
CHECK(c.empty());
}
{
Set c;
c.insert(a1);
CHECK(c.size() == 1);
c.insert(a2);
CHECK(c.size() == 2);
CHECK(c.erase(Issue(c1, i2)) == 0);
CHECK(c.erase(Issue(c1, i1)) == 1);
CHECK(c.erase(Issue(c2, i2)) == 1);
CHECK(c.empty());
#if STL_SET_HAS_EMPLACE
c.emplace(c1, i1);
CHECK(c.size() == 1);
c.emplace(c2, i2);
CHECK(c.size() == 2);
#endif
}
}
template <class Map>
void
testIssueMap()
{
Currency const c1(1);
AccountID const i1(1);
Currency const c2(2);
AccountID const i2(2);
Issue const a1(c1, i1);
Issue const a2(c2, i2);
{
Map c;
c.insert(std::make_pair(a1, 1));
CHECK(c.size() == 1);
c.insert(std::make_pair(a2, 2));
CHECK(c.size() == 2);
CHECK(c.erase(Issue(c1, i2)) == 0);
CHECK(c.erase(Issue(c1, i1)) == 1);
CHECK(c.erase(Issue(c2, i2)) == 1);
CHECK(c.empty());
}
{
Map c;
c.insert(std::make_pair(a1, 1));
CHECK(c.size() == 1);
c.insert(std::make_pair(a2, 2));
CHECK(c.size() == 2);
CHECK(c.erase(Issue(c1, i2)) == 0);
CHECK(c.erase(Issue(c1, i1)) == 1);
CHECK(c.erase(Issue(c2, i2)) == 1);
CHECK(c.empty());
}
}
template <class Set>
void
testIssueDomainSet()
{
Currency const c1(1);
AccountID const i1(1);
Currency const c2(2);
AccountID const i2(2);
Issue const a1(c1, i1);
Issue const a2(c2, i2);
uint256 const domain1{1};
uint256 const domain2{2};
Set c;
c.insert(std::make_pair(a1, domain1));
CHECK(c.size() == 1);
c.insert(std::make_pair(a2, domain1));
CHECK(c.size() == 2);
c.insert(std::make_pair(a2, domain2));
CHECK(c.size() == 3);
CHECK(c.erase(std::make_pair(Issue(c1, i2), domain1)) == 0);
CHECK(c.erase(std::make_pair(a1, domain1)) == 1);
CHECK(c.erase(std::make_pair(a2, domain1)) == 1);
CHECK(c.erase(std::make_pair(a2, domain2)) == 1);
CHECK(c.empty());
}
template <class Map>
void
testIssueDomainMap()
{
Currency const c1(1);
AccountID const i1(1);
Currency const c2(2);
AccountID const i2(2);
Issue const a1(c1, i1);
Issue const a2(c2, i2);
uint256 const domain1{1};
uint256 const domain2{2};
Map c;
c.insert(std::make_pair(std::make_pair(a1, domain1), 1));
CHECK(c.size() == 1);
c.insert(std::make_pair(std::make_pair(a2, domain1), 2));
CHECK(c.size() == 2);
c.insert(std::make_pair(std::make_pair(a2, domain2), 2));
CHECK(c.size() == 3);
CHECK(c.erase(std::make_pair(Issue(c1, i2), domain1)) == 0);
CHECK(c.erase(std::make_pair(a1, domain1)) == 1);
CHECK(c.erase(std::make_pair(a2, domain1)) == 1);
CHECK(c.erase(std::make_pair(a2, domain2)) == 1);
CHECK(c.empty());
}
//--------------------------------------------------------------------------
// Comparison, hash tests for Book
template <class Book>
void
testBook()
{
Currency const c1(1);
AccountID const i1(1);
Currency const c2(2);
AccountID const i2(2);
Currency const c3(3);
AccountID const i3(3);
Issue a1(c1, i1);
Issue a2(c1, i2);
Issue a3(c2, i2);
Issue a4(c3, i2);
uint256 const domain1{1};
uint256 const domain2{2};
// Books without domains
CHECK(Book(a1, a2, std::nullopt) != Book(a2, a3, std::nullopt));
CHECK(Book(a1, a2, std::nullopt) < Book(a2, a3, std::nullopt));
CHECK(Book(a1, a2, std::nullopt) <= Book(a2, a3, std::nullopt));
CHECK(Book(a2, a3, std::nullopt) <= Book(a2, a3, std::nullopt));
CHECK(Book(a2, a3, std::nullopt) == Book(a2, a3, std::nullopt));
CHECK(Book(a2, a3, std::nullopt) >= Book(a2, a3, std::nullopt));
CHECK(Book(a3, a4, std::nullopt) >= Book(a2, a3, std::nullopt));
CHECK(Book(a3, a4, std::nullopt) > Book(a2, a3, std::nullopt));
// test domain books
{
// Books with different domains
CHECK(Book(a2, a3, domain1) != Book(a2, a3, domain2));
CHECK(Book(a2, a3, domain1) < Book(a2, a3, domain2));
CHECK(Book(a2, a3, domain2) > Book(a2, a3, domain1));
// One Book has a domain, the other does not
CHECK(Book(a2, a3, domain1) != Book(a2, a3, std::nullopt));
CHECK(Book(a2, a3, std::nullopt) < Book(a2, a3, domain1));
CHECK(Book(a2, a3, domain1) > Book(a2, a3, std::nullopt));
// Both Books have the same domain
CHECK(Book(a2, a3, domain1) == Book(a2, a3, domain1));
CHECK(Book(a2, a3, domain2) == Book(a2, a3, domain2));
CHECK(
Book(a2, a3, std::nullopt) == Book(a2, a3, std::nullopt));
// Both Books have no domain
CHECK(
Book(a2, a3, std::nullopt) == Book(a2, a3, std::nullopt));
// Testing comparisons with >= and <=
// When comparing books with domain1 vs domain2
CHECK(Book(a2, a3, domain1) <= Book(a2, a3, domain2));
CHECK(Book(a2, a3, domain2) >= Book(a2, a3, domain1));
CHECK(Book(a2, a3, domain1) >= Book(a2, a3, domain1));
CHECK(Book(a2, a3, domain2) <= Book(a2, a3, domain2));
// One Book has domain1 and the other has no domain
CHECK(Book(a2, a3, domain1) > Book(a2, a3, std::nullopt));
CHECK(Book(a2, a3, std::nullopt) < Book(a2, a3, domain1));
// One Book has domain2 and the other has no domain
CHECK(Book(a2, a3, domain2) > Book(a2, a3, std::nullopt));
CHECK(Book(a2, a3, std::nullopt) < Book(a2, a3, domain2));
// Comparing two Books with no domains
CHECK(
Book(a2, a3, std::nullopt) <= Book(a2, a3, std::nullopt));
CHECK(
Book(a2, a3, std::nullopt) >= Book(a2, a3, std::nullopt));
// Test case where domain1 is less than domain2
CHECK(Book(a2, a3, domain1) <= Book(a2, a3, domain2));
CHECK(Book(a2, a3, domain2) >= Book(a2, a3, domain1));
// Test case where domain2 is equal to domain1
CHECK(Book(a2, a3, domain1) >= Book(a2, a3, domain1));
CHECK(Book(a2, a3, domain1) <= Book(a2, a3, domain1));
// More test cases involving a4 (with domain2)
// Comparing Book with domain2 (a4) to a Book with domain1
CHECK(Book(a2, a3, domain1) < Book(a3, a4, domain2));
CHECK(Book(a3, a4, domain2) > Book(a2, a3, domain1));
// Comparing Book with domain2 (a4) to a Book with no domain
CHECK(Book(a3, a4, domain2) > Book(a2, a3, std::nullopt));
CHECK(Book(a2, a3, std::nullopt) < Book(a3, a4, domain2));
// Comparing Book with domain2 (a4) to a Book with the same domain
CHECK(Book(a3, a4, domain2) == Book(a3, a4, domain2));
// Comparing Book with domain2 (a4) to a Book with domain1
CHECK(Book(a2, a3, domain1) < Book(a3, a4, domain2));
CHECK(Book(a3, a4, domain2) > Book(a2, a3, domain1));
}
std::hash<Book> hash;
// log << std::hex << hash (Book (a1, a2));
// log << std::hex << hash (Book (a1, a2));
//
// log << std::hex << hash (Book (a1, a3));
// log << std::hex << hash (Book (a1, a3));
//
// log << std::hex << hash (Book (a1, a4));
// log << std::hex << hash (Book (a1, a4));
//
// log << std::hex << hash (Book (a2, a3));
// log << std::hex << hash (Book (a2, a3));
//
// log << std::hex << hash (Book (a2, a4));
// log << std::hex << hash (Book (a2, a4));
//
// log << std::hex << hash (Book (a3, a4));
// log << std::hex << hash (Book (a3, a4));
CHECK(
hash(Book(a1, a2, std::nullopt)) ==
hash(Book(a1, a2, std::nullopt)));
CHECK(
hash(Book(a1, a3, std::nullopt)) ==
hash(Book(a1, a3, std::nullopt)));
CHECK(
hash(Book(a1, a4, std::nullopt)) ==
hash(Book(a1, a4, std::nullopt)));
CHECK(
hash(Book(a2, a3, std::nullopt)) ==
hash(Book(a2, a3, std::nullopt)));
CHECK(
hash(Book(a2, a4, std::nullopt)) ==
hash(Book(a2, a4, std::nullopt)));
CHECK(
hash(Book(a3, a4, std::nullopt)) ==
hash(Book(a3, a4, std::nullopt)));
CHECK(
hash(Book(a1, a2, std::nullopt)) !=
hash(Book(a1, a3, std::nullopt)));
CHECK(
hash(Book(a1, a2, std::nullopt)) !=
hash(Book(a1, a4, std::nullopt)));
CHECK(
hash(Book(a1, a2, std::nullopt)) !=
hash(Book(a2, a3, std::nullopt)));
CHECK(
hash(Book(a1, a2, std::nullopt)) !=
hash(Book(a2, a4, std::nullopt)));
CHECK(
hash(Book(a1, a2, std::nullopt)) !=
hash(Book(a3, a4, std::nullopt)));
// Books with domain
CHECK(
hash(Book(a1, a2, domain1)) == hash(Book(a1, a2, domain1)));
CHECK(
hash(Book(a1, a3, domain1)) == hash(Book(a1, a3, domain1)));
CHECK(
hash(Book(a1, a4, domain1)) == hash(Book(a1, a4, domain1)));
CHECK(
hash(Book(a2, a3, domain1)) == hash(Book(a2, a3, domain1)));
CHECK(
hash(Book(a2, a4, domain1)) == hash(Book(a2, a4, domain1)));
CHECK(
hash(Book(a3, a4, domain1)) == hash(Book(a3, a4, domain1)));
CHECK(
hash(Book(a1, a2, std::nullopt)) ==
hash(Book(a1, a2, std::nullopt)));
// Comparing Books with domain1 vs no domain
CHECK(
hash(Book(a1, a2, std::nullopt)) != hash(Book(a1, a2, domain1)));
CHECK(
hash(Book(a1, a3, std::nullopt)) != hash(Book(a1, a3, domain1)));
CHECK(
hash(Book(a1, a4, std::nullopt)) != hash(Book(a1, a4, domain1)));
CHECK(
hash(Book(a2, a3, std::nullopt)) != hash(Book(a2, a3, domain1)));
CHECK(
hash(Book(a2, a4, std::nullopt)) != hash(Book(a2, a4, domain1)));
CHECK(
hash(Book(a3, a4, std::nullopt)) != hash(Book(a3, a4, domain1)));
// Books with domain1 but different Issues
CHECK(
hash(Book(a1, a2, domain1)) != hash(Book(a1, a3, domain1)));
CHECK(
hash(Book(a1, a2, domain1)) != hash(Book(a1, a4, domain1)));
CHECK(
hash(Book(a2, a3, domain1)) != hash(Book(a2, a4, domain1)));
CHECK(
hash(Book(a1, a2, domain1)) != hash(Book(a2, a3, domain1)));
CHECK(
hash(Book(a2, a4, domain1)) != hash(Book(a3, a4, domain1)));
CHECK(
hash(Book(a3, a4, domain1)) != hash(Book(a1, a4, domain1)));
// Books with domain1 and domain2
CHECK(
hash(Book(a1, a2, domain1)) != hash(Book(a1, a2, domain2)));
CHECK(
hash(Book(a1, a3, domain1)) != hash(Book(a1, a3, domain2)));
CHECK(
hash(Book(a1, a4, domain1)) != hash(Book(a1, a4, domain2)));
CHECK(
hash(Book(a2, a3, domain1)) != hash(Book(a2, a3, domain2)));
CHECK(
hash(Book(a2, a4, domain1)) != hash(Book(a2, a4, domain2)));
CHECK(
hash(Book(a3, a4, domain1)) != hash(Book(a3, a4, domain2)));
}
//--------------------------------------------------------------------------
template <class Set>
void
testBookSet()
{
Currency const c1(1);
AccountID const i1(1);
Currency const c2(2);
AccountID const i2(2);
Issue const a1(c1, i1);
Issue const a2(c2, i2);
Book const b1(a1, a2, std::nullopt);
Book const b2(a2, a1, std::nullopt);
uint256 const domain1{1};
uint256 const domain2{2};
Book const b1_d1(a1, a2, domain1);
Book const b2_d1(a2, a1, domain1);
Book const b1_d2(a1, a2, domain2);
Book const b2_d2(a2, a1, domain2);
{
Set c;
c.insert(b1);
CHECK(c.size() == 1);
c.insert(b2);
CHECK(c.size() == 2);
CHECK(c.erase(Book(a1, a1, std::nullopt)) == 0);
CHECK(c.erase(Book(a1, a2, std::nullopt)) == 1);
CHECK(c.erase(Book(a2, a1, std::nullopt)) == 1);
CHECK(c.empty());
}
{
Set c;
c.insert(b1);
CHECK(c.size() == 1);
c.insert(b2);
CHECK(c.size() == 2);
CHECK(c.erase(Book(a1, a1, std::nullopt)) == 0);
CHECK(c.erase(Book(a1, a2, std::nullopt)) == 1);
CHECK(c.erase(Book(a2, a1, std::nullopt)) == 1);
CHECK(c.empty());
#if STL_SET_HAS_EMPLACE
c.emplace(a1, a2);
CHECK(c.size() == 1);
c.emplace(a2, a1);
CHECK(c.size() == 2);
#endif
}
{
Set c;
c.insert(b1_d1);
CHECK(c.size() == 1);
c.insert(b2_d1);
CHECK(c.size() == 2);
c.insert(b1_d2);
CHECK(c.size() == 3);
c.insert(b2_d2);
CHECK(c.size() == 4);
// Try removing non-existent elements
CHECK(c.erase(Book(a2, a2, domain1)) == 0);
CHECK(c.erase(Book(a1, a2, domain1)) == 1);
CHECK(c.erase(Book(a2, a1, domain1)) == 1);
CHECK(c.size() == 2);
CHECK(c.erase(Book(a1, a2, domain2)) == 1);
CHECK(c.erase(Book(a2, a1, domain2)) == 1);
CHECK(c.empty());
}
{
Set c;
c.insert(b1);
c.insert(b2);
c.insert(b1_d1);
c.insert(b2_d1);
CHECK(c.size() == 4);
CHECK(c.erase(Book(a1, a2, std::nullopt)) == 1);
CHECK(c.erase(Book(a2, a1, std::nullopt)) == 1);
CHECK(c.size() == 2);
CHECK(c.erase(Book(a1, a2, domain1)) == 1);
CHECK(c.erase(Book(a2, a1, domain1)) == 1);
CHECK(c.empty());
}
}
template <class Map>
void
testBookMap()
{
Currency const c1(1);
AccountID const i1(1);
Currency const c2(2);
AccountID const i2(2);
Issue const a1(c1, i1);
Issue const a2(c2, i2);
Book const b1(a1, a2, std::nullopt);
Book const b2(a2, a1, std::nullopt);
uint256 const domain1{1};
uint256 const domain2{2};
Book const b1_d1(a1, a2, domain1);
Book const b2_d1(a2, a1, domain1);
Book const b1_d2(a1, a2, domain2);
Book const b2_d2(a2, a1, domain2);
// typename Map::value_type value_type;
// std::pair <Book const, int> value_type;
{
Map c;
// c.insert (value_type (b1, 1));
c.insert(std::make_pair(b1, 1));
CHECK(c.size() == 1);
// c.insert (value_type (b2, 2));
c.insert(std::make_pair(b2, 1));
CHECK(c.size() == 2);
CHECK(c.erase(Book(a1, a1, std::nullopt)) == 0);
CHECK(c.erase(Book(a1, a2, std::nullopt)) == 1);
CHECK(c.erase(Book(a2, a1, std::nullopt)) == 1);
CHECK(c.empty());
}
{
Map c;
// c.insert (value_type (b1, 1));
c.insert(std::make_pair(b1, 1));
CHECK(c.size() == 1);
// c.insert (value_type (b2, 2));
c.insert(std::make_pair(b2, 1));
CHECK(c.size() == 2);
CHECK(c.erase(Book(a1, a1, std::nullopt)) == 0);
CHECK(c.erase(Book(a1, a2, std::nullopt)) == 1);
CHECK(c.erase(Book(a2, a1, std::nullopt)) == 1);
CHECK(c.empty());
}
{
Map c;
c.insert(std::make_pair(b1_d1, 10));
CHECK(c.size() == 1);
c.insert(std::make_pair(b2_d1, 20));
CHECK(c.size() == 2);
c.insert(std::make_pair(b1_d2, 30));
CHECK(c.size() == 3);
c.insert(std::make_pair(b2_d2, 40));
CHECK(c.size() == 4);
// Try removing non-existent elements
CHECK(c.erase(Book(a2, a2, domain1)) == 0);
CHECK(c.erase(Book(a1, a2, domain1)) == 1);
CHECK(c.erase(Book(a2, a1, domain1)) == 1);
CHECK(c.size() == 2);
CHECK(c.erase(Book(a1, a2, domain2)) == 1);
CHECK(c.erase(Book(a2, a1, domain2)) == 1);
CHECK(c.empty());
}
{
Map c;
c.insert(std::make_pair(b1, 1));
c.insert(std::make_pair(b2, 2));
c.insert(std::make_pair(b1_d1, 3));
c.insert(std::make_pair(b2_d1, 4));
CHECK(c.size() == 4);
// Try removing non-existent elements
CHECK(c.erase(Book(a1, a1, domain1)) == 0);
CHECK(c.erase(Book(a2, a2, domain2)) == 0);
CHECK(c.erase(Book(a1, a2, std::nullopt)) == 1);
CHECK(c.erase(Book(a2, a1, std::nullopt)) == 1);
CHECK(c.size() == 2);
CHECK(c.erase(Book(a1, a2, domain1)) == 1);
CHECK(c.erase(Book(a2, a1, domain1)) == 1);
CHECK(c.empty());
}
}
//--------------------------------------------------------------------------
// End of helper functions.
}
} // namespace ripple
using namespace ripple;
TEST_SUITE_BEGIN("Issue");
TEST_CASE("Unsigned types")
{
testUnsigned<Currency>();
testUnsigned<AccountID>();
}
TEST_CASE("Issue")
{
testIssue<Issue>();
}
TEST_CASE("Issue sets")
{
INFO("std::set <Issue>");
testIssueSet<std::set<Issue>>();
INFO("std::set <Issue>");
testIssueSet<std::set<Issue>>();
#if RIPPLE_ASSETS_ENABLE_STD_HASH
INFO("std::unordered_set <Issue>");
testIssueSet<std::unordered_set<Issue>>();
INFO("std::unordered_set <Issue>");
testIssueSet<std::unordered_set<Issue>>();
#endif
INFO("hash_set <Issue>");
testIssueSet<hash_set<Issue>>();
INFO("hash_set <Issue>");
testIssueSet<hash_set<Issue>>();
}
TEST_CASE("Issue maps")
{
INFO("std::map <Issue, int>");
testIssueMap<std::map<Issue, int>>();
INFO("std::map <Issue, int>");
testIssueMap<std::map<Issue, int>>();
#if RIPPLE_ASSETS_ENABLE_STD_HASH
INFO("std::unordered_map <Issue, int>");
testIssueMap<std::unordered_map<Issue, int>>();
INFO("std::unordered_map <Issue, int>");
testIssueMap<std::unordered_map<Issue, int>>();
INFO("hash_map <Issue, int>");
testIssueMap<hash_map<Issue, int>>();
INFO("hash_map <Issue, int>");
testIssueMap<hash_map<Issue, int>>();
#endif
}
TEST_CASE("Book")
{
testBook<Book>();
}
TEST_CASE("Book sets")
{
INFO("std::set <Book>");
testBookSet<std::set<Book>>();
INFO("std::set <Book>");
testBookSet<std::set<Book>>();
#if RIPPLE_ASSETS_ENABLE_STD_HASH
INFO("std::unordered_set <Book>");
testBookSet<std::unordered_set<Book>>();
INFO("std::unordered_set <Book>");
testBookSet<std::unordered_set<Book>>();
#endif
INFO("hash_set <Book>");
testBookSet<hash_set<Book>>();
INFO("hash_set <Book>");
testBookSet<hash_set<Book>>();
}
TEST_CASE("Book maps")
{
INFO("std::map <Book, int>");
testBookMap<std::map<Book, int>>();
INFO("std::map <Book, int>");
testBookMap<std::map<Book, int>>();
#if RIPPLE_ASSETS_ENABLE_STD_HASH
INFO("std::unordered_map <Book, int>");
testBookMap<std::unordered_map<Book, int>>();
INFO("std::unordered_map <Book, int>");
testBookMap<std::unordered_map<Book, int>>();
INFO("hash_map <Book, int>");
testBookMap<hash_map<Book, int>>();
INFO("hash_map <Book, int>");
testBookMap<hash_map<Book, int>>();
#endif
}
TEST_CASE("Issue domain sets")
{
INFO("std::set <std::pair<Issue, Domain>>");
testIssueDomainSet<std::set<std::pair<Issue, Domain>>>();
INFO("std::set <std::pair<Issue, Domain>>");
testIssueDomainSet<std::set<std::pair<Issue, Domain>>>();
INFO("hash_set <std::pair<Issue, Domain>>");
testIssueDomainSet<hash_set<std::pair<Issue, Domain>>>();
INFO("hash_set <std::pair<Issue, Domain>>");
testIssueDomainSet<hash_set<std::pair<Issue, Domain>>>();
}
TEST_CASE("Issue domain maps")
{
INFO("std::map <std::pair<Issue, Domain>, int>");
testIssueDomainMap<std::map<std::pair<Issue, Domain>, int>>();
INFO("std::map <std::pair<Issue, Domain>, int>");
testIssueDomainMap<std::map<std::pair<Issue, Domain>, int>>();
#if RIPPLE_ASSETS_ENABLE_STD_HASH
INFO("hash_map <std::pair<Issue, Domain>, int>");
testIssueDomainMap<hash_map<std::pair<Issue, Domain>, int>>();
INFO("hash_map <std::pair<Issue, Domain>, int>");
testIssueDomainMap<hash_map<std::pair<Issue, Domain>, int>>();
INFO("hardened_hash_map <std::pair<Issue, Domain>, int>");
testIssueDomainMap<hardened_hash_map<std::pair<Issue, Domain>, int>>();
INFO("hardened_hash_map <std::pair<Issue, Domain>, int>");
testIssueDomainMap<hardened_hash_map<std::pair<Issue, Domain>, int>>();
#endif
}
TEST_SUITE_END();

View File

@@ -0,0 +1,129 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2022 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 <test/jtx.h>
#include <doctest/doctest.h>
#include <xrpl/beast/unit_test.h>
#include <xrpl/basics/strHex.h>
using namespace ripple;
TEST_SUITE_BEGIN("Memo");
TEST_CASE("Memo")
{
struct DummySuite : beast::unit_test::suite
{
void run() override {}
} suite;
using namespace test::jtx;
Account alice{"alice"};
Env env{suite};
env.fund(XRP(10000), alice);
env.close();
// Lambda that returns a valid JTx with a memo that we can hack up.
// This is the basis for building tests of invalid states.
auto makeJtxWithMemo = [&env, &alice]() {
JTx example = noop(alice);
memo const exampleMemo{"tic", "tac", "toe"};
exampleMemo(env, example);
return example;
};
// A valid memo.
env(makeJtxWithMemo());
env.close();
{
// Make sure that too big a memo is flagged as invalid.
JTx memoSize = makeJtxWithMemo();
memoSize.jv[sfMemos.jsonName][0u][sfMemo.jsonName][sfMemoData.jsonName] =
std::string(2020, '0');
env(memoSize,
rpc(
"invalidTransaction",
"fails local checks: The memo exceeds the maximum allowed size."));
// This memo is just barely small enough.
memoSize.jv[sfMemos.jsonName][0u][sfMemo.jsonName][sfMemoData.jsonName] =
std::string(2018, '1');
env(memoSize);
}
{
// Put a non-Memo in the Memos array.
JTx memoNonMemo = noop(alice);
auto& jv = memoNonMemo.jv;
auto& ma = jv[sfMemos.jsonName];
auto& mi = ma[ma.size()];
auto& m = mi[sfCreatedNode.jsonName]; // CreatedNode in Memos
m[sfMemoData.jsonName] = "3030303030";
env(
memoNonMemo,
rpc(
"invalidTransaction",
"fails local checks: A memo array may contain only Memo objects."));
}
{
// Put an invalid field in a Memo object.
JTx memoExtra = makeJtxWithMemo();
memoExtra.jv[sfMemos.jsonName][0u][sfMemo.jsonName][sfFlags.jsonName] = 13;
env(
memoExtra,
rpc(
"invalidTransaction",
"fails local checks: A memo may contain only MemoType, MemoData or MemoFormat fields."));
}
{
// Put a character that is not allowed in a URL in a MemoType field.
JTx memoBadChar = makeJtxWithMemo();
memoBadChar.jv[sfMemos.jsonName][0u][sfMemo.jsonName][sfMemoType.jsonName] =
strHex(std::string_view("ONE<INFINITY"));
env(
memoBadChar,
rpc(
"invalidTransaction",
"fails local checks: The MemoType and MemoFormat fields may only contain characters that are allowed in URLs under RFC 3986."));
}
{
// Put a character that is not allowed in a URL in a MemoData field.
// That's okay.
JTx memoLegitChar = makeJtxWithMemo();
memoLegitChar.jv[sfMemos.jsonName][0u][sfMemo.jsonName][sfMemoData.jsonName] =
strHex(std::string_view("ONE<INFINITY"));
env(memoLegitChar);
}
{
// Put a character that is not allowed in a URL in a MemoFormat.
JTx memoBadChar = makeJtxWithMemo();
memoBadChar.jv[sfMemos.jsonName][0u][sfMemo.jsonName][sfMemoFormat.jsonName] =
strHex(std::string_view("NoBraces{}InURL"));
env(
memoBadChar,
rpc(
"invalidTransaction",
"fails local checks: The MemoType and MemoFormat fields may only contain characters that are allowed in URLs under RFC 3986."));
}
}
TEST_SUITE_END();

View File

@@ -0,0 +1,646 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/XRPLF/rippled/
Copyright (c) 2023 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 <doctest/doctest.h>
#include <xrpl/protocol/MultiApiJson.h>
#include <cstdint>
#include <limits>
#include <optional>
#include <type_traits>
#include <utility>
namespace ripple {
namespace test {
namespace {
// This needs to be in a namespace because of deduction guide
template <typename... Ts>
struct Overload : Ts...
{
using Ts::operator()...;
};
template <typename... Ts>
Overload(Ts...) -> Overload<Ts...>;
} // namespace
static auto
makeJson(char const* key, int val)
{
Json::Value obj(Json::objectValue);
obj[key] = val;
return obj;
}
struct MultiApiJsonFixture
{
using MultiApiJson13 = ripple::detail::MultiApiJson<1, 3>;
Json::Value const obj1{makeJson("value", 1)};
Json::Value const obj2{makeJson("value", 2)};
Json::Value const obj3{makeJson("value", 3)};
Json::Value const jsonNull{};
MultiApiJsonFixture() = default;
};
TEST_SUITE_BEGIN("MultiApiJson");
TEST_CASE_METHOD(
MultiApiJsonFixture,
"forApiVersions, forAllApiVersions",
"[MultiApiJson]")
{
using ripple::detail::MultiApiJson;
MultiApiJson13 subject{};
static_assert(sizeof(subject) == sizeof(subject.val));
static_assert(subject.size == subject.val.size());
static_assert(
std::is_same_v<decltype(subject.val), std::array<Json::Value, 3>>);
CHECK(subject.val.size() == 3);
CHECK(subject.val == std::array<Json::Value, 3>{jsonNull, jsonNull, jsonNull});
subject.val[0] = obj1;
subject.val[1] = obj2;
// Some static data for test inputs
static int const primes[] = {2, 3, 5, 7, 11, 13, 17, 19, 23,
29, 31, 37, 41, 43, 47, 53, 59, 61,
67, 71, 73, 79, 83, 89, 97};
static_assert(std::size(primes) > RPC::apiMaximumValidVersion);
MultiApiJson13 s1{};
static_assert(
s1.size ==
RPC::apiMaximumValidVersion + 1 - RPC::apiMinimumSupportedVersion);
int productAllVersions = 1;
for (unsigned i = RPC::apiMinimumSupportedVersion;
i <= RPC::apiMaximumValidVersion;
++i)
{
auto const index = i - RPC::apiMinimumSupportedVersion;
CHECK(index == s1.index(i));
CHECK(s1.valid(i));
s1.val[index] = makeJson("value", primes[i]);
productAllVersions *= primes[i];
}
CHECK(!s1.valid(0));
CHECK(!s1.valid(RPC::apiMaximumValidVersion + 1));
CHECK(
!s1.valid(std::numeric_limits<
decltype(RPC::apiMaximumValidVersion.value)>::max()));
int result = 1;
static_assert(
RPC::apiMinimumSupportedVersion + 1 <= RPC::apiMaximumValidVersion);
forApiVersions<
RPC::apiMinimumSupportedVersion,
RPC::apiMinimumSupportedVersion + 1>(
std::as_const(s1).visit(),
[](
Json::Value const& json,
unsigned int version,
int* result) {
CHECK(
version >= RPC::apiMinimumSupportedVersion &&
version <= RPC::apiMinimumSupportedVersion + 1);
if (CHECK(json.isMember("value")))
{
*result *= json["value"].asInt();
}
},
&result);
CHECK(
result ==
primes[RPC::apiMinimumSupportedVersion] *
primes[RPC::apiMinimumSupportedVersion + 1]);
// Check all the values with mutable data
forAllApiVersions(
s1.visit(), [&s1](Json::Value& json, auto version) {
CHECK(s1.val[s1.index(version)] == json);
if (CHECK(json.isMember("value")))
{
CHECK(json["value"].asInt() == primes[version]);
}
});
result = 1;
forAllApiVersions(
std::as_const(s1).visit(),
[](
Json::Value const& json,
unsigned int version,
int* result) {
CHECK(
version >= RPC::apiMinimumSupportedVersion &&
version <= RPC::apiMaximumValidVersion);
if (CHECK(json.isMember("value")))
{
*result *= json["value"].asInt();
}
},
&result);
CHECK(result == productAllVersions);
// Several overloads we want to fail
static_assert([](auto&& v) {
return !requires {
forAllApiVersions(
std::forward<decltype(v)>(v).visit(), //
[](Json::Value&, auto) {}); // missing const
};
}(std::as_const(s1)));
static_assert([](auto&& v) {
return !requires {
forAllApiVersions(
std::forward<decltype(v)>(v).visit(), //
[](Json::Value&) {}); // missing const
};
}(std::as_const(s1)));
static_assert([](auto&& v) {
return !requires {
forAllApiVersions(
std::forward<decltype(v)>(v).visit(), //
[]() {}); // missing parameters
};
}(std::as_const(s1)));
static_assert([](auto&& v) {
return !requires {
forAllApiVersions(
std::forward<decltype(v)>(v).visit(), //
[](auto) {},
1); // missing parameters
};
}(std::as_const(s1)));
static_assert([](auto&& v) {
return !requires {
forAllApiVersions(
std::forward<decltype(v)>(v).visit(), //
[](auto, auto) {},
1); // missing parameters
};
}(std::as_const(s1)));
static_assert([](auto&& v) {
return !requires {
forAllApiVersions(
std::forward<decltype(v)>(v).visit(), //
[](auto, auto, char const*) {},
1); // parameter type mismatch
};
}(std::as_const(s1)));
// Sanity checks
static_assert([](auto&& v) {
return requires {
forAllApiVersions(
std::forward<decltype(v)>(v).visit(), //
[](auto) {});
};
}(s1));
static_assert([](auto&& v) {
return requires {
forAllApiVersions(
std::forward<decltype(v)>(v).visit(), //
[](Json::Value const&) {});
};
}(std::as_const(s1)));
static_assert([](auto&& v) {
return requires {
forAllApiVersions(
std::forward<decltype(v)>(v).visit(), //
[](auto...) {});
};
}(s1));
static_assert([](auto&& v) {
return requires {
forAllApiVersions(
std::forward<decltype(v)>(v).visit(), //
[](Json::Value const&, auto...) {});
};
}(std::as_const(s1)));
static_assert([](auto&& v) {
return requires {
forAllApiVersions(
std::forward<decltype(v)>(v).visit(), //
[](Json::Value&, auto, auto, auto...) {},
0,
"");
};
}(s1));
static_assert([](auto&& v) {
return requires {
forAllApiVersions(
std::forward<decltype(v)>(v).visit(), //
[]<unsigned int Version>(
Json::Value const&,
std::integral_constant<unsigned int, Version>,
int,
char const*) {},
0,
"");
};
}(std::as_const(s1)));
static_assert([](auto&& v) {
return requires {
forAllApiVersions(
std::forward<decltype(v)>(v).visit(), //
[](auto...) {});
};
}(std::move(s1)));
static_assert([](auto&& v) {
return requires {
forAllApiVersions(
std::forward<decltype(v)>(v).visit(), //
[](auto...) {});
};
}(std::move(std::as_const(s1))));
}
TEST_CASE_METHOD(
MultiApiJsonFixture,
"default copy construction / assignment",
"[MultiApiJson]")
{
using ripple::detail::MultiApiJson;
MultiApiJson13 subject{};
subject.val[0] = obj1;
subject.val[1] = obj2;
MultiApiJson13 x{subject};
CHECK(x.val.size() == subject.val.size());
CHECK(x.val[0] == subject.val[0]);
CHECK(x.val[1] == subject.val[1]);
CHECK(x.val[2] == subject.val[2]);
CHECK(x.val == subject.val);
CHECK(&x.val[0] != &subject.val[0]);
CHECK(&x.val[1] != &subject.val[1]);
CHECK(&x.val[2] != &subject.val[2]);
MultiApiJson13 y;
CHECK((y.val == std::array<Json::Value, 3>{}));
y = subject;
CHECK(y.val == subject.val);
CHECK(&y.val[0] != &subject.val[0]);
CHECK(&y.val[1] != &subject.val[1]);
CHECK(&y.val[2] != &subject.val[2]);
y = std::move(x);
CHECK(y.val == subject.val);
CHECK(&y.val[0] != &subject.val[0]);
CHECK(&y.val[1] != &subject.val[1]);
CHECK(&y.val[2] != &subject.val[2]);
}
TEST_CASE_METHOD(MultiApiJsonFixture, "set", "[MultiApiJson]")
{
using ripple::detail::MultiApiJson;
auto x = MultiApiJson<1, 2>{Json::objectValue};
x.set("name1", 42);
CHECK(x.val[0].isMember("name1"));
CHECK(x.val[1].isMember("name1"));
CHECK(x.val[0]["name1"].isInt());
CHECK(x.val[1]["name1"].isInt());
CHECK(x.val[0]["name1"].asInt() == 42);
CHECK(x.val[1]["name1"].asInt() == 42);
x.set("name2", "bar");
CHECK(x.val[0].isMember("name2"));
CHECK(x.val[1].isMember("name2"));
CHECK(x.val[0]["name2"].isString());
CHECK(x.val[1]["name2"].isString());
CHECK(x.val[0]["name2"].asString() == "bar");
CHECK(x.val[1]["name2"].asString() == "bar");
// Tests of requires clause - these are expected to match
static_assert([](auto&& v) {
return requires { v.set("name", Json::nullValue); };
}(x));
static_assert([](auto&& v) {
return requires { v.set("name", "value"); };
}(x));
static_assert(
[](auto&& v) { return requires { v.set("name", true); }; }(x));
static_assert(
[](auto&& v) { return requires { v.set("name", 42); }; }(x));
// Tests of requires clause - these are expected NOT to match
struct foo_t final {};
static_assert([](auto&& v) {
return !requires { v.set("name", foo_t{}); };
}(x));
static_assert([](auto&& v) {
return !requires { v.set("name", std::nullopt); };
}(x));
}
TEST_CASE_METHOD(MultiApiJsonFixture, "isMember", "[MultiApiJson]")
{
using ripple::detail::MultiApiJson;
MultiApiJson13 subject{};
subject.val[0] = obj1;
subject.val[1] = obj2;
// Well defined behaviour even if we have different types of members
CHECK(subject.isMember("foo") == decltype(subject)::none);
{
// All variants have element "One", none have element "Two"
MultiApiJson<1, 2> s1{};
s1.val[0] = makeJson("One", 12);
s1.val[1] = makeJson("One", 42);
CHECK(s1.isMember("One") == decltype(s1)::all);
CHECK(s1.isMember("Two") == decltype(s1)::none);
}
{
// Some variants have element "One" and some have "Two"
MultiApiJson<1, 2> s2{};
s2.val[0] = makeJson("One", 12);
s2.val[1] = makeJson("Two", 42);
CHECK(s2.isMember("One") == decltype(s2)::some);
CHECK(s2.isMember("Two") == decltype(s2)::some);
}
{
// Not all variants have element "One", because last one is null
MultiApiJson<1, 3> s3{};
s3.val[0] = makeJson("One", 12);
s3.val[1] = makeJson("One", 42);
CHECK(s3.isMember("One") == decltype(s3)::some);
CHECK(s3.isMember("Two") == decltype(s3)::none);
}
}
TEST_CASE_METHOD(MultiApiJsonFixture, "visitor", "[MultiApiJson]")
{
using ripple::detail::MultiApiJson;
MultiApiJson13 s1{};
s1.val[0] = makeJson("value", 2);
s1.val[1] = makeJson("value", 3);
s1.val[2] = makeJson("value", 5);
CHECK(not s1.valid(0));
CHECK(s1.index(0) == 0);
CHECK(s1.valid(1));
CHECK(s1.index(1) == 0);
CHECK(not s1.valid(4));
// Test different overloads
static_assert([](auto&& v) {
return requires {
v.visitor(
v,
std::integral_constant<unsigned, 1>{},
[](Json::Value&, std::integral_constant<unsigned, 1>) {});
};
}(s1));
CHECK(
s1.visitor(
s1,
std::integral_constant<unsigned, 1>{},
Overload{
[](Json::Value& v,
std::integral_constant<unsigned, 1>) {
return v["value"].asInt();
},
[](Json::Value const&, auto) { return 0; },
[](auto, auto) { return 0; }}) == 2);
static_assert([](auto&& v) {
return requires {
v.visitor(
v,
std::integral_constant<unsigned, 1>{},
[](Json::Value&) {});
};
}(s1));
CHECK(
s1.visitor(
s1,
std::integral_constant<unsigned, 1>{},
Overload{
[](Json::Value& v) { return v["value"].asInt(); },
[](Json::Value const&) { return 0; },
[](auto...) { return 0; }}) == 2);
static_assert([](auto&& v) {
return requires {
v.visitor(
v,
std::integral_constant<unsigned, 1>{},
[](Json::Value const&,
std::integral_constant<unsigned, 1>) {});
};
}(std::as_const(s1)));
CHECK(
s1.visitor(
std::as_const(s1),
std::integral_constant<unsigned, 2>{},
Overload{
[](Json::Value const& v,
std::integral_constant<unsigned, 2>) {
return v["value"].asInt();
},
[](Json::Value&, auto) { return 0; },
[](auto, auto) { return 0; }}) == 3);
static_assert([](auto&& v) {
return requires {
v.visitor(
v,
std::integral_constant<unsigned, 1>{},
[](auto, auto, auto...) {});
};
}(s1));
static_assert([](auto&& v) {
return requires {
v.visitor(v, 1, [](auto, auto, auto...) {});
};
}(s1));
static_assert([](auto&& v) {
return requires {
v.visitor(v, 1, [](auto, auto, auto...) {}, "");
};
}(s1));
static_assert([](auto&& v) {
return requires {
v.visitor(v, 1, [](auto, auto, auto, auto...) {}, "");
};
}(s1));
}
TEST_CASE_METHOD(MultiApiJsonFixture, "visit", "[MultiApiJson]")
{
using ripple::detail::MultiApiJson;
MultiApiJson13 s1{};
s1.val[0] = makeJson("value", 2);
s1.val[1] = makeJson("value", 3);
s1.val[2] = makeJson("value", 5);
// Test different overloads
static_assert([](auto&& v) {
return requires {
v.visit(
std::integral_constant<unsigned, 1>{},
[](Json::Value&, std::integral_constant<unsigned, 1>) {});
};
}(s1));
CHECK(
s1.visit(
std::integral_constant<unsigned, 1>{},
Overload{
[](Json::Value& v,
std::integral_constant<unsigned, 1>) {
return v["value"].asInt();
},
[](Json::Value const&, auto) { return 0; },
[](auto, auto) { return 0; }}) == 2);
static_assert([](auto&& v) {
return requires {
v.visit()(
std::integral_constant<unsigned, 1>{},
[](Json::Value&, std::integral_constant<unsigned, 1>) {});
};
}(s1));
CHECK(
s1.visit()(std::integral_constant<unsigned, 1>{},
Overload{[](Json::Value& v,
std::integral_constant<unsigned, 1>) {
return v["value"].asInt();
},
[](Json::Value const&, auto) { return 0; },
[](auto, auto) { return 0; }}) == 2);
static_assert([](auto&& v) {
return requires {
v.visit(
std::integral_constant<unsigned, 1>{},
[](Json::Value&) {});
};
}(s1));
CHECK(
s1.visit(
std::integral_constant<unsigned, 1>{},
Overload{
[](Json::Value& v) { return v["value"].asInt(); },
[](Json::Value const&) { return 0; },
[](auto...) { return 0; }}) == 2);
static_assert([](auto&& v) {
return requires {
v.visit()(std::integral_constant<unsigned, 1>{}, [](Json::Value&) {});
};
}(s1));
CHECK(
s1.visit()(std::integral_constant<unsigned, 1>{},
Overload{[](Json::Value& v) { return v["value"].asInt(); },
[](Json::Value const&) { return 0; },
[](auto...) { return 0; }}) == 2);
static_assert([](auto&& v) {
return requires {
v.visit(
std::integral_constant<unsigned, 1>{},
[](Json::Value const&,
std::integral_constant<unsigned, 1>) {});
};
}(std::as_const(s1)));
CHECK(
std::as_const(s1).visit(
std::integral_constant<unsigned, 2>{},
Overload{
[](Json::Value const& v,
std::integral_constant<unsigned, 2>) {
return v["value"].asInt();
},
[](Json::Value&, auto) { return 0; },
[](auto, auto) { return 0; }}) == 3);
static_assert([](auto&& v) {
return requires {
v.visit()(std::integral_constant<unsigned, 1>{},
[](Json::Value const&,
std::integral_constant<unsigned, 1>) {});
};
}(std::as_const(s1)));
CHECK(
std::as_const(s1).visit()(std::integral_constant<unsigned, 2>{},
Overload{[](Json::Value const& v,
std::integral_constant<unsigned,
2>) {
return v["value"].asInt();
},
[](Json::Value&, auto) { return 0; },
[](auto, auto) { return 0; }}) == 3);
static_assert([](auto&& v) {
return requires {
v.visit(
std::integral_constant<unsigned, 1>{}, [](Json::Value const&) {});
};
}(std::as_const(s1)));
CHECK(
std::as_const(s1).visit(
std::integral_constant<unsigned, 2>{},
Overload{
[](Json::Value const& v) { return v["value"].asInt(); },
[](Json::Value&) { return 0; },
[](auto...) { return 0; }}) == 3);
static_assert([](auto&& v) {
return requires {
v.visit()(std::integral_constant<unsigned, 1>{},
[](Json::Value const&) {});
};
}(std::as_const(s1)));
CHECK(
std::as_const(s1).visit()(std::integral_constant<unsigned, 2>{},
Overload{[](Json::Value const& v) {
return v["value"].asInt();
},
[](Json::Value&) { return 0; },
[](auto...) { return 0; }}) == 3);
static_assert([](auto&& v) {
return requires { v.visit(v, 1, [](auto, auto, auto...) {}); };
}(s1));
static_assert([](auto&& v) {
return requires { v.visit(v, 1, [](auto, auto, auto...) {}, ""); };
}(s1));
static_assert([](auto&& v) {
return requires {
v.visit(v, 1, [](auto, auto, auto, auto...) {}, "");
};
}(s1));
}
TEST_SUITE_END();

View File

@@ -0,0 +1,507 @@
//------------------------------------------------------------------------------
/*
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 <doctest/doctest.h>
#include <xrpl/protocol/PublicKey.h>
#include <xrpl/protocol/SecretKey.h>
#include <vector>
namespace ripple {
namespace {
using blob = std::vector<std::uint8_t>;
template <class FwdIter, class Container>
void
hex_to_binary(FwdIter first, FwdIter last, Container& out)
{
struct Table
{
int val[256];
Table()
{
std::fill(val, val + 256, 0);
for (int i = 0; i < 10; ++i)
val['0' + i] = i;
for (int i = 0; i < 6; ++i)
{
val['A' + i] = 10 + i;
val['a' + i] = 10 + i;
}
}
int
operator[](int i)
{
return val[i];
}
};
static Table lut;
out.reserve(std::distance(first, last) / 2);
while (first != last)
{
auto const hi(lut[(*first++)]);
auto const lo(lut[(*first++)]);
out.push_back((hi * 16) + lo);
}
}
blob
sig(std::string const& hex)
{
blob b;
hex_to_binary(hex.begin(), hex.end(), b);
return b;
}
bool
check(std::optional<ECDSACanonicality> answer, std::string const& s)
{
return ecdsaCanonicality(makeSlice(sig(s))) == answer;
}
} // namespace
TEST_SUITE_BEGIN("PublicKey");
TEST_CASE("Base58")
{
INFO("Base58: secp256k1");
{
auto const pk1 = derivePublicKey(
KeyType::secp256k1,
generateSecretKey(
KeyType::secp256k1, generateSeed("masterpassphrase")));
auto const pk2 = parseBase58<PublicKey>(
TokenType::NodePublic,
"n94a1u4jAz288pZLtw6yFWVbi89YamiC6JBXPVUj5zmExe5fTVg9");
CHECK(pk2);
CHECK(pk1 == *pk2);
}
// Try converting short, long and malformed data for secp256k1 keys
CHECK(!parseBase58<PublicKey>(TokenType::NodePublic, ""));
CHECK(!parseBase58<PublicKey>(TokenType::NodePublic, " "));
CHECK(!parseBase58<PublicKey>(TokenType::NodePublic, "!ty89234gh45"));
auto good = toBase58(
TokenType::NodePublic, derivePublicKey(KeyType::secp256k1, randomSecretKey()));
{
auto s = good;
std::hash<std::string> r;
while (!s.empty())
{
s.erase(r(s) % s.size(), 1);
CHECK(!parseBase58<PublicKey>(TokenType::NodePublic, s));
}
}
for (std::size_t i = 1; i != 16; i++)
{
auto s = good;
s.resize(s.size() + i, s[i % s.size()]);
CHECK(!parseBase58<PublicKey>(TokenType::NodePublic, s));
}
for (auto c : std::string("0IOl"))
{
for (std::size_t i = 0; i != good.size(); ++i)
{
auto s = good;
s[i % s.size()] = c;
CHECK(!parseBase58<PublicKey>(TokenType::NodePublic, s));
}
}
{
auto s = good;
for (auto c : std::string("apsrJqtv7"))
{
s[0] = c;
CHECK(!parseBase58<PublicKey>(TokenType::NodePublic, s));
}
}
std::vector<PublicKey> keys;
keys.reserve(32);
for (std::size_t i = 0; i != keys.capacity(); ++i)
keys.emplace_back(derivePublicKey(KeyType::secp256k1, randomSecretKey()));
CHECK(keys.size() == 32);
for (std::size_t i = 0; i != keys.size(); ++i)
{
auto const si = toBase58(TokenType::NodePublic, keys[i]);
CHECK(!si.empty());
auto const ski = parseBase58<PublicKey>(TokenType::NodePublic, si);
CHECK(ski);
CHECK(keys[i] == *ski);
for (std::size_t j = i; j != keys.size(); ++j)
{
CHECK((keys[i] == keys[j]) == (i == j));
auto const sj = toBase58(TokenType::NodePublic, keys[j]);
CHECK((si == sj) == (i == j));
auto const skj = parseBase58<PublicKey>(TokenType::NodePublic, sj);
CHECK(skj);
CHECK(keys[j] == *skj);
CHECK((*ski == *skj) == (i == j));
}
}
INFO("Base58: ed25519");
{
auto const pk1 = derivePublicKey(
KeyType::ed25519,
generateSecretKey(
KeyType::ed25519, generateSeed("masterpassphrase")));
auto const pk2 = parseBase58<PublicKey>(
TokenType::NodePublic,
"nHUeeJCSY2dM71oxM8Cgjouf5ekTuev2mwDpc374aLMxzDLXNmjf");
CHECK(pk2);
CHECK(pk1 == *pk2);
}
// Repeat Base58 conversion tests for ed25519 keys
good = toBase58(
TokenType::NodePublic, derivePublicKey(KeyType::ed25519, randomSecretKey()));
{
auto s = good;
std::hash<std::string> r;
while (!s.empty())
{
s.erase(r(s) % s.size(), 1);
CHECK(!parseBase58<PublicKey>(TokenType::NodePublic, s));
}
}
for (std::size_t i = 1; i != 16; i++)
{
auto s = good;
s.resize(s.size() + i, s[i % s.size()]);
CHECK(!parseBase58<PublicKey>(TokenType::NodePublic, s));
}
for (auto c : std::string("0IOl"))
{
for (std::size_t i = 0; i != good.size(); ++i)
{
auto s = good;
s[i % s.size()] = c;
CHECK(!parseBase58<PublicKey>(TokenType::NodePublic, s));
}
}
{
auto s = good;
for (auto c : std::string("apsrJqtv7"))
{
s[0] = c;
CHECK(!parseBase58<PublicKey>(TokenType::NodePublic, s));
}
}
keys.clear();
for (std::size_t i = 0; i != 32; ++i)
keys.emplace_back(derivePublicKey(KeyType::ed25519, randomSecretKey()));
CHECK(keys.size() == 32);
for (std::size_t i = 0; i != keys.size(); ++i)
{
auto const si = toBase58(TokenType::NodePublic, keys[i]);
CHECK(!si.empty());
auto const ski = parseBase58<PublicKey>(TokenType::NodePublic, si);
CHECK(ski);
CHECK(keys[i] == *ski);
for (std::size_t j = i; j != keys.size(); ++j)
{
CHECK((keys[i] == keys[j]) == (i == j));
auto const sj = toBase58(TokenType::NodePublic, keys[j]);
CHECK((si == sj) == (i == j));
auto const skj = parseBase58<PublicKey>(TokenType::NodePublic, sj);
CHECK(skj);
CHECK(keys[j] == *skj);
CHECK((*ski == *skj) == (i == j));
}
}
}
TEST_CASE("Canonical")
{
INFO("Canonical");
// Fully canonical
CHECK(check(
ECDSACanonicality::fullyCanonical,
"3045"
"022100FF478110D1D4294471EC76E0157540C2181F47DEBD25D7F9E7DDCCCD47EE"
"E905"
"0220078F07CDAE6C240855D084AD91D1479609533C147C93B0AEF19BC9724D003F"
"28"));
CHECK(check(
ECDSACanonicality::fullyCanonical,
"3045"
"0221009218248292F1762D8A51BE80F8A7F2CD288D810CE781D5955700DA1684DF"
"1D2D"
"022041A1EE1746BFD72C9760CC93A7AAA8047D52C8833A03A20EAAE92EA19717B4"
"54"));
CHECK(check(
ECDSACanonicality::fullyCanonical,
"3044"
"02206A9E43775F73B6D1EC420E4DDD222A80D4C6DF5D1BEECC431A91B63C928B75"
"81"
"022023E9CC2D61DDA6F73EAA6BCB12688BEB0F434769276B3127E4044ED895C9D9"
"6B"));
CHECK(check(
ECDSACanonicality::fullyCanonical,
"3044"
"022056E720007221F3CD4EFBB6352741D8E5A0968D48D8D032C2FBC4F6304AD1D0"
"4E"
"02201F39EB392C20D7801C3E8D81D487E742FA84A1665E923225BD6323847C7187"
"9F"));
CHECK(check(
ECDSACanonicality::fullyCanonical,
"3045"
"022100FDFD5AD05518CEA0017A2DCB5C4DF61E7C73B6D3A38E7AE93210A1564E8C"
"2F12"
"0220214FF061CCC123C81D0BB9D0EDEA04CD40D96BF1425D311DA62A7096BB18EA"
"18"));
// Canonical but not fully canonical
CHECK(check(
ECDSACanonicality::canonical,
"3046"
"022100F477B3FA6F31C7CB3A0D1AD94A231FDD24B8D78862EE334CEA7CD08F6CBC"
"0A1B"
"022100928E6BCF1ED2684679730C5414AEC48FD62282B090041C41453C1D064AF5"
"97A1"));
CHECK(check(
ECDSACanonicality::canonical,
"3045"
"022063E7C7CA93CB2400E413A342C027D00665F8BAB9C22EF0A7B8AE3AAF092230"
"B6"
"0221008F2E8BB7D09521ABBC277717B14B93170AE6465C5A1B36561099319C4BEB"
"254C"));
CHECK(check(
ECDSACanonicality::canonical,
"3046"
"02210099DCA1188663DDEA506A06A7B20C2B7D8C26AFF41DECE69D6C5F7C967D32"
"625F"
"022100897658A6B1F9EEE5D140D7A332DA0BD73BB98974EA53F6201B01C1B594F2"
"86EA"));
CHECK(check(
ECDSACanonicality::canonical,
"3045"
"02200855DE366E4E323AA2CE2A25674401A7D11F72EC432770D07F7B57DF7387AE"
"C0"
"022100DA4C6ADDEA14888858DE2AC5B91ED9050D6972BB388DEF582628CEE32869"
"AE35"));
// valid
CHECK(check(
ECDSACanonicality::fullyCanonical,
"3006"
"020101"
"020102"));
CHECK(check(
ECDSACanonicality::fullyCanonical,
"3044"
"02203932c892e2e550f3af8ee4ce9c215a87f9bb831dcac87b2838e2c2eaa891df"
"0c"
"022030b61dd36543125d56b9f9f3a1f53189e5af33cdda8d77a5209aec03978fa0"
"01"));
CHECK(check(
ECDSACanonicality::canonical,
"3045"
"0220076045be6f9eca28ff1ec606b833d0b87e70b2a630f5e3a496b110967a40f9"
"0a"
"0221008fffd599910eefe00bc803c688eca1d2ba7f6b180620eaa03488e6585db6"
"ba01"));
CHECK(check(
ECDSACanonicality::canonical,
"3046"
"022100876045be6f9eca28ff1ec606b833d0b87e70b2a630f5e3a496b110967a40"
"f90a"
"0221008fffd599910eefe00bc803c688c2eca1d2ba7f6b180620eaa03488e6585d"
"b6ba"));
CHECK(check(
std::nullopt,
"3005"
"0201FF"
"0200"));
CHECK(check(
std::nullopt,
"3006"
"020101"
"020202"));
CHECK(check(
std::nullopt,
"3006"
"020701"
"020102"));
CHECK(check(
std::nullopt,
"3006"
"020401"
"020102"));
CHECK(check(
std::nullopt,
"3006"
"020501"
"020102"));
CHECK(check(
std::nullopt,
"3006"
"020201"
"020102"));
CHECK(check(
std::nullopt,
"3006"
"020301"
"020202"));
CHECK(check(
std::nullopt,
"3006"
"020401"
"020202"));
CHECK(check(
std::nullopt,
"3047"
"0221005990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba"
"6105"
"022200002d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e56"
"6695ed"));
CHECK(check(
std::nullopt,
"3144"
"02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
"05"
"02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695"
"ed"));
CHECK(check(
std::nullopt,
"3045"
"02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
"05"
"02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695"
"ed"));
CHECK(check(
std::nullopt,
"301F"
"01205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1"));
CHECK(check(
std::nullopt,
"3045"
"02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
"05"
"02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695"
"ed00"));
CHECK(check(
std::nullopt,
"3044"
"01205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
"05"
"02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695"
"ed"));
CHECK(check(
std::nullopt,
"3024"
"0200"
"02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695"
"ed"));
CHECK(check(
std::nullopt,
"3044"
"02208990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
"05"
"02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695"
"ed"));
CHECK(check(
std::nullopt,
"3045"
"0221005990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba"
"6105"
"02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695"
"ed"));
CHECK(check(
std::nullopt,
"3044"
"02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
"05012"
"02d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695e"
"d"));
CHECK(check(
std::nullopt,
"3024"
"02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
"05"
"0200"));
CHECK(check(
std::nullopt,
"3044"
"02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
"05"
"0220fd5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695"
"ed"));
CHECK(check(
std::nullopt,
"3045"
"02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
"05"
"0221002d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e5666"
"95ed"));
}
TEST_CASE("Miscellaneous operations")
{
INFO("Miscellaneous operations");
auto const pk1 = derivePublicKey(
KeyType::secp256k1,
generateSecretKey(
KeyType::secp256k1, generateSeed("masterpassphrase")));
PublicKey pk2(pk1);
CHECK(pk1 == pk2);
CHECK(pk2 == pk1);
PublicKey pk3 = derivePublicKey(
KeyType::secp256k1,
generateSecretKey(
KeyType::secp256k1, generateSeed("arbitraryPassPhrase")));
pk3 = pk2;
CHECK(pk3 == pk2);
CHECK(pk1 == pk3);
}
TEST_SUITE_END();
} // namespace ripple

View File

@@ -0,0 +1,2 @@
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include <doctest/doctest.h>