Skip to content

Commit

Permalink
fix: support multiple partitions in PoRep
Browse files Browse the repository at this point in the history
  • Loading branch information
th7nder committed Feb 14, 2025
1 parent 7957571 commit 7edfd7a
Show file tree
Hide file tree
Showing 12 changed files with 116 additions and 45 deletions.
45 changes: 30 additions & 15 deletions pallets/proofs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,28 +141,43 @@ pub mod pallet {
sector: SectorNumber,
ticket: Ticket,
seed: Ticket,
proof: BoundedVec<u8, ConstU32<MAX_SEAL_PROOF_BYTES>>,
proofs: BoundedVec<
BoundedVec<u8, ConstU32<MAX_SEAL_PROOF_BYTES>>,
ConstU32<MAX_PROOFS_PER_BLOCK>,
>,
) -> DispatchResult {
let proof_len = proof.len();
ensure!(proof_len >= seal_proof.proof_size(), {
log::error!(
target: LOG_TARGET,
"PoRep proof submission does not contain enough bytes. Expected minimum length is {} got {}",
seal_proof.proof_size(), proof_len
);
Error::<T>::InvalidPoRepProof
});
let proof = Proof::<Bls12>::decode(&mut proof.as_slice()).map_err(|e| {
log::error!(target: LOG_TARGET, "failed to parse PoRep proof {:?}", e);
Error::<T>::Conversion
})?;
let mut parsed_proofs = BoundedVec::new();
for proof in proofs.iter() {
let proof_len = proof.len();
ensure!(proof_len >= seal_proof.proof_size(), {
log::error!(
target: LOG_TARGET,
"PoRep proof submission does not contain enough bytes. Expected minimum length is {} got {}",
seal_proof.proof_size(), proof_len
);
Error::<T>::InvalidPoRepProof
});
let proof = Proof::<Bls12>::decode(&mut proof.as_slice()).map_err(|e| {
log::error!(target: LOG_TARGET, "failed to parse PoRep proof {:?}", e);
Error::<T>::Conversion
})?;

parsed_proofs.try_push(proof).expect("internal (porep::ProofScheme) and external (ProofVerification) apis have the same limits on number of proofs");
}
let proof_scheme = porep::ProofScheme::setup(seal_proof);

let vkey = PoRepVerifyingKey::<T>::get().ok_or(Error::<T>::MissingPoRepVerifyingKey)?;
log::info!(target: LOG_TARGET, "Verifying PoRep proof for sector: {}...", sector);
proof_scheme
.verify(
&comm_r, &comm_d, &prover_id, sector, &ticket, &seed, vkey, &proof,
&comm_r,
&comm_d,
&prover_id,
sector,
&ticket,
&seed,
vkey,
parsed_proofs,
)
.map_err(Into::<Error<T>>::into)?;

Expand Down
8 changes: 8 additions & 0 deletions pallets/proofs/src/porep/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub struct Config {
porep_id: PoRepID,
nodes: usize,
challenges: InteractiveChallenges,
required_partitions: usize,
}

/// References:
Expand Down Expand Up @@ -71,6 +72,7 @@ impl Config {
// PRE-COND: sector size must be divisible by 32
nodes: (seal_proof.sector_size().bytes() / 32) as usize,
challenges: InteractiveChallenges::new(partitions, minimum_challenges(seal_proof)),
required_partitions: partitions,
}
}

Expand All @@ -88,6 +90,12 @@ impl Config {
pub fn challenges(&self, leaves: usize, replica_id: &Fr, seed: &[u8; 32], k: u8) -> Vec<usize> {
self.challenges.derive(leaves, replica_id, seed, k)
}

/// Expected number of partitions for given PoRep.
/// 1 partition == 1 proof.
pub fn required_partitions(&self) -> usize {
self.required_partitions
}
}

/// Reference:
Expand Down
29 changes: 26 additions & 3 deletions pallets/proofs/src/porep/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
mod config;

