Skip to content

Commit

Permalink
Merge pull request #2020 from subspace/farming-prefer-higher-quality-…
Browse files Browse the repository at this point in the history
…solutions

Prefer higher quality solutions
  • Loading branch information
nazar-pc authored Sep 30, 2023
2 parents 0caf3bf + fbe96e5 commit 7779c1f
Show file tree
Hide file tree
Showing 7 changed files with 273 additions and 131 deletions.
19 changes: 11 additions & 8 deletions crates/pallet-subspace/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,17 +48,17 @@ use subspace_core_primitives::crypto::kzg::{embedded_kzg_settings, Kzg};
use subspace_core_primitives::crypto::Scalar;
use subspace_core_primitives::{
ArchivedBlockProgress, ArchivedHistorySegment, Blake2b256Hash, BlockNumber, HistorySize,
LastArchivedBlock, Piece, PieceOffset, PotOutput, PublicKey, Record, RecordedHistorySegment,
SegmentCommitment, SegmentHeader, SegmentIndex, SlotNumber, Solution, SolutionRange,
REWARD_SIGNING_CONTEXT,
LastArchivedBlock, Piece, PieceOffset, PosSeed, PotOutput, PublicKey, Record,
RecordedHistorySegment, SegmentCommitment, SegmentHeader, SegmentIndex, SlotNumber, Solution,
SolutionRange, REWARD_SIGNING_CONTEXT,
};
use subspace_erasure_coding::ErasureCoding;
use subspace_farmer_components::auditing::audit_sector;
use subspace_farmer_components::plotting::{plot_sector, PieceGetterRetryPolicy};
use subspace_farmer_components::sector::{sector_size, SectorMetadataChecksummed};
use subspace_farmer_components::FarmerProtocolInfo;
use subspace_proof_of_space::shim::ShimTable;
use subspace_proof_of_space::Table;
use subspace_proof_of_space::{Table, TableGenerator};

type PosTable = ShimTable;

Expand Down Expand Up @@ -468,7 +468,7 @@ pub fn create_signed_vote(
))
.unwrap();

