worgl_client/purse.rs
1//! A holder that talks to its mints and saves itself.
2
3use std::collections::HashMap;
4use std::path::PathBuf;
5use std::sync::{Mutex, MutexGuard};
6use std::time::Duration;
7
8use tokio::time::{Instant, sleep, sleep_until};
9use worgl::{KeySet, Note, PublicKey, TransferRequest};
10use worgl_sdk::{Balance, Holder, Payment, PaymentRequest, ScheduledTransfer, Spacing};
11
12use crate::client::Client;
13use crate::error::Error;
14use crate::store::{FileStore, Locked, Store};
15
16/// Longest wait honored before submitting a transfer, in seconds: a year.
17/// Later times are submitted at this delay.
18pub const MAX_DELAY_SECS: u64 = 365 * 24 * 60 * 60;
19
20/// Longest a mint's `Retry-After` makes a purse wait before a retry.
21const MAX_RETRY_AFTER: Duration = Duration::from_secs(30);
22
23/// A [`Holder`] connected to a server and a [`Store`]: it reads mint info
24/// itself, submits transfers at their scheduled times, retries what failed
25/// on the way, and saves after every change.
26///
27/// Nothing that depends on a change leaves the purse before the change is
28/// saved: a payment request's keys are saved before the request is
29/// returned, and the spent notes and change keys of a payment before the
30/// payment is. If a save fails, the change is undone and the call fails.
31///
32/// Methods take `&self`, so one purse can be shared between tasks; a
33/// refresh waiting for its schedule does not block payments. The holder is
34/// locked only while it changes, never across a request.
35///
36/// Several processes can share one store too. Before each change the purse
37/// takes the store's [lock](Store::lock) and reads the holder saved there,
38/// makes the change to that, and saves before letting go, so a note
39/// another process took in meanwhile is kept, not saved over. Reads such as
40/// [`holder`](Self::holder) show the holder as this purse last read or
41/// saved it.
42#[derive(Debug)]
43pub struct Purse<S: Store = FileStore> {
44 client: Client,
45 holder: Mutex<Holder>,
46 store: S,
47 retry: Retry,
48}
49
50/// How a purse resends a transfer that failed on the way: a lost
51/// connection, a timeout, or a mint that was paused, busy or could not
52/// store it. Resending is safe, since the mint answers an identical
53/// transfer with the identical notes, before or after a rotation, for as
54/// long as it remembers the transfer (see [`Purse::settle`]).
55///
56/// When the mint answers `busy`, the next wait is as long as its
57/// `Retry-After` asks, up to 30 seconds, if that is longer; the waits after
58/// it double from where they were. A `Retry-After` on any other answer,
59/// such as a proxy's, changes no wait. The mint answers `busy` only to a
60/// request it did not apply, so with the default and [`Client::new`]'s
61/// 30-second timeout the last try still ends within 127 seconds of any the
62/// mint may have applied, inside the 128 seconds a mint keeps each answer
63/// by default, and is answered as before.
64#[derive(Debug, Clone, Copy, PartialEq, Eq)]
65pub struct Retry {
66 /// Tries in all, the first included.
67 pub attempts: u32,
68 /// Wait before the second try; it doubles before each further one.
69 pub first_delay: Duration,
70}
71
72impl Default for Retry {
73 /// Four tries, one, two and four seconds apart.
74 fn default() -> Self {
75 Self {
76 attempts: 4,
77 first_delay: Duration::from_secs(1),
78 }
79 }
80}
81
82/// What submitting a set of transfers came to.
83#[derive(Debug, Default)]
84pub struct Submitted {
85 /// Value the purse took in: payments to it and its own change.
86 pub value: u128,
87 /// Transfers the mint answered.
88 pub answered: usize,
89 /// Every note in the answers, this purse's own included.
90 pub notes: Vec<Note>,
91 /// The notes in the answers for neither a note this purse holds nor a
92 /// key it expects: the counterparty's (see [`Holder::theirs`]). Hand
93 /// them over for its [`Purse::accept`]: a payee hands the payer its
94 /// change, and a payer that submitted a payment hands the payee its
95 /// notes. The payer's purse keeps those for the payee too, until
96 /// [`forget_owed`](Purse::forget_owed) says they are handed over.
97 pub theirs: Vec<Note>,
98 /// Transfers the mint refused, or that kept failing on the way.
99 pub refused: Vec<Refused>,
100}
101
102/// A transfer that did not go through, and why.
103#[derive(Debug)]
104pub struct Refused {
105 /// The transfer.
106 pub request: TransferRequest,
107 /// The last error. [`Error::is_transient`] tells whether submitting it
108 /// again later may work; a refusal such as `spent`, `expired` or
109 /// `layer_full` will not.
110 pub error: Error,
111 /// For a transfer refused as `spent` or `expired` that this purse made
112 /// or is paid by, what it may lose ([`Holder::at_stake`]), as it stood
113 /// before the transfer was dropped: the transfer may have been applied,
114 /// with an answer the mint no longer keeps. Only a counterparty that
115 /// submitted it can then hand the notes over, for
116 /// [`accept`](Purse::accept). `None` otherwise.
117 pub at_stake: Option<u128>,
118 /// What the purse did with the transfer.
119 pub outcome: Outcome,
120}
121
122/// What a purse did with a transfer the mint refused.
123#[derive(Debug, Clone, Copy, PartialEq, Eq)]
124pub enum Outcome {
125 /// Nothing: a transfer this purse made stays outgoing, for the next
126 /// [`settle`](Purse::settle). So it is after a refusal that may pass
127 /// later, one [`Error::is_transient`] accepts or `frozen`, and after one
128 /// that shows nothing about the transfer: `unknown_mint` or `unissued`
129 /// from a server that is not the mint's or lost its state, `spent` or
130 /// `expired` with another status than the mint's 409, `layer_full` with
131 /// another than its 503, or `unauthorized` or `duplicate_input`, which
132 /// the mint never answers to a transfer a holder signed.
133 Kept,
134 /// Refused as `spent` or `expired`, with the mint's status 409: the note
135 /// it spends is gone, so the purse [forgot](Holder::forget) the
136 /// transfer. Change the counterparty hands over later is still taken in
137 /// by [`accept`](Purse::accept).
138 Dropped,
139 /// Refused as `layer_full`, with the mint's status 503, or as `policy`,
140 /// when the policy the mint publishes now refuses it too: the purse
141 /// [cancelled](Holder::cancel) the transfer and took its note back. The
142 /// mint checks the note before the layer the new notes would land in, so
143 /// after `layer_full` the note is live and the transfer was never
144 /// applied, and it never will be: that layer gets no bits back before
145 /// the note's own is dropped.
146 Cancelled,
147}
148
149impl Purse<FileStore> {
150 /// A purse saved in the file at `path`, created on the first save if it
151 /// does not exist yet.
152 pub fn open(client: Client, path: impl Into<PathBuf>) -> Result<Self, Error> {
153 let store = FileStore::new(path);
154 let holder = store.load()?;
155 Ok(Self::new(client, holder, store))
156 }
157}
158
159impl<S: Store> Purse<S> {
160 /// A purse holding `holder`, talking to `client` and saving to `store`.
161 pub fn new(client: Client, holder: Holder, store: S) -> Self {
162 Self {
163 client,
164 holder: Mutex::new(holder),
165 store,
166 retry: Retry::default(),
167 }
168 }
169
170 /// Sets how failed transfers are retried.
171 pub fn with_retry(mut self, retry: Retry) -> Self {
172 self.retry = retry;
173 self
174 }
175
176 /// The client requests go through.
177 pub fn client(&self) -> &Client {
178 &self.client
179 }
180
181 /// The store saves go to.
182 pub fn store(&self) -> &S {
183 &self.store
184 }
185
186 /// A copy of the holder as it is now.
187 pub fn holder(&self) -> Holder {
188 self.lock().clone()
189 }
190
191 /// Value held at `mint`, by what it can do now, from the mint's info.
192 pub async fn balance(&self, mint: &PublicKey) -> Result<Balance, Error> {
193 let view = self.client.view(mint).await?;
194 Ok(self.lock().balance(&view))
195 }
196
197 /// Asks for `amount` from `mint`: a payment request with a fresh key
198 /// per note, saved before it is returned. Send it to the payer.
199 pub async fn request(&self, mint: &PublicKey, amount: u64) -> Result<PaymentRequest, Error> {
200 let view = self.client.view(mint).await?;
201 self.change(
202 |holder| Ok(holder.request(&view, amount)?),
203 |holder, request| holder.withdraw(request),
204 )
205 }
206
207 /// Like [`request`](Self::request), in notes of at most `largest`, for a
208 /// payer whose notes are small.
209 pub async fn request_at_most(
210 &self,
211 mint: &PublicKey,
212 amount: u64,
213 largest: u64,
214 ) -> Result<PaymentRequest, Error> {
215 let view = self.client.view(mint).await?;
216 self.change(
217 |holder| Ok(holder.request_at_most(&view, amount, largest)?),
218 |holder, request| holder.withdraw(request),
219 )
220 }
221
222 /// Asks a playground faucet for a note and keeps it. The faucet's mint
223 /// must be on this purse's server; the faucet may share its URL. The
224 /// note goes to a fresh key, saved before the faucet is asked, as for
225 /// any payment request. Returns the faucet's mint and the value kept.
226 pub async fn take_from_faucet(&self, faucet: &Client) -> Result<(PublicKey, u128), Error> {
227 let about = faucet.faucet().await?;
228 let request = self
229 .request_at_most(&about.mint, about.amount, about.amount)
230 .await?;
231 let [output] = request.outputs.as_slice() else {
232 self.withdraw(&request)?;
233 return Err(Error::FaucetAmount(about.amount));
234 };
235 let taken = match faucet.ask_faucet(&output.holder).await {
236 Ok(note) => self.accept(&[note]).await,
237 Err(error) => Err(error),
238 };
239 if taken.is_err() {
240 self.withdraw(&request)?;
241 }
242 Ok((about.mint, taken?))
243 }
244
245 /// Stops expecting the notes of a request that will not be paid.
246 pub fn withdraw(&self, request: &PaymentRequest) -> Result<(), Error> {
247 self.change(
248 |holder| {
249 holder.withdraw(request);
250 Ok(())
251 },
252 |_, ()| {},
253 )
254 }
255
256 /// Answers a payment request, after reading the mint's info. The notes
257 /// spent, the keys for the change and the payment itself (see
258 /// [`Holder::payments`]) are saved before the payment is returned. Send
259 /// it to the payee, who submits it and can hand back the change for
260 /// [`accept`](Self::accept); or [`settle`](Self::settle) later to take it
261 /// in. A request for a note the mint would not make, or to a key this
262 /// purse has paid already, is refused before anything is signed (see
263 /// [`Holder::pay`]).
264 pub async fn pay(&self, request: &PaymentRequest, spacing: Spacing) -> Result<Payment, Error> {
265 let view = self.client.view(&request.mint).await?;
266 self.change_or_restore(|holder| Ok(holder.pay(&view, request, spacing)?))
267 }
268
269 /// Takes back the note of an outgoing transfer the mint never applied,
270 /// keeping the keys of its change in case it did, and saves (see
271 /// [`Holder::cancel`]). Returns whether the transfer was outgoing.
272 ///
273 /// A payee that holds the transfer can still submit it: if the mint
274 /// applies it, or already had, the payee's notes and the note taken back
275 /// claim the same value, and the mint refuses whichever is spent second.
276 pub fn cancel(&self, request: &TransferRequest) -> Result<bool, Error> {
277 self.change_or_restore(|holder| Ok(holder.cancel(request)))
278 }
279
280 /// Drops an outgoing transfer whose note is gone, keeping the keys of its
281 /// change so that change handed over later is still taken in, and saves
282 /// (see [`Holder::forget`]). Returns whether the transfer was outgoing.
283 pub fn forget(&self, request: &TransferRequest) -> Result<bool, Error> {
284 self.change_or_restore(|holder| Ok(holder.forget(request)))
285 }
286
287 /// Stops keeping notes owed to payees, once they are handed over, and
288 /// saves (see [`Holder::forget_owed`]). The notes of a payment are in
289 /// [`Holder::payments`]: write them where the payee gets them, then
290 /// forget them. Returns the value it stopped keeping.
291 pub fn forget_owed(&self, notes: &[Note]) -> Result<u128, Error> {
292 self.change_or_restore(|holder| Ok(holder.forget_owed(notes)))
293 }
294
295 /// Takes a payment for `request`: checks that it pays what was asked,
296 /// then submits each transfer at its time and takes in the notes.
297 /// Returns once the last transfer is answered or refused. The payer
298 /// chose the times; wrap the call in a timeout to bound the wait. Hand
299 /// [`Submitted::theirs`], the payer's change, back to the payer.
300 pub async fn receive(
301 &self,
302 request: &PaymentRequest,
303 payment: &Payment,
304 ) -> Result<Submitted, Error> {
305 request.check(payment)?;
306 self.submit(&payment.transfers).await
307 }
308
309 /// Takes in notes the counterparty handed over from the mint's answers,
310 /// after checking each one's signature against its mint's keys, and
311 /// settles the outgoing transfers they answer (see
312 /// [`Holder::take_answers`]). Notes for keys this purse never handed out
313 /// are ignored. A note whose layer the mint has dropped no longer
314 /// verifies and fails the call. Returns the value taken in.
315 ///
316 /// The signatures show that the mint issued the notes, not that it
317 /// applied the transfers they settle: a counterparty could pay the same
318 /// keys from notes of its own (see [`Holder::take_answers`]).
319 pub async fn accept(&self, notes: &[Note]) -> Result<u128, Error> {
320 let mut keys: HashMap<PublicKey, KeySet> = HashMap::new();
321 for note in notes {
322 if !keys.contains_key(note.issuer()) {
323 let set = self.client.keys(note.issuer()).await?;
324 keys.insert(*note.issuer(), set);
325 }
326 note.verify(&keys[note.issuer()])?;
327 }
328 self.change(|holder| Ok(holder.take_answers(notes)), |_, _| {})
329 }
330
331 /// Resends the transfers this purse made and has not seen answered, at
332 /// random gaps of `spacing`, and takes in their change. Call it once a
333 /// payee has had time to submit a payment, or after a restart. Planned
334 /// refreshes still waiting for their time are resent too. The answer to
335 /// a payment's transfer keeps the payee's notes for it, in case the payee
336 /// has not submitted the payment and the resend carried it out (see
337 /// [`MadePayment::owed`](worgl_sdk::MadePayment::owed)), and a transfer
338 /// refused for good is dropped or cancelled (see [`Refused::outcome`]).
339 ///
340 /// The mint answers a resent transfer with the same notes only while it
341 /// keeps its answer, and at the latest until the notes it issued expire
342 /// (see [Error codes]). After that, a transfer already applied is
343 /// refused as `spent` or `expired`, with [`Refused::at_stake`] set, and
344 /// its change can only come from the counterparty, for
345 /// [`accept`](Self::accept). [`refresh`](Self::refresh) resends the
346 /// transfers whose chance the next rotation ends.
347 ///
348 /// [Error codes]: https://github.com/zhangheqi/worgl/blob/main/site/src/content/docs/docs/error-codes.md#sending-a-request-again
349 pub async fn settle(&self, spacing: Spacing) -> Result<Submitted, Error> {
350 let transfers = self.lock().resend(spacing);
351 self.submit(&transfers).await
352 }
353
354 /// Plans the refresh of `mint`'s notes that die at the next rotation,
355 /// after reading its info, and saves the plan. The outgoing transfers
356 /// to resend before that rotation join it (see
357 /// [`Holder::due_resends`]); if the save fails, only the refreshes are
358 /// undone. Submit the transfers at their times with
359 /// [`submit`](Self::submit).
360 ///
361 /// The times are planned by the server's clock, from the `Date` of its
362 /// info answer ([`Client::view_and_time`]), so this machine's clock,
363 /// slow or fast, moves none of them past the rotation. When the answer
364 /// has no `Date`, they are planned by this machine's clock instead. Once
365 /// the rotation's time has come and the mint has not made it yet, they
366 /// are all due at once.
367 pub async fn plan_refresh(&self, mint: &PublicKey) -> Result<Vec<ScheduledTransfer>, Error> {
368 let (view, now) = self.client.view_and_time(mint).await?;
369 let (mut transfers, resends) = self.change_or_restore(|holder| {
370 // Before the refreshes join the outgoing transfers.
371 let resends = holder.due_resends(&view, now);
372 Ok((holder.refresh(&view, now), resends))
373 })?;
374 transfers.extend(resends);
375 transfers.sort_by_key(|t| t.after_secs);
376 Ok(transfers)
377 }
378
379 /// Refreshes `mint`'s notes that die at the next rotation and resends
380 /// the outgoing transfers that must land before it: plans the transfers
381 /// with [`plan_refresh`](Self::plan_refresh) and submits each at its
382 /// random time before the rotation. Returns once the last one is done,
383 /// which may be most of a rotation period away; run it in a task of its
384 /// own.
385 pub async fn refresh(&self, mint: &PublicKey) -> Result<Submitted, Error> {
386 let transfers = self.plan_refresh(mint).await?;
387 self.submit(&transfers).await
388 }
389
390 /// Drops `mint`'s notes whose layer was dropped, after reading its info,
391 /// and returns the value lost. It also stops waiting for unconfirmed
392 /// change that would sit in a dropped layer, and stops keeping notes
393 /// owed to payees once their layer is dropped (see [`Holder::prune`]).
394 /// The info alone decides, not this machine's clock: a note goes, with
395 /// its key, only once the mint has dropped its layer.
396 pub async fn prune(&self, mint: &PublicKey) -> Result<u128, Error> {
397 let view = self.client.view(mint).await?;
398 self.change(|holder| Ok(holder.prune(&view)), |_, _| {})
399 }
400
401 /// Submits each transfer at its time, counted from now, retrying
402 /// failures on the way, and records each answer: the transfer stops
403 /// being outgoing and the notes for this purse are taken in, and the
404 /// payee's notes are kept for it when the transfer is part of a payment
405 /// this purse made (see [`Holder::settle`]). A payer that submits its own
406 /// payment hands them to the payee from [`Submitted::theirs`], then
407 /// calls [`forget_owed`](Self::forget_owed) with them, as `worgl submit`
408 /// does.
409 ///
410 /// A refused transfer does not stop the others; it is listed in
411 /// [`Submitted::refused`]. One this purse made that the mint refused for
412 /// good is dropped or cancelled ([`Refused::outcome`]); the rest stay
413 /// outgoing. To confirm a `policy` refusal, the purse reads the mint's
414 /// info again. The call fails only if a save does.
415 pub async fn submit(&self, transfers: &[ScheduledTransfer]) -> Result<Submitted, Error> {
416 let mut order: Vec<&ScheduledTransfer> = transfers.iter().collect();
417 order.sort_by_key(|transfer| transfer.after_secs);
418 let start = Instant::now();
419 let mut submitted = Submitted::default();
420 for transfer in order {
421 let delay = Duration::from_secs(transfer.after_secs.min(MAX_DELAY_SECS));
422 sleep_until(start + delay).await;
423 match self.send(&transfer.request).await {
424 Ok(notes) => {
425 let (value, theirs) = self.change(
426 |holder| {
427 let value = holder.settle(&transfer.request, ¬es);
428 Ok((value, holder.theirs(¬es)))
429 },
430 |_, _| {},
431 )?;
432 submitted.value += value;
433 submitted.answered += 1;
434 submitted.notes.extend(notes);
435 submitted.theirs.extend(theirs);
436 }
437 Err(error) => {
438 let (at_stake, outcome) = self.refused(&transfer.request, &error).await?;
439 submitted.refused.push(Refused {
440 request: transfer.request.clone(),
441 error,
442 at_stake,
443 outcome,
444 });
445 }
446 }
447 }
448 Ok(submitted)
449 }
450
451 /// Drops or cancels a transfer the mint refused, when the refusal shows
452 /// it can never be applied, and returns what is at stake on it and what
453 /// was done. A transfer that is not outgoing is left as it is.
454 async fn refused(
455 &self,
456 request: &TransferRequest,
457 error: &Error,
458 ) -> Result<(Option<u128>, Outcome), Error> {
459 let cancel = match error.code() {
460 Some("spent" | "expired") => {
461 // The mint answers both with 409 Conflict. Any other answer,
462 // such as a gateway's, shows nothing about the note.
463 if !matches!(error, Error::Refused(refusal) if refusal.status == 409) {
464 return Ok((self.lock().at_stake(request), Outcome::Kept));
465 }
466 return self.change_or_restore(|holder| {
467 // Before the transfer is dropped.
468 let at_stake = holder.at_stake(request);
469 let outcome = if holder.forget(request) {
470 Outcome::Dropped
471 } else {
472 Outcome::Kept
473 };
474 Ok((at_stake, outcome))
475 });
476 }
477 // The mint answers so with 503, after finding every note spent
478 // live. Any other answer shows nothing about the notes.
479 Some("layer_full") => {
480 matches!(error, Error::Refused(refusal) if refusal.status == 503)
481 }
482 // The mint checks its policy before its receipts, so a transfer
483 // applied under a looser policy is refused too: its change keys
484 // stay. Cancel only if the policy published now agrees.
485 Some("policy") => match request.inputs.first() {
486 Some(spend) => match self.client.view(spend.note.issuer()).await {
487 Ok(view) => {
488 let inputs: Vec<u64> =
489 request.inputs.iter().map(|s| s.note.amount()).collect();
490 let outputs: Vec<u64> = request.outputs.iter().map(|o| o.amount).collect();
491 view.policy.check_transfer(&inputs, &outputs).is_err()
492 }
493 Err(_) => false,
494 },
495 None => false,
496 },
497 // Anything else may pass later or shows nothing about the
498 // transfer: `unknown_mint` or `unissued` from a server that is
499 // not the mint's or lost its state, or `unauthorized` or
500 // `duplicate_input`, which the mint never answers to a transfer
501 // a holder signed, so they come from another server or version.
502 _ => false,
503 };
504 if !cancel {
505 return Ok((None, Outcome::Kept));
506 }
507 let cancelled = self.change_or_restore(|holder| Ok(holder.cancel(request)))?;
508 let outcome = if cancelled {
509 Outcome::Cancelled
510 } else {
511 Outcome::Kept
512 };
513 Ok((None, outcome))
514 }
515
516 /// Sends one transfer, retrying while the failure is transient.
517 async fn send(&self, request: &TransferRequest) -> Result<Vec<Note>, Error> {
518 let mut delay = self.retry.first_delay;
519 let mut attempt = 1;
520 loop {
521 match self.client.transfer(request).await {
522 Err(error) if error.is_transient() && attempt < self.retry.attempts => {
523 sleep(next_wait(delay, &error)).await;
524 delay = delay.saturating_mul(2);
525 attempt += 1;
526 }
527 result => return result,
528 }
529 }
530 }
531
532 /// Like [`change`](Self::change), but a failed save puts the holder back
533 /// as it was before `apply`.
534 fn change_or_restore<T>(
535 &self,
536 apply: impl FnOnce(&mut Holder) -> Result<T, Error>,
537 ) -> Result<T, Error> {
538 let (value, _) = self.change(
539 |holder| {
540 let before = holder.clone();
541 Ok((apply(holder)?, before))
542 },
543 |holder, (_, before)| *holder = before.clone(),
544 )?;
545 Ok(value)
546 }
547
548 /// Changes the holder as saved now and saves it, holding the store's
549 /// lock throughout. If the save fails, `undo` takes the change back and
550 /// the save's error is returned.
551 fn change<T>(
552 &self,
553 apply: impl FnOnce(&mut Holder) -> Result<T, Error>,
554 undo: impl FnOnce(&mut Holder, &T),
555 ) -> Result<T, Error> {
556 let mut holder = self.lock();
557 // Held until the save is done.
558 let locked = self.store.lock()?;
559 if let Some(saved) = locked.as_ref().and_then(Locked::saved) {
560 *holder = Holder::from_json(saved)?;
561 }
562 let value = apply(&mut holder)?;
563 if let Err(error) = self.store.save(&holder.to_json()) {
564 undo(&mut holder, &value);
565 return Err(error.into());
566 }
567 Ok(value)
568 }
569
570 fn lock(&self) -> MutexGuard<'_, Holder> {
571 // A panic in another task holding the lock should not take the
572 // purse down with it.
573 self.holder.lock().unwrap_or_else(|e| e.into_inner())
574 }
575}
576
577/// The wait before the next try after `error`: `delay`, or after `busy`
578/// what the mint's `Retry-After` asks if that is longer, up to
579/// [`MAX_RETRY_AFTER`]. It stretches this wait only, never the ones after.
580///
581/// Only `busy` stretches it: the mint answers so only to a request it did
582/// not apply. Another answer, such as a proxy's, may come after the mint
583/// applied the try, and longer waits could take the last try past the
584/// time the mint keeps the receipt for.
585fn next_wait(delay: Duration, error: &Error) -> Duration {
586 match error.retry_after() {
587 Some(asked) if error.code() == Some("busy") => delay.max(asked.min(MAX_RETRY_AFTER)),
588 _ => delay,
589 }
590}