Skip to content

Commit

Permalink
Merge pull request #2160 from subspace/pos-tweaks
Browse files Browse the repository at this point in the history
PoS tweaks
  • Loading branch information
nazar-pc committed Oct 25, 2023
2 parents 50a80ae + 6d1043b commit 3a33091
Show file tree
Hide file tree
Showing 10 changed files with 78 additions and 204 deletions.
23 changes: 0 additions & 23 deletions crates/subspace-core-primitives/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,29 +169,6 @@ impl PosSeed {
pub const SIZE: usize = 32;
}

/// Proof of space quality.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Deref)]
pub struct PosQualityBytes([u8; Self::SIZE]);

impl From<[u8; PosQualityBytes::SIZE]> for PosQualityBytes {
#[inline]
fn from(value: [u8; Self::SIZE]) -> Self {
Self(value)
}
}

impl From<PosQualityBytes> for [u8; PosQualityBytes::SIZE] {
#[inline]
fn from(value: PosQualityBytes) -> Self {
value.0
}
}

impl PosQualityBytes {
/// Size of proof of space quality in bytes.
pub const SIZE: usize = 32;
}

/// Proof of space proof bytes.
#[derive(
Debug, Copy, Clone, Eq, PartialEq, Deref, DerefMut, Encode, Decode, TypeInfo, MaxEncodedLen,
Expand Down
8 changes: 3 additions & 5 deletions crates/subspace-farmer-components/src/plotting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use subspace_core_primitives::{
SectorId, SectorIndex,
};
use subspace_erasure_coding::ErasureCoding;
use subspace_proof_of_space::{Quality, Table, TableGenerator};
use subspace_proof_of_space::{Table, TableGenerator};
use thiserror::Error;
use tokio::sync::Semaphore;
use tokio::task::yield_now;
Expand Down Expand Up @@ -353,11 +353,9 @@ where
.interleave(&parity_record_chunks),
)
.map(|(s_bucket, record_chunk)| {
let quality = pos_table.find_quality(s_bucket.into())?;
let proof = pos_table.find_proof(s_bucket.into())?;

Some(
Simd::from(record_chunk.to_bytes()) ^ Simd::from(quality.create_proof().hash()),
)
Some(Simd::from(record_chunk.to_bytes()) ^ Simd::from(proof.hash()))
})
.collect_into_vec(&mut chunks_scratch);
let num_successfully_encoded_chunks = chunks_scratch
Expand Down
12 changes: 4 additions & 8 deletions crates/subspace-farmer-components/src/proving.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use subspace_core_primitives::{
RecordWitness, SBucket, SectorId, Solution, SolutionRange,
};
use subspace_erasure_coding::ErasureCoding;
use subspace_proof_of_space::{Quality, Table};
use subspace_proof_of_space::Table;
use thiserror::Error;

/// Solutions that can be proven if necessary.
Expand Down Expand Up @@ -440,13 +440,9 @@ where
);
let record_metadata = record_metadata_fut.await?;

let proof_of_space = pos_table
.find_quality(state.s_bucket.into())
.expect(
"Quality exists for this s-bucket, otherwise it wouldn't be a winning \
chunk; qed",
)
.create_proof();
let proof_of_space = pos_table.find_proof(state.s_bucket.into()).expect(
"Quality exists for this s-bucket, otherwise it wouldn't be a winning chunk; qed",
);

let chunk_witness = state
.kzg
Expand Down
19 changes: 9 additions & 10 deletions crates/subspace-farmer-components/src/reading.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use subspace_core_primitives::{
Piece, PieceOffset, Record, RecordCommitment, RecordWitness, SBucket, SectorId,
};
use subspace_erasure_coding::ErasureCoding;
use subspace_proof_of_space::{Quality, Table, TableGenerator};
use subspace_proof_of_space::{Table, TableGenerator};
use thiserror::Error;
use tracing::debug;

Expand Down Expand Up @@ -148,13 +148,12 @@ where

// Decode chunk if necessary
if encoded_chunk_used {
let quality = pos_table.find_quality(s_bucket.into()).expect(
"encoded_chunk_used implies quality exists for this chunk; qed",
);
let proof = pos_table
.find_proof(s_bucket.into())
.expect("encoded_chunk_used implies proof exists for this chunk; qed");

record_chunk = Simd::to_array(
Simd::from(record_chunk) ^ Simd::from(quality.create_proof().hash()),
);
record_chunk =
Simd::to_array(Simd::from(record_chunk) ^ Simd::from(proof.hash()));
}

maybe_record_chunk.replace(Scalar::try_from(record_chunk).map_err(
Expand Down Expand Up @@ -194,12 +193,12 @@ where

// Decode chunk if necessary
if encoded_chunk_used {
let quality = pos_table.find_quality(s_bucket.into()).expect(
"encoded_chunk_used implies quality exists for this chunk; qed",
let proof = pos_table.find_proof(s_bucket.into()).expect(
"encoded_chunk_used implies proof exists for this chunk; qed",
);

record_chunk = Simd::to_array(
Simd::from(record_chunk) ^ Simd::from(quality.create_proof().hash()),
Simd::from(record_chunk) ^ Simd::from(proof.hash()),
);
}

Expand Down
28 changes: 11 additions & 17 deletions crates/subspace-proof-of-space/benches/pos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use criterion::{black_box, criterion_group, criterion_main, Criterion};
#[cfg(feature = "parallel")]
use rayon::ThreadPoolBuilder;
use subspace_core_primitives::PosSeed;
use subspace_proof_of_space::{Quality, Table, TableGenerator};
use subspace_proof_of_space::{Table, TableGenerator};

fn pos_bench<PosTable>(
c: &mut Criterion,
Expand Down Expand Up @@ -49,37 +49,31 @@ fn pos_bench<PosTable>(

let table = generator_instance.generate(&seed);

group.bench_function("quality/no-solution", |b| {
group.bench_function("proof/missing", |b| {
b.iter(|| {
assert!(table
.find_quality(black_box(challenge_index_without_solution))
.find_proof(black_box(challenge_index_without_solution))
.is_none());
});
});

group.bench_function("quality/solution", |b| {
group.bench_function("proof/present", |b| {
b.iter(|| {
assert!(table
.find_quality(black_box(challenge_index_with_solution))
.find_proof(black_box(challenge_index_with_solution))
.is_some());
});
});

let quality = table.find_quality(challenge_index_with_solution).unwrap();

group.bench_function("proof", |b| {
b.iter(|| {
quality.create_proof();
});
});

let proof = quality.create_proof();
let proof = table.find_proof(challenge_index_with_solution).unwrap();

group.bench_function("verification", |b| {
b.iter(|| {
assert!(
PosTable::is_proof_valid(&seed, challenge_index_with_solution, &proof).is_some()
);
assert!(PosTable::is_proof_valid(
&seed,
challenge_index_with_solution,
&proof
));
});
});
group.finish();
Expand Down
72 changes: 17 additions & 55 deletions crates/subspace-proof-of-space/src/chia.rs
Original file line number Diff line number Diff line change
@@ -1,36 +1,11 @@
//! Chia proof of space implementation
use crate::chiapos::{Tables, TablesCache};
use crate::{PosTableType, Quality, Table, TableGenerator};
use crate::{PosTableType, Table, TableGenerator};
use core::mem;
use subspace_core_primitives::{PosProof, PosQualityBytes, PosSeed};
use subspace_core_primitives::{PosProof, PosSeed};

const K: u8 = 20;

/// Abstraction that represents quality of the solution in the table.
///
/// Chia implementation.
#[derive(Debug)]
#[must_use]
pub struct ChiaQuality<'a> {
bytes: PosQualityBytes,
challenge: [u8; 32],
tables: &'a Tables<K>,
}

impl<'a> Quality for ChiaQuality<'a> {
fn to_bytes(&self) -> PosQualityBytes {
self.bytes
}

fn create_proof(&self) -> PosProof {
self.tables
.find_proof(&self.challenge)
.next()
.map(PosProof::from)
.expect("Proof always exists if quality exists; qed")
}
}

/// Subspace proof of space table generator.
///
/// Chia implementation.
Expand Down Expand Up @@ -66,8 +41,6 @@ impl Table for ChiaTable {
const TABLE_TYPE: PosTableType = PosTableType::Chia;
type Generator = ChiaTableGenerator;

type Quality<'a> = ChiaQuality<'a>;

fn generate(seed: &PosSeed) -> ChiaTable {
Self {
tables: Tables::<K>::create_simple((*seed).into()),
Expand All @@ -81,25 +54,22 @@ impl Table for ChiaTable {
}
}

fn find_quality(&self, challenge_index: u32) -> Option<Self::Quality<'_>> {
fn find_proof(&self, challenge_index: u32) -> Option<PosProof> {
let mut challenge = [0; 32];
challenge[..mem::size_of::<u32>()].copy_from_slice(&challenge_index.to_le_bytes());
let maybe_quality = self.tables.find_quality(&challenge).next();
maybe_quality.map(|quality| ChiaQuality {
bytes: PosQualityBytes::from(quality),
challenge,
tables: &self.tables,
})

let proof = self
.tables
.find_proof(&challenge)
.next()
.map(PosProof::from);
proof
}

fn is_proof_valid(
seed: &PosSeed,
challenge_index: u32,
proof: &PosProof,
) -> Option<PosQualityBytes> {
fn is_proof_valid(seed: &PosSeed, challenge_index: u32, proof: &PosProof) -> bool {
let mut challenge = [0; 32];
challenge[..mem::size_of::<u32>()].copy_from_slice(&challenge_index.to_le_bytes());
Tables::<K>::verify(**seed, &challenge, proof).map(PosQualityBytes::from)
Tables::<K>::verify(**seed, &challenge, proof).is_some()
}
}

Expand All @@ -117,22 +87,14 @@ mod tests {
let table = ChiaTable::generate(&seed);
let table_parallel = ChiaTable::generate_parallel(&seed);

assert!(table.find_quality(1232460437).is_none());
assert!(table_parallel.find_quality(1232460437).is_none());
assert!(table.find_proof(1232460437).is_none());
assert!(table_parallel.find_proof(1232460437).is_none());

{
let challenge_index = 600426542;
let quality = table.find_quality(challenge_index).unwrap();
assert_eq!(
quality.to_bytes(),
table_parallel
.find_quality(challenge_index)
.unwrap()
.to_bytes()
);
let proof = quality.create_proof();
let maybe_quality = ChiaTable::is_proof_valid(&seed, challenge_index, &proof);
assert_eq!(maybe_quality, Some(quality.to_bytes()));
let proof = table.find_proof(challenge_index).unwrap();
assert_eq!(proof, table_parallel.find_proof(challenge_index).unwrap());
assert!(ChiaTable::is_proof_valid(&seed, challenge_index, &proof));
}
}
}
2 changes: 2 additions & 0 deletions crates/subspace-proof-of-space/src/chiapos/tables.rs
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,8 @@ where
let mut hasher = Sha256::new();
hasher.update(challenge);

// NOTE: this works correctly, but may overflow if `quality_index` is changed to
// not be zero-initialized anymore
let left_right_xs_bit_offset = quality_index * usize::from(K * 2);
// Collect `left_x` and `right_x` bits, potentially with extra bits at the beginning
// and the end
Expand Down
26 changes: 4 additions & 22 deletions crates/subspace-proof-of-space/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,7 @@ pub mod chiapos;
pub mod shim;

use core::fmt;
use subspace_core_primitives::{PosProof, PosQualityBytes, PosSeed};

/// Abstraction that represents quality of the solution in the table
pub trait Quality {
/// Get underlying bytes representation of the quality
fn to_bytes(&self) -> PosQualityBytes;

/// Create proof for this solution
fn create_proof(&self) -> PosProof;
}
use subspace_core_primitives::{PosProof, PosSeed};

/// Proof of space table type
#[derive(Debug, Clone, Copy)]
Expand Down Expand Up @@ -62,11 +53,6 @@ pub trait Table: Sized + Send + Sync + 'static {
/// Instance that can be used to generate tables with better performance
type Generator: TableGenerator<Self>;

/// Abstraction that represents quality of the solution in the table
type Quality<'a>: Quality
where
Self: 'a;

/// Generate new table with 32 bytes seed.
///
/// There is also [`Self::generate_parallel()`] that can achieve lower latency.
Expand All @@ -81,15 +67,11 @@ pub trait Table: Sized + Send + Sync + 'static {
Self::generate(seed)
}

/// Try to find quality of the proof at `challenge_index` if proof exists
fn find_quality(&self, challenge_index: u32) -> Option<Self::Quality<'_>>;
/// Try to find proof at `challenge_index` if it exists
fn find_proof(&self, challenge_index: u32) -> Option<PosProof>;

/// Check whether proof created earlier is valid and return quality bytes if yes
fn is_proof_valid(
seed: &PosSeed,
challenge_index: u32,
proof: &PosProof,
) -> Option<PosQualityBytes>;
fn is_proof_valid(seed: &PosSeed, challenge_index: u32, proof: &PosProof) -> bool;

/// Returns a stateful table generator with better performance
fn generator() -> Self::Generator {
Expand Down

0 comments on commit 3a33091

Please sign in to comment.