let maybe_solution_candidates = audit_sector(
let maybe_audit_result = audit_sector(
&public_key,
sector_index,
&proof_of_time
Expand All @@ -479,13 +479,16 @@ pub fn create_signed_vote(
&plotted_sector.sector_metadata,
);

let Some(solution_candidates) = maybe_solution_candidates else {
let Some(audit_result) = maybe_audit_result else {
// Sector didn't have any solutions
continue;
};

let solution = solution_candidates
.into_iter::<_, PosTable>(&reward_address, kzg, erasure_coding, &mut table_generator)
let solution = audit_result
.solution_candidates
.into_solutions(&reward_address, kzg, erasure_coding, |seed: &PosSeed| {
table_generator.generate_parallel(seed)
})
.unwrap()
.next()
.unwrap()
Expand Down
27 changes: 16 additions & 11 deletions crates/subspace-farmer-components/benches/proving.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ use subspace_archiving::archiver::Archiver;
use subspace_core_primitives::crypto::kzg;
use subspace_core_primitives::crypto::kzg::Kzg;
use subspace_core_primitives::{
Blake2b256Hash, HistorySize, PublicKey, Record, RecordedHistorySegment, SectorId, SolutionRange,
Blake2b256Hash, HistorySize, PosSeed, PublicKey, Record, RecordedHistorySegment, SectorId,
SolutionRange,
};
use subspace_erasure_coding::ErasureCoding;
use subspace_farmer_components::auditing::audit_sector;
Expand All @@ -22,7 +23,7 @@ use subspace_farmer_components::sector::{
};
use subspace_farmer_components::{FarmerProtocolInfo, ReadAt};
use subspace_proof_of_space::chia::ChiaTable;
use subspace_proof_of_space::Table;
use subspace_proof_of_space::{Table, TableGenerator};

type PosTable = ChiaTable;

Expand Down Expand Up @@ -151,7 +152,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
let mut global_challenge = Blake2b256Hash::default();
rng.fill_bytes(&mut global_challenge);

let maybe_solution_candidates = audit_sector(
let maybe_audit_result = audit_sector(
&public_key,
sector_index,
&global_challenge,
Expand All @@ -160,16 +161,18 @@ pub fn criterion_benchmark(c: &mut Criterion) {
&plotted_sector.sector_metadata,
);

let solution_candidates = match maybe_solution_candidates {
Some(solution_candidates) => solution_candidates,
let solution_candidates = match maybe_audit_result {
Some(audit_result) => audit_result.solution_candidates,
None => {
continue;
}
};

let num_actual_solutions = solution_candidates
.clone()
.into_iter::<_, PosTable>(&reward_address, &kzg, &erasure_coding, &mut table_generator)
.into_solutions(&reward_address, &kzg, &erasure_coding, |seed: &PosSeed| {
table_generator.generate_parallel(seed)
})
.unwrap()
.len();

Expand All @@ -188,18 +191,19 @@ pub fn criterion_benchmark(c: &mut Criterion) {
&plotted_sector_bytes,
&plotted_sector.sector_metadata,
)
.unwrap();
.unwrap()
.solution_candidates;

group.throughput(Throughput::Elements(1));
group.bench_function("memory", |b| {
b.iter(|| {
solution_candidates
.clone()
.into_iter::<_, PosTable>(
.into_solutions(
black_box(&reward_address),
black_box(&kzg),
black_box(&erasure_coding),
black_box(&mut table_generator),
black_box(|seed: &PosSeed| table_generator.generate_parallel(seed)),
)
.unwrap()
// Process just one solution
Expand Down Expand Up @@ -249,6 +253,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
&plotted_sector.sector_metadata,
)
.unwrap()
.solution_candidates
})
.collect::<Vec<_>>();

Expand All @@ -259,11 +264,11 @@ pub fn criterion_benchmark(c: &mut Criterion) {
for _i in 0..iters {
for solution_candidates in solution_candidates.clone() {
solution_candidates
.into_iter::<_, PosTable>(
.into_solutions(
black_box(&reward_address),
black_box(&kzg),
black_box(&erasure_coding),
black_box(&mut table_generator),
black_box(|seed: &PosSeed| table_generator.generate_parallel(seed)),
)
.unwrap()
// Process just one solution
Expand Down
97 changes: 77 additions & 20 deletions crates/subspace-farmer-components/src/auditing.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,41 @@
use crate::proving::SolutionCandidates;
use crate::sector::{SectorContentsMap, SectorMetadataChecksummed};
use crate::ReadAt;
use std::collections::VecDeque;
use std::mem;
use subspace_core_primitives::crypto::Scalar;
use subspace_core_primitives::{Blake2b256Hash, PublicKey, SectorId, SectorIndex, SolutionRange};
use subspace_verification::is_within_solution_range;
use tracing::warn;

/// Result of sector audit
#[derive(Debug, Clone)]
pub struct AuditResult<'a, Sector>
where
Sector: ?Sized,
{
/// Solution candidates
pub solution_candidates: SolutionCandidates<'a, Sector>,
/// Best solution distance found
pub best_solution_distance: SolutionRange,
}

/// Audit chunk candidate
#[derive(Debug, Clone)]
pub(crate) struct AuditChunkCandidate {
/// Audit chunk offset
pub(crate) offset: u8,
/// Solution distance of this audit chunk, can be used to prioritize higher quality solutions
pub(crate) solution_distance: SolutionRange,
}

/// Chunk candidate, contains one or more potentially winning audit chunks (in case chunk itself was
/// encoded and eligible for claiming a reward)
#[derive(Debug, Clone)]
pub(crate) struct ChunkCandidate {
/// Chunk offset within s-bucket
pub(crate) chunk_offset: u32,
/// Audit chunk offsets in above chunk
pub(crate) audit_chunk_offsets: VecDeque<u8>,
/// Audit chunk candidates in above chunk
pub(crate) audit_chunks: Vec<AuditChunkCandidate>,
}

/// Audit a single sector and generate a stream of solutions, where `sector` must be positioned
Expand All @@ -26,7 +48,7 @@ pub fn audit_sector<'a, Sector>(
solution_range: SolutionRange,
sector: &'a Sector,
sector_metadata: &'a SectorMetadataChecksummed,
) -> Option<SolutionCandidates<'a, Sector>>
) -> Option<AuditResult<'a, Sector>>
where
Sector: ReadAt + ?Sized,
{
Expand All @@ -51,7 +73,7 @@ where
let s_bucket_audit_offset_in_sector = sector_contents_map_size + s_bucket_audit_offset;

// Map all winning chunks
let winning_chunks = (0..s_bucket_audit_size)
let mut winning_chunks = (0..s_bucket_audit_size)
.filter_map(|chunk_offset| {
let mut chunk = [0; Scalar::FULL_BYTES];
if let Err(error) = sector.read_at(
Expand All @@ -62,7 +84,7 @@ where
return None;
}
// Check all audit chunks within chunk, there might be more than one winning
let winning_audit_chunk_offsets = chunk
let mut winning_audit_chunks = chunk
.array_chunks::<{ mem::size_of::<SolutionRange>() }>()
.enumerate()
.filter_map(|(audit_chunk_offset, &audit_chunk)| {
Expand All @@ -72,34 +94,69 @@ where
&sector_slot_challenge,
solution_range,
)
.then_some(audit_chunk_offset as u8)
.map(|solution_distance| AuditChunkCandidate {
offset: audit_chunk_offset as u8,
solution_distance,
})
})
.collect::<VecDeque<_>>();
.collect::<Vec<_>>();

// In case none of the audit chunks are winning, we don't care about this sector
if winning_audit_chunk_offsets.is_empty() {
if winning_audit_chunks.is_empty() {
return None;
}

winning_audit_chunks.sort_by(|a, b| {
// Comparing `b` to `a` because we want smaller values first
b.solution_distance.cmp(&a.solution_distance)
});

Some(ChunkCandidate {
chunk_offset: chunk_offset as u32,
audit_chunk_offsets: winning_audit_chunk_offsets,
audit_chunks: winning_audit_chunks,
})
})
.collect::<VecDeque<_>>();
.collect::<Vec<_>>();

// Check if there are any solutions possible
if winning_chunks.is_empty() {
return None;
}

Some(SolutionCandidates::new(
public_key,
sector_index,
sector_id,
s_bucket_audit_index,
sector,
sector_metadata,
winning_chunks,
))
winning_chunks.sort_by(|a, b| {
let a_solution_distance = a
.audit_chunks
.first()
.expect("Lists of audit chunks are non-empty; qed")
.solution_distance;
let b_solution_distance = b
.audit_chunks
.first()
.expect("Lists of audit chunks are non-empty; qed")
.solution_distance;

// Comparing `b` to `a` because we want smaller values first
b_solution_distance.cmp(&a_solution_distance)
});

let best_solution_distance = winning_chunks
.first()
.expect("Not empty, checked above; qed")
.audit_chunks
.first()
.expect("Lists of audit chunks are non-empty; qed")
.solution_distance;

Some(AuditResult {
solution_candidates: SolutionCandidates::new(
public_key,
sector_index,
sector_id,
s_bucket_audit_index,
sector,
sector_metadata,
winning_chunks.into(),
),
best_solution_distance,
})
}
Loading

0 comments on commit 7779c1f

Please sign in to comment.