Files
rippled/include/xrpl/protocol/SeqProxy.h
2026-07-13 10:40:40 +00:00

152 lines
4.0 KiB
C++

#pragma once
#include <cstdint>
#include <ostream>
namespace xrpl {
/**
* A type that represents either a sequence value or a ticket value.
*
* We use the value() of a SeqProxy in places where a sequence was used
* before. An example of this is the sequence of an Offer stored in the
* ledger. We do the same thing with the in-ledger identifier of a
* Check, Payment Channel, and Escrow.
*
* Why is this safe? If we use the SeqProxy::value(), how do we know that
* each ledger entry will be unique?
*
* There are two components that make this safe:
*
* 1. A "TicketCreate" transaction carefully avoids creating a ticket
* that corresponds with an already used Sequence or Ticket value.
* The transactor does this by referring to the account root's
* sequence number. Creating the ticket advances the account root's
* sequence number so the same ticket (or sequence) value cannot be
* used again.
*
* 2. When a "TicketCreate" transaction creates a batch of tickets it advances
* the account root sequence to one past the largest created ticket.
*
* Therefore all tickets in a batch other than the first may never have
* the same value as a sequence on that same account. And since a ticket
* may only be used once there will never be any duplicates within this
* account.
*/
class SeqProxy
{
public:
enum class Type : std::uint8_t { Seq = 0, Ticket };
private:
std::uint32_t value_;
Type type_;
public:
constexpr explicit SeqProxy(Type t, std::uint32_t v) : value_{v}, type_{t}
{
}
SeqProxy(SeqProxy const& other) = default;
SeqProxy&
operator=(SeqProxy const& other) = default;
/**
* Factory function to return a sequence-based SeqProxy
*/
static constexpr SeqProxy
sequence(std::uint32_t v)
{
return SeqProxy{Type::Seq, v};
}
[[nodiscard]] constexpr std::uint32_t
value() const
{
return value_;
}
[[nodiscard]] constexpr bool
isSeq() const
{
return type_ == Type::Seq;
}
[[nodiscard]] constexpr bool
isTicket() const
{
return type_ == Type::Ticket;
}
// Occasionally it is convenient to be able to increase the value_
// of a SeqProxy. But it's unusual. So, rather than putting in an
// addition operator, you must invoke the method by name. That makes
// if more difficult to invoke accidentally.
SeqProxy&
advanceBy(std::uint32_t amount)
{
value_ += amount;
return *this;
}
// Comparison
//
// The comparison is designed specifically so _all_ Sequence
// representations sort in front of Ticket representations. This
// is true even if the Ticket value() is less that the Sequence
// value().
//
// This somewhat surprising sort order has benefits for transaction
// processing. It guarantees that transactions creating Tickets are
// sorted in from of transactions that consume Tickets.
friend constexpr bool
operator==(SeqProxy lhs, SeqProxy rhs)
{
if (lhs.type_ != rhs.type_)
return false;
return (lhs.value() == rhs.value());
}
friend constexpr bool
operator!=(SeqProxy lhs, SeqProxy rhs)
{
return !(lhs == rhs);
}
friend constexpr bool
operator<(SeqProxy lhs, SeqProxy rhs)
{
if (lhs.type_ != rhs.type_)
return lhs.type_ < rhs.type_;
return lhs.value() < rhs.value();
}
friend constexpr bool
operator>(SeqProxy lhs, SeqProxy rhs)
{
return rhs < lhs;
}
friend constexpr bool
operator>=(SeqProxy lhs, SeqProxy rhs)
{
return !(lhs < rhs);
}
friend constexpr bool
operator<=(SeqProxy lhs, SeqProxy rhs)
{
return !(lhs > rhs);
}
friend std::ostream&
operator<<(std::ostream& os, SeqProxy seqProx)
{
os << (seqProx.isSeq() ? "sequence " : "ticket ");
os << seqProx.value();
return os;
}
};
} // namespace xrpl