Skip to main content

ab_farmer/
plotter.rs

1//! Plotter abstraction
2//!
3//! Plotter is abstracted away to support different implementations. Originally it was just CPU, but
4//! eventually abstract network-attached, GPU and hybrid plotters became 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 farmer being aware of implementation details.
7
8pub mod cpu;
9pub mod gpu;
10pub mod pool;
11
12use ab_core_primitives::ed25519::Ed25519PublicKey;
13use ab_core_primitives::sectors::SectorIndex;
14use ab_core_primitives::solutions::ShardCommitmentHash;
15use ab_farmer_components::FarmerProtocolInfo;
16use ab_farmer_components::plotting::PlottedSector;
17use async_trait::async_trait;
18use bytes::Bytes;
19use futures::Stream;
20use futures::channel::mpsc;
21use std::fmt;
22use std::pin::Pin;
23use std::time::Duration;
24use triomphe::Arc;
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 the 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            Self::Downloading => f.write_str("Downloading"),
57            Self::Downloaded(time) => f.debug_tuple("Downloaded").field(time).finish(),
58            Self::Encoding => f.write_str("Encoding"),
59            Self::Encoded(time) => f.debug_tuple("Encoded").field(time).finish(),
60            Self::Finished {
61                plotted_sector,
62                time,
63                sector: _,
64            } => f
65                .debug_struct("Finished")
66                .field("plotted_sector", plotted_sector)
67                .field("time", time)
68                .field("sector", &format_args!("<stream>"))
69                .finish(),
70            Self::Error { error } => f.debug_struct("Error").field("error", error).finish(),
71        }
72    }
73}
74
75/// Abstract plotter implementation
76#[async_trait]
77pub trait Plotter: fmt::Debug {
78    /// Whether plotter has free capacity to encode more sectors
79    async fn has_free_capacity(&self) -> Result<bool, String>;
80
81    /// Plot one sector, sending sector plotting events via the provided stream.
82    ///
83    /// Future returns once plotting is successfully scheduled (for backpressure purposes).
84    // TODO: Struct for arguments
85    #[expect(clippy::too_many_arguments, reason = "TODO")]
86    async fn plot_sector(
87        &self,
88        public_key: Ed25519PublicKey,
89        shard_commitments_root: ShardCommitmentHash,
90        sector_index: SectorIndex,
91        farmer_protocol_info: FarmerProtocolInfo,
92        pieces_in_sector: u16,
93        replotting: bool,
94        progress_sender: mpsc::Sender<SectorPlottingProgress>,
95    );
96
97    /// Try to plot one sector, sending sector plotting events via the provided stream.
98    ///
99    /// Returns `true` if plotting started successfully and `false` if there is no capacity to start
100    /// plotting immediately.
101    // TODO: Struct for arguments
102    #[expect(clippy::too_many_arguments, reason = "TODO")]
103    async fn try_plot_sector(
104        &self,
105        public_key: Ed25519PublicKey,
106        shard_commitments_root: ShardCommitmentHash,
107        sector_index: SectorIndex,
108        farmer_protocol_info: FarmerProtocolInfo,
109        pieces_in_sector: u16,
110        replotting: bool,
111        progress_sender: mpsc::Sender<SectorPlottingProgress>,
112    ) -> bool;
113}
114
115#[async_trait]
116impl<P> Plotter for Arc<P>
117where
118    P: Plotter + Send + Sync,
119{
120    #[inline]
121    async fn has_free_capacity(&self) -> Result<bool, String> {
122        self.as_ref().has_free_capacity().await
123    }
124
125    #[inline]
126    async fn plot_sector(
127        &self,
128        public_key: Ed25519PublicKey,
129        shard_commitments_root: ShardCommitmentHash,
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                shard_commitments_root,
140                sector_index,
141                farmer_protocol_info,
142                pieces_in_sector,
143                replotting,
144                progress_sender,
145            )
146            .await;
147    }
148
149    #[inline]
150    async fn try_plot_sector(
151        &self,
152        public_key: Ed25519PublicKey,
153        shard_commitments_root: ShardCommitmentHash,
154        sector_index: SectorIndex,
155        farmer_protocol_info: FarmerProtocolInfo,
156        pieces_in_sector: u16,
157        replotting: bool,
158        progress_sender: mpsc::Sender<SectorPlottingProgress>,
159    ) -> bool {
160        self.as_ref()
161            .try_plot_sector(
162                public_key,
163                shard_commitments_root,
164                sector_index,
165                farmer_protocol_info,
166                pieces_in_sector,
167                replotting,
168                progress_sender,
169            )
170            .await
171    }
172}