From e55720240fbe546bddd7a1db7bfb84dacdd6cfef Mon Sep 17 00:00:00 2001 From: Xabi Losada Date: Tue, 18 Jun 2024 13:50:54 +0200 Subject: [PATCH 01/18] test: replace branch --- .github/workflows/calimero_node_macos.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/calimero_node_macos.yml b/.github/workflows/calimero_node_macos.yml index 453d62611..34aa5fcdb 100644 --- a/.github/workflows/calimero_node_macos.yml +++ b/.github/workflows/calimero_node_macos.yml @@ -105,7 +105,8 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - if gh release view "v${{ github.event.inputs.version }}" >/dev/null 2>&1; then + VERSION=${{ needs.build.outputs.version }} + if gh release view "v$VERSION" >/dev/null 2>&1; then echo "release_exists=true" >> $GITHUB_ENV else echo "release_exists=false" >> $GITHUB_ENV From f7cb785157b8827b3b7c2f7f5bdbe9ce8b907c43 Mon Sep 17 00:00:00 2001 From: Xabi Losada Date: Tue, 18 Jun 2024 14:06:34 +0200 Subject: [PATCH 02/18] fix: conditionally run linux create_release --- .github/workflows/calimero_node_linux.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/calimero_node_linux.yml b/.github/workflows/calimero_node_linux.yml index be76117bf..ba53c4b14 100644 --- a/.github/workflows/calimero_node_linux.yml +++ b/.github/workflows/calimero_node_linux.yml @@ -128,6 +128,7 @@ jobs: create_release: runs-on: ubuntu-latest needs: build + if: ${{ github.ref == 'refs/heads/master' || (github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'master') }} outputs: release_exists: ${{ steps.check_release.outputs.release_exists }} version: ${{ needs.build.outputs.version }} From 6993fb64bb6d5d7f1d1d0bb4e0fc4cb9ed77abcf Mon Sep 17 00:00:00 2001 From: petarjuki7 Date: Tue, 11 Jun 2024 13:50:58 +0200 Subject: [PATCH 03/18] Cli crate init --- crates/cli/Cargo.toml | 12 ++++++++++++ crates/cli/src/lib.rs | 14 ++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 crates/cli/Cargo.toml create mode 100644 crates/cli/src/lib.rs diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml new file mode 100644 index 000000000..a301d3bba --- /dev/null +++ b/crates/cli/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "cli" +version = "0.1.0" +authors.workspace = true +edition.workspace = true +repository.workspace = true +license.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +clap = { workspace = true, features = ["env", "derive"] } diff --git a/crates/cli/src/lib.rs b/crates/cli/src/lib.rs new file mode 100644 index 000000000..7d12d9af8 --- /dev/null +++ b/crates/cli/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: usize, right: usize) -> usize { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} From f476fc29cbf2ede6c135bf74ae98fb04c467270e Mon Sep 17 00:00:00 2001 From: petarjuki7 Date: Tue, 11 Jun 2024 13:51:38 +0200 Subject: [PATCH 04/18] Cli crate init --- Cargo.lock | 7 +++++++ Cargo.toml | 1 + 2 files changed, 8 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 50d78660d..a59728b41 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1492,6 +1492,13 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +[[package]] +name = "cli" +version = "0.1.0" +dependencies = [ + "clap 4.5.4", +] + [[package]] name = "color-eyre" version = "0.6.3" diff --git a/Cargo.toml b/Cargo.toml index 0172531f7..62df77366 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ license = "MIT OR Apache-2.0" resolver = "2" members = [ "./crates/application", + "./crates/cli", "./crates/identity", "./crates/network", "./crates/node", From 44c4a15c09fef4b79c64ca323a8b31100ed678b4 Mon Sep 17 00:00:00 2001 From: petarjuki7 Date: Tue, 11 Jun 2024 15:10:07 +0200 Subject: [PATCH 05/18] git pull --- Cargo.lock | 45 ++++++++++ crates/cli/Cargo.toml | 1 + crates/cli/src/lib.rs | 14 --- crates/cli/src/main.rs | 195 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 241 insertions(+), 14 deletions(-) delete mode 100644 crates/cli/src/lib.rs create mode 100644 crates/cli/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index a59728b41..0b477da7c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1497,6 +1497,7 @@ name = "cli" version = "0.1.0" dependencies = [ "clap 4.5.4", + "git2", ] [[package]] @@ -2768,6 +2769,21 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +[[package]] +name = "git2" +version = "0.18.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "232e6a7bfe35766bf715e55a88b39a700596c0ccfd88cd3680b4cdb40d66ef70" +dependencies = [ + "bitflags 2.5.0", + "libc", + "libgit2-sys", + "log", + "openssl-probe", + "openssl-sys", + "url", +] + [[package]] name = "glob" version = "0.3.1" @@ -3612,6 +3628,20 @@ version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +[[package]] +name = "libgit2-sys" +version = "0.16.2+1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee4126d8b4ee5c9d9ea891dd875cfdc1e9d0950437179104b183d7d8a74d24e8" +dependencies = [ + "cc", + "libc", + "libssh2-sys", + "libz-sys", + "openssl-sys", + "pkg-config", +] + [[package]] name = "libloading" version = "0.7.4" @@ -4407,6 +4437,20 @@ dependencies = [ "libsecp256k1-core", ] +[[package]] +name = "libssh2-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dc8a030b787e2119a731f1951d6a773e2280c660f8ec4b0f5e1505a386e71ee" +dependencies = [ + "cc", + "libc", + "libz-sys", + "openssl-sys", + "pkg-config", + "vcpkg", +] + [[package]] name = "libz-sys" version = "1.1.16" @@ -4414,6 +4458,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e143b5e666b2695d28f6bca6497720813f699c9602dd7f5cac91008b8ada7f9" dependencies = [ "cc", + "libc", "pkg-config", "vcpkg", ] diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index a301d3bba..da02a1504 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -10,3 +10,4 @@ license.workspace = true [dependencies] clap = { workspace = true, features = ["env", "derive"] } +git2 = "0.18.3" diff --git a/crates/cli/src/lib.rs b/crates/cli/src/lib.rs deleted file mode 100644 index 7d12d9af8..000000000 --- a/crates/cli/src/lib.rs +++ /dev/null @@ -1,14 +0,0 @@ -pub fn add(left: usize, right: usize) -> usize { - left + right -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } -} diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs new file mode 100644 index 000000000..c3316a2a5 --- /dev/null +++ b/crates/cli/src/main.rs @@ -0,0 +1,195 @@ +use std::io::{self, Write}; +use std::str; + +use clap::Parser; +use git2::Repository; + +#[derive(Parser)] +struct Args { + arg_remote: Option, + arg_branch: Option, +} + +fn do_fetch<'a>( + repo: &'a git2::Repository, + refs: &[&str], + remote: &'a mut git2::Remote, +) -> Result, git2::Error> { + let mut cb = git2::RemoteCallbacks::new(); + + // Print out our transfer progress. + cb.transfer_progress(|stats| { + if stats.received_objects() == stats.total_objects() { + print!( + "Resolving deltas {}/{}\r", + stats.indexed_deltas(), + stats.total_deltas() + ); + } else if stats.total_objects() > 0 { + print!( + "Received {}/{} objects ({}) in {} bytes\r", + stats.received_objects(), + stats.total_objects(), + stats.indexed_objects(), + stats.received_bytes() + ); + } + io::stdout().flush().unwrap(); + true + }); + + let mut fo = git2::FetchOptions::new(); + fo.remote_callbacks(cb); + // Always fetch all tags. + // Perform a download and also update tips + fo.download_tags(git2::AutotagOption::All); + println!("Fetching {} for repo", remote.name().unwrap()); + remote.fetch(refs, Some(&mut fo), None)?; + + // If there are local objects (we got a thin pack), then tell the user + // how many objects we saved from having to cross the network. + let stats = remote.stats(); + if stats.local_objects() > 0 { + println!( + "\rReceived {}/{} objects in {} bytes (used {} local \ + objects)", + stats.indexed_objects(), + stats.total_objects(), + stats.received_bytes(), + stats.local_objects() + ); + } else { + println!( + "\rReceived {}/{} objects in {} bytes", + stats.indexed_objects(), + stats.total_objects(), + stats.received_bytes() + ); + } + + let fetch_head = repo.find_reference("FETCH_HEAD")?; + Ok(repo.reference_to_annotated_commit(&fetch_head)?) +} + +fn fast_forward( + repo: &Repository, + lb: &mut git2::Reference, + rc: &git2::AnnotatedCommit, +) -> Result<(), git2::Error> { + let name = match lb.name() { + Some(s) => s.to_string(), + None => String::from_utf8_lossy(lb.name_bytes()).to_string(), + }; + let msg = format!("Fast-Forward: Setting {} to id: {}", name, rc.id()); + println!("{}", msg); + lb.set_target(rc.id(), &msg)?; + repo.set_head(&name)?; + repo.checkout_head(Some( + git2::build::CheckoutBuilder::default() + // For some reason the force is required to make the working directory actually get updated + // I suspect we should be adding some logic to handle dirty working directory states + // but this is just an example so maybe not. + .force(), + ))?; + Ok(()) +} + +fn normal_merge( + repo: &Repository, + local: &git2::AnnotatedCommit, + remote: &git2::AnnotatedCommit, +) -> Result<(), git2::Error> { + let local_tree = repo.find_commit(local.id())?.tree()?; + let remote_tree = repo.find_commit(remote.id())?.tree()?; + let ancestor = repo + .find_commit(repo.merge_base(local.id(), remote.id())?)? + .tree()?; + let mut idx = repo.merge_trees(&ancestor, &local_tree, &remote_tree, None)?; + + if idx.has_conflicts() { + println!("Merge conflicts detected..."); + repo.checkout_index(Some(&mut idx), None)?; + return Ok(()); + } + let result_tree = repo.find_tree(idx.write_tree_to(repo)?)?; + // now create the merge commit + let msg = format!("Merge: {} into {}", remote.id(), local.id()); + let sig = repo.signature()?; + let local_commit = repo.find_commit(local.id())?; + let remote_commit = repo.find_commit(remote.id())?; + // Do our merge commit and set current branch head to that commit. + let _merge_commit = repo.commit( + Some("HEAD"), + &sig, + &sig, + &msg, + &result_tree, + &[&local_commit, &remote_commit], + )?; + // Set working tree to match head. + repo.checkout_head(None)?; + Ok(()) +} + +fn do_merge<'a>( + repo: &'a Repository, + remote_branch: &str, + fetch_commit: git2::AnnotatedCommit<'a>, +) -> Result<(), git2::Error> { + // 1. do a merge analysis + let analysis = repo.merge_analysis(&[&fetch_commit])?; + + // 2. Do the appropriate merge + if analysis.0.is_fast_forward() { + println!("Doing a fast forward"); + // do a fast forward + let refname = format!("refs/heads/{}", remote_branch); + match repo.find_reference(&refname) { + Ok(mut r) => { + fast_forward(repo, &mut r, &fetch_commit)?; + } + Err(_) => { + // The branch doesn't exist so just set the reference to the + // commit directly. Usually this is because you are pulling + // into an empty repository. + repo.reference( + &refname, + fetch_commit.id(), + true, + &format!("Setting {} to {}", remote_branch, fetch_commit.id()), + )?; + repo.set_head(&refname)?; + repo.checkout_head(Some( + git2::build::CheckoutBuilder::default() + .allow_conflicts(true) + .conflict_style_merge(true) + .force(), + ))?; + } + }; + } else if analysis.0.is_normal() { + // do a normal merge + let head_commit = repo.reference_to_annotated_commit(&repo.head()?)?; + normal_merge(&repo, &head_commit, &fetch_commit)?; + } else { + println!("Nothing to do..."); + } + Ok(()) +} + +fn run(args: &Args) -> Result<(), git2::Error> { + let remote_name = args.arg_remote.as_ref().map(|s| &s[..]).unwrap_or("origin"); + let remote_branch = args.arg_branch.as_ref().map(|s| &s[..]).unwrap_or("master"); + let repo = Repository::open(".")?; + let mut remote = repo.find_remote(remote_name)?; + let fetch_commit = do_fetch(&repo, &[remote_branch], &mut remote)?; + do_merge(&repo, &remote_branch, fetch_commit) +} + +fn main() { + let args = Args::parse(); + match run(&args) { + Ok(()) => {} + Err(e) => println!("error: {}", e), + } +} From f697e067a1969992e4b5dfa2cc467c43d6b54e40 Mon Sep 17 00:00:00 2001 From: petarjuki7 Date: Thu, 13 Jun 2024 13:42:03 +0200 Subject: [PATCH 06/18] Finished init --- crates/cli/src/cli.rs | 38 +++++++++ crates/cli/src/cli/init.rs | 81 ++++++++++++++++++ crates/cli/src/cli/setup.rs | 166 ++++++++++++++++++++++++++++++++++++ 3 files changed, 285 insertions(+) create mode 100644 crates/cli/src/cli.rs create mode 100644 crates/cli/src/cli/init.rs create mode 100644 crates/cli/src/cli/setup.rs diff --git a/crates/cli/src/cli.rs b/crates/cli/src/cli.rs new file mode 100644 index 000000000..650f6f51a --- /dev/null +++ b/crates/cli/src/cli.rs @@ -0,0 +1,38 @@ +use calimero_node::config; +use clap::{Parser, Subcommand}; + +mod init; +mod setup; + +#[derive(Debug, Parser)] +#[command(author, about, version)] +pub struct RootCommand { + #[command(flatten)] + pub args: RootArgs, + + #[command(subcommand)] + pub action: SubCommands, +} + +#[derive(Debug, Subcommand)] +pub enum SubCommands { + Init(init::InitCommand), + Config(setup::SetupCommand), +} + +#[derive(Debug, Parser)] +pub struct RootArgs { + /// Directory for config and data + #[arg(long, value_name = "PATH", default_value_t = config::default_chat_dir())] + #[arg(env = "CALIMERO_HOME", hide_env_values = true)] + pub home: camino::Utf8PathBuf, +} + +impl RootCommand { + pub async fn run(self) -> eyre::Result<()> { + match self.action { + SubCommands::Init(init) => return init.run(self.args), + SubCommands::Config(setup) => return setup.run(self.args), + } + } +} diff --git a/crates/cli/src/cli/init.rs b/crates/cli/src/cli/init.rs new file mode 100644 index 000000000..f81556ee3 --- /dev/null +++ b/crates/cli/src/cli/init.rs @@ -0,0 +1,81 @@ +use std::fs; + +use calimero_node::config::ConfigFile; +use clap::Parser; +use eyre::WrapErr; +use libp2p::identity; +use tracing::{info, warn}; + +use crate::cli; + +/// Initialize node configuration +#[derive(Debug, Parser)] + +pub struct InitCommand { + /// Name of node + #[arg(short, long, value_name = "NAME")] + pub node_name: camino::Utf8PathBuf, + + /// Force initialization even if the directory already exists + #[clap(short, long)] + pub force: bool, +} + +impl InitCommand { + pub fn run(self, root_args: cli::RootArgs) -> eyre::Result<()> { + let path = root_args.home.join(&self.node_name); + // tu dodati neki if il nes + fs::create_dir_all(&path) + .wrap_err_with(|| format!("failed to create directory {:?}", &path))?; + + if ConfigFile::exists(&path) { + match ConfigFile::load(&path) { + Ok(config) => { + if self.force { + warn!( + "Overriding config.toml file for {}, keeping identity", + self.node_name + ); + let config_new = ConfigFile { + identity: config.identity, + network: None, + store: None, + application: None, + }; + config_new.save(&path)?; + return Ok(()); + } else { + eyre::bail!( + "Node {} is already initialized in {:?}", + self.node_name, + path + ); + } + } + Err(err) => { + if !self.force { + eyre::bail!("failed to load existing configuration: {}", err); + } + warn!( + "Failed to load existing configuration, overwriting: {}", + err + ); + } + } + } + let identity = identity::Keypair::generate_ed25519(); + info!("Generated identity: {:?}", identity.public().to_peer_id()); + + let config = ConfigFile { + identity: identity.clone(), + network: None, + store: None, + application: None, + }; + + config.save(&path)?; + + println!("{:?}", path); + Ok(()) + } +} diff --git a/crates/cli/src/cli/setup.rs b/crates/cli/src/cli/setup.rs new file mode 100644 index 000000000..b141d4e01 --- /dev/null +++ b/crates/cli/src/cli/setup.rs @@ -0,0 +1,166 @@ +use std::fs; +use std::net::IpAddr; + +use calimero_network::config::{BootstrapConfig, BootstrapNodes, DiscoveryConfig, SwarmConfig}; +use calimero_node::config::{self, ApplicationConfig, ConfigFile, NetworkConfig, StoreConfig}; +use clap::{Parser, ValueEnum}; +use eyre::WrapErr; +use libp2p::identity; +use multiaddr::Multiaddr; +use tracing::{info, warn}; + +use crate::cli; + +/// Initialize node configuration +#[derive(Debug, Parser)] +pub struct SetupCommand { + /// List of bootstrap nodes + #[clap(long, value_name = "ADDR")] + pub boot_nodes: Vec, + + /// Use nodes from a known network + #[clap(long, value_name = "NETWORK", default_value = "calimero-dev")] + pub boot_network: Option, + + /// Host to listen on + #[clap(long, value_name = "HOST")] + #[clap(default_value = "0.0.0.0,::")] + #[clap(use_value_delimiter = true)] + pub swarm_host: Vec, + + /// Port to listen on + #[clap(long, value_name = "PORT")] + #[clap(default_value_t = calimero_network::config::DEFAULT_PORT)] + pub swarm_port: u16, + + /// Host to listen on for RPC + #[clap(long, value_name = "HOST")] + #[clap(default_value = "127.0.0.1,::1")] + #[clap(use_value_delimiter = true)] + pub server_host: Vec, + + /// Port to listen on for RPC + #[clap(long, value_name = "PORT")] + #[clap(default_value_t = calimero_server::config::DEFAULT_PORT)] + pub server_port: u16, + + /// Enable mDNS discovery + #[clap(long, default_value_t = true)] + #[clap(overrides_with("no_mdns"))] + pub mdns: bool, + + #[clap(long, hide = true)] + #[clap(overrides_with("mdns"))] + pub no_mdns: bool, + + /// Force initialization even if the directory already exists + #[clap(long)] + pub force: bool, +} + +#[derive(Clone, Debug, ValueEnum)] +pub enum BootstrapNetwork { + CalimeroDev, + Ipfs, +} + +impl SetupCommand { + pub fn run(self, root_args: cli::RootArgs) -> eyre::Result<()> { + let mdns = self.mdns && !self.no_mdns; + + if !root_args.home.exists() { + if root_args.home == config::default_chat_dir() { + fs::create_dir_all(&root_args.home) + } else { + fs::create_dir(&root_args.home) + } + .wrap_err_with(|| format!("failed to create directory {:?}", root_args.home))?; + } + + if ConfigFile::exists(&root_args.home) { + if let Err(err) = ConfigFile::load(&root_args.home) { + if self.force { + warn!( + "Failed to load existing configuration, overwriting: {}", + err + ); + } else { + eyre::bail!("failed to load existing configuration: {}", err); + } + } + if !self.force { + eyre::bail!("chat node is already initialized in {:?}", root_args.home); + } + } + + let identity = identity::Keypair::generate_ed25519(); + info!("Generated identity: {:?}", identity.public().to_peer_id()); + + let mut listen: Vec = vec![]; + + for host in self.swarm_host { + let host = format!( + "/{}/{}", + match host { + std::net::IpAddr::V4(_) => "ip4", + std::net::IpAddr::V6(_) => "ip6", + }, + host, + ); + listen.push(format!("{}/tcp/{}", host, self.swarm_port).parse()?); + listen.push(format!("{}/udp/{}/quic-v1", host, self.swarm_port).parse()?); + } + + let mut boot_nodes = self.boot_nodes; + if let Some(network) = self.boot_network { + match network { + BootstrapNetwork::CalimeroDev => { + boot_nodes.extend(BootstrapNodes::calimero_dev().list) + } + BootstrapNetwork::Ipfs => boot_nodes.extend(BootstrapNodes::ipfs().list), + } + } + + let config = ConfigFile { + identity: identity.clone(), + store: Some(StoreConfig { + path: "data".into(), + }), + application: Some(ApplicationConfig { + path: "apps".into(), + }), + network: Some(NetworkConfig { + swarm: SwarmConfig { listen }, + bootstrap: BootstrapConfig { + nodes: BootstrapNodes { list: boot_nodes }, + }, + discovery: DiscoveryConfig { + mdns, + rendezvous: Default::default(), + }, + server: calimero_node::config::ServerConfig { + listen: self + .server_host + .into_iter() + .map(|host| { + Multiaddr::from(host).with(multiaddr::Protocol::Tcp(self.server_port)) + }) + .collect(), + admin: Some(calimero_server::admin::service::AdminConfig { enabled: true }), + jsonrpc: Some(calimero_server::jsonrpc::JsonRpcConfig { enabled: true }), + websocket: Some(calimero_server::ws::WsConfig { enabled: true }), + }, + }), + }; + + config.save(&root_args.home)?; + + calimero_store::Store::open(&calimero_store::config::StoreConfig { + path: root_args.home.join(config.store.unwrap().path), // I'm gonna make sure that it is provided + })?; + + info!("Initialized a chat node in {:?}", root_args.home); + + Ok(()) + } +} From 9626f1562fec73c3645b3b08e35730d63603b669 Mon Sep 17 00:00:00 2001 From: petarjuki7 Date: Thu, 13 Jun 2024 13:42:28 +0200 Subject: [PATCH 07/18] Testing changes --- Cargo.lock | 68 ++++-------- crates/cli/Cargo.toml | 25 ++++- crates/cli/src/main.rs | 206 ++++-------------------------------- crates/node/src/cli/init.rs | 16 +-- crates/node/src/config.rs | 10 +- 5 files changed, 80 insertions(+), 245 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0b477da7c..ea5c36502 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1496,8 +1496,30 @@ checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" name = "cli" version = "0.1.0" dependencies = [ + "calimero-application", + "calimero-identity", + "calimero-network", + "calimero-node", + "calimero-node-primitives", + "calimero-primitives", + "calimero-runtime", + "calimero-server", + "calimero-store", + "camino", "clap 4.5.4", - "git2", + "color-eyre", + "dirs", + "eyre", + "libp2p", + "multiaddr", + "owo-colors", + "semver", + "serde", + "serde_json", + "tokio", + "toml 0.8.12", + "tracing", + "tracing-subscriber", ] [[package]] @@ -2769,21 +2791,6 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" -[[package]] -name = "git2" -version = "0.18.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "232e6a7bfe35766bf715e55a88b39a700596c0ccfd88cd3680b4cdb40d66ef70" -dependencies = [ - "bitflags 2.5.0", - "libc", - "libgit2-sys", - "log", - "openssl-probe", - "openssl-sys", - "url", -] - [[package]] name = "glob" version = "0.3.1" @@ -3628,20 +3635,6 @@ version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" -[[package]] -name = "libgit2-sys" -version = "0.16.2+1.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee4126d8b4ee5c9d9ea891dd875cfdc1e9d0950437179104b183d7d8a74d24e8" -dependencies = [ - "cc", - "libc", - "libssh2-sys", - "libz-sys", - "openssl-sys", - "pkg-config", -] - [[package]] name = "libloading" version = "0.7.4" @@ -4437,20 +4430,6 @@ dependencies = [ "libsecp256k1-core", ] -[[package]] -name = "libssh2-sys" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dc8a030b787e2119a731f1951d6a773e2280c660f8ec4b0f5e1505a386e71ee" -dependencies = [ - "cc", - "libc", - "libz-sys", - "openssl-sys", - "pkg-config", - "vcpkg", -] - [[package]] name = "libz-sys" version = "1.1.16" @@ -4458,7 +4437,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e143b5e666b2695d28f6bca6497720813f699c9602dd7f5cac91008b8ada7f9" dependencies = [ "cc", - "libc", "pkg-config", "vcpkg", ] diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index da02a1504..6d8c91f3a 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -9,5 +9,28 @@ license.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +camino = { workspace = true, features = ["serde1"] } clap = { workspace = true, features = ["env", "derive"] } -git2 = "0.18.3" +tracing-subscriber = { workspace = true, features = ["env-filter"] } +color-eyre.workspace = true +libp2p.workspace = true +dirs.workspace = true +eyre.workspace = true +tokio = { workspace = true, features = ["io-std", "macros"] } +multiaddr.workspace = true +owo-colors.workspace = true +semver.workspace = true +serde = { workspace = true, features = ["derive"] } +serde_json.workspace = true +toml.workspace = true +tracing.workspace = true + +calimero-node = { path = "../node" } +calimero-network = { path = "../network" } +calimero-application = { path = "../application" } +calimero-identity = { path = "../identity" } +calimero-node-primitives = { path = "../node-primitives" } +calimero-primitives = { path = "../primitives" } +calimero-runtime = { path = "../runtime" } +calimero-server = { path = "../server", features = ["jsonrpc", "websocket", "admin"] } +calimero-store = { path = "../store" } diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index c3316a2a5..5cf6c72ce 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -1,195 +1,27 @@ -use std::io::{self, Write}; -use std::str; - use clap::Parser; -use git2::Repository; - -#[derive(Parser)] -struct Args { - arg_remote: Option, - arg_branch: Option, -} - -fn do_fetch<'a>( - repo: &'a git2::Repository, - refs: &[&str], - remote: &'a mut git2::Remote, -) -> Result, git2::Error> { - let mut cb = git2::RemoteCallbacks::new(); - - // Print out our transfer progress. - cb.transfer_progress(|stats| { - if stats.received_objects() == stats.total_objects() { - print!( - "Resolving deltas {}/{}\r", - stats.indexed_deltas(), - stats.total_deltas() - ); - } else if stats.total_objects() > 0 { - print!( - "Received {}/{} objects ({}) in {} bytes\r", - stats.received_objects(), - stats.total_objects(), - stats.indexed_objects(), - stats.received_bytes() - ); - } - io::stdout().flush().unwrap(); - true - }); - - let mut fo = git2::FetchOptions::new(); - fo.remote_callbacks(cb); - // Always fetch all tags. - // Perform a download and also update tips - fo.download_tags(git2::AutotagOption::All); - println!("Fetching {} for repo", remote.name().unwrap()); - remote.fetch(refs, Some(&mut fo), None)?; - - // If there are local objects (we got a thin pack), then tell the user - // how many objects we saved from having to cross the network. - let stats = remote.stats(); - if stats.local_objects() > 0 { - println!( - "\rReceived {}/{} objects in {} bytes (used {} local \ - objects)", - stats.indexed_objects(), - stats.total_objects(), - stats.received_bytes(), - stats.local_objects() - ); - } else { - println!( - "\rReceived {}/{} objects in {} bytes", - stats.indexed_objects(), - stats.total_objects(), - stats.received_bytes() - ); - } +use tracing_subscriber::prelude::*; +use tracing_subscriber::EnvFilter; - let fetch_head = repo.find_reference("FETCH_HEAD")?; - Ok(repo.reference_to_annotated_commit(&fetch_head)?) -} - -fn fast_forward( - repo: &Repository, - lb: &mut git2::Reference, - rc: &git2::AnnotatedCommit, -) -> Result<(), git2::Error> { - let name = match lb.name() { - Some(s) => s.to_string(), - None => String::from_utf8_lossy(lb.name_bytes()).to_string(), - }; - let msg = format!("Fast-Forward: Setting {} to id: {}", name, rc.id()); - println!("{}", msg); - lb.set_target(rc.id(), &msg)?; - repo.set_head(&name)?; - repo.checkout_head(Some( - git2::build::CheckoutBuilder::default() - // For some reason the force is required to make the working directory actually get updated - // I suspect we should be adding some logic to handle dirty working directory states - // but this is just an example so maybe not. - .force(), - ))?; - Ok(()) -} +mod cli; -fn normal_merge( - repo: &Repository, - local: &git2::AnnotatedCommit, - remote: &git2::AnnotatedCommit, -) -> Result<(), git2::Error> { - let local_tree = repo.find_commit(local.id())?.tree()?; - let remote_tree = repo.find_commit(remote.id())?.tree()?; - let ancestor = repo - .find_commit(repo.merge_base(local.id(), remote.id())?)? - .tree()?; - let mut idx = repo.merge_trees(&ancestor, &local_tree, &remote_tree, None)?; +#[tokio::main] +async fn main() -> eyre::Result<()> { + setup()?; - if idx.has_conflicts() { - println!("Merge conflicts detected..."); - repo.checkout_index(Some(&mut idx), None)?; - return Ok(()); - } - let result_tree = repo.find_tree(idx.write_tree_to(repo)?)?; - // now create the merge commit - let msg = format!("Merge: {} into {}", remote.id(), local.id()); - let sig = repo.signature()?; - let local_commit = repo.find_commit(local.id())?; - let remote_commit = repo.find_commit(remote.id())?; - // Do our merge commit and set current branch head to that commit. - let _merge_commit = repo.commit( - Some("HEAD"), - &sig, - &sig, - &msg, - &result_tree, - &[&local_commit, &remote_commit], - )?; - // Set working tree to match head. - repo.checkout_head(None)?; - Ok(()) -} - -fn do_merge<'a>( - repo: &'a Repository, - remote_branch: &str, - fetch_commit: git2::AnnotatedCommit<'a>, -) -> Result<(), git2::Error> { - // 1. do a merge analysis - let analysis = repo.merge_analysis(&[&fetch_commit])?; - - // 2. Do the appropriate merge - if analysis.0.is_fast_forward() { - println!("Doing a fast forward"); - // do a fast forward - let refname = format!("refs/heads/{}", remote_branch); - match repo.find_reference(&refname) { - Ok(mut r) => { - fast_forward(repo, &mut r, &fetch_commit)?; - } - Err(_) => { - // The branch doesn't exist so just set the reference to the - // commit directly. Usually this is because you are pulling - // into an empty repository. - repo.reference( - &refname, - fetch_commit.id(), - true, - &format!("Setting {} to {}", remote_branch, fetch_commit.id()), - )?; - repo.set_head(&refname)?; - repo.checkout_head(Some( - git2::build::CheckoutBuilder::default() - .allow_conflicts(true) - .conflict_style_merge(true) - .force(), - ))?; - } - }; - } else if analysis.0.is_normal() { - // do a normal merge - let head_commit = repo.reference_to_annotated_commit(&repo.head()?)?; - normal_merge(&repo, &head_commit, &fetch_commit)?; - } else { - println!("Nothing to do..."); - } - Ok(()) -} + let command = cli::RootCommand::parse(); -fn run(args: &Args) -> Result<(), git2::Error> { - let remote_name = args.arg_remote.as_ref().map(|s| &s[..]).unwrap_or("origin"); - let remote_branch = args.arg_branch.as_ref().map(|s| &s[..]).unwrap_or("master"); - let repo = Repository::open(".")?; - let mut remote = repo.find_remote(remote_name)?; - let fetch_commit = do_fetch(&repo, &[remote_branch], &mut remote)?; - do_merge(&repo, &remote_branch, fetch_commit) + command.run().await } -fn main() { - let args = Args::parse(); - match run(&args) { - Ok(()) => {} - Err(e) => println!("error: {}", e), - } +fn setup() -> eyre::Result<()> { + tracing_subscriber::registry() + .with(EnvFilter::builder().parse(format!( + "info,{}", + // "debug,libp2p_core=warn,libp2p_gossipsub=warn,{}", + std::env::var("RUST_LOG").unwrap_or_default() + ))?) + .with(tracing_subscriber::fmt::layer()) + .init(); + + color_eyre::install() } diff --git a/crates/node/src/cli/init.rs b/crates/node/src/cli/init.rs index 0bd837891..fa0d9ce99 100644 --- a/crates/node/src/cli/init.rs +++ b/crates/node/src/cli/init.rs @@ -125,15 +125,17 @@ impl InitCommand { } } + println!("{:?}", boot_nodes); + let config = ConfigFile { identity: identity.clone(), - store: StoreConfig { + store: Some(StoreConfig { path: "data".into(), - }, - application: ApplicationConfig { + }), + application: Some(ApplicationConfig { path: "apps".into(), - }, - network: NetworkConfig { + }), + network: Some(NetworkConfig { swarm: SwarmConfig { listen }, bootstrap: BootstrapConfig { nodes: BootstrapNodes { list: boot_nodes }, @@ -154,13 +156,13 @@ impl InitCommand { jsonrpc: Some(calimero_server::jsonrpc::JsonRpcConfig { enabled: true }), websocket: Some(calimero_server::ws::WsConfig { enabled: true }), }, - }, + }), }; config.save(&root_args.home)?; calimero_store::Store::open(&calimero_store::config::StoreConfig { - path: root_args.home.join(config.store.path), + path: root_args.home.join(config.store.unwrap().path), })?; info!("Initialized a chat node in {:?}", root_args.home); diff --git a/crates/node/src/config.rs b/crates/node/src/config.rs index 851eca039..c733af8f8 100644 --- a/crates/node/src/config.rs +++ b/crates/node/src/config.rs @@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize}; const CONFIG_FILE: &str = "config.toml"; -pub const DEFAULT_CALIMERO_CHAT_HOME: &str = ".calimero/experiments/chat-p0c"; +pub(crate) const DEFAULT_CALIMERO_HOME: &str = "Documents/core/data"; //ovo nece biti ovako, ne smije, treba nekako naci path #[derive(Debug, Serialize, Deserialize)] pub struct ConfigFile { @@ -17,11 +17,11 @@ pub struct ConfigFile { pub identity: identity::Keypair, #[serde(flatten)] - pub network: NetworkConfig, + pub network: Option, - pub store: StoreConfig, + pub store: Option, - pub application: ApplicationConfig, + pub application: Option, } #[derive(Debug, Serialize, Deserialize)] @@ -96,7 +96,7 @@ impl ConfigFile { pub fn default_chat_dir() -> camino::Utf8PathBuf { if let Some(home) = dirs::home_dir() { let home = camino::Utf8Path::from_path(&home).expect("invalid home directory"); - return home.join(DEFAULT_CALIMERO_CHAT_HOME); + return home.join(DEFAULT_CALIMERO_HOME); } Default::default() From 4a97340350a7f9f99adf741a071c303eb6f128fa Mon Sep 17 00:00:00 2001 From: petarjuki7 Date: Mon, 17 Jun 2024 11:11:14 +0200 Subject: [PATCH 08/18] Added config file --- crates/cli/src/cli.rs | 5 +- crates/cli/src/cli/init.rs | 42 ++++--- crates/cli/src/cli/setup.rs | 234 ++++++++++++++++++++++++------------ crates/cli/src/config.rs | 122 +++++++++++++++++++ crates/cli/src/main.rs | 2 +- 5 files changed, 305 insertions(+), 100 deletions(-) create mode 100644 crates/cli/src/config.rs diff --git a/crates/cli/src/cli.rs b/crates/cli/src/cli.rs index 650f6f51a..36809d5a5 100644 --- a/crates/cli/src/cli.rs +++ b/crates/cli/src/cli.rs @@ -17,7 +17,7 @@ pub struct RootCommand { #[derive(Debug, Subcommand)] pub enum SubCommands { Init(init::InitCommand), - Config(setup::SetupCommand), + Setup(setup::SetupCommand), } #[derive(Debug, Parser)] @@ -30,9 +30,10 @@ pub struct RootArgs { impl RootCommand { pub async fn run(self) -> eyre::Result<()> { + let c = RootCommand::parse(); match self.action { SubCommands::Init(init) => return init.run(self.args), - SubCommands::Config(setup) => return setup.run(self.args), + SubCommands::Setup(setup) => return setup.run(self.args), } } } diff --git a/crates/cli/src/cli/init.rs b/crates/cli/src/cli/init.rs index f81556ee3..7cabb91c4 100644 --- a/crates/cli/src/cli/init.rs +++ b/crates/cli/src/cli/init.rs @@ -1,16 +1,17 @@ use std::fs; -use calimero_node::config::ConfigFile; +//use calimero_node::config::{ConfigFile, ConfigImpl, InitFile}; use clap::Parser; use eyre::WrapErr; use libp2p::identity; +use serde::{Deserialize, Serialize}; use tracing::{info, warn}; use crate::cli; +use crate::cli::config::{ConfigFile, ConfigImpl, InitFile}; /// Initialize node configuration #[derive(Debug, Parser)] - pub struct InitCommand { /// Name of node #[arg(short, long, value_name = "NAME")] @@ -28,7 +29,7 @@ impl InitCommand { fs::create_dir_all(&path) .wrap_err_with(|| format!("failed to create directory {:?}", &path))?; - if ConfigFile::exists(&path) { + if InitFile::exists(&path) { match ConfigFile::load(&path) { Ok(config) => { if self.force { @@ -36,11 +37,8 @@ impl InitCommand { "Overriding config.toml file for {}, keeping identity", self.node_name ); - let config_new = ConfigFile { + let config_new = InitFile { identity: config.identity, - network: None, - store: None, - application: None, }; config_new.save(&path)?; return Ok(()); @@ -52,25 +50,31 @@ impl InitCommand { ); } } - Err(err) => { - if !self.force { - eyre::bail!("failed to load existing configuration: {}", err); + Err(err) => match InitFile::load(&path) { + Ok(config) => { + if self.force { + eyre::bail!( + "Node {} is already initialized in {:?}\nCan not override node identity", + self.node_name, + path + ); + } else { + eyre::bail!( + "Node {} is already initialized in {:?}", + self.node_name, + path + ); + } } - warn!( - "Failed to load existing configuration, overwriting: {}", - err - ); - } + Err(err) => eyre::bail!("failed to load existing configuration: {}", err), + }, } } let identity = identity::Keypair::generate_ed25519(); info!("Generated identity: {:?}", identity.public().to_peer_id()); - let config = ConfigFile { + let config = InitFile { identity: identity.clone(), - network: None, - store: None, - application: None, }; config.save(&path)?; diff --git a/crates/cli/src/cli/setup.rs b/crates/cli/src/cli/setup.rs index b141d4e01..f50a9a4b9 100644 --- a/crates/cli/src/cli/setup.rs +++ b/crates/cli/src/cli/setup.rs @@ -2,9 +2,14 @@ use std::fs; use std::net::IpAddr; use calimero_network::config::{BootstrapConfig, BootstrapNodes, DiscoveryConfig, SwarmConfig}; -use calimero_node::config::{self, ApplicationConfig, ConfigFile, NetworkConfig, StoreConfig}; -use clap::{Parser, ValueEnum}; -use eyre::WrapErr; +use calimero_node::config::{ + self, ApplicationConfig, ConfigFile, ConfigImpl, InitFile, NetworkConfig, StoreConfig, +}; +use calimero_runtime::Constraint; +use camino::Utf8PathBuf; +use clap::parser::ValueSource; +use clap::{ArgMatches, Command, CommandFactory, Parser, ValueEnum}; +use eyre::eyre; use libp2p::identity; use multiaddr::Multiaddr; use tracing::{info, warn}; @@ -14,47 +19,53 @@ use crate::cli; /// Initialize node configuration #[derive(Debug, Parser)] pub struct SetupCommand { + /// Name of node + #[arg(short, long, value_name = "NAME")] + pub node_name: Utf8PathBuf, + /// List of bootstrap nodes - #[clap(long, value_name = "ADDR")] + #[arg(long, value_name = "ADDR")] pub boot_nodes: Vec, /// Use nodes from a known network - #[clap(long, value_name = "NETWORK", default_value = "calimero-dev")] + #[arg(long, value_name = "NETWORK", default_value = "calimero-dev")] pub boot_network: Option, /// Host to listen on - #[clap(long, value_name = "HOST")] - #[clap(default_value = "0.0.0.0,::")] - #[clap(use_value_delimiter = true)] + #[arg( + long, + value_name = "HOST", + default_value = "0.0.0.0,::", + use_value_delimiter = true + )] pub swarm_host: Vec, /// Port to listen on - #[clap(long, value_name = "PORT")] - #[clap(default_value_t = calimero_network::config::DEFAULT_PORT)] + #[arg(long, value_name = "PORT", default_value_t = calimero_network::config::DEFAULT_PORT)] pub swarm_port: u16, /// Host to listen on for RPC - #[clap(long, value_name = "HOST")] - #[clap(default_value = "127.0.0.1,::1")] - #[clap(use_value_delimiter = true)] + #[arg( + long, + value_name = "HOST", + default_value = "127.0.0.1,::1", + use_value_delimiter = true + )] pub server_host: Vec, /// Port to listen on for RPC - #[clap(long, value_name = "PORT")] - #[clap(default_value_t = calimero_server::config::DEFAULT_PORT)] + #[arg(long, value_name = "PORT", default_value_t = calimero_server::config::DEFAULT_PORT)] pub server_port: u16, /// Enable mDNS discovery - #[clap(long, default_value_t = true)] - #[clap(overrides_with("no_mdns"))] + #[arg(long, default_value_t = true, overrides_with("no_mdns"))] pub mdns: bool, - #[clap(long, hide = true)] - #[clap(overrides_with("mdns"))] + #[arg(long, hide = true, overrides_with("mdns"))] pub no_mdns: bool, - /// Force initialization even if the directory already exists - #[clap(long)] + /// Force edit even if the argument already exists + #[arg(long, short)] pub force: bool, } @@ -65,71 +76,127 @@ pub enum BootstrapNetwork { } impl SetupCommand { - pub fn run(self, root_args: cli::RootArgs) -> eyre::Result<()> { - let mdns = self.mdns && !self.no_mdns; - - if !root_args.home.exists() { - if root_args.home == config::default_chat_dir() { - fs::create_dir_all(&root_args.home) + pub fn run(mut self, root_args: cli::RootArgs) -> eyre::Result<()> { + let path = root_args.home.join(&self.node_name); + + let matches = cli::RootCommand::command().get_matches(); // get matches for value_source + + // Extract subcommand matches for SetupCommand + let setup_matches = matches.subcommand_matches("setup").unwrap(); + + /// Znaci + /// Ako se ucitao config, onda mijenjamo + /// Ako se nije ucitao config, onda samo guras ovo sta je dano i ostalo default + /// + /// Ako se ucitao: + /// ako nema -f force, otpili ga + /// ako ima, provjeri samo za svaki dal je zastavica tu: + /// ako je, daj novi + /// ako nije, daj mu stari + /// + /// SPREMI + let boot_nodes_provided = check_if_provided(&setup_matches, "boot_nodes"); + let boot_network_provided = check_if_provided(&setup_matches, "boot_network"); + let swarm_host_provided = check_if_provided(&setup_matches, "swarm_host"); + let swarm_port_provided = check_if_provided(&setup_matches, "swarm_port"); + let server_host_provided = check_if_provided(&setup_matches, "server_host"); + let server_port_provided = check_if_provided(&setup_matches, "server_port"); + let mdns_provided = check_if_provided(&setup_matches, "mdns"); + let no_mdns_provided = check_if_provided(&setup_matches, "no_mdns"); + + // You can now use these boolean variables to check if the fields were provided + println!("boot_nodes provided: {}", boot_nodes_provided); + println!("swarm_host provided: {}", swarm_host_provided); + println!("swarm_port provided: {}", swarm_port_provided); + println!("server_host provided: {}", server_host_provided); + println!("server_port provided: {}", server_port_provided); + println!("mdns provided: {}", mdns_provided); + println!("no_mdns provided: {}", no_mdns_provided); + + let identity; + let mut swarm_listen = None; + let mut server_listen = None; + + if InitFile::exists(&path) { + if let Ok(config_identity) = InitFile::load(&path) { + identity = config_identity.identity; } else { - fs::create_dir(&root_args.home) + eyre::bail!("Failed to open identity for node \nRun command node init -n "); } - .wrap_err_with(|| format!("failed to create directory {:?}", root_args.home))?; - } - - if ConfigFile::exists(&root_args.home) { - if let Err(err) = ConfigFile::load(&root_args.home) { - if self.force { - warn!( - "Failed to load existing configuration, overwriting: {}", - err + if let Ok(config) = ConfigFile::load(&path) { + if !self.force { + eyre::bail!( + "The config for the node is already initialized \nYou can override a setting by adding the -f flag" ); } else { - eyre::bail!("failed to load existing configuration: {}", err); + if !boot_network_provided { + self.boot_nodes = config.network.bootstrap.nodes.list; + } + if !swarm_host_provided && !swarm_port_provided { + swarm_listen = Some(config.network.swarm.listen); + } + if !server_host_provided && !server_port_provided { + server_listen = Some(config.network.server.listen); + } + if !mdns_provided { + self.mdns = config.network.discovery.mdns; + } } } - if !self.force { - eyre::bail!("chat node is already initialized in {:?}", root_args.home); - } + } else { + eyre::bail!("You have to initialize the node first \nRun command node init -n "); } - let identity = identity::Keypair::generate_ed25519(); - info!("Generated identity: {:?}", identity.public().to_peer_id()); + //sada ako je postavljeno, i ides promijenit port + //promijenit ce se port ali sa default hostom + //al to je ok...? + //jedino ako ne das ni jedno ni drugo, onda ostaje isto + + let mdns = self.mdns && !self.no_mdns; let mut listen: Vec = vec![]; - for host in self.swarm_host { - let host = format!( - "/{}/{}", - match host { - std::net::IpAddr::V4(_) => "ip4", - std::net::IpAddr::V6(_) => "ip6", - }, - host, - ); - listen.push(format!("{}/tcp/{}", host, self.swarm_port).parse()?); - listen.push(format!("{}/udp/{}/quic-v1", host, self.swarm_port).parse()?); + match swarm_listen { + Some(data) => listen.extend(data), + None => { + for host in self.swarm_host { + let host = format!( + "/{}/{}", + match host { + std::net::IpAddr::V4(_) => "ip4", + std::net::IpAddr::V6(_) => "ip6", + }, + host, + ); + listen.push(format!("{}/tcp/{}", host, self.swarm_port).parse()?); + listen.push(format!("{}/udp/{}/quic-v1", host, self.swarm_port).parse()?); + } + } } - let mut boot_nodes = self.boot_nodes; - if let Some(network) = self.boot_network { - match network { - BootstrapNetwork::CalimeroDev => { - boot_nodes.extend(BootstrapNodes::calimero_dev().list) + let mut boot_nodes = vec![]; + if boot_network_provided { + if let Some(network) = self.boot_network { + match network { + BootstrapNetwork::CalimeroDev => { + boot_nodes.extend(BootstrapNodes::calimero_dev().list) + } + BootstrapNetwork::Ipfs => boot_nodes.extend(BootstrapNodes::ipfs().list), } - BootstrapNetwork::Ipfs => boot_nodes.extend(BootstrapNodes::ipfs().list), } + } else { + boot_nodes = self.boot_nodes; } - let config = ConfigFile { - identity: identity.clone(), - store: Some(StoreConfig { + let config_new = ConfigFile { + identity, + store: StoreConfig { path: "data".into(), - }), - application: Some(ApplicationConfig { + }, + application: ApplicationConfig { path: "apps".into(), - }), - network: Some(NetworkConfig { + }, + network: NetworkConfig { swarm: SwarmConfig { listen }, bootstrap: BootstrapConfig { nodes: BootstrapNodes { list: boot_nodes }, @@ -139,28 +206,39 @@ impl SetupCommand { rendezvous: Default::default(), }, server: calimero_node::config::ServerConfig { - listen: self - .server_host - .into_iter() - .map(|host| { - Multiaddr::from(host).with(multiaddr::Protocol::Tcp(self.server_port)) - }) - .collect(), + listen: match server_listen { + Some(data) => data, + None => self + .server_host + .into_iter() + .map(|host| { + Multiaddr::from(host) + .with(multiaddr::Protocol::Tcp(self.server_port)) + }) + .collect(), + }, admin: Some(calimero_server::admin::service::AdminConfig { enabled: true }), jsonrpc: Some(calimero_server::jsonrpc::JsonRpcConfig { enabled: true }), websocket: Some(calimero_server::ws::WsConfig { enabled: true }), }, - }), + }, }; - config.save(&root_args.home)?; + config_new.save(&path)?; calimero_store::Store::open(&calimero_store::config::StoreConfig { - path: root_args.home.join(config.store.unwrap().path), // I'm gonna make sure that it is provided + path: path.join(config_new.store.path), })?; - info!("Initialized a chat node in {:?}", root_args.home); + info!("Initialized confing for a node in {:?}", root_args.home); Ok(()) } } + +fn check_if_provided(matches: &ArgMatches, arg_name: &str) -> bool { + if let Some(ValueSource::CommandLine) = matches.value_source(arg_name) { + return true; + } + false +} diff --git a/crates/cli/src/config.rs b/crates/cli/src/config.rs new file mode 100644 index 000000000..2e6f318e2 --- /dev/null +++ b/crates/cli/src/config.rs @@ -0,0 +1,122 @@ +use std::fs; + +use eyre::WrapErr; +use libp2p::{identity, Multiaddr}; +use serde::{Deserialize, Serialize}; + +const CONFIG_FILE: &str = "config.toml"; + +pub(crate) const DEFAULT_CALIMERO_HOME: &str = "Documents/core/data"; //ovo nece biti ovako, ne smije, treba nekako naci path + +#[derive(Debug, Serialize, Deserialize)] +pub struct ConfigFile { + #[serde( + with = "calimero_primitives::identity::serde_identity", + default = "identity::Keypair::generate_ed25519" + )] + pub identity: identity::Keypair, + + #[serde(flatten)] + pub network: NetworkConfig, + + pub store: StoreConfig, + + pub application: ApplicationConfig, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct NetworkConfig { + pub swarm: calimero_network::config::SwarmConfig, + + pub server: ServerConfig, + + #[serde(default)] + pub bootstrap: calimero_network::config::BootstrapConfig, + + #[serde(default)] + pub discovery: calimero_network::config::DiscoveryConfig, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct ServerConfig { + pub listen: Vec, + + #[serde(default)] + pub admin: Option, + + #[serde(default)] + pub jsonrpc: Option, + + #[serde(default)] + pub websocket: Option, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct StoreConfig { + pub path: camino::Utf8PathBuf, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct InitFile { + #[serde( + with = "calimero_primitives::identity::serde_identity", + default = "identity::Keypair::generate_ed25519" + )] + pub identity: identity::Keypair, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct ApplicationConfig { + pub path: camino::Utf8PathBuf, +} + +pub trait ConfigImpl { + fn exists(dir: &camino::Utf8Path) -> bool { + dir.join(CONFIG_FILE).is_file() + } + + fn load(dir: &camino::Utf8Path) -> eyre::Result + where + Self: Sized, + for<'de> Self: Deserialize<'de>, + { + let path = dir.join(CONFIG_FILE); + let content = fs::read_to_string(&path).wrap_err_with(|| { + format!( + "failed to read configuration from {:?}", + dir.join(CONFIG_FILE) + ) + })?; + + toml::from_str(&content).map_err(Into::into) + } + + fn save(&self, dir: &camino::Utf8Path) -> eyre::Result<()> + where + Self: Serialize, + { + let path = dir.join(CONFIG_FILE); + let content = toml::to_string_pretty(self)?; + + fs::write(&path, content).wrap_err_with(|| { + format!( + "failed to write configuration to {:?}", + dir.join(CONFIG_FILE) + ) + })?; + + Ok(()) + } +} + +impl ConfigImpl for ConfigFile {} +impl ConfigImpl for InitFile {} + +pub fn default_chat_dir() -> camino::Utf8PathBuf { + if let Some(home) = dirs::home_dir() { + let home = camino::Utf8Path::from_path(&home).expect("invalid home directory"); + return home.join(DEFAULT_CALIMERO_HOME); + } + + Default::default() +} diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 5cf6c72ce..5d9ebb32f 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -1,4 +1,4 @@ -use clap::Parser; +use clap::{CommandFactory, Parser}; use tracing_subscriber::prelude::*; use tracing_subscriber::EnvFilter; From 9225c7aa725e8833cda27b0433d7d7bb9f375ae6 Mon Sep 17 00:00:00 2001 From: petarjuki7 Date: Mon, 17 Jun 2024 12:42:12 +0200 Subject: [PATCH 09/18] Deleted old cli from node --- Cargo.lock | 5 - crates/cli/Cargo.toml | 5 - crates/cli/src/cli.rs | 9 +- crates/cli/src/cli/init.rs | 10 +- crates/{node => cli}/src/cli/run.rs | 19 +-- crates/cli/src/cli/setup.rs | 41 +------ crates/cli/src/config.rs | 2 +- crates/cli/src/main.rs | 6 +- crates/node/src/cli.rs | 38 ------ crates/node/src/cli/init.rs | 172 ---------------------------- crates/node/src/config.rs | 103 ----------------- crates/node/src/lib.rs | 1 - crates/node/src/main.rs | 27 ----- 13 files changed, 34 insertions(+), 404 deletions(-) rename crates/{node => cli}/src/cli/run.rs (80%) delete mode 100644 crates/node/src/cli.rs delete mode 100644 crates/node/src/cli/init.rs delete mode 100644 crates/node/src/config.rs delete mode 100644 crates/node/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index ea5c36502..f4fd66c24 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1497,12 +1497,10 @@ name = "cli" version = "0.1.0" dependencies = [ "calimero-application", - "calimero-identity", "calimero-network", "calimero-node", "calimero-node-primitives", "calimero-primitives", - "calimero-runtime", "calimero-server", "calimero-store", "camino", @@ -1512,10 +1510,7 @@ dependencies = [ "eyre", "libp2p", "multiaddr", - "owo-colors", - "semver", "serde", - "serde_json", "tokio", "toml 0.8.12", "tracing", diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 6d8c91f3a..737a184f2 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -18,19 +18,14 @@ dirs.workspace = true eyre.workspace = true tokio = { workspace = true, features = ["io-std", "macros"] } multiaddr.workspace = true -owo-colors.workspace = true -semver.workspace = true serde = { workspace = true, features = ["derive"] } -serde_json.workspace = true toml.workspace = true tracing.workspace = true calimero-node = { path = "../node" } calimero-network = { path = "../network" } calimero-application = { path = "../application" } -calimero-identity = { path = "../identity" } calimero-node-primitives = { path = "../node-primitives" } calimero-primitives = { path = "../primitives" } -calimero-runtime = { path = "../runtime" } calimero-server = { path = "../server", features = ["jsonrpc", "websocket", "admin"] } calimero-store = { path = "../store" } diff --git a/crates/cli/src/cli.rs b/crates/cli/src/cli.rs index 36809d5a5..828aaabea 100644 --- a/crates/cli/src/cli.rs +++ b/crates/cli/src/cli.rs @@ -1,9 +1,10 @@ -use calimero_node::config; use clap::{Parser, Subcommand}; +use crate::config; + mod init; +mod run; mod setup; - #[derive(Debug, Parser)] #[command(author, about, version)] pub struct RootCommand { @@ -18,6 +19,7 @@ pub struct RootCommand { pub enum SubCommands { Init(init::InitCommand), Setup(setup::SetupCommand), + Run(run::RunCommand), } #[derive(Debug, Parser)] @@ -30,10 +32,11 @@ pub struct RootArgs { impl RootCommand { pub async fn run(self) -> eyre::Result<()> { - let c = RootCommand::parse(); + let _c = RootCommand::parse(); match self.action { SubCommands::Init(init) => return init.run(self.args), SubCommands::Setup(setup) => return setup.run(self.args), + SubCommands::Run(run) => return run.run(self.args).await, } } } diff --git a/crates/cli/src/cli/init.rs b/crates/cli/src/cli/init.rs index 7cabb91c4..d9abc784d 100644 --- a/crates/cli/src/cli/init.rs +++ b/crates/cli/src/cli/init.rs @@ -1,14 +1,12 @@ use std::fs; -//use calimero_node::config::{ConfigFile, ConfigImpl, InitFile}; use clap::Parser; use eyre::WrapErr; use libp2p::identity; -use serde::{Deserialize, Serialize}; use tracing::{info, warn}; use crate::cli; -use crate::cli::config::{ConfigFile, ConfigImpl, InitFile}; +use crate::config::{ConfigFile, ConfigImpl, InitFile}; /// Initialize node configuration #[derive(Debug, Parser)] @@ -25,7 +23,7 @@ pub struct InitCommand { impl InitCommand { pub fn run(self, root_args: cli::RootArgs) -> eyre::Result<()> { let path = root_args.home.join(&self.node_name); - // tu dodati neki if il nes + fs::create_dir_all(&path) .wrap_err_with(|| format!("failed to create directory {:?}", &path))?; @@ -50,8 +48,8 @@ impl InitCommand { ); } } - Err(err) => match InitFile::load(&path) { - Ok(config) => { + Err(_err) => match InitFile::load(&path) { + Ok(_config) => { if self.force { eyre::bail!( "Node {} is already initialized in {:?}\nCan not override node identity", diff --git a/crates/node/src/cli/run.rs b/crates/cli/src/cli/run.rs similarity index 80% rename from crates/node/src/cli/run.rs rename to crates/cli/src/cli/run.rs index b14818672..d3953ebcd 100644 --- a/crates/node/src/cli/run.rs +++ b/crates/cli/src/cli/run.rs @@ -1,11 +1,15 @@ -use calimero_node::config::ConfigFile; use clap::{Parser, ValueEnum}; use crate::cli; +use crate::config::{ConfigFile, ConfigImpl}; /// Run a node #[derive(Debug, Parser)] pub struct RunCommand { + /// Name of node + #[arg(short, long, value_name = "NAME")] + pub node_name: camino::Utf8PathBuf, + #[clap(long, value_name = "TYPE")] #[clap(value_enum, default_value_t)] pub node_type: NodeType, @@ -29,21 +33,22 @@ impl From for calimero_node_primitives::NodeType { impl RunCommand { pub async fn run(self, root_args: cli::RootArgs) -> eyre::Result<()> { - if !ConfigFile::exists(&root_args.home) { - eyre::bail!("chat node is not initialized in {:?}", root_args.home); + let path = root_args.home.join(self.node_name); + if !ConfigFile::exists(&path) { + eyre::bail!("chat node is not initialized in {:?}", path); } - let config = ConfigFile::load(&root_args.home)?; + let config = ConfigFile::load(&path)?; calimero_node::start(calimero_node::NodeConfig { - home: root_args.home.clone(), + home: path.clone(), node_type: self.node_type.into(), identity: config.identity.clone(), store: calimero_store::config::StoreConfig { - path: root_args.home.join(config.store.path), + path: path.join(config.store.path), }, application: calimero_application::config::ApplicationConfig { - dir: root_args.home.join(config.application.path), + dir: path.join(config.application.path), }, network: calimero_network::config::NetworkConfig { identity: config.identity.clone(), diff --git a/crates/cli/src/cli/setup.rs b/crates/cli/src/cli/setup.rs index f50a9a4b9..6e7f373ed 100644 --- a/crates/cli/src/cli/setup.rs +++ b/crates/cli/src/cli/setup.rs @@ -1,20 +1,16 @@ -use std::fs; use std::net::IpAddr; use calimero_network::config::{BootstrapConfig, BootstrapNodes, DiscoveryConfig, SwarmConfig}; -use calimero_node::config::{ - self, ApplicationConfig, ConfigFile, ConfigImpl, InitFile, NetworkConfig, StoreConfig, -}; -use calimero_runtime::Constraint; use camino::Utf8PathBuf; use clap::parser::ValueSource; -use clap::{ArgMatches, Command, CommandFactory, Parser, ValueEnum}; -use eyre::eyre; -use libp2p::identity; +use clap::{ArgMatches, CommandFactory, Parser, ValueEnum}; use multiaddr::Multiaddr; -use tracing::{info, warn}; +use tracing::info; use crate::cli; +use crate::config::{ + ApplicationConfig, ConfigFile, ConfigImpl, InitFile, NetworkConfig, ServerConfig, StoreConfig, +}; /// Initialize node configuration #[derive(Debug, Parser)] @@ -84,17 +80,6 @@ impl SetupCommand { // Extract subcommand matches for SetupCommand let setup_matches = matches.subcommand_matches("setup").unwrap(); - /// Znaci - /// Ako se ucitao config, onda mijenjamo - /// Ako se nije ucitao config, onda samo guras ovo sta je dano i ostalo default - /// - /// Ako se ucitao: - /// ako nema -f force, otpili ga - /// ako ima, provjeri samo za svaki dal je zastavica tu: - /// ako je, daj novi - /// ako nije, daj mu stari - /// - /// SPREMI let boot_nodes_provided = check_if_provided(&setup_matches, "boot_nodes"); let boot_network_provided = check_if_provided(&setup_matches, "boot_network"); let swarm_host_provided = check_if_provided(&setup_matches, "swarm_host"); @@ -104,15 +89,6 @@ impl SetupCommand { let mdns_provided = check_if_provided(&setup_matches, "mdns"); let no_mdns_provided = check_if_provided(&setup_matches, "no_mdns"); - // You can now use these boolean variables to check if the fields were provided - println!("boot_nodes provided: {}", boot_nodes_provided); - println!("swarm_host provided: {}", swarm_host_provided); - println!("swarm_port provided: {}", swarm_port_provided); - println!("server_host provided: {}", server_host_provided); - println!("server_port provided: {}", server_port_provided); - println!("mdns provided: {}", mdns_provided); - println!("no_mdns provided: {}", no_mdns_provided); - let identity; let mut swarm_listen = None; let mut server_listen = None; @@ -147,11 +123,6 @@ impl SetupCommand { eyre::bail!("You have to initialize the node first \nRun command node init -n "); } - //sada ako je postavljeno, i ides promijenit port - //promijenit ce se port ali sa default hostom - //al to je ok...? - //jedino ako ne das ni jedno ni drugo, onda ostaje isto - let mdns = self.mdns && !self.no_mdns; let mut listen: Vec = vec![]; @@ -205,7 +176,7 @@ impl SetupCommand { mdns, rendezvous: Default::default(), }, - server: calimero_node::config::ServerConfig { + server: ServerConfig { listen: match server_listen { Some(data) => data, None => self diff --git a/crates/cli/src/config.rs b/crates/cli/src/config.rs index 2e6f318e2..e36a7d61a 100644 --- a/crates/cli/src/config.rs +++ b/crates/cli/src/config.rs @@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize}; const CONFIG_FILE: &str = "config.toml"; -pub(crate) const DEFAULT_CALIMERO_HOME: &str = "Documents/core/data"; //ovo nece biti ovako, ne smije, treba nekako naci path +pub(crate) const DEFAULT_CALIMERO_HOME: &str = "Documents/core/data"; //this has to be changed, find path to /core #[derive(Debug, Serialize, Deserialize)] pub struct ConfigFile { diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 5d9ebb32f..f86eac179 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -1,8 +1,12 @@ -use clap::{CommandFactory, Parser}; +#![warn(unused_extern_crates)] +#![deny(unused_crate_dependencies)] + +use clap::Parser; use tracing_subscriber::prelude::*; use tracing_subscriber::EnvFilter; mod cli; +mod config; #[tokio::main] async fn main() -> eyre::Result<()> { diff --git a/crates/node/src/cli.rs b/crates/node/src/cli.rs deleted file mode 100644 index 6becb0e0f..000000000 --- a/crates/node/src/cli.rs +++ /dev/null @@ -1,38 +0,0 @@ -use calimero_node::config; -use clap::{Parser, Subcommand}; - -mod init; -mod run; - -#[derive(Debug, Parser)] -#[clap(author, about, version)] -pub struct RootCommand { - #[clap(flatten)] - pub args: RootArgs, - - #[clap(subcommand)] - pub action: SubCommands, -} - -#[derive(Debug, Subcommand)] -pub enum SubCommands { - Init(init::InitCommand), - Run(run::RunCommand), -} - -#[derive(Debug, Parser)] -pub struct RootArgs { - /// Directory for config and data - #[clap(long, value_name = "PATH", default_value_t = config::default_chat_dir())] - #[clap(env = "CALIMERO_CHAT_HOME", hide_env_values = true)] - pub home: camino::Utf8PathBuf, -} - -impl RootCommand { - pub async fn run(self) -> eyre::Result<()> { - match self.action { - SubCommands::Init(init) => return init.run(self.args), - SubCommands::Run(run) => return run.run(self.args).await, - } - } -} diff --git a/crates/node/src/cli/init.rs b/crates/node/src/cli/init.rs deleted file mode 100644 index fa0d9ce99..000000000 --- a/crates/node/src/cli/init.rs +++ /dev/null @@ -1,172 +0,0 @@ -use std::fs; -use std::net::IpAddr; - -use calimero_network::config::{BootstrapConfig, BootstrapNodes, DiscoveryConfig, SwarmConfig}; -use calimero_node::config::{self, ApplicationConfig, ConfigFile, NetworkConfig, StoreConfig}; -use clap::{Parser, ValueEnum}; -use eyre::WrapErr; -use libp2p::identity; -use multiaddr::Multiaddr; -use tracing::{info, warn}; - -use crate::cli; - -/// Initialize node configuration -#[derive(Debug, Parser)] -// todo! simplify this, by splitting the steps -// todo! $ calimero node init -// todo! $ calimero node config 'swarm.listen:=["", ""]' discovery.mdns:=false -// todo! $ calimero node config discovery.mdns -pub struct InitCommand { - /// List of bootstrap nodes - #[clap(long, value_name = "ADDR")] - pub boot_nodes: Vec, - - /// Use nodes from a known network - #[clap(long, value_name = "NETWORK", default_value = "calimero-dev")] - pub boot_network: Option, - - /// Host to listen on - #[clap(long, value_name = "HOST")] - #[clap(default_value = "0.0.0.0,::")] - #[clap(use_value_delimiter = true)] - pub swarm_host: Vec, - - /// Port to listen on - #[clap(long, value_name = "PORT")] - #[clap(default_value_t = calimero_network::config::DEFAULT_PORT)] - pub swarm_port: u16, - - /// Host to listen on for RPC - #[clap(long, value_name = "HOST")] - #[clap(default_value = "127.0.0.1,::1")] - #[clap(use_value_delimiter = true)] - pub server_host: Vec, - - /// Port to listen on for RPC - #[clap(long, value_name = "PORT")] - #[clap(default_value_t = calimero_server::config::DEFAULT_PORT)] - pub server_port: u16, - - /// Enable mDNS discovery - #[clap(long, default_value_t = true)] - #[clap(overrides_with("no_mdns"))] - pub mdns: bool, - - #[clap(long, hide = true)] - #[clap(overrides_with("mdns"))] - pub no_mdns: bool, - - /// Force initialization even if the directory already exists - #[clap(long)] - pub force: bool, -} - -#[derive(Clone, Debug, ValueEnum)] -pub enum BootstrapNetwork { - CalimeroDev, - Ipfs, -} - -impl InitCommand { - pub fn run(self, root_args: cli::RootArgs) -> eyre::Result<()> { - let mdns = self.mdns && !self.no_mdns; - - if !root_args.home.exists() { - if root_args.home == config::default_chat_dir() { - fs::create_dir_all(&root_args.home) - } else { - fs::create_dir(&root_args.home) - } - .wrap_err_with(|| format!("failed to create directory {:?}", root_args.home))?; - } - - if ConfigFile::exists(&root_args.home) { - if let Err(err) = ConfigFile::load(&root_args.home) { - if self.force { - warn!( - "Failed to load existing configuration, overwriting: {}", - err - ); - } else { - eyre::bail!("failed to load existing configuration: {}", err); - } - } - if !self.force { - eyre::bail!("chat node is already initialized in {:?}", root_args.home); - } - } - - let identity = identity::Keypair::generate_ed25519(); - info!("Generated identity: {:?}", identity.public().to_peer_id()); - - let mut listen: Vec = vec![]; - - for host in self.swarm_host { - let host = format!( - "/{}/{}", - match host { - std::net::IpAddr::V4(_) => "ip4", - std::net::IpAddr::V6(_) => "ip6", - }, - host, - ); - listen.push(format!("{}/tcp/{}", host, self.swarm_port).parse()?); - listen.push(format!("{}/udp/{}/quic-v1", host, self.swarm_port).parse()?); - } - - let mut boot_nodes = self.boot_nodes; - if let Some(network) = self.boot_network { - match network { - BootstrapNetwork::CalimeroDev => { - boot_nodes.extend(BootstrapNodes::calimero_dev().list) - } - BootstrapNetwork::Ipfs => boot_nodes.extend(BootstrapNodes::ipfs().list), - } - } - - println!("{:?}", boot_nodes); - - let config = ConfigFile { - identity: identity.clone(), - store: Some(StoreConfig { - path: "data".into(), - }), - application: Some(ApplicationConfig { - path: "apps".into(), - }), - network: Some(NetworkConfig { - swarm: SwarmConfig { listen }, - bootstrap: BootstrapConfig { - nodes: BootstrapNodes { list: boot_nodes }, - }, - discovery: DiscoveryConfig { - mdns, - rendezvous: Default::default(), - }, - server: calimero_node::config::ServerConfig { - listen: self - .server_host - .into_iter() - .map(|host| { - Multiaddr::from(host).with(multiaddr::Protocol::Tcp(self.server_port)) - }) - .collect(), - admin: Some(calimero_server::admin::service::AdminConfig { enabled: true }), - jsonrpc: Some(calimero_server::jsonrpc::JsonRpcConfig { enabled: true }), - websocket: Some(calimero_server::ws::WsConfig { enabled: true }), - }, - }), - }; - - config.save(&root_args.home)?; - - calimero_store::Store::open(&calimero_store::config::StoreConfig { - path: root_args.home.join(config.store.unwrap().path), - })?; - - info!("Initialized a chat node in {:?}", root_args.home); - - Ok(()) - } -} diff --git a/crates/node/src/config.rs b/crates/node/src/config.rs deleted file mode 100644 index c733af8f8..000000000 --- a/crates/node/src/config.rs +++ /dev/null @@ -1,103 +0,0 @@ -use std::fs; - -use eyre::WrapErr; -use libp2p::{identity, Multiaddr}; -use serde::{Deserialize, Serialize}; - -const CONFIG_FILE: &str = "config.toml"; - -pub(crate) const DEFAULT_CALIMERO_HOME: &str = "Documents/core/data"; //ovo nece biti ovako, ne smije, treba nekako naci path - -#[derive(Debug, Serialize, Deserialize)] -pub struct ConfigFile { - #[serde( - with = "calimero_primitives::identity::serde_identity", - default = "identity::Keypair::generate_ed25519" - )] - pub identity: identity::Keypair, - - #[serde(flatten)] - pub network: Option, - - pub store: Option, - - pub application: Option, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct NetworkConfig { - pub swarm: calimero_network::config::SwarmConfig, - - pub server: ServerConfig, - - #[serde(default)] - pub bootstrap: calimero_network::config::BootstrapConfig, - - #[serde(default)] - pub discovery: calimero_network::config::DiscoveryConfig, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct ServerConfig { - pub listen: Vec, - - #[serde(default)] - pub admin: Option, - - #[serde(default)] - pub jsonrpc: Option, - - #[serde(default)] - pub websocket: Option, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct StoreConfig { - pub path: camino::Utf8PathBuf, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct ApplicationConfig { - pub path: camino::Utf8PathBuf, -} - -impl ConfigFile { - pub fn exists(dir: &camino::Utf8Path) -> bool { - dir.join(CONFIG_FILE).is_file() - } - - pub fn load(dir: &camino::Utf8Path) -> eyre::Result { - let path = dir.join(CONFIG_FILE); - let content = fs::read_to_string(&path).wrap_err_with(|| { - format!( - "failed to read configuration from {:?}", - dir.join(CONFIG_FILE) - ) - })?; - - toml::from_str(&content).map_err(Into::into) - } - - pub fn save(&self, dir: &camino::Utf8Path) -> eyre::Result<()> { - let path = dir.join(CONFIG_FILE); - let content = toml::to_string_pretty(self)?; - - fs::write(&path, content).wrap_err_with(|| { - format!( - "failed to write configuration to {:?}", - dir.join(CONFIG_FILE) - ) - })?; - - Ok(()) - } -} - -pub fn default_chat_dir() -> camino::Utf8PathBuf { - if let Some(home) = dirs::home_dir() { - let home = camino::Utf8Path::from_path(&home).expect("invalid home directory"); - return home.join(DEFAULT_CALIMERO_HOME); - } - - Default::default() -} diff --git a/crates/node/src/lib.rs b/crates/node/src/lib.rs index eb380f682..16e973be7 100644 --- a/crates/node/src/lib.rs +++ b/crates/node/src/lib.rs @@ -9,7 +9,6 @@ use tokio::io::AsyncBufReadExt; use tokio::sync::{broadcast, mpsc, oneshot}; use tracing::{error, info}; -pub mod config; pub mod temporal_runtime_store; pub mod transaction_pool; pub mod types; diff --git a/crates/node/src/main.rs b/crates/node/src/main.rs deleted file mode 100644 index 5cf6c72ce..000000000 --- a/crates/node/src/main.rs +++ /dev/null @@ -1,27 +0,0 @@ -use clap::Parser; -use tracing_subscriber::prelude::*; -use tracing_subscriber::EnvFilter; - -mod cli; - -#[tokio::main] -async fn main() -> eyre::Result<()> { - setup()?; - - let command = cli::RootCommand::parse(); - - command.run().await -} - -fn setup() -> eyre::Result<()> { - tracing_subscriber::registry() - .with(EnvFilter::builder().parse(format!( - "info,{}", - // "debug,libp2p_core=warn,libp2p_gossipsub=warn,{}", - std::env::var("RUST_LOG").unwrap_or_default() - ))?) - .with(tracing_subscriber::fmt::layer()) - .init(); - - color_eyre::install() -} From cc158c4c6c819f115c597b58d3ba1d866ee2c309 Mon Sep 17 00:00:00 2001 From: petarjuki7 Date: Mon, 17 Jun 2024 14:46:26 +0200 Subject: [PATCH 10/18] Added print of setup --- crates/cli/src/cli/init.rs | 2 +- crates/cli/src/cli/setup.rs | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/crates/cli/src/cli/init.rs b/crates/cli/src/cli/init.rs index d9abc784d..3797c914c 100644 --- a/crates/cli/src/cli/init.rs +++ b/crates/cli/src/cli/init.rs @@ -8,7 +8,7 @@ use tracing::{info, warn}; use crate::cli; use crate::config::{ConfigFile, ConfigImpl, InitFile}; -/// Initialize node configuration +/// Initialize node and it's identity #[derive(Debug, Parser)] pub struct InitCommand { /// Name of node diff --git a/crates/cli/src/cli/setup.rs b/crates/cli/src/cli/setup.rs index 6e7f373ed..4c7e90c8d 100644 --- a/crates/cli/src/cli/setup.rs +++ b/crates/cli/src/cli/setup.rs @@ -63,6 +63,10 @@ pub struct SetupCommand { /// Force edit even if the argument already exists #[arg(long, short)] pub force: bool, + + /// Print the config file + #[arg(long, short)] + pub print: bool, } #[derive(Clone, Debug, ValueEnum)] @@ -80,14 +84,12 @@ impl SetupCommand { // Extract subcommand matches for SetupCommand let setup_matches = matches.subcommand_matches("setup").unwrap(); - let boot_nodes_provided = check_if_provided(&setup_matches, "boot_nodes"); let boot_network_provided = check_if_provided(&setup_matches, "boot_network"); let swarm_host_provided = check_if_provided(&setup_matches, "swarm_host"); let swarm_port_provided = check_if_provided(&setup_matches, "swarm_port"); let server_host_provided = check_if_provided(&setup_matches, "server_host"); let server_port_provided = check_if_provided(&setup_matches, "server_port"); let mdns_provided = check_if_provided(&setup_matches, "mdns"); - let no_mdns_provided = check_if_provided(&setup_matches, "no_mdns"); let identity; let mut swarm_listen = None; @@ -100,6 +102,10 @@ impl SetupCommand { eyre::bail!("Failed to open identity for node \nRun command node init -n "); } if let Ok(config) = ConfigFile::load(&path) { + if self.print { + println!("{}", toml::to_string_pretty(&config)?); + return Ok(()); + } if !self.force { eyre::bail!( "The config for the node is already initialized \nYou can override a setting by adding the -f flag" From b18a737866f0bc070cedc62308ade9f8eb5d5833 Mon Sep 17 00:00:00 2001 From: petarjuki7 Date: Mon, 17 Jun 2024 15:59:23 +0200 Subject: [PATCH 11/18] Remove warnings --- crates/cli/src/main.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index f86eac179..fd4eb03bb 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -1,6 +1,3 @@ -#![warn(unused_extern_crates)] -#![deny(unused_crate_dependencies)] - use clap::Parser; use tracing_subscriber::prelude::*; use tracing_subscriber::EnvFilter; From 734d5a915197f09336a94d8e5c604de603bf706d Mon Sep 17 00:00:00 2001 From: petarjuki7 Date: Mon, 17 Jun 2024 18:13:47 +0200 Subject: [PATCH 12/18] Logic fix --- crates/cli/src/cli/setup.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/cli/src/cli/setup.rs b/crates/cli/src/cli/setup.rs index 4c7e90c8d..500b104e1 100644 --- a/crates/cli/src/cli/setup.rs +++ b/crates/cli/src/cli/setup.rs @@ -84,7 +84,7 @@ impl SetupCommand { // Extract subcommand matches for SetupCommand let setup_matches = matches.subcommand_matches("setup").unwrap(); - let boot_network_provided = check_if_provided(&setup_matches, "boot_network"); + let mut boot_network_provided = check_if_provided(&setup_matches, "boot_network"); let swarm_host_provided = check_if_provided(&setup_matches, "swarm_host"); let swarm_port_provided = check_if_provided(&setup_matches, "swarm_port"); let server_host_provided = check_if_provided(&setup_matches, "server_host"); @@ -124,6 +124,8 @@ impl SetupCommand { self.mdns = config.network.discovery.mdns; } } + } else { + boot_network_provided = true; } } else { eyre::bail!("You have to initialize the node first \nRun command node init -n "); From b4211534baa34e59c2d89dda301dad08b359dcb1 Mon Sep 17 00:00:00 2001 From: petarjuki7 Date: Mon, 17 Jun 2024 22:18:39 +0200 Subject: [PATCH 13/18] Added symlinking --- Cargo.lock | 2 ++ crates/cli/Cargo.toml | 2 ++ crates/cli/src/cli.rs | 3 ++ crates/cli/src/cli/link.rs | 65 ++++++++++++++++++++++++++++++++++++++ crates/cli/src/mama.txt | 1 + 5 files changed, 73 insertions(+) create mode 100644 crates/cli/src/cli/link.rs create mode 100644 crates/cli/src/mama.txt diff --git a/Cargo.lock b/Cargo.lock index f4fd66c24..085e428d6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1508,9 +1508,11 @@ dependencies = [ "color-eyre", "dirs", "eyre", + "hex", "libp2p", "multiaddr", "serde", + "sha2 0.10.8", "tokio", "toml 0.8.12", "tracing", diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 737a184f2..a6c679f10 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -19,8 +19,10 @@ eyre.workspace = true tokio = { workspace = true, features = ["io-std", "macros"] } multiaddr.workspace = true serde = { workspace = true, features = ["derive"] } +sha2.workspace = true toml.workspace = true tracing.workspace = true +hex.workspace = true calimero-node = { path = "../node" } calimero-network = { path = "../network" } diff --git a/crates/cli/src/cli.rs b/crates/cli/src/cli.rs index 828aaabea..825415256 100644 --- a/crates/cli/src/cli.rs +++ b/crates/cli/src/cli.rs @@ -3,6 +3,7 @@ use clap::{Parser, Subcommand}; use crate::config; mod init; +mod link; mod run; mod setup; #[derive(Debug, Parser)] @@ -20,6 +21,7 @@ pub enum SubCommands { Init(init::InitCommand), Setup(setup::SetupCommand), Run(run::RunCommand), + Link(link::LinkCommand), } #[derive(Debug, Parser)] @@ -37,6 +39,7 @@ impl RootCommand { SubCommands::Init(init) => return init.run(self.args), SubCommands::Setup(setup) => return setup.run(self.args), SubCommands::Run(run) => return run.run(self.args).await, + SubCommands::Link(link) => link.run(self.args), } } } diff --git a/crates/cli/src/cli/link.rs b/crates/cli/src/cli/link.rs new file mode 100644 index 000000000..994d55146 --- /dev/null +++ b/crates/cli/src/cli/link.rs @@ -0,0 +1,65 @@ +use std::fs; +#[cfg(unix)] +use std::os::unix::fs::symlink; +#[cfg(windows)] +use std::os::windows::fs::symlink_file as symlink; + +use clap::Parser; +use eyre::WrapErr; +use sha2::{Digest, Sha256}; +use tracing::info; + +use crate::cli; +use crate::config::{ConfigFile, ConfigImpl}; + +/// Setup symlink to application in the node +#[derive(Debug, Parser)] +pub struct LinkCommand { + /// Name of node + #[arg(short, long, value_name = "NAME")] + pub node_name: camino::Utf8PathBuf, + + /// Path to original file + #[clap(short, long)] + pub path: camino::Utf8PathBuf, + + /// Name of application + #[clap(short, long)] + pub app_name: camino::Utf8PathBuf, +} + +impl LinkCommand { + pub fn run(self, root_args: cli::RootArgs) -> eyre::Result<()> { + let path_to_node = root_args.home.join(&self.node_name); + if ConfigFile::exists(&path_to_node) { + match ConfigFile::load(&path_to_node) { + Ok(config) => { + let id = format!("{}:{}", self.node_name, self.app_name); + let mut hasher = Sha256::new(); + hasher.update(id.as_bytes()); + let hash_string = hex::encode(hasher.finalize()); + + let app_path = path_to_node.join(config.application.path).join(hash_string); + + fs::create_dir_all(&app_path) + .wrap_err_with(|| format!("failed to create directory {:?}", &app_path))?; + info!("Linking original file to: {:?}", app_path); + + symlink(self.path, app_path.join("binary.wasm"))?; + info!( + "Application {} linked to node {}\nPath to linked file at {}", + self.app_name, + self.node_name, + app_path.join("binary.wasm") + ); + return Ok(()); + } + Err(err) => { + eyre::bail!("failed to load existing configuration: {}", err); + } + } + } else { + eyre::bail!("You have to initialize the node first \nRun command node init -n "); + } + } +} diff --git a/crates/cli/src/mama.txt b/crates/cli/src/mama.txt new file mode 100644 index 000000000..d3b8ffc2c --- /dev/null +++ b/crates/cli/src/mama.txt @@ -0,0 +1 @@ +MAMAAAA gladan sam \ No newline at end of file From 2fada00750e48a4335531639f16c6302be842259 Mon Sep 17 00:00:00 2001 From: petarjuki7 Date: Tue, 18 Jun 2024 11:45:57 +0200 Subject: [PATCH 14/18] Added app version to linking --- crates/cli/src/cli/link.rs | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/crates/cli/src/cli/link.rs b/crates/cli/src/cli/link.rs index 994d55146..c0cf42c46 100644 --- a/crates/cli/src/cli/link.rs +++ b/crates/cli/src/cli/link.rs @@ -26,6 +26,26 @@ pub struct LinkCommand { /// Name of application #[clap(short, long)] pub app_name: camino::Utf8PathBuf, + + /// Version + #[clap(short, long, value_parser = validate_version)] + pub version: String, +} + +fn validate_version(v: &str) -> Result { + let parts: Vec<&str> = v.split('.').collect(); + if parts.len() != 3 { + return Err(String::from("Version must have exactly three parts")); + } + + for part in parts { + match part.parse::() { + Ok(_) => {} + Err(e) => return Err(format!("Invalid version number: {}", e)), + } + } + + Ok(v.to_string()) } impl LinkCommand { @@ -39,13 +59,19 @@ impl LinkCommand { hasher.update(id.as_bytes()); let hash_string = hex::encode(hasher.finalize()); - let app_path = path_to_node.join(config.application.path).join(hash_string); + let app_path = path_to_node + .join(config.application.path) + .join(hash_string) + .join(self.version); fs::create_dir_all(&app_path) .wrap_err_with(|| format!("failed to create directory {:?}", &app_path))?; info!("Linking original file to: {:?}", app_path); - symlink(self.path, app_path.join("binary.wasm"))?; + match symlink(self.path, app_path.join("binary.wasm")) { + Ok(_) => {} + Err(err) => eyre::bail!("Symlinking failed: {}", err), + } info!( "Application {} linked to node {}\nPath to linked file at {}", self.app_name, From 0c9b1b1624bcfcacd2b7cadc2ebe377631eba421 Mon Sep 17 00:00:00 2001 From: petarjuki7 Date: Tue, 18 Jun 2024 12:44:07 +0200 Subject: [PATCH 15/18] Deleted unnecessary files --- Cargo.lock | 54 ++++++++++++++++++++--------------------- crates/cli/Cargo.toml | 2 +- crates/cli/src/mama.txt | 1 - 3 files changed, 28 insertions(+), 29 deletions(-) delete mode 100644 crates/cli/src/mama.txt diff --git a/Cargo.lock b/Cargo.lock index 085e428d6..322447769 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1053,6 +1053,33 @@ dependencies = [ "tracing", ] +[[package]] +name = "calimero-cli" +version = "0.1.0" +dependencies = [ + "calimero-application", + "calimero-network", + "calimero-node", + "calimero-node-primitives", + "calimero-primitives", + "calimero-server", + "calimero-store", + "camino", + "clap 4.5.4", + "color-eyre", + "dirs", + "eyre", + "hex", + "libp2p", + "multiaddr", + "serde", + "sha2 0.10.8", + "tokio", + "toml 0.8.12", + "tracing", + "tracing-subscriber", +] + [[package]] name = "calimero-identity" version = "0.1.0" @@ -1492,33 +1519,6 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" -[[package]] -name = "cli" -version = "0.1.0" -dependencies = [ - "calimero-application", - "calimero-network", - "calimero-node", - "calimero-node-primitives", - "calimero-primitives", - "calimero-server", - "calimero-store", - "camino", - "clap 4.5.4", - "color-eyre", - "dirs", - "eyre", - "hex", - "libp2p", - "multiaddr", - "serde", - "sha2 0.10.8", - "tokio", - "toml 0.8.12", - "tracing", - "tracing-subscriber", -] - [[package]] name = "color-eyre" version = "0.6.3" diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index a6c679f10..ecb966160 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "cli" +name = "calimero-cli" version = "0.1.0" authors.workspace = true edition.workspace = true diff --git a/crates/cli/src/mama.txt b/crates/cli/src/mama.txt deleted file mode 100644 index d3b8ffc2c..000000000 --- a/crates/cli/src/mama.txt +++ /dev/null @@ -1 +0,0 @@ -MAMAAAA gladan sam \ No newline at end of file From c1312c4c80a4e7ab182c448815ddc24395a63d35 Mon Sep 17 00:00:00 2001 From: petarjuki7 Date: Tue, 18 Jun 2024 15:06:04 +0200 Subject: [PATCH 16/18] Removed duplication of identity --- crates/cli/src/cli/link.rs | 2 +- crates/cli/src/config.rs | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/crates/cli/src/cli/link.rs b/crates/cli/src/cli/link.rs index c0cf42c46..030a185c3 100644 --- a/crates/cli/src/cli/link.rs +++ b/crates/cli/src/cli/link.rs @@ -28,7 +28,7 @@ pub struct LinkCommand { pub app_name: camino::Utf8PathBuf, /// Version - #[clap(short, long, value_parser = validate_version)] + #[clap(short, long, value_parser = validate_version, default_value = "1.0.0")] pub version: String, } diff --git a/crates/cli/src/config.rs b/crates/cli/src/config.rs index e36a7d61a..ca27a25a7 100644 --- a/crates/cli/src/config.rs +++ b/crates/cli/src/config.rs @@ -58,10 +58,7 @@ pub struct StoreConfig { #[derive(Debug, Serialize, Deserialize)] pub struct InitFile { - #[serde( - with = "calimero_primitives::identity::serde_identity", - default = "identity::Keypair::generate_ed25519" - )] + #[serde(with = "calimero_primitives::identity::serde_identity")] pub identity: identity::Keypair, } From 08ba17fa2e6f34a8beb47e9c21e2550347e90aa0 Mon Sep 17 00:00:00 2001 From: Matej Vukosav Date: Tue, 18 Jun 2024 16:27:22 +0200 Subject: [PATCH 17/18] Update README.mdx (#388) --- README.mdx | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.mdx b/README.mdx index 8b954492b..2822dc99f 100644 --- a/README.mdx +++ b/README.mdx @@ -1,8 +1,6 @@ # Welcome to Calimero [![Discord](https://dcbadge.vercel.app/api/server/urJeMtRRMu?style=flat&theme=default-inverted)](https://discord.gg/urJeMtRRMu) -[![Telegram Group](https://img.shields.io/badge/Join-Telegram%20Group-blue.svg?logo=telegram)](https://t.me/+_6h-gJlnXO83OGVk) -[![Docs deploy gh action](https://github.com/calimero-network/core/actions/workflows/docusaurus_deploy.yml/badge.svg)](https://github.com/calimero-network/core/actions/workflows/docusaurus_deploy.yml) [![SDK publish gh action](https://github.com/calimero-network/core/actions/workflows/calimero_sdk_publish.yml/badge.svg)](https://github.com/calimero-network/core/actions/workflows/calimero_sdk_publish.yml) [![npm version](https://badge.fury.io/js/@calimero-is-near%2Fcalimero-p2p-sdk.svg)](https://badge.fury.io/js/@calimero-is-near%2Fcalimero-p2p-sdk) [![License](https://img.shields.io/badge/License-Apache--2.0-blue)](#license) From f3afedc3c46173b8815a3dcdf943b8fb06f62f40 Mon Sep 17 00:00:00 2001 From: Matej Vukosav Date: Tue, 18 Jun 2024 17:01:19 +0200 Subject: [PATCH 18/18] fix: set inital value with use effects (#389) --- packages/calimero-sdk/package.json | 2 +- packages/calimero-sdk/src/setup/SetupModal.tsx | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/calimero-sdk/package.json b/packages/calimero-sdk/package.json index d25359e7c..f87970642 100644 --- a/packages/calimero-sdk/package.json +++ b/packages/calimero-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@calimero-is-near/calimero-p2p-sdk", - "version": "0.0.20", + "version": "0.0.21", "description": "Javascript library to interact with Calimero P2P node", "type": "module", "main": "lib/index.js", diff --git a/packages/calimero-sdk/src/setup/SetupModal.tsx b/packages/calimero-sdk/src/setup/SetupModal.tsx index 16b4115ee..c99b3b064 100644 --- a/packages/calimero-sdk/src/setup/SetupModal.tsx +++ b/packages/calimero-sdk/src/setup/SetupModal.tsx @@ -1,4 +1,4 @@ -import { useCallback, useState } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import apiClient from '../api'; import React from 'react'; import Spinner from '../components/loader/Spinner'; @@ -87,9 +87,13 @@ export const SetupModal: React.FC = ( ) => { const [error, setError] = useState(null); const [loading, setLoading] = useState(false); - const [url, setUrl] = useState(props.getNodeUrl()); + const [url, setUrl] = useState(null); const MINIMUM_LOADING_TIME_MS = 1000; + useEffect(() => { + setUrl(props.getNodeUrl()); + }, [props]); + function validateUrl(value: string): boolean { try { new URL(value);