Skip to content
This repository was archived by the owner on Dec 13, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
cc7a1dd
Moving stuff from other branch
BoisterousCoder Oct 26, 2022
3e5e770
IPFS Trait Complete
BoisterousCoder Oct 28, 2022
9bf1c5e
adding what I got so I can review brian's pr
BoisterousCoder Nov 8, 2022
af8d470
ready for testing for a lot of it
BoisterousCoder Nov 9, 2022
9dc9831
first test done
BoisterousCoder Nov 15, 2022
caaa869
fibonacci http retry wait
BoisterousCoder Nov 18, 2022
3243116
try fail http fixed
BoisterousCoder Nov 25, 2022
cc4f983
refactor and more
BoisterousCoder Nov 29, 2022
faede32
files upload to ipfs finnally
BoisterousCoder Nov 29, 2022
7c4d779
removing auto disconnect for now
BoisterousCoder Nov 29, 2022
c4442de
oops
BoisterousCoder Nov 29, 2022
a8114fe
testing add file
BoisterousCoder Nov 29, 2022
66c040e
finnishing add file
BoisterousCoder Nov 29, 2022
03bf7a4
quick fix for file adding
BoisterousCoder Nov 29, 2022
72911e0
remove warnings
BoisterousCoder Nov 29, 2022
31c0dee
fixing config
BoisterousCoder Dec 1, 2022
3750b4f
Moving stuff from other branch
BoisterousCoder Oct 26, 2022
5c665be
IPFS Trait Complete
BoisterousCoder Oct 28, 2022
617cf35
adding what I got so I can review brian's pr
BoisterousCoder Nov 8, 2022
a8024c2
ready for testing for a lot of it
BoisterousCoder Nov 9, 2022
3c06f28
first test done
BoisterousCoder Nov 15, 2022
bf7db06
fibonacci http retry wait
BoisterousCoder Nov 18, 2022
0999da9
try fail http fixed
BoisterousCoder Nov 25, 2022
262c6e7
refactor and more
BoisterousCoder Nov 29, 2022
d27be60
files upload to ipfs finnally
BoisterousCoder Nov 29, 2022
1660c8a
removing auto disconnect for now
BoisterousCoder Nov 29, 2022
5928dde
oops
BoisterousCoder Nov 29, 2022
6034492
testing add file
BoisterousCoder Nov 29, 2022
a00f9d9
finnishing add file
BoisterousCoder Nov 29, 2022
40c76ee
quick fix for file adding
BoisterousCoder Nov 29, 2022
69e43c6
remove warnings
BoisterousCoder Nov 29, 2022
d13c31b
fixing config
BoisterousCoder Dec 1, 2022
9c38cf3
rebase fix
BoisterousCoder Dec 2, 2022
cd728a5
Merge branch 'adding-ipfs-trait' of github.com:fission-codes/fission-…
BoisterousCoder Dec 2, 2022
0a06fc7
fix duplicate
BoisterousCoder Dec 2, 2022
5356210
started on cofig
BoisterousCoder Dec 5, 2022
ddf6387
Add error and formatting
bgins Dec 5, 2022
1cd9f55
re adding the disconnect from on drop
BoisterousCoder Dec 5, 2022
e3b0423
remove add bootstrap
BoisterousCoder Dec 5, 2022
0deb42c
Config templating
BoisterousCoder Dec 6, 2022
b604f5c
ready for refactor
BoisterousCoder Dec 6, 2022
c95cf61
Update import formatting
bgins Dec 6, 2022
1ec8e4f
Rename IPFS Daemon
bgins Dec 6, 2022
d16023b
Handle null values in config
BoisterousCoder Dec 6, 2022
1c963b5
Separate out daemon tests into a module
bgins Dec 6, 2022
719f91d
Move http module to utils
bgins Dec 6, 2022
c746cc0
Add whitespace
bgins Dec 6, 2022
97f91df
Starting to move to ipfs-api llib
BoisterousCoder Dec 15, 2022
098141d
config moved to lib
BoisterousCoder Dec 15, 2022
1548f7c
Graceful Shutdown
BoisterousCoder Dec 16, 2022
d48cc24
add bootstap start
BoisterousCoder Dec 17, 2022
e3a8750
Adding Documentation
BoisterousCoder Dec 17, 2022
5243302
connect to
BoisterousCoder Dec 29, 2022
0758c6b
switch to main fork for ipfs-api
BoisterousCoder Jan 3, 2023
b1b8126
Update src/ipfs.rs
Jan 4, 2023
88eea6c
Update src/ipfs.rs
Jan 4, 2023
09fae95
Update src/ipfs.rs
Jan 4, 2023
3f919c1
Update src/ipfs.rs
Jan 4, 2023
d3d5344
Update src/utils/config.rs
Jan 4, 2023
5c0bc5b
Update src/ipfs.rs
Jan 4, 2023
831647f
Accidently made changes to the wrong branch
Jan 11, 2023
e345e60
mostly copy edits
BoisterousCoder Jan 11, 2023
a3c934a
ignored doc tests
BoisterousCoder Jan 17, 2023
1a80100
Removing dead code
BoisterousCoder Jan 18, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,595 changes: 1,273 additions & 322 deletions Cargo.lock

