MPP.Methods.Tempo.SponsorBudget (mpp v0.12.0)

Copy Markdown View Source

Atomic aggregate in-flight accounting for Tempo fee sponsorship.

Reservations are scoped by chain and sponsor identity and move through :prepared, :broadcasting, and :pending phases. The store pins one limits set while reservations are live and rejects divergent configurations.

The guarantee applies to one physical atomic store. MPP.Tempo.ConCacheStore is suitable for a single BEAM node; every node and endpoint sponsoring the same wallet must select the same shared backend for a cluster-wide bound.

Reservation expiry is deliberately conservative. A reservation remains live through its transaction valid_before plus a named clock-skew margin, and the state-derived store TTL covers that same boundary.

Summary

Types

Fail-closed budget error.

Opaque ownership handle returned by reserve/3.

Pinned aggregate sponsor ceilings.

Reservation request accepted by reserve/3.

Functions

Release an owned reservation after a pre-broadcast failure or terminal receipt.

Atomically reserve worst-case sponsor capacity before signing.

Purely remove expired reservations at now.

Move an owned reservation to :broadcasting or {:pending, tx_hash}.

Types

error_reason()

@type error_reason() ::
  {:capacity_exhausted, pos_integer()}
  | :incompatible_state
  | :invalid_request
  | :limits_mismatch
  | :ownership_lost
  | :store_unavailable

Fail-closed budget error.

handle()

@type handle() :: %{key: String.t(), reservation_id: String.t()}

Opaque ownership handle returned by reserve/3.

limits()

@type limits() :: %{
  max_in_flight_total_fee: pos_integer(),
  max_in_flight_reservations: pos_integer()
}

Pinned aggregate sponsor ceilings.

reservation_params()

@type reservation_params() :: %{
  chain_id: non_neg_integer(),
  sponsor_id: String.t(),
  fee: pos_integer(),
  valid_before: pos_integer(),
  limits: limits()
}

Reservation request accepted by reserve/3.

Functions

release(store, handle, opts \\ [])

@spec release(MPP.Tempo.Store.store_ref(), handle(), keyword()) ::
  :ok | {:error, error_reason()}

Release an owned reservation after a pre-broadcast failure or terminal receipt.

reserve(store, params, opts \\ [])

@spec reserve(MPP.Tempo.Store.store_ref(), reservation_params(), keyword()) ::
  {:ok, handle()} | {:error, error_reason()}

Atomically reserve worst-case sponsor capacity before signing.

Pass :reconcile with a one-argument receipt fetcher to opt into bounded pending-receipt reconciliation when the budget is at capacity. :now is available for deterministic tests.

sweep(reservations, now)

@spec sweep(map(), integer()) :: map()

Purely remove expired reservations at now.

Chain-valid reservations remain through valid_before + clock-skew margin; the exact conservative boundary is retained and removal starts after it.

transition(store, handle, target, opts \\ [])

@spec transition(
  MPP.Tempo.Store.store_ref(),
  handle(),
  :broadcasting | {:pending, String.t()},
  keyword()
) :: :ok | {:error, error_reason()}

Move an owned reservation to :broadcasting or {:pending, tx_hash}.

Only the random handle returned by reserve/3 can mutate its reservation.