diff --git a/.github/workflows/merge.yml b/.github/workflows/merge.yml index 60faed6af6..9e1dadd148 100644 --- a/.github/workflows/merge.yml +++ b/.github/workflows/merge.yml @@ -125,7 +125,11 @@ jobs: - name: Run autonomi tests timeout-minutes: 25 - run: cargo test --release --package autonomi --lib --features="full,fs" + run: cargo test --release --package autonomi --features full,local --lib + + - name: Run autonomi doc tests + timeout-minutes: 25 + run: cargo test --release --package autonomi --features full,local --doc - name: Run bootstrap tests timeout-minutes: 25 @@ -134,6 +138,10 @@ jobs: - name: Run node tests timeout-minutes: 25 run: cargo test --release --package ant-node --lib + + - name: Run launchpad tests + timeout-minutes: 25 + run: cargo test --release --package node-launchpad # The `can_store_after_restart` can be executed with other package tests together and passing # on local machine. However keeps failing (when executed together) on CI machines. @@ -404,7 +412,7 @@ jobs: if: matrix.os != 'windows-latest' run: | set -e - for i in {1..100}; do + for i in {1..50}; do dd if=/dev/urandom of=random_file_$i.bin bs=1M count=1 status=none ./target/release/ant --log-output-dest data-dir file upload random_file_$i.bin --public ./target/release/ant --log-output-dest data-dir file upload random_file_$i.bin @@ -419,7 +427,7 @@ jobs: shell: pwsh run: | $ErrorActionPreference = "Stop" - for ($i = 1; $i -le 100; $i++) { + for ($i = 1; $i -le 50; $i++) { $fileName = "random_file_$i.bin" $byteArray = [byte[]]@(0xFF) * (1MB) # Create a 1 MB array filled with 0xFF [System.IO.File]::WriteAllBytes($fileName, $byteArray) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 23a9b78f99..8b4cc22cce 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -252,6 +252,10 @@ jobs: timeout-minutes: 25 run: cargo test --release --package ant-bootstrap + - name: Run launchpad tests + timeout-minutes: 25 + run: cargo test --release --package node-launchpad + - name: Run node tests timeout-minutes: 25 run: cargo test --release --package ant-node --lib diff --git a/CHANGELOG.md b/CHANGELOG.md index c26c8e9c2d..0791380b8e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,107 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 *When editing this file, please respect a line length of 100.* +## 2024-12-18 + +### General + +#### Changed + +- For a branding alignment that moves Safe Network to Autonomi, all crates in the workspace prefixed + `sn-` were renamed with an `ant-` prefix. For example, `sn-node` was renamed `ant-node`. +- To further support this alignment, several binaries were renamed: + + `autonomi` -> `ant` + + `safenode` -> `antnode` + + `safenode-manager` -> `antctl` + + `safenode_rpc_client` -> `antnode_rpc_client` +- The location of data directories used by the binaries were changed from `~/.local/share/safe` to + `~/.local/share/autonomi`. The same is true of the equivalent locations on macOS and Windows. +- The prefixes of metric names in the `safenode` binary (now `antnode`) were changed from `sn_` to + `ant_`. + +### Network + +#### Added + +- Provide Python bindings for `antnode`. +- Generic `Transaction` data type +- Upgraded quoting with smart-contract-based pricing. This makes pricing fairer, as more nodes + are rewarded and there are less incentives to cheat. +- Upgraded data payments verification. +- New storage proof verification which attempts to avoid outsourcing attack +- RBS support, dynamic `responsible_range` based on `network_density` equation estimation. +- Node support for client’s RBS `get_closest` query. +- More quoting metrics for potential future quoting scheme. +- Implement bootstrap cache for local, decentralized network contacts. +- Increased the number of peers returned for the `get_closest` query result. + +#### Changed + +- The `SignedSpend` data type was replaced by `Transaction`. +- Removed `group_consensus` on `BadNode` to support RBS in the future. +- Removed node-side quoting history check as part of the new quoting scheme. +- Rename `continuous_bootstrap` to `network_discovery`. +- Convert `Distance` into `U256` via output string. This avoids the need to access the + `libp2p::Distance` private field because the change for it has not been published yet. +- For node and protocol versioning we remove the use of various keys in favour of a simple + integer between `0` and `255`. We reserve the value `1` for the main production network. +- The `websockets` feature was removed from the node binary. We will no longer support the `ws` + protocol for connections. + +#### Fixed + +- Populate `records_by_bucket` during restart so that proper quoting can be retained after restart. +- Scramble `libp2p` native bootstrap to avoid patterned spike of resource usage. +- Replicate fresh `ScratchPad` +- Accumulate and merge `ScratchPad` on record get. +- Remove an external address if it is unreliable. +- Bootstrap nodes were being replaced too frequently in the routing table. + +### Client + +#### Added + +- Provide Python bindings. +- Support for generic `Transaction` data type. +- Upgraded quoting with smart contract. +- Upgraded data payments with new quoting. +- Retry failed PUTs. This will retry when chunks failed to upload. +- WASM function to generate a vault key from a wallet signature. +- Use bootstrap cache mechanism to initialize `Client` object. +- Exposed many types at top-level, for more ergonomic use of the API. Together with more examples on + function usage. +- Deprecated registers for the client, planning on replacing them fully with transactions and + pointers. +- Wait a short while for initial network discovery to settle before quoting or uploading tasks + begin. +- Stress tests for the register features of the vault. +- Improved logging for vault end-to-end test cases. +- More debugging logging for the client API and `evmlib`. +- Added support for adding a wallet from an environment variable if no wallet files are present. +- Provide `wallet export` command to export a wallet’s private key + +#### Changed + +- Added and modified documentation in various places to improve developer experience. +- Renamed various methods to 'default' to private uploading, while public will have `_public` + suffixed. Also has various changes to allow more granular uploading of archives and data maps. +- Archives now store relative paths to files instead of absolute paths. +- The `wallet create --private-key` command has been changed to `wallet import`. + +#### Fixed + +- Files now download to a specific destination path. +- Retry when the number of quotes obtained are not enough. +- Return the wallet from an environment variable rather than creating a file. +- Error when decrypting a wallet that was imported without the `0x` prefix. +- Issue when selecting a wallet that had multiple wallet files (unencrypted & encrypted). + +### Launchpad + +#### Added + +- Added `--network-id` and `--antnode-path` args for testing + ## 2024-11-25 ### Network diff --git a/Cargo.lock b/Cargo.lock index e359907fd9..2cb2e06184 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -798,7 +798,7 @@ dependencies = [ [[package]] name = "ant-build-info" -version = "0.1.19" +version = "0.1.20" dependencies = [ "chrono", "tracing", @@ -807,7 +807,7 @@ dependencies = [ [[package]] name = "ant-cli" -version = "0.1.5" +version = "0.3.0" dependencies = [ "ant-bootstrap", "ant-build-info", @@ -838,7 +838,7 @@ dependencies = [ [[package]] name = "ant-evm" -version = "0.1.4" +version = "0.1.5" dependencies = [ "custom_debug", "evmlib", @@ -861,7 +861,7 @@ dependencies = [ [[package]] name = "ant-logging" -version = "0.2.40" +version = "0.2.41" dependencies = [ "chrono", "color-eyre", @@ -886,7 +886,7 @@ dependencies = [ [[package]] name = "ant-metrics" -version = "0.1.20" +version = "0.1.21" dependencies = [ "clap", "color-eyre", @@ -900,7 +900,7 @@ dependencies = [ [[package]] name = "ant-networking" -version = "0.19.5" +version = "0.3.0" dependencies = [ "aes-gcm-siv", "ant-bootstrap", @@ -948,7 +948,7 @@ dependencies = [ [[package]] name = "ant-node" -version = "0.112.6" +version = "0.3.0" dependencies = [ "ant-bootstrap", "ant-build-info", @@ -1006,7 +1006,7 @@ dependencies = [ [[package]] name = "ant-node-manager" -version = "0.11.3" +version = "0.11.4" dependencies = [ "ant-bootstrap", "ant-build-info", @@ -1049,7 +1049,7 @@ dependencies = [ [[package]] name = "ant-node-rpc-client" -version = "0.6.36" +version = "0.6.37" dependencies = [ "ant-build-info", "ant-logging", @@ -1073,7 +1073,7 @@ dependencies = [ [[package]] name = "ant-protocol" -version = "0.17.15" +version = "0.3.0" dependencies = [ "ant-build-info", "ant-evm", @@ -1103,7 +1103,7 @@ dependencies = [ [[package]] name = "ant-registers" -version = "0.4.3" +version = "0.4.4" dependencies = [ "blsttc", "crdts", @@ -1140,7 +1140,7 @@ dependencies = [ [[package]] name = "ant-service-management" -version = "0.4.3" +version = "0.4.4" dependencies = [ "ant-bootstrap", "ant-evm", @@ -1167,7 +1167,7 @@ dependencies = [ [[package]] name = "ant-token-supplies" -version = "0.1.58" +version = "0.1.59" dependencies = [ "dirs-next", "reqwest 0.11.27", @@ -1591,7 +1591,7 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "autonomi" -version = "0.2.4" +version = "0.3.0" dependencies = [ "alloy", "ant-bootstrap", @@ -3357,7 +3357,7 @@ dependencies = [ [[package]] name = "evm-testnet" -version = "0.1.4" +version = "0.1.5" dependencies = [ "ant-evm", "clap", @@ -3368,7 +3368,7 @@ dependencies = [ [[package]] name = "evmlib" -version = "0.1.4" +version = "0.1.5" dependencies = [ "alloy", "dirs-next", @@ -6285,7 +6285,7 @@ dependencies = [ [[package]] name = "nat-detection" -version = "0.2.11" +version = "0.2.12" dependencies = [ "ant-build-info", "ant-networking", @@ -6402,7 +6402,7 @@ dependencies = [ [[package]] name = "node-launchpad" -version = "0.4.5" +version = "0.5.0" dependencies = [ "ant-bootstrap", "ant-build-info", @@ -9322,7 +9322,7 @@ checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "test-utils" -version = "0.4.11" +version = "0.4.12" dependencies = [ "bytes", "color-eyre", diff --git a/ant-bootstrap/Cargo.toml b/ant-bootstrap/Cargo.toml index 1e292cd64d..9f4714c4b0 100644 --- a/ant-bootstrap/Cargo.toml +++ b/ant-bootstrap/Cargo.toml @@ -13,8 +13,8 @@ version = "0.1.0" local = [] [dependencies] -ant-logging = { path = "../ant-logging", version = "0.2.40" } -ant-protocol = { version = "0.17.15", path = "../ant-protocol" } +ant-logging = { path = "../ant-logging", version = "0.2.41" } +ant-protocol = { path = "../ant-protocol", version = "0.3.0" } atomic-write-file = "0.2.2" chrono = { version = "0.4", features = ["serde"] } clap = { version = "4.2.1", features = ["derive", "env"] } @@ -38,4 +38,4 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] } tempfile = "3.8.1" [target.'cfg(target_arch = "wasm32")'.dependencies] -wasmtimer = "0.2.0" \ No newline at end of file +wasmtimer = "0.2.0" diff --git a/ant-bootstrap/src/initial_peers.rs b/ant-bootstrap/src/initial_peers.rs index 55b3f78e16..27e59d899c 100644 --- a/ant-bootstrap/src/initial_peers.rs +++ b/ant-bootstrap/src/initial_peers.rs @@ -107,14 +107,21 @@ impl PeersArgs { return Ok(vec![]); } + let mut bootstrap_addresses = vec![]; + + // Read from ANT_PEERS environment variable if present + bootstrap_addresses.extend(Self::read_bootstrap_addr_from_env()); + + if !bootstrap_addresses.is_empty() { + return Ok(bootstrap_addresses); + } + // If local mode is enabled, return empty store (will use mDNS) if self.local || cfg!(feature = "local") { info!("Local mode enabled, using only local discovery."); return Ok(vec![]); } - let mut bootstrap_addresses = vec![]; - // Add addrs from arguments if present for addr in &self.addrs { if let Some(addr) = craft_valid_multiaddr(addr, false) { @@ -124,8 +131,6 @@ impl PeersArgs { warn!("Invalid multiaddress format from arguments: {addr}"); } } - // Read from ANT_PEERS environment variable if present - bootstrap_addresses.extend(Self::read_bootstrap_addr_from_env()); if let Some(count) = count { if bootstrap_addresses.len() >= count { diff --git a/ant-build-info/Cargo.toml b/ant-build-info/Cargo.toml index 045ae93c4f..084f626445 100644 --- a/ant-build-info/Cargo.toml +++ b/ant-build-info/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL-3.0" name = "ant-build-info" readme = "README.md" repository = "https://github.com/maidsafe/autonomi" -version = "0.1.19" +version = "0.1.20" build = "build.rs" include = ["Cargo.toml", "src/**/*", "build.rs"] diff --git a/ant-build-info/src/release_info.rs b/ant-build-info/src/release_info.rs index c79a7039a7..cc425f22fc 100644 --- a/ant-build-info/src/release_info.rs +++ b/ant-build-info/src/release_info.rs @@ -1,4 +1,4 @@ pub const RELEASE_YEAR: &str = "2024"; -pub const RELEASE_MONTH: &str = "11"; +pub const RELEASE_MONTH: &str = "12"; pub const RELEASE_CYCLE: &str = "1"; -pub const RELEASE_CYCLE_COUNTER: &str = "6"; +pub const RELEASE_CYCLE_COUNTER: &str = "5"; diff --git a/ant-cli/Cargo.toml b/ant-cli/Cargo.toml index c6eecb42f6..69aa0a7c3e 100644 --- a/ant-cli/Cargo.toml +++ b/ant-cli/Cargo.toml @@ -3,7 +3,7 @@ authors = ["MaidSafe Developers "] name = "ant-cli" description = "CLI client for the Autonomi network" license = "GPL-3.0" -version = "0.1.5" +version = "0.3.0" edition = "2021" homepage = "https://maidsafe.net" readme = "README.md" @@ -15,8 +15,9 @@ path = "src/main.rs" [features] default = ["metrics"] -local = ["ant-bootstrap/local", "autonomi/local"] +local = ["ant-bootstrap/local", "autonomi/local", "ant-logging/process-metrics"] metrics = ["ant-logging/process-metrics"] +nightly = [] [[bench]] name = "files" @@ -24,10 +25,10 @@ harness = false [dependencies] ant-bootstrap = { path = "../ant-bootstrap", version = "0.1.0" } -ant-build-info = { path = "../ant-build-info", version = "0.1.19" } -ant-logging = { path = "../ant-logging", version = "0.2.40" } -ant-protocol = { path = "../ant-protocol", version = "0.17.15" } -autonomi = { path = "../autonomi", version = "0.2.4", features = [ +ant-build-info = { path = "../ant-build-info", version = "0.1.20" } +ant-logging = { path = "../ant-logging", version = "0.2.41" } +ant-protocol = { path = "../ant-protocol", version = "0.3.0" } +autonomi = { path = "../autonomi", version = "0.3.0", features = [ "fs", "vault", "registers", @@ -59,7 +60,7 @@ tracing = { version = "~0.1.26" } walkdir = "2.5.0" [dev-dependencies] -autonomi = { path = "../autonomi", version = "0.2.4", features = ["fs"]} +autonomi = { path = "../autonomi", version = "0.3.0", features = ["fs"]} criterion = "0.5.1" eyre = "0.6.8" rand = { version = "~0.8.5", features = ["small_rng"] } diff --git a/ant-cli/src/actions/connect.rs b/ant-cli/src/actions/connect.rs index cfe971d14e..cba9ac217a 100644 --- a/ant-cli/src/actions/connect.rs +++ b/ant-cli/src/actions/connect.rs @@ -22,7 +22,7 @@ pub async fn connect_to_network(peers: Vec) -> Result { progress_bar.set_message("Connecting to The Autonomi Network..."); - match Client::connect(&peers).await { + match Client::init_with_peers(peers).await { Ok(client) => { info!("Connected to the Network"); progress_bar.finish_with_message("Connected to the Network"); diff --git a/ant-cli/src/commands.rs b/ant-cli/src/commands.rs index a1d1fd487a..ff065a06c0 100644 --- a/ant-cli/src/commands.rs +++ b/ant-cli/src/commands.rs @@ -158,14 +158,26 @@ pub enum WalletCmd { /// Optional flag to not add a password. #[clap(long, action)] no_password: bool, - /// Optional hex-encoded private key. - #[clap(long)] - private_key: Option, /// Optional password to encrypt the wallet with. #[clap(long, short)] password: Option, }, + /// Import an existing wallet. + Import { + /// Hex-encoded private key. + private_key: String, + /// Optional flag to not add a password. + #[clap(long, action)] + no_password: bool, + /// Optional password to encrypt the wallet with. + #[clap(long, short)] + password: Option, + }, + + /// Print the private key of a wallet. + Export, + /// Check the balance of the wallet. Balance, } @@ -175,7 +187,7 @@ pub async fn handle_subcommand(opt: Opt) -> Result<()> { let cmd = opt.command; match cmd { - SubCmd::File { command } => match command { + Some(SubCmd::File { command }) => match command { FileCmd::Cost { file } => file::cost(&file, peers.await?).await, FileCmd::Upload { file, public } => file::upload(&file, public, peers.await?).await, FileCmd::Download { addr, dest_file } => { @@ -183,7 +195,7 @@ pub async fn handle_subcommand(opt: Opt) -> Result<()> { } FileCmd::List => file::list(), }, - SubCmd::Register { command } => match command { + Some(SubCmd::Register { command }) => match command { RegisterCmd::GenerateKey { overwrite } => register::generate_key(overwrite), RegisterCmd::Cost { name } => register::cost(&name, peers.await?).await, RegisterCmd::Create { @@ -199,19 +211,25 @@ pub async fn handle_subcommand(opt: Opt) -> Result<()> { RegisterCmd::Get { address, name } => register::get(address, name, peers.await?).await, RegisterCmd::List => register::list(), }, - SubCmd::Vault { command } => match command { + Some(SubCmd::Vault { command }) => match command { VaultCmd::Cost => vault::cost(peers.await?).await, VaultCmd::Create => vault::create(peers.await?).await, VaultCmd::Load => vault::load(peers.await?).await, VaultCmd::Sync { force } => vault::sync(peers.await?, force).await, }, - SubCmd::Wallet { command } => match command { + Some(SubCmd::Wallet { command }) => match command { WalletCmd::Create { no_password, + password, + } => wallet::create(no_password, password), + WalletCmd::Import { private_key, + no_password, password, - } => wallet::create(no_password, private_key, password), - WalletCmd::Balance => Ok(wallet::balance().await?), + } => wallet::import(private_key, no_password, password), + WalletCmd::Export => wallet::export(), + WalletCmd::Balance => wallet::balance().await, }, + None => Ok(()), } } diff --git a/ant-cli/src/commands/register.rs b/ant-cli/src/commands/register.rs index 17c30b2559..5598fc0544 100644 --- a/ant-cli/src/commands/register.rs +++ b/ant-cli/src/commands/register.rs @@ -6,6 +6,8 @@ // KIND, either express or implied. Please review the Licences for the specific language governing // permissions and limitations relating to use of the SAFE Network Software. +#![allow(deprecated)] + use crate::utils::collect_upload_summary; use crate::wallet::load_wallet; use autonomi::client::registers::RegisterAddress; diff --git a/ant-cli/src/commands/wallet.rs b/ant-cli/src/commands/wallet.rs index 3b31a873b2..b1a2caf70b 100644 --- a/ant-cli/src/commands/wallet.rs +++ b/ant-cli/src/commands/wallet.rs @@ -6,7 +6,7 @@ // KIND, either express or implied. Please review the Licences for the specific language governing // permissions and limitations relating to use of the SAFE Network Software. -use crate::wallet::fs::{select_wallet, store_private_key}; +use crate::wallet::fs::{select_wallet, select_wallet_private_key, store_private_key}; use crate::wallet::input::request_password; use crate::wallet::DUMMY_NETWORK; use autonomi::Wallet; @@ -16,42 +16,49 @@ use prettytable::{Cell, Row, Table}; const WALLET_PASSWORD_REQUIRED: bool = false; -pub fn create( +pub fn create(no_password: bool, password: Option) -> Result<()> { + let maybe_encryption_password = maybe_request_password(no_password, password)?; + + let wallet_private_key = Wallet::random_private_key(); + + let wallet_address = Wallet::new_from_private_key(DUMMY_NETWORK, &wallet_private_key) + .expect("Infallible") + .address() + .to_string(); + + // Save the private key file + let file_path = store_private_key(&wallet_private_key, maybe_encryption_password)?; + + println!("Wallet address: {wallet_address}"); + println!("Wallet private key: {wallet_private_key}"); + println!("Stored wallet in: {file_path:?}"); + + Ok(()) +} + +pub fn import( + mut wallet_private_key: String, no_password: bool, - private_key: Option, password: Option, ) -> Result<()> { - if no_password && password.is_some() { - return Err(eyre!( - "Only one of `--no-password` or `--password` may be specified" - )); - } - - // Set a password for encryption or not - let encryption_password: Option = match (no_password, password) { - (true, _) => None, - (false, Some(pass)) => Some(pass.to_owned()), - (false, None) => request_password(WALLET_PASSWORD_REQUIRED), - }; - - let wallet_private_key = if let Some(private_key) = private_key { - // Validate imported key - Wallet::new_from_private_key(DUMMY_NETWORK, &private_key) - .map_err(|_| eyre!("Please provide a valid secret key in hex format"))?; + // Validate imported key + Wallet::new_from_private_key(DUMMY_NETWORK, &wallet_private_key) + .map_err(|_| eyre!("Please provide a valid private key in hex format"))?; - private_key - } else { - // Create a new key - Wallet::random_private_key() - }; + let maybe_encryption_password = maybe_request_password(no_password, password)?; let wallet_address = Wallet::new_from_private_key(DUMMY_NETWORK, &wallet_private_key) .expect("Infallible") .address() .to_string(); + // Prepend with 0x if it isn't already + if !wallet_private_key.starts_with("0x") { + wallet_private_key = format!("0x{wallet_private_key}"); + } + // Save the private key file - let file_path = store_private_key(&wallet_private_key, encryption_password)?; + let file_path = store_private_key(&wallet_private_key, maybe_encryption_password)?; println!("Wallet address: {wallet_address}"); println!("Stored wallet in: {file_path:?}"); @@ -59,6 +66,20 @@ pub fn create( Ok(()) } +pub fn export() -> Result<()> { + let wallet_private_key = select_wallet_private_key()?; + + let wallet_address = Wallet::new_from_private_key(DUMMY_NETWORK, &wallet_private_key) + .expect("Infallible") + .address() + .to_string(); + + println!("Wallet address: {wallet_address}"); + println!("Wallet private key: {wallet_private_key}"); + + Ok(()) +} + pub async fn balance() -> Result<()> { let wallet = select_wallet()?; @@ -83,3 +104,20 @@ pub async fn balance() -> Result<()> { Ok(()) } + +fn maybe_request_password(no_password: bool, password: Option) -> Result> { + if no_password && password.is_some() { + return Err(eyre!( + "Only one of `--no-password` or `--password` may be specified" + )); + } + + // Set a password for encryption or not + let maybe_password = match (no_password, password) { + (true, _) => None, + (false, Some(pass)) => Some(pass.to_owned()), + (false, None) => request_password(WALLET_PASSWORD_REQUIRED), + }; + + Ok(maybe_password) +} diff --git a/ant-cli/src/main.rs b/ant-cli/src/main.rs index c0404e9f75..971c38fd6a 100644 --- a/ant-cli/src/main.rs +++ b/ant-cli/src/main.rs @@ -24,9 +24,10 @@ pub use access::user_data; use clap::Parser; use color_eyre::Result; -#[cfg(feature = "metrics")] +#[cfg(feature = "local")] use ant_logging::metrics::init_metrics; use ant_logging::{LogBuilder, LogFormat, ReloadHandle, WorkerGuard}; +use ant_protocol::version; use opt::Opt; use tracing::Level; @@ -37,15 +38,47 @@ async fn main() -> Result<()> { if let Some(network_id) = opt.network_id { ant_protocol::version::set_network_id(network_id); } + + // The clone is necessary to resolve a clippy warning related to a mutex. + let identify_protocol_str = version::IDENTIFY_PROTOCOL_STR + .read() + .expect("Failed to obtain read lock for IDENTIFY_PROTOCOL_STR") + .clone(); + if opt.version { + println!( + "{}", + ant_build_info::version_string( + "Autonomi Client", + env!("CARGO_PKG_VERSION"), + Some(&identify_protocol_str) + ) + ); + return Ok(()); + } + + if opt.crate_version { + println!("Crate version: {}", env!("CARGO_PKG_VERSION")); + return Ok(()); + } + + if opt.protocol_version { + println!("Network version: {identify_protocol_str}"); + return Ok(()); + } + + #[cfg(not(feature = "nightly"))] + if opt.package_version { + println!("Package version: {}", ant_build_info::package_version()); + return Ok(()); + } + let _log_guards = init_logging_and_metrics(&opt)?; - #[cfg(feature = "metrics")] + #[cfg(feature = "local")] tokio::spawn(init_metrics(std::process::id())); - // Log the full command that was run and the git version info!("\"{}\"", std::env::args().collect::>().join(" ")); let version = ant_build_info::git_info(); info!("autonomi client built with git version: {version}"); - println!("autonomi client built with git version: {version}"); commands::handle_subcommand(opt).await?; diff --git a/ant-cli/src/opt.rs b/ant-cli/src/opt.rs index 3ffa1eb5f6..9d7e4edd9b 100644 --- a/ant-cli/src/opt.rs +++ b/ant-cli/src/opt.rs @@ -16,8 +16,29 @@ use std::time::Duration; // Please do not remove the blank lines in these doc comments. // They are used for inserting line breaks when the help menu is rendered in the UI. #[derive(Parser)] +#[command(disable_version_flag = true)] #[command(author, version, about, long_about = None)] pub(crate) struct Opt { + /// Available sub commands. + #[clap(subcommand)] + pub command: Option, + + /// The maximum duration to wait for a connection to the network before timing out. + #[clap(long = "timeout", global = true, value_parser = |t: &str| -> Result { Ok(t.parse().map(Duration::from_secs)?) })] + pub connection_timeout: Option, + + /// Print the crate version. + #[clap(long)] + pub crate_version: bool, + + /// Specify the logging format. + /// + /// Valid values are "default" or "json". + /// + /// If the argument is not used, the default format will be applied. + #[clap(long, value_parser = LogFormat::parse_from_str, verbatim_doc_comment)] + pub log_format: Option, + /// Specify the logging output destination. /// /// Valid values are "stdout", "data-dir", or a custom path. @@ -32,25 +53,6 @@ pub(crate) struct Opt { #[clap(long, value_parser = LogOutputDest::parse_from_str, verbatim_doc_comment, default_value = "data-dir")] pub log_output_dest: LogOutputDest, - /// Specify the logging format. - /// - /// Valid values are "default" or "json". - /// - /// If the argument is not used, the default format will be applied. - #[clap(long, value_parser = LogFormat::parse_from_str, verbatim_doc_comment)] - pub log_format: Option, - - #[command(flatten)] - pub(crate) peers: PeersArgs, - - /// Available sub commands. - #[clap(subcommand)] - pub command: SubCmd, - - /// The maximum duration to wait for a connection to the network before timing out. - #[clap(long = "timeout", global = true, value_parser = |t: &str| -> Result { Ok(t.parse().map(Duration::from_secs)?) })] - pub connection_timeout: Option, - /// Specify the network ID to use. This will allow you to run the CLI on a different network. /// /// By default, the network ID is set to 1, which represents the mainnet. @@ -62,4 +64,20 @@ pub(crate) struct Opt { /// This may increase operation speed, but offers no guarantees that operations were successful. #[clap(global = true, long = "no-verify", short = 'x')] pub no_verify: bool, + + #[command(flatten)] + pub(crate) peers: PeersArgs, + + /// Print the package version. + #[cfg(not(feature = "nightly"))] + #[clap(long)] + pub package_version: bool, + + /// Print the network protocol version. + #[clap(long)] + pub protocol_version: bool, + + /// Print version information. + #[clap(long)] + pub version: bool, } diff --git a/ant-cli/src/wallet/encryption.rs b/ant-cli/src/wallet/encryption.rs index bc673574ce..88f53afa15 100644 --- a/ant-cli/src/wallet/encryption.rs +++ b/ant-cli/src/wallet/encryption.rs @@ -123,11 +123,8 @@ pub fn decrypt_private_key(encrypted_data: &str, password: &str) -> Result Result { let encrypted_file_path = wallets_folder.join(format!("{wallet_address}{ENCRYPTED_PRIVATE_KEY_EXT}")); - let is_encrypted = encrypted_file_path.exists(); + let is_plain = wallets_folder.join(&file_name).exists(); + + // Trick to favour the plain file in case they both exist + let is_encrypted = encrypted_file_path.exists() && !is_plain; if is_encrypted { file_name.push_str(ENCRYPTED_PRIVATE_KEY_EXT); @@ -115,6 +118,11 @@ pub(crate) fn load_wallet_from_address(wallet_address: &str) -> Result Result { + // try if there is a wallet set in the ENV first + if let Ok(env_wallet) = load_evm_wallet_from_env() { + return Ok(env_wallet); + } + let wallet_address = select_wallet_address()?; load_wallet_from_address(&wallet_address) } @@ -134,11 +142,7 @@ pub(crate) fn select_wallet_address() -> Result { let wallet_files = get_wallet_files(&wallets_folder)?; let wallet_address = match wallet_files.len() { - 0 => { - let secret_key = - get_secret_key_from_env().map_err(|_| Error::NoWalletsFoundAndNoSecretKeysInEnv)?; - Ok(secret_key) - } + 0 => Err(Error::NoWalletsFound), 1 => Ok(filter_wallet_file_extension(&wallet_files[0])), _ => get_wallet_selection(wallet_files), }?; diff --git a/ant-evm/Cargo.toml b/ant-evm/Cargo.toml index 6e184a6ee1..88ce78eaeb 100644 --- a/ant-evm/Cargo.toml +++ b/ant-evm/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL-3.0" name = "ant-evm" readme = "README.md" repository = "https://github.com/maidsafe/autonomi" -version = "0.1.4" +version = "0.1.5" [features] local = ["evmlib/local"] @@ -16,7 +16,7 @@ test-utils = [] [dependencies] custom_debug = "~0.6.1" -evmlib = { path = "../evmlib", version = "0.1.4" } +evmlib = { path = "../evmlib", version = "0.1.5" } hex = "~0.4.3" lazy_static = "~1.4.0" libp2p = { version = "0.54.1", features = ["identify", "kad"] } diff --git a/ant-logging/Cargo.toml b/ant-logging/Cargo.toml index d923329bca..8fb4cf86f4 100644 --- a/ant-logging/Cargo.toml +++ b/ant-logging/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL-3.0" name = "ant-logging" readme = "README.md" repository = "https://github.com/maidsafe/autonomi" -version = "0.2.40" +version = "0.2.41" [dependencies] chrono = "~0.4.19" diff --git a/ant-metrics/Cargo.toml b/ant-metrics/Cargo.toml index 45efbc4eea..1a57195b2f 100644 --- a/ant-metrics/Cargo.toml +++ b/ant-metrics/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL-3.0" name = "ant-metrics" readme = "README.md" repository = "https://github.com/maidsafe/autonomi" -version = "0.1.20" +version = "0.1.21" [[bin]] path = "src/main.rs" diff --git a/ant-networking/Cargo.toml b/ant-networking/Cargo.toml index 8849a3752b..d23fff4e84 100644 --- a/ant-networking/Cargo.toml +++ b/ant-networking/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL-3.0" name = "ant-networking" readme = "README.md" repository = "https://github.com/maidsafe/autonomi" -version = "0.19.5" +version = "0.3.0" [features] default = [] @@ -21,10 +21,10 @@ upnp = ["libp2p/upnp"] [dependencies] aes-gcm-siv = "0.11.1" ant-bootstrap = { path = "../ant-bootstrap", version = "0.1.0" } -ant-build-info = { path = "../ant-build-info", version = "0.1.19" } -ant-evm = { path = "../ant-evm", version = "0.1.4" } -ant-protocol = { path = "../ant-protocol", version = "0.17.15" } -ant-registers = { path = "../ant-registers", version = "0.4.3" } +ant-build-info = { path = "../ant-build-info", version = "0.1.20" } +ant-evm = { path = "../ant-evm", version = "0.1.5" } +ant-protocol = { path = "../ant-protocol", version = "0.3.0" } +ant-registers = { path = "../ant-registers", version = "0.4.4" } async-trait = "0.1" bytes = { version = "1.0.1", features = ["serde"] } custom_debug = "~0.6.1" diff --git a/ant-networking/src/driver.rs b/ant-networking/src/driver.rs index 4534b49110..bb1637a099 100644 --- a/ant-networking/src/driver.rs +++ b/ant-networking/src/driver.rs @@ -135,7 +135,7 @@ const PERIODIC_KAD_BOOTSTRAP_INTERVAL_MAX_S: u64 = 21600; // Init during compilation, instead of runtime error that should never happen // Option::expect will be stabilised as const in the future (https://github.com/rust-lang/rust/issues/67441) -const REPLICATION_FACTOR: NonZeroUsize = match NonZeroUsize::new(CLOSE_GROUP_SIZE) { +const REPLICATION_FACTOR: NonZeroUsize = match NonZeroUsize::new(CLOSE_GROUP_SIZE + 2) { Some(v) => v, None => panic!("CLOSE_GROUP_SIZE should not be zero"), }; diff --git a/ant-networking/src/event/swarm.rs b/ant-networking/src/event/swarm.rs index 3bf65eb6d9..6897ff8a08 100644 --- a/ant-networking/src/event/swarm.rs +++ b/ant-networking/src/event/swarm.rs @@ -253,7 +253,10 @@ impl SwarmDriver { // If we are not local, we care only for peers that we dialed and thus are reachable. if self.local || has_dialed { // A bad node cannot establish a connection with us. So we can add it to the RT directly. - self.remove_bootstrap_from_full(peer_id); + + // With the new bootstrap cache, the workload is distributed, + // hence no longer need to replace bootstrap nodes for workload share. + // self.remove_bootstrap_from_full(peer_id); // Avoid have `direct link format` addrs co-exists with `relay` addr if has_relayed { @@ -624,12 +627,16 @@ impl SwarmDriver { } // if target bucket is full, remove a bootstrap node if presents. + #[allow(dead_code)] fn remove_bootstrap_from_full(&mut self, peer_id: PeerId) { let mut shall_removed = None; + let mut bucket_index = Some(0); + if let Some(kbucket) = self.swarm.behaviour_mut().kademlia.kbucket(peer_id) { if kbucket.num_entries() >= K_VALUE.into() { - if let Some(peers) = self.bootstrap_peers.get(&kbucket.range().0.ilog2()) { + bucket_index = kbucket.range().0.ilog2(); + if let Some(peers) = self.bootstrap_peers.get(&bucket_index) { for peer_entry in kbucket.iter() { if peers.contains(peer_entry.node.key.preimage()) { shall_removed = Some(*peer_entry.node.key.preimage()); @@ -649,6 +656,13 @@ impl SwarmDriver { if let Some(removed_peer) = entry { self.update_on_peer_removal(*removed_peer.node.key.preimage()); } + + // With the switch to using bootstrap cache, workload is distributed already. + // to avoid peers keeps being replaced by each other, + // there shall be just one time of removal to be undertaken. + if let Some(peers) = self.bootstrap_peers.get_mut(&bucket_index) { + let _ = peers.remove(&to_be_removed_bootstrap); + } } } diff --git a/ant-networking/src/lib.rs b/ant-networking/src/lib.rs index c43cdcdf8e..fca47f18d0 100644 --- a/ant-networking/src/lib.rs +++ b/ant-networking/src/lib.rs @@ -387,6 +387,10 @@ impl Network { .await?; // Filter out results from the ignored peers. close_nodes.retain(|peer_id| !ignore_peers.contains(peer_id)); + info!( + "For record {record_address:?} quoting {} nodes. ignore_peers is {ignore_peers:?}", + close_nodes.len() + ); if close_nodes.is_empty() { error!("Can't get store_cost of {record_address:?}, as all close_nodes are ignored"); @@ -1111,7 +1115,8 @@ impl Network { ); } - let closest_peers = sort_peers_by_address(&closest_peers, key, CLOSE_GROUP_SIZE + 1)?; + let expanded_close_group = CLOSE_GROUP_SIZE + CLOSE_GROUP_SIZE / 2; + let closest_peers = sort_peers_by_address(&closest_peers, key, expanded_close_group)?; Ok(closest_peers.into_iter().cloned().collect()) } diff --git a/ant-node-manager/Cargo.toml b/ant-node-manager/Cargo.toml index ad66bd6d5f..23b4ecc1f4 100644 --- a/ant-node-manager/Cargo.toml +++ b/ant-node-manager/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL-3.0" name = "ant-node-manager" readme = "README.md" repository = "https://github.com/maidsafe/autonomi" -version = "0.11.3" +version = "0.11.4" [[bin]] name = "antctl" @@ -31,12 +31,12 @@ websockets = [] [dependencies] ant-bootstrap = { path = "../ant-bootstrap", version = "0.1.0" } -ant-build-info = { path = "../ant-build-info", version = "0.1.19" } -ant-evm = { path = "../ant-evm", version = "0.1.4" } -ant-logging = { path = "../ant-logging", version = "0.2.40" } -ant-protocol = { path = "../ant-protocol", version = "0.17.15" } +ant-build-info = { path = "../ant-build-info", version = "0.1.20" } +ant-evm = { path = "../ant-evm", version = "0.1.5" } +ant-logging = { path = "../ant-logging", version = "0.2.41" } +ant-protocol = { path = "../ant-protocol", version = "0.3.0" } ant-releases = { version = "0.4.0" } -ant-service-management = { path = "../ant-service-management", version = "0.4.3" } +ant-service-management = { path = "../ant-service-management", version = "0.4.4" } chrono = "~0.4.19" clap = { version = "4.4.6", features = ["derive", "env"] } colored = "2.0.4" diff --git a/ant-node-rpc-client/Cargo.toml b/ant-node-rpc-client/Cargo.toml index 9d8b9cc61d..c58e8cadc7 100644 --- a/ant-node-rpc-client/Cargo.toml +++ b/ant-node-rpc-client/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL-3.0" name = "ant-node-rpc-client" readme = "README.md" repository = "https://github.com/maidsafe/autonomi" -version = "0.6.36" +version = "0.6.37" [[bin]] name = "antnode_rpc_client" @@ -17,11 +17,11 @@ path = "src/main.rs" nightly = [] [dependencies] -ant-build-info = { path = "../ant-build-info", version = "0.1.19" } -ant-logging = { path = "../ant-logging", version = "0.2.40" } -ant-protocol = { path = "../ant-protocol", version = "0.17.15", features=["rpc"] } -ant-node = { path = "../ant-node", version = "0.112.6" } -ant-service-management = { path = "../ant-service-management", version = "0.4.3" } +ant-build-info = { path = "../ant-build-info", version = "0.1.20" } +ant-logging = { path = "../ant-logging", version = "0.2.41" } +ant-protocol = { path = "../ant-protocol", version = "0.3.0", features=["rpc"] } +ant-node = { path = "../ant-node", version = "0.3.0" } +ant-service-management = { path = "../ant-service-management", version = "0.4.4" } async-trait = "0.1" bls = { package = "blsttc", version = "8.0.1" } clap = { version = "4.2.1", features = ["derive"] } diff --git a/ant-node/Cargo.toml b/ant-node/Cargo.toml index 09741f2fb9..63add89f00 100644 --- a/ant-node/Cargo.toml +++ b/ant-node/Cargo.toml @@ -2,7 +2,7 @@ authors = ["MaidSafe Developers "] description = "The Autonomi node binary" name = "ant-node" -version = "0.112.6" +version = "0.3.0" edition = "2021" license = "GPL-3.0" homepage = "https://maidsafe.net" @@ -17,9 +17,9 @@ path = "src/bin/antnode/main.rs" default = ["metrics", "upnp", "open-metrics", "encrypt-records"] encrypt-records = ["ant-networking/encrypt-records"] extension-module = ["pyo3/extension-module"] -local = ["ant-networking/local", "ant-evm/local", "ant-bootstrap/local"] +local = ["ant-networking/local", "ant-evm/local", "ant-bootstrap/local", "ant-logging/process-metrics"] loud = ["ant-networking/loud"] # loud mode: print important messages to console -metrics = ["ant-logging/process-metrics"] +metrics = [] nightly = [] open-metrics = ["ant-networking/open-metrics", "prometheus-client"] otlp = ["ant-logging/otlp"] @@ -27,13 +27,13 @@ upnp = ["ant-networking/upnp"] [dependencies] ant-bootstrap = { path = "../ant-bootstrap", version = "0.1.0" } -ant-build-info = { path = "../ant-build-info", version = "0.1.19" } -ant-evm = { path = "../ant-evm", version = "0.1.4" } -ant-logging = { path = "../ant-logging", version = "0.2.40" } -ant-networking = { path = "../ant-networking", version = "0.19.5" } -ant-protocol = { path = "../ant-protocol", version = "0.17.15" } -ant-registers = { path = "../ant-registers", version = "0.4.3" } -ant-service-management = { path = "../ant-service-management", version = "0.4.3" } +ant-build-info = { path = "../ant-build-info", version = "0.1.20" } +ant-evm = { path = "../ant-evm", version = "0.1.5" } +ant-logging = { path = "../ant-logging", version = "0.2.41" } +ant-networking = { path = "../ant-networking", version = "0.3.0" } +ant-protocol = { path = "../ant-protocol", version = "0.3.0" } +ant-registers = { path = "../ant-registers", version = "0.4.4" } +ant-service-management = { path = "../ant-service-management", version = "0.4.4" } async-trait = "0.1" bls = { package = "blsttc", version = "8.0.1" } bytes = { version = "1.0.1", features = ["serde"] } @@ -83,10 +83,10 @@ walkdir = "~2.5.0" xor_name = "5.0.0" [dev-dependencies] -ant-protocol = { path = "../ant-protocol", version = "0.17.15", features = ["rpc"] } +ant-protocol = { path = "../ant-protocol", version = "0.3.0", features = ["rpc"] } assert_fs = "1.0.0" -evmlib = { path = "../evmlib", version = "0.1.4" } -autonomi = { path = "../autonomi", version = "0.2.4", features = ["registers"] } +evmlib = { path = "../evmlib", version = "0.1.5" } +autonomi = { path = "../autonomi", version = "0.3.0", features = ["registers"] } reqwest = { version = "0.12.2", default-features = false, features = [ "rustls-tls-manual-roots", ] } diff --git a/ant-node/src/bin/antnode/main.rs b/ant-node/src/bin/antnode/main.rs index db40d00101..3397d81461 100644 --- a/ant-node/src/bin/antnode/main.rs +++ b/ant-node/src/bin/antnode/main.rs @@ -15,7 +15,7 @@ mod subcommands; use crate::subcommands::EvmNetworkCommand; use ant_bootstrap::{BootstrapCacheConfig, BootstrapCacheStore, PeersArgs}; use ant_evm::{get_evm_network_from_env, EvmNetwork, RewardsAddress}; -#[cfg(feature = "metrics")] +#[cfg(feature = "local")] use ant_logging::metrics::init_metrics; use ant_logging::{Level, LogFormat, LogOutputDest, ReloadHandle}; use ant_node::{Marker, NodeBuilder, NodeEvent, NodeEventsReceiver}; @@ -306,7 +306,7 @@ fn main() -> Result<()> { // Create a tokio runtime per `run_node` attempt, this ensures // any spawned tasks are closed before we would attempt to run // another process with these args. - #[cfg(feature = "metrics")] + #[cfg(feature = "local")] rt.spawn(init_metrics(std::process::id())); let initial_peres = rt.block_on(opt.peers.get_addrs(None, Some(100)))?; debug!("Node's owner set to: {:?}", opt.owner); diff --git a/ant-node/src/node.rs b/ant-node/src/node.rs index 4908c0bc23..2515af6344 100644 --- a/ant-node/src/node.rs +++ b/ant-node/src/node.rs @@ -16,7 +16,9 @@ use ant_bootstrap::BootstrapCacheStore; use ant_evm::RewardsAddress; #[cfg(feature = "open-metrics")] use ant_networking::MetricsRegistries; -use ant_networking::{Instant, Network, NetworkBuilder, NetworkEvent, NodeIssue, SwarmDriver}; +use ant_networking::{ + target_arch::sleep, Instant, Network, NetworkBuilder, NetworkEvent, NodeIssue, SwarmDriver, +}; use ant_protocol::{ convert_distance_to_u256, error::Error as ProtocolError, @@ -969,7 +971,7 @@ impl Node { } } // Sleep a short while to avoid causing a spike on resource usage. - std::thread::sleep(std::time::Duration::from_secs(10)); + sleep(std::time::Duration::from_secs(10)).await; } } } diff --git a/ant-node/tests/common/client.rs b/ant-node/tests/common/client.rs index 55126c1fc8..faf8c1ae05 100644 --- a/ant-node/tests/common/client.rs +++ b/ant-node/tests/common/client.rs @@ -131,7 +131,7 @@ impl LocalNetwork { println!("Client bootstrap with peer {bootstrap_peers:?}"); info!("Client bootstrap with peer {bootstrap_peers:?}"); - Client::connect(&bootstrap_peers) + Client::init_with_peers(bootstrap_peers) .await .expect("Client shall be successfully created.") } diff --git a/ant-node/tests/data_with_churn.rs b/ant-node/tests/data_with_churn.rs index 4112863140..87261779c4 100644 --- a/ant-node/tests/data_with_churn.rs +++ b/ant-node/tests/data_with_churn.rs @@ -6,6 +6,9 @@ // KIND, either express or implied. Please review the Licences for the specific language governing // permissions and limitations relating to use of the SAFE Network Software. +// TODO: Remove this once the registers are removed +#![expect(deprecated)] + mod common; use crate::common::{ diff --git a/ant-node/tests/verify_data_location.rs b/ant-node/tests/verify_data_location.rs index a15a0e18be..e8e2c6938a 100644 --- a/ant-node/tests/verify_data_location.rs +++ b/ant-node/tests/verify_data_location.rs @@ -6,7 +6,10 @@ // KIND, either express or implied. Please review the Licences for the specific language governing // permissions and limitations relating to use of the SAFE Network Software. +// TODO: Remove this once the registers are removed +#![expect(deprecated)] #![allow(clippy::mutable_key_type)] + mod common; use ant_logging::LogBuilder; diff --git a/ant-protocol/Cargo.toml b/ant-protocol/Cargo.toml index c8c4b6808d..d84d5b5404 100644 --- a/ant-protocol/Cargo.toml +++ b/ant-protocol/Cargo.toml @@ -7,16 +7,16 @@ license = "GPL-3.0" name = "ant-protocol" readme = "README.md" repository = "https://github.com/maidsafe/autonomi" -version = "0.17.15" +version = "0.3.0" [features] default = [] rpc = ["tonic", "prost"] [dependencies] -ant-build-info = { path = "../ant-build-info", version = "0.1.19" } -ant-evm = { path = "../ant-evm", version = "0.1.4" } -ant-registers = { path = "../ant-registers", version = "0.4.3" } +ant-build-info = { path = "../ant-build-info", version = "0.1.20" } +ant-evm = { path = "../ant-evm", version = "0.1.5" } +ant-registers = { path = "../ant-registers", version = "0.4.4" } bls = { package = "blsttc", version = "8.0.1" } bytes = { version = "1.0.1", features = ["serde"] } color-eyre = "0.6.2" diff --git a/ant-registers/Cargo.toml b/ant-registers/Cargo.toml index f7607a8398..5c54f01f6f 100644 --- a/ant-registers/Cargo.toml +++ b/ant-registers/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL-3.0" name = "ant-registers" readme = "README.md" repository = "https://github.com/maidsafe/autonomi" -version = "0.4.3" +version = "0.4.4" [features] test-utils = [] diff --git a/ant-service-management/Cargo.toml b/ant-service-management/Cargo.toml index 45c8a8d6b5..8fadb57777 100644 --- a/ant-service-management/Cargo.toml +++ b/ant-service-management/Cargo.toml @@ -7,13 +7,13 @@ license = "GPL-3.0" name = "ant-service-management" readme = "README.md" repository = "https://github.com/maidsafe/autonomi" -version = "0.4.3" +version = "0.4.4" [dependencies] ant-bootstrap = { path = "../ant-bootstrap", version = "0.1.0" } -ant-evm = { path = "../ant-evm", version = "0.1.4" } -ant-logging = { path = "../ant-logging", version = "0.2.40" } -ant-protocol = { path = "../ant-protocol", version = "0.17.15", features = ["rpc"] } +ant-evm = { path = "../ant-evm", version = "0.1.5" } +ant-logging = { path = "../ant-logging", version = "0.2.41" } +ant-protocol = { path = "../ant-protocol", version = "0.3.0", features = ["rpc"] } async-trait = "0.1" dirs-next = "2.0.0" libp2p = { version = "0.54.1", features = ["kad"] } diff --git a/ant-token-supplies/Cargo.toml b/ant-token-supplies/Cargo.toml index abacf83744..95aa9ceac7 100644 --- a/ant-token-supplies/Cargo.toml +++ b/ant-token-supplies/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL-3.0" name = "ant-token-supplies" readme = "README.md" repository = "https://github.com/maidsafe/autonomi" -version = "0.1.58" +version = "0.1.59" [dependencies] diff --git a/autonomi/Cargo.toml b/autonomi/Cargo.toml index 87c93dc9cd..9888538806 100644 --- a/autonomi/Cargo.toml +++ b/autonomi/Cargo.toml @@ -3,7 +3,7 @@ authors = ["MaidSafe Developers "] description = "Autonomi client API" name = "autonomi" license = "GPL-3.0" -version = "0.2.4" +version = "0.3.0" edition = "2021" homepage = "https://maidsafe.net" readme = "README.md" @@ -13,6 +13,14 @@ repository = "https://github.com/maidsafe/autonomi" name = "autonomi" crate-type = ["cdylib", "rlib"] +[[example]] +name = "data_and_archive" +required-features = ["full"] + +[[example]] +name = "put_and_dir_upload" +required-features = ["full"] + [features] default = ["vault"] external-signer = ["ant-evm/external-signer"] @@ -26,10 +34,10 @@ vault = ["registers"] [dependencies] ant-bootstrap = { path = "../ant-bootstrap", version = "0.1.0" } -ant-evm = { path = "../ant-evm", version = "0.1.4" } -ant-networking = { path = "../ant-networking", version = "0.19.5" } -ant-protocol = { version = "0.17.15", path = "../ant-protocol" } -ant-registers = { path = "../ant-registers", version = "0.4.3" } +ant-evm = { path = "../ant-evm", version = "0.1.5" } +ant-networking = { path = "../ant-networking", version = "0.3.0" } +ant-protocol = { path = "../ant-protocol", version = "0.3.0" } +ant-registers = { path = "../ant-registers", version = "0.4.4" } bip39 = "2.0.0" blst = "0.3.13" blstrs = "0.7.1" @@ -61,7 +69,7 @@ xor_name = "5.0.0" [dev-dependencies] alloy = { version = "0.7.3", default-features = false, features = ["contract", "json-rpc", "network", "node-bindings", "provider-http", "reqwest-rustls-tls", "rpc-client", "rpc-types", "signer-local", "std"] } -ant-logging = { path = "../ant-logging", version = "0.2.40" } +ant-logging = { path = "../ant-logging", version = "0.2.41" } eyre = "0.6.5" sha2 = "0.10.6" # Do not specify the version field. Release process expects even the local dev deps to be published. @@ -73,7 +81,7 @@ wasm-bindgen-test = "0.3.43" [target.'cfg(target_arch = "wasm32")'.dependencies] console_error_panic_hook = "0.1.7" -evmlib = { path = "../evmlib", version = "0.1.4", features = ["wasm-bindgen"] } +evmlib = { path = "../evmlib", version = "0.1.5", features = ["wasm-bindgen"] } # See https://github.com/sebcrozet/instant/blob/7bd13f51f5c930239fddc0476a837870fb239ed7/README.md#using-instant-for-a-wasm-platform-where-performancenow-is-not-available instant = { version = "0.1", features = ["wasm-bindgen", "inaccurate"] } js-sys = "0.3.70" diff --git a/autonomi/README.md b/autonomi/README.md index 63235554a1..d77c38a81b 100644 --- a/autonomi/README.md +++ b/autonomi/README.md @@ -20,10 +20,10 @@ use autonomi::{Bytes, Client, Wallet}; #[tokio::main] async fn main() -> Result<(), Box> { + let client = Client::init().await?; + // Default wallet of testnet. let key = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"; - - let client = Client::connect(&["/ip4/127.0.0.1/udp/1234/quic-v1".parse()?]).await?; let wallet = Wallet::new_from_private_key(Default::default(), key)?; // Put and fetch data. @@ -51,6 +51,10 @@ let wallet = Wallet::new_from_private_key(EvmNetwork::ArbitrumSepolia, key)?; let wallet = Wallet::new_from_private_key(EvmNetwork::new_custom("", "", ""), key)?; ``` +# Registers + +Registers are deprecated and planned to be replaced by transactions and pointers. Currently, transactions can already be used. For example usage, see [the transaction test](tests/transaction.rs). Pointers are not yet implemented, but will follow soon. + ## Running tests To run the tests, we can run a local network: @@ -130,3 +134,11 @@ Chunk payments address: 0x8464135c8F25Da09e49BC8782676a84730C318bC Deployer wallet private key: 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 Genesis wallet balance: (tokens: 20000000000000000000000000, gas: 9998998011366954730202) ``` + +# WASM + +For documentation on WASM, see [./README_WASM.md]. + +# Python + +For documentation on the Python bindings, see [./README_PYTHON.md]. diff --git a/autonomi/examples/data_and_archive.rs b/autonomi/examples/data_and_archive.rs new file mode 100644 index 0000000000..07fddd560f --- /dev/null +++ b/autonomi/examples/data_and_archive.rs @@ -0,0 +1,37 @@ +use autonomi::{Bytes, Client, Metadata, PrivateArchive}; +use test_utils::evm::get_funded_wallet; +use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter}; + +#[tokio::main] +async fn main() -> eyre::Result<()> { + tracing_subscriber::registry() + .with(fmt::layer()) + .with(EnvFilter::from_env("RUST_LOG")) + .init(); + + let client = Client::init().await?; + let wallet = get_funded_wallet(); + + // Upload 10MiB of random data and verify it by fetching it back. + let data = Bytes::from("Hello, World!"); + let data_map = client.data_put(data.clone(), (&wallet).into()).await?; + let data_fetched = client.data_get(data_map.clone()).await?; + assert_eq!(data, data_fetched); + + // Upload the data as part of an archive, giving it the name `test.txt`. + let mut archive = PrivateArchive::new(); + archive.add_file( + "test.txt".into(), + data_map, + Metadata::new_with_size(data.len() as u64), + ); + + // Upload the archive to the network. + let archive_data_map = client.archive_put(&archive, (&wallet).into()).await?; + let archive_fetched = client.archive_get(archive_data_map).await?; + assert_eq!(archive, archive_fetched); + + println!("Archive uploaded successfully"); + + Ok(()) +} diff --git a/autonomi/examples/put_and_dir_upload.rs b/autonomi/examples/put_and_dir_upload.rs index 874ca57980..4af5e20b11 100644 --- a/autonomi/examples/put_and_dir_upload.rs +++ b/autonomi/examples/put_and_dir_upload.rs @@ -1,12 +1,16 @@ -use autonomi::{Bytes, Client, Wallet}; +use autonomi::{Bytes, Client}; +use test_utils::evm::get_funded_wallet; +use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter}; #[tokio::main] async fn main() -> Result<(), Box> { - // Default wallet of testnet. - let key = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"; + tracing_subscriber::registry() + .with(fmt::layer()) + .with(EnvFilter::from_env("RUST_LOG")) + .init(); - let client = Client::connect(&["/ip4/127.0.0.1/udp/1234/quic-v1".parse()?]).await?; - let wallet = Wallet::new_from_private_key(Default::default(), key)?; + let client = Client::init().await?; + let wallet = get_funded_wallet(); // Put and fetch data. let data_addr = client diff --git a/autonomi/examples/autonomi_advanced.py b/autonomi/python/examples/autonomi_advanced.py similarity index 100% rename from autonomi/examples/autonomi_advanced.py rename to autonomi/python/examples/autonomi_advanced.py diff --git a/autonomi/examples/autonomi_data_registers.py b/autonomi/python/examples/autonomi_data_registers.py similarity index 100% rename from autonomi/examples/autonomi_data_registers.py rename to autonomi/python/examples/autonomi_data_registers.py diff --git a/autonomi/examples/autonomi_example.py b/autonomi/python/examples/autonomi_example.py similarity index 100% rename from autonomi/examples/autonomi_example.py rename to autonomi/python/examples/autonomi_example.py diff --git a/autonomi/examples/autonomi_private_data.py b/autonomi/python/examples/autonomi_private_data.py similarity index 100% rename from autonomi/examples/autonomi_private_data.py rename to autonomi/python/examples/autonomi_private_data.py diff --git a/autonomi/examples/autonomi_private_encryption.py b/autonomi/python/examples/autonomi_private_encryption.py similarity index 100% rename from autonomi/examples/autonomi_private_encryption.py rename to autonomi/python/examples/autonomi_private_encryption.py diff --git a/autonomi/examples/autonomi_vault.py b/autonomi/python/examples/autonomi_vault.py similarity index 100% rename from autonomi/examples/autonomi_vault.py rename to autonomi/python/examples/autonomi_vault.py diff --git a/autonomi/examples/basic.py b/autonomi/python/examples/basic.py similarity index 100% rename from autonomi/examples/basic.py rename to autonomi/python/examples/basic.py diff --git a/autonomi/src/client/data/mod.rs b/autonomi/src/client/data/mod.rs index e1967f0c95..e64c6872e4 100644 --- a/autonomi/src/client/data/mod.rs +++ b/autonomi/src/client/data/mod.rs @@ -160,6 +160,19 @@ fn hash_to_short_string(input: &str) -> String { impl Client { /// Fetch a blob of (private) data from the network + /// + /// # Example + /// + /// ```no_run + /// use autonomi::{Client, Bytes}; + /// # #[tokio::main] + /// # async fn main() -> Result<(), Box> { + /// # let client = Client::init().await?; + /// # let data_map = todo!(); + /// let data_fetched = client.data_get(data_map).await?; + /// # Ok(()) + /// # } + /// ``` pub async fn data_get(&self, data_map: DataMapChunk) -> Result { info!( "Fetching private data from Data Map {:?}", @@ -175,6 +188,22 @@ impl Client { /// The [`DataMapChunk`] is not uploaded to the network, keeping the data private. /// /// Returns the [`DataMapChunk`] containing the map to the encrypted chunks. + /// + /// # Example + /// + /// ```no_run + /// use autonomi::{Client, Bytes}; + /// # #[tokio::main] + /// # async fn main() -> Result<(), Box> { + /// # let client = Client::init().await?; + /// # let wallet = todo!(); + /// let data = Bytes::from("Hello, World"); + /// let data_map = client.data_put(data, wallet).await?; + /// let data_fetched = client.data_get(data_map).await?; + /// assert_eq!(data, data_fetched); + /// # Ok(()) + /// # } + /// ``` pub async fn data_put( &self, data: Bytes, diff --git a/autonomi/src/client/data_private.rs b/autonomi/src/client/data_private.rs deleted file mode 100644 index d1288bb193..0000000000 --- a/autonomi/src/client/data_private.rs +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright 2024 MaidSafe.net limited. -// -// This SAFE Network Software is licensed to you under The General Public License (GPL), version 3. -// Unless required by applicable law or agreed to in writing, the SAFE Network Software distributed -// under the GPL Licence is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. Please review the Licences for the specific language governing -// permissions and limitations relating to use of the SAFE Network Software. - -use std::hash::{DefaultHasher, Hash, Hasher}; - -use ant_evm::Amount; -use ant_protocol::storage::Chunk; -use bytes::Bytes; -use serde::{Deserialize, Serialize}; - -use super::data::{GetError, PutError}; -use crate::client::payment::PaymentOption; -use crate::client::{ClientEvent, UploadSummary}; -use crate::{self_encryption::encrypt, Client}; - -/// Private data on the network can be accessed with this -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct PrivateDataAccess(Chunk); - -impl PrivateDataAccess { - pub fn to_hex(&self) -> String { - hex::encode(self.0.value()) - } - - pub fn from_hex(hex: &str) -> Result { - let data = hex::decode(hex)?; - Ok(Self(Chunk::new(Bytes::from(data)))) - } - - /// Get a private address for [`PrivateDataAccess`]. Note that this is not a network address, it is only used for refering to private data client side. - pub fn address(&self) -> String { - hash_to_short_string(&self.to_hex()) - } -} - -fn hash_to_short_string(input: &str) -> String { - let mut hasher = DefaultHasher::new(); - input.hash(&mut hasher); - let hash_value = hasher.finish(); - hash_value.to_string() -} - -impl Client { - /// Fetch a blob of private data from the network - pub async fn private_data_get(&self, data_map: PrivateDataAccess) -> Result { - info!( - "Fetching private data from Data Map {:?}", - data_map.0.address() - ); - let data = self.fetch_from_data_map_chunk(data_map.0.value()).await?; - - Ok(data) - } - - /// Upload a piece of private data to the network. This data will be self-encrypted. - /// Returns the [`PrivateDataAccess`] containing the map to the encrypted chunks. - /// This data is private and only accessible with the [`PrivateDataAccess`]. - pub async fn private_data_put( - &self, - data: Bytes, - payment_option: PaymentOption, - ) -> Result { - let now = ant_networking::target_arch::Instant::now(); - let (data_map_chunk, chunks) = encrypt(data)?; - debug!("Encryption took: {:.2?}", now.elapsed()); - - // Pay for all chunks - let xor_names: Vec<_> = chunks.iter().map(|chunk| *chunk.name()).collect(); - info!("Paying for {} addresses", xor_names.len()); - let receipt = self - .pay_for_content_addrs(xor_names.into_iter(), payment_option) - .await - .inspect_err(|err| error!("Error paying for data: {err:?}"))?; - - // Upload the chunks with the payments - debug!("Uploading {} chunks", chunks.len()); - - let mut failed_uploads = self - .upload_chunks_with_retries(chunks.iter().collect(), &receipt) - .await; - - // Return the last chunk upload error - if let Some(last_chunk_fail) = failed_uploads.pop() { - tracing::error!( - "Error uploading chunk ({:?}): {:?}", - last_chunk_fail.0.address(), - last_chunk_fail.1 - ); - return Err(last_chunk_fail.1); - } - - let record_count = chunks.len(); - - // Reporting - if let Some(channel) = self.client_event_sender.as_ref() { - let tokens_spent = receipt - .values() - .map(|(_proof, price)| price.as_atto()) - .sum::(); - - let summary = UploadSummary { - record_count, - tokens_spent, - }; - if let Err(err) = channel.send(ClientEvent::UploadComplete(summary)).await { - error!("Failed to send client event: {err:?}"); - } - } - - Ok(PrivateDataAccess(data_map_chunk)) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_hex() { - let data_map = PrivateDataAccess(Chunk::new(Bytes::from_static(b"hello"))); - let hex = data_map.to_hex(); - let data_map2 = PrivateDataAccess::from_hex(&hex).expect("Failed to decode hex"); - assert_eq!(data_map, data_map2); - } -} diff --git a/autonomi/src/client/files/archive.rs b/autonomi/src/client/files/archive.rs index 58f0788059..8aebc1df85 100644 --- a/autonomi/src/client/files/archive.rs +++ b/autonomi/src/client/files/archive.rs @@ -142,7 +142,7 @@ impl PrivateArchive { } /// Serialize to bytes. - pub fn into_bytes(&self) -> Result { + pub fn to_bytes(&self) -> Result { let root_serialized = rmp_serde::to_vec(&self)?; let root_serialized = Bytes::from(root_serialized); @@ -163,11 +163,11 @@ impl Client { /// Upload a [`PrivateArchive`] to the network pub async fn archive_put( &self, - archive: PrivateArchive, + archive: &PrivateArchive, payment_option: PaymentOption, ) -> Result { let bytes = archive - .into_bytes() + .to_bytes() .map_err(|e| PutError::Serialization(format!("Failed to serialize archive: {e:?}")))?; let result = self.data_put(bytes, payment_option).await; debug!("Uploaded private archive {archive:?} to the network and address is {result:?}"); diff --git a/autonomi/src/client/files/archive_public.rs b/autonomi/src/client/files/archive_public.rs index 108d220553..f4b487747f 100644 --- a/autonomi/src/client/files/archive_public.rs +++ b/autonomi/src/client/files/archive_public.rs @@ -104,7 +104,7 @@ impl PublicArchive { } /// Serialize to bytes. - pub fn into_bytes(&self) -> Result { + pub fn to_bytes(&self) -> Result { let root_serialized = rmp_serde::to_vec(&self)?; let root_serialized = Bytes::from(root_serialized); @@ -118,11 +118,10 @@ impl Client { /// # Example /// /// ```no_run - /// # use autonomi::client::{Client, archive::ArchiveAddr}; + /// # use autonomi::{Client, client::files::archive_public::ArchiveAddr}; /// # #[tokio::main] /// # async fn main() -> Result<(), Box> { - /// # let peers = ["/ip4/127.0.0.1/udp/1234/quic-v1".parse()?]; - /// let client = Client::connect(&peers).await?; + /// let client = Client::init().await?; /// let archive = client.archive_get_public(ArchiveAddr::random(&mut rand::thread_rng())).await?; /// # Ok(()) /// # } @@ -139,26 +138,25 @@ impl Client { /// Create simple archive containing `file.txt` pointing to random XOR name. /// /// ```no_run - /// # use autonomi::client::{Client, data::DataAddr, archive::{PublicArchive, ArchiveAddr, Metadata}}; + /// # use autonomi::{Client, client::{data::DataAddr, files::{archive::Metadata, archive_public::{PublicArchive, ArchiveAddr}}}}; /// # use std::path::PathBuf; /// # #[tokio::main] /// # async fn main() -> Result<(), Box> { - /// # let peers = ["/ip4/127.0.0.1/udp/1234/quic-v1".parse()?]; - /// # let client = Client::connect(&peers).await?; + /// # let client = Client::init().await?; /// # let wallet = todo!(); /// let mut archive = PublicArchive::new(); /// archive.add_file(PathBuf::from("file.txt"), DataAddr::random(&mut rand::thread_rng()), Metadata::new_with_size(0)); - /// let address = client.archive_put_public(archive, &wallet).await?; + /// let address = client.archive_put_public(&archive, &wallet).await?; /// # Ok(()) /// # } /// ``` pub async fn archive_put_public( &self, - archive: PublicArchive, + archive: &PublicArchive, wallet: &EvmWallet, ) -> Result { let bytes = archive - .into_bytes() + .to_bytes() .map_err(|e| PutError::Serialization(format!("Failed to serialize archive: {e:?}")))?; let result = self.data_put_public(bytes, wallet.into()).await; debug!("Uploaded archive {archive:?} to the network and the address is {result:?}"); @@ -168,7 +166,7 @@ impl Client { /// Get the cost to upload an archive pub async fn archive_cost(&self, archive: PublicArchive) -> Result { let bytes = archive - .into_bytes() + .to_bytes() .map_err(|e| CostError::Serialization(format!("Failed to serialize archive: {e:?}")))?; let result = self.data_cost(bytes).await; debug!("Calculated the cost to upload archive {archive:?} is {result:?}"); diff --git a/autonomi/src/client/files/fs.rs b/autonomi/src/client/files/fs.rs index 0d41f0744d..2428f2d344 100644 --- a/autonomi/src/client/files/fs.rs +++ b/autonomi/src/client/files/fs.rs @@ -173,7 +173,7 @@ impl Client { wallet: &EvmWallet, ) -> Result { let archive = self.dir_upload(dir_path, wallet).await?; - let archive_addr = self.archive_put(archive, wallet.into()).await?; + let archive_addr = self.archive_put(&archive, wallet.into()).await?; Ok(archive_addr) } diff --git a/autonomi/src/client/files/fs_public.rs b/autonomi/src/client/files/fs_public.rs index 52e79c300a..a35cce82f2 100644 --- a/autonomi/src/client/files/fs_public.rs +++ b/autonomi/src/client/files/fs_public.rs @@ -118,7 +118,7 @@ impl Client { wallet: &EvmWallet, ) -> Result { let archive = self.dir_upload_public(dir_path, wallet).await?; - let archive_addr = self.archive_put_public(archive, wallet).await?; + let archive_addr = self.archive_put_public(&archive, wallet).await?; Ok(archive_addr) } diff --git a/autonomi/src/client/mod.rs b/autonomi/src/client/mod.rs index fae0a87ba8..d118a5f065 100644 --- a/autonomi/src/client/mod.rs +++ b/autonomi/src/client/mod.rs @@ -34,12 +34,11 @@ pub mod wasm; mod rate_limiter; mod utils; -use ant_bootstrap::{BootstrapCacheConfig, BootstrapCacheStore}; +use ant_bootstrap::{BootstrapCacheConfig, BootstrapCacheStore, PeersArgs}; pub use ant_evm::Amount; - use ant_evm::EvmNetwork; use ant_networking::{interval, multiaddr_is_global, Network, NetworkBuilder, NetworkEvent}; -use ant_protocol::{version::IDENTIFY_PROTOCOL_STR, CLOSE_GROUP_SIZE}; +use ant_protocol::version::IDENTIFY_PROTOCOL_STR; use libp2p::{identity::Keypair, Multiaddr}; use std::{collections::HashSet, sync::Arc, time::Duration}; use tokio::sync::mpsc; @@ -49,18 +48,20 @@ pub const CONNECT_TIMEOUT_SECS: u64 = 10; const CLIENT_EVENT_CHANNEL_SIZE: usize = 100; -/// Represents a connection to the Autonomi network. +// Amount of peers to confirm into our routing table before we consider the client ready. +pub use ant_protocol::CLOSE_GROUP_SIZE; + +/// Represents a client for the Autonomi network. /// /// # Example /// -/// To connect to the network, use [`Client::connect`]. +/// To start interacting with the network, use [`Client::init`]. /// /// ```no_run /// # use autonomi::client::Client; /// # #[tokio::main] /// # async fn main() -> Result<(), Box> { -/// let peers = ["/ip4/127.0.0.1/udp/1234/quic-v1".parse()?]; -/// let client = Client::connect(&peers).await?; +/// let client = Client::init().await?; /// # Ok(()) /// # } /// ``` @@ -71,18 +72,143 @@ pub struct Client { pub(crate) evm_network: EvmNetwork, } -/// Error returned by [`Client::connect`]. +/// Configuration for [`Client::init_with_config`]. +#[derive(Debug, Clone)] +pub struct ClientConfig { + /// Whether we're expected to connect to a local network. + /// + /// If `local` feature is enabled, [`ClientConfig::default()`] will set this to `true`. + pub local: bool, + + /// List of peers to connect to. + /// + /// If not provided, the client will use the default bootstrap peers. + pub peers: Option>, +} + +impl Default for ClientConfig { + fn default() -> Self { + Self { + #[cfg(feature = "local")] + local: true, + #[cfg(not(feature = "local"))] + local: false, + peers: None, + } + } +} + +/// Error returned by [`Client::init`]. #[derive(Debug, thiserror::Error)] pub enum ConnectError { - /// Did not manage to connect to enough peers in time. - #[error("Could not connect to enough peers in time.")] + /// Did not manage to populate the routing table with enough peers. + #[error("Failed to populate our routing table with enough peers in time")] TimedOut, + /// Same as [`ConnectError::TimedOut`] but with a list of incompatible protocols. - #[error("Could not connect to peers due to incompatible protocol: {0:?}")] + #[error("Failed to populate our routing table due to incompatible protocol: {0:?}")] TimedOutWithIncompatibleProtocol(HashSet, String), + + /// An error occurred while bootstrapping the client. + #[error("Failed to bootstrap the client")] + Bootstrap(#[from] ant_bootstrap::Error), } impl Client { + /// Initialize the client with default configuration. + /// + /// See [`Client::init_with_config`]. + pub async fn init() -> Result { + Self::init_with_config(Default::default()).await + } + + /// Initialize a client that is configured to be local. + /// + /// See [`Client::init_with_config`]. + pub async fn init_local() -> Result { + Self::init_with_config(ClientConfig { + local: true, + ..Default::default() + }) + .await + } + + /// Initialize a client that bootstraps from a list of peers. + /// + /// If any of the provided peers is a global address, the client will not be local. + /// + /// ```no_run + /// # use autonomi::Client; + /// # #[tokio::main] + /// # async fn main() -> Result<(), Box> { + /// // Will set `local` to true. + /// let client = Client::init_with_peers(vec!["/ip4/127.0.0.1/udp/1234/quic-v1".parse()?]).await?; + /// # Ok(()) + /// # } + /// ``` + pub async fn init_with_peers(peers: Vec) -> Result { + // Any global address makes the client non-local + let local = !peers.iter().any(multiaddr_is_global); + + Self::init_with_config(ClientConfig { + local, + peers: Some(peers), + }) + .await + } + + /// Initialize the client with the given configuration. + /// + /// This will block until [`CLOSE_GROUP_SIZE`] have been added to the routing table. + /// + /// See [`ClientConfig`]. + /// + /// ```no_run + /// use autonomi::client::Client; + /// # #[tokio::main] + /// # async fn main() -> Result<(), Box> { + /// let client = Client::init_with_config(Default::default()).await?; + /// # Ok(()) + /// # } + /// ``` + pub async fn init_with_config(config: ClientConfig) -> Result { + let (network, event_receiver) = build_client_and_run_swarm(config.local); + + let peers_args = PeersArgs { + disable_mainnet_contacts: config.local, + addrs: config.peers.unwrap_or_default(), + ..Default::default() + }; + + let peers = match peers_args.get_addrs(None, None).await { + Ok(peers) => peers, + Err(e) => return Err(e.into()), + }; + + let network_clone = network.clone(); + let peers = peers.to_vec(); + let _handle = ant_networking::target_arch::spawn(async move { + for addr in peers { + if let Err(err) = network_clone.dial(addr.clone()).await { + error!("Failed to dial addr={addr} with err: {err:?}"); + eprintln!("addr={addr} Failed to dial: {err:?}"); + }; + } + }); + + // Wait until we have added a few peers to our routing table. + let (sender, receiver) = futures::channel::oneshot::channel(); + ant_networking::target_arch::spawn(handle_event_receiver(event_receiver, sender)); + receiver.await.expect("sender should not close")?; + debug!("Enough peers were added to our routing table, initialization complete"); + + Ok(Self { + network, + client_event_sender: Arc::new(None), + evm_network: Default::default(), + }) + } + /// Connect to the network. /// /// This will timeout after [`CONNECT_TIMEOUT_SECS`] secs. @@ -92,10 +218,15 @@ impl Client { /// # #[tokio::main] /// # async fn main() -> Result<(), Box> { /// let peers = ["/ip4/127.0.0.1/udp/1234/quic-v1".parse()?]; + /// #[allow(deprecated)] /// let client = Client::connect(&peers).await?; /// # Ok(()) /// # } /// ``` + #[deprecated( + since = "0.2.4", + note = "Use [`Client::init`] or [`Client::init_with_config`] instead" + )] pub async fn connect(peers: &[Multiaddr]) -> Result { // Any global address makes the client non-local let local = !peers.iter().any(multiaddr_is_global); @@ -120,6 +251,12 @@ impl Client { receiver.await.expect("sender should not close")?; debug!("Client is connected to the network"); + // With the switch to the new bootstrap cache scheme, + // Seems the too many `initial dial`s could result in failure, + // when startup quoting/upload tasks got started up immediatly. + // Hence, put in a forced wait to allow `initial network discovery` to be completed. + ant_networking::target_arch::sleep(Duration::from_secs(5)).await; + Ok(Self { network, client_event_sender: Arc::new(None), diff --git a/autonomi/src/client/quote.rs b/autonomi/src/client/quote.rs index 9794f165d7..38dfd7f6fd 100644 --- a/autonomi/src/client/quote.rs +++ b/autonomi/src/client/quote.rs @@ -11,7 +11,7 @@ use crate::client::rate_limiter::RateLimiter; use ant_evm::payment_vault::get_market_price; use ant_evm::{Amount, EvmNetwork, PaymentQuote, QuotePayment, QuotingMetrics}; use ant_networking::{Network, NetworkError}; -use ant_protocol::{storage::ChunkAddress, NetworkAddress}; +use ant_protocol::{storage::ChunkAddress, NetworkAddress, CLOSE_GROUP_SIZE}; use libp2p::PeerId; use std::collections::HashMap; use xor_name::XorName; @@ -159,6 +159,14 @@ async fn fetch_store_quote_with_retries( loop { match fetch_store_quote(network, content_addr).await { Ok(quote) => { + if quote.len() < CLOSE_GROUP_SIZE { + retries += 1; + error!("Error while fetching store quote: not enough quotes ({}/{CLOSE_GROUP_SIZE}), retry #{retries}, quotes {quote:?}", + quote.len()); + if retries > 2 { + break Err(CostError::CouldNotGetStoreQuote(content_addr)); + } + } break Ok((content_addr, quote)); } Err(err) if retries < 2 => { @@ -172,6 +180,9 @@ async fn fetch_store_quote_with_retries( break Err(CostError::CouldNotGetStoreQuote(content_addr)); } } + // Shall have a sleep between retries to avoid choking the network. + // This shall be rare to happen though. + std::thread::sleep(std::time::Duration::from_secs(5)); } } diff --git a/autonomi/src/client/registers.rs b/autonomi/src/client/registers.rs index d2ae5f203a..dc56e37b45 100644 --- a/autonomi/src/client/registers.rs +++ b/autonomi/src/client/registers.rs @@ -6,6 +6,8 @@ // KIND, either express or implied. Please review the Licences for the specific language governing // permissions and limitations relating to use of the SAFE Network Software. +#![allow(deprecated)] + use crate::client::data::PayError; use crate::client::Client; use crate::client::ClientEvent; @@ -53,6 +55,10 @@ pub enum RegisterError { PayeesMissing, } +#[deprecated( + since = "0.2.4", + note = "Use transactions instead (see Client::transaction_put)" +)] #[derive(Clone, Debug)] pub struct Register { signed_reg: SignedRegister, @@ -122,6 +128,10 @@ impl Register { } } +#[deprecated( + since = "0.2.4", + note = "Use transactions instead (see Client::transaction_put)" +)] impl Client { /// Generate a new register key pub fn register_generate_key() -> RegisterSecretKey { diff --git a/autonomi/src/client/wasm.rs b/autonomi/src/client/wasm.rs index 0f9a2ea802..ce49ba83d2 100644 --- a/autonomi/src/client/wasm.rs +++ b/autonomi/src/client/wasm.rs @@ -70,7 +70,7 @@ impl JsClient { .map(|peer| peer.parse()) .collect::, _>>()?; - let client = super::Client::connect(&peers).await?; + let client = super::Client::init_with_peers(peers).await?; Ok(JsClient(client)) } @@ -263,10 +263,7 @@ mod archive { archive: &JsArchive, wallet: &JsWallet, ) -> Result { - let addr = self - .0 - .archive_put_public(archive.0.clone(), &wallet.0) - .await?; + let addr = self.0.archive_put_public(&archive.0, &wallet.0).await?; Ok(addr_to_str(addr)) } @@ -348,10 +345,7 @@ mod archive_private { archive: &JsPrivateArchive, wallet: &JsWallet, ) -> Result { - let private_archive_access = self - .0 - .archive_put(archive.0.clone(), (&wallet.0).into()) - .await?; + let private_archive_access = self.0.archive_put(&archive.0, (&wallet.0).into()).await?; let js_value = serde_wasm_bindgen::to_value(&private_archive_access)?; @@ -370,10 +364,7 @@ mod archive_private { ) -> Result { let receipt: Receipt = serde_wasm_bindgen::from_value(receipt)?; - let private_archive_access = self - .0 - .archive_put(archive.0.clone(), receipt.into()) - .await?; + let private_archive_access = self.0.archive_put(&archive.0, receipt.into()).await?; let js_value = serde_wasm_bindgen::to_value(&private_archive_access)?; diff --git a/autonomi/src/lib.rs b/autonomi/src/lib.rs index f612146f1d..81ff866006 100644 --- a/autonomi/src/lib.rs +++ b/autonomi/src/lib.rs @@ -10,12 +10,12 @@ //! //! # Example //! -//! ```rust +//! ```no_run //! use autonomi::{Bytes, Client, Wallet}; //! //! #[tokio::main] //! async fn main() -> Result<(), Box> { -//! let client = Client::connect(&["/ip4/127.0.0.1/udp/1234/quic-v1".parse()?]).await?; +//! let client = Client::init().await?; //! //! // Default wallet of testnet. //! let key = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"; @@ -26,7 +26,7 @@ //! let _data_fetched = client.data_get_public(data_addr).await?; //! //! // Put and fetch directory from local file system. -//! let dir_addr = client.dir_upload_public("files/to/upload".into(), &wallet).await?; +//! let dir_addr = client.dir_and_archive_upload_public("files/to/upload".into(), &wallet).await?; //! client.dir_download_public(dir_addr, "files/downloaded".into()).await?; //! //! Ok(()) @@ -76,7 +76,7 @@ pub use bytes::Bytes; pub use libp2p::Multiaddr; #[doc(inline)] -pub use client::{files::archive::PrivateArchive, Client}; +pub use client::{files::archive::Metadata, files::archive::PrivateArchive, Client, ClientConfig}; #[cfg(feature = "extension-module")] mod python; diff --git a/autonomi/src/python.rs b/autonomi/src/python.rs index 0c28401b55..1f1c4d443b 100644 --- a/autonomi/src/python.rs +++ b/autonomi/src/python.rs @@ -31,9 +31,11 @@ impl PyClient { pyo3::exceptions::PyValueError::new_err(format!("Invalid multiaddr: {e}")) })?; - let client = rt.block_on(RustClient::connect(&peers)).map_err(|e| { - pyo3::exceptions::PyValueError::new_err(format!("Failed to connect: {e}")) - })?; + let client = rt + .block_on(RustClient::init_with_peers(peers)) + .map_err(|e| { + pyo3::exceptions::PyValueError::new_err(format!("Failed to connect: {e}")) + })?; Ok(Self { inner: client }) } diff --git a/autonomi/tests/evm/file.rs b/autonomi/tests/evm/file.rs index 3e2cbe0e5f..0c2aff9fe6 100644 --- a/autonomi/tests/evm/file.rs +++ b/autonomi/tests/evm/file.rs @@ -20,7 +20,7 @@ mod test { let _log_appender_guard = ant_logging::LogBuilder::init_single_threaded_tokio_test("file", false); - let mut client = Client::connect(&[]).await.unwrap(); + let mut client = Client::init_local().await?; let mut wallet = get_funded_wallet(); // let data = common::gen_random_data(1024 * 1024 * 1000); @@ -47,7 +47,7 @@ mod test { async fn file_into_vault() -> eyre::Result<()> { common::enable_logging(); - let mut client = Client::connect(&[]) + let mut client = Client::init() .await? .with_vault_entropy(Bytes::from("at least 32 bytes of entropy here"))?; @@ -66,7 +66,7 @@ mod test { ); // now assert over the stored account packet - let new_client = Client::connect(&[]) + let new_client = Client::init() .await? .with_vault_entropy(Bytes::from("at least 32 bytes of entropy here"))?; diff --git a/autonomi/tests/external_signer.rs b/autonomi/tests/external_signer.rs index 6b918f9370..755a1cac8f 100644 --- a/autonomi/tests/external_signer.rs +++ b/autonomi/tests/external_signer.rs @@ -15,7 +15,7 @@ use bytes::Bytes; use std::collections::BTreeMap; use std::time::Duration; use test_utils::evm::get_funded_wallet; -use test_utils::{gen_random_data, peers_from_env}; +use test_utils::gen_random_data; use tokio::time::sleep; use xor_name::XorName; @@ -103,7 +103,7 @@ async fn external_signer_put() -> eyre::Result<()> { let _log_appender_guard = LogBuilder::init_single_threaded_tokio_test("external_signer_put", false); - let client = Client::connect(&peers_from_env()?).await?; + let client = Client::init_local().await?; let wallet = get_funded_wallet(); let data = gen_random_data(1024 * 1024 * 10); @@ -120,13 +120,13 @@ async fn external_signer_put() -> eyre::Result<()> { Metadata::new_with_size(data.len() as u64), ); - let archive_serialized = private_archive.into_bytes()?; + let archive_serialized = private_archive.to_bytes()?; let receipt = pay_for_data(&client, &wallet, archive_serialized.clone()).await?; sleep(Duration::from_secs(5)).await; - let private_archive_access = client.archive_put(private_archive, receipt.into()).await?; + let private_archive_access = client.archive_put(&private_archive, receipt.into()).await?; let vault_key = VaultSecretKey::random(); diff --git a/autonomi/tests/fs.rs b/autonomi/tests/fs.rs index 1b8b59f801..926baeb4fd 100644 --- a/autonomi/tests/fs.rs +++ b/autonomi/tests/fs.rs @@ -15,7 +15,7 @@ use sha2::{Digest, Sha256}; use std::fs::File; use std::io::{BufReader, Read}; use std::time::Duration; -use test_utils::{evm::get_funded_wallet, peers_from_env}; +use test_utils::evm::get_funded_wallet; use tokio::time::sleep; use walkdir::WalkDir; @@ -26,7 +26,7 @@ async fn dir_upload_download() -> Result<()> { let _log_appender_guard = LogBuilder::init_single_threaded_tokio_test("dir_upload_download", false); - let client = Client::connect(&peers_from_env()?).await?; + let client = Client::init_local().await?; let wallet = get_funded_wallet(); let addr = client @@ -81,7 +81,7 @@ fn compute_dir_sha256(dir: &str) -> Result { async fn file_into_vault() -> Result<()> { let _log_appender_guard = LogBuilder::init_single_threaded_tokio_test("file", false); - let client = Client::connect(&peers_from_env()?).await?; + let client = Client::init_local().await?; let wallet = get_funded_wallet(); let client_sk = bls::SecretKey::random(); @@ -93,16 +93,11 @@ async fn file_into_vault() -> Result<()> { let archive = client.archive_get_public(addr).await?; let set_version = 0; client - .write_bytes_to_vault( - archive.into_bytes()?, - wallet.into(), - &client_sk, - set_version, - ) + .write_bytes_to_vault(archive.to_bytes()?, wallet.into(), &client_sk, set_version) .await?; // now assert over the stored account packet - let new_client = Client::connect(&[]).await?; + let new_client = Client::init_local().await?; let (ap, got_version) = new_client.fetch_and_decrypt_vault(&client_sk).await?; assert_eq!(set_version, got_version); diff --git a/autonomi/tests/put.rs b/autonomi/tests/put.rs index f5d411e691..df9a9fbce8 100644 --- a/autonomi/tests/put.rs +++ b/autonomi/tests/put.rs @@ -9,22 +9,18 @@ use ant_logging::LogBuilder; use autonomi::Client; use eyre::Result; -use std::time::Duration; -use test_utils::{evm::get_funded_wallet, gen_random_data, peers_from_env}; -use tokio::time::sleep; +use test_utils::{evm::get_funded_wallet, gen_random_data}; #[tokio::test] async fn put() -> Result<()> { let _log_appender_guard = LogBuilder::init_single_threaded_tokio_test("put", false); - let client = Client::connect(&peers_from_env()?).await?; + let client = Client::init_local().await?; let wallet = get_funded_wallet(); let data = gen_random_data(1024 * 1024 * 10); let addr = client.data_put_public(data.clone(), wallet.into()).await?; - sleep(Duration::from_secs(10)).await; - let data_fetched = client.data_get_public(addr).await?; assert_eq!(data, data_fetched, "data fetched should match data put"); diff --git a/autonomi/tests/register.rs b/autonomi/tests/register.rs index e698809d46..0709779d5c 100644 --- a/autonomi/tests/register.rs +++ b/autonomi/tests/register.rs @@ -7,6 +7,7 @@ // permissions and limitations relating to use of the SAFE Network Software. #![cfg(feature = "registers")] +#![allow(deprecated)] use ant_logging::LogBuilder; use autonomi::Client; @@ -14,14 +15,14 @@ use bytes::Bytes; use eyre::Result; use rand::Rng; use std::time::Duration; -use test_utils::{evm::get_funded_wallet, peers_from_env}; +use test_utils::evm::get_funded_wallet; use tokio::time::sleep; #[tokio::test] async fn register() -> Result<()> { let _log_appender_guard = LogBuilder::init_single_threaded_tokio_test("register", false); - let client = Client::connect(&peers_from_env()?).await?; + let client = Client::init_local().await?; let wallet = get_funded_wallet(); // Owner key of the register. diff --git a/autonomi/tests/transaction.rs b/autonomi/tests/transaction.rs index 76f0bd760d..b0523618b3 100644 --- a/autonomi/tests/transaction.rs +++ b/autonomi/tests/transaction.rs @@ -10,13 +10,13 @@ use ant_logging::LogBuilder; use ant_protocol::storage::Transaction; use autonomi::{client::transactions::TransactionError, Client}; use eyre::Result; -use test_utils::{evm::get_funded_wallet, peers_from_env}; +use test_utils::evm::get_funded_wallet; #[tokio::test] async fn transaction_put() -> Result<()> { let _log_appender_guard = LogBuilder::init_single_threaded_tokio_test("transaction", false); - let client = Client::connect(&peers_from_env()?).await?; + let client = Client::init_local().await?; let wallet = get_funded_wallet(); let key = bls::SecretKey::random(); diff --git a/autonomi/tests/wasm.rs b/autonomi/tests/wasm.rs index efdc8d179e..d0531c0999 100644 --- a/autonomi/tests/wasm.rs +++ b/autonomi/tests/wasm.rs @@ -12,7 +12,7 @@ use std::time::Duration; use ant_networking::target_arch::sleep; use autonomi::Client; -use test_utils::{evm::get_funded_wallet, gen_random_data, peers_from_env}; +use test_utils::{evm::get_funded_wallet, gen_random_data}; use wasm_bindgen_test::*; wasm_bindgen_test_configure!(run_in_browser); @@ -21,7 +21,7 @@ wasm_bindgen_test_configure!(run_in_browser); async fn put() -> Result<(), Box> { enable_logging_wasm("ant-networking,autonomi,wasm"); - let client = Client::connect(&peers_from_env()?).await?; + let client = Client::init_local().await?; let wallet = get_funded_wallet(); let data = gen_random_data(1024 * 1024 * 10); diff --git a/evm-testnet/Cargo.toml b/evm-testnet/Cargo.toml index 42aaf737b6..da64a097db 100644 --- a/evm-testnet/Cargo.toml +++ b/evm-testnet/Cargo.toml @@ -6,13 +6,13 @@ homepage = "https://maidsafe.net" license = "GPL-3.0" name = "evm-testnet" repository = "https://github.com/maidsafe/autonomi" -version = "0.1.4" +version = "0.1.5" [dependencies] -ant-evm = { path = "../ant-evm", version = "0.1.4" } +ant-evm = { path = "../ant-evm", version = "0.1.5" } clap = { version = "4.5", features = ["derive"] } dirs-next = "~2.0.0" -evmlib = { path = "../evmlib", version = "0.1.4" } +evmlib = { path = "../evmlib", version = "0.1.5" } tokio = { version = "1.40", features = ["rt-multi-thread", "signal"] } [lints] diff --git a/evmlib/Cargo.toml b/evmlib/Cargo.toml index 5e4a5b805e..770c23788e 100644 --- a/evmlib/Cargo.toml +++ b/evmlib/Cargo.toml @@ -6,7 +6,7 @@ homepage = "https://maidsafe.net" license = "GPL-3.0" name = "evmlib" repository = "https://github.com/maidsafe/safe_network" -version = "0.1.4" +version = "0.1.5" [features] wasm-bindgen = ["alloy/wasm-bindgen"] diff --git a/nat-detection/Cargo.toml b/nat-detection/Cargo.toml index 78290ad748..cbca793e61 100644 --- a/nat-detection/Cargo.toml +++ b/nat-detection/Cargo.toml @@ -7,7 +7,7 @@ license = "GPL-3.0" name = "nat-detection" readme = "README.md" repository = "https://github.com/maidsafe/autonomi" -version = "0.2.11" +version = "0.2.12" [[bin]] name = "nat-detection" @@ -17,9 +17,9 @@ path = "src/main.rs" nightly = [] [dependencies] -ant-build-info = { path = "../ant-build-info", version = "0.1.19" } -ant-networking = { path = "../ant-networking", version = "0.19.5" } -ant-protocol = { path = "../ant-protocol", version = "0.17.15" } +ant-build-info = { path = "../ant-build-info", version = "0.1.20" } +ant-networking = { path = "../ant-networking", version = "0.3.0" } +ant-protocol = { path = "../ant-protocol", version = "0.3.0" } clap = { version = "4.5.4", features = ["derive"] } clap-verbosity-flag = "2.2.0" color-eyre = { version = "0.6", default-features = false } diff --git a/node-launchpad/Cargo.toml b/node-launchpad/Cargo.toml index d1605468ad..1709de4dac 100644 --- a/node-launchpad/Cargo.toml +++ b/node-launchpad/Cargo.toml @@ -2,7 +2,7 @@ authors = ["MaidSafe Developers "] description = "TUI for running nodes on the Autonomi network" name = "node-launchpad" -version = "0.4.5" +version = "0.5.0" edition = "2021" license = "GPL-3.0" homepage = "https://maidsafe.net" @@ -19,12 +19,12 @@ nightly = [] [dependencies] ant-bootstrap = { path = "../ant-bootstrap", version = "0.1.0" } -ant-build-info = { path = "../ant-build-info", version = "0.1.19" } -ant-evm = { path = "../ant-evm", version = "0.1.4" } -ant-node-manager = { version = "0.11.3", path = "../ant-node-manager" } -ant-protocol = { path = "../ant-protocol", version = "0.17.15" } +ant-build-info = { path = "../ant-build-info", version = "0.1.20" } +ant-evm = { path = "../ant-evm", version = "0.1.5" } +ant-node-manager = { version = "0.11.4", path = "../ant-node-manager" } +ant-protocol = { path = "../ant-protocol", version = "0.3.0" } ant-releases = { version = "0.4.0" } -ant-service-management = { version = "0.4.3", path = "../ant-service-management" } +ant-service-management = { version = "0.4.4", path = "../ant-service-management" } arboard = "3.4.1" atty = "0.2.14" better-panic = "0.3.0" diff --git a/node-launchpad/src/app.rs b/node-launchpad/src/app.rs index 605c51efd3..457ba41f6d 100644 --- a/node-launchpad/src/app.rs +++ b/node-launchpad/src/app.rs @@ -55,6 +55,7 @@ impl App { peers_args: PeersArgs, antnode_path: Option, app_data_path: Option, + network_id: Option, ) -> Result { // Configurations let app_data = AppData::load(app_data_path)?; @@ -93,6 +94,7 @@ impl App { allocated_disk_space: app_data.nodes_to_start, rewards_address: app_data.discord_username.clone(), peers_args, + network_id, antnode_path, data_dir_path, connection_mode, @@ -319,6 +321,7 @@ mod tests { use super::*; use ant_bootstrap::PeersArgs; use color_eyre::eyre::Result; + use serde_json::json; use std::io::Cursor; use std::io::Write; use tempfile::tempdir; @@ -331,22 +334,17 @@ mod tests { let mountpoint = get_primary_mount_point(); - // Create a valid configuration file with all fields - let valid_config = format!( - r#" - {{ + let config = json!({ "discord_username": "happy_user", "nodes_to_start": 5, - "storage_mountpoint": "{}", + "storage_mountpoint": mountpoint.display().to_string(), "storage_drive": "C:", "connection_mode": "Automatic", "port_from": 12000, "port_to": 13000 - }} - "#, - mountpoint.display() - ); + }); + let valid_config = serde_json::to_string_pretty(&config)?; std::fs::write(&config_path, valid_config)?; // Create default PeersArgs @@ -356,7 +354,7 @@ mod tests { let mut output = Cursor::new(Vec::new()); // Create and run the App, capturing its output - let app_result = App::new(60.0, 60.0, peers_args, None, Some(config_path)).await; + let app_result = App::new(60.0, 60.0, peers_args, None, Some(config_path), None).await; match app_result { Ok(app) => { @@ -417,7 +415,8 @@ mod tests { let mut output = Cursor::new(Vec::new()); // Create and run the App, capturing its output - let app_result = App::new(60.0, 60.0, peers_args, None, Some(test_app_data_path)).await; + let app_result = + App::new(60.0, 60.0, peers_args, None, Some(test_app_data_path), None).await; match app_result { Ok(app) => { @@ -472,8 +471,15 @@ mod tests { let mut output = Cursor::new(Vec::new()); // Create and run the App, capturing its output - let app_result = - App::new(60.0, 60.0, peers_args, None, Some(non_existent_config_path)).await; + let app_result = App::new( + 60.0, + 60.0, + peers_args, + None, + Some(non_existent_config_path), + None, + ) + .await; match app_result { Ok(app) => { @@ -535,7 +541,7 @@ mod tests { let peers_args = PeersArgs::default(); // Create and run the App, capturing its output - let app_result = App::new(60.0, 60.0, peers_args, None, Some(config_path)).await; + let app_result = App::new(60.0, 60.0, peers_args, None, Some(config_path), None).await; // Could be that the mountpoint doesn't exists // or that the user doesn't have permissions to access it @@ -576,7 +582,8 @@ mod tests { let peers_args = PeersArgs::default(); // Create and run the App - let app_result = App::new(60.0, 60.0, peers_args, None, Some(test_app_data_path)).await; + let app_result = + App::new(60.0, 60.0, peers_args, None, Some(test_app_data_path), None).await; match app_result { Ok(app) => { diff --git a/node-launchpad/src/bin/tui/main.rs b/node-launchpad/src/bin/tui/main.rs index 969e2c811a..46d733681d 100644 --- a/node-launchpad/src/bin/tui/main.rs +++ b/node-launchpad/src/bin/tui/main.rs @@ -26,42 +26,44 @@ use std::{env, path::PathBuf}; #[derive(Parser, Debug)] #[command(disable_version_flag = true)] pub struct Cli { - #[arg( - short, - long, - value_name = "FLOAT", - help = "Tick rate, i.e. number of ticks per second", - default_value_t = 1.0 - )] - pub tick_rate: f64, - - #[arg( - short, - long, - value_name = "FLOAT", - help = "Frame rate, i.e. number of frames per second", - default_value_t = 60.0 - )] - pub frame_rate: f64, - /// Provide a path for the antnode binary to be used by the service. /// /// Useful for creating the service using a custom built binary. #[clap(long)] antnode_path: Option, - #[command(flatten)] - pub(crate) peers: PeersArgs, - /// Print the crate version. #[clap(long)] crate_version: bool, + /// Specify the network ID to use. This will allow you to run the node on a different network. + /// + /// By default, the network ID is set to 1, which represents the mainnet. + #[clap(long, verbatim_doc_comment)] + network_id: Option, + + /// Frame rate, i.e. number of frames per second + #[arg(short, long, value_name = "FLOAT", default_value_t = 60.0)] + frame_rate: f64, + + /// Provide a path for the antnode binary to be used by the service. + /// + /// Useful for creating the service using a custom built binary. + #[clap(long)] + path: Option, + + #[command(flatten)] + peers: PeersArgs, + /// Print the package version. #[clap(long)] #[cfg(not(feature = "nightly"))] package_version: bool, + /// Tick rate, i.e. number of ticks per second + #[arg(short, long, value_name = "FLOAT", default_value_t = 1.0)] + tick_rate: f64, + /// Print the version. #[clap(long)] version: bool, @@ -129,7 +131,8 @@ async fn main() -> Result<()> { args.frame_rate, args.peers, args.antnode_path, - None, + args.path, + args.network_id, ) .await?; app.run().await?; diff --git a/node-launchpad/src/components/status.rs b/node-launchpad/src/components/status.rs index 1899bbd9bc..5ce84cf6fc 100644 --- a/node-launchpad/src/components/status.rs +++ b/node-launchpad/src/components/status.rs @@ -83,6 +83,8 @@ pub struct Status<'a> { // Nodes node_services: Vec, items: Option>>, + /// To pass into node services. + network_id: Option, // Node Management node_management: NodeManagement, // Amount of nodes @@ -117,13 +119,14 @@ pub enum LockRegistryState { pub struct StatusConfig { pub allocated_disk_space: usize, - pub rewards_address: String, - pub peers_args: PeersArgs, pub antnode_path: Option, - pub data_dir_path: PathBuf, pub connection_mode: ConnectionMode, + pub data_dir_path: PathBuf, + pub network_id: Option, + pub peers_args: PeersArgs, pub port_from: Option, pub port_to: Option, + pub rewards_address: String, } impl Status<'_> { @@ -135,6 +138,7 @@ impl Status<'_> { active: true, is_nat_status_determined: false, error_while_running_nat_detection: 0, + network_id: config.network_id, node_stats: NodeStats::default(), node_stats_last_update: Instant::now(), node_services: Default::default(), @@ -614,16 +618,17 @@ impl Component for Status<'_> { let action_sender = self.get_actions_sender()?; let maintain_nodes_args = MaintainNodesArgs { + action_sender: action_sender.clone(), + antnode_path: self.antnode_path.clone(), + connection_mode: self.connection_mode, count: self.nodes_to_start as u16, + data_dir_path: Some(self.data_dir_path.clone()), + network_id: self.network_id, owner: self.rewards_address.clone(), peers_args: self.peers_args.clone(), - run_nat_detection: self.should_we_run_nat_detection(), - antnode_path: self.antnode_path.clone(), - data_dir_path: Some(self.data_dir_path.clone()), - action_sender: action_sender.clone(), - connection_mode: self.connection_mode, port_range: Some(port_range), rewards_address: self.rewards_address.clone(), + run_nat_detection: self.should_we_run_nat_detection(), }; debug!("Calling maintain_n_running_nodes"); diff --git a/node-launchpad/src/node_mgmt.rs b/node-launchpad/src/node_mgmt.rs index 735f049fea..18780b4f2b 100644 --- a/node-launchpad/src/node_mgmt.rs +++ b/node-launchpad/src/node_mgmt.rs @@ -122,16 +122,17 @@ async fn stop_nodes(services: Vec, action_sender: UnboundedSender, + pub antnode_path: Option, + pub connection_mode: ConnectionMode, pub count: u16, + pub data_dir_path: Option, + pub network_id: Option, pub owner: String, pub peers_args: PeersArgs, - pub run_nat_detection: bool, - pub antnode_path: Option, - pub data_dir_path: Option, - pub action_sender: UnboundedSender, - pub connection_mode: ConnectionMode, pub port_range: Option, pub rewards_address: String, + pub run_nat_detection: bool, } /// Maintain the specified number of nodes @@ -289,16 +290,17 @@ async fn load_node_registry( } struct NodeConfig { + antnode_path: Option, auto_set_nat_flags: bool, - upnp: bool, - home_network: bool, - custom_ports: Option, - owner: Option, count: u16, + custom_ports: Option, data_dir_path: Option, + home_network: bool, + network_id: Option, + owner: Option, peers_args: PeersArgs, - antnode_path: Option, rewards_address: String, + upnp: bool, } /// Run the NAT detection process @@ -344,9 +346,10 @@ async fn run_nat_detection(action_sender: &UnboundedSender) { fn prepare_node_config(args: &MaintainNodesArgs) -> NodeConfig { NodeConfig { + antnode_path: args.antnode_path.clone(), auto_set_nat_flags: args.connection_mode == ConnectionMode::Automatic, - upnp: args.connection_mode == ConnectionMode::UPnP, - home_network: args.connection_mode == ConnectionMode::HomeNetwork, + data_dir_path: args.data_dir_path.clone(), + count: args.count, custom_ports: if args.connection_mode == ConnectionMode::CustomPorts { args.port_range.clone() } else { @@ -357,11 +360,11 @@ fn prepare_node_config(args: &MaintainNodesArgs) -> NodeConfig { } else { Some(args.owner.clone()) }, - count: args.count, - data_dir_path: args.data_dir_path.clone(), + home_network: args.connection_mode == ConnectionMode::HomeNetwork, + network_id: args.network_id, peers_args: args.peers_args.clone(), - antnode_path: args.antnode_path.clone(), rewards_address: args.rewards_address.clone(), + upnp: args.connection_mode == ConnectionMode::UPnP, } } @@ -373,8 +376,8 @@ fn debug_log_config(config: &NodeConfig, args: &MaintainNodesArgs) { config.count ); debug!( - " owner: {:?}, peers_args: {:?}, antnode_path: {:?}", - config.owner, config.peers_args, config.antnode_path + " owner: {:?}, peers_args: {:?}, antnode_path: {:?}, network_id: {:?}", + config.owner, config.peers_args, config.antnode_path, args.network_id ); debug!( " data_dir_path: {:?}, connection_mode: {:?}", @@ -423,7 +426,7 @@ async fn scale_down_nodes(config: &NodeConfig, count: u16) { None, None, None, - None, + config.network_id, None, None, // We don't care about the port, as we are scaling down config.owner.clone(), @@ -497,7 +500,7 @@ async fn add_nodes( None, None, None, - None, + config.network_id, None, port_range, config.owner.clone(), diff --git a/release-cycle-info b/release-cycle-info index 0109aac6b2..2d3c9fa1ee 100644 --- a/release-cycle-info +++ b/release-cycle-info @@ -13,6 +13,6 @@ # Both of these numbers are used in the packaged version number, which is a collective version # number for all the released binaries. release-year: 2024 -release-month: 11 +release-month: 12 release-cycle: 1 -release-cycle-counter: 6 +release-cycle-counter: 5 diff --git a/resources/scripts/release-candidate-description.py b/resources/scripts/release-candidate-description.py index 51fb0037e8..10a91e0b96 100755 --- a/resources/scripts/release-candidate-description.py +++ b/resources/scripts/release-candidate-description.py @@ -72,7 +72,7 @@ def main(pr_numbers): crate_binary_map = { "ant-node": "antnode", "ant-node-manager": "antctl", - "autonomi-cli": "autonomi", + "ant-cli": "ant", "nat-detection": "nat-detection", "node-launchpad": "node-launchpad" } diff --git a/test-utils/Cargo.toml b/test-utils/Cargo.toml index 417428de02..89409905b8 100644 --- a/test-utils/Cargo.toml +++ b/test-utils/Cargo.toml @@ -7,13 +7,13 @@ license = "GPL-3.0" name = "test-utils" readme = "README.md" repository = "https://github.com/maidsafe/safe_network" -version = "0.4.11" +version = "0.4.12" [dependencies] bytes = { version = "1.0.1", features = ["serde"] } color-eyre = "~0.6.2" dirs-next = "~2.0.0" -evmlib = { path = "../evmlib", version = "0.1.4" } +evmlib = { path = "../evmlib", version = "0.1.5" } libp2p = { version = "0.54.1", features = ["identify", "kad"] } rand = "0.8.5" serde = { version = "1.0.133", features = ["derive"] }