Skip to main content

HoprTicketManager

Struct HoprTicketManager 

Source
pub struct HoprTicketManager<S, Q> {
    pub(crate) out_idx_tracker: OutgoingIndexCache,
    pub(crate) channel_tickets: DashMap<ChannelId, ChannelTicketQueue<ValueCachedQueue<Q>>>,
    pub(crate) store: Arc<RwLock<S>>,
}
Expand description

Keeps track of indices for outgoing tickets and (optionally) of incoming redeemable tickets.

The capabilities of the HoprTicketManager are given by the store S:

  • if the store implements OutgoingIndexStore, the outgoing index tracking functions are available.
  • if the store implements TicketQueueStore, the incoming redeemable ticket management is available.

It is possible to have both implementations in the same store object. Some store implementations may offer persistence, others may not. The HoprTicketManager takes ownership of the store object, and other processes should not attempt to change the store externally. For this reason, stores should not be cloneable.

The HOPR node type gives typical use-cases of the HoprTicketManager:

  • Entry/Exit nodes only need to provide an OutgoingIndexStore, since they are dealing with outgoing tickets only.
  • Relay nodes need to provide a store which implements both OutgoingIndexStore + TicketQueueStore, because they need to deal with both outgoing tickets and incoming redeemable tickets.

To synchronize the on-chain state with the store, it is advised to call sync_outgoing_channels (and sync_incoming_channels if applicable to the chosen store) early after the construction of the manager, to make sure outdated data is discarded early. This is typically done only once after construction and not needed to be done during the life-time of the manager.

The manager is safe to be shared via an Arc. It is typically shared between the packet processing pipelines (outgoing on Entry/Exit nodes, incoming on Relay nodes) and some higher level component that performs redeemable ticket extractions (in the case of a Relay node).

§Usage in outgoing packet pipeline

The outgoing packet pipeline usually just calls the create_multihop_ticket to create a ticket for the next hop on a multi-hop path. To create zero/last-hop tickets, the ticket manager is not needed as these tickets essentially contain bogus data and there’s no channel required.

The outgoing indices are not automatically synchronized back to the underlying store for performance reasons. The user is responsible for calling save_outgoing_indices to save the outgoing indices to the store.

This usage is typical for all kinds of nodes (Entry/Relay/Exit).

§Usage in incoming packet pipeline

The incoming packet pipeline usually just calls the insert_incoming_ticket whenever a new winning, redeemable ticket is received on an incoming channel.

This usage is typical for Relay nodes only.

§Redeemable ticket extraction

On Relay nodes, the manager maintains FIFO queues of redeemable tickets per incoming channel. There are two ways to extract tickets from the queue on a Relay:

  1. redeeming them via redeem_stream
  2. neglecting them via neglect_tickets

Both of these operations extract the tickets in the FIFO order from the queue, making sure that they are always processed in their natural order (by epoch and index).

Both ticket extraction operations are mutually exclusive and cannot be performed simultaneously.

§Locking and lock-contention

There are several methods in the HoprTicketManager object that are expected to be called in the highly performance-sensitive code, on a per-packet basis.

§Outgoing ticket creation

