From 9b6538c3009223673bbd1da50c58712c8feb25d3 Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Mon, 24 Jul 2023 11:30:32 +0300 Subject: [PATCH] Introduce and use stateful proof of space generator API --- crates/pallet-subspace/src/mock.rs | 6 +++- crates/sp-lightclient/src/tests.rs | 5 ++++ .../benches/auditing.rs | 3 ++ .../benches/plotting.rs | 3 ++ .../benches/proving.rs | 7 ++++- .../benches/reading.rs | 5 ++++ .../src/plotting.rs | 14 ++++++---- .../subspace-farmer-components/src/proving.rs | 17 +++++++---- .../subspace-farmer-components/src/reading.rs | 5 ++-- .../src/single_disk_plot/farming.rs | 3 ++ .../src/single_disk_plot/piece_reader.rs | 5 ++++ .../src/single_disk_plot/plotting.rs | 2 ++ crates/subspace-proof-of-space/benches/pos.rs | 10 ++++--- crates/subspace-proof-of-space/src/chia.rs | 27 ++++++++++++++++-- .../src/chiapos/table.rs | 3 +- crates/subspace-proof-of-space/src/lib.rs | 25 +++++++++++++++++ crates/subspace-proof-of-space/src/shim.rs | 15 +++++++++- test/subspace-test-client/src/lib.rs | 28 +++++++++++-------- 18 files changed, 148 insertions(+), 35 deletions(-) diff --git a/crates/pallet-subspace/src/mock.rs b/crates/pallet-subspace/src/mock.rs index 8a98dc35a1..5b65c24f2b 100644 --- a/crates/pallet-subspace/src/mock.rs +++ b/crates/pallet-subspace/src/mock.rs @@ -53,6 +53,7 @@ use subspace_farmer_components::plotting::{plot_sector, PieceGetterRetryPolicy}; use subspace_farmer_components::sector::{sector_size, SectorMetadata}; use subspace_farmer_components::FarmerProtocolInfo; use subspace_proof_of_space::shim::ShimTable; +use subspace_proof_of_space::Table; use subspace_solving::REWARD_SIGNING_CONTEXT; type PosTable = ShimTable; @@ -412,6 +413,8 @@ pub fn create_signed_vote( let pieces_in_sector = farmer_protocol_info.max_pieces_in_sector; let sector_size = sector_size(pieces_in_sector); + let mut table_generator = PosTable::generator(); + for sector_index in iter::from_fn(|| Some(rand::random())) { let mut plotted_sector_bytes = vec![0; sector_size]; let mut plotted_sector_metadata_bytes = vec![0; SectorMetadata::encoded_size()]; @@ -427,6 +430,7 @@ pub fn create_signed_vote( pieces_in_sector, &mut plotted_sector_bytes, &mut plotted_sector_metadata_bytes, + &mut table_generator, )) .unwrap(); @@ -445,7 +449,7 @@ pub fn create_signed_vote( }; let solution = solution_candidates - .into_iter::<_, PosTable>(&reward_address, kzg, erasure_coding) + .into_iter::<_, PosTable>(&reward_address, kzg, erasure_coding, &mut table_generator) .unwrap() .next() .unwrap() diff --git a/crates/sp-lightclient/src/tests.rs b/crates/sp-lightclient/src/tests.rs index a582271ddd..4683388b97 100644 --- a/crates/sp-lightclient/src/tests.rs +++ b/crates/sp-lightclient/src/tests.rs @@ -33,6 +33,7 @@ use subspace_farmer_components::auditing::audit_sector; use subspace_farmer_components::plotting::{plot_sector, PieceGetterRetryPolicy}; use subspace_farmer_components::sector::{sector_size, SectorMetadata}; use subspace_farmer_components::FarmerProtocolInfo; +use subspace_proof_of_space::Table; use subspace_solving::REWARD_SIGNING_CONTEXT; use subspace_verification::{ calculate_block_weight, derive_randomness, verify_solution, VerifySolutionParams, @@ -154,6 +155,8 @@ fn valid_header( let pieces_in_sector = farmer_parameters.farmer_protocol_info.max_pieces_in_sector; let sector_size = sector_size(pieces_in_sector); + let mut table_generator = PosTable::generator(); + for sector_index in iter::from_fn(|| Some(rand::random())) { let mut plotted_sector_bytes = vec![0; sector_size]; let mut plotted_sector_metadata_bytes = vec![0; SectorMetadata::encoded_size()]; @@ -169,6 +172,7 @@ fn valid_header( pieces_in_sector, &mut plotted_sector_bytes, &mut plotted_sector_metadata_bytes, + &mut table_generator, )) .unwrap(); @@ -193,6 +197,7 @@ fn valid_header( &public_key, &farmer_parameters.kzg, &farmer_parameters.erasure_coding, + &mut table_generator, ) .unwrap() .next() diff --git a/crates/subspace-farmer-components/benches/auditing.rs b/crates/subspace-farmer-components/benches/auditing.rs index 5d5408d4e6..5565ec6813 100644 --- a/crates/subspace-farmer-components/benches/auditing.rs +++ b/crates/subspace-farmer-components/benches/auditing.rs @@ -21,6 +21,7 @@ use subspace_farmer_components::plotting::{plot_sector, PieceGetterRetryPolicy, use subspace_farmer_components::sector::{sector_size, SectorContentsMap, SectorMetadata}; use subspace_farmer_components::FarmerProtocolInfo; use subspace_proof_of_space::chia::ChiaTable; +use subspace_proof_of_space::Table; type PosTable = ChiaTable; @@ -51,6 +52,7 @@ pub fn criterion_benchmark(c: &mut Criterion) { NonZeroUsize::new(Record::NUM_S_BUCKETS.next_power_of_two().ilog2() as usize).unwrap(), ) .unwrap(); + let mut table_generator = PosTable::generator(); let archived_history_segment = archiver .add_block( AsRef::<[u8]>::as_ref(input.as_ref()).to_vec(), @@ -123,6 +125,7 @@ pub fn criterion_benchmark(c: &mut Criterion) { pieces_in_sector, &mut plotted_sector_bytes, &mut plotted_sector_metadata_bytes, + &mut table_generator, )) .unwrap(); diff --git a/crates/subspace-farmer-components/benches/plotting.rs b/crates/subspace-farmer-components/benches/plotting.rs index 14ed4086a9..f15369d29f 100644 --- a/crates/subspace-farmer-components/benches/plotting.rs +++ b/crates/subspace-farmer-components/benches/plotting.rs @@ -12,6 +12,7 @@ use subspace_farmer_components::plotting::{plot_sector, PieceGetterRetryPolicy}; use subspace_farmer_components::sector::{sector_size, SectorMetadata}; use subspace_farmer_components::FarmerProtocolInfo; use subspace_proof_of_space::chia::ChiaTable; +use subspace_proof_of_space::Table; type PosTable = ChiaTable; @@ -33,6 +34,7 @@ fn criterion_benchmark(c: &mut Criterion) { NonZeroUsize::new(Record::NUM_S_BUCKETS.next_power_of_two().ilog2() as usize).unwrap(), ) .unwrap(); + let mut table_generator = PosTable::generator(); let archived_history_segment = archiver .add_block( AsRef::<[u8]>::as_ref(input.as_ref()).to_vec(), @@ -73,6 +75,7 @@ fn criterion_benchmark(c: &mut Criterion) { black_box(pieces_in_sector), black_box(&mut sector_bytes), black_box(&mut sector_metadata_bytes), + black_box(&mut table_generator), )) .unwrap(); }) diff --git a/crates/subspace-farmer-components/benches/proving.rs b/crates/subspace-farmer-components/benches/proving.rs index 9fb78bf77b..eaf3411224 100644 --- a/crates/subspace-farmer-components/benches/proving.rs +++ b/crates/subspace-farmer-components/benches/proving.rs @@ -21,6 +21,7 @@ use subspace_farmer_components::plotting::{plot_sector, PieceGetterRetryPolicy, use subspace_farmer_components::sector::{sector_size, SectorContentsMap, SectorMetadata}; use subspace_farmer_components::FarmerProtocolInfo; use subspace_proof_of_space::chia::ChiaTable; +use subspace_proof_of_space::Table; type PosTable = ChiaTable; @@ -53,6 +54,7 @@ pub fn criterion_benchmark(c: &mut Criterion) { NonZeroUsize::new(Record::NUM_S_BUCKETS.next_power_of_two().ilog2() as usize).unwrap(), ) .unwrap(); + let mut table_generator = PosTable::generator(); let archived_history_segment = archiver .add_block( AsRef::<[u8]>::as_ref(input.as_ref()).to_vec(), @@ -125,6 +127,7 @@ pub fn criterion_benchmark(c: &mut Criterion) { pieces_in_sector, &mut plotted_sector_bytes, &mut plotted_sector_metadata_bytes, + &mut table_generator, )) .unwrap(); @@ -164,7 +167,7 @@ pub fn criterion_benchmark(c: &mut Criterion) { let num_actual_solutions = solution_candidates .clone() - .into_iter::<_, PosTable>(&reward_address, &kzg, &erasure_coding) + .into_iter::<_, PosTable>(&reward_address, &kzg, &erasure_coding, &mut table_generator) .unwrap() .len(); @@ -194,6 +197,7 @@ pub fn criterion_benchmark(c: &mut Criterion) { black_box(&reward_address), black_box(&kzg), black_box(&erasure_coding), + black_box(&mut table_generator), ) .unwrap() // Process just one solution @@ -260,6 +264,7 @@ pub fn criterion_benchmark(c: &mut Criterion) { black_box(&reward_address), black_box(&kzg), black_box(&erasure_coding), + black_box(&mut table_generator), ) .unwrap() // Process just one solution diff --git a/crates/subspace-farmer-components/benches/reading.rs b/crates/subspace-farmer-components/benches/reading.rs index a1a59fb9ad..5540ac9ee3 100644 --- a/crates/subspace-farmer-components/benches/reading.rs +++ b/crates/subspace-farmer-components/benches/reading.rs @@ -20,6 +20,7 @@ use subspace_farmer_components::reading::read_piece; use subspace_farmer_components::sector::{sector_size, SectorContentsMap, SectorMetadata}; use subspace_farmer_components::FarmerProtocolInfo; use subspace_proof_of_space::chia::ChiaTable; +use subspace_proof_of_space::Table; type PosTable = ChiaTable; @@ -50,6 +51,7 @@ pub fn criterion_benchmark(c: &mut Criterion) { NonZeroUsize::new(Record::NUM_S_BUCKETS.next_power_of_two().ilog2() as usize).unwrap(), ) .unwrap(); + let mut table_generator = PosTable::generator(); let archived_history_segment = archiver .add_block( AsRef::<[u8]>::as_ref(input.as_ref()).to_vec(), @@ -120,6 +122,7 @@ pub fn criterion_benchmark(c: &mut Criterion) { pieces_in_sector, &mut plotted_sector_bytes, &mut plotted_sector_metadata_bytes, + &mut table_generator, )) .unwrap(); @@ -148,6 +151,7 @@ pub fn criterion_benchmark(c: &mut Criterion) { black_box(&plotted_sector.sector_metadata), black_box(&plotted_sector_bytes), black_box(&erasure_coding), + black_box(&mut table_generator), ) .unwrap(); }) @@ -195,6 +199,7 @@ pub fn criterion_benchmark(c: &mut Criterion) { black_box(&plotted_sector.sector_metadata), black_box(sector), black_box(&erasure_coding), + black_box(&mut table_generator), ) .unwrap(); } diff --git a/crates/subspace-farmer-components/src/plotting.rs b/crates/subspace-farmer-components/src/plotting.rs index 1e18f7a92a..c073aee83a 100644 --- a/crates/subspace-farmer-components/src/plotting.rs +++ b/crates/subspace-farmer-components/src/plotting.rs @@ -11,7 +11,6 @@ use futures::stream::FuturesOrdered; use futures::StreamExt; use parity_scale_codec::Encode; use parking_lot::Mutex; -use rayon::prelude::*; use std::error::Error; use std::simd::Simd; use std::sync::Arc; @@ -23,7 +22,7 @@ use subspace_core_primitives::{ RecordWitness, SBucket, SectorId, SectorIndex, }; use subspace_erasure_coding::ErasureCoding; -use subspace_proof_of_space::{Quality, Table}; +use subspace_proof_of_space::{Quality, Table, TableGenerator}; use thiserror::Error; use tokio::sync::Semaphore; use tracing::{debug, warn}; @@ -169,6 +168,7 @@ pub async fn plot_sector( pieces_in_sector: u16, sector_output: &mut [u8], sector_metadata_output: &mut [u8], + table_generator: &mut PosTable::Generator, ) -> Result where PG: PieceGetter, @@ -248,11 +248,13 @@ where (PieceOffset::ZERO..) .zip(raw_sector.records.iter_mut()) .zip(sector_contents_map.iter_record_bitfields_mut()) - // TODO: Doesn't work without a bridge: https://github.com/ferrilab/bitvec/issues/143 - .par_bridge() + // TODO: Ideally, we'd use parallelism here, but using `.par_bridge()` causes Chia table + // derivation to only use a single thread, which slows everything to essentially + // single-threaded .for_each(|((piece_offset, record), mut encoded_chunks_used)| { - // Derive PoSpace table - let pos_table = PosTable::generate( + // Derive PoSpace table (use parallel mode because multiple tables concurrently will use + // too much RAM) + let pos_table = table_generator.generate_parallel( §or_id.derive_evaluation_seed(piece_offset, farmer_protocol_info.history_size), ); diff --git a/crates/subspace-farmer-components/src/proving.rs b/crates/subspace-farmer-components/src/proving.rs index f96dad80ab..a7f60e2268 100644 --- a/crates/subspace-farmer-components/src/proving.rs +++ b/crates/subspace-farmer-components/src/proving.rs @@ -2,14 +2,13 @@ use crate::auditing::ChunkCandidate; use crate::reading::{read_record_metadata, read_sector_record_chunks, ReadingError}; use crate::sector::{SectorContentsMap, SectorContentsMapFromBytesError, SectorMetadata}; use std::collections::VecDeque; -use std::marker::PhantomData; use subspace_core_primitives::crypto::kzg::{Commitment, Kzg, Witness}; use subspace_core_primitives::crypto::Scalar; use subspace_core_primitives::{ PieceOffset, PosProof, PublicKey, Record, SBucket, SectorId, SectorIndex, Solution, }; use subspace_erasure_coding::ErasureCoding; -use subspace_proof_of_space::{Quality, Table}; +use subspace_proof_of_space::{Quality, Table, TableGenerator}; use thiserror::Error; /// Errors that happen during proving @@ -116,6 +115,7 @@ impl<'a> SolutionCandidates<'a> { reward_address: &'a RewardAddress, kzg: &'a Kzg, erasure_coding: &'a ErasureCoding, + table_generator: &'a mut PosTable::Generator, ) -> Result< impl ExactSizeIterator, ProvingError>> + 'a, ProvingError, @@ -135,6 +135,7 @@ impl<'a> SolutionCandidates<'a> { kzg, erasure_coding, self.chunk_candidates, + table_generator, ) } } @@ -148,7 +149,10 @@ struct ChunkCache { proof_of_space: PosProof, } -struct SolutionCandidatesIterator<'a, RewardAddress, PosTable> { +struct SolutionCandidatesIterator<'a, RewardAddress, PosTable> +where + PosTable: Table, +{ public_key: &'a PublicKey, reward_address: &'a RewardAddress, sector_index: SectorIndex, @@ -163,7 +167,7 @@ struct SolutionCandidatesIterator<'a, RewardAddress, PosTable> { winning_chunks: VecDeque, count: usize, chunk_cache: Option, - _pos_table: PhantomData, + table_generator: &'a mut PosTable::Generator, } // TODO: This can be potentially parallelized with rayon @@ -200,7 +204,7 @@ where } // Derive PoSpace table - let pos_table = PosTable::generate_parallel( + let pos_table = self.table_generator.generate_parallel( &self .sector_id .derive_evaluation_seed(piece_offset, self.sector_metadata.history_size), @@ -351,6 +355,7 @@ where kzg: &'a Kzg, erasure_coding: &'a ErasureCoding, chunk_candidates: VecDeque, + table_generator: &'a mut PosTable::Generator, ) -> Result { if erasure_coding.max_shards() < Record::NUM_S_BUCKETS { return Err(ProvingError::InvalidErasureCodingInstance); @@ -411,7 +416,7 @@ where winning_chunks, count, chunk_cache: None, - _pos_table: PhantomData, + table_generator, }) } } diff --git a/crates/subspace-farmer-components/src/reading.rs b/crates/subspace-farmer-components/src/reading.rs index fb0c08f551..6771111781 100644 --- a/crates/subspace-farmer-components/src/reading.rs +++ b/crates/subspace-farmer-components/src/reading.rs @@ -10,7 +10,7 @@ use subspace_core_primitives::{ Piece, PieceOffset, Record, RecordCommitment, RecordWitness, SBucket, SectorId, }; use subspace_erasure_coding::ErasureCoding; -use subspace_proof_of_space::{Quality, Table}; +use subspace_proof_of_space::{Quality, Table, TableGenerator}; use thiserror::Error; /// Errors that happen during reading @@ -257,6 +257,7 @@ pub fn read_piece( sector_metadata: &SectorMetadata, sector: &[u8], erasure_coding: &ErasureCoding, + table_generator: &mut PosTable::Generator, ) -> Result where PosTable: Table, @@ -284,7 +285,7 @@ where pieces_in_sector, §or_metadata.s_bucket_offsets(), §or_contents_map, - &PosTable::generate( + &table_generator.generate( §or_id.derive_evaluation_seed(piece_offset, sector_metadata.history_size), ), sector, diff --git a/crates/subspace-farmer/src/single_disk_plot/farming.rs b/crates/subspace-farmer/src/single_disk_plot/farming.rs index 3fa794e09e..decbd3ba0d 100644 --- a/crates/subspace-farmer/src/single_disk_plot/farming.rs +++ b/crates/subspace-farmer/src/single_disk_plot/farming.rs @@ -83,6 +83,8 @@ where NC: NodeClient, PosTable: Table, { + let mut table_generator = PosTable::generator(); + while let Some(slot_info) = slot_info_notifications.next().await { let slot = slot_info.slot_number; let sectors_metadata = sectors_metadata.read(); @@ -120,6 +122,7 @@ where &reward_address, &kzg, &erasure_coding, + &mut table_generator, )? { let solution = match maybe_solution { Ok(solution) => solution, diff --git a/crates/subspace-farmer/src/single_disk_plot/piece_reader.rs b/crates/subspace-farmer/src/single_disk_plot/piece_reader.rs index 21efe3065f..48ab9cc78b 100644 --- a/crates/subspace-farmer/src/single_disk_plot/piece_reader.rs +++ b/crates/subspace-farmer/src/single_disk_plot/piece_reader.rs @@ -98,6 +98,8 @@ async fn read_pieces( } } + let mut table_generator = PosTable::generator(); + while let Some(read_piece_request) = read_piece_receiver.next().await { let ReadPieceRequest { sector_index, @@ -145,6 +147,7 @@ async fn read_pieces( §or_metadata, &global_plot_mmap, &erasure_coding, + &mut table_generator, ); // Doesn't matter if receiver still cares about it @@ -161,6 +164,7 @@ fn read_piece( sector_metadata: &SectorMetadata, global_plot: &[u8], erasure_coding: &ErasureCoding, + table_generator: &mut PosTable::Generator, ) -> Option where PosTable: Table, @@ -198,6 +202,7 @@ where sector_metadata, sector, erasure_coding, + table_generator, ) { Ok(piece) => piece, Err(error) => { diff --git a/crates/subspace-farmer/src/single_disk_plot/plotting.rs b/crates/subspace-farmer/src/single_disk_plot/plotting.rs index 2986629ead..4e61b65746 100644 --- a/crates/subspace-farmer/src/single_disk_plot/plotting.rs +++ b/crates/subspace-farmer/src/single_disk_plot/plotting.rs @@ -103,6 +103,7 @@ where PG: PieceGetter + Send + 'static, PosTable: Table, { + let mut table_generator = PosTable::generator(); // TODO: Concurrency while let Some((sector_index, _acknowledgement_sender)) = sectors_to_plot.next().await { trace!(%sector_index, "Preparing to plot sector"); @@ -174,6 +175,7 @@ where pieces_in_sector, &mut sector, &mut sector_metadata, + &mut table_generator, ); // Inform others that this sector is being modified diff --git a/crates/subspace-proof-of-space/benches/pos.rs b/crates/subspace-proof-of-space/benches/pos.rs index 15e96e2818..10de4961c3 100644 --- a/crates/subspace-proof-of-space/benches/pos.rs +++ b/crates/subspace-proof-of-space/benches/pos.rs @@ -8,7 +8,7 @@ use rayon::ThreadPoolBuilder; #[cfg(any(feature = "chia", feature = "shim"))] use subspace_core_primitives::PosSeed; #[cfg(any(feature = "chia", feature = "shim"))] -use subspace_proof_of_space::{Quality, Table}; +use subspace_proof_of_space::{Quality, Table, TableGenerator}; #[cfg(any(feature = "chia", feature = "shim"))] fn pos_bench( @@ -35,22 +35,24 @@ fn pos_bench( let mut group = c.benchmark_group(name); + let mut generator_instance = PosTable::generator(); group.bench_function("table/single", |b| { b.iter(|| { - PosTable::generate(black_box(&seed)); + generator_instance.generate(black_box(&seed)); }); }); #[cfg(feature = "parallel")] { + let mut generator_instance = PosTable::generator(); group.bench_function("table/parallel", |b| { b.iter(|| { - PosTable::generate_parallel(black_box(&seed)); + generator_instance.generate_parallel(black_box(&seed)); }); }); } - let table = PosTable::generate(&seed); + let table = generator_instance.generate(&seed); group.bench_function("quality/no-solution", |b| { b.iter(|| { diff --git a/crates/subspace-proof-of-space/src/chia.rs b/crates/subspace-proof-of-space/src/chia.rs index ce55ef26bb..a3c8b946f3 100644 --- a/crates/subspace-proof-of-space/src/chia.rs +++ b/crates/subspace-proof-of-space/src/chia.rs @@ -2,7 +2,7 @@ use crate::chiapos::Tables; #[cfg(any(feature = "parallel", test))] use crate::chiapos::TablesCache; -use crate::{PosTableType, Quality, Table}; +use crate::{PosTableType, Quality, Table, TableGenerator}; use core::mem; use subspace_core_primitives::{PosProof, PosQualityBytes, PosSeed}; @@ -33,7 +33,29 @@ impl<'a> Quality for ChiaQuality<'a> { } } -/// Subspace proof of space table +/// Subspace proof of space table generator. +/// +/// Chia implementation. +#[derive(Debug, Default, Clone)] +pub struct ChiaTableGenerator { + tables_cache: TablesCache, +} + +impl TableGenerator for ChiaTableGenerator { + fn generate(&mut self, seed: &PosSeed) -> ChiaTable { + ChiaTable { + tables: Tables::::create((*seed).into(), &mut self.tables_cache), + } + } + + fn generate_parallel(&mut self, seed: &PosSeed) -> ChiaTable { + ChiaTable { + tables: Tables::::create_parallel((*seed).into(), &mut self.tables_cache), + } + } +} + +/// Subspace proof of space table. /// /// Chia implementation. #[derive(Debug)] @@ -43,6 +65,7 @@ pub struct ChiaTable { impl Table for ChiaTable { const TABLE_TYPE: PosTableType = PosTableType::Chia; + type Generator = ChiaTableGenerator; type Quality<'a> = ChiaQuality<'a>; diff --git a/crates/subspace-proof-of-space/src/chiapos/table.rs b/crates/subspace-proof-of-space/src/chiapos/table.rs index d60ef06fb2..b291c5baa5 100644 --- a/crates/subspace-proof-of-space/src/chiapos/table.rs +++ b/crates/subspace-proof-of-space/src/chiapos/table.rs @@ -120,7 +120,7 @@ fn calculate_left_target_on_demand(parity: usize, r: usize, m: usize) -> usize { } /// Caches that can be used to optimize creation of multiple [`Tables`](super::Tables). -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct TablesCache { buckets: Vec, rmap_scratch: Vec, @@ -713,6 +713,7 @@ where /// Almost the same as [`Self::create()`], but uses parallelism internally for better /// performance (though not efficiency of CPU and memory usage), if you create multiple tables /// in parallel, prefer [`Self::create()`] for better overall performance. + // TODO: Cache more allocations in this function to improve performance further #[cfg(any(feature = "parallel", test))] pub(super) fn create_parallel( last_table: &Table, diff --git a/crates/subspace-proof-of-space/src/lib.rs b/crates/subspace-proof-of-space/src/lib.rs index 5814fddca3..3dddba32cd 100644 --- a/crates/subspace-proof-of-space/src/lib.rs +++ b/crates/subspace-proof-of-space/src/lib.rs @@ -20,6 +20,7 @@ pub mod chiapos; #[cfg(feature = "shim")] pub mod shim; +use core::fmt; use subspace_core_primitives::{PosProof, PosQualityBytes, PosSeed}; /// Abstraction that represents quality of the solution in the table @@ -42,10 +43,29 @@ pub enum PosTableType { Shim, } +/// Stateful table generator with better performance +pub trait TableGenerator: fmt::Debug + Default + Clone + Send + Sized + 'static { + /// Generate new table with 32 bytes seed. + /// + /// There is also [`Self::generate_parallel()`] that can achieve lower latency. + fn generate(&mut self, seed: &PosSeed) -> T; + + /// Generate new table with 32 bytes seed using parallelism. + /// + /// This implementation will trade efficiency of CPU and memory usage for lower latency, prefer + /// [`Self::generate()`] unless lower latency is critical. + #[cfg(any(feature = "parallel", test))] + fn generate_parallel(&mut self, seed: &PosSeed) -> T { + self.generate(seed) + } +} + /// Proof of space kind pub trait Table: Sized + Send + Sync + 'static { /// Proof of space table type const TABLE_TYPE: PosTableType; + /// Instance that can be used to generate tables with better performance + type Generator: TableGenerator; /// Abstraction that represents quality of the solution in the table type Quality<'a>: Quality @@ -75,4 +95,9 @@ pub trait Table: Sized + Send + Sync + 'static { challenge_index: u32, proof: &PosProof, ) -> Option; + + /// Returns a stateful table generator with better performance + fn generator() -> Self::Generator { + Self::Generator::default() + } } diff --git a/crates/subspace-proof-of-space/src/shim.rs b/crates/subspace-proof-of-space/src/shim.rs index 9f68047cab..ebbedb4177 100644 --- a/crates/subspace-proof-of-space/src/shim.rs +++ b/crates/subspace-proof-of-space/src/shim.rs @@ -1,6 +1,6 @@ //! Shim proof of space implementation -use crate::{PosTableType, Quality, Table}; +use crate::{PosTableType, Quality, Table, TableGenerator}; use core::iter; use subspace_core_primitives::crypto::blake2b_256_hash; use subspace_core_primitives::{Blake2b256Hash, PosProof, PosQualityBytes, PosSeed, U256}; @@ -36,6 +36,18 @@ impl<'a> Quality for ShimQuality<'a> { } } +/// Subspace proof of space table generator. +/// +/// Shim implementation. +#[derive(Debug, Default, Clone)] +pub struct ShimTableGenerator; + +impl TableGenerator for ShimTableGenerator { + fn generate(&mut self, seed: &PosSeed) -> ShimTable { + ShimTable::generate(seed) + } +} + /// Subspace proof of space table. /// /// Shim implementation. @@ -46,6 +58,7 @@ pub struct ShimTable { impl Table for ShimTable { const TABLE_TYPE: PosTableType = PosTableType::Shim; + type Generator = ShimTableGenerator; type Quality<'a> = ShimQuality<'a>; diff --git a/test/subspace-test-client/src/lib.rs b/test/subspace-test-client/src/lib.rs index e9adcb3d5d..0b76e10a61 100644 --- a/test/subspace-test-client/src/lib.rs +++ b/test/subspace-test-client/src/lib.rs @@ -150,24 +150,28 @@ async fn start_farming( ) .unwrap(); + let table_generator = PosTable::generator(); + std::thread::spawn({ let keypair = keypair.clone(); let erasure_coding = erasure_coding.clone(); move || { - let (sector, sector_metadata) = block_on(plot_one_segment::( - client.as_ref(), - &keypair, - MAX_PIECES_IN_SECTOR, - &erasure_coding, - )); + let (sector, sector_metadata, table_generator) = + block_on(plot_one_segment::( + client.as_ref(), + &keypair, + MAX_PIECES_IN_SECTOR, + &erasure_coding, + table_generator, + )); plotting_result_sender - .send((sector, sector_metadata)) + .send((sector, sector_metadata, table_generator)) .unwrap(); } }); - let (sector, plotted_sector) = plotting_result_receiver.await.unwrap(); + let (sector, plotted_sector, mut table_generator) = plotting_result_receiver.await.unwrap(); let sector_index = 0; let public_key = PublicKey::from(keypair.public.to_bytes()); @@ -193,7 +197,7 @@ async fn start_farming( .expect("With max solution range there must be a sector eligible; qed"); let solution = solution_candidates - .into_iter::<_, PosTable>(&public_key, &kzg, &erasure_coding) + .into_iter::<_, PosTable>(&public_key, &kzg, &erasure_coding, &mut table_generator) .unwrap() .next() .expect("With max solution range there must be a solution; qed") @@ -213,7 +217,8 @@ async fn plot_one_segment( keypair: &schnorrkel::Keypair, pieces_in_sector: u16, erasure_coding: &ErasureCoding, -) -> (Vec, PlottedSector) + mut table_generator: PosTable::Generator, +) -> (Vec, PlottedSector, PosTable::Generator) where PosTable: Table, Client: BlockBackend + HeaderBackend, @@ -255,9 +260,10 @@ where pieces_in_sector, &mut sector, &mut sector_metadata, + &mut table_generator, ) .await .expect("Plotting one sector in memory must not fail"); - (sector, plotted_sector) + (sector, plotted_sector, table_generator) }