From 26c71434726d3e1c1cb59bfe66b76babdc6b95bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rok=20=C4=8Cerni=C4=8D?= Date: Mon, 18 Nov 2024 16:29:14 -0800 Subject: [PATCH] feat(commitment): change abstraction (#566) --- Cargo.lock | 12 + Cargo.toml | 1 + .../client/src/commands/proofs.rs | 17 +- .../common/src/commp.rs | 13 +- .../server/src/pipeline/mod.rs | 41 ++- .../server/src/pipeline/types.rs | 24 +- cli/polka-storage-provider/server/src/rpc.rs | 17 +- lib/polka-storage-proofs/src/porep/sealer.rs | 50 ++-- pallets/market/src/lib.rs | 6 +- pallets/market/src/test.rs | 5 +- pallets/storage-provider/src/lib.rs | 10 +- pallets/storage-provider/src/tests/mod.rs | 5 +- primitives/commitment/Cargo.toml | 1 + primitives/commitment/src/commd.rs | 17 +- primitives/commitment/src/lib.rs | 273 ++++++++++-------- primitives/commitment/src/piece.rs | 26 +- 16 files changed, 274 insertions(+), 244 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a5621200..4b199adc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11102,6 +11102,7 @@ dependencies = [ "filecoin-proofs", "primitives-proofs", "rand", + "sealed", "serde", "sha2 0.10.8", ] @@ -13804,6 +13805,17 @@ dependencies = [ "thiserror", ] +[[package]] +name = "sealed" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22f968c5ea23d555e670b449c1c5e7b2fc399fdaec1d304a17cd48e288abc107" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "sec1" version = "0.7.3" diff --git a/Cargo.toml b/Cargo.toml index a80f02b0..9a8ad676 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -90,6 +90,7 @@ rand_chacha = { version = "0.3.1", default-features = false } rand_xorshift = "0.3" rocksdb = { version = "0.21" } scale-info = { version = "2.11.1", default-features = false } +sealed = "0.6.0" serde = { version = "1.0.197", default-features = false } serde-big-array = { version = "0.3.2" } serde_derive = { version = "1.0.117" } diff --git a/cli/polka-storage-provider/client/src/commands/proofs.rs b/cli/polka-storage-provider/client/src/commands/proofs.rs index b166a8cd..973ad31e 100644 --- a/cli/polka-storage-provider/client/src/commands/proofs.rs +++ b/cli/polka-storage-provider/client/src/commands/proofs.rs @@ -14,7 +14,7 @@ use polka_storage_proofs::{ use polka_storage_provider_common::commp::{calculate_piece_commitment, CommPError}; use primitives_commitment::{ piece::{PaddedPieceSize, PieceInfo}, - Commitment, CommitmentKind, + Commitment, }; use primitives_proofs::{derive_prover_id, RegisteredPoStProof, RegisteredSealProof}; use storagext::multipair::{MultiPairArgs, MultiPairSigner}; @@ -247,14 +247,11 @@ impl ProofsCommand { let piece_file_length = PaddedPieceSize::from_arbitrary_size(piece_file_length); let piece_file = ZeroPaddingReader::new(piece_file, *piece_file_length.unpadded()); - let commp = cid::Cid::from_str(commp.as_str()) + let commp = cid::Cid::from_str(&commp) .map_err(|e| UtilsCommandError::InvalidPieceCommP(commp, e))?; let piece_info = PieceInfo { - commitment: primitives_commitment::Commitment::from_cid( - &commp, - CommitmentKind::Piece, - ) - .map_err(|e| UtilsCommandError::InvalidPieceType(commp.to_string(), e))?, + commitment: Commitment::try_from(commp) + .map_err(|e| UtilsCommandError::InvalidPieceType(commp.to_string(), e))?, size: piece_file_length, }; @@ -299,13 +296,13 @@ impl ProofsCommand { sector_id, ticket, Some(seed), - precommit.clone(), + precommit, &piece_infos, ) .map_err(|e| UtilsCommandError::GeneratePoRepError(e))?; - println!("CommD: {:?}", Commitment::data(precommit.comm_d).cid()); - println!("CommR: {:?}", Commitment::replica(precommit.comm_r).cid()); + println!("CommD: {}", precommit.comm_d.cid()); + println!("CommR: {}", precommit.comm_r.cid()); println!("Proof: {:?}", proofs); // 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. diff --git a/cli/polka-storage-provider/common/src/commp.rs b/cli/polka-storage-provider/common/src/commp.rs index f3d4e1e3..7b9ed2e6 100644 --- a/cli/polka-storage-provider/common/src/commp.rs +++ b/cli/polka-storage-provider/common/src/commp.rs @@ -5,7 +5,7 @@ use filecoin_hashers::{ Domain, }; use fr32::Fr32Reader; -use primitives_commitment::{piece::PaddedPieceSize, Commitment, CommitmentKind, NODE_SIZE}; +use primitives_commitment::{piece::PaddedPieceSize, CommP, Commitment, NODE_SIZE}; use storage_proofs_core::merkle::BinaryMerkleTree; use thiserror::Error; @@ -15,7 +15,7 @@ use thiserror::Error; pub fn calculate_piece_commitment( source: R, piece_size: PaddedPieceSize, -) -> Result { +) -> Result, CommPError> { // This reader adds two zero bits to each 254 bits of data read from the source. let mut fr32_reader = Fr32Reader::new(source); @@ -35,14 +35,12 @@ pub fn calculate_piece_commitment( .map_err(|err| CommPError::TreeBuild(err.to_string()))?; // Read and return the root of the tree - let mut commitment = [0; NODE_SIZE]; + let mut raw = [0; NODE_SIZE]; tree.root() - .write_bytes(&mut commitment) + .write_bytes(&mut raw) .expect("destination buffer large enough"); - let commitment = Commitment::new(commitment, CommitmentKind::Piece); - - Ok(commitment) + Ok(raw.into()) } #[derive(Debug, Error)] @@ -93,7 +91,6 @@ mod tests { let commitment = calculate_piece_commitment(zero_padding_reader, padded_piece_size).unwrap(); - dbg!(commitment.raw()); assert_eq!( commitment.raw(), diff --git a/cli/polka-storage-provider/server/src/pipeline/mod.rs b/cli/polka-storage-provider/server/src/pipeline/mod.rs index 35881612..aa652e6c 100644 --- a/cli/polka-storage-provider/server/src/pipeline/mod.rs +++ b/cli/polka-storage-provider/server/src/pipeline/mod.rs @@ -7,7 +7,7 @@ use polka_storage_proofs::porep::{ PoRepError, PoRepParameters, }; use polka_storage_provider_common::rpc::ServerInfo; -use primitives_commitment::Commitment; +use primitives_commitment::{CommD, CommP, CommR, Commitment}; use primitives_proofs::{ derive_prover_id, randomness::{draw_randomness, DomainSeparationTag}, @@ -115,15 +115,15 @@ impl PipelineOperations for TaskTracker { deal, published_deal_id, piece_path, - piece_cid, + commitment, } = msg; self.spawn(async move { tokio::select! { // AddPiece is cancellation safe, as it can be retried and the state will be fine. - res = add_piece(state, piece_path, piece_cid, deal, published_deal_id) => { + res = add_piece(state, piece_path, commitment, deal, published_deal_id) => { match res { - Ok(_) => tracing::info!("Add Piece for piece {:?}, deal id {}, finished successfully.", piece_cid, published_deal_id), - Err(err) => tracing::error!(%err, "Add Piece for piece {:?}, deal id {}, failed!", piece_cid, published_deal_id), + Ok(_) => tracing::info!("Add Piece for piece {}, deal id {}, finished successfully.", commitment, published_deal_id), + Err(err) => tracing::error!(%err, "Add Piece for piece {}, deal id {}, failed!", commitment, published_deal_id), } }, () = token.cancelled() => { @@ -206,7 +206,7 @@ async fn find_sector_for_piece( async fn add_piece( state: Arc, piece_path: PathBuf, - piece_cid: Commitment, + commitment: Commitment, deal: DealProposal, deal_id: u64, ) -> Result<(), PipelineError> { @@ -223,7 +223,7 @@ async fn add_piece( .open(§or.unsealed_path)?; tracing::info!("Preparing piece..."); - let (padded_reader, piece_info) = prepare_piece(piece_path, piece_cid)?; + let (padded_reader, piece_info) = prepare_piece(piece_path, commitment)?; tracing::info!("Adding piece..."); let occupied_piece_space = sealer.add_piece( padded_reader, @@ -322,6 +322,9 @@ async fn precommit( let sealing_output = sealing_handle.await??; tracing::info!("Created sector's replica: {:?}", sealing_output); + let sealing_output_commr = Commitment::::from(sealing_output.comm_r); + let sealing_output_commd = Commitment::::from(sealing_output.comm_d); + tracing::debug!("Precommiting at block: {}", current_block); let result = state .xt_client @@ -338,16 +341,8 @@ async fn precommit( + SECTOR_EXPIRATION_MARGIN, sector_number: sector_number, seal_proof: state.server_info.seal_proof, - sealed_cid: primitives_commitment::Commitment::new( - sealing_output.comm_r, - primitives_commitment::CommitmentKind::Replica, - ) - .cid(), - unsealed_cid: primitives_commitment::Commitment::new( - sealing_output.comm_d, - primitives_commitment::CommitmentKind::Data, - ) - .cid(), + sealed_cid: sealing_output_commr.cid(), + unsealed_cid: sealing_output_commd.cid(), seal_randomness_height: current_block, }], true, @@ -367,8 +362,8 @@ async fn precommit( let sector = PreCommittedSector::create( sector, sealed_path, - Commitment::replica(sealing_output.comm_r), - Commitment::data(sealing_output.comm_d), + sealing_output_commr, + sealing_output_commd, current_block, precommited_sectors[0].block, ) @@ -452,8 +447,7 @@ async fn prove_commit( let cache_dir = state.sealing_cache_dir.clone(); let sealed_path = sector.sealed_path.clone(); let piece_infos = sector.piece_infos.clone(); - let comm_r = sector.comm_r.raw(); - let comm_d = sector.comm_d.raw(); + tokio::task::spawn_blocking(move || { sealer.prove_sector( porep_params.as_ref(), @@ -463,7 +457,10 @@ async fn prove_commit( sector_number, ticket, Some(seed), - PreCommitOutput { comm_r, comm_d }, + PreCommitOutput { + comm_r: sector.comm_r, + comm_d: sector.comm_d, + }, &piece_infos, ) }) diff --git a/cli/polka-storage-provider/server/src/pipeline/types.rs b/cli/polka-storage-provider/server/src/pipeline/types.rs index 34a5cc12..bbc08af7 100644 --- a/cli/polka-storage-provider/server/src/pipeline/types.rs +++ b/cli/polka-storage-provider/server/src/pipeline/types.rs @@ -1,6 +1,6 @@ use std::path::PathBuf; -use primitives_commitment::{piece::PieceInfo, Commitment}; +use primitives_commitment::{piece::PieceInfo, CommD, CommP, CommR, Commitment}; use primitives_proofs::{DealId, SectorNumber}; use serde::{Deserialize, Serialize}; use storagext::types::market::DealProposal; @@ -26,7 +26,7 @@ pub struct AddPieceMessage { /// Path where the deal data (.car archive) is stored pub piece_path: PathBuf, /// CommP of the .car archive stored at `piece_path` - pub piece_cid: Commitment, + pub commitment: Commitment, } /// Sector to be sealed and pre-commited to the chain @@ -95,11 +95,11 @@ pub struct PreCommittedSector { /// the file has contents which should not be touched and are used for later steps. pub sealed_path: std::path::PathBuf, - /// CID of the sealed sector. - pub comm_r: Commitment, + /// Sealed sector commitment. + pub comm_r: Commitment, - /// CID of the unsealed data of the sector. - pub comm_d: Commitment, + /// Data commitment of the sector. + pub comm_d: Commitment, /// Block at which randomness has been fetched to perform [`PipelineMessage::PreCommit`]. /// @@ -143,8 +143,8 @@ impl PreCommittedSector { pub async fn create( unsealed: UnsealedSector, sealed_path: std::path::PathBuf, - comm_r: Commitment, - comm_d: Commitment, + comm_r: Commitment, + comm_d: Commitment, seal_randomness_height: u64, precommit_block: u64, ) -> Result { @@ -181,11 +181,11 @@ pub struct ProvenSector { /// Path of an existing file where the sealed sector data is stored. pub sealed_path: std::path::PathBuf, - /// CID of the sealed sector. - pub comm_r: Commitment, + /// Sealed sector commitment. + pub comm_r: Commitment, - /// CID of the unsealed data of the sector. - pub comm_d: Commitment, + /// Data commitment of the sector. + pub comm_d: Commitment, } impl ProvenSector { diff --git a/cli/polka-storage-provider/server/src/rpc.rs b/cli/polka-storage-provider/server/src/rpc.rs index e358feea..ab968f4a 100644 --- a/cli/polka-storage-provider/server/src/rpc.rs +++ b/cli/polka-storage-provider/server/src/rpc.rs @@ -121,22 +121,19 @@ impl StorageProviderRpcServer for RpcServerState { // We always publish only 1 deal let deal_id = published_deals[0].deal_id; - let piece_cid = Commitment::new( - piece_cid.hash().digest().try_into().map_err(|e| { - RpcError::invalid_params( - e, - Some(serde_json::to_value(piece_cid).expect("cid to be serializable")), - ) - })?, - primitives_commitment::CommitmentKind::Piece, - ); + let commitment = Commitment::from_cid(&piece_cid).map_err(|e| { + RpcError::invalid_params( + e, + Some(serde_json::to_value(piece_cid).expect("cid to be serializable")), + ) + })?; self.pipeline_sender .send(PipelineMessage::AddPiece(AddPieceMessage { deal: deal_proposal, published_deal_id: deal_id, piece_path, - piece_cid, + commitment, })) .map_err(|e| RpcError::internal_error(e, None))?; diff --git a/lib/polka-storage-proofs/src/porep/sealer.rs b/lib/polka-storage-proofs/src/porep/sealer.rs index 29a04ba0..aee0d570 100644 --- a/lib/polka-storage-proofs/src/porep/sealer.rs +++ b/lib/polka-storage-proofs/src/porep/sealer.rs @@ -10,9 +10,9 @@ use filecoin_proofs::{ }; use primitives_commitment::{ piece::{PaddedPieceSize, PieceInfo}, - Commitment, + CommD, CommP, CommR, Commitment, }; -use primitives_proofs::{RawCommitment, RegisteredSealProof, SectorNumber}; +use primitives_proofs::{RegisteredSealProof, SectorNumber}; use storage_proofs_core::{compound_proof, compound_proof::CompoundProof}; use storage_proofs_porep::stacked::{self, StackedCompound, StackedDrg}; @@ -39,7 +39,7 @@ pub type SubstrateProof = crate::Proof; /// and then by wrapping the respective file reader with a [`ZeroPaddingReader`]. pub fn prepare_piece

( piece_path: P, - piece_comm_p: Commitment, + commitment: Commitment, ) -> Result<(ZeroPaddingReader, PieceInfo), std::io::Error> where P: AsRef, @@ -57,7 +57,7 @@ where let piece_padded_file = ZeroPaddingReader::new(piece_file, *piece_padded_unpadded_length); let piece_info = PieceInfo { - commitment: piece_comm_p, + commitment, size: padded_piece_size, }; @@ -120,9 +120,7 @@ impl Sealer { let sector_size: UnpaddedBytesAmount = self.porep_config.sector_size.into(); let padding_pieces = filler_pieces(sector_size - UnpaddedBytesAmount(sector_occupied_space)); - result_pieces.extend(padding_pieces.into_iter().map(|p| { - PieceInfo::from_filecoin_piece_info(p, primitives_commitment::CommitmentKind::Piece) - })); + result_pieces.extend(padding_pieces.into_iter().map(PieceInfo::from)); Ok(result_pieces) } @@ -231,7 +229,10 @@ impl Sealer { sealed_sector, )?; - Ok(PreCommitOutput { comm_r, comm_d }) + Ok(PreCommitOutput { + comm_r: Commitment::from(comm_r), + comm_d: Commitment::from(comm_d), + }) } /// Generates a zk-SNARK proof guaranteeing a sealed_sector at `replica_path` is being stored. @@ -267,8 +268,9 @@ impl Sealer { let piece_infos = piece_infos .into_iter() - .map(|p| (*p).into()) - .collect::>(); + .copied() + .map(filecoin_proofs::PieceInfo::from) + .collect::>(); let scp1: filecoin_proofs::SealCommitPhase1Output = filecoin_proofs::seal_commit_phase1_inner( @@ -279,10 +281,7 @@ impl Sealer { sector_id.into(), ticket, seed, - SealPreCommitOutput { - comm_d: pre_commit.comm_d, - comm_r: pre_commit.comm_r, - }, + pre_commit.into(), &piece_infos, false, )?; @@ -388,12 +387,21 @@ pub fn filler_pieces(remaining_space: UnpaddedBytesAmount) -> Vec] /// * -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy)] pub struct PreCommitOutput { /// Sealed Sector (Replica) Commitment, after padding and processing it. - pub comm_r: RawCommitment, + pub comm_r: Commitment, /// Data commitment, after padding, before processing it into a replica. - pub comm_d: RawCommitment, + pub comm_d: Commitment, +} + +impl From for filecoin_proofs::SealPreCommitOutput { + fn from(value: PreCommitOutput) -> Self { + Self { + comm_r: value.comm_r.raw(), + comm_d: value.comm_d.raw(), + } + } } #[cfg(test)] @@ -478,13 +486,7 @@ mod test { filecoin_proofs::generate_piece_commitment(Cursor::new(&mut piece_bytes), piece_size) .unwrap(); - ( - piece_bytes, - primitives_commitment::piece::PieceInfo::from_filecoin_piece_info( - piece_info, - primitives_commitment::CommitmentKind::Piece, - ), - ) + (piece_bytes, piece_info.into()) } /// Computes CommD from the raw data, not from the pieces. diff --git a/pallets/market/src/lib.rs b/pallets/market/src/lib.rs index d36225f7..9e586d2a 100644 --- a/pallets/market/src/lib.rs +++ b/pallets/market/src/lib.rs @@ -39,7 +39,7 @@ pub mod pallet { use primitives_commitment::{ commd::compute_unsealed_sector_commitment, piece::{PaddedPieceSize, PieceInfo}, - Commitment, CommitmentKind, + Commitment, }; use primitives_proofs::{ ActiveDeal, ActiveSector, DealId, Market, RegisteredSealProof, SectorDeal, SectorNumber, @@ -978,8 +978,8 @@ pub mod pallet { let pieces = proposals .map(|p| { let cid = p.cid()?; - let commitment = Commitment::from_cid(&cid, CommitmentKind::Piece) - .map_err(|err| ProposalError::CommD(err))?; + let commitment = + Commitment::from_cid(&cid).map_err(|err| ProposalError::CommD(err))?; let size = PaddedPieceSize::new(p.piece_size) .map_err(|err| ProposalError::InvalidPieceSize(err))?; diff --git a/pallets/market/src/test.rs b/pallets/market/src/test.rs index 35fb235a..822fd289 100644 --- a/pallets/market/src/test.rs +++ b/pallets/market/src/test.rs @@ -8,7 +8,7 @@ use frame_support::{ traits::Currency, BoundedVec, }; -use primitives_commitment::{Commitment, CommitmentKind}; +use primitives_commitment::{CommP, Commitment}; use primitives_proofs::{ ActiveDeal, ActiveSector, DealId, Market as MarketTrait, RegisteredSealProof, SectorDeal, MAX_DEALS_PER_SECTOR, @@ -1774,8 +1774,7 @@ pub struct DealProposalBuilder { impl> Default for DealProposalBuilder { fn default() -> Self { - let piece_commitment = - Commitment::new(*b"dummydummydummydummydummydummydu", CommitmentKind::Piece); + let piece_commitment = Commitment::::from(*b"dummydummydummydummydummydummydu"); Self { piece_cid: piece_commitment diff --git a/pallets/storage-provider/src/lib.rs b/pallets/storage-provider/src/lib.rs index 2bc4772a..08830986 100644 --- a/pallets/storage-provider/src/lib.rs +++ b/pallets/storage-provider/src/lib.rs @@ -55,7 +55,7 @@ pub mod pallet { pallet_prelude::{BlockNumberFor, *}, Config as SystemConfig, }; - use primitives_commitment::{Commitment, CommitmentKind}; + use primitives_commitment::{CommD, CommR, Commitment}; use primitives_proofs::{ derive_prover_id, randomness::{draw_randomness, DomainSeparationTag}, @@ -495,14 +495,14 @@ pub mod pallet { )?; // Validate the data commitment - let commd = Commitment::from_cid_bytes(§or.unsealed_cid[..], CommitmentKind::Data) + let commd = Commitment::::from_cid_bytes(§or.unsealed_cid[..]) .map_err(|err| { log::error!(target: LOG_TARGET, err:?; "pre_commit_sectors: invalid unsealed_cid"); Error::::InvalidCid })?; // Validate the replica commitment - let _ = Commitment::from_cid_bytes(§or.sealed_cid[..], CommitmentKind::Replica) + let _ = Commitment::::from_cid_bytes(§or.sealed_cid[..]) .map_err(|err| { log::error!(target: LOG_TARGET, err:?; "pre_commit_sectors: invalid sealed_cid"); Error::::InvalidCid @@ -1528,14 +1528,14 @@ pub mod pallet { } // Validate the data commitment - let commd = Commitment::from_cid_bytes(&precommit.info.unsealed_cid[..], CommitmentKind::Data) + let commd = Commitment::::from_cid_bytes(&precommit.info.unsealed_cid[..]) .map_err(|err| { log::error!(target: LOG_TARGET, err:?; "validate_seal_proof: invalid unsealed_cid {:?}", &precommit.info.unsealed_cid); Error::::InvalidCid })?; // Validate the replica commitment - let commr = Commitment::from_cid_bytes(&precommit.info.sealed_cid[..], CommitmentKind::Replica) + let commr = Commitment::::from_cid_bytes(&precommit.info.sealed_cid[..]) .map_err(|err| { log::error!(target: LOG_TARGET, err:?; "validate_seal_proof: invalid sealed_cid {:?}", &precommit.info.sealed_cid); Error::::InvalidCid diff --git a/pallets/storage-provider/src/tests/mod.rs b/pallets/storage-provider/src/tests/mod.rs index 24571a07..98739487 100644 --- a/pallets/storage-provider/src/tests/mod.rs +++ b/pallets/storage-provider/src/tests/mod.rs @@ -14,7 +14,7 @@ use frame_support::{ }; use frame_system::pallet_prelude::BlockNumberFor; use pallet_market::{BalanceOf, ClientDealProposal, DealProposal, DealState}; -use primitives_commitment::{Commitment, CommitmentKind}; +use primitives_commitment::{CommP, Commitment}; use primitives_proofs::{ DealId, ProofVerification, PublicReplicaInfo, Randomness, RegisteredPoStProof, RegisteredSealProof, SectorNumber, CID_SIZE_IN_BYTES, MAX_DEALS_PER_SECTOR, @@ -421,8 +421,7 @@ struct DealProposalBuilder { impl Default for DealProposalBuilder { fn default() -> Self { - let piece_commitment = - Commitment::new(*b"dummydummydummydummydummydummydu", CommitmentKind::Piece); + let piece_commitment = Commitment::::from(*b"dummydummydummydummydummydummydu"); Self { piece_cid: piece_commitment diff --git a/primitives/commitment/Cargo.toml b/primitives/commitment/Cargo.toml index c4fcd1ae..71135821 100644 --- a/primitives/commitment/Cargo.toml +++ b/primitives/commitment/Cargo.toml @@ -13,6 +13,7 @@ std = ["dep:filecoin-proofs"] [dependencies] primitives-proofs.workspace = true +sealed.workspace = true cid.workspace = true sha2.workspace = true diff --git a/primitives/commitment/src/commd.rs b/primitives/commitment/src/commd.rs index fd9dbac8..410dfe48 100644 --- a/primitives/commitment/src/commd.rs +++ b/primitives/commitment/src/commd.rs @@ -7,7 +7,7 @@ use sha2::{Digest, Sha256}; use crate::{ piece::{PaddedPieceSize, PieceInfo, UnpaddedPieceSize}, - zero_piece_commitment, Commitment, CommitmentKind, NODE_SIZE, + CommD, CommP, Commitment, NODE_SIZE, }; // Ensure that the pieces are correct sizes @@ -38,13 +38,14 @@ fn ensure_piece_sizes( pub fn compute_unsealed_sector_commitment( sector_size: SectorSize, piece_infos: &[PieceInfo], -) -> Result { +) -> Result, CommDError> { let padded_sector_size = PaddedPieceSize::new(sector_size.bytes()).unwrap(); // In case of no pieces, return the piece zero commitment for the whole // sector size. if piece_infos.is_empty() { - return Ok(zero_piece_commitment(padded_sector_size)); + let piece_commitment = Commitment::::zero(padded_sector_size); + return Ok(Commitment::from(piece_commitment.raw())); } // Check if pieces are correct sizes. @@ -164,7 +165,7 @@ impl CommDPieceReduction { /// Finish the reduction of all pieces. Result is a data commitment for the /// pieces added. - fn finish(mut self, sector_size: SectorSize) -> Option { + fn finish(mut self, sector_size: SectorSize) -> Option> { // Add padding pieces to the end until the sector size is reached and we // have only a single piece left on the stack. loop { @@ -179,14 +180,14 @@ impl CommDPieceReduction { // Finally a single piece with the commitment that represents all // reduced pieces - Some(self.pieces.pop()?.commitment) + Some(Commitment::from(self.pieces.pop()?.commitment.raw())) } } /// Create a piece of specific size used as a padding. fn padding_piece(piece_size: PaddedPieceSize) -> PieceInfo { PieceInfo { - commitment: zero_piece_commitment(piece_size), + commitment: Commitment::::zero(piece_size), size: piece_size, } } @@ -206,7 +207,7 @@ fn join_piece_infos(left: PieceInfo, right: PieceInfo) -> Result::from(comm), size, }) } @@ -321,7 +322,7 @@ mod tests { .map(|(cid, size)| { let size = PaddedPieceSize::new(size).unwrap(); let cid = Cid::from_str(cid).unwrap(); - let commitment = Commitment::from_cid(&cid, CommitmentKind::Piece).unwrap(); + let commitment = Commitment::from_cid(&cid).unwrap(); PieceInfo { commitment, size } }) diff --git a/primitives/commitment/src/lib.rs b/primitives/commitment/src/lib.rs index 2b0ee0b0..b1c3959a 100644 --- a/primitives/commitment/src/lib.rs +++ b/primitives/commitment/src/lib.rs @@ -4,8 +4,11 @@ pub mod commd; pub mod piece; mod zero; +use core::{fmt::Display, marker::PhantomData}; + use cid::{multihash::Multihash, Cid}; use primitives_proofs::RegisteredSealProof; +use sealed::sealed; use crate::piece::PaddedPieceSize; @@ -34,144 +37,176 @@ pub const SHA2_256_TRUNC254_PADDED: u64 = 0x1012; /// https://github.com/multiformats/multicodec/blob/badcfe56bb7e0bbb06b60d57565186cd6be1f932/table.csv#L537 pub const POSEIDON_BLS12_381_A1_FC1: u64 = 0xb401; -#[cfg_attr(feature = "serde", derive(::serde::Deserialize, ::serde::Serialize))] -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum CommitmentKind { - // CommP - Piece commitment - Piece, - // CommD - Data commitment - Data, - // CommR - Replica commitment - Replica, +#[sealed] +pub trait CommitmentKind { + /// Returns the [Multicodec](https://github.com/multiformats/multicodec/blob/master/table.csv) code for the commitment kind. + fn multicodec() -> u64; + /// Returns the [Multihash](https://github.com/multiformats/multicodec/blob/master/table.csv) code for the commitment kind. + fn multihash() -> u64; } -impl CommitmentKind { - /// Returns the [Multicodec](https://github.com/multiformats/multicodec/blob/master/table.csv) code for the commitment kind. - fn multicodec(&self) -> u64 { - match self { - CommitmentKind::Piece | CommitmentKind::Data => FIL_COMMITMENT_UNSEALED, - CommitmentKind::Replica => FIL_COMMITMENT_SEALED, - } +/// Data commitment +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct CommD; + +#[sealed] +impl CommitmentKind for CommD { + fn multicodec() -> u64 { + FIL_COMMITMENT_UNSEALED } - /// Returns the [Multihash](https://github.com/multiformats/multicodec/blob/master/table.csv) code for the commitment kind. - fn multihash(&self) -> u64 { - match self { - CommitmentKind::Piece | CommitmentKind::Data => SHA2_256_TRUNC254_PADDED, - CommitmentKind::Replica => POSEIDON_BLS12_381_A1_FC1, - } + fn multihash() -> u64 { + SHA2_256_TRUNC254_PADDED } } -#[cfg_attr(feature = "serde", derive(::serde::Deserialize, ::serde::Serialize))] +/// Piece commitment #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct Commitment { - commitment: [u8; 32], - kind: CommitmentKind, -} +pub struct CommP; -impl Commitment { - pub fn new(commitment: [u8; 32], kind: CommitmentKind) -> Self { - Self { commitment, kind } +#[sealed] +impl CommitmentKind for CommP { + fn multicodec() -> u64 { + FIL_COMMITMENT_UNSEALED } - /// Create a new piece commitment. - pub fn piece(commitment: [u8; 32]) -> Self { - Self::new(commitment, CommitmentKind::Piece) + fn multihash() -> u64 { + SHA2_256_TRUNC254_PADDED } +} - /// Create a new data commitment. - pub fn data(commitment: [u8; 32]) -> Self { - Self::new(commitment, CommitmentKind::Data) +/// Replica commitment +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct CommR; + +#[sealed] +impl CommitmentKind for CommR { + fn multicodec() -> u64 { + FIL_COMMITMENT_SEALED } - /// Create a new replica commitment. - pub fn replica(commitment: [u8; 32]) -> Self { - Self::new(commitment, CommitmentKind::Replica) + fn multihash() -> u64 { + POSEIDON_BLS12_381_A1_FC1 } +} - /// Creates a new `Commitment` from bytes of a valid CID. - /// Returns an error if the bytes passed do not represent a valid commitment. - pub fn from_cid_bytes(bytes: &[u8], kind: CommitmentKind) -> Result { +#[cfg_attr(feature = "serde", derive(::serde::Deserialize, ::serde::Serialize))] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Commitment +where + Kind: CommitmentKind, +{ + raw: [u8; 32], + kind: PhantomData, +} + +impl Commitment +where + Kind: CommitmentKind, +{ + /// Creates a new `Commitment` from bytes of a valid CID. Returns an error + /// if the bytes passed do not represent a valid commitment. + pub fn from_cid_bytes(bytes: &[u8]) -> Result { let cid = Cid::try_from(bytes).map_err(|_| "bytes not a valid cid")?; - Self::from_cid(&cid, kind) + Self::from_cid(&cid) } /// Creates a new `Commitment` from a CID. Returns an error if the CID /// passed does not represent a commitment kind. - pub fn from_cid(cid: &Cid, kind: CommitmentKind) -> Result { - let mut commitment = [0; 32]; - commitment.copy_from_slice(cid.hash().digest()); - - let multicodec = cid.codec(); - let multihash = cid.hash().code(); - - match kind { - CommitmentKind::Piece | CommitmentKind::Data => { - if multicodec != FIL_COMMITMENT_UNSEALED { - return Err("invalid multicodec for piece/data commitment"); - } - - if multihash != SHA2_256_TRUNC254_PADDED { - return Err("invalid multihash for piece/data commitment"); - } - } - CommitmentKind::Replica => { - if multicodec != FIL_COMMITMENT_SEALED { - return Err("invalid multicodec for replica commitment"); - } - - if multihash != POSEIDON_BLS12_381_A1_FC1 { - return Err("invalid multihash for replica commitment"); - } - } + pub fn from_cid(cid: &Cid) -> Result { + let mut raw = [0; 32]; + raw.copy_from_slice(cid.hash().digest()); + + // Check multicodec of the cid + if cid.codec() != Kind::multicodec() { + return Err("invalid multicodec for commitment"); + } + + // Check multihash of the cid + if cid.hash().code() != Kind::multihash() { + return Err("invalid multihash for commitment"); } - Ok(Self { commitment, kind }) + Ok(Self { + raw, + kind: PhantomData, + }) } /// Returns the raw commitment bytes. pub fn raw(&self) -> [u8; 32] { - self.commitment + self.raw } /// Converts the commitment to a CID. pub fn cid(&self) -> Cid { - let multihash = self.kind.multihash(); - let multicodec = self.kind.multicodec(); - let hash = Multihash::wrap(multihash, &self.commitment) + let multihash = Kind::multihash(); + let multicodec = Kind::multicodec(); + let hash = Multihash::wrap(multihash, &self.raw) .expect("multihash is large enough so it can wrap the commitment"); Cid::new_v1(multicodec, hash) } } -/// Returns a zero-piece commitment for a given piece size. -pub fn zero_piece_commitment(size: PaddedPieceSize) -> Commitment { - Commitment { - commitment: zero::zero_piece_commitment(size), - kind: CommitmentKind::Piece, +impl Display for Commitment +where + Kind: CommitmentKind, +{ + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{}", self.cid()) + } +} + +impl TryFrom for Commitment +where + Kind: CommitmentKind, +{ + type Error = &'static str; + + fn try_from(value: Cid) -> Result { + Self::from_cid(&value) + } +} + +impl From<[u8; 32]> for Commitment +where + Kind: CommitmentKind, +{ + fn from(value: [u8; 32]) -> Self { + Self { + raw: value, + kind: PhantomData, + } + } +} + +impl Commitment { + /// Returns a zero piece commitment for a given piece size. + pub fn zero(size: PaddedPieceSize) -> Self { + Commitment::from(zero::zero_piece_commitment(size)) } } -/// Return a zero data commitment for specific seal proof. -pub fn zero_data_commitment(seal_proof: RegisteredSealProof) -> Commitment { - let size = seal_proof.sector_size().bytes(); - let size = PaddedPieceSize::new(size).expect("sector size is a valid padded size"); +impl Commitment { + /// Return a zero data commitment for specific seal proof. + pub fn zero(seal_proof: RegisteredSealProof) -> Self { + let size = seal_proof.sector_size().bytes(); + let size = PaddedPieceSize::new(size).expect("sector size is a valid padded size"); - Commitment { - // Zero data commitment is the same as zero piece commitment of the same - // size. - commitment: zero::zero_piece_commitment(size), - kind: CommitmentKind::Data, + // Zero data commitment is the same as zero piece commitment of the + // same size. + Commitment::from(zero::zero_piece_commitment(size)) } } #[cfg(test)] mod tests { + use core::marker::PhantomData; + use cid::{multihash::Multihash, Cid}; use crate::{ - Commitment, CommitmentKind, FIL_COMMITMENT_SEALED, FIL_COMMITMENT_UNSEALED, + CommD, CommP, CommR, Commitment, FIL_COMMITMENT_SEALED, FIL_COMMITMENT_UNSEALED, POSEIDON_BLS12_381_A1_FC1, SHA2_256_TRUNC254_PADDED, }; @@ -181,40 +216,40 @@ mod tests { #[test] fn comm_d_to_cid() { - let comm = rand_comm(); + let raw = rand_comm(); - let cid = Commitment::new(comm, CommitmentKind::Data).cid(); + let cid = Commitment::::from(raw).cid(); assert_eq!(cid.codec(), FIL_COMMITMENT_UNSEALED); assert_eq!(cid.hash().code(), SHA2_256_TRUNC254_PADDED); - assert_eq!(cid.hash().digest(), comm); + assert_eq!(cid.hash().digest(), raw); } #[test] fn cid_to_comm_d() { - let comm = rand_comm(); + let raw = rand_comm(); // Correct hash format - let mh = Multihash::wrap(SHA2_256_TRUNC254_PADDED, &comm).unwrap(); + let mh = Multihash::wrap(SHA2_256_TRUNC254_PADDED, &raw).unwrap(); let c = Cid::new_v1(FIL_COMMITMENT_UNSEALED, mh); - let commitment = Commitment::from_cid(&c, CommitmentKind::Data).unwrap(); - assert_eq!(commitment.raw(), comm); + let commitment = Commitment::::from_cid(&c).unwrap(); + assert_eq!(commitment.raw(), raw); // Should fail with incorrect codec let c = Cid::new_v1(FIL_COMMITMENT_SEALED, mh); - let commitment = Commitment::from_cid(&c, CommitmentKind::Data); + let commitment = Commitment::::from_cid(&c); assert!(commitment.is_err()); // Incorrect hash format - let mh = Multihash::wrap(0x9999, &comm).unwrap(); + let mh = Multihash::wrap(0x9999, &raw).unwrap(); let c = Cid::new_v1(FIL_COMMITMENT_UNSEALED, mh); - let commitment = Commitment::from_cid(&c, CommitmentKind::Data); + let commitment = Commitment::::from_cid(&c); assert!(commitment.is_err()); } #[test] fn comm_r_to_cid() { let comm = rand_comm(); - let cid = Commitment::new(comm, CommitmentKind::Replica).cid(); + let cid = Commitment::::from(comm).cid(); assert_eq!(cid.codec(), FIL_COMMITMENT_SEALED); assert_eq!(cid.hash().code(), POSEIDON_BLS12_381_A1_FC1); @@ -228,52 +263,52 @@ mod tests { // Correct hash format let mh = Multihash::wrap(POSEIDON_BLS12_381_A1_FC1, &comm).unwrap(); let c = Cid::new_v1(FIL_COMMITMENT_SEALED, mh); - let commitment = Commitment::from_cid(&c, CommitmentKind::Replica).unwrap(); + let commitment = Commitment::::from_cid(&c).unwrap(); assert_eq!(commitment.raw(), comm); // Should fail with incorrect codec let c = Cid::new_v1(FIL_COMMITMENT_UNSEALED, mh); - let commitment = Commitment::from_cid(&c, CommitmentKind::Replica); + let commitment = Commitment::::from_cid(&c); assert!(commitment.is_err()); // Incorrect hash format let mh = Multihash::wrap(0x9999, &comm).unwrap(); let c = Cid::new_v1(FIL_COMMITMENT_SEALED, mh); - let commitment = Commitment::from_cid(&c, CommitmentKind::Replica); + let commitment = Commitment::::from_cid(&c); assert!(commitment.is_err()); } #[test] fn symmetric_conversion() { - let comm = rand_comm(); + let raw = rand_comm(); // piece - let cid = Commitment::new(comm, CommitmentKind::Piece).cid(); + let cid = Commitment::::from(raw).cid(); assert_eq!( - Commitment::from_cid(&cid, CommitmentKind::Piece).unwrap(), - Commitment { - commitment: comm, - kind: CommitmentKind::Piece + Commitment::::from_cid(&cid).unwrap(), + Commitment:: { + raw, + kind: PhantomData } ); // data - let cid = Commitment::new(comm, CommitmentKind::Data).cid(); + let cid = Commitment::::from(raw).cid(); assert_eq!( - Commitment::from_cid(&cid, CommitmentKind::Data).unwrap(), - Commitment { - commitment: comm, - kind: CommitmentKind::Data + Commitment::::from_cid(&cid).unwrap(), + Commitment:: { + raw, + kind: PhantomData } ); // replica - let cid = Commitment::new(comm, CommitmentKind::Replica).cid(); + let cid = Commitment::::from(raw).cid(); assert_eq!( - Commitment::from_cid(&cid, CommitmentKind::Replica).unwrap(), - Commitment { - commitment: comm, - kind: CommitmentKind::Replica + Commitment::::from_cid(&cid).unwrap(), + Commitment:: { + raw, + kind: PhantomData } ); } diff --git a/primitives/commitment/src/piece.rs b/primitives/commitment/src/piece.rs index 287b3cea..36ee02ce 100644 --- a/primitives/commitment/src/piece.rs +++ b/primitives/commitment/src/piece.rs @@ -1,41 +1,33 @@ use core::ops::{Add, AddAssign, Deref}; -use crate::{Commitment, NODE_SIZE}; +use crate::{CommP, Commitment, NODE_SIZE}; /// Piece info contains piece commitment and piece size. #[cfg_attr(feature = "serde", derive(::serde::Deserialize, ::serde::Serialize))] #[derive(PartialEq, Debug, Eq, Clone, Copy)] pub struct PieceInfo { /// Piece commitment - pub commitment: Commitment, + pub commitment: Commitment, /// Piece size pub size: PaddedPieceSize, } #[cfg(feature = "std")] -impl PieceInfo { - /// Convert a [`filecoin_proofs::PieceInfo`] into a [`PieceInfo`]. - /// - /// With some generics trickery we could move the CommitmentKind to a compile-time thing - /// and further get more safety out of the Commitment type; additionally, this method - /// could be turned into a `from`. - pub fn from_filecoin_piece_info( - piece_info: filecoin_proofs::PieceInfo, - kind: crate::CommitmentKind, - ) -> Self { +impl From for PieceInfo { + fn from(piece_info: filecoin_proofs::PieceInfo) -> Self { Self { - commitment: Commitment::new(piece_info.commitment, kind), + commitment: Commitment::::from(piece_info.commitment), size: PaddedPieceSize::from_arbitrary_size(piece_info.size.0), } } } #[cfg(feature = "std")] -impl Into for PieceInfo { - fn into(self) -> filecoin_proofs::PieceInfo { +impl From for filecoin_proofs::PieceInfo { + fn from(value: PieceInfo) -> Self { filecoin_proofs::PieceInfo { - commitment: self.commitment.commitment, - size: filecoin_proofs::UnpaddedBytesAmount(self.size.unpadded().0), + commitment: value.commitment.raw, + size: filecoin_proofs::UnpaddedBytesAmount(value.size.unpadded().0), } } }