Large diffs are not rendered by default.

26 changes: 20 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,23 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
anyhow = "1.0.66"
bs58 = "0.4.0"
base64 = "0.13.0"
clap = { version = "3.2.12", features = ["derive"] }
did-key = "0.1.1"
colored = "2.0.0"
anyhow = "1.0"
bs58 = "0.4"
base64 = "0.20"
clap = { version = "3.2", features = ["derive"] }
did-key = "0.2"
colored = "2.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
hyper = { version = "0.14", features = ["full"] }
bytes = "1.2"
tokio = { version = "1", features = ["full"] }
async-trait = "0.1"
futures = "0.3"
ipfs-api-backend-hyper = "0.6"
walkdir = "2.3"
graceful = "0.1"

[dev-dependencies]
proptest = "1.0"
serial_test = "0.9"
24 changes: 18 additions & 6 deletions src/cmd/generate.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use clap::{Args, Subcommand};
use did_key::{Ed25519KeyPair, KeyMaterial, DIDCore, Config};
use colored::Colorize;
use did_key::{Config, DIDCore, Ed25519KeyPair, KeyMaterial};

#[derive(Args)]
pub struct Generate {
Expand All @@ -18,10 +18,22 @@ pub fn run_command(g: Generate) {
GenerateCommands::Credentials => {
let keys = did_key::generate::<Ed25519KeyPair>(None);

println!("{}", "✅ Generated an Ed25519 key pair and associated DID".bright_green());
println!("🗝️ Private key: {}", base64::encode(keys.private_key_bytes().as_slice()).bright_blue());
println!("🔑 Public key: {}", base64::encode(keys.public_key_bytes().as_slice()).bright_blue());
println!("🆔 DID: {}", keys.get_did_document(Config::default()).id.bright_blue());
println!(
"{}",
"✅ Generated an Ed25519 key pair and associated DID".bright_green()
);
println!(
"🗝️ Private key: {}",
base64::encode(keys.private_key_bytes().as_slice()).bright_blue()
);
println!(
"🔑 Public key: {}",
base64::encode(keys.public_key_bytes().as_slice()).bright_blue()
);
println!(
"🆔 DID: {}",
keys.get_did_document(Config::default()).id.bright_blue()
);
}
}
}
}
42 changes: 42 additions & 0 deletions src/ipfs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use std::{collections::HashMap, path::Path};

use anyhow::Result;
use async_trait::async_trait;
use serde_json::Value;

