Skip to content

Commit

Permalink
refactor: get rid of Blake3Cid (#1204)
Browse files Browse the repository at this point in the history
Display and FromStr for iroh_bytes::Hash will now directly support the cid
format.
  • Loading branch information
rklaehn authored Jul 10, 2023
1 parent 69649ba commit cf0573a
Show file tree
Hide file tree
Showing 11 changed files with 90 additions and 153 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

105 changes: 0 additions & 105 deletions iroh-bytes/src/cid.rs

This file was deleted.

1 change: 0 additions & 1 deletion iroh-bytes/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
#![recursion_limit = "256"]

pub mod blobs;
pub mod cid;
pub mod get;
pub mod progress;
pub mod protocol;
Expand Down
97 changes: 72 additions & 25 deletions iroh-bytes/src/util.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
//! Utility functions and types.
use anyhow::{ensure, Context, Result};
use anyhow::{Context, Result};
use bao_tree::io::{error::EncodeError, sync::encode_ranges_validated};
use bytes::Bytes;
use derive_more::Display;
use postcard::experimental::max_size::MaxSize;
use range_collections::RangeSet2;
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use std::{
fmt::{self, Display},
fmt::{self},
io::{self, Read, Seek, Write},
path::{Component, Path, PathBuf},
result,
Expand All @@ -34,7 +34,35 @@ impl Hash {
self.0.as_bytes()
}

/// Hex string of the hash.
/// Get the cid as bytes.
pub fn as_cid_bytes(&self) -> [u8; 36] {
let hash = self.0.as_bytes();
let mut res = [0u8; 36];
res[0..4].copy_from_slice(&CID_PREFIX);
res[4..36].copy_from_slice(hash);
res
}

/// Try to create a blake3 cid from cid bytes.
///
/// This will only work if the prefix is the following:
/// - version 1
/// - raw codec
/// - blake3 hash function
/// - 32 byte hash size
pub fn from_cid_bytes(bytes: &[u8]) -> anyhow::Result<Self> {
anyhow::ensure!(
bytes.len() == 36,
"invalid cid length, expected 36, got {}",
bytes.len()
);
anyhow::ensure!(bytes[0..4] == CID_PREFIX, "invalid cid prefix");
let mut hash = [0u8; 32];
hash.copy_from_slice(&bytes[4..36]);
Ok(Self::from(hash))
}

/// Convert the hash to a hex string.
pub fn to_hex(&self) -> String {
self.0.to_hex().to_string()
}
Expand Down Expand Up @@ -76,35 +104,47 @@ impl Ord for Hash {
}
}

impl Display for Hash {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let text = data_encoding::BASE32_NOPAD
.encode(self.0.as_bytes())
.to_ascii_lowercase();
write!(f, "{text}")
impl fmt::Display for Hash {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// result will be 58 bytes plus prefix
let mut res = [b'b'; 59];
// write the encoded bytes
data_encoding::BASE32_NOPAD.encode_mut(&self.as_cid_bytes(), &mut res[1..]);
// convert to string, this is guaranteed to succeed
let t = std::str::from_utf8_mut(res.as_mut()).unwrap();
// hack since data_encoding doesn't have BASE32LOWER_NOPAD as a const
t.make_ascii_lowercase();
// write the str, no allocations
f.write_str(t)
}
}

impl FromStr for Hash {
type Err = anyhow::Error;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut arr = [0u8; 32];
let val = data_encoding::BASE32_NOPAD.decode(s.to_ascii_uppercase().as_bytes())?; // todo: use a custom error type
ensure!(
val.len() == 32,
"invalid byte length, expected 32, got {}",
val.len()
);
ensure!(
val.len() == 32,
"invalid byte length, expected 32, got {}",
val.len()
);
arr.copy_from_slice(&val);
let hash = blake3::Hash::from(arr);

Ok(Hash(hash))
let sb = s.as_bytes();
if sb.len() == 59 && sb[0] == b'b' {
// this is a base32 encoded cid, we can decode it directly
let mut t = [0u8; 58];
t.copy_from_slice(&sb[1..]);
// hack since data_encoding doesn't have BASE32LOWER_NOPAD as a const
std::str::from_utf8_mut(t.as_mut())
.unwrap()
.make_ascii_uppercase();
// decode the bytes
let mut res = [0u8; 36];
data_encoding::BASE32_NOPAD
.decode_mut(&t, &mut res)
.map_err(|_e| anyhow::anyhow!("invalid base32"))?;
// convert to cid, this will check the prefix
Self::from_cid_bytes(&res)
} else {
// if we want to support all the weird multibase prefixes, we have no choice
// but to use the multibase crate
let (_base, bytes) = multibase::decode(s)?;
Self::from_cid_bytes(bytes.as_ref())
}
}
}

Expand Down Expand Up @@ -148,6 +188,13 @@ impl MaxSize for Hash {
const POSTCARD_MAX_SIZE: usize = 32;
}

const CID_PREFIX: [u8; 4] = [
0x01, // version
0x55, // raw codec
0x1e, // hash function, blake3
0x20, // hash size, 32 bytes
];

