use std::sync::Arc;
use async_trait::async_trait;
use bytes::Bytes;
use iroh_blobs::{BlobsProtocol, store::mem::MemStore, Hash as IrohHash};
use xlb_net::{Endpoint, NodeAddr};
use crate::{
source::{BlobSource, FetchTier},
AssetClass, BlakeHash,
};
fn to_iroh(h: &BlakeHash) -> IrohHash {
IrohHash::from_bytes(*h.as_bytes())
}
pub struct BlobTransport {
store: MemStore,
router: iroh::protocol::Router,
endpoint: Endpoint,
}
impl BlobTransport {
pub async fn new(endpoint: Endpoint) -> anyhow::Result<Self> {
let store = MemStore::new();
let blobs_proto = BlobsProtocol::new(&store, None);
let router = iroh::protocol::Router::builder(endpoint.inner().clone())
.accept(iroh_blobs::ALPN, blobs_proto)
.spawn();
Ok(Self { store, router, endpoint })
}
pub async fn add_blob(&self, data: impl Into<Bytes>) -> anyhow::Result<BlakeHash> {
let data: Bytes = data.into();
let xlb_hash = BlakeHash::hash(&data);
let mut tt = self.store.add_bytes(data).temp_tag().await
.map_err(|e| anyhow::anyhow!("iroh-blobs add_bytes: {e}"))?;
tt.leak();
Ok(xlb_hash)
}
pub fn node_addr(&self) -> NodeAddr {
self.endpoint.endpoint_addr()
}
pub fn attach_fetcher(&self, class: &AssetClass, seeder: NodeAddr, tier: FetchTier) {
class.add_source(Arc::new(IrohFetcher {
tier,
endpoint: self.endpoint.clone(),
peer: seeder,
local_store: self.store.clone(),
}));
}
pub async fn shutdown(self) {
if let Err(e) = self.router.shutdown().await {
tracing::warn!("BlobTransport::shutdown: {e}");
}
}
}
struct IrohFetcher {
tier: FetchTier,
endpoint: Endpoint,
peer: NodeAddr,
local_store: MemStore,
}
#[async_trait]
impl BlobSource for IrohFetcher {
fn tier(&self) -> FetchTier {
self.tier
}
async fn fetch_raw(&self, hash: &BlakeHash) -> Option<Bytes> {
let iroh_hash = to_iroh(hash);
if let Ok(bytes) = self.local_store.get_bytes(iroh_hash).await {
return Some(bytes);
}
let conn = self
.endpoint
.inner()
.connect(self.peer.clone(), iroh_blobs::ALPN)
.await
.map_err(|e| tracing::warn!(%hash, "iroh-blobs connect: {e}"))
.ok()?;
self.local_store
.remote()
.fetch(conn, iroh_hash)
.await
.map_err(|e| tracing::warn!(%hash, "iroh-blobs fetch: {e}"))
.ok()?;
self.local_store
.get_bytes(iroh_hash)
.await
.map_err(|e| tracing::warn!(%hash, "iroh-blobs get_bytes: {e}"))
.ok()
}
}