mirror of
https://github.com/XRPLF/clio.git
synced 2026-04-29 15:37:53 +00:00
172
src/data/AmendmentCenter.cpp
Normal file
172
src/data/AmendmentCenter.cpp
Normal file
@@ -0,0 +1,172 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2024, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and 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 "data/AmendmentCenter.hpp"
|
||||
|
||||
#include "data/BackendInterface.hpp"
|
||||
#include "data/Types.hpp"
|
||||
#include "util/Assert.hpp"
|
||||
|
||||
#include <boost/asio/spawn.hpp>
|
||||
#include <fmt/compile.h>
|
||||
#include <xrpl/basics/Slice.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
#include <xrpl/protocol/Serializer.h>
|
||||
#include <xrpl/protocol/digest.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <ranges>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
|
||||
std::unordered_set<std::string>&
|
||||
SUPPORTED_AMENDMENTS()
|
||||
{
|
||||
static std::unordered_set<std::string> amendments = {};
|
||||
return amendments;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace data {
|
||||
namespace impl {
|
||||
|
||||
WritingAmendmentKey::WritingAmendmentKey(std::string amendmentName) : AmendmentKey{std::move(amendmentName)}
|
||||
{
|
||||
ASSERT(not SUPPORTED_AMENDMENTS().contains(name), "Attempt to register the same amendment twice");
|
||||
SUPPORTED_AMENDMENTS().insert(name);
|
||||
}
|
||||
|
||||
} // namespace impl
|
||||
|
||||
AmendmentKey::operator std::string const&() const
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
AmendmentKey::operator ripple::uint256() const
|
||||
{
|
||||
return Amendment::GetAmendmentId(name);
|
||||
}
|
||||
|
||||
AmendmentCenter::AmendmentCenter(std::shared_ptr<data::BackendInterface> const& backend) : backend_{backend}
|
||||
{
|
||||
namespace rg = std::ranges;
|
||||
namespace vs = std::views;
|
||||
|
||||
rg::copy(
|
||||
ripple::allAmendments() | vs::transform([&](auto const& p) {
|
||||
auto const& [name, support] = p;
|
||||
return Amendment{
|
||||
.name = name,
|
||||
.feature = Amendment::GetAmendmentId(name),
|
||||
.isSupportedByXRPL = support != ripple::AmendmentSupport::Unsupported,
|
||||
.isSupportedByClio = rg::find(SUPPORTED_AMENDMENTS(), name) != rg::end(SUPPORTED_AMENDMENTS()),
|
||||
.isRetired = support == ripple::AmendmentSupport::Retired
|
||||
};
|
||||
}),
|
||||
std::back_inserter(all_)
|
||||
);
|
||||
|
||||
for (auto const& am : all_ | vs::filter([](auto const& am) { return am.isSupportedByClio; }))
|
||||
supported_.insert_or_assign(am.name, am);
|
||||
}
|
||||
|
||||
bool
|
||||
AmendmentCenter::isSupported(AmendmentKey const& key) const
|
||||
{
|
||||
return supported_.contains(key);
|
||||
}
|
||||
|
||||
std::map<std::string, Amendment> const&
|
||||
AmendmentCenter::getSupported() const
|
||||
{
|
||||
return supported_;
|
||||
}
|
||||
|
||||
std::vector<Amendment> const&
|
||||
AmendmentCenter::getAll() const
|
||||
{
|
||||
return all_;
|
||||
}
|
||||
|
||||
bool
|
||||
AmendmentCenter::isEnabled(AmendmentKey const& key, uint32_t seq) const
|
||||
{
|
||||
return data::synchronous([this, &key, seq](auto yield) { return isEnabled(yield, key, seq); });
|
||||
}
|
||||
|
||||
bool
|
||||
AmendmentCenter::isEnabled(boost::asio::yield_context yield, AmendmentKey const& key, uint32_t seq) const
|
||||
{
|
||||
namespace rg = std::ranges;
|
||||
|
||||
// the amendments should always be present on the ledger
|
||||
auto const& amendments = backend_->fetchLedgerObject(ripple::keylet::amendments().key, seq, yield);
|
||||
ASSERT(amendments.has_value(), "Amendments ledger object must be present in the database");
|
||||
|
||||
ripple::SLE const amendmentsSLE{
|
||||
ripple::SerialIter{amendments->data(), amendments->size()}, ripple::keylet::amendments().key
|
||||
};
|
||||
|
||||
if (not amendmentsSLE.isFieldPresent(ripple::sfAmendments))
|
||||
return false;
|
||||
|
||||
auto const listAmendments = amendmentsSLE.getFieldV256(ripple::sfAmendments);
|
||||
|
||||
if (auto am = rg::find(all_, key.name, [](auto const& am) { return am.name; }); am != rg::end(all_)) {
|
||||
return rg::find(listAmendments, am->feature) != rg::end(listAmendments);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Amendment const&
|
||||
AmendmentCenter::getAmendment(AmendmentKey const& key) const
|
||||
{
|
||||
ASSERT(supported_.contains(key), "The amendment '{}' must be present in supported amendments list", key.name);
|
||||
return supported_.at(key);
|
||||
}
|
||||
|
||||
Amendment const&
|
||||
AmendmentCenter::operator[](AmendmentKey const& key) const
|
||||
{
|
||||
return getAmendment(key);
|
||||
}
|
||||
|
||||
ripple::uint256
|
||||
Amendment::GetAmendmentId(std::string_view name)
|
||||
{
|
||||
return ripple::sha512Half(ripple::Slice(name.data(), name.size()));
|
||||
}
|
||||
|
||||
} // namespace data
|
||||
234
src/data/AmendmentCenter.hpp
Normal file
234
src/data/AmendmentCenter.hpp
Normal file
@@ -0,0 +1,234 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2024, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and 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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "data/AmendmentCenterInterface.hpp"
|
||||
#include "data/BackendInterface.hpp"
|
||||
#include "data/Types.hpp"
|
||||
|
||||
#include <boost/asio/spawn.hpp>
|
||||
#include <boost/preprocessor.hpp>
|
||||
#include <boost/preprocessor/seq/for_each.hpp>
|
||||
#include <boost/preprocessor/stringize.hpp>
|
||||
#include <boost/preprocessor/variadic/to_seq.hpp>
|
||||
#include <xrpl/basics/Slice.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
#include <xrpl/protocol/Serializer.h>
|
||||
#include <xrpl/protocol/digest.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#define REGISTER(name) inline static impl::WritingAmendmentKey const name = std::string(BOOST_PP_STRINGIZE(name))
|
||||
|
||||
namespace data {
|
||||
namespace impl {
|
||||
|
||||
struct WritingAmendmentKey : AmendmentKey {
|
||||
WritingAmendmentKey(std::string amendmentName);
|
||||
};
|
||||
|
||||
} // namespace impl
|
||||
|
||||
/**
|
||||
* @brief List of supported amendments
|
||||
*/
|
||||
struct Amendments {
|
||||
// NOTE: if Clio wants to report it supports an Amendment it should be listed here.
|
||||
// Whether an amendment is obsolete and/or supported by libxrpl is extracted directly from libxrpl.
|
||||
// If an amendment is in the list below it just means Clio did whatever changes needed to support it.
|
||||
// Most of the time it's going to be no changes at all.
|
||||
|
||||
/** @cond */
|
||||
REGISTER(OwnerPaysFee);
|
||||
REGISTER(Flow);
|
||||
REGISTER(FlowCross);
|
||||
REGISTER(fix1513);
|
||||
REGISTER(DepositAuth);
|
||||
REGISTER(Checks);
|
||||
REGISTER(fix1571);
|
||||
REGISTER(fix1543);
|
||||
REGISTER(fix1623);
|
||||
REGISTER(DepositPreauth);
|
||||
REGISTER(fix1515);
|
||||
REGISTER(fix1578);
|
||||
REGISTER(MultiSignReserve);
|
||||
REGISTER(fixTakerDryOfferRemoval);
|
||||
REGISTER(fixMasterKeyAsRegularKey);
|
||||
REGISTER(fixCheckThreading);
|
||||
REGISTER(fixPayChanRecipientOwnerDir);
|
||||
REGISTER(DeletableAccounts);
|
||||
REGISTER(fixQualityUpperBound);
|
||||
REGISTER(RequireFullyCanonicalSig);
|
||||
REGISTER(fix1781);
|
||||
REGISTER(HardenedValidations);
|
||||
REGISTER(fixAmendmentMajorityCalc);
|
||||
REGISTER(NegativeUNL);
|
||||
REGISTER(TicketBatch);
|
||||
REGISTER(FlowSortStrands);
|
||||
REGISTER(fixSTAmountCanonicalize);
|
||||
REGISTER(fixRmSmallIncreasedQOffers);
|
||||
REGISTER(CheckCashMakesTrustLine);
|
||||
REGISTER(ExpandedSignerList);
|
||||
REGISTER(NonFungibleTokensV1_1);
|
||||
REGISTER(fixTrustLinesToSelf);
|
||||
REGISTER(fixRemoveNFTokenAutoTrustLine);
|
||||
REGISTER(ImmediateOfferKilled);
|
||||
REGISTER(DisallowIncoming);
|
||||
REGISTER(XRPFees);
|
||||
REGISTER(fixUniversalNumber);
|
||||
REGISTER(fixNonFungibleTokensV1_2);
|
||||
REGISTER(fixNFTokenRemint);
|
||||
REGISTER(fixReducedOffersV1);
|
||||
REGISTER(Clawback);
|
||||
REGISTER(AMM);
|
||||
REGISTER(XChainBridge);
|
||||
REGISTER(fixDisallowIncomingV1);
|
||||
REGISTER(DID);
|
||||
REGISTER(fixFillOrKill);
|
||||
REGISTER(fixNFTokenReserve);
|
||||
REGISTER(fixInnerObjTemplate);
|
||||
REGISTER(fixAMMOverflowOffer);
|
||||
REGISTER(PriceOracle);
|
||||
REGISTER(fixEmptyDID);
|
||||
REGISTER(fixXChainRewardRounding);
|
||||
REGISTER(fixPreviousTxnID);
|
||||
REGISTER(fixAMMv1_1);
|
||||
REGISTER(NFTokenMintOffer);
|
||||
REGISTER(fixReducedOffersV2);
|
||||
REGISTER(fixEnforceNFTokenTrustline);
|
||||
|
||||
// Obsolete but supported by libxrpl
|
||||
REGISTER(CryptoConditionsSuite);
|
||||
REGISTER(NonFungibleTokensV1);
|
||||
REGISTER(fixNFTokenDirV1);
|
||||
REGISTER(fixNFTokenNegOffer);
|
||||
|
||||
// Retired amendments
|
||||
REGISTER(MultiSign);
|
||||
REGISTER(TrustSetAuth);
|
||||
REGISTER(FeeEscalation);
|
||||
REGISTER(PayChan);
|
||||
REGISTER(fix1368);
|
||||
REGISTER(CryptoConditions);
|
||||
REGISTER(Escrow);
|
||||
REGISTER(TickSize);
|
||||
REGISTER(fix1373);
|
||||
REGISTER(EnforceInvariants);
|
||||
REGISTER(SortedDirectories);
|
||||
REGISTER(fix1201);
|
||||
REGISTER(fix1512);
|
||||
REGISTER(fix1523);
|
||||
REGISTER(fix1528);
|
||||
/** @endcond */
|
||||
};
|
||||
|
||||
#undef REGISTER
|
||||
|
||||
/**
|
||||
* @brief Knowledge center for amendments within XRPL
|
||||
*/
|
||||
class AmendmentCenter : public AmendmentCenterInterface {
|
||||
std::shared_ptr<data::BackendInterface> backend_;
|
||||
|
||||
std::map<std::string, Amendment> supported_;
|
||||
std::vector<Amendment> all_;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new AmendmentCenter instance
|
||||
*
|
||||
* @param backend The backend
|
||||
*/
|
||||
explicit AmendmentCenter(std::shared_ptr<data::BackendInterface> const& backend);
|
||||
|
||||
/**
|
||||
* @brief Check whether an amendment is supported by Clio
|
||||
*
|
||||
* @param key The key of the amendment to check
|
||||
* @return true if supported; false otherwise
|
||||
*/
|
||||
bool
|
||||
isSupported(AmendmentKey const& key) const final;
|
||||
|
||||
/**
|
||||
* @brief Get all supported amendments as a map
|
||||
*
|
||||
* @return The amendments supported by Clio
|
||||
*/
|
||||
std::map<std::string, Amendment> const&
|
||||
getSupported() const final;
|
||||
|
||||
/**
|
||||
* @brief Get all known amendments
|
||||
*
|
||||
* @return All known amendments as a vector
|
||||
*/
|
||||
std::vector<Amendment> const&
|
||||
getAll() const final;
|
||||
|
||||
/**
|
||||
* @brief Check whether an amendment was/is enabled for a given sequence
|
||||
*
|
||||
* @param key The key of the amendment to check
|
||||
* @param seq The sequence to check for
|
||||
* @return true if enabled; false otherwise
|
||||
*/
|
||||
bool
|
||||
isEnabled(AmendmentKey const& key, uint32_t seq) const final;
|
||||
|
||||
/**
|
||||
* @brief Check whether an amendment was/is enabled for a given sequence
|
||||
*
|
||||
* @param yield The coroutine context to use
|
||||
* @param key The key of the amendment to check
|
||||
* @param seq The sequence to check for
|
||||
* @return true if enabled; false otherwise
|
||||
*/
|
||||
bool
|
||||
isEnabled(boost::asio::yield_context yield, AmendmentKey const& key, uint32_t seq) const final;
|
||||
|
||||
/**
|
||||
* @brief Get an amendment
|
||||
*
|
||||
* @param key The key of the amendment to get
|
||||
* @return The amendment as a const ref; asserts if the amendment is unknown
|
||||
*/
|
||||
Amendment const&
|
||||
getAmendment(AmendmentKey const& key) const final;
|
||||
|
||||
/**
|
||||
* @brief Get an amendment by its key
|
||||
|
||||
* @param key The amendment key from @see Amendments
|
||||
* @return The amendment as a const ref; asserts if the amendment is unknown
|
||||
*/
|
||||
Amendment const&
|
||||
operator[](AmendmentKey const& key) const final;
|
||||
};
|
||||
|
||||
} // namespace data
|
||||
105
src/data/AmendmentCenterInterface.hpp
Normal file
105
src/data/AmendmentCenterInterface.hpp
Normal file
@@ -0,0 +1,105 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2024, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and 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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "data/Types.hpp"
|
||||
|
||||
#include <boost/asio/spawn.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace data {
|
||||
|
||||
/**
|
||||
* @brief The interface of an amendment center
|
||||
*/
|
||||
class AmendmentCenterInterface {
|
||||
public:
|
||||
virtual ~AmendmentCenterInterface() = default;
|
||||
|
||||
/**
|
||||
* @brief Check whether an amendment is supported by Clio
|
||||
*
|
||||
* @param key The key of the amendment to check
|
||||
* @return true if supported; false otherwise
|
||||
*/
|
||||
virtual bool
|
||||
isSupported(AmendmentKey const& key) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Get all supported amendments as a map
|
||||
*
|
||||
* @return The amendments supported by Clio
|
||||
*/
|
||||
virtual std::map<std::string, Amendment> const&
|
||||
getSupported() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Get all known amendments
|
||||
*
|
||||
* @return All known amendments as a vector
|
||||
*/
|
||||
virtual std::vector<Amendment> const&
|
||||
getAll() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Check whether an amendment was/is enabled for a given sequence
|
||||
*
|
||||
* @param key The key of the amendment to check
|
||||
* @param seq The sequence to check for
|
||||
* @return true if enabled; false otherwise
|
||||
*/
|
||||
virtual bool
|
||||
isEnabled(AmendmentKey const& key, uint32_t seq) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Check whether an amendment was/is enabled for a given sequence
|
||||
*
|
||||
* @param yield The coroutine context to use
|
||||
* @param key The key of the amendment to check
|
||||
* @param seq The sequence to check for
|
||||
* @return true if enabled; false otherwise
|
||||
*/
|
||||
virtual bool
|
||||
isEnabled(boost::asio::yield_context yield, AmendmentKey const& key, uint32_t seq) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Get an amendment
|
||||
*
|
||||
* @param key The key of the amendment to get
|
||||
* @return The amendment as a const ref; asserts if the amendment is unknown
|
||||
*/
|
||||
virtual Amendment const&
|
||||
getAmendment(AmendmentKey const& key) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Get an amendment by its key
|
||||
*
|
||||
* @param key The amendment key from @see Amendments
|
||||
* @return The amendment as a const ref; asserts if the amendment is unknown
|
||||
*/
|
||||
virtual Amendment const&
|
||||
operator[](AmendmentKey const& key) const = 0;
|
||||
};
|
||||
|
||||
} // namespace data
|
||||
@@ -1,7 +1,8 @@
|
||||
add_library(clio_data)
|
||||
target_sources(
|
||||
clio_data
|
||||
PRIVATE BackendCounters.cpp
|
||||
PRIVATE AmendmentCenter.cpp
|
||||
BackendCounters.cpp
|
||||
BackendInterface.cpp
|
||||
LedgerCache.cpp
|
||||
cassandra/impl/Future.cpp
|
||||
|
||||
@@ -22,8 +22,10 @@
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
|
||||
#include <concepts>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
@@ -239,6 +241,66 @@ struct LedgerRange {
|
||||
std::uint32_t maxSequence = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Represents an amendment in the XRPL
|
||||
*/
|
||||
struct Amendment {
|
||||
std::string name;
|
||||
ripple::uint256 feature;
|
||||
bool isSupportedByXRPL;
|
||||
bool isSupportedByClio;
|
||||
bool isRetired;
|
||||
|
||||
/**
|
||||
* @brief Get the amendment Id from its name
|
||||
*
|
||||
* @param name The name of the amendment
|
||||
* @return The amendment Id as uint256
|
||||
*/
|
||||
static ripple::uint256
|
||||
GetAmendmentId(std::string_view const name);
|
||||
|
||||
/**
|
||||
* @brief Equality comparison operator
|
||||
* @param other The object to compare to
|
||||
* @return Whether the objects are equal
|
||||
*/
|
||||
bool
|
||||
operator==(Amendment const& other) const
|
||||
{
|
||||
return name == other.name;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A helper for amendment name to feature conversions
|
||||
*/
|
||||
struct AmendmentKey {
|
||||
std::string name;
|
||||
|
||||
/**
|
||||
* @brief Construct a new AmendmentKey
|
||||
* @param val Anything convertible to a string
|
||||
*/
|
||||
AmendmentKey(std::convertible_to<std::string> auto&& val) : name{std::forward<decltype(val)>(val)}
|
||||
{
|
||||
}
|
||||
|
||||
/** @brief Conversion to string */
|
||||
operator std::string const&() const;
|
||||
|
||||
/** @brief Conversion to uint256 */
|
||||
operator ripple::uint256() const;
|
||||
|
||||
/**
|
||||
* @brief Comparison operators
|
||||
* @param other The object to compare to
|
||||
* @return Whether the objects are equal, greater or less
|
||||
*/
|
||||
auto
|
||||
operator<=>(AmendmentKey const& other) const = default;
|
||||
};
|
||||
|
||||
constexpr ripple::uint256 firstKey{"0000000000000000000000000000000000000000000000000000000000000000"};
|
||||
constexpr ripple::uint256 lastKey{"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"};
|
||||
constexpr ripple::uint256 hi192{"0000000000000000000000000000000000000000000000001111111111111111"};
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "etl/ETLHelpers.hpp"
|
||||
#include "etl/NetworkValidatedLedgersInterface.hpp"
|
||||
#include "etl/Source.hpp"
|
||||
#include "feed/SubscriptionManagerInterface.hpp"
|
||||
#include "util/Mutex.hpp"
|
||||
@@ -99,8 +99,8 @@ public:
|
||||
* @param wsPort The port of the source
|
||||
* @param validatedLedgers The network validated ledgers object
|
||||
* @param subscriptions The subscription manager object
|
||||
* @param onConnect The onConnect hook. Called when the connection is established
|
||||
* @param onDisconnect The onDisconnect hook. Called when the connection is lost
|
||||
* @param onNewLedger The onNewLedger hook. Called when a new ledger is received
|
||||
* @param onLedgerClosed The onLedgerClosed hook. Called when the ledger is closed but only if the source is
|
||||
* forwarding
|
||||
* @param connectionTimeout The connection timeout. Defaults to 30 seconds
|
||||
|
||||
@@ -109,6 +109,7 @@ public:
|
||||
* @brief Subscribe to the ledger feed.
|
||||
* @param yield The coroutine context
|
||||
* @param subscriber The subscriber to the ledger feed
|
||||
*
|
||||
* @return The ledger feed
|
||||
*/
|
||||
virtual boost::json::object
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include "data/AmendmentCenter.hpp"
|
||||
#include "data/BackendFactory.hpp"
|
||||
#include "etl/ETLService.hpp"
|
||||
#include "etl/NetworkValidatedLedgers.hpp"
|
||||
@@ -217,8 +218,9 @@ try {
|
||||
|
||||
auto workQueue = rpc::WorkQueue::make_WorkQueue(config);
|
||||
auto counters = rpc::Counters::make_Counters(workQueue);
|
||||
auto const amendmentCenter = std::make_shared<data::AmendmentCenter const>(backend);
|
||||
auto const handlerProvider = std::make_shared<rpc::impl::ProductionHandlerProvider const>(
|
||||
config, backend, subscriptions, balancer, etl, counters
|
||||
config, backend, subscriptions, balancer, etl, amendmentCenter, counters
|
||||
);
|
||||
auto const rpcEngine =
|
||||
rpc::RPCEngine::make_RPCEngine(backend, balancer, dosGuard, workQueue, counters, handlerProvider);
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2023, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and 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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Slice.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/protocol/digest.h>
|
||||
|
||||
#include <string_view>
|
||||
|
||||
namespace rpc {
|
||||
|
||||
#define REGISTER_AMENDMENT(name) inline static const ripple::uint256 name = GetAmendmentId(#name);
|
||||
|
||||
/**
|
||||
* @brief Represents a list of amendments in the XRPL.
|
||||
*/
|
||||
struct Amendments {
|
||||
/**
|
||||
* @param name The name of the amendment
|
||||
* @return The corresponding amendment Id
|
||||
*/
|
||||
static ripple::uint256
|
||||
GetAmendmentId(std::string_view const name)
|
||||
{
|
||||
return ripple::sha512Half(ripple::Slice(name.data(), name.size()));
|
||||
}
|
||||
|
||||
REGISTER_AMENDMENT(DisallowIncoming)
|
||||
REGISTER_AMENDMENT(Clawback)
|
||||
};
|
||||
} // namespace rpc
|
||||
@@ -1288,25 +1288,6 @@ getNFTID(boost::json::object const& request)
|
||||
return tokenid;
|
||||
}
|
||||
|
||||
bool
|
||||
isAmendmentEnabled(
|
||||
std::shared_ptr<data::BackendInterface const> const& backend,
|
||||
boost::asio::yield_context yield,
|
||||
uint32_t seq,
|
||||
ripple::uint256 amendmentId
|
||||
)
|
||||
{
|
||||
// the amendments should always be present in ledger
|
||||
auto const& amendments = backend->fetchLedgerObject(ripple::keylet::amendments().key, seq, yield);
|
||||
|
||||
ripple::SLE const amendmentsSLE{
|
||||
ripple::SerialIter{amendments->data(), amendments->size()}, ripple::keylet::amendments().key
|
||||
};
|
||||
|
||||
auto const listAmendments = amendmentsSLE.getFieldV256(ripple::sfAmendments);
|
||||
return std::find(listAmendments.begin(), listAmendments.end(), amendmentId) != listAmendments.end();
|
||||
}
|
||||
|
||||
boost::json::object
|
||||
toJsonWithBinaryTx(data::TransactionAndMetadata const& txnPlusMeta, std::uint32_t const apiVersion)
|
||||
{
|
||||
|
||||
@@ -566,23 +566,6 @@ specifiesCurrentOrClosedLedger(boost::json::object const& request);
|
||||
std::variant<ripple::uint256, Status>
|
||||
getNFTID(boost::json::object const& request);
|
||||
|
||||
/**
|
||||
* @brief Check if the amendment is enabled
|
||||
*
|
||||
* @param backend The backend to use
|
||||
* @param yield The yield context
|
||||
* @param seq The ledger sequence
|
||||
* @param amendmentId The amendment ID
|
||||
* @return true if the amendment is enabled
|
||||
*/
|
||||
bool
|
||||
isAmendmentEnabled(
|
||||
std::shared_ptr<data::BackendInterface const> const& backend,
|
||||
boost::asio::yield_context yield,
|
||||
uint32_t seq,
|
||||
ripple::uint256 amendmentId
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Encode CTID as string
|
||||
*
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
|
||||
#include "rpc/common/impl/HandlerProvider.hpp"
|
||||
|
||||
#include "data/AmendmentCenter.hpp"
|
||||
#include "data/AmendmentCenterInterface.hpp"
|
||||
#include "data/BackendInterface.hpp"
|
||||
#include "etl/ETLService.hpp"
|
||||
#include "feed/SubscriptionManager.hpp"
|
||||
@@ -71,12 +73,13 @@ ProductionHandlerProvider::ProductionHandlerProvider(
|
||||
std::shared_ptr<feed::SubscriptionManager> const& subscriptionManager,
|
||||
std::shared_ptr<etl::LoadBalancer> const& balancer,
|
||||
std::shared_ptr<etl::ETLService const> const& etl,
|
||||
std::shared_ptr<data::AmendmentCenterInterface const> const& amendmentCenter,
|
||||
Counters const& counters
|
||||
)
|
||||
: handlerMap_{
|
||||
{"account_channels", {AccountChannelsHandler{backend}}},
|
||||
{"account_currencies", {AccountCurrenciesHandler{backend}}},
|
||||
{"account_info", {AccountInfoHandler{backend}}},
|
||||
{"account_info", {AccountInfoHandler{backend, amendmentCenter}}},
|
||||
{"account_lines", {AccountLinesHandler{backend}}},
|
||||
{"account_nfts", {AccountNFTsHandler{backend}}},
|
||||
{"account_objects", {AccountObjectsHandler{backend}}},
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "data/AmendmentCenterInterface.hpp"
|
||||
#include "data/BackendInterface.hpp"
|
||||
#include "feed/SubscriptionManager.hpp"
|
||||
#include "rpc/common/AnyHandler.hpp"
|
||||
@@ -59,6 +60,7 @@ public:
|
||||
std::shared_ptr<feed::SubscriptionManager> const& subscriptionManager,
|
||||
std::shared_ptr<etl::LoadBalancer> const& balancer,
|
||||
std::shared_ptr<etl::ETLService const> const& etl,
|
||||
std::shared_ptr<data::AmendmentCenterInterface const> const& amendmentCenter,
|
||||
Counters const& counters
|
||||
);
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
#include "rpc/handlers/AccountInfo.hpp"
|
||||
|
||||
#include "rpc/Amendments.hpp"
|
||||
#include "data/AmendmentCenter.hpp"
|
||||
#include "rpc/Errors.hpp"
|
||||
#include "rpc/JS.hpp"
|
||||
#include "rpc/RPCHelpers.hpp"
|
||||
@@ -52,6 +52,8 @@ namespace rpc {
|
||||
AccountInfoHandler::Result
|
||||
AccountInfoHandler::process(AccountInfoHandler::Input input, Context const& ctx) const
|
||||
{
|
||||
using namespace data;
|
||||
|
||||
if (!input.account && !input.ident)
|
||||
return Error{Status{RippledError::rpcINVALID_PARAMS, ripple::RPC::missing_field_message(JS(account))}};
|
||||
|
||||
@@ -79,11 +81,12 @@ AccountInfoHandler::process(AccountInfoHandler::Input input, Context const& ctx)
|
||||
if (!accountKeylet.check(sle))
|
||||
return Error{Status{RippledError::rpcDB_DESERIALIZATION}};
|
||||
|
||||
auto const isDisallowIncomingEnabled =
|
||||
rpc::isAmendmentEnabled(sharedPtrBackend_, ctx.yield, lgrInfo.seq, rpc::Amendments::DisallowIncoming);
|
||||
auto isEnabled = [this, &ctx, seq = lgrInfo.seq](auto key) {
|
||||
return amendmentCenter_->isEnabled(ctx.yield, key, seq);
|
||||
};
|
||||
|
||||
auto const isClawbackEnabled =
|
||||
rpc::isAmendmentEnabled(sharedPtrBackend_, ctx.yield, lgrInfo.seq, rpc::Amendments::Clawback);
|
||||
auto const isDisallowIncomingEnabled = isEnabled(Amendments::DisallowIncoming);
|
||||
auto const isClawbackEnabled = isEnabled(Amendments::Clawback);
|
||||
|
||||
// Return SignerList(s) if that is requested.
|
||||
if (input.signerLists) {
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "data/AmendmentCenterInterface.hpp"
|
||||
#include "data/BackendInterface.hpp"
|
||||
#include "rpc/JS.hpp"
|
||||
#include "rpc/common/Checkers.hpp"
|
||||
@@ -48,6 +49,7 @@ namespace rpc {
|
||||
*/
|
||||
class AccountInfoHandler {
|
||||
std::shared_ptr<BackendInterface> sharedPtrBackend_;
|
||||
std::shared_ptr<data::AmendmentCenterInterface const> amendmentCenter_;
|
||||
|
||||
public:
|
||||
/**
|
||||
@@ -115,8 +117,13 @@ public:
|
||||
* @brief Construct a new AccountInfoHandler object
|
||||
*
|
||||
* @param sharedPtrBackend The backend to use
|
||||
* @param amendmentCenter The amendment center to use
|
||||
*/
|
||||
AccountInfoHandler(std::shared_ptr<BackendInterface> const& sharedPtrBackend) : sharedPtrBackend_(sharedPtrBackend)
|
||||
AccountInfoHandler(
|
||||
std::shared_ptr<BackendInterface> const& sharedPtrBackend,
|
||||
std::shared_ptr<data::AmendmentCenterInterface const> const& amendmentCenter
|
||||
)
|
||||
: sharedPtrBackend_(sharedPtrBackend), amendmentCenter_{amendmentCenter}
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -307,22 +307,7 @@ template <template <typename> typename MockType = ::testing::NiceMock>
|
||||
struct HandlerBaseTestBase : util::prometheus::WithPrometheus,
|
||||
MockBackendTestBase<MockType>,
|
||||
SyncAsioContextTest,
|
||||
MockETLServiceTestBase<MockType> {
|
||||
protected:
|
||||
void
|
||||
SetUp() override
|
||||
{
|
||||
SyncAsioContextTest::SetUp();
|
||||
MockETLServiceTestBase<MockType>::SetUp();
|
||||
}
|
||||
|
||||
void
|
||||
TearDown() override
|
||||
{
|
||||
MockETLServiceTestBase<MockType>::TearDown();
|
||||
SyncAsioContextTest::TearDown();
|
||||
}
|
||||
};
|
||||
MockETLServiceTestBase<MockType> {};
|
||||
|
||||
/**
|
||||
* @brief Fixture with a "nice" backend mock and an embedded boost::asio context.
|
||||
|
||||
83
tests/common/util/MockAmendmentCenter.hpp
Normal file
83
tests/common/util/MockAmendmentCenter.hpp
Normal file
@@ -0,0 +1,83 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2024, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and 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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "data/AmendmentCenterInterface.hpp"
|
||||
#include "data/Types.hpp"
|
||||
|
||||
#include <boost/asio/spawn.hpp>
|
||||
#include <boost/json/object.hpp>
|
||||
#include <gmock/gmock.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/LedgerHeader.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct MockAmendmentCenter : public data::AmendmentCenterInterface {
|
||||
MOCK_METHOD(bool, isSupported, (data::AmendmentKey const&), (const, override));
|
||||
|
||||
MOCK_METHOD((std::map<std::string, data::Amendment> const&), getSupported, (), (const, override));
|
||||
|
||||
MOCK_METHOD(std::vector<data::Amendment> const&, getAll, (), (const, override));
|
||||
|
||||
MOCK_METHOD(bool, isEnabled, (data::AmendmentKey const&, uint32_t), (const, override));
|
||||
|
||||
MOCK_METHOD(bool, isEnabled, (boost::asio::yield_context, data::AmendmentKey const&, uint32_t), (const, override));
|
||||
|
||||
MOCK_METHOD(data::Amendment const&, getAmendment, (data::AmendmentKey const&), (const, override));
|
||||
|
||||
MOCK_METHOD(data::Amendment const&, IndexOperator, (data::AmendmentKey const&), (const));
|
||||
|
||||
data::Amendment const&
|
||||
operator[](data::AmendmentKey const& key) const override
|
||||
{
|
||||
return IndexOperator(key);
|
||||
}
|
||||
};
|
||||
|
||||
template <template <typename> typename MockType = ::testing::NiceMock>
|
||||
struct MockAmendmentCenterSharedPtrImpl {
|
||||
std::shared_ptr<MockType<MockAmendmentCenter>> amendmentCenterMock =
|
||||
std::make_shared<MockType<MockAmendmentCenter>>();
|
||||
|
||||
operator std::shared_ptr<data::AmendmentCenterInterface>()
|
||||
{
|
||||
return amendmentCenterMock;
|
||||
}
|
||||
|
||||
operator std::shared_ptr<data::AmendmentCenterInterface const>()
|
||||
{
|
||||
return amendmentCenterMock;
|
||||
}
|
||||
|
||||
MockType<MockAmendmentCenter>&
|
||||
operator*()
|
||||
{
|
||||
return *amendmentCenterMock;
|
||||
}
|
||||
};
|
||||
|
||||
using MockAmendmentCenterSharedPtr = MockAmendmentCenterSharedPtrImpl<>;
|
||||
using StrictMockAmendmentCenterSharedPtr = MockAmendmentCenterSharedPtrImpl<testing::StrictMock>;
|
||||
@@ -4,6 +4,7 @@ target_sources(
|
||||
clio_tests
|
||||
PRIVATE # Common
|
||||
ConfigTests.cpp
|
||||
data/AmendmentCenterTests.cpp
|
||||
data/BackendCountersTests.cpp
|
||||
data/BackendInterfaceTests.cpp
|
||||
data/cassandra/AsyncExecutorTests.cpp
|
||||
@@ -45,7 +46,6 @@ target_sources(
|
||||
Playground.cpp
|
||||
ProfilerTests.cpp
|
||||
# RPC
|
||||
rpc/AmendmentsTests.cpp
|
||||
rpc/APIVersionTests.cpp
|
||||
rpc/BaseTests.cpp
|
||||
rpc/CountersTests.cpp
|
||||
|
||||
@@ -22,6 +22,11 @@
|
||||
* Note: Please don't push your temporary work to the repo.
|
||||
*/
|
||||
|
||||
// TEST(PlaygroundTest, MyTest)
|
||||
// #include <gmock/gmock.h>
|
||||
// #include <gtest/gtest.h>
|
||||
|
||||
// using namespace testing;
|
||||
|
||||
// TEST(PlaygroundTest, Test)
|
||||
// {
|
||||
// }
|
||||
|
||||
129
tests/unit/data/AmendmentCenterTests.cpp
Normal file
129
tests/unit/data/AmendmentCenterTests.cpp
Normal file
@@ -0,0 +1,129 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2024, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and 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 "data/AmendmentCenter.hpp"
|
||||
#include "data/Types.hpp"
|
||||
#include "util/Fixtures.hpp"
|
||||
#include "util/MockPrometheus.hpp"
|
||||
#include "util/TestObject.hpp"
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
using namespace data;
|
||||
|
||||
constexpr auto SEQ = 30;
|
||||
|
||||
struct AmendmentCenterTest : util::prometheus::WithPrometheus, MockBackendTest {
|
||||
AmendmentCenter amendmentCenter{backend};
|
||||
};
|
||||
|
||||
// This is a safety net test that will fail anytime we built Clio against a new libXRPL that added some Amendment that
|
||||
// we forgot to register in data::Amendments.
|
||||
TEST_F(AmendmentCenterTest, AllAmendmentsFromLibXRPLAreSupported)
|
||||
{
|
||||
for (auto const& [name, _] : ripple::allAmendments()) {
|
||||
ASSERT_TRUE(amendmentCenter.isSupported(name)) << "XRPL amendment not supported by Clio: " << name;
|
||||
}
|
||||
|
||||
ASSERT_EQ(amendmentCenter.getSupported().size(), ripple::allAmendments().size());
|
||||
ASSERT_EQ(amendmentCenter.getAll().size(), ripple::allAmendments().size());
|
||||
}
|
||||
|
||||
TEST_F(AmendmentCenterTest, Accessors)
|
||||
{
|
||||
{
|
||||
auto const am = amendmentCenter.getAmendment("DisallowIncoming");
|
||||
EXPECT_EQ(am.feature, ripple::uint256("47C3002ABA31628447E8E9A8B315FAA935CE30183F9A9B86845E469CA2CDC3DF"));
|
||||
}
|
||||
{
|
||||
auto const am = amendmentCenter["DisallowIncoming"];
|
||||
EXPECT_EQ(am.feature, ripple::uint256("47C3002ABA31628447E8E9A8B315FAA935CE30183F9A9B86845E469CA2CDC3DF"));
|
||||
}
|
||||
|
||||
auto const a = amendmentCenter[Amendments::OwnerPaysFee];
|
||||
auto const b = amendmentCenter["OwnerPaysFee"];
|
||||
EXPECT_EQ(a, b);
|
||||
}
|
||||
|
||||
TEST_F(AmendmentCenterTest, IsEnabled)
|
||||
{
|
||||
EXPECT_TRUE(amendmentCenter.isSupported("fixUniversalNumber"));
|
||||
EXPECT_FALSE(amendmentCenter.isSupported("unknown"));
|
||||
|
||||
auto const amendments = CreateAmendmentsObject({Amendments::fixUniversalNumber});
|
||||
EXPECT_CALL(*backend, doFetchLedgerObject(ripple::keylet::amendments().key, SEQ, testing::_))
|
||||
.WillRepeatedly(testing::Return(amendments.getSerializer().peekData()));
|
||||
|
||||
EXPECT_TRUE(amendmentCenter.isEnabled("fixUniversalNumber", SEQ));
|
||||
EXPECT_FALSE(amendmentCenter.isEnabled("unknown", SEQ));
|
||||
EXPECT_FALSE(amendmentCenter.isEnabled("ImmediateOfferKilled", SEQ));
|
||||
}
|
||||
|
||||
TEST(AmendmentTest, GenerateAmendmentId)
|
||||
{
|
||||
// https://xrpl.org/known-amendments.html#disallowincoming refer to the published id
|
||||
EXPECT_EQ(
|
||||
ripple::uint256("47C3002ABA31628447E8E9A8B315FAA935CE30183F9A9B86845E469CA2CDC3DF"),
|
||||
Amendment::GetAmendmentId("DisallowIncoming")
|
||||
);
|
||||
}
|
||||
|
||||
struct AmendmentCenterDeathTest : AmendmentCenterTest {};
|
||||
|
||||
TEST_F(AmendmentCenterDeathTest, GetInvalidAmendmentAsserts)
|
||||
{
|
||||
EXPECT_DEATH({ amendmentCenter.getAmendment("invalidAmendmentKey"); }, ".*");
|
||||
EXPECT_DEATH({ amendmentCenter["invalidAmendmentKey"]; }, ".*");
|
||||
}
|
||||
|
||||
struct AmendmentKeyTest : testing::Test {};
|
||||
|
||||
TEST_F(AmendmentKeyTest, Convertible)
|
||||
{
|
||||
std::string key1 = "key1";
|
||||
auto key2 = "key2";
|
||||
|
||||
EXPECT_NO_THROW({
|
||||
auto const first = AmendmentKey(key1);
|
||||
auto const second = AmendmentKey(key2);
|
||||
auto const third = AmendmentKey("test");
|
||||
|
||||
std::string s1 = first;
|
||||
EXPECT_EQ(s1, key1);
|
||||
|
||||
ripple::uint256 k1 = first;
|
||||
ripple::uint256 k2 = second;
|
||||
|
||||
EXPECT_EQ(k1, ripple::uint256{"7E365F775657DC0EB960E6295A1F44B3F67479F54D5D12C5D87E6DB234F072E3"});
|
||||
EXPECT_EQ(k2, ripple::uint256{"B4F33541E0E2FC2F7AA17D2D2E6A9B424809123485251D3413E91CC462309772"});
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(AmendmentKeyTest, Comparison)
|
||||
{
|
||||
auto const first = AmendmentKey("1");
|
||||
auto const second = AmendmentKey("2");
|
||||
EXPECT_GT(second, first);
|
||||
}
|
||||
@@ -17,13 +17,15 @@
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include "data/AmendmentCenter.hpp"
|
||||
#include "data/AmendmentCenterInterface.hpp"
|
||||
#include "data/Types.hpp"
|
||||
#include "rpc/Amendments.hpp"
|
||||
#include "rpc/Errors.hpp"
|
||||
#include "rpc/common/AnyHandler.hpp"
|
||||
#include "rpc/common/Types.hpp"
|
||||
#include "rpc/handlers/AccountInfo.hpp"
|
||||
#include "util/Fixtures.hpp"
|
||||
#include "util/MockAmendmentCenter.hpp"
|
||||
#include "util/NameGenerator.hpp"
|
||||
#include "util/TestObject.hpp"
|
||||
|
||||
@@ -52,7 +54,10 @@ constexpr static auto ACCOUNT2 = "rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun";
|
||||
constexpr static auto LEDGERHASH = "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652";
|
||||
constexpr static auto INDEX1 = "1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515BC";
|
||||
|
||||
class RPCAccountInfoHandlerTest : public HandlerBaseTest {};
|
||||
struct RPCAccountInfoHandlerTest : HandlerBaseTest {
|
||||
protected:
|
||||
StrictMockAmendmentCenterSharedPtr mockAmendmentCenterPtr;
|
||||
};
|
||||
|
||||
struct AccountInfoParamTestCaseBundle {
|
||||
std::string testName;
|
||||
@@ -112,7 +117,7 @@ TEST_P(AccountInfoParameterTest, InvalidParams)
|
||||
{
|
||||
auto const testBundle = GetParam();
|
||||
runSpawn([&, this](auto yield) {
|
||||
auto const handler = AnyHandler{AccountInfoHandler{backend}};
|
||||
auto const handler = AnyHandler{AccountInfoHandler{backend, mockAmendmentCenterPtr}};
|
||||
auto const req = json::parse(testBundle.testJson);
|
||||
auto const output = handler.process(req, Context{.yield = yield, .apiVersion = 2});
|
||||
ASSERT_FALSE(output);
|
||||
@@ -132,7 +137,7 @@ TEST_F(AccountInfoParameterTest, ApiV1SignerListIsNotBool)
|
||||
EXPECT_CALL(*backend, fetchLedgerBySequence);
|
||||
|
||||
runSpawn([&, this](auto yield) {
|
||||
auto const handler = AnyHandler{AccountInfoHandler{backend}};
|
||||
auto const handler = AnyHandler{AccountInfoHandler{backend, mockAmendmentCenterPtr}};
|
||||
auto const req = json::parse(reqJson);
|
||||
auto const output = handler.process(req, Context{.yield = yield, .apiVersion = 1});
|
||||
ASSERT_FALSE(output);
|
||||
@@ -158,7 +163,7 @@ TEST_F(RPCAccountInfoHandlerTest, LedgerNonExistViaIntSequence)
|
||||
}})",
|
||||
ACCOUNT
|
||||
));
|
||||
auto const handler = AnyHandler{AccountInfoHandler{backend}};
|
||||
auto const handler = AnyHandler{AccountInfoHandler{backend, mockAmendmentCenterPtr}};
|
||||
runSpawn([&](auto yield) {
|
||||
auto const output = handler.process(input, Context{yield});
|
||||
ASSERT_FALSE(output);
|
||||
@@ -182,7 +187,7 @@ TEST_F(RPCAccountInfoHandlerTest, LedgerNonExistViaStringSequence)
|
||||
}})",
|
||||
ACCOUNT
|
||||
));
|
||||
auto const handler = AnyHandler{AccountInfoHandler{backend}};
|
||||
auto const handler = AnyHandler{AccountInfoHandler{backend, mockAmendmentCenterPtr}};
|
||||
runSpawn([&](auto yield) {
|
||||
auto const output = handler.process(input, Context{yield});
|
||||
ASSERT_FALSE(output);
|
||||
@@ -208,7 +213,7 @@ TEST_F(RPCAccountInfoHandlerTest, LedgerNonExistViaHash)
|
||||
ACCOUNT,
|
||||
LEDGERHASH
|
||||
));
|
||||
auto const handler = AnyHandler{AccountInfoHandler{backend}};
|
||||
auto const handler = AnyHandler{AccountInfoHandler{backend, mockAmendmentCenterPtr}};
|
||||
runSpawn([&](auto yield) {
|
||||
auto const output = handler.process(input, Context{yield});
|
||||
ASSERT_FALSE(output);
|
||||
@@ -234,7 +239,7 @@ TEST_F(RPCAccountInfoHandlerTest, AccountNotExist)
|
||||
}})",
|
||||
ACCOUNT
|
||||
));
|
||||
auto const handler = AnyHandler{AccountInfoHandler{backend}};
|
||||
auto const handler = AnyHandler{AccountInfoHandler{backend, mockAmendmentCenterPtr}};
|
||||
runSpawn([&](auto yield) {
|
||||
auto const output = handler.process(input, Context{yield});
|
||||
ASSERT_FALSE(output);
|
||||
@@ -261,7 +266,7 @@ TEST_F(RPCAccountInfoHandlerTest, AccountInvalid)
|
||||
}})",
|
||||
ACCOUNT
|
||||
));
|
||||
auto const handler = AnyHandler{AccountInfoHandler{backend}};
|
||||
auto const handler = AnyHandler{AccountInfoHandler{backend, mockAmendmentCenterPtr}};
|
||||
runSpawn([&](auto yield) {
|
||||
auto const output = handler.process(input, Context{yield});
|
||||
ASSERT_FALSE(output);
|
||||
@@ -286,9 +291,9 @@ TEST_F(RPCAccountInfoHandlerTest, SignerListsInvalid)
|
||||
auto signersKey = ripple::keylet::signers(account).key;
|
||||
ON_CALL(*backend, doFetchLedgerObject(signersKey, 30, _))
|
||||
.WillByDefault(Return(CreateLegacyFeeSettingBlob(1, 2, 3, 4, 0)));
|
||||
ON_CALL(*backend, doFetchLedgerObject(ripple::keylet::amendments().key, 30, _))
|
||||
.WillByDefault(Return(CreateAmendmentsObject({}).getSerializer().peekData()));
|
||||
EXPECT_CALL(*backend, doFetchLedgerObject).Times(4);
|
||||
EXPECT_CALL(*mockAmendmentCenterPtr, isEnabled(_, Amendments::DisallowIncoming, _)).WillOnce(Return(false));
|
||||
EXPECT_CALL(*mockAmendmentCenterPtr, isEnabled(_, Amendments::Clawback, _)).WillOnce(Return(false));
|
||||
EXPECT_CALL(*backend, doFetchLedgerObject).Times(2);
|
||||
|
||||
auto static const input = json::parse(fmt::format(
|
||||
R"({{
|
||||
@@ -297,7 +302,7 @@ TEST_F(RPCAccountInfoHandlerTest, SignerListsInvalid)
|
||||
}})",
|
||||
ACCOUNT
|
||||
));
|
||||
auto const handler = AnyHandler{AccountInfoHandler{backend}};
|
||||
auto const handler = AnyHandler{AccountInfoHandler{backend, mockAmendmentCenterPtr}};
|
||||
runSpawn([&](auto yield) {
|
||||
auto const output = handler.process(input, Context{yield});
|
||||
ASSERT_FALSE(output);
|
||||
@@ -390,9 +395,9 @@ TEST_F(RPCAccountInfoHandlerTest, SignerListsTrueV2)
|
||||
auto signersKey = ripple::keylet::signers(account).key;
|
||||
ON_CALL(*backend, doFetchLedgerObject(signersKey, 30, _))
|
||||
.WillByDefault(Return(CreateSignerLists({{ACCOUNT1, 1}, {ACCOUNT2, 1}}).getSerializer().peekData()));
|
||||
ON_CALL(*backend, doFetchLedgerObject(ripple::keylet::amendments().key, 30, _))
|
||||
.WillByDefault(Return(CreateAmendmentsObject({}).getSerializer().peekData()));
|
||||
EXPECT_CALL(*backend, doFetchLedgerObject).Times(4);
|
||||
EXPECT_CALL(*mockAmendmentCenterPtr, isEnabled(_, Amendments::DisallowIncoming, _)).WillOnce(Return(false));
|
||||
EXPECT_CALL(*mockAmendmentCenterPtr, isEnabled(_, Amendments::Clawback, _)).WillOnce(Return(false));
|
||||
EXPECT_CALL(*backend, doFetchLedgerObject).Times(2);
|
||||
|
||||
auto static const input = json::parse(fmt::format(
|
||||
R"({{
|
||||
@@ -401,7 +406,7 @@ TEST_F(RPCAccountInfoHandlerTest, SignerListsTrueV2)
|
||||
}})",
|
||||
ACCOUNT
|
||||
));
|
||||
auto const handler = AnyHandler{AccountInfoHandler{backend}};
|
||||
auto const handler = AnyHandler{AccountInfoHandler{backend, mockAmendmentCenterPtr}};
|
||||
runSpawn([&](auto yield) {
|
||||
auto const output = handler.process(input, Context{.yield = yield, .apiVersion = 2});
|
||||
ASSERT_TRUE(output);
|
||||
@@ -492,9 +497,9 @@ TEST_F(RPCAccountInfoHandlerTest, SignerListsTrueV1)
|
||||
auto signersKey = ripple::keylet::signers(account).key;
|
||||
ON_CALL(*backend, doFetchLedgerObject(signersKey, 30, _))
|
||||
.WillByDefault(Return(CreateSignerLists({{ACCOUNT1, 1}, {ACCOUNT2, 1}}).getSerializer().peekData()));
|
||||
ON_CALL(*backend, doFetchLedgerObject(ripple::keylet::amendments().key, 30, _))
|
||||
.WillByDefault(Return(CreateAmendmentsObject({}).getSerializer().peekData()));
|
||||
EXPECT_CALL(*backend, doFetchLedgerObject).Times(4);
|
||||
EXPECT_CALL(*mockAmendmentCenterPtr, isEnabled(_, Amendments::DisallowIncoming, _)).WillOnce(Return(false));
|
||||
EXPECT_CALL(*mockAmendmentCenterPtr, isEnabled(_, Amendments::Clawback, _)).WillOnce(Return(false));
|
||||
EXPECT_CALL(*backend, doFetchLedgerObject).Times(2);
|
||||
|
||||
auto static const input = json::parse(fmt::format(
|
||||
R"({{
|
||||
@@ -503,7 +508,7 @@ TEST_F(RPCAccountInfoHandlerTest, SignerListsTrueV1)
|
||||
}})",
|
||||
ACCOUNT
|
||||
));
|
||||
auto const handler = AnyHandler{AccountInfoHandler{backend}};
|
||||
auto const handler = AnyHandler{AccountInfoHandler{backend, mockAmendmentCenterPtr}};
|
||||
runSpawn([&](auto yield) {
|
||||
auto const output = handler.process(input, Context{.yield = yield, .apiVersion = 1});
|
||||
ASSERT_TRUE(output);
|
||||
@@ -567,9 +572,9 @@ TEST_F(RPCAccountInfoHandlerTest, Flags)
|
||||
);
|
||||
ON_CALL(*backend, doFetchLedgerObject(accountKk, 30, _))
|
||||
.WillByDefault(Return(accountRoot.getSerializer().peekData()));
|
||||
ON_CALL(*backend, doFetchLedgerObject(ripple::keylet::amendments().key, 30, _))
|
||||
.WillByDefault(Return(CreateAmendmentsObject({}).getSerializer().peekData()));
|
||||
EXPECT_CALL(*backend, doFetchLedgerObject).Times(3);
|
||||
EXPECT_CALL(*mockAmendmentCenterPtr, isEnabled(_, Amendments::DisallowIncoming, _)).WillOnce(Return(false));
|
||||
EXPECT_CALL(*mockAmendmentCenterPtr, isEnabled(_, Amendments::Clawback, _)).WillOnce(Return(false));
|
||||
EXPECT_CALL(*backend, doFetchLedgerObject);
|
||||
|
||||
auto static const input = json::parse(fmt::format(
|
||||
R"({{
|
||||
@@ -577,7 +582,7 @@ TEST_F(RPCAccountInfoHandlerTest, Flags)
|
||||
}})",
|
||||
ACCOUNT
|
||||
));
|
||||
auto const handler = AnyHandler{AccountInfoHandler{backend}};
|
||||
auto const handler = AnyHandler{AccountInfoHandler{backend, mockAmendmentCenterPtr}};
|
||||
runSpawn([&](auto yield) {
|
||||
auto const output = handler.process(input, Context{yield});
|
||||
ASSERT_TRUE(output);
|
||||
@@ -597,9 +602,9 @@ TEST_F(RPCAccountInfoHandlerTest, IdentAndSignerListsFalse)
|
||||
auto const accountRoot = CreateAccountRootObject(ACCOUNT, 0, 2, 200, 2, INDEX1, 2);
|
||||
ON_CALL(*backend, doFetchLedgerObject(accountKk, 30, _))
|
||||
.WillByDefault(Return(accountRoot.getSerializer().peekData()));
|
||||
ON_CALL(*backend, doFetchLedgerObject(ripple::keylet::amendments().key, 30, _))
|
||||
.WillByDefault(Return(CreateAmendmentsObject({}).getSerializer().peekData()));
|
||||
EXPECT_CALL(*backend, doFetchLedgerObject).Times(3);
|
||||
EXPECT_CALL(*mockAmendmentCenterPtr, isEnabled(_, Amendments::DisallowIncoming, _)).WillOnce(Return(false));
|
||||
EXPECT_CALL(*mockAmendmentCenterPtr, isEnabled(_, Amendments::Clawback, _)).WillOnce(Return(false));
|
||||
EXPECT_CALL(*backend, doFetchLedgerObject);
|
||||
|
||||
auto static const input = json::parse(fmt::format(
|
||||
R"({{
|
||||
@@ -607,7 +612,7 @@ TEST_F(RPCAccountInfoHandlerTest, IdentAndSignerListsFalse)
|
||||
}})",
|
||||
ACCOUNT
|
||||
));
|
||||
auto const handler = AnyHandler{AccountInfoHandler{backend}};
|
||||
auto const handler = AnyHandler{AccountInfoHandler{backend, mockAmendmentCenterPtr}};
|
||||
runSpawn([&](auto yield) {
|
||||
auto const output = handler.process(input, Context{yield});
|
||||
ASSERT_TRUE(output);
|
||||
@@ -676,9 +681,9 @@ TEST_F(RPCAccountInfoHandlerTest, DisallowIncoming)
|
||||
);
|
||||
ON_CALL(*backend, doFetchLedgerObject(accountKk, 30, _))
|
||||
.WillByDefault(Return(accountRoot.getSerializer().peekData()));
|
||||
ON_CALL(*backend, doFetchLedgerObject(ripple::keylet::amendments().key, 30, _))
|
||||
.WillByDefault(Return(CreateAmendmentsObject({rpc::Amendments::DisallowIncoming}).getSerializer().peekData()));
|
||||
EXPECT_CALL(*backend, doFetchLedgerObject).Times(3);
|
||||
EXPECT_CALL(*mockAmendmentCenterPtr, isEnabled(_, Amendments::DisallowIncoming, _)).WillOnce(Return(true));
|
||||
EXPECT_CALL(*mockAmendmentCenterPtr, isEnabled(_, Amendments::Clawback, _)).WillOnce(Return(false));
|
||||
EXPECT_CALL(*backend, doFetchLedgerObject);
|
||||
|
||||
auto static const input = json::parse(fmt::format(
|
||||
R"({{
|
||||
@@ -686,7 +691,7 @@ TEST_F(RPCAccountInfoHandlerTest, DisallowIncoming)
|
||||
}})",
|
||||
ACCOUNT
|
||||
));
|
||||
auto const handler = AnyHandler{AccountInfoHandler{backend}};
|
||||
auto const handler = AnyHandler{AccountInfoHandler{backend, mockAmendmentCenterPtr}};
|
||||
runSpawn([&](auto yield) {
|
||||
auto const output = handler.process(input, Context{yield});
|
||||
ASSERT_TRUE(output);
|
||||
@@ -751,9 +756,9 @@ TEST_F(RPCAccountInfoHandlerTest, Clawback)
|
||||
);
|
||||
ON_CALL(*backend, doFetchLedgerObject(accountKk, 30, _))
|
||||
.WillByDefault(Return(accountRoot.getSerializer().peekData()));
|
||||
ON_CALL(*backend, doFetchLedgerObject(ripple::keylet::amendments().key, 30, _))
|
||||
.WillByDefault(Return(CreateAmendmentsObject({rpc::Amendments::Clawback}).getSerializer().peekData()));
|
||||
EXPECT_CALL(*backend, doFetchLedgerObject).Times(3);
|
||||
EXPECT_CALL(*mockAmendmentCenterPtr, isEnabled(_, Amendments::DisallowIncoming, _)).WillOnce(Return(false));
|
||||
EXPECT_CALL(*mockAmendmentCenterPtr, isEnabled(_, Amendments::Clawback, _)).WillOnce(Return(true));
|
||||
EXPECT_CALL(*backend, doFetchLedgerObject);
|
||||
|
||||
auto static const input = json::parse(fmt::format(
|
||||
R"({{
|
||||
@@ -761,7 +766,7 @@ TEST_F(RPCAccountInfoHandlerTest, Clawback)
|
||||
}})",
|
||||
ACCOUNT
|
||||
));
|
||||
auto const handler = AnyHandler{AccountInfoHandler{backend}};
|
||||
auto const handler = AnyHandler{AccountInfoHandler{backend, mockAmendmentCenterPtr}};
|
||||
runSpawn([&](auto yield) {
|
||||
auto const output = handler.process(input, Context{yield});
|
||||
ASSERT_TRUE(output);
|
||||
|
||||
Reference in New Issue
Block a user