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..6e41f1af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,3 +6,12 @@ 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" } + +[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..440b6076 --- /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) + .build("node") + .await + .expect("Failed to init a node"); + + node.sync().await; + + let reward_address = PublicKey::from([0; 32]); + let mut farmer: Farmer = Farmer::builder() + .ws_rpc("127.0.0.1:9955".parse().unwrap()) + .listen_on("/ip4/0.0.0.0/tcp/40333".parse().unwrap()) + .build( + reward_address, + node.clone(), + PlotDescription::new("plot", ByteSize::gb(10)), + ) + .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) + .build("node") + .await + .expect("Failed to init a node"); + node.sync().await; + + let mut farmer = Farmer::builder() + .build( + reward_address, + node.clone(), + PlotDescription::new("plot", ByteSize::gb(10)), + ) + .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; +} diff --git a/src/farmer.rs b/src/farmer.rs new file mode 100644 index 00000000..8f53a05c --- /dev/null +++ b/src/farmer.rs @@ -0,0 +1,128 @@ +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 { + listen_on: Option, + ws_rpc: Option, +} + +impl Builder { + pub fn new() -> Self { + Self::default() + } + + 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 + // 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!() + } +} + +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..e5bfd7c0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,14 +1,25 @@ -pub fn add(left: usize, right: usize) -> usize { - left + right -} +pub mod farmer; +pub mod node; + +use std::path::PathBuf; -#[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; + +#[derive(Default)] +#[non_exhaustive] +pub enum Directory { + #[default] + Default, + Tmp, + Custom(PathBuf), +} - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); +impl> From

for Directory { + fn from(path: P) -> Self { + Self::Custom(path.into()) } } diff --git a/src/node.rs b/src/node.rs new file mode 100644 index 00000000..9ebb0814 --- /dev/null +++ b/src/node.rs @@ -0,0 +1,109 @@ +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, + 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 port(mut self, port: u16) -> Self { + self.port = port; + self + } + + /// It supposed to open node at the supplied location + pub async fn build(self, directory: impl Into) -> Result { + let _ = directory; + 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; + } +}