use config::{Config, PoRepID};
use frame_support::pallet_prelude::*;
use primitives::{
commitment::RawCommitment,
proofs::{ProverId, RegisteredSealProof, Ticket},
sector::SectorNumber,
MAX_PROOFS_PER_BLOCK,
};
use sha2::{Digest, Sha256};

Expand All @@ -21,6 +23,8 @@ use crate::{
vec, Error, Vec,
};

const LOG_TARGET: &'static str = "runtime::proofs::porep";

/// A unique 32-byte ID assigned to each distinct replica.
/// Replication is the entire process by which a sector is uniquely encoded into a replica.
pub type ReplicaId = Fr;
Expand Down Expand Up @@ -89,6 +93,8 @@ pub enum ProofError {
InvalidVerifyingKey,
/// Returned in case of failed conversion, i.e. in `bytes_into_fr()`.
Conversion,
/// When supplied number of proofs doesn't match required number of proofs (partitions).
InvalidNumberOfProofs,
}

impl From<VerificationError> for ProofError {
Expand All @@ -106,15 +112,18 @@ impl<T> From<ProofError> for Error<T> {
ProofError::InvalidProof => Error::<T>::InvalidPoRepProof,
ProofError::InvalidVerifyingKey => Error::<T>::InvalidVerifyingKey,
ProofError::Conversion => Error::<T>::Conversion,
ProofError::InvalidNumberOfProofs => Error::<T>::InvalidPoRepProof,
}
}
}

#[derive(Clone)]
pub struct Tau {
comm_d: Fr,
comm_r: Fr,
}

#[derive(Clone)]
pub struct PublicInputs {
replica_id: ReplicaId,
tau: Tau,
Expand Down Expand Up @@ -145,11 +154,18 @@ impl ProofScheme {
ticket: &Ticket,
seed: &Ticket,
vk: VerifyingKey<Bls12>,
proof: &Proof<Bls12>,
proofs: BoundedVec<Proof<Bls12>, ConstU32<MAX_PROOFS_PER_BLOCK>>,
) -> Result<(), ProofError> {
let comm_d_fr = fr32::bytes_into_fr(comm_d).map_err(|_| ProofError::Conversion)?;
let comm_r_fr = fr32::bytes_into_fr(comm_r).map_err(|_| ProofError::Conversion)?;

// Proof per partition
if proofs.len() != self.config.required_partitions() {
log::error!(target: LOG_TARGET, "Expected {} proofs (1 per partition) got {} proofs",
self.config.required_partitions(), proofs.len());
return Err(ProofError::InvalidNumberOfProofs);
}

let replica_id = self.generate_replica_id(prover_id, sector, ticket, comm_d);
let public_inputs = PublicInputs {
replica_id,
Expand All @@ -160,10 +176,17 @@ impl ProofScheme {
seed: *seed,
};

let public_inputs = self.generate_public_inputs(public_inputs, None)?;
let pvk = prepare_verifying_key(vk);

verify_proof(&pvk, proof, public_inputs.as_slice()).map_err(Into::<ProofError>::into)
for partition_index in 0..proofs.len() {
let inputs =
self.generate_public_inputs(public_inputs.clone(), Some(partition_index))?;
verify_proof(&pvk, &proofs[partition_index], inputs.as_slice()).inspect_err(|_| {
log::error!(target: LOG_TARGET, "failed to verify partition {}", partition_index);
})?;
}

Ok(())
}

/// References:
Expand Down
3 changes: 1 addition & 2 deletions pallets/proofs/src/post/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,7 @@ impl ProofScheme {
challenge_hasher.update(&u64::from(sector.id).to_le_bytes()[..]);

for n in 0..self.config.challenges_per_sector {
// let sector_index =
// partition_index * self.config.challenged_sectors_per_partition + i;
// https://github.com/filecoin-project/rust-fil-proofs/blob/8e96f1de6ca8468f5308773c3706c1a25509cf95/storage-proofs-post/src/fallback/utils.rs#L14
let challenge_index = n as u64;
let challenged_leaf =
self.generate_leaf_challenge_inner(challenge_hasher.clone(), challenge_index);
Expand Down
6 changes: 4 additions & 2 deletions pallets/proofs/src/tests/porep.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ fn verification_invalid_verifyingkey() {
sector,
ticket,
seed,
BoundedVec::try_from(proof_bytes).expect("proof bytes should be valid"),
bounded_vec![
BoundedVec::try_from(proof_bytes).expect("proof bytes should be valid")
],
),
Error::<Test>::InvalidVerifyingKey,
);
Expand Down Expand Up @@ -82,7 +84,7 @@ fn porep_verification_succeeds() {
sector,
ticket,
seed,
BoundedVec::try_from(proof_bytes).expect("proof bytes should be valid"),
bounded_vec![BoundedVec::try_from(proof_bytes).expect("proof bytes should be valid")],
));
});
}
Expand Down
21 changes: 13 additions & 8 deletions pallets/storage-provider/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ pub mod pallet {
proofs::{derive_prover_id, PublicReplicaInfo, RegisteredPoStProof},
randomness::{draw_randomness, AuthorVrfHistory, DomainSeparationTag},
sector::{ProveCommitSector, SectorNumber, SectorPreCommitInfo},
PartitionNumber, MAX_PARTITIONS_PER_DEADLINE, MAX_SEAL_PROOF_BYTES, MAX_SECTORS,
MAX_SECTORS_PER_CALL,
PartitionNumber, MAX_PARTITIONS_PER_DEADLINE, MAX_PROOFS_PER_BLOCK, MAX_SEAL_PROOF_BYTES,
MAX_SECTORS, MAX_SECTORS_PER_CALL,
};
use scale_info::TypeInfo;
use sp_arithmetic::traits::Zero;
Expand Down Expand Up @@ -601,7 +601,7 @@ pub mod pallet {
});