pub mod daemon;
#[async_trait]
pub trait Ipfs {
/// This method uploads a file or directory at a given path to the IPFS swarm you are
/// currently connected to.
async fn add(&self, path:&Path) -> Result<HashMap<String, String>>;
/// This method connects to the given address, adding the address to the current swarm
async fn connect_to(&self, peer_id:&str) -> Result<()>;
/// This method returns a list of all the addresses that are currently connected
async fn get_connected(&self) -> Result<Vec<String>>;
/// This method changes the value of a given property in the IPFS config
///
/// ```no_run
/// use fission::ipfs::daemon::IpfsDaemon;
/// use fission::ipfs::Ipfs;
/// use futures::executor::block_on;
/// use serde_json::Value;
///
/// let ipfs = IpfsDaemon::default();
/// let config_value = Value::from("11GB");
/// block_on(ipfs.set_config("Datastore.StorageMax", &config_value)).unwrap();
/// ```
async fn set_config(&self, property:&str, val:&Value) -> Result<()>;

/// This method returns the value a property in the IPFS config
///
/// ```no_run
/// use fission::ipfs::daemon::IpfsDaemon;
/// use fission::ipfs::Ipfs;
/// use futures::executor::block_on;
///
/// let ipfs = IpfsDaemon::default();
/// let config_value = block_on(ipfs.get_config("Datastore.StorageMax")).unwrap();
/// ```
async fn get_config(&self, property: &str) -> Result<Value>;
}
236 changes: 236 additions & 0 deletions src/ipfs/daemon.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
use std::collections::HashMap;
use std::path::Path;
use std::process::Command;
use std::thread;
use std::time::{Duration, SystemTime};

use anyhow::{bail, Result};
use async_trait::async_trait;
use colored::Colorize;
use futures::executor::block_on;
use graceful::SignalGuard;
use ipfs_api_backend_hyper::{IpfsApi, IpfsClient, Logger, LoggingLevel, TryFromUri};
use serde_json::Value;
use tokio::runtime::Runtime;

use crate::ipfs::Ipfs;
use crate::utils::config::{
IPFS_ADDR, IPFS_API_PORT, IPFS_BOOT_TIME_OUT, IPFS_EXE, IPFS_SLEEP_LENGTH,
};

/// This struct is a wrapper for the information needed to point the IPFS daemon at a diffrent address
/// than the default.
#[derive(Clone, Debug)]
pub struct IpfsConnInfo {
pub address: String,
pub port: u16,
}

pub struct IpfsDaemon {
conn_info: IpfsConnInfo,
client: IpfsClient,
tokio: Runtime,
}

impl IpfsDaemon {
/// This method launches the IPFS daemon on the port/address given when the instance was
/// created. During this proccess, it also creates a thread that listens for a shutdown signal
/// and stops the IPFS daemon gracefully if the signal is given.
pub async fn launch(&self) -> Result<()> {
//launch the daemon
let api_addr = format!(
"/ip4/{}/tcp/{}",
self.conn_info.address, self.conn_info.port
);
println!("Launching IPFS...");
Command::new(IPFS_EXE)
.arg("--api")
.arg(&api_addr)
.arg("daemon")
.spawn()
.unwrap_or_else(|e| panic!("Failed to start IPFS daemon: {}\n This error may be because the Kubo binary is not on your PATH.", e));

// Wait ipfs to be ready
println!("Waiting for IPFS to ready..");
Comment thread
BoisterousCoder marked this conversation as resolved.
self.await_ready().await?;

// Reduce log level for IPFS
self.tokio.block_on(async {
self.client
.log_level(Logger::All, LoggingLevel::Error)
.await
})?;

// Setup graceful shutdown
println!("Creating graceful shutdown for IPFS...");
let me = self.clone();
thread::spawn(move || {
let signal_guard = SignalGuard::new();

signal_guard.at_exit(move |sig| {
Comment thread
BoisterousCoder marked this conversation as resolved.
println!("Signal {} received. Attempting to stop IPFS...", sig);
match me.shutdown() {
Ok(_) => println!("{}", "IPFS has shutdown successfully.".green()),
Err(_) => println!("{}", "IPFS failed to shutdown succefully! You may need to stop the proccess yourself.".red())
};
});
});

println!("{}", "IPFS has launched successfully!!".green());
Ok(())
}

/// This method sends an http signal to the IPFS deamon to shutdown.
///
/// Note 1: This method should work even if the daemon was not started by this proccess.
///
/// Note 2: This method returns when it recieves an http response from the daemon, not
/// when the proccess has actually stopped.
pub fn shutdown(&self) -> Result<()> {
self.tokio
.block_on(async { block_on(self.client.shutdown()) })?;
Ok(())
}

async fn is_ipfs_ready(&self) -> bool {
let res = self
.tokio
.block_on(async { self.client.config_show().await });
return match res {
Ok(_) => true,
Err(_) => false,
};
}

async fn await_ready(&self) -> Result<()> {
let start_time = SystemTime::now();
loop {
println!("{}", "Checking if ipfs is ready...".green());

if self.is_ipfs_ready().await {
println!("{}", "IPFS is ready!!".green());
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🕺✨

break;
}

thread::sleep(Duration::new(IPFS_SLEEP_LENGTH as u64, 0));

let now = SystemTime::now();
if now.duration_since(start_time)? > Duration::new(IPFS_BOOT_TIME_OUT as u64, 0) {
bail!(
"{}",
"Failed to start IPFS because the timeout reached!!".red()
)
}
}
anyhow::Ok(())
}
}

