Compare commits

..

7 Commits

Author SHA1 Message Date
JCW
ac55c8725c Fix unit tests 2025-07-02 11:14:06 +01:00
Jingchen
793768a7ba Migrate some tests 2025-06-27 14:02:36 +01:00
Vlad
e18f27f5f7 test: switch some unit tests to doctest (#5383)
This change moves some tests from the current unit tests that are compiled into the rippled binary to using the doctest framework.
2025-06-26 19:35:31 +00:00
Jingchen
df6daf0d8f Add XRPL_ABANDON and use it to abandon OwnerPaysFee (#5510) 2025-06-26 12:09:05 -04:00
Jingchen
e9d46f0bfc Remove OwnerPaysFee as it's never fully supported (#5435)
The OwnerPaysFee amendment was never fully supported, and this change removes the feature to the extent possible.
2025-06-24 18:56:58 +00:00
Bart
42fd74b77b Removes release notes from codebase (#5508) 2025-06-24 13:10:00 -04:00
tequ
c55ea56c5e Add nftoken_id, nftoken_ids, offer_id to meta for transaction stream (#5230) 2025-06-24 09:02:22 -04:00
57 changed files with 3713 additions and 9292 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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()

File diff suppressed because it is too large Load Diff

41
cmake/xrpl_add_test.cmake Normal file
View 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()

View File

@@ -24,6 +24,7 @@ class Xrpl(ConanFile):
}
requires = [
'doctest/2.4.11',
'grpc/1.50.1',
'libarchive/3.7.6',
'nudb/2.0.8',

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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

View File

@@ -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();

View File

@@ -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);

View File

@@ -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;

View File

@@ -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,

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -1,74 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/XRPLF/rippled/
Copyright (c) 2023 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <xrpl/beast/unit_test.h>
#include <xrpl/beast/unit_test/suite.h>
#include <xrpl/protocol/ApiVersion.h>
#include <array>
#include <cstdint>
#include <limits>
#include <optional>
#include <type_traits>
#include <utility>
namespace ripple {
namespace test {
struct ApiVersion_test : beast::unit_test::suite
{
void
run() override
{
{
testcase("API versions invariants");
static_assert(
RPC::apiMinimumSupportedVersion <=
RPC::apiMaximumSupportedVersion);
static_assert(
RPC::apiMinimumSupportedVersion <= RPC::apiMaximumValidVersion);
static_assert(
RPC::apiMaximumSupportedVersion <= RPC::apiMaximumValidVersion);
static_assert(RPC::apiBetaVersion <= RPC::apiMaximumValidVersion);
BEAST_EXPECT(true);
}
{
// Update when we change versions
testcase("API versions");
static_assert(RPC::apiMinimumSupportedVersion >= 1);
static_assert(RPC::apiMinimumSupportedVersion < 2);
static_assert(RPC::apiMaximumSupportedVersion >= 2);
static_assert(RPC::apiMaximumSupportedVersion < 3);
static_assert(RPC::apiMaximumValidVersion >= 3);
static_assert(RPC::apiMaximumValidVersion < 4);
static_assert(RPC::apiBetaVersion >= 3);
static_assert(RPC::apiBetaVersion < 4);
BEAST_EXPECT(true);
}
}
};
BEAST_DEFINE_TESTSUITE(ApiVersion, protocol, ripple);
} // namespace test
} // namespace ripple

View File

@@ -1,118 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2020 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <xrpl/beast/unit_test.h>
#include <xrpl/protocol/BuildInfo.h>
namespace ripple {
class BuildInfo_test : public beast::unit_test::suite
{
public:
void
testEncodeSoftwareVersion()
{
testcase("EncodeSoftwareVersion");
auto encodedVersion = BuildInfo::encodeSoftwareVersion("1.2.3-b7");
// the first two bytes identify the particular implementation, 0x183B
BEAST_EXPECT(
(encodedVersion & 0xFFFF'0000'0000'0000LLU) ==
0x183B'0000'0000'0000LLU);
// the next three bytes: major version, minor version, patch version,
// 0x010203
BEAST_EXPECT(
(encodedVersion & 0x0000'FFFF'FF00'0000LLU) ==
0x0000'0102'0300'0000LLU);
// the next two bits:
{
// 01 if a beta
BEAST_EXPECT(
(encodedVersion & 0x0000'0000'00C0'0000LLU) >> 22 == 0b01);
// 10 if an RC
encodedVersion = BuildInfo::encodeSoftwareVersion("1.2.4-rc7");
BEAST_EXPECT(
(encodedVersion & 0x0000'0000'00C0'0000LLU) >> 22 == 0b10);
// 11 if neither an RC nor a beta
encodedVersion = BuildInfo::encodeSoftwareVersion("1.2.5");
BEAST_EXPECT(
(encodedVersion & 0x0000'0000'00C0'0000LLU) >> 22 == 0b11);
}
// the next six bits: rc/beta number (1-63)
encodedVersion = BuildInfo::encodeSoftwareVersion("1.2.6-b63");
BEAST_EXPECT((encodedVersion & 0x0000'0000'003F'0000LLU) >> 16 == 63);
// the last two bytes are zeros
BEAST_EXPECT((encodedVersion & 0x0000'0000'0000'FFFFLLU) == 0);
// Test some version strings with wrong formats:
// no rc/beta number
encodedVersion = BuildInfo::encodeSoftwareVersion("1.2.3-b");
BEAST_EXPECT((encodedVersion & 0x0000'0000'00FF'0000LLU) == 0);
// rc/beta number out of range
encodedVersion = BuildInfo::encodeSoftwareVersion("1.2.3-b64");
BEAST_EXPECT((encodedVersion & 0x0000'0000'00FF'0000LLU) == 0);
// Check that the rc/beta number of a release is 0:
encodedVersion = BuildInfo::encodeSoftwareVersion("1.2.6");
BEAST_EXPECT((encodedVersion & 0x0000'0000'003F'0000LLU) == 0);
}
void
testIsRippledVersion()
{
testcase("IsRippledVersion");
auto vFF = 0xFFFF'FFFF'FFFF'FFFFLLU;
BEAST_EXPECT(!BuildInfo::isRippledVersion(vFF));
auto vRippled = 0x183B'0000'0000'0000LLU;
BEAST_EXPECT(BuildInfo::isRippledVersion(vRippled));
}
void
testIsNewerVersion()
{
testcase("IsNewerVersion");
auto vFF = 0xFFFF'FFFF'FFFF'FFFFLLU;
BEAST_EXPECT(!BuildInfo::isNewerVersion(vFF));
auto v159 = BuildInfo::encodeSoftwareVersion("1.5.9");
BEAST_EXPECT(!BuildInfo::isNewerVersion(v159));
auto vCurrent = BuildInfo::getEncodedVersion();
BEAST_EXPECT(!BuildInfo::isNewerVersion(vCurrent));
auto vMax = BuildInfo::encodeSoftwareVersion("255.255.255");
BEAST_EXPECT(BuildInfo::isNewerVersion(vMax));
}
void
run() override
{
testEncodeSoftwareVersion();
testIsRippledVersion();
testIsNewerVersion();
}
};
BEAST_DEFINE_TESTSUITE(BuildInfo, protocol, ripple);
} // namespace ripple

View File

@@ -1,198 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012-2017 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <test/jtx.h>
#include <xrpl/protocol/Feature.h>
#include <functional>
#include <vector>
namespace ripple {
class Hooks_test : public beast::unit_test::suite
{
/**
* This unit test was requested here:
* https://github.com/ripple/rippled/pull/4089#issuecomment-1050274539
* These are tests that exercise facilities that are reserved for when Hooks
* is merged in the future.
**/
void
testHookFields()
{
testcase("Test Hooks fields");
using namespace test::jtx;
std::vector<std::reference_wrapper<SField const>> fields_to_test = {
sfHookResult,
sfHookStateChangeCount,
sfHookEmitCount,
sfHookExecutionIndex,
sfHookApiVersion,
sfHookStateCount,
sfEmitGeneration,
sfHookOn,
sfHookInstructionCount,
sfEmitBurden,
sfHookReturnCode,
sfReferenceCount,
sfEmitParentTxnID,
sfEmitNonce,
sfEmitHookHash,
sfHookStateKey,
sfHookHash,
sfHookNamespace,
sfHookSetTxnID,
sfHookStateData,
sfHookReturnString,
sfHookParameterName,
sfHookParameterValue,
sfEmitCallback,
sfHookAccount,
sfEmittedTxn,
sfHook,
sfHookDefinition,
sfHookParameter,
sfHookGrant,
sfEmitDetails,
sfHookExecutions,
sfHookExecution,
sfHookParameters,
sfHooks,
sfHookGrants};
for (auto const& rf : fields_to_test)
{
SField const& f = rf.get();
STObject dummy{sfGeneric};
BEAST_EXPECT(!dummy.isFieldPresent(f));
switch (f.fieldType)
{
case STI_UINT8: {
dummy.setFieldU8(f, 0);
BEAST_EXPECT(dummy.getFieldU8(f) == 0);
dummy.setFieldU8(f, 255);
BEAST_EXPECT(dummy.getFieldU8(f) == 255);
BEAST_EXPECT(dummy.isFieldPresent(f));
break;
}
case STI_UINT16: {
dummy.setFieldU16(f, 0);
BEAST_EXPECT(dummy.getFieldU16(f) == 0);
dummy.setFieldU16(f, 0xFFFFU);
BEAST_EXPECT(dummy.getFieldU16(f) == 0xFFFFU);
BEAST_EXPECT(dummy.isFieldPresent(f));
break;
}
case STI_UINT32: {
dummy.setFieldU32(f, 0);
BEAST_EXPECT(dummy.getFieldU32(f) == 0);
dummy.setFieldU32(f, 0xFFFFFFFFU);
BEAST_EXPECT(dummy.getFieldU32(f) == 0xFFFFFFFFU);
BEAST_EXPECT(dummy.isFieldPresent(f));
break;
}
case STI_UINT64: {
dummy.setFieldU64(f, 0);
BEAST_EXPECT(dummy.getFieldU64(f) == 0);
dummy.setFieldU64(f, 0xFFFFFFFFFFFFFFFFU);
BEAST_EXPECT(dummy.getFieldU64(f) == 0xFFFFFFFFFFFFFFFFU);
BEAST_EXPECT(dummy.isFieldPresent(f));
break;
}
case STI_UINT256: {
uint256 u = uint256::fromVoid(
"DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBE"
"EFDEADBEEF");
dummy.setFieldH256(f, u);
BEAST_EXPECT(dummy.getFieldH256(f) == u);
BEAST_EXPECT(dummy.isFieldPresent(f));
break;
}
case STI_VL: {
std::vector<uint8_t> v{1, 2, 3};
dummy.setFieldVL(f, v);
BEAST_EXPECT(dummy.getFieldVL(f) == v);
BEAST_EXPECT(dummy.isFieldPresent(f));
break;
}
case STI_ACCOUNT: {
AccountID id = *parseBase58<AccountID>(
"rwfSjJNK2YQuN64bSWn7T2eY9FJAyAPYJT");
dummy.setAccountID(f, id);
BEAST_EXPECT(dummy.getAccountID(f) == id);
BEAST_EXPECT(dummy.isFieldPresent(f));
break;
}
case STI_OBJECT: {
dummy.emplace_back(STObject{f});
BEAST_EXPECT(dummy.getField(f).getFName() == f);
BEAST_EXPECT(dummy.isFieldPresent(f));
break;
}
case STI_ARRAY: {
STArray dummy2{f, 2};
dummy2.push_back(STObject{sfGeneric});
dummy2.push_back(STObject{sfGeneric});
dummy.setFieldArray(f, dummy2);
BEAST_EXPECT(dummy.getFieldArray(f) == dummy2);
BEAST_EXPECT(dummy.isFieldPresent(f));
break;
}
default:
BEAST_EXPECT(false);
}
}
}
public:
void
run() override
{
using namespace test::jtx;
testHookFields();
}
};
BEAST_DEFINE_TESTSUITE(Hooks, protocol, ripple);
} // namespace ripple

View File

@@ -1,983 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <xrpl/basics/UnorderedContainers.h>
#include <xrpl/beast/unit_test.h>
#include <xrpl/protocol/Book.h>
#include <xrpl/protocol/Issue.h>
#include <sys/types.h>
#include <map>
#include <optional>
#include <set>
#include <typeinfo>
#include <unordered_set>
#if BEAST_MSVC
#define STL_SET_HAS_EMPLACE 1
#else
#define STL_SET_HAS_EMPLACE 0
#endif
#ifndef RIPPLE_ASSETS_ENABLE_STD_HASH
#if BEAST_MAC || BEAST_IOS
#define RIPPLE_ASSETS_ENABLE_STD_HASH 0
#else
#define RIPPLE_ASSETS_ENABLE_STD_HASH 1
#endif
#endif
namespace ripple {
class Issue_test : public beast::unit_test::suite
{
public:
using Domain = uint256;
// Comparison, hash tests for uint60 (via base_uint)
template <typename Unsigned>
void
testUnsigned()
{
Unsigned const u1(1);
Unsigned const u2(2);
Unsigned const u3(3);
BEAST_EXPECT(u1 != u2);
BEAST_EXPECT(u1 < u2);
BEAST_EXPECT(u1 <= u2);
BEAST_EXPECT(u2 <= u2);
BEAST_EXPECT(u2 == u2);
BEAST_EXPECT(u2 >= u2);
BEAST_EXPECT(u3 >= u2);
BEAST_EXPECT(u3 > u2);
std::hash<Unsigned> hash;
BEAST_EXPECT(hash(u1) == hash(u1));
BEAST_EXPECT(hash(u2) == hash(u2));
BEAST_EXPECT(hash(u3) == hash(u3));
BEAST_EXPECT(hash(u1) != hash(u2));
BEAST_EXPECT(hash(u1) != hash(u3));
BEAST_EXPECT(hash(u2) != hash(u3));
}
//--------------------------------------------------------------------------
// Comparison, hash tests for Issue
template <class Issue>
void
testIssue()
{
Currency const c1(1);
AccountID const i1(1);
Currency const c2(2);
AccountID const i2(2);
Currency const c3(3);
AccountID const i3(3);
BEAST_EXPECT(Issue(c1, i1) != Issue(c2, i1));
BEAST_EXPECT(Issue(c1, i1) < Issue(c2, i1));
BEAST_EXPECT(Issue(c1, i1) <= Issue(c2, i1));
BEAST_EXPECT(Issue(c2, i1) <= Issue(c2, i1));
BEAST_EXPECT(Issue(c2, i1) == Issue(c2, i1));
BEAST_EXPECT(Issue(c2, i1) >= Issue(c2, i1));
BEAST_EXPECT(Issue(c3, i1) >= Issue(c2, i1));
BEAST_EXPECT(Issue(c3, i1) > Issue(c2, i1));
BEAST_EXPECT(Issue(c1, i1) != Issue(c1, i2));
BEAST_EXPECT(Issue(c1, i1) < Issue(c1, i2));
BEAST_EXPECT(Issue(c1, i1) <= Issue(c1, i2));
BEAST_EXPECT(Issue(c1, i2) <= Issue(c1, i2));
BEAST_EXPECT(Issue(c1, i2) == Issue(c1, i2));
BEAST_EXPECT(Issue(c1, i2) >= Issue(c1, i2));
BEAST_EXPECT(Issue(c1, i3) >= Issue(c1, i2));
BEAST_EXPECT(Issue(c1, i3) > Issue(c1, i2));
std::hash<Issue> hash;
BEAST_EXPECT(hash(Issue(c1, i1)) == hash(Issue(c1, i1)));
BEAST_EXPECT(hash(Issue(c1, i2)) == hash(Issue(c1, i2)));
BEAST_EXPECT(hash(Issue(c1, i3)) == hash(Issue(c1, i3)));
BEAST_EXPECT(hash(Issue(c2, i1)) == hash(Issue(c2, i1)));
BEAST_EXPECT(hash(Issue(c2, i2)) == hash(Issue(c2, i2)));
BEAST_EXPECT(hash(Issue(c2, i3)) == hash(Issue(c2, i3)));
BEAST_EXPECT(hash(Issue(c3, i1)) == hash(Issue(c3, i1)));
BEAST_EXPECT(hash(Issue(c3, i2)) == hash(Issue(c3, i2)));
BEAST_EXPECT(hash(Issue(c3, i3)) == hash(Issue(c3, i3)));
BEAST_EXPECT(hash(Issue(c1, i1)) != hash(Issue(c1, i2)));
BEAST_EXPECT(hash(Issue(c1, i1)) != hash(Issue(c1, i3)));
BEAST_EXPECT(hash(Issue(c1, i1)) != hash(Issue(c2, i1)));
BEAST_EXPECT(hash(Issue(c1, i1)) != hash(Issue(c2, i2)));
BEAST_EXPECT(hash(Issue(c1, i1)) != hash(Issue(c2, i3)));
BEAST_EXPECT(hash(Issue(c1, i1)) != hash(Issue(c3, i1)));
BEAST_EXPECT(hash(Issue(c1, i1)) != hash(Issue(c3, i2)));
BEAST_EXPECT(hash(Issue(c1, i1)) != hash(Issue(c3, i3)));
}
template <class Set>
void
testIssueSet()
{
Currency const c1(1);
AccountID const i1(1);
Currency const c2(2);
AccountID const i2(2);
Issue const a1(c1, i1);
Issue const a2(c2, i2);
{
Set c;
c.insert(a1);
if (!BEAST_EXPECT(c.size() == 1))
return;
c.insert(a2);
if (!BEAST_EXPECT(c.size() == 2))
return;
if (!BEAST_EXPECT(c.erase(Issue(c1, i2)) == 0))
return;
if (!BEAST_EXPECT(c.erase(Issue(c1, i1)) == 1))
return;
if (!BEAST_EXPECT(c.erase(Issue(c2, i2)) == 1))
return;
if (!BEAST_EXPECT(c.empty()))
return;
}
{
Set c;
c.insert(a1);
if (!BEAST_EXPECT(c.size() == 1))
return;
c.insert(a2);
if (!BEAST_EXPECT(c.size() == 2))
return;
if (!BEAST_EXPECT(c.erase(Issue(c1, i2)) == 0))
return;
if (!BEAST_EXPECT(c.erase(Issue(c1, i1)) == 1))
return;
if (!BEAST_EXPECT(c.erase(Issue(c2, i2)) == 1))
return;
if (!BEAST_EXPECT(c.empty()))
return;
#if STL_SET_HAS_EMPLACE
c.emplace(c1, i1);
if (!BEAST_EXPECT(c.size() == 1))
return;
c.emplace(c2, i2);
if (!BEAST_EXPECT(c.size() == 2))
return;
#endif
}
}
template <class Map>
void
testIssueMap()
{
Currency const c1(1);
AccountID const i1(1);
Currency const c2(2);
AccountID const i2(2);
Issue const a1(c1, i1);
Issue const a2(c2, i2);
{
Map c;
c.insert(std::make_pair(a1, 1));
if (!BEAST_EXPECT(c.size() == 1))
return;
c.insert(std::make_pair(a2, 2));
if (!BEAST_EXPECT(c.size() == 2))
return;
if (!BEAST_EXPECT(c.erase(Issue(c1, i2)) == 0))
return;
if (!BEAST_EXPECT(c.erase(Issue(c1, i1)) == 1))
return;
if (!BEAST_EXPECT(c.erase(Issue(c2, i2)) == 1))
return;
if (!BEAST_EXPECT(c.empty()))
return;
}
{
Map c;
c.insert(std::make_pair(a1, 1));
if (!BEAST_EXPECT(c.size() == 1))
return;
c.insert(std::make_pair(a2, 2));
if (!BEAST_EXPECT(c.size() == 2))
return;
if (!BEAST_EXPECT(c.erase(Issue(c1, i2)) == 0))
return;
if (!BEAST_EXPECT(c.erase(Issue(c1, i1)) == 1))
return;
if (!BEAST_EXPECT(c.erase(Issue(c2, i2)) == 1))
return;
if (!BEAST_EXPECT(c.empty()))
return;
}
}
template <class Set>
void
testIssueDomainSet()
{
Currency const c1(1);
AccountID const i1(1);
Currency const c2(2);
AccountID const i2(2);
Issue const a1(c1, i1);
Issue const a2(c2, i2);
uint256 const domain1{1};
uint256 const domain2{2};
Set c;
c.insert(std::make_pair(a1, domain1));
if (!BEAST_EXPECT(c.size() == 1))
return;
c.insert(std::make_pair(a2, domain1));
if (!BEAST_EXPECT(c.size() == 2))
return;
c.insert(std::make_pair(a2, domain2));
if (!BEAST_EXPECT(c.size() == 3))
return;
if (!BEAST_EXPECT(c.erase(std::make_pair(Issue(c1, i2), domain1)) == 0))
return;
if (!BEAST_EXPECT(c.erase(std::make_pair(a1, domain1)) == 1))
return;
if (!BEAST_EXPECT(c.erase(std::make_pair(a2, domain1)) == 1))
return;
if (!BEAST_EXPECT(c.erase(std::make_pair(a2, domain2)) == 1))
return;
if (!BEAST_EXPECT(c.empty()))
return;
}
template <class Map>
void
testIssueDomainMap()
{
Currency const c1(1);
AccountID const i1(1);
Currency const c2(2);
AccountID const i2(2);
Issue const a1(c1, i1);
Issue const a2(c2, i2);
uint256 const domain1{1};
uint256 const domain2{2};
Map c;
c.insert(std::make_pair(std::make_pair(a1, domain1), 1));
if (!BEAST_EXPECT(c.size() == 1))
return;
c.insert(std::make_pair(std::make_pair(a2, domain1), 2));
if (!BEAST_EXPECT(c.size() == 2))
return;
c.insert(std::make_pair(std::make_pair(a2, domain2), 2));
if (!BEAST_EXPECT(c.size() == 3))
return;
if (!BEAST_EXPECT(c.erase(std::make_pair(Issue(c1, i2), domain1)) == 0))
return;
if (!BEAST_EXPECT(c.erase(std::make_pair(a1, domain1)) == 1))
return;
if (!BEAST_EXPECT(c.erase(std::make_pair(a2, domain1)) == 1))
return;
if (!BEAST_EXPECT(c.erase(std::make_pair(a2, domain2)) == 1))
return;
if (!BEAST_EXPECT(c.empty()))
return;
}
void
testIssueDomainSets()
{
testcase("std::set <std::pair<Issue, Domain>>");
testIssueDomainSet<std::set<std::pair<Issue, Domain>>>();
testcase("std::set <std::pair<Issue, Domain>>");
testIssueDomainSet<std::set<std::pair<Issue, Domain>>>();
testcase("hash_set <std::pair<Issue, Domain>>");
testIssueDomainSet<hash_set<std::pair<Issue, Domain>>>();
testcase("hash_set <std::pair<Issue, Domain>>");
testIssueDomainSet<hash_set<std::pair<Issue, Domain>>>();
}
void
testIssueDomainMaps()
{
testcase("std::map <std::pair<Issue, Domain>, int>");
testIssueDomainMap<std::map<std::pair<Issue, Domain>, int>>();
testcase("std::map <std::pair<Issue, Domain>, int>");
testIssueDomainMap<std::map<std::pair<Issue, Domain>, int>>();
#if RIPPLE_ASSETS_ENABLE_STD_HASH
testcase("hash_map <std::pair<Issue, Domain>, int>");
testIssueDomainMap<hash_map<std::pair<Issue, Domain>, int>>();
testcase("hash_map <std::pair<Issue, Domain>, int>");
testIssueDomainMap<hash_map<std::pair<Issue, Domain>, int>>();
testcase("hardened_hash_map <std::pair<Issue, Domain>, int>");
testIssueDomainMap<hardened_hash_map<std::pair<Issue, Domain>, int>>();
testcase("hardened_hash_map <std::pair<Issue, Domain>, int>");
testIssueDomainMap<hardened_hash_map<std::pair<Issue, Domain>, int>>();
#endif
}
void
testIssueSets()
{
testcase("std::set <Issue>");
testIssueSet<std::set<Issue>>();
testcase("std::set <Issue>");
testIssueSet<std::set<Issue>>();
#if RIPPLE_ASSETS_ENABLE_STD_HASH
testcase("std::unordered_set <Issue>");
testIssueSet<std::unordered_set<Issue>>();
testcase("std::unordered_set <Issue>");
testIssueSet<std::unordered_set<Issue>>();
#endif
testcase("hash_set <Issue>");
testIssueSet<hash_set<Issue>>();
testcase("hash_set <Issue>");
testIssueSet<hash_set<Issue>>();
}
void
testIssueMaps()
{
testcase("std::map <Issue, int>");
testIssueMap<std::map<Issue, int>>();
testcase("std::map <Issue, int>");
testIssueMap<std::map<Issue, int>>();
#if RIPPLE_ASSETS_ENABLE_STD_HASH
testcase("std::unordered_map <Issue, int>");
testIssueMap<std::unordered_map<Issue, int>>();
testcase("std::unordered_map <Issue, int>");
testIssueMap<std::unordered_map<Issue, int>>();
testcase("hash_map <Issue, int>");
testIssueMap<hash_map<Issue, int>>();
testcase("hash_map <Issue, int>");
testIssueMap<hash_map<Issue, int>>();
#endif
}
//--------------------------------------------------------------------------
// Comparison, hash tests for Book
template <class Book>
void
testBook()
{
Currency const c1(1);
AccountID const i1(1);
Currency const c2(2);
AccountID const i2(2);
Currency const c3(3);
AccountID const i3(3);
Issue a1(c1, i1);
Issue a2(c1, i2);
Issue a3(c2, i2);
Issue a4(c3, i2);
uint256 const domain1{1};
uint256 const domain2{2};
// Books without domains
BEAST_EXPECT(Book(a1, a2, std::nullopt) != Book(a2, a3, std::nullopt));
BEAST_EXPECT(Book(a1, a2, std::nullopt) < Book(a2, a3, std::nullopt));
BEAST_EXPECT(Book(a1, a2, std::nullopt) <= Book(a2, a3, std::nullopt));
BEAST_EXPECT(Book(a2, a3, std::nullopt) <= Book(a2, a3, std::nullopt));
BEAST_EXPECT(Book(a2, a3, std::nullopt) == Book(a2, a3, std::nullopt));
BEAST_EXPECT(Book(a2, a3, std::nullopt) >= Book(a2, a3, std::nullopt));
BEAST_EXPECT(Book(a3, a4, std::nullopt) >= Book(a2, a3, std::nullopt));
BEAST_EXPECT(Book(a3, a4, std::nullopt) > Book(a2, a3, std::nullopt));
// test domain books
{
// Books with different domains
BEAST_EXPECT(Book(a2, a3, domain1) != Book(a2, a3, domain2));
BEAST_EXPECT(Book(a2, a3, domain1) < Book(a2, a3, domain2));
BEAST_EXPECT(Book(a2, a3, domain2) > Book(a2, a3, domain1));
// One Book has a domain, the other does not
BEAST_EXPECT(Book(a2, a3, domain1) != Book(a2, a3, std::nullopt));
BEAST_EXPECT(Book(a2, a3, std::nullopt) < Book(a2, a3, domain1));
BEAST_EXPECT(Book(a2, a3, domain1) > Book(a2, a3, std::nullopt));
// Both Books have the same domain
BEAST_EXPECT(Book(a2, a3, domain1) == Book(a2, a3, domain1));
BEAST_EXPECT(Book(a2, a3, domain2) == Book(a2, a3, domain2));
BEAST_EXPECT(
Book(a2, a3, std::nullopt) == Book(a2, a3, std::nullopt));
// Both Books have no domain
BEAST_EXPECT(
Book(a2, a3, std::nullopt) == Book(a2, a3, std::nullopt));
// Testing comparisons with >= and <=
// When comparing books with domain1 vs domain2
BEAST_EXPECT(Book(a2, a3, domain1) <= Book(a2, a3, domain2));
BEAST_EXPECT(Book(a2, a3, domain2) >= Book(a2, a3, domain1));
BEAST_EXPECT(Book(a2, a3, domain1) >= Book(a2, a3, domain1));
BEAST_EXPECT(Book(a2, a3, domain2) <= Book(a2, a3, domain2));
// One Book has domain1 and the other has no domain
BEAST_EXPECT(Book(a2, a3, domain1) > Book(a2, a3, std::nullopt));
BEAST_EXPECT(Book(a2, a3, std::nullopt) < Book(a2, a3, domain1));
// One Book has domain2 and the other has no domain
BEAST_EXPECT(Book(a2, a3, domain2) > Book(a2, a3, std::nullopt));
BEAST_EXPECT(Book(a2, a3, std::nullopt) < Book(a2, a3, domain2));
// Comparing two Books with no domains
BEAST_EXPECT(
Book(a2, a3, std::nullopt) <= Book(a2, a3, std::nullopt));
BEAST_EXPECT(
Book(a2, a3, std::nullopt) >= Book(a2, a3, std::nullopt));
// Test case where domain1 is less than domain2
BEAST_EXPECT(Book(a2, a3, domain1) <= Book(a2, a3, domain2));
BEAST_EXPECT(Book(a2, a3, domain2) >= Book(a2, a3, domain1));
// Test case where domain2 is equal to domain1
BEAST_EXPECT(Book(a2, a3, domain1) >= Book(a2, a3, domain1));
BEAST_EXPECT(Book(a2, a3, domain1) <= Book(a2, a3, domain1));
// More test cases involving a4 (with domain2)
// Comparing Book with domain2 (a4) to a Book with domain1
BEAST_EXPECT(Book(a2, a3, domain1) < Book(a3, a4, domain2));
BEAST_EXPECT(Book(a3, a4, domain2) > Book(a2, a3, domain1));
// Comparing Book with domain2 (a4) to a Book with no domain
BEAST_EXPECT(Book(a3, a4, domain2) > Book(a2, a3, std::nullopt));
BEAST_EXPECT(Book(a2, a3, std::nullopt) < Book(a3, a4, domain2));
// Comparing Book with domain2 (a4) to a Book with the same domain
BEAST_EXPECT(Book(a3, a4, domain2) == Book(a3, a4, domain2));
// Comparing Book with domain2 (a4) to a Book with domain1
BEAST_EXPECT(Book(a2, a3, domain1) < Book(a3, a4, domain2));
BEAST_EXPECT(Book(a3, a4, domain2) > Book(a2, a3, domain1));
}
std::hash<Book> hash;
// log << std::hex << hash (Book (a1, a2));
// log << std::hex << hash (Book (a1, a2));
//
// log << std::hex << hash (Book (a1, a3));
// log << std::hex << hash (Book (a1, a3));
//
// log << std::hex << hash (Book (a1, a4));
// log << std::hex << hash (Book (a1, a4));
//
// log << std::hex << hash (Book (a2, a3));
// log << std::hex << hash (Book (a2, a3));
//
// log << std::hex << hash (Book (a2, a4));
// log << std::hex << hash (Book (a2, a4));
//
// log << std::hex << hash (Book (a3, a4));
// log << std::hex << hash (Book (a3, a4));
BEAST_EXPECT(
hash(Book(a1, a2, std::nullopt)) ==
hash(Book(a1, a2, std::nullopt)));
BEAST_EXPECT(
hash(Book(a1, a3, std::nullopt)) ==
hash(Book(a1, a3, std::nullopt)));
BEAST_EXPECT(
hash(Book(a1, a4, std::nullopt)) ==
hash(Book(a1, a4, std::nullopt)));
BEAST_EXPECT(
hash(Book(a2, a3, std::nullopt)) ==
hash(Book(a2, a3, std::nullopt)));
BEAST_EXPECT(
hash(Book(a2, a4, std::nullopt)) ==
hash(Book(a2, a4, std::nullopt)));
BEAST_EXPECT(
hash(Book(a3, a4, std::nullopt)) ==
hash(Book(a3, a4, std::nullopt)));
BEAST_EXPECT(
hash(Book(a1, a2, std::nullopt)) !=
hash(Book(a1, a3, std::nullopt)));
BEAST_EXPECT(
hash(Book(a1, a2, std::nullopt)) !=
hash(Book(a1, a4, std::nullopt)));
BEAST_EXPECT(
hash(Book(a1, a2, std::nullopt)) !=
hash(Book(a2, a3, std::nullopt)));
BEAST_EXPECT(
hash(Book(a1, a2, std::nullopt)) !=
hash(Book(a2, a4, std::nullopt)));
BEAST_EXPECT(
hash(Book(a1, a2, std::nullopt)) !=
hash(Book(a3, a4, std::nullopt)));
// Books with domain
BEAST_EXPECT(
hash(Book(a1, a2, domain1)) == hash(Book(a1, a2, domain1)));
BEAST_EXPECT(
hash(Book(a1, a3, domain1)) == hash(Book(a1, a3, domain1)));
BEAST_EXPECT(
hash(Book(a1, a4, domain1)) == hash(Book(a1, a4, domain1)));
BEAST_EXPECT(
hash(Book(a2, a3, domain1)) == hash(Book(a2, a3, domain1)));
BEAST_EXPECT(
hash(Book(a2, a4, domain1)) == hash(Book(a2, a4, domain1)));
BEAST_EXPECT(
hash(Book(a3, a4, domain1)) == hash(Book(a3, a4, domain1)));
BEAST_EXPECT(
hash(Book(a1, a2, std::nullopt)) ==
hash(Book(a1, a2, std::nullopt)));
// Comparing Books with domain1 vs no domain
BEAST_EXPECT(
hash(Book(a1, a2, std::nullopt)) != hash(Book(a1, a2, domain1)));
BEAST_EXPECT(
hash(Book(a1, a3, std::nullopt)) != hash(Book(a1, a3, domain1)));
BEAST_EXPECT(
hash(Book(a1, a4, std::nullopt)) != hash(Book(a1, a4, domain1)));
BEAST_EXPECT(
hash(Book(a2, a3, std::nullopt)) != hash(Book(a2, a3, domain1)));
BEAST_EXPECT(
hash(Book(a2, a4, std::nullopt)) != hash(Book(a2, a4, domain1)));
BEAST_EXPECT(
hash(Book(a3, a4, std::nullopt)) != hash(Book(a3, a4, domain1)));
// Books with domain1 but different Issues
BEAST_EXPECT(
hash(Book(a1, a2, domain1)) != hash(Book(a1, a3, domain1)));
BEAST_EXPECT(
hash(Book(a1, a2, domain1)) != hash(Book(a1, a4, domain1)));
BEAST_EXPECT(
hash(Book(a2, a3, domain1)) != hash(Book(a2, a4, domain1)));
BEAST_EXPECT(
hash(Book(a1, a2, domain1)) != hash(Book(a2, a3, domain1)));
BEAST_EXPECT(
hash(Book(a2, a4, domain1)) != hash(Book(a3, a4, domain1)));
BEAST_EXPECT(
hash(Book(a3, a4, domain1)) != hash(Book(a1, a4, domain1)));
// Books with domain1 and domain2
BEAST_EXPECT(
hash(Book(a1, a2, domain1)) != hash(Book(a1, a2, domain2)));
BEAST_EXPECT(
hash(Book(a1, a3, domain1)) != hash(Book(a1, a3, domain2)));
BEAST_EXPECT(
hash(Book(a1, a4, domain1)) != hash(Book(a1, a4, domain2)));
BEAST_EXPECT(
hash(Book(a2, a3, domain1)) != hash(Book(a2, a3, domain2)));
BEAST_EXPECT(
hash(Book(a2, a4, domain1)) != hash(Book(a2, a4, domain2)));
BEAST_EXPECT(
hash(Book(a3, a4, domain1)) != hash(Book(a3, a4, domain2)));
}
//--------------------------------------------------------------------------
template <class Set>
void
testBookSet()
{
Currency const c1(1);
AccountID const i1(1);
Currency const c2(2);
AccountID const i2(2);
Issue const a1(c1, i1);
Issue const a2(c2, i2);
Book const b1(a1, a2, std::nullopt);
Book const b2(a2, a1, std::nullopt);
uint256 const domain1{1};
uint256 const domain2{2};
Book const b1_d1(a1, a2, domain1);
Book const b2_d1(a2, a1, domain1);
Book const b1_d2(a1, a2, domain2);
Book const b2_d2(a2, a1, domain2);
{
Set c;
c.insert(b1);
if (!BEAST_EXPECT(c.size() == 1))
return;
c.insert(b2);
if (!BEAST_EXPECT(c.size() == 2))
return;
if (!BEAST_EXPECT(c.erase(Book(a1, a1, std::nullopt)) == 0))
return;
if (!BEAST_EXPECT(c.erase(Book(a1, a2, std::nullopt)) == 1))
return;
if (!BEAST_EXPECT(c.erase(Book(a2, a1, std::nullopt)) == 1))
return;
if (!BEAST_EXPECT(c.empty()))
return;
}
{
Set c;
c.insert(b1);
if (!BEAST_EXPECT(c.size() == 1))
return;
c.insert(b2);
if (!BEAST_EXPECT(c.size() == 2))
return;
if (!BEAST_EXPECT(c.erase(Book(a1, a1, std::nullopt)) == 0))
return;
if (!BEAST_EXPECT(c.erase(Book(a1, a2, std::nullopt)) == 1))
return;
if (!BEAST_EXPECT(c.erase(Book(a2, a1, std::nullopt)) == 1))
return;
if (!BEAST_EXPECT(c.empty()))
return;
#if STL_SET_HAS_EMPLACE
c.emplace(a1, a2);
if (!BEAST_EXPECT(c.size() == 1))
return;
c.emplace(a2, a1);
if (!BEAST_EXPECT(c.size() == 2))
return;
#endif
}
{
Set c;
c.insert(b1_d1);
if (!BEAST_EXPECT(c.size() == 1))
return;
c.insert(b2_d1);
if (!BEAST_EXPECT(c.size() == 2))
return;
c.insert(b1_d2);
if (!BEAST_EXPECT(c.size() == 3))
return;
c.insert(b2_d2);
if (!BEAST_EXPECT(c.size() == 4))
return;
// Try removing non-existent elements
if (!BEAST_EXPECT(c.erase(Book(a2, a2, domain1)) == 0))
return;
if (!BEAST_EXPECT(c.erase(Book(a1, a2, domain1)) == 1))
return;
if (!BEAST_EXPECT(c.erase(Book(a2, a1, domain1)) == 1))
return;
if (!BEAST_EXPECT(c.size() == 2))
return;
if (!BEAST_EXPECT(c.erase(Book(a1, a2, domain2)) == 1))
return;
if (!BEAST_EXPECT(c.erase(Book(a2, a1, domain2)) == 1))
return;
if (!BEAST_EXPECT(c.empty()))
return;
}
{
Set c;
c.insert(b1);
c.insert(b2);
c.insert(b1_d1);
c.insert(b2_d1);
if (!BEAST_EXPECT(c.size() == 4))
return;
if (!BEAST_EXPECT(c.erase(Book(a1, a2, std::nullopt)) == 1))
return;
if (!BEAST_EXPECT(c.erase(Book(a2, a1, std::nullopt)) == 1))
return;
if (!BEAST_EXPECT(c.size() == 2))
return;
if (!BEAST_EXPECT(c.erase(Book(a1, a2, domain1)) == 1))
return;
if (!BEAST_EXPECT(c.erase(Book(a2, a1, domain1)) == 1))
return;
if (!BEAST_EXPECT(c.empty()))
return;
}
}
template <class Map>
void
testBookMap()
{
Currency const c1(1);
AccountID const i1(1);
Currency const c2(2);
AccountID const i2(2);
Issue const a1(c1, i1);
Issue const a2(c2, i2);
Book const b1(a1, a2, std::nullopt);
Book const b2(a2, a1, std::nullopt);
uint256 const domain1{1};
uint256 const domain2{2};
Book const b1_d1(a1, a2, domain1);
Book const b2_d1(a2, a1, domain1);
Book const b1_d2(a1, a2, domain2);
Book const b2_d2(a2, a1, domain2);
// typename Map::value_type value_type;
// std::pair <Book const, int> value_type;
{
Map c;
// c.insert (value_type (b1, 1));
c.insert(std::make_pair(b1, 1));
if (!BEAST_EXPECT(c.size() == 1))
return;
// c.insert (value_type (b2, 2));
c.insert(std::make_pair(b2, 1));
if (!BEAST_EXPECT(c.size() == 2))
return;
if (!BEAST_EXPECT(c.erase(Book(a1, a1, std::nullopt)) == 0))
return;
if (!BEAST_EXPECT(c.erase(Book(a1, a2, std::nullopt)) == 1))
return;
if (!BEAST_EXPECT(c.erase(Book(a2, a1, std::nullopt)) == 1))
return;
if (!BEAST_EXPECT(c.empty()))
return;
}
{
Map c;
// c.insert (value_type (b1, 1));
c.insert(std::make_pair(b1, 1));
if (!BEAST_EXPECT(c.size() == 1))
return;
// c.insert (value_type (b2, 2));
c.insert(std::make_pair(b2, 1));
if (!BEAST_EXPECT(c.size() == 2))
return;
if (!BEAST_EXPECT(c.erase(Book(a1, a1, std::nullopt)) == 0))
return;
if (!BEAST_EXPECT(c.erase(Book(a1, a2, std::nullopt)) == 1))
return;
if (!BEAST_EXPECT(c.erase(Book(a2, a1, std::nullopt)) == 1))
return;
if (!BEAST_EXPECT(c.empty()))
return;
}
{
Map c;
c.insert(std::make_pair(b1_d1, 10));
if (!BEAST_EXPECT(c.size() == 1))
return;
c.insert(std::make_pair(b2_d1, 20));
if (!BEAST_EXPECT(c.size() == 2))
return;
c.insert(std::make_pair(b1_d2, 30));
if (!BEAST_EXPECT(c.size() == 3))
return;
c.insert(std::make_pair(b2_d2, 40));
if (!BEAST_EXPECT(c.size() == 4))
return;
// Try removing non-existent elements
if (!BEAST_EXPECT(c.erase(Book(a2, a2, domain1)) == 0))
return;
if (!BEAST_EXPECT(c.erase(Book(a1, a2, domain1)) == 1))
return;
if (!BEAST_EXPECT(c.erase(Book(a2, a1, domain1)) == 1))
return;
if (!BEAST_EXPECT(c.size() == 2))
return;
if (!BEAST_EXPECT(c.erase(Book(a1, a2, domain2)) == 1))
return;
if (!BEAST_EXPECT(c.erase(Book(a2, a1, domain2)) == 1))
return;
if (!BEAST_EXPECT(c.empty()))
return;
}
{
Map c;
c.insert(std::make_pair(b1, 1));
c.insert(std::make_pair(b2, 2));
c.insert(std::make_pair(b1_d1, 3));
c.insert(std::make_pair(b2_d1, 4));
if (!BEAST_EXPECT(c.size() == 4))
return;
// Try removing non-existent elements
if (!BEAST_EXPECT(c.erase(Book(a1, a1, domain1)) == 0))
return;
if (!BEAST_EXPECT(c.erase(Book(a2, a2, domain2)) == 0))
return;
if (!BEAST_EXPECT(c.erase(Book(a1, a2, std::nullopt)) == 1))
return;
if (!BEAST_EXPECT(c.erase(Book(a2, a1, std::nullopt)) == 1))
return;
if (!BEAST_EXPECT(c.size() == 2))
return;
if (!BEAST_EXPECT(c.erase(Book(a1, a2, domain1)) == 1))
return;
if (!BEAST_EXPECT(c.erase(Book(a2, a1, domain1)) == 1))
return;
if (!BEAST_EXPECT(c.empty()))
return;
}
}
void
testBookSets()
{
testcase("std::set <Book>");
testBookSet<std::set<Book>>();
testcase("std::set <Book>");
testBookSet<std::set<Book>>();
#if RIPPLE_ASSETS_ENABLE_STD_HASH
testcase("std::unordered_set <Book>");
testBookSet<std::unordered_set<Book>>();
testcase("std::unordered_set <Book>");
testBookSet<std::unordered_set<Book>>();
#endif
testcase("hash_set <Book>");
testBookSet<hash_set<Book>>();
testcase("hash_set <Book>");
testBookSet<hash_set<Book>>();
}
void
testBookMaps()
{
testcase("std::map <Book, int>");
testBookMap<std::map<Book, int>>();
testcase("std::map <Book, int>");
testBookMap<std::map<Book, int>>();
#if RIPPLE_ASSETS_ENABLE_STD_HASH
testcase("std::unordered_map <Book, int>");
testBookMap<std::unordered_map<Book, int>>();
testcase("std::unordered_map <Book, int>");
testBookMap<std::unordered_map<Book, int>>();
testcase("hash_map <Book, int>");
testBookMap<hash_map<Book, int>>();
testcase("hash_map <Book, int>");
testBookMap<hash_map<Book, int>>();
#endif
}
//--------------------------------------------------------------------------
void
run() override
{
testcase("Currency");
testUnsigned<Currency>();
testcase("AccountID");
testUnsigned<AccountID>();
// ---
testcase("Issue");
testIssue<Issue>();
testcase("Issue");
testIssue<Issue>();
testIssueSets();
testIssueMaps();
// ---
testcase("Book");
testBook<Book>();
testcase("Book");
testBook<Book>();
testBookSets();
testBookMaps();
// ---
testIssueDomainSets();
testIssueDomainMaps();
}
};
BEAST_DEFINE_TESTSUITE(Issue, protocol, ripple);
} // namespace ripple

View File

@@ -1,140 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2022 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <test/jtx.h>
#include <xrpl/basics/strHex.h>
namespace ripple {
class Memo_test : public beast::unit_test::suite
{
public:
void
testMemos()
{
testcase("Test memos");
using namespace test::jtx;
Account alice{"alice"};
Env env(*this);
env.fund(XRP(10000), alice);
env.close();
// Lambda that returns a valid JTx with a memo that we can hack up.
// This is the basis for building tests of invalid states.
auto makeJtxWithMemo = [&env, &alice]() {
JTx example = noop(alice);
memo const exampleMemo{"tic", "tac", "toe"};
exampleMemo(env, example);
return example;
};
// A valid memo.
env(makeJtxWithMemo());
env.close();
{
// Make sure that too big a memo is flagged as invalid.
JTx memoSize = makeJtxWithMemo();
memoSize.jv[sfMemos.jsonName][0u][sfMemo.jsonName]
[sfMemoData.jsonName] = std::string(2020, '0');
env(memoSize,
rpc("invalidTransaction",
"fails local checks: The memo exceeds the maximum allowed "
"size."));
// This memo is just barely small enough.
memoSize.jv[sfMemos.jsonName][0u][sfMemo.jsonName]
[sfMemoData.jsonName] = std::string(2018, '1');
env(memoSize);
}
{
// Put a non-Memo in the Memos array.
JTx memoNonMemo = noop(alice);
auto& jv = memoNonMemo.jv;
auto& ma = jv[sfMemos.jsonName];
auto& mi = ma[ma.size()];
auto& m = mi[sfCreatedNode.jsonName]; // CreatedNode in Memos
m[sfMemoData.jsonName] = "3030303030";
env(memoNonMemo,
rpc("invalidTransaction",
"fails local checks: A memo array may contain only Memo "
"objects."));
}
{
// Put an invalid field in a Memo object.
JTx memoExtra = makeJtxWithMemo();
memoExtra
.jv[sfMemos.jsonName][0u][sfMemo.jsonName][sfFlags.jsonName] =
13;
env(memoExtra,
rpc("invalidTransaction",
"fails local checks: A memo may contain only MemoType, "
"MemoData or MemoFormat fields."));
}
{
// Put a character that is not allowed in a URL in a MemoType field.
JTx memoBadChar = makeJtxWithMemo();
memoBadChar.jv[sfMemos.jsonName][0u][sfMemo.jsonName]
[sfMemoType.jsonName] =
strHex(std::string_view("ONE<INFINITY"));
env(memoBadChar,
rpc("invalidTransaction",
"fails local checks: The MemoType and MemoFormat fields "
"may only contain characters that are allowed in URLs "
"under RFC 3986."));
}
{
// Put a character that is not allowed in a URL in a MemoData field.
// That's okay.
JTx memoLegitChar = makeJtxWithMemo();
memoLegitChar.jv[sfMemos.jsonName][0u][sfMemo.jsonName]
[sfMemoData.jsonName] =
strHex(std::string_view("ONE<INFINITY"));
env(memoLegitChar);
}
{
// Put a character that is not allowed in a URL in a MemoFormat.
JTx memoBadChar = makeJtxWithMemo();
memoBadChar.jv[sfMemos.jsonName][0u][sfMemo.jsonName]
[sfMemoFormat.jsonName] =
strHex(std::string_view("NoBraces{}InURL"));
env(memoBadChar,
rpc("invalidTransaction",
"fails local checks: The MemoType and MemoFormat fields "
"may only contain characters that are allowed in URLs "
"under RFC 3986."));
}
}
//--------------------------------------------------------------------------
void
run() override
{
testMemos();
}
};
BEAST_DEFINE_TESTSUITE(Memo, ripple_data, ripple);
} // namespace ripple

File diff suppressed because it is too large Load Diff

View File

@@ -1,474 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <xrpl/beast/unit_test.h>
#include <xrpl/protocol/PublicKey.h>
#include <xrpl/protocol/SecretKey.h>
#include <vector>
namespace ripple {
class PublicKey_test : public beast::unit_test::suite
{
public:
using blob = std::vector<std::uint8_t>;
template <class FwdIter, class Container>
static void
hex_to_binary(FwdIter first, FwdIter last, Container& out)
{
struct Table
{
int val[256];
Table()
{
std::fill(val, val + 256, 0);
for (int i = 0; i < 10; ++i)
val['0' + i] = i;
for (int i = 0; i < 6; ++i)
{
val['A' + i] = 10 + i;
val['a' + i] = 10 + i;
}
}
int
operator[](int i)
{
return val[i];
}
};
static Table lut;
out.reserve(std::distance(first, last) / 2);
while (first != last)
{
auto const hi(lut[(*first++)]);
auto const lo(lut[(*first++)]);
out.push_back((hi * 16) + lo);
}
}
blob
sig(std::string const& hex)
{
blob b;
hex_to_binary(hex.begin(), hex.end(), b);
return b;
}
bool
check(std::optional<ECDSACanonicality> answer, std::string const& s)
{
return ecdsaCanonicality(makeSlice(sig(s))) == answer;
}
void
testCanonical()
{
testcase("Canonical");
// Fully canonical
BEAST_EXPECT(check(
ECDSACanonicality::fullyCanonical,
"3045"
"022100FF478110D1D4294471EC76E0157540C2181F47DEBD25D7F9E7DDCCCD47EE"
"E905"
"0220078F07CDAE6C240855D084AD91D1479609533C147C93B0AEF19BC9724D003F"
"28"));
BEAST_EXPECT(check(
ECDSACanonicality::fullyCanonical,
"3045"
"0221009218248292F1762D8A51BE80F8A7F2CD288D810CE781D5955700DA1684DF"
"1D2D"
"022041A1EE1746BFD72C9760CC93A7AAA8047D52C8833A03A20EAAE92EA19717B4"
"54"));
BEAST_EXPECT(check(
ECDSACanonicality::fullyCanonical,
"3044"
"02206A9E43775F73B6D1EC420E4DDD222A80D4C6DF5D1BEECC431A91B63C928B75"
"81"
"022023E9CC2D61DDA6F73EAA6BCB12688BEB0F434769276B3127E4044ED895C9D9"
"6B"));
BEAST_EXPECT(check(
ECDSACanonicality::fullyCanonical,
"3044"
"022056E720007221F3CD4EFBB6352741D8E5A0968D48D8D032C2FBC4F6304AD1D0"
"4E"
"02201F39EB392C20D7801C3E8D81D487E742FA84A1665E923225BD6323847C7187"
"9F"));
BEAST_EXPECT(check(
ECDSACanonicality::fullyCanonical,
"3045"
"022100FDFD5AD05518CEA0017A2DCB5C4DF61E7C73B6D3A38E7AE93210A1564E8C"
"2F12"
"0220214FF061CCC123C81D0BB9D0EDEA04CD40D96BF1425D311DA62A7096BB18EA"
"18"));
// Canonical but not fully canonical
BEAST_EXPECT(check(
ECDSACanonicality::canonical,
"3046"
"022100F477B3FA6F31C7CB3A0D1AD94A231FDD24B8D78862EE334CEA7CD08F6CBC"
"0A1B"
"022100928E6BCF1ED2684679730C5414AEC48FD62282B090041C41453C1D064AF5"
"97A1"));
BEAST_EXPECT(check(
ECDSACanonicality::canonical,
"3045"
"022063E7C7CA93CB2400E413A342C027D00665F8BAB9C22EF0A7B8AE3AAF092230"
"B6"
"0221008F2E8BB7D09521ABBC277717B14B93170AE6465C5A1B36561099319C4BEB"
"254C"));
BEAST_EXPECT(check(
ECDSACanonicality::canonical,
"3046"
"02210099DCA1188663DDEA506A06A7B20C2B7D8C26AFF41DECE69D6C5F7C967D32"
"625F"
"022100897658A6B1F9EEE5D140D7A332DA0BD73BB98974EA53F6201B01C1B594F2"
"86EA"));
BEAST_EXPECT(check(
ECDSACanonicality::canonical,
"3045"
"02200855DE366E4E323AA2CE2A25674401A7D11F72EC432770D07F7B57DF7387AE"
"C0"
"022100DA4C6ADDEA14888858DE2AC5B91ED9050D6972BB388DEF582628CEE32869"
"AE35"));
// valid
BEAST_EXPECT(check(
ECDSACanonicality::fullyCanonical,
"3006"
"020101"
"020102"));
BEAST_EXPECT(check(
ECDSACanonicality::fullyCanonical,
"3044"
"02203932c892e2e550f3af8ee4ce9c215a87f9bb831dcac87b2838e2c2eaa891df"
"0c"
"022030b61dd36543125d56b9f9f3a1f53189e5af33cdda8d77a5209aec03978fa0"
"01"));
BEAST_EXPECT(check(
ECDSACanonicality::canonical,
"3045"
"0220076045be6f9eca28ff1ec606b833d0b87e70b2a630f5e3a496b110967a40f9"
"0a"
"0221008fffd599910eefe00bc803c688eca1d2ba7f6b180620eaa03488e6585db6"
"ba01"));
BEAST_EXPECT(check(
ECDSACanonicality::canonical,
"3046"
"022100876045be6f9eca28ff1ec606b833d0b87e70b2a630f5e3a496b110967a40"
"f90a"
"0221008fffd599910eefe00bc803c688c2eca1d2ba7f6b180620eaa03488e6585d"
"b6ba"));
BEAST_EXPECT(check(
std::nullopt,
"3005"
"0201FF"
"0200"));
BEAST_EXPECT(check(
std::nullopt,
"3006"
"020101"
"020202"));
BEAST_EXPECT(check(
std::nullopt,
"3006"
"020701"
"020102"));
BEAST_EXPECT(check(
std::nullopt,
"3006"
"020401"
"020102"));
BEAST_EXPECT(check(
std::nullopt,
"3006"
"020501"
"020102"));
BEAST_EXPECT(check(
std::nullopt,
"3006"
"020201"
"020102"));
BEAST_EXPECT(check(
std::nullopt,
"3006"
"020301"
"020202"));
BEAST_EXPECT(check(
std::nullopt,
"3006"
"020401"
"020202"));
BEAST_EXPECT(check(
std::nullopt,
"3047"
"0221005990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba"
"6105"
"022200002d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e56"
"6695ed"));
BEAST_EXPECT(check(
std::nullopt,
"3144"
"02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
"05"
"02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695"
"ed"));
BEAST_EXPECT(check(
std::nullopt,
"3045"
"02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
"05"
"02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695"
"ed"));
BEAST_EXPECT(check(
std::nullopt,
"301F"
"01205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1"));
BEAST_EXPECT(check(
std::nullopt,
"3045"
"02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
"05"
"02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695"
"ed00"));
BEAST_EXPECT(check(
std::nullopt,
"3044"
"01205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
"05"
"02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695"
"ed"));
BEAST_EXPECT(check(
std::nullopt,
"3024"
"0200"
"02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695"
"ed"));
BEAST_EXPECT(check(
std::nullopt,
"3044"
"02208990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
"05"
"02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695"
"ed"));
BEAST_EXPECT(check(
std::nullopt,
"3045"
"0221005990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba"
"6105"
"02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695"
"ed"));
BEAST_EXPECT(check(
std::nullopt,
"3044"
"02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
"05012"
"02d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695e"
"d"));
BEAST_EXPECT(check(
std::nullopt,
"3024"
"02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
"05"
"0200"));
BEAST_EXPECT(check(
std::nullopt,
"3044"
"02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
"05"
"0220fd5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695"
"ed"));
BEAST_EXPECT(check(
std::nullopt,
"3045"
"02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
"05"
"0221002d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e5666"
"95ed"));
}
void
testBase58(KeyType keyType)
{
// Try converting short, long and malformed data
BEAST_EXPECT(!parseBase58<PublicKey>(TokenType::NodePublic, ""));
BEAST_EXPECT(!parseBase58<PublicKey>(TokenType::NodePublic, " "));
BEAST_EXPECT(
!parseBase58<PublicKey>(TokenType::NodePublic, "!ty89234gh45"));
auto const good = toBase58(
TokenType::NodePublic, derivePublicKey(keyType, randomSecretKey()));
// Short (non-empty) strings
{
auto s = good;
// Remove all characters from the string in random order:
std::hash<std::string> r;
while (!s.empty())
{
s.erase(r(s) % s.size(), 1);
BEAST_EXPECT(!parseBase58<PublicKey>(TokenType::NodePublic, s));
}
}
// Long strings
for (std::size_t i = 1; i != 16; i++)
{
auto s = good;
s.resize(s.size() + i, s[i % s.size()]);
BEAST_EXPECT(!parseBase58<PublicKey>(TokenType::NodePublic, s));
}
// Strings with invalid Base58 characters
for (auto c : std::string("0IOl"))
{
for (std::size_t i = 0; i != good.size(); ++i)
{
auto s = good;
s[i % s.size()] = c;
BEAST_EXPECT(!parseBase58<PublicKey>(TokenType::NodePublic, s));
}
}
// Strings with incorrect prefix
{
auto s = good;
for (auto c : std::string("apsrJqtv7"))
{
s[0] = c;
BEAST_EXPECT(!parseBase58<PublicKey>(TokenType::NodePublic, s));
}
}
// Try some random secret keys
std::vector<PublicKey> keys;
keys.reserve(32);
for (std::size_t i = 0; i != keys.capacity(); ++i)
keys.emplace_back(derivePublicKey(keyType, randomSecretKey()));
BEAST_EXPECT(keys.size() == 32);
for (std::size_t i = 0; i != keys.size(); ++i)
{
auto const si = toBase58(TokenType::NodePublic, keys[i]);
BEAST_EXPECT(!si.empty());
auto const ski = parseBase58<PublicKey>(TokenType::NodePublic, si);
BEAST_EXPECT(ski && (keys[i] == *ski));
for (std::size_t j = i; j != keys.size(); ++j)
{
BEAST_EXPECT((keys[i] == keys[j]) == (i == j));
auto const sj = toBase58(TokenType::NodePublic, keys[j]);
BEAST_EXPECT((si == sj) == (i == j));
auto const skj =
parseBase58<PublicKey>(TokenType::NodePublic, sj);
BEAST_EXPECT(skj && (keys[j] == *skj));
BEAST_EXPECT((*ski == *skj) == (i == j));
}
}
}
void
testBase58()
{
testcase("Base58: secp256k1");
{
auto const pk1 = derivePublicKey(
KeyType::secp256k1,
generateSecretKey(
KeyType::secp256k1, generateSeed("masterpassphrase")));
auto const pk2 = parseBase58<PublicKey>(
TokenType::NodePublic,
"n94a1u4jAz288pZLtw6yFWVbi89YamiC6JBXPVUj5zmExe5fTVg9");
BEAST_EXPECT(pk2);
BEAST_EXPECT(pk1 == *pk2);
}
testBase58(KeyType::secp256k1);
testcase("Base58: ed25519");
{
auto const pk1 = derivePublicKey(
KeyType::ed25519,
generateSecretKey(
KeyType::ed25519, generateSeed("masterpassphrase")));
auto const pk2 = parseBase58<PublicKey>(
TokenType::NodePublic,
"nHUeeJCSY2dM71oxM8Cgjouf5ekTuev2mwDpc374aLMxzDLXNmjf");
BEAST_EXPECT(pk2);
BEAST_EXPECT(pk1 == *pk2);
}
testBase58(KeyType::ed25519);
}
void
testMiscOperations()
{
testcase("Miscellaneous operations");
auto const pk1 = derivePublicKey(
KeyType::secp256k1,
generateSecretKey(
KeyType::secp256k1, generateSeed("masterpassphrase")));
PublicKey pk2(pk1);
BEAST_EXPECT(pk1 == pk2);
BEAST_EXPECT(pk2 == pk1);
PublicKey pk3 = derivePublicKey(
KeyType::secp256k1,
generateSecretKey(
KeyType::secp256k1, generateSeed("arbitraryPassPhrase")));
// Testing the copy assignment operation of PublicKey class
pk3 = pk2;
BEAST_EXPECT(pk3 == pk2);
BEAST_EXPECT(pk1 == pk3);
}
void
run() override
{
testBase58();
testCanonical();
testMiscOperations();
}
};
BEAST_DEFINE_TESTSUITE(PublicKey, protocol, ripple);
} // namespace ripple

View File

@@ -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");

View File

@@ -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
View 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.

View 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)

View 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();

View 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();

View 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));
}

View File

@@ -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);
}
}

View File

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

View 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);
}

View 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);
}

View 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();

View File

@@ -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));
}

View File

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

View File

@@ -0,0 +1,56 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/XRPLF/rippled/
Copyright (c) 2023 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <xrpl/protocol/ApiVersion.h>
#include <doctest/doctest.h>
using namespace ripple;
TEST_SUITE_BEGIN("ApiVersion");
TEST_CASE("API versions invariants")
{
static_assert(
RPC::apiMinimumSupportedVersion <= RPC::apiMaximumSupportedVersion);
static_assert(
RPC::apiMinimumSupportedVersion <= RPC::apiMaximumValidVersion);
static_assert(
RPC::apiMaximumSupportedVersion <= RPC::apiMaximumValidVersion);
static_assert(RPC::apiBetaVersion <= RPC::apiMaximumValidVersion);
CHECK(true);
}
TEST_CASE("API versions")
{
// Update when we change versions
static_assert(RPC::apiMinimumSupportedVersion >= 1);
static_assert(RPC::apiMinimumSupportedVersion < 2);
static_assert(RPC::apiMaximumSupportedVersion >= 2);
static_assert(RPC::apiMaximumSupportedVersion < 3);
static_assert(RPC::apiMaximumValidVersion >= 3);
static_assert(RPC::apiMaximumValidVersion < 4);
static_assert(RPC::apiBetaVersion >= 3);
static_assert(RPC::apiBetaVersion < 4);
CHECK(true);
}
TEST_SUITE_END();

View File

@@ -0,0 +1,98 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2020 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <xrpl/protocol/BuildInfo.h>
#include <doctest/doctest.h>
using namespace ripple;
TEST_SUITE_BEGIN("BuildInfo");
TEST_CASE("EncodeSoftwareVersion")
{
auto encodedVersion = BuildInfo::encodeSoftwareVersion("1.2.3-b7");
// the first two bytes identify the particular implementation, 0x183B
CHECK(
(encodedVersion & 0xFFFF'0000'0000'0000LLU) ==
0x183B'0000'0000'0000LLU);
// the next three bytes: major version, minor version, patch version,
// 0x010203
CHECK(
(encodedVersion & 0x0000'FFFF'FF00'0000LLU) ==
0x0000'0102'0300'0000LLU);
// the next two bits:
{
// 01 if a beta
CHECK((encodedVersion & 0x0000'0000'00C0'0000LLU) >> 22 == 0b01);
// 10 if an RC
encodedVersion = BuildInfo::encodeSoftwareVersion("1.2.4-rc7");
CHECK((encodedVersion & 0x0000'0000'00C0'0000LLU) >> 22 == 0b10);
// 11 if neither an RC nor a beta
encodedVersion = BuildInfo::encodeSoftwareVersion("1.2.5");
CHECK((encodedVersion & 0x0000'0000'00C0'0000LLU) >> 22 == 0b11);
}
// the next six bits: rc/beta number (1-63)
encodedVersion = BuildInfo::encodeSoftwareVersion("1.2.6-b63");
CHECK((encodedVersion & 0x0000'0000'003F'0000LLU) >> 16 == 63);
// the last two bytes are zeros
CHECK((encodedVersion & 0x0000'0000'0000'FFFFLLU) == 0);
// Test some version strings with wrong formats:
// no rc/beta number
encodedVersion = BuildInfo::encodeSoftwareVersion("1.2.3-b");
CHECK((encodedVersion & 0x0000'0000'00FF'0000LLU) == 0);
// rc/beta number out of range
encodedVersion = BuildInfo::encodeSoftwareVersion("1.2.3-b64");
CHECK((encodedVersion & 0x0000'0000'00FF'0000LLU) == 0);
// Check that the rc/beta number of a release is 0:
encodedVersion = BuildInfo::encodeSoftwareVersion("1.2.6");
CHECK((encodedVersion & 0x0000'0000'003F'0000LLU) == 0);
}
TEST_CASE("IsRippledVersion")
{
auto vFF = 0xFFFF'FFFF'FFFF'FFFFLLU;
CHECK(!BuildInfo::isRippledVersion(vFF));
auto vRippled = 0x183B'0000'0000'0000LLU;
CHECK(BuildInfo::isRippledVersion(vRippled));
}
TEST_CASE("IsNewerVersion")
{
auto vFF = 0xFFFF'FFFF'FFFF'FFFFLLU;
CHECK(!BuildInfo::isNewerVersion(vFF));
auto v159 = BuildInfo::encodeSoftwareVersion("1.5.9");
CHECK(!BuildInfo::isNewerVersion(v159));
auto vCurrent = BuildInfo::getEncodedVersion();
CHECK(!BuildInfo::isNewerVersion(vCurrent));
auto vMax = BuildInfo::encodeSoftwareVersion("255.255.255");
CHECK(BuildInfo::isNewerVersion(vMax));
}
TEST_SUITE_END();

View File

@@ -0,0 +1,179 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012-2017 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <test/jtx.h>
#include <xrpl/protocol/Feature.h>
#include <doctest/doctest.h>
#include <functional>
#include <vector>
using namespace ripple;
TEST_SUITE_BEGIN("Hooks");
TEST_CASE("Test Hooks fields")
{
using namespace test::jtx;
std::vector<std::reference_wrapper<SField const>> fields_to_test = {
sfHookResult,
sfHookStateChangeCount,
sfHookEmitCount,
sfHookExecutionIndex,
sfHookApiVersion,
sfHookStateCount,
sfEmitGeneration,
sfHookOn,
sfHookInstructionCount,
sfEmitBurden,
sfHookReturnCode,
sfReferenceCount,
sfEmitParentTxnID,
sfEmitNonce,
sfEmitHookHash,
sfHookStateKey,
sfHookHash,
sfHookNamespace,
sfHookSetTxnID,
sfHookStateData,
sfHookReturnString,
sfHookParameterName,
sfHookParameterValue,
sfEmitCallback,
sfHookAccount,
sfEmittedTxn,
sfHook,
sfHookDefinition,
sfHookParameter,
sfHookGrant,
sfEmitDetails,
sfHookExecutions,
sfHookExecution,
sfHookParameters,
sfHooks,
sfHookGrants};
for (auto const& rf : fields_to_test)
{
SField const& f = rf.get();
STObject dummy{sfGeneric};
CHECK(!dummy.isFieldPresent(f));
switch (f.fieldType)
{
case STI_UINT8: {
dummy.setFieldU8(f, 0);
CHECK(dummy.getFieldU8(f) == 0);
dummy.setFieldU8(f, 255);
CHECK(dummy.getFieldU8(f) == 255);
CHECK(dummy.isFieldPresent(f));
break;
}
case STI_UINT16: {
dummy.setFieldU16(f, 0);
CHECK(dummy.getFieldU16(f) == 0);
dummy.setFieldU16(f, 0xFFFFU);
CHECK(dummy.getFieldU16(f) == 0xFFFFU);
CHECK(dummy.isFieldPresent(f));
break;
}
case STI_UINT32: {
dummy.setFieldU32(f, 0);
CHECK(dummy.getFieldU32(f) == 0);
dummy.setFieldU32(f, 0xFFFFFFFFU);
CHECK(dummy.getFieldU32(f) == 0xFFFFFFFFU);
CHECK(dummy.isFieldPresent(f));
break;
}
case STI_UINT64: {
dummy.setFieldU64(f, 0);
CHECK(dummy.getFieldU64(f) == 0);
dummy.setFieldU64(f, 0xFFFFFFFFFFFFFFFFU);
CHECK(dummy.getFieldU64(f) == 0xFFFFFFFFFFFFFFFFU);
CHECK(dummy.isFieldPresent(f));
break;
}
case STI_UINT256: {
uint256 u = uint256::fromVoid(
"DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDE"
"ADBEEF");
dummy.setFieldH256(f, u);
CHECK(dummy.getFieldH256(f) == u);
CHECK(dummy.isFieldPresent(f));
break;
}
case STI_VL: {
std::vector<uint8_t> v{1, 2, 3};
dummy.setFieldVL(f, v);
CHECK(dummy.getFieldVL(f) == v);
CHECK(dummy.isFieldPresent(f));
break;
}
case STI_ACCOUNT: {
AccountID id = *parseBase58<AccountID>(
"rwfSjJNK2YQuN64bSWn7T2eY9FJAyAPYJT");
dummy.setAccountID(f, id);
CHECK(dummy.getAccountID(f) == id);
CHECK(dummy.isFieldPresent(f));
break;
}
case STI_OBJECT: {
dummy.emplace_back(STObject{f});
CHECK(dummy.getField(f).getFName() == f);
CHECK(dummy.isFieldPresent(f));
break;
}
case STI_ARRAY: {
STArray dummy2{f, 2};
dummy2.push_back(STObject{sfGeneric});
dummy2.push_back(STObject{sfGeneric});
dummy.setFieldArray(f, dummy2);
CHECK(dummy.getFieldArray(f) == dummy2);
CHECK(dummy.isFieldPresent(f));
break;
}
default:
CHECK(false);
}
}
}
TEST_SUITE_END();

View File

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

View File

@@ -0,0 +1,860 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <xrpl/basics/UnorderedContainers.h>
#include <doctest/doctest.h>
#include <xrpl/protocol/Book.h>
#include <xrpl/protocol/Issue.h>
#include <sys/types.h>
#include <map>
#include <optional>
#include <set>
#include <typeinfo>
#include <unordered_set>
#if BEAST_MSVC
#define STL_SET_HAS_EMPLACE 1
#else
#define STL_SET_HAS_EMPLACE 0
#endif
#ifndef RIPPLE_ASSETS_ENABLE_STD_HASH
#if BEAST_MAC || BEAST_IOS
#define RIPPLE_ASSETS_ENABLE_STD_HASH 0
#else
#define RIPPLE_ASSETS_ENABLE_STD_HASH 1
#endif
#endif
namespace ripple {
namespace {
using Domain = uint256;
// Comparison, hash tests for uint60 (via base_uint)
template <typename Unsigned>
void
testUnsigned()
{
Unsigned const u1(1);
Unsigned const u2(2);
Unsigned const u3(3);
CHECK(u1 != u2);
CHECK(u1 < u2);
CHECK(u1 <= u2);
CHECK(u2 <= u2);
CHECK(u2 == u2);
CHECK(u2 >= u2);
CHECK(u3 >= u2);
CHECK(u3 > u2);
std::hash<Unsigned> hash;
CHECK(hash(u1) == hash(u1));
CHECK(hash(u2) == hash(u2));
CHECK(hash(u3) == hash(u3));
CHECK(hash(u1) != hash(u2));
CHECK(hash(u1) != hash(u3));
CHECK(hash(u2) != hash(u3));
}
//--------------------------------------------------------------------------
// Comparison, hash tests for Issue
template <class Issue>
void
testIssue()
{
Currency const c1(1);
AccountID const i1(1);
Currency const c2(2);
AccountID const i2(2);
Currency const c3(3);
AccountID const i3(3);
CHECK(Issue(c1, i1) != Issue(c2, i1));
CHECK(Issue(c1, i1) < Issue(c2, i1));
CHECK(Issue(c1, i1) <= Issue(c2, i1));
CHECK(Issue(c2, i1) <= Issue(c2, i1));
CHECK(Issue(c2, i1) == Issue(c2, i1));
CHECK(Issue(c2, i1) >= Issue(c2, i1));
CHECK(Issue(c3, i1) >= Issue(c2, i1));
CHECK(Issue(c3, i1) > Issue(c2, i1));
CHECK(Issue(c1, i1) != Issue(c1, i2));
CHECK(Issue(c1, i1) < Issue(c1, i2));
CHECK(Issue(c1, i1) <= Issue(c1, i2));
CHECK(Issue(c1, i2) <= Issue(c1, i2));
CHECK(Issue(c1, i2) == Issue(c1, i2));
CHECK(Issue(c1, i2) >= Issue(c1, i2));
CHECK(Issue(c1, i3) >= Issue(c1, i2));
CHECK(Issue(c1, i3) > Issue(c1, i2));
std::hash<Issue> hash;
CHECK(hash(Issue(c1, i1)) == hash(Issue(c1, i1)));
CHECK(hash(Issue(c1, i2)) == hash(Issue(c1, i2)));
CHECK(hash(Issue(c1, i3)) == hash(Issue(c1, i3)));
CHECK(hash(Issue(c2, i1)) == hash(Issue(c2, i1)));
CHECK(hash(Issue(c2, i2)) == hash(Issue(c2, i2)));
CHECK(hash(Issue(c2, i3)) == hash(Issue(c2, i3)));
CHECK(hash(Issue(c3, i1)) == hash(Issue(c3, i1)));
CHECK(hash(Issue(c3, i2)) == hash(Issue(c3, i2)));
CHECK(hash(Issue(c3, i3)) == hash(Issue(c3, i3)));
CHECK(hash(Issue(c1, i1)) != hash(Issue(c1, i2)));
CHECK(hash(Issue(c1, i1)) != hash(Issue(c1, i3)));
CHECK(hash(Issue(c1, i1)) != hash(Issue(c2, i1)));
CHECK(hash(Issue(c1, i1)) != hash(Issue(c2, i2)));
CHECK(hash(Issue(c1, i1)) != hash(Issue(c2, i3)));
CHECK(hash(Issue(c1, i1)) != hash(Issue(c3, i1)));
CHECK(hash(Issue(c1, i1)) != hash(Issue(c3, i2)));
CHECK(hash(Issue(c1, i1)) != hash(Issue(c3, i3)));
}
template <class Set>
void
testIssueSet()
{
Currency const c1(1);
AccountID const i1(1);
Currency const c2(2);
AccountID const i2(2);
Issue const a1(c1, i1);
Issue const a2(c2, i2);
{
Set c;
c.insert(a1);
CHECK(c.size() == 1);
c.insert(a2);
CHECK(c.size() == 2);
CHECK(c.erase(Issue(c1, i2)) == 0);
CHECK(c.erase(Issue(c1, i1)) == 1);
CHECK(c.erase(Issue(c2, i2)) == 1);
CHECK(c.empty());
}
{
Set c;
c.insert(a1);
CHECK(c.size() == 1);
c.insert(a2);
CHECK(c.size() == 2);
CHECK(c.erase(Issue(c1, i2)) == 0);
CHECK(c.erase(Issue(c1, i1)) == 1);
CHECK(c.erase(Issue(c2, i2)) == 1);
CHECK(c.empty());
#if STL_SET_HAS_EMPLACE
c.emplace(c1, i1);
CHECK(c.size() == 1);
c.emplace(c2, i2);
CHECK(c.size() == 2);
#endif
}
}
template <class Map>
void
testIssueMap()
{
Currency const c1(1);
AccountID const i1(1);
Currency const c2(2);
AccountID const i2(2);
Issue const a1(c1, i1);
Issue const a2(c2, i2);
{
Map c;
c.insert(std::make_pair(a1, 1));
CHECK(c.size() == 1);
c.insert(std::make_pair(a2, 2));
CHECK(c.size() == 2);
CHECK(c.erase(Issue(c1, i2)) == 0);
CHECK(c.erase(Issue(c1, i1)) == 1);
CHECK(c.erase(Issue(c2, i2)) == 1);
CHECK(c.empty());
}
{
Map c;
c.insert(std::make_pair(a1, 1));
CHECK(c.size() == 1);
c.insert(std::make_pair(a2, 2));
CHECK(c.size() == 2);
CHECK(c.erase(Issue(c1, i2)) == 0);
CHECK(c.erase(Issue(c1, i1)) == 1);
CHECK(c.erase(Issue(c2, i2)) == 1);
CHECK(c.empty());
}
}
template <class Set>
void
testIssueDomainSet()
{
Currency const c1(1);
AccountID const i1(1);
Currency const c2(2);
AccountID const i2(2);
Issue const a1(c1, i1);
Issue const a2(c2, i2);
uint256 const domain1{1};
uint256 const domain2{2};
Set c;
c.insert(std::make_pair(a1, domain1));
CHECK(c.size() == 1);
c.insert(std::make_pair(a2, domain1));
CHECK(c.size() == 2);
c.insert(std::make_pair(a2, domain2));
CHECK(c.size() == 3);
CHECK(c.erase(std::make_pair(Issue(c1, i2), domain1)) == 0);
CHECK(c.erase(std::make_pair(a1, domain1)) == 1);
CHECK(c.erase(std::make_pair(a2, domain1)) == 1);
CHECK(c.erase(std::make_pair(a2, domain2)) == 1);
CHECK(c.empty());
}
template <class Map>
void
testIssueDomainMap()
{
Currency const c1(1);
AccountID const i1(1);
Currency const c2(2);
AccountID const i2(2);
Issue const a1(c1, i1);
Issue const a2(c2, i2);
uint256 const domain1{1};
uint256 const domain2{2};
Map c;
c.insert(std::make_pair(std::make_pair(a1, domain1), 1));
CHECK(c.size() == 1);
c.insert(std::make_pair(std::make_pair(a2, domain1), 2));
CHECK(c.size() == 2);
c.insert(std::make_pair(std::make_pair(a2, domain2), 2));
CHECK(c.size() == 3);
CHECK(c.erase(std::make_pair(Issue(c1, i2), domain1)) == 0);
CHECK(c.erase(std::make_pair(a1, domain1)) == 1);
CHECK(c.erase(std::make_pair(a2, domain1)) == 1);
CHECK(c.erase(std::make_pair(a2, domain2)) == 1);
CHECK(c.empty());
}
//--------------------------------------------------------------------------
// Comparison, hash tests for Book
template <class Book>
void
testBook()
{
Currency const c1(1);
AccountID const i1(1);
Currency const c2(2);
AccountID const i2(2);
Currency const c3(3);
AccountID const i3(3);
Issue a1(c1, i1);
Issue a2(c1, i2);
Issue a3(c2, i2);
Issue a4(c3, i2);
uint256 const domain1{1};
uint256 const domain2{2};
// Books without domains
CHECK(Book(a1, a2, std::nullopt) != Book(a2, a3, std::nullopt));
CHECK(Book(a1, a2, std::nullopt) < Book(a2, a3, std::nullopt));
CHECK(Book(a1, a2, std::nullopt) <= Book(a2, a3, std::nullopt));
CHECK(Book(a2, a3, std::nullopt) <= Book(a2, a3, std::nullopt));
CHECK(Book(a2, a3, std::nullopt) == Book(a2, a3, std::nullopt));
CHECK(Book(a2, a3, std::nullopt) >= Book(a2, a3, std::nullopt));
CHECK(Book(a3, a4, std::nullopt) >= Book(a2, a3, std::nullopt));
CHECK(Book(a3, a4, std::nullopt) > Book(a2, a3, std::nullopt));
// test domain books
{
// Books with different domains
CHECK(Book(a2, a3, domain1) != Book(a2, a3, domain2));
CHECK(Book(a2, a3, domain1) < Book(a2, a3, domain2));
CHECK(Book(a2, a3, domain2) > Book(a2, a3, domain1));
// One Book has a domain, the other does not
CHECK(Book(a2, a3, domain1) != Book(a2, a3, std::nullopt));
CHECK(Book(a2, a3, std::nullopt) < Book(a2, a3, domain1));
CHECK(Book(a2, a3, domain1) > Book(a2, a3, std::nullopt));
// Both Books have the same domain
CHECK(Book(a2, a3, domain1) == Book(a2, a3, domain1));
CHECK(Book(a2, a3, domain2) == Book(a2, a3, domain2));
CHECK(
Book(a2, a3, std::nullopt) == Book(a2, a3, std::nullopt));
// Both Books have no domain
CHECK(
Book(a2, a3, std::nullopt) == Book(a2, a3, std::nullopt));
// Testing comparisons with >= and <=
// When comparing books with domain1 vs domain2
CHECK(Book(a2, a3, domain1) <= Book(a2, a3, domain2));
CHECK(Book(a2, a3, domain2) >= Book(a2, a3, domain1));
CHECK(Book(a2, a3, domain1) >= Book(a2, a3, domain1));
CHECK(Book(a2, a3, domain2) <= Book(a2, a3, domain2));
// One Book has domain1 and the other has no domain
CHECK(Book(a2, a3, domain1) > Book(a2, a3, std::nullopt));
CHECK(Book(a2, a3, std::nullopt) < Book(a2, a3, domain1));
// One Book has domain2 and the other has no domain
CHECK(Book(a2, a3, domain2) > Book(a2, a3, std::nullopt));
CHECK(Book(a2, a3, std::nullopt) < Book(a2, a3, domain2));
// Comparing two Books with no domains
CHECK(
Book(a2, a3, std::nullopt) <= Book(a2, a3, std::nullopt));
CHECK(
Book(a2, a3, std::nullopt) >= Book(a2, a3, std::nullopt));
// Test case where domain1 is less than domain2
CHECK(Book(a2, a3, domain1) <= Book(a2, a3, domain2));
CHECK(Book(a2, a3, domain2) >= Book(a2, a3, domain1));
// Test case where domain2 is equal to domain1
CHECK(Book(a2, a3, domain1) >= Book(a2, a3, domain1));
CHECK(Book(a2, a3, domain1) <= Book(a2, a3, domain1));
// More test cases involving a4 (with domain2)
// Comparing Book with domain2 (a4) to a Book with domain1
CHECK(Book(a2, a3, domain1) < Book(a3, a4, domain2));
CHECK(Book(a3, a4, domain2) > Book(a2, a3, domain1));
// Comparing Book with domain2 (a4) to a Book with no domain
CHECK(Book(a3, a4, domain2) > Book(a2, a3, std::nullopt));
CHECK(Book(a2, a3, std::nullopt) < Book(a3, a4, domain2));
// Comparing Book with domain2 (a4) to a Book with the same domain
CHECK(Book(a3, a4, domain2) == Book(a3, a4, domain2));
// Comparing Book with domain2 (a4) to a Book with domain1
CHECK(Book(a2, a3, domain1) < Book(a3, a4, domain2));
CHECK(Book(a3, a4, domain2) > Book(a2, a3, domain1));
}
std::hash<Book> hash;
// log << std::hex << hash (Book (a1, a2));
// log << std::hex << hash (Book (a1, a2));
//
// log << std::hex << hash (Book (a1, a3));
// log << std::hex << hash (Book (a1, a3));
//
// log << std::hex << hash (Book (a1, a4));
// log << std::hex << hash (Book (a1, a4));
//
// log << std::hex << hash (Book (a2, a3));
// log << std::hex << hash (Book (a2, a3));
//
// log << std::hex << hash (Book (a2, a4));
// log << std::hex << hash (Book (a2, a4));
//
// log << std::hex << hash (Book (a3, a4));
// log << std::hex << hash (Book (a3, a4));
CHECK(
hash(Book(a1, a2, std::nullopt)) ==
hash(Book(a1, a2, std::nullopt)));
CHECK(
hash(Book(a1, a3, std::nullopt)) ==
hash(Book(a1, a3, std::nullopt)));
CHECK(
hash(Book(a1, a4, std::nullopt)) ==
hash(Book(a1, a4, std::nullopt)));
CHECK(
hash(Book(a2, a3, std::nullopt)) ==
hash(Book(a2, a3, std::nullopt)));
CHECK(
hash(Book(a2, a4, std::nullopt)) ==
hash(Book(a2, a4, std::nullopt)));
CHECK(
hash(Book(a3, a4, std::nullopt)) ==
hash(Book(a3, a4, std::nullopt)));
CHECK(
hash(Book(a1, a2, std::nullopt)) !=
hash(Book(a1, a3, std::nullopt)));
CHECK(
hash(Book(a1, a2, std::nullopt)) !=
hash(Book(a1, a4, std::nullopt)));
CHECK(
hash(Book(a1, a2, std::nullopt)) !=
hash(Book(a2, a3, std::nullopt)));
CHECK(
hash(Book(a1, a2, std::nullopt)) !=
hash(Book(a2, a4, std::nullopt)));
CHECK(
hash(Book(a1, a2, std::nullopt)) !=
hash(Book(a3, a4, std::nullopt)));
// Books with domain
CHECK(
hash(Book(a1, a2, domain1)) == hash(Book(a1, a2, domain1)));
CHECK(
hash(Book(a1, a3, domain1)) == hash(Book(a1, a3, domain1)));
CHECK(
hash(Book(a1, a4, domain1)) == hash(Book(a1, a4, domain1)));
CHECK(
hash(Book(a2, a3, domain1)) == hash(Book(a2, a3, domain1)));
CHECK(
hash(Book(a2, a4, domain1)) == hash(Book(a2, a4, domain1)));
CHECK(
hash(Book(a3, a4, domain1)) == hash(Book(a3, a4, domain1)));
CHECK(
hash(Book(a1, a2, std::nullopt)) ==
hash(Book(a1, a2, std::nullopt)));
// Comparing Books with domain1 vs no domain
CHECK(
hash(Book(a1, a2, std::nullopt)) != hash(Book(a1, a2, domain1)));
CHECK(
hash(Book(a1, a3, std::nullopt)) != hash(Book(a1, a3, domain1)));
CHECK(
hash(Book(a1, a4, std::nullopt)) != hash(Book(a1, a4, domain1)));
CHECK(
hash(Book(a2, a3, std::nullopt)) != hash(Book(a2, a3, domain1)));
CHECK(
hash(Book(a2, a4, std::nullopt)) != hash(Book(a2, a4, domain1)));
CHECK(
hash(Book(a3, a4, std::nullopt)) != hash(Book(a3, a4, domain1)));
// Books with domain1 but different Issues
CHECK(
hash(Book(a1, a2, domain1)) != hash(Book(a1, a3, domain1)));
CHECK(
hash(Book(a1, a2, domain1)) != hash(Book(a1, a4, domain1)));
CHECK(
hash(Book(a2, a3, domain1)) != hash(Book(a2, a4, domain1)));
CHECK(
hash(Book(a1, a2, domain1)) != hash(Book(a2, a3, domain1)));
CHECK(
hash(Book(a2, a4, domain1)) != hash(Book(a3, a4, domain1)));
CHECK(
hash(Book(a3, a4, domain1)) != hash(Book(a1, a4, domain1)));
// Books with domain1 and domain2
CHECK(
hash(Book(a1, a2, domain1)) != hash(Book(a1, a2, domain2)));
CHECK(
hash(Book(a1, a3, domain1)) != hash(Book(a1, a3, domain2)));
CHECK(
hash(Book(a1, a4, domain1)) != hash(Book(a1, a4, domain2)));
CHECK(
hash(Book(a2, a3, domain1)) != hash(Book(a2, a3, domain2)));
CHECK(
hash(Book(a2, a4, domain1)) != hash(Book(a2, a4, domain2)));
CHECK(
hash(Book(a3, a4, domain1)) != hash(Book(a3, a4, domain2)));
}
//--------------------------------------------------------------------------
template <class Set>
void
testBookSet()
{
Currency const c1(1);
AccountID const i1(1);
Currency const c2(2);
AccountID const i2(2);
Issue const a1(c1, i1);
Issue const a2(c2, i2);
Book const b1(a1, a2, std::nullopt);
Book const b2(a2, a1, std::nullopt);
uint256 const domain1{1};
uint256 const domain2{2};
Book const b1_d1(a1, a2, domain1);
Book const b2_d1(a2, a1, domain1);
Book const b1_d2(a1, a2, domain2);
Book const b2_d2(a2, a1, domain2);
{
Set c;
c.insert(b1);
CHECK(c.size() == 1);
c.insert(b2);
CHECK(c.size() == 2);
CHECK(c.erase(Book(a1, a1, std::nullopt)) == 0);
CHECK(c.erase(Book(a1, a2, std::nullopt)) == 1);
CHECK(c.erase(Book(a2, a1, std::nullopt)) == 1);
CHECK(c.empty());
}
{
Set c;
c.insert(b1);
CHECK(c.size() == 1);
c.insert(b2);
CHECK(c.size() == 2);
CHECK(c.erase(Book(a1, a1, std::nullopt)) == 0);
CHECK(c.erase(Book(a1, a2, std::nullopt)) == 1);
CHECK(c.erase(Book(a2, a1, std::nullopt)) == 1);
CHECK(c.empty());
#if STL_SET_HAS_EMPLACE
c.emplace(a1, a2);
CHECK(c.size() == 1);
c.emplace(a2, a1);
CHECK(c.size() == 2);
#endif
}
{
Set c;
c.insert(b1_d1);
CHECK(c.size() == 1);
c.insert(b2_d1);
CHECK(c.size() == 2);
c.insert(b1_d2);
CHECK(c.size() == 3);
c.insert(b2_d2);
CHECK(c.size() == 4);
// Try removing non-existent elements
CHECK(c.erase(Book(a2, a2, domain1)) == 0);
CHECK(c.erase(Book(a1, a2, domain1)) == 1);
CHECK(c.erase(Book(a2, a1, domain1)) == 1);
CHECK(c.size() == 2);
CHECK(c.erase(Book(a1, a2, domain2)) == 1);
CHECK(c.erase(Book(a2, a1, domain2)) == 1);
CHECK(c.empty());
}
{
Set c;
c.insert(b1);
c.insert(b2);
c.insert(b1_d1);
c.insert(b2_d1);
CHECK(c.size() == 4);
CHECK(c.erase(Book(a1, a2, std::nullopt)) == 1);
CHECK(c.erase(Book(a2, a1, std::nullopt)) == 1);
CHECK(c.size() == 2);
CHECK(c.erase(Book(a1, a2, domain1)) == 1);
CHECK(c.erase(Book(a2, a1, domain1)) == 1);
CHECK(c.empty());
}
}
template <class Map>
void
testBookMap()
{
Currency const c1(1);
AccountID const i1(1);
Currency const c2(2);
AccountID const i2(2);
Issue const a1(c1, i1);
Issue const a2(c2, i2);
Book const b1(a1, a2, std::nullopt);
Book const b2(a2, a1, std::nullopt);
uint256 const domain1{1};
uint256 const domain2{2};
Book const b1_d1(a1, a2, domain1);
Book const b2_d1(a2, a1, domain1);
Book const b1_d2(a1, a2, domain2);
Book const b2_d2(a2, a1, domain2);
// typename Map::value_type value_type;
// std::pair <Book const, int> value_type;
{
Map c;
// c.insert (value_type (b1, 1));
c.insert(std::make_pair(b1, 1));
CHECK(c.size() == 1);
// c.insert (value_type (b2, 2));
c.insert(std::make_pair(b2, 1));
CHECK(c.size() == 2);
CHECK(c.erase(Book(a1, a1, std::nullopt)) == 0);
CHECK(c.erase(Book(a1, a2, std::nullopt)) == 1);
CHECK(c.erase(Book(a2, a1, std::nullopt)) == 1);
CHECK(c.empty());
}
{
Map c;
// c.insert (value_type (b1, 1));
c.insert(std::make_pair(b1, 1));
CHECK(c.size() == 1);
// c.insert (value_type (b2, 2));
c.insert(std::make_pair(b2, 1));
CHECK(c.size() == 2);
CHECK(c.erase(Book(a1, a1, std::nullopt)) == 0);
CHECK(c.erase(Book(a1, a2, std::nullopt)) == 1);
CHECK(c.erase(Book(a2, a1, std::nullopt)) == 1);
CHECK(c.empty());
}
{
Map c;
c.insert(std::make_pair(b1_d1, 10));
CHECK(c.size() == 1);
c.insert(std::make_pair(b2_d1, 20));
CHECK(c.size() == 2);
c.insert(std::make_pair(b1_d2, 30));
CHECK(c.size() == 3);
c.insert(std::make_pair(b2_d2, 40));
CHECK(c.size() == 4);
// Try removing non-existent elements
CHECK(c.erase(Book(a2, a2, domain1)) == 0);
CHECK(c.erase(Book(a1, a2, domain1)) == 1);
CHECK(c.erase(Book(a2, a1, domain1)) == 1);
CHECK(c.size() == 2);
CHECK(c.erase(Book(a1, a2, domain2)) == 1);
CHECK(c.erase(Book(a2, a1, domain2)) == 1);
CHECK(c.empty());
}
{
Map c;
c.insert(std::make_pair(b1, 1));
c.insert(std::make_pair(b2, 2));
c.insert(std::make_pair(b1_d1, 3));
c.insert(std::make_pair(b2_d1, 4));
CHECK(c.size() == 4);
// Try removing non-existent elements
CHECK(c.erase(Book(a1, a1, domain1)) == 0);
CHECK(c.erase(Book(a2, a2, domain2)) == 0);
CHECK(c.erase(Book(a1, a2, std::nullopt)) == 1);
CHECK(c.erase(Book(a2, a1, std::nullopt)) == 1);
CHECK(c.size() == 2);
CHECK(c.erase(Book(a1, a2, domain1)) == 1);
CHECK(c.erase(Book(a2, a1, domain1)) == 1);
CHECK(c.empty());
}
}
//--------------------------------------------------------------------------
// End of helper functions.
}
} // namespace ripple
using namespace ripple;
TEST_SUITE_BEGIN("Issue");
TEST_CASE("Unsigned types")
{
testUnsigned<Currency>();
testUnsigned<AccountID>();
}
TEST_CASE("Issue")
{
testIssue<Issue>();
}
TEST_CASE("Issue sets")
{
INFO("std::set <Issue>");
testIssueSet<std::set<Issue>>();
INFO("std::set <Issue>");
testIssueSet<std::set<Issue>>();
#if RIPPLE_ASSETS_ENABLE_STD_HASH
INFO("std::unordered_set <Issue>");
testIssueSet<std::unordered_set<Issue>>();
INFO("std::unordered_set <Issue>");
testIssueSet<std::unordered_set<Issue>>();
#endif
INFO("hash_set <Issue>");
testIssueSet<hash_set<Issue>>();
INFO("hash_set <Issue>");
testIssueSet<hash_set<Issue>>();
}
TEST_CASE("Issue maps")
{
INFO("std::map <Issue, int>");
testIssueMap<std::map<Issue, int>>();
INFO("std::map <Issue, int>");
testIssueMap<std::map<Issue, int>>();
#if RIPPLE_ASSETS_ENABLE_STD_HASH
INFO("std::unordered_map <Issue, int>");
testIssueMap<std::unordered_map<Issue, int>>();
INFO("std::unordered_map <Issue, int>");
testIssueMap<std::unordered_map<Issue, int>>();
INFO("hash_map <Issue, int>");
testIssueMap<hash_map<Issue, int>>();
INFO("hash_map <Issue, int>");
testIssueMap<hash_map<Issue, int>>();
#endif
}
TEST_CASE("Book")
{
testBook<Book>();
}
TEST_CASE("Book sets")
{
INFO("std::set <Book>");
testBookSet<std::set<Book>>();
INFO("std::set <Book>");
testBookSet<std::set<Book>>();
#if RIPPLE_ASSETS_ENABLE_STD_HASH
INFO("std::unordered_set <Book>");
testBookSet<std::unordered_set<Book>>();
INFO("std::unordered_set <Book>");
testBookSet<std::unordered_set<Book>>();
#endif
INFO("hash_set <Book>");
testBookSet<hash_set<Book>>();
INFO("hash_set <Book>");
testBookSet<hash_set<Book>>();
}
TEST_CASE("Book maps")
{
INFO("std::map <Book, int>");
testBookMap<std::map<Book, int>>();
INFO("std::map <Book, int>");
testBookMap<std::map<Book, int>>();
#if RIPPLE_ASSETS_ENABLE_STD_HASH
INFO("std::unordered_map <Book, int>");
testBookMap<std::unordered_map<Book, int>>();
INFO("std::unordered_map <Book, int>");
testBookMap<std::unordered_map<Book, int>>();
INFO("hash_map <Book, int>");
testBookMap<hash_map<Book, int>>();
INFO("hash_map <Book, int>");
testBookMap<hash_map<Book, int>>();
#endif
}
TEST_CASE("Issue domain sets")
{
INFO("std::set <std::pair<Issue, Domain>>");
testIssueDomainSet<std::set<std::pair<Issue, Domain>>>();
INFO("std::set <std::pair<Issue, Domain>>");
testIssueDomainSet<std::set<std::pair<Issue, Domain>>>();
INFO("hash_set <std::pair<Issue, Domain>>");
testIssueDomainSet<hash_set<std::pair<Issue, Domain>>>();
INFO("hash_set <std::pair<Issue, Domain>>");
testIssueDomainSet<hash_set<std::pair<Issue, Domain>>>();
}
TEST_CASE("Issue domain maps")
{
INFO("std::map <std::pair<Issue, Domain>, int>");
testIssueDomainMap<std::map<std::pair<Issue, Domain>, int>>();
INFO("std::map <std::pair<Issue, Domain>, int>");
testIssueDomainMap<std::map<std::pair<Issue, Domain>, int>>();
#if RIPPLE_ASSETS_ENABLE_STD_HASH
INFO("hash_map <std::pair<Issue, Domain>, int>");
testIssueDomainMap<hash_map<std::pair<Issue, Domain>, int>>();
INFO("hash_map <std::pair<Issue, Domain>, int>");
testIssueDomainMap<hash_map<std::pair<Issue, Domain>, int>>();
INFO("hardened_hash_map <std::pair<Issue, Domain>, int>");
testIssueDomainMap<hardened_hash_map<std::pair<Issue, Domain>, int>>();
INFO("hardened_hash_map <std::pair<Issue, Domain>, int>");
testIssueDomainMap<hardened_hash_map<std::pair<Issue, Domain>, int>>();
#endif
}
TEST_SUITE_END();

View File

@@ -0,0 +1,129 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2022 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <test/jtx.h>
#include <doctest/doctest.h>
#include <xrpl/beast/unit_test.h>
#include <xrpl/basics/strHex.h>
using namespace ripple;
TEST_SUITE_BEGIN("Memo");
TEST_CASE("Memo")
{
struct DummySuite : beast::unit_test::suite
{
void run() override {}
} suite;
using namespace test::jtx;
Account alice{"alice"};
Env env{suite};
env.fund(XRP(10000), alice);
env.close();
// Lambda that returns a valid JTx with a memo that we can hack up.
// This is the basis for building tests of invalid states.
auto makeJtxWithMemo = [&env, &alice]() {
JTx example = noop(alice);
memo const exampleMemo{"tic", "tac", "toe"};
exampleMemo(env, example);
return example;
};
// A valid memo.
env(makeJtxWithMemo());
env.close();
{
// Make sure that too big a memo is flagged as invalid.
JTx memoSize = makeJtxWithMemo();
memoSize.jv[sfMemos.jsonName][0u][sfMemo.jsonName][sfMemoData.jsonName] =
std::string(2020, '0');
env(memoSize,
rpc(
"invalidTransaction",
"fails local checks: The memo exceeds the maximum allowed size."));
// This memo is just barely small enough.
memoSize.jv[sfMemos.jsonName][0u][sfMemo.jsonName][sfMemoData.jsonName] =
std::string(2018, '1');
env(memoSize);
}
{
// Put a non-Memo in the Memos array.
JTx memoNonMemo = noop(alice);
auto& jv = memoNonMemo.jv;
auto& ma = jv[sfMemos.jsonName];
auto& mi = ma[ma.size()];
auto& m = mi[sfCreatedNode.jsonName]; // CreatedNode in Memos
m[sfMemoData.jsonName] = "3030303030";
env(
memoNonMemo,
rpc(
"invalidTransaction",
"fails local checks: A memo array may contain only Memo objects."));
}
{
// Put an invalid field in a Memo object.
JTx memoExtra = makeJtxWithMemo();
memoExtra.jv[sfMemos.jsonName][0u][sfMemo.jsonName][sfFlags.jsonName] = 13;
env(
memoExtra,
rpc(
"invalidTransaction",
"fails local checks: A memo may contain only MemoType, MemoData or MemoFormat fields."));
}
{
// Put a character that is not allowed in a URL in a MemoType field.
JTx memoBadChar = makeJtxWithMemo();
memoBadChar.jv[sfMemos.jsonName][0u][sfMemo.jsonName][sfMemoType.jsonName] =
strHex(std::string_view("ONE<INFINITY"));
env(
memoBadChar,
rpc(
"invalidTransaction",
"fails local checks: The MemoType and MemoFormat fields may only contain characters that are allowed in URLs under RFC 3986."));
}
{
// Put a character that is not allowed in a URL in a MemoData field.
// That's okay.
JTx memoLegitChar = makeJtxWithMemo();
memoLegitChar.jv[sfMemos.jsonName][0u][sfMemo.jsonName][sfMemoData.jsonName] =
strHex(std::string_view("ONE<INFINITY"));
env(memoLegitChar);
}
{
// Put a character that is not allowed in a URL in a MemoFormat.
JTx memoBadChar = makeJtxWithMemo();
memoBadChar.jv[sfMemos.jsonName][0u][sfMemo.jsonName][sfMemoFormat.jsonName] =
strHex(std::string_view("NoBraces{}InURL"));
env(
memoBadChar,
rpc(
"invalidTransaction",
"fails local checks: The MemoType and MemoFormat fields may only contain characters that are allowed in URLs under RFC 3986."));
}
}
TEST_SUITE_END();

View File

@@ -0,0 +1,646 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/XRPLF/rippled/
Copyright (c) 2023 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <doctest/doctest.h>
#include <xrpl/protocol/MultiApiJson.h>
#include <cstdint>
#include <limits>
#include <optional>
#include <type_traits>
#include <utility>
namespace ripple {
namespace test {
namespace {
// This needs to be in a namespace because of deduction guide
template <typename... Ts>
struct Overload : Ts...
{
using Ts::operator()...;
};
template <typename... Ts>
Overload(Ts...) -> Overload<Ts...>;
} // namespace
static auto
makeJson(char const* key, int val)
{
Json::Value obj(Json::objectValue);
obj[key] = val;
return obj;
}
struct MultiApiJsonFixture
{
using MultiApiJson13 = ripple::detail::MultiApiJson<1, 3>;
Json::Value const obj1{makeJson("value", 1)};
Json::Value const obj2{makeJson("value", 2)};
Json::Value const obj3{makeJson("value", 3)};
Json::Value const jsonNull{};
MultiApiJsonFixture() = default;
};
TEST_SUITE_BEGIN("MultiApiJson");
TEST_CASE_METHOD(
MultiApiJsonFixture,
"forApiVersions, forAllApiVersions",
"[MultiApiJson]")
{
using ripple::detail::MultiApiJson;
MultiApiJson13 subject{};
static_assert(sizeof(subject) == sizeof(subject.val));
static_assert(subject.size == subject.val.size());
static_assert(
std::is_same_v<decltype(subject.val), std::array<Json::Value, 3>>);
CHECK(subject.val.size() == 3);
CHECK(subject.val == std::array<Json::Value, 3>{jsonNull, jsonNull, jsonNull});
subject.val[0] = obj1;
subject.val[1] = obj2;
// Some static data for test inputs
static int const primes[] = {2, 3, 5, 7, 11, 13, 17, 19, 23,
29, 31, 37, 41, 43, 47, 53, 59, 61,
67, 71, 73, 79, 83, 89, 97};
static_assert(std::size(primes) > RPC::apiMaximumValidVersion);
MultiApiJson13 s1{};
static_assert(
s1.size ==
RPC::apiMaximumValidVersion + 1 - RPC::apiMinimumSupportedVersion);
int productAllVersions = 1;
for (unsigned i = RPC::apiMinimumSupportedVersion;
i <= RPC::apiMaximumValidVersion;
++i)
{
auto const index = i - RPC::apiMinimumSupportedVersion;
CHECK(index == s1.index(i));
CHECK(s1.valid(i));
s1.val[index] = makeJson("value", primes[i]);
productAllVersions *= primes[i];
}
CHECK(!s1.valid(0));
CHECK(!s1.valid(RPC::apiMaximumValidVersion + 1));
CHECK(
!s1.valid(std::numeric_limits<
decltype(RPC::apiMaximumValidVersion.value)>::max()));
int result = 1;
static_assert(
RPC::apiMinimumSupportedVersion + 1 <= RPC::apiMaximumValidVersion);
forApiVersions<
RPC::apiMinimumSupportedVersion,
RPC::apiMinimumSupportedVersion + 1>(
std::as_const(s1).visit(),
[](
Json::Value const& json,
unsigned int version,
int* result) {
CHECK(
version >= RPC::apiMinimumSupportedVersion &&
version <= RPC::apiMinimumSupportedVersion + 1);
if (CHECK(json.isMember("value")))
{
*result *= json["value"].asInt();
}
},
&result);
CHECK(
result ==
primes[RPC::apiMinimumSupportedVersion] *
primes[RPC::apiMinimumSupportedVersion + 1]);
// Check all the values with mutable data
forAllApiVersions(
s1.visit(), [&s1](Json::Value& json, auto version) {
CHECK(s1.val[s1.index(version)] == json);
if (CHECK(json.isMember("value")))
{
CHECK(json["value"].asInt() == primes[version]);
}
});
result = 1;
forAllApiVersions(
std::as_const(s1).visit(),
[](
Json::Value const& json,
unsigned int version,
int* result) {
CHECK(
version >= RPC::apiMinimumSupportedVersion &&
version <= RPC::apiMaximumValidVersion);
if (CHECK(json.isMember("value")))
{
*result *= json["value"].asInt();
}
},
&result);
CHECK(result == productAllVersions);
// Several overloads we want to fail
static_assert([](auto&& v) {
return !requires {
forAllApiVersions(
std::forward<decltype(v)>(v).visit(), //
[](Json::Value&, auto) {}); // missing const
};
}(std::as_const(s1)));
static_assert([](auto&& v) {
return !requires {
forAllApiVersions(
std::forward<decltype(v)>(v).visit(), //
[](Json::Value&) {}); // missing const
};
}(std::as_const(s1)));
static_assert([](auto&& v) {
return !requires {
forAllApiVersions(
std::forward<decltype(v)>(v).visit(), //
[]() {}); // missing parameters
};
}(std::as_const(s1)));
static_assert([](auto&& v) {
return !requires {
forAllApiVersions(
std::forward<decltype(v)>(v).visit(), //
[](auto) {},
1); // missing parameters
};
}(std::as_const(s1)));
static_assert([](auto&& v) {
return !requires {
forAllApiVersions(
std::forward<decltype(v)>(v).visit(), //
[](auto, auto) {},
1); // missing parameters
};
}(std::as_const(s1)));
static_assert([](auto&& v) {
return !requires {
forAllApiVersions(
std::forward<decltype(v)>(v).visit(), //
[](auto, auto, char const*) {},
1); // parameter type mismatch
};
}(std::as_const(s1)));
// Sanity checks
static_assert([](auto&& v) {
return requires {
forAllApiVersions(
std::forward<decltype(v)>(v).visit(), //
[](auto) {});
};
}(s1));
static_assert([](auto&& v) {
return requires {
forAllApiVersions(
std::forward<decltype(v)>(v).visit(), //
[](Json::Value const&) {});
};
}(std::as_const(s1)));
static_assert([](auto&& v) {
return requires {
forAllApiVersions(
std::forward<decltype(v)>(v).visit(), //
[](auto...) {});
};
}(s1));
static_assert([](auto&& v) {
return requires {
forAllApiVersions(
std::forward<decltype(v)>(v).visit(), //
[](Json::Value const&, auto...) {});
};
}(std::as_const(s1)));
static_assert([](auto&& v) {
return requires {
forAllApiVersions(
std::forward<decltype(v)>(v).visit(), //
[](Json::Value&, auto, auto, auto...) {},
0,
"");
};
}(s1));
static_assert([](auto&& v) {
return requires {
forAllApiVersions(
std::forward<decltype(v)>(v).visit(), //
[]<unsigned int Version>(
Json::Value const&,
std::integral_constant<unsigned int, Version>,
int,
char const*) {},
0,
"");
};
}(std::as_const(s1)));
static_assert([](auto&& v) {
return requires {
forAllApiVersions(
std::forward<decltype(v)>(v).visit(), //
[](auto...) {});
};
}(std::move(s1)));
static_assert([](auto&& v) {
return requires {
forAllApiVersions(
std::forward<decltype(v)>(v).visit(), //
[](auto...) {});
};
}(std::move(std::as_const(s1))));
}
TEST_CASE_METHOD(
MultiApiJsonFixture,
"default copy construction / assignment",
"[MultiApiJson]")
{
using ripple::detail::MultiApiJson;
MultiApiJson13 subject{};
subject.val[0] = obj1;
subject.val[1] = obj2;
MultiApiJson13 x{subject};
CHECK(x.val.size() == subject.val.size());
CHECK(x.val[0] == subject.val[0]);
CHECK(x.val[1] == subject.val[1]);
CHECK(x.val[2] == subject.val[2]);
CHECK(x.val == subject.val);
CHECK(&x.val[0] != &subject.val[0]);
CHECK(&x.val[1] != &subject.val[1]);
CHECK(&x.val[2] != &subject.val[2]);
MultiApiJson13 y;
CHECK((y.val == std::array<Json::Value, 3>{}));
y = subject;
CHECK(y.val == subject.val);
CHECK(&y.val[0] != &subject.val[0]);
CHECK(&y.val[1] != &subject.val[1]);
CHECK(&y.val[2] != &subject.val[2]);
y = std::move(x);
CHECK(y.val == subject.val);
CHECK(&y.val[0] != &subject.val[0]);
CHECK(&y.val[1] != &subject.val[1]);
CHECK(&y.val[2] != &subject.val[2]);
}
TEST_CASE_METHOD(MultiApiJsonFixture, "set", "[MultiApiJson]")
{
using ripple::detail::MultiApiJson;
auto x = MultiApiJson<1, 2>{Json::objectValue};
x.set("name1", 42);
CHECK(x.val[0].isMember("name1"));
CHECK(x.val[1].isMember("name1"));
CHECK(x.val[0]["name1"].isInt());
CHECK(x.val[1]["name1"].isInt());
CHECK(x.val[0]["name1"].asInt() == 42);
CHECK(x.val[1]["name1"].asInt() == 42);
x.set("name2", "bar");
CHECK(x.val[0].isMember("name2"));
CHECK(x.val[1].isMember("name2"));
CHECK(x.val[0]["name2"].isString());
CHECK(x.val[1]["name2"].isString());
CHECK(x.val[0]["name2"].asString() == "bar");
CHECK(x.val[1]["name2"].asString() == "bar");
// Tests of requires clause - these are expected to match
static_assert([](auto&& v) {
return requires { v.set("name", Json::nullValue); };
}(x));
static_assert([](auto&& v) {
return requires { v.set("name", "value"); };
}(x));
static_assert(
[](auto&& v) { return requires { v.set("name", true); }; }(x));
static_assert(
[](auto&& v) { return requires { v.set("name", 42); }; }(x));
// Tests of requires clause - these are expected NOT to match
struct foo_t final {};
static_assert([](auto&& v) {
return !requires { v.set("name", foo_t{}); };
}(x));
static_assert([](auto&& v) {
return !requires { v.set("name", std::nullopt); };
}(x));
}
TEST_CASE_METHOD(MultiApiJsonFixture, "isMember", "[MultiApiJson]")
{
using ripple::detail::MultiApiJson;
MultiApiJson13 subject{};
subject.val[0] = obj1;
subject.val[1] = obj2;
// Well defined behaviour even if we have different types of members
CHECK(subject.isMember("foo") == decltype(subject)::none);
{
// All variants have element "One", none have element "Two"
MultiApiJson<1, 2> s1{};
s1.val[0] = makeJson("One", 12);
s1.val[1] = makeJson("One", 42);
CHECK(s1.isMember("One") == decltype(s1)::all);
CHECK(s1.isMember("Two") == decltype(s1)::none);
}
{
// Some variants have element "One" and some have "Two"
MultiApiJson<1, 2> s2{};
s2.val[0] = makeJson("One", 12);
s2.val[1] = makeJson("Two", 42);
CHECK(s2.isMember("One") == decltype(s2)::some);
CHECK(s2.isMember("Two") == decltype(s2)::some);
}
{
// Not all variants have element "One", because last one is null
MultiApiJson<1, 3> s3{};
s3.val[0] = makeJson("One", 12);
s3.val[1] = makeJson("One", 42);
CHECK(s3.isMember("One") == decltype(s3)::some);
CHECK(s3.isMember("Two") == decltype(s3)::none);
}
}
TEST_CASE_METHOD(MultiApiJsonFixture, "visitor", "[MultiApiJson]")
{
using ripple::detail::MultiApiJson;
MultiApiJson13 s1{};
s1.val[0] = makeJson("value", 2);
s1.val[1] = makeJson("value", 3);
s1.val[2] = makeJson("value", 5);
CHECK(not s1.valid(0));
CHECK(s1.index(0) == 0);
CHECK(s1.valid(1));
CHECK(s1.index(1) == 0);
CHECK(not s1.valid(4));
// Test different overloads
static_assert([](auto&& v) {
return requires {
v.visitor(
v,
std::integral_constant<unsigned, 1>{},
[](Json::Value&, std::integral_constant<unsigned, 1>) {});
};
}(s1));
CHECK(
s1.visitor(
s1,
std::integral_constant<unsigned, 1>{},
Overload{
[](Json::Value& v,
std::integral_constant<unsigned, 1>) {
return v["value"].asInt();
},
[](Json::Value const&, auto) { return 0; },
[](auto, auto) { return 0; }}) == 2);
static_assert([](auto&& v) {
return requires {
v.visitor(
v,
std::integral_constant<unsigned, 1>{},
[](Json::Value&) {});
};
}(s1));
CHECK(
s1.visitor(
s1,
std::integral_constant<unsigned, 1>{},
Overload{
[](Json::Value& v) { return v["value"].asInt(); },
[](Json::Value const&) { return 0; },
[](auto...) { return 0; }}) == 2);
static_assert([](auto&& v) {
return requires {
v.visitor(
v,
std::integral_constant<unsigned, 1>{},
[](Json::Value const&,
std::integral_constant<unsigned, 1>) {});
};
}(std::as_const(s1)));
CHECK(
s1.visitor(
std::as_const(s1),
std::integral_constant<unsigned, 2>{},
Overload{
[](Json::Value const& v,
std::integral_constant<unsigned, 2>) {
return v["value"].asInt();
},
[](Json::Value&, auto) { return 0; },
[](auto, auto) { return 0; }}) == 3);
static_assert([](auto&& v) {
return requires {
v.visitor(
v,
std::integral_constant<unsigned, 1>{},
[](auto, auto, auto...) {});
};
}(s1));
static_assert([](auto&& v) {
return requires {
v.visitor(v, 1, [](auto, auto, auto...) {});
};
}(s1));
static_assert([](auto&& v) {
return requires {
v.visitor(v, 1, [](auto, auto, auto...) {}, "");
};
}(s1));
static_assert([](auto&& v) {
return requires {
v.visitor(v, 1, [](auto, auto, auto, auto...) {}, "");
};
}(s1));
}
TEST_CASE_METHOD(MultiApiJsonFixture, "visit", "[MultiApiJson]")
{
using ripple::detail::MultiApiJson;
MultiApiJson13 s1{};
s1.val[0] = makeJson("value", 2);
s1.val[1] = makeJson("value", 3);
s1.val[2] = makeJson("value", 5);
// Test different overloads
static_assert([](auto&& v) {
return requires {
v.visit(
std::integral_constant<unsigned, 1>{},
[](Json::Value&, std::integral_constant<unsigned, 1>) {});
};
}(s1));
CHECK(
s1.visit(
std::integral_constant<unsigned, 1>{},
Overload{
[](Json::Value& v,
std::integral_constant<unsigned, 1>) {
return v["value"].asInt();
},
[](Json::Value const&, auto) { return 0; },
[](auto, auto) { return 0; }}) == 2);
static_assert([](auto&& v) {
return requires {
v.visit()(
std::integral_constant<unsigned, 1>{},
[](Json::Value&, std::integral_constant<unsigned, 1>) {});
};
}(s1));
CHECK(
s1.visit()(std::integral_constant<unsigned, 1>{},
Overload{[](Json::Value& v,
std::integral_constant<unsigned, 1>) {
return v["value"].asInt();
},
[](Json::Value const&, auto) { return 0; },
[](auto, auto) { return 0; }}) == 2);
static_assert([](auto&& v) {
return requires {
v.visit(
std::integral_constant<unsigned, 1>{},
[](Json::Value&) {});
};
}(s1));
CHECK(
s1.visit(
std::integral_constant<unsigned, 1>{},
Overload{
[](Json::Value& v) { return v["value"].asInt(); },
[](Json::Value const&) { return 0; },
[](auto...) { return 0; }}) == 2);
static_assert([](auto&& v) {
return requires {
v.visit()(std::integral_constant<unsigned, 1>{}, [](Json::Value&) {});
};
}(s1));
CHECK(
s1.visit()(std::integral_constant<unsigned, 1>{},
Overload{[](Json::Value& v) { return v["value"].asInt(); },
[](Json::Value const&) { return 0; },
[](auto...) { return 0; }}) == 2);
static_assert([](auto&& v) {
return requires {
v.visit(
std::integral_constant<unsigned, 1>{},
[](Json::Value const&,
std::integral_constant<unsigned, 1>) {});
};
}(std::as_const(s1)));
CHECK(
std::as_const(s1).visit(
std::integral_constant<unsigned, 2>{},
Overload{
[](Json::Value const& v,
std::integral_constant<unsigned, 2>) {
return v["value"].asInt();
},
[](Json::Value&, auto) { return 0; },
[](auto, auto) { return 0; }}) == 3);
static_assert([](auto&& v) {
return requires {
v.visit()(std::integral_constant<unsigned, 1>{},
[](Json::Value const&,
std::integral_constant<unsigned, 1>) {});
};
}(std::as_const(s1)));
CHECK(
std::as_const(s1).visit()(std::integral_constant<unsigned, 2>{},
Overload{[](Json::Value const& v,
std::integral_constant<unsigned,
2>) {
return v["value"].asInt();
},
[](Json::Value&, auto) { return 0; },
[](auto, auto) { return 0; }}) == 3);
static_assert([](auto&& v) {
return requires {
v.visit(
std::integral_constant<unsigned, 1>{}, [](Json::Value const&) {});
};
}(std::as_const(s1)));
CHECK(
std::as_const(s1).visit(
std::integral_constant<unsigned, 2>{},
Overload{
[](Json::Value const& v) { return v["value"].asInt(); },
[](Json::Value&) { return 0; },
[](auto...) { return 0; }}) == 3);
static_assert([](auto&& v) {
return requires {
v.visit()(std::integral_constant<unsigned, 1>{},
[](Json::Value const&) {});
};
}(std::as_const(s1)));
CHECK(
std::as_const(s1).visit()(std::integral_constant<unsigned, 2>{},
Overload{[](Json::Value const& v) {
return v["value"].asInt();
},
[](Json::Value&) { return 0; },
[](auto...) { return 0; }}) == 3);
static_assert([](auto&& v) {
return requires { v.visit(v, 1, [](auto, auto, auto...) {}); };
}(s1));
static_assert([](auto&& v) {
return requires { v.visit(v, 1, [](auto, auto, auto...) {}, ""); };
}(s1));
static_assert([](auto&& v) {
return requires {
v.visit(v, 1, [](auto, auto, auto, auto...) {}, "");
};
}(s1));
}
TEST_SUITE_END();

View File

@@ -0,0 +1,507 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <doctest/doctest.h>
#include <xrpl/protocol/PublicKey.h>
#include <xrpl/protocol/SecretKey.h>
#include <vector>
namespace ripple {
namespace {
using blob = std::vector<std::uint8_t>;
template <class FwdIter, class Container>
void
hex_to_binary(FwdIter first, FwdIter last, Container& out)
{
struct Table
{
int val[256];
Table()
{
std::fill(val, val + 256, 0);
for (int i = 0; i < 10; ++i)
val['0' + i] = i;
for (int i = 0; i < 6; ++i)
{
val['A' + i] = 10 + i;
val['a' + i] = 10 + i;
}
}
int
operator[](int i)
{
return val[i];
}
};
static Table lut;
out.reserve(std::distance(first, last) / 2);
while (first != last)
{
auto const hi(lut[(*first++)]);
auto const lo(lut[(*first++)]);
out.push_back((hi * 16) + lo);
}
}
blob
sig(std::string const& hex)
{
blob b;
hex_to_binary(hex.begin(), hex.end(), b);
return b;
}
bool
check(std::optional<ECDSACanonicality> answer, std::string const& s)
{
return ecdsaCanonicality(makeSlice(sig(s))) == answer;
}
} // namespace
TEST_SUITE_BEGIN("PublicKey");
TEST_CASE("Base58")
{
INFO("Base58: secp256k1");
{
auto const pk1 = derivePublicKey(
KeyType::secp256k1,
generateSecretKey(
KeyType::secp256k1, generateSeed("masterpassphrase")));
auto const pk2 = parseBase58<PublicKey>(
TokenType::NodePublic,
"n94a1u4jAz288pZLtw6yFWVbi89YamiC6JBXPVUj5zmExe5fTVg9");
CHECK(pk2);
CHECK(pk1 == *pk2);
}
// Try converting short, long and malformed data for secp256k1 keys
CHECK(!parseBase58<PublicKey>(TokenType::NodePublic, ""));
CHECK(!parseBase58<PublicKey>(TokenType::NodePublic, " "));
CHECK(!parseBase58<PublicKey>(TokenType::NodePublic, "!ty89234gh45"));
auto good = toBase58(
TokenType::NodePublic, derivePublicKey(KeyType::secp256k1, randomSecretKey()));
{
auto s = good;
std::hash<std::string> r;
while (!s.empty())
{
s.erase(r(s) % s.size(), 1);
CHECK(!parseBase58<PublicKey>(TokenType::NodePublic, s));
}
}
for (std::size_t i = 1; i != 16; i++)
{
auto s = good;
s.resize(s.size() + i, s[i % s.size()]);
CHECK(!parseBase58<PublicKey>(TokenType::NodePublic, s));
}
for (auto c : std::string("0IOl"))
{
for (std::size_t i = 0; i != good.size(); ++i)
{
auto s = good;
s[i % s.size()] = c;
CHECK(!parseBase58<PublicKey>(TokenType::NodePublic, s));
}
}
{
auto s = good;
for (auto c : std::string("apsrJqtv7"))
{
s[0] = c;
CHECK(!parseBase58<PublicKey>(TokenType::NodePublic, s));
}
}
std::vector<PublicKey> keys;
keys.reserve(32);
for (std::size_t i = 0; i != keys.capacity(); ++i)
keys.emplace_back(derivePublicKey(KeyType::secp256k1, randomSecretKey()));
CHECK(keys.size() == 32);
for (std::size_t i = 0; i != keys.size(); ++i)
{
auto const si = toBase58(TokenType::NodePublic, keys[i]);
CHECK(!si.empty());
auto const ski = parseBase58<PublicKey>(TokenType::NodePublic, si);
CHECK(ski);
CHECK(keys[i] == *ski);
for (std::size_t j = i; j != keys.size(); ++j)
{
CHECK((keys[i] == keys[j]) == (i == j));
auto const sj = toBase58(TokenType::NodePublic, keys[j]);
CHECK((si == sj) == (i == j));
auto const skj = parseBase58<PublicKey>(TokenType::NodePublic, sj);
CHECK(skj);
CHECK(keys[j] == *skj);
CHECK((*ski == *skj) == (i == j));
}
}
INFO("Base58: ed25519");
{
auto const pk1 = derivePublicKey(
KeyType::ed25519,
generateSecretKey(
KeyType::ed25519, generateSeed("masterpassphrase")));
auto const pk2 = parseBase58<PublicKey>(
TokenType::NodePublic,
"nHUeeJCSY2dM71oxM8Cgjouf5ekTuev2mwDpc374aLMxzDLXNmjf");
CHECK(pk2);
CHECK(pk1 == *pk2);
}
// Repeat Base58 conversion tests for ed25519 keys
good = toBase58(
TokenType::NodePublic, derivePublicKey(KeyType::ed25519, randomSecretKey()));
{
auto s = good;
std::hash<std::string> r;
while (!s.empty())
{
s.erase(r(s) % s.size(), 1);
CHECK(!parseBase58<PublicKey>(TokenType::NodePublic, s));
}
}
for (std::size_t i = 1; i != 16; i++)
{
auto s = good;
s.resize(s.size() + i, s[i % s.size()]);
CHECK(!parseBase58<PublicKey>(TokenType::NodePublic, s));
}
for (auto c : std::string("0IOl"))
{
for (std::size_t i = 0; i != good.size(); ++i)
{
auto s = good;
s[i % s.size()] = c;
CHECK(!parseBase58<PublicKey>(TokenType::NodePublic, s));
}
}
{
auto s = good;
for (auto c : std::string("apsrJqtv7"))
{
s[0] = c;
CHECK(!parseBase58<PublicKey>(TokenType::NodePublic, s));
}
}
keys.clear();
for (std::size_t i = 0; i != 32; ++i)
keys.emplace_back(derivePublicKey(KeyType::ed25519, randomSecretKey()));
CHECK(keys.size() == 32);
for (std::size_t i = 0; i != keys.size(); ++i)
{
auto const si = toBase58(TokenType::NodePublic, keys[i]);
CHECK(!si.empty());
auto const ski = parseBase58<PublicKey>(TokenType::NodePublic, si);
CHECK(ski);
CHECK(keys[i] == *ski);
for (std::size_t j = i; j != keys.size(); ++j)
{
CHECK((keys[i] == keys[j]) == (i == j));
auto const sj = toBase58(TokenType::NodePublic, keys[j]);
CHECK((si == sj) == (i == j));
auto const skj = parseBase58<PublicKey>(TokenType::NodePublic, sj);
CHECK(skj);
CHECK(keys[j] == *skj);
CHECK((*ski == *skj) == (i == j));
}
}
}
TEST_CASE("Canonical")
{
INFO("Canonical");
// Fully canonical
CHECK(check(
ECDSACanonicality::fullyCanonical,
"3045"
"022100FF478110D1D4294471EC76E0157540C2181F47DEBD25D7F9E7DDCCCD47EE"
"E905"
"0220078F07CDAE6C240855D084AD91D1479609533C147C93B0AEF19BC9724D003F"
"28"));
CHECK(check(
ECDSACanonicality::fullyCanonical,
"3045"
"0221009218248292F1762D8A51BE80F8A7F2CD288D810CE781D5955700DA1684DF"
"1D2D"
"022041A1EE1746BFD72C9760CC93A7AAA8047D52C8833A03A20EAAE92EA19717B4"
"54"));
CHECK(check(
ECDSACanonicality::fullyCanonical,
"3044"
"02206A9E43775F73B6D1EC420E4DDD222A80D4C6DF5D1BEECC431A91B63C928B75"
"81"
"022023E9CC2D61DDA6F73EAA6BCB12688BEB0F434769276B3127E4044ED895C9D9"
"6B"));
CHECK(check(
ECDSACanonicality::fullyCanonical,
"3044"
"022056E720007221F3CD4EFBB6352741D8E5A0968D48D8D032C2FBC4F6304AD1D0"
"4E"
"02201F39EB392C20D7801C3E8D81D487E742FA84A1665E923225BD6323847C7187"
"9F"));
CHECK(check(
ECDSACanonicality::fullyCanonical,
"3045"
"022100FDFD5AD05518CEA0017A2DCB5C4DF61E7C73B6D3A38E7AE93210A1564E8C"
"2F12"
"0220214FF061CCC123C81D0BB9D0EDEA04CD40D96BF1425D311DA62A7096BB18EA"
"18"));
// Canonical but not fully canonical
CHECK(check(
ECDSACanonicality::canonical,
"3046"
"022100F477B3FA6F31C7CB3A0D1AD94A231FDD24B8D78862EE334CEA7CD08F6CBC"
"0A1B"
"022100928E6BCF1ED2684679730C5414AEC48FD62282B090041C41453C1D064AF5"
"97A1"));
CHECK(check(
ECDSACanonicality::canonical,
"3045"
"022063E7C7CA93CB2400E413A342C027D00665F8BAB9C22EF0A7B8AE3AAF092230"
"B6"
"0221008F2E8BB7D09521ABBC277717B14B93170AE6465C5A1B36561099319C4BEB"
"254C"));
CHECK(check(
ECDSACanonicality::canonical,
"3046"
"02210099DCA1188663DDEA506A06A7B20C2B7D8C26AFF41DECE69D6C5F7C967D32"
"625F"
"022100897658A6B1F9EEE5D140D7A332DA0BD73BB98974EA53F6201B01C1B594F2"
"86EA"));
CHECK(check(
ECDSACanonicality::canonical,
"3045"
"02200855DE366E4E323AA2CE2A25674401A7D11F72EC432770D07F7B57DF7387AE"
"C0"
"022100DA4C6ADDEA14888858DE2AC5B91ED9050D6972BB388DEF582628CEE32869"
"AE35"));
// valid
CHECK(check(
ECDSACanonicality::fullyCanonical,
"3006"
"020101"
"020102"));
CHECK(check(
ECDSACanonicality::fullyCanonical,
"3044"
"02203932c892e2e550f3af8ee4ce9c215a87f9bb831dcac87b2838e2c2eaa891df"
"0c"
"022030b61dd36543125d56b9f9f3a1f53189e5af33cdda8d77a5209aec03978fa0"
"01"));
CHECK(check(
ECDSACanonicality::canonical,
"3045"
"0220076045be6f9eca28ff1ec606b833d0b87e70b2a630f5e3a496b110967a40f9"
"0a"
"0221008fffd599910eefe00bc803c688eca1d2ba7f6b180620eaa03488e6585db6"
"ba01"));
CHECK(check(
ECDSACanonicality::canonical,
"3046"
"022100876045be6f9eca28ff1ec606b833d0b87e70b2a630f5e3a496b110967a40"
"f90a"
"0221008fffd599910eefe00bc803c688c2eca1d2ba7f6b180620eaa03488e6585d"
"b6ba"));
CHECK(check(
std::nullopt,
"3005"
"0201FF"
"0200"));
CHECK(check(
std::nullopt,
"3006"
"020101"
"020202"));
CHECK(check(
std::nullopt,
"3006"
"020701"
"020102"));
CHECK(check(
std::nullopt,
"3006"
"020401"
"020102"));
CHECK(check(
std::nullopt,
"3006"
"020501"
"020102"));
CHECK(check(
std::nullopt,
"3006"
"020201"
"020102"));
CHECK(check(
std::nullopt,
"3006"
"020301"
"020202"));
CHECK(check(
std::nullopt,
"3006"
"020401"
"020202"));
CHECK(check(
std::nullopt,
"3047"
"0221005990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba"
"6105"
"022200002d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e56"
"6695ed"));
CHECK(check(
std::nullopt,
"3144"
"02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
"05"
"02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695"
"ed"));
CHECK(check(
std::nullopt,
"3045"
"02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
"05"
"02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695"
"ed"));
CHECK(check(
std::nullopt,
"301F"
"01205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1"));
CHECK(check(
std::nullopt,
"3045"
"02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
"05"
"02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695"
"ed00"));
CHECK(check(
std::nullopt,
"3044"
"01205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
"05"
"02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695"
"ed"));
CHECK(check(
std::nullopt,
"3024"
"0200"
"02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695"
"ed"));
CHECK(check(
std::nullopt,
"3044"
"02208990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
"05"
"02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695"
"ed"));
CHECK(check(
std::nullopt,
"3045"
"0221005990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba"
"6105"
"02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695"
"ed"));
CHECK(check(
std::nullopt,
"3044"
"02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
"05012"
"02d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695e"
"d"));
CHECK(check(
std::nullopt,
"3024"
"02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
"05"
"0200"));
CHECK(check(
std::nullopt,
"3044"
"02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
"05"
"0220fd5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695"
"ed"));
CHECK(check(
std::nullopt,
"3045"
"02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61"
"05"
"0221002d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e5666"
"95ed"));
}
TEST_CASE("Miscellaneous operations")
{
INFO("Miscellaneous operations");
auto const pk1 = derivePublicKey(
KeyType::secp256k1,
generateSecretKey(
KeyType::secp256k1, generateSeed("masterpassphrase")));
PublicKey pk2(pk1);
CHECK(pk1 == pk2);
CHECK(pk2 == pk1);
PublicKey pk3 = derivePublicKey(
KeyType::secp256k1,
generateSecretKey(
KeyType::secp256k1, generateSeed("arbitraryPassPhrase")));
pk3 = pk2;
CHECK(pk3 == pk2);
CHECK(pk1 == pk3);
}
TEST_SUITE_END();
} // namespace ripple

View File

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

View File

@@ -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());
}

View File

@@ -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,

View File

@@ -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);
}

View File

@@ -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);
}
}