The create_multihop_ticket method is designed to be high-performance and to be called per each outgoing packet. It is using only atomics to track the outgoing ticket index for a channel. The synchronization to the underlying storage is done on-demand by calling save_outgoing_indices, making quick snapshots of the current state of outgoing indices. No significant contention is expected unless save_outgoing_indices is called very frequently.`

§Incoming winning ticket retrieval

The insert_incoming_ticket method is designed to be high-performance and to be called per each incoming packet after it has been forwarded to a next hop.

This operation acquires the write-part of an RW lock (per incoming channel). This may block the hot-path only if one of the following (also write) operations is performed: 1. Ticket redemption has just finished in that particular channel, and the redeemed ticket is dropped from the same incoming channel queue. 2. Ticket neglection has just finished in that particular channel, and the neglected ticket is dropped from the same incoming channel queue.

Both of these operations happen rarely, and the write lock is usually held only for a short time. In addition, incoming winning tickets are not supposed to usually happen very often. Therefore, high contention on the write lock is not expected.

§Incoming unacknowledged ticket verification

The unrealized_value method is designed to be high-performance and to be called per each incoming packet before it is forwarded to a next hop.

This operation acquires the read-part of an RW lock (per incoming channel). This may block the hot-path only if one of the following (write) operations is performed at the same moment: 1. A new incoming winning ticket is inserted into the same incoming channel queue. 2. Ticket redemption has just finished in that particular channel, and the redeemed ticket is dropped from the same incoming channel queue. 3. Ticket neglection has just finished in that particular channel, and the neglected ticket is dropped from the same incoming channel queue.

All 3 of these operations are not expected to happen very often on a single channel; therefore, high contention on the RW lock is not expected.

Fields§

§out_idx_tracker: OutgoingIndexCache§channel_tickets: DashMap<ChannelId, ChannelTicketQueue<ValueCachedQueue<Q>>>§store: Arc<RwLock<S>>

Implementations§

Source§

impl<S, Q> HoprTicketManager<S, Q>
where S: OutgoingIndexStore + Send + Sync + 'static,

Source

pub fn new(store: S) -> Result<Self, TicketManagerError>

Creates a new ticket manager instance given the desired store.

The instance is supposed to take complete ownership of the store object. The store implementations should not allow

It is advised to call HoprTicketManager::sync_from_outgoing_channels and HoprTicketManager::sync_from_incoming_channels at least once before the manager is used any further.

Source

pub(crate) fn next_outgoing_ticket_index(&self, channel: &ChannelEntry) -> u64

Gets the next usable ticket index for an outgoing ticket in the given channel and epoch.

This operation is fast and does not immediately put the index into the OutgoingIndexStore.

The returned value is always guaranteed to be greater or equal to the ticket index on the given channel.

Source

pub fn save_outgoing_indices(&self) -> Result<(), TicketManagerError>

Saves outgoing ticket indices back to the store.

The operation does nothing if there were no new tickets created on any tracked channel.

Source

pub fn sync_from_outgoing_channels( &self, outgoing_channels: &[ChannelEntry], ) -> Result<(), TicketManagerError>

Synchronizes the outgoing index counters based on the current on-chain channel state given by outgoing_channels.

Outgoing indices for channels that either are not present in outgoing_channels or not present as opened channels will be removed from the store.

Outgoing indices for existing open channels in outgoing_channels will be either:

  • added to the store with their current index and epoch (if not present in the store), or
  • updated to the maximum of the two index values (if present in the store)

It is advised to call this function early after the construction of the HoprTicketManager to ensure pruning of dangling or out-of-date values.

Source

pub fn next_multihop_ticket( &self, channel: &ChannelEntry, current_path_pos: u8, winning_prob: WinningProbability, ticket_price: HoprBalance, ) -> Result<TicketBuilder, TicketManagerError>

Creates a ticket for the next hop on a multi-hop path.

The current_path_pos indicates the position of the current hop in the multi-hop path. It is used to determine the value of the ticket: price * (current_path_pos - 1) / winning_prob. The function does not make sense for current_path_pos <= 1 and returns an error if such an argument is provided.

For last-hop tickets (current_path_pos equal to 1), a zero hop ticket should be created instead.

The function will fail for channels that are not opened or do not have enough funds to cover the ticket value. The ticket index of the returned ticket is guaranteed to be greater or equal to the ticket index on the given channel argument.

Source§

impl<S> HoprTicketManager<S, S::Queue>
where S: TicketQueueStore + Send + Sync + 'static, S::Queue: Send + Sync + 'static,

Source

pub fn sync_from_incoming_channels( &self, incoming_channels: &[ChannelEntry], ) -> Result<Vec<VerifiedTicket>, TicketManagerError>

Synchronizes the existing incoming redeemable ticket queues with the state of the current incoming_channels.

Any incoming ticket queues that correspond to a channel that is no longer open or effectively open (in incoming_channels) will be dropped and the tickets neglected.

For all opened or effectively opened incoming channels inside incoming_channels, either an existing ticket queue is opened or a new one is created (without any tickets in it).

All the neglected tickets are returned from the function to make further accounting possible, but they are no longer redeemable.

It is advised to call this function early after the construction of the HoprTicketManager to ensure pruning of dangling or out-of-date values.

Source

pub fn insert_incoming_ticket( &self, ticket: RedeemableTicket, ) -> Result<Vec<VerifiedTicket>, TicketManagerError>

Inserts a new incoming winning redeemable ticket into the ticket manager.

On success, the method returns all tickets that have been neglected in the ticket queue of this channel, in case the inserted ticket has a greater channel epoch than the next extractable ticket in the queue. This situation can happen when unredeemed tickets are left in the queue, while the corresponding channel restarts its lifecycle and a new winning ticket is received. Otherwise, the returned vector is empty.

Source

pub fn unrealized_value( &self, channel_id: &ChannelId, min_index: Option<u64>, ) -> Result<Option<HoprBalance>, TicketManagerError>

Returns the total value of unredeemed tickets in the given channel and its latest epoch.

NOTE: The function is less efficient when the min_index is specified, as a full scan of the queue is required to calculate the unrealized value.

Trait Implementations§

Source§

impl<S: Debug, Q: Debug> Debug for HoprTicketManager<S, Q>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<S> TicketManagement for HoprTicketManager<S, S::Queue>
where S: TicketQueueStore + Send + Sync + 'static, S::Queue: Send + Sync + 'static,

Source§

fn redeem_stream<C: ChainWriteTicketOperations + Send + Sync + 'static>( &self, chain: C, channel_id: ChannelId, min_amount: Option<HoprBalance>, ) -> Result<impl Stream<Item = Result<RedemptionResult, Self::Error>> + Send, Self::Error>

Creates a stream that redeems tickets in-order one by one in the given channel, using the given [ChainWriteTicketOperations] on-chain client implementation.

If min_redeem_value is given, all the tickets that are lower than the given value are neglected in the process.

If there’s already an existing redeem stream for the channel, an error is returned without creating a new stream.

The stream terminates when there are no more tickets to process in the queue, or an error is encountered.

Source§

fn neglect_tickets( &self, channel_id: &ChannelId, up_to_index: Option<u64>, ) -> Result<Vec<VerifiedTicket>, TicketManagerError>

Removes all the tickets in the given [ChannelId], optionally only up to the given ticket index (inclusive).

If the up_to_index is given and lower than the lowest index of an unredeemed ticket in the queue, the function does nothing.

If there’s ticket redemption ongoing in the same channel and the neglection intersects with the redeemed range, the redemption will be cut short, with remaining unredeemed tickets neglected.

Source§

fn ticket_stats( &self, channel: Option<&ChannelId>, ) -> Result<ChannelStats, TicketManagerError>

Computes statistics for the given channel or for all channels if None is given.

If the given channel does not exist, it returns zero statistics instead of an error.

Apart from unredeemed_value, the statistics are not persistent.

Source§

type Error = TicketManagerError

Auto Trait Implementations§

§

impl<S, Q> Freeze for HoprTicketManager<S, Q>

§

impl<S, Q> !RefUnwindSafe for HoprTicketManager<S, Q>

§

impl<S, Q> Send for HoprTicketManager<S, Q>
where S: Send + Sync, Q: Send + Sync,

§

impl<S, Q> Sync for HoprTicketManager<S, Q>
where S: Send + Sync, Q: Send + Sync,

§

impl<S, Q> Unpin for HoprTicketManager<S, Q>

§

impl<S, Q> !UnwindSafe for HoprTicketManager<S, Q>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
§

impl<T> PolicyExt for T
where T: ?Sized,

§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] only if self and other return Action::Follow. Read more
§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
§

impl<T> TicketManagementExt for T
where T: TicketManagement + ?Sized,

§

fn redeem_in_channels<'life0, 'async_trait, C>( &'life0 self, client: C, selector: Option<ChannelSelector>, min_amount: Option<Balance<WxHOPR>>, min_grace_period: Option<Duration>, ) -> Pin<Box<dyn Future<Output = Result<impl Stream<Item = Result<RedemptionResult, Self::Error>> + Send, <C as ChainReadChannelOperations>::Error>> + Send + 'async_trait>>
where 'life0: 'async_trait, C: ChainReadChannelOperations + ChainWriteTicketOperations + Clone + Send + Sync + 'static + 'async_trait, Self: Sync + 'async_trait,

Performs redemptions in multiple channels. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more