mirror of
https://github.com/XRPLF/rippled.git
synced 2026-07-06 23:00:20 +00:00
Compare commits
7 Commits
2.5.0
...
a1q123456/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ac55c8725c | ||
|
|
793768a7ba | ||
|
|
e18f27f5f7 | ||
|
|
df6daf0d8f | ||
|
|
e9d46f0bfc | ||
|
|
42fd74b77b | ||
|
|
c55ea56c5e |
5
.github/workflows/macos.yml
vendored
5
.github/workflows/macos.yml
vendored
@@ -96,4 +96,7 @@ jobs:
|
||||
run: |
|
||||
n=$(nproc)
|
||||
echo "Using $n test jobs"
|
||||
${build_dir}/rippled --unittest --unittest-jobs $n
|
||||
|
||||
cd ${build_dir}
|
||||
./rippled --unittest --unittest-jobs $n
|
||||
ctest -j $n --output-on-failure
|
||||
|
||||
10
.github/workflows/nix.yml
vendored
10
.github/workflows/nix.yml
vendored
@@ -163,7 +163,9 @@ jobs:
|
||||
cmake-args: "-Dassert=TRUE -Dwerr=TRUE ${{ matrix.cmake-args }}"
|
||||
- name: test
|
||||
run: |
|
||||
${build_dir}/rippled --unittest --unittest-jobs $(nproc)
|
||||
cd ${build_dir}
|
||||
./rippled --unittest --unittest-jobs $(nproc)
|
||||
ctest -j $(nproc) --output-on-failure
|
||||
|
||||
reference-fee-test:
|
||||
strategy:
|
||||
@@ -217,8 +219,9 @@ jobs:
|
||||
cmake-args: "-Dassert=TRUE -Dwerr=TRUE ${{ matrix.cmake-args }}"
|
||||
- name: test
|
||||
run: |
|
||||
${build_dir}/rippled --unittest --unittest-jobs $(nproc)
|
||||
|
||||
cd ${build_dir}
|
||||
./rippled --unittest --unittest-jobs $(nproc)
|
||||
ctest -j $(nproc) --output-on-failure
|
||||
coverage:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -441,3 +444,4 @@ jobs:
|
||||
run: |
|
||||
cd ${BUILD_DIR}
|
||||
./rippled -u --unittest-jobs $(( $(nproc)/4 ))
|
||||
ctest -j $(nproc) --output-on-failure
|
||||
|
||||
5
.github/workflows/windows.yml
vendored
5
.github/workflows/windows.yml
vendored
@@ -95,5 +95,6 @@ jobs:
|
||||
shell: bash
|
||||
if: ${{ matrix.configuration.tests }}
|
||||
run: |
|
||||
${build_dir}/${{ matrix.configuration.type }}/rippled --unittest \
|
||||
--unittest-jobs $(nproc)
|
||||
cd ${build_dir}/${{ matrix.configuration.type }}
|
||||
./rippled --unittest --unittest-jobs $(nproc)
|
||||
ctest -j $(nproc) --output-on-failure
|
||||
|
||||
@@ -132,6 +132,7 @@ test.shamap > xrpl.protocol
|
||||
test.toplevel > test.csf
|
||||
test.toplevel > xrpl.json
|
||||
test.unit_test > xrpl.basics
|
||||
tests.libxrpl > xrpl.basics
|
||||
xrpl.json > xrpl.basics
|
||||
xrpl.protocol > xrpl.basics
|
||||
xrpl.protocol > xrpl.json
|
||||
|
||||
@@ -90,6 +90,11 @@ set_target_properties(OpenSSL::SSL PROPERTIES
|
||||
INTERFACE_COMPILE_DEFINITIONS OPENSSL_NO_SSL2
|
||||
)
|
||||
set(SECP256K1_INSTALL TRUE)
|
||||
set(SECP256K1_BUILD_BENCHMARK FALSE)
|
||||
set(SECP256K1_BUILD_TESTS FALSE)
|
||||
set(SECP256K1_BUILD_EXHAUSTIVE_TESTS FALSE)
|
||||
set(SECP256K1_BUILD_CTIME_TESTS FALSE)
|
||||
set(SECP256K1_BUILD_EXAMPLES FALSE)
|
||||
add_subdirectory(external/secp256k1)
|
||||
add_library(secp256k1::secp256k1 ALIAS secp256k1)
|
||||
add_subdirectory(external/ed25519-donna)
|
||||
@@ -144,3 +149,8 @@ set(PROJECT_EXPORT_SET RippleExports)
|
||||
include(RippledCore)
|
||||
include(RippledInstall)
|
||||
include(RippledValidatorKeys)
|
||||
|
||||
if(tests)
|
||||
include(CTest)
|
||||
add_subdirectory(src/tests/libxrpl)
|
||||
endif()
|
||||
|
||||
4817
RELEASENOTES.md
4817
RELEASENOTES.md
File diff suppressed because it is too large
Load Diff
41
cmake/xrpl_add_test.cmake
Normal file
41
cmake/xrpl_add_test.cmake
Normal file
@@ -0,0 +1,41 @@
|
||||
include(isolate_headers)
|
||||
|
||||
function(xrpl_add_test name)
|
||||
set(target ${PROJECT_NAME}.test.${name})
|
||||
|
||||
file(GLOB_RECURSE sources CONFIGURE_DEPENDS
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/${name}/*.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/${name}.cpp"
|
||||
)
|
||||
add_executable(${target} EXCLUDE_FROM_ALL ${ARGN} ${sources})
|
||||
|
||||
isolate_headers(
|
||||
${target}
|
||||
"${CMAKE_SOURCE_DIR}"
|
||||
"${CMAKE_SOURCE_DIR}/tests/${name}"
|
||||
PRIVATE
|
||||
)
|
||||
|
||||
# Make sure the test isn't optimized away in unity builds
|
||||
set_target_properties(${target} PROPERTIES
|
||||
UNITY_BUILD_MODE GROUP
|
||||
UNITY_BUILD_BATCH_SIZE 0) # Adjust as needed
|
||||
|
||||
add_test(NAME ${target} COMMAND ${target})
|
||||
set_tests_properties(
|
||||
${target} PROPERTIES
|
||||
FIXTURES_REQUIRED ${target}_fixture
|
||||
)
|
||||
|
||||
add_test(
|
||||
NAME ${target}.build
|
||||
COMMAND
|
||||
${CMAKE_COMMAND}
|
||||
--build ${CMAKE_BINARY_DIR}
|
||||
--config $<CONFIG>
|
||||
--target ${target}
|
||||
)
|
||||
set_tests_properties(${target}.build PROPERTIES
|
||||
FIXTURES_SETUP ${target}_fixture
|
||||
)
|
||||
endfunction()
|
||||
@@ -24,6 +24,7 @@ class Xrpl(ConanFile):
|
||||
}
|
||||
|
||||
requires = [
|
||||
'doctest/2.4.11',
|
||||
'grpc/1.50.1',
|
||||
'libarchive/3.7.6',
|
||||
'nudb/2.0.8',
|
||||
|
||||
@@ -55,6 +55,18 @@
|
||||
* `VoteBehavior::DefaultYes`. The communication process is beyond
|
||||
* the scope of these instructions.
|
||||
*
|
||||
* 5) A feature marked as Obsolete can mean either:
|
||||
* 1) It is in the ledger (marked as Supported::yes) and it is on its way to
|
||||
* become Retired
|
||||
* 2) The feature is not in the ledger (has always been marked as
|
||||
* Supported::no) and the code to support it has been removed
|
||||
*
|
||||
* If we want to discontinue a feature that we've never fully supported and
|
||||
* the feature has never been enabled, we should remove all the related
|
||||
* code, and mark the feature as "abandoned". To do this:
|
||||
*
|
||||
* 1) Open features.macro, move the feature to the abandoned section and
|
||||
* change the macro to XRPL_ABANDON
|
||||
*
|
||||
* When a feature has been enabled for several years, the conditional code
|
||||
* may be removed, and the feature "retired". To retire a feature:
|
||||
@@ -88,10 +100,13 @@ namespace detail {
|
||||
#undef XRPL_FIX
|
||||
#pragma push_macro("XRPL_RETIRE")
|
||||
#undef XRPL_RETIRE
|
||||
#pragma push_macro("XRPL_ABANDON")
|
||||
#undef XRPL_ABANDON
|
||||
|
||||
#define XRPL_FEATURE(name, supported, vote) +1
|
||||
#define XRPL_FIX(name, supported, vote) +1
|
||||
#define XRPL_RETIRE(name) +1
|
||||
#define XRPL_ABANDON(name) +1
|
||||
|
||||
// This value SHOULD be equal to the number of amendments registered in
|
||||
// Feature.cpp. Because it's only used to reserve storage, and determine how
|
||||
@@ -108,6 +123,8 @@ static constexpr std::size_t numFeatures =
|
||||
#pragma pop_macro("XRPL_FIX")
|
||||
#undef XRPL_FEATURE
|
||||
#pragma pop_macro("XRPL_FEATURE")
|
||||
#undef XRPL_ABANDON
|
||||
#pragma pop_macro("XRPL_ABANDON")
|
||||
|
||||
/** Amendments that this server supports and the default voting behavior.
|
||||
Whether they are enabled depends on the Rules defined in the validated
|
||||
@@ -349,10 +366,13 @@ foreachFeature(FeatureBitset bs, F&& f)
|
||||
#undef XRPL_FIX
|
||||
#pragma push_macro("XRPL_RETIRE")
|
||||
#undef XRPL_RETIRE
|
||||
#pragma push_macro("XRPL_ABANDON")
|
||||
#undef XRPL_ABANDON
|
||||
|
||||
#define XRPL_FEATURE(name, supported, vote) extern uint256 const feature##name;
|
||||
#define XRPL_FIX(name, supported, vote) extern uint256 const fix##name;
|
||||
#define XRPL_RETIRE(name)
|
||||
#define XRPL_ABANDON(name)
|
||||
|
||||
#include <xrpl/protocol/detail/features.macro>
|
||||
|
||||
@@ -362,6 +382,8 @@ foreachFeature(FeatureBitset bs, F&& f)
|
||||
#pragma pop_macro("XRPL_FIX")
|
||||
#undef XRPL_FEATURE
|
||||
#pragma pop_macro("XRPL_FEATURE")
|
||||
#undef XRPL_ABANDON
|
||||
#pragma pop_macro("XRPL_ABANDON")
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
|
||||
namespace ripple {
|
||||
|
||||
namespace RPC {
|
||||
|
||||
/**
|
||||
Adds common synthetic fields to transaction-related JSON responses
|
||||
|
||||
@@ -40,6 +42,7 @@ insertNFTSyntheticInJson(
|
||||
TxMeta const&);
|
||||
/** @} */
|
||||
|
||||
} // namespace RPC
|
||||
} // namespace ripple
|
||||
|
||||
#endif
|
||||
|
||||
@@ -26,6 +26,9 @@
|
||||
#if !defined(XRPL_RETIRE)
|
||||
#error "undefined macro: XRPL_RETIRE"
|
||||
#endif
|
||||
#if !defined(XRPL_ABANDON)
|
||||
#error "undefined macro: XRPL_ABANDON"
|
||||
#endif
|
||||
|
||||
// Add new amendments to the top of this list.
|
||||
// Keep it sorted in reverse chronological order.
|
||||
@@ -114,7 +117,6 @@ XRPL_FEATURE(DepositAuth, Supported::yes, VoteBehavior::DefaultYe
|
||||
XRPL_FIX (1513, Supported::yes, VoteBehavior::DefaultYes)
|
||||
XRPL_FEATURE(FlowCross, Supported::yes, VoteBehavior::DefaultYes)
|
||||
XRPL_FEATURE(Flow, Supported::yes, VoteBehavior::DefaultYes)
|
||||
XRPL_FEATURE(OwnerPaysFee, Supported::no, VoteBehavior::DefaultNo)
|
||||
|
||||
// The following amendments are obsolete, but must remain supported
|
||||
// because they could potentially get enabled.
|
||||
@@ -132,6 +134,11 @@ XRPL_FIX (NFTokenDirV1, Supported::yes, VoteBehavior::Obsolete)
|
||||
XRPL_FEATURE(NonFungibleTokensV1, Supported::yes, VoteBehavior::Obsolete)
|
||||
XRPL_FEATURE(CryptoConditionsSuite, Supported::yes, VoteBehavior::Obsolete)
|
||||
|
||||
// The following amendments were never supported, never enabled, and
|
||||
// we've abanded them. These features should never be in the ledger,
|
||||
// and we've removed all the related code.
|
||||
XRPL_ABANDON(OwnerPaysFee)
|
||||
|
||||
// The following amendments have been active for at least two years. Their
|
||||
// pre-amendment code has been removed and the identifiers are deprecated.
|
||||
// All known amendments and amendments that may appear in a validated
|
||||
|
||||
@@ -254,7 +254,7 @@ FeatureCollections::registerFeature(
|
||||
{
|
||||
check(!readOnly, "Attempting to register a feature after startup.");
|
||||
check(
|
||||
support == Supported::yes || vote == VoteBehavior::DefaultNo,
|
||||
support == Supported::yes || vote != VoteBehavior::DefaultYes,
|
||||
"Invalid feature parameters. Must be supported to be up-voted.");
|
||||
Feature const* i = getByName(name);
|
||||
if (!i)
|
||||
@@ -268,7 +268,7 @@ FeatureCollections::registerFeature(
|
||||
features.emplace_back(name, f);
|
||||
|
||||
auto const getAmendmentSupport = [=]() {
|
||||
if (vote == VoteBehavior::Obsolete)
|
||||
if (vote == VoteBehavior::Obsolete && support == Supported::yes)
|
||||
return AmendmentSupport::Retired;
|
||||
return support == Supported::yes ? AmendmentSupport::Supported
|
||||
: AmendmentSupport::Unsupported;
|
||||
@@ -398,6 +398,14 @@ retireFeature(std::string const& name)
|
||||
return registerFeature(name, Supported::yes, VoteBehavior::Obsolete);
|
||||
}
|
||||
|
||||
// Abandoned features are not in the ledger and have no code controlled by the
|
||||
// feature. They were never supported, and cannot be voted on.
|
||||
uint256
|
||||
abandonFeature(std::string const& name)
|
||||
{
|
||||
return registerFeature(name, Supported::no, VoteBehavior::Obsolete);
|
||||
}
|
||||
|
||||
/** Tell FeatureCollections when registration is complete. */
|
||||
bool
|
||||
registrationIsDone()
|
||||
@@ -432,6 +440,8 @@ featureToName(uint256 const& f)
|
||||
#undef XRPL_FIX
|
||||
#pragma push_macro("XRPL_RETIRE")
|
||||
#undef XRPL_RETIRE
|
||||
#pragma push_macro("XRPL_ABANDON")
|
||||
#undef XRPL_ABANDON
|
||||
|
||||
#define XRPL_FEATURE(name, supported, vote) \
|
||||
uint256 const feature##name = registerFeature(#name, supported, vote);
|
||||
@@ -443,6 +453,11 @@ featureToName(uint256 const& f)
|
||||
[[deprecated("The referenced amendment has been retired")]] \
|
||||
[[maybe_unused]] \
|
||||
uint256 const retired##name = retireFeature(#name);
|
||||
|
||||
#define XRPL_ABANDON(name) \
|
||||
[[deprecated("The referenced amendment has been abandoned")]] \
|
||||
[[maybe_unused]] \
|
||||
uint256 const abandoned##name = abandonFeature(#name);
|
||||
// clang-format on
|
||||
|
||||
#include <xrpl/protocol/detail/features.macro>
|
||||
@@ -453,6 +468,8 @@ featureToName(uint256 const& f)
|
||||
#pragma pop_macro("XRPL_FIX")
|
||||
#undef XRPL_FEATURE
|
||||
#pragma pop_macro("XRPL_FEATURE")
|
||||
#undef XRPL_ABANDON
|
||||
#pragma pop_macro("XRPL_ABANDON")
|
||||
|
||||
// All of the features should now be registered, since variables in a cpp file
|
||||
// are initialized from top to bottom.
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <memory>
|
||||
|
||||
namespace ripple {
|
||||
namespace RPC {
|
||||
|
||||
void
|
||||
insertNFTSyntheticInJson(
|
||||
@@ -39,4 +40,5 @@ insertNFTSyntheticInJson(
|
||||
insertNFTokenOfferID(response[jss::meta], transaction, transactionMeta);
|
||||
}
|
||||
|
||||
} // namespace RPC
|
||||
} // namespace ripple
|
||||
|
||||
@@ -1183,9 +1183,7 @@ private:
|
||||
|
||||
using namespace jtx;
|
||||
|
||||
// The problem was identified when featureOwnerPaysFee was enabled,
|
||||
// so make sure that gets included.
|
||||
Env env{*this, features | featureOwnerPaysFee};
|
||||
Env env{*this, features};
|
||||
|
||||
// The fee that's charged for transactions.
|
||||
auto const fee = env.current()->fees().base;
|
||||
@@ -2217,271 +2215,6 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testTransferRate(FeatureBitset features)
|
||||
{
|
||||
testcase("Transfer Rate");
|
||||
|
||||
using namespace jtx;
|
||||
|
||||
{
|
||||
// transfer fee on AMM
|
||||
Env env(*this, features);
|
||||
|
||||
fund(env, gw, {alice, bob, carol}, XRP(10'000), {USD(1'000)});
|
||||
env(rate(gw, 1.25));
|
||||
env.close();
|
||||
|
||||
AMM ammBob(env, bob, XRP(100), USD(150));
|
||||
// no transfer fee on create
|
||||
BEAST_EXPECT(expectLine(env, bob, USD(1000 - 150)));
|
||||
|
||||
env(pay(alice, carol, USD(50)), path(~USD), sendmax(XRP(50)));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(expectLine(env, bob, USD(1'000 - 150)));
|
||||
BEAST_EXPECT(
|
||||
ammBob.expectBalances(XRP(150), USD(100), ammBob.tokens()));
|
||||
BEAST_EXPECT(expectLedgerEntryRoot(
|
||||
env, alice, xrpMinusFee(env, 10'000 - 50)));
|
||||
BEAST_EXPECT(expectLine(env, carol, USD(1'050)));
|
||||
}
|
||||
|
||||
{
|
||||
// Transfer fee AMM and offer
|
||||
Env env(*this, features);
|
||||
|
||||
fund(
|
||||
env,
|
||||
gw,
|
||||
{alice, bob, carol},
|
||||
XRP(10'000),
|
||||
{USD(1'000), EUR(1'000)});
|
||||
env(rate(gw, 1.25));
|
||||
env.close();
|
||||
|
||||
AMM ammBob(env, bob, XRP(100), USD(140));
|
||||
BEAST_EXPECT(expectLine(env, bob, USD(1'000 - 140)));
|
||||
|
||||
env(offer(bob, USD(50), EUR(50)));
|
||||
|
||||
// alice buys 40EUR with 40XRP
|
||||
env(pay(alice, carol, EUR(40)), path(~USD, ~EUR), sendmax(XRP(40)));
|
||||
|
||||
// 40XRP is swapped in for 40USD
|
||||
BEAST_EXPECT(
|
||||
ammBob.expectBalances(XRP(140), USD(100), ammBob.tokens()));
|
||||
// 40USD buys 40EUR via bob's offer. 40EUR delivered to carol
|
||||
// and bob pays 25% on 40EUR, 40EUR*0.25=10EUR
|
||||
BEAST_EXPECT(expectLine(env, bob, EUR(1'000 - 40 - 40 * 0.25)));
|
||||
// bob gets 40USD back from the offer
|
||||
BEAST_EXPECT(expectLine(env, bob, USD(1'000 - 140 + 40)));
|
||||
BEAST_EXPECT(expectLedgerEntryRoot(
|
||||
env, alice, xrpMinusFee(env, 10'000 - 40)));
|
||||
BEAST_EXPECT(expectLine(env, carol, EUR(1'040)));
|
||||
BEAST_EXPECT(expectOffers(env, bob, 1, {{USD(10), EUR(10)}}));
|
||||
}
|
||||
|
||||
{
|
||||
// Transfer fee two consecutive AMM
|
||||
Env env(*this, features);
|
||||
|
||||
fund(
|
||||
env,
|
||||
gw,
|
||||
{alice, bob, carol},
|
||||
XRP(10'000),
|
||||
{USD(1'000), EUR(1'000)});
|
||||
env(rate(gw, 1.25));
|
||||
env.close();
|
||||
|
||||
AMM ammBobXRP_USD(env, bob, XRP(100), USD(140));
|
||||
BEAST_EXPECT(expectLine(env, bob, USD(1'000 - 140)));
|
||||
|
||||
AMM ammBobUSD_EUR(env, bob, USD(100), EUR(140));
|
||||
BEAST_EXPECT(expectLine(env, bob, EUR(1'000 - 140)));
|
||||
BEAST_EXPECT(expectLine(env, bob, USD(1'000 - 140 - 100)));
|
||||
|
||||
// alice buys 40EUR with 40XRP
|
||||
env(pay(alice, carol, EUR(40)), path(~USD, ~EUR), sendmax(XRP(40)));
|
||||
|
||||
// 40XRP is swapped in for 40USD
|
||||
BEAST_EXPECT(ammBobXRP_USD.expectBalances(
|
||||
XRP(140), USD(100), ammBobXRP_USD.tokens()));
|
||||
// 40USD is swapped in for 40EUR
|
||||
BEAST_EXPECT(ammBobUSD_EUR.expectBalances(
|
||||
USD(140), EUR(100), ammBobUSD_EUR.tokens()));
|
||||
// no other charges on bob
|
||||
BEAST_EXPECT(expectLine(env, bob, USD(1'000 - 140 - 100)));
|
||||
BEAST_EXPECT(expectLine(env, bob, EUR(1'000 - 140)));
|
||||
BEAST_EXPECT(expectLedgerEntryRoot(
|
||||
env, alice, xrpMinusFee(env, 10'000 - 40)));
|
||||
BEAST_EXPECT(expectLine(env, carol, EUR(1'040)));
|
||||
}
|
||||
|
||||
{
|
||||
// Payment via AMM with limit quality, deliver less
|
||||
// than requested
|
||||
Env env(*this, features);
|
||||
|
||||
fund(
|
||||
env,
|
||||
gw,
|
||||
{alice, bob, carol},
|
||||
XRP(1'000),
|
||||
{USD(1'200), GBP(1'200)});
|
||||
env(rate(gw, 1.25));
|
||||
env.close();
|
||||
|
||||
AMM amm(env, bob, GBP(1'000), USD(1'100));
|
||||
|
||||
// requested quality limit is 90USD/110GBP = 0.8181
|
||||
// trade quality is 77.2727USD/94.4444GBP = 0.8181
|
||||
env(pay(alice, carol, USD(90)),
|
||||
path(~USD),
|
||||
sendmax(GBP(110)),
|
||||
txflags(tfNoRippleDirect | tfPartialPayment | tfLimitQuality));
|
||||
env.close();
|
||||
|
||||
if (!features[fixAMMv1_1])
|
||||
{
|
||||
// alice buys 77.2727USD with 75.5555GBP and pays 25% tr fee
|
||||
// on 75.5555GBP
|
||||
// 1,200 - 75.55555*1.25 = 1200 - 94.4444 = 1105.55555GBP
|
||||
BEAST_EXPECT(expectLine(
|
||||
env,
|
||||
alice,
|
||||
STAmount{GBP, UINT64_C(1'105'555555555555), -12}));
|
||||
// 75.5555GBP is swapped in for 77.7272USD
|
||||
BEAST_EXPECT(amm.expectBalances(
|
||||
STAmount{GBP, UINT64_C(1'075'555555555556), -12},
|
||||
STAmount{USD, UINT64_C(1'022'727272727272), -12},
|
||||
amm.tokens()));
|
||||
}
|
||||
else
|
||||
{
|
||||
// alice buys 77.2727USD with 75.5555GBP and pays 25% tr fee
|
||||
// on 75.5555GBP
|
||||
// 1,200 - 75.55555*1.25 = 1200 - 94.4444 = 1105.55555GBP
|
||||
BEAST_EXPECT(expectLine(
|
||||
env,
|
||||
alice,
|
||||
STAmount{GBP, UINT64_C(1'105'555555555554), -12}));
|
||||
// 75.5555GBP is swapped in for 77.7272USD
|
||||
BEAST_EXPECT(amm.expectBalances(
|
||||
STAmount{GBP, UINT64_C(1'075'555555555557), -12},
|
||||
STAmount{USD, UINT64_C(1'022'727272727272), -12},
|
||||
amm.tokens()));
|
||||
}
|
||||
BEAST_EXPECT(expectLine(
|
||||
env, carol, STAmount{USD, UINT64_C(1'277'272727272728), -12}));
|
||||
}
|
||||
|
||||
{
|
||||
// AMM offer crossing
|
||||
Env env(*this, features);
|
||||
|
||||
fund(env, gw, {alice, bob}, XRP(1'000), {USD(1'200), EUR(1'200)});
|
||||
env(rate(gw, 1.25));
|
||||
env.close();
|
||||
|
||||
AMM amm(env, bob, USD(1'000), EUR(1'150));
|
||||
|
||||
env(offer(alice, EUR(100), USD(100)));
|
||||
env.close();
|
||||
|
||||
if (!features[fixAMMv1_1])
|
||||
{
|
||||
// 95.2380USD is swapped in for 100EUR
|
||||
BEAST_EXPECT(amm.expectBalances(
|
||||
STAmount{USD, UINT64_C(1'095'238095238095), -12},
|
||||
EUR(1'050),
|
||||
amm.tokens()));
|
||||
// alice pays 25% tr fee on 95.2380USD
|
||||
// 1200-95.2380*1.25 = 1200 - 119.0477 = 1080.9523USD
|
||||
BEAST_EXPECT(expectLine(
|
||||
env,
|
||||
alice,
|
||||
STAmount{USD, UINT64_C(1'080'952380952381), -12},
|
||||
EUR(1'300)));
|
||||
}
|
||||
else
|
||||
{
|
||||
// 95.2380USD is swapped in for 100EUR
|
||||
BEAST_EXPECT(amm.expectBalances(
|
||||
STAmount{USD, UINT64_C(1'095'238095238096), -12},
|
||||
EUR(1'050),
|
||||
amm.tokens()));
|
||||
// alice pays 25% tr fee on 95.2380USD
|
||||
// 1200-95.2380*1.25 = 1200 - 119.0477 = 1080.9523USD
|
||||
BEAST_EXPECT(expectLine(
|
||||
env,
|
||||
alice,
|
||||
STAmount{USD, UINT64_C(1'080'95238095238), -11},
|
||||
EUR(1'300)));
|
||||
}
|
||||
BEAST_EXPECT(expectOffers(env, alice, 0));
|
||||
}
|
||||
|
||||
{
|
||||
// First pass through a strand redeems, second pass issues,
|
||||
// through an offer limiting step is not an endpoint
|
||||
Env env(*this, features);
|
||||
auto const USDA = alice["USD"];
|
||||
auto const USDB = bob["USD"];
|
||||
Account const dan("dan");
|
||||
|
||||
env.fund(XRP(10'000), bob, carol, dan, gw);
|
||||
fund(env, {alice}, XRP(10'000));
|
||||
env(rate(gw, 1.25));
|
||||
env.trust(USD(2'000), alice, bob, carol, dan);
|
||||
env.trust(EUR(2'000), carol, dan);
|
||||
env.trust(USDA(1'000), bob);
|
||||
env.trust(USDB(1'000), gw);
|
||||
env(pay(gw, bob, USD(50)));
|
||||
env(pay(gw, dan, EUR(1'050)));
|
||||
env(pay(gw, dan, USD(1'000)));
|
||||
AMM ammDan(env, dan, USD(1'000), EUR(1'050));
|
||||
|
||||
if (!features[fixAMMv1_1])
|
||||
{
|
||||
// alice -> bob -> gw -> carol. $50 should have transfer fee;
|
||||
// $10, no fee
|
||||
env(pay(alice, carol, EUR(50)),
|
||||
path(bob, gw, ~EUR),
|
||||
sendmax(USDA(60)),
|
||||
txflags(tfNoRippleDirect));
|
||||
BEAST_EXPECT(ammDan.expectBalances(
|
||||
USD(1'050), EUR(1'000), ammDan.tokens()));
|
||||
BEAST_EXPECT(expectLine(env, dan, USD(0)));
|
||||
BEAST_EXPECT(expectLine(env, dan, EUR(0)));
|
||||
BEAST_EXPECT(expectLine(env, bob, USD(-10)));
|
||||
BEAST_EXPECT(expectLine(env, bob, USDA(60)));
|
||||
BEAST_EXPECT(expectLine(env, carol, EUR(50)));
|
||||
}
|
||||
else
|
||||
{
|
||||
// alice -> bob -> gw -> carol. $50 should have transfer fee;
|
||||
// $10, no fee
|
||||
env(pay(alice, carol, EUR(50)),
|
||||
path(bob, gw, ~EUR),
|
||||
sendmax(USDA(60.1)),
|
||||
txflags(tfNoRippleDirect));
|
||||
BEAST_EXPECT(ammDan.expectBalances(
|
||||
STAmount{USD, UINT64_C(1'050'000000000001), -12},
|
||||
EUR(1'000),
|
||||
ammDan.tokens()));
|
||||
BEAST_EXPECT(expectLine(env, dan, USD(0)));
|
||||
BEAST_EXPECT(expectLine(env, dan, EUR(0)));
|
||||
BEAST_EXPECT(expectLine(
|
||||
env, bob, STAmount{USD, INT64_C(-10'000000000001), -12}));
|
||||
BEAST_EXPECT(expectLine(
|
||||
env, bob, STAmount{USDA, UINT64_C(60'000000000001), -12}));
|
||||
BEAST_EXPECT(expectLine(env, carol, EUR(50)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testTransferRateNoOwnerFee(FeatureBitset features)
|
||||
{
|
||||
@@ -4057,13 +3790,9 @@ private:
|
||||
{
|
||||
using namespace jtx;
|
||||
FeatureBitset const all{supported_amendments()};
|
||||
FeatureBitset const ownerPaysFee{featureOwnerPaysFee};
|
||||
|
||||
testFalseDry(all);
|
||||
testBookStep(all);
|
||||
testBookStep(all | ownerPaysFee);
|
||||
testTransferRate(all | ownerPaysFee);
|
||||
testTransferRate((all - fixAMMv1_1 - fixAMMv1_3) | ownerPaysFee);
|
||||
testTransferRateNoOwnerFee(all);
|
||||
testTransferRateNoOwnerFee(all - fixAMMv1_1 - fixAMMv1_3);
|
||||
testLimitQuality();
|
||||
|
||||
@@ -599,158 +599,18 @@ struct Flow_test : public beast::unit_test::suite
|
||||
Account const bob("bob");
|
||||
Account const carol("carol");
|
||||
|
||||
{
|
||||
// Simple payment through a gateway with a
|
||||
// transfer rate
|
||||
Env env(*this, features);
|
||||
// Offer where the owner is also the issuer, sender pays fee
|
||||
Env env(*this, features);
|
||||
|
||||
env.fund(XRP(10000), alice, bob, carol, gw);
|
||||
env.close();
|
||||
env(rate(gw, 1.25));
|
||||
env.trust(USD(1000), alice, bob, carol);
|
||||
env(pay(gw, alice, USD(50)));
|
||||
env.require(balance(alice, USD(50)));
|
||||
env(pay(alice, bob, USD(40)), sendmax(USD(50)));
|
||||
env.require(balance(bob, USD(40)), balance(alice, USD(0)));
|
||||
}
|
||||
{
|
||||
// transfer rate is not charged when issuer is src or dst
|
||||
Env env(*this, features);
|
||||
|
||||
env.fund(XRP(10000), alice, bob, carol, gw);
|
||||
env.close();
|
||||
env(rate(gw, 1.25));
|
||||
env.trust(USD(1000), alice, bob, carol);
|
||||
env(pay(gw, alice, USD(50)));
|
||||
env.require(balance(alice, USD(50)));
|
||||
env(pay(alice, gw, USD(40)), sendmax(USD(40)));
|
||||
env.require(balance(alice, USD(10)));
|
||||
}
|
||||
{
|
||||
// transfer fee on an offer
|
||||
Env env(*this, features);
|
||||
|
||||
env.fund(XRP(10000), alice, bob, carol, gw);
|
||||
env.close();
|
||||
env(rate(gw, 1.25));
|
||||
env.trust(USD(1000), alice, bob, carol);
|
||||
env(pay(gw, bob, USD(65)));
|
||||
|
||||
env(offer(bob, XRP(50), USD(50)));
|
||||
|
||||
env(pay(alice, carol, USD(50)), path(~USD), sendmax(XRP(50)));
|
||||
env.require(
|
||||
balance(alice, xrpMinusFee(env, 10000 - 50)),
|
||||
balance(bob, USD(2.5)), // owner pays transfer fee
|
||||
balance(carol, USD(50)));
|
||||
}
|
||||
|
||||
{
|
||||
// Transfer fee two consecutive offers
|
||||
Env env(*this, features);
|
||||
|
||||
env.fund(XRP(10000), alice, bob, carol, gw);
|
||||
env.close();
|
||||
env(rate(gw, 1.25));
|
||||
env.trust(USD(1000), alice, bob, carol);
|
||||
env.trust(EUR(1000), alice, bob, carol);
|
||||
env(pay(gw, bob, USD(50)));
|
||||
env(pay(gw, bob, EUR(50)));
|
||||
|
||||
env(offer(bob, XRP(50), USD(50)));
|
||||
env(offer(bob, USD(50), EUR(50)));
|
||||
|
||||
env(pay(alice, carol, EUR(40)), path(~USD, ~EUR), sendmax(XRP(40)));
|
||||
env.require(
|
||||
balance(alice, xrpMinusFee(env, 10000 - 40)),
|
||||
balance(bob, USD(40)),
|
||||
balance(bob, EUR(0)),
|
||||
balance(carol, EUR(40)));
|
||||
}
|
||||
|
||||
{
|
||||
// First pass through a strand redeems, second pass issues, no
|
||||
// offers limiting step is not an endpoint
|
||||
Env env(*this, features);
|
||||
auto const USDA = alice["USD"];
|
||||
auto const USDB = bob["USD"];
|
||||
|
||||
env.fund(XRP(10000), alice, bob, carol, gw);
|
||||
env.close();
|
||||
env(rate(gw, 1.25));
|
||||
env.trust(USD(1000), alice, bob, carol);
|
||||
env.trust(USDA(1000), bob);
|
||||
env.trust(USDB(1000), gw);
|
||||
env(pay(gw, bob, USD(50)));
|
||||
// alice -> bob -> gw -> carol. $50 should have transfer fee; $10,
|
||||
// no fee
|
||||
env(pay(alice, carol, USD(50)), path(bob), sendmax(USDA(60)));
|
||||
env.require(
|
||||
balance(bob, USD(-10)),
|
||||
balance(bob, USDA(60)),
|
||||
balance(carol, USD(50)));
|
||||
}
|
||||
{
|
||||
// First pass through a strand redeems, second pass issues, through
|
||||
// an offer limiting step is not an endpoint
|
||||
Env env(*this, features);
|
||||
auto const USDA = alice["USD"];
|
||||
auto const USDB = bob["USD"];
|
||||
Account const dan("dan");
|
||||
|
||||
env.fund(XRP(10000), alice, bob, carol, dan, gw);
|
||||
env.close();
|
||||
env(rate(gw, 1.25));
|
||||
env.trust(USD(1000), alice, bob, carol, dan);
|
||||
env.trust(EUR(1000), carol, dan);
|
||||
env.trust(USDA(1000), bob);
|
||||
env.trust(USDB(1000), gw);
|
||||
env(pay(gw, bob, USD(50)));
|
||||
env(pay(gw, dan, EUR(100)));
|
||||
env(offer(dan, USD(100), EUR(100)));
|
||||
// alice -> bob -> gw -> carol. $50 should have transfer fee; $10,
|
||||
// no fee
|
||||
env(pay(alice, carol, EUR(50)),
|
||||
path(bob, gw, ~EUR),
|
||||
sendmax(USDA(60)),
|
||||
txflags(tfNoRippleDirect));
|
||||
env.require(
|
||||
balance(bob, USD(-10)),
|
||||
balance(bob, USDA(60)),
|
||||
balance(dan, USD(50)),
|
||||
balance(dan, EUR(37.5)),
|
||||
balance(carol, EUR(50)));
|
||||
}
|
||||
|
||||
{
|
||||
// Offer where the owner is also the issuer, owner pays fee
|
||||
Env env(*this, features);
|
||||
|
||||
env.fund(XRP(10000), alice, bob, gw);
|
||||
env.close();
|
||||
env(rate(gw, 1.25));
|
||||
env.trust(USD(1000), alice, bob);
|
||||
env(offer(gw, XRP(100), USD(100)));
|
||||
env(pay(alice, bob, USD(100)), sendmax(XRP(100)));
|
||||
env.require(
|
||||
balance(alice, xrpMinusFee(env, 10000 - 100)),
|
||||
balance(bob, USD(100)));
|
||||
}
|
||||
if (!features[featureOwnerPaysFee])
|
||||
{
|
||||
// Offer where the owner is also the issuer, sender pays fee
|
||||
Env env(*this, features);
|
||||
|
||||
env.fund(XRP(10000), alice, bob, gw);
|
||||
env.close();
|
||||
env(rate(gw, 1.25));
|
||||
env.trust(USD(1000), alice, bob);
|
||||
env(offer(gw, XRP(125), USD(125)));
|
||||
env(pay(alice, bob, USD(100)), sendmax(XRP(200)));
|
||||
env.require(
|
||||
balance(alice, xrpMinusFee(env, 10000 - 125)),
|
||||
balance(bob, USD(100)));
|
||||
}
|
||||
env.fund(XRP(10000), alice, bob, gw);
|
||||
env.close();
|
||||
env(rate(gw, 1.25));
|
||||
env.trust(USD(1000), alice, bob);
|
||||
env(offer(gw, XRP(125), USD(125)));
|
||||
env(pay(alice, bob, USD(100)), sendmax(XRP(200)));
|
||||
env.require(
|
||||
balance(alice, xrpMinusFee(env, 10000 - 125)),
|
||||
balance(bob, USD(100)));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -1445,7 +1305,6 @@ struct Flow_test : public beast::unit_test::suite
|
||||
testWithFeats(FeatureBitset features)
|
||||
{
|
||||
using namespace jtx;
|
||||
FeatureBitset const ownerPaysFee{featureOwnerPaysFee};
|
||||
FeatureBitset const reducedOffersV2(fixReducedOffersV2);
|
||||
|
||||
testLineQuality(features);
|
||||
@@ -1453,9 +1312,7 @@ struct Flow_test : public beast::unit_test::suite
|
||||
testBookStep(features - reducedOffersV2);
|
||||
testDirectStep(features);
|
||||
testBookStep(features);
|
||||
testDirectStep(features | ownerPaysFee);
|
||||
testBookStep(features | ownerPaysFee);
|
||||
testTransferRate(features | ownerPaysFee);
|
||||
testTransferRate(features);
|
||||
testSelfPayment1(features);
|
||||
testSelfPayment2(features);
|
||||
testSelfFundedXRPEndpoint(false, features);
|
||||
|
||||
@@ -3643,9 +3643,7 @@ public:
|
||||
|
||||
using namespace jtx;
|
||||
|
||||
// The problem was identified when featureOwnerPaysFee was enabled,
|
||||
// so make sure that gets included.
|
||||
Env env{*this, features | featureOwnerPaysFee};
|
||||
Env env{*this, features};
|
||||
|
||||
// The fee that's charged for transactions.
|
||||
auto const fee = env.current()->fees().base;
|
||||
|
||||
@@ -264,7 +264,7 @@ class TheoreticalQuality_test : public beast::unit_test::suite
|
||||
sendMaxIssue,
|
||||
rcp.paths,
|
||||
/*defaultPaths*/ rcp.paths.empty(),
|
||||
sb.rules().enabled(featureOwnerPaysFee),
|
||||
false,
|
||||
OfferCrossing::no,
|
||||
ammContext,
|
||||
std::nullopt,
|
||||
|
||||
@@ -1,144 +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/RangeSet.h>
|
||||
#include <xrpl/beast/unit_test.h>
|
||||
|
||||
namespace ripple {
|
||||
class RangeSet_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
void
|
||||
testPrevMissing()
|
||||
{
|
||||
testcase("prevMissing");
|
||||
|
||||
// Set will include:
|
||||
// [ 0, 5]
|
||||
// [10,15]
|
||||
// [20,25]
|
||||
// etc...
|
||||
|
||||
RangeSet<std::uint32_t> set;
|
||||
for (std::uint32_t i = 0; i < 10; ++i)
|
||||
set.insert(range(10 * i, 10 * i + 5));
|
||||
|
||||
for (std::uint32_t i = 1; i < 100; ++i)
|
||||
{
|
||||
std::optional<std::uint32_t> expected;
|
||||
// no prev missing in domain for i <= 6
|
||||
if (i > 6)
|
||||
{
|
||||
std::uint32_t const oneBelowRange = (10 * (i / 10)) - 1;
|
||||
|
||||
expected = ((i % 10) > 6) ? (i - 1) : oneBelowRange;
|
||||
}
|
||||
BEAST_EXPECT(prevMissing(set, i) == expected);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testToString()
|
||||
{
|
||||
testcase("toString");
|
||||
|
||||
RangeSet<std::uint32_t> set;
|
||||
BEAST_EXPECT(to_string(set) == "empty");
|
||||
|
||||
set.insert(1);
|
||||
BEAST_EXPECT(to_string(set) == "1");
|
||||
|
||||
set.insert(range(4u, 6u));
|
||||
BEAST_EXPECT(to_string(set) == "1,4-6");
|
||||
|
||||
set.insert(2);
|
||||
BEAST_EXPECT(to_string(set) == "1-2,4-6");
|
||||
|
||||
set.erase(range(4u, 5u));
|
||||
BEAST_EXPECT(to_string(set) == "1-2,6");
|
||||
}
|
||||
|
||||
void
|
||||
testFromString()
|
||||
{
|
||||
testcase("fromString");
|
||||
|
||||
RangeSet<std::uint32_t> set;
|
||||
|
||||
BEAST_EXPECT(!from_string(set, ""));
|
||||
BEAST_EXPECT(boost::icl::length(set) == 0);
|
||||
|
||||
BEAST_EXPECT(!from_string(set, "#"));
|
||||
BEAST_EXPECT(boost::icl::length(set) == 0);
|
||||
|
||||
BEAST_EXPECT(!from_string(set, ","));
|
||||
BEAST_EXPECT(boost::icl::length(set) == 0);
|
||||
|
||||
BEAST_EXPECT(!from_string(set, ",-"));
|
||||
BEAST_EXPECT(boost::icl::length(set) == 0);
|
||||
|
||||
BEAST_EXPECT(!from_string(set, "1,,2"));
|
||||
BEAST_EXPECT(boost::icl::length(set) == 0);
|
||||
|
||||
BEAST_EXPECT(from_string(set, "1"));
|
||||
BEAST_EXPECT(boost::icl::length(set) == 1);
|
||||
BEAST_EXPECT(boost::icl::first(set) == 1);
|
||||
|
||||
BEAST_EXPECT(from_string(set, "1,1"));
|
||||
BEAST_EXPECT(boost::icl::length(set) == 1);
|
||||
BEAST_EXPECT(boost::icl::first(set) == 1);
|
||||
|
||||
BEAST_EXPECT(from_string(set, "1-1"));
|
||||
BEAST_EXPECT(boost::icl::length(set) == 1);
|
||||
BEAST_EXPECT(boost::icl::first(set) == 1);
|
||||
|
||||
BEAST_EXPECT(from_string(set, "1,4-6"));
|
||||
BEAST_EXPECT(boost::icl::length(set) == 4);
|
||||
BEAST_EXPECT(boost::icl::first(set) == 1);
|
||||
BEAST_EXPECT(!boost::icl::contains(set, 2));
|
||||
BEAST_EXPECT(!boost::icl::contains(set, 3));
|
||||
BEAST_EXPECT(boost::icl::contains(set, 4));
|
||||
BEAST_EXPECT(boost::icl::contains(set, 5));
|
||||
BEAST_EXPECT(boost::icl::last(set) == 6);
|
||||
|
||||
BEAST_EXPECT(from_string(set, "1-2,4-6"));
|
||||
BEAST_EXPECT(boost::icl::length(set) == 5);
|
||||
BEAST_EXPECT(boost::icl::first(set) == 1);
|
||||
BEAST_EXPECT(boost::icl::contains(set, 2));
|
||||
BEAST_EXPECT(boost::icl::contains(set, 4));
|
||||
BEAST_EXPECT(boost::icl::last(set) == 6);
|
||||
|
||||
BEAST_EXPECT(from_string(set, "1-2,6"));
|
||||
BEAST_EXPECT(boost::icl::length(set) == 3);
|
||||
BEAST_EXPECT(boost::icl::first(set) == 1);
|
||||
BEAST_EXPECT(boost::icl::contains(set, 2));
|
||||
BEAST_EXPECT(boost::icl::last(set) == 6);
|
||||
}
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testPrevMissing();
|
||||
testToString();
|
||||
testFromString();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(RangeSet, ripple_basics, ripple);
|
||||
|
||||
} // namespace ripple
|
||||
@@ -1,116 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github0.com/ripple/rippled
|
||||
Copyright (c) 2012-2016 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <xrpl/basics/Slice.h>
|
||||
#include <xrpl/beast/unit_test.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
struct Slice_test : beast::unit_test::suite
|
||||
{
|
||||
void
|
||||
run() override
|
||||
{
|
||||
std::uint8_t const data[] = {
|
||||
0xa8, 0xa1, 0x38, 0x45, 0x23, 0xec, 0xe4, 0x23, 0x71, 0x6d, 0x2a,
|
||||
0x18, 0xb4, 0x70, 0xcb, 0xf5, 0xac, 0x2d, 0x89, 0x4d, 0x19, 0x9c,
|
||||
0xf0, 0x2c, 0x15, 0xd1, 0xf9, 0x9b, 0x66, 0xd2, 0x30, 0xd3};
|
||||
|
||||
{
|
||||
testcase("Equality & Inequality");
|
||||
|
||||
Slice const s0{};
|
||||
|
||||
BEAST_EXPECT(s0.size() == 0);
|
||||
BEAST_EXPECT(s0.data() == nullptr);
|
||||
BEAST_EXPECT(s0 == s0);
|
||||
|
||||
// Test slices of equal and unequal size pointing to same data:
|
||||
for (std::size_t i = 0; i != sizeof(data); ++i)
|
||||
{
|
||||
Slice const s1{data, i};
|
||||
|
||||
BEAST_EXPECT(s1.size() == i);
|
||||
BEAST_EXPECT(s1.data() != nullptr);
|
||||
|
||||
if (i == 0)
|
||||
BEAST_EXPECT(s1 == s0);
|
||||
else
|
||||
BEAST_EXPECT(s1 != s0);
|
||||
|
||||
for (std::size_t j = 0; j != sizeof(data); ++j)
|
||||
{
|
||||
Slice const s2{data, j};
|
||||
|
||||
if (i == j)
|
||||
BEAST_EXPECT(s1 == s2);
|
||||
else
|
||||
BEAST_EXPECT(s1 != s2);
|
||||
}
|
||||
}
|
||||
|
||||
// Test slices of equal size but pointing to different data:
|
||||
std::array<std::uint8_t, sizeof(data)> a;
|
||||
std::array<std::uint8_t, sizeof(data)> b;
|
||||
|
||||
for (std::size_t i = 0; i != sizeof(data); ++i)
|
||||
a[i] = b[i] = data[i];
|
||||
|
||||
BEAST_EXPECT(makeSlice(a) == makeSlice(b));
|
||||
b[7]++;
|
||||
BEAST_EXPECT(makeSlice(a) != makeSlice(b));
|
||||
a[7]++;
|
||||
BEAST_EXPECT(makeSlice(a) == makeSlice(b));
|
||||
}
|
||||
|
||||
{
|
||||
testcase("Indexing");
|
||||
|
||||
Slice const s{data, sizeof(data)};
|
||||
|
||||
for (std::size_t i = 0; i != sizeof(data); ++i)
|
||||
BEAST_EXPECT(s[i] == data[i]);
|
||||
}
|
||||
|
||||
{
|
||||
testcase("Advancing");
|
||||
|
||||
for (std::size_t i = 0; i < sizeof(data); ++i)
|
||||
{
|
||||
for (std::size_t j = 0; i + j < sizeof(data); ++j)
|
||||
{
|
||||
Slice s(data + i, sizeof(data) - i);
|
||||
s += j;
|
||||
|
||||
BEAST_EXPECT(s.data() == data + i + j);
|
||||
BEAST_EXPECT(s.size() == sizeof(data) - i - j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(Slice, ripple_basics, ripple);
|
||||
|
||||
} // namespace test
|
||||
} // namespace ripple
|
||||
@@ -1,82 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012-2018 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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
//
|
||||
// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#include <xrpl/basics/base64.h>
|
||||
#include <xrpl/beast/unit_test.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
class base64_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
void
|
||||
check(std::string const& in, std::string const& out)
|
||||
{
|
||||
auto const encoded = base64_encode(in);
|
||||
BEAST_EXPECT(encoded == out);
|
||||
BEAST_EXPECT(base64_decode(encoded) == in);
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
check("", "");
|
||||
check("f", "Zg==");
|
||||
check("fo", "Zm8=");
|
||||
check("foo", "Zm9v");
|
||||
check("foob", "Zm9vYg==");
|
||||
check("fooba", "Zm9vYmE=");
|
||||
check("foobar", "Zm9vYmFy");
|
||||
|
||||
check(
|
||||
"Man is distinguished, not only by his reason, but by this "
|
||||
"singular passion from "
|
||||
"other animals, which is a lust of the mind, that by a "
|
||||
"perseverance of delight "
|
||||
"in the continued and indefatigable generation of knowledge, "
|
||||
"exceeds the short "
|
||||
"vehemence of any carnal pleasure.",
|
||||
"TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dC"
|
||||
"BieSB0aGlz"
|
||||
"IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIG"
|
||||
"x1c3Qgb2Yg"
|
||||
"dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aG"
|
||||
"UgY29udGlu"
|
||||
"dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleG"
|
||||
"NlZWRzIHRo"
|
||||
"ZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=");
|
||||
|
||||
std::string const notBase64 = "not_base64!!";
|
||||
std::string const truncated = "not";
|
||||
BEAST_EXPECT(base64_decode(notBase64) == base64_decode(truncated));
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(base64, ripple_basics, ripple);
|
||||
|
||||
} // namespace ripple
|
||||
@@ -1,62 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012-2016 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <xrpl/basics/mulDiv.h>
|
||||
#include <xrpl/beast/unit_test.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
struct mulDiv_test : beast::unit_test::suite
|
||||
{
|
||||
void
|
||||
run() override
|
||||
{
|
||||
auto const max = std::numeric_limits<std::uint64_t>::max();
|
||||
std::uint64_t const max32 = std::numeric_limits<std::uint32_t>::max();
|
||||
|
||||
auto result = mulDiv(85, 20, 5);
|
||||
BEAST_EXPECT(result && *result == 340);
|
||||
result = mulDiv(20, 85, 5);
|
||||
BEAST_EXPECT(result && *result == 340);
|
||||
|
||||
result = mulDiv(0, max - 1, max - 3);
|
||||
BEAST_EXPECT(result && *result == 0);
|
||||
result = mulDiv(max - 1, 0, max - 3);
|
||||
BEAST_EXPECT(result && *result == 0);
|
||||
|
||||
result = mulDiv(max, 2, max / 2);
|
||||
BEAST_EXPECT(result && *result == 4);
|
||||
result = mulDiv(max, 1000, max / 1000);
|
||||
BEAST_EXPECT(result && *result == 1000000);
|
||||
result = mulDiv(max, 1000, max / 1001);
|
||||
BEAST_EXPECT(result && *result == 1001000);
|
||||
result = mulDiv(max32 + 1, max32 + 1, 5);
|
||||
BEAST_EXPECT(result && *result == 3689348814741910323);
|
||||
|
||||
// Overflow
|
||||
result = mulDiv(max - 1, max - 2, 5);
|
||||
BEAST_EXPECT(!result);
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(mulDiv, ripple_basics, ripple);
|
||||
|
||||
} // namespace test
|
||||
} // namespace ripple
|
||||
@@ -1,193 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github0.com/ripple/rippled
|
||||
Copyright (c) 2021 Ripple 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/scope.h>
|
||||
#include <xrpl/beast/unit_test.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
struct scope_test : beast::unit_test::suite
|
||||
{
|
||||
void
|
||||
test_scope_exit()
|
||||
{
|
||||
// scope_exit always executes the functor on destruction,
|
||||
// unless release() is called
|
||||
int i = 0;
|
||||
{
|
||||
scope_exit x{[&i]() { i = 1; }};
|
||||
}
|
||||
BEAST_EXPECT(i == 1);
|
||||
{
|
||||
scope_exit x{[&i]() { i = 2; }};
|
||||
x.release();
|
||||
}
|
||||
BEAST_EXPECT(i == 1);
|
||||
{
|
||||
scope_exit x{[&i]() { i += 2; }};
|
||||
auto x2 = std::move(x);
|
||||
}
|
||||
BEAST_EXPECT(i == 3);
|
||||
{
|
||||
scope_exit x{[&i]() { i = 4; }};
|
||||
x.release();
|
||||
auto x2 = std::move(x);
|
||||
}
|
||||
BEAST_EXPECT(i == 3);
|
||||
{
|
||||
try
|
||||
{
|
||||
scope_exit x{[&i]() { i = 5; }};
|
||||
throw 1;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}
|
||||
BEAST_EXPECT(i == 5);
|
||||
{
|
||||
try
|
||||
{
|
||||
scope_exit x{[&i]() { i = 6; }};
|
||||
x.release();
|
||||
throw 1;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}
|
||||
BEAST_EXPECT(i == 5);
|
||||
}
|
||||
|
||||
void
|
||||
test_scope_fail()
|
||||
{
|
||||
// scope_fail executes the functor on destruction only
|
||||
// if an exception is unwinding, unless release() is called
|
||||
int i = 0;
|
||||
{
|
||||
scope_fail x{[&i]() { i = 1; }};
|
||||
}
|
||||
BEAST_EXPECT(i == 0);
|
||||
{
|
||||
scope_fail x{[&i]() { i = 2; }};
|
||||
x.release();
|
||||
}
|
||||
BEAST_EXPECT(i == 0);
|
||||
{
|
||||
scope_fail x{[&i]() { i = 3; }};
|
||||
auto x2 = std::move(x);
|
||||
}
|
||||
BEAST_EXPECT(i == 0);
|
||||
{
|
||||
scope_fail x{[&i]() { i = 4; }};
|
||||
x.release();
|
||||
auto x2 = std::move(x);
|
||||
}
|
||||
BEAST_EXPECT(i == 0);
|
||||
{
|
||||
try
|
||||
{
|
||||
scope_fail x{[&i]() { i = 5; }};
|
||||
throw 1;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}
|
||||
BEAST_EXPECT(i == 5);
|
||||
{
|
||||
try
|
||||
{
|
||||
scope_fail x{[&i]() { i = 6; }};
|
||||
x.release();
|
||||
throw 1;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}
|
||||
BEAST_EXPECT(i == 5);
|
||||
}
|
||||
|
||||
void
|
||||
test_scope_success()
|
||||
{
|
||||
// scope_success executes the functor on destruction only
|
||||
// if an exception is not unwinding, unless release() is called
|
||||
int i = 0;
|
||||
{
|
||||
scope_success x{[&i]() { i = 1; }};
|
||||
}
|
||||
BEAST_EXPECT(i == 1);
|
||||
{
|
||||
scope_success x{[&i]() { i = 2; }};
|
||||
x.release();
|
||||
}
|
||||
BEAST_EXPECT(i == 1);
|
||||
{
|
||||
scope_success x{[&i]() { i += 2; }};
|
||||
auto x2 = std::move(x);
|
||||
}
|
||||
BEAST_EXPECT(i == 3);
|
||||
{
|
||||
scope_success x{[&i]() { i = 4; }};
|
||||
x.release();
|
||||
auto x2 = std::move(x);
|
||||
}
|
||||
BEAST_EXPECT(i == 3);
|
||||
{
|
||||
try
|
||||
{
|
||||
scope_success x{[&i]() { i = 5; }};
|
||||
throw 1;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}
|
||||
BEAST_EXPECT(i == 3);
|
||||
{
|
||||
try
|
||||
{
|
||||
scope_success x{[&i]() { i = 6; }};
|
||||
x.release();
|
||||
throw 1;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}
|
||||
BEAST_EXPECT(i == 3);
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
test_scope_exit();
|
||||
test_scope_fail();
|
||||
test_scope_success();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(scope, ripple_basics, ripple);
|
||||
|
||||
} // namespace test
|
||||
} // namespace ripple
|
||||
@@ -1,258 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright 2014, Nikolaos D. Bougalis <nikb@bougalis.net>
|
||||
|
||||
|
||||
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/tagged_integer.h>
|
||||
#include <xrpl/beast/unit_test.h>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
class tagged_integer_test : public beast::unit_test::suite
|
||||
{
|
||||
private:
|
||||
struct Tag1
|
||||
{
|
||||
};
|
||||
struct Tag2
|
||||
{
|
||||
};
|
||||
|
||||
// Static checks that types are not interoperable
|
||||
|
||||
using TagUInt1 = tagged_integer<std::uint32_t, Tag1>;
|
||||
using TagUInt2 = tagged_integer<std::uint32_t, Tag2>;
|
||||
using TagUInt3 = tagged_integer<std::uint64_t, Tag1>;
|
||||
|
||||
// Check construction of tagged_integers
|
||||
static_assert(
|
||||
std::is_constructible<TagUInt1, std::uint32_t>::value,
|
||||
"TagUInt1 should be constructible using a std::uint32_t");
|
||||
|
||||
static_assert(
|
||||
!std::is_constructible<TagUInt1, std::uint64_t>::value,
|
||||
"TagUInt1 should not be constructible using a std::uint64_t");
|
||||
|
||||
static_assert(
|
||||
std::is_constructible<TagUInt3, std::uint32_t>::value,
|
||||
"TagUInt3 should be constructible using a std::uint32_t");
|
||||
|
||||
static_assert(
|
||||
std::is_constructible<TagUInt3, std::uint64_t>::value,
|
||||
"TagUInt3 should be constructible using a std::uint64_t");
|
||||
|
||||
// Check assignment of tagged_integers
|
||||
static_assert(
|
||||
!std::is_assignable<TagUInt1, std::uint32_t>::value,
|
||||
"TagUInt1 should not be assignable with a std::uint32_t");
|
||||
|
||||
static_assert(
|
||||
!std::is_assignable<TagUInt1, std::uint64_t>::value,
|
||||
"TagUInt1 should not be assignable with a std::uint64_t");
|
||||
|
||||
static_assert(
|
||||
!std::is_assignable<TagUInt3, std::uint32_t>::value,
|
||||
"TagUInt3 should not be assignable with a std::uint32_t");
|
||||
|
||||
static_assert(
|
||||
!std::is_assignable<TagUInt3, std::uint64_t>::value,
|
||||
"TagUInt3 should not be assignable with a std::uint64_t");
|
||||
|
||||
static_assert(
|
||||
std::is_assignable<TagUInt1, TagUInt1>::value,
|
||||
"TagUInt1 should be assignable with a TagUInt1");
|
||||
|
||||
static_assert(
|
||||
!std::is_assignable<TagUInt1, TagUInt2>::value,
|
||||
"TagUInt1 should not be assignable with a TagUInt2");
|
||||
|
||||
static_assert(
|
||||
std::is_assignable<TagUInt3, TagUInt3>::value,
|
||||
"TagUInt3 should be assignable with a TagUInt1");
|
||||
|
||||
static_assert(
|
||||
!std::is_assignable<TagUInt1, TagUInt3>::value,
|
||||
"TagUInt1 should not be assignable with a TagUInt3");
|
||||
|
||||
static_assert(
|
||||
!std::is_assignable<TagUInt3, TagUInt1>::value,
|
||||
"TagUInt3 should not be assignable with a TagUInt1");
|
||||
|
||||
// Check convertibility of tagged_integers
|
||||
static_assert(
|
||||
!std::is_convertible<std::uint32_t, TagUInt1>::value,
|
||||
"std::uint32_t should not be convertible to a TagUInt1");
|
||||
|
||||
static_assert(
|
||||
!std::is_convertible<std::uint32_t, TagUInt3>::value,
|
||||
"std::uint32_t should not be convertible to a TagUInt3");
|
||||
|
||||
static_assert(
|
||||
!std::is_convertible<std::uint64_t, TagUInt3>::value,
|
||||
"std::uint64_t should not be convertible to a TagUInt3");
|
||||
|
||||
static_assert(
|
||||
!std::is_convertible<std::uint64_t, TagUInt2>::value,
|
||||
"std::uint64_t should not be convertible to a TagUInt2");
|
||||
|
||||
static_assert(
|
||||
!std::is_convertible<TagUInt1, TagUInt2>::value,
|
||||
"TagUInt1 should not be convertible to TagUInt2");
|
||||
|
||||
static_assert(
|
||||
!std::is_convertible<TagUInt1, TagUInt3>::value,
|
||||
"TagUInt1 should not be convertible to TagUInt3");
|
||||
|
||||
static_assert(
|
||||
!std::is_convertible<TagUInt2, TagUInt3>::value,
|
||||
"TagUInt2 should not be convertible to a TagUInt3");
|
||||
|
||||
public:
|
||||
void
|
||||
run() override
|
||||
{
|
||||
using TagInt = tagged_integer<std::int32_t, Tag1>;
|
||||
|
||||
{
|
||||
testcase("Comparison Operators");
|
||||
|
||||
TagInt const zero(0);
|
||||
TagInt const one(1);
|
||||
|
||||
BEAST_EXPECT(one == one);
|
||||
BEAST_EXPECT(!(one == zero));
|
||||
|
||||
BEAST_EXPECT(one != zero);
|
||||
BEAST_EXPECT(!(one != one));
|
||||
|
||||
BEAST_EXPECT(zero < one);
|
||||
BEAST_EXPECT(!(one < zero));
|
||||
|
||||
BEAST_EXPECT(one > zero);
|
||||
BEAST_EXPECT(!(zero > one));
|
||||
|
||||
BEAST_EXPECT(one >= one);
|
||||
BEAST_EXPECT(one >= zero);
|
||||
BEAST_EXPECT(!(zero >= one));
|
||||
|
||||
BEAST_EXPECT(zero <= one);
|
||||
BEAST_EXPECT(zero <= zero);
|
||||
BEAST_EXPECT(!(one <= zero));
|
||||
}
|
||||
|
||||
{
|
||||
testcase("Increment/Decrement Operators");
|
||||
TagInt const zero(0);
|
||||
TagInt const one(1);
|
||||
TagInt a{0};
|
||||
++a;
|
||||
BEAST_EXPECT(a == one);
|
||||
--a;
|
||||
BEAST_EXPECT(a == zero);
|
||||
a++;
|
||||
BEAST_EXPECT(a == one);
|
||||
a--;
|
||||
BEAST_EXPECT(a == zero);
|
||||
}
|
||||
|
||||
{
|
||||
testcase("Arithmetic Operators");
|
||||
TagInt a{-2};
|
||||
BEAST_EXPECT(+a == TagInt{-2});
|
||||
BEAST_EXPECT(-a == TagInt{2});
|
||||
BEAST_EXPECT(TagInt{-3} + TagInt{4} == TagInt{1});
|
||||
BEAST_EXPECT(TagInt{-3} - TagInt{4} == TagInt{-7});
|
||||
BEAST_EXPECT(TagInt{-3} * TagInt{4} == TagInt{-12});
|
||||
BEAST_EXPECT(TagInt{8} / TagInt{4} == TagInt{2});
|
||||
BEAST_EXPECT(TagInt{7} % TagInt{4} == TagInt{3});
|
||||
|
||||
BEAST_EXPECT(~TagInt{8} == TagInt{~TagInt::value_type{8}});
|
||||
BEAST_EXPECT((TagInt{6} & TagInt{3}) == TagInt{2});
|
||||
BEAST_EXPECT((TagInt{6} | TagInt{3}) == TagInt{7});
|
||||
BEAST_EXPECT((TagInt{6} ^ TagInt{3}) == TagInt{5});
|
||||
|
||||
BEAST_EXPECT((TagInt{4} << TagInt{2}) == TagInt{16});
|
||||
BEAST_EXPECT((TagInt{16} >> TagInt{2}) == TagInt{4});
|
||||
}
|
||||
{
|
||||
testcase("Assignment Operators");
|
||||
TagInt a{-2};
|
||||
TagInt b{0};
|
||||
b = a;
|
||||
BEAST_EXPECT(b == TagInt{-2});
|
||||
|
||||
// -3 + 4 == 1
|
||||
a = TagInt{-3};
|
||||
a += TagInt{4};
|
||||
BEAST_EXPECT(a == TagInt{1});
|
||||
|
||||
// -3 - 4 == -7
|
||||
a = TagInt{-3};
|
||||
a -= TagInt{4};
|
||||
BEAST_EXPECT(a == TagInt{-7});
|
||||
|
||||
// -3 * 4 == -12
|
||||
a = TagInt{-3};
|
||||
a *= TagInt{4};
|
||||
BEAST_EXPECT(a == TagInt{-12});
|
||||
|
||||
// 8/4 == 2
|
||||
a = TagInt{8};
|
||||
a /= TagInt{4};
|
||||
BEAST_EXPECT(a == TagInt{2});
|
||||
|
||||
// 7 % 4 == 3
|
||||
a = TagInt{7};
|
||||
a %= TagInt{4};
|
||||
BEAST_EXPECT(a == TagInt{3});
|
||||
|
||||
// 6 & 3 == 2
|
||||
a = TagInt{6};
|
||||
a /= TagInt{3};
|
||||
BEAST_EXPECT(a == TagInt{2});
|
||||
|
||||
// 6 | 3 == 7
|
||||
a = TagInt{6};
|
||||
a |= TagInt{3};
|
||||
BEAST_EXPECT(a == TagInt{7});
|
||||
|
||||
// 6 ^ 3 == 5
|
||||
a = TagInt{6};
|
||||
a ^= TagInt{3};
|
||||
BEAST_EXPECT(a == TagInt{5});
|
||||
|
||||
// 4 << 2 == 16
|
||||
a = TagInt{4};
|
||||
a <<= TagInt{2};
|
||||
BEAST_EXPECT(a == TagInt{16});
|
||||
|
||||
// 16 >> 2 == 4
|
||||
a = TagInt{16};
|
||||
a >>= TagInt{2};
|
||||
BEAST_EXPECT(a == TagInt{4});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(tagged_integer, ripple_basics, ripple);
|
||||
|
||||
} // namespace test
|
||||
} // namespace ripple
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
@@ -139,7 +139,8 @@ class Feature_test : public beast::unit_test::suite
|
||||
|
||||
// Test a random sampling of the variables. If any of these get retired
|
||||
// or removed, swap out for any other feature.
|
||||
BEAST_EXPECT(featureToName(featureOwnerPaysFee) == "OwnerPaysFee");
|
||||
BEAST_EXPECT(
|
||||
featureToName(fixTrustLinesToSelf) == "fixTrustLinesToSelf");
|
||||
BEAST_EXPECT(featureToName(featureFlow) == "Flow");
|
||||
BEAST_EXPECT(featureToName(featureNegativeUNL) == "NegativeUNL");
|
||||
BEAST_EXPECT(featureToName(fix1578) == "fix1578");
|
||||
|
||||
@@ -1354,6 +1354,225 @@ public:
|
||||
}));
|
||||
}
|
||||
|
||||
void
|
||||
testNFToken(FeatureBitset features)
|
||||
{
|
||||
// `nftoken_id` is added for `transaction` stream in the `subscribe`
|
||||
// response for NFTokenMint and NFTokenAcceptOffer.
|
||||
//
|
||||
// `nftoken_ids` is added for `transaction` stream in the `subscribe`
|
||||
// response for NFTokenCancelOffer
|
||||
//
|
||||
// `offer_id` is added for `transaction` stream in the `subscribe`
|
||||
// response for NFTokenCreateOffer
|
||||
//
|
||||
// The values of these fields are dependent on the NFTokenID/OfferID
|
||||
// changed in its corresponding transaction. We want to validate each
|
||||
// response to make sure the synethic fields hold the right values.
|
||||
|
||||
testcase("Test synthetic fields from Subscribe response");
|
||||
|
||||
using namespace test::jtx;
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
Account const alice{"alice"};
|
||||
Account const bob{"bob"};
|
||||
Account const broker{"broker"};
|
||||
|
||||
Env env{*this, features};
|
||||
env.fund(XRP(10000), alice, bob, broker);
|
||||
env.close();
|
||||
|
||||
auto wsc = test::makeWSClient(env.app().config());
|
||||
Json::Value stream;
|
||||
stream[jss::streams] = Json::arrayValue;
|
||||
stream[jss::streams].append("transactions");
|
||||
auto jv = wsc->invoke("subscribe", stream);
|
||||
|
||||
// Verify `nftoken_id` value equals to the NFTokenID that was
|
||||
// changed in the most recent NFTokenMint or NFTokenAcceptOffer
|
||||
// transaction
|
||||
auto verifyNFTokenID = [&](uint256 const& actualNftID) {
|
||||
BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) {
|
||||
uint256 nftID;
|
||||
BEAST_EXPECT(
|
||||
nftID.parseHex(jv[jss::meta][jss::nftoken_id].asString()));
|
||||
return nftID == actualNftID;
|
||||
}));
|
||||
};
|
||||
|
||||
// Verify `nftoken_ids` value equals to the NFTokenIDs that were
|
||||
// changed in the most recent NFTokenCancelOffer transaction
|
||||
auto verifyNFTokenIDsInCancelOffer =
|
||||
[&](std::vector<uint256> actualNftIDs) {
|
||||
BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) {
|
||||
std::vector<uint256> metaIDs;
|
||||
std::transform(
|
||||
jv[jss::meta][jss::nftoken_ids].begin(),
|
||||
jv[jss::meta][jss::nftoken_ids].end(),
|
||||
std::back_inserter(metaIDs),
|
||||
[this](Json::Value id) {
|
||||
uint256 nftID;
|
||||
BEAST_EXPECT(nftID.parseHex(id.asString()));
|
||||
return nftID;
|
||||
});
|
||||
// Sort both array to prepare for comparison
|
||||
std::sort(metaIDs.begin(), metaIDs.end());
|
||||
std::sort(actualNftIDs.begin(), actualNftIDs.end());
|
||||
|
||||
// Make sure the expect number of NFTs is correct
|
||||
BEAST_EXPECT(metaIDs.size() == actualNftIDs.size());
|
||||
|
||||
// Check the value of NFT ID in the meta with the
|
||||
// actual values
|
||||
for (size_t i = 0; i < metaIDs.size(); ++i)
|
||||
BEAST_EXPECT(metaIDs[i] == actualNftIDs[i]);
|
||||
return true;
|
||||
}));
|
||||
};
|
||||
|
||||
// Verify `offer_id` value equals to the offerID that was
|
||||
// changed in the most recent NFTokenCreateOffer tx
|
||||
auto verifyNFTokenOfferID = [&](uint256 const& offerID) {
|
||||
BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) {
|
||||
uint256 metaOfferID;
|
||||
BEAST_EXPECT(metaOfferID.parseHex(
|
||||
jv[jss::meta][jss::offer_id].asString()));
|
||||
return metaOfferID == offerID;
|
||||
}));
|
||||
};
|
||||
|
||||
// Check new fields in tx meta when for all NFTtransactions
|
||||
{
|
||||
// Alice mints 2 NFTs
|
||||
// Verify the NFTokenIDs are correct in the NFTokenMint tx meta
|
||||
uint256 const nftId1{
|
||||
token::getNextID(env, alice, 0u, tfTransferable)};
|
||||
env(token::mint(alice, 0u), txflags(tfTransferable));
|
||||
env.close();
|
||||
verifyNFTokenID(nftId1);
|
||||
|
||||
uint256 const nftId2{
|
||||
token::getNextID(env, alice, 0u, tfTransferable)};
|
||||
env(token::mint(alice, 0u), txflags(tfTransferable));
|
||||
env.close();
|
||||
verifyNFTokenID(nftId2);
|
||||
|
||||
// Alice creates one sell offer for each NFT
|
||||
// Verify the offer indexes are correct in the NFTokenCreateOffer tx
|
||||
// meta
|
||||
uint256 const aliceOfferIndex1 =
|
||||
keylet::nftoffer(alice, env.seq(alice)).key;
|
||||
env(token::createOffer(alice, nftId1, drops(1)),
|
||||
txflags(tfSellNFToken));
|
||||
env.close();
|
||||
verifyNFTokenOfferID(aliceOfferIndex1);
|
||||
|
||||
uint256 const aliceOfferIndex2 =
|
||||
keylet::nftoffer(alice, env.seq(alice)).key;
|
||||
env(token::createOffer(alice, nftId2, drops(1)),
|
||||
txflags(tfSellNFToken));
|
||||
env.close();
|
||||
verifyNFTokenOfferID(aliceOfferIndex2);
|
||||
|
||||
// Alice cancels two offers she created
|
||||
// Verify the NFTokenIDs are correct in the NFTokenCancelOffer tx
|
||||
// meta
|
||||
env(token::cancelOffer(
|
||||
alice, {aliceOfferIndex1, aliceOfferIndex2}));
|
||||
env.close();
|
||||
verifyNFTokenIDsInCancelOffer({nftId1, nftId2});
|
||||
|
||||
// Bobs creates a buy offer for nftId1
|
||||
// Verify the offer id is correct in the NFTokenCreateOffer tx meta
|
||||
auto const bobBuyOfferIndex =
|
||||
keylet::nftoffer(bob, env.seq(bob)).key;
|
||||
env(token::createOffer(bob, nftId1, drops(1)), token::owner(alice));
|
||||
env.close();
|
||||
verifyNFTokenOfferID(bobBuyOfferIndex);
|
||||
|
||||
// Alice accepts bob's buy offer
|
||||
// Verify the NFTokenID is correct in the NFTokenAcceptOffer tx meta
|
||||
env(token::acceptBuyOffer(alice, bobBuyOfferIndex));
|
||||
env.close();
|
||||
verifyNFTokenID(nftId1);
|
||||
}
|
||||
|
||||
// Check `nftoken_ids` in brokered mode
|
||||
{
|
||||
// Alice mints a NFT
|
||||
uint256 const nftId{
|
||||
token::getNextID(env, alice, 0u, tfTransferable)};
|
||||
env(token::mint(alice, 0u), txflags(tfTransferable));
|
||||
env.close();
|
||||
verifyNFTokenID(nftId);
|
||||
|
||||
// Alice creates sell offer and set broker as destination
|
||||
uint256 const offerAliceToBroker =
|
||||
keylet::nftoffer(alice, env.seq(alice)).key;
|
||||
env(token::createOffer(alice, nftId, drops(1)),
|
||||
token::destination(broker),
|
||||
txflags(tfSellNFToken));
|
||||
env.close();
|
||||
verifyNFTokenOfferID(offerAliceToBroker);
|
||||
|
||||
// Bob creates buy offer
|
||||
uint256 const offerBobToBroker =
|
||||
keylet::nftoffer(bob, env.seq(bob)).key;
|
||||
env(token::createOffer(bob, nftId, drops(1)), token::owner(alice));
|
||||
env.close();
|
||||
verifyNFTokenOfferID(offerBobToBroker);
|
||||
|
||||
// Check NFTokenID meta for NFTokenAcceptOffer in brokered mode
|
||||
env(token::brokerOffers(
|
||||
broker, offerBobToBroker, offerAliceToBroker));
|
||||
env.close();
|
||||
verifyNFTokenID(nftId);
|
||||
}
|
||||
|
||||
// Check if there are no duplicate nft id in Cancel transactions where
|
||||
// multiple offers are cancelled for the same NFT
|
||||
{
|
||||
// Alice mints a NFT
|
||||
uint256 const nftId{
|
||||
token::getNextID(env, alice, 0u, tfTransferable)};
|
||||
env(token::mint(alice, 0u), txflags(tfTransferable));
|
||||
env.close();
|
||||
verifyNFTokenID(nftId);
|
||||
|
||||
// Alice creates 2 sell offers for the same NFT
|
||||
uint256 const aliceOfferIndex1 =
|
||||
keylet::nftoffer(alice, env.seq(alice)).key;
|
||||
env(token::createOffer(alice, nftId, drops(1)),
|
||||
txflags(tfSellNFToken));
|
||||
env.close();
|
||||
verifyNFTokenOfferID(aliceOfferIndex1);
|
||||
|
||||
uint256 const aliceOfferIndex2 =
|
||||
keylet::nftoffer(alice, env.seq(alice)).key;
|
||||
env(token::createOffer(alice, nftId, drops(1)),
|
||||
txflags(tfSellNFToken));
|
||||
env.close();
|
||||
verifyNFTokenOfferID(aliceOfferIndex2);
|
||||
|
||||
// Make sure the metadata only has 1 nft id, since both offers are
|
||||
// for the same nft
|
||||
env(token::cancelOffer(
|
||||
alice, {aliceOfferIndex1, aliceOfferIndex2}));
|
||||
env.close();
|
||||
verifyNFTokenIDsInCancelOffer({nftId});
|
||||
}
|
||||
|
||||
if (features[featureNFTokenMintOffer])
|
||||
{
|
||||
uint256 const aliceMintWithOfferIndex1 =
|
||||
keylet::nftoffer(alice, env.seq(alice)).key;
|
||||
env(token::mint(alice), token::amount(XRP(0)));
|
||||
env.close();
|
||||
verifyNFTokenOfferID(aliceMintWithOfferIndex1);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
@@ -1373,6 +1592,8 @@ public:
|
||||
testSubByUrl();
|
||||
testHistoryTxStream();
|
||||
testSubBookChanges();
|
||||
testNFToken(all);
|
||||
testNFToken(all - featureNFTokenMintOffer);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
4
src/tests/README.md
Normal file
4
src/tests/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# Unit tests
|
||||
This directory contains unit tests for the project. The difference from existing `src/test` folder
|
||||
is that we switch to 3rd party testing framework (doctest). We intend to gradually move existing tests
|
||||
from our own framework to doctest and such tests will be moved to this new folder.
|
||||
16
src/tests/libxrpl/CMakeLists.txt
Normal file
16
src/tests/libxrpl/CMakeLists.txt
Normal file
@@ -0,0 +1,16 @@
|
||||
include(xrpl_add_test)
|
||||
|
||||
# Test requirements.
|
||||
find_package(doctest REQUIRED)
|
||||
|
||||
# Common library dependencies for the rest of the tests.
|
||||
add_library(xrpl.imports.test INTERFACE)
|
||||
target_link_libraries(xrpl.imports.test INTERFACE doctest::doctest xrpl.libxrpl)
|
||||
|
||||
# One test for each module.
|
||||
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)
|
||||
129
src/tests/libxrpl/basics/RangeSet.cpp
Normal file
129
src/tests/libxrpl/basics/RangeSet.cpp
Normal file
@@ -0,0 +1,129 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012 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/RangeSet.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
using namespace ripple;
|
||||
|
||||
TEST_SUITE_BEGIN("RangeSet");
|
||||
|
||||
TEST_CASE("prevMissing")
|
||||
{
|
||||
// Set will include:
|
||||
// [ 0, 5]
|
||||
// [10,15]
|
||||
// [20,25]
|
||||
// etc...
|
||||
|
||||
RangeSet<std::uint32_t> set;
|
||||
for (std::uint32_t i = 0; i < 10; ++i)
|
||||
set.insert(range(10 * i, 10 * i + 5));
|
||||
|
||||
for (std::uint32_t i = 1; i < 100; ++i)
|
||||
{
|
||||
std::optional<std::uint32_t> expected;
|
||||
// no prev missing in domain for i <= 6
|
||||
if (i > 6)
|
||||
{
|
||||
std::uint32_t const oneBelowRange = (10 * (i / 10)) - 1;
|
||||
|
||||
expected = ((i % 10) > 6) ? (i - 1) : oneBelowRange;
|
||||
}
|
||||
CHECK(prevMissing(set, i) == expected);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("toString")
|
||||
{
|
||||
RangeSet<std::uint32_t> set;
|
||||
CHECK(to_string(set) == "empty");
|
||||
|
||||
set.insert(1);
|
||||
CHECK(to_string(set) == "1");
|
||||
|
||||
set.insert(range(4u, 6u));
|
||||
CHECK(to_string(set) == "1,4-6");
|
||||
|
||||
set.insert(2);
|
||||
CHECK(to_string(set) == "1-2,4-6");
|
||||
|
||||
set.erase(range(4u, 5u));
|
||||
CHECK(to_string(set) == "1-2,6");
|
||||
}
|
||||
|
||||
TEST_CASE("fromString")
|
||||
{
|
||||
RangeSet<std::uint32_t> set;
|
||||
|
||||
CHECK(!from_string(set, ""));
|
||||
CHECK(boost::icl::length(set) == 0);
|
||||
|
||||
CHECK(!from_string(set, "#"));
|
||||
CHECK(boost::icl::length(set) == 0);
|
||||
|
||||
CHECK(!from_string(set, ","));
|
||||
CHECK(boost::icl::length(set) == 0);
|
||||
|
||||
CHECK(!from_string(set, ",-"));
|
||||
CHECK(boost::icl::length(set) == 0);
|
||||
|
||||
CHECK(!from_string(set, "1,,2"));
|
||||
CHECK(boost::icl::length(set) == 0);
|
||||
|
||||
CHECK(from_string(set, "1"));
|
||||
CHECK(boost::icl::length(set) == 1);
|
||||
CHECK(boost::icl::first(set) == 1);
|
||||
|
||||
CHECK(from_string(set, "1,1"));
|
||||
CHECK(boost::icl::length(set) == 1);
|
||||
CHECK(boost::icl::first(set) == 1);
|
||||
|
||||
CHECK(from_string(set, "1-1"));
|
||||
CHECK(boost::icl::length(set) == 1);
|
||||
CHECK(boost::icl::first(set) == 1);
|
||||
|
||||
CHECK(from_string(set, "1,4-6"));
|
||||
CHECK(boost::icl::length(set) == 4);
|
||||
CHECK(boost::icl::first(set) == 1);
|
||||
CHECK(!boost::icl::contains(set, 2));
|
||||
CHECK(!boost::icl::contains(set, 3));
|
||||
CHECK(boost::icl::contains(set, 4));
|
||||
CHECK(boost::icl::contains(set, 5));
|
||||
CHECK(boost::icl::last(set) == 6);
|
||||
|
||||
CHECK(from_string(set, "1-2,4-6"));
|
||||
CHECK(boost::icl::length(set) == 5);
|
||||
CHECK(boost::icl::first(set) == 1);
|
||||
CHECK(boost::icl::contains(set, 2));
|
||||
CHECK(boost::icl::contains(set, 4));
|
||||
CHECK(boost::icl::last(set) == 6);
|
||||
|
||||
CHECK(from_string(set, "1-2,6"));
|
||||
CHECK(boost::icl::length(set) == 3);
|
||||
CHECK(boost::icl::first(set) == 1);
|
||||
CHECK(boost::icl::contains(set, 2));
|
||||
CHECK(boost::icl::last(set) == 6);
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
105
src/tests/libxrpl/basics/Slice.cpp
Normal file
105
src/tests/libxrpl/basics/Slice.cpp
Normal file
@@ -0,0 +1,105 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012 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/Slice.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
using namespace ripple;
|
||||
|
||||
static std::uint8_t const data[] = {
|
||||
0xa8, 0xa1, 0x38, 0x45, 0x23, 0xec, 0xe4, 0x23, 0x71, 0x6d, 0x2a,
|
||||
0x18, 0xb4, 0x70, 0xcb, 0xf5, 0xac, 0x2d, 0x89, 0x4d, 0x19, 0x9c,
|
||||
0xf0, 0x2c, 0x15, 0xd1, 0xf9, 0x9b, 0x66, 0xd2, 0x30, 0xd3};
|
||||
|
||||
TEST_SUITE_BEGIN("Slice");
|
||||
|
||||
TEST_CASE("equality & inequality")
|
||||
{
|
||||
Slice const s0{};
|
||||
|
||||
CHECK(s0.size() == 0);
|
||||
CHECK(s0.data() == nullptr);
|
||||
CHECK(s0 == s0);
|
||||
|
||||
// Test slices of equal and unequal size pointing to same data:
|
||||
for (std::size_t i = 0; i != sizeof(data); ++i)
|
||||
{
|
||||
Slice const s1{data, i};
|
||||
|
||||
CHECK(s1.size() == i);
|
||||
CHECK(s1.data() != nullptr);
|
||||
|
||||
if (i == 0)
|
||||
CHECK(s1 == s0);
|
||||
else
|
||||
CHECK(s1 != s0);
|
||||
|
||||
for (std::size_t j = 0; j != sizeof(data); ++j)
|
||||
{
|
||||
Slice const s2{data, j};
|
||||
|
||||
if (i == j)
|
||||
CHECK(s1 == s2);
|
||||
else
|
||||
CHECK(s1 != s2);
|
||||
}
|
||||
}
|
||||
|
||||
// Test slices of equal size but pointing to different data:
|
||||
std::array<std::uint8_t, sizeof(data)> a;
|
||||
std::array<std::uint8_t, sizeof(data)> b;
|
||||
|
||||
for (std::size_t i = 0; i != sizeof(data); ++i)
|
||||
a[i] = b[i] = data[i];
|
||||
|
||||
CHECK(makeSlice(a) == makeSlice(b));
|
||||
b[7]++;
|
||||
CHECK(makeSlice(a) != makeSlice(b));
|
||||
a[7]++;
|
||||
CHECK(makeSlice(a) == makeSlice(b));
|
||||
}
|
||||
|
||||
TEST_CASE("indexing")
|
||||
{
|
||||
Slice const s{data, sizeof(data)};
|
||||
|
||||
for (std::size_t i = 0; i != sizeof(data); ++i)
|
||||
CHECK(s[i] == data[i]);
|
||||
}
|
||||
|
||||
TEST_CASE("advancing")
|
||||
{
|
||||
for (std::size_t i = 0; i < sizeof(data); ++i)
|
||||
{
|
||||
for (std::size_t j = 0; i + j < sizeof(data); ++j)
|
||||
{
|
||||
Slice s(data + i, sizeof(data) - i);
|
||||
s += j;
|
||||
|
||||
CHECK(s.data() == data + i + j);
|
||||
CHECK(s.size() == sizeof(data) - i - j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
67
src/tests/libxrpl/basics/base64.cpp
Normal file
67
src/tests/libxrpl/basics/base64.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012 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/base64.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
using namespace ripple;
|
||||
|
||||
static void
|
||||
check(std::string const& in, std::string const& out)
|
||||
{
|
||||
auto const encoded = base64_encode(in);
|
||||
CHECK(encoded == out);
|
||||
CHECK(base64_decode(encoded) == in);
|
||||
}
|
||||
|
||||
TEST_CASE("base64")
|
||||
{
|
||||
check("", "");
|
||||
check("f", "Zg==");
|
||||
check("fo", "Zm8=");
|
||||
check("foo", "Zm9v");
|
||||
check("foob", "Zm9vYg==");
|
||||
check("fooba", "Zm9vYmE=");
|
||||
check("foobar", "Zm9vYmFy");
|
||||
|
||||
check(
|
||||
"Man is distinguished, not only by his reason, but by this "
|
||||
"singular passion from "
|
||||
"other animals, which is a lust of the mind, that by a "
|
||||
"perseverance of delight "
|
||||
"in the continued and indefatigable generation of knowledge, "
|
||||
"exceeds the short "
|
||||
"vehemence of any carnal pleasure.",
|
||||
"TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dC"
|
||||
"BieSB0aGlz"
|
||||
"IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIG"
|
||||
"x1c3Qgb2Yg"
|
||||
"dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aG"
|
||||
"UgY29udGlu"
|
||||
"dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleG"
|
||||
"NlZWRzIHRo"
|
||||
"ZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=");
|
||||
|
||||
std::string const notBase64 = "not_base64!!";
|
||||
std::string const truncated = "not";
|
||||
CHECK(base64_decode(notBase64) == base64_decode(truncated));
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
Copyright (c) 2012 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
|
||||
@@ -18,46 +18,39 @@
|
||||
//==============================================================================
|
||||
|
||||
#include <xrpl/basics/contract.h>
|
||||
#include <xrpl/beast/unit_test.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
namespace ripple {
|
||||
using namespace ripple;
|
||||
|
||||
class contract_test : public beast::unit_test::suite
|
||||
TEST_CASE("contract")
|
||||
{
|
||||
public:
|
||||
void
|
||||
run() override
|
||||
try
|
||||
{
|
||||
Throw<std::runtime_error>("Throw test");
|
||||
}
|
||||
catch (std::runtime_error const& e1)
|
||||
{
|
||||
CHECK(std::string(e1.what()) == "Throw test");
|
||||
|
||||
try
|
||||
{
|
||||
Throw<std::runtime_error>("Throw test");
|
||||
Rethrow();
|
||||
}
|
||||
catch (std::runtime_error const& e1)
|
||||
catch (std::runtime_error const& e2)
|
||||
{
|
||||
BEAST_EXPECT(std::string(e1.what()) == "Throw test");
|
||||
|
||||
try
|
||||
{
|
||||
Rethrow();
|
||||
}
|
||||
catch (std::runtime_error const& e2)
|
||||
{
|
||||
BEAST_EXPECT(std::string(e2.what()) == "Throw test");
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
BEAST_EXPECT(false);
|
||||
}
|
||||
CHECK(std::string(e2.what()) == "Throw test");
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
BEAST_EXPECT(false);
|
||||
CHECK(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(contract, basics, ripple);
|
||||
|
||||
} // namespace ripple
|
||||
catch (...)
|
||||
{
|
||||
CHECK(false);
|
||||
}
|
||||
}
|
||||
2
src/tests/libxrpl/basics/main.cpp
Normal file
2
src/tests/libxrpl/basics/main.cpp
Normal file
@@ -0,0 +1,2 @@
|
||||
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
|
||||
#include <doctest/doctest.h>
|
||||
64
src/tests/libxrpl/basics/mulDiv.cpp
Normal file
64
src/tests/libxrpl/basics/mulDiv.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012 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/mulDiv.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
|
||||
using namespace ripple;
|
||||
|
||||
TEST_CASE("mulDiv")
|
||||
{
|
||||
auto const max = std::numeric_limits<std::uint64_t>::max();
|
||||
std::uint64_t const max32 = std::numeric_limits<std::uint32_t>::max();
|
||||
|
||||
auto result = mulDiv(85, 20, 5);
|
||||
REQUIRE(result);
|
||||
CHECK(*result == 340);
|
||||
result = mulDiv(20, 85, 5);
|
||||
REQUIRE(result);
|
||||
CHECK(*result == 340);
|
||||
|
||||
result = mulDiv(0, max - 1, max - 3);
|
||||
REQUIRE(result);
|
||||
CHECK(*result == 0);
|
||||
result = mulDiv(max - 1, 0, max - 3);
|
||||
REQUIRE(result);
|
||||
CHECK(*result == 0);
|
||||
|
||||
result = mulDiv(max, 2, max / 2);
|
||||
REQUIRE(result);
|
||||
CHECK(*result == 4);
|
||||
result = mulDiv(max, 1000, max / 1000);
|
||||
REQUIRE(result);
|
||||
CHECK(*result == 1000000);
|
||||
result = mulDiv(max, 1000, max / 1001);
|
||||
REQUIRE(result);
|
||||
CHECK(*result == 1001000);
|
||||
result = mulDiv(max32 + 1, max32 + 1, 5);
|
||||
REQUIRE(result);
|
||||
CHECK(*result == 3689348814741910323);
|
||||
|
||||
// Overflow
|
||||
result = mulDiv(max - 1, max - 2, 5);
|
||||
CHECK(!result);
|
||||
}
|
||||
174
src/tests/libxrpl/basics/scope.cpp
Normal file
174
src/tests/libxrpl/basics/scope.cpp
Normal file
@@ -0,0 +1,174 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2021 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/scope.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
using namespace ripple;
|
||||
|
||||
TEST_CASE("scope_exit")
|
||||
{
|
||||
// scope_exit always executes the functor on destruction,
|
||||
// unless release() is called
|
||||
int i = 0;
|
||||
{
|
||||
scope_exit x{[&i]() { i = 1; }};
|
||||
}
|
||||
CHECK(i == 1);
|
||||
{
|
||||
scope_exit x{[&i]() { i = 2; }};
|
||||
x.release();
|
||||
}
|
||||
CHECK(i == 1);
|
||||
{
|
||||
scope_exit x{[&i]() { i += 2; }};
|
||||
auto x2 = std::move(x);
|
||||
}
|
||||
CHECK(i == 3);
|
||||
{
|
||||
scope_exit x{[&i]() { i = 4; }};
|
||||
x.release();
|
||||
auto x2 = std::move(x);
|
||||
}
|
||||
CHECK(i == 3);
|
||||
{
|
||||
try
|
||||
{
|
||||
scope_exit x{[&i]() { i = 5; }};
|
||||
throw 1;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}
|
||||
CHECK(i == 5);
|
||||
{
|
||||
try
|
||||
{
|
||||
scope_exit x{[&i]() { i = 6; }};
|
||||
x.release();
|
||||
throw 1;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}
|
||||
CHECK(i == 5);
|
||||
}
|
||||
|
||||
TEST_CASE("scope_fail")
|
||||
{
|
||||
// scope_fail executes the functor on destruction only
|
||||
// if an exception is unwinding, unless release() is called
|
||||
int i = 0;
|
||||
{
|
||||
scope_fail x{[&i]() { i = 1; }};
|
||||
}
|
||||
CHECK(i == 0);
|
||||
{
|
||||
scope_fail x{[&i]() { i = 2; }};
|
||||
x.release();
|
||||
}
|
||||
CHECK(i == 0);
|
||||
{
|
||||
scope_fail x{[&i]() { i = 3; }};
|
||||
auto x2 = std::move(x);
|
||||
}
|
||||
CHECK(i == 0);
|
||||
{
|
||||
scope_fail x{[&i]() { i = 4; }};
|
||||
x.release();
|
||||
auto x2 = std::move(x);
|
||||
}
|
||||
CHECK(i == 0);
|
||||
{
|
||||
try
|
||||
{
|
||||
scope_fail x{[&i]() { i = 5; }};
|
||||
throw 1;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}
|
||||
CHECK(i == 5);
|
||||
{
|
||||
try
|
||||
{
|
||||
scope_fail x{[&i]() { i = 6; }};
|
||||
x.release();
|
||||
throw 1;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}
|
||||
CHECK(i == 5);
|
||||
}
|
||||
|
||||
TEST_CASE("scope_success")
|
||||
{
|
||||
// scope_success executes the functor on destruction only
|
||||
// if an exception is not unwinding, unless release() is called
|
||||
int i = 0;
|
||||
{
|
||||
scope_success x{[&i]() { i = 1; }};
|
||||
}
|
||||
CHECK(i == 1);
|
||||
{
|
||||
scope_success x{[&i]() { i = 2; }};
|
||||
x.release();
|
||||
}
|
||||
CHECK(i == 1);
|
||||
{
|
||||
scope_success x{[&i]() { i += 2; }};
|
||||
auto x2 = std::move(x);
|
||||
}
|
||||
CHECK(i == 3);
|
||||
{
|
||||
scope_success x{[&i]() { i = 4; }};
|
||||
x.release();
|
||||
auto x2 = std::move(x);
|
||||
}
|
||||
CHECK(i == 3);
|
||||
{
|
||||
try
|
||||
{
|
||||
scope_success x{[&i]() { i = 5; }};
|
||||
throw 1;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}
|
||||
CHECK(i == 3);
|
||||
{
|
||||
try
|
||||
{
|
||||
scope_success x{[&i]() { i = 6; }};
|
||||
x.release();
|
||||
throw 1;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}
|
||||
CHECK(i == 3);
|
||||
}
|
||||
247
src/tests/libxrpl/basics/tagged_integer.cpp
Normal file
247
src/tests/libxrpl/basics/tagged_integer.cpp
Normal file
@@ -0,0 +1,247 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2014 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/tagged_integer.h>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
using namespace ripple;
|
||||
|
||||
struct Tag1
|
||||
{
|
||||
};
|
||||
struct Tag2
|
||||
{
|
||||
};
|
||||
|
||||
// Static checks that types are not interoperable
|
||||
|
||||
using TagUInt1 = tagged_integer<std::uint32_t, Tag1>;
|
||||
using TagUInt2 = tagged_integer<std::uint32_t, Tag2>;
|
||||
using TagUInt3 = tagged_integer<std::uint64_t, Tag1>;
|
||||
|
||||
// Check construction of tagged_integers
|
||||
static_assert(
|
||||
std::is_constructible<TagUInt1, std::uint32_t>::value,
|
||||
"TagUInt1 should be constructible using a std::uint32_t");
|
||||
|
||||
static_assert(
|
||||
!std::is_constructible<TagUInt1, std::uint64_t>::value,
|
||||
"TagUInt1 should not be constructible using a std::uint64_t");
|
||||
|
||||
static_assert(
|
||||
std::is_constructible<TagUInt3, std::uint32_t>::value,
|
||||
"TagUInt3 should be constructible using a std::uint32_t");
|
||||
|
||||
static_assert(
|
||||
std::is_constructible<TagUInt3, std::uint64_t>::value,
|
||||
"TagUInt3 should be constructible using a std::uint64_t");
|
||||
|
||||
// Check assignment of tagged_integers
|
||||
static_assert(
|
||||
!std::is_assignable<TagUInt1, std::uint32_t>::value,
|
||||
"TagUInt1 should not be assignable with a std::uint32_t");
|
||||
|
||||
static_assert(
|
||||
!std::is_assignable<TagUInt1, std::uint64_t>::value,
|
||||
"TagUInt1 should not be assignable with a std::uint64_t");
|
||||
|
||||
static_assert(
|
||||
!std::is_assignable<TagUInt3, std::uint32_t>::value,
|
||||
"TagUInt3 should not be assignable with a std::uint32_t");
|
||||
|
||||
static_assert(
|
||||
!std::is_assignable<TagUInt3, std::uint64_t>::value,
|
||||
"TagUInt3 should not be assignable with a std::uint64_t");
|
||||
|
||||
static_assert(
|
||||
std::is_assignable<TagUInt1, TagUInt1>::value,
|
||||
"TagUInt1 should be assignable with a TagUInt1");
|
||||
|
||||
static_assert(
|
||||
!std::is_assignable<TagUInt1, TagUInt2>::value,
|
||||
"TagUInt1 should not be assignable with a TagUInt2");
|
||||
|
||||
static_assert(
|
||||
std::is_assignable<TagUInt3, TagUInt3>::value,
|
||||
"TagUInt3 should be assignable with a TagUInt1");
|
||||
|
||||
static_assert(
|
||||
!std::is_assignable<TagUInt1, TagUInt3>::value,
|
||||
"TagUInt1 should not be assignable with a TagUInt3");
|
||||
|
||||
static_assert(
|
||||
!std::is_assignable<TagUInt3, TagUInt1>::value,
|
||||
"TagUInt3 should not be assignable with a TagUInt1");
|
||||
|
||||
// Check convertibility of tagged_integers
|
||||
static_assert(
|
||||
!std::is_convertible<std::uint32_t, TagUInt1>::value,
|
||||
"std::uint32_t should not be convertible to a TagUInt1");
|
||||
|
||||
static_assert(
|
||||
!std::is_convertible<std::uint32_t, TagUInt3>::value,
|
||||
"std::uint32_t should not be convertible to a TagUInt3");
|
||||
|
||||
static_assert(
|
||||
!std::is_convertible<std::uint64_t, TagUInt3>::value,
|
||||
"std::uint64_t should not be convertible to a TagUInt3");
|
||||
|
||||
static_assert(
|
||||
!std::is_convertible<std::uint64_t, TagUInt2>::value,
|
||||
"std::uint64_t should not be convertible to a TagUInt2");
|
||||
|
||||
static_assert(
|
||||
!std::is_convertible<TagUInt1, TagUInt2>::value,
|
||||
"TagUInt1 should not be convertible to TagUInt2");
|
||||
|
||||
static_assert(
|
||||
!std::is_convertible<TagUInt1, TagUInt3>::value,
|
||||
"TagUInt1 should not be convertible to TagUInt3");
|
||||
|
||||
static_assert(
|
||||
!std::is_convertible<TagUInt2, TagUInt3>::value,
|
||||
"TagUInt2 should not be convertible to a TagUInt3");
|
||||
|
||||
TEST_SUITE_BEGIN("tagged_integer");
|
||||
|
||||
using TagInt = tagged_integer<std::int32_t, Tag1>;
|
||||
|
||||
TEST_CASE("comparison operators")
|
||||
{
|
||||
TagInt const zero(0);
|
||||
TagInt const one(1);
|
||||
|
||||
CHECK(one == one);
|
||||
CHECK(!(one == zero));
|
||||
|
||||
CHECK(one != zero);
|
||||
CHECK(!(one != one));
|
||||
|
||||
CHECK(zero < one);
|
||||
CHECK(!(one < zero));
|
||||
|
||||
CHECK(one > zero);
|
||||
CHECK(!(zero > one));
|
||||
|
||||
CHECK(one >= one);
|
||||
CHECK(one >= zero);
|
||||
CHECK(!(zero >= one));
|
||||
|
||||
CHECK(zero <= one);
|
||||
CHECK(zero <= zero);
|
||||
CHECK(!(one <= zero));
|
||||
}
|
||||
|
||||
TEST_CASE("increment / decrement operators")
|
||||
{
|
||||
TagInt const zero(0);
|
||||
TagInt const one(1);
|
||||
TagInt a{0};
|
||||
++a;
|
||||
CHECK(a == one);
|
||||
--a;
|
||||
CHECK(a == zero);
|
||||
a++;
|
||||
CHECK(a == one);
|
||||
a--;
|
||||
CHECK(a == zero);
|
||||
}
|
||||
|
||||
TEST_CASE("arithmetic operators")
|
||||
{
|
||||
TagInt a{-2};
|
||||
CHECK(+a == TagInt{-2});
|
||||
CHECK(-a == TagInt{2});
|
||||
CHECK(TagInt{-3} + TagInt{4} == TagInt{1});
|
||||
CHECK(TagInt{-3} - TagInt{4} == TagInt{-7});
|
||||
CHECK(TagInt{-3} * TagInt{4} == TagInt{-12});
|
||||
CHECK(TagInt{8} / TagInt{4} == TagInt{2});
|
||||
CHECK(TagInt{7} % TagInt{4} == TagInt{3});
|
||||
|
||||
CHECK(~TagInt{8} == TagInt{~TagInt::value_type{8}});
|
||||
CHECK((TagInt{6} & TagInt{3}) == TagInt{2});
|
||||
CHECK((TagInt{6} | TagInt{3}) == TagInt{7});
|
||||
CHECK((TagInt{6} ^ TagInt{3}) == TagInt{5});
|
||||
|
||||
CHECK((TagInt{4} << TagInt{2}) == TagInt{16});
|
||||
CHECK((TagInt{16} >> TagInt{2}) == TagInt{4});
|
||||
}
|
||||
|
||||
TEST_CASE("assignment operators")
|
||||
{
|
||||
TagInt a{-2};
|
||||
TagInt b{0};
|
||||
b = a;
|
||||
CHECK(b == TagInt{-2});
|
||||
|
||||
// -3 + 4 == 1
|
||||
a = TagInt{-3};
|
||||
a += TagInt{4};
|
||||
CHECK(a == TagInt{1});
|
||||
|
||||
// -3 - 4 == -7
|
||||
a = TagInt{-3};
|
||||
a -= TagInt{4};
|
||||
CHECK(a == TagInt{-7});
|
||||
|
||||
// -3 * 4 == -12
|
||||
a = TagInt{-3};
|
||||
a *= TagInt{4};
|
||||
CHECK(a == TagInt{-12});
|
||||
|
||||
// 8/4 == 2
|
||||
a = TagInt{8};
|
||||
a /= TagInt{4};
|
||||
CHECK(a == TagInt{2});
|
||||
|
||||
// 7 % 4 == 3
|
||||
a = TagInt{7};
|
||||
a %= TagInt{4};
|
||||
CHECK(a == TagInt{3});
|
||||
|
||||
// 6 & 3 == 2
|
||||
a = TagInt{6};
|
||||
a /= TagInt{3};
|
||||
CHECK(a == TagInt{2});
|
||||
|
||||
// 6 | 3 == 7
|
||||
a = TagInt{6};
|
||||
a |= TagInt{3};
|
||||
CHECK(a == TagInt{7});
|
||||
|
||||
// 6 ^ 3 == 5
|
||||
a = TagInt{6};
|
||||
a ^= TagInt{3};
|
||||
CHECK(a == TagInt{5});
|
||||
|
||||
// 4 << 2 == 16
|
||||
a = TagInt{4};
|
||||
a <<= TagInt{2};
|
||||
CHECK(a == TagInt{16});
|
||||
|
||||
// 16 >> 2 == 4
|
||||
a = TagInt{16};
|
||||
a >>= TagInt{2};
|
||||
CHECK(a == TagInt{4});
|
||||
}
|
||||
|
||||
TEST_SUITE_END();
|
||||
@@ -17,44 +17,18 @@
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <test/jtx/Env.h>
|
||||
|
||||
#include <xrpl/beast/utility/temp_dir.h>
|
||||
#include <xrpl/crypto/csprng.h>
|
||||
|
||||
namespace ripple {
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
class CryptoPRNG_test : public beast::unit_test::suite
|
||||
using namespace ripple;
|
||||
|
||||
TEST_CASE("get values")
|
||||
{
|
||||
void
|
||||
testGetValues()
|
||||
{
|
||||
testcase("Get Values");
|
||||
try
|
||||
{
|
||||
auto& engine = crypto_prng();
|
||||
auto rand_val = engine();
|
||||
BEAST_EXPECT(rand_val >= engine.min());
|
||||
BEAST_EXPECT(rand_val <= engine.max());
|
||||
|
||||
uint16_t twoByte{0};
|
||||
engine(&twoByte, sizeof(uint16_t));
|
||||
pass();
|
||||
}
|
||||
catch (std::exception&)
|
||||
{
|
||||
fail();
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testGetValues();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(CryptoPRNG, core, ripple);
|
||||
|
||||
} // namespace ripple
|
||||
auto& engine = crypto_prng();
|
||||
auto rand_val = engine();
|
||||
CHECK(rand_val >= engine.min());
|
||||
CHECK(rand_val <= engine.max());
|
||||
uint16_t twoByte{0};
|
||||
engine(&twoByte, sizeof(uint16_t));
|
||||
}
|
||||
2
src/tests/libxrpl/crypto/main.cpp
Normal file
2
src/tests/libxrpl/crypto/main.cpp
Normal file
@@ -0,0 +1,2 @@
|
||||
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
|
||||
#include <doctest/doctest.h>
|
||||
56
src/tests/libxrpl/protocol/ApiVersion.cpp
Normal file
56
src/tests/libxrpl/protocol/ApiVersion.cpp
Normal 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();
|
||||
98
src/tests/libxrpl/protocol/BuildInfo.cpp
Normal file
98
src/tests/libxrpl/protocol/BuildInfo.cpp
Normal 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();
|
||||
179
src/tests/libxrpl/protocol/Hooks.cpp
Normal file
179
src/tests/libxrpl/protocol/Hooks.cpp
Normal 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();
|
||||
@@ -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();
|
||||
860
src/tests/libxrpl/protocol/Issue.cpp
Normal file
860
src/tests/libxrpl/protocol/Issue.cpp
Normal 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();
|
||||
|
||||
|
||||
129
src/tests/libxrpl/protocol/Memo.cpp
Normal file
129
src/tests/libxrpl/protocol/Memo.cpp
Normal 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();
|
||||
646
src/tests/libxrpl/protocol/MultiApiJson.cpp
Normal file
646
src/tests/libxrpl/protocol/MultiApiJson.cpp
Normal 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();
|
||||
507
src/tests/libxrpl/protocol/PublicKey.cpp
Normal file
507
src/tests/libxrpl/protocol/PublicKey.cpp
Normal 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
|
||||
|
||||
2
src/tests/libxrpl/protocol/main.cpp
Normal file
2
src/tests/libxrpl/protocol/main.cpp
Normal file
@@ -0,0 +1,2 @@
|
||||
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
|
||||
#include <doctest/doctest.h>
|
||||
@@ -63,6 +63,7 @@
|
||||
#include <xrpl/protocol/BuildInfo.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/MultiApiJson.h>
|
||||
#include <xrpl/protocol/NFTSyntheticSerializer.h>
|
||||
#include <xrpl/protocol/RPCErr.h>
|
||||
#include <xrpl/protocol/TxFlags.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
@@ -3258,6 +3259,7 @@ NetworkOPsImp::transJson(
|
||||
jvObj[jss::meta] = meta->get().getJson(JsonOptions::none);
|
||||
RPC::insertDeliveredAmount(
|
||||
jvObj[jss::meta], *ledger, transaction, meta->get());
|
||||
RPC::insertNFTSyntheticInJson(jvObj, transaction, meta->get());
|
||||
RPC::insertMPTokenIssuanceID(
|
||||
jvObj[jss::meta], transaction, meta->get());
|
||||
}
|
||||
|
||||
@@ -95,9 +95,6 @@ RippleCalc::rippleCalculate(
|
||||
return std::nullopt;
|
||||
}();
|
||||
|
||||
bool const ownerPaysTransferFee =
|
||||
view.rules().enabled(featureOwnerPaysFee);
|
||||
|
||||
try
|
||||
{
|
||||
flowOut = flow(
|
||||
@@ -108,7 +105,7 @@ RippleCalc::rippleCalculate(
|
||||
spsPaths,
|
||||
defaultPaths,
|
||||
partialPayment,
|
||||
ownerPaysTransferFee,
|
||||
false,
|
||||
OfferCrossing::no,
|
||||
limitQuality,
|
||||
sendMax,
|
||||
|
||||
@@ -348,7 +348,7 @@ populateJsonResponse(
|
||||
txnMeta->getJson(JsonOptions::include_date);
|
||||
insertDeliveredAmount(
|
||||
jvObj[jss::meta], context, txn, *txnMeta);
|
||||
insertNFTSyntheticInJson(jvObj, sttx, *txnMeta);
|
||||
RPC::insertNFTSyntheticInJson(jvObj, sttx, *txnMeta);
|
||||
RPC::insertMPTokenIssuanceID(
|
||||
jvObj[jss::meta], sttx, *txnMeta);
|
||||
}
|
||||
|
||||
@@ -270,7 +270,7 @@ populateJsonResponse(
|
||||
response[jss::meta] = meta->getJson(JsonOptions::none);
|
||||
insertDeliveredAmount(
|
||||
response[jss::meta], context, result.txn, *meta);
|
||||
insertNFTSyntheticInJson(response, sttx, *meta);
|
||||
RPC::insertNFTSyntheticInJson(response, sttx, *meta);
|
||||
RPC::insertMPTokenIssuanceID(response[jss::meta], sttx, *meta);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user