/// A serializable error type for use in RPC responses.
#[derive(Serialize, Deserialize, Debug, Error)]
pub struct RpcError(serde_error::Error);
Expand Down
2 changes: 2 additions & 0 deletions iroh/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ rust-version = "1.65"
[dependencies]
anyhow = { version = "1", features = ["backtrace"] }
bao-tree = { version = "0.5.0-rc.1", features = ["tokio_fsm"], default-features = false }
blake3 = "1.3.3"
data-encoding = "2.3.3"
derive_more = { version = "0.99.17", git = "/~https://github.com/JelteF/derive_more", features = ["debug", "display", "from", "try_into"] }
flume = "0.10.14"
futures = "0.3.25"
Expand Down
6 changes: 3 additions & 3 deletions iroh/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use std::{net::SocketAddr, path::PathBuf};
use anyhow::{Context, Result};
use clap::{Parser, Subcommand};
use iroh::rpc_protocol::*;
use iroh_bytes::{cid::Blake3Cid, protocol::RequestToken, provider::Ticket, runtime};
use iroh_bytes::{protocol::RequestToken, provider::Ticket, runtime, Hash};
use iroh_net::tls::{Keypair, PeerId};
use quic_rpc::transport::quinn::QuinnConnection;
use quic_rpc::RpcClient;
Expand Down Expand Up @@ -77,7 +77,7 @@ impl Cli {
}
} else if let (Some(peer), Some(hash)) = (peer, hash) {
self::get::GetInteractive {
hash: *hash.as_hash(),
hash,
opts: iroh_bytes::get::Options {
addrs,
peer_id: peer,
Expand Down Expand Up @@ -219,7 +219,7 @@ pub enum Commands {
Get {
/// The hash to retrieve, as a Blake3 CID
#[clap(conflicts_with = "ticket", required_unless_present = "ticket")]
hash: Option<Blake3Cid>,
hash: Option<Hash>,
/// PeerId of the provider
#[clap(
long,
Expand Down
4 changes: 2 additions & 2 deletions iroh/src/commands/add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use anyhow::{Context, Result};
use futures::{Stream, StreamExt};
use indicatif::{HumanBytes, MultiProgress, ProgressBar, ProgressStyle};
use iroh::rpc_protocol::ProvideRequest;
use iroh_bytes::{cid::Blake3Cid, provider::ProvideProgress, Hash};
use iroh_bytes::{provider::ProvideProgress, Hash};

use crate::commands::make_rpc_client;

Expand Down Expand Up @@ -105,7 +105,7 @@ pub fn print_add_response(hash: Hash, entries: Vec<ProvideResponseEntry>) {
}
println!("Total: {}", HumanBytes(total_size));
println!();
println!("Collection: {}", Blake3Cid::new(hash));
println!("Collection: {}", hash);
}

#[derive(Debug)]
Expand Down
9 changes: 4 additions & 5 deletions iroh/src/commands/get.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ use indicatif::{
};
use iroh_bytes::{
blobs::Collection,
cid::Blake3Cid,
get::{
self,
get_response_machine::{self, ConnectedNext, EndBlobNext},
Expand Down Expand Up @@ -45,10 +44,10 @@ impl GetInteractive {
/// Get a single file.
async fn get_to_file_single(self, out_dir: PathBuf, temp_dir: PathBuf) -> Result<()> {
let hash = self.hash;
write(format!("Fetching: {}", Blake3Cid::new(hash)));
write(format!("Fetching: {}", hash));
write(format!("{} Connecting ...", style("[1/3]").bold().dim()));

let name = Blake3Cid::new(hash).to_string();
let name = hash.to_string();
// range I am missing for the 1 file I am downloading
let range = get::get_missing_range(&self.hash, name.as_str(), &temp_dir, &out_dir)?;
if range.is_all() {
Expand Down Expand Up @@ -139,7 +138,7 @@ impl GetInteractive {
/// Get into a file or directory
async fn get_to_dir_multi(self, out_dir: PathBuf, temp_dir: PathBuf) -> Result<()> {
let hash = self.hash;
write(format!("Fetching: {}", Blake3Cid::new(hash)));
write(format!("Fetching: {}", hash));
write(format!("{} Connecting ...", style("[1/3]").bold().dim()));
let (query, collection) = get::get_missing_ranges(self.hash, &out_dir, &temp_dir)?;
let collection = collection.map(|x| x.into_inner()).unwrap_or_default();
Expand Down Expand Up @@ -309,7 +308,7 @@ impl GetInteractive {

/// Get to stdout, no resume possible.
async fn get_to_stdout(self) -> Result<()> {
write(format!("Fetching: {}", Blake3Cid::new(self.hash)));
write(format!("Fetching: {}", self.hash));
write(format!("{} Connecting ...", style("[1/3]").bold().dim()));
let query = if self.single {
// just get the entire first item
Expand Down
10 changes: 2 additions & 8 deletions iroh/src/commands/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ use clap::Subcommand;
use futures::StreamExt;
use indicatif::HumanBytes;
use iroh::rpc_protocol::{ListBlobsRequest, ListCollectionsRequest};
use iroh_bytes::cid::Blake3Cid;

use super::{make_rpc_client, DEFAULT_RPC_PORT};

Expand Down Expand Up @@ -31,12 +30,7 @@ impl Commands {
let mut response = client.server_streaming(ListBlobsRequest).await?;
while let Some(item) = response.next().await {
let item = item?;
println!(
"{} {} ({})",
item.path,
Blake3Cid(item.hash),
HumanBytes(item.size),
);
println!("{} {} ({})", item.path, item.hash, HumanBytes(item.size),);
}
}
Commands::Collections { rpc_port } => {
Expand All @@ -46,7 +40,7 @@ impl Commands {
let collection = collection?;
println!(
"{}: {} {} ({})",
Blake3Cid(collection.hash),
collection.hash,
collection.total_blobs_count,
if collection.total_blobs_count > 1 {
"blobs"
Expand Down
Loading

0 comments on commit cf0573a

Please sign in to comment.