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#[derive(Debug)]
17pub enum UpgradeResult {
18 Upgraded,
20 Pending,
22 Failed(Error),
24}
25
26impl Sdk {
27 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, |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}