Skip to main content

BuyerPoolService

Struct BuyerPoolService 

Source
pub struct BuyerPoolService<P: Provider + Clone + 'static> { /* private fields */ }
Expand description

Buyer-side PaymentPool service. Generic over the alloy Provider (a wallet-filled provider is required for the approve / openPool / topUp / closePool / reclaim write paths). Cheap to construct; owns its background reclaim task.

Implementations§

Source§

impl<P: Provider + Clone + 'static> BuyerPoolService<P>

Source

pub async fn bootstrap( provider: P, deployment: Deployment, owner: Address, store: Arc<dyn BuyerPoolStore>, signer: Arc<PrivateKeySigner>, working_deposit: U256, ensure_max_approval: bool, metrics: Arc<Metrics>, ) -> Result<Self>

Bootstrap the service: self-check the contract, read the immutable USDC token, issue the one-time USDC approval if requested, and spawn the reclaim sweep.

deployment is the chain and PaymentPool the service buys on; the voucher EIP-712 domain derives from it. working_deposit is the deposit a fresh open escrows and the target a reused pool’s low-water refill tops it up toward.

§Errors

Returns an error if the usdc() self-check call fails (a bad payment_pool_address or an unreachable RPC), if the persisted buyer pools cannot be loaded, or if the one-time approval transaction fails. The runtime treats the error as non-fatal: the node serves, and only its paid node-to-node cache-miss leg is off.

Source

pub async fn open_or_reuse_pool( &self, provider_addr: Address, budget: Duration, ) -> Result<PoolContext>

Return a PoolContext for paying provider_addr, pinned to that provider and carrying the node’s self-issued capability: reuse the single pool the node owns, or lazily open one.

A freshly-opened pool escrows the service’s working_deposit; on reuse that deposit is ignored (a low-water refill tops a live pool up instead).

