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::sync::Arc;
24use std::time::Duration;
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            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 the provided stream.
89    ///
90    /// Future returns once plotting is successfully scheduled (for backpressure purposes).
91    // TODO: Struct for arguments
92    #[expect(clippy::too_many_arguments)]
93    async fn plot_sector(
94        &self,
95        public_key: Ed25519PublicKey,
96        shard_commitments_root: ShardCommitmentHash,
97        sector_index: SectorIndex,
98        farmer_protocol_info: FarmerProtocolInfo,
99        pieces_in_sector: u16,
100        replotting: bool,
101        progress_sender: mpsc::Sender<SectorPlottingProgress>,
102    );
103
104    /// Try to plot one sector, sending sector plotting events via the provided stream.
105    ///
106    /// Returns `true` if plotting started successfully and `false` if there is no capacity to start
107    /// plotting immediately.
108    // TODO: Struct for arguments
109    #[expect(clippy::too_many_arguments)]
110    async fn try_plot_sector(
111        &self,
112        public_key: Ed25519PublicKey,
113        shard_commitments_root: ShardCommitmentHash,
114        sector_index: SectorIndex,
115        farmer_protocol_info: FarmerProtocolInfo,
116        pieces_in_sector: u16,
117        replotting: bool,
118        progress_sender: mpsc::Sender<SectorPlottingProgress>,
119    ) -> bool;
120}
121
122#[async_trait]
123impl<P> Plotter for Arc<P>
124where
125    P: Plotter + Send + Sync,
126{
127    #[inline]
128    async fn has_free_capacity(&self) -> Result<bool, String> {
129        self.as_ref().has_free_capacity().await
130    }
131
132    #[inline]
133    async fn plot_sector(
134        &self,
135        public_key: Ed25519PublicKey,
136        shard_commitments_root: ShardCommitmentHash,
137        sector_index: SectorIndex,
138        farmer_protocol_info: FarmerProtocolInfo,
139        pieces_in_sector: u16,
140        replotting: bool,
141        progress_sender: mpsc::Sender<SectorPlottingProgress>,
142    ) {
143        self.as_ref()
144            .plot_sector(
145                public_key,
146                shard_commitments_root,
147                sector_index,
148                farmer_protocol_info,
149                pieces_in_sector,
150                replotting,
151                progress_sender,
152            )
153            .await
154    }
155
156    #[inline]
157    async fn try_plot_sector(
158        &self,
159        public_key: Ed25519PublicKey,
160        shard_commitments_root: ShardCommitmentHash,
161        sector_index: SectorIndex,
162        farmer_protocol_info: FarmerProtocolInfo,
163        pieces_in_sector: u16,
164        replotting: bool,
165        progress_sender: mpsc::Sender<SectorPlottingProgress>,
166    ) -> bool {
167        self.as_ref()
168            .try_plot_sector(
169                public_key,
170                shard_commitments_root,
171                sector_index,
172                farmer_protocol_info,
173                pieces_in_sector,
174                replotting,
175                progress_sender,
176            )
177            .await
178    }
179}