1use ab_core_primitives::pieces::{Piece, PieceIndex, PieceOffset};
11use ab_core_primitives::sectors::SectorIndex;
12use ab_core_primitives::segments::SegmentIndex;
13use ab_farmer_components::auditing::AuditingError;
14use ab_farmer_components::plotting::PlottedSector;
15use ab_farmer_components::proving::ProvingError;
16use ab_farmer_rpc_primitives::SolutionResponse;
17use ab_networking::libp2p::kad::RecordKey;
18use async_trait::async_trait;
19use derive_more::{Display, From};
20use futures::Stream;
21use parity_scale_codec::{Decode, Encode, EncodeLike, Input, Output};
22use serde::{Deserialize, Serialize};
23use std::future::Future;
24use std::pin::Pin;
25use std::sync::Arc;
26use std::time::Duration;
27use std::{fmt, io};
28use thiserror::Error;
29use ulid::Ulid;
30
31pub mod plotted_pieces;
32
33pub type FarmError = Box<dyn std::error::Error + Send + Sync + 'static>;
35pub type HandlerFn<A> = Arc<dyn Fn(&A) + Send + Sync + 'static>;
37
38#[async_trait]
40pub trait PlottedSectors: Send + Sync + fmt::Debug {
41 async fn get(
43 &self,
44 ) -> Result<
45 Box<dyn Stream<Item = Result<PlottedSector, FarmError>> + Unpin + Send + '_>,
46 FarmError,
47 >;
48}
49
50#[derive(
52 Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Serialize, Deserialize, Display, From,
53)]
54#[serde(untagged)]
55pub enum PieceCacheId {
56 Ulid(Ulid),
58}
59
60impl Encode for PieceCacheId {
61 #[inline]
62 fn size_hint(&self) -> usize {
63 1_usize
64 + match self {
65 PieceCacheId::Ulid(ulid) => 0_usize.saturating_add(Encode::size_hint(&ulid.0)),
66 }
67 }
68
69 #[inline]
70 fn encode_to<O>(&self, dest: &mut O)
71 where
72 O: Output + ?Sized,
73 {
74 match self {
75 PieceCacheId::Ulid(ulid) => {
76 dest.push_byte(0);
77 Encode::encode_to(&ulid.0, dest);
78 }
79 }
80 }
81}
82
83impl EncodeLike for PieceCacheId {}
84
85impl Decode for PieceCacheId {
86 #[inline]
87 fn decode<I>(input: &mut I) -> Result<Self, parity_scale_codec::Error>
88 where
89 I: Input,
90 {
91 match input
92 .read_byte()
93 .map_err(|e| e.chain("Could not decode `PieceCacheId`, failed to read variant byte"))?
94 {
95 0 => u128::decode(input)
96 .map(|ulid| PieceCacheId::Ulid(Ulid(ulid)))
97 .map_err(|e| e.chain("Could not decode `PieceCacheId::Ulid.0`")),
98 _ => Err("Could not decode `PieceCacheId`, variant doesn't exist".into()),
99 }
100 }
101}
102
103#[expect(
104 clippy::new_without_default,
105 reason = "Default has different semantics"
106)]
107impl PieceCacheId {
108 #[inline]
110 pub fn new() -> Self {
111 Self::Ulid(Ulid::generate())
112 }
113}
114
115#[derive(Debug, Display, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Encode, Decode)]
117#[repr(transparent)]
118pub struct PieceCacheOffset(pub(crate) u32);
119
120#[async_trait]
126pub trait PieceCache: Send + Sync + fmt::Debug {
127 fn id(&self) -> &PieceCacheId;
129
130 fn max_num_elements(&self) -> u32;
132
133 async fn contents(
138 &self,
139 ) -> Result<
140 Box<
141 dyn Stream<Item = Result<(PieceCacheOffset, Option<PieceIndex>), FarmError>>
142 + Unpin
143 + Send
144 + '_,
145 >,
146 FarmError,
147 >;
148
149 async fn write_piece(
154 &self,
155 offset: PieceCacheOffset,
156 piece_index: PieceIndex,
157 piece: &Piece,
158 ) -> Result<(), FarmError>;
159
160 async fn read_piece_index(
167 &self,
168 offset: PieceCacheOffset,
169 ) -> Result<Option<PieceIndex>, FarmError>;
170
171 async fn read_piece(
178 &self,
179 offset: PieceCacheOffset,
180 ) -> Result<Option<(PieceIndex, Piece)>, FarmError>;
181
182 async fn read_pieces(
190 &self,
191 offsets: Box<dyn Iterator<Item = PieceCacheOffset> + Send>,
192 ) -> Result<
193 Box<
194 dyn Stream<Item = Result<(PieceCacheOffset, Option<(PieceIndex, Piece)>), FarmError>>
195 + Send
196 + Unpin
197 + '_,
198 >,
199 FarmError,
200 >;
201}
202
203#[derive(Debug, Copy, Clone, Encode, Decode)]
205pub enum MaybePieceStoredResult {
206 No,
208 Vacant,
212 Yes,
214}
215
216#[async_trait]
223pub trait PlotCache: Send + Sync + fmt::Debug {
224 async fn is_piece_maybe_stored(
228 &self,
229 key: &RecordKey,
230 ) -> Result<MaybePieceStoredResult, FarmError>;
231
232 async fn try_store_piece(
235 &self,
236 piece_index: PieceIndex,
237 piece: &Piece,
238 ) -> Result<bool, FarmError>;
239
240 async fn read_piece(&self, key: &RecordKey) -> Result<Option<Piece>, FarmError>;
244}
245
246#[derive(Debug, Copy, Clone, Encode, Decode)]
248pub struct AuditingDetails {
249 pub sectors_count: u16,
251 pub time: Duration,
253}
254
255#[derive(Debug, Copy, Clone, Encode, Decode)]
257pub enum ProvingResult {
258 Success,
260 Timeout,
262 Rejected,
265 Failed,
267}
268
269impl fmt::Display for ProvingResult {
270 #[inline]
271 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
272 f.write_str(match self {
273 Self::Success => "Success",
274 Self::Timeout => "Timeout",
275 Self::Rejected => "Rejected",
276 Self::Failed => "Failed",
277 })
278 }
279}
280
281#[derive(Debug, Copy, Clone, Encode, Decode)]
283pub struct ProvingDetails {
284 pub result: ProvingResult,
286 pub time: Duration,
288}
289
290#[derive(Debug, Encode, Decode)]
292pub struct DecodedFarmingError {
293 error: String,
295 is_fatal: bool,
297}
298
299impl fmt::Display for DecodedFarmingError {
300 #[inline]
301 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
302 fmt::Display::fmt(&self.error, f)
303 }
304}
305
306#[derive(Debug, Error)]
308pub enum FarmingError {
309 #[error("Failed to subscribe to slot info notifications: {error}")]
311 FailedToSubscribeSlotInfo {
312 error: anyhow::Error,
314 },
315 #[error("Failed to retrieve farmer info: {error}")]
317 FailedToGetFarmerInfo {
318 error: anyhow::Error,
320 },
321 #[error("Slot info notification stream ended")]
323 SlotNotificationStreamEnded,
324 #[error("Low-level auditing error: {0}")]
326 LowLevelAuditing(#[from] AuditingError),
327 #[error("Low-level proving error: {0}")]
329 LowLevelProving(#[from] ProvingError),
330 #[error("Farming I/O error: {0}")]
332 Io(#[from] io::Error),
333 #[error("Decoded farming error {0}")]
335 Decoded(DecodedFarmingError),
336}
337
338impl Encode for FarmingError {
339 #[inline]
340 fn encode_to<O>(&self, dest: &mut O)
341 where
342 O: Output + ?Sized,
343 {
344 let error = DecodedFarmingError {
345 error: self.to_string(),
346 is_fatal: self.is_fatal(),
347 };
348
349 error.encode_to(dest);
350 }
351}
352
353impl Decode for FarmingError {
354 #[inline]
355 fn decode<I>(input: &mut I) -> Result<Self, parity_scale_codec::Error>
356 where
357 I: Input,
358 {
359 DecodedFarmingError::decode(input).map(FarmingError::Decoded)
360 }
361}
362
363impl FarmingError {
364 #[inline]
366 pub fn str_variant(&self) -> &str {
367 #[expect(
368 clippy::rest_pattern_accessible_field,
369 reason = "Do not care about fields"
370 )]
371 match self {
372 FarmingError::FailedToSubscribeSlotInfo { .. } => "FailedToSubscribeSlotInfo",
373 FarmingError::FailedToGetFarmerInfo { .. } => "FailedToGetFarmerInfo",
374 FarmingError::LowLevelAuditing(_) => "LowLevelAuditing",
375 FarmingError::LowLevelProving(_) => "LowLevelProving",
376 FarmingError::Io(_) => "Io",
377 FarmingError::Decoded(_) => "Decoded",
378 FarmingError::SlotNotificationStreamEnded => "SlotNotificationStreamEnded",
379 }
380 }
381
382 pub fn is_fatal(&self) -> bool {
384 #[expect(
385 clippy::rest_pattern_accessible_field,
386 reason = "Do not care about fields"
387 )]
388 match self {
389 FarmingError::FailedToSubscribeSlotInfo { .. } => true,
390 FarmingError::FailedToGetFarmerInfo { .. } => true,
391 FarmingError::LowLevelAuditing(_) => true,
392 FarmingError::LowLevelProving(error) => error.is_fatal(),
393 FarmingError::Io(_) => true,
394 FarmingError::Decoded(error) => error.is_fatal,
395 FarmingError::SlotNotificationStreamEnded => true,
396 }
397 }
398}
399
400#[derive(Debug, Clone, Encode, Decode)]
402pub enum FarmingNotification {
403 Auditing(AuditingDetails),
405 Proving(ProvingDetails),
407 NonFatalError(Arc<FarmingError>),
409}
410
411#[derive(Debug, Clone, Encode, Decode)]
413pub enum SectorPlottingDetails {
414 Starting {
416 progress: f32,
418 replotting: bool,
420 last_queued: bool,
422 },
423 Downloading,
425 Downloaded(Duration),
427 Encoding,
429 Encoded(Duration),
431 Writing,
433 Written(Duration),
435 Finished {
437 plotted_sector: PlottedSector,
439 old_plotted_sector: Option<PlottedSector>,
441 time: Duration,
443 },
444 Error(String),
446}
447
448#[derive(Debug, Clone, Encode, Decode)]
450pub enum SectorExpirationDetails {
451 Determined {
453 expires_at: SegmentIndex,
455 },
456 AboutToExpire,
458 Expired,
460}
461
462#[derive(Debug, Clone, Encode, Decode)]
464pub enum SectorUpdate {
465 Plotting(SectorPlottingDetails),
467 Expiration(SectorExpirationDetails),
469}
470
471#[async_trait]
473pub trait PieceReader: Send + Sync + fmt::Debug {
474 async fn read_piece(
477 &self,
478 sector_index: SectorIndex,
479 piece_offset: PieceOffset,
480 ) -> Result<Option<Piece>, FarmError>;
481}
482
483pub trait HandlerId: Send + Sync + fmt::Debug {
485 fn detach(&self);
487}
488
489impl HandlerId for event_listener_primitives::HandlerId {
490 #[inline]
491 fn detach(&self) {
492 self.detach();
493 }
494}
495
496#[derive(
498 Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Serialize, Deserialize, Display, From,
499)]
500#[serde(untagged)]
501pub enum FarmId {
502 Ulid(Ulid),
504}
505
506impl Encode for FarmId {
507 #[inline]
508 fn size_hint(&self) -> usize {
509 1_usize
510 + match self {
511 FarmId::Ulid(ulid) => 0_usize.saturating_add(Encode::size_hint(&ulid.0)),
512 }
513 }
514
515 #[inline]
516 fn encode_to<O>(&self, dest: &mut O)
517 where
518 O: Output + ?Sized,
519 {
520 match self {
521 FarmId::Ulid(ulid) => {
522 dest.push_byte(0);
523 Encode::encode_to(&ulid.0, dest);
524 }
525 }
526 }
527}
528
529impl EncodeLike for FarmId {}
530
531impl Decode for FarmId {
532 #[inline]
533 fn decode<I>(input: &mut I) -> Result<Self, parity_scale_codec::Error>
534 where
535 I: Input,
536 {
537 match input
538 .read_byte()
539 .map_err(|e| e.chain("Could not decode `FarmId`, failed to read variant byte"))?
540 {
541 0 => u128::decode(input)
542 .map(|ulid| FarmId::Ulid(Ulid(ulid)))
543 .map_err(|e| e.chain("Could not decode `FarmId::Ulid.0`")),
544 _ => Err("Could not decode `FarmId`, variant doesn't exist".into()),
545 }
546 }
547}
548
549#[expect(
550 clippy::new_without_default,
551 reason = "Default has different semantics"
552)]
553impl FarmId {
554 #[inline]
556 pub fn new() -> Self {
557 Self::Ulid(Ulid::generate())
558 }
559}
560
561#[async_trait(?Send)]
563pub trait Farm {
564 fn id(&self) -> &FarmId;
566
567 fn total_sectors_count(&self) -> u16;
569
570 fn plotted_sectors(&self) -> Arc<dyn PlottedSectors + 'static>;
572
573 fn piece_reader(&self) -> Arc<dyn PieceReader + 'static>;
575
576 fn on_sector_update(
578 &self,
579 callback: HandlerFn<(SectorIndex, SectorUpdate)>,
580 ) -> Box<dyn HandlerId>;
581
582 fn on_farming_notification(
584 &self,
585 callback: HandlerFn<FarmingNotification>,
586 ) -> Box<dyn HandlerId>;
587
588 fn on_solution(&self, callback: HandlerFn<SolutionResponse>) -> Box<dyn HandlerId>;
590
591 fn run(self: Box<Self>) -> Pin<Box<dyn Future<Output = anyhow::Result<()>> + Send>>;
593}
594
595#[async_trait]
596impl<T> Farm for Box<T>
597where
598 T: Farm + ?Sized,
599{
600 #[inline]
601 fn id(&self) -> &FarmId {
602 self.as_ref().id()
603 }
604
605 #[inline]
606 fn total_sectors_count(&self) -> u16 {
607 self.as_ref().total_sectors_count()
608 }
609
610 #[inline]
611 fn plotted_sectors(&self) -> Arc<dyn PlottedSectors + 'static> {
612 self.as_ref().plotted_sectors()
613 }
614
615 #[inline]
616 fn piece_reader(&self) -> Arc<dyn PieceReader + 'static> {
617 self.as_ref().piece_reader()
618 }
619
620 #[inline]
621 fn on_sector_update(
622 &self,
623 callback: HandlerFn<(SectorIndex, SectorUpdate)>,
624 ) -> Box<dyn HandlerId> {
625 self.as_ref().on_sector_update(callback)
626 }
627
628 #[inline]
629 fn on_farming_notification(
630 &self,
631 callback: HandlerFn<FarmingNotification>,
632 ) -> Box<dyn HandlerId> {
633 self.as_ref().on_farming_notification(callback)
634 }
635
636 #[inline]
637 fn on_solution(&self, callback: HandlerFn<SolutionResponse>) -> Box<dyn HandlerId> {
638 self.as_ref().on_solution(callback)
639 }
640
641 #[inline]
642 fn run(self: Box<Self>) -> Pin<Box<dyn Future<Output = anyhow::Result<()>> + Send>> {
643 (*self).run()
644 }
645}