clawft_kernel/
mesh_swarm_pkg.rs1use std::sync::Arc;
12
13use crate::mesh_artifact::ArtifactExchange;
14use crate::mesh_artifact_pkg::{ExchangedPackage, PackageExchangeError, pinned};
15use crate::mesh_artifact_types::ArtifactKey;
16use crate::mesh_swarm_fetch::{Expect, PeerDialer, SwarmFetchOptions};
17use crate::mesh_swarm_picker::PeerCandidate;
18use crate::workload_kind::KindRegistry;
19use crate::workload_pkg::codec::{hex_decode_exact, hex_encode};
20use crate::workload_pkg::manifest::MAX_MANIFEST_BYTES;
21use crate::workload_pkg::verify::{VerifyError, verify_manifest_signatures_in};
22use crate::workload_pkg::{TrustAnchors, VerifyPolicy, verify_stored_in};
23
24impl ArtifactExchange {
25 pub async fn swarm_fetch_package(
27 self: &Arc<Self>,
28 dialer: Arc<dyn PeerDialer>,
29 candidates: &[PeerCandidate],
30 manifest_hash: &str,
31 anchors: &TrustAnchors,
32 opts: &SwarmFetchOptions,
33 ) -> Result<ExchangedPackage, PackageExchangeError> {
34 self.swarm_fetch_package_in(
35 dialer,
36 candidates,
37 manifest_hash,
38 anchors,
39 opts,
40 &KindRegistry::builtin(),
41 )
42 .await
43 }
44
45 pub async fn swarm_fetch_package_in(
47 self: &Arc<Self>,
48 dialer: Arc<dyn PeerDialer>,
49 candidates: &[PeerCandidate],
50 manifest_hash: &str,
51 anchors: &TrustAnchors,
52 opts: &SwarmFetchOptions,
53 kinds: &KindRegistry,
54 ) -> Result<ExchangedPackage, PackageExchangeError> {
55 let mh = hex_decode_exact::<32>(manifest_hash).ok_or_else(|| {
56 VerifyError::Manifest("manifest hash must be 64 lower-case hex".into())
57 })?;
58 let cache = self.swarm.cache();
59 if let Some(c) = &cache {
60 c.pin_content(mh);
61 }
62 let mopts = SwarmFetchOptions {
63 expect: Expect {
64 max_size: Some(MAX_MANIFEST_BYTES as u64),
65 ..opts.expect.clone()
66 },
67 ..opts.clone()
68 };
69 let m = self
70 .swarm_fetch(dialer.clone(), candidates.to_vec(), ArtifactKey::Content(mh), &mopts)
71 .await?;
72 if m.descriptor.total_size > MAX_MANIFEST_BYTES as u64 {
73 return Err(VerifyError::Manifest("manifest too large".into()).into());
74 }
75 let manifest = self.read_all(&m.id)?;
76 let verified = verify_manifest_signatures_in(&manifest, anchors, kinds)?;
77 self.refuse_if_revoked(&verified, anchors, &mh)?;
78 let grant = self.authorize(verified.clone(), mh, Vec::new(), anchors);
79
80 if let Some(c) = &cache {
81 for file in verified.body.files() {
82 c.pin_content(pinned(file)?.0);
83 }
84 }
85 let mut files = Vec::new();
86 let mut all_small = true;
87 for file in verified.body.files() {
88 let (hash, size) = pinned(file)?;
89 let fopts = SwarmFetchOptions {
90 expect: Expect {
91 size: Some(size),
92 ..opts.expect.clone()
93 },
94 ..opts.clone()
95 };
96 let got = self
97 .swarm_fetch(dialer.clone(), candidates.to_vec(), ArtifactKey::Content(hash), &fopts)
98 .await?;
99 if got.descriptor.total_size != size {
100 return Err(VerifyError::HashMismatch {
101 path: file.path.clone(),
102 expected: format!("{size} bytes"),
103 actual: format!("{} bytes", got.descriptor.total_size),
104 }
105 .into());
106 }
107 all_small &= size <= self.config().materialize_limit;
108 files.push((file.path.clone(), got.id));
109 }
110 if all_small {
111 verify_stored_in(
112 self.store(),
113 &hex_encode(&mh),
114 anchors,
115 &VerifyPolicy::default(),
116 kinds,
117 )?;
118 }
119 Ok(ExchangedPackage { files, ..grant })
120 }
121}