mirror of
https://github.com/XRPLF/rippled.git
synced 2026-04-29 15:37:57 +00:00
Merge branch 'develop' into ximinez/number-maxint-range
This commit is contained in:
@@ -208,7 +208,8 @@ SharedIntrusive<T>::operator->() const noexcept
|
||||
}
|
||||
|
||||
template <class T>
|
||||
SharedIntrusive<T>::operator bool() const noexcept
|
||||
SharedIntrusive<T>::
|
||||
operator bool() const noexcept
|
||||
{
|
||||
return bool(unsafeGetRawPtr());
|
||||
}
|
||||
@@ -503,7 +504,8 @@ SharedWeakUnion<T>::getStrong() const
|
||||
}
|
||||
|
||||
template <class T>
|
||||
SharedWeakUnion<T>::operator bool() const noexcept
|
||||
SharedWeakUnion<T>::
|
||||
operator bool() const noexcept
|
||||
{
|
||||
return bool(get());
|
||||
}
|
||||
|
||||
@@ -63,7 +63,8 @@ SharedWeakCachePointer<T>::getStrong() const
|
||||
}
|
||||
|
||||
template <class T>
|
||||
SharedWeakCachePointer<T>::operator bool() const noexcept
|
||||
SharedWeakCachePointer<T>::
|
||||
operator bool() const noexcept
|
||||
{
|
||||
return !!std::get_if<std::shared_ptr<T>>(&combo_);
|
||||
}
|
||||
|
||||
@@ -215,7 +215,7 @@ public:
|
||||
// clang-format off
|
||||
if (!buf) [[unlikely]]
|
||||
return nullptr;
|
||||
// clang-format on
|
||||
// clang-format on
|
||||
|
||||
#if BOOST_OS_LINUX
|
||||
// When allocating large blocks, attempt to leverage Linux's
|
||||
|
||||
93
include/xrpl/conditions/Condition.h
Normal file
93
include/xrpl/conditions/Condition.h
Normal file
@@ -0,0 +1,93 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Buffer.h>
|
||||
#include <xrpl/basics/Slice.h>
|
||||
#include <xrpl/conditions/detail/utils.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <set>
|
||||
|
||||
namespace xrpl {
|
||||
namespace cryptoconditions {
|
||||
|
||||
enum class Type : std::uint8_t {
|
||||
preimageSha256 = 0,
|
||||
prefixSha256 = 1,
|
||||
thresholdSha256 = 2,
|
||||
rsaSha256 = 3,
|
||||
ed25519Sha256 = 4
|
||||
};
|
||||
|
||||
class Condition
|
||||
{
|
||||
public:
|
||||
/** The largest binary condition we support.
|
||||
|
||||
@note This value will be increased in the future, but it
|
||||
must never decrease, as that could cause conditions
|
||||
that were previously considered valid to no longer
|
||||
be allowed.
|
||||
*/
|
||||
static constexpr std::size_t maxSerializedCondition = 128;
|
||||
|
||||
/** Load a condition from its binary form
|
||||
|
||||
@param s The buffer containing the fulfillment to load.
|
||||
@param ec Set to the error, if any occurred.
|
||||
|
||||
The binary format for a condition is specified in the
|
||||
cryptoconditions RFC. See:
|
||||
|
||||
https://tools.ietf.org/html/draft-thomas-crypto-conditions-02#section-7.2
|
||||
*/
|
||||
static std::unique_ptr<Condition>
|
||||
deserialize(Slice s, std::error_code& ec);
|
||||
|
||||
public:
|
||||
Type type;
|
||||
|
||||
/** An identifier for this condition.
|
||||
|
||||
This fingerprint is meant to be unique only with
|
||||
respect to other conditions of the same type.
|
||||
*/
|
||||
Buffer fingerprint;
|
||||
|
||||
/** The cost associated with this condition. */
|
||||
std::uint32_t cost;
|
||||
|
||||
/** For compound conditions, set of conditions includes */
|
||||
std::set<Type> subtypes;
|
||||
|
||||
Condition(Type t, std::uint32_t c, Slice fp) : type(t), fingerprint(fp), cost(c)
|
||||
{
|
||||
}
|
||||
|
||||
Condition(Type t, std::uint32_t c, Buffer&& fp) : type(t), fingerprint(std::move(fp)), cost(c)
|
||||
{
|
||||
}
|
||||
|
||||
~Condition() = default;
|
||||
|
||||
Condition(Condition const&) = default;
|
||||
Condition(Condition&&) = default;
|
||||
|
||||
Condition() = delete;
|
||||
};
|
||||
|
||||
inline bool
|
||||
operator==(Condition const& lhs, Condition const& rhs)
|
||||
{
|
||||
return lhs.type == rhs.type && lhs.cost == rhs.cost && lhs.subtypes == rhs.subtypes &&
|
||||
lhs.fingerprint == rhs.fingerprint;
|
||||
}
|
||||
|
||||
inline bool
|
||||
operator!=(Condition const& lhs, Condition const& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
} // namespace cryptoconditions
|
||||
|
||||
} // namespace xrpl
|
||||
122
include/xrpl/conditions/Fulfillment.h
Normal file
122
include/xrpl/conditions/Fulfillment.h
Normal file
@@ -0,0 +1,122 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Buffer.h>
|
||||
#include <xrpl/basics/Slice.h>
|
||||
#include <xrpl/conditions/Condition.h>
|
||||
|
||||
namespace xrpl {
|
||||
namespace cryptoconditions {
|
||||
|
||||
struct Fulfillment
|
||||
{
|
||||
public:
|
||||
/** The largest binary fulfillment we support.
|
||||
|
||||
@note This value will be increased in the future, but it
|
||||
must never decrease, as that could cause fulfillments
|
||||
that were previously considered valid to no longer
|
||||
be allowed.
|
||||
*/
|
||||
static constexpr std::size_t maxSerializedFulfillment = 256;
|
||||
|
||||
/** Load a fulfillment from its binary form
|
||||
|
||||
@param s The buffer containing the fulfillment to load.
|
||||
@param ec Set to the error, if any occurred.
|
||||
|
||||
The binary format for a fulfillment is specified in the
|
||||
cryptoconditions RFC. See:
|
||||
|
||||
https://tools.ietf.org/html/draft-thomas-crypto-conditions-02#section-7.3
|
||||
*/
|
||||
static std::unique_ptr<Fulfillment>
|
||||
deserialize(Slice s, std::error_code& ec);
|
||||
|
||||
public:
|
||||
virtual ~Fulfillment() = default;
|
||||
|
||||
/** Returns the fulfillment's fingerprint:
|
||||
|
||||
The fingerprint is an octet string uniquely
|
||||
representing this fulfillment's condition
|
||||
with respect to other conditions of the
|
||||
same type.
|
||||
*/
|
||||
virtual Buffer
|
||||
fingerprint() const = 0;
|
||||
|
||||
/** Returns the type of this condition. */
|
||||
virtual Type
|
||||
type() const = 0;
|
||||
|
||||
/** Validates a fulfillment. */
|
||||
virtual bool
|
||||
validate(Slice data) const = 0;
|
||||
|
||||
/** Calculates the cost associated with this fulfillment. *
|
||||
|
||||
The cost function is deterministic and depends on the
|
||||
type and properties of the condition and the fulfillment
|
||||
that the condition is generated from.
|
||||
*/
|
||||
virtual std::uint32_t
|
||||
cost() const = 0;
|
||||
|
||||
/** Returns the condition associated with the given fulfillment.
|
||||
|
||||
This process is completely deterministic. All implementations
|
||||
will, if compliant, produce the identical condition for the
|
||||
same fulfillment.
|
||||
*/
|
||||
virtual Condition
|
||||
condition() const = 0;
|
||||
};
|
||||
|
||||
inline bool
|
||||
operator==(Fulfillment const& lhs, Fulfillment const& rhs)
|
||||
{
|
||||
// FIXME: for compound conditions, need to also check subtypes
|
||||
return lhs.type() == rhs.type() && lhs.cost() == rhs.cost() && lhs.fingerprint() == rhs.fingerprint();
|
||||
}
|
||||
|
||||
inline bool
|
||||
operator!=(Fulfillment const& lhs, Fulfillment const& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
/** Determine whether the given fulfillment and condition match */
|
||||
bool
|
||||
match(Fulfillment const& f, Condition const& c);
|
||||
|
||||
/** Verify if the given message satisfies the fulfillment.
|
||||
|
||||
@param f The fulfillment
|
||||
@param c The condition
|
||||
@param m The message
|
||||
|
||||
@note the message is not relevant for some conditions
|
||||
and a fulfillment will successfully satisfy its
|
||||
condition for any given message.
|
||||
*/
|
||||
bool
|
||||
validate(Fulfillment const& f, Condition const& c, Slice m);
|
||||
|
||||
/** Verify a cryptoconditional trigger.
|
||||
|
||||
A cryptoconditional trigger is a cryptocondition with
|
||||
an empty message.
|
||||
|
||||
When using such triggers, it is recommended that the
|
||||
trigger be of type preimage, prefix or threshold. If
|
||||
a signature type is used (i.e. Ed25519 or RSA-SHA256)
|
||||
then the Ed25519 or RSA keys should be single-use keys.
|
||||
|
||||
@param f The fulfillment
|
||||
@param c The condition
|
||||
*/
|
||||
bool
|
||||
validate(Fulfillment const& f, Condition const& c);
|
||||
|
||||
} // namespace cryptoconditions
|
||||
} // namespace xrpl
|
||||
131
include/xrpl/conditions/detail/PreimageSha256.h
Normal file
131
include/xrpl/conditions/detail/PreimageSha256.h
Normal file
@@ -0,0 +1,131 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Buffer.h>
|
||||
#include <xrpl/basics/Slice.h>
|
||||
#include <xrpl/conditions/Condition.h>
|
||||
#include <xrpl/conditions/Fulfillment.h>
|
||||
#include <xrpl/conditions/detail/error.h>
|
||||
#include <xrpl/protocol/digest.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace xrpl {
|
||||
namespace cryptoconditions {
|
||||
|
||||
class PreimageSha256 final : public Fulfillment
|
||||
{
|
||||
public:
|
||||
/** The maximum allowed length of a preimage.
|
||||
|
||||
The specification does not specify a minimum supported
|
||||
length, nor does it require all conditions to support
|
||||
the same minimum length.
|
||||
|
||||
While future versions of this code will never lower
|
||||
this limit, they may opt to raise it.
|
||||
*/
|
||||
static constexpr std::size_t maxPreimageLength = 128;
|
||||
|
||||
/** Parse the payload for a PreimageSha256 condition
|
||||
|
||||
@param s A slice containing the DER encoded payload
|
||||
@param ec indicates success or failure of the operation
|
||||
@return the preimage, if successful; empty pointer otherwise.
|
||||
*/
|
||||
static std::unique_ptr<Fulfillment>
|
||||
deserialize(Slice s, std::error_code& ec)
|
||||
{
|
||||
// Per the RFC, a preimage fulfillment is defined as
|
||||
// follows:
|
||||
//
|
||||
// PreimageFulfillment ::= SEQUENCE {
|
||||
// preimage OCTET STRING
|
||||
// }
|
||||
|
||||
using namespace der;
|
||||
|
||||
auto p = parsePreamble(s, ec);
|
||||
if (ec)
|
||||
return nullptr;
|
||||
|
||||
if (!isPrimitive(p) || !isContextSpecific(p))
|
||||
{
|
||||
ec = error::incorrect_encoding;
|
||||
return {};
|
||||
}
|
||||
|
||||
if (p.tag != 0)
|
||||
{
|
||||
ec = error::unexpected_tag;
|
||||
return {};
|
||||
}
|
||||
|
||||
if (s.size() != p.length)
|
||||
{
|
||||
ec = error::trailing_garbage;
|
||||
return {};
|
||||
}
|
||||
|
||||
if (s.size() > maxPreimageLength)
|
||||
{
|
||||
ec = error::preimage_too_long;
|
||||
return {};
|
||||
}
|
||||
|
||||
auto b = parseOctetString(s, p.length, ec);
|
||||
if (ec)
|
||||
return {};
|
||||
|
||||
return std::make_unique<PreimageSha256>(std::move(b));
|
||||
}
|
||||
|
||||
private:
|
||||
Buffer payload_;
|
||||
|
||||
public:
|
||||
PreimageSha256(Buffer&& b) noexcept : payload_(std::move(b))
|
||||
{
|
||||
}
|
||||
|
||||
PreimageSha256(Slice s) noexcept : payload_(s)
|
||||
{
|
||||
}
|
||||
|
||||
Type
|
||||
type() const override
|
||||
{
|
||||
return Type::preimageSha256;
|
||||
}
|
||||
|
||||
Buffer
|
||||
fingerprint() const override
|
||||
{
|
||||
sha256_hasher h;
|
||||
h(payload_.data(), payload_.size());
|
||||
auto const d = static_cast<sha256_hasher::result_type>(h);
|
||||
return {d.data(), d.size()};
|
||||
}
|
||||
|
||||
std::uint32_t
|
||||
cost() const override
|
||||
{
|
||||
return static_cast<std::uint32_t>(payload_.size());
|
||||
}
|
||||
|
||||
Condition
|
||||
condition() const override
|
||||
{
|
||||
return {type(), cost(), fingerprint()};
|
||||
}
|
||||
|
||||
bool
|
||||
validate(Slice) const override
|
||||
{
|
||||
// Perhaps counterintuitively, the message isn't
|
||||
// relevant.
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace cryptoconditions
|
||||
} // namespace xrpl
|
||||
44
include/xrpl/conditions/detail/error.h
Normal file
44
include/xrpl/conditions/detail/error.h
Normal file
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include <system_error>
|
||||
|
||||
namespace xrpl {
|
||||
namespace cryptoconditions {
|
||||
|
||||
enum class error {
|
||||
generic = 1,
|
||||
unsupported_type,
|
||||
unsupported_subtype,
|
||||
unknown_type,
|
||||
unknown_subtype,
|
||||
fingerprint_size,
|
||||
incorrect_encoding,
|
||||
trailing_garbage,
|
||||
buffer_empty,
|
||||
buffer_overfull,
|
||||
buffer_underfull,
|
||||
malformed_encoding,
|
||||
short_preamble,
|
||||
unexpected_tag,
|
||||
long_tag,
|
||||
large_size,
|
||||
preimage_too_long
|
||||
};
|
||||
|
||||
std::error_code
|
||||
make_error_code(error ev);
|
||||
|
||||
} // namespace cryptoconditions
|
||||
} // namespace xrpl
|
||||
|
||||
namespace std {
|
||||
|
||||
template <>
|
||||
struct is_error_code_enum<xrpl::cryptoconditions::error>
|
||||
{
|
||||
explicit is_error_code_enum() = default;
|
||||
|
||||
static bool const value = true;
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
209
include/xrpl/conditions/detail/utils.h
Normal file
209
include/xrpl/conditions/detail/utils.h
Normal file
@@ -0,0 +1,209 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Buffer.h>
|
||||
#include <xrpl/basics/Slice.h>
|
||||
#include <xrpl/conditions/detail/error.h>
|
||||
|
||||
#include <boost/dynamic_bitset.hpp>
|
||||
|
||||
#include <limits>
|
||||
|
||||
namespace xrpl {
|
||||
namespace cryptoconditions {
|
||||
|
||||
// A collection of functions to decode binary blobs
|
||||
// encoded with X.690 Distinguished Encoding Rules.
|
||||
//
|
||||
// This is a very trivial decoder and only implements
|
||||
// the bare minimum needed to support PreimageSha256.
|
||||
namespace der {
|
||||
|
||||
// The preamble encapsulates the DER identifier and
|
||||
// length octets:
|
||||
struct Preamble
|
||||
{
|
||||
explicit Preamble() = default;
|
||||
std::uint8_t type = 0;
|
||||
std::size_t tag = 0;
|
||||
std::size_t length = 0;
|
||||
};
|
||||
|
||||
inline bool
|
||||
isPrimitive(Preamble const& p)
|
||||
{
|
||||
return (p.type & 0x20) == 0;
|
||||
}
|
||||
|
||||
inline bool
|
||||
isConstructed(Preamble const& p)
|
||||
{
|
||||
return !isPrimitive(p);
|
||||
}
|
||||
|
||||
inline bool
|
||||
isUniversal(Preamble const& p)
|
||||
{
|
||||
return (p.type & 0xC0) == 0;
|
||||
}
|
||||
|
||||
inline bool
|
||||
isApplication(Preamble const& p)
|
||||
{
|
||||
return (p.type & 0xC0) == 0x40;
|
||||
}
|
||||
|
||||
inline bool
|
||||
isContextSpecific(Preamble const& p)
|
||||
{
|
||||
return (p.type & 0xC0) == 0x80;
|
||||
}
|
||||
|
||||
inline bool
|
||||
isPrivate(Preamble const& p)
|
||||
{
|
||||
return (p.type & 0xC0) == 0xC0;
|
||||
}
|
||||
|
||||
inline Preamble
|
||||
parsePreamble(Slice& s, std::error_code& ec)
|
||||
{
|
||||
Preamble p;
|
||||
|
||||
if (s.size() < 2)
|
||||
{
|
||||
ec = error::short_preamble;
|
||||
return p;
|
||||
}
|
||||
|
||||
p.type = s[0] & 0xE0;
|
||||
p.tag = s[0] & 0x1F;
|
||||
|
||||
s += 1;
|
||||
|
||||
if (p.tag == 0x1F)
|
||||
{ // Long tag form, which we do not support:
|
||||
ec = error::long_tag;
|
||||
return p;
|
||||
}
|
||||
|
||||
p.length = s[0];
|
||||
s += 1;
|
||||
|
||||
if (p.length & 0x80)
|
||||
{ // Long form length:
|
||||
std::size_t const cnt = p.length & 0x7F;
|
||||
|
||||
if (cnt == 0)
|
||||
{
|
||||
ec = error::malformed_encoding;
|
||||
return p;
|
||||
}
|
||||
|
||||
if (cnt > sizeof(std::size_t))
|
||||
{
|
||||
ec = error::large_size;
|
||||
return p;
|
||||
}
|
||||
|
||||
if (cnt > s.size())
|
||||
{
|
||||
ec = error::short_preamble;
|
||||
return p;
|
||||
}
|
||||
|
||||
p.length = 0;
|
||||
|
||||
for (std::size_t i = 0; i != cnt; ++i)
|
||||
p.length = (p.length << 8) + s[i];
|
||||
|
||||
s += cnt;
|
||||
|
||||
if (p.length == 0)
|
||||
{
|
||||
ec = error::malformed_encoding;
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
inline Buffer
|
||||
parseOctetString(Slice& s, std::uint32_t count, std::error_code& ec)
|
||||
{
|
||||
if (count > s.size())
|
||||
{
|
||||
ec = error::buffer_underfull;
|
||||
return {};
|
||||
}
|
||||
|
||||
if (count > 65535)
|
||||
{
|
||||
ec = error::large_size;
|
||||
return {};
|
||||
}
|
||||
|
||||
Buffer b(s.data(), count);
|
||||
s += count;
|
||||
return b;
|
||||
}
|
||||
|
||||
template <class Integer>
|
||||
Integer
|
||||
parseInteger(Slice& s, std::size_t count, std::error_code& ec)
|
||||
{
|
||||
Integer v{0};
|
||||
|
||||
if (s.empty())
|
||||
{
|
||||
// can never have zero sized integers
|
||||
ec = error::malformed_encoding;
|
||||
return v;
|
||||
}
|
||||
|
||||
if (count > s.size())
|
||||
{
|
||||
ec = error::buffer_underfull;
|
||||
return v;
|
||||
}
|
||||
|
||||
bool const isSigned = std::numeric_limits<Integer>::is_signed;
|
||||
// unsigned types may have a leading zero octet
|
||||
size_t const maxLength = isSigned ? sizeof(Integer) : sizeof(Integer) + 1;
|
||||
if (count > maxLength)
|
||||
{
|
||||
ec = error::large_size;
|
||||
return v;
|
||||
}
|
||||
|
||||
if (!isSigned && (s[0] & (1 << 7)))
|
||||
{
|
||||
// trying to decode a negative number into a positive value
|
||||
ec = error::malformed_encoding;
|
||||
return v;
|
||||
}
|
||||
|
||||
if (!isSigned && count == sizeof(Integer) + 1 && s[0])
|
||||
{
|
||||
// since integers are coded as two's complement, the first byte may
|
||||
// be zero for unsigned reps
|
||||
ec = error::malformed_encoding;
|
||||
return v;
|
||||
}
|
||||
|
||||
v = 0;
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
v = (v << 8) | (s[i] & 0xff);
|
||||
|
||||
if (isSigned && (s[0] & (1 << 7)))
|
||||
{
|
||||
for (int i = count; i < sizeof(Integer); ++i)
|
||||
v |= (Integer(0xff) << (8 * i));
|
||||
}
|
||||
s += count;
|
||||
return v;
|
||||
}
|
||||
|
||||
} // namespace der
|
||||
} // namespace cryptoconditions
|
||||
} // namespace xrpl
|
||||
254
include/xrpl/core/HashRouter.h
Normal file
254
include/xrpl/core/HashRouter.h
Normal file
@@ -0,0 +1,254 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/CountedObject.h>
|
||||
#include <xrpl/basics/UnorderedContainers.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/basics/chrono.h>
|
||||
#include <xrpl/beast/container/aged_unordered_map.h>
|
||||
|
||||
#include <optional>
|
||||
#include <set>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
enum class HashRouterFlags : std::uint16_t {
|
||||
// Public flags
|
||||
UNDEFINED = 0x00,
|
||||
BAD = 0x02, // Temporarily bad
|
||||
SAVED = 0x04,
|
||||
HELD = 0x08, // Held by LedgerMaster after potential processing failure
|
||||
TRUSTED = 0x10, // Comes from a trusted source
|
||||
|
||||
// Private flags (used internally in apply.cpp)
|
||||
// Do not attempt to read, set, or reuse.
|
||||
PRIVATE1 = 0x0100,
|
||||
PRIVATE2 = 0x0200,
|
||||
PRIVATE3 = 0x0400,
|
||||
PRIVATE4 = 0x0800,
|
||||
PRIVATE5 = 0x1000,
|
||||
PRIVATE6 = 0x2000
|
||||
};
|
||||
|
||||
constexpr HashRouterFlags
|
||||
operator|(HashRouterFlags lhs, HashRouterFlags rhs)
|
||||
{
|
||||
return static_cast<HashRouterFlags>(
|
||||
static_cast<std::underlying_type_t<HashRouterFlags>>(lhs) |
|
||||
static_cast<std::underlying_type_t<HashRouterFlags>>(rhs));
|
||||
}
|
||||
|
||||
constexpr HashRouterFlags&
|
||||
operator|=(HashRouterFlags& lhs, HashRouterFlags rhs)
|
||||
{
|
||||
lhs = lhs | rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
constexpr HashRouterFlags
|
||||
operator&(HashRouterFlags lhs, HashRouterFlags rhs)
|
||||
{
|
||||
return static_cast<HashRouterFlags>(
|
||||
static_cast<std::underlying_type_t<HashRouterFlags>>(lhs) &
|
||||
static_cast<std::underlying_type_t<HashRouterFlags>>(rhs));
|
||||
}
|
||||
|
||||
constexpr HashRouterFlags&
|
||||
operator&=(HashRouterFlags& lhs, HashRouterFlags rhs)
|
||||
{
|
||||
lhs = lhs & rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
constexpr bool
|
||||
any(HashRouterFlags flags)
|
||||
{
|
||||
return static_cast<std::underlying_type_t<HashRouterFlags>>(flags) != 0;
|
||||
}
|
||||
|
||||
class Config;
|
||||
|
||||
/** Routing table for objects identified by hash.
|
||||
|
||||
This table keeps track of which hashes have been received by which peers.
|
||||
It is used to manage the routing and broadcasting of messages in the peer
|
||||
to peer overlay.
|
||||
*/
|
||||
class HashRouter
|
||||
{
|
||||
public:
|
||||
// The type here *MUST* match the type of Peer::id_t
|
||||
using PeerShortID = std::uint32_t;
|
||||
|
||||
/** Structure used to customize @ref HashRouter behavior.
|
||||
*
|
||||
* Even though these items are configurable, they are undocumented. Don't
|
||||
* change them unless there is a good reason, and network-wide coordination
|
||||
* to do it.
|
||||
*
|
||||
* Configuration is processed in setup_HashRouter.
|
||||
*/
|
||||
struct Setup
|
||||
{
|
||||
/// Default constructor
|
||||
explicit Setup() = default;
|
||||
|
||||
using seconds = std::chrono::seconds;
|
||||
|
||||
/** Expiration time for a hash entry
|
||||
*/
|
||||
seconds holdTime{300};
|
||||
|
||||
/** Amount of time required before a relayed item will be relayed again.
|
||||
*/
|
||||
seconds relayTime{30};
|
||||
};
|
||||
|
||||
private:
|
||||
/** An entry in the routing table.
|
||||
*/
|
||||
class Entry : public CountedObject<Entry>
|
||||
{
|
||||
public:
|
||||
Entry()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
addPeer(PeerShortID peer)
|
||||
{
|
||||
if (peer != 0)
|
||||
peers_.insert(peer);
|
||||
}
|
||||
|
||||
HashRouterFlags
|
||||
getFlags(void) const
|
||||
{
|
||||
return flags_;
|
||||
}
|
||||
|
||||
void
|
||||
setFlags(HashRouterFlags flagsToSet)
|
||||
{
|
||||
flags_ |= flagsToSet;
|
||||
}
|
||||
|
||||
/** Return set of peers we've relayed to and reset tracking */
|
||||
std::set<PeerShortID>
|
||||
releasePeerSet()
|
||||
{
|
||||
return std::move(peers_);
|
||||
}
|
||||
|
||||
/** Return seated relay time point if the message has been relayed */
|
||||
std::optional<Stopwatch::time_point>
|
||||
relayed() const
|
||||
{
|
||||
return relayed_;
|
||||
}
|
||||
|
||||
/** Determines if this item should be relayed.
|
||||
|
||||
Checks whether the item has been recently relayed.
|
||||
If it has, return false. If it has not, update the
|
||||
last relay timestamp and return true.
|
||||
*/
|
||||
bool
|
||||
shouldRelay(Stopwatch::time_point const& now, std::chrono::seconds relayTime)
|
||||
{
|
||||
if (relayed_ && *relayed_ + relayTime > now)
|
||||
return false;
|
||||
relayed_.emplace(now);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
shouldProcess(Stopwatch::time_point now, std::chrono::seconds interval)
|
||||
{
|
||||
if (processed_ && ((*processed_ + interval) > now))
|
||||
return false;
|
||||
processed_.emplace(now);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
HashRouterFlags flags_ = HashRouterFlags::UNDEFINED;
|
||||
std::set<PeerShortID> peers_;
|
||||
// This could be generalized to a map, if more
|
||||
// than one flag needs to expire independently.
|
||||
std::optional<Stopwatch::time_point> relayed_;
|
||||
std::optional<Stopwatch::time_point> processed_;
|
||||
};
|
||||
|
||||
public:
|
||||
HashRouter(Setup const& setup, Stopwatch& clock) : setup_(setup), suppressionMap_(clock)
|
||||
{
|
||||
}
|
||||
|
||||
HashRouter&
|
||||
operator=(HashRouter const&) = delete;
|
||||
|
||||
virtual ~HashRouter() = default;
|
||||
|
||||
// VFALCO TODO Replace "Suppression" terminology with something more
|
||||
// semantically meaningful.
|
||||
void
|
||||
addSuppression(uint256 const& key);
|
||||
|
||||
bool
|
||||
addSuppressionPeer(uint256 const& key, PeerShortID peer);
|
||||
|
||||
/** Add a suppression peer and get message's relay status.
|
||||
* Return pair:
|
||||
* element 1: true if the peer is added.
|
||||
* element 2: optional is seated to the relay time point or
|
||||
* is unseated if has not relayed yet. */
|
||||
std::pair<bool, std::optional<Stopwatch::time_point>>
|
||||
addSuppressionPeerWithStatus(uint256 const& key, PeerShortID peer);
|
||||
|
||||
bool
|
||||
addSuppressionPeer(uint256 const& key, PeerShortID peer, HashRouterFlags& flags);
|
||||
|
||||
// Add a peer suppression and return whether the entry should be processed
|
||||
bool
|
||||
shouldProcess(uint256 const& key, PeerShortID peer, HashRouterFlags& flags, std::chrono::seconds tx_interval);
|
||||
|
||||
/** Set the flags on a hash.
|
||||
|
||||
@return `true` if the flags were changed. `false` if unchanged.
|
||||
*/
|
||||
bool
|
||||
setFlags(uint256 const& key, HashRouterFlags flags);
|
||||
|
||||
HashRouterFlags
|
||||
getFlags(uint256 const& key);
|
||||
|
||||
/** Determines whether the hashed item should be relayed.
|
||||
|
||||
Effects:
|
||||
|
||||
If the item should be relayed, this function will not
|
||||
return a seated optional again until the relay time has expired.
|
||||
The internal set of peers will also be reset.
|
||||
|
||||
@return A `std::optional` set of peers which do not need to be
|
||||
relayed to. If the result is unseated, the item should
|
||||
_not_ be relayed.
|
||||
*/
|
||||
std::optional<std::set<PeerShortID>>
|
||||
shouldRelay(uint256 const& key);
|
||||
|
||||
private:
|
||||
// pair.second indicates whether the entry was created
|
||||
std::pair<Entry&, bool>
|
||||
emplace(uint256 const&);
|
||||
|
||||
std::mutex mutable mutex_;
|
||||
|
||||
// Configurable parameters
|
||||
Setup const setup_;
|
||||
|
||||
// Stores all suppressed hashes and their expiration time
|
||||
beast::aged_unordered_map<uint256, Entry, Stopwatch::clock_type, hardened_hash<strong_hash>> suppressionMap_;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
33
include/xrpl/core/NetworkIDService.h
Normal file
33
include/xrpl/core/NetworkIDService.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/** Service that provides access to the network ID.
|
||||
|
||||
This service provides read-only access to the network ID configured
|
||||
for this server. The network ID identifies which network (mainnet,
|
||||
testnet, devnet, or custom network) this server is configured to
|
||||
connect to.
|
||||
|
||||
Well-known network IDs:
|
||||
- 0: Mainnet
|
||||
- 1: Testnet
|
||||
- 2: Devnet
|
||||
- 1025+: Custom networks (require NetworkID field in transactions)
|
||||
*/
|
||||
class NetworkIDService
|
||||
{
|
||||
public:
|
||||
virtual ~NetworkIDService() = default;
|
||||
|
||||
/** Get the configured network ID
|
||||
*
|
||||
* @return The network ID this server is configured for
|
||||
*/
|
||||
virtual std::uint32_t
|
||||
getNetworkID() const noexcept = 0;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
98
include/xrpl/core/PeerReservationTable.h
Normal file
98
include/xrpl/core/PeerReservationTable.h
Normal file
@@ -0,0 +1,98 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/hash/hash_append.h>
|
||||
#include <xrpl/beast/hash/uhash.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/protocol/PublicKey.h>
|
||||
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class DatabaseCon;
|
||||
|
||||
// Value type for reservations.
|
||||
struct PeerReservation final
|
||||
{
|
||||
public:
|
||||
PublicKey nodeId;
|
||||
std::string description{};
|
||||
|
||||
auto
|
||||
toJson() const -> Json::Value;
|
||||
|
||||
template <typename Hasher>
|
||||
friend void
|
||||
hash_append(Hasher& h, PeerReservation const& x) noexcept
|
||||
{
|
||||
using beast::hash_append;
|
||||
hash_append(h, x.nodeId);
|
||||
}
|
||||
|
||||
friend bool
|
||||
operator<(PeerReservation const& a, PeerReservation const& b)
|
||||
{
|
||||
return a.nodeId < b.nodeId;
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: When C++20 arrives, take advantage of "equivalence" instead of
|
||||
// "equality". Add an overload for `(PublicKey, PeerReservation)`, and just
|
||||
// pass a `PublicKey` directly to `unordered_set.find`.
|
||||
struct KeyEqual final
|
||||
{
|
||||
bool
|
||||
operator()(PeerReservation const& lhs, PeerReservation const& rhs) const
|
||||
{
|
||||
return lhs.nodeId == rhs.nodeId;
|
||||
}
|
||||
};
|
||||
|
||||
class PeerReservationTable final
|
||||
{
|
||||
public:
|
||||
explicit PeerReservationTable(beast::Journal journal = beast::Journal(beast::Journal::getNullSink()))
|
||||
: journal_(journal)
|
||||
{
|
||||
}
|
||||
|
||||
std::vector<PeerReservation>
|
||||
list() const;
|
||||
|
||||
bool
|
||||
contains(PublicKey const& nodeId)
|
||||
{
|
||||
std::lock_guard lock(this->mutex_);
|
||||
return table_.find({nodeId}) != table_.end();
|
||||
}
|
||||
|
||||
// Because `ApplicationImp` has two-phase initialization, so must we.
|
||||
// Our dependencies are not prepared until the second phase.
|
||||
bool
|
||||
load(DatabaseCon& connection);
|
||||
|
||||
/**
|
||||
* @return the replaced reservation if it existed
|
||||
* @throw soci::soci_error
|
||||
*/
|
||||
std::optional<PeerReservation>
|
||||
insert_or_assign(PeerReservation const& reservation);
|
||||
|
||||
/**
|
||||
* @return the erased reservation if it existed
|
||||
*/
|
||||
std::optional<PeerReservation>
|
||||
erase(PublicKey const& nodeId);
|
||||
|
||||
private:
|
||||
beast::Journal mutable journal_;
|
||||
std::mutex mutable mutex_;
|
||||
DatabaseCon* connection_;
|
||||
std::unordered_set<PeerReservation, beast::uhash<>, KeyEqual> table_;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -5,6 +5,8 @@
|
||||
#include <xrpl/basics/TaggedCache.h>
|
||||
#include <xrpl/ledger/CachedSLEs.h>
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
// Forward declarations
|
||||
@@ -18,6 +20,10 @@ namespace perf {
|
||||
class PerfLog;
|
||||
}
|
||||
|
||||
// This is temporary until we migrate all code to use ServiceRegistry.
|
||||
class Application;
|
||||
|
||||
// Forward declarations
|
||||
class AcceptedLedger;
|
||||
class AmendmentTable;
|
||||
class Cluster;
|
||||
@@ -35,6 +41,7 @@ class LoadFeeTrack;
|
||||
class LoadManager;
|
||||
class ManifestCache;
|
||||
class NetworkOPs;
|
||||
class NetworkIDService;
|
||||
class OpenLedger;
|
||||
class OrderBookDB;
|
||||
class Overlay;
|
||||
@@ -93,6 +100,9 @@ public:
|
||||
virtual CachedSLEs&
|
||||
cachedSLEs() = 0;
|
||||
|
||||
virtual NetworkIDService&
|
||||
getNetworkIDService() = 0;
|
||||
|
||||
// Protocol and validation services
|
||||
virtual AmendmentTable&
|
||||
getAmendmentTable() = 0;
|
||||
@@ -194,6 +204,31 @@ public:
|
||||
|
||||
virtual perf::PerfLog&
|
||||
getPerfLog() = 0;
|
||||
|
||||
// Configuration and state
|
||||
virtual bool
|
||||
isStopping() const = 0;
|
||||
|
||||
virtual beast::Journal
|
||||
journal(std::string const& name) = 0;
|
||||
|
||||
virtual boost::asio::io_context&
|
||||
getIOContext() = 0;
|
||||
|
||||
virtual Logs&
|
||||
logs() = 0;
|
||||
|
||||
virtual std::optional<uint256> const&
|
||||
trapTxID() const = 0;
|
||||
|
||||
/** Retrieve the "wallet database" */
|
||||
virtual DatabaseCon&
|
||||
getWalletDB() = 0;
|
||||
|
||||
// Temporary: Get the underlying Application for functions that haven't
|
||||
// been migrated yet. This should be removed once all code is migrated.
|
||||
virtual Application&
|
||||
app() = 0;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
16
include/xrpl/core/StartUpType.h
Normal file
16
include/xrpl/core/StartUpType.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <iosfwd>
|
||||
#include <type_traits>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
enum class StartUpType { FRESH, NORMAL, LOAD, LOAD_FILE, REPLAY, NETWORK };
|
||||
|
||||
inline std::ostream&
|
||||
operator<<(std::ostream& os, StartUpType const& type)
|
||||
{
|
||||
return os << static_cast<std::underlying_type_t<StartUpType>>(type);
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
87
include/xrpl/ledger/AcceptedLedgerTx.h
Normal file
87
include/xrpl/ledger/AcceptedLedgerTx.h
Normal file
@@ -0,0 +1,87 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/CountedObject.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
#include <xrpl/protocol/TxMeta.h>
|
||||
|
||||
#include <boost/container/flat_set.hpp>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
A transaction that is in a closed ledger.
|
||||
|
||||
Description
|
||||
|
||||
An accepted ledger transaction contains additional information that the
|
||||
server needs to tell clients about the transaction. For example,
|
||||
- The transaction in JSON form
|
||||
- Which accounts are affected
|
||||
* This is used by InfoSub to report to clients
|
||||
- Cached stuff
|
||||
*/
|
||||
class AcceptedLedgerTx : public CountedObject<AcceptedLedgerTx>
|
||||
{
|
||||
public:
|
||||
AcceptedLedgerTx(
|
||||
std::shared_ptr<ReadView const> const& ledger,
|
||||
std::shared_ptr<STTx const> const&,
|
||||
std::shared_ptr<STObject const> const&);
|
||||
|
||||
std::shared_ptr<STTx const> const&
|
||||
getTxn() const
|
||||
{
|
||||
return mTxn;
|
||||
}
|
||||
TxMeta const&
|
||||
getMeta() const
|
||||
{
|
||||
return mMeta;
|
||||
}
|
||||
|
||||
boost::container::flat_set<AccountID> const&
|
||||
getAffected() const
|
||||
{
|
||||
return mAffected;
|
||||
}
|
||||
|
||||
TxID
|
||||
getTransactionID() const
|
||||
{
|
||||
return mTxn->getTransactionID();
|
||||
}
|
||||
TxType
|
||||
getTxnType() const
|
||||
{
|
||||
return mTxn->getTxnType();
|
||||
}
|
||||
TER
|
||||
getResult() const
|
||||
{
|
||||
return mMeta.getResultTER();
|
||||
}
|
||||
std::uint32_t
|
||||
getTxnSeq() const
|
||||
{
|
||||
return mMeta.getIndex();
|
||||
}
|
||||
std::string
|
||||
getEscMeta() const;
|
||||
|
||||
Json::Value const&
|
||||
getJson() const
|
||||
{
|
||||
return mJson;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<STTx const> mTxn;
|
||||
TxMeta mMeta;
|
||||
boost::container::flat_set<AccountID> mAffected;
|
||||
Blob mRawMeta;
|
||||
Json::Value mJson;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
175
include/xrpl/ledger/AmendmentTable.h
Normal file
175
include/xrpl/ledger/AmendmentTable.h
Normal file
@@ -0,0 +1,175 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Protocol.h>
|
||||
#include <xrpl/protocol/STValidation.h>
|
||||
#include <xrpl/shamap/SHAMap.h>
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class ServiceRegistry;
|
||||
|
||||
/** The amendment table stores the list of enabled and potential amendments.
|
||||
Individuals amendments are voted on by validators during the consensus
|
||||
process.
|
||||
*/
|
||||
class AmendmentTable
|
||||
{
|
||||
public:
|
||||
struct FeatureInfo
|
||||
{
|
||||
FeatureInfo() = delete;
|
||||
FeatureInfo(std::string const& n, uint256 const& f, VoteBehavior v) : name(n), feature(f), vote(v)
|
||||
{
|
||||
}
|
||||
|
||||
std::string const name;
|
||||
uint256 const feature;
|
||||
VoteBehavior const vote;
|
||||
};
|
||||
|
||||
virtual ~AmendmentTable() = default;
|
||||
|
||||
virtual uint256
|
||||
find(std::string const& name) const = 0;
|
||||
|
||||
virtual bool
|
||||
veto(uint256 const& amendment) = 0;
|
||||
virtual bool
|
||||
unVeto(uint256 const& amendment) = 0;
|
||||
|
||||
virtual bool
|
||||
enable(uint256 const& amendment) = 0;
|
||||
|
||||
virtual bool
|
||||
isEnabled(uint256 const& amendment) const = 0;
|
||||
virtual bool
|
||||
isSupported(uint256 const& amendment) const = 0;
|
||||
|
||||
/**
|
||||
* @brief returns true if one or more amendments on the network
|
||||
* have been enabled that this server does not support
|
||||
*
|
||||
* @return true if an unsupported feature is enabled on the network
|
||||
*/
|
||||
virtual bool
|
||||
hasUnsupportedEnabled() const = 0;
|
||||
|
||||
virtual std::optional<NetClock::time_point>
|
||||
firstUnsupportedExpected() const = 0;
|
||||
|
||||
virtual Json::Value
|
||||
getJson(bool isAdmin) const = 0;
|
||||
|
||||
/** Returns a Json::objectValue. */
|
||||
virtual Json::Value
|
||||
getJson(uint256 const& amendment, bool isAdmin) const = 0;
|
||||
|
||||
/** Called when a new fully-validated ledger is accepted. */
|
||||
void
|
||||
doValidatedLedger(std::shared_ptr<ReadView const> const& lastValidatedLedger)
|
||||
{
|
||||
if (needValidatedLedger(lastValidatedLedger->seq()))
|
||||
doValidatedLedger(
|
||||
lastValidatedLedger->seq(),
|
||||
getEnabledAmendments(*lastValidatedLedger),
|
||||
getMajorityAmendments(*lastValidatedLedger));
|
||||
}
|
||||
|
||||
/** Called to determine whether the amendment logic needs to process
|
||||
a new validated ledger. (If it could have changed things.)
|
||||
*/
|
||||
virtual bool
|
||||
needValidatedLedger(LedgerIndex seq) const = 0;
|
||||
|
||||
virtual void
|
||||
doValidatedLedger(
|
||||
LedgerIndex ledgerSeq,
|
||||
std::set<uint256> const& enabled,
|
||||
majorityAmendments_t const& majority) = 0;
|
||||
|
||||
// Called when the set of trusted validators changes.
|
||||
virtual void
|
||||
trustChanged(hash_set<PublicKey> const& allTrusted) = 0;
|
||||
|
||||
// Called by the consensus code when we need to
|
||||
// inject pseudo-transactions
|
||||
virtual std::map<uint256, std::uint32_t>
|
||||
doVoting(
|
||||
Rules const& rules,
|
||||
NetClock::time_point closeTime,
|
||||
std::set<uint256> const& enabledAmendments,
|
||||
majorityAmendments_t const& majorityAmendments,
|
||||
std::vector<std::shared_ptr<STValidation>> const& valSet) = 0;
|
||||
|
||||
// Called by the consensus code when we need to
|
||||
// add feature entries to a validation
|
||||
virtual std::vector<uint256>
|
||||
doValidation(std::set<uint256> const& enabled) const = 0;
|
||||
|
||||
// The set of amendments to enable in the genesis ledger
|
||||
// This will return all known, non-vetoed amendments.
|
||||
// If we ever have two amendments that should not both be
|
||||
// enabled at the same time, we should ensure one is vetoed.
|
||||
virtual std::vector<uint256>
|
||||
getDesired() const = 0;
|
||||
|
||||
// The function below adapts the API callers expect to the
|
||||
// internal amendment table API. This allows the amendment
|
||||
// table implementation to be independent of the ledger
|
||||
// implementation. These APIs will merge when the view code
|
||||
// supports a full ledger API
|
||||
|
||||
void
|
||||
doVoting(
|
||||
std::shared_ptr<ReadView const> const& lastClosedLedger,
|
||||
std::vector<std::shared_ptr<STValidation>> const& parentValidations,
|
||||
std::shared_ptr<SHAMap> const& initialPosition,
|
||||
beast::Journal j)
|
||||
{
|
||||
// Ask implementation what to do
|
||||
auto actions = doVoting(
|
||||
lastClosedLedger->rules(),
|
||||
lastClosedLedger->parentCloseTime(),
|
||||
getEnabledAmendments(*lastClosedLedger),
|
||||
getMajorityAmendments(*lastClosedLedger),
|
||||
parentValidations);
|
||||
|
||||
// Inject appropriate pseudo-transactions
|
||||
for (auto const& it : actions)
|
||||
{
|
||||
STTx amendTx(ttAMENDMENT, [&it, seq = lastClosedLedger->seq() + 1](auto& obj) {
|
||||
obj.setAccountID(sfAccount, AccountID());
|
||||
obj.setFieldH256(sfAmendment, it.first);
|
||||
obj.setFieldU32(sfLedgerSequence, seq);
|
||||
|
||||
if (it.second != 0)
|
||||
obj.setFieldU32(sfFlags, it.second);
|
||||
});
|
||||
|
||||
Serializer s;
|
||||
amendTx.add(s);
|
||||
|
||||
JLOG(j.debug()) << "Amendments: Adding pseudo-transaction: " << amendTx.getTransactionID() << ": "
|
||||
<< strHex(s.slice()) << ": " << amendTx;
|
||||
|
||||
initialPosition->addGiveItem(
|
||||
SHAMapNodeType::tnTRANSACTION_NM, make_shamapitem(amendTx.getTransactionID(), s.slice()));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<AmendmentTable>
|
||||
make_AmendmentTable(
|
||||
ServiceRegistry& registry,
|
||||
std::chrono::seconds majorityTime,
|
||||
std::vector<AmendmentTable::FeatureInfo> const& supported,
|
||||
Section const& enabled,
|
||||
Section const& vetoed,
|
||||
beast::Journal journal);
|
||||
|
||||
} // namespace xrpl
|
||||
51
include/xrpl/ledger/BookListeners.h
Normal file
51
include/xrpl/ledger/BookListeners.h
Normal file
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/protocol/MultiApiJson.h>
|
||||
#include <xrpl/server/InfoSub.h>
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/** Listen to public/subscribe messages from a book. */
|
||||
class BookListeners
|
||||
{
|
||||
public:
|
||||
using pointer = std::shared_ptr<BookListeners>;
|
||||
|
||||
BookListeners()
|
||||
{
|
||||
}
|
||||
|
||||
/** Add a new subscription for this book
|
||||
*/
|
||||
void
|
||||
addSubscriber(InfoSub::ref sub);
|
||||
|
||||
/** Stop publishing to a subscriber
|
||||
*/
|
||||
void
|
||||
removeSubscriber(std::uint64_t sub);
|
||||
|
||||
/** Publish a transaction to subscribers
|
||||
|
||||
Publish a transaction to clients subscribed to changes on this book.
|
||||
Uses havePublished to prevent sending duplicate transactions to clients
|
||||
that have subscribed to multiple books.
|
||||
|
||||
@param jvObj JSON transaction data to publish
|
||||
@param havePublished InfoSub sequence numbers that have already
|
||||
published this transaction.
|
||||
|
||||
*/
|
||||
void
|
||||
publish(MultiApiJson const& jvObj, hash_set<std::uint64_t>& havePublished);
|
||||
|
||||
private:
|
||||
std::recursive_mutex mLock;
|
||||
|
||||
hash_map<std::uint64_t, InfoSub::wptr> mListeners;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
93
include/xrpl/ledger/OrderBookDB.h
Normal file
93
include/xrpl/ledger/OrderBookDB.h
Normal file
@@ -0,0 +1,93 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/ledger/AcceptedLedgerTx.h>
|
||||
#include <xrpl/ledger/BookListeners.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/protocol/Book.h>
|
||||
#include <xrpl/protocol/Issue.h>
|
||||
#include <xrpl/protocol/MultiApiJson.h>
|
||||
#include <xrpl/protocol/UintTypes.h>
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/** Tracks order books in the ledger.
|
||||
|
||||
This interface provides access to order book information, including:
|
||||
- Which order books exist in the ledger
|
||||
- Querying order books by issue
|
||||
- Managing order book subscriptions
|
||||
|
||||
The order book database is updated as ledgers are accepted and provides
|
||||
efficient lookup of order book information for pathfinding and client
|
||||
subscriptions.
|
||||
*/
|
||||
class OrderBookDB
|
||||
{
|
||||
public:
|
||||
virtual ~OrderBookDB() = default;
|
||||
|
||||
/** Initialize or update the order book database with a new ledger.
|
||||
|
||||
This method should be called when a new ledger is accepted to update
|
||||
the order book database with the current state of all order books.
|
||||
|
||||
@param ledger The ledger to scan for order books
|
||||
*/
|
||||
virtual void
|
||||
setup(std::shared_ptr<ReadView const> const& ledger) = 0;
|
||||
|
||||
/** Add an order book to track.
|
||||
|
||||
@param book The order book to add
|
||||
*/
|
||||
virtual void
|
||||
addOrderBook(Book const& book) = 0;
|
||||
|
||||
/** Get all order books that want a specific issue.
|
||||
|
||||
Returns a list of all order books where the taker pays the specified
|
||||
issue. This is useful for pathfinding to find all possible next hops
|
||||
from a given currency.
|
||||
|
||||
@param issue The issue to search for
|
||||
@param domain Optional domain restriction for the order book
|
||||
@return Vector of books that want this issue
|
||||
*/
|
||||
virtual std::vector<Book>
|
||||
getBooksByTakerPays(Issue const& issue, std::optional<Domain> const& domain = std::nullopt) = 0;
|
||||
|
||||
/** Get the count of order books that want a specific issue.
|
||||
|
||||
@param issue The issue to search for
|
||||
@param domain Optional domain restriction for the order book
|
||||
@return Number of books that want this issue
|
||||
*/
|
||||
virtual int
|
||||
getBookSize(Issue const& issue, std::optional<Domain> const& domain = std::nullopt) = 0;
|
||||
|
||||
/** Check if an order book to XRP exists for the given issue.
|
||||
|
||||
@param issue The issue to check
|
||||
@param domain Optional domain restriction for the order book
|
||||
@return true if a book from this issue to XRP exists
|
||||
*/
|
||||
virtual bool
|
||||
isBookToXRP(Issue const& issue, std::optional<Domain> domain = std::nullopt) = 0;
|
||||
|
||||
virtual void
|
||||
processTxn(
|
||||
std::shared_ptr<ReadView const> const& ledger,
|
||||
AcceptedLedgerTx const& alTx,
|
||||
MultiApiJson const& jvObj) = 0;
|
||||
|
||||
virtual BookListeners::pointer
|
||||
getBookListeners(Book const&) = 0;
|
||||
virtual BookListeners::pointer
|
||||
makeBookListeners(Book const&) = 0;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -133,10 +133,6 @@ public:
|
||||
std::uint32_t ledgerSeq,
|
||||
std::function<void(std::shared_ptr<NodeObject> const&)>&& callback);
|
||||
|
||||
/** Remove expired entries from the positive and negative caches. */
|
||||
virtual void
|
||||
sweep() = 0;
|
||||
|
||||
/** Gather statistics pertaining to read and write activities.
|
||||
*
|
||||
* @param obj Json object reference into which to place counters.
|
||||
|
||||
@@ -23,32 +23,6 @@ public:
|
||||
beast::Journal j)
|
||||
: Database(scheduler, readThreads, config, j), backend_(std::move(backend))
|
||||
{
|
||||
std::optional<int> cacheSize, cacheAge;
|
||||
|
||||
if (config.exists("cache_size"))
|
||||
{
|
||||
cacheSize = get<int>(config, "cache_size");
|
||||
if (cacheSize.value() < 0)
|
||||
{
|
||||
Throw<std::runtime_error>("Specified negative value for cache_size");
|
||||
}
|
||||
}
|
||||
|
||||
if (config.exists("cache_age"))
|
||||
{
|
||||
cacheAge = get<int>(config, "cache_age");
|
||||
if (cacheAge.value() < 0)
|
||||
{
|
||||
Throw<std::runtime_error>("Specified negative value for cache_age");
|
||||
}
|
||||
}
|
||||
|
||||
if (cacheSize != 0 || cacheAge != 0)
|
||||
{
|
||||
cache_ = std::make_shared<TaggedCache<uint256, NodeObject>>(
|
||||
"DatabaseNodeImp", cacheSize.value_or(0), std::chrono::minutes(cacheAge.value_or(0)), stopwatch(), j);
|
||||
}
|
||||
|
||||
XRPL_ASSERT(
|
||||
backend_,
|
||||
"xrpl::NodeStore::DatabaseNodeImp::DatabaseNodeImp : non-null "
|
||||
@@ -103,13 +77,7 @@ public:
|
||||
std::uint32_t ledgerSeq,
|
||||
std::function<void(std::shared_ptr<NodeObject> const&)>&& callback) override;
|
||||
|
||||
void
|
||||
sweep() override;
|
||||
|
||||
private:
|
||||
// Cache for database objects. This cache is not always initialized. Check
|
||||
// for null before using.
|
||||
std::shared_ptr<TaggedCache<uint256, NodeObject>> cache_;
|
||||
// Persistent key/value storage
|
||||
std::shared_ptr<Backend> backend_;
|
||||
|
||||
|
||||
@@ -55,9 +55,6 @@ public:
|
||||
void
|
||||
sync() override;
|
||||
|
||||
void
|
||||
sweep() override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<Backend> writableBackend_;
|
||||
std::shared_ptr<Backend> archiveBackend_;
|
||||
|
||||
@@ -86,9 +86,9 @@ message TMPublicKey {
|
||||
// you must first combine coins from one address to another.
|
||||
|
||||
enum TransactionStatus {
|
||||
tsNEW = 1; // origin node did/could not validate
|
||||
tsCURRENT = 2; // scheduled to go in this ledger
|
||||
tsCOMMITED = 3; // in a closed ledger
|
||||
tsNEW = 1; // origin node did/could not validate
|
||||
tsCURRENT = 2; // scheduled to go in this ledger
|
||||
tsCOMMITTED = 3; // in a closed ledger
|
||||
tsREJECT_CONFLICT = 4;
|
||||
tsREJECT_INVALID = 5;
|
||||
tsREJECT_FUNDS = 6;
|
||||
|
||||
@@ -296,7 +296,7 @@ public:
|
||||
friend FeatureBitset
|
||||
operator^(FeatureBitset const& lhs, uint256 const& rhs)
|
||||
{
|
||||
return lhs ^ FeatureBitset { rhs };
|
||||
return lhs ^ FeatureBitset{rhs};
|
||||
}
|
||||
|
||||
friend FeatureBitset
|
||||
|
||||
@@ -110,7 +110,8 @@ IOUAmount::operator=(beast::Zero)
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline IOUAmount::operator Number() const
|
||||
inline IOUAmount::
|
||||
operator Number() const
|
||||
{
|
||||
return Number{mantissa_, exponent_};
|
||||
}
|
||||
@@ -140,7 +141,8 @@ IOUAmount::operator<(IOUAmount const& other) const
|
||||
return Number{*this} < Number{other};
|
||||
}
|
||||
|
||||
inline IOUAmount::operator bool() const noexcept
|
||||
inline IOUAmount::
|
||||
operator bool() const noexcept
|
||||
{
|
||||
return mantissa_ != 0;
|
||||
}
|
||||
|
||||
22
include/xrpl/protocol/LedgerShortcut.h
Normal file
22
include/xrpl/protocol/LedgerShortcut.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* @brief Enumeration of ledger shortcuts for specifying which ledger to use.
|
||||
*
|
||||
* These shortcuts provide a convenient way to reference commonly used ledgers
|
||||
* without needing to specify their exact hash or sequence number.
|
||||
*/
|
||||
enum class LedgerShortcut {
|
||||
/** The current working ledger (open, not yet closed) */
|
||||
Current,
|
||||
|
||||
/** The most recently closed ledger (may not be validated) */
|
||||
Closed,
|
||||
|
||||
/** The most recently validated ledger */
|
||||
Validated
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -93,7 +93,8 @@ MPTAmount::operator=(beast::Zero)
|
||||
}
|
||||
|
||||
/** Returns true if the amount is not zero */
|
||||
constexpr MPTAmount::operator bool() const noexcept
|
||||
constexpr MPTAmount::
|
||||
operator bool() const noexcept
|
||||
{
|
||||
return value_ != 0;
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ struct MultiApiJson
|
||||
// unsigned int version, extra arguments
|
||||
template <typename Json, typename Version, typename... Args, typename Fn>
|
||||
requires(!some_integral_constant<Version>) && std::convertible_to<Version, unsigned> &&
|
||||
std::same_as<std::remove_cvref_t<Json>, MultiApiJson>
|
||||
std::same_as<std::remove_cvref_t<Json>, MultiApiJson>
|
||||
auto
|
||||
operator()(Json& json, Version version, Fn fn, Args&&... args) const
|
||||
-> std::invoke_result_t<Fn, decltype(json.val[0]), Version, Args&&...>
|
||||
@@ -122,7 +122,7 @@ struct MultiApiJson
|
||||
// unsigned int version, Json only
|
||||
template <typename Json, typename Version, typename Fn>
|
||||
requires(!some_integral_constant<Version>) && std::convertible_to<Version, unsigned> &&
|
||||
std::same_as<std::remove_cvref_t<Json>, MultiApiJson>
|
||||
std::same_as<std::remove_cvref_t<Json>, MultiApiJson>
|
||||
auto
|
||||
operator()(Json& json, Version version, Fn fn) const -> std::invoke_result_t<Fn, decltype(json.val[0])>
|
||||
{
|
||||
|
||||
@@ -253,6 +253,16 @@ std::uint8_t constexpr maxAssetCheckDepth = 5;
|
||||
/** A ledger index. */
|
||||
using LedgerIndex = std::uint32_t;
|
||||
|
||||
std::uint32_t constexpr FLAG_LEDGER_INTERVAL = 256;
|
||||
|
||||
/** Returns true if the given ledgerIndex is a voting ledgerIndex */
|
||||
bool
|
||||
isVotingLedger(LedgerIndex seq);
|
||||
|
||||
/** Returns true if the given ledgerIndex is a flag ledgerIndex */
|
||||
bool
|
||||
isFlagLedger(LedgerIndex seq);
|
||||
|
||||
/** A transaction identifier.
|
||||
The value is computed as the hash of the
|
||||
canonicalized, serialized transaction object.
|
||||
|
||||
@@ -480,12 +480,14 @@ STAmount::zeroed() const
|
||||
return STAmount(mAsset);
|
||||
}
|
||||
|
||||
inline STAmount::operator bool() const noexcept
|
||||
inline STAmount::
|
||||
operator bool() const noexcept
|
||||
{
|
||||
return *this != beast::zero;
|
||||
}
|
||||
|
||||
inline STAmount::operator Number() const
|
||||
inline STAmount::
|
||||
operator Number() const
|
||||
{
|
||||
if (native())
|
||||
return xrp();
|
||||
|
||||
@@ -169,7 +169,8 @@ STBitString<Bits>::value() const
|
||||
}
|
||||
|
||||
template <int Bits>
|
||||
STBitString<Bits>::operator value_type() const
|
||||
STBitString<Bits>::
|
||||
operator value_type() const
|
||||
{
|
||||
return value_;
|
||||
}
|
||||
|
||||
@@ -134,7 +134,8 @@ STInteger<Integer>::setValue(Integer v)
|
||||
}
|
||||
|
||||
template <typename Integer>
|
||||
inline STInteger<Integer>::operator Integer() const
|
||||
inline STInteger<Integer>::
|
||||
operator Integer() const
|
||||
{
|
||||
return value_;
|
||||
}
|
||||
|
||||
@@ -147,9 +147,12 @@ public:
|
||||
int
|
||||
getCount() const;
|
||||
|
||||
bool setFlag(std::uint32_t);
|
||||
bool clearFlag(std::uint32_t);
|
||||
bool isFlag(std::uint32_t) const;
|
||||
bool
|
||||
setFlag(std::uint32_t);
|
||||
bool
|
||||
clearFlag(std::uint32_t);
|
||||
bool
|
||||
isFlag(std::uint32_t) const;
|
||||
|
||||
std::uint32_t
|
||||
getFlags() const;
|
||||
@@ -799,7 +802,8 @@ STObject::ValueProxy<T>::operator-=(U const& u)
|
||||
}
|
||||
|
||||
template <class T>
|
||||
STObject::ValueProxy<T>::operator value_type() const
|
||||
STObject::ValueProxy<T>::
|
||||
operator value_type() const
|
||||
{
|
||||
return this->value();
|
||||
}
|
||||
@@ -812,13 +816,15 @@ STObject::ValueProxy<T>::ValueProxy(STObject* st, TypedField<T> const* f) : Prox
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <class T>
|
||||
STObject::OptionalProxy<T>::operator bool() const noexcept
|
||||
STObject::OptionalProxy<T>::
|
||||
operator bool() const noexcept
|
||||
{
|
||||
return engaged();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
STObject::OptionalProxy<T>::operator typename STObject::OptionalProxy<T>::optional_type() const
|
||||
STObject::OptionalProxy<T>::
|
||||
operator typename STObject::OptionalProxy<T>::optional_type() const
|
||||
{
|
||||
return optional_value();
|
||||
}
|
||||
|
||||
@@ -135,7 +135,8 @@ STVector256::setValue(STVector256 const& v)
|
||||
}
|
||||
|
||||
/** Retrieve a copy of the vector we contain */
|
||||
inline STVector256::operator std::vector<uint256>() const
|
||||
inline STVector256::
|
||||
operator std::vector<uint256>() const
|
||||
{
|
||||
return mValue;
|
||||
}
|
||||
|
||||
@@ -484,60 +484,54 @@ public:
|
||||
// Only enabled if both arguments return int if TERtiInt is called with them.
|
||||
template <typename L, typename R>
|
||||
constexpr auto
|
||||
operator==(L const& lhs, R const& rhs)
|
||||
-> std::enable_if_t<
|
||||
std::is_same<decltype(TERtoInt(lhs)), int>::value && std::is_same<decltype(TERtoInt(rhs)), int>::value,
|
||||
bool>
|
||||
operator==(L const& lhs, R const& rhs) -> std::enable_if_t<
|
||||
std::is_same<decltype(TERtoInt(lhs)), int>::value && std::is_same<decltype(TERtoInt(rhs)), int>::value,
|
||||
bool>
|
||||
{
|
||||
return TERtoInt(lhs) == TERtoInt(rhs);
|
||||
}
|
||||
|
||||
template <typename L, typename R>
|
||||
constexpr auto
|
||||
operator!=(L const& lhs, R const& rhs)
|
||||
-> std::enable_if_t<
|
||||
std::is_same<decltype(TERtoInt(lhs)), int>::value && std::is_same<decltype(TERtoInt(rhs)), int>::value,
|
||||
bool>
|
||||
operator!=(L const& lhs, R const& rhs) -> std::enable_if_t<
|
||||
std::is_same<decltype(TERtoInt(lhs)), int>::value && std::is_same<decltype(TERtoInt(rhs)), int>::value,
|
||||
bool>
|
||||
{
|
||||
return TERtoInt(lhs) != TERtoInt(rhs);
|
||||
}
|
||||
|
||||
template <typename L, typename R>
|
||||
constexpr auto
|
||||
operator<(L const& lhs, R const& rhs)
|
||||
-> std::enable_if_t<
|
||||
std::is_same<decltype(TERtoInt(lhs)), int>::value && std::is_same<decltype(TERtoInt(rhs)), int>::value,
|
||||
bool>
|
||||
operator<(L const& lhs, R const& rhs) -> std::enable_if_t<
|
||||
std::is_same<decltype(TERtoInt(lhs)), int>::value && std::is_same<decltype(TERtoInt(rhs)), int>::value,
|
||||
bool>
|
||||
{
|
||||
return TERtoInt(lhs) < TERtoInt(rhs);
|
||||
}
|
||||
|
||||
template <typename L, typename R>
|
||||
constexpr auto
|
||||
operator<=(L const& lhs, R const& rhs)
|
||||
-> std::enable_if_t<
|
||||
std::is_same<decltype(TERtoInt(lhs)), int>::value && std::is_same<decltype(TERtoInt(rhs)), int>::value,
|
||||
bool>
|
||||
operator<=(L const& lhs, R const& rhs) -> std::enable_if_t<
|
||||
std::is_same<decltype(TERtoInt(lhs)), int>::value && std::is_same<decltype(TERtoInt(rhs)), int>::value,
|
||||
bool>
|
||||
{
|
||||
return TERtoInt(lhs) <= TERtoInt(rhs);
|
||||
}
|
||||
|
||||
template <typename L, typename R>
|
||||
constexpr auto
|
||||
operator>(L const& lhs, R const& rhs)
|
||||
-> std::enable_if_t<
|
||||
std::is_same<decltype(TERtoInt(lhs)), int>::value && std::is_same<decltype(TERtoInt(rhs)), int>::value,
|
||||
bool>
|
||||
operator>(L const& lhs, R const& rhs) -> std::enable_if_t<
|
||||
std::is_same<decltype(TERtoInt(lhs)), int>::value && std::is_same<decltype(TERtoInt(rhs)), int>::value,
|
||||
bool>
|
||||
{
|
||||
return TERtoInt(lhs) > TERtoInt(rhs);
|
||||
}
|
||||
|
||||
template <typename L, typename R>
|
||||
constexpr auto
|
||||
operator>=(L const& lhs, R const& rhs)
|
||||
-> std::enable_if_t<
|
||||
std::is_same<decltype(TERtoInt(lhs)), int>::value && std::is_same<decltype(TERtoInt(rhs)), int>::value,
|
||||
bool>
|
||||
operator>=(L const& lhs, R const& rhs) -> std::enable_if_t<
|
||||
std::is_same<decltype(TERtoInt(lhs)), int>::value && std::is_same<decltype(TERtoInt(rhs)), int>::value,
|
||||
bool>
|
||||
{
|
||||
return TERtoInt(lhs) >= TERtoInt(rhs);
|
||||
}
|
||||
|
||||
7
include/xrpl/protocol/TxSearched.h
Normal file
7
include/xrpl/protocol/TxSearched.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
enum class TxSearched { all, some, unknown };
|
||||
|
||||
}
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
// Add new amendments to the top of this list.
|
||||
// Keep it sorted in reverse chronological order.
|
||||
|
||||
XRPL_FIX (PermissionedDomainInvariant, Supported::yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FIX (ExpiredNFTokenOfferRemoval, Supported::yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FIX (BatchInnerSigs, Supported::yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FEATURE(LendingProtocol, Supported::yes, VoteBehavior::DefaultNo)
|
||||
|
||||
@@ -8,11 +8,11 @@
|
||||
* To ease maintenance, you may replace any unneeded values with "..."
|
||||
* e.g. #define TRANSACTION(tag, value, name, ...)
|
||||
*
|
||||
* You must define a transactor class in the `ripple` namespace named `name`,
|
||||
* You must define a transactor class in the `xrpl` namespace named `name`,
|
||||
* and include its header alongside the TRANSACTOR definition using this
|
||||
* format:
|
||||
* #if TRANSACTION_INCLUDE
|
||||
* # include <xrpld/app/tx/detail/HEADER.h>
|
||||
* # include <xrpl/tx/transactors/HEADER.h>
|
||||
* #endif
|
||||
*
|
||||
* The `privileges` parameter of the TRANSACTION macro is a bitfield
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
/** This transaction type executes a payment. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/Payment.h>
|
||||
# include <xrpl/tx/transactors/Payment.h>
|
||||
#endif
|
||||
TRANSACTION(ttPAYMENT, 0, Payment,
|
||||
Delegation::delegable,
|
||||
@@ -42,7 +42,7 @@ TRANSACTION(ttPAYMENT, 0, Payment,
|
||||
|
||||
/** This transaction type creates an escrow object. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/Escrow.h>
|
||||
# include <xrpl/tx/transactors/Escrow.h>
|
||||
#endif
|
||||
TRANSACTION(ttESCROW_CREATE, 1, EscrowCreate,
|
||||
Delegation::delegable,
|
||||
@@ -73,7 +73,7 @@ TRANSACTION(ttESCROW_FINISH, 2, EscrowFinish,
|
||||
|
||||
/** This transaction type adjusts various account settings. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/SetAccount.h>
|
||||
# include <xrpl/tx/transactors/SetAccount.h>
|
||||
#endif
|
||||
TRANSACTION(ttACCOUNT_SET, 3, AccountSet,
|
||||
Delegation::notDelegable,
|
||||
@@ -94,7 +94,7 @@ TRANSACTION(ttACCOUNT_SET, 3, AccountSet,
|
||||
|
||||
/** This transaction type cancels an existing escrow. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/Escrow.h>
|
||||
# include <xrpl/tx/transactors/Escrow.h>
|
||||
#endif
|
||||
TRANSACTION(ttESCROW_CANCEL, 4, EscrowCancel,
|
||||
Delegation::delegable,
|
||||
@@ -107,7 +107,7 @@ TRANSACTION(ttESCROW_CANCEL, 4, EscrowCancel,
|
||||
|
||||
/** This transaction type sets or clears an account's "regular key". */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/SetRegularKey.h>
|
||||
# include <xrpl/tx/transactors/SetRegularKey.h>
|
||||
#endif
|
||||
TRANSACTION(ttREGULAR_KEY_SET, 5, SetRegularKey,
|
||||
Delegation::notDelegable,
|
||||
@@ -121,7 +121,7 @@ TRANSACTION(ttREGULAR_KEY_SET, 5, SetRegularKey,
|
||||
|
||||
/** This transaction type creates an offer to trade one asset for another. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/CreateOffer.h>
|
||||
# include <xrpl/tx/transactors/Offer/CreateOffer.h>
|
||||
#endif
|
||||
TRANSACTION(ttOFFER_CREATE, 7, OfferCreate,
|
||||
Delegation::delegable,
|
||||
@@ -137,7 +137,7 @@ TRANSACTION(ttOFFER_CREATE, 7, OfferCreate,
|
||||
|
||||
/** This transaction type cancels existing offers to trade one asset for another. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/CancelOffer.h>
|
||||
# include <xrpl/tx/transactors/Offer/CancelOffer.h>
|
||||
#endif
|
||||
TRANSACTION(ttOFFER_CANCEL, 8, OfferCancel,
|
||||
Delegation::delegable,
|
||||
@@ -151,7 +151,7 @@ TRANSACTION(ttOFFER_CANCEL, 8, OfferCancel,
|
||||
|
||||
/** This transaction type creates a new set of tickets. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/CreateTicket.h>
|
||||
# include <xrpl/tx/transactors/CreateTicket.h>
|
||||
#endif
|
||||
TRANSACTION(ttTICKET_CREATE, 10, TicketCreate,
|
||||
Delegation::delegable,
|
||||
@@ -167,7 +167,7 @@ TRANSACTION(ttTICKET_CREATE, 10, TicketCreate,
|
||||
// The SignerEntries are optional because a SignerList is deleted by
|
||||
// setting the SignerQuorum to zero and omitting SignerEntries.
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/SetSignerList.h>
|
||||
# include <xrpl/tx/transactors/SetSignerList.h>
|
||||
#endif
|
||||
TRANSACTION(ttSIGNER_LIST_SET, 12, SignerListSet,
|
||||
Delegation::notDelegable,
|
||||
@@ -180,7 +180,7 @@ TRANSACTION(ttSIGNER_LIST_SET, 12, SignerListSet,
|
||||
|
||||
/** This transaction type creates a new unidirectional XRP payment channel. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/PayChan.h>
|
||||
# include <xrpl/tx/transactors/PayChan.h>
|
||||
#endif
|
||||
TRANSACTION(ttPAYCHAN_CREATE, 13, PaymentChannelCreate,
|
||||
Delegation::delegable,
|
||||
@@ -222,7 +222,7 @@ TRANSACTION(ttPAYCHAN_CLAIM, 15, PaymentChannelClaim,
|
||||
|
||||
/** This transaction type creates a new check. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/CreateCheck.h>
|
||||
# include <xrpl/tx/transactors/Check/CreateCheck.h>
|
||||
#endif
|
||||
TRANSACTION(ttCHECK_CREATE, 16, CheckCreate,
|
||||
Delegation::delegable,
|
||||
@@ -238,7 +238,7 @@ TRANSACTION(ttCHECK_CREATE, 16, CheckCreate,
|
||||
|
||||
/** This transaction type cashes an existing check. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/CashCheck.h>
|
||||
# include <xrpl/tx/transactors/Check/CashCheck.h>
|
||||
#endif
|
||||
TRANSACTION(ttCHECK_CASH, 17, CheckCash,
|
||||
Delegation::delegable,
|
||||
@@ -252,7 +252,7 @@ TRANSACTION(ttCHECK_CASH, 17, CheckCash,
|
||||
|
||||
/** This transaction type cancels an existing check. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/CancelCheck.h>
|
||||
# include <xrpl/tx/transactors/Check/CancelCheck.h>
|
||||
#endif
|
||||
TRANSACTION(ttCHECK_CANCEL, 18, CheckCancel,
|
||||
Delegation::delegable,
|
||||
@@ -264,7 +264,7 @@ TRANSACTION(ttCHECK_CANCEL, 18, CheckCancel,
|
||||
|
||||
/** This transaction type grants or revokes authorization to transfer funds. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/DepositPreauth.h>
|
||||
# include <xrpl/tx/transactors/DepositPreauth.h>
|
||||
#endif
|
||||
TRANSACTION(ttDEPOSIT_PREAUTH, 19, DepositPreauth,
|
||||
Delegation::delegable,
|
||||
@@ -279,7 +279,7 @@ TRANSACTION(ttDEPOSIT_PREAUTH, 19, DepositPreauth,
|
||||
|
||||
/** This transaction type modifies a trustline between two accounts. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/SetTrust.h>
|
||||
# include <xrpl/tx/transactors/SetTrust.h>
|
||||
#endif
|
||||
TRANSACTION(ttTRUST_SET, 20, TrustSet,
|
||||
Delegation::delegable,
|
||||
@@ -293,7 +293,7 @@ TRANSACTION(ttTRUST_SET, 20, TrustSet,
|
||||
|
||||
/** This transaction type deletes an existing account. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/DeleteAccount.h>
|
||||
# include <xrpl/tx/transactors/DeleteAccount.h>
|
||||
#endif
|
||||
TRANSACTION(ttACCOUNT_DELETE, 21, AccountDelete,
|
||||
Delegation::notDelegable,
|
||||
@@ -309,7 +309,7 @@ TRANSACTION(ttACCOUNT_DELETE, 21, AccountDelete,
|
||||
|
||||
/** This transaction mints a new NFT. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/NFTokenMint.h>
|
||||
# include <xrpl/tx/transactors/NFT/NFTokenMint.h>
|
||||
#endif
|
||||
TRANSACTION(ttNFTOKEN_MINT, 25, NFTokenMint,
|
||||
Delegation::delegable,
|
||||
@@ -327,7 +327,7 @@ TRANSACTION(ttNFTOKEN_MINT, 25, NFTokenMint,
|
||||
|
||||
/** This transaction burns (i.e. destroys) an existing NFT. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/NFTokenBurn.h>
|
||||
# include <xrpl/tx/transactors/NFT/NFTokenBurn.h>
|
||||
#endif
|
||||
TRANSACTION(ttNFTOKEN_BURN, 26, NFTokenBurn,
|
||||
Delegation::delegable,
|
||||
@@ -340,7 +340,7 @@ TRANSACTION(ttNFTOKEN_BURN, 26, NFTokenBurn,
|
||||
|
||||
/** This transaction creates a new offer to buy or sell an NFT. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/NFTokenCreateOffer.h>
|
||||
# include <xrpl/tx/transactors/NFT/NFTokenCreateOffer.h>
|
||||
#endif
|
||||
TRANSACTION(ttNFTOKEN_CREATE_OFFER, 27, NFTokenCreateOffer,
|
||||
Delegation::delegable,
|
||||
@@ -356,7 +356,7 @@ TRANSACTION(ttNFTOKEN_CREATE_OFFER, 27, NFTokenCreateOffer,
|
||||
|
||||
/** This transaction cancels an existing offer to buy or sell an existing NFT. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/NFTokenCancelOffer.h>
|
||||
# include <xrpl/tx/transactors/NFT/NFTokenCancelOffer.h>
|
||||
#endif
|
||||
TRANSACTION(ttNFTOKEN_CANCEL_OFFER, 28, NFTokenCancelOffer,
|
||||
Delegation::delegable,
|
||||
@@ -368,7 +368,7 @@ TRANSACTION(ttNFTOKEN_CANCEL_OFFER, 28, NFTokenCancelOffer,
|
||||
|
||||
/** This transaction accepts an existing offer to buy or sell an existing NFT. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/NFTokenAcceptOffer.h>
|
||||
# include <xrpl/tx/transactors/NFT/NFTokenAcceptOffer.h>
|
||||
#endif
|
||||
TRANSACTION(ttNFTOKEN_ACCEPT_OFFER, 29, NFTokenAcceptOffer,
|
||||
Delegation::delegable,
|
||||
@@ -382,7 +382,7 @@ TRANSACTION(ttNFTOKEN_ACCEPT_OFFER, 29, NFTokenAcceptOffer,
|
||||
|
||||
/** This transaction claws back issued tokens. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/Clawback.h>
|
||||
# include <xrpl/tx/transactors/Clawback.h>
|
||||
#endif
|
||||
TRANSACTION(ttCLAWBACK, 30, Clawback,
|
||||
Delegation::delegable,
|
||||
@@ -395,7 +395,7 @@ TRANSACTION(ttCLAWBACK, 30, Clawback,
|
||||
|
||||
/** This transaction claws back tokens from an AMM pool. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/AMMClawback.h>
|
||||
# include <xrpl/tx/transactors/AMM/AMMClawback.h>
|
||||
#endif
|
||||
TRANSACTION(ttAMM_CLAWBACK, 31, AMMClawback,
|
||||
Delegation::delegable,
|
||||
@@ -410,7 +410,7 @@ TRANSACTION(ttAMM_CLAWBACK, 31, AMMClawback,
|
||||
|
||||
/** This transaction type creates an AMM instance */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/AMMCreate.h>
|
||||
# include <xrpl/tx/transactors/AMM/AMMCreate.h>
|
||||
#endif
|
||||
TRANSACTION(ttAMM_CREATE, 35, AMMCreate,
|
||||
Delegation::delegable,
|
||||
@@ -424,7 +424,7 @@ TRANSACTION(ttAMM_CREATE, 35, AMMCreate,
|
||||
|
||||
/** This transaction type deposits into an AMM instance */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/AMMDeposit.h>
|
||||
# include <xrpl/tx/transactors/AMM/AMMDeposit.h>
|
||||
#endif
|
||||
TRANSACTION(ttAMM_DEPOSIT, 36, AMMDeposit,
|
||||
Delegation::delegable,
|
||||
@@ -442,7 +442,7 @@ TRANSACTION(ttAMM_DEPOSIT, 36, AMMDeposit,
|
||||
|
||||
/** This transaction type withdraws from an AMM instance */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/AMMWithdraw.h>
|
||||
# include <xrpl/tx/transactors/AMM/AMMWithdraw.h>
|
||||
#endif
|
||||
TRANSACTION(ttAMM_WITHDRAW, 37, AMMWithdraw,
|
||||
Delegation::delegable,
|
||||
@@ -459,7 +459,7 @@ TRANSACTION(ttAMM_WITHDRAW, 37, AMMWithdraw,
|
||||
|
||||
/** This transaction type votes for the trading fee */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/AMMVote.h>
|
||||
# include <xrpl/tx/transactors/AMM/AMMVote.h>
|
||||
#endif
|
||||
TRANSACTION(ttAMM_VOTE, 38, AMMVote,
|
||||
Delegation::delegable,
|
||||
@@ -473,7 +473,7 @@ TRANSACTION(ttAMM_VOTE, 38, AMMVote,
|
||||
|
||||
/** This transaction type bids for the auction slot */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/AMMBid.h>
|
||||
# include <xrpl/tx/transactors/AMM/AMMBid.h>
|
||||
#endif
|
||||
TRANSACTION(ttAMM_BID, 39, AMMBid,
|
||||
Delegation::delegable,
|
||||
@@ -489,7 +489,7 @@ TRANSACTION(ttAMM_BID, 39, AMMBid,
|
||||
|
||||
/** This transaction type deletes AMM in the empty state */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/AMMDelete.h>
|
||||
# include <xrpl/tx/transactors/AMM/AMMDelete.h>
|
||||
#endif
|
||||
TRANSACTION(ttAMM_DELETE, 40, AMMDelete,
|
||||
Delegation::delegable,
|
||||
@@ -502,7 +502,7 @@ TRANSACTION(ttAMM_DELETE, 40, AMMDelete,
|
||||
|
||||
/** This transactions creates a crosschain sequence number */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/XChainBridge.h>
|
||||
# include <xrpl/tx/transactors/XChainBridge.h>
|
||||
#endif
|
||||
TRANSACTION(ttXCHAIN_CREATE_CLAIM_ID, 41, XChainCreateClaimID,
|
||||
Delegation::delegable,
|
||||
@@ -617,7 +617,7 @@ TRANSACTION(ttXCHAIN_CREATE_BRIDGE, 48, XChainCreateBridge,
|
||||
|
||||
/** This transaction type creates or updates a DID */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/DID.h>
|
||||
# include <xrpl/tx/transactors/DID.h>
|
||||
#endif
|
||||
TRANSACTION(ttDID_SET, 49, DIDSet,
|
||||
Delegation::delegable,
|
||||
@@ -638,7 +638,7 @@ TRANSACTION(ttDID_DELETE, 50, DIDDelete,
|
||||
|
||||
/** This transaction type creates an Oracle instance */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/SetOracle.h>
|
||||
# include <xrpl/tx/transactors/SetOracle.h>
|
||||
#endif
|
||||
TRANSACTION(ttORACLE_SET, 51, OracleSet,
|
||||
Delegation::delegable,
|
||||
@@ -655,7 +655,7 @@ TRANSACTION(ttORACLE_SET, 51, OracleSet,
|
||||
|
||||
/** This transaction type deletes an Oracle instance */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/DeleteOracle.h>
|
||||
# include <xrpl/tx/transactors/DeleteOracle.h>
|
||||
#endif
|
||||
TRANSACTION(ttORACLE_DELETE, 52, OracleDelete,
|
||||
Delegation::delegable,
|
||||
@@ -667,7 +667,7 @@ TRANSACTION(ttORACLE_DELETE, 52, OracleDelete,
|
||||
|
||||
/** This transaction type fixes a problem in the ledger state */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/LedgerStateFix.h>
|
||||
# include <xrpl/tx/transactors/LedgerStateFix.h>
|
||||
#endif
|
||||
TRANSACTION(ttLEDGER_STATE_FIX, 53, LedgerStateFix,
|
||||
Delegation::delegable,
|
||||
@@ -680,7 +680,7 @@ TRANSACTION(ttLEDGER_STATE_FIX, 53, LedgerStateFix,
|
||||
|
||||
/** This transaction type creates a MPTokensIssuance instance */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/MPTokenIssuanceCreate.h>
|
||||
# include <xrpl/tx/transactors/MPT/MPTokenIssuanceCreate.h>
|
||||
#endif
|
||||
TRANSACTION(ttMPTOKEN_ISSUANCE_CREATE, 54, MPTokenIssuanceCreate,
|
||||
Delegation::delegable,
|
||||
@@ -697,7 +697,7 @@ TRANSACTION(ttMPTOKEN_ISSUANCE_CREATE, 54, MPTokenIssuanceCreate,
|
||||
|
||||
/** This transaction type destroys a MPTokensIssuance instance */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/MPTokenIssuanceDestroy.h>
|
||||
# include <xrpl/tx/transactors/MPT/MPTokenIssuanceDestroy.h>
|
||||
#endif
|
||||
TRANSACTION(ttMPTOKEN_ISSUANCE_DESTROY, 55, MPTokenIssuanceDestroy,
|
||||
Delegation::delegable,
|
||||
@@ -709,7 +709,7 @@ TRANSACTION(ttMPTOKEN_ISSUANCE_DESTROY, 55, MPTokenIssuanceDestroy,
|
||||
|
||||
/** This transaction type sets flags on a MPTokensIssuance or MPToken instance */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/MPTokenIssuanceSet.h>
|
||||
# include <xrpl/tx/transactors/MPT/MPTokenIssuanceSet.h>
|
||||
#endif
|
||||
TRANSACTION(ttMPTOKEN_ISSUANCE_SET, 56, MPTokenIssuanceSet,
|
||||
Delegation::delegable,
|
||||
@@ -726,7 +726,7 @@ TRANSACTION(ttMPTOKEN_ISSUANCE_SET, 56, MPTokenIssuanceSet,
|
||||
|
||||
/** This transaction type authorizes a MPToken instance */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/MPTokenAuthorize.h>
|
||||
# include <xrpl/tx/transactors/MPT/MPTokenAuthorize.h>
|
||||
#endif
|
||||
TRANSACTION(ttMPTOKEN_AUTHORIZE, 57, MPTokenAuthorize,
|
||||
Delegation::delegable,
|
||||
@@ -739,7 +739,7 @@ TRANSACTION(ttMPTOKEN_AUTHORIZE, 57, MPTokenAuthorize,
|
||||
|
||||
/** This transaction type create an Credential instance */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/Credentials.h>
|
||||
# include <xrpl/tx/transactors/Credentials.h>
|
||||
#endif
|
||||
TRANSACTION(ttCREDENTIAL_CREATE, 58, CredentialCreate,
|
||||
Delegation::delegable,
|
||||
@@ -775,7 +775,7 @@ TRANSACTION(ttCREDENTIAL_DELETE, 60, CredentialDelete,
|
||||
|
||||
/** This transaction type modify a NFToken */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/NFTokenModify.h>
|
||||
# include <xrpl/tx/transactors/NFT/NFTokenModify.h>
|
||||
#endif
|
||||
TRANSACTION(ttNFTOKEN_MODIFY, 61, NFTokenModify,
|
||||
Delegation::delegable,
|
||||
@@ -789,7 +789,7 @@ TRANSACTION(ttNFTOKEN_MODIFY, 61, NFTokenModify,
|
||||
|
||||
/** This transaction type creates or modifies a Permissioned Domain */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/PermissionedDomainSet.h>
|
||||
# include <xrpl/tx/transactors/PermissionedDomain/PermissionedDomainSet.h>
|
||||
#endif
|
||||
TRANSACTION(ttPERMISSIONED_DOMAIN_SET, 62, PermissionedDomainSet,
|
||||
Delegation::delegable,
|
||||
@@ -802,7 +802,7 @@ TRANSACTION(ttPERMISSIONED_DOMAIN_SET, 62, PermissionedDomainSet,
|
||||
|
||||
/** This transaction type deletes a Permissioned Domain */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/PermissionedDomainDelete.h>
|
||||
# include <xrpl/tx/transactors/PermissionedDomain/PermissionedDomainDelete.h>
|
||||
#endif
|
||||
TRANSACTION(ttPERMISSIONED_DOMAIN_DELETE, 63, PermissionedDomainDelete,
|
||||
Delegation::delegable,
|
||||
@@ -814,7 +814,7 @@ TRANSACTION(ttPERMISSIONED_DOMAIN_DELETE, 63, PermissionedDomainDelete,
|
||||
|
||||
/** This transaction type delegates authorized account specified permissions */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/DelegateSet.h>
|
||||
# include <xrpl/tx/transactors/Delegate/DelegateSet.h>
|
||||
#endif
|
||||
TRANSACTION(ttDELEGATE_SET, 64, DelegateSet,
|
||||
Delegation::notDelegable,
|
||||
@@ -827,7 +827,7 @@ TRANSACTION(ttDELEGATE_SET, 64, DelegateSet,
|
||||
|
||||
/** This transaction creates a single asset vault. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/VaultCreate.h>
|
||||
# include <xrpl/tx/transactors/Vault/VaultCreate.h>
|
||||
#endif
|
||||
TRANSACTION(ttVAULT_CREATE, 65, VaultCreate,
|
||||
Delegation::delegable,
|
||||
@@ -845,7 +845,7 @@ TRANSACTION(ttVAULT_CREATE, 65, VaultCreate,
|
||||
|
||||
/** This transaction updates a single asset vault. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/VaultSet.h>
|
||||
# include <xrpl/tx/transactors/Vault/VaultSet.h>
|
||||
#endif
|
||||
TRANSACTION(ttVAULT_SET, 66, VaultSet,
|
||||
Delegation::delegable,
|
||||
@@ -860,7 +860,7 @@ TRANSACTION(ttVAULT_SET, 66, VaultSet,
|
||||
|
||||
/** This transaction deletes a single asset vault. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/VaultDelete.h>
|
||||
# include <xrpl/tx/transactors/Vault/VaultDelete.h>
|
||||
#endif
|
||||
TRANSACTION(ttVAULT_DELETE, 67, VaultDelete,
|
||||
Delegation::delegable,
|
||||
@@ -872,7 +872,7 @@ TRANSACTION(ttVAULT_DELETE, 67, VaultDelete,
|
||||
|
||||
/** This transaction trades assets for shares with a vault. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/VaultDeposit.h>
|
||||
# include <xrpl/tx/transactors/Vault/VaultDeposit.h>
|
||||
#endif
|
||||
TRANSACTION(ttVAULT_DEPOSIT, 68, VaultDeposit,
|
||||
Delegation::delegable,
|
||||
@@ -885,7 +885,7 @@ TRANSACTION(ttVAULT_DEPOSIT, 68, VaultDeposit,
|
||||
|
||||
/** This transaction trades shares for assets with a vault. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/VaultWithdraw.h>
|
||||
# include <xrpl/tx/transactors/Vault/VaultWithdraw.h>
|
||||
#endif
|
||||
TRANSACTION(ttVAULT_WITHDRAW, 69, VaultWithdraw,
|
||||
Delegation::delegable,
|
||||
@@ -900,7 +900,7 @@ TRANSACTION(ttVAULT_WITHDRAW, 69, VaultWithdraw,
|
||||
|
||||
/** This transaction claws back tokens from a vault. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/VaultClawback.h>
|
||||
# include <xrpl/tx/transactors/Vault/VaultClawback.h>
|
||||
#endif
|
||||
TRANSACTION(ttVAULT_CLAWBACK, 70, VaultClawback,
|
||||
Delegation::delegable,
|
||||
@@ -914,7 +914,7 @@ TRANSACTION(ttVAULT_CLAWBACK, 70, VaultClawback,
|
||||
|
||||
/** This transaction type batches together transactions. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/Batch.h>
|
||||
# include <xrpl/tx/transactors/Batch.h>
|
||||
#endif
|
||||
TRANSACTION(ttBATCH, 71, Batch,
|
||||
Delegation::notDelegable,
|
||||
@@ -929,7 +929,7 @@ TRANSACTION(ttBATCH, 71, Batch,
|
||||
|
||||
/** This transaction creates and updates a Loan Broker */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/LoanBrokerSet.h>
|
||||
# include <xrpl/tx/transactors/Lending/LoanBrokerSet.h>
|
||||
#endif
|
||||
TRANSACTION(ttLOAN_BROKER_SET, 74, LoanBrokerSet,
|
||||
Delegation::delegable,
|
||||
@@ -946,7 +946,7 @@ TRANSACTION(ttLOAN_BROKER_SET, 74, LoanBrokerSet,
|
||||
|
||||
/** This transaction deletes a Loan Broker */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/LoanBrokerDelete.h>
|
||||
# include <xrpl/tx/transactors/Lending/LoanBrokerDelete.h>
|
||||
#endif
|
||||
TRANSACTION(ttLOAN_BROKER_DELETE, 75, LoanBrokerDelete,
|
||||
Delegation::delegable,
|
||||
@@ -957,7 +957,7 @@ TRANSACTION(ttLOAN_BROKER_DELETE, 75, LoanBrokerDelete,
|
||||
|
||||
/** This transaction deposits First Loss Capital into a Loan Broker */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/LoanBrokerCoverDeposit.h>
|
||||
# include <xrpl/tx/transactors/Lending/LoanBrokerCoverDeposit.h>
|
||||
#endif
|
||||
TRANSACTION(ttLOAN_BROKER_COVER_DEPOSIT, 76, LoanBrokerCoverDeposit,
|
||||
Delegation::delegable,
|
||||
@@ -969,7 +969,7 @@ TRANSACTION(ttLOAN_BROKER_COVER_DEPOSIT, 76, LoanBrokerCoverDeposit,
|
||||
|
||||
/** This transaction withdraws First Loss Capital from a Loan Broker */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/LoanBrokerCoverWithdraw.h>
|
||||
# include <xrpl/tx/transactors/Lending/LoanBrokerCoverWithdraw.h>
|
||||
#endif
|
||||
TRANSACTION(ttLOAN_BROKER_COVER_WITHDRAW, 77, LoanBrokerCoverWithdraw,
|
||||
Delegation::delegable,
|
||||
@@ -984,7 +984,7 @@ TRANSACTION(ttLOAN_BROKER_COVER_WITHDRAW, 77, LoanBrokerCoverWithdraw,
|
||||
/** This transaction claws back First Loss Capital from a Loan Broker to
|
||||
the issuer of the capital */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/LoanBrokerCoverClawback.h>
|
||||
# include <xrpl/tx/transactors/Lending/LoanBrokerCoverClawback.h>
|
||||
#endif
|
||||
TRANSACTION(ttLOAN_BROKER_COVER_CLAWBACK, 78, LoanBrokerCoverClawback,
|
||||
Delegation::delegable,
|
||||
@@ -996,7 +996,7 @@ TRANSACTION(ttLOAN_BROKER_COVER_CLAWBACK, 78, LoanBrokerCoverClawback,
|
||||
|
||||
/** This transaction creates a Loan */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/LoanSet.h>
|
||||
# include <xrpl/tx/transactors/Lending/LoanSet.h>
|
||||
#endif
|
||||
TRANSACTION(ttLOAN_SET, 80, LoanSet,
|
||||
Delegation::delegable,
|
||||
@@ -1023,7 +1023,7 @@ TRANSACTION(ttLOAN_SET, 80, LoanSet,
|
||||
|
||||
/** This transaction deletes an existing Loan */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/LoanDelete.h>
|
||||
# include <xrpl/tx/transactors/Lending/LoanDelete.h>
|
||||
#endif
|
||||
TRANSACTION(ttLOAN_DELETE, 81, LoanDelete,
|
||||
Delegation::delegable,
|
||||
@@ -1034,7 +1034,7 @@ TRANSACTION(ttLOAN_DELETE, 81, LoanDelete,
|
||||
|
||||
/** This transaction is used to change the delinquency status of an existing Loan */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/LoanManage.h>
|
||||
# include <xrpl/tx/transactors/Lending/LoanManage.h>
|
||||
#endif
|
||||
TRANSACTION(ttLOAN_MANAGE, 82, LoanManage,
|
||||
Delegation::delegable,
|
||||
@@ -1048,7 +1048,7 @@ TRANSACTION(ttLOAN_MANAGE, 82, LoanManage,
|
||||
|
||||
/** The Borrower uses this transaction to make a Payment on the Loan. */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/LoanPay.h>
|
||||
# include <xrpl/tx/transactors/Lending/LoanPay.h>
|
||||
#endif
|
||||
TRANSACTION(ttLOAN_PAY, 84, LoanPay,
|
||||
Delegation::delegable,
|
||||
@@ -1063,7 +1063,7 @@ TRANSACTION(ttLOAN_PAY, 84, LoanPay,
|
||||
For details, see: https://xrpl.org/amendments.html
|
||||
*/
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/Change.h>
|
||||
# include <xrpl/tx/transactors/Change.h>
|
||||
#endif
|
||||
TRANSACTION(ttAMENDMENT, 100, EnableAmendment,
|
||||
Delegation::notDelegable,
|
||||
|
||||
118
include/xrpl/rdb/DBInit.h
Normal file
118
include/xrpl/rdb/DBInit.h
Normal file
@@ -0,0 +1,118 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// These pragmas are built at startup and applied to all database
|
||||
// connections, unless otherwise noted.
|
||||
inline constexpr char const* CommonDBPragmaJournal{"PRAGMA journal_mode=%s;"};
|
||||
inline constexpr char const* CommonDBPragmaSync{"PRAGMA synchronous=%s;"};
|
||||
inline constexpr char const* CommonDBPragmaTemp{"PRAGMA temp_store=%s;"};
|
||||
// A warning will be logged if any lower-safety sqlite tuning settings
|
||||
// are used and at least this much ledger history is configured. This
|
||||
// includes full history nodes. This is because such a large amount of
|
||||
// data will be more difficult to recover if a rare failure occurs,
|
||||
// which are more likely with some of the other available tuning settings.
|
||||
inline constexpr std::uint32_t SQLITE_TUNING_CUTOFF = 10'000'000;
|
||||
|
||||
// Ledger database holds ledgers and ledger confirmations
|
||||
inline constexpr auto LgrDBName{"ledger.db"};
|
||||
|
||||
inline constexpr std::array<char const*, 5> LgrDBInit{
|
||||
{"BEGIN TRANSACTION;",
|
||||
|
||||
"CREATE TABLE IF NOT EXISTS Ledgers ( \
|
||||
LedgerHash CHARACTER(64) PRIMARY KEY, \
|
||||
LedgerSeq BIGINT UNSIGNED, \
|
||||
PrevHash CHARACTER(64), \
|
||||
TotalCoins BIGINT UNSIGNED, \
|
||||
ClosingTime BIGINT UNSIGNED, \
|
||||
PrevClosingTime BIGINT UNSIGNED, \
|
||||
CloseTimeRes BIGINT UNSIGNED, \
|
||||
CloseFlags BIGINT UNSIGNED, \
|
||||
AccountSetHash CHARACTER(64), \
|
||||
TransSetHash CHARACTER(64) \
|
||||
);",
|
||||
"CREATE INDEX IF NOT EXISTS SeqLedger ON Ledgers(LedgerSeq);",
|
||||
|
||||
// Old table and indexes no longer needed
|
||||
"DROP TABLE IF EXISTS Validations;",
|
||||
|
||||
"END TRANSACTION;"}};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Transaction database holds transactions and public keys
|
||||
inline constexpr auto TxDBName{"transaction.db"};
|
||||
|
||||
inline constexpr std::array<char const*, 8> TxDBInit{
|
||||
{"BEGIN TRANSACTION;",
|
||||
|
||||
"CREATE TABLE IF NOT EXISTS Transactions ( \
|
||||
TransID CHARACTER(64) PRIMARY KEY, \
|
||||
TransType CHARACTER(24), \
|
||||
FromAcct CHARACTER(35), \
|
||||
FromSeq BIGINT UNSIGNED, \
|
||||
LedgerSeq BIGINT UNSIGNED, \
|
||||
Status CHARACTER(1), \
|
||||
RawTxn BLOB, \
|
||||
TxnMeta BLOB \
|
||||
);",
|
||||
"CREATE INDEX IF NOT EXISTS TxLgrIndex ON \
|
||||
Transactions(LedgerSeq);",
|
||||
|
||||
"CREATE TABLE IF NOT EXISTS AccountTransactions ( \
|
||||
TransID CHARACTER(64), \
|
||||
Account CHARACTER(64), \
|
||||
LedgerSeq BIGINT UNSIGNED, \
|
||||
TxnSeq INTEGER \
|
||||
);",
|
||||
"CREATE INDEX IF NOT EXISTS AcctTxIDIndex ON \
|
||||
AccountTransactions(TransID);",
|
||||
"CREATE INDEX IF NOT EXISTS AcctTxIndex ON \
|
||||
AccountTransactions(Account, LedgerSeq, TxnSeq, TransID);",
|
||||
"CREATE INDEX IF NOT EXISTS AcctLgrIndex ON \
|
||||
AccountTransactions(LedgerSeq, Account, TransID);",
|
||||
|
||||
"END TRANSACTION;"}};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
inline constexpr auto WalletDBName{"wallet.db"};
|
||||
|
||||
inline constexpr std::array<char const*, 6> WalletDBInit{
|
||||
{"BEGIN TRANSACTION;",
|
||||
|
||||
// A node's identity must be persisted, including
|
||||
// for clustering purposes. This table holds one
|
||||
// entry: the server's unique identity, but the
|
||||
// value can be overriden by specifying a node
|
||||
// identity in the config file using a [node_seed]
|
||||
// entry.
|
||||
"CREATE TABLE IF NOT EXISTS NodeIdentity ( \
|
||||
PublicKey CHARACTER(53), \
|
||||
PrivateKey CHARACTER(52) \
|
||||
);",
|
||||
|
||||
// Peer reservations
|
||||
"CREATE TABLE IF NOT EXISTS PeerReservations ( \
|
||||
PublicKey CHARACTER(53) UNIQUE NOT NULL, \
|
||||
Description CHARACTER(64) NOT NULL \
|
||||
);",
|
||||
|
||||
// Validator Manifests
|
||||
"CREATE TABLE IF NOT EXISTS ValidatorManifests ( \
|
||||
RawData BLOB NOT NULL \
|
||||
);",
|
||||
|
||||
"CREATE TABLE IF NOT EXISTS PublisherManifests ( \
|
||||
RawData BLOB NOT NULL \
|
||||
);",
|
||||
|
||||
"END TRANSACTION;"}};
|
||||
|
||||
} // namespace xrpl
|
||||
231
include/xrpl/rdb/DatabaseCon.h
Normal file
231
include/xrpl/rdb/DatabaseCon.h
Normal file
@@ -0,0 +1,231 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/core/PerfLog.h>
|
||||
#include <xrpl/core/StartUpType.h>
|
||||
#include <xrpl/rdb/DBInit.h>
|
||||
#include <xrpl/rdb/SociDB.h>
|
||||
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace soci {
|
||||
class session;
|
||||
}
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class LockedSociSession
|
||||
{
|
||||
public:
|
||||
using mutex = std::recursive_mutex;
|
||||
|
||||
private:
|
||||
std::shared_ptr<soci::session> session_;
|
||||
std::unique_lock<mutex> lock_;
|
||||
|
||||
public:
|
||||
LockedSociSession(std::shared_ptr<soci::session> it, mutex& m) : session_(std::move(it)), lock_(m)
|
||||
{
|
||||
}
|
||||
LockedSociSession(LockedSociSession&& rhs) noexcept : session_(std::move(rhs.session_)), lock_(std::move(rhs.lock_))
|
||||
{
|
||||
}
|
||||
LockedSociSession() = delete;
|
||||
LockedSociSession(LockedSociSession const& rhs) = delete;
|
||||
LockedSociSession&
|
||||
operator=(LockedSociSession const& rhs) = delete;
|
||||
|
||||
soci::session*
|
||||
get()
|
||||
{
|
||||
return session_.get();
|
||||
}
|
||||
soci::session&
|
||||
operator*()
|
||||
{
|
||||
return *session_;
|
||||
}
|
||||
soci::session*
|
||||
operator->()
|
||||
{
|
||||
return session_.get();
|
||||
}
|
||||
explicit
|
||||
operator bool() const
|
||||
{
|
||||
return bool(session_);
|
||||
}
|
||||
};
|
||||
|
||||
class DatabaseCon
|
||||
{
|
||||
public:
|
||||
struct Setup
|
||||
{
|
||||
explicit Setup() = default;
|
||||
|
||||
StartUpType startUp = StartUpType::NORMAL;
|
||||
bool standAlone = false;
|
||||
boost::filesystem::path dataDir;
|
||||
// Indicates whether or not to return the `globalPragma`
|
||||
// from commonPragma()
|
||||
bool useGlobalPragma = false;
|
||||
|
||||
std::vector<std::string> const*
|
||||
commonPragma() const
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
!useGlobalPragma || globalPragma,
|
||||
"xrpl::DatabaseCon::Setup::commonPragma : consistent global "
|
||||
"pragma");
|
||||
return useGlobalPragma && globalPragma ? globalPragma.get() : nullptr;
|
||||
}
|
||||
|
||||
static std::unique_ptr<std::vector<std::string> const> globalPragma;
|
||||
std::array<std::string, 4> txPragma;
|
||||
std::array<std::string, 1> lgrPragma;
|
||||
};
|
||||
|
||||
struct CheckpointerSetup
|
||||
{
|
||||
JobQueue* jobQueue;
|
||||
Logs* logs;
|
||||
};
|
||||
|
||||
template <std::size_t N, std::size_t M>
|
||||
DatabaseCon(
|
||||
Setup const& setup,
|
||||
std::string const& dbName,
|
||||
std::array<std::string, N> const& pragma,
|
||||
std::array<char const*, M> const& initSQL,
|
||||
beast::Journal journal)
|
||||
// Use temporary files or regular DB files?
|
||||
: DatabaseCon(
|
||||
setup.standAlone && setup.startUp != StartUpType::LOAD && setup.startUp != StartUpType::LOAD_FILE &&
|
||||
setup.startUp != StartUpType::REPLAY
|
||||
? ""
|
||||
: (setup.dataDir / dbName),
|
||||
setup.commonPragma(),
|
||||
pragma,
|
||||
initSQL,
|
||||
journal)
|
||||
{
|
||||
}
|
||||
|
||||
// Use this constructor to setup checkpointing
|
||||
template <std::size_t N, std::size_t M>
|
||||
DatabaseCon(
|
||||
Setup const& setup,
|
||||
std::string const& dbName,
|
||||
std::array<std::string, N> const& pragma,
|
||||
std::array<char const*, M> const& initSQL,
|
||||
CheckpointerSetup const& checkpointerSetup,
|
||||
beast::Journal journal)
|
||||
: DatabaseCon(setup, dbName, pragma, initSQL, journal)
|
||||
{
|
||||
setupCheckpointing(checkpointerSetup.jobQueue, *checkpointerSetup.logs);
|
||||
}
|
||||
|
||||
template <std::size_t N, std::size_t M>
|
||||
DatabaseCon(
|
||||
boost::filesystem::path const& dataDir,
|
||||
std::string const& dbName,
|
||||
std::array<std::string, N> const& pragma,
|
||||
std::array<char const*, M> const& initSQL,
|
||||
beast::Journal journal)
|
||||
: DatabaseCon(dataDir / dbName, nullptr, pragma, initSQL, journal)
|
||||
{
|
||||
}
|
||||
|
||||
// Use this constructor to setup checkpointing
|
||||
template <std::size_t N, std::size_t M>
|
||||
DatabaseCon(
|
||||
boost::filesystem::path const& dataDir,
|
||||
std::string const& dbName,
|
||||
std::array<std::string, N> const& pragma,
|
||||
std::array<char const*, M> const& initSQL,
|
||||
CheckpointerSetup const& checkpointerSetup,
|
||||
beast::Journal journal)
|
||||
: DatabaseCon(dataDir, dbName, pragma, initSQL, journal)
|
||||
{
|
||||
setupCheckpointing(checkpointerSetup.jobQueue, *checkpointerSetup.logs);
|
||||
}
|
||||
|
||||
~DatabaseCon();
|
||||
|
||||
soci::session&
|
||||
getSession()
|
||||
{
|
||||
return *session_;
|
||||
}
|
||||
|
||||
LockedSociSession
|
||||
checkoutDb()
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
LockedSociSession session =
|
||||
perf::measureDurationAndLog([&]() { return LockedSociSession(session_, lock_); }, "checkoutDb", 10ms, j_);
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
private:
|
||||
void
|
||||
setupCheckpointing(JobQueue*, Logs&);
|
||||
|
||||
template <std::size_t N, std::size_t M>
|
||||
DatabaseCon(
|
||||
boost::filesystem::path const& pPath,
|
||||
std::vector<std::string> const* commonPragma,
|
||||
std::array<std::string, N> const& pragma,
|
||||
std::array<char const*, M> const& initSQL,
|
||||
beast::Journal journal)
|
||||
: session_(std::make_shared<soci::session>()), j_(journal)
|
||||
{
|
||||
open(*session_, "sqlite", pPath.string());
|
||||
|
||||
for (auto const& p : pragma)
|
||||
{
|
||||
soci::statement st = session_->prepare << p;
|
||||
st.execute(true);
|
||||
}
|
||||
|
||||
if (commonPragma)
|
||||
{
|
||||
for (auto const& p : *commonPragma)
|
||||
{
|
||||
soci::statement st = session_->prepare << p;
|
||||
st.execute(true);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto const& sql : initSQL)
|
||||
{
|
||||
soci::statement st = session_->prepare << sql;
|
||||
st.execute(true);
|
||||
}
|
||||
}
|
||||
|
||||
LockedSociSession::mutex lock_;
|
||||
|
||||
// checkpointer may outlive the DatabaseCon when the checkpointer jobQueue
|
||||
// callback locks a weak pointer and the DatabaseCon is then destroyed. In
|
||||
// this case, the checkpointer needs to make sure it doesn't use an already
|
||||
// destroyed session. Thus this class keeps a shared_ptr to the session (so
|
||||
// the checkpointer can keep a weak_ptr) and the checkpointer is a
|
||||
// shared_ptr in this class. session_ will never be null.
|
||||
std::shared_ptr<soci::session> const session_;
|
||||
std::shared_ptr<Checkpointer> checkpointer_;
|
||||
|
||||
beast::Journal const j_;
|
||||
};
|
||||
|
||||
// Return the checkpointer from its id. If the checkpointer no longer exists, an
|
||||
// nullptr is returned
|
||||
std::shared_ptr<Checkpointer>
|
||||
checkpointerFromId(std::uintptr_t id);
|
||||
|
||||
} // namespace xrpl
|
||||
475
include/xrpl/rdb/RelationalDatabase.h
Normal file
475
include/xrpl/rdb/RelationalDatabase.h
Normal file
@@ -0,0 +1,475 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/RangeSet.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/LedgerHeader.h>
|
||||
#include <xrpl/protocol/LedgerShortcut.h>
|
||||
#include <xrpl/protocol/TxMeta.h>
|
||||
#include <xrpl/protocol/TxSearched.h>
|
||||
#include <xrpl/rdb/DatabaseCon.h>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/variant.hpp>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class Transaction;
|
||||
class Ledger;
|
||||
|
||||
struct LedgerHashPair
|
||||
{
|
||||
uint256 ledgerHash;
|
||||
uint256 parentHash;
|
||||
};
|
||||
|
||||
struct LedgerRange
|
||||
{
|
||||
uint32_t min;
|
||||
uint32_t max;
|
||||
};
|
||||
|
||||
class RelationalDatabase
|
||||
{
|
||||
public:
|
||||
struct CountMinMax
|
||||
{
|
||||
std::size_t numberOfRows;
|
||||
LedgerIndex minLedgerSequence;
|
||||
LedgerIndex maxLedgerSequence;
|
||||
};
|
||||
|
||||
struct AccountTxMarker
|
||||
{
|
||||
std::uint32_t ledgerSeq = 0;
|
||||
std::uint32_t txnSeq = 0;
|
||||
};
|
||||
|
||||
struct AccountTxOptions
|
||||
{
|
||||
AccountID const& account;
|
||||
std::uint32_t minLedger;
|
||||
std::uint32_t maxLedger;
|
||||
std::uint32_t offset;
|
||||
std::uint32_t limit;
|
||||
bool bUnlimited;
|
||||
};
|
||||
|
||||
struct AccountTxPageOptions
|
||||
{
|
||||
AccountID const& account;
|
||||
std::uint32_t minLedger;
|
||||
std::uint32_t maxLedger;
|
||||
std::optional<AccountTxMarker> marker;
|
||||
std::uint32_t limit;
|
||||
bool bAdmin;
|
||||
};
|
||||
|
||||
using AccountTx = std::pair<std::shared_ptr<Transaction>, std::shared_ptr<TxMeta>>;
|
||||
using AccountTxs = std::vector<AccountTx>;
|
||||
using txnMetaLedgerType = std::tuple<Blob, Blob, std::uint32_t>;
|
||||
using MetaTxsList = std::vector<txnMetaLedgerType>;
|
||||
|
||||
using LedgerSequence = uint32_t;
|
||||
using LedgerHash = uint256;
|
||||
using LedgerSpecifier = std::variant<LedgerRange, LedgerShortcut, LedgerSequence, LedgerHash>;
|
||||
|
||||
struct AccountTxArgs
|
||||
{
|
||||
AccountID account;
|
||||
std::optional<LedgerSpecifier> ledger;
|
||||
bool binary = false;
|
||||
bool forward = false;
|
||||
uint32_t limit = 0;
|
||||
std::optional<AccountTxMarker> marker;
|
||||
};
|
||||
|
||||
struct AccountTxResult
|
||||
{
|
||||
std::variant<AccountTxs, MetaTxsList> transactions;
|
||||
LedgerRange ledgerRange;
|
||||
uint32_t limit;
|
||||
std::optional<AccountTxMarker> marker;
|
||||
};
|
||||
|
||||
virtual ~RelationalDatabase() = default;
|
||||
|
||||
/**
|
||||
* @brief getMinLedgerSeq Returns the minimum ledger sequence in the Ledgers
|
||||
* table.
|
||||
* @return Ledger sequence or no value if no ledgers exist.
|
||||
*/
|
||||
virtual std::optional<LedgerIndex>
|
||||
getMinLedgerSeq() = 0;
|
||||
|
||||
/**
|
||||
* @brief getMaxLedgerSeq Returns the maximum ledger sequence in the Ledgers
|
||||
* table.
|
||||
* @return Ledger sequence or none if no ledgers exist.
|
||||
*/
|
||||
virtual std::optional<LedgerIndex>
|
||||
getMaxLedgerSeq() = 0;
|
||||
|
||||
/**
|
||||
* @brief getLedgerInfoByIndex Returns a ledger by its sequence.
|
||||
* @param ledgerSeq Ledger sequence.
|
||||
* @return The ledger if found, otherwise no value.
|
||||
*/
|
||||
virtual std::optional<LedgerHeader>
|
||||
getLedgerInfoByIndex(LedgerIndex ledgerSeq) = 0;
|
||||
|
||||
/**
|
||||
* @brief getNewestLedgerInfo Returns the info of the newest saved ledger.
|
||||
* @return Ledger info if found, otherwise no value.
|
||||
*/
|
||||
virtual std::optional<LedgerHeader>
|
||||
getNewestLedgerInfo() = 0;
|
||||
|
||||
/**
|
||||
* @brief getLedgerInfoByHash Returns the info of the ledger with given
|
||||
* hash.
|
||||
* @param ledgerHash Hash of the ledger.
|
||||
* @return Ledger if found, otherwise no value.
|
||||
*/
|
||||
virtual std::optional<LedgerHeader>
|
||||
getLedgerInfoByHash(uint256 const& ledgerHash) = 0;
|
||||
|
||||
/**
|
||||
* @brief getHashByIndex Returns the hash of the ledger with the given
|
||||
* sequence.
|
||||
* @param ledgerIndex Ledger sequence.
|
||||
* @return Hash of the ledger.
|
||||
*/
|
||||
virtual uint256
|
||||
getHashByIndex(LedgerIndex ledgerIndex) = 0;
|
||||
|
||||
/**
|
||||
* @brief getHashesByIndex Returns the hashes of the ledger and its parent
|
||||
* as specified by the ledgerIndex.
|
||||
* @param ledgerIndex Ledger sequence.
|
||||
* @return Struct LedgerHashPair which contains hashes of the ledger and
|
||||
* its parent.
|
||||
*/
|
||||
virtual std::optional<LedgerHashPair>
|
||||
getHashesByIndex(LedgerIndex ledgerIndex) = 0;
|
||||
|
||||
/**
|
||||
* @brief getHashesByIndex Returns hashes of each ledger and its parent for
|
||||
* all ledgers within the provided range.
|
||||
* @param minSeq Minimum ledger sequence.
|
||||
* @param maxSeq Maximum ledger sequence.
|
||||
* @return Container that maps the sequence number of a found ledger to the
|
||||
* struct LedgerHashPair which contains the hashes of the ledger and
|
||||
* its parent.
|
||||
*/
|
||||
virtual std::map<LedgerIndex, LedgerHashPair>
|
||||
getHashesByIndex(LedgerIndex minSeq, LedgerIndex maxSeq) = 0;
|
||||
|
||||
/**
|
||||
* @brief getTxHistory Returns the 20 most recent transactions starting from
|
||||
* the given number.
|
||||
* @param startIndex First number of returned entry.
|
||||
* @return Vector of shared pointers to transactions sorted in
|
||||
* descending order by ledger sequence.
|
||||
*/
|
||||
virtual std::vector<std::shared_ptr<Transaction>>
|
||||
getTxHistory(LedgerIndex startIndex) = 0;
|
||||
|
||||
/**
|
||||
* @brief getTransactionsMinLedgerSeq Returns the minimum ledger sequence
|
||||
* stored in the Transactions table.
|
||||
* @return Ledger sequence or no value if no ledgers exist.
|
||||
*/
|
||||
virtual std::optional<LedgerIndex>
|
||||
getTransactionsMinLedgerSeq() = 0;
|
||||
|
||||
/**
|
||||
* @brief getAccountTransactionsMinLedgerSeq Returns the minimum ledger
|
||||
* sequence stored in the AccountTransactions table.
|
||||
* @return Ledger sequence or no value if no ledgers exist.
|
||||
*/
|
||||
virtual std::optional<LedgerIndex>
|
||||
getAccountTransactionsMinLedgerSeq() = 0;
|
||||
|
||||
/**
|
||||
* @brief deleteTransactionByLedgerSeq Deletes transactions from the ledger
|
||||
* with the given sequence.
|
||||
* @param ledgerSeq Ledger sequence.
|
||||
*/
|
||||
virtual void
|
||||
deleteTransactionByLedgerSeq(LedgerIndex ledgerSeq) = 0;
|
||||
|
||||
/**
|
||||
* @brief deleteBeforeLedgerSeq Deletes all ledgers with a sequence number
|
||||
* less than or equal to the given ledger sequence.
|
||||
* @param ledgerSeq Ledger sequence.
|
||||
*/
|
||||
virtual void
|
||||
deleteBeforeLedgerSeq(LedgerIndex ledgerSeq) = 0;
|
||||
|
||||
/**
|
||||
* @brief deleteTransactionsBeforeLedgerSeq Deletes all transactions with
|
||||
* a sequence number less than or equal to the given ledger
|
||||
* sequence.
|
||||
* @param ledgerSeq Ledger sequence.
|
||||
*/
|
||||
virtual void
|
||||
deleteTransactionsBeforeLedgerSeq(LedgerIndex ledgerSeq) = 0;
|
||||
|
||||
/**
|
||||
* @brief deleteAccountTransactionsBeforeLedgerSeq Deletes all account
|
||||
* transactions with a sequence number less than or equal to the
|
||||
* given ledger sequence.
|
||||
* @param ledgerSeq Ledger sequence.
|
||||
*/
|
||||
virtual void
|
||||
deleteAccountTransactionsBeforeLedgerSeq(LedgerIndex ledgerSeq) = 0;
|
||||
|
||||
/**
|
||||
* @brief getTransactionCount Returns the number of transactions.
|
||||
* @return Number of transactions.
|
||||
*/
|
||||
virtual std::size_t
|
||||
getTransactionCount() = 0;
|
||||
|
||||
/**
|
||||
* @brief getAccountTransactionCount Returns the number of account
|
||||
* transactions.
|
||||
* @return Number of account transactions.
|
||||
*/
|
||||
virtual std::size_t
|
||||
getAccountTransactionCount() = 0;
|
||||
|
||||
/**
|
||||
* @brief getLedgerCountMinMax Returns the minimum ledger sequence,
|
||||
* maximum ledger sequence and total number of saved ledgers.
|
||||
* @return Struct CountMinMax which contains the minimum sequence,
|
||||
* maximum sequence and number of ledgers.
|
||||
*/
|
||||
virtual struct CountMinMax
|
||||
getLedgerCountMinMax() = 0;
|
||||
|
||||
/**
|
||||
* @brief saveValidatedLedger Saves a ledger into the database.
|
||||
* @param ledger The ledger.
|
||||
* @param current True if the ledger is current.
|
||||
* @return True if saving was successful.
|
||||
*/
|
||||
virtual bool
|
||||
saveValidatedLedger(std::shared_ptr<Ledger const> const& ledger, bool current) = 0;
|
||||
|
||||
/**
|
||||
* @brief getLimitedOldestLedgerInfo Returns the info of the oldest ledger
|
||||
* whose sequence number is greater than or equal to the given
|
||||
* sequence number.
|
||||
* @param ledgerFirstIndex Minimum ledger sequence.
|
||||
* @return Ledger info if found, otherwise no value.
|
||||
*/
|
||||
virtual std::optional<LedgerHeader>
|
||||
getLimitedOldestLedgerInfo(LedgerIndex ledgerFirstIndex) = 0;
|
||||
|
||||
/**
|
||||
* @brief getLimitedNewestLedgerInfo Returns the info of the newest ledger
|
||||
* whose sequence number is greater than or equal to the given
|
||||
* sequence number.
|
||||
* @param ledgerFirstIndex Minimum ledger sequence.
|
||||
* @return Ledger info if found, otherwise no value.
|
||||
*/
|
||||
virtual std::optional<LedgerHeader>
|
||||
getLimitedNewestLedgerInfo(LedgerIndex ledgerFirstIndex) = 0;
|
||||
|
||||
/**
|
||||
* @brief getOldestAccountTxs Returns the oldest transactions for the
|
||||
* account that matches the given criteria starting from the provided
|
||||
* offset.
|
||||
* @param options Struct AccountTxOptions which contains the criteria to
|
||||
* match: the account, ledger search range, the offset of the first
|
||||
* entry to return, the number of transactions to return, a flag if
|
||||
* this number is unlimited.
|
||||
* @return Vector of pairs of found transactions and their metadata
|
||||
* sorted in ascending order by account sequence.
|
||||
*/
|
||||
virtual AccountTxs
|
||||
getOldestAccountTxs(AccountTxOptions const& options) = 0;
|
||||
|
||||
/**
|
||||
* @brief getNewestAccountTxs Returns the newest transactions for the
|
||||
* account that matches the given criteria starting from the provided
|
||||
* offset.
|
||||
* @param options Struct AccountTxOptions which contains the criteria to
|
||||
* match: the account, the ledger search range, the offset of the
|
||||
* first entry to return, the number of transactions to return, a
|
||||
* flag if this number unlimited.
|
||||
* @return Vector of pairs of found transactions and their metadata
|
||||
* sorted in descending order by account sequence.
|
||||
*/
|
||||
virtual AccountTxs
|
||||
getNewestAccountTxs(AccountTxOptions const& options) = 0;
|
||||
|
||||
/**
|
||||
* @brief getOldestAccountTxsB Returns the oldest transactions in binary
|
||||
* form for the account that matches the given criteria starting from
|
||||
* the provided offset.
|
||||
* @param options Struct AccountTxOptions which contains the criteria to
|
||||
* match: the account, the ledger search range, the offset of the
|
||||
* first entry to return, the number of transactions to return, a
|
||||
* flag if this number unlimited.
|
||||
* @return Vector of tuples of found transactions, their metadata and
|
||||
* account sequences sorted in ascending order by account sequence.
|
||||
*/
|
||||
virtual MetaTxsList
|
||||
getOldestAccountTxsB(AccountTxOptions const& options) = 0;
|
||||
|
||||
/**
|
||||
* @brief getNewestAccountTxsB Returns the newest transactions in binary
|
||||
* form for the account that matches the given criteria starting from
|
||||
* the provided offset.
|
||||
* @param options Struct AccountTxOptions which contains the criteria to
|
||||
* match: the account, the ledger search range, the offset of the
|
||||
* first entry to return, the number of transactions to return, a
|
||||
* flag if this number is unlimited.
|
||||
* @return Vector of tuples of found transactions, their metadata and
|
||||
* account sequences sorted in descending order by account
|
||||
* sequence.
|
||||
*/
|
||||
virtual MetaTxsList
|
||||
getNewestAccountTxsB(AccountTxOptions const& options) = 0;
|
||||
|
||||
/**
|
||||
* @brief oldestAccountTxPage Returns the oldest transactions for the
|
||||
* account that matches the given criteria starting from the
|
||||
* provided marker.
|
||||
* @param options Struct AccountTxPageOptions which contains the criteria to
|
||||
* match: the account, the ledger search range, the marker of first
|
||||
* returned entry, the number of transactions to return, a flag if
|
||||
* this number is unlimited.
|
||||
* @return Vector of pairs of found transactions and their metadata
|
||||
* sorted in ascending order by account sequence and a marker
|
||||
* for the next search if the search was not finished.
|
||||
*/
|
||||
virtual std::pair<AccountTxs, std::optional<AccountTxMarker>>
|
||||
oldestAccountTxPage(AccountTxPageOptions const& options) = 0;
|
||||
|
||||
/**
|
||||
* @brief newestAccountTxPage Returns the newest transactions for the
|
||||
* account that matches the given criteria starting from the provided
|
||||
* marker.
|
||||
* @param options Struct AccountTxPageOptions which contains the criteria to
|
||||
* match: the account, the ledger search range, the marker of the
|
||||
* first returned entry, the number of transactions to return, a flag
|
||||
* if this number unlimited.
|
||||
* @return Vector of pairs of found transactions and their metadata
|
||||
* sorted in descending order by account sequence and a marker
|
||||
* for the next search if the search was not finished.
|
||||
*/
|
||||
virtual std::pair<AccountTxs, std::optional<AccountTxMarker>>
|
||||
newestAccountTxPage(AccountTxPageOptions const& options) = 0;
|
||||
|
||||
/**
|
||||
* @brief oldestAccountTxPageB Returns the oldest transactions in binary
|
||||
* form for the account that matches the given criteria starting from
|
||||
* the provided marker.
|
||||
* @param options Struct AccountTxPageOptions which contains criteria to
|
||||
* match: the account, the ledger search range, the marker of the
|
||||
* first returned entry, the number of transactions to return, a flag
|
||||
* if this number unlimited.
|
||||
* @return Vector of tuples of found transactions, their metadata and
|
||||
* account sequences sorted in ascending order by account
|
||||
* sequence and a marker for the next search if the search was not
|
||||
* finished.
|
||||
*/
|
||||
virtual std::pair<MetaTxsList, std::optional<AccountTxMarker>>
|
||||
oldestAccountTxPageB(AccountTxPageOptions const& options) = 0;
|
||||
|
||||
/**
|
||||
* @brief newestAccountTxPageB Returns the newest transactions in binary
|
||||
* form for the account that matches the given criteria starting from
|
||||
* the provided marker.
|
||||
* @param options Struct AccountTxPageOptions which contains the criteria to
|
||||
* match: the account, the ledger search range, the marker of the
|
||||
* first returned entry, the number of transactions to return, a flag
|
||||
* if this number is unlimited.
|
||||
* @return Vector of tuples of found transactions, their metadata and
|
||||
* account sequences sorted in descending order by account
|
||||
* sequence and a marker for the next search if the search was not
|
||||
* finished.
|
||||
*/
|
||||
virtual std::pair<MetaTxsList, std::optional<AccountTxMarker>>
|
||||
newestAccountTxPageB(AccountTxPageOptions const& options) = 0;
|
||||
|
||||
/**
|
||||
* @brief getTransaction Returns the transaction with the given hash. If a
|
||||
* range is provided but the transaction is not found, then check if
|
||||
* all ledgers in the range are present in the database.
|
||||
* @param id Hash of the transaction.
|
||||
* @param range Range of ledgers to check, if present.
|
||||
* @param ec Default error code value.
|
||||
* @return Transaction and its metadata if found, otherwise TxSearched::all
|
||||
* if a range is provided and all ledgers from the range are present
|
||||
* in the database, TxSearched::some if a range is provided and not
|
||||
* all ledgers are present, TxSearched::unknown if the range is not
|
||||
* provided or a deserializing error occurred. In the last case the
|
||||
* error code is returned via the ec parameter, in other cases the
|
||||
* default error code is not changed.
|
||||
*/
|
||||
virtual std::variant<AccountTx, TxSearched>
|
||||
getTransaction(uint256 const& id, std::optional<ClosedInterval<uint32_t>> const& range, error_code_i& ec) = 0;
|
||||
|
||||
/**
|
||||
* @brief getKBUsedAll Returns the amount of space used by all databases.
|
||||
* @return Space in kilobytes.
|
||||
*/
|
||||
virtual uint32_t
|
||||
getKBUsedAll() = 0;
|
||||
|
||||
/**
|
||||
* @brief getKBUsedLedger Returns the amount of space space used by the
|
||||
* ledger database.
|
||||
* @return Space in kilobytes.
|
||||
*/
|
||||
virtual uint32_t
|
||||
getKBUsedLedger() = 0;
|
||||
|
||||
/**
|
||||
* @brief getKBUsedTransaction Returns the amount of space used by the
|
||||
* transaction database.
|
||||
* @return Space in kilobytes.
|
||||
*/
|
||||
virtual uint32_t
|
||||
getKBUsedTransaction() = 0;
|
||||
|
||||
/**
|
||||
* @brief Closes the ledger database
|
||||
*/
|
||||
virtual void
|
||||
closeLedgerDB() = 0;
|
||||
|
||||
/**
|
||||
* @brief Closes the transaction database
|
||||
*/
|
||||
virtual void
|
||||
closeTransactionDB() = 0;
|
||||
};
|
||||
|
||||
template <class T, class C>
|
||||
T
|
||||
rangeCheckedCast(C c)
|
||||
{
|
||||
if ((c > std::numeric_limits<T>::max()) || (!std::numeric_limits<T>::is_signed && c < 0) ||
|
||||
(std::numeric_limits<T>::is_signed && std::numeric_limits<C>::is_signed &&
|
||||
c < std::numeric_limits<T>::lowest()))
|
||||
{
|
||||
// This should never happen
|
||||
// LCOV_EXCL_START
|
||||
UNREACHABLE("xrpl::rangeCheckedCast : domain error");
|
||||
JLOG(debugLog().error()) << "rangeCheckedCast domain error:"
|
||||
<< " value = " << c << " min = " << std::numeric_limits<T>::lowest()
|
||||
<< " max: " << std::numeric_limits<T>::max();
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
return static_cast<T>(c);
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
120
include/xrpl/rdb/SociDB.h
Normal file
120
include/xrpl/rdb/SociDB.h
Normal file
@@ -0,0 +1,120 @@
|
||||
#pragma once
|
||||
|
||||
/** An embedded database wrapper with an intuitive, type-safe interface.
|
||||
|
||||
This collection of classes let's you access embedded SQLite databases
|
||||
using C++ syntax that is very similar to regular SQL.
|
||||
|
||||
This module requires the @ref beast_sqlite external module.
|
||||
*/
|
||||
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated"
|
||||
#endif
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/core/JobQueue.h>
|
||||
|
||||
#define SOCI_USE_BOOST
|
||||
#include <soci/soci.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace sqlite_api {
|
||||
struct sqlite3;
|
||||
}
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class BasicConfig;
|
||||
|
||||
/**
|
||||
DBConfig is used when a client wants to delay opening a soci::session after
|
||||
parsing the config parameters. If a client want to open a session
|
||||
immediately, use the free function "open" below.
|
||||
*/
|
||||
class DBConfig
|
||||
{
|
||||
std::string connectionString_;
|
||||
explicit DBConfig(std::string const& dbPath);
|
||||
|
||||
public:
|
||||
DBConfig(BasicConfig const& config, std::string const& dbName);
|
||||
std::string
|
||||
connectionString() const;
|
||||
void
|
||||
open(soci::session& s) const;
|
||||
};
|
||||
|
||||
/**
|
||||
Open a soci session.
|
||||
|
||||
@param s Session to open.
|
||||
|
||||
@param config Parameters to pick the soci backend and how to connect to that
|
||||
backend.
|
||||
|
||||
@param dbName Name of the database. This has different meaning for different
|
||||
backends. Sometimes it is part of a filename (sqlite3),
|
||||
other times it is a database name (postgresql).
|
||||
*/
|
||||
void
|
||||
open(soci::session& s, BasicConfig const& config, std::string const& dbName);
|
||||
|
||||
/**
|
||||
* Open a soci session.
|
||||
*
|
||||
* @param s Session to open.
|
||||
* @param beName Backend name.
|
||||
* @param connectionString Connection string to forward to soci::open.
|
||||
* see the soci::open documentation for how to use this.
|
||||
*
|
||||
*/
|
||||
void
|
||||
open(soci::session& s, std::string const& beName, std::string const& connectionString);
|
||||
|
||||
std::uint32_t
|
||||
getKBUsedAll(soci::session& s);
|
||||
std::uint32_t
|
||||
getKBUsedDB(soci::session& s);
|
||||
|
||||
void
|
||||
convert(soci::blob& from, std::vector<std::uint8_t>& to);
|
||||
void
|
||||
convert(soci::blob& from, std::string& to);
|
||||
void
|
||||
convert(std::vector<std::uint8_t> const& from, soci::blob& to);
|
||||
void
|
||||
convert(std::string const& from, soci::blob& to);
|
||||
|
||||
class Checkpointer : public std::enable_shared_from_this<Checkpointer>
|
||||
{
|
||||
public:
|
||||
virtual std::uintptr_t
|
||||
id() const = 0;
|
||||
virtual ~Checkpointer() = default;
|
||||
|
||||
virtual void
|
||||
schedule() = 0;
|
||||
|
||||
virtual void
|
||||
checkpoint() = 0;
|
||||
};
|
||||
|
||||
/** Returns a new checkpointer which makes checkpoints of a
|
||||
soci database every checkpointPageCount pages, using a job on the job queue.
|
||||
|
||||
The checkpointer contains references to the session and job queue
|
||||
and so must outlive them both.
|
||||
*/
|
||||
std::shared_ptr<Checkpointer>
|
||||
makeCheckpointer(std::uintptr_t id, std::weak_ptr<soci::session>, JobQueue&, Logs&);
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
224
include/xrpl/server/InfoSub.h
Normal file
224
include/xrpl/server/InfoSub.h
Normal file
@@ -0,0 +1,224 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/CountedObject.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/protocol/Book.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/resource/Consumer.h>
|
||||
#include <xrpl/server/Manifest.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
// Operations that clients may wish to perform against the network
|
||||
// Master operational handler, server sequencer, network tracker
|
||||
|
||||
class InfoSubRequest : public CountedObject<InfoSubRequest>
|
||||
{
|
||||
public:
|
||||
using pointer = std::shared_ptr<InfoSubRequest>;
|
||||
|
||||
virtual ~InfoSubRequest() = default;
|
||||
|
||||
virtual Json::Value
|
||||
doClose() = 0;
|
||||
virtual Json::Value
|
||||
doStatus(Json::Value const&) = 0;
|
||||
};
|
||||
|
||||
/** Manages a client's subscription to data feeds.
|
||||
*/
|
||||
class InfoSub : public CountedObject<InfoSub>
|
||||
{
|
||||
public:
|
||||
using pointer = std::shared_ptr<InfoSub>;
|
||||
|
||||
// VFALCO TODO Standardize on the names of weak / strong pointer type
|
||||
// aliases.
|
||||
using wptr = std::weak_ptr<InfoSub>;
|
||||
|
||||
using ref = std::shared_ptr<InfoSub> const&;
|
||||
|
||||
using Consumer = Resource::Consumer;
|
||||
|
||||
public:
|
||||
/** Abstracts the source of subscription data.
|
||||
*/
|
||||
class Source
|
||||
{
|
||||
public:
|
||||
virtual ~Source() = default;
|
||||
|
||||
// For some reason, these were originally called "rt"
|
||||
// for "real time". They actually refer to whether
|
||||
// you get transactions as they occur or once their
|
||||
// results are confirmed
|
||||
virtual void
|
||||
subAccount(ref ispListener, hash_set<AccountID> const& vnaAccountIDs, bool realTime) = 0;
|
||||
|
||||
// for normal use, removes from InfoSub and server
|
||||
virtual void
|
||||
unsubAccount(ref isplistener, hash_set<AccountID> const& vnaAccountIDs, bool realTime) = 0;
|
||||
|
||||
// for use during InfoSub destruction
|
||||
// Removes only from the server
|
||||
virtual void
|
||||
unsubAccountInternal(std::uint64_t uListener, hash_set<AccountID> const& vnaAccountIDs, bool realTime) = 0;
|
||||
|
||||
/**
|
||||
* subscribe an account's new transactions and retrieve the account's
|
||||
* historical transactions
|
||||
* @return rpcSUCCESS if successful, otherwise an error code
|
||||
*/
|
||||
virtual error_code_i
|
||||
subAccountHistory(ref ispListener, AccountID const& account) = 0;
|
||||
|
||||
/**
|
||||
* unsubscribe an account's transactions
|
||||
* @param historyOnly if true, only stop historical transactions
|
||||
* @note once a client receives enough historical transactions,
|
||||
* it should unsubscribe with historyOnly == true to stop receiving
|
||||
* more historical transactions. It will continue to receive new
|
||||
* transactions.
|
||||
*/
|
||||
virtual void
|
||||
unsubAccountHistory(ref ispListener, AccountID const& account, bool historyOnly) = 0;
|
||||
|
||||
virtual void
|
||||
unsubAccountHistoryInternal(std::uint64_t uListener, AccountID const& account, bool historyOnly) = 0;
|
||||
|
||||
// VFALCO TODO Document the bool return value
|
||||
virtual bool
|
||||
subLedger(ref ispListener, Json::Value& jvResult) = 0;
|
||||
virtual bool
|
||||
unsubLedger(std::uint64_t uListener) = 0;
|
||||
|
||||
virtual bool
|
||||
subBookChanges(ref ispListener) = 0;
|
||||
virtual bool
|
||||
unsubBookChanges(std::uint64_t uListener) = 0;
|
||||
|
||||
virtual bool
|
||||
subManifests(ref ispListener) = 0;
|
||||
virtual bool
|
||||
unsubManifests(std::uint64_t uListener) = 0;
|
||||
virtual void
|
||||
pubManifest(Manifest const&) = 0;
|
||||
|
||||
virtual bool
|
||||
subServer(ref ispListener, Json::Value& jvResult, bool admin) = 0;
|
||||
virtual bool
|
||||
unsubServer(std::uint64_t uListener) = 0;
|
||||
|
||||
virtual bool
|
||||
subBook(ref ispListener, Book const&) = 0;
|
||||
virtual bool
|
||||
unsubBook(std::uint64_t uListener, Book const&) = 0;
|
||||
|
||||
virtual bool
|
||||
subTransactions(ref ispListener) = 0;
|
||||
virtual bool
|
||||
unsubTransactions(std::uint64_t uListener) = 0;
|
||||
|
||||
virtual bool
|
||||
subRTTransactions(ref ispListener) = 0;
|
||||
virtual bool
|
||||
unsubRTTransactions(std::uint64_t uListener) = 0;
|
||||
|
||||
virtual bool
|
||||
subValidations(ref ispListener) = 0;
|
||||
virtual bool
|
||||
unsubValidations(std::uint64_t uListener) = 0;
|
||||
|
||||
virtual bool
|
||||
subPeerStatus(ref ispListener) = 0;
|
||||
|
||||
virtual bool
|
||||
unsubPeerStatus(std::uint64_t uListener) = 0;
|
||||
virtual void
|
||||
pubPeerStatus(std::function<Json::Value(void)> const&) = 0;
|
||||
|
||||
virtual bool
|
||||
subConsensus(ref ispListener) = 0;
|
||||
virtual bool
|
||||
unsubConsensus(std::uint64_t uListener) = 0;
|
||||
|
||||
// VFALCO TODO Remove
|
||||
// This was added for one particular partner, it
|
||||
// "pushes" subscription data to a particular URL.
|
||||
//
|
||||
virtual pointer
|
||||
findRpcSub(std::string const& strUrl) = 0;
|
||||
virtual pointer
|
||||
addRpcSub(std::string const& strUrl, ref rspEntry) = 0;
|
||||
virtual bool
|
||||
tryRemoveRpcSub(std::string const& strUrl) = 0;
|
||||
};
|
||||
|
||||
public:
|
||||
InfoSub(Source& source);
|
||||
InfoSub(Source& source, Consumer consumer);
|
||||
|
||||
virtual ~InfoSub();
|
||||
|
||||
Consumer&
|
||||
getConsumer();
|
||||
|
||||
virtual void
|
||||
send(Json::Value const& jvObj, bool broadcast) = 0;
|
||||
|
||||
std::uint64_t
|
||||
getSeq();
|
||||
|
||||
void
|
||||
onSendEmpty();
|
||||
|
||||
void
|
||||
insertSubAccountInfo(AccountID const& account, bool rt);
|
||||
|
||||
void
|
||||
deleteSubAccountInfo(AccountID const& account, bool rt);
|
||||
|
||||
// return false if already subscribed to this account
|
||||
bool
|
||||
insertSubAccountHistory(AccountID const& account);
|
||||
|
||||
void
|
||||
deleteSubAccountHistory(AccountID const& account);
|
||||
|
||||
void
|
||||
clearRequest();
|
||||
|
||||
void
|
||||
setRequest(std::shared_ptr<InfoSubRequest> const& req);
|
||||
|
||||
std::shared_ptr<InfoSubRequest> const&
|
||||
getRequest();
|
||||
|
||||
void
|
||||
setApiVersion(unsigned int apiVersion);
|
||||
|
||||
unsigned int
|
||||
getApiVersion() const noexcept;
|
||||
|
||||
protected:
|
||||
std::mutex mLock;
|
||||
|
||||
private:
|
||||
Consumer m_consumer;
|
||||
Source& m_source;
|
||||
hash_set<AccountID> realTimeSubscriptions_;
|
||||
hash_set<AccountID> normalSubscriptions_;
|
||||
std::shared_ptr<InfoSubRequest> request_;
|
||||
std::uint64_t mSeq;
|
||||
hash_set<AccountID> accountHistorySubscriptions_;
|
||||
unsigned int apiVersion_ = 0;
|
||||
|
||||
static int
|
||||
assign_id()
|
||||
{
|
||||
static std::atomic<std::uint64_t> id(0);
|
||||
return ++id;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
137
include/xrpl/server/LoadFeeTrack.h
Normal file
137
include/xrpl/server/LoadFeeTrack.h
Normal file
@@ -0,0 +1,137 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
struct Fees;
|
||||
|
||||
/** Manages the current fee schedule.
|
||||
|
||||
The "base" fee is the cost to send a reference transaction under no load,
|
||||
expressed in millionths of one XRP.
|
||||
|
||||
The "load" fee is how much the local server currently charges to send a
|
||||
reference transaction. This fee fluctuates based on the load of the
|
||||
server.
|
||||
*/
|
||||
class LoadFeeTrack final
|
||||
{
|
||||
public:
|
||||
explicit LoadFeeTrack(beast::Journal journal = beast::Journal(beast::Journal::getNullSink()))
|
||||
: j_(journal)
|
||||
, localTxnLoadFee_(lftNormalFee)
|
||||
, remoteTxnLoadFee_(lftNormalFee)
|
||||
, clusterTxnLoadFee_(lftNormalFee)
|
||||
, raiseCount_(0)
|
||||
{
|
||||
}
|
||||
|
||||
~LoadFeeTrack() = default;
|
||||
|
||||
void
|
||||
setRemoteFee(std::uint32_t f)
|
||||
{
|
||||
JLOG(j_.trace()) << "setRemoteFee: " << f;
|
||||
std::lock_guard sl(lock_);
|
||||
remoteTxnLoadFee_ = f;
|
||||
}
|
||||
|
||||
std::uint32_t
|
||||
getRemoteFee() const
|
||||
{
|
||||
std::lock_guard sl(lock_);
|
||||
return remoteTxnLoadFee_;
|
||||
}
|
||||
|
||||
std::uint32_t
|
||||
getLocalFee() const
|
||||
{
|
||||
std::lock_guard sl(lock_);
|
||||
return localTxnLoadFee_;
|
||||
}
|
||||
|
||||
std::uint32_t
|
||||
getClusterFee() const
|
||||
{
|
||||
std::lock_guard sl(lock_);
|
||||
return clusterTxnLoadFee_;
|
||||
}
|
||||
|
||||
std::uint32_t
|
||||
getLoadBase() const
|
||||
{
|
||||
return lftNormalFee;
|
||||
}
|
||||
|
||||
std::uint32_t
|
||||
getLoadFactor() const
|
||||
{
|
||||
std::lock_guard sl(lock_);
|
||||
return std::max({clusterTxnLoadFee_, localTxnLoadFee_, remoteTxnLoadFee_});
|
||||
}
|
||||
|
||||
std::pair<std::uint32_t, std::uint32_t>
|
||||
getScalingFactors() const
|
||||
{
|
||||
std::lock_guard sl(lock_);
|
||||
|
||||
return std::make_pair(
|
||||
std::max(localTxnLoadFee_, remoteTxnLoadFee_), std::max(remoteTxnLoadFee_, clusterTxnLoadFee_));
|
||||
}
|
||||
|
||||
void
|
||||
setClusterFee(std::uint32_t fee)
|
||||
{
|
||||
JLOG(j_.trace()) << "setClusterFee: " << fee;
|
||||
std::lock_guard sl(lock_);
|
||||
clusterTxnLoadFee_ = fee;
|
||||
}
|
||||
|
||||
bool
|
||||
raiseLocalFee();
|
||||
bool
|
||||
lowerLocalFee();
|
||||
|
||||
bool
|
||||
isLoadedLocal() const
|
||||
{
|
||||
std::lock_guard sl(lock_);
|
||||
return (raiseCount_ != 0) || (localTxnLoadFee_ != lftNormalFee);
|
||||
}
|
||||
|
||||
bool
|
||||
isLoadedCluster() const
|
||||
{
|
||||
std::lock_guard sl(lock_);
|
||||
return (raiseCount_ != 0) || (localTxnLoadFee_ != lftNormalFee) || (clusterTxnLoadFee_ != lftNormalFee);
|
||||
}
|
||||
|
||||
private:
|
||||
static std::uint32_t constexpr lftNormalFee = 256; // 256 is the minimum/normal load factor
|
||||
static std::uint32_t constexpr lftFeeIncFraction = 4; // increase fee by 1/4
|
||||
static std::uint32_t constexpr lftFeeDecFraction = 4; // decrease fee by 1/4
|
||||
static std::uint32_t constexpr lftFeeMax = lftNormalFee * 1000000;
|
||||
|
||||
beast::Journal const j_;
|
||||
std::mutex mutable lock_;
|
||||
|
||||
std::uint32_t localTxnLoadFee_; // Scale factor, lftNormalFee = normal fee
|
||||
std::uint32_t remoteTxnLoadFee_; // Scale factor, lftNormalFee = normal fee
|
||||
std::uint32_t clusterTxnLoadFee_; // Scale factor, lftNormalFee = normal fee
|
||||
std::uint32_t raiseCount_;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Scale using load as well as base rate
|
||||
XRPAmount
|
||||
scaleFeeLoad(XRPAmount fee, LoadFeeTrack const& feeTrack, Fees const& fees, bool bUnlimited);
|
||||
|
||||
} // namespace xrpl
|
||||
428
include/xrpl/server/Manifest.h
Normal file
428
include/xrpl/server/Manifest.h
Normal file
@@ -0,0 +1,428 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/UnorderedContainers.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/protocol/PublicKey.h>
|
||||
#include <xrpl/protocol/SecretKey.h>
|
||||
|
||||
#include <optional>
|
||||
#include <shared_mutex>
|
||||
#include <string>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/*
|
||||
Validator key manifests
|
||||
-----------------------
|
||||
|
||||
Suppose the secret keys installed on a Ripple validator are compromised. Not
|
||||
only do you have to generate and install new key pairs on each validator,
|
||||
EVERY rippled needs to have its config updated with the new public keys, and
|
||||
is vulnerable to forged validation signatures until this is done. The
|
||||
solution is a new layer of indirection: A master secret key under
|
||||
restrictive access control is used to sign a "manifest": essentially, a
|
||||
certificate including the master public key, an ephemeral public key for
|
||||
verifying validations (which will be signed by its secret counterpart), a
|
||||
sequence number, and a digital signature.
|
||||
|
||||
The manifest has two serialized forms: one which includes the digital
|
||||
signature and one which doesn't. There is an obvious causal dependency
|
||||
relationship between the (latter) form with no signature, the signature
|
||||
of that form, and the (former) form which includes that signature. In
|
||||
other words, a message can't contain a signature of itself. The code
|
||||
below stores a serialized manifest which includes the signature, and
|
||||
dynamically generates the signatureless form when it needs to verify
|
||||
the signature.
|
||||
|
||||
An instance of ManifestCache stores, for each trusted validator, (a) its
|
||||
master public key, and (b) the most senior of all valid manifests it has
|
||||
seen for that validator, if any. On startup, the [validator_token] config
|
||||
entry (which contains the manifest for this validator) is decoded and
|
||||
added to the manifest cache. Other manifests are added as "gossip"
|
||||
received from rippled peers.
|
||||
|
||||
When an ephemeral key is compromised, a new signing key pair is created,
|
||||
along with a new manifest vouching for it (with a higher sequence number),
|
||||
signed by the master key. When a rippled peer receives the new manifest,
|
||||
it verifies it with the master key and (assuming it's valid) discards the
|
||||
old ephemeral key and stores the new one. If the master key itself gets
|
||||
compromised, a manifest with sequence number 0xFFFFFFFF will supersede a
|
||||
prior manifest and discard any existing ephemeral key without storing a
|
||||
new one. These revocation manifests are loaded from the
|
||||
[validator_key_revocation] config entry as well as received as gossip from
|
||||
peers. Since no further manifests for this master key will be accepted
|
||||
(since no higher sequence number is possible), and no signing key is on
|
||||
record, no validations will be accepted from the compromised validator.
|
||||
*/
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
struct Manifest
|
||||
{
|
||||
/// The manifest in serialized form.
|
||||
std::string serialized;
|
||||
|
||||
/// The master key associated with this manifest.
|
||||
PublicKey masterKey;
|
||||
|
||||
/// The ephemeral key associated with this manifest.
|
||||
// A revoked manifest does not have a signingKey
|
||||
// This field is specified as "optional" in manifestFormat's
|
||||
// SOTemplate
|
||||
std::optional<PublicKey> signingKey;
|
||||
|
||||
/// The sequence number of this manifest.
|
||||
std::uint32_t sequence = 0;
|
||||
|
||||
/// The domain, if one was specified in the manifest; empty otherwise.
|
||||
std::string domain;
|
||||
|
||||
Manifest() = delete;
|
||||
|
||||
Manifest(
|
||||
std::string const& serialized_,
|
||||
PublicKey const& masterKey_,
|
||||
std::optional<PublicKey> const& signingKey_,
|
||||
std::uint32_t seq,
|
||||
std::string const& domain_)
|
||||
: serialized(serialized_), masterKey(masterKey_), signingKey(signingKey_), sequence(seq), domain(domain_)
|
||||
{
|
||||
}
|
||||
|
||||
Manifest(Manifest const& other) = delete;
|
||||
Manifest&
|
||||
operator=(Manifest const& other) = delete;
|
||||
Manifest(Manifest&& other) = default;
|
||||
Manifest&
|
||||
operator=(Manifest&& other) = default;
|
||||
|
||||
/// Returns `true` if manifest signature is valid
|
||||
bool
|
||||
verify() const;
|
||||
|
||||
/// Returns hash of serialized manifest data
|
||||
uint256
|
||||
hash() const;
|
||||
|
||||
/// Returns `true` if manifest revokes master key
|
||||
// The maximum possible sequence number means that the master key has
|
||||
// been revoked
|
||||
static bool
|
||||
revoked(std::uint32_t sequence);
|
||||
|
||||
/// Returns `true` if manifest revokes master key
|
||||
bool
|
||||
revoked() const;
|
||||
|
||||
/// Returns manifest signature
|
||||
std::optional<Blob>
|
||||
getSignature() const;
|
||||
|
||||
/// Returns manifest master key signature
|
||||
Blob
|
||||
getMasterSignature() const;
|
||||
};
|
||||
|
||||
/** Format the specified manifest to a string for debugging purposes. */
|
||||
std::string
|
||||
to_string(Manifest const& m);
|
||||
|
||||
/** Constructs Manifest from serialized string
|
||||
|
||||
@param s Serialized manifest string
|
||||
|
||||
@return `std::nullopt` if string is invalid
|
||||
|
||||
@note This does not verify manifest signatures.
|
||||
`Manifest::verify` should be called after constructing manifest.
|
||||
*/
|
||||
/** @{ */
|
||||
std::optional<Manifest>
|
||||
deserializeManifest(Slice s, beast::Journal journal);
|
||||
|
||||
inline std::optional<Manifest>
|
||||
deserializeManifest(std::string const& s, beast::Journal journal = beast::Journal(beast::Journal::getNullSink()))
|
||||
{
|
||||
return deserializeManifest(makeSlice(s), journal);
|
||||
}
|
||||
|
||||
template <class T, class = std::enable_if_t<std::is_same<T, char>::value || std::is_same<T, unsigned char>::value>>
|
||||
std::optional<Manifest>
|
||||
deserializeManifest(std::vector<T> const& v, beast::Journal journal = beast::Journal(beast::Journal::getNullSink()))
|
||||
{
|
||||
return deserializeManifest(makeSlice(v), journal);
|
||||
}
|
||||
/** @} */
|
||||
|
||||
inline bool
|
||||
operator==(Manifest const& lhs, Manifest const& rhs)
|
||||
{
|
||||
// In theory, comparing the two serialized strings should be
|
||||
// sufficient.
|
||||
return lhs.sequence == rhs.sequence && lhs.masterKey == rhs.masterKey && lhs.signingKey == rhs.signingKey &&
|
||||
lhs.domain == rhs.domain && lhs.serialized == rhs.serialized;
|
||||
}
|
||||
|
||||
inline bool
|
||||
operator!=(Manifest const& lhs, Manifest const& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
struct ValidatorToken
|
||||
{
|
||||
std::string manifest;
|
||||
SecretKey validationSecret;
|
||||
};
|
||||
|
||||
std::optional<ValidatorToken>
|
||||
loadValidatorToken(
|
||||
std::vector<std::string> const& blob,
|
||||
beast::Journal journal = beast::Journal(beast::Journal::getNullSink()));
|
||||
|
||||
enum class ManifestDisposition {
|
||||
/// Manifest is valid
|
||||
accepted = 0,
|
||||
|
||||
/// Sequence is too old
|
||||
stale,
|
||||
|
||||
/// The master key is not acceptable to us
|
||||
badMasterKey,
|
||||
|
||||
/// The ephemeral key is not acceptable to us
|
||||
badEphemeralKey,
|
||||
|
||||
/// Timely, but invalid signature
|
||||
invalid
|
||||
};
|
||||
|
||||
inline std::string
|
||||
to_string(ManifestDisposition m)
|
||||
{
|
||||
switch (m)
|
||||
{
|
||||
case ManifestDisposition::accepted:
|
||||
return "accepted";
|
||||
case ManifestDisposition::stale:
|
||||
return "stale";
|
||||
case ManifestDisposition::badMasterKey:
|
||||
return "badMasterKey";
|
||||
case ManifestDisposition::badEphemeralKey:
|
||||
return "badEphemeralKey";
|
||||
case ManifestDisposition::invalid:
|
||||
return "invalid";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
class DatabaseCon;
|
||||
|
||||
/** Remembers manifests with the highest sequence number. */
|
||||
class ManifestCache
|
||||
{
|
||||
private:
|
||||
beast::Journal j_;
|
||||
std::shared_mutex mutable mutex_;
|
||||
|
||||
/** Active manifests stored by master public key. */
|
||||
hash_map<PublicKey, Manifest> map_;
|
||||
|
||||
/** Master public keys stored by current ephemeral public key. */
|
||||
hash_map<PublicKey, PublicKey> signingToMasterKeys_;
|
||||
|
||||
std::atomic<std::uint32_t> seq_{0};
|
||||
|
||||
public:
|
||||
explicit ManifestCache(beast::Journal j = beast::Journal(beast::Journal::getNullSink())) : j_(j)
|
||||
{
|
||||
}
|
||||
|
||||
/** A monotonically increasing number used to detect new manifests. */
|
||||
std::uint32_t
|
||||
sequence() const
|
||||
{
|
||||
return seq_.load();
|
||||
}
|
||||
|
||||
/** Returns master key's current signing key.
|
||||
|
||||
@param pk Master public key
|
||||
|
||||
@return pk if no known signing key from a manifest
|
||||
|
||||
@par Thread Safety
|
||||
|
||||
May be called concurrently
|
||||
*/
|
||||
std::optional<PublicKey>
|
||||
getSigningKey(PublicKey const& pk) const;
|
||||
|
||||
/** Returns ephemeral signing key's master public key.
|
||||
|
||||
@param pk Ephemeral signing public key
|
||||
|
||||
@return pk if signing key is not in a valid manifest
|
||||
|
||||
@par Thread Safety
|
||||
|
||||
May be called concurrently
|
||||
*/
|
||||
PublicKey
|
||||
getMasterKey(PublicKey const& pk) const;
|
||||
|
||||
/** Returns master key's current manifest sequence.
|
||||
|
||||
@return sequence corresponding to Master public key
|
||||
if configured or std::nullopt otherwise
|
||||
*/
|
||||
std::optional<std::uint32_t>
|
||||
getSequence(PublicKey const& pk) const;
|
||||
|
||||
/** Returns domain claimed by a given public key
|
||||
|
||||
@return domain corresponding to Master public key
|
||||
if present, otherwise std::nullopt
|
||||
*/
|
||||
std::optional<std::string>
|
||||
getDomain(PublicKey const& pk) const;
|
||||
|
||||
/** Returns manifest corresponding to a given public key
|
||||
|
||||
@return manifest corresponding to Master public key
|
||||
if present, otherwise std::nullopt
|
||||
*/
|
||||
std::optional<std::string>
|
||||
getManifest(PublicKey const& pk) const;
|
||||
|
||||
/** Returns `true` if master key has been revoked in a manifest.
|
||||
|
||||
@param pk Master public key
|
||||
|
||||
@par Thread Safety
|
||||
|
||||
May be called concurrently
|
||||
*/
|
||||
bool
|
||||
revoked(PublicKey const& pk) const;
|
||||
|
||||
/** Add manifest to cache.
|
||||
|
||||
@param m Manifest to add
|
||||
|
||||
@return `ManifestDisposition::accepted` if successful, or
|
||||
`stale` or `invalid` otherwise
|
||||
|
||||
@par Thread Safety
|
||||
|
||||
May be called concurrently
|
||||
*/
|
||||
ManifestDisposition
|
||||
applyManifest(Manifest m);
|
||||
|
||||
/** Populate manifest cache with manifests in database and config.
|
||||
|
||||
@param dbCon Database connection with dbTable
|
||||
|
||||
@param dbTable Database table
|
||||
|
||||
@param configManifest Base64 encoded manifest for local node's
|
||||
validator keys
|
||||
|
||||
@param configRevocation Base64 encoded validator key revocation
|
||||
from the config
|
||||
|
||||
@par Thread Safety
|
||||
|
||||
May be called concurrently
|
||||
*/
|
||||
bool
|
||||
load(
|
||||
DatabaseCon& dbCon,
|
||||
std::string const& dbTable,
|
||||
std::string const& configManifest,
|
||||
std::vector<std::string> const& configRevocation);
|
||||
|
||||
/** Populate manifest cache with manifests in database.
|
||||
|
||||
@param dbCon Database connection with dbTable
|
||||
|
||||
@param dbTable Database table
|
||||
|
||||
@par Thread Safety
|
||||
|
||||
May be called concurrently
|
||||
*/
|
||||
void
|
||||
load(DatabaseCon& dbCon, std::string const& dbTable);
|
||||
|
||||
/** Save cached manifests to database.
|
||||
|
||||
@param dbCon Database connection with `ValidatorManifests` table
|
||||
|
||||
@param isTrusted Function that returns true if manifest is trusted
|
||||
|
||||
@par Thread Safety
|
||||
|
||||
May be called concurrently
|
||||
*/
|
||||
void
|
||||
save(DatabaseCon& dbCon, std::string const& dbTable, std::function<bool(PublicKey const&)> const& isTrusted);
|
||||
|
||||
/** Invokes the callback once for every populated manifest.
|
||||
|
||||
@note Do not call ManifestCache member functions from within the
|
||||
callback. This can re-lock the mutex from the same thread, which is UB.
|
||||
@note Do not write ManifestCache member variables from within the
|
||||
callback. This can lead to data races.
|
||||
|
||||
@param f Function called for each manifest
|
||||
|
||||
@par Thread Safety
|
||||
|
||||
May be called concurrently
|
||||
*/
|
||||
template <class Function>
|
||||
void
|
||||
for_each_manifest(Function&& f) const
|
||||
{
|
||||
std::shared_lock lock{mutex_};
|
||||
for (auto const& [_, manifest] : map_)
|
||||
{
|
||||
(void)_;
|
||||
f(manifest);
|
||||
}
|
||||
}
|
||||
|
||||
/** Invokes the callback once for every populated manifest.
|
||||
|
||||
@note Do not call ManifestCache member functions from within the
|
||||
callback. This can re-lock the mutex from the same thread, which is UB.
|
||||
@note Do not write ManifestCache member variables from
|
||||
within the callback. This can lead to data races.
|
||||
|
||||
@param pf Pre-function called with the maximum number of times f will be
|
||||
called (useful for memory allocations)
|
||||
|
||||
@param f Function called for each manifest
|
||||
|
||||
@par Thread Safety
|
||||
|
||||
May be called concurrently
|
||||
*/
|
||||
template <class PreFun, class EachFun>
|
||||
void
|
||||
for_each_manifest(PreFun&& pf, EachFun&& f) const
|
||||
{
|
||||
std::shared_lock lock{mutex_};
|
||||
pf(map_.size());
|
||||
for (auto const& [_, manifest] : map_)
|
||||
{
|
||||
(void)_;
|
||||
f(manifest);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
250
include/xrpl/server/NetworkOPs.h
Normal file
250
include/xrpl/server/NetworkOPs.h
Normal file
@@ -0,0 +1,250 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/core/JobQueue.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/protocol/STValidation.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/messages.h>
|
||||
#include <xrpl/server/InfoSub.h>
|
||||
#include <xrpl/shamap/SHAMap.h>
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
// Operations that clients may wish to perform against the network
|
||||
// Master operational handler, server sequencer, network tracker
|
||||
|
||||
class Peer;
|
||||
class STTx;
|
||||
class ReadView;
|
||||
class LedgerMaster;
|
||||
class Transaction;
|
||||
class ValidatorKeys;
|
||||
class CanonicalTXSet;
|
||||
class RCLCxPeerPos;
|
||||
|
||||
// This is the primary interface into the "client" portion of the program.
|
||||
// Code that wants to do normal operations on the network such as
|
||||
// creating and monitoring accounts, creating transactions, and so on
|
||||
// should use this interface. The RPC code will primarily be a light wrapper
|
||||
// over this code.
|
||||
//
|
||||
// Eventually, it will check the node's operating mode (synced, unsynced,
|
||||
// etcetera) and defer to the correct means of processing. The current
|
||||
// code assumes this node is synced (and will continue to do so until
|
||||
// there's a functional network.
|
||||
//
|
||||
|
||||
/** Specifies the mode under which the server believes it's operating.
|
||||
|
||||
This has implications about how the server processes transactions and
|
||||
how it responds to requests (e.g. account balance request).
|
||||
|
||||
@note Other code relies on the numerical values of these constants; do
|
||||
not change them without verifying each use and ensuring that it is
|
||||
not a breaking change.
|
||||
*/
|
||||
enum class OperatingMode {
|
||||
DISCONNECTED = 0, //!< not ready to process requests
|
||||
CONNECTED = 1, //!< convinced we are talking to the network
|
||||
SYNCING = 2, //!< fallen slightly behind
|
||||
TRACKING = 3, //!< convinced we agree with the network
|
||||
FULL = 4 //!< we have the ledger and can even validate
|
||||
};
|
||||
|
||||
/** Provides server functionality for clients.
|
||||
|
||||
Clients include backend applications, local commands, and connected
|
||||
clients. This class acts as a proxy, fulfilling the command with local
|
||||
data if possible, or asking the network and returning the results if
|
||||
needed.
|
||||
|
||||
A backend application or local client can trust a local instance of
|
||||
rippled / NetworkOPs. However, client software connecting to non-local
|
||||
instances of rippled will need to be hardened to protect against hostile
|
||||
or unreliable servers.
|
||||
*/
|
||||
class NetworkOPs : public InfoSub::Source
|
||||
{
|
||||
public:
|
||||
using clock_type = beast::abstract_clock<std::chrono::steady_clock>;
|
||||
|
||||
enum class FailHard : unsigned char { no, yes };
|
||||
static inline FailHard
|
||||
doFailHard(bool noMeansDont)
|
||||
{
|
||||
return noMeansDont ? FailHard::yes : FailHard::no;
|
||||
}
|
||||
|
||||
public:
|
||||
~NetworkOPs() override = default;
|
||||
|
||||
virtual void
|
||||
stop() = 0;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// Network information
|
||||
//
|
||||
|
||||
virtual OperatingMode
|
||||
getOperatingMode() const = 0;
|
||||
virtual std::string
|
||||
strOperatingMode(OperatingMode const mode, bool const admin = false) const = 0;
|
||||
virtual std::string
|
||||
strOperatingMode(bool const admin = false) const = 0;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// Transaction processing
|
||||
//
|
||||
|
||||
// must complete immediately
|
||||
virtual void
|
||||
submitTransaction(std::shared_ptr<STTx const> const&) = 0;
|
||||
|
||||
/**
|
||||
* Process transactions as they arrive from the network or which are
|
||||
* submitted by clients. Process local transactions synchronously
|
||||
*
|
||||
* @param transaction Transaction object
|
||||
* @param bUnlimited Whether a privileged client connection submitted it.
|
||||
* @param bLocal Client submission.
|
||||
* @param failType fail_hard setting from transaction submission.
|
||||
*/
|
||||
virtual void
|
||||
processTransaction(std::shared_ptr<Transaction>& transaction, bool bUnlimited, bool bLocal, FailHard failType) = 0;
|
||||
|
||||
/**
|
||||
* Process a set of transactions synchronously, and ensuring that they are
|
||||
* processed in one batch.
|
||||
*
|
||||
* @param set Transaction object set
|
||||
*/
|
||||
virtual void
|
||||
processTransactionSet(CanonicalTXSet const& set) = 0;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// Owner functions
|
||||
//
|
||||
|
||||
virtual Json::Value
|
||||
getOwnerInfo(std::shared_ptr<ReadView const> lpLedger, AccountID const& account) = 0;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// Book functions
|
||||
//
|
||||
|
||||
virtual void
|
||||
getBookPage(
|
||||
std::shared_ptr<ReadView const>& lpLedger,
|
||||
Book const& book,
|
||||
AccountID const& uTakerID,
|
||||
bool const bProof,
|
||||
unsigned int iLimit,
|
||||
Json::Value const& jvMarker,
|
||||
Json::Value& jvResult) = 0;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
// ledger proposal/close functions
|
||||
virtual bool
|
||||
processTrustedProposal(RCLCxPeerPos peerPos) = 0;
|
||||
|
||||
virtual bool
|
||||
recvValidation(std::shared_ptr<STValidation> const& val, std::string const& source) = 0;
|
||||
|
||||
virtual void
|
||||
mapComplete(std::shared_ptr<SHAMap> const& map, bool fromAcquire) = 0;
|
||||
|
||||
// network state machine
|
||||
virtual bool
|
||||
beginConsensus(uint256 const& netLCL, std::unique_ptr<std::stringstream> const& clog) = 0;
|
||||
virtual void
|
||||
endConsensus(std::unique_ptr<std::stringstream> const& clog) = 0;
|
||||
virtual void
|
||||
setStandAlone() = 0;
|
||||
virtual void
|
||||
setStateTimer() = 0;
|
||||
|
||||
virtual void
|
||||
setNeedNetworkLedger() = 0;
|
||||
virtual void
|
||||
clearNeedNetworkLedger() = 0;
|
||||
virtual bool
|
||||
isNeedNetworkLedger() = 0;
|
||||
virtual bool
|
||||
isFull() = 0;
|
||||
virtual void
|
||||
setMode(OperatingMode om) = 0;
|
||||
virtual bool
|
||||
isBlocked() = 0;
|
||||
virtual bool
|
||||
isAmendmentBlocked() = 0;
|
||||
virtual void
|
||||
setAmendmentBlocked() = 0;
|
||||
virtual bool
|
||||
isAmendmentWarned() = 0;
|
||||
virtual void
|
||||
setAmendmentWarned() = 0;
|
||||
virtual void
|
||||
clearAmendmentWarned() = 0;
|
||||
virtual bool
|
||||
isUNLBlocked() = 0;
|
||||
virtual void
|
||||
setUNLBlocked() = 0;
|
||||
virtual void
|
||||
clearUNLBlocked() = 0;
|
||||
virtual void
|
||||
consensusViewChange() = 0;
|
||||
|
||||
virtual Json::Value
|
||||
getConsensusInfo() = 0;
|
||||
virtual Json::Value
|
||||
getServerInfo(bool human, bool admin, bool counters) = 0;
|
||||
virtual void
|
||||
clearLedgerFetch() = 0;
|
||||
virtual Json::Value
|
||||
getLedgerFetchInfo() = 0;
|
||||
|
||||
/** Accepts the current transaction tree, return the new ledger's sequence
|
||||
|
||||
This API is only used via RPC with the server in STANDALONE mode and
|
||||
performs a virtual consensus round, with all the transactions we are
|
||||
proposing being accepted.
|
||||
*/
|
||||
virtual std::uint32_t
|
||||
acceptLedger(std::optional<std::chrono::milliseconds> consensusDelay = std::nullopt) = 0;
|
||||
|
||||
virtual void
|
||||
reportFeeChange() = 0;
|
||||
|
||||
virtual void
|
||||
updateLocalTx(ReadView const& newValidLedger) = 0;
|
||||
virtual std::size_t
|
||||
getLocalTxCount() = 0;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// Monitoring: publisher side
|
||||
//
|
||||
virtual void
|
||||
pubLedger(std::shared_ptr<ReadView const> const& lpAccepted) = 0;
|
||||
virtual void
|
||||
pubProposedTransaction(
|
||||
std::shared_ptr<ReadView const> const& ledger,
|
||||
std::shared_ptr<STTx const> const& transaction,
|
||||
TER result) = 0;
|
||||
virtual void
|
||||
pubValidation(std::shared_ptr<STValidation> const& val) = 0;
|
||||
|
||||
virtual void
|
||||
stateAccounting(Json::Value& obj) = 0;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
71
include/xrpl/server/State.h
Normal file
71
include/xrpl/server/State.h
Normal file
@@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/protocol/Protocol.h>
|
||||
#include <xrpl/rdb/DatabaseCon.h>
|
||||
#include <xrpl/server/Manifest.h>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
struct SavedState
|
||||
{
|
||||
std::string writableDb;
|
||||
std::string archiveDb;
|
||||
LedgerIndex lastRotated;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief initStateDB Opens a session with the State database.
|
||||
* @param session Provides a session with the database.
|
||||
* @param config Path to the database and other opening parameters.
|
||||
* @param dbName Name of the database.
|
||||
*/
|
||||
void
|
||||
initStateDB(soci::session& session, BasicConfig const& config, std::string const& dbName);
|
||||
|
||||
/**
|
||||
* @brief getCanDelete Returns the ledger sequence which can be deleted.
|
||||
* @param session Session with the database.
|
||||
* @return Ledger sequence.
|
||||
*/
|
||||
LedgerIndex
|
||||
getCanDelete(soci::session& session);
|
||||
|
||||
/**
|
||||
* @brief setCanDelete Updates the ledger sequence which can be deleted.
|
||||
* @param session Session with the database.
|
||||
* @param canDelete Ledger sequence to save.
|
||||
* @return Previous value of the ledger sequence which can be deleted.
|
||||
*/
|
||||
LedgerIndex
|
||||
setCanDelete(soci::session& session, LedgerIndex canDelete);
|
||||
|
||||
/**
|
||||
* @brief getSavedState Returns the saved state.
|
||||
* @param session Session with the database.
|
||||
* @return The SavedState structure which contains the names of the writable
|
||||
* database, the archive database and the last rotated ledger sequence.
|
||||
*/
|
||||
SavedState
|
||||
getSavedState(soci::session& session);
|
||||
|
||||
/**
|
||||
* @brief setSavedState Saves the given state.
|
||||
* @param session Session with the database.
|
||||
* @param state The SavedState structure which contains the names of the
|
||||
* writable database, the archive database and the last rotated ledger
|
||||
* sequence.
|
||||
*/
|
||||
void
|
||||
setSavedState(soci::session& session, SavedState const& state);
|
||||
|
||||
/**
|
||||
* @brief setLastRotated Updates the last rotated ledger sequence.
|
||||
* @param session Session with the database.
|
||||
* @param seq New value of the last rotated ledger sequence.
|
||||
*/
|
||||
void
|
||||
setLastRotated(soci::session& session, LedgerIndex seq);
|
||||
|
||||
} // namespace xrpl
|
||||
16
include/xrpl/server/Vacuum.h
Normal file
16
include/xrpl/server/Vacuum.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/rdb/DatabaseCon.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* @brief doVacuumDB Creates, initialises, and performs cleanup on a database.
|
||||
* @param setup Path to the database and other opening parameters.
|
||||
* @param j Journal.
|
||||
* @return True if the vacuum process completed successfully.
|
||||
*/
|
||||
bool
|
||||
doVacuumDB(DatabaseCon::Setup const& setup, beast::Journal j);
|
||||
|
||||
} // namespace xrpl
|
||||
145
include/xrpl/server/Wallet.h
Normal file
145
include/xrpl/server/Wallet.h
Normal file
@@ -0,0 +1,145 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/core/PeerReservationTable.h>
|
||||
#include <xrpl/rdb/DatabaseCon.h>
|
||||
#include <xrpl/server/Manifest.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* @brief makeWalletDB Opens the wallet database and returns it.
|
||||
* @param setup Path to the database and other opening parameters.
|
||||
* @param j Journal.
|
||||
* @return Unique pointer to the database descriptor.
|
||||
*/
|
||||
std::unique_ptr<DatabaseCon>
|
||||
makeWalletDB(DatabaseCon::Setup const& setup, beast::Journal j);
|
||||
|
||||
/**
|
||||
* @brief makeTestWalletDB Opens a test wallet database with an arbitrary name.
|
||||
* @param setup Path to the database and other opening parameters.
|
||||
* @param dbname Name of the database.
|
||||
* @param j Journal.
|
||||
* @return Unique pointer to the database descriptor.
|
||||
*/
|
||||
std::unique_ptr<DatabaseCon>
|
||||
makeTestWalletDB(DatabaseCon::Setup const& setup, std::string const& dbname, beast::Journal j);
|
||||
|
||||
/**
|
||||
* @brief getManifests Loads a manifest from the wallet database and stores it
|
||||
* in the cache.
|
||||
* @param session Session with the database.
|
||||
* @param dbTable Name of the database table from which the manifest will be
|
||||
* extracted.
|
||||
* @param mCache Cache for storing the manifest.
|
||||
* @param j Journal.
|
||||
*/
|
||||
void
|
||||
getManifests(soci::session& session, std::string const& dbTable, ManifestCache& mCache, beast::Journal j);
|
||||
|
||||
/**
|
||||
* @brief saveManifests Saves all given manifests to the database.
|
||||
* @param session Session with the database.
|
||||
* @param dbTable Name of the database table that will store the manifest.
|
||||
* @param isTrusted Callback that returns true if the key is trusted.
|
||||
* @param map Maps public keys to manifests.
|
||||
* @param j Journal.
|
||||
*/
|
||||
void
|
||||
saveManifests(
|
||||
soci::session& session,
|
||||
std::string const& dbTable,
|
||||
std::function<bool(PublicKey const&)> const& isTrusted,
|
||||
hash_map<PublicKey, Manifest> const& map,
|
||||
beast::Journal j);
|
||||
|
||||
/**
|
||||
* @brief addValidatorManifest Saves the manifest of a validator to the
|
||||
* database.
|
||||
* @param session Session with the database.
|
||||
* @param serialized Manifest of the validator in raw format.
|
||||
*/
|
||||
void
|
||||
addValidatorManifest(soci::session& session, std::string const& serialized);
|
||||
|
||||
/** Delete any saved public/private key associated with this node. */
|
||||
void
|
||||
clearNodeIdentity(soci::session& session);
|
||||
|
||||
/** Returns a stable public and private key for this node.
|
||||
|
||||
The node's public identity is defined by a secp256k1 keypair
|
||||
that is (normally) randomly generated. This function will
|
||||
return such a keypair, securely generating one if needed.
|
||||
|
||||
@param session Session with the database.
|
||||
|
||||
@return Pair of public and private secp256k1 keys.
|
||||
*/
|
||||
std::pair<PublicKey, SecretKey>
|
||||
getNodeIdentity(soci::session& session);
|
||||
|
||||
/**
|
||||
* @brief getPeerReservationTable Returns the peer reservation table.
|
||||
* @param session Session with the database.
|
||||
* @param j Journal.
|
||||
* @return Peer reservation hash table.
|
||||
*/
|
||||
std::unordered_set<PeerReservation, beast::uhash<>, KeyEqual>
|
||||
getPeerReservationTable(soci::session& session, beast::Journal j);
|
||||
|
||||
/**
|
||||
* @brief insertPeerReservation Adds an entry to the peer reservation table.
|
||||
* @param session Session with the database.
|
||||
* @param nodeId Public key of the node.
|
||||
* @param description Description of the node.
|
||||
*/
|
||||
void
|
||||
insertPeerReservation(soci::session& session, PublicKey const& nodeId, std::string const& description);
|
||||
|
||||
/**
|
||||
* @brief deletePeerReservation Deletes an entry from the peer reservation
|
||||
* table.
|
||||
* @param session Session with the database.
|
||||
* @param nodeId Public key of the node to remove.
|
||||
*/
|
||||
void
|
||||
deletePeerReservation(soci::session& session, PublicKey const& nodeId);
|
||||
|
||||
/**
|
||||
* @brief createFeatureVotes Creates the FeatureVote table if it does not exist.
|
||||
* @param session Session with the wallet database.
|
||||
* @return true if the table already exists
|
||||
*/
|
||||
bool
|
||||
createFeatureVotes(soci::session& session);
|
||||
|
||||
// For historical reasons the up-vote and down-vote integer representations
|
||||
// are unintuitive.
|
||||
enum class AmendmentVote : int { obsolete = -1, up = 0, down = 1 };
|
||||
|
||||
/**
|
||||
* @brief readAmendments Reads all amendments from the FeatureVotes table.
|
||||
* @param session Session with the wallet database.
|
||||
* @param callback Callback called for each amendment with its hash, name and
|
||||
* optionally a flag denoting whether the amendment should be vetoed.
|
||||
*/
|
||||
void
|
||||
readAmendments(
|
||||
soci::session& session,
|
||||
std::function<void(
|
||||
boost::optional<std::string> amendment_hash,
|
||||
boost::optional<std::string> amendment_name,
|
||||
boost::optional<AmendmentVote> vote)> const& callback);
|
||||
|
||||
/**
|
||||
* @brief voteAmendment Set the veto value for a particular amendment.
|
||||
* @param session Session with the wallet database.
|
||||
* @param amendment Hash of the amendment.
|
||||
* @param name Name of the amendment.
|
||||
* @param vote Whether to vote in favor of this amendment.
|
||||
*/
|
||||
void
|
||||
voteAmendment(soci::session& session, uint256 const& amendment, std::string const& name, AmendmentVote vote);
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -218,7 +218,7 @@ void
|
||||
BaseHTTPPeer<Handler, Impl>::close()
|
||||
{
|
||||
if (!strand_.running_in_this_thread())
|
||||
return post(strand_, std::bind((void(BaseHTTPPeer::*)(void)) & BaseHTTPPeer::close, impl().shared_from_this()));
|
||||
return post(strand_, std::bind((void (BaseHTTPPeer::*)(void))&BaseHTTPPeer::close, impl().shared_from_this()));
|
||||
boost::beast::get_lowest_layer(impl().stream_).close();
|
||||
}
|
||||
|
||||
@@ -436,7 +436,7 @@ BaseHTTPPeer<Handler, Impl>::close(bool graceful)
|
||||
return post(
|
||||
strand_,
|
||||
std::bind(
|
||||
(void(BaseHTTPPeer::*)(bool)) & BaseHTTPPeer<Handler, Impl>::close,
|
||||
(void (BaseHTTPPeer::*)(bool))&BaseHTTPPeer<Handler, Impl>::close,
|
||||
impl().shared_from_this(),
|
||||
graceful));
|
||||
|
||||
|
||||
@@ -178,8 +178,9 @@ BaseWSPeer<Handler, Impl>::run()
|
||||
impl().ws_.control_callback(control_callback_);
|
||||
start_timer();
|
||||
close_on_timer_ = true;
|
||||
impl().ws_.set_option(boost::beast::websocket::stream_base::decorator(
|
||||
[](auto& res) { res.set(boost::beast::http::field::server, BuildInfo::getFullVersionString()); }));
|
||||
impl().ws_.set_option(boost::beast::websocket::stream_base::decorator([](auto& res) {
|
||||
res.set(boost::beast::http::field::server, BuildInfo::getFullVersionString());
|
||||
}));
|
||||
impl().ws_.async_accept(
|
||||
request_,
|
||||
bind_executor(
|
||||
|
||||
129
include/xrpl/tx/ApplyContext.h
Normal file
129
include/xrpl/tx/ApplyContext.h
Normal file
@@ -0,0 +1,129 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ApplyViewImpl.h>
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/** State information when applying a tx. */
|
||||
class ApplyContext
|
||||
{
|
||||
public:
|
||||
explicit ApplyContext(
|
||||
ServiceRegistry& registry,
|
||||
OpenView& base,
|
||||
std::optional<uint256 const> const& parentBatchId,
|
||||
STTx const& tx,
|
||||
TER preclaimResult,
|
||||
XRPAmount baseFee,
|
||||
ApplyFlags flags,
|
||||
beast::Journal journal = beast::Journal{beast::Journal::getNullSink()});
|
||||
|
||||
explicit ApplyContext(
|
||||
ServiceRegistry& registry,
|
||||
OpenView& base,
|
||||
STTx const& tx,
|
||||
TER preclaimResult,
|
||||
XRPAmount baseFee,
|
||||
ApplyFlags flags,
|
||||
beast::Journal journal = beast::Journal{beast::Journal::getNullSink()})
|
||||
: ApplyContext(registry, base, std::nullopt, tx, preclaimResult, baseFee, flags, journal)
|
||||
{
|
||||
XRPL_ASSERT((flags & tapBATCH) == 0, "Batch apply flag should not be set");
|
||||
}
|
||||
|
||||
ServiceRegistry& registry;
|
||||
STTx const& tx;
|
||||
TER const preclaimResult;
|
||||
XRPAmount const baseFee;
|
||||
beast::Journal const journal;
|
||||
|
||||
ApplyView&
|
||||
view()
|
||||
{
|
||||
return *view_;
|
||||
}
|
||||
|
||||
ApplyView const&
|
||||
view() const
|
||||
{
|
||||
return *view_;
|
||||
}
|
||||
|
||||
// VFALCO Unfortunately this is necessary
|
||||
RawView&
|
||||
rawView()
|
||||
{
|
||||
return *view_;
|
||||
}
|
||||
|
||||
ApplyFlags const&
|
||||
flags() const
|
||||
{
|
||||
return flags_;
|
||||
}
|
||||
|
||||
/** Sets the DeliveredAmount field in the metadata */
|
||||
void
|
||||
deliver(STAmount const& amount)
|
||||
{
|
||||
view_->deliver(amount);
|
||||
}
|
||||
|
||||
/** Discard changes and start fresh. */
|
||||
void
|
||||
discard();
|
||||
|
||||
/** Apply the transaction result to the base. */
|
||||
std::optional<TxMeta> apply(TER);
|
||||
|
||||
/** Get the number of unapplied changes. */
|
||||
std::size_t
|
||||
size();
|
||||
|
||||
/** Visit unapplied changes. */
|
||||
void
|
||||
visit(
|
||||
std::function<void(
|
||||
uint256 const& key,
|
||||
bool isDelete,
|
||||
std::shared_ptr<SLE const> const& before,
|
||||
std::shared_ptr<SLE const> const& after)> const& func);
|
||||
|
||||
void
|
||||
destroyXRP(XRPAmount const& fee)
|
||||
{
|
||||
view_->rawDestroyXRP(fee);
|
||||
}
|
||||
|
||||
/** Applies all invariant checkers one by one.
|
||||
|
||||
@param result the result generated by processing this transaction.
|
||||
@param fee the fee charged for this transaction
|
||||
@return the result code that should be returned for this transaction.
|
||||
*/
|
||||
TER
|
||||
checkInvariants(TER const result, XRPAmount const fee);
|
||||
|
||||
private:
|
||||
TER
|
||||
failInvariantCheck(TER const result);
|
||||
|
||||
template <std::size_t... Is>
|
||||
TER
|
||||
checkInvariantsHelper(TER const result, XRPAmount const fee, std::index_sequence<Is...>);
|
||||
|
||||
OpenView& base_;
|
||||
ApplyFlags flags_;
|
||||
std::optional<ApplyViewImpl> view_;
|
||||
|
||||
// The ID of the batch transaction we are executing under, if seated.
|
||||
std::optional<uint256 const> parentBatchId_;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
723
include/xrpl/tx/InvariantCheck.h
Normal file
723
include/xrpl/tx/InvariantCheck.h
Normal file
@@ -0,0 +1,723 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Number.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/protocol/MPTIssue.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <tuple>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class ReadView;
|
||||
|
||||
#if GENERATING_DOCS
|
||||
/**
|
||||
* @brief Prototype for invariant check implementations.
|
||||
*
|
||||
* __THIS CLASS DOES NOT EXIST__ - or rather it exists in documentation only to
|
||||
* communicate the interface required of any invariant checker. Any invariant
|
||||
* check implementation should implement the public methods documented here.
|
||||
*
|
||||
*/
|
||||
class InvariantChecker_PROTOTYPE
|
||||
{
|
||||
public:
|
||||
explicit InvariantChecker_PROTOTYPE() = default;
|
||||
|
||||
/**
|
||||
* @brief called for each ledger entry in the current transaction.
|
||||
*
|
||||
* @param isDelete true if the SLE is being deleted
|
||||
* @param before ledger entry before modification by the transaction
|
||||
* @param after ledger entry after modification by the transaction
|
||||
*/
|
||||
void
|
||||
visitEntry(bool isDelete, std::shared_ptr<SLE const> const& before, std::shared_ptr<SLE const> const& after);
|
||||
|
||||
/**
|
||||
* @brief called after all ledger entries have been visited to determine
|
||||
* the final status of the check
|
||||
*
|
||||
* @param tx the transaction being applied
|
||||
* @param tec the current TER result of the transaction
|
||||
* @param fee the fee actually charged for this transaction
|
||||
* @param view a ReadView of the ledger being modified
|
||||
* @param j journal for logging
|
||||
*
|
||||
* @return true if check passes, false if it fails
|
||||
*/
|
||||
bool
|
||||
finalize(STTx const& tx, TER const tec, XRPAmount const fee, ReadView const& view, beast::Journal const& j);
|
||||
};
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Invariant: We should never charge a transaction a negative fee or a
|
||||
* fee that is larger than what the transaction itself specifies.
|
||||
*
|
||||
* We can, in some circumstances, charge less.
|
||||
*/
|
||||
class TransactionFeeCheck
|
||||
{
|
||||
public:
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Invariant: A transaction must not create XRP and should only destroy
|
||||
* the XRP fee.
|
||||
*
|
||||
* We iterate through all account roots, payment channels and escrow entries
|
||||
* that were modified and calculate the net change in XRP caused by the
|
||||
* transactions.
|
||||
*/
|
||||
class XRPNotCreated
|
||||
{
|
||||
std::int64_t drops_ = 0;
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Invariant: we cannot remove an account ledger entry
|
||||
*
|
||||
* We iterate all account roots that were modified, and ensure that any that
|
||||
* were present before the transaction was applied continue to be present
|
||||
* afterwards unless they were explicitly deleted by a successful
|
||||
* AccountDelete transaction.
|
||||
*/
|
||||
class AccountRootsNotDeleted
|
||||
{
|
||||
std::uint32_t accountsDeleted_ = 0;
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Invariant: a deleted account must not have any objects left
|
||||
*
|
||||
* We iterate all deleted account roots, and ensure that there are no
|
||||
* objects left that are directly accessible with that account's ID.
|
||||
*
|
||||
* There should only be one deleted account, but that's checked by
|
||||
* AccountRootsNotDeleted. This invariant will handle multiple deleted account
|
||||
* roots without a problem.
|
||||
*/
|
||||
class AccountRootsDeletedClean
|
||||
{
|
||||
// Pair is <before, after>. Before is used for most of the checks, so that
|
||||
// if, for example, an object ID field is cleared, but the object is not
|
||||
// deleted, it can still be found. After is used specifically for any checks
|
||||
// that are expected as part of the deletion, such as zeroing out the
|
||||
// balance.
|
||||
std::vector<std::pair<std::shared_ptr<SLE const>, std::shared_ptr<SLE const>>> accountsDeleted_;
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Invariant: An account XRP balance must be in XRP and take a value
|
||||
* between 0 and INITIAL_XRP drops, inclusive.
|
||||
*
|
||||
* We iterate all account roots modified by the transaction and ensure that
|
||||
* their XRP balances are reasonable.
|
||||
*/
|
||||
class XRPBalanceChecks
|
||||
{
|
||||
bool bad_ = false;
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Invariant: corresponding modified ledger entries should match in type
|
||||
* and added entries should be a valid type.
|
||||
*/
|
||||
class LedgerEntryTypesMatch
|
||||
{
|
||||
bool typeMismatch_ = false;
|
||||
bool invalidTypeAdded_ = false;
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Invariant: Trust lines using XRP are not allowed.
|
||||
*
|
||||
* We iterate all the trust lines created by this transaction and ensure
|
||||
* that they are against a valid issuer.
|
||||
*/
|
||||
class NoXRPTrustLines
|
||||
{
|
||||
bool xrpTrustLine_ = false;
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Invariant: Trust lines with deep freeze flag are not allowed if normal
|
||||
* freeze flag is not set.
|
||||
*
|
||||
* We iterate all the trust lines created by this transaction and ensure
|
||||
* that they don't have deep freeze flag set without normal freeze flag set.
|
||||
*/
|
||||
class NoDeepFreezeTrustLinesWithoutFreeze
|
||||
{
|
||||
bool deepFreezeWithoutFreeze_ = false;
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Invariant: frozen trust line balance change is not allowed.
|
||||
*
|
||||
* We iterate all affected trust lines and ensure that they don't have
|
||||
* unexpected change of balance if they're frozen.
|
||||
*/
|
||||
class TransfersNotFrozen
|
||||
{
|
||||
struct BalanceChange
|
||||
{
|
||||
std::shared_ptr<SLE const> const line;
|
||||
int const balanceChangeSign;
|
||||
};
|
||||
|
||||
struct IssuerChanges
|
||||
{
|
||||
std::vector<BalanceChange> senders;
|
||||
std::vector<BalanceChange> receivers;
|
||||
};
|
||||
|
||||
using ByIssuer = std::map<Issue, IssuerChanges>;
|
||||
ByIssuer balanceChanges_;
|
||||
|
||||
std::map<AccountID, std::shared_ptr<SLE const> const> possibleIssuers_;
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
|
||||
private:
|
||||
bool
|
||||
isValidEntry(std::shared_ptr<SLE const> const& before, std::shared_ptr<SLE const> const& after);
|
||||
|
||||
STAmount
|
||||
calculateBalanceChange(
|
||||
std::shared_ptr<SLE const> const& before,
|
||||
std::shared_ptr<SLE const> const& after,
|
||||
bool isDelete);
|
||||
|
||||
void
|
||||
recordBalance(Issue const& issue, BalanceChange change);
|
||||
|
||||
void
|
||||
recordBalanceChanges(std::shared_ptr<SLE const> const& after, STAmount const& balanceChange);
|
||||
|
||||
std::shared_ptr<SLE const>
|
||||
findIssuer(AccountID const& issuerID, ReadView const& view);
|
||||
|
||||
bool
|
||||
validateIssuerChanges(
|
||||
std::shared_ptr<SLE const> const& issuer,
|
||||
IssuerChanges const& changes,
|
||||
STTx const& tx,
|
||||
beast::Journal const& j,
|
||||
bool enforce);
|
||||
|
||||
bool
|
||||
validateFrozenState(
|
||||
BalanceChange const& change,
|
||||
bool high,
|
||||
STTx const& tx,
|
||||
beast::Journal const& j,
|
||||
bool enforce,
|
||||
bool globalFreeze);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Invariant: offers should be for non-negative amounts and must not
|
||||
* be XRP to XRP.
|
||||
*
|
||||
* Examine all offers modified by the transaction and ensure that there are
|
||||
* no offers which contain negative amounts or which exchange XRP for XRP.
|
||||
*/
|
||||
class NoBadOffers
|
||||
{
|
||||
bool bad_ = false;
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Invariant: an escrow entry must take a value between 0 and
|
||||
* INITIAL_XRP drops exclusive.
|
||||
*/
|
||||
class NoZeroEscrow
|
||||
{
|
||||
bool bad_ = false;
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Invariant: a new account root must be the consequence of a payment,
|
||||
* must have the right starting sequence, and the payment
|
||||
* may not create more than one new account root.
|
||||
*/
|
||||
class ValidNewAccountRoot
|
||||
{
|
||||
std::uint32_t accountsCreated_ = 0;
|
||||
std::uint32_t accountSeq_ = 0;
|
||||
bool pseudoAccount_ = false;
|
||||
std::uint32_t flags_ = 0;
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Invariant: Validates several invariants for NFToken pages.
|
||||
*
|
||||
* The following checks are made:
|
||||
* - The page is correctly associated with the owner.
|
||||
* - The page is correctly ordered between the next and previous links.
|
||||
* - The page contains at least one and no more than 32 NFTokens.
|
||||
* - The NFTokens on this page do not belong on a lower or higher page.
|
||||
* - The NFTokens are correctly sorted on the page.
|
||||
* - Each URI, if present, is not empty.
|
||||
*/
|
||||
class ValidNFTokenPage
|
||||
{
|
||||
bool badEntry_ = false;
|
||||
bool badLink_ = false;
|
||||
bool badSort_ = false;
|
||||
bool badURI_ = false;
|
||||
bool invalidSize_ = false;
|
||||
bool deletedFinalPage_ = false;
|
||||
bool deletedLink_ = false;
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Invariant: Validates counts of NFTokens after all transaction types.
|
||||
*
|
||||
* The following checks are made:
|
||||
* - The number of minted or burned NFTokens can only be changed by
|
||||
* NFTokenMint or NFTokenBurn transactions.
|
||||
* - A successful NFTokenMint must increase the number of NFTokens.
|
||||
* - A failed NFTokenMint must not change the number of minted NFTokens.
|
||||
* - An NFTokenMint transaction cannot change the number of burned NFTokens.
|
||||
* - A successful NFTokenBurn must increase the number of burned NFTokens.
|
||||
* - A failed NFTokenBurn must not change the number of burned NFTokens.
|
||||
* - An NFTokenBurn transaction cannot change the number of minted NFTokens.
|
||||
*/
|
||||
class NFTokenCountTracking
|
||||
{
|
||||
std::uint32_t beforeMintedTotal = 0;
|
||||
std::uint32_t beforeBurnedTotal = 0;
|
||||
std::uint32_t afterMintedTotal = 0;
|
||||
std::uint32_t afterBurnedTotal = 0;
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Invariant: Token holder's trustline balance cannot be negative after
|
||||
* Clawback.
|
||||
*
|
||||
* We iterate all the trust lines affected by this transaction and ensure
|
||||
* that no more than one trustline is modified, and also holder's balance is
|
||||
* non-negative.
|
||||
*/
|
||||
class ValidClawback
|
||||
{
|
||||
std::uint32_t trustlinesChanged = 0;
|
||||
std::uint32_t mptokensChanged = 0;
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
};
|
||||
|
||||
class ValidMPTIssuance
|
||||
{
|
||||
std::uint32_t mptIssuancesCreated_ = 0;
|
||||
std::uint32_t mptIssuancesDeleted_ = 0;
|
||||
|
||||
std::uint32_t mptokensCreated_ = 0;
|
||||
std::uint32_t mptokensDeleted_ = 0;
|
||||
// non-MPT transactions may attempt to create
|
||||
// MPToken by an issuer
|
||||
bool mptCreatedByIssuer_ = false;
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Invariants: Permissioned Domains must have some rules and
|
||||
* AcceptedCredentials must have length between 1 and 10 inclusive.
|
||||
*
|
||||
* Since only permissions constitute rules, an empty credentials list
|
||||
* means that there are no rules and the invariant is violated.
|
||||
*
|
||||
* Credentials must be sorted and no duplicates allowed
|
||||
*
|
||||
*/
|
||||
class ValidPermissionedDomain
|
||||
{
|
||||
struct SleStatus
|
||||
{
|
||||
std::size_t credentialsSize_{0};
|
||||
bool isSorted_ = false;
|
||||
bool isUnique_ = false;
|
||||
bool isDelete_ = false;
|
||||
};
|
||||
std::vector<SleStatus> sleStatus_;
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Invariants: Pseudo-accounts have valid and consistent properties
|
||||
*
|
||||
* Pseudo-accounts have certain properties, and some of those properties are
|
||||
* unique to pseudo-accounts. Check that all pseudo-accounts are following the
|
||||
* rules, and that only pseudo-accounts look like pseudo-accounts.
|
||||
*
|
||||
*/
|
||||
class ValidPseudoAccounts
|
||||
{
|
||||
std::vector<std::string> errors_;
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
};
|
||||
|
||||
class ValidPermissionedDEX
|
||||
{
|
||||
bool regularOffers_ = false;
|
||||
bool badHybrids_ = false;
|
||||
hash_set<uint256> domains_;
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
};
|
||||
|
||||
class ValidAMM
|
||||
{
|
||||
std::optional<AccountID> ammAccount_;
|
||||
std::optional<STAmount> lptAMMBalanceAfter_;
|
||||
std::optional<STAmount> lptAMMBalanceBefore_;
|
||||
bool ammPoolChanged_;
|
||||
|
||||
public:
|
||||
enum class ZeroAllowed : bool { No = false, Yes = true };
|
||||
|
||||
ValidAMM() : ammPoolChanged_{false}
|
||||
{
|
||||
}
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
|
||||
private:
|
||||
bool
|
||||
finalizeBid(bool enforce, beast::Journal const&) const;
|
||||
bool
|
||||
finalizeVote(bool enforce, beast::Journal const&) const;
|
||||
bool
|
||||
finalizeCreate(STTx const&, ReadView const&, bool enforce, beast::Journal const&) const;
|
||||
bool
|
||||
finalizeDelete(bool enforce, TER res, beast::Journal const&) const;
|
||||
bool
|
||||
finalizeDeposit(STTx const&, ReadView const&, bool enforce, beast::Journal const&) const;
|
||||
// Includes clawback
|
||||
bool
|
||||
finalizeWithdraw(STTx const&, ReadView const&, bool enforce, beast::Journal const&) const;
|
||||
bool
|
||||
finalizeDEX(bool enforce, beast::Journal const&) const;
|
||||
bool
|
||||
generalInvariant(STTx const&, ReadView const&, ZeroAllowed zeroAllowed, beast::Journal const&) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Invariants: Some fields are unmodifiable
|
||||
*
|
||||
* Check that any fields specified as unmodifiable are not modified when the
|
||||
* object is modified. Creation and deletion are ignored.
|
||||
*
|
||||
*/
|
||||
class NoModifiedUnmodifiableFields
|
||||
{
|
||||
// Pair is <before, after>.
|
||||
std::set<std::pair<SLE::const_pointer, SLE::const_pointer>> changedEntries_;
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Invariants: Loan brokers are internally consistent
|
||||
*
|
||||
* 1. If `LoanBroker.OwnerCount = 0` the `DirectoryNode` will have at most one
|
||||
* node (the root), which will only hold entries for `RippleState` or
|
||||
* `MPToken` objects.
|
||||
*
|
||||
*/
|
||||
class ValidLoanBroker
|
||||
{
|
||||
// Not all of these elements will necessarily be populated. Remaining items
|
||||
// will be looked up as needed.
|
||||
struct BrokerInfo
|
||||
{
|
||||
SLE::const_pointer brokerBefore = nullptr;
|
||||
// After is used for most of the checks, except
|
||||
// those that check changed values.
|
||||
SLE::const_pointer brokerAfter = nullptr;
|
||||
};
|
||||
// Collect all the LoanBrokers found directly or indirectly through
|
||||
// pseudo-accounts. Key is the brokerID / index. It will be used to find the
|
||||
// LoanBroker object if brokerBefore and brokerAfter are nullptr
|
||||
std::map<uint256, BrokerInfo> brokers_;
|
||||
// Collect all the modified trust lines. Their high and low accounts will be
|
||||
// loaded to look for LoanBroker pseudo-accounts.
|
||||
std::vector<SLE::const_pointer> lines_;
|
||||
// Collect all the modified MPTokens. Their accounts will be loaded to look
|
||||
// for LoanBroker pseudo-accounts.
|
||||
std::vector<SLE::const_pointer> mpts_;
|
||||
|
||||
bool
|
||||
goodZeroDirectory(ReadView const& view, SLE::const_ref dir, beast::Journal const& j) const;
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Invariants: Loans are internally consistent
|
||||
*
|
||||
* 1. If `Loan.PaymentRemaining = 0` then `Loan.PrincipalOutstanding = 0`
|
||||
*
|
||||
*/
|
||||
class ValidLoan
|
||||
{
|
||||
// Pair is <before, after>. After is used for most of the checks, except
|
||||
// those that check changed values.
|
||||
std::vector<std::pair<SLE::const_pointer, SLE::const_pointer>> loans_;
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
};
|
||||
|
||||
/*
|
||||
* @brief Invariants: Vault object and MPTokenIssuance for vault shares
|
||||
*
|
||||
* - vault deleted and vault created is empty
|
||||
* - vault created must be linked to pseudo-account for shares and assets
|
||||
* - vault must have MPTokenIssuance for shares
|
||||
* - vault without shares outstanding must have no shares
|
||||
* - loss unrealized does not exceed the difference between assets total and
|
||||
* assets available
|
||||
* - assets available do not exceed assets total
|
||||
* - vault deposit increases assets and share issuance, and adds to:
|
||||
* total assets, assets available, shares outstanding
|
||||
* - vault withdrawal and clawback reduce assets and share issuance, and
|
||||
* subtracts from: total assets, assets available, shares outstanding
|
||||
* - vault set must not alter the vault assets or shares balance
|
||||
* - no vault transaction can change loss unrealized (it's updated by loan
|
||||
* transactions)
|
||||
*
|
||||
*/
|
||||
class ValidVault
|
||||
{
|
||||
Number static constexpr zero{};
|
||||
|
||||
struct Vault final
|
||||
{
|
||||
uint256 key = beast::zero;
|
||||
Asset asset = {};
|
||||
AccountID pseudoId = {};
|
||||
AccountID owner = {};
|
||||
uint192 shareMPTID = beast::zero;
|
||||
Number assetsTotal = 0;
|
||||
Number assetsAvailable = 0;
|
||||
Number assetsMaximum = 0;
|
||||
Number lossUnrealized = 0;
|
||||
|
||||
Vault static make(SLE const&);
|
||||
};
|
||||
|
||||
struct Shares final
|
||||
{
|
||||
MPTIssue share = {};
|
||||
std::uint64_t sharesTotal = 0;
|
||||
std::uint64_t sharesMaximum = 0;
|
||||
|
||||
Shares static make(SLE const&);
|
||||
};
|
||||
|
||||
std::vector<Vault> afterVault_ = {};
|
||||
std::vector<Shares> afterMPTs_ = {};
|
||||
std::vector<Vault> beforeVault_ = {};
|
||||
std::vector<Shares> beforeMPTs_ = {};
|
||||
std::unordered_map<uint256, Number> deltas_ = {};
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(bool, std::shared_ptr<SLE const> const&, std::shared_ptr<SLE const> const&);
|
||||
|
||||
bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
};
|
||||
|
||||
// additional invariant checks can be declared above and then added to this
|
||||
// tuple
|
||||
using InvariantChecks = std::tuple<
|
||||
TransactionFeeCheck,
|
||||
AccountRootsNotDeleted,
|
||||
AccountRootsDeletedClean,
|
||||
LedgerEntryTypesMatch,
|
||||
XRPBalanceChecks,
|
||||
XRPNotCreated,
|
||||
NoXRPTrustLines,
|
||||
NoDeepFreezeTrustLinesWithoutFreeze,
|
||||
TransfersNotFrozen,
|
||||
NoBadOffers,
|
||||
NoZeroEscrow,
|
||||
ValidNewAccountRoot,
|
||||
ValidNFTokenPage,
|
||||
NFTokenCountTracking,
|
||||
ValidClawback,
|
||||
ValidMPTIssuance,
|
||||
ValidPermissionedDomain,
|
||||
ValidPermissionedDEX,
|
||||
ValidAMM,
|
||||
NoModifiedUnmodifiableFields,
|
||||
ValidPseudoAccounts,
|
||||
ValidLoanBroker,
|
||||
ValidLoan,
|
||||
ValidVault>;
|
||||
|
||||
/**
|
||||
* @brief get a tuple of all invariant checks
|
||||
*
|
||||
* @return std::tuple of instances that implement the required invariant check
|
||||
* methods
|
||||
*
|
||||
* @see xrpl::InvariantChecker_PROTOTYPE
|
||||
*/
|
||||
inline InvariantChecks
|
||||
getInvariantChecks()
|
||||
{
|
||||
return InvariantChecks{};
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
64
include/xrpl/tx/SignerEntries.h
Normal file
64
include/xrpl/tx/SignerEntries.h
Normal file
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Expected.h> //
|
||||
#include <xrpl/beast/utility/Journal.h> // beast::Journal
|
||||
#include <xrpl/protocol/TER.h> // temMALFORMED
|
||||
#include <xrpl/protocol/UintTypes.h> // AccountID
|
||||
#include <xrpl/tx/Transactor.h> // NotTEC
|
||||
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
// Forward declarations
|
||||
class STObject;
|
||||
|
||||
// Support for SignerEntries that is needed by a few Transactors.
|
||||
//
|
||||
// SignerEntries is represented as a std::vector<SignerEntries::SignerEntry>.
|
||||
// There is no direct constructor for SignerEntries.
|
||||
//
|
||||
// o A std::vector<SignerEntries::SignerEntry> is a SignerEntries.
|
||||
// o More commonly, SignerEntries are extracted from an STObject by
|
||||
// calling SignerEntries::deserialize().
|
||||
class SignerEntries
|
||||
{
|
||||
public:
|
||||
explicit SignerEntries() = delete;
|
||||
|
||||
struct SignerEntry
|
||||
{
|
||||
AccountID account;
|
||||
std::uint16_t weight;
|
||||
std::optional<uint256> tag;
|
||||
|
||||
SignerEntry(AccountID const& inAccount, std::uint16_t inWeight, std::optional<uint256> inTag)
|
||||
: account(inAccount), weight(inWeight), tag(inTag)
|
||||
{
|
||||
}
|
||||
|
||||
// For sorting to look for duplicate accounts
|
||||
friend bool
|
||||
operator<(SignerEntry const& lhs, SignerEntry const& rhs)
|
||||
{
|
||||
return lhs.account < rhs.account;
|
||||
}
|
||||
|
||||
friend bool
|
||||
operator==(SignerEntry const& lhs, SignerEntry const& rhs)
|
||||
{
|
||||
return lhs.account == rhs.account;
|
||||
}
|
||||
};
|
||||
|
||||
// Deserialize a SignerEntries array from the network or from the ledger.
|
||||
//
|
||||
// obj Contains a SignerEntries field that is an STArray.
|
||||
// journal For reporting error conditions.
|
||||
// annotation Source of SignerEntries, like "ledger" or "transaction".
|
||||
static Expected<std::vector<SignerEntry>, NotTEC>
|
||||
deserialize(STObject const& obj, beast::Journal journal, std::string_view annotation);
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
419
include/xrpl/tx/Transactor.h
Normal file
419
include/xrpl/tx/Transactor.h
Normal file
@@ -0,0 +1,419 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/beast/utility/WrappedSink.h>
|
||||
#include <xrpl/protocol/Permissions.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
#include <xrpl/tx/ApplyContext.h>
|
||||
#include <xrpl/tx/applySteps.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/** State information when preflighting a tx. */
|
||||
struct PreflightContext
|
||||
{
|
||||
public:
|
||||
ServiceRegistry& registry;
|
||||
STTx const& tx;
|
||||
Rules const rules;
|
||||
ApplyFlags flags;
|
||||
std::optional<uint256 const> parentBatchId;
|
||||
beast::Journal const j;
|
||||
|
||||
PreflightContext(
|
||||
ServiceRegistry& registry_,
|
||||
STTx const& tx_,
|
||||
uint256 parentBatchId_,
|
||||
Rules const& rules_,
|
||||
ApplyFlags flags_,
|
||||
beast::Journal j_ = beast::Journal{beast::Journal::getNullSink()})
|
||||
: registry(registry_), tx(tx_), rules(rules_), flags(flags_), parentBatchId(parentBatchId_), j(j_)
|
||||
{
|
||||
XRPL_ASSERT((flags_ & tapBATCH) == tapBATCH, "Batch apply flag should be set");
|
||||
}
|
||||
|
||||
PreflightContext(
|
||||
ServiceRegistry& registry_,
|
||||
STTx const& tx_,
|
||||
Rules const& rules_,
|
||||
ApplyFlags flags_,
|
||||
beast::Journal j_ = beast::Journal{beast::Journal::getNullSink()})
|
||||
: registry(registry_), tx(tx_), rules(rules_), flags(flags_), j(j_)
|
||||
{
|
||||
XRPL_ASSERT((flags_ & tapBATCH) == 0, "Batch apply flag should not be set");
|
||||
}
|
||||
|
||||
PreflightContext&
|
||||
operator=(PreflightContext const&) = delete;
|
||||
};
|
||||
|
||||
/** State information when determining if a tx is likely to claim a fee. */
|
||||
struct PreclaimContext
|
||||
{
|
||||
public:
|
||||
ServiceRegistry& registry;
|
||||
ReadView const& view;
|
||||
TER preflightResult;
|
||||
ApplyFlags flags;
|
||||
STTx const& tx;
|
||||
std::optional<uint256 const> const parentBatchId;
|
||||
beast::Journal const j;
|
||||
|
||||
PreclaimContext(
|
||||
ServiceRegistry& registry_,
|
||||
ReadView const& view_,
|
||||
TER preflightResult_,
|
||||
STTx const& tx_,
|
||||
ApplyFlags flags_,
|
||||
std::optional<uint256> parentBatchId_,
|
||||
beast::Journal j_ = beast::Journal{beast::Journal::getNullSink()})
|
||||
: registry(registry_)
|
||||
, view(view_)
|
||||
, preflightResult(preflightResult_)
|
||||
, flags(flags_)
|
||||
, tx(tx_)
|
||||
, parentBatchId(parentBatchId_)
|
||||
, j(j_)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
parentBatchId.has_value() == ((flags_ & tapBATCH) == tapBATCH),
|
||||
"Parent Batch ID should be set if batch apply flag is set");
|
||||
}
|
||||
|
||||
PreclaimContext(
|
||||
ServiceRegistry& registry_,
|
||||
ReadView const& view_,
|
||||
TER preflightResult_,
|
||||
STTx const& tx_,
|
||||
ApplyFlags flags_,
|
||||
beast::Journal j_ = beast::Journal{beast::Journal::getNullSink()})
|
||||
: PreclaimContext(registry_, view_, preflightResult_, tx_, flags_, std::nullopt, j_)
|
||||
{
|
||||
XRPL_ASSERT((flags_ & tapBATCH) == 0, "Batch apply flag should not be set");
|
||||
}
|
||||
|
||||
PreclaimContext&
|
||||
operator=(PreclaimContext const&) = delete;
|
||||
};
|
||||
|
||||
class TxConsequences;
|
||||
struct PreflightResult;
|
||||
// Needed for preflight specialization
|
||||
class Change;
|
||||
|
||||
class Transactor
|
||||
{
|
||||
protected:
|
||||
ApplyContext& ctx_;
|
||||
beast::WrappedSink sink_;
|
||||
beast::Journal const j_;
|
||||
|
||||
AccountID const account_;
|
||||
XRPAmount mPriorBalance; // Balance before fees.
|
||||
XRPAmount mSourceBalance; // Balance after fees.
|
||||
|
||||
virtual ~Transactor() = default;
|
||||
Transactor(Transactor const&) = delete;
|
||||
Transactor&
|
||||
operator=(Transactor const&) = delete;
|
||||
|
||||
public:
|
||||
enum ConsequencesFactoryType { Normal, Blocker, Custom };
|
||||
/** Process the transaction. */
|
||||
ApplyResult
|
||||
operator()();
|
||||
|
||||
ApplyView&
|
||||
view()
|
||||
{
|
||||
return ctx_.view();
|
||||
}
|
||||
|
||||
ApplyView const&
|
||||
view() const
|
||||
{
|
||||
return ctx_.view();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/*
|
||||
These static functions are called from invoke_preclaim<Tx>
|
||||
using name hiding to accomplish compile-time polymorphism,
|
||||
so derived classes can override for different or extra
|
||||
functionality. Use with care, as these are not really
|
||||
virtual and so don't have the compiler-time protection that
|
||||
comes with it.
|
||||
*/
|
||||
|
||||
static NotTEC
|
||||
checkSeqProxy(ReadView const& view, STTx const& tx, beast::Journal j);
|
||||
|
||||
static NotTEC
|
||||
checkPriorTxAndLastLedger(PreclaimContext const& ctx);
|
||||
|
||||
static TER
|
||||
checkFee(PreclaimContext const& ctx, XRPAmount baseFee);
|
||||
|
||||
static NotTEC
|
||||
checkSign(PreclaimContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
checkBatchSign(PreclaimContext const& ctx);
|
||||
|
||||
// Returns the fee in fee units, not scaled for load.
|
||||
static XRPAmount
|
||||
calculateBaseFee(ReadView const& view, STTx const& tx);
|
||||
|
||||
/* Do NOT define an invokePreflight function in a derived class.
|
||||
Instead, define:
|
||||
|
||||
// Optional if the transaction is gated on an amendment that
|
||||
// isn't specified in transactions.macro
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
// Optional if the transaction uses any flags other than tfUniversal
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
// Required, even if it just returns tesSUCCESS.
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
// Optional, rarely needed, if the transaction does any expensive
|
||||
// checks after the signature is verified.
|
||||
static NotTEC preflightSigValidated(PreflightContext const& ctx);
|
||||
|
||||
* Do not try to call preflight1 or preflight2 directly.
|
||||
* Do not check whether relevant amendments are enabled in preflight.
|
||||
Instead, define checkExtraFeatures.
|
||||
* Do not check flags in preflight. Instead, define getFlagsMask.
|
||||
*/
|
||||
template <class T>
|
||||
static NotTEC
|
||||
invokePreflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
// Most transactors do nothing
|
||||
// after checkSeq/Fee/Sign.
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
checkPermission(ReadView const& view, STTx const& tx);
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
// Interface used by DeleteAccount
|
||||
static TER
|
||||
ticketDelete(ApplyView& view, AccountID const& account, uint256 const& ticketIndex, beast::Journal j);
|
||||
|
||||
protected:
|
||||
TER
|
||||
apply();
|
||||
|
||||
explicit Transactor(ApplyContext& ctx);
|
||||
|
||||
virtual void
|
||||
preCompute();
|
||||
|
||||
virtual TER
|
||||
doApply() = 0;
|
||||
|
||||
/** Compute the minimum fee required to process a transaction
|
||||
with a given baseFee based on the current server load.
|
||||
|
||||
@param registry The service registry.
|
||||
@param baseFee The base fee of a candidate transaction
|
||||
@see xrpl::calculateBaseFee
|
||||
@param fees Fee settings from the current ledger
|
||||
@param flags Transaction processing fees
|
||||
*/
|
||||
static XRPAmount
|
||||
minimumFee(ServiceRegistry& registry, XRPAmount baseFee, Fees const& fees, ApplyFlags flags);
|
||||
|
||||
// Returns the fee in fee units, not scaled for load.
|
||||
static XRPAmount
|
||||
calculateOwnerReserveFee(ReadView const& view, STTx const& tx);
|
||||
|
||||
static NotTEC
|
||||
checkSign(
|
||||
ReadView const& view,
|
||||
ApplyFlags flags,
|
||||
std::optional<uint256 const> const& parentBatchId,
|
||||
AccountID const& idAccount,
|
||||
STObject const& sigObject,
|
||||
beast::Journal const j);
|
||||
|
||||
// Base class always returns true
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
// Base class always returns tfUniversalMask
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
// Base class always returns tesSUCCESS
|
||||
static NotTEC
|
||||
preflightSigValidated(PreflightContext const& ctx);
|
||||
|
||||
static bool
|
||||
validDataLength(std::optional<Slice> const& slice, std::size_t maxLength);
|
||||
|
||||
template <class T>
|
||||
static bool
|
||||
validNumericRange(std::optional<T> value, T max, T min = T{});
|
||||
|
||||
template <class T, class Unit>
|
||||
static bool
|
||||
validNumericRange(
|
||||
std::optional<T> value,
|
||||
unit::ValueUnit<Unit, T> max,
|
||||
unit::ValueUnit<Unit, T> min = unit::ValueUnit<Unit, T>{});
|
||||
|
||||
/// Minimum will usually be zero.
|
||||
template <class T>
|
||||
static bool
|
||||
validNumericMinimum(std::optional<T> value, T min = T{});
|
||||
|
||||
/// Minimum will usually be zero.
|
||||
template <class T, class Unit>
|
||||
static bool
|
||||
validNumericMinimum(std::optional<T> value, unit::ValueUnit<Unit, T> min = unit::ValueUnit<Unit, T>{});
|
||||
|
||||
private:
|
||||
std::pair<TER, XRPAmount>
|
||||
reset(XRPAmount fee);
|
||||
|
||||
TER
|
||||
consumeSeqProxy(SLE::pointer const& sleAccount);
|
||||
TER
|
||||
payFee();
|
||||
static NotTEC
|
||||
checkSingleSign(
|
||||
ReadView const& view,
|
||||
AccountID const& idSigner,
|
||||
AccountID const& idAccount,
|
||||
std::shared_ptr<SLE const> sleAccount,
|
||||
beast::Journal const j);
|
||||
static NotTEC
|
||||
checkMultiSign(
|
||||
ReadView const& view,
|
||||
ApplyFlags flags,
|
||||
AccountID const& id,
|
||||
STObject const& sigObject,
|
||||
beast::Journal const j);
|
||||
|
||||
void trapTransaction(uint256) const;
|
||||
|
||||
/** Performs early sanity checks on the account and fee fields.
|
||||
|
||||
(And passes flagMask to preflight0)
|
||||
|
||||
Do not try to call preflight1 from preflight() in derived classes. See
|
||||
the description of invokePreflight for details.
|
||||
*/
|
||||
static NotTEC
|
||||
preflight1(PreflightContext const& ctx, std::uint32_t flagMask);
|
||||
|
||||
/** Checks whether the signature appears valid
|
||||
|
||||
Do not try to call preflight2 from preflight() in derived classes. See
|
||||
the description of invokePreflight for details.
|
||||
*/
|
||||
static NotTEC
|
||||
preflight2(PreflightContext const& ctx);
|
||||
};
|
||||
|
||||
inline bool
|
||||
Transactor::checkExtraFeatures(PreflightContext const& ctx)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Performs early sanity checks on the txid and flags */
|
||||
NotTEC
|
||||
preflight0(PreflightContext const& ctx, std::uint32_t flagMask);
|
||||
|
||||
namespace detail {
|
||||
|
||||
/** Checks the validity of the transactor signing key.
|
||||
*
|
||||
* Normally called from preflight1 with ctx.tx.
|
||||
*/
|
||||
NotTEC
|
||||
preflightCheckSigningKey(STObject const& sigObject, beast::Journal j);
|
||||
|
||||
/** Checks the special signing key state needed for simulation
|
||||
*
|
||||
* Normally called from preflight2 with ctx.tx.
|
||||
*/
|
||||
std::optional<NotTEC>
|
||||
preflightCheckSimulateKeys(ApplyFlags flags, STObject const& sigObject, beast::Journal j);
|
||||
} // namespace detail
|
||||
|
||||
// Defined in Change.cpp
|
||||
template <>
|
||||
NotTEC
|
||||
Transactor::invokePreflight<Change>(PreflightContext const& ctx);
|
||||
|
||||
template <class T>
|
||||
NotTEC
|
||||
Transactor::invokePreflight(PreflightContext const& ctx)
|
||||
{
|
||||
// Using this lookup does NOT require checking the fixDelegateV1_1. The data
|
||||
// exists regardless of whether it is enabled.
|
||||
auto const feature = Permission::getInstance().getTxFeature(ctx.tx.getTxnType());
|
||||
|
||||
if (feature && !ctx.rules.enabled(*feature))
|
||||
return temDISABLED;
|
||||
|
||||
if (!T::checkExtraFeatures(ctx))
|
||||
return temDISABLED;
|
||||
|
||||
if (auto const ret = preflight1(ctx, T::getFlagsMask(ctx)))
|
||||
return ret;
|
||||
|
||||
if (auto const ret = T::preflight(ctx))
|
||||
return ret;
|
||||
|
||||
if (auto const ret = preflight2(ctx))
|
||||
return ret;
|
||||
|
||||
return T::preflightSigValidated(ctx);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool
|
||||
Transactor::validNumericRange(std::optional<T> value, T max, T min)
|
||||
{
|
||||
if (!value)
|
||||
return true;
|
||||
return value >= min && value <= max;
|
||||
}
|
||||
|
||||
template <class T, class Unit>
|
||||
bool
|
||||
Transactor::validNumericRange(std::optional<T> value, unit::ValueUnit<Unit, T> max, unit::ValueUnit<Unit, T> min)
|
||||
{
|
||||
return validNumericRange(value, max.value(), min.value());
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool
|
||||
Transactor::validNumericMinimum(std::optional<T> value, T min)
|
||||
{
|
||||
if (!value)
|
||||
return true;
|
||||
return value >= min;
|
||||
}
|
||||
|
||||
template <class T, class Unit>
|
||||
bool
|
||||
Transactor::validNumericMinimum(std::optional<T> value, unit::ValueUnit<Unit, T> min)
|
||||
{
|
||||
return validNumericMinimum(value, min.value());
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
129
include/xrpl/tx/apply.h
Normal file
129
include/xrpl/tx/apply.h
Normal file
@@ -0,0 +1,129 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
#include <xrpl/tx/applySteps.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class HashRouter;
|
||||
class ServiceRegistry;
|
||||
|
||||
/** Describes the pre-processing validity of a transaction.
|
||||
|
||||
@see checkValidity, forceValidity
|
||||
*/
|
||||
enum class Validity {
|
||||
/// Signature is bad. Didn't do local checks.
|
||||
SigBad,
|
||||
/// Signature is good, but local checks fail.
|
||||
SigGoodOnly,
|
||||
/// Signature and local checks are good / passed.
|
||||
Valid
|
||||
};
|
||||
|
||||
/** Checks transaction signature and local checks.
|
||||
|
||||
@return A `Validity` enum representing how valid the
|
||||
`STTx` is and, if not `Valid`, a reason string.
|
||||
|
||||
@note Results are cached internally, so tests will not be
|
||||
repeated over repeated calls, unless cache expires.
|
||||
|
||||
@return `std::pair`, where `.first` is the status, and
|
||||
`.second` is the reason if appropriate.
|
||||
|
||||
@see Validity
|
||||
*/
|
||||
std::pair<Validity, std::string>
|
||||
checkValidity(HashRouter& router, STTx const& tx, Rules const& rules);
|
||||
|
||||
/** Sets the validity of a given transaction in the cache.
|
||||
|
||||
@warning Use with extreme care.
|
||||
|
||||
@note Can only raise the validity to a more valid state,
|
||||
and can not override anything cached bad.
|
||||
|
||||
@see checkValidity, Validity
|
||||
*/
|
||||
void
|
||||
forceValidity(HashRouter& router, uint256 const& txid, Validity validity);
|
||||
|
||||
/** Apply a transaction to an `OpenView`.
|
||||
|
||||
This function is the canonical way to apply a transaction
|
||||
to a ledger. It rolls the validation and application
|
||||
steps into one function. To do the steps manually, the
|
||||
correct calling order is:
|
||||
@code{.cpp}
|
||||
preflight -> preclaim -> doApply
|
||||
@endcode
|
||||
The result of one function must be passed to the next.
|
||||
The `preflight` result can be safely cached and reused
|
||||
asynchronously, but `preclaim` and `doApply` must be called
|
||||
in the same thread and with the same view.
|
||||
|
||||
@note Does not throw.
|
||||
|
||||
For open ledgers, the `Transactor` will catch exceptions
|
||||
and return `tefEXCEPTION`. For closed ledgers, the
|
||||
`Transactor` will attempt to only charge a fee,
|
||||
and return `tecFAILED_PROCESSING`.
|
||||
|
||||
If the `Transactor` gets an exception while trying
|
||||
to charge the fee, it will be caught and
|
||||
turned into `tefEXCEPTION`.
|
||||
|
||||
For network health, a `Transactor` makes its
|
||||
best effort to at least charge a fee if the
|
||||
ledger is closed.
|
||||
|
||||
@param app The current running `Application`.
|
||||
@param view The open ledger that the transaction
|
||||
will attempt to be applied to.
|
||||
@param tx The transaction to be checked.
|
||||
@param flags `ApplyFlags` describing processing options.
|
||||
@param journal A journal.
|
||||
|
||||
@see preflight, preclaim, doApply
|
||||
|
||||
@return A pair with the `TER` and a `bool` indicating
|
||||
whether or not the transaction was applied.
|
||||
*/
|
||||
ApplyResult
|
||||
apply(ServiceRegistry& registry, OpenView& view, STTx const& tx, ApplyFlags flags, beast::Journal journal);
|
||||
|
||||
/** Enum class for return value from `applyTransaction`
|
||||
|
||||
@see applyTransaction
|
||||
*/
|
||||
enum class ApplyTransactionResult {
|
||||
/// Applied to this ledger
|
||||
Success,
|
||||
/// Should not be retried in this ledger
|
||||
Fail,
|
||||
/// Should be retried in this ledger
|
||||
Retry
|
||||
};
|
||||
|
||||
/** Transaction application helper
|
||||
|
||||
Provides more detailed logging and decodes the
|
||||
correct behavior based on the `TER` type
|
||||
|
||||
@see ApplyTransactionResult
|
||||
*/
|
||||
ApplyTransactionResult
|
||||
applyTransaction(
|
||||
ServiceRegistry& registry,
|
||||
OpenView& view,
|
||||
STTx const& tx,
|
||||
bool retryAssured,
|
||||
ApplyFlags flags,
|
||||
beast::Journal journal);
|
||||
|
||||
} // namespace xrpl
|
||||
338
include/xrpl/tx/applySteps.h
Normal file
338
include/xrpl/tx/applySteps.h
Normal file
@@ -0,0 +1,338 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ApplyViewImpl.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class ServiceRegistry;
|
||||
class STTx;
|
||||
class TxQ;
|
||||
|
||||
struct ApplyResult
|
||||
{
|
||||
TER ter;
|
||||
bool applied;
|
||||
std::optional<TxMeta> metadata;
|
||||
|
||||
ApplyResult(TER t, bool a, std::optional<TxMeta> m = std::nullopt) : ter(t), applied(a), metadata(std::move(m))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/** Return true if the transaction can claim a fee (tec),
|
||||
and the `ApplyFlags` do not allow soft failures.
|
||||
*/
|
||||
inline bool
|
||||
isTecClaimHardFail(TER ter, ApplyFlags flags)
|
||||
{
|
||||
return isTecClaim(ter) && !(flags & tapRETRY);
|
||||
}
|
||||
|
||||
/** Class describing the consequences to the account
|
||||
of applying a transaction if the transaction consumes
|
||||
the maximum XRP allowed.
|
||||
*/
|
||||
class TxConsequences
|
||||
{
|
||||
public:
|
||||
/// Describes how the transaction affects subsequent
|
||||
/// transactions
|
||||
enum Category {
|
||||
/// Moves currency around, creates offers, etc.
|
||||
normal = 0,
|
||||
/// Affects the ability of subsequent transactions
|
||||
/// to claim a fee. Eg. `SetRegularKey`
|
||||
blocker
|
||||
};
|
||||
|
||||
private:
|
||||
/// Describes how the transaction affects subsequent
|
||||
/// transactions
|
||||
bool isBlocker_;
|
||||
/// Transaction fee
|
||||
XRPAmount fee_;
|
||||
/// Does NOT include the fee.
|
||||
XRPAmount potentialSpend_;
|
||||
/// SeqProxy of transaction.
|
||||
SeqProxy seqProx_;
|
||||
/// Number of sequences consumed.
|
||||
std::uint32_t sequencesConsumed_;
|
||||
|
||||
public:
|
||||
// Constructor if preflight returns a value other than tesSUCCESS.
|
||||
// Asserts if tesSUCCESS is passed.
|
||||
explicit TxConsequences(NotTEC pfResult);
|
||||
|
||||
/// Constructor if the STTx has no notable consequences for the TxQ.
|
||||
explicit TxConsequences(STTx const& tx);
|
||||
|
||||
/// Constructor for a blocker.
|
||||
TxConsequences(STTx const& tx, Category category);
|
||||
|
||||
/// Constructor for an STTx that may consume more XRP than the fee.
|
||||
TxConsequences(STTx const& tx, XRPAmount potentialSpend);
|
||||
|
||||
/// Constructor for an STTx that consumes more than the usual sequences.
|
||||
TxConsequences(STTx const& tx, std::uint32_t sequencesConsumed);
|
||||
|
||||
/// Copy constructor
|
||||
TxConsequences(TxConsequences const&) = default;
|
||||
/// Copy assignment operator
|
||||
TxConsequences&
|
||||
operator=(TxConsequences const&) = default;
|
||||
/// Move constructor
|
||||
TxConsequences(TxConsequences&&) = default;
|
||||
/// Move assignment operator
|
||||
TxConsequences&
|
||||
operator=(TxConsequences&&) = default;
|
||||
|
||||
/// Fee
|
||||
XRPAmount
|
||||
fee() const
|
||||
{
|
||||
return fee_;
|
||||
}
|
||||
|
||||
/// Potential Spend
|
||||
XRPAmount const&
|
||||
potentialSpend() const
|
||||
{
|
||||
return potentialSpend_;
|
||||
}
|
||||
|
||||
/// SeqProxy
|
||||
SeqProxy
|
||||
seqProxy() const
|
||||
{
|
||||
return seqProx_;
|
||||
}
|
||||
|
||||
/// Sequences consumed
|
||||
std::uint32_t
|
||||
sequencesConsumed() const
|
||||
{
|
||||
return sequencesConsumed_;
|
||||
}
|
||||
|
||||
/// Returns true if the transaction is a blocker.
|
||||
bool
|
||||
isBlocker() const
|
||||
{
|
||||
return isBlocker_;
|
||||
}
|
||||
|
||||
// Return the SeqProxy that would follow this.
|
||||
SeqProxy
|
||||
followingSeq() const
|
||||
{
|
||||
SeqProxy following = seqProx_;
|
||||
following.advanceBy(sequencesConsumed());
|
||||
return following;
|
||||
}
|
||||
};
|
||||
|
||||
/** Describes the results of the `preflight` check
|
||||
|
||||
@note All members are const to make it more difficult
|
||||
to "fake" a result without calling `preflight`.
|
||||
@see preflight, preclaim, doApply, apply
|
||||
*/
|
||||
struct PreflightResult
|
||||
{
|
||||
public:
|
||||
/// From the input - the transaction
|
||||
STTx const& tx;
|
||||
/// From the input - the batch identifier, if part of a batch
|
||||
std::optional<uint256 const> const parentBatchId;
|
||||
/// From the input - the rules
|
||||
Rules const rules;
|
||||
/// Consequences of the transaction
|
||||
TxConsequences const consequences;
|
||||
/// From the input - the flags
|
||||
ApplyFlags const flags;
|
||||
/// From the input - the journal
|
||||
beast::Journal const j;
|
||||
|
||||
/// Intermediate transaction result
|
||||
NotTEC const ter;
|
||||
|
||||
/// Constructor
|
||||
template <class Context>
|
||||
PreflightResult(Context const& ctx_, std::pair<NotTEC, TxConsequences> const& result)
|
||||
: tx(ctx_.tx)
|
||||
, parentBatchId(ctx_.parentBatchId)
|
||||
, rules(ctx_.rules)
|
||||
, consequences(result.second)
|
||||
, flags(ctx_.flags)
|
||||
, j(ctx_.j)
|
||||
, ter(result.first)
|
||||
{
|
||||
}
|
||||
|
||||
PreflightResult(PreflightResult const&) = default;
|
||||
/// Deleted copy assignment operator
|
||||
PreflightResult&
|
||||
operator=(PreflightResult const&) = delete;
|
||||
};
|
||||
|
||||
/** Describes the results of the `preclaim` check
|
||||
|
||||
@note All members are const to make it more difficult
|
||||
to "fake" a result without calling `preclaim`.
|
||||
@see preflight, preclaim, doApply, apply
|
||||
*/
|
||||
struct PreclaimResult
|
||||
{
|
||||
public:
|
||||
/// From the input - the ledger view
|
||||
ReadView const& view;
|
||||
/// From the input - the transaction
|
||||
STTx const& tx;
|
||||
/// From the input - the batch identifier, if part of a batch
|
||||
std::optional<uint256 const> const parentBatchId;
|
||||
/// From the input - the flags
|
||||
ApplyFlags const flags;
|
||||
/// From the input - the journal
|
||||
beast::Journal const j;
|
||||
|
||||
/// Intermediate transaction result
|
||||
TER const ter;
|
||||
|
||||
/// Success flag - whether the transaction is likely to
|
||||
/// claim a fee
|
||||
bool const likelyToClaimFee;
|
||||
|
||||
/// Constructor
|
||||
template <class Context>
|
||||
PreclaimResult(Context const& ctx_, TER ter_)
|
||||
: view(ctx_.view)
|
||||
, tx(ctx_.tx)
|
||||
, parentBatchId(ctx_.parentBatchId)
|
||||
, flags(ctx_.flags)
|
||||
, j(ctx_.j)
|
||||
, ter(ter_)
|
||||
, likelyToClaimFee(ter == tesSUCCESS || isTecClaimHardFail(ter, flags))
|
||||
{
|
||||
}
|
||||
|
||||
PreclaimResult(PreclaimResult const&) = default;
|
||||
/// Deleted copy assignment operator
|
||||
PreclaimResult&
|
||||
operator=(PreclaimResult const&) = delete;
|
||||
};
|
||||
|
||||
/** Gate a transaction based on static information.
|
||||
|
||||
The transaction is checked against all possible
|
||||
validity constraints that do not require a ledger.
|
||||
|
||||
@param app The current running `Application`.
|
||||
@param rules The `Rules` in effect at the time of the check.
|
||||
@param tx The transaction to be checked.
|
||||
@param flags `ApplyFlags` describing processing options.
|
||||
@param j A journal.
|
||||
|
||||
@see PreflightResult, preclaim, doApply, apply
|
||||
|
||||
@return A `PreflightResult` object containing, among
|
||||
other things, the `TER` code.
|
||||
*/
|
||||
/** @{ */
|
||||
PreflightResult
|
||||
preflight(ServiceRegistry& registry, Rules const& rules, STTx const& tx, ApplyFlags flags, beast::Journal j);
|
||||
|
||||
PreflightResult
|
||||
preflight(
|
||||
ServiceRegistry& registry,
|
||||
Rules const& rules,
|
||||
uint256 const& parentBatchId,
|
||||
STTx const& tx,
|
||||
ApplyFlags flags,
|
||||
beast::Journal j);
|
||||
/** @} */
|
||||
|
||||
/** Gate a transaction based on static ledger information.
|
||||
|
||||
The transaction is checked against all possible
|
||||
validity constraints that DO require a ledger.
|
||||
|
||||
If preclaim succeeds, then the transaction is very
|
||||
likely to claim a fee. This will determine if the
|
||||
transaction is safe to relay without being applied
|
||||
to the open ledger.
|
||||
|
||||
"Succeeds" in this case is defined as returning a
|
||||
`tes` or `tec`, since both lead to claiming a fee.
|
||||
|
||||
@pre The transaction has been checked
|
||||
and validated using `preflight`
|
||||
|
||||
@param preflightResult The result of a previous
|
||||
call to `preflight` for the transaction.
|
||||
@param app The current running `Application`.
|
||||
@param view The open ledger that the transaction
|
||||
will attempt to be applied to.
|
||||
|
||||
@see PreclaimResult, preflight, doApply, apply
|
||||
|
||||
@return A `PreclaimResult` object containing, among
|
||||
other things the `TER` code and the base fee value for
|
||||
this transaction.
|
||||
*/
|
||||
PreclaimResult
|
||||
preclaim(PreflightResult const& preflightResult, ServiceRegistry& registry, OpenView const& view);
|
||||
|
||||
/** Compute only the expected base fee for a transaction.
|
||||
|
||||
Base fees are transaction specific, so any calculation
|
||||
needing them must get the base fee for each transaction.
|
||||
|
||||
No validation is done or implied by this function.
|
||||
|
||||
Caller is responsible for handling any exceptions.
|
||||
Since none should be thrown, that will usually
|
||||
mean terminating.
|
||||
|
||||
@param view The current open ledger.
|
||||
@param tx The transaction to be checked.
|
||||
|
||||
@return The base fee.
|
||||
*/
|
||||
XRPAmount
|
||||
calculateBaseFee(ReadView const& view, STTx const& tx);
|
||||
|
||||
/** Return the minimum fee that an "ordinary" transaction would pay.
|
||||
|
||||
When computing the FeeLevel for a transaction the TxQ sometimes needs
|
||||
the know what an "ordinary" or reference transaction would be required
|
||||
to pay.
|
||||
|
||||
@param view The current open ledger.
|
||||
@param tx The transaction so the correct multisigner count is used.
|
||||
|
||||
@return The base fee in XRPAmount.
|
||||
*/
|
||||
XRPAmount
|
||||
calculateDefaultBaseFee(ReadView const& view, STTx const& tx);
|
||||
|
||||
/** Apply a prechecked transaction to an OpenView.
|
||||
|
||||
@pre The transaction has been checked
|
||||
and validated using `preflight` and `preclaim`
|
||||
|
||||
@param preclaimResult The result of a previous
|
||||
call to `preclaim` for the transaction.
|
||||
@param registry The service registry.
|
||||
@param view The open ledger that the transaction
|
||||
will attempt to be applied to.
|
||||
|
||||
@see preflight, preclaim, apply
|
||||
|
||||
@return A pair with the `TER` and a `bool` indicating
|
||||
whether or not the transaction was applied.
|
||||
*/
|
||||
ApplyResult
|
||||
doApply(PreclaimResult const& preclaimResult, ServiceRegistry& registry, OpenView& view);
|
||||
|
||||
} // namespace xrpl
|
||||
63
include/xrpl/tx/paths/BookTip.h
Normal file
63
include/xrpl/tx/paths/BookTip.h
Normal file
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/Quality.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class Logs;
|
||||
|
||||
/** Iterates and consumes raw offers in an order book.
|
||||
Offers are presented from highest quality to lowest quality. This will
|
||||
return all offers present including missing, invalid, unfunded, etc.
|
||||
*/
|
||||
class BookTip
|
||||
{
|
||||
private:
|
||||
ApplyView& view_;
|
||||
bool m_valid;
|
||||
uint256 m_book;
|
||||
uint256 m_end;
|
||||
uint256 m_dir;
|
||||
uint256 m_index;
|
||||
std::shared_ptr<SLE> m_entry;
|
||||
Quality m_quality;
|
||||
|
||||
public:
|
||||
/** Create the iterator. */
|
||||
BookTip(ApplyView& view, Book const& book);
|
||||
|
||||
uint256 const&
|
||||
dir() const noexcept
|
||||
{
|
||||
return m_dir;
|
||||
}
|
||||
|
||||
uint256 const&
|
||||
index() const noexcept
|
||||
{
|
||||
return m_index;
|
||||
}
|
||||
|
||||
Quality const&
|
||||
quality() const noexcept
|
||||
{
|
||||
return m_quality;
|
||||
}
|
||||
|
||||
SLE::pointer const&
|
||||
entry() const noexcept
|
||||
{
|
||||
return m_entry;
|
||||
}
|
||||
|
||||
/** Erases the current offer and advance to the next offer.
|
||||
Complexity: Constant
|
||||
@return `true` if there is a next offer
|
||||
*/
|
||||
bool
|
||||
step(beast::Journal j);
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
52
include/xrpl/tx/paths/Flow.h
Normal file
52
include/xrpl/tx/paths/Flow.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/protocol/Quality.h>
|
||||
#include <xrpl/tx/paths/RippleCalc.h>
|
||||
#include <xrpl/tx/paths/detail/Steps.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
namespace path {
|
||||
namespace detail {
|
||||
struct FlowDebugInfo;
|
||||
}
|
||||
} // namespace path
|
||||
|
||||
/**
|
||||
Make a payment from the src account to the dst account
|
||||
|
||||
@param view Trust lines and balances
|
||||
@param deliver Amount to deliver to the dst account
|
||||
@param src Account providing input funds for the payment
|
||||
@param dst Account receiving the payment
|
||||
@param paths Set of paths to explore for liquidity
|
||||
@param defaultPaths Include defaultPaths in the path set
|
||||
@param partialPayment If the payment cannot deliver the entire
|
||||
requested amount, deliver as much as possible, given the constraints
|
||||
@param ownerPaysTransferFee If true then owner, not sender, pays fee
|
||||
@param offerCrossing If Yes or Sell then flow is executing offer crossing, not
|
||||
payments
|
||||
@param limitQuality Do not use liquidity below this quality threshold
|
||||
@param sendMax Do not spend more than this amount
|
||||
@param j Journal to write journal messages to
|
||||
@param flowDebugInfo If non-null a pointer to FlowDebugInfo for debugging
|
||||
@return Actual amount in and out, and the result code
|
||||
*/
|
||||
path::RippleCalc::Output
|
||||
flow(
|
||||
PaymentSandbox& view,
|
||||
STAmount const& deliver,
|
||||
AccountID const& src,
|
||||
AccountID const& dst,
|
||||
STPathSet const& paths,
|
||||
bool defaultPaths,
|
||||
bool partialPayment,
|
||||
bool ownerPaysTransferFee,
|
||||
OfferCrossing offerCrossing,
|
||||
std::optional<Quality> const& limitQuality,
|
||||
std::optional<STAmount> const& sendMax,
|
||||
std::optional<uint256> const& domainID,
|
||||
beast::Journal j,
|
||||
path::detail::FlowDebugInfo* flowDebugInfo = nullptr);
|
||||
|
||||
} // namespace xrpl
|
||||
302
include/xrpl/tx/paths/Offer.h
Normal file
302
include/xrpl/tx/paths/Offer.h
Normal file
@@ -0,0 +1,302 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/contract.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/protocol/Quality.h>
|
||||
#include <xrpl/protocol/Rules.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <class TIn, class TOut>
|
||||
class TOfferBase
|
||||
{
|
||||
protected:
|
||||
Issue issIn_;
|
||||
Issue issOut_;
|
||||
};
|
||||
|
||||
template <>
|
||||
class TOfferBase<STAmount, STAmount>
|
||||
{
|
||||
public:
|
||||
explicit TOfferBase() = default;
|
||||
};
|
||||
|
||||
template <class TIn = STAmount, class TOut = STAmount>
|
||||
class TOffer : private TOfferBase<TIn, TOut>
|
||||
{
|
||||
private:
|
||||
SLE::pointer m_entry;
|
||||
Quality m_quality;
|
||||
AccountID m_account;
|
||||
|
||||
TAmounts<TIn, TOut> m_amounts;
|
||||
void
|
||||
setFieldAmounts();
|
||||
|
||||
public:
|
||||
TOffer() = default;
|
||||
|
||||
TOffer(SLE::pointer const& entry, Quality quality);
|
||||
|
||||
/** Returns the quality of the offer.
|
||||
Conceptually, the quality is the ratio of output to input currency.
|
||||
The implementation calculates it as the ratio of input to output
|
||||
currency (so it sorts ascending). The quality is computed at the time
|
||||
the offer is placed, and never changes for the lifetime of the offer.
|
||||
This is an important business rule that maintains accuracy when an
|
||||
offer is partially filled; Subsequent partial fills will use the
|
||||
original quality.
|
||||
*/
|
||||
Quality
|
||||
quality() const noexcept
|
||||
{
|
||||
return m_quality;
|
||||
}
|
||||
|
||||
/** Returns the account id of the offer's owner. */
|
||||
AccountID const&
|
||||
owner() const
|
||||
{
|
||||
return m_account;
|
||||
}
|
||||
|
||||
/** Returns the in and out amounts.
|
||||
Some or all of the out amount may be unfunded.
|
||||
*/
|
||||
TAmounts<TIn, TOut> const&
|
||||
amount() const
|
||||
{
|
||||
return m_amounts;
|
||||
}
|
||||
|
||||
/** Returns `true` if no more funds can flow through this offer. */
|
||||
bool
|
||||
fully_consumed() const
|
||||
{
|
||||
if (m_amounts.in <= beast::zero)
|
||||
return true;
|
||||
if (m_amounts.out <= beast::zero)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Adjusts the offer to indicate that we consumed some (or all) of it. */
|
||||
void
|
||||
consume(ApplyView& view, TAmounts<TIn, TOut> const& consumed)
|
||||
{
|
||||
if (consumed.in > m_amounts.in)
|
||||
Throw<std::logic_error>("can't consume more than is available.");
|
||||
|
||||
if (consumed.out > m_amounts.out)
|
||||
Throw<std::logic_error>("can't produce more than is available.");
|
||||
|
||||
m_amounts -= consumed;
|
||||
setFieldAmounts();
|
||||
view.update(m_entry);
|
||||
}
|
||||
|
||||
std::string
|
||||
id() const
|
||||
{
|
||||
return to_string(m_entry->key());
|
||||
}
|
||||
|
||||
std::optional<uint256>
|
||||
key() const
|
||||
{
|
||||
return m_entry->key();
|
||||
}
|
||||
|
||||
Issue const&
|
||||
issueIn() const;
|
||||
Issue const&
|
||||
issueOut() const;
|
||||
|
||||
TAmounts<TIn, TOut>
|
||||
limitOut(TAmounts<TIn, TOut> const& offerAmount, TOut const& limit, bool roundUp) const;
|
||||
|
||||
TAmounts<TIn, TOut>
|
||||
limitIn(TAmounts<TIn, TOut> const& offerAmount, TIn const& limit, bool roundUp) const;
|
||||
|
||||
template <typename... Args>
|
||||
static TER
|
||||
send(Args&&... args);
|
||||
|
||||
bool
|
||||
isFunded() const
|
||||
{
|
||||
// Offer owner is issuer; they have unlimited funds
|
||||
return m_account == issueOut().account;
|
||||
}
|
||||
|
||||
static std::pair<std::uint32_t, std::uint32_t>
|
||||
adjustRates(std::uint32_t ofrInRate, std::uint32_t ofrOutRate)
|
||||
{
|
||||
// CLOB offer pays the transfer fee
|
||||
return {ofrInRate, ofrOutRate};
|
||||
}
|
||||
|
||||
/** Check any required invariant. Limit order book offer
|
||||
* always returns true.
|
||||
*/
|
||||
bool
|
||||
checkInvariant(TAmounts<TIn, TOut> const& consumed, beast::Journal j) const
|
||||
{
|
||||
if (!isFeatureEnabled(fixAMMv1_3))
|
||||
return true;
|
||||
|
||||
if (consumed.in > m_amounts.in || consumed.out > m_amounts.out)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.error()) << "AMMOffer::checkInvariant failed: consumed " << to_string(consumed.in) << " "
|
||||
<< to_string(consumed.out) << " amounts " << to_string(m_amounts.in) << " "
|
||||
<< to_string(m_amounts.out);
|
||||
|
||||
return false;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
using Offer = TOffer<>;
|
||||
|
||||
template <class TIn, class TOut>
|
||||
TOffer<TIn, TOut>::TOffer(SLE::pointer const& entry, Quality quality)
|
||||
: m_entry(entry), m_quality(quality), m_account(m_entry->getAccountID(sfAccount))
|
||||
{
|
||||
auto const tp = m_entry->getFieldAmount(sfTakerPays);
|
||||
auto const tg = m_entry->getFieldAmount(sfTakerGets);
|
||||
m_amounts.in = toAmount<TIn>(tp);
|
||||
m_amounts.out = toAmount<TOut>(tg);
|
||||
this->issIn_ = tp.issue();
|
||||
this->issOut_ = tg.issue();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline TOffer<STAmount, STAmount>::TOffer(SLE::pointer const& entry, Quality quality)
|
||||
: m_entry(entry)
|
||||
, m_quality(quality)
|
||||
, m_account(m_entry->getAccountID(sfAccount))
|
||||
, m_amounts(m_entry->getFieldAmount(sfTakerPays), m_entry->getFieldAmount(sfTakerGets))
|
||||
{
|
||||
}
|
||||
|
||||
template <class TIn, class TOut>
|
||||
void
|
||||
TOffer<TIn, TOut>::setFieldAmounts()
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
#ifdef _MSC_VER
|
||||
UNREACHABLE("xrpl::TOffer::setFieldAmounts : must be specialized");
|
||||
#else
|
||||
static_assert(sizeof(TOut) == -1, "Must be specialized");
|
||||
#endif
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
template <class TIn, class TOut>
|
||||
TAmounts<TIn, TOut>
|
||||
TOffer<TIn, TOut>::limitOut(TAmounts<TIn, TOut> const& offerAmount, TOut const& limit, bool roundUp) const
|
||||
{
|
||||
// It turns out that the ceil_out implementation has some slop in
|
||||
// it, which ceil_out_strict removes.
|
||||
return quality().ceil_out_strict(offerAmount, limit, roundUp);
|
||||
}
|
||||
|
||||
template <class TIn, class TOut>
|
||||
TAmounts<TIn, TOut>
|
||||
TOffer<TIn, TOut>::limitIn(TAmounts<TIn, TOut> const& offerAmount, TIn const& limit, bool roundUp) const
|
||||
{
|
||||
if (auto const& rules = getCurrentTransactionRules(); rules && rules->enabled(fixReducedOffersV2))
|
||||
// It turns out that the ceil_in implementation has some slop in
|
||||
// it. ceil_in_strict removes that slop. But removing that slop
|
||||
// affects transaction outcomes, so the change must be made using
|
||||
// an amendment.
|
||||
return quality().ceil_in_strict(offerAmount, limit, roundUp);
|
||||
return m_quality.ceil_in(offerAmount, limit);
|
||||
}
|
||||
|
||||
template <class TIn, class TOut>
|
||||
template <typename... Args>
|
||||
TER
|
||||
TOffer<TIn, TOut>::send(Args&&... args)
|
||||
{
|
||||
return accountSend(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void
|
||||
TOffer<STAmount, STAmount>::setFieldAmounts()
|
||||
{
|
||||
m_entry->setFieldAmount(sfTakerPays, m_amounts.in);
|
||||
m_entry->setFieldAmount(sfTakerGets, m_amounts.out);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void
|
||||
TOffer<IOUAmount, IOUAmount>::setFieldAmounts()
|
||||
{
|
||||
m_entry->setFieldAmount(sfTakerPays, toSTAmount(m_amounts.in, issIn_));
|
||||
m_entry->setFieldAmount(sfTakerGets, toSTAmount(m_amounts.out, issOut_));
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void
|
||||
TOffer<IOUAmount, XRPAmount>::setFieldAmounts()
|
||||
{
|
||||
m_entry->setFieldAmount(sfTakerPays, toSTAmount(m_amounts.in, issIn_));
|
||||
m_entry->setFieldAmount(sfTakerGets, toSTAmount(m_amounts.out));
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void
|
||||
TOffer<XRPAmount, IOUAmount>::setFieldAmounts()
|
||||
{
|
||||
m_entry->setFieldAmount(sfTakerPays, toSTAmount(m_amounts.in));
|
||||
m_entry->setFieldAmount(sfTakerGets, toSTAmount(m_amounts.out, issOut_));
|
||||
}
|
||||
|
||||
template <class TIn, class TOut>
|
||||
Issue const&
|
||||
TOffer<TIn, TOut>::issueIn() const
|
||||
{
|
||||
return this->issIn_;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline Issue const&
|
||||
TOffer<STAmount, STAmount>::issueIn() const
|
||||
{
|
||||
return m_amounts.in.issue();
|
||||
}
|
||||
|
||||
template <class TIn, class TOut>
|
||||
Issue const&
|
||||
TOffer<TIn, TOut>::issueOut() const
|
||||
{
|
||||
return this->issOut_;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline Issue const&
|
||||
TOffer<STAmount, STAmount>::issueOut() const
|
||||
{
|
||||
return m_amounts.out.issue();
|
||||
}
|
||||
|
||||
template <class TIn, class TOut>
|
||||
inline std::ostream&
|
||||
operator<<(std::ostream& os, TOffer<TIn, TOut> const& offer)
|
||||
{
|
||||
return os << offer.id();
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
174
include/xrpl/tx/paths/OfferStream.h
Normal file
174
include/xrpl/tx/paths/OfferStream.h
Normal file
@@ -0,0 +1,174 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/chrono.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/tx/paths/BookTip.h>
|
||||
#include <xrpl/tx/paths/Offer.h>
|
||||
|
||||
#include <boost/container/flat_set.hpp>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
template <class TIn, class TOut>
|
||||
class TOfferStreamBase
|
||||
{
|
||||
public:
|
||||
class StepCounter
|
||||
{
|
||||
private:
|
||||
std::uint32_t const limit_;
|
||||
std::uint32_t count_;
|
||||
beast::Journal j_;
|
||||
|
||||
public:
|
||||
StepCounter(std::uint32_t limit, beast::Journal j) : limit_(limit), count_(0), j_(j)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
step()
|
||||
{
|
||||
if (count_ >= limit_)
|
||||
{
|
||||
JLOG(j_.debug()) << "Exceeded " << limit_ << " step limit.";
|
||||
return false;
|
||||
}
|
||||
count_++;
|
||||
return true;
|
||||
}
|
||||
std::uint32_t
|
||||
count() const
|
||||
{
|
||||
return count_;
|
||||
}
|
||||
};
|
||||
|
||||
protected:
|
||||
beast::Journal const j_;
|
||||
ApplyView& view_;
|
||||
ApplyView& cancelView_;
|
||||
Book book_;
|
||||
bool validBook_;
|
||||
NetClock::time_point const expire_;
|
||||
BookTip tip_;
|
||||
TOffer<TIn, TOut> offer_;
|
||||
std::optional<TOut> ownerFunds_;
|
||||
StepCounter& counter_;
|
||||
|
||||
void
|
||||
erase(ApplyView& view);
|
||||
|
||||
virtual void
|
||||
permRmOffer(uint256 const& offerIndex) = 0;
|
||||
|
||||
template <class TTakerPays, class TTakerGets>
|
||||
bool
|
||||
shouldRmSmallIncreasedQOffer() const;
|
||||
|
||||
public:
|
||||
TOfferStreamBase(
|
||||
ApplyView& view,
|
||||
ApplyView& cancelView,
|
||||
Book const& book,
|
||||
NetClock::time_point when,
|
||||
StepCounter& counter,
|
||||
beast::Journal journal);
|
||||
|
||||
virtual ~TOfferStreamBase() = default;
|
||||
|
||||
/** Returns the offer at the tip of the order book.
|
||||
Offers are always presented in decreasing quality.
|
||||
Only valid if step() returned `true`.
|
||||
*/
|
||||
TOffer<TIn, TOut>&
|
||||
tip() const
|
||||
{
|
||||
return const_cast<TOfferStreamBase*>(this)->offer_;
|
||||
}
|
||||
|
||||
/** Advance to the next valid offer.
|
||||
This automatically removes:
|
||||
- Offers with missing ledger entries
|
||||
- Offers found unfunded
|
||||
- expired offers
|
||||
@return `true` if there is a valid offer.
|
||||
*/
|
||||
bool
|
||||
step();
|
||||
|
||||
TOut
|
||||
ownerFunds() const
|
||||
{
|
||||
return *ownerFunds_;
|
||||
}
|
||||
};
|
||||
|
||||
/** Presents and consumes the offers in an order book.
|
||||
|
||||
Two `ApplyView` objects accumulate changes to the ledger. `view`
|
||||
is applied when the calling transaction succeeds. If the calling
|
||||
transaction fails, then `view_cancel` is applied.
|
||||
|
||||
Certain invalid offers are automatically removed:
|
||||
- Offers with missing ledger entries
|
||||
- Offers that expired
|
||||
- Offers found unfunded:
|
||||
An offer is found unfunded when the corresponding balance is zero
|
||||
and the caller has not modified the balance. This is accomplished
|
||||
by also looking up the balance in the cancel view.
|
||||
|
||||
When an offer is removed, it is removed from both views. This grooms the
|
||||
order book regardless of whether or not the transaction is successful.
|
||||
*/
|
||||
class OfferStream : public TOfferStreamBase<STAmount, STAmount>
|
||||
{
|
||||
protected:
|
||||
void
|
||||
permRmOffer(uint256 const& offerIndex) override;
|
||||
|
||||
public:
|
||||
using TOfferStreamBase<STAmount, STAmount>::TOfferStreamBase;
|
||||
};
|
||||
|
||||
/** Presents and consumes the offers in an order book.
|
||||
|
||||
The `view_' ` `ApplyView` accumulates changes to the ledger.
|
||||
The `cancelView_` is used to determine if an offer is found
|
||||
unfunded or became unfunded.
|
||||
The `permToRemove` collection identifies offers that should be
|
||||
removed even if the strand associated with this OfferStream
|
||||
is not applied.
|
||||
|
||||
Certain invalid offers are added to the `permToRemove` collection:
|
||||
- Offers with missing ledger entries
|
||||
- Offers that expired
|
||||
- Offers found unfunded:
|
||||
An offer is found unfunded when the corresponding balance is zero
|
||||
and the caller has not modified the balance. This is accomplished
|
||||
by also looking up the balance in the cancel view.
|
||||
*/
|
||||
template <class TIn, class TOut>
|
||||
class FlowOfferStream : public TOfferStreamBase<TIn, TOut>
|
||||
{
|
||||
private:
|
||||
boost::container::flat_set<uint256> permToRemove_;
|
||||
|
||||
public:
|
||||
using TOfferStreamBase<TIn, TOut>::TOfferStreamBase;
|
||||
|
||||
// The following interface allows offer crossing to permanently
|
||||
// remove self crossed offers. The motivation is somewhat
|
||||
// unintuitive. See the discussion in the comments for
|
||||
// BookOfferCrossingStep::limitSelfCrossQuality().
|
||||
void
|
||||
permRmOffer(uint256 const& offerIndex) override;
|
||||
|
||||
boost::container::flat_set<uint256> const&
|
||||
permToRemove() const
|
||||
{
|
||||
return permToRemove_;
|
||||
}
|
||||
};
|
||||
} // namespace xrpl
|
||||
109
include/xrpl/tx/paths/RippleCalc.h
Normal file
109
include/xrpl/tx/paths/RippleCalc.h
Normal file
@@ -0,0 +1,109 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/ledger/PaymentSandbox.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
|
||||
#include <boost/container/flat_set.hpp>
|
||||
|
||||
namespace xrpl {
|
||||
class Config;
|
||||
namespace path {
|
||||
|
||||
namespace detail {
|
||||
struct FlowDebugInfo;
|
||||
}
|
||||
|
||||
/** RippleCalc calculates the quality of a payment path.
|
||||
|
||||
Quality is the amount of input required to produce a given output along a
|
||||
specified path - another name for this is exchange rate.
|
||||
*/
|
||||
class RippleCalc
|
||||
{
|
||||
public:
|
||||
struct Input
|
||||
{
|
||||
explicit Input() = default;
|
||||
|
||||
bool partialPaymentAllowed = false;
|
||||
bool defaultPathsAllowed = true;
|
||||
bool limitQuality = false;
|
||||
bool isLedgerOpen = true;
|
||||
};
|
||||
struct Output
|
||||
{
|
||||
explicit Output() = default;
|
||||
|
||||
// The computed input amount.
|
||||
STAmount actualAmountIn;
|
||||
|
||||
// The computed output amount.
|
||||
STAmount actualAmountOut;
|
||||
|
||||
// Collection of offers found expired or unfunded. When a payment
|
||||
// succeeds, unfunded and expired offers are removed. When a payment
|
||||
// fails, they are not removed. This vector contains the offers that
|
||||
// could have been removed but were not because the payment fails. It is
|
||||
// useful for offer crossing, which does remove the offers.
|
||||
boost::container::flat_set<uint256> removableOffers;
|
||||
|
||||
private:
|
||||
TER calculationResult_ = temUNKNOWN;
|
||||
|
||||
public:
|
||||
TER
|
||||
result() const
|
||||
{
|
||||
return calculationResult_;
|
||||
}
|
||||
void
|
||||
setResult(TER const value)
|
||||
{
|
||||
calculationResult_ = value;
|
||||
}
|
||||
};
|
||||
|
||||
static Output
|
||||
rippleCalculate(
|
||||
PaymentSandbox& view,
|
||||
|
||||
// Compute paths using this ledger entry set. Up to caller to actually
|
||||
// apply to ledger.
|
||||
|
||||
// Issuer:
|
||||
// XRP: xrpAccount()
|
||||
// non-XRP: uSrcAccountID (for any issuer) or another account with
|
||||
// trust node.
|
||||
STAmount const& saMaxAmountReq, // --> -1 = no limit.
|
||||
|
||||
// Issuer:
|
||||
// XRP: xrpAccount()
|
||||
// non-XRP: uDstAccountID (for any issuer) or another account with
|
||||
// trust node.
|
||||
STAmount const& saDstAmountReq,
|
||||
|
||||
AccountID const& uDstAccountID,
|
||||
AccountID const& uSrcAccountID,
|
||||
|
||||
// A set of paths that are included in the transaction that we'll
|
||||
// explore for liquidity.
|
||||
STPathSet const& spsPaths,
|
||||
|
||||
std::optional<uint256> const& domainID,
|
||||
Logs& l,
|
||||
Input const* const pInputs = nullptr);
|
||||
|
||||
// The view we are currently working on
|
||||
PaymentSandbox& view;
|
||||
|
||||
// If the transaction fails to meet some constraint, still need to delete
|
||||
// unfunded offers in a deterministic order (hence the ordered container).
|
||||
//
|
||||
// Offers that were found unfunded.
|
||||
boost::container::flat_set<uint256> permanentlyUnfundedOffers_;
|
||||
};
|
||||
|
||||
} // namespace path
|
||||
} // namespace xrpl
|
||||
198
include/xrpl/tx/paths/detail/AmountSpec.h
Normal file
198
include/xrpl/tx/paths/detail/AmountSpec.h
Normal file
@@ -0,0 +1,198 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/protocol/IOUAmount.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
struct AmountSpec
|
||||
{
|
||||
explicit AmountSpec() = default;
|
||||
|
||||
bool native;
|
||||
union
|
||||
{
|
||||
XRPAmount xrp;
|
||||
IOUAmount iou = {};
|
||||
};
|
||||
std::optional<AccountID> issuer;
|
||||
std::optional<Currency> currency;
|
||||
|
||||
friend std::ostream&
|
||||
operator<<(std::ostream& stream, AmountSpec const& amt)
|
||||
{
|
||||
if (amt.native)
|
||||
stream << to_string(amt.xrp);
|
||||
else
|
||||
stream << to_string(amt.iou);
|
||||
if (amt.currency)
|
||||
stream << "/(" << *amt.currency << ")";
|
||||
if (amt.issuer)
|
||||
stream << "/" << *amt.issuer << "";
|
||||
return stream;
|
||||
}
|
||||
};
|
||||
|
||||
struct EitherAmount
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
bool native = false;
|
||||
#endif
|
||||
|
||||
union
|
||||
{
|
||||
IOUAmount iou = {};
|
||||
XRPAmount xrp;
|
||||
};
|
||||
|
||||
EitherAmount() = default;
|
||||
|
||||
explicit EitherAmount(IOUAmount const& a) : iou(a)
|
||||
{
|
||||
}
|
||||
|
||||
#if defined(__GNUC__) && !defined(__clang__)
|
||||
#pragma GCC diagnostic push
|
||||
// ignore warning about half of iou amount being uninitialized
|
||||
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
|
||||
#endif
|
||||
explicit EitherAmount(XRPAmount const& a) : xrp(a)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
native = true;
|
||||
#endif
|
||||
}
|
||||
#if defined(__GNUC__) && !defined(__clang__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
explicit EitherAmount(AmountSpec const& a)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
native = a.native;
|
||||
#endif
|
||||
if (a.native)
|
||||
xrp = a.xrp;
|
||||
else
|
||||
iou = a.iou;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
friend std::ostream&
|
||||
operator<<(std::ostream& stream, EitherAmount const& amt)
|
||||
{
|
||||
if (amt.native)
|
||||
stream << to_string(amt.xrp);
|
||||
else
|
||||
stream << to_string(amt.iou);
|
||||
return stream;
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
template <class T>
|
||||
T&
|
||||
get(EitherAmount& amt)
|
||||
{
|
||||
static_assert(sizeof(T) == -1, "Must used specialized function");
|
||||
return T(0);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline IOUAmount&
|
||||
get<IOUAmount>(EitherAmount& amt)
|
||||
{
|
||||
XRPL_ASSERT(!amt.native, "xrpl::get<IOUAmount>(EitherAmount&) : is not XRP");
|
||||
return amt.iou;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline XRPAmount&
|
||||
get<XRPAmount>(EitherAmount& amt)
|
||||
{
|
||||
XRPL_ASSERT(amt.native, "xrpl::get<XRPAmount>(EitherAmount&) : is XRP");
|
||||
return amt.xrp;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T const&
|
||||
get(EitherAmount const& amt)
|
||||
{
|
||||
static_assert(sizeof(T) == -1, "Must used specialized function");
|
||||
return T(0);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline IOUAmount const&
|
||||
get<IOUAmount>(EitherAmount const& amt)
|
||||
{
|
||||
XRPL_ASSERT(!amt.native, "xrpl::get<IOUAmount>(EitherAmount const&) : is not XRP");
|
||||
return amt.iou;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline XRPAmount const&
|
||||
get<XRPAmount>(EitherAmount const& amt)
|
||||
{
|
||||
XRPL_ASSERT(amt.native, "xrpl::get<XRPAmount>(EitherAmount const&) : is XRP");
|
||||
return amt.xrp;
|
||||
}
|
||||
|
||||
inline AmountSpec
|
||||
toAmountSpec(STAmount const& amt)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
amt.mantissa() < std::numeric_limits<std::int64_t>::max(),
|
||||
"xrpl::toAmountSpec(STAmount const&) : maximum mantissa");
|
||||
bool const isNeg = amt.negative();
|
||||
std::int64_t const sMant = isNeg ? -std::int64_t(amt.mantissa()) : amt.mantissa();
|
||||
AmountSpec result;
|
||||
|
||||
result.native = isXRP(amt);
|
||||
if (result.native)
|
||||
{
|
||||
result.xrp = XRPAmount(sMant);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.iou = IOUAmount(sMant, amt.exponent());
|
||||
result.issuer = amt.issue().account;
|
||||
result.currency = amt.issue().currency;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
inline EitherAmount
|
||||
toEitherAmount(STAmount const& amt)
|
||||
{
|
||||
if (isXRP(amt))
|
||||
return EitherAmount{amt.xrp()};
|
||||
return EitherAmount{amt.iou()};
|
||||
}
|
||||
|
||||
inline AmountSpec
|
||||
toAmountSpec(EitherAmount const& ea, std::optional<Currency> const& c)
|
||||
{
|
||||
AmountSpec r;
|
||||
r.native = (!c || isXRP(*c));
|
||||
r.currency = c;
|
||||
XRPL_ASSERT(
|
||||
ea.native == r.native,
|
||||
"xrpl::toAmountSpec(EitherAmount const&&, std::optional<Currency>) : "
|
||||
"matching native");
|
||||
if (r.native)
|
||||
{
|
||||
r.xrp = ea.xrp;
|
||||
}
|
||||
else
|
||||
{
|
||||
r.iou = ea.iou;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
24
include/xrpl/tx/paths/detail/FlatSets.h
Normal file
24
include/xrpl/tx/paths/detail/FlatSets.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/container/flat_set.hpp>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/** Given two flat sets dst and src, compute dst = dst union src
|
||||
|
||||
@param dst set to store the resulting union, and also a source of elements
|
||||
for the union
|
||||
@param src second source of elements for the union
|
||||
*/
|
||||
template <class T>
|
||||
void
|
||||
SetUnion(boost::container::flat_set<T>& dst, boost::container::flat_set<T> const& src)
|
||||
{
|
||||
if (src.empty())
|
||||
return;
|
||||
|
||||
dst.reserve(dst.size() + src.size());
|
||||
dst.insert(boost::container::ordered_unique_range_t{}, src.begin(), src.end());
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
329
include/xrpl/tx/paths/detail/FlowDebugInfo.h
Normal file
329
include/xrpl/tx/paths/detail/FlowDebugInfo.h
Normal file
@@ -0,0 +1,329 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/ledger/PaymentSandbox.h>
|
||||
#include <xrpl/protocol/IOUAmount.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
#include <xrpl/tx/paths/detail/AmountSpec.h>
|
||||
|
||||
#include <boost/container/flat_map.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
|
||||
namespace xrpl {
|
||||
namespace path {
|
||||
namespace detail {
|
||||
// Track performance information of a single payment
|
||||
struct FlowDebugInfo
|
||||
{
|
||||
using clock = std::chrono::high_resolution_clock;
|
||||
using time_point = clock::time_point;
|
||||
boost::container::flat_map<std::string, std::pair<time_point, time_point>> timePoints;
|
||||
boost::container::flat_map<std::string, std::size_t> counts;
|
||||
|
||||
struct PassInfo
|
||||
{
|
||||
PassInfo() = delete;
|
||||
PassInfo(bool nativeIn_, bool nativeOut_) : nativeIn(nativeIn_), nativeOut(nativeOut_)
|
||||
{
|
||||
}
|
||||
bool const nativeIn;
|
||||
bool const nativeOut;
|
||||
std::vector<EitherAmount> in;
|
||||
std::vector<EitherAmount> out;
|
||||
std::vector<size_t> numActive;
|
||||
|
||||
std::vector<std::vector<EitherAmount>> liquiditySrcIn;
|
||||
std::vector<std::vector<EitherAmount>> liquiditySrcOut;
|
||||
|
||||
void
|
||||
reserve(size_t s)
|
||||
{
|
||||
in.reserve(s);
|
||||
out.reserve(s);
|
||||
liquiditySrcIn.reserve(s);
|
||||
liquiditySrcOut.reserve(s);
|
||||
numActive.reserve(s);
|
||||
}
|
||||
|
||||
size_t
|
||||
size() const
|
||||
{
|
||||
return in.size();
|
||||
}
|
||||
|
||||
void
|
||||
push_back(EitherAmount const& in_amt, EitherAmount const& out_amt, std::size_t active)
|
||||
{
|
||||
in.push_back(in_amt);
|
||||
out.push_back(out_amt);
|
||||
numActive.push_back(active);
|
||||
}
|
||||
|
||||
void
|
||||
pushLiquiditySrc(EitherAmount const& eIn, EitherAmount const& eOut)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
!liquiditySrcIn.empty(),
|
||||
"xrpl::path::detail::FlowDebugInfo::pushLiquiditySrc : "
|
||||
"non-empty liquidity source");
|
||||
liquiditySrcIn.back().push_back(eIn);
|
||||
liquiditySrcOut.back().push_back(eOut);
|
||||
}
|
||||
|
||||
void
|
||||
newLiquidityPass()
|
||||
{
|
||||
auto const s = liquiditySrcIn.size();
|
||||
size_t const r = !numActive.empty() ? numActive.back() : 16;
|
||||
liquiditySrcIn.resize(s + 1);
|
||||
liquiditySrcIn.back().reserve(r);
|
||||
liquiditySrcOut.resize(s + 1);
|
||||
liquiditySrcOut.back().reserve(r);
|
||||
}
|
||||
};
|
||||
|
||||
PassInfo passInfo;
|
||||
|
||||
FlowDebugInfo() = delete;
|
||||
FlowDebugInfo(bool nativeIn, bool nativeOut) : passInfo(nativeIn, nativeOut)
|
||||
{
|
||||
timePoints.reserve(16);
|
||||
counts.reserve(16);
|
||||
passInfo.reserve(64);
|
||||
}
|
||||
|
||||
auto
|
||||
duration(std::string const& tag) const
|
||||
{
|
||||
auto i = timePoints.find(tag);
|
||||
if (i == timePoints.end())
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
UNREACHABLE(
|
||||
"xrpl::path::detail::FlowDebugInfo::duration : timepoint not "
|
||||
"found");
|
||||
return std::chrono::duration<double>(0);
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
auto const& t = i->second;
|
||||
return std::chrono::duration_cast<std::chrono::duration<double>>(t.second - t.first);
|
||||
}
|
||||
|
||||
std::size_t
|
||||
count(std::string const& tag) const
|
||||
{
|
||||
auto i = counts.find(tag);
|
||||
if (i == counts.end())
|
||||
return 0;
|
||||
return i->second;
|
||||
}
|
||||
|
||||
// Time the duration of the existence of the result
|
||||
auto
|
||||
timeBlock(std::string name)
|
||||
{
|
||||
struct Stopper
|
||||
{
|
||||
std::string tag;
|
||||
FlowDebugInfo* info;
|
||||
Stopper(std::string name, FlowDebugInfo& pi) : tag(std::move(name)), info(&pi)
|
||||
{
|
||||
auto const start = FlowDebugInfo::clock::now();
|
||||
info->timePoints.emplace(tag, std::make_pair(start, start));
|
||||
}
|
||||
~Stopper()
|
||||
{
|
||||
auto const end = FlowDebugInfo::clock::now();
|
||||
info->timePoints[tag].second = end;
|
||||
}
|
||||
Stopper(Stopper&&) = default;
|
||||
};
|
||||
return Stopper(std::move(name), *this);
|
||||
}
|
||||
|
||||
void
|
||||
inc(std::string const& tag)
|
||||
{
|
||||
auto i = counts.find(tag);
|
||||
if (i == counts.end())
|
||||
{
|
||||
counts[tag] = 1;
|
||||
}
|
||||
++i->second;
|
||||
}
|
||||
|
||||
void
|
||||
setCount(std::string const& tag, std::size_t c)
|
||||
{
|
||||
counts[tag] = c;
|
||||
}
|
||||
|
||||
std::size_t
|
||||
passCount() const
|
||||
{
|
||||
return passInfo.size();
|
||||
}
|
||||
|
||||
void
|
||||
pushPass(EitherAmount const& in, EitherAmount const& out, std::size_t activeStrands)
|
||||
{
|
||||
passInfo.push_back(in, out, activeStrands);
|
||||
}
|
||||
|
||||
void
|
||||
pushLiquiditySrc(EitherAmount const& in, EitherAmount const& out)
|
||||
{
|
||||
passInfo.pushLiquiditySrc(in, out);
|
||||
}
|
||||
|
||||
void
|
||||
newLiquidityPass()
|
||||
{
|
||||
passInfo.newLiquidityPass();
|
||||
}
|
||||
|
||||
std::string
|
||||
to_string(bool writePassInfo) const
|
||||
{
|
||||
std::ostringstream ostr;
|
||||
|
||||
auto const d = duration("main");
|
||||
|
||||
ostr << "duration: " << d.count() << ", pass_count: " << passCount();
|
||||
|
||||
if (writePassInfo)
|
||||
{
|
||||
auto write_list = [&ostr](auto const& vals, auto&& fun, char delim = ';') {
|
||||
ostr << '[';
|
||||
if (!vals.empty())
|
||||
{
|
||||
ostr << fun(vals[0]);
|
||||
for (size_t i = 1, e = vals.size(); i < e; ++i)
|
||||
ostr << delim << fun(vals[i]);
|
||||
}
|
||||
ostr << ']';
|
||||
};
|
||||
auto writeXrpAmtList = [&write_list](std::vector<EitherAmount> const& amts, char delim = ';') {
|
||||
auto get_val = [](EitherAmount const& a) -> std::string { return xrpl::to_string(a.xrp); };
|
||||
write_list(amts, get_val, delim);
|
||||
};
|
||||
auto writeIouAmtList = [&write_list](std::vector<EitherAmount> const& amts, char delim = ';') {
|
||||
auto get_val = [](EitherAmount const& a) -> std::string { return xrpl::to_string(a.iou); };
|
||||
write_list(amts, get_val, delim);
|
||||
};
|
||||
auto writeIntList = [&write_list](std::vector<size_t> const& vals, char delim = ';') {
|
||||
auto get_val = [](size_t const& v) -> size_t const& { return v; };
|
||||
write_list(vals, get_val);
|
||||
};
|
||||
auto writeNestedIouAmtList = [&ostr, &writeIouAmtList](std::vector<std::vector<EitherAmount>> const& amts) {
|
||||
ostr << '[';
|
||||
if (!amts.empty())
|
||||
{
|
||||
writeIouAmtList(amts[0], '|');
|
||||
for (size_t i = 1, e = amts.size(); i < e; ++i)
|
||||
{
|
||||
ostr << ';';
|
||||
writeIouAmtList(amts[i], '|');
|
||||
}
|
||||
}
|
||||
ostr << ']';
|
||||
};
|
||||
auto writeNestedXrpAmtList = [&ostr, &writeXrpAmtList](std::vector<std::vector<EitherAmount>> const& amts) {
|
||||
ostr << '[';
|
||||
if (!amts.empty())
|
||||
{
|
||||
writeXrpAmtList(amts[0], '|');
|
||||
for (size_t i = 1, e = amts.size(); i < e; ++i)
|
||||
{
|
||||
ostr << ';';
|
||||
writeXrpAmtList(amts[i], '|');
|
||||
}
|
||||
}
|
||||
ostr << ']';
|
||||
};
|
||||
|
||||
ostr << ", in_pass: ";
|
||||
if (passInfo.nativeIn)
|
||||
writeXrpAmtList(passInfo.in);
|
||||
else
|
||||
writeIouAmtList(passInfo.in);
|
||||
ostr << ", out_pass: ";
|
||||
if (passInfo.nativeOut)
|
||||
writeXrpAmtList(passInfo.out);
|
||||
else
|
||||
writeIouAmtList(passInfo.out);
|
||||
ostr << ", num_active: ";
|
||||
writeIntList(passInfo.numActive);
|
||||
if (!passInfo.liquiditySrcIn.empty() && !passInfo.liquiditySrcIn.back().empty())
|
||||
{
|
||||
ostr << ", l_src_in: ";
|
||||
if (passInfo.nativeIn)
|
||||
writeNestedXrpAmtList(passInfo.liquiditySrcIn);
|
||||
else
|
||||
writeNestedIouAmtList(passInfo.liquiditySrcIn);
|
||||
ostr << ", l_src_out: ";
|
||||
if (passInfo.nativeOut)
|
||||
writeNestedXrpAmtList(passInfo.liquiditySrcOut);
|
||||
else
|
||||
writeNestedIouAmtList(passInfo.liquiditySrcOut);
|
||||
}
|
||||
}
|
||||
|
||||
return ostr.str();
|
||||
}
|
||||
};
|
||||
|
||||
inline void
|
||||
writeDiffElement(std::ostringstream& ostr, std::pair<std::tuple<AccountID, AccountID, Currency>, STAmount> const& elem)
|
||||
{
|
||||
using namespace std;
|
||||
auto const k = elem.first;
|
||||
auto const v = elem.second;
|
||||
ostr << '[' << get<0>(k) << '|' << get<1>(k) << '|' << get<2>(k) << '|' << v << ']';
|
||||
};
|
||||
|
||||
template <class Iter>
|
||||
void
|
||||
writeDiffs(std::ostringstream& ostr, Iter begin, Iter end)
|
||||
{
|
||||
ostr << '[';
|
||||
if (begin != end)
|
||||
{
|
||||
writeDiffElement(ostr, *begin);
|
||||
++begin;
|
||||
}
|
||||
for (; begin != end; ++begin)
|
||||
{
|
||||
ostr << ';';
|
||||
writeDiffElement(ostr, *begin);
|
||||
}
|
||||
ostr << ']';
|
||||
};
|
||||
|
||||
using BalanceDiffs = std::pair<std::map<std::tuple<AccountID, AccountID, Currency>, STAmount>, XRPAmount>;
|
||||
|
||||
inline BalanceDiffs
|
||||
balanceDiffs(PaymentSandbox const& sb, ReadView const& rv)
|
||||
{
|
||||
return {sb.balanceChanges(rv), sb.xrpDestroyed()};
|
||||
}
|
||||
|
||||
inline std::string
|
||||
balanceDiffsToString(std::optional<BalanceDiffs> const& bd)
|
||||
{
|
||||
if (!bd)
|
||||
return std::string{};
|
||||
auto const& diffs = bd->first;
|
||||
auto const& xrpDestroyed = bd->second;
|
||||
std::ostringstream ostr;
|
||||
ostr << ", xrpDestroyed: " << to_string(xrpDestroyed);
|
||||
ostr << ", balanceDiffs: ";
|
||||
writeDiffs(ostr, diffs.begin(), diffs.end());
|
||||
return ostr.str();
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace path
|
||||
} // namespace xrpl
|
||||
591
include/xrpl/tx/paths/detail/Steps.h
Normal file
591
include/xrpl/tx/paths/detail/Steps.h
Normal file
@@ -0,0 +1,591 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/protocol/Quality.h>
|
||||
#include <xrpl/protocol/QualityFunction.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/tx/paths/detail/AmountSpec.h>
|
||||
|
||||
#include <boost/container/flat_set.hpp>
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace xrpl {
|
||||
class PaymentSandbox;
|
||||
class ReadView;
|
||||
class ApplyView;
|
||||
class AMMContext;
|
||||
|
||||
enum class DebtDirection { issues, redeems };
|
||||
enum class QualityDirection { in, out };
|
||||
enum class StrandDirection { forward, reverse };
|
||||
enum OfferCrossing { no = 0, yes = 1, sell = 2 };
|
||||
|
||||
inline bool
|
||||
redeems(DebtDirection dir)
|
||||
{
|
||||
return dir == DebtDirection::redeems;
|
||||
}
|
||||
|
||||
inline bool
|
||||
issues(DebtDirection dir)
|
||||
{
|
||||
return dir == DebtDirection::issues;
|
||||
}
|
||||
|
||||
/**
|
||||
A step in a payment path
|
||||
|
||||
There are five concrete step classes:
|
||||
DirectStepI is an IOU step between accounts
|
||||
BookStepII is an IOU/IOU offer book
|
||||
BookStepIX is an IOU/XRP offer book
|
||||
BookStepXI is an XRP/IOU offer book
|
||||
XRPEndpointStep is the source or destination account for XRP
|
||||
|
||||
Amounts may be transformed through a step in either the forward or the
|
||||
reverse direction. In the forward direction, the function `fwd` is used to
|
||||
find the amount the step would output given an input amount. In the reverse
|
||||
direction, the function `rev` is used to find the amount of input needed to
|
||||
produce the desired output.
|
||||
|
||||
Amounts are always transformed using liquidity with the same quality (quality
|
||||
is the amount out/amount in). For example, a BookStep may use multiple offers
|
||||
when executing `fwd` or `rev`, but all those offers will be from the same
|
||||
quality directory.
|
||||
|
||||
A step may not have enough liquidity to transform the entire requested
|
||||
amount. Both `fwd` and `rev` return a pair of amounts (one for input amount,
|
||||
one for output amount) that show how much of the requested amount the step
|
||||
was actually able to use.
|
||||
*/
|
||||
class Step
|
||||
{
|
||||
public:
|
||||
virtual ~Step() = default;
|
||||
|
||||
/**
|
||||
Find the amount we need to put into the step to get the requested out
|
||||
subject to liquidity limits
|
||||
|
||||
@param sb view with the strand's state of balances and offers
|
||||
@param afView view the state of balances before the strand runs
|
||||
this determines if an offer becomes unfunded or is found unfunded
|
||||
@param ofrsToRm offers found unfunded or in an error state are added to
|
||||
this collection
|
||||
@param out requested step output
|
||||
@return actual step input and output
|
||||
*/
|
||||
virtual std::pair<EitherAmount, EitherAmount>
|
||||
rev(PaymentSandbox& sb,
|
||||
ApplyView& afView,
|
||||
boost::container::flat_set<uint256>& ofrsToRm,
|
||||
EitherAmount const& out) = 0;
|
||||
|
||||
/**
|
||||
Find the amount we get out of the step given the input
|
||||
subject to liquidity limits
|
||||
|
||||
@param sb view with the strand's state of balances and offers
|
||||
@param afView view the state of balances before the strand runs
|
||||
this determines if an offer becomes unfunded or is found unfunded
|
||||
@param ofrsToRm offers found unfunded or in an error state are added to
|
||||
this collection
|
||||
@param in requested step input
|
||||
@return actual step input and output
|
||||
*/
|
||||
virtual std::pair<EitherAmount, EitherAmount>
|
||||
fwd(PaymentSandbox& sb,
|
||||
ApplyView& afView,
|
||||
boost::container::flat_set<uint256>& ofrsToRm,
|
||||
EitherAmount const& in) = 0;
|
||||
|
||||
/**
|
||||
Amount of currency computed coming into the Step the last time the
|
||||
step ran in reverse.
|
||||
*/
|
||||
virtual std::optional<EitherAmount>
|
||||
cachedIn() const = 0;
|
||||
|
||||
/**
|
||||
Amount of currency computed coming out of the Step the last time the
|
||||
step ran in reverse.
|
||||
*/
|
||||
virtual std::optional<EitherAmount>
|
||||
cachedOut() const = 0;
|
||||
|
||||
/**
|
||||
If this step is DirectStepI (IOU->IOU direct step), return the src
|
||||
account. This is needed for checkNoRipple.
|
||||
*/
|
||||
virtual std::optional<AccountID>
|
||||
directStepSrcAcct() const
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// for debugging. Return the src and dst accounts for a direct step
|
||||
// For XRP endpoints, one of src or dst will be the root account
|
||||
virtual std::optional<std::pair<AccountID, AccountID>>
|
||||
directStepAccts() const
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
If this step is a DirectStepI and the src redeems to the dst, return
|
||||
true, otherwise return false. If this step is a BookStep, return false if
|
||||
the owner pays the transfer fee, otherwise return true.
|
||||
|
||||
@param sb view with the strand's state of balances and offers
|
||||
@param dir reverse -> called from rev(); forward -> called from fwd().
|
||||
*/
|
||||
virtual DebtDirection
|
||||
debtDirection(ReadView const& sb, StrandDirection dir) const = 0;
|
||||
|
||||
/**
|
||||
If this step is a DirectStepI, return the quality in of the dst account.
|
||||
*/
|
||||
virtual std::uint32_t
|
||||
lineQualityIn(ReadView const&) const
|
||||
{
|
||||
return QUALITY_ONE;
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
/**
|
||||
Find an upper bound of quality for the step
|
||||
|
||||
@param v view to query the ledger state from
|
||||
@param prevStepDir Set to DebtDirection::redeems if the previous step redeems.
|
||||
@return A pair. The first element is the upper bound of quality for the step, or std::nullopt if the
|
||||
step is dry. The second element will be set to DebtDirection::redeems if this steps redeems,
|
||||
DebtDirection:issues if this step issues.
|
||||
@note it is an upper bound because offers on the books may be unfunded.
|
||||
If there is always a funded offer at the tip of the book, then we could
|
||||
rename this `theoreticalQuality` rather than `qualityUpperBound`. It
|
||||
could still differ from the actual quality, but except for "dust" amounts,
|
||||
it should be a good estimate for the actual quality.
|
||||
*/
|
||||
// clang-format on
|
||||
virtual std::pair<std::optional<Quality>, DebtDirection>
|
||||
qualityUpperBound(ReadView const& v, DebtDirection prevStepDir) const = 0;
|
||||
|
||||
/** Get QualityFunction. Used in one path optimization where
|
||||
* the quality function is non-constant (has AMM) and there is
|
||||
* limitQuality. QualityFunction allows calculation of
|
||||
* required path output given requested limitQuality.
|
||||
* All steps, except for BookStep have the default
|
||||
* implementation.
|
||||
*/
|
||||
virtual std::pair<std::optional<QualityFunction>, DebtDirection>
|
||||
getQualityFunc(ReadView const& v, DebtDirection prevStepDir) const;
|
||||
|
||||
/** Return the number of offers consumed or partially consumed the last time
|
||||
the step ran, including expired and unfunded offers.
|
||||
|
||||
N.B. This this not the total number offers consumed by this step for the
|
||||
entire payment, it is only the number the last time it ran. Offers may
|
||||
be partially consumed multiple times during a payment.
|
||||
*/
|
||||
virtual std::uint32_t
|
||||
offersUsed() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
If this step is a BookStep, return the book.
|
||||
*/
|
||||
virtual std::optional<Book>
|
||||
bookStepBook() const
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
Check if amount is zero
|
||||
*/
|
||||
virtual bool
|
||||
isZero(EitherAmount const& out) const = 0;
|
||||
|
||||
/**
|
||||
Return true if the step should be considered inactive.
|
||||
A strand that has additional liquidity may be marked inactive if a step
|
||||
has consumed too many offers.
|
||||
*/
|
||||
virtual bool
|
||||
inactive() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
Return true if Out of lhs == Out of rhs.
|
||||
*/
|
||||
virtual bool
|
||||
equalOut(EitherAmount const& lhs, EitherAmount const& rhs) const = 0;
|
||||
|
||||
/**
|
||||
Return true if In of lhs == In of rhs.
|
||||
*/
|
||||
virtual bool
|
||||
equalIn(EitherAmount const& lhs, EitherAmount const& rhs) const = 0;
|
||||
|
||||
/**
|
||||
Check that the step can correctly execute in the forward direction
|
||||
|
||||
@param sb view with the strands state of balances and offers
|
||||
@param afView view the state of balances before the strand runs
|
||||
this determines if an offer becomes unfunded or is found unfunded
|
||||
@param in requested step input
|
||||
@return first element is true if step is valid, second element is out
|
||||
amount
|
||||
*/
|
||||
virtual std::pair<bool, EitherAmount>
|
||||
validFwd(PaymentSandbox& sb, ApplyView& afView, EitherAmount const& in) = 0;
|
||||
|
||||
/** Return true if lhs == rhs.
|
||||
|
||||
@param lhs Step to compare.
|
||||
@param rhs Step to compare.
|
||||
@return true if lhs == rhs.
|
||||
*/
|
||||
friend bool
|
||||
operator==(Step const& lhs, Step const& rhs)
|
||||
{
|
||||
return lhs.equal(rhs);
|
||||
}
|
||||
|
||||
/** Return true if lhs != rhs.
|
||||
|
||||
@param lhs Step to compare.
|
||||
@param rhs Step to compare.
|
||||
@return true if lhs != rhs.
|
||||
*/
|
||||
friend bool
|
||||
operator!=(Step const& lhs, Step const& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
/** Streaming operator for a Step. */
|
||||
friend std::ostream&
|
||||
operator<<(std::ostream& stream, Step const& step)
|
||||
{
|
||||
stream << step.logString();
|
||||
return stream;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual std::string
|
||||
logString() const = 0;
|
||||
|
||||
virtual bool
|
||||
equal(Step const& rhs) const = 0;
|
||||
};
|
||||
|
||||
inline std::pair<std::optional<QualityFunction>, DebtDirection>
|
||||
Step::getQualityFunc(ReadView const& v, DebtDirection prevStepDir) const
|
||||
{
|
||||
if (auto const res = qualityUpperBound(v, prevStepDir); res.first)
|
||||
return {QualityFunction{*res.first, QualityFunction::CLOBLikeTag{}}, res.second};
|
||||
else
|
||||
return {std::nullopt, res.second};
|
||||
}
|
||||
|
||||
/// @cond INTERNAL
|
||||
using Strand = std::vector<std::unique_ptr<Step>>;
|
||||
|
||||
inline std::uint32_t
|
||||
offersUsed(Strand const& strand)
|
||||
{
|
||||
std::uint32_t r = 0;
|
||||
for (auto const& step : strand)
|
||||
{
|
||||
if (step)
|
||||
r += step->offersUsed();
|
||||
}
|
||||
return r;
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
/// @cond INTERNAL
|
||||
inline bool
|
||||
operator==(Strand const& lhs, Strand const& rhs)
|
||||
{
|
||||
if (lhs.size() != rhs.size())
|
||||
return false;
|
||||
for (size_t i = 0, e = lhs.size(); i != e; ++i)
|
||||
if (*lhs[i] != *rhs[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
/*
|
||||
Normalize a path by inserting implied accounts and offers
|
||||
|
||||
@param src Account that is sending assets
|
||||
@param dst Account that is receiving assets
|
||||
@param deliver Asset the dst account will receive
|
||||
(if issuer of deliver == dst, then accept any issuer)
|
||||
@param sendMax Optional asset to send.
|
||||
@param path Liquidity sources to use for this strand of the payment. The path
|
||||
contains an ordered collection of the offer books to use and
|
||||
accounts to ripple through.
|
||||
@return error code and normalized path
|
||||
*/
|
||||
std::pair<TER, STPath>
|
||||
normalizePath(
|
||||
AccountID const& src,
|
||||
AccountID const& dst,
|
||||
Issue const& deliver,
|
||||
std::optional<Issue> const& sendMaxIssue,
|
||||
STPath const& path);
|
||||
|
||||
/**
|
||||
Create a Strand for the specified path
|
||||
|
||||
@param sb view for trust lines, balances, and attributes like auth and freeze
|
||||
@param src Account that is sending assets
|
||||
@param dst Account that is receiving assets
|
||||
@param deliver Asset the dst account will receive
|
||||
(if issuer of deliver == dst, then accept any issuer)
|
||||
@param limitQuality Offer crossing BookSteps use this value in an
|
||||
optimization. If, during direct offer crossing, the
|
||||
quality of the tip of the book drops below this value,
|
||||
then evaluating the strand can stop.
|
||||
@param sendMaxIssue Optional asset to send.
|
||||
@param path Liquidity sources to use for this strand of the payment. The path
|
||||
contains an ordered collection of the offer books to use and
|
||||
accounts to ripple through.
|
||||
@param ownerPaysTransferFee false -> charge sender; true -> charge offer
|
||||
owner
|
||||
@param offerCrossing false -> payment; true -> offer crossing
|
||||
@param ammContext counts iterations with AMM offers
|
||||
@param domainID the domain that order books will use
|
||||
@param j Journal for logging messages
|
||||
@return Error code and constructed Strand
|
||||
*/
|
||||
std::pair<TER, Strand>
|
||||
toStrand(
|
||||
ReadView const& sb,
|
||||
AccountID const& src,
|
||||
AccountID const& dst,
|
||||
Issue const& deliver,
|
||||
std::optional<Quality> const& limitQuality,
|
||||
std::optional<Issue> const& sendMaxIssue,
|
||||
STPath const& path,
|
||||
bool ownerPaysTransferFee,
|
||||
OfferCrossing offerCrossing,
|
||||
AMMContext& ammContext,
|
||||
std::optional<uint256> const& domainID,
|
||||
beast::Journal j);
|
||||
|
||||
/**
|
||||
Create a Strand for each specified path (including the default path, if
|
||||
indicated)
|
||||
|
||||
@param sb View for trust lines, balances, and attributes like auth and freeze
|
||||
@param src Account that is sending assets
|
||||
@param dst Account that is receiving assets
|
||||
@param deliver Asset the dst account will receive
|
||||
(if issuer of deliver == dst, then accept any issuer)
|
||||
@param limitQuality Offer crossing BookSteps use this value in an
|
||||
optimization. If, during direct offer crossing, the
|
||||
quality of the tip of the book drops below this value,
|
||||
then evaluating the strand can stop.
|
||||
@param sendMax Optional asset to send.
|
||||
@param paths Paths to use to fulfill the payment. Each path in the pathset
|
||||
contains an ordered collection of the offer books to use and
|
||||
accounts to ripple through.
|
||||
@param addDefaultPath Determines if the default path should be included
|
||||
@param ownerPaysTransferFee false -> charge sender; true -> charge offer
|
||||
owner
|
||||
@param offerCrossing false -> payment; true -> offer crossing
|
||||
@param ammContext counts iterations with AMM offers
|
||||
@param domainID the domain that order books will use
|
||||
@param j Journal for logging messages
|
||||
@return error code and collection of strands
|
||||
*/
|
||||
std::pair<TER, std::vector<Strand>>
|
||||
toStrands(
|
||||
ReadView const& sb,
|
||||
AccountID const& src,
|
||||
AccountID const& dst,
|
||||
Issue const& deliver,
|
||||
std::optional<Quality> const& limitQuality,
|
||||
std::optional<Issue> const& sendMax,
|
||||
STPathSet const& paths,
|
||||
bool addDefaultPath,
|
||||
bool ownerPaysTransferFee,
|
||||
OfferCrossing offerCrossing,
|
||||
AMMContext& ammContext,
|
||||
std::optional<uint256> const& domainID,
|
||||
beast::Journal j);
|
||||
|
||||
/// @cond INTERNAL
|
||||
template <class TIn, class TOut, class TDerived>
|
||||
struct StepImp : public Step
|
||||
{
|
||||
explicit StepImp() = default;
|
||||
|
||||
std::pair<EitherAmount, EitherAmount>
|
||||
rev(PaymentSandbox& sb,
|
||||
ApplyView& afView,
|
||||
boost::container::flat_set<uint256>& ofrsToRm,
|
||||
EitherAmount const& out) override
|
||||
{
|
||||
auto const r = static_cast<TDerived*>(this)->revImp(sb, afView, ofrsToRm, get<TOut>(out));
|
||||
return {EitherAmount(r.first), EitherAmount(r.second)};
|
||||
}
|
||||
|
||||
// Given the requested amount to consume, compute the amount produced.
|
||||
// Return the consumed/produced
|
||||
std::pair<EitherAmount, EitherAmount>
|
||||
fwd(PaymentSandbox& sb,
|
||||
ApplyView& afView,
|
||||
boost::container::flat_set<uint256>& ofrsToRm,
|
||||
EitherAmount const& in) override
|
||||
{
|
||||
auto const r = static_cast<TDerived*>(this)->fwdImp(sb, afView, ofrsToRm, get<TIn>(in));
|
||||
return {EitherAmount(r.first), EitherAmount(r.second)};
|
||||
}
|
||||
|
||||
bool
|
||||
isZero(EitherAmount const& out) const override
|
||||
{
|
||||
return get<TOut>(out) == beast::zero;
|
||||
}
|
||||
|
||||
bool
|
||||
equalOut(EitherAmount const& lhs, EitherAmount const& rhs) const override
|
||||
{
|
||||
return get<TOut>(lhs) == get<TOut>(rhs);
|
||||
}
|
||||
|
||||
bool
|
||||
equalIn(EitherAmount const& lhs, EitherAmount const& rhs) const override
|
||||
{
|
||||
return get<TIn>(lhs) == get<TIn>(rhs);
|
||||
}
|
||||
};
|
||||
/// @endcond
|
||||
|
||||
/// @cond INTERNAL
|
||||
// Thrown when unexpected errors occur
|
||||
class FlowException : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
TER ter;
|
||||
|
||||
FlowException(TER t, std::string const& msg) : std::runtime_error(msg), ter(t)
|
||||
{
|
||||
}
|
||||
|
||||
explicit FlowException(TER t) : std::runtime_error(transHuman(t)), ter(t)
|
||||
{
|
||||
}
|
||||
};
|
||||
/// @endcond
|
||||
|
||||
/// @cond INTERNAL
|
||||
// Check equal with tolerance
|
||||
bool
|
||||
checkNear(IOUAmount const& expected, IOUAmount const& actual);
|
||||
bool
|
||||
checkNear(XRPAmount const& expected, XRPAmount const& actual);
|
||||
/// @endcond
|
||||
|
||||
/**
|
||||
Context needed to build Strand Steps and for error checking
|
||||
*/
|
||||
struct StrandContext
|
||||
{
|
||||
ReadView const& view; ///< Current ReadView
|
||||
AccountID const strandSrc; ///< Strand source account
|
||||
AccountID const strandDst; ///< Strand destination account
|
||||
Issue const strandDeliver; ///< Issue strand delivers
|
||||
std::optional<Quality> const limitQuality; ///< Worst accepted quality
|
||||
bool const isFirst; ///< true if Step is first in Strand
|
||||
bool const isLast = false; ///< true if Step is last in Strand
|
||||
bool const ownerPaysTransferFee; ///< true if owner, not sender, pays fee
|
||||
OfferCrossing const offerCrossing; ///< Yes/Sell if offer crossing, not payment
|
||||
bool const isDefaultPath; ///< true if Strand is default path
|
||||
size_t const strandSize; ///< Length of Strand
|
||||
/** The previous step in the strand. Needed to check the no ripple
|
||||
constraint
|
||||
*/
|
||||
Step const* const prevStep = nullptr;
|
||||
/** A strand may not include the same account node more than once
|
||||
in the same currency. In a direct step, an account will show up
|
||||
at most twice: once as a src and once as a dst (hence the two element
|
||||
array). The strandSrc and strandDst will only show up once each.
|
||||
*/
|
||||
std::array<boost::container::flat_set<Issue>, 2>& seenDirectIssues;
|
||||
/** A strand may not include an offer that output the same issue more
|
||||
than once
|
||||
*/
|
||||
boost::container::flat_set<Issue>& seenBookOuts;
|
||||
AMMContext& ammContext;
|
||||
std::optional<uint256> domainID; // the domain the order book will use
|
||||
beast::Journal const j;
|
||||
|
||||
/** StrandContext constructor. */
|
||||
StrandContext(
|
||||
ReadView const& view_,
|
||||
std::vector<std::unique_ptr<Step>> const& strand_,
|
||||
// A strand may not include an inner node that
|
||||
// replicates the source or destination.
|
||||
AccountID const& strandSrc_,
|
||||
AccountID const& strandDst_,
|
||||
Issue const& strandDeliver_,
|
||||
std::optional<Quality> const& limitQuality_,
|
||||
bool isLast_,
|
||||
bool ownerPaysTransferFee_,
|
||||
OfferCrossing offerCrossing_,
|
||||
bool isDefaultPath_,
|
||||
std::array<boost::container::flat_set<Issue>, 2>& seenDirectIssues_, ///< For detecting currency loops
|
||||
boost::container::flat_set<Issue>& seenBookOuts_, ///< For detecting book loops
|
||||
AMMContext& ammContext_,
|
||||
std::optional<uint256> const& domainID,
|
||||
beast::Journal j_); ///< Journal for logging
|
||||
};
|
||||
|
||||
/// @cond INTERNAL
|
||||
namespace test {
|
||||
// Needed for testing
|
||||
bool
|
||||
directStepEqual(Step const& step, AccountID const& src, AccountID const& dst, Currency const& currency);
|
||||
|
||||
bool
|
||||
xrpEndpointStepEqual(Step const& step, AccountID const& acc);
|
||||
|
||||
bool
|
||||
bookStepEqual(Step const& step, xrpl::Book const& book);
|
||||
} // namespace test
|
||||
|
||||
std::pair<TER, std::unique_ptr<Step>>
|
||||
make_DirectStepI(StrandContext const& ctx, AccountID const& src, AccountID const& dst, Currency const& c);
|
||||
|
||||
std::pair<TER, std::unique_ptr<Step>>
|
||||
make_BookStepII(StrandContext const& ctx, Issue const& in, Issue const& out);
|
||||
|
||||
std::pair<TER, std::unique_ptr<Step>>
|
||||
make_BookStepIX(StrandContext const& ctx, Issue const& in);
|
||||
|
||||
std::pair<TER, std::unique_ptr<Step>>
|
||||
make_BookStepXI(StrandContext const& ctx, Issue const& out);
|
||||
|
||||
std::pair<TER, std::unique_ptr<Step>>
|
||||
make_XRPEndpointStep(StrandContext const& ctx, AccountID const& acc);
|
||||
|
||||
template <class InAmt, class OutAmt>
|
||||
bool
|
||||
isDirectXrpToXrp(Strand const& strand);
|
||||
/// @endcond
|
||||
|
||||
} // namespace xrpl
|
||||
784
include/xrpl/tx/paths/detail/StrandFlow.h
Normal file
784
include/xrpl/tx/paths/detail/StrandFlow.h
Normal file
@@ -0,0 +1,784 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/ledger/Credit.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/IOUAmount.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
#include <xrpl/tx/paths/Flow.h>
|
||||
#include <xrpl/tx/paths/detail/AmountSpec.h>
|
||||
#include <xrpl/tx/paths/detail/FlatSets.h>
|
||||
#include <xrpl/tx/paths/detail/FlowDebugInfo.h>
|
||||
#include <xrpl/tx/paths/detail/Steps.h>
|
||||
#include <xrpl/tx/transactors/AMM/AMMContext.h>
|
||||
#include <xrpl/tx/transactors/AMM/AMMHelpers.h>
|
||||
|
||||
#include <boost/container/flat_set.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <numeric>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/** Result of flow() execution of a single Strand. */
|
||||
template <class TInAmt, class TOutAmt>
|
||||
struct StrandResult
|
||||
{
|
||||
bool success; ///< Strand succeeded
|
||||
TInAmt in = beast::zero; ///< Currency amount in
|
||||
TOutAmt out = beast::zero; ///< Currency amount out
|
||||
std::optional<PaymentSandbox> sandbox; ///< Resulting Sandbox state
|
||||
boost::container::flat_set<uint256> ofrsToRm; ///< Offers to remove
|
||||
// Num offers consumed or partially consumed (includes expired and unfunded
|
||||
// offers)
|
||||
std::uint32_t ofrsUsed = 0;
|
||||
// strand can be inactive if there is no more liquidity or too many offers
|
||||
// have been consumed
|
||||
bool inactive = false; ///< Strand should not considered as a further
|
||||
///< source of liquidity (dry)
|
||||
|
||||
/** Strand result constructor */
|
||||
StrandResult() = default;
|
||||
|
||||
StrandResult(
|
||||
Strand const& strand,
|
||||
TInAmt const& in_,
|
||||
TOutAmt const& out_,
|
||||
PaymentSandbox&& sandbox_,
|
||||
boost::container::flat_set<uint256> ofrsToRm_,
|
||||
bool inactive_)
|
||||
: success(true)
|
||||
, in(in_)
|
||||
, out(out_)
|
||||
, sandbox(std::move(sandbox_))
|
||||
, ofrsToRm(std::move(ofrsToRm_))
|
||||
, ofrsUsed(offersUsed(strand))
|
||||
, inactive(inactive_)
|
||||
{
|
||||
}
|
||||
|
||||
StrandResult(Strand const& strand, boost::container::flat_set<uint256> ofrsToRm_)
|
||||
: success(false), ofrsToRm(std::move(ofrsToRm_)), ofrsUsed(offersUsed(strand))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
Request `out` amount from a strand
|
||||
|
||||
@param baseView Trust lines and balances
|
||||
@param strand Steps of Accounts to ripple through and offer books to use
|
||||
@param maxIn Max amount of input allowed
|
||||
@param out Amount of output requested from the strand
|
||||
@param j Journal to write log messages to
|
||||
@return Actual amount in and out from the strand, errors, offers to remove,
|
||||
and payment sandbox
|
||||
*/
|
||||
template <class TInAmt, class TOutAmt>
|
||||
StrandResult<TInAmt, TOutAmt>
|
||||
flow(
|
||||
PaymentSandbox const& baseView,
|
||||
Strand const& strand,
|
||||
std::optional<TInAmt> const& maxIn,
|
||||
TOutAmt const& out,
|
||||
beast::Journal j)
|
||||
{
|
||||
using Result = StrandResult<TInAmt, TOutAmt>;
|
||||
if (strand.empty())
|
||||
{
|
||||
JLOG(j.warn()) << "Empty strand passed to Liquidity";
|
||||
return {};
|
||||
}
|
||||
|
||||
boost::container::flat_set<uint256> ofrsToRm;
|
||||
|
||||
if (isDirectXrpToXrp<TInAmt, TOutAmt>(strand))
|
||||
{
|
||||
return Result{strand, std::move(ofrsToRm)};
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
std::size_t const s = strand.size();
|
||||
|
||||
std::size_t limitingStep = strand.size();
|
||||
std::optional<PaymentSandbox> sb(&baseView);
|
||||
// The "all funds" view determines if an offer becomes unfunded or is
|
||||
// found unfunded
|
||||
// These are the account balances before the strand executes
|
||||
std::optional<PaymentSandbox> afView(&baseView);
|
||||
EitherAmount limitStepOut;
|
||||
{
|
||||
EitherAmount stepOut(out);
|
||||
for (auto i = s; i--;)
|
||||
{
|
||||
auto r = strand[i]->rev(*sb, *afView, ofrsToRm, stepOut);
|
||||
if (strand[i]->isZero(r.second))
|
||||
{
|
||||
JLOG(j.trace()) << "Strand found dry in rev";
|
||||
return Result{strand, std::move(ofrsToRm)};
|
||||
}
|
||||
|
||||
if (i == 0 && maxIn && *maxIn < get<TInAmt>(r.first))
|
||||
{
|
||||
// limiting - exceeded maxIn
|
||||
// Throw out previous results
|
||||
sb.emplace(&baseView);
|
||||
limitingStep = i;
|
||||
|
||||
// re-execute the limiting step
|
||||
r = strand[i]->fwd(*sb, *afView, ofrsToRm, EitherAmount(*maxIn));
|
||||
limitStepOut = r.second;
|
||||
|
||||
if (strand[i]->isZero(r.second))
|
||||
{
|
||||
JLOG(j.trace()) << "First step found dry";
|
||||
return Result{strand, std::move(ofrsToRm)};
|
||||
}
|
||||
if (get<TInAmt>(r.first) != *maxIn)
|
||||
{
|
||||
// Something is very wrong
|
||||
// throwing out the sandbox can only increase liquidity
|
||||
// yet the limiting is still limiting
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.fatal()) << "Re-executed limiting step failed. r.first: "
|
||||
<< to_string(get<TInAmt>(r.first)) << " maxIn: " << to_string(*maxIn);
|
||||
UNREACHABLE(
|
||||
"xrpl::flow : first step re-executing the "
|
||||
"limiting step failed");
|
||||
return Result{strand, std::move(ofrsToRm)};
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
}
|
||||
else if (!strand[i]->equalOut(r.second, stepOut))
|
||||
{
|
||||
// limiting
|
||||
// Throw out previous results
|
||||
sb.emplace(&baseView);
|
||||
afView.emplace(&baseView);
|
||||
limitingStep = i;
|
||||
|
||||
// re-execute the limiting step
|
||||
stepOut = r.second;
|
||||
r = strand[i]->rev(*sb, *afView, ofrsToRm, stepOut);
|
||||
limitStepOut = r.second;
|
||||
|
||||
if (strand[i]->isZero(r.second))
|
||||
{
|
||||
// A tiny input amount can cause this step to output
|
||||
// zero. I.e. 10^-80 IOU into an IOU -> XRP offer.
|
||||
JLOG(j.trace()) << "Limiting step found dry";
|
||||
return Result{strand, std::move(ofrsToRm)};
|
||||
}
|
||||
if (!strand[i]->equalOut(r.second, stepOut))
|
||||
{
|
||||
// Something is very wrong
|
||||
// throwing out the sandbox can only increase liquidity
|
||||
// yet the limiting is still limiting
|
||||
// LCOV_EXCL_START
|
||||
#ifndef NDEBUG
|
||||
JLOG(j.fatal()) << "Re-executed limiting step failed. r.second: " << r.second
|
||||
<< " stepOut: " << stepOut;
|
||||
#else
|
||||
JLOG(j.fatal()) << "Re-executed limiting step failed";
|
||||
#endif
|
||||
UNREACHABLE(
|
||||
"xrpl::flow : limiting step re-executing the "
|
||||
"limiting step failed");
|
||||
return Result{strand, std::move(ofrsToRm)};
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
}
|
||||
|
||||
// prev node needs to produce what this node wants to consume
|
||||
stepOut = r.first;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
EitherAmount stepIn(limitStepOut);
|
||||
for (auto i = limitingStep + 1; i < s; ++i)
|
||||
{
|
||||
auto const r = strand[i]->fwd(*sb, *afView, ofrsToRm, stepIn);
|
||||
if (strand[i]->isZero(r.second))
|
||||
{
|
||||
// A tiny input amount can cause this step to output zero.
|
||||
// I.e. 10^-80 IOU into an IOU -> XRP offer.
|
||||
JLOG(j.trace()) << "Non-limiting step found dry";
|
||||
return Result{strand, std::move(ofrsToRm)};
|
||||
}
|
||||
if (!strand[i]->equalIn(r.first, stepIn))
|
||||
{
|
||||
// The limits should already have been found, so executing a
|
||||
// strand forward from the limiting step should not find a
|
||||
// new limit
|
||||
// LCOV_EXCL_START
|
||||
#ifndef NDEBUG
|
||||
JLOG(j.fatal()) << "Re-executed forward pass failed. r.first: " << r.first << " stepIn: " << stepIn;
|
||||
#else
|
||||
JLOG(j.fatal()) << "Re-executed forward pass failed";
|
||||
#endif
|
||||
UNREACHABLE(
|
||||
"xrpl::flow : non-limiting step re-executing the "
|
||||
"forward pass failed");
|
||||
return Result{strand, std::move(ofrsToRm)};
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
stepIn = r.second;
|
||||
}
|
||||
}
|
||||
|
||||
auto const strandIn = *strand.front()->cachedIn();
|
||||
auto const strandOut = *strand.back()->cachedOut();
|
||||
|
||||
#ifndef NDEBUG
|
||||
{
|
||||
// Check that the strand will execute as intended
|
||||
// Re-executing the strand will change the cached values
|
||||
PaymentSandbox checkSB(&baseView);
|
||||
PaymentSandbox checkAfView(&baseView);
|
||||
EitherAmount stepIn(*strand[0]->cachedIn());
|
||||
for (auto i = 0; i < s; ++i)
|
||||
{
|
||||
bool valid;
|
||||
std::tie(valid, stepIn) = strand[i]->validFwd(checkSB, checkAfView, stepIn);
|
||||
if (!valid)
|
||||
{
|
||||
JLOG(j.warn()) << "Strand re-execute check failed. Step: " << i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bool const inactive = std::any_of(
|
||||
strand.begin(), strand.end(), [](std::unique_ptr<Step> const& step) { return step->inactive(); });
|
||||
|
||||
return Result(
|
||||
strand, get<TInAmt>(strandIn), get<TOutAmt>(strandOut), std::move(*sb), std::move(ofrsToRm), inactive);
|
||||
}
|
||||
catch (FlowException const&)
|
||||
{
|
||||
return Result{strand, std::move(ofrsToRm)};
|
||||
}
|
||||
}
|
||||
|
||||
/// @cond INTERNAL
|
||||
template <class TInAmt, class TOutAmt>
|
||||
struct FlowResult
|
||||
{
|
||||
TInAmt in = beast::zero;
|
||||
TOutAmt out = beast::zero;
|
||||
std::optional<PaymentSandbox> sandbox;
|
||||
boost::container::flat_set<uint256> removableOffers;
|
||||
TER ter = temUNKNOWN;
|
||||
|
||||
FlowResult() = default;
|
||||
|
||||
FlowResult(
|
||||
TInAmt const& in_,
|
||||
TOutAmt const& out_,
|
||||
PaymentSandbox&& sandbox_,
|
||||
boost::container::flat_set<uint256> ofrsToRm)
|
||||
: in(in_), out(out_), sandbox(std::move(sandbox_)), removableOffers(std::move(ofrsToRm)), ter(tesSUCCESS)
|
||||
{
|
||||
}
|
||||
|
||||
FlowResult(TER ter_, boost::container::flat_set<uint256> ofrsToRm) : removableOffers(std::move(ofrsToRm)), ter(ter_)
|
||||
{
|
||||
}
|
||||
|
||||
FlowResult(TER ter_, TInAmt const& in_, TOutAmt const& out_, boost::container::flat_set<uint256> ofrsToRm)
|
||||
: in(in_), out(out_), removableOffers(std::move(ofrsToRm)), ter(ter_)
|
||||
{
|
||||
}
|
||||
};
|
||||
/// @endcond
|
||||
|
||||
/// @cond INTERNAL
|
||||
inline std::optional<Quality>
|
||||
qualityUpperBound(ReadView const& v, Strand const& strand)
|
||||
{
|
||||
Quality q{STAmount::uRateOne};
|
||||
std::optional<Quality> stepQ;
|
||||
DebtDirection dir = DebtDirection::issues;
|
||||
for (auto const& step : strand)
|
||||
{
|
||||
if (std::tie(stepQ, dir) = step->qualityUpperBound(v, dir); stepQ)
|
||||
q = composed_quality(q, *stepQ);
|
||||
else
|
||||
return std::nullopt;
|
||||
}
|
||||
return q;
|
||||
};
|
||||
/// @endcond
|
||||
|
||||
/// @cond INTERNAL
|
||||
/** Limit remaining out only if one strand and limitQuality is included.
|
||||
* Targets one path payment with AMM where the average quality is linear
|
||||
* and instant quality is quadratic function of output. Calculating quality
|
||||
* function for the whole strand enables figuring out required output
|
||||
* to produce requested strand's limitQuality. Reducing the output,
|
||||
* increases quality of AMM steps, increasing the strand's composite
|
||||
* quality as the result.
|
||||
*/
|
||||
template <typename TOutAmt>
|
||||
inline TOutAmt
|
||||
limitOut(ReadView const& v, Strand const& strand, TOutAmt const& remainingOut, Quality const& limitQuality)
|
||||
{
|
||||
std::optional<QualityFunction> stepQualityFunc;
|
||||
std::optional<QualityFunction> qf;
|
||||
DebtDirection dir = DebtDirection::issues;
|
||||
for (auto const& step : strand)
|
||||
{
|
||||
if (std::tie(stepQualityFunc, dir) = step->getQualityFunc(v, dir); stepQualityFunc)
|
||||
{
|
||||
if (!qf)
|
||||
qf = stepQualityFunc;
|
||||
else
|
||||
qf->combine(*stepQualityFunc);
|
||||
}
|
||||
else
|
||||
return remainingOut;
|
||||
}
|
||||
|
||||
// QualityFunction is constant
|
||||
if (!qf || qf->isConst())
|
||||
return remainingOut;
|
||||
|
||||
auto const out = [&]() {
|
||||
if (auto const out = qf->outFromAvgQ(limitQuality); !out)
|
||||
return remainingOut;
|
||||
else if constexpr (std::is_same_v<TOutAmt, XRPAmount>)
|
||||
return XRPAmount{*out};
|
||||
else if constexpr (std::is_same_v<TOutAmt, IOUAmount>)
|
||||
return IOUAmount{*out};
|
||||
else
|
||||
return STAmount{remainingOut.issue(), out->mantissa(), out->exponent()};
|
||||
}();
|
||||
// A tiny difference could be due to the round off
|
||||
if (withinRelativeDistance(out, remainingOut, Number(1, -9)))
|
||||
return remainingOut;
|
||||
return std::min(out, remainingOut);
|
||||
};
|
||||
/// @endcond
|
||||
|
||||
/// @cond INTERNAL
|
||||
/* Track the non-dry strands
|
||||
|
||||
flow will search the non-dry strands (stored in `cur_`) for the best
|
||||
available liquidity If flow doesn't use all the liquidity of a strand, that
|
||||
strand is added to `next_`. The strands in `next_` are searched after the
|
||||
current best liquidity is used.
|
||||
*/
|
||||
class ActiveStrands
|
||||
{
|
||||
private:
|
||||
// Strands to be explored for liquidity
|
||||
std::vector<Strand const*> cur_;
|
||||
// Strands that may be explored for liquidity on the next iteration
|
||||
std::vector<Strand const*> next_;
|
||||
|
||||
public:
|
||||
ActiveStrands(std::vector<Strand> const& strands)
|
||||
{
|
||||
cur_.reserve(strands.size());
|
||||
next_.reserve(strands.size());
|
||||
for (auto& strand : strands)
|
||||
next_.push_back(&strand);
|
||||
}
|
||||
|
||||
// Start a new iteration in the search for liquidity
|
||||
// Set the current strands to the strands in `next_`
|
||||
void
|
||||
activateNext(ReadView const& v, std::optional<Quality> const& limitQuality)
|
||||
{
|
||||
// add the strands in `next_` to `cur_`, sorted by theoretical quality.
|
||||
// Best quality first.
|
||||
cur_.clear();
|
||||
if (!next_.empty())
|
||||
{
|
||||
std::vector<std::pair<Quality, Strand const*>> strandQualities;
|
||||
strandQualities.reserve(next_.size());
|
||||
if (next_.size() > 1) // no need to sort one strand
|
||||
{
|
||||
for (Strand const* strand : next_)
|
||||
{
|
||||
if (!strand)
|
||||
{
|
||||
// should not happen
|
||||
continue;
|
||||
}
|
||||
if (auto const qual = qualityUpperBound(v, *strand))
|
||||
{
|
||||
if (limitQuality && *qual < *limitQuality)
|
||||
{
|
||||
// If a strand's quality is ever over limitQuality
|
||||
// it is no longer part of the candidate set. Note
|
||||
// that when transfer fees are charged, and an
|
||||
// account goes from redeeming to issuing then
|
||||
// strand quality _can_ increase; However, this is
|
||||
// an unusual corner case.
|
||||
continue;
|
||||
}
|
||||
strandQualities.push_back({*qual, strand});
|
||||
}
|
||||
}
|
||||
// must stable sort for deterministic order across different c++
|
||||
// standard library implementations
|
||||
std::stable_sort(strandQualities.begin(), strandQualities.end(), [](auto const& lhs, auto const& rhs) {
|
||||
// higher qualities first
|
||||
return std::get<Quality>(lhs) > std::get<Quality>(rhs);
|
||||
});
|
||||
next_.clear();
|
||||
next_.reserve(strandQualities.size());
|
||||
for (auto const& sq : strandQualities)
|
||||
{
|
||||
next_.push_back(std::get<Strand const*>(sq));
|
||||
}
|
||||
}
|
||||
}
|
||||
std::swap(cur_, next_);
|
||||
}
|
||||
|
||||
Strand const*
|
||||
get(size_t i) const
|
||||
{
|
||||
if (i >= cur_.size())
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
UNREACHABLE("xrpl::ActiveStrands::get : input out of range");
|
||||
return nullptr;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
return cur_[i];
|
||||
}
|
||||
|
||||
void
|
||||
push(Strand const* s)
|
||||
{
|
||||
next_.push_back(s);
|
||||
}
|
||||
|
||||
// Push the strands from index i to the end of cur_ to next_
|
||||
void
|
||||
pushRemainingCurToNext(size_t i)
|
||||
{
|
||||
if (i >= cur_.size())
|
||||
return;
|
||||
next_.insert(next_.end(), std::next(cur_.begin(), i), cur_.end());
|
||||
}
|
||||
|
||||
auto
|
||||
size() const
|
||||
{
|
||||
return cur_.size();
|
||||
}
|
||||
|
||||
void
|
||||
removeIndex(std::size_t i)
|
||||
{
|
||||
if (i >= next_.size())
|
||||
return;
|
||||
next_.erase(next_.begin() + i);
|
||||
}
|
||||
};
|
||||
/// @endcond
|
||||
|
||||
/**
|
||||
Request `out` amount from a collection of strands
|
||||
|
||||
Attempt to fulfill the payment by using liquidity from the strands in order
|
||||
from least expensive to most expensive
|
||||
|
||||
@param baseView Trust lines and balances
|
||||
@param strands Each strand contains the steps of accounts to ripple through
|
||||
and offer books to use
|
||||
@param outReq Amount of output requested from the strand
|
||||
@param partialPayment If true allow less than the full payment
|
||||
@param offerCrossing If true offer crossing, not handling a standard payment
|
||||
@param limitQuality If present, the minimum quality for any strand taken
|
||||
@param sendMaxST If present, the maximum STAmount to send
|
||||
@param j Journal to write journal messages to
|
||||
@param ammContext counts iterations with AMM offers
|
||||
@param flowDebugInfo If pointer is non-null, write flow debug info here
|
||||
@return Actual amount in and out from the strands, errors, and payment
|
||||
sandbox
|
||||
*/
|
||||
template <class TInAmt, class TOutAmt>
|
||||
FlowResult<TInAmt, TOutAmt>
|
||||
flow(
|
||||
PaymentSandbox const& baseView,
|
||||
std::vector<Strand> const& strands,
|
||||
TOutAmt const& outReq,
|
||||
bool partialPayment,
|
||||
OfferCrossing offerCrossing,
|
||||
std::optional<Quality> const& limitQuality,
|
||||
std::optional<STAmount> const& sendMaxST,
|
||||
beast::Journal j,
|
||||
AMMContext& ammContext,
|
||||
path::detail::FlowDebugInfo* flowDebugInfo = nullptr)
|
||||
{
|
||||
// Used to track the strand that offers the best quality (output/input
|
||||
// ratio)
|
||||
struct BestStrand
|
||||
{
|
||||
TInAmt in;
|
||||
TOutAmt out;
|
||||
PaymentSandbox sb;
|
||||
Strand const& strand;
|
||||
Quality quality;
|
||||
|
||||
BestStrand(
|
||||
TInAmt const& in_,
|
||||
TOutAmt const& out_,
|
||||
PaymentSandbox&& sb_,
|
||||
Strand const& strand_,
|
||||
Quality const& quality_)
|
||||
: in(in_), out(out_), sb(std::move(sb_)), strand(strand_), quality(quality_)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
std::size_t const maxTries = 1000;
|
||||
std::size_t curTry = 0;
|
||||
std::uint32_t maxOffersToConsider = 1500;
|
||||
std::uint32_t offersConsidered = 0;
|
||||
|
||||
// There is a bug in gcc that incorrectly warns about using uninitialized
|
||||
// values if `remainingIn` is initialized through a copy constructor. We can
|
||||
// get similar warnings for `sendMax` if it is initialized in the most
|
||||
// natural way. Using `make_optional`, allows us to work around this bug.
|
||||
TInAmt const sendMaxInit = sendMaxST ? toAmount<TInAmt>(*sendMaxST) : TInAmt{beast::zero};
|
||||
std::optional<TInAmt> const sendMax =
|
||||
(sendMaxST && sendMaxInit >= beast::zero) ? std::make_optional(sendMaxInit) : std::nullopt;
|
||||
std::optional<TInAmt> remainingIn = !!sendMax ? std::make_optional(sendMaxInit) : std::nullopt;
|
||||
// std::optional<TInAmt> remainingIn{sendMax};
|
||||
|
||||
TOutAmt remainingOut(outReq);
|
||||
|
||||
PaymentSandbox sb(&baseView);
|
||||
|
||||
// non-dry strands
|
||||
ActiveStrands activeStrands(strands);
|
||||
|
||||
// Keeping a running sum of the amount in the order they are processed
|
||||
// will not give the best precision. Keep a collection so they may be summed
|
||||
// from smallest to largest
|
||||
boost::container::flat_multiset<TInAmt> savedIns;
|
||||
savedIns.reserve(maxTries);
|
||||
boost::container::flat_multiset<TOutAmt> savedOuts;
|
||||
savedOuts.reserve(maxTries);
|
||||
|
||||
auto sum = [](auto const& col) {
|
||||
using TResult = std::decay_t<decltype(*col.begin())>;
|
||||
if (col.empty())
|
||||
return TResult{beast::zero};
|
||||
return std::accumulate(col.begin() + 1, col.end(), *col.begin());
|
||||
};
|
||||
|
||||
// These offers only need to be removed if the payment is not
|
||||
// successful
|
||||
boost::container::flat_set<uint256> ofrsToRmOnFail;
|
||||
|
||||
while (remainingOut > beast::zero && (!remainingIn || *remainingIn > beast::zero))
|
||||
{
|
||||
++curTry;
|
||||
if (curTry >= maxTries)
|
||||
{
|
||||
return {telFAILED_PROCESSING, std::move(ofrsToRmOnFail)};
|
||||
}
|
||||
|
||||
activeStrands.activateNext(sb, limitQuality);
|
||||
|
||||
ammContext.setMultiPath(activeStrands.size() > 1);
|
||||
|
||||
// Limit only if one strand and limitQuality
|
||||
auto const limitRemainingOut = [&]() {
|
||||
if (activeStrands.size() == 1 && limitQuality)
|
||||
if (auto const strand = activeStrands.get(0))
|
||||
return limitOut(sb, *strand, remainingOut, *limitQuality);
|
||||
return remainingOut;
|
||||
}();
|
||||
auto const adjustedRemOut = limitRemainingOut != remainingOut;
|
||||
|
||||
boost::container::flat_set<uint256> ofrsToRm;
|
||||
std::optional<BestStrand> best;
|
||||
if (flowDebugInfo)
|
||||
flowDebugInfo->newLiquidityPass();
|
||||
// Index of strand to mark as inactive (remove from the active list) if
|
||||
// the liquidity is used. This is used for strands that consume too many
|
||||
// offers Constructed as `false,0` to workaround a gcc warning about
|
||||
// uninitialized variables
|
||||
std::optional<std::size_t> markInactiveOnUse;
|
||||
for (size_t strandIndex = 0, sie = activeStrands.size(); strandIndex != sie; ++strandIndex)
|
||||
{
|
||||
Strand const* strand = activeStrands.get(strandIndex);
|
||||
if (!strand)
|
||||
{
|
||||
// should not happen
|
||||
continue;
|
||||
}
|
||||
// Clear AMM liquidity used flag. The flag might still be set if
|
||||
// the previous strand execution failed. It has to be reset
|
||||
// since this strand might not have AMM liquidity.
|
||||
ammContext.clear();
|
||||
if (offerCrossing && limitQuality)
|
||||
{
|
||||
auto const strandQ = qualityUpperBound(sb, *strand);
|
||||
if (!strandQ || *strandQ < *limitQuality)
|
||||
continue;
|
||||
}
|
||||
auto f = flow<TInAmt, TOutAmt>(sb, *strand, remainingIn, limitRemainingOut, j);
|
||||
|
||||
// rm bad offers even if the strand fails
|
||||
SetUnion(ofrsToRm, f.ofrsToRm);
|
||||
|
||||
offersConsidered += f.ofrsUsed;
|
||||
|
||||
if (!f.success || f.out == beast::zero)
|
||||
continue;
|
||||
|
||||
if (flowDebugInfo)
|
||||
flowDebugInfo->pushLiquiditySrc(EitherAmount(f.in), EitherAmount(f.out));
|
||||
|
||||
XRPL_ASSERT(
|
||||
f.out <= remainingOut && f.sandbox && (!remainingIn || f.in <= *remainingIn),
|
||||
"xrpl::flow : remaining constraints");
|
||||
|
||||
Quality const q(f.out, f.in);
|
||||
|
||||
JLOG(j.trace()) << "New flow iter (iter, in, out): " << curTry - 1 << " " << to_string(f.in) << " "
|
||||
<< to_string(f.out);
|
||||
|
||||
// limitOut() finds output to generate exact requested
|
||||
// limitQuality. But the actual limit quality might be slightly
|
||||
// off due to the round off.
|
||||
if (limitQuality && q < *limitQuality &&
|
||||
(!adjustedRemOut || !withinRelativeDistance(q, *limitQuality, Number(1, -7))))
|
||||
{
|
||||
JLOG(j.trace()) << "Path rejected by limitQuality"
|
||||
<< " limit: " << *limitQuality << " path q: " << q;
|
||||
continue;
|
||||
}
|
||||
|
||||
XRPL_ASSERT(!best, "xrpl::flow : best is unset");
|
||||
if (!f.inactive)
|
||||
activeStrands.push(strand);
|
||||
best.emplace(f.in, f.out, std::move(*f.sandbox), *strand, q);
|
||||
activeStrands.pushRemainingCurToNext(strandIndex + 1);
|
||||
break;
|
||||
}
|
||||
|
||||
bool const shouldBreak = !best || offersConsidered >= maxOffersToConsider;
|
||||
|
||||
if (best)
|
||||
{
|
||||
if (markInactiveOnUse)
|
||||
{
|
||||
activeStrands.removeIndex(*markInactiveOnUse);
|
||||
markInactiveOnUse.reset();
|
||||
}
|
||||
savedIns.insert(best->in);
|
||||
savedOuts.insert(best->out);
|
||||
remainingOut = outReq - sum(savedOuts);
|
||||
if (sendMax)
|
||||
remainingIn = *sendMax - sum(savedIns);
|
||||
|
||||
if (flowDebugInfo)
|
||||
flowDebugInfo->pushPass(EitherAmount(best->in), EitherAmount(best->out), activeStrands.size());
|
||||
|
||||
JLOG(j.trace()) << "Best path: in: " << to_string(best->in) << " out: " << to_string(best->out)
|
||||
<< " remainingOut: " << to_string(remainingOut);
|
||||
|
||||
best->sb.apply(sb);
|
||||
ammContext.update();
|
||||
}
|
||||
else
|
||||
{
|
||||
JLOG(j.trace()) << "All strands dry.";
|
||||
}
|
||||
|
||||
best.reset(); // view in best must be destroyed before modifying base
|
||||
// view
|
||||
if (!ofrsToRm.empty())
|
||||
{
|
||||
SetUnion(ofrsToRmOnFail, ofrsToRm);
|
||||
for (auto const& o : ofrsToRm)
|
||||
{
|
||||
if (auto ok = sb.peek(keylet::offer(o)))
|
||||
offerDelete(sb, ok, j);
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldBreak)
|
||||
break;
|
||||
}
|
||||
|
||||
auto const actualOut = sum(savedOuts);
|
||||
auto const actualIn = sum(savedIns);
|
||||
|
||||
JLOG(j.trace()) << "Total flow: in: " << to_string(actualIn) << " out: " << to_string(actualOut);
|
||||
|
||||
/* flowCross doesn't handle offer crossing with tfFillOrKill flag correctly.
|
||||
* 1. If tfFillOrKill is set then the owner must receive the full
|
||||
* TakerPays. We reverse pays and gets because during crossing
|
||||
* we are taking, therefore the owner must deliver the full TakerPays and
|
||||
* the entire TakerGets doesn't have to be spent.
|
||||
* Pre-fixFillOrKill amendment code fails if the entire TakerGets
|
||||
* is not spent. fixFillOrKill addresses this issue.
|
||||
* 2. If tfSell is also set then the owner must spend the entire TakerGets
|
||||
* even if it means obtaining more than TakerPays. Since the pays and gets
|
||||
* are reversed, the owner must send the entire TakerGets.
|
||||
*/
|
||||
bool const fillOrKillEnabled = baseView.rules().enabled(fixFillOrKill);
|
||||
|
||||
if (actualOut != outReq)
|
||||
{
|
||||
if (actualOut > outReq)
|
||||
{
|
||||
// Rounding in the payment engine is causing this assert to
|
||||
// sometimes fire with "dust" amounts. This is causing issues when
|
||||
// running debug builds of rippled. While this issue still needs to
|
||||
// be resolved, the assert is causing more harm than good at this
|
||||
// point.
|
||||
// UNREACHABLE("xrpl::flow : rounding error");
|
||||
|
||||
return {tefEXCEPTION, std::move(ofrsToRmOnFail)};
|
||||
}
|
||||
if (!partialPayment)
|
||||
{
|
||||
// If we're offerCrossing a !partialPayment, then we're
|
||||
// handling tfFillOrKill.
|
||||
// Pre-fixFillOrKill amendment:
|
||||
// That case is handled below; not here.
|
||||
// fixFillOrKill amendment:
|
||||
// That case is handled here if tfSell is also not set; i.e,
|
||||
// case 1.
|
||||
if (!offerCrossing || (fillOrKillEnabled && offerCrossing != OfferCrossing::sell))
|
||||
return {tecPATH_PARTIAL, actualIn, actualOut, std::move(ofrsToRmOnFail)};
|
||||
}
|
||||
else if (actualOut == beast::zero)
|
||||
{
|
||||
return {tecPATH_DRY, std::move(ofrsToRmOnFail)};
|
||||
}
|
||||
}
|
||||
if (offerCrossing && (!partialPayment && (!fillOrKillEnabled || offerCrossing == OfferCrossing::sell)))
|
||||
{
|
||||
// If we're offer crossing and partialPayment is *not* true, then
|
||||
// we're handling a FillOrKill offer. In this case remainingIn must
|
||||
// be zero (all funds must be consumed) or else we kill the offer.
|
||||
// Pre-fixFillOrKill amendment:
|
||||
// Handles both cases 1. and 2.
|
||||
// fixFillOrKill amendment:
|
||||
// Handles 2. 1. is handled above and falls through for tfSell.
|
||||
XRPL_ASSERT(remainingIn, "xrpl::flow : nonzero remainingIn");
|
||||
if (remainingIn && *remainingIn != beast::zero)
|
||||
return {tecPATH_PARTIAL, actualIn, actualOut, std::move(ofrsToRmOnFail)};
|
||||
}
|
||||
|
||||
return {actualIn, actualOut, std::move(sb), std::move(ofrsToRmOnFail)};
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
67
include/xrpl/tx/transactors/AMM/AMMBid.h
Normal file
67
include/xrpl/tx/transactors/AMM/AMMBid.h
Normal file
@@ -0,0 +1,67 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/** AMMBid implements AMM bid Transactor.
|
||||
* This is a mechanism for an AMM instance to auction-off
|
||||
* the trading advantages to users (arbitrageurs) at a discounted
|
||||
* TradingFee for a 24 hour slot. Any account that owns corresponding
|
||||
* LPTokens can bid for the auction slot of that AMM instance.
|
||||
* Part of the proceeds from the auction, i.e. LPTokens are refunded
|
||||
* to the current slot-holder computed on a pro rata basis.
|
||||
* Remaining part of the proceeds - in the units of LPTokens- is burnt,
|
||||
* thus effectively increasing the LPs shares.
|
||||
* Total slot time of 24 hours is divided into 20 equal intervals.
|
||||
* The auction slot can be in any of the following states at any time:
|
||||
* - Empty - no account currently holds the slot.
|
||||
* - Occupied - an account owns the slot with at least 5% of the remaining
|
||||
* slot time (in one of 1-19 intervals).
|
||||
* - Tailing - an account owns the slot with less than 5% of the remaining time.
|
||||
* The slot-holder owns the slot privileges when in state Occupied or Tailing.
|
||||
* If x is the fraction of used slot time for the current slot holder
|
||||
* and X is the price at which the slot can be bought specified in LPTokens
|
||||
* then: The minimum bid price for the slot in first interval is
|
||||
* f(x) = X * 1.05 + min_slot_price
|
||||
* The bid price of slot any time is
|
||||
* f(x) = X * 1.05 * (1 - x^60) + min_slot_price, where min_slot_price
|
||||
* is a constant fraction of the total LPTokens.
|
||||
* The revenue from a successful bid is split between the current slot-holder
|
||||
* and the pool. The current slot holder is always refunded the remaining slot
|
||||
* value f(x) = (1 - x) * X.
|
||||
* The remaining LPTokens are burnt.
|
||||
* The auction information is maintained in AuctionSlot of ltAMM object.
|
||||
* AuctionSlot contains:
|
||||
* Account - account id, which owns the slot.
|
||||
* Expiration - slot expiration time
|
||||
* DiscountedFee - trading fee charged to the account, default is 0.
|
||||
* Price - price paid for the slot in LPTokens.
|
||||
* AuthAccounts - up to four accounts authorized to trade at
|
||||
* the discounted fee.
|
||||
* @see [XLS30d:Continuous Auction
|
||||
* Mechanism](https://github.com/XRPLF/XRPL-Standards/discussions/78)
|
||||
*/
|
||||
class AMMBid : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit AMMBid(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
56
include/xrpl/tx/transactors/AMM/AMMClawback.h
Normal file
56
include/xrpl/tx/transactors/AMM/AMMClawback.h
Normal file
@@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
class Sandbox;
|
||||
class AMMClawback : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit AMMClawback(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
|
||||
private:
|
||||
TER
|
||||
applyGuts(Sandbox& view);
|
||||
|
||||
/** Withdraw both assets by providing maximum amount of asset1,
|
||||
* asset2's amount will be calculated according to the current proportion.
|
||||
* Since it is two-asset withdrawal, tfee is omitted.
|
||||
* @param view
|
||||
* @param ammAccount current AMM account
|
||||
* @param amountBalance current AMM asset1 balance
|
||||
* @param amount2Balance current AMM asset2 balance
|
||||
* @param lptAMMBalance current AMM LPT balance
|
||||
* @param amount asset1 withdraw amount
|
||||
* @return
|
||||
*/
|
||||
std::tuple<TER, STAmount, STAmount, std::optional<STAmount>>
|
||||
equalWithdrawMatchingOneAmount(
|
||||
Sandbox& view,
|
||||
SLE const& ammSle,
|
||||
AccountID const& holder,
|
||||
AccountID const& ammAccount,
|
||||
STAmount const& amountBalance,
|
||||
STAmount const& amount2Balance,
|
||||
STAmount const& lptAMMBalance,
|
||||
STAmount const& holdLPtokens,
|
||||
STAmount const& amount);
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
96
include/xrpl/tx/transactors/AMM/AMMContext.h
Normal file
96
include/xrpl/tx/transactors/AMM/AMMContext.h
Normal file
@@ -0,0 +1,96 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/** Maintains AMM info per overall payment engine execution and
|
||||
* individual iteration.
|
||||
* Only one instance of this class is created in Flow.cpp::flow().
|
||||
* The reference is percolated through calls to AMMLiquidity class,
|
||||
* which handles AMM offer generation.
|
||||
*/
|
||||
class AMMContext
|
||||
{
|
||||
public:
|
||||
// Restrict number of AMM offers. If this restriction is removed
|
||||
// then need to restrict in some other way because AMM offers are
|
||||
// not counted in the BookStep offer counter.
|
||||
constexpr static std::uint8_t MaxIterations = 30;
|
||||
|
||||
private:
|
||||
// Tx account owner is required to get the AMM trading fee in BookStep
|
||||
AccountID account_;
|
||||
// true if payment has multiple paths
|
||||
bool multiPath_{false};
|
||||
// Is true if AMM offer is consumed during a payment engine iteration.
|
||||
bool ammUsed_{false};
|
||||
// Counter of payment engine iterations with consumed AMM
|
||||
std::uint16_t ammIters_{0};
|
||||
|
||||
public:
|
||||
AMMContext(AccountID const& account, bool multiPath) : account_(account), multiPath_(multiPath)
|
||||
{
|
||||
}
|
||||
~AMMContext() = default;
|
||||
AMMContext(AMMContext const&) = delete;
|
||||
AMMContext&
|
||||
operator=(AMMContext const&) = delete;
|
||||
|
||||
bool
|
||||
multiPath() const
|
||||
{
|
||||
return multiPath_;
|
||||
}
|
||||
|
||||
void
|
||||
setMultiPath(bool fs)
|
||||
{
|
||||
multiPath_ = fs;
|
||||
}
|
||||
|
||||
void
|
||||
setAMMUsed()
|
||||
{
|
||||
ammUsed_ = true;
|
||||
}
|
||||
|
||||
void
|
||||
update()
|
||||
{
|
||||
if (ammUsed_)
|
||||
++ammIters_;
|
||||
ammUsed_ = false;
|
||||
}
|
||||
|
||||
bool
|
||||
maxItersReached() const
|
||||
{
|
||||
return ammIters_ >= MaxIterations;
|
||||
}
|
||||
|
||||
std::uint16_t
|
||||
curIters() const
|
||||
{
|
||||
return ammIters_;
|
||||
}
|
||||
|
||||
AccountID
|
||||
account() const
|
||||
{
|
||||
return account_;
|
||||
}
|
||||
|
||||
/** Strand execution may fail. Reset the flag at the start
|
||||
* of each payment engine iteration.
|
||||
*/
|
||||
void
|
||||
clear()
|
||||
{
|
||||
ammUsed_ = false;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
63
include/xrpl/tx/transactors/AMM/AMMCreate.h
Normal file
63
include/xrpl/tx/transactors/AMM/AMMCreate.h
Normal file
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/** AMMCreate implements Automatic Market Maker(AMM) creation Transactor.
|
||||
* It creates a new AMM instance with two tokens. Any trader, or Liquidity
|
||||
* Provider (LP), can create the AMM instance and receive in return shares
|
||||
* of the AMM pool in the form of LPTokens. The number of tokens that LP gets
|
||||
* are determined by LPTokens = sqrt(A * B), where A and B is the current
|
||||
* composition of the AMM pool. LP can add (AMMDeposit) or withdraw
|
||||
* (AMMWithdraw) tokens from AMM and
|
||||
* AMM can be used transparently in the payment or offer crossing transactions.
|
||||
* Trading fee is charged to the traders for the trades executed against
|
||||
* AMM instance. The fee is added to the AMM pool and distributed to the LPs
|
||||
* in proportion to the LPTokens upon liquidity removal. The fee can be voted
|
||||
* on by LP's (AMMVote). LP's can continuously bid (AMMBid) for the 24 hour
|
||||
* auction slot, which enables LP's to trade at zero trading fee.
|
||||
* AMM instance creates AccountRoot object with disabled master key
|
||||
* for book-keeping of XRP balance if one of the tokens
|
||||
* is XRP, a trustline for each IOU token, a trustline to keep track
|
||||
* of LPTokens, and ltAMM ledger object. AccountRoot ID is generated
|
||||
* internally from the parent's hash. ltAMM's object ID is
|
||||
* hash{token1.currency, token1.issuer, token2.currency, token2.issuer}, where
|
||||
* issue1 < issue2. ltAMM object provides mapping from the hash to AccountRoot
|
||||
* ID and contains: AMMAccount - AMM AccountRoot ID. TradingFee - AMM voted
|
||||
* TradingFee. VoteSlots - Array of VoteEntry, contains fee vote information.
|
||||
* AuctionSlot - Auction slot, contains discounted fee bid information.
|
||||
* LPTokenBalance - LPTokens outstanding balance.
|
||||
* AMMToken - currency/issuer information for AMM tokens.
|
||||
* AMMDeposit, AMMWithdraw, AMMVote, and AMMBid transactions use the hash
|
||||
* to access AMM instance.
|
||||
* @see [XLS30d:Creating AMM instance on
|
||||
* XRPL](https://github.com/XRPLF/XRPL-Standards/discussions/78)
|
||||
*/
|
||||
class AMMCreate : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit AMMCreate(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static XRPAmount
|
||||
calculateBaseFee(ReadView const& view, STTx const& tx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
/** Attempt to create the AMM instance. */
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
35
include/xrpl/tx/transactors/AMM/AMMDelete.h
Normal file
35
include/xrpl/tx/transactors/AMM/AMMDelete.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/** AMMDelete implements AMM delete transactor. This is a mechanism to
|
||||
* delete AMM in an empty state when the number of LP tokens is 0.
|
||||
* AMMDelete deletes the trustlines up to configured maximum. If all
|
||||
* trustlines are deleted then AMM ltAMM and root account are deleted.
|
||||
* Otherwise AMMDelete should be called again.
|
||||
*/
|
||||
class AMMDelete : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit AMMDelete(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
231
include/xrpl/tx/transactors/AMM/AMMDeposit.h
Normal file
231
include/xrpl/tx/transactors/AMM/AMMDeposit.h
Normal file
@@ -0,0 +1,231 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class Sandbox;
|
||||
|
||||
/** AMMDeposit implements AMM deposit Transactor.
|
||||
* The deposit transaction is used to add liquidity to the AMM instance pool,
|
||||
* thus obtaining some share of the instance's pools in the form of LPTokens.
|
||||
* If the trader deposits proportional values of both assets without changing
|
||||
* their relative price, then no trading fee is charged on the transaction.
|
||||
* The trader can specify different combination of the fields in the deposit.
|
||||
* LPTokens - transaction assumes proportional deposit of pools assets in
|
||||
* exchange for the specified amount of LPTokens of the AMM instance.
|
||||
* Asset1In - transaction assumes single asset deposit of the amount of asset
|
||||
* specified by Asset1In. This is essentially a swap and an equal asset
|
||||
* deposit.
|
||||
* Asset1In and Asset2In - transaction assumes proportional deposit of pool
|
||||
* assets with the constraints on the maximum amount of each asset that
|
||||
* the trader is willing to deposit.
|
||||
* Asset1In and LPTokens - transaction assumes that a single asset asset1
|
||||
* is deposited to obtain some share of the AMM instance's pools
|
||||
* represented by amount of LPTokens.
|
||||
* Asset1In and EPrice - transaction assumes single asset deposit with
|
||||
* the following two constraints:
|
||||
* a. amount of asset1 if specified (not 0) in Asset1In specifies the
|
||||
* maximum amount of asset1 that the trader is willing to deposit b. The
|
||||
* effective-price of the LPTokens traded out does not exceed the specified
|
||||
* EPrice. Following updates after a successful AMMDeposit transaction: The
|
||||
* deposited asset, if XRP, is transferred from the account that initiated the
|
||||
* transaction to the AMM instance account, thus changing the Balance field of
|
||||
* each account. The deposited asset, if tokens, are balanced between the AMM
|
||||
* account and the issuer account trustline. The LPTokens are issued by the AMM
|
||||
* instance account to the account that initiated the transaction and a new
|
||||
* trustline is created, if there does not exist one. The pool composition is
|
||||
* updated.
|
||||
* @see [XLS30d:AMMDeposit
|
||||
* transaction](https://github.com/XRPLF/XRPL-Standards/discussions/78)
|
||||
*/
|
||||
class AMMDeposit : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit AMMDeposit(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
|
||||
private:
|
||||
std::pair<TER, bool>
|
||||
applyGuts(Sandbox& view);
|
||||
|
||||
/** Deposit requested assets and token amount into LP account.
|
||||
* Return new total LPToken balance.
|
||||
* @param view
|
||||
* @param ammAccount
|
||||
* @param amountBalance current AMM asset1 balance
|
||||
* @param amountDeposit
|
||||
* @param amount2Deposit
|
||||
* @param lptAMMBalance current AMM LPT balance
|
||||
* @param lpTokensDeposit amount of tokens to deposit
|
||||
* @param depositMin minimum accepted amount deposit
|
||||
* @param deposit2Min minimum accepted amount2 deposit
|
||||
* @param lpTokensDepositMin minimum accepted LPTokens deposit
|
||||
* @param tfee trading fee in basis points
|
||||
* @return
|
||||
*/
|
||||
std::pair<TER, STAmount>
|
||||
deposit(
|
||||
Sandbox& view,
|
||||
AccountID const& ammAccount,
|
||||
STAmount const& amountBalance,
|
||||
STAmount const& amountDeposit,
|
||||
std::optional<STAmount> const& amount2Deposit,
|
||||
STAmount const& lptAMMBalance,
|
||||
STAmount const& lpTokensDeposit,
|
||||
std::optional<STAmount> const& depositMin,
|
||||
std::optional<STAmount> const& deposit2Min,
|
||||
std::optional<STAmount> const& lpTokensDepositMin,
|
||||
std::uint16_t tfee);
|
||||
|
||||
/** Equal asset deposit (LPTokens) for the specified share of
|
||||
* the AMM instance pools. The trading fee is not charged.
|
||||
* @param view
|
||||
* @param ammAccount
|
||||
* @param amountBalance current AMM asset1 balance
|
||||
* @param amount2Balance current AMM asset2 balance
|
||||
* @param lptAMMBalance current AMM LPT balance
|
||||
* @param lpTokensDeposit amount of tokens to deposit
|
||||
* @param depositMin minimum accepted amount deposit
|
||||
* @param deposit2Min minimum accepted amount2 deposit
|
||||
* @param tfee trading fee in basis points
|
||||
* @return
|
||||
*/
|
||||
std::pair<TER, STAmount>
|
||||
equalDepositTokens(
|
||||
Sandbox& view,
|
||||
AccountID const& ammAccount,
|
||||
STAmount const& amountBalance,
|
||||
STAmount const& amount2Balance,
|
||||
STAmount const& lptAMMBalance,
|
||||
STAmount const& lpTokensDeposit,
|
||||
std::optional<STAmount> const& depositMin,
|
||||
std::optional<STAmount> const& deposit2Min,
|
||||
std::uint16_t tfee);
|
||||
|
||||
/** Equal asset deposit (Asset1In, Asset2In) with the constraint on
|
||||
* the maximum amount of both assets that the trader is willing to deposit.
|
||||
* The trading fee is not charged.
|
||||
* @param view
|
||||
* @param ammAccount
|
||||
* @param amountBalance current AMM asset1 balance
|
||||
* @param amount2Balance current AMM asset2 balance
|
||||
* @param lptAMMBalance current AMM LPT balance
|
||||
* @param amount maximum asset1 deposit amount
|
||||
* @param amount2 maximum asset2 deposit amount
|
||||
* @param lpTokensDepositMin minimum accepted LPTokens deposit
|
||||
* @param tfee trading fee in basis points
|
||||
* @return
|
||||
*/
|
||||
std::pair<TER, STAmount>
|
||||
equalDepositLimit(
|
||||
Sandbox& view,
|
||||
AccountID const& ammAccount,
|
||||
STAmount const& amountBalance,
|
||||
STAmount const& amount2Balance,
|
||||
STAmount const& lptAMMBalance,
|
||||
STAmount const& amount,
|
||||
STAmount const& amount2,
|
||||
std::optional<STAmount> const& lpTokensDepositMin,
|
||||
std::uint16_t tfee);
|
||||
|
||||
/** Single asset deposit (Asset1In) by the amount.
|
||||
* The trading fee is charged.
|
||||
* @param view
|
||||
* @param ammAccount
|
||||
* @param amountBalance current AMM asset1 balance
|
||||
* @param lptAMMBalance current AMM LPT balance
|
||||
* @param amount requested asset1 deposit amount
|
||||
* @param lpTokensDepositMin minimum accepted LPTokens deposit
|
||||
* @param tfee trading fee in basis points
|
||||
* @return
|
||||
*/
|
||||
std::pair<TER, STAmount>
|
||||
singleDeposit(
|
||||
Sandbox& view,
|
||||
AccountID const& ammAccount,
|
||||
STAmount const& amountBalance,
|
||||
STAmount const& lptAMMBalance,
|
||||
STAmount const& amount,
|
||||
std::optional<STAmount> const& lpTokensDepositMin,
|
||||
std::uint16_t tfee);
|
||||
|
||||
/** Single asset deposit (Asset1In, LPTokens) by the tokens.
|
||||
* The trading fee is charged.
|
||||
* @param view
|
||||
* @param ammAccount
|
||||
* @param amountBalance current AMM asset1 balance
|
||||
* @param amount max asset1 to deposit
|
||||
* @param lptAMMBalance current AMM LPT balance
|
||||
* @param lpTokensDeposit amount of tokens to deposit
|
||||
* @param tfee trading fee in basis points
|
||||
* @return
|
||||
*/
|
||||
std::pair<TER, STAmount>
|
||||
singleDepositTokens(
|
||||
Sandbox& view,
|
||||
AccountID const& ammAccount,
|
||||
STAmount const& amountBalance,
|
||||
STAmount const& amount,
|
||||
STAmount const& lptAMMBalance,
|
||||
STAmount const& lpTokensDeposit,
|
||||
std::uint16_t tfee);
|
||||
|
||||
/** Single asset deposit (Asset1In, EPrice) with two constraints.
|
||||
* The trading fee is charged.
|
||||
* @param view
|
||||
* @param ammAccount
|
||||
* @param amountBalance current AMM asset1 balance
|
||||
* @param amount requested asset1 deposit amount
|
||||
* @param lptAMMBalance current AMM LPT balance
|
||||
* @param ePrice maximum effective price
|
||||
* @param tfee
|
||||
* @return
|
||||
*/
|
||||
std::pair<TER, STAmount>
|
||||
singleDepositEPrice(
|
||||
Sandbox& view,
|
||||
AccountID const& ammAccount,
|
||||
STAmount const& amountBalance,
|
||||
STAmount const& amount,
|
||||
STAmount const& lptAMMBalance,
|
||||
STAmount const& ePrice,
|
||||
std::uint16_t tfee);
|
||||
|
||||
/** Equal deposit in empty AMM state (LP tokens balance is 0)
|
||||
* @param view
|
||||
* @param ammAccount
|
||||
* @param amount requested asset1 deposit amount
|
||||
* @param amount2 requested asset2 deposit amount
|
||||
* @param tfee
|
||||
* @return
|
||||
*/
|
||||
std::pair<TER, STAmount>
|
||||
equalDepositInEmptyState(
|
||||
Sandbox& view,
|
||||
AccountID const& ammAccount,
|
||||
STAmount const& amount,
|
||||
STAmount const& amount2,
|
||||
Issue const& lptIssue,
|
||||
std::uint16_t tfee);
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
689
include/xrpl/tx/transactors/AMM/AMMHelpers.h
Normal file
689
include/xrpl/tx/transactors/AMM/AMMHelpers.h
Normal file
@@ -0,0 +1,689 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/Number.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/protocol/AMMCore.h>
|
||||
#include <xrpl/protocol/AmountConversions.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/IOUAmount.h>
|
||||
#include <xrpl/protocol/Issue.h>
|
||||
#include <xrpl/protocol/Quality.h>
|
||||
#include <xrpl/protocol/Rules.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
namespace detail {
|
||||
|
||||
Number
|
||||
reduceOffer(auto const& amount)
|
||||
{
|
||||
static Number const reducedOfferPct(9999, -4);
|
||||
|
||||
// Make sure the result is always less than amount or zero.
|
||||
NumberRoundModeGuard mg(Number::towards_zero);
|
||||
return amount * reducedOfferPct;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
enum class IsDeposit : bool { No = false, Yes = true };
|
||||
|
||||
/** Calculate LP Tokens given AMM pool reserves.
|
||||
* @param asset1 AMM one side of the pool reserve
|
||||
* @param asset2 AMM another side of the pool reserve
|
||||
* @return LP Tokens as IOU
|
||||
*/
|
||||
STAmount
|
||||
ammLPTokens(STAmount const& asset1, STAmount const& asset2, Issue const& lptIssue);
|
||||
|
||||
/** Calculate LP Tokens given asset's deposit amount.
|
||||
* @param asset1Balance current AMM asset1 balance
|
||||
* @param asset1Deposit requested asset1 deposit amount
|
||||
* @param lptAMMBalance AMM LPT balance
|
||||
* @param tfee trading fee in basis points
|
||||
* @return tokens
|
||||
*/
|
||||
STAmount
|
||||
lpTokensOut(
|
||||
STAmount const& asset1Balance,
|
||||
STAmount const& asset1Deposit,
|
||||
STAmount const& lptAMMBalance,
|
||||
std::uint16_t tfee);
|
||||
|
||||
/** Calculate asset deposit given LP Tokens.
|
||||
* @param asset1Balance current AMM asset1 balance
|
||||
* @param lpTokens LP Tokens
|
||||
* @param lptAMMBalance AMM LPT balance
|
||||
* @param tfee trading fee in basis points
|
||||
* @return
|
||||
*/
|
||||
STAmount
|
||||
ammAssetIn(STAmount const& asset1Balance, STAmount const& lptAMMBalance, STAmount const& lpTokens, std::uint16_t tfee);
|
||||
|
||||
/** Calculate LP Tokens given asset's withdraw amount. Return 0
|
||||
* if can't calculate.
|
||||
* @param asset1Balance current AMM asset1 balance
|
||||
* @param asset1Withdraw requested asset1 withdraw amount
|
||||
* @param lptAMMBalance AMM LPT balance
|
||||
* @param tfee trading fee in basis points
|
||||
* @return tokens out amount
|
||||
*/
|
||||
STAmount
|
||||
lpTokensIn(
|
||||
STAmount const& asset1Balance,
|
||||
STAmount const& asset1Withdraw,
|
||||
STAmount const& lptAMMBalance,
|
||||
std::uint16_t tfee);
|
||||
|
||||
/** Calculate asset withdrawal by tokens
|
||||
* @param assetBalance balance of the asset being withdrawn
|
||||
* @param lptAMMBalance total AMM Tokens balance
|
||||
* @param lpTokens LP Tokens balance
|
||||
* @param tfee trading fee in basis points
|
||||
* @return calculated asset amount
|
||||
*/
|
||||
STAmount
|
||||
ammAssetOut(STAmount const& assetBalance, STAmount const& lptAMMBalance, STAmount const& lpTokens, std::uint16_t tfee);
|
||||
|
||||
/** Check if the relative distance between the qualities
|
||||
* is within the requested distance.
|
||||
* @param calcQuality calculated quality
|
||||
* @param reqQuality requested quality
|
||||
* @param dist requested relative distance
|
||||
* @return true if within dist, false otherwise
|
||||
*/
|
||||
inline bool
|
||||
withinRelativeDistance(Quality const& calcQuality, Quality const& reqQuality, Number const& dist)
|
||||
{
|
||||
if (calcQuality == reqQuality)
|
||||
return true;
|
||||
auto const [min, max] = std::minmax(calcQuality, reqQuality);
|
||||
// Relative distance is (max - min)/max. Can't use basic operations
|
||||
// on Quality. Have to use Quality::rate() instead, which
|
||||
// is inverse of quality: (1/max.rate - 1/min.rate)/(1/max.rate)
|
||||
return ((min.rate() - max.rate()) / min.rate()) < dist;
|
||||
}
|
||||
|
||||
/** Check if the relative distance between the amounts
|
||||
* is within the requested distance.
|
||||
* @param calc calculated amount
|
||||
* @param req requested amount
|
||||
* @param dist requested relative distance
|
||||
* @return true if within dist, false otherwise
|
||||
*/
|
||||
// clang-format off
|
||||
template <typename Amt>
|
||||
requires(
|
||||
std::is_same_v<Amt, STAmount> || std::is_same_v<Amt, IOUAmount> ||
|
||||
std::is_same_v<Amt, XRPAmount> || std::is_same_v<Amt, Number>)
|
||||
bool
|
||||
withinRelativeDistance(Amt const& calc, Amt const& req, Number const& dist)
|
||||
{
|
||||
if (calc == req)
|
||||
return true;
|
||||
auto const [min, max] = std::minmax(calc, req);
|
||||
return ((max - min) / max) < dist;
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
/** Solve quadratic equation to find takerGets or takerPays. Round
|
||||
* to minimize the amount in order to maximize the quality.
|
||||
*/
|
||||
std::optional<Number>
|
||||
solveQuadraticEqSmallest(Number const& a, Number const& b, Number const& c);
|
||||
|
||||
/** Generate AMM offer starting with takerGets when AMM pool
|
||||
* from the payment perspective is IOU(in)/XRP(out)
|
||||
* Equations:
|
||||
* Spot Price Quality after the offer is consumed:
|
||||
* Qsp = (O - o) / (I + i) -- equation (1)
|
||||
* where O is poolPays, I is poolGets, o is takerGets, i is takerPays
|
||||
* Swap out:
|
||||
* i = (I * o) / (O - o) * f -- equation (2)
|
||||
* where f is (1 - tfee/100000), tfee is in basis points
|
||||
* Effective price targetQuality:
|
||||
* Qep = o / i -- equation (3)
|
||||
* There are two scenarios to consider
|
||||
* A) Qsp = Qep. Substitute i in (1) with (2) and solve for o
|
||||
* and Qsp = targetQuality(Qt):
|
||||
* o**2 + o * (I * Qt * (1 - 1 / f) - 2 * O) + O**2 - Qt * I * O = 0
|
||||
* B) Qep = Qsp. Substitute i in (3) with (2) and solve for o
|
||||
* and Qep = targetQuality(Qt):
|
||||
* o = O - I * Qt / f
|
||||
* Since the scenario is not known a priori, both A and B are solved and
|
||||
* the lowest value of o is takerGets. takerPays is calculated with
|
||||
* swap out eq (2). If o is less or equal to 0 then the offer can't
|
||||
* be generated.
|
||||
*/
|
||||
template <typename TIn, typename TOut>
|
||||
std::optional<TAmounts<TIn, TOut>>
|
||||
getAMMOfferStartWithTakerGets(TAmounts<TIn, TOut> const& pool, Quality const& targetQuality, std::uint16_t const& tfee)
|
||||
{
|
||||
if (targetQuality.rate() == beast::zero)
|
||||
return std::nullopt;
|
||||
|
||||
NumberRoundModeGuard mg(Number::to_nearest);
|
||||
auto const f = feeMult(tfee);
|
||||
auto const a = 1;
|
||||
auto const b = pool.in * (1 - 1 / f) / targetQuality.rate() - 2 * pool.out;
|
||||
auto const c = pool.out * pool.out - (pool.in * pool.out) / targetQuality.rate();
|
||||
|
||||
auto nTakerGets = solveQuadraticEqSmallest(a, b, c);
|
||||
if (!nTakerGets || *nTakerGets <= 0)
|
||||
return std::nullopt; // LCOV_EXCL_LINE
|
||||
|
||||
auto const nTakerGetsConstraint = pool.out - pool.in / (targetQuality.rate() * f);
|
||||
if (nTakerGetsConstraint <= 0)
|
||||
return std::nullopt;
|
||||
|
||||
// Select the smallest to maximize the quality
|
||||
if (nTakerGetsConstraint < *nTakerGets)
|
||||
nTakerGets = nTakerGetsConstraint;
|
||||
|
||||
auto getAmounts = [&pool, &tfee](Number const& nTakerGetsProposed) {
|
||||
// Round downward to minimize the offer and to maximize the quality.
|
||||
// This has the most impact when takerGets is XRP.
|
||||
auto const takerGets = toAmount<TOut>(getIssue(pool.out), nTakerGetsProposed, Number::downward);
|
||||
return TAmounts<TIn, TOut>{swapAssetOut(pool, takerGets, tfee), takerGets};
|
||||
};
|
||||
|
||||
// Try to reduce the offer size to improve the quality.
|
||||
// The quality might still not match the targetQuality for a tiny offer.
|
||||
if (auto const amounts = getAmounts(*nTakerGets); Quality{amounts} < targetQuality)
|
||||
return getAmounts(detail::reduceOffer(amounts.out));
|
||||
else
|
||||
return amounts;
|
||||
}
|
||||
|
||||
/** Generate AMM offer starting with takerPays when AMM pool
|
||||
* from the payment perspective is XRP(in)/IOU(out) or IOU(in)/IOU(out).
|
||||
* Equations:
|
||||
* Spot Price Quality after the offer is consumed:
|
||||
* Qsp = (O - o) / (I + i) -- equation (1)
|
||||
* where O is poolPays, I is poolGets, o is takerGets, i is takerPays
|
||||
* Swap in:
|
||||
* o = (O * i * f) / (I + i * f) -- equation (2)
|
||||
* where f is (1 - tfee/100000), tfee is in basis points
|
||||
* Effective price quality:
|
||||
* Qep = o / i -- equation (3)
|
||||
* There are two scenarios to consider
|
||||
* A) Qsp = Qep. Substitute o in (1) with (2) and solve for i
|
||||
* and Qsp = targetQuality(Qt):
|
||||
* i**2 * f + i * I * (1 + f) + I**2 - I * O / Qt = 0
|
||||
* B) Qep = Qsp. Substitute i in (3) with (2) and solve for i
|
||||
* and Qep = targetQuality(Qt):
|
||||
* i = O / Qt - I / f
|
||||
* Since the scenario is not known a priori, both A and B are solved and
|
||||
* the lowest value of i is takerPays. takerGets is calculated with
|
||||
* swap in eq (2). If i is less or equal to 0 then the offer can't
|
||||
* be generated.
|
||||
*/
|
||||
template <typename TIn, typename TOut>
|
||||
std::optional<TAmounts<TIn, TOut>>
|
||||
getAMMOfferStartWithTakerPays(TAmounts<TIn, TOut> const& pool, Quality const& targetQuality, std::uint16_t tfee)
|
||||
{
|
||||
if (targetQuality.rate() == beast::zero)
|
||||
return std::nullopt;
|
||||
|
||||
NumberRoundModeGuard mg(Number::to_nearest);
|
||||
auto const f = feeMult(tfee);
|
||||
auto const& a = f;
|
||||
auto const b = pool.in * (1 + f);
|
||||
auto const c = pool.in * pool.in - pool.in * pool.out * targetQuality.rate();
|
||||
|
||||
auto nTakerPays = solveQuadraticEqSmallest(a, b, c);
|
||||
if (!nTakerPays || nTakerPays <= 0)
|
||||
return std::nullopt; // LCOV_EXCL_LINE
|
||||
|
||||
auto const nTakerPaysConstraint = pool.out * targetQuality.rate() - pool.in / f;
|
||||
if (nTakerPaysConstraint <= 0)
|
||||
return std::nullopt;
|
||||
|
||||
// Select the smallest to maximize the quality
|
||||
if (nTakerPaysConstraint < *nTakerPays)
|
||||
nTakerPays = nTakerPaysConstraint;
|
||||
|
||||
auto getAmounts = [&pool, &tfee](Number const& nTakerPaysProposed) {
|
||||
// Round downward to minimize the offer and to maximize the quality.
|
||||
// This has the most impact when takerPays is XRP.
|
||||
auto const takerPays = toAmount<TIn>(getIssue(pool.in), nTakerPaysProposed, Number::downward);
|
||||
return TAmounts<TIn, TOut>{takerPays, swapAssetIn(pool, takerPays, tfee)};
|
||||
};
|
||||
|
||||
// Try to reduce the offer size to improve the quality.
|
||||
// The quality might still not match the targetQuality for a tiny offer.
|
||||
if (auto const amounts = getAmounts(*nTakerPays); Quality{amounts} < targetQuality)
|
||||
return getAmounts(detail::reduceOffer(amounts.in));
|
||||
else
|
||||
return amounts;
|
||||
}
|
||||
|
||||
/** Generate AMM offer so that either updated Spot Price Quality (SPQ)
|
||||
* is equal to LOB quality (in this case AMM offer quality is
|
||||
* better than LOB quality) or AMM offer is equal to LOB quality
|
||||
* (in this case SPQ is better than LOB quality).
|
||||
* Pre-amendment code calculates takerPays first. If takerGets is XRP,
|
||||
* it is rounded down, which results in worse offer quality than
|
||||
* LOB quality, and the offer might fail to generate.
|
||||
* Post-amendment code calculates the XRP offer side first. The result
|
||||
* is rounded down, which makes the offer quality better.
|
||||
* It might not be possible to match either SPQ or AMM offer to LOB
|
||||
* quality. This generally happens at higher fees.
|
||||
* @param pool AMM pool balances
|
||||
* @param quality requested quality
|
||||
* @param tfee trading fee in basis points
|
||||
* @return seated in/out amounts if the quality can be changed
|
||||
*/
|
||||
template <typename TIn, typename TOut>
|
||||
std::optional<TAmounts<TIn, TOut>>
|
||||
changeSpotPriceQuality(
|
||||
TAmounts<TIn, TOut> const& pool,
|
||||
Quality const& quality,
|
||||
std::uint16_t tfee,
|
||||
Rules const& rules,
|
||||
beast::Journal j)
|
||||
{
|
||||
if (!rules.enabled(fixAMMv1_1))
|
||||
{
|
||||
// Finds takerPays (i) and takerGets (o) such that given pool
|
||||
// composition poolGets(I) and poolPays(O): (O - o) / (I + i) = quality.
|
||||
// Where takerGets is calculated as the swapAssetIn (see below).
|
||||
// The above equation produces the quadratic equation:
|
||||
// i^2*(1-fee) + i*I*(2-fee) + I^2 - I*O/quality,
|
||||
// which is solved for i, and o is found with swapAssetIn().
|
||||
auto const f = feeMult(tfee); // 1 - fee
|
||||
auto const& a = f;
|
||||
auto const b = pool.in * (1 + f);
|
||||
Number const c = pool.in * pool.in - pool.in * pool.out * quality.rate();
|
||||
if (auto const res = b * b - 4 * a * c; res < 0)
|
||||
return std::nullopt; // LCOV_EXCL_LINE
|
||||
else if (auto const nTakerPaysPropose = (-b + root2(res)) / (2 * a); nTakerPaysPropose > 0)
|
||||
{
|
||||
auto const nTakerPays = [&]() {
|
||||
// The fee might make the AMM offer quality less than CLOB
|
||||
// quality. Therefore, AMM offer has to satisfy this constraint:
|
||||
// o / i >= q. Substituting o with swapAssetIn() gives: i <= O /
|
||||
// q - I / (1 - fee).
|
||||
auto const nTakerPaysConstraint = pool.out * quality.rate() - pool.in / f;
|
||||
if (nTakerPaysPropose > nTakerPaysConstraint)
|
||||
return nTakerPaysConstraint;
|
||||
return nTakerPaysPropose;
|
||||
}();
|
||||
if (nTakerPays <= 0)
|
||||
{
|
||||
JLOG(j.trace()) << "changeSpotPriceQuality calc failed: " << to_string(pool.in) << " "
|
||||
<< to_string(pool.out) << " " << quality << " " << tfee;
|
||||
return std::nullopt;
|
||||
}
|
||||
auto const takerPays = toAmount<TIn>(getIssue(pool.in), nTakerPays, Number::upward);
|
||||
// should not fail
|
||||
if (auto const amounts = TAmounts<TIn, TOut>{takerPays, swapAssetIn(pool, takerPays, tfee)};
|
||||
Quality{amounts} < quality && !withinRelativeDistance(Quality{amounts}, quality, Number(1, -7)))
|
||||
{
|
||||
JLOG(j.error()) << "changeSpotPriceQuality failed: " << to_string(pool.in) << " " << to_string(pool.out)
|
||||
<< " "
|
||||
<< " " << quality << " " << tfee << " " << to_string(amounts.in) << " "
|
||||
<< to_string(amounts.out);
|
||||
Throw<std::runtime_error>("changeSpotPriceQuality failed");
|
||||
}
|
||||
else
|
||||
{
|
||||
JLOG(j.trace()) << "changeSpotPriceQuality succeeded: " << to_string(pool.in) << " "
|
||||
<< to_string(pool.out) << " "
|
||||
<< " " << quality << " " << tfee << " " << to_string(amounts.in) << " "
|
||||
<< to_string(amounts.out);
|
||||
return amounts;
|
||||
}
|
||||
}
|
||||
JLOG(j.trace()) << "changeSpotPriceQuality calc failed: " << to_string(pool.in) << " " << to_string(pool.out)
|
||||
<< " " << quality << " " << tfee;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Generate the offer starting with XRP side. Return seated offer amounts
|
||||
// if the offer can be generated, otherwise nullopt.
|
||||
auto const amounts = [&]() {
|
||||
if (isXRP(getIssue(pool.out)))
|
||||
return getAMMOfferStartWithTakerGets(pool, quality, tfee);
|
||||
return getAMMOfferStartWithTakerPays(pool, quality, tfee);
|
||||
}();
|
||||
if (!amounts)
|
||||
{
|
||||
JLOG(j.trace()) << "changeSpotPrice calc failed: " << to_string(pool.in) << " " << to_string(pool.out) << " "
|
||||
<< quality << " " << tfee << std::endl;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (Quality{*amounts} < quality)
|
||||
{
|
||||
JLOG(j.error()) << "changeSpotPriceQuality failed: " << to_string(pool.in) << " " << to_string(pool.out) << " "
|
||||
<< quality << " " << tfee << " " << to_string(amounts->in) << " " << to_string(amounts->out);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
JLOG(j.trace()) << "changeSpotPriceQuality succeeded: " << to_string(pool.in) << " " << to_string(pool.out) << " "
|
||||
<< " " << quality << " " << tfee << " " << to_string(amounts->in) << " " << to_string(amounts->out);
|
||||
|
||||
return amounts;
|
||||
}
|
||||
|
||||
/** AMM pool invariant - the product (A * B) after swap in/out has to remain
|
||||
* at least the same: (A + in) * (B - out) >= A * B
|
||||
* XRP round-off may result in a smaller product after swap in/out.
|
||||
* To address this:
|
||||
* - if on swapIn the out is XRP then the amount is round-off
|
||||
* downward, making the product slightly larger since out
|
||||
* value is reduced.
|
||||
* - if on swapOut the in is XRP then the amount is round-off
|
||||
* upward, making the product slightly larger since in
|
||||
* value is increased.
|
||||
*/
|
||||
|
||||
/** Swap assetIn into the pool and swap out a proportional amount
|
||||
* of the other asset. Implements AMM Swap in.
|
||||
* @see [XLS30d:AMM
|
||||
* Swap](https://github.com/XRPLF/XRPL-Standards/discussions/78)
|
||||
* @param pool current AMM pool balances
|
||||
* @param assetIn amount to swap in
|
||||
* @param tfee trading fee in basis points
|
||||
* @return
|
||||
*/
|
||||
template <typename TIn, typename TOut>
|
||||
TOut
|
||||
swapAssetIn(TAmounts<TIn, TOut> const& pool, TIn const& assetIn, std::uint16_t tfee)
|
||||
{
|
||||
if (auto const& rules = getCurrentTransactionRules(); rules && rules->enabled(fixAMMv1_1))
|
||||
{
|
||||
// set rounding to always favor the amm. Clip to zero.
|
||||
// calculate:
|
||||
// pool.out -
|
||||
// (pool.in * pool.out) / (pool.in + assetIn * feeMult(tfee)),
|
||||
// and explicitly set the rounding modes
|
||||
// Favoring the amm means we should:
|
||||
// minimize:
|
||||
// pool.out -
|
||||
// (pool.in * pool.out) / (pool.in + assetIn * feeMult(tfee)),
|
||||
// maximize:
|
||||
// (pool.in * pool.out) / (pool.in + assetIn * feeMult(tfee)),
|
||||
// (pool.in * pool.out)
|
||||
// minimize:
|
||||
// (pool.in + assetIn * feeMult(tfee)),
|
||||
// minimize:
|
||||
// assetIn * feeMult(tfee)
|
||||
// feeMult is: (1-fee), fee is tfee/100000
|
||||
// minimize:
|
||||
// 1-fee
|
||||
// maximize:
|
||||
// fee
|
||||
saveNumberRoundMode _{Number::getround()};
|
||||
|
||||
Number::setround(Number::upward);
|
||||
auto const numerator = pool.in * pool.out;
|
||||
auto const fee = getFee(tfee);
|
||||
|
||||
Number::setround(Number::downward);
|
||||
auto const denom = pool.in + assetIn * (1 - fee);
|
||||
|
||||
if (denom.signum() <= 0)
|
||||
return toAmount<TOut>(getIssue(pool.out), 0);
|
||||
|
||||
Number::setround(Number::upward);
|
||||
auto const ratio = numerator / denom;
|
||||
|
||||
Number::setround(Number::downward);
|
||||
auto const swapOut = pool.out - ratio;
|
||||
|
||||
if (swapOut.signum() < 0)
|
||||
return toAmount<TOut>(getIssue(pool.out), 0);
|
||||
|
||||
return toAmount<TOut>(getIssue(pool.out), swapOut, Number::downward);
|
||||
}
|
||||
else
|
||||
{
|
||||
return toAmount<TOut>(
|
||||
getIssue(pool.out),
|
||||
pool.out - (pool.in * pool.out) / (pool.in + assetIn * feeMult(tfee)),
|
||||
Number::downward);
|
||||
}
|
||||
}
|
||||
|
||||
/** Swap assetOut out of the pool and swap in a proportional amount
|
||||
* of the other asset. Implements AMM Swap out.
|
||||
* @see [XLS30d:AMM
|
||||
* Swap](https://github.com/XRPLF/XRPL-Standards/discussions/78)
|
||||
* @param pool current AMM pool balances
|
||||
* @param assetOut amount to swap out
|
||||
* @param tfee trading fee in basis points
|
||||
* @return
|
||||
*/
|
||||
template <typename TIn, typename TOut>
|
||||
TIn
|
||||
swapAssetOut(TAmounts<TIn, TOut> const& pool, TOut const& assetOut, std::uint16_t tfee)
|
||||
{
|
||||
if (auto const& rules = getCurrentTransactionRules(); rules && rules->enabled(fixAMMv1_1))
|
||||
{
|
||||
// set rounding to always favor the amm. Clip to zero.
|
||||
// calculate:
|
||||
// ((pool.in * pool.out) / (pool.out - assetOut) - pool.in) /
|
||||
// (1-tfee/100000)
|
||||
// maximize:
|
||||
// ((pool.in * pool.out) / (pool.out - assetOut) - pool.in)
|
||||
// maximize:
|
||||
// (pool.in * pool.out) / (pool.out - assetOut)
|
||||
// maximize:
|
||||
// (pool.in * pool.out)
|
||||
// minimize
|
||||
// (pool.out - assetOut)
|
||||
// minimize:
|
||||
// (1-tfee/100000)
|
||||
// maximize:
|
||||
// tfee/100000
|
||||
|
||||
saveNumberRoundMode _{Number::getround()};
|
||||
|
||||
Number::setround(Number::upward);
|
||||
auto const numerator = pool.in * pool.out;
|
||||
|
||||
Number::setround(Number::downward);
|
||||
auto const denom = pool.out - assetOut;
|
||||
if (denom.signum() <= 0)
|
||||
{
|
||||
return toMaxAmount<TIn>(getIssue(pool.in));
|
||||
}
|
||||
|
||||
Number::setround(Number::upward);
|
||||
auto const ratio = numerator / denom;
|
||||
auto const numerator2 = ratio - pool.in;
|
||||
auto const fee = getFee(tfee);
|
||||
|
||||
Number::setround(Number::downward);
|
||||
auto const feeMult = 1 - fee;
|
||||
|
||||
Number::setround(Number::upward);
|
||||
auto const swapIn = numerator2 / feeMult;
|
||||
if (swapIn.signum() < 0)
|
||||
return toAmount<TIn>(getIssue(pool.in), 0);
|
||||
|
||||
return toAmount<TIn>(getIssue(pool.in), swapIn, Number::upward);
|
||||
}
|
||||
else
|
||||
{
|
||||
return toAmount<TIn>(
|
||||
getIssue(pool.in),
|
||||
((pool.in * pool.out) / (pool.out - assetOut) - pool.in) / feeMult(tfee),
|
||||
Number::upward);
|
||||
}
|
||||
}
|
||||
|
||||
/** Return square of n.
|
||||
*/
|
||||
Number
|
||||
square(Number const& n);
|
||||
|
||||
/** Adjust LP tokens to deposit/withdraw.
|
||||
* Amount type keeps 16 digits. Maintaining the LP balance by adding
|
||||
* deposited tokens or subtracting withdrawn LP tokens from LP balance
|
||||
* results in losing precision in LP balance. I.e. the resulting LP balance
|
||||
* is less than the actual sum of LP tokens. To adjust for this, subtract
|
||||
* old tokens balance from the new one for deposit or vice versa for
|
||||
* withdraw to cancel out the precision loss.
|
||||
* @param lptAMMBalance LPT AMM Balance
|
||||
* @param lpTokens LP tokens to deposit or withdraw
|
||||
* @param isDeposit Yes if deposit, No if withdraw
|
||||
*/
|
||||
STAmount
|
||||
adjustLPTokens(STAmount const& lptAMMBalance, STAmount const& lpTokens, IsDeposit isDeposit);
|
||||
|
||||
/** Calls adjustLPTokens() and adjusts deposit or withdraw amounts if
|
||||
* the adjusted LP tokens are less than the provided LP tokens.
|
||||
* @param amountBalance asset1 pool balance
|
||||
* @param amount asset1 to deposit or withdraw
|
||||
* @param amount2 asset2 to deposit or withdraw
|
||||
* @param lptAMMBalance LPT AMM Balance
|
||||
* @param lpTokens LP tokens to deposit or withdraw
|
||||
* @param tfee trading fee in basis points
|
||||
* @param isDeposit Yes if deposit, No if withdraw
|
||||
* @return
|
||||
*/
|
||||
std::tuple<STAmount, std::optional<STAmount>, STAmount>
|
||||
adjustAmountsByLPTokens(
|
||||
STAmount const& amountBalance,
|
||||
STAmount const& amount,
|
||||
std::optional<STAmount> const& amount2,
|
||||
STAmount const& lptAMMBalance,
|
||||
STAmount const& lpTokens,
|
||||
std::uint16_t tfee,
|
||||
IsDeposit isDeposit);
|
||||
|
||||
/** Positive solution for quadratic equation:
|
||||
* x = (-b + sqrt(b**2 + 4*a*c))/(2*a)
|
||||
*/
|
||||
Number
|
||||
solveQuadraticEq(Number const& a, Number const& b, Number const& c);
|
||||
|
||||
STAmount
|
||||
multiply(STAmount const& amount, Number const& frac, Number::rounding_mode rm);
|
||||
|
||||
namespace detail {
|
||||
|
||||
inline Number::rounding_mode
|
||||
getLPTokenRounding(IsDeposit isDeposit)
|
||||
{
|
||||
// Minimize on deposit, maximize on withdraw to ensure
|
||||
// AMM invariant sqrt(poolAsset1 * poolAsset2) >= LPTokensBalance
|
||||
return isDeposit == IsDeposit::Yes ? Number::downward : Number::upward;
|
||||
}
|
||||
|
||||
inline Number::rounding_mode
|
||||
getAssetRounding(IsDeposit isDeposit)
|
||||
{
|
||||
// Maximize on deposit, minimize on withdraw to ensure
|
||||
// AMM invariant sqrt(poolAsset1 * poolAsset2) >= LPTokensBalance
|
||||
return isDeposit == IsDeposit::Yes ? Number::upward : Number::downward;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/** Round AMM equal deposit/withdrawal amount. Deposit/withdrawal formulas
|
||||
* calculate the amount as a fractional value of the pool balance. The rounding
|
||||
* takes place on the last step of multiplying the balance by the fraction if
|
||||
* AMMv1_3 is enabled.
|
||||
*/
|
||||
template <typename A>
|
||||
STAmount
|
||||
getRoundedAsset(Rules const& rules, STAmount const& balance, A const& frac, IsDeposit isDeposit)
|
||||
{
|
||||
if (!rules.enabled(fixAMMv1_3))
|
||||
{
|
||||
if constexpr (std::is_same_v<A, STAmount>)
|
||||
return multiply(balance, frac, balance.issue());
|
||||
else
|
||||
return toSTAmount(balance.issue(), balance * frac);
|
||||
}
|
||||
auto const rm = detail::getAssetRounding(isDeposit);
|
||||
return multiply(balance, frac, rm);
|
||||
}
|
||||
|
||||
/** Round AMM single deposit/withdrawal amount.
|
||||
* The lambda's are used to delay evaluation until the function
|
||||
* is executed so that the calculation is not done twice. noRoundCb() is
|
||||
* called if AMMv1_3 is disabled. Otherwise, the rounding is set and
|
||||
* the amount is:
|
||||
* isDeposit is Yes - the balance multiplied by productCb()
|
||||
* isDeposit is No - the result of productCb(). The rounding is
|
||||
* the same for all calculations in productCb()
|
||||
*/
|
||||
STAmount
|
||||
getRoundedAsset(
|
||||
Rules const& rules,
|
||||
std::function<Number()>&& noRoundCb,
|
||||
STAmount const& balance,
|
||||
std::function<Number()>&& productCb,
|
||||
IsDeposit isDeposit);
|
||||
|
||||
/** Round AMM deposit/withdrawal LPToken amount. Deposit/withdrawal formulas
|
||||
* calculate the lptokens as a fractional value of the AMM total lptokens.
|
||||
* The rounding takes place on the last step of multiplying the balance by
|
||||
* the fraction if AMMv1_3 is enabled. The tokens are then
|
||||
* adjusted to factor in the loss in precision (we only keep 16 significant
|
||||
* digits) when adding the lptokens to the balance.
|
||||
*/
|
||||
STAmount
|
||||
getRoundedLPTokens(Rules const& rules, STAmount const& balance, Number const& frac, IsDeposit isDeposit);
|
||||
|
||||
/** Round AMM single deposit/withdrawal LPToken amount.
|
||||
* The lambda's are used to delay evaluation until the function is executed
|
||||
* so that the calculations are not done twice.
|
||||
* noRoundCb() is called if AMMv1_3 is disabled. Otherwise, the rounding is set
|
||||
* and the lptokens are:
|
||||
* if isDeposit is Yes - the result of productCb(). The rounding is
|
||||
* the same for all calculations in productCb()
|
||||
* if isDeposit is No - the balance multiplied by productCb()
|
||||
* The lptokens are then adjusted to factor in the loss in precision
|
||||
* (we only keep 16 significant digits) when adding the lptokens to the balance.
|
||||
*/
|
||||
STAmount
|
||||
getRoundedLPTokens(
|
||||
Rules const& rules,
|
||||
std::function<Number()>&& noRoundCb,
|
||||
STAmount const& lptAMMBalance,
|
||||
std::function<Number()>&& productCb,
|
||||
IsDeposit isDeposit);
|
||||
|
||||
/* Next two functions adjust asset in/out amount to factor in the adjusted
|
||||
* lptokens. The lptokens are calculated from the asset in/out. The lptokens are
|
||||
* then adjusted to factor in the loss in precision. The adjusted lptokens might
|
||||
* be less than the initially calculated tokens. Therefore, the asset in/out
|
||||
* must be adjusted. The rounding might result in the adjusted amount being
|
||||
* greater than the original asset in/out amount. If this happens,
|
||||
* then the original amount is reduced by the difference in the adjusted amount
|
||||
* and the original amount. The actual tokens and the actual adjusted amount
|
||||
* are then recalculated. The minimum of the original and the actual
|
||||
* adjusted amount is returned.
|
||||
*/
|
||||
std::pair<STAmount, STAmount>
|
||||
adjustAssetInByTokens(
|
||||
Rules const& rules,
|
||||
STAmount const& balance,
|
||||
STAmount const& amount,
|
||||
STAmount const& lptAMMBalance,
|
||||
STAmount const& tokens,
|
||||
std::uint16_t tfee);
|
||||
std::pair<STAmount, STAmount>
|
||||
adjustAssetOutByTokens(
|
||||
Rules const& rules,
|
||||
STAmount const& balance,
|
||||
STAmount const& amount,
|
||||
STAmount const& lptAMMBalance,
|
||||
STAmount const& tokens,
|
||||
std::uint16_t tfee);
|
||||
|
||||
/** Find a fraction of tokens after the tokens are adjusted. The fraction
|
||||
* is used to adjust equal deposit/withdraw amount.
|
||||
*/
|
||||
Number
|
||||
adjustFracByTokens(Rules const& rules, STAmount const& lptAMMBalance, STAmount const& tokens, Number const& frac);
|
||||
|
||||
} // namespace xrpl
|
||||
101
include/xrpl/tx/transactors/AMM/AMMUtils.h
Normal file
101
include/xrpl/tx/transactors/AMM/AMMUtils.h
Normal file
@@ -0,0 +1,101 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Expected.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class ReadView;
|
||||
class ApplyView;
|
||||
class Sandbox;
|
||||
class NetClock;
|
||||
|
||||
/** Get AMM pool balances.
|
||||
*/
|
||||
std::pair<STAmount, STAmount>
|
||||
ammPoolHolds(
|
||||
ReadView const& view,
|
||||
AccountID const& ammAccountID,
|
||||
Issue const& issue1,
|
||||
Issue const& issue2,
|
||||
FreezeHandling freezeHandling,
|
||||
beast::Journal const j);
|
||||
|
||||
/** Get AMM pool and LP token balances. If both optIssue are
|
||||
* provided then they are used as the AMM token pair issues.
|
||||
* Otherwise the missing issues are fetched from ammSle.
|
||||
*/
|
||||
Expected<std::tuple<STAmount, STAmount, STAmount>, TER>
|
||||
ammHolds(
|
||||
ReadView const& view,
|
||||
SLE const& ammSle,
|
||||
std::optional<Issue> const& optIssue1,
|
||||
std::optional<Issue> const& optIssue2,
|
||||
FreezeHandling freezeHandling,
|
||||
beast::Journal const j);
|
||||
|
||||
/** Get the balance of LP tokens.
|
||||
*/
|
||||
STAmount
|
||||
ammLPHolds(
|
||||
ReadView const& view,
|
||||
Currency const& cur1,
|
||||
Currency const& cur2,
|
||||
AccountID const& ammAccount,
|
||||
AccountID const& lpAccount,
|
||||
beast::Journal const j);
|
||||
|
||||
STAmount
|
||||
ammLPHolds(ReadView const& view, SLE const& ammSle, AccountID const& lpAccount, beast::Journal const j);
|
||||
|
||||
/** Get AMM trading fee for the given account. The fee is discounted
|
||||
* if the account is the auction slot owner or one of the slot's authorized
|
||||
* accounts.
|
||||
*/
|
||||
std::uint16_t
|
||||
getTradingFee(ReadView const& view, SLE const& ammSle, AccountID const& account);
|
||||
|
||||
/** Returns total amount held by AMM for the given token.
|
||||
*/
|
||||
STAmount
|
||||
ammAccountHolds(ReadView const& view, AccountID const& ammAccountID, Issue const& issue);
|
||||
|
||||
/** Delete trustlines to AMM. If all trustlines are deleted then
|
||||
* AMM object and account are deleted. Otherwise tecIMPCOMPLETE is returned.
|
||||
*/
|
||||
TER
|
||||
deleteAMMAccount(Sandbox& view, Issue const& asset, Issue const& asset2, beast::Journal j);
|
||||
|
||||
/** Initialize Auction and Voting slots and set the trading/discounted fee.
|
||||
*/
|
||||
void
|
||||
initializeFeeAuctionVote(
|
||||
ApplyView& view,
|
||||
std::shared_ptr<SLE>& ammSle,
|
||||
AccountID const& account,
|
||||
Issue const& lptIssue,
|
||||
std::uint16_t tfee);
|
||||
|
||||
/** Return true if the Liquidity Provider is the only AMM provider, false
|
||||
* otherwise. Return tecINTERNAL if encountered an unexpected condition,
|
||||
* for instance Liquidity Provider has more than one LPToken trustline.
|
||||
*/
|
||||
Expected<bool, TER>
|
||||
isOnlyLiquidityProvider(ReadView const& view, Issue const& ammIssue, AccountID const& lpAccount);
|
||||
|
||||
/** Due to rounding, the LPTokenBalance of the last LP might
|
||||
* not match the LP's trustline balance. If it's within the tolerance,
|
||||
* update LPTokenBalance to match the LP's trustline balance.
|
||||
*/
|
||||
Expected<bool, TER>
|
||||
verifyAndAdjustLPTokenBalance(
|
||||
Sandbox& sb,
|
||||
STAmount const& lpTokens,
|
||||
std::shared_ptr<SLE>& ammSle,
|
||||
AccountID const& account);
|
||||
|
||||
} // namespace xrpl
|
||||
52
include/xrpl/tx/transactors/AMM/AMMVote.h
Normal file
52
include/xrpl/tx/transactors/AMM/AMMVote.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/** AMMVote implements AMM vote Transactor.
|
||||
* This transactor allows for the TradingFee of the AMM instance be a votable
|
||||
* parameter. Any account (LP) that holds the corresponding LPTokens can cast
|
||||
* a vote using the new AMMVote transaction. VoteSlots array in ltAMM object
|
||||
* keeps track of upto eight active votes (VoteEntry) for the instance.
|
||||
* VoteEntry contains:
|
||||
* Account - account id that cast the vote.
|
||||
* FeeVal - proposed fee in basis points.
|
||||
* VoteWeight - LPTokens owned by the account in basis points.
|
||||
* TradingFee is calculated as sum(VoteWeight_i * fee_i)/sum(VoteWeight_i).
|
||||
* Every time AMMVote transaction is submitted, the transactor
|
||||
* - Fails the transaction if the account doesn't hold LPTokens
|
||||
* - Removes VoteEntry for accounts that don't hold LPTokens
|
||||
* - If there are fewer than eight VoteEntry objects then add new VoteEntry
|
||||
* object for the account.
|
||||
* - If all eight VoteEntry slots are full, then remove VoteEntry that
|
||||
* holds less LPTokens than the account. If all accounts hold more
|
||||
* LPTokens then fail transaction.
|
||||
* - If the account already holds a vote, then update VoteEntry.
|
||||
* - Calculate and update TradingFee.
|
||||
* @see [XLS30d:Governance: Trading Fee Voting
|
||||
* Mechanism](https://github.com/XRPLF/XRPL-Standards/discussions/78)
|
||||
*/
|
||||
class AMMVote : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit AMMVote(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
294
include/xrpl/tx/transactors/AMM/AMMWithdraw.h
Normal file
294
include/xrpl/tx/transactors/AMM/AMMWithdraw.h
Normal file
@@ -0,0 +1,294 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class Sandbox;
|
||||
|
||||
/** AMMWithdraw implements AMM withdraw Transactor.
|
||||
* The withdraw transaction is used to remove liquidity from the AMM instance
|
||||
* pool, thus redeeming some share of the pools that one owns in the form
|
||||
* of LPTokens. If the trader withdraws proportional values of both assets
|
||||
* without changing their relative pricing, no trading fee is charged on
|
||||
* the transaction. The trader can specify different combination of
|
||||
* the fields in the withdrawal.
|
||||
* LPTokens - transaction assumes proportional withdrawal of pool assets
|
||||
* for the amount of LPTokens.
|
||||
* Asset1Out - transaction assumes withdrawal of single asset equivalent
|
||||
* to the amount specified in Asset1Out.
|
||||
* Asset1Out and Asset2Out - transaction assumes all assets withdrawal
|
||||
* with the constraints on the maximum amount of each asset that
|
||||
* the trader is willing to withdraw.
|
||||
* Asset1Out and LPTokens - transaction assumes withdrawal of single
|
||||
* asset specified in Asset1Out proportional to the share represented
|
||||
* by the amount of LPTokens.
|
||||
* Asset1Out and EPrice - transaction assumes withdrawal of single
|
||||
* asset with the following constraints:
|
||||
* a. Amount of asset1 if specified (not 0) in Asset1Out specifies
|
||||
* the minimum amount of asset1 that the trader is willing
|
||||
* to withdraw.
|
||||
* b. The effective price of asset traded out does not exceed
|
||||
* the amount specified in EPrice.
|
||||
* Following updates after a successful transaction:
|
||||
* The withdrawn asset, if XRP, is transferred from AMM instance account
|
||||
* to the account that initiated the transaction, thus changing
|
||||
* the Balance field of each account.
|
||||
* The withdrawn asset, if token, is balanced between the AMM instance
|
||||
* account and the issuer account.
|
||||
* The LPTokens ~ are balanced between the AMM instance account and
|
||||
* the account that initiated the transaction.
|
||||
* The pool composition is updated.
|
||||
* @see [XLS30d:AMMWithdraw
|
||||
* transaction](https://github.com/XRPLF/XRPL-Standards/discussions/78)
|
||||
*/
|
||||
|
||||
enum class WithdrawAll : bool { No = false, Yes };
|
||||
|
||||
class AMMWithdraw : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit AMMWithdraw(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
|
||||
/** Equal-asset withdrawal (LPTokens) of some AMM instance pools
|
||||
* shares represented by the number of LPTokens .
|
||||
* The trading fee is not charged.
|
||||
* @param view
|
||||
* @param ammAccount
|
||||
* @param amountBalance current LP asset1 balance
|
||||
* @param amount2Balance current LP asset2 balance
|
||||
* @param lptAMMBalance current AMM LPT balance
|
||||
* @param lpTokens current LPT balance
|
||||
* @param lpTokensWithdraw amount of tokens to withdraw
|
||||
* @param tfee trading fee in basis points
|
||||
* @param withdrawAll if withdrawing all lptokens
|
||||
* @param priorBalance balance before fees
|
||||
* @return
|
||||
*/
|
||||
static std::tuple<TER, STAmount, STAmount, std::optional<STAmount>>
|
||||
equalWithdrawTokens(
|
||||
Sandbox& view,
|
||||
SLE const& ammSle,
|
||||
AccountID const account,
|
||||
AccountID const& ammAccount,
|
||||
STAmount const& amountBalance,
|
||||
STAmount const& amount2Balance,
|
||||
STAmount const& lptAMMBalance,
|
||||
STAmount const& lpTokens,
|
||||
STAmount const& lpTokensWithdraw,
|
||||
std::uint16_t tfee,
|
||||
FreezeHandling freezeHanding,
|
||||
WithdrawAll withdrawAll,
|
||||
XRPAmount const& priorBalance,
|
||||
beast::Journal const& journal);
|
||||
|
||||
/** Withdraw requested assets and token from AMM into LP account.
|
||||
* Return new total LPToken balance and the withdrawn amounts for both
|
||||
* assets.
|
||||
* @param view
|
||||
* @param ammSle AMM ledger entry
|
||||
* @param ammAccount AMM account
|
||||
* @param amountBalance current LP asset1 balance
|
||||
* @param amountWithdraw asset1 withdraw amount
|
||||
* @param amount2Withdraw asset2 withdraw amount
|
||||
* @param lpTokensAMMBalance current AMM LPT balance
|
||||
* @param lpTokensWithdraw amount of lptokens to withdraw
|
||||
* @param tfee trading fee in basis points
|
||||
* @param withdrawAll if withdraw all lptokens
|
||||
* @param priorBalance balance before fees
|
||||
* @return
|
||||
*/
|
||||
static std::tuple<TER, STAmount, STAmount, std::optional<STAmount>>
|
||||
withdraw(
|
||||
Sandbox& view,
|
||||
SLE const& ammSle,
|
||||
AccountID const& ammAccount,
|
||||
AccountID const& account,
|
||||
STAmount const& amountBalance,
|
||||
STAmount const& amountWithdraw,
|
||||
std::optional<STAmount> const& amount2Withdraw,
|
||||
STAmount const& lpTokensAMMBalance,
|
||||
STAmount const& lpTokensWithdraw,
|
||||
std::uint16_t tfee,
|
||||
FreezeHandling freezeHandling,
|
||||
WithdrawAll withdrawAll,
|
||||
XRPAmount const& priorBalance,
|
||||
beast::Journal const& journal);
|
||||
|
||||
static std::pair<TER, bool>
|
||||
deleteAMMAccountIfEmpty(
|
||||
Sandbox& sb,
|
||||
std::shared_ptr<SLE> const ammSle,
|
||||
STAmount const& lpTokenBalance,
|
||||
Issue const& issue1,
|
||||
Issue const& issue2,
|
||||
beast::Journal const& journal);
|
||||
|
||||
private:
|
||||
std::pair<TER, bool>
|
||||
applyGuts(Sandbox& view);
|
||||
|
||||
/** Withdraw requested assets and token from AMM into LP account.
|
||||
* Return new total LPToken balance.
|
||||
* @param view
|
||||
* @param ammSle AMM ledger entry
|
||||
* @param ammAccount AMM account
|
||||
* @param amountBalance current LP asset1 balance
|
||||
* @param amountWithdraw asset1 withdraw amount
|
||||
* @param amount2Withdraw asset2 withdraw amount
|
||||
* @param lpTokensAMMBalance current AMM LPT balance
|
||||
* @param lpTokensWithdraw amount of lptokens to withdraw
|
||||
* @return
|
||||
*/
|
||||
std::pair<TER, STAmount>
|
||||
withdraw(
|
||||
Sandbox& view,
|
||||
SLE const& ammSle,
|
||||
AccountID const& ammAccount,
|
||||
STAmount const& amountBalance,
|
||||
STAmount const& amountWithdraw,
|
||||
std::optional<STAmount> const& amount2Withdraw,
|
||||
STAmount const& lpTokensAMMBalance,
|
||||
STAmount const& lpTokensWithdraw,
|
||||
std::uint16_t tfee);
|
||||
|
||||
/** Equal-asset withdrawal (LPTokens) of some AMM instance pools
|
||||
* shares represented by the number of LPTokens .
|
||||
* The trading fee is not charged.
|
||||
* @param view
|
||||
* @param ammAccount
|
||||
* @param amountBalance current LP asset1 balance
|
||||
* @param amount2Balance current LP asset2 balance
|
||||
* @param lptAMMBalance current AMM LPT balance
|
||||
* @param lpTokens current LPT balance
|
||||
* @param lpTokensWithdraw amount of tokens to withdraw
|
||||
* @param tfee trading fee in basis points
|
||||
* @return
|
||||
*/
|
||||
std::pair<TER, STAmount>
|
||||
equalWithdrawTokens(
|
||||
Sandbox& view,
|
||||
SLE const& ammSle,
|
||||
AccountID const& ammAccount,
|
||||
STAmount const& amountBalance,
|
||||
STAmount const& amount2Balance,
|
||||
STAmount const& lptAMMBalance,
|
||||
STAmount const& lpTokens,
|
||||
STAmount const& lpTokensWithdraw,
|
||||
std::uint16_t tfee);
|
||||
|
||||
/** Withdraw both assets (Asset1Out, Asset2Out) with the constraints
|
||||
* on the maximum amount of each asset that the trader is willing
|
||||
* to withdraw. The trading fee is not charged.
|
||||
* @param view
|
||||
* @param ammAccount
|
||||
* @param amountBalance current AMM asset1 balance
|
||||
* @param amount2Balance current AMM asset2 balance
|
||||
* @param lptAMMBalance current AMM LPT balance
|
||||
* @param amount asset1 withdraw amount
|
||||
* @param amount2 max asset2 withdraw amount
|
||||
* @param tfee trading fee in basis points
|
||||
* @return
|
||||
*/
|
||||
std::pair<TER, STAmount>
|
||||
equalWithdrawLimit(
|
||||
Sandbox& view,
|
||||
SLE const& ammSle,
|
||||
AccountID const& ammAccount,
|
||||
STAmount const& amountBalance,
|
||||
STAmount const& amount2Balance,
|
||||
STAmount const& lptAMMBalance,
|
||||
STAmount const& amount,
|
||||
STAmount const& amount2,
|
||||
std::uint16_t tfee);
|
||||
|
||||
/** Single asset withdrawal (Asset1Out) equivalent to the amount specified
|
||||
* in Asset1Out. The trading fee is charged.
|
||||
* @param view
|
||||
* @param ammAccount
|
||||
* @param amountBalance current AMM asset1 balance
|
||||
* @param lptAMMBalance current AMM LPT balance
|
||||
* @param amount asset1 withdraw amount
|
||||
* @param tfee trading fee in basis points
|
||||
* @return
|
||||
*/
|
||||
std::pair<TER, STAmount>
|
||||
singleWithdraw(
|
||||
Sandbox& view,
|
||||
SLE const& ammSle,
|
||||
AccountID const& ammAccount,
|
||||
STAmount const& amountBalance,
|
||||
STAmount const& lptAMMBalance,
|
||||
STAmount const& amount,
|
||||
std::uint16_t tfee);
|
||||
|
||||
/** Single asset withdrawal (Asset1Out, LPTokens) proportional
|
||||
* to the share specified by tokens. The trading fee is charged.
|
||||
* @param view
|
||||
* @param ammAccount
|
||||
* @param amountBalance current AMM asset1 balance
|
||||
* @param lptAMMBalance current AMM LPT balance
|
||||
* @param amount asset1 withdraw amount
|
||||
* @param lpTokensWithdraw amount of tokens to withdraw
|
||||
* @param tfee trading fee in basis points
|
||||
* @return
|
||||
*/
|
||||
std::pair<TER, STAmount>
|
||||
singleWithdrawTokens(
|
||||
Sandbox& view,
|
||||
SLE const& ammSle,
|
||||
AccountID const& ammAccount,
|
||||
STAmount const& amountBalance,
|
||||
STAmount const& lptAMMBalance,
|
||||
STAmount const& amount,
|
||||
STAmount const& lpTokensWithdraw,
|
||||
std::uint16_t tfee);
|
||||
|
||||
/** Withdraw single asset (Asset1Out, EPrice) with two constraints.
|
||||
* The trading fee is charged.
|
||||
* @param view
|
||||
* @param ammAccount
|
||||
* @param amountBalance current AMM asset1 balance
|
||||
* @param lptAMMBalance current AMM LPT balance
|
||||
* @param amount asset1 withdraw amount
|
||||
* @param ePrice maximum asset1 effective price
|
||||
* @param tfee trading fee in basis points
|
||||
* @return
|
||||
*/
|
||||
std::pair<TER, STAmount>
|
||||
singleWithdrawEPrice(
|
||||
Sandbox& view,
|
||||
SLE const& ammSle,
|
||||
AccountID const& ammAccount,
|
||||
STAmount const& amountBalance,
|
||||
STAmount const& lptAMMBalance,
|
||||
STAmount const& amount,
|
||||
STAmount const& ePrice,
|
||||
std::uint16_t tfee);
|
||||
|
||||
/** Check from the flags if it's withdraw all */
|
||||
static WithdrawAll
|
||||
isWithdrawAll(STTx const& tx);
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
55
include/xrpl/tx/transactors/Batch.h
Normal file
55
include/xrpl/tx/transactors/Batch.h
Normal file
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class Batch : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit Batch(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static XRPAmount
|
||||
calculateBaseFee(ReadView const& view, STTx const& tx);
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflightSigValidated(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
checkSign(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
|
||||
static constexpr auto disabledTxTypes = std::to_array<TxType>({
|
||||
ttVAULT_CREATE,
|
||||
ttVAULT_SET,
|
||||
ttVAULT_DELETE,
|
||||
ttVAULT_DEPOSIT,
|
||||
ttVAULT_WITHDRAW,
|
||||
ttVAULT_CLAWBACK,
|
||||
ttLOAN_BROKER_SET,
|
||||
ttLOAN_BROKER_DELETE,
|
||||
ttLOAN_BROKER_COVER_DEPOSIT,
|
||||
ttLOAN_BROKER_COVER_WITHDRAW,
|
||||
ttLOAN_BROKER_COVER_CLAWBACK,
|
||||
ttLOAN_SET,
|
||||
ttLOAN_DELETE,
|
||||
ttLOAN_MANAGE,
|
||||
ttLOAN_PAY,
|
||||
});
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
45
include/xrpl/tx/transactors/Change.h
Normal file
45
include/xrpl/tx/transactors/Change.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class Change : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit Change(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
void
|
||||
preCompute() override;
|
||||
|
||||
static XRPAmount
|
||||
calculateBaseFee(ReadView const& view, STTx const& tx)
|
||||
{
|
||||
return XRPAmount{0};
|
||||
}
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
private:
|
||||
TER
|
||||
applyAmendment();
|
||||
|
||||
TER
|
||||
applyFee();
|
||||
|
||||
TER
|
||||
applyUNLModify();
|
||||
};
|
||||
|
||||
using EnableAmendment = Change;
|
||||
using SetFee = Change;
|
||||
using UNLModify = Change;
|
||||
|
||||
} // namespace xrpl
|
||||
28
include/xrpl/tx/transactors/Check/CancelCheck.h
Normal file
28
include/xrpl/tx/transactors/Check/CancelCheck.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class CancelCheck : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit CancelCheck(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
using CheckCancel = CancelCheck;
|
||||
|
||||
} // namespace xrpl
|
||||
28
include/xrpl/tx/transactors/Check/CashCheck.h
Normal file
28
include/xrpl/tx/transactors/Check/CashCheck.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class CashCheck : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit CashCheck(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
using CheckCash = CashCheck;
|
||||
|
||||
} // namespace xrpl
|
||||
28
include/xrpl/tx/transactors/Check/CreateCheck.h
Normal file
28
include/xrpl/tx/transactors/Check/CreateCheck.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class CreateCheck : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit CreateCheck(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
using CheckCreate = CreateCheck;
|
||||
|
||||
} // namespace xrpl
|
||||
29
include/xrpl/tx/transactors/Clawback.h
Normal file
29
include/xrpl/tx/transactors/Clawback.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class Clawback : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit Clawback(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
66
include/xrpl/tx/transactors/CreateTicket.h
Normal file
66
include/xrpl/tx/transactors/CreateTicket.h
Normal file
@@ -0,0 +1,66 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class CreateTicket : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Custom};
|
||||
|
||||
constexpr static std::uint32_t minValidCount = 1;
|
||||
|
||||
// A note on how the maxValidCount was determined. The goal is for
|
||||
// a single TicketCreate transaction to not use more compute power than
|
||||
// a single compute-intensive Payment.
|
||||
//
|
||||
// Timing was performed using a MacBook Pro laptop and a release build
|
||||
// with asserts off. 20 measurements were taken of each of the Payment
|
||||
// and TicketCreate transactions and averaged to get timings.
|
||||
//
|
||||
// For the example compute-intensive Payment a Discrepancy unit test
|
||||
// unit test Payment with 3 paths was chosen. With all the latest
|
||||
// amendments enabled, that Payment::doApply() operation took, on
|
||||
// average, 1.25 ms.
|
||||
//
|
||||
// Using that same test set up creating 250 Tickets in a single
|
||||
// CreateTicket::doApply() in a unit test took, on average, 1.21 ms.
|
||||
//
|
||||
// So, for the moment, a single transaction creating 250 Tickets takes
|
||||
// about the same compute time as a single compute-intensive payment.
|
||||
//
|
||||
// October 2018.
|
||||
constexpr static std::uint32_t maxValidCount = 250;
|
||||
|
||||
// The maximum number of Tickets an account may hold. If a
|
||||
// TicketCreate would cause an account to own more than this many
|
||||
// tickets, then the TicketCreate will fail.
|
||||
//
|
||||
// The number was chosen arbitrarily and is an effort toward avoiding
|
||||
// ledger-stuffing with Tickets.
|
||||
constexpr static std::uint32_t maxTicketThreshold = 250;
|
||||
|
||||
explicit CreateTicket(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static TxConsequences
|
||||
makeTxConsequences(PreflightContext const& ctx);
|
||||
|
||||
/** Enforce constraints beyond those of the Transactor base class. */
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
/** Enforce constraints beyond those of the Transactor base class. */
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
/** Precondition: fee collection is likely. Attempt to create ticket(s). */
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
using TicketCreate = CreateTicket;
|
||||
|
||||
} // namespace xrpl
|
||||
77
include/xrpl/tx/transactors/Credentials.h
Normal file
77
include/xrpl/tx/transactors/Credentials.h
Normal file
@@ -0,0 +1,77 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class CredentialCreate : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit CredentialCreate(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class CredentialDelete : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit CredentialDelete(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class CredentialAccept : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit CredentialAccept(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
47
include/xrpl/tx/transactors/DID.h
Normal file
47
include/xrpl/tx/transactors/DID.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class DIDSet : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit DIDSet(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class DIDDelete : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit DIDDelete(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
deleteSLE(ApplyContext& ctx, Keylet sleKeylet, AccountID const owner);
|
||||
|
||||
static TER
|
||||
deleteSLE(ApplyView& view, std::shared_ptr<SLE> sle, AccountID const owner, beast::Journal j);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
30
include/xrpl/tx/transactors/Delegate/DelegateSet.h
Normal file
30
include/xrpl/tx/transactors/Delegate/DelegateSet.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class DelegateSet : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit DelegateSet(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
|
||||
// Interface used by DeleteAccount
|
||||
static TER
|
||||
deleteDelegate(ApplyView& view, std::shared_ptr<SLE> const& sle, AccountID const& account, beast::Journal j);
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
35
include/xrpl/tx/transactors/Delegate/DelegateUtils.h
Normal file
35
include/xrpl/tx/transactors/Delegate/DelegateUtils.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/protocol/Permissions.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* Check if the delegate account has permission to execute the transaction.
|
||||
* @param delegate The delegate account.
|
||||
* @param tx The transaction that the delegate account intends to execute.
|
||||
* @return tesSUCCESS if the transaction is allowed, terNO_DELEGATE_PERMISSION
|
||||
* if not.
|
||||
*/
|
||||
NotTEC
|
||||
checkTxPermission(std::shared_ptr<SLE const> const& delegate, STTx const& tx);
|
||||
|
||||
/**
|
||||
* Load the granular permissions granted to the delegate account for the
|
||||
* specified transaction type
|
||||
* @param delegate The delegate account.
|
||||
* @param type Used to determine which granted granular permissions to load,
|
||||
* based on the transaction type.
|
||||
* @param granularPermissions Granted granular permissions tied to the
|
||||
* transaction type.
|
||||
*/
|
||||
void
|
||||
loadGranularPermission(
|
||||
std::shared_ptr<SLE const> const& delegate,
|
||||
TxType const& type,
|
||||
std::unordered_set<GranularPermissionType>& granularPermissions);
|
||||
|
||||
} // namespace xrpl
|
||||
34
include/xrpl/tx/transactors/DeleteAccount.h
Normal file
34
include/xrpl/tx/transactors/DeleteAccount.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class DeleteAccount : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Blocker};
|
||||
|
||||
explicit DeleteAccount(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static XRPAmount
|
||||
calculateBaseFee(ReadView const& view, STTx const& tx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
using AccountDelete = DeleteAccount;
|
||||
|
||||
} // namespace xrpl
|
||||
40
include/xrpl/tx/transactors/DeleteOracle.h
Normal file
40
include/xrpl/tx/transactors/DeleteOracle.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
Price Oracle is a system that acts as a bridge between
|
||||
a blockchain network and the external world, providing off-chain price data
|
||||
to decentralized applications (dApps) on the blockchain. This implementation
|
||||
conforms to the requirements specified in the XLS-47d.
|
||||
|
||||
The DeleteOracle transactor implements the deletion of Oracle objects.
|
||||
*/
|
||||
|
||||
class DeleteOracle : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit DeleteOracle(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
|
||||
static TER
|
||||
deleteOracle(ApplyView& view, std::shared_ptr<SLE> const& sle, AccountID const& account, beast::Journal j);
|
||||
};
|
||||
|
||||
using OracleDelete = DeleteOracle;
|
||||
|
||||
} // namespace xrpl
|
||||
33
include/xrpl/tx/transactors/DepositPreauth.h
Normal file
33
include/xrpl/tx/transactors/DepositPreauth.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class DepositPreauth : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit DepositPreauth(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
|
||||
// Interface used by DeleteAccount
|
||||
static TER
|
||||
removeFromLedger(ApplyView& view, uint256 const& delIndex, beast::Journal j);
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
80
include/xrpl/tx/transactors/Escrow.h
Normal file
80
include/xrpl/tx/transactors/Escrow.h
Normal file
@@ -0,0 +1,80 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class EscrowCreate : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Custom};
|
||||
|
||||
explicit EscrowCreate(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static TxConsequences
|
||||
makeTxConsequences(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class EscrowFinish : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit EscrowFinish(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflightSigValidated(PreflightContext const& ctx);
|
||||
|
||||
static XRPAmount
|
||||
calculateBaseFee(ReadView const& view, STTx const& tx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class EscrowCancel : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit EscrowCancel(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
33
include/xrpl/tx/transactors/LedgerStateFix.h
Normal file
33
include/xrpl/tx/transactors/LedgerStateFix.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class LedgerStateFix : public Transactor
|
||||
{
|
||||
public:
|
||||
enum FixType : std::uint16_t {
|
||||
nfTokenPageLink = 1,
|
||||
};
|
||||
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit LedgerStateFix(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static XRPAmount
|
||||
calculateBaseFee(ReadView const& view, STTx const& tx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
467
include/xrpl/tx/transactors/Lending/LendingHelpers.h
Normal file
467
include/xrpl/tx/transactors/Lending/LendingHelpers.h
Normal file
@@ -0,0 +1,467 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/ledger/View.h>
|
||||
#include <xrpl/protocol/st.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
struct PreflightContext;
|
||||
|
||||
// Lending protocol has dependencies, so capture them here.
|
||||
bool
|
||||
checkLendingProtocolDependencies(PreflightContext const& ctx);
|
||||
|
||||
static constexpr std::uint32_t secondsInYear = 365 * 24 * 60 * 60;
|
||||
|
||||
Number
|
||||
loanPeriodicRate(TenthBips32 interestRate, std::uint32_t paymentInterval);
|
||||
|
||||
/// Ensure the periodic payment is always rounded consistently
|
||||
inline Number
|
||||
roundPeriodicPayment(Asset const& asset, Number const& periodicPayment, std::int32_t scale)
|
||||
{
|
||||
return roundToAsset(asset, periodicPayment, scale, Number::upward);
|
||||
}
|
||||
|
||||
/* Represents the breakdown of amounts to be paid and changes applied to the
|
||||
* Loan object while processing a loan payment.
|
||||
*
|
||||
* This structure is returned after processing a loan payment transaction and
|
||||
* captures the amounts that need to be paid. The actual ledger entry changes
|
||||
* are made in LoanPay based on this structure values.
|
||||
*
|
||||
* The sum of principalPaid, interestPaid, and feePaid represents the total
|
||||
* amount to be deducted from the borrower's account. The valueChange field
|
||||
* tracks whether the loan's total value increased or decreased beyond normal
|
||||
* amortization.
|
||||
*
|
||||
* This structure is explained in the XLS-66 spec, section 3.2.4.2 (Payment
|
||||
* Processing).
|
||||
*/
|
||||
struct LoanPaymentParts
|
||||
{
|
||||
// The amount of principal paid that reduces the loan balance.
|
||||
// This amount is subtracted from sfPrincipalOutstanding in the Loan object
|
||||
// and paid to the Vault
|
||||
Number principalPaid = numZero;
|
||||
|
||||
// The total amount of interest paid to the Vault.
|
||||
// This includes:
|
||||
// - Tracked interest from the amortization schedule
|
||||
// - Untracked interest (e.g., late payment penalty interest)
|
||||
// This value is always non-negative.
|
||||
Number interestPaid = numZero;
|
||||
|
||||
// The change in the loan's total value outstanding.
|
||||
// - If valueChange < 0: Loan value decreased
|
||||
// - If valueChange > 0: Loan value increased
|
||||
// - If valueChange = 0: No value adjustment
|
||||
//
|
||||
// For regular on-time payments, this is always 0. Non-zero values occur
|
||||
// when:
|
||||
// - Overpayments reduce the loan balance beyond the scheduled amount
|
||||
// - Late payments add penalty interest to the loan value
|
||||
// - Early full payment may increase or decrease the loan value based on
|
||||
// terms
|
||||
Number valueChange = numZero;
|
||||
|
||||
/* The total amount of fees paid to the Broker.
|
||||
* This includes:
|
||||
* - Tracked management fees from the amortization schedule
|
||||
* - Untracked fees (e.g., late payment fees, service fees, origination
|
||||
* fees) This value is always non-negative.
|
||||
*/
|
||||
Number feePaid = numZero;
|
||||
|
||||
LoanPaymentParts&
|
||||
operator+=(LoanPaymentParts const& other);
|
||||
|
||||
bool
|
||||
operator==(LoanPaymentParts const& other) const;
|
||||
};
|
||||
|
||||
/** This structure captures the parts of a loan state.
|
||||
*
|
||||
* Whether the values are theoretical (unrounded) or rounded will depend on how
|
||||
* it was computed.
|
||||
*
|
||||
* Many of the fields can be derived from each other, but they're all provided
|
||||
* here to reduce code duplication and possible mistakes.
|
||||
* e.g.
|
||||
* * interestOutstanding = valueOutstanding - principalOutstanding
|
||||
* * interestDue = interestOutstanding - managementFeeDue
|
||||
*/
|
||||
struct LoanState
|
||||
{
|
||||
// Total value still due to be paid by the borrower.
|
||||
Number valueOutstanding;
|
||||
// Principal still due to be paid by the borrower.
|
||||
Number principalOutstanding;
|
||||
// Interest still due to be paid to the Vault.
|
||||
// This is a portion of interestOutstanding
|
||||
Number interestDue;
|
||||
// Management fee still due to be paid to the broker.
|
||||
// This is a portion of interestOutstanding
|
||||
Number managementFeeDue;
|
||||
|
||||
// Interest still due to be paid by the borrower.
|
||||
Number
|
||||
interestOutstanding() const
|
||||
{
|
||||
XRPL_ASSERT_PARTS(
|
||||
interestDue + managementFeeDue == valueOutstanding - principalOutstanding,
|
||||
"xrpl::LoanState::interestOutstanding",
|
||||
"other values add up correctly");
|
||||
return interestDue + managementFeeDue;
|
||||
}
|
||||
};
|
||||
|
||||
/* Describes the initial computed properties of a loan.
|
||||
*
|
||||
* This structure contains the fundamental calculated values that define a
|
||||
* loan's payment structure and amortization schedule. These properties are
|
||||
* computed:
|
||||
* - At loan creation (LoanSet transaction)
|
||||
* - When loan terms change (e.g., after an overpayment that reduces the loan
|
||||
* balance)
|
||||
*/
|
||||
struct LoanProperties
|
||||
{
|
||||
// The unrounded amount to be paid at each regular payment period.
|
||||
// Calculated using the standard amortization formula based on principal,
|
||||
// interest rate, and number of payments.
|
||||
// The actual amount paid in the LoanPay transaction must be rounded up to
|
||||
// the precision of the asset and loan.
|
||||
Number periodicPayment;
|
||||
|
||||
// The loan's current state, with all values rounded to the loan's scale.
|
||||
LoanState loanState;
|
||||
|
||||
// The scale (decimal places) used for rounding all loan amounts.
|
||||
// This is the maximum of:
|
||||
// - The asset's native scale
|
||||
// - A minimum scale required to represent the periodic payment accurately
|
||||
// All loan state values (principal, interest, fees) are rounded to this
|
||||
// scale.
|
||||
std::int32_t loanScale;
|
||||
|
||||
// The principal portion of the first payment.
|
||||
Number firstPaymentPrincipal;
|
||||
};
|
||||
|
||||
// Some values get re-rounded to the vault scale any time they are adjusted. In
|
||||
// addition, they are prevented from ever going below zero. This helps avoid
|
||||
// accumulated rounding errors and leftover dust amounts.
|
||||
template <class NumberProxy>
|
||||
void
|
||||
adjustImpreciseNumber(NumberProxy value, Number const& adjustment, Asset const& asset, int vaultScale)
|
||||
{
|
||||
value = roundToAsset(asset, value + adjustment, vaultScale);
|
||||
|
||||
if (*value < beast::zero)
|
||||
value = 0;
|
||||
}
|
||||
|
||||
inline int
|
||||
getAssetsTotalScale(SLE::const_ref vaultSle)
|
||||
{
|
||||
if (!vaultSle)
|
||||
return Number::minExponent - 1; // LCOV_EXCL_LINE
|
||||
return STAmount{vaultSle->at(sfAsset), vaultSle->at(sfAssetsTotal)}.exponent();
|
||||
}
|
||||
|
||||
TER
|
||||
checkLoanGuards(
|
||||
Asset const& vaultAsset,
|
||||
Number const& principalRequested,
|
||||
bool expectInterest,
|
||||
std::uint32_t paymentTotal,
|
||||
LoanProperties const& properties,
|
||||
beast::Journal j);
|
||||
|
||||
LoanState
|
||||
computeTheoreticalLoanState(
|
||||
Number const& periodicPayment,
|
||||
Number const& periodicRate,
|
||||
std::uint32_t const paymentRemaining,
|
||||
TenthBips32 const managementFeeRate);
|
||||
|
||||
// Constructs a valid LoanState object from arbitrary inputs
|
||||
LoanState
|
||||
constructLoanState(
|
||||
Number const& totalValueOutstanding,
|
||||
Number const& principalOutstanding,
|
||||
Number const& managementFeeOutstanding);
|
||||
|
||||
// Constructs a valid LoanState object from a Loan object, which always has
|
||||
// rounded values
|
||||
LoanState
|
||||
constructRoundedLoanState(SLE::const_ref loan);
|
||||
|
||||
Number
|
||||
computeManagementFee(Asset const& asset, Number const& interest, TenthBips32 managementFeeRate, std::int32_t scale);
|
||||
|
||||
Number
|
||||
computeFullPaymentInterest(
|
||||
Number const& theoreticalPrincipalOutstanding,
|
||||
Number const& periodicRate,
|
||||
NetClock::time_point parentCloseTime,
|
||||
std::uint32_t paymentInterval,
|
||||
std::uint32_t prevPaymentDate,
|
||||
std::uint32_t startDate,
|
||||
TenthBips32 closeInterestRate);
|
||||
|
||||
namespace detail {
|
||||
// These classes and functions should only be accessed by LendingHelper
|
||||
// functions and unit tests
|
||||
|
||||
enum class PaymentSpecialCase { none, final, extra };
|
||||
|
||||
/* Represents a single loan payment component parts.
|
||||
|
||||
* This structure captures the "delta" (change) values that will be applied to
|
||||
* the tracked fields in the Loan ledger object when a payment is processed.
|
||||
*
|
||||
* These are called "deltas" because they represent the amount by which each
|
||||
* corresponding field in the Loan object will be reduced.
|
||||
* They are "tracked" as they change tracked loan values.
|
||||
*/
|
||||
struct PaymentComponents
|
||||
{
|
||||
// The change in total value outstanding for this payment.
|
||||
// This amount will be subtracted from sfTotalValueOutstanding in the Loan
|
||||
// object. Equal to the sum of trackedPrincipalDelta,
|
||||
// trackedInterestPart(), and trackedManagementFeeDelta.
|
||||
Number trackedValueDelta;
|
||||
|
||||
// The change in principal outstanding for this payment.
|
||||
// This amount will be subtracted from sfPrincipalOutstanding in the Loan
|
||||
// object, representing the portion of the payment that reduces the
|
||||
// original loan amount.
|
||||
Number trackedPrincipalDelta;
|
||||
|
||||
// The change in management fee outstanding for this payment.
|
||||
// This amount will be subtracted from sfManagementFeeOutstanding in the
|
||||
// Loan object. This represents only the tracked management fees from the
|
||||
// amortization schedule and does not include additional untracked fees
|
||||
// (such as late payment fees) that go directly to the broker.
|
||||
Number trackedManagementFeeDelta;
|
||||
|
||||
// Indicates if this payment has special handling requirements.
|
||||
// - none: Regular scheduled payment
|
||||
// - final: The last payment that closes out the loan
|
||||
// - extra: An additional payment beyond the regular schedule (overpayment)
|
||||
PaymentSpecialCase specialCase = PaymentSpecialCase::none;
|
||||
|
||||
// Calculates the tracked interest portion of this payment.
|
||||
// This is derived from the other components as:
|
||||
// trackedValueDelta - trackedPrincipalDelta - trackedManagementFeeDelta
|
||||
//
|
||||
// @return The amount of tracked interest included in this payment that
|
||||
// will be paid to the vault.
|
||||
Number
|
||||
trackedInterestPart() const;
|
||||
};
|
||||
|
||||
/* Extends PaymentComponents with untracked payment amounts.
|
||||
*
|
||||
* This structure adds untracked fees and interest to the base
|
||||
* PaymentComponents, representing amounts that don't affect the Loan object's
|
||||
* tracked state but are still part of the total payment due from the borrower.
|
||||
*
|
||||
* Untracked amounts include:
|
||||
* - Late payment fees that go directly to the Broker
|
||||
* - Late payment penalty interest that goes directly to the Vault
|
||||
* - Service fees
|
||||
*
|
||||
* The key distinction is that tracked amounts reduce the Loan object's state
|
||||
* (sfTotalValueOutstanding, sfPrincipalOutstanding,
|
||||
* sfManagementFeeOutstanding), while untracked amounts are paid directly to the
|
||||
* recipient without affecting the loan's amortization schedule.
|
||||
*/
|
||||
struct ExtendedPaymentComponents : public PaymentComponents
|
||||
{
|
||||
// Additional management fees that go directly to the Broker.
|
||||
// This includes fees not part of the standard amortization schedule
|
||||
// (e.g., late fees, service fees, origination fees).
|
||||
// This value may be negative, though the final value returned in
|
||||
// LoanPaymentParts.feePaid will never be negative.
|
||||
Number untrackedManagementFee;
|
||||
|
||||
// Additional interest that goes directly to the Vault.
|
||||
// This includes interest not part of the standard amortization schedule
|
||||
// (e.g., late payment penalty interest).
|
||||
// This value may be negative, though the final value returned in
|
||||
// LoanPaymentParts.interestPaid will never be negative.
|
||||
Number untrackedInterest;
|
||||
|
||||
// The complete amount due from the borrower for this payment.
|
||||
// Calculated as: trackedValueDelta + untrackedInterest +
|
||||
// untrackedManagementFee
|
||||
//
|
||||
// This value is used to validate that the payment amount provided by the
|
||||
// borrower is sufficient to cover all components of the payment.
|
||||
Number totalDue;
|
||||
|
||||
ExtendedPaymentComponents(PaymentComponents const& p, Number fee, Number interest = numZero)
|
||||
: PaymentComponents(p)
|
||||
, untrackedManagementFee(fee)
|
||||
, untrackedInterest(interest)
|
||||
, totalDue(trackedValueDelta + untrackedInterest + untrackedManagementFee)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/* Represents the differences between two loan states.
|
||||
*
|
||||
* This structure is used to capture the change in each component of a loan's
|
||||
* state, typically when computing the difference between two LoanState objects
|
||||
* (e.g., before and after a payment). It is a convenient way to capture changes
|
||||
* in each component. How that difference is used depends on the context.
|
||||
*/
|
||||
struct LoanStateDeltas
|
||||
{
|
||||
// The difference in principal outstanding between two loan states.
|
||||
Number principal;
|
||||
|
||||
// The difference in interest due between two loan states.
|
||||
Number interest;
|
||||
|
||||
// The difference in management fee outstanding between two loan states.
|
||||
Number managementFee;
|
||||
|
||||
/* Calculates the total change across all components.
|
||||
* @return The sum of principal, interest, and management fee deltas.
|
||||
*/
|
||||
Number
|
||||
total() const
|
||||
{
|
||||
return principal + interest + managementFee;
|
||||
}
|
||||
|
||||
// Ensures all delta values are non-negative.
|
||||
void
|
||||
nonNegative();
|
||||
};
|
||||
|
||||
Expected<std::pair<LoanPaymentParts, LoanProperties>, TER>
|
||||
tryOverpayment(
|
||||
Asset const& asset,
|
||||
std::int32_t loanScale,
|
||||
ExtendedPaymentComponents const& overpaymentComponents,
|
||||
LoanState const& roundedLoanState,
|
||||
Number const& periodicPayment,
|
||||
Number const& periodicRate,
|
||||
std::uint32_t paymentRemaining,
|
||||
TenthBips16 const managementFeeRate,
|
||||
beast::Journal j);
|
||||
|
||||
Number
|
||||
computeRaisedRate(Number const& periodicRate, std::uint32_t paymentsRemaining);
|
||||
|
||||
Number
|
||||
computePaymentFactor(Number const& periodicRate, std::uint32_t paymentsRemaining);
|
||||
|
||||
std::pair<Number, Number>
|
||||
computeInterestAndFeeParts(
|
||||
Asset const& asset,
|
||||
Number const& interest,
|
||||
TenthBips16 managementFeeRate,
|
||||
std::int32_t loanScale);
|
||||
|
||||
Number
|
||||
loanPeriodicPayment(Number const& principalOutstanding, Number const& periodicRate, std::uint32_t paymentsRemaining);
|
||||
|
||||
Number
|
||||
loanPrincipalFromPeriodicPayment(
|
||||
Number const& periodicPayment,
|
||||
Number const& periodicRate,
|
||||
std::uint32_t paymentsRemaining);
|
||||
|
||||
Number
|
||||
loanLatePaymentInterest(
|
||||
Number const& principalOutstanding,
|
||||
TenthBips32 lateInterestRate,
|
||||
NetClock::time_point parentCloseTime,
|
||||
std::uint32_t nextPaymentDueDate);
|
||||
|
||||
Number
|
||||
loanAccruedInterest(
|
||||
Number const& principalOutstanding,
|
||||
Number const& periodicRate,
|
||||
NetClock::time_point parentCloseTime,
|
||||
std::uint32_t startDate,
|
||||
std::uint32_t prevPaymentDate,
|
||||
std::uint32_t paymentInterval);
|
||||
|
||||
ExtendedPaymentComponents
|
||||
computeOverpaymentComponents(
|
||||
Asset const& asset,
|
||||
int32_t const loanScale,
|
||||
Number const& overpayment,
|
||||
TenthBips32 const overpaymentInterestRate,
|
||||
TenthBips32 const overpaymentFeeRate,
|
||||
TenthBips16 const managementFeeRate);
|
||||
|
||||
PaymentComponents
|
||||
computePaymentComponents(
|
||||
Asset const& asset,
|
||||
std::int32_t scale,
|
||||
Number const& totalValueOutstanding,
|
||||
Number const& principalOutstanding,
|
||||
Number const& managementFeeOutstanding,
|
||||
Number const& periodicPayment,
|
||||
Number const& periodicRate,
|
||||
std::uint32_t paymentRemaining,
|
||||
TenthBips16 managementFeeRate);
|
||||
|
||||
} // namespace detail
|
||||
|
||||
detail::LoanStateDeltas
|
||||
operator-(LoanState const& lhs, LoanState const& rhs);
|
||||
|
||||
LoanState
|
||||
operator-(LoanState const& lhs, detail::LoanStateDeltas const& rhs);
|
||||
|
||||
LoanState
|
||||
operator+(LoanState const& lhs, detail::LoanStateDeltas const& rhs);
|
||||
|
||||
LoanProperties
|
||||
computeLoanProperties(
|
||||
Asset const& asset,
|
||||
Number const& principalOutstanding,
|
||||
TenthBips32 interestRate,
|
||||
std::uint32_t paymentInterval,
|
||||
std::uint32_t paymentsRemaining,
|
||||
TenthBips32 managementFeeRate,
|
||||
std::int32_t minimumScale);
|
||||
|
||||
LoanProperties
|
||||
computeLoanProperties(
|
||||
Asset const& asset,
|
||||
Number const& principalOutstanding,
|
||||
Number const& periodicRate,
|
||||
std::uint32_t paymentsRemaining,
|
||||
TenthBips32 managementFeeRate,
|
||||
std::int32_t minimumScale);
|
||||
|
||||
bool
|
||||
isRounded(Asset const& asset, Number const& value, std::int32_t scale);
|
||||
|
||||
// Indicates what type of payment is being made.
|
||||
// regular, late, and full are mutually exclusive.
|
||||
// overpayment is an "add on" to a regular payment, and follows that path with
|
||||
// potential extra work at the end.
|
||||
enum class LoanPaymentType { regular = 0, late, full, overpayment };
|
||||
|
||||
Expected<LoanPaymentParts, TER>
|
||||
loanMakePayment(
|
||||
Asset const& asset,
|
||||
ApplyView& view,
|
||||
SLE::ref loan,
|
||||
SLE::const_ref brokerSle,
|
||||
STAmount const& amount,
|
||||
LoanPaymentType const paymentType,
|
||||
beast::Journal j);
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class LoanBrokerCoverClawback : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit LoanBrokerCoverClawback(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
} // namespace xrpl
|
||||
31
include/xrpl/tx/transactors/Lending/LoanBrokerCoverDeposit.h
Normal file
31
include/xrpl/tx/transactors/Lending/LoanBrokerCoverDeposit.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class LoanBrokerCoverDeposit : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit LoanBrokerCoverDeposit(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class LoanBrokerCoverWithdraw : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit LoanBrokerCoverWithdraw(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
} // namespace xrpl
|
||||
31
include/xrpl/tx/transactors/Lending/LoanBrokerDelete.h
Normal file
31
include/xrpl/tx/transactors/Lending/LoanBrokerDelete.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class LoanBrokerDelete : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit LoanBrokerDelete(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
} // namespace xrpl
|
||||
34
include/xrpl/tx/transactors/Lending/LoanBrokerSet.h
Normal file
34
include/xrpl/tx/transactors/Lending/LoanBrokerSet.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class LoanBrokerSet : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit LoanBrokerSet(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static std::vector<OptionaledField<STNumber>> const&
|
||||
getValueFields();
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
} // namespace xrpl
|
||||
31
include/xrpl/tx/transactors/Lending/LoanDelete.h
Normal file
31
include/xrpl/tx/transactors/Lending/LoanDelete.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class LoanDelete : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit LoanDelete(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
} // namespace xrpl
|
||||
55
include/xrpl/tx/transactors/Lending/LoanManage.h
Normal file
55
include/xrpl/tx/transactors/Lending/LoanManage.h
Normal file
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class LoanManage : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit LoanManage(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
/** Helper function that might be needed by other transactors
|
||||
*/
|
||||
static TER
|
||||
defaultLoan(
|
||||
ApplyView& view,
|
||||
SLE::ref loanSle,
|
||||
SLE::ref brokerSle,
|
||||
SLE::ref vaultSle,
|
||||
Asset const& vaultAsset,
|
||||
beast::Journal j);
|
||||
|
||||
/** Helper function that might be needed by other transactors
|
||||
*/
|
||||
static TER
|
||||
impairLoan(ApplyView& view, SLE::ref loanSle, SLE::ref vaultSle, Asset const& vaultAsset, beast::Journal j);
|
||||
|
||||
/** Helper function that might be needed by other transactors
|
||||
*/
|
||||
[[nodiscard]] static TER
|
||||
unimpairLoan(ApplyView& view, SLE::ref loanSle, SLE::ref vaultSle, Asset const& vaultAsset, beast::Journal j);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
} // namespace xrpl
|
||||
37
include/xrpl/tx/transactors/Lending/LoanPay.h
Normal file
37
include/xrpl/tx/transactors/Lending/LoanPay.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class LoanPay : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit LoanPay(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
checkExtraFeatures(PreflightContext const& ctx);
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
preflight(PreflightContext const& ctx);
|
||||
|
||||
static XRPAmount
|
||||
calculateBaseFee(ReadView const& view, STTx const& tx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
} // namespace xrpl
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user