Skip to main content

uts_sdk/
upgrade.rs

1use crate::{Result, Sdk, error::Error};
2use http::{Method, StatusCode};
3use std::collections::BTreeMap;
4use tracing::{debug, warn};
5use url::Url;
6use uts_core::{
7    alloc::Allocator,
8    codec::{
9        DecodeIn,
10        v1::{Attestation, DetachedTimestamp, PendingAttestation, Timestamp},
11    },
12    utils::Hexed,
13};
14
15/// Result of attempting to upgrade a pending attestation.
16#[derive(Debug)]
17pub enum UpgradeResult {
18    /// The attestation has been successfully upgraded.
19    Upgraded,
20    /// The attestation is still pending and not ready to be upgraded.
21    Pending,
22    /// The attestation upgrade failed due to an error.
23    Failed(Error),
24}
25
26impl Sdk {
27    /// Upgrades all pending attestations in the given detached timestamp.
28    pub async fn upgrade<A: Allocator + Clone>(
29        &self,
30        stamp: &mut DetachedTimestamp<A>,
31    ) -> Result<BTreeMap<String, UpgradeResult>> {
32        let mut results = BTreeMap::new();
33
34        let alloc = stamp.allocator().clone();
35        for step in stamp.pending_attestations_mut() {
36            let (calendar_server, retrieve_uri) = {
37                let Timestamp::Attestation(attestation) = step else {
38                    unreachable!("bug: PendingAttestationIterMut should only yield Attestations");
39                };
40                let commitment = attestation.value().expect("finalized when decode");
41                let calendar_server = PendingAttestation::from_raw(&*attestation)?.uri;
42
43                let retrieve_uri = Url::parse(&calendar_server)?
44                    .join(&format!("timestamp/{}", Hexed(commitment)))?;
45
46                (calendar_server.to_string(), retrieve_uri)
47            };
48
49            let result = self
50                .http_request_with_retry(
51                    Method::GET,
52                    retrieve_uri,
53                    10 * 1024 * 1024, // 10 MiB response size limit
54                    |req| req.header("Accept", "application/vnd.opentimestamps.v1"),
55                )
56                .await;
57
58            let result = match result {
59                Ok((parts, _)) if parts.status == StatusCode::NOT_FOUND => {
60                    debug!("attestation from {calendar_server} not ready yet, skipping");
61                    UpgradeResult::Pending
62                }
63                Ok((_, response)) => {
64                    let attestation = Timestamp::decode_in(&mut &*response, alloc.clone())?;
65
66                    *step = if self.inner.keep_pending {
67                        Timestamp::merge_in(
68                            uts_core::alloc::vec![in alloc.clone(); attestation, step.clone()],
69                            alloc.clone(),
70                        )
71                    } else {
72                        attestation
73                    };
74                    UpgradeResult::Upgraded
75                }
76                Err(e) => {
77                    warn!("failed to upgrade pending attestation from {calendar_server}: {e}");
78                    UpgradeResult::Failed(e)
79                }
80            };
81
82            if let Some(old) = results.insert(calendar_server.clone(), result) {
83                warn!(
84                    "multiple pending attestations from {calendar_server}, previous result was {old:?}, you should only attest to a calendar once per timestamp"
85                );
86            }
87        }
88        Ok(results)
89    }
90}