pub const PILE_SYNC_ALPN: &[u8] = b"/triblespace/pile-sync/4";
pub const OP_LIST: u8 = 0x01;
pub const OP_GET_BLOB: u8 = 0x02;
pub const OP_CHILDREN: u8 = 0x03;
pub const OP_HEAD: u8 = 0x04;
pub const OP_AUTH: u8 = 0x05;
pub const AUTH_OK: u8 = 0x00;
pub const AUTH_REJECTED: u8 = 0x01;
pub const NIL_HASH: RawHash = [0u8; 32];
pub const NIL_BRANCH_ID: RawBranchId = [0u8; 16];
pub type RawHash = [u8; 32];
pub type RawBranchId = [u8; 16];
use anyhow::{Result, anyhow};
use iroh::endpoint::{SendStream, RecvStream, Connection};
pub async fn send_u8(send: &mut SendStream, v: u8) -> Result<()> {
send.write_all(&[v]).await.map_err(|e| anyhow!("send: {e}"))
}
pub async fn send_hash(send: &mut SendStream, hash: &RawHash) -> Result<()> {
send.write_all(hash).await.map_err(|e| anyhow!("send: {e}"))
}
pub async fn send_branch_id(send: &mut SendStream, id: &RawBranchId) -> Result<()> {
send.write_all(id).await.map_err(|e| anyhow!("send: {e}"))
}
pub async fn send_u32_be(send: &mut SendStream, v: u32) -> Result<()> {
send.write_all(&v.to_be_bytes()).await.map_err(|e| anyhow!("send: {e}"))
}
pub async fn send_u64_be(send: &mut SendStream, v: u64) -> Result<()> {
send.write_all(&v.to_be_bytes()).await.map_err(|e| anyhow!("send: {e}"))
}
pub async fn recv_u8(recv: &mut RecvStream) -> Result<u8> {
let mut buf = [0u8; 1];
recv.read_exact(&mut buf).await.map_err(|e| anyhow!("recv: {e}"))?;
Ok(buf[0])
}
pub async fn recv_hash(recv: &mut RecvStream) -> Result<RawHash> {
let mut buf = [0u8; 32];
recv.read_exact(&mut buf).await.map_err(|e| anyhow!("recv: {e}"))?;
Ok(buf)
}
pub async fn recv_branch_id(recv: &mut RecvStream) -> Result<RawBranchId> {
let mut buf = [0u8; 16];
recv.read_exact(&mut buf).await.map_err(|e| anyhow!("recv: {e}"))?;
Ok(buf)
}
pub async fn recv_u32_be(recv: &mut RecvStream) -> Result<u32> {
let mut buf = [0u8; 4];
recv.read_exact(&mut buf).await.map_err(|e| anyhow!("recv: {e}"))?;
Ok(u32::from_be_bytes(buf))
}
pub async fn recv_u64_be(recv: &mut RecvStream) -> Result<u64> {
let mut buf = [0u8; 8];
recv.read_exact(&mut buf).await.map_err(|e| anyhow!("recv: {e}"))?;
Ok(u64::from_be_bytes(buf))
}
pub async fn op_auth(conn: &Connection, cap_handle: &RawHash) -> Result<()> {
let (mut send, mut recv) = conn.open_bi().await.map_err(|e| anyhow!("open_bi: {e}"))?;
send_u8(&mut send, OP_AUTH).await?;
send_hash(&mut send, cap_handle).await?;
send.finish().map_err(|e| anyhow!("finish: {e}"))?;
let resp = recv_u8(&mut recv).await?;
match resp {
AUTH_OK => Ok(()),
AUTH_REJECTED => Err(anyhow!("server rejected capability")),
other => Err(anyhow!("unknown auth response: {other:#x}")),
}
}
pub async fn op_list(conn: &Connection) -> Result<Vec<(RawBranchId, RawHash)>> {
let (mut send, mut recv) = conn.open_bi().await.map_err(|e| anyhow!("open_bi: {e}"))?;
send_u8(&mut send, OP_LIST).await?;
send.finish().map_err(|e| anyhow!("finish: {e}"))?;
let mut branches = Vec::new();
loop {
let id = recv_branch_id(&mut recv).await?;
if id == NIL_BRANCH_ID { break; }
let head = recv_hash(&mut recv).await?;
branches.push((id, head));
}
Ok(branches)
}
pub async fn op_head(conn: &Connection, branch_id: &RawBranchId) -> Result<Option<RawHash>> {
let (mut send, mut recv) = conn.open_bi().await.map_err(|e| anyhow!("open_bi: {e}"))?;
send_u8(&mut send, OP_HEAD).await?;
send_branch_id(&mut send, branch_id).await?;
send.finish().map_err(|e| anyhow!("finish: {e}"))?;
let hash = recv_hash(&mut recv).await?;
if hash == NIL_HASH { Ok(None) } else { Ok(Some(hash)) }
}
pub async fn op_get_blob(conn: &Connection, hash: &RawHash) -> Result<Option<Vec<u8>>> {
let (mut send, mut recv) = conn.open_bi().await.map_err(|e| anyhow!("open_bi: {e}"))?;
send_u8(&mut send, OP_GET_BLOB).await?;
send_hash(&mut send, hash).await?;
send.finish().map_err(|e| anyhow!("finish: {e}"))?;
let len = recv_u64_be(&mut recv).await?;
if len == u64::MAX { return Ok(None); }
let mut data = vec![0u8; len as usize];
recv.read_exact(&mut data).await.map_err(|e| anyhow!("recv: {e}"))?;
Ok(Some(data))
}
pub async fn op_children(
conn: &Connection,
parent: &RawHash,
) -> Result<Vec<RawHash>> {
let (mut send, mut recv) = conn.open_bi().await.map_err(|e| anyhow!("open_bi: {e}"))?;
send_u8(&mut send, OP_CHILDREN).await?;
send_hash(&mut send, parent).await?;
send.finish().map_err(|e| anyhow!("finish: {e}"))?;
let mut children = Vec::new();
loop {
let hash = recv_hash(&mut recv).await?;
if hash == NIL_HASH { break; }
children.push(hash);
}
Ok(children)
}