§Bounding (#1143)

budget bounds how long the CALLER waits — not how long the open runs. When it expires this returns PoolOpenPending and the open keeps going in a detached task: a broadcast openPool tx that is dropped mid-flight would escrow a deposit against a pool nobody tracks, so the open is never cancelled, only stopped-waiting-on. The next miss reuses it once it lands.

§Errors

PoolOpenPending when budget expires with the open still running. Otherwise: store errors, any failure of the openPool transaction (submit, revert, or receipt — carrying a decdn_incentive::PoolOpenFailureReason the caller can downcast_ref), or a capability-signing fault.

Source

pub fn record_progress( &self, provider_addr: Address, pool_id: PoolId, write: ProgressWrite, ) -> Result<()>

Persist one lane’s voucher progress after a delivery exchange so a later reuse (or a restart) resumes the (signer, provider) lane at the right cumulative bytes / amount. The buyer prices cumulative = bytes × rate from its own BLAKE3-verified bytes, so there is no nonce to track. ProgressWrite says whether the write rebases the record down first.

§Errors

Errors on a store write fault. A stale write (the owner’s pool was replaced by a newer open) or a superseded watermark (a concurrent settle wrote a higher value first) is metered and returns Ok(()) — neither loses a voucher and neither must fail the already-paid pull.

Source

pub async fn recover_pool(&self, seen_deposit: U256) -> Result<Recovery>

Run this node’s funding recovery step (ADR 003 § Funding recovery): top the current pool up toward the working deposit, or, when the pool is Closing or Closed and its topUp reverts PoolNotOpen, open a new pool at the working deposit and make it the current pool in the store. The closed pool is not reclaimed here; the reclaim sweep recovers it once its dispute window ends.

Concurrent fills share one pool, so one step funds them all. seen_deposit is the deposit the calling fill saw. A row that already holds more is a sibling’s step that landed: the call returns the row’s deposit and escrows nothing. A recovery top-up still in flight is a sibling’s step too: the call waits for it and returns its deposit. When that top-up reverts PoolNotOpen, the call joins the replacement open.

The step sizes itself from the pool row: the deposit less the spend its lanes recorded. A fill runs the step only once its own pulls ended, and each pull records its progress as it ends, so the row holds that spend. A pool that already holds the working deposit has nothing to add. A topUp gas estimate (no transaction) then tells a closing or closed pool, which a new pool replaces. An open pool is the upstreams’ stale view of a deposit they have not seen yet: the step settles and returns the row’s deposit, so the fill’s settle window asks them again.

§Errors

No pool is tracked, the topUp or replacement openPool fails, a joined top-up fails for any reason but PoolNotOpen, or a mined transaction cannot be recorded (escrowed and untracked).

Source

pub fn pool_deposit(&self) -> Option<U256>

The deposit the store’s pool row records, or None with no pool tracked: the deposit a fill sees.

Source

pub async fn top_up_pool_by(&self, additional: U256) -> Result<TopUpLanded>

Fund additional of new headroom in the node’s pool and return the pool’s NEW total deposit with the amount this call added (#1530): the top-up behind Self::recover_pool.

A topUp already in flight is JOINED, not duplicated. This call counts only the part of it that no other pull relies on: all of a refill’s amount, none of another recovery top-up’s. When that part is less than additional, this method funds the remainder with a further topUp (see top_up_at_least). added is below additional only when the funding calls run out, or when the chain credits less than a topUp asked for. The caller compares the two.

Routes through the detached, join-or-spawn funding task (never an inline .await): topUp waits on an unbounded get_receipt, and this runs inside the miss-pull future the foreground serve path DROPS on its deadline — an inline await cancelled mid-receipt would leave the deposit escrowed and untracked. On the spawned task, a dropped caller loses only the answer.

§Errors

Errors if no pool is tracked, if our own allowance/topUp fails, if a topUp mined but the local row could not be credited (terminal — the deposit is escrowed-and-untracked; a retry would escrow again), or if every funding call joined a topUp that failed.

Source

pub async fn sweep_reclaimable_once(&self)

Run one reclaim-sweep pass synchronously: complete the grace-window reclaim of the node’s pool if it has been closed and its dispute window has elapsed. Best-effort — errors are logged, never propagated. Exposed so the runtime (or a test) can trigger an immediate pass.

Trait Implementations§

Source§

impl<P: Provider + Clone + 'static> Debug for BuyerPoolService<P>

Source§

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

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

impl<P: Provider + Clone + 'static> PoolOpener for BuyerPoolService<P>

Source§

fn open_or_reuse_pool<'life0, 'async_trait>( &'life0 self, provider_addr: Address, budget: Duration, ) -> Pin<Box<dyn Future<Output = Result<PoolContext>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Open or reuse the node’s buyer pool and return a PoolContext pinned to provider_addr (with the self-issued capability attached). See BuyerPoolService::open_or_reuse_pool for the full contract.
Source§

fn record_progress( &self, provider_addr: Address, pool_id: PoolId, write: ProgressWrite, ) -> Result<()>

Persist one lane’s voucher progress on the (signer, provider) lane of pool_id. See BuyerPoolService::record_progress. Read more
Source§

fn recover_pool<'life0, 'async_trait>( &'life0 self, seen_deposit: U256, ) -> Pin<Box<dyn Future<Output = Result<Recovery>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Run the node’s funding recovery step on its buyer pool, for a fill that saw seen_deposit. See BuyerPoolService::recover_pool. Read more
Source§

fn pool_deposit(&self) -> Option<U256>

The deposit the node’s pool row records now, or None with no pool tracked. See BuyerPoolService::pool_deposit.

Auto Trait Implementations§

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.

Source§

impl<T> FutureExt for T

Source§

fn with_context(self, otel_cx: Context) -> WithContext<Self> ⓘ

Attaches the provided Context to this type, returning a WithContext wrapper. Read more
Source§

fn with_current_context(self) -> WithContext<Self> ⓘ

Attaches the current Context to this type, returning a WithContext wrapper. Read more
Source§

impl<T> Instrument for T

Source§

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

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

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
Source§

impl<T> IntoRequest<T> for T

Source§

fn into_request(self) -> Request<T>

Wrap the input message T in a tonic::Request
Source§

impl<Unshared, Shared> IntoShared<Shared> for Unshared
where Shared: FromUnshared<Unshared>,

Source§

fn into_shared(self) -> Shared

Creates a shared type from an unshared type.
Source§

impl<L> LayerExt<L> for L

Source§

fn named_layer<S>(&self, service: S) -> Layered<<L as Layer<S>>::Service, S>
where L: Layer<S>,

Applies the layer to a service and wraps it in Layered.
Source§

impl<T> MaybeSend for T
where T: Send,

Source§

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

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + 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
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + 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
Source§

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

Source§

type Error = !

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

fn try_from(value: U) -> Result<T, !>

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.
Source§

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

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

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
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

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