mirror of
https://github.com/XRPLF/rippled.git
synced 2026-04-29 15:37:57 +00:00
Merge branch 'develop' into pratik/Fix_asan_lsan_flagged_issues
This commit is contained in:
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
|
||||
@@ -214,6 +214,13 @@ public:
|
||||
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&
|
||||
|
||||
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
|
||||
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
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include <xrpl/core/JobQueue.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/protocol/STValidation.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/messages.h>
|
||||
@@ -19,6 +18,8 @@ namespace xrpl {
|
||||
// Master operational handler, server sequencer, network tracker
|
||||
|
||||
class Peer;
|
||||
class STTx;
|
||||
class ReadView;
|
||||
class LedgerMaster;
|
||||
class Transaction;
|
||||
class ValidatorKeys;
|
||||
|
||||
Reference in New Issue
Block a user