impl TryFrom<IpfsConnInfo> for IpfsDaemon {
type Error = anyhow::Error;
/// This is one of the two ways to make a new instance of the IPFS daemon struct. Use this
/// method when you want to make a new instance and you need to set the port and address of
/// the daemon you are attempting to reference
fn try_from(conn_info: IpfsConnInfo) -> Result<Self> {
let client =
IpfsClient::from_host_and_port("http".parse()?, &conn_info.address, conn_info.port)?;

let runtime = tokio::runtime::Runtime::new()?;
Ok(Self {
client,
tokio: runtime,
conn_info,
})
}
}

impl Default for IpfsDaemon {
/// This is one of the two ways to make a new instance of the IPFS daemon struct. Use this
/// method when you want to make a new instance and you okay with using the default port and address
/// set by the config.
///
/// Note: This method has the possiblity of throwing an error if the configuration of the port and
/// address is not setup right.
fn default() -> Self {
Self::try_from(IpfsConnInfo {
address: IPFS_ADDR.to_string(),
port: IPFS_API_PORT,
})
.unwrap()
}
}
impl Clone for IpfsDaemon {
fn clone(&self) -> Self {
return Self {
client: self.client.clone(),
tokio: Runtime::new().unwrap(),
conn_info: self.conn_info.clone(),
};
}
}

#[async_trait]
impl Ipfs for IpfsDaemon {
async fn add(&self, path: &Path) -> Result<HashMap<String, String>> {
let response_list = self
.tokio
.block_on(async { self.client.add_path(path).await })?;
return Ok(response_list
.into_iter()
.map(|res| (res.name, res.hash))
.collect());
}
async fn connect_to(&self, peer_id: &str) -> Result<()> {
let messages = self
.tokio
.block_on(async { self.client.swarm_connect(peer_id).await })?
.strings;
for msg in messages {
println!("{}", msg.blue());
if !msg.contains("success") {
bail!(msg);
}
}
return Ok(());
}
async fn get_connected(&self) -> Result<Vec<String>> {
let peers = self
.tokio
.block_on(async { self.client.swarm_peers().await })?;
Ok(peers.peers.into_iter().map(|peer| peer.addr).collect())
}
async fn get_config(&self, prop: &str) -> Result<Value> {
let config = self
.tokio
.block_on(async { self.client.config_get_json(prop).await })?;
return Ok(config.value);
}
async fn set_config(&self, prop: &str, val: &Value) -> Result<()> {
if val.is_boolean() {
self.tokio.block_on(async {
self.client
.config_set_bool(prop, val.as_bool().unwrap())
.await
})?;
return Ok(());
}
if val.is_string() {
self.tokio.block_on(async {
self.client
.config_set_string(prop, val.as_str().unwrap())
.await
})?;
return Ok(());
}
if val.is_number() || val.is_null() {
bail!(
"{}",
"The IPFS config API does not suport null or number json types"
)
}
self.tokio
.block_on(async { self.client.config_set_json(prop, &val.to_string()).await })?;

return Ok(());
}
}
4 changes: 4 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
pub mod cmd;
pub mod ipfs;
pub mod legacy;
pub mod utils;

#[cfg(test)]
pub mod test;
3 changes: 2 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ enum Commands {
remote: Option<String>,
},
}

fn main() {
let cli = Cli::parse();

Expand All @@ -70,7 +71,7 @@ fn main() {
keyfile,
os,
verbose,
remote
remote,
} => match run_setup_command(username, email, keyfile, os, verbose, remote) {
Ok(()) => (),
Err(_err) => eprintln!("💥 Failed to execute setup command."),
Expand Down
1 change: 1 addition & 0 deletions src/test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod daemon;
Loading