pub struct ResolvingPoolView<P: Provider + Clone> { /* private fields */ }Expand description
A PoolView that confirms a pool on-chain
before a serve is admitted.
The serve gates read {owner, remaining} from the event-fed PoolProjection
the settlement watcher folds, so the common case costs no eth_call. A pool
the projection has not observed is the hard case: a pool opened before this
node’s ColdStart::Head anchor never appears in the forward-only PoolOpened
scan, so its owner is absent from the projection even though the pool is live.
Rather than fail open on that gap, status does ONE getPool at
admission — a read the admission path tolerates (it may block) — folds a
servable pool into the projection, and refuses an absent, closed, or faulted
pool. Every admit-path chain read (getPool and the signer’s
getAuthorization) runs under chain_events::timed’s default bound, so a
hung read times out and takes the fault path instead of stalling admission.
The client re-sends its capability on its next request (the documented
lane recovery path), and by then the folded owner registers the lane.
The mid-stream re-check calls cached_status, which reads the
projection ONLY and never blocks on a getPool — a chain read at a voucher
boundary would stall delivery.
A short-TTL negative cache suppresses a repeat getPool for a pool just
found not-servable (RESOLVE_VERDICT_TTL) or whose read faulted (the far
shorter RESOLVE_FAULT_TTL), so a client re-requesting the same dead pool
every request cannot drive one getPool per request, and one failed read does
not refuse a live pool for the full verdict window. Reads are coalesced per
pool: a request that finds a getPool in flight for its pool waits for it
and takes its answer.
The signer confirm reads getAuthorization once per (pool, signer) and
keeps a Registered answer for good (see AuthRead); only an
Unregistered answer is re-read. Those reads are coalesced per pair the same
way.
Implementations§
Source§impl<P: Provider + Clone> ResolvingPoolView<P>
impl<P: Provider + Clone> ResolvingPoolView<P>
Sourcepub fn new(
contract: PaymentPoolInstance<P>,
projection: PoolProjection,
metrics: Arc<Metrics>,
) -> Self
pub fn new( contract: PaymentPoolInstance<P>, projection: PoolProjection, metrics: Arc<Metrics>, ) -> Self
Wrap the event-fed projection with admit-path getPool and
getAuthorization fallbacks against contract.
Trait Implementations§
Source§impl<P: Provider + Clone + 'static> PoolView for ResolvingPoolView<P>
impl<P: Provider + Clone + 'static> PoolView for ResolvingPoolView<P>
Source§fn status<'life0, 'async_trait>(
&'life0 self,
pool_id: B256,
) -> Pin<Box<dyn Future<Output = Option<PoolStatus>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn status<'life0, 'async_trait>(
&'life0 self,
pool_id: B256,
) -> Pin<Box<dyn Future<Output = Option<PoolStatus>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
None if the pool cannot be confirmed. The
stream-admission read: a caller here may tolerate a blocking source, so an
implementation MAY do slow work. The bare PoolProjection reads from
memory and returns None for a pool it has not folded; the production
wrapper (crate::payment_settlement::ResolvingPoolView) confirms such a
pool with one getPool before returning, so the admit gate refuses a
pool it still cannot confirm rather than failing open.Source§fn cached_status<'life0, 'async_trait>(
&'life0 self,
pool_id: B256,
) -> Pin<Box<dyn Future<Output = Option<PoolStatus>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn cached_status<'life0, 'async_trait>(
&'life0 self,
pool_id: B256,
) -> Pin<Box<dyn Future<Output = Option<PoolStatus>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
None rather than doing any slow or
remote work. The mid-stream serve re-check calls it at each voucher
boundary, where a blocking read would stall delivery. The default delegates
to Self::status for in-memory test doubles; PoolProjection serves
both from the same in-memory map, so neither ever touches the network.Source§fn signer_spent_cached<'life0, 'async_trait>(
&'life0 self,
pool_id: B256,
signer: Address,
) -> Pin<Box<dyn Future<Output = Option<u64>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn signer_spent_cached<'life0, 'async_trait>(
&'life0 self,
pool_id: B256,
signer: Address,
) -> Pin<Box<dyn Future<Output = Option<u64>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
signer’s total on-chain spent across
every provider in pool_id, from the event-fed projection ONLY — never a
chain call. The serve loop pairs it with the cap the node already holds on
the lane to test held_cap − spent headroom at the POOL_RECHECK_INTERVAL
cadence, so a signer draining its shared cap at another node mid-stream stops
this stream before it over-delivers unredeemable bytes (ADR 003 §Pool
solvency). Read moresigner on-chain
authorization in pool_id (ADR 003 §Capability delegation, §Pool
solvency). A signer’s cap is shared across every provider, so a signer
that has drawn its full cap at other nodes is uncashable here. Its
registered cap and expiry are write-once, so they bound every voucher
whatever capability the client presents. Read moreAuto Trait Implementations§
impl<P> !Freeze for ResolvingPoolView<P>
impl<P> !RefUnwindSafe for ResolvingPoolView<P>
impl<P> !UnwindSafe for ResolvingPoolView<P>
impl<P> Send for ResolvingPoolView<P>where
PaymentPoolInstance<P>: Send,
impl<P> Sync for ResolvingPoolView<P>where
PaymentPoolInstance<P>: Sync,
impl<P> Unpin for ResolvingPoolView<P>where
PaymentPoolInstance<P>: Unpin,
impl<P> UnsafeUnpin for ResolvingPoolView<P>where
PaymentPoolInstance<P>: UnsafeUnpin,
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> FutureExt for T
impl<T> FutureExt for T
Source§fn with_context(self, otel_cx: Context) -> WithContext<Self> ⓘ
fn with_context(self, otel_cx: Context) -> WithContext<Self> ⓘ
Source§fn with_current_context(self) -> WithContext<Self> ⓘ
fn with_current_context(self) -> WithContext<Self> ⓘ
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
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 moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
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 moreSource§impl<T> IntoRequest<T> for T
impl<T> IntoRequest<T> for T
Source§fn into_request(self) -> Request<T>
fn into_request(self) -> Request<T>
T in a tonic::Request