// Validate the proof
validate_seal_proof::<T>(&owner, &precommit, sector.proof)?;
validate_seal_proof::<T>(&owner, &precommit, sector.proofs)?;

// Sector deals that will be activated after the sector is
// successfully proven.
Expand Down Expand Up @@ -1634,14 +1634,19 @@ pub mod pallet {
fn validate_seal_proof<T: Config>(
owner: &T::AccountId,
precommit: &SectorPreCommitOnChainInfo<BalanceOf<T>, BlockNumberFor<T>>,
proof: BoundedVec<u8, ConstU32<MAX_SEAL_PROOF_BYTES>>,
proofs: BoundedVec<
BoundedVec<u8, ConstU32<MAX_SEAL_PROOF_BYTES>>,
ConstU32<MAX_PROOFS_PER_BLOCK>,
>,
) -> Result<(), DispatchError> {
let max_proof_size = precommit.info.seal_proof.proof_size();

// Check proof size
if proof.len() > max_proof_size {
log::error!(target: LOG_TARGET, "sector proof size {} exceeds max {}", proof.len(), max_proof_size);
return Err(Error::<T>::InvalidProof)?;
for proof in proofs.iter() {
if proof.len() > max_proof_size {
log::error!(target: LOG_TARGET, "sector proof size {} exceeds max {}", proof.len(), max_proof_size);
return Err(Error::<T>::InvalidProof)?;
}
}

let current_block_number = <frame_system::Pallet<T>>::block_number();
Expand Down Expand Up @@ -1692,7 +1697,7 @@ pub mod pallet {
precommit.info.sector_number,
precommit.seal_randomness,
interactive_randomness,
proof,
proofs,
)
}

Expand Down
5 changes: 4 additions & 1 deletion primitives/src/pallets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@ pub trait ProofVerification {
sector: SectorNumber,
ticket: Ticket,
seed: Ticket,
proof: BoundedVec<u8, ConstU32<MAX_SEAL_PROOF_BYTES>>,
proofs: BoundedVec<
BoundedVec<u8, ConstU32<MAX_SEAL_PROOF_BYTES>>,
ConstU32<MAX_PROOFS_PER_BLOCK>,
>,
) -> DispatchResult;

fn verify_post(
Expand Down
5 changes: 4 additions & 1 deletion primitives/src/proofs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,10 @@ pub mod testing {
_sector: SectorNumber,
_ticket: Ticket,
_seed: Ticket,
_proof: BoundedVec<u8, ConstU32<MAX_SEAL_PROOF_BYTES>>,
_proofs: BoundedVec<
BoundedVec<u8, ConstU32<MAX_SEAL_PROOF_BYTES>>,
ConstU32<MAX_PROOFS_PER_BLOCK>,
>,
) -> sp_runtime::DispatchResult {
Ok(())
}
Expand Down
5 changes: 3 additions & 2 deletions primitives/src/sector/prove_commit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ use scale_info::TypeInfo;
use sp_core::{ConstU32, RuntimeDebug};
use sp_runtime::BoundedVec;

use crate::{sector::SectorNumber, MAX_SEAL_PROOF_BYTES};
use crate::{sector::SectorNumber, MAX_PROOFS_PER_BLOCK, MAX_SEAL_PROOF_BYTES};

/// Arguments passed into the `prove_commit_sector` extrinsic.
#[derive(Clone, RuntimeDebug, Decode, Encode, PartialEq, TypeInfo)]
pub struct ProveCommitSector {
pub sector_number: SectorNumber,
pub proof: BoundedVec<u8, ConstU32<MAX_SEAL_PROOF_BYTES>>,
pub proofs:
BoundedVec<BoundedVec<u8, ConstU32<MAX_SEAL_PROOF_BYTES>>, ConstU32<MAX_PROOFS_PER_BLOCK>>,
}
20 changes: 10 additions & 10 deletions storage-provider/common/src/sector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use serde::{Deserialize, Serialize};
use storagext::{
types::{
market::DealProposal,
storage_provider::{ProveCommitSector, SectorPreCommitInfo},
storage_provider::{PoRepProof, ProveCommitSector, SectorPreCommitInfo},
},
RandomnessClientExt, StorageProviderClientExt, SystemClientExt,
};
Expand Down Expand Up @@ -454,23 +454,23 @@ impl PreCommittedSector {
}
};

// TODO(@th7nder,28/01/2025): adjust proofs in PoRep for bigger sector sizes
// We use sector size 2KiB only at this point, which guarantees to have 1 proof, because it has 1 partition in the config.
// That's why `prove_commit` will always generate a 1 proof.
let proof: SubstrateProof = proofs[0]
.clone()
.try_into()
.expect("converstion between rust-fil-proofs and polka-storage-proofs to work");
let proofs = proofs
.into_iter()
.map(|p| PoRepProof {
proof: codec::Encode::encode(&TryInto::<SubstrateProof>::try_into(p).expect(
"converstion between rust-fil-proofs and polka-storage-proofs should work",
)),
})
.collect::<Vec<_>>();

let proof = codec::Encode::encode(&proof);
tracing::info!("Proven sector: {}", self.sector_number);

let result = xt_client
.prove_commit_sectors(
xt_keypair,
vec![ProveCommitSector {
sector_number: self.sector_number,
proof,
proofs,
}],
true,
)
Expand Down
Binary file modified storagext/lib/artifacts/metadata.scale
Binary file not shown.
14 changes: 13 additions & 1 deletion storagext/lib/src/types/storage_provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,12 @@ pub struct ProveCommitSector {
pub sector_number: SectorNumber,
/// Raw proof bytes serialized with [`parity_scale_codec::Encode::encode`]
/// and using [`bls12_381::Bls12`] as a curve.
#[serde(flatten)]
pub proofs: Vec<PoRepProof>,
}

#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize)]
pub struct PoRepProof {
#[serde(with = "hex")]
pub proof: Vec<u8>,
}
Expand All @@ -149,7 +155,13 @@ impl From<ProveCommitSector> for RuntimeProveCommitSector {
fn from(value: ProveCommitSector) -> Self {
Self {
sector_number: value.sector_number,
proof: value.proof.into_bounded_byte_vec(),
proofs: bounded_vec::BoundedVec(
value
.proofs
.into_iter()
.map(|p| p.proof.into_bounded_byte_vec())
.collect::<Vec<_>>(),
),
}
}
}
Expand Down

0 comments on commit 7edfd7a

Please sign in to comment.