From fe1fc3223079c37407bda8bac78412b388e8d318 Mon Sep 17 00:00:00 2001 From: i1i1 Date: Mon, 3 Oct 2022 18:15:31 +0300 Subject: [PATCH 1/3] Add initial API --- .github/workflows/push.yml | 2 + Cargo.toml | 6 ++ src/farmer.rs | 140 +++++++++++++++++++++++++++++++++++++ src/lib.rs | 25 ++++--- src/node.rs | 121 ++++++++++++++++++++++++++++++++ 5 files changed, 283 insertions(+), 11 deletions(-) create mode 100644 src/farmer.rs create mode 100644 src/node.rs diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 27d3e00f..9e94e301 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -6,6 +6,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 + - name: Install Protoc + uses: arduino/setup-protoc@v1 - uses: actions-rs/toolchain@v1 with: toolchain: nightly diff --git a/Cargo.toml b/Cargo.toml index dd117aad..e22fcda8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,3 +6,9 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +libp2p-core = "0.36" +either = "1.8" +tempdir = "0.3" +bytesize = "1.1" + +subspace-core-primitives = { git = "https://github.com/subspace/subspace", rev = "8007a0acd57064264aa03a8c71eb1c9cc9466994" } diff --git a/src/farmer.rs b/src/farmer.rs new file mode 100644 index 00000000..47bf8c33 --- /dev/null +++ b/src/farmer.rs @@ -0,0 +1,140 @@ +use std::{io, net::SocketAddr, path::PathBuf}; + +use bytesize::ByteSize; +use either::Either; +use libp2p_core::multiaddr::Multiaddr; +use tempdir::TempDir; + +use crate::{Node, PublicKey}; + +// TODO: Should it be non-exhaustive? +pub struct PlotDescription { + pub directory: Either, + pub space_pledged: ByteSize, +} + +impl PlotDescription { + // TODO: should we check that plot is valid at this stage? + // Or it can be literally a description of a plot + pub fn new(directory: impl Into, space_pledged: ByteSize) -> Self { + Self { + directory: Either::Left(directory.into()), + space_pledged, + } + } + + pub fn with_tempdir(space_pledged: ByteSize) -> io::Result { + TempDir::new("plot") + .map(Either::Right) + .map(|directory| Self { + directory, + space_pledged, + }) + } +} + +#[derive(Default)] +pub struct Builder { + reward_address: Option, + node: Option, + listen_on: Option, + ws_rpc: Option, + // TODO: Should we just require a single plot? + plots: Vec, +} + +impl Builder { + pub fn new() -> Self { + Self::default() + } + + pub fn node(mut self, node: Node) -> Self { + self.node = Some(node); + self + } + + pub fn reward_address(mut self, reward_address: PublicKey) -> Self { + self.reward_address = Some(reward_address); + self + } + + pub fn plot(mut self, plot: PlotDescription) -> Self { + self.plots.push(plot); + self + } + + pub fn listen_on(mut self, multiaddr: Multiaddr) -> Self { + self.listen_on = Some(multiaddr); + self + } + + pub fn ws_rpc(mut self, ws_rpc: SocketAddr) -> Self { + self.ws_rpc = Some(ws_rpc); + self + } + + /// It supposed to open node at the supplied location + pub async fn build(self) -> Result { + todo!() + } +} + +pub struct Farmer { + _ensure_cant_construct: (), +} + +#[derive(Debug)] +#[non_exhaustive] +pub struct Info { + pub version: String, + pub reward_address: PublicKey, + pub dsn_peers: u64, + pub space_pledged: ByteSize, +} + +#[derive(Debug)] +pub struct Solution { + _ensure_cant_construct: (), +} + +pub struct Plot { + _ensure_cant_construct: (), +} + +impl Plot { + pub async fn delete(&mut self) {} +} + +impl Farmer { + pub fn builder() -> Builder { + Builder::new() + } + + pub async fn sync(&mut self) {} + + pub async fn start_farming(&mut self) {} + + pub async fn stop_farming(&mut self) {} + + pub async fn get_info(&mut self) -> Info { + todo!() + } + + pub async fn on_solution(&mut self, on_solution: H) + where + H: Clone + Send + Sync + 'static + FnMut(Solution) -> F, + F: Send + 'static + std::future::Future, + { + let _ = on_solution; + } + + pub async fn plots(&mut self) -> &mut [Plot] { + todo!() + } + + // Stops farming, closes plots, and sends signal to the node + pub async fn close(self) {} + + // Runs `.close()` and also wipes farmer's state + pub async fn wipe(self) {} +} diff --git a/src/lib.rs b/src/lib.rs index 7d12d9af..761f1ebc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,14 +1,17 @@ -pub fn add(left: usize, right: usize) -> usize { - left + right -} +pub mod farmer; +pub mod node; -#[cfg(test)] -mod tests { - use super::*; +pub use farmer::{ + Builder as FarmerBuilder, Farmer, Info as NodeInfo, Plot, PlotDescription, Solution, +}; +pub use node::{Builder as NodeBuilder, Info as FarmerInfo, Mode as NodeMode, Network, Node}; +pub use subspace_core_primitives::PublicKey; - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } +#[derive(Default)] +#[non_exhaustive] +enum Directory { + #[default] + Default, + Tmp, + Custom(std::path::PathBuf), } diff --git a/src/node.rs b/src/node.rs new file mode 100644 index 00000000..bee30473 --- /dev/null +++ b/src/node.rs @@ -0,0 +1,121 @@ +use std::path::PathBuf; + +use crate::Directory; + +#[non_exhaustive] +#[derive(Debug, Default)] +pub enum Mode { + #[default] + Full, +} + +#[non_exhaustive] +#[derive(Debug, Default)] +pub enum Network { + #[default] + Gemini2a, + // TODO: put proper type here + Custom(std::convert::Infallible), +} + +#[derive(Default)] +pub struct Builder { + mode: Mode, + network: Network, + name: Option, + directory: Directory, + port: u16, +} + +impl Builder { + pub fn new() -> Self { + Self::default() + } + + pub fn mode(mut self, ty: Mode) -> Self { + self.mode = ty; + self + } + + pub fn network(mut self, network: Network) -> Self { + self.network = network; + self + } + + pub fn name(mut self, name: impl AsRef) -> Self { + if !name.as_ref().is_empty() { + self.name = Some(name.as_ref().to_owned()); + } + self + } + + pub fn directory(mut self, path: impl Into) -> Self { + self.directory = Directory::Custom(path.into()); + self + } + + pub fn tmp_directory(mut self) -> Self { + self.directory = Directory::Tmp; + self + } + + pub fn port(mut self, port: u16) -> Self { + self.port = port; + self + } + + /// It supposed to open node at the supplied location + pub async fn build(self) -> Result { + todo!() + } +} + +#[derive(Clone)] +pub struct Node { + _ensure_cant_construct: (), +} + +#[derive(Debug)] +#[non_exhaustive] +pub struct Info { + pub version: String, + pub network: Network, + pub mode: Mode, + pub name: Option, + pub connected_peers: u64, + pub best_block: u64, + pub total_space_pledged: u64, + pub total_history_size: u64, + pub space_pledged: u64, +} + +#[derive(Debug)] +pub struct Block { + _ensure_cant_construct: (), +} + +impl Node { + pub fn builder() -> Builder { + Builder::new() + } + + pub async fn sync(&mut self) {} + + // Leaves the network and gracefully shuts down + pub async fn close(self) {} + + // Runs `.close()` and also wipes node's state + pub async fn wipe(self) {} + + pub async fn get_info(&mut self) -> Info { + todo!() + } + + pub async fn on_block(&mut self, callback: H) + where + H: Clone + Send + Sync + 'static + FnMut(Block) -> F, + F: Send + 'static + std::future::Future, + { + let _ = callback; + } +} From 81ac516db890a267b76f126cca6946a383e0b56d Mon Sep 17 00:00:00 2001 From: i1i1 Date: Mon, 3 Oct 2022 18:15:37 +0300 Subject: [PATCH 2/3] Add example --- Cargo.toml | 3 ++ examples/simple.rs | 77 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 examples/simple.rs diff --git a/Cargo.toml b/Cargo.toml index e22fcda8..6e41f1af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,3 +12,6 @@ tempdir = "0.3" bytesize = "1.1" subspace-core-primitives = { git = "https://github.com/subspace/subspace", rev = "8007a0acd57064264aa03a8c71eb1c9cc9466994" } + +[dev-dependencies] +tokio = { version = "1.21", features = ["rt-multi-thread", "macros"] } diff --git a/examples/simple.rs b/examples/simple.rs new file mode 100644 index 00000000..ec18b5af --- /dev/null +++ b/examples/simple.rs @@ -0,0 +1,77 @@ +use bytesize::ByteSize; +use subspace_sdk::{Farmer, Network, Node, NodeMode, PlotDescription, PublicKey}; + +#[tokio::main] +async fn main() { + let mut node: Node = Node::builder() + .mode(NodeMode::Full) + .network(Network::Gemini2a) + .name("i1i1") + .port(1337) + .directory("node") + .build() + .await + .expect("Failed to init a node"); + + node.sync().await; + + let reward_address = PublicKey::from([0; 32]); + let mut farmer: Farmer = Farmer::builder() + .node(node.clone()) + .plot(PlotDescription::new("plot", ByteSize::gb(10))) + .ws_rpc("127.0.0.1:9955".parse().unwrap()) + .listen_on("/ip4/0.0.0.0/tcp/40333".parse().unwrap()) + .reward_address(reward_address) + .build() + .await + .expect("Failed to init a farmer"); + + farmer.sync().await; + + farmer + .on_solution(|solution| async move { + eprintln!("Found solution: {solution:?}"); + }) + .await; + node.on_block(|block| async move { + eprintln!("New block: {block:?}"); + }) + .await; + + farmer.start_farming().await; + + dbg!(node.get_info().await); + dbg!(farmer.get_info().await); + + farmer.stop_farming().await; + farmer.close().await; + node.close().await; + + // Restarting + let mut node = Node::builder() + .mode(NodeMode::Full) + .network(Network::Gemini2a) + .directory("node") + .build() + .await + .expect("Failed to init a node"); + node.sync().await; + + let mut farmer = Farmer::builder() + .node(node.clone()) + .plot(PlotDescription::new("plot", ByteSize::gb(10))) + .reward_address(reward_address) + .build() + .await + .expect("Failed to init a farmer"); + + farmer.sync().await; + farmer.start_farming().await; + + // Delete everything + for plot in farmer.plots().await { + plot.delete().await; + } + farmer.wipe().await; + node.wipe().await; +} From 44ea07acfe82145a292e4bdb02ed2fc0f9bcae47 Mon Sep 17 00:00:00 2001 From: i1i1 Date: Thu, 6 Oct 2022 13:49:01 +0300 Subject: [PATCH 3/3] Require currently required fields --- examples/simple.rs | 24 ++++++++++++------------ src/farmer.rs | 28 ++++++++-------------------- src/lib.rs | 12 ++++++++++-- src/node.rs | 16 ++-------------- 4 files changed, 32 insertions(+), 48 deletions(-) diff --git a/examples/simple.rs b/examples/simple.rs index ec18b5af..440b6076 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -8,8 +8,7 @@ async fn main() { .network(Network::Gemini2a) .name("i1i1") .port(1337) - .directory("node") - .build() + .build("node") .await .expect("Failed to init a node"); @@ -17,12 +16,13 @@ async fn main() { let reward_address = PublicKey::from([0; 32]); let mut farmer: Farmer = Farmer::builder() - .node(node.clone()) - .plot(PlotDescription::new("plot", ByteSize::gb(10))) .ws_rpc("127.0.0.1:9955".parse().unwrap()) .listen_on("/ip4/0.0.0.0/tcp/40333".parse().unwrap()) - .reward_address(reward_address) - .build() + .build( + reward_address, + node.clone(), + PlotDescription::new("plot", ByteSize::gb(10)), + ) .await .expect("Failed to init a farmer"); @@ -51,17 +51,17 @@ async fn main() { let mut node = Node::builder() .mode(NodeMode::Full) .network(Network::Gemini2a) - .directory("node") - .build() + .build("node") .await .expect("Failed to init a node"); node.sync().await; let mut farmer = Farmer::builder() - .node(node.clone()) - .plot(PlotDescription::new("plot", ByteSize::gb(10))) - .reward_address(reward_address) - .build() + .build( + reward_address, + node.clone(), + PlotDescription::new("plot", ByteSize::gb(10)), + ) .await .expect("Failed to init a farmer"); diff --git a/src/farmer.rs b/src/farmer.rs index 47bf8c33..8f53a05c 100644 --- a/src/farmer.rs +++ b/src/farmer.rs @@ -35,12 +35,8 @@ impl PlotDescription { #[derive(Default)] pub struct Builder { - reward_address: Option, - node: Option, listen_on: Option, ws_rpc: Option, - // TODO: Should we just require a single plot? - plots: Vec, } impl Builder { @@ -48,21 +44,6 @@ impl Builder { Self::default() } - pub fn node(mut self, node: Node) -> Self { - self.node = Some(node); - self - } - - pub fn reward_address(mut self, reward_address: PublicKey) -> Self { - self.reward_address = Some(reward_address); - self - } - - pub fn plot(mut self, plot: PlotDescription) -> Self { - self.plots.push(plot); - self - } - pub fn listen_on(mut self, multiaddr: Multiaddr) -> Self { self.listen_on = Some(multiaddr); self @@ -74,7 +55,14 @@ impl Builder { } /// It supposed to open node at the supplied location - pub async fn build(self) -> Result { + // TODO: Should we just require multiple plots? + pub async fn build( + self, + reward_address: PublicKey, + node: Node, + plot: PlotDescription, + ) -> Result { + let _ = (reward_address, node, plot); todo!() } } diff --git a/src/lib.rs b/src/lib.rs index 761f1ebc..e5bfd7c0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,8 @@ pub mod farmer; pub mod node; +use std::path::PathBuf; + pub use farmer::{ Builder as FarmerBuilder, Farmer, Info as NodeInfo, Plot, PlotDescription, Solution, }; @@ -9,9 +11,15 @@ pub use subspace_core_primitives::PublicKey; #[derive(Default)] #[non_exhaustive] -enum Directory { +pub enum Directory { #[default] Default, Tmp, - Custom(std::path::PathBuf), + Custom(PathBuf), +} + +impl> From

for Directory { + fn from(path: P) -> Self { + Self::Custom(path.into()) + } } diff --git a/src/node.rs b/src/node.rs index bee30473..9ebb0814 100644 --- a/src/node.rs +++ b/src/node.rs @@ -1,5 +1,3 @@ -use std::path::PathBuf; - use crate::Directory; #[non_exhaustive] @@ -23,7 +21,6 @@ pub struct Builder { mode: Mode, network: Network, name: Option, - directory: Directory, port: u16, } @@ -49,23 +46,14 @@ impl Builder { self } - pub fn directory(mut self, path: impl Into) -> Self { - self.directory = Directory::Custom(path.into()); - self - } - - pub fn tmp_directory(mut self) -> Self { - self.directory = Directory::Tmp; - self - } - pub fn port(mut self, port: u16) -> Self { self.port = port; self } /// It supposed to open node at the supplied location - pub async fn build(self) -> Result { + pub async fn build(self, directory: impl Into) -> Result { + let _ = directory; todo!() } }