clawft_kernel/
mesh_swarm_lookup.rs1use std::collections::BTreeMap;
21use std::sync::Arc;
22
23use clawft_types::placement::AttrValue;
24
25use crate::mesh_artifact::ArtifactExchange;
26use crate::mesh_artifact_types::{ArtifactDescriptor, ArtifactKey, Bitfield};
27use crate::mesh_swarm_fetch::PeerDialer;
28use crate::mesh_swarm_picker::PeerCandidate;
29use crate::node_facts::NodeFactsCache;
30use crate::workload_pkg::codec::hex_encode;
31
32pub const LAN_CAPABILITY: &str = "net.lan";
34
35pub const WHO_HAS_CONCURRENCY: usize = 8;
37pub const WHO_HAS_MAX_PEERS: usize = 64;
39
40#[derive(Debug, Clone, PartialEq, Eq)]
42pub struct Holder {
43 pub peer: String,
45 pub descriptor: ArtifactDescriptor,
47 pub have: Bitfield,
49}
50
51pub fn holders_from_facts(
54 cache: &NodeFactsCache,
55 content_hash: &[u8; 32],
56 now: u64,
57 me: &str,
58) -> Vec<PeerCandidate> {
59 let hex = hex_encode(content_hash);
60 let prefix = format!("store.artifact.{}", &hex[..16]);
61 cache
62 .list(now)
63 .into_iter()
64 .filter(|c| c.node_id() != me)
65 .filter(|c| {
66 c.capabilities().iter().any(|cap| {
67 cap.id.as_str() == prefix
68 && matches!(cap.attrs.get("content"), Some(AttrValue::Str(s)) if *s == hex)
69 })
70 })
71 .map(|c| {
72 let lan = c.capabilities().iter().find_map(|cap| {
73 (cap.id.as_str() == LAN_CAPABILITY)
74 .then(|| match cap.attrs.get("lan_id") {
75 Some(AttrValue::Str(s)) => Some(s.clone()),
76 _ => None,
77 })
78 .flatten()
79 });
80 PeerCandidate {
81 peer_id: c.node_id().to_string(),
82 lan_id: lan,
83 tier: c.trust_tier(),
84 }
85 })
86 .collect()
87}
88
89impl ArtifactExchange {
90 pub async fn who_has(
93 self: &Arc<Self>,
94 dialer: Arc<dyn PeerDialer>,
95 peers: &[PeerCandidate],
96 key: ArtifactKey,
97 ) -> Vec<Holder> {
98 let mut set = tokio::task::JoinSet::new();
99 let gate = Arc::new(tokio::sync::Semaphore::new(WHO_HAS_CONCURRENCY));
100 for p in peers
101 .iter()
102 .filter(|p| !self.is_banned(&p.peer_id))
103 .take(WHO_HAS_MAX_PEERS)
104 {
105 let (ex, dialer, peer, gate) =
106 (self.clone(), dialer.clone(), p.peer_id.clone(), gate.clone());
107 set.spawn(async move {
108 let _slot = gate.acquire_owned().await.ok()?;
109 let (mut s, d) = ex.open_session_for_lookup(dialer.as_ref(), &peer, key).await.ok()?;
110 let _ = s.stream.close().await;
111 Some(Holder {
112 peer,
113 descriptor: d,
114 have: s.has,
115 })
116 });
117 }
118 let mut out = BTreeMap::new();
119 while let Some(r) = set.join_next().await {
120 if let Ok(Some(h)) = r {
121 out.insert(h.peer.clone(), h);
122 }
123 }
124 out.into_values().collect()
125 }
126
127 pub async fn find_holders(
130 self: &Arc<Self>,
131 dialer: Arc<dyn PeerDialer>,
132 facts: &NodeFactsCache,
133 neighbors: &[PeerCandidate],
134 key: ArtifactKey,
135 now: u64,
136 ) -> Vec<PeerCandidate> {
137 let mut out: BTreeMap<String, PeerCandidate> = BTreeMap::new();
138 if let Some(d) = self.resolve(&key) {
139 for c in holders_from_facts(facts, &d.content_hash, now, self.node_id()) {
140 out.insert(c.peer_id.clone(), c);
141 }
142 } else if let ArtifactKey::Content(h) = key {
143 for c in holders_from_facts(facts, &h, now, self.node_id()) {
144 out.insert(c.peer_id.clone(), c);
145 }
146 }
147 for h in self.who_has(dialer, neighbors, key).await {
148 let known = neighbors.iter().find(|n| n.peer_id == h.peer);
149 out.entry(h.peer.clone())
150 .or_insert_with(|| known.cloned().unwrap_or_else(|| PeerCandidate::new(h.peer)));
151 }
152 out.into_values().collect()
153 }
154}