subspace_farmer/
plotter.rs

1//! Plotter abstraction
2//!
3//! Plotter is abstracted away to support different implementation. Originally it was just CPU, but
4//! eventually abstract network-attached, GPU or hybrid plotters become an option as well. Having a
5//! trait with async API representing plotting functionality allows composition of different
6//! implementations without the rest of the library being aware of implementation details.
7
8pub mod cpu;
9#[cfg(feature = "_gpu")]
10pub mod gpu;
11pub mod pool;
12
13use async_trait::async_trait;
14use bytes::Bytes;
15use futures::channel::mpsc;
16use futures::Stream;
17use std::fmt;
18use std::pin::Pin;
19use std::sync::Arc;
20use std::time::Duration;
21use subspace_core_primitives::sectors::SectorIndex;
22use subspace_core_primitives::PublicKey;
23use subspace_farmer_components::plotting::PlottedSector;
24use subspace_farmer_components::FarmerProtocolInfo;
25
26/// Sector plotting progress
27pub enum SectorPlottingProgress {
28    /// Downloading sector pieces
29    Downloading,
30    /// Downloaded sector pieces
31    Downloaded(Duration),
32    /// Encoding sector pieces
33    Encoding,
34    /// Encoded sector pieces
35    Encoded(Duration),
36    /// Finished plotting
37    Finished {
38        /// Information about plotted sector
39        plotted_sector: PlottedSector,
40        /// How much time it took to plot a sector
41        time: Duration,
42        /// Stream of all plotted sector bytes
43        sector: Pin<Box<dyn Stream<Item = Result<Bytes, String>> + Send + Sync>>,
44    },
45    /// Plotting failed
46    Error {
47        /// Error message
48        error: String,
49    },
50}
51
52impl fmt::Debug for SectorPlottingProgress {
53    #[inline]
54    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55        match self {
56            SectorPlottingProgress::Downloading => fmt::Formatter::write_str(f, "Downloading"),
57            SectorPlottingProgress::Downloaded(time) => {
58                f.debug_tuple_field1_finish("Downloaded", &time)
59            }
60            SectorPlottingProgress::Encoding => fmt::Formatter::write_str(f, "Encoding"),
61            SectorPlottingProgress::Encoded(time) => f.debug_tuple_field1_finish("Encoded", &time),
62            SectorPlottingProgress::Finished {
63                plotted_sector,
64                time,
65                sector: _,
66            } => f.debug_struct_field3_finish(
67                "Finished",
68                "plotted_sector",
69                plotted_sector,
70                "time",
71                time,
72                "sector",
73                &"<stream>",
74            ),
75            SectorPlottingProgress::Error { error } => {
76                f.debug_struct_field1_finish("Error", "error", &error)
77            }
78        }
79    }
80}
81
82/// Abstract plotter implementation
83#[async_trait]
84pub trait Plotter: fmt::Debug {
85    /// Whether plotter has free capacity to encode more sectors
86    async fn has_free_capacity(&self) -> Result<bool, String>;
87
88    /// Plot one sector, sending sector plotting events via provided stream.
89    ///
90    /// Future returns once plotting is successfully scheduled (for backpressure purposes).
91    async fn plot_sector(
92        &self,
93        public_key: PublicKey,
94        sector_index: SectorIndex,
95        farmer_protocol_info: FarmerProtocolInfo,
96        pieces_in_sector: u16,
97        replotting: bool,
98        progress_sender: mpsc::Sender<SectorPlottingProgress>,
99    );
100
101    /// Try to plot one sector, sending sector plotting events via provided stream.
102    ///
103    /// Returns `true` if plotting started successfully and `false` if there is no capacity to start
104    /// plotting immediately.
105    async fn try_plot_sector(
106        &self,
107        public_key: PublicKey,
108        sector_index: SectorIndex,
109        farmer_protocol_info: FarmerProtocolInfo,
110        pieces_in_sector: u16,
111        replotting: bool,
112        progress_sender: mpsc::Sender<SectorPlottingProgress>,
113    ) -> bool;
114}
115
116#[async_trait]
117impl<P> Plotter for Arc<P>
118where
119    P: Plotter + Send + Sync,
120{
121    #[inline]
122    async fn has_free_capacity(&self) -> Result<bool, String> {
123        self.as_ref().has_free_capacity().await
124    }
125
126    #[inline]
127    async fn plot_sector(
128        &self,
129        public_key: PublicKey,
130        sector_index: SectorIndex,
131        farmer_protocol_info: FarmerProtocolInfo,
132        pieces_in_sector: u16,
133        replotting: bool,
134        progress_sender: mpsc::Sender<SectorPlottingProgress>,
135    ) {
136        self.as_ref()
137            .plot_sector(
138                public_key,
139                sector_index,
140                farmer_protocol_info,
141                pieces_in_sector,
142                replotting,
143                progress_sender,
144            )
145            .await
146    }
147
148    #[inline]
149    async fn try_plot_sector(
150        &self,
151        public_key: PublicKey,
152        sector_index: SectorIndex,
153        farmer_protocol_info: FarmerProtocolInfo,
154        pieces_in_sector: u16,
155        replotting: bool,
156        progress_sender: mpsc::Sender<SectorPlottingProgress>,
157    ) -> bool {
158        self.as_ref()
159            .try_plot_sector(
160                public_key,
161                sector_index,
162                farmer_protocol_info,
163                pieces_in_sector,
164                replotting,
165                progress_sender,
166            )
167            .await
168    }
169}