Skip to content

Commit

Permalink
feat(market-pallet): verify_deaals_for_activation
Browse files Browse the repository at this point in the history
  • Loading branch information
th7nder committed Jun 21, 2024
1 parent 3ef637d commit 6eef4b0
Show file tree
Hide file tree
Showing 2 changed files with 197 additions and 4 deletions.
2 changes: 1 addition & 1 deletion pallets/market/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ targets = ["x86_64-unknown-linux-gnu"]
cid = { workspace = true, default-features = false, features = ["scale-codec"] }
codec = { workspace = true, default-features = false, features = ["derive"] }
log = { workspace = true }
multihash-codetable = { workspace = true, features = ["blake2b"] }
scale-info = { workspace = true, default-features = false, features = ["derive"] }

# frame deps
Expand All @@ -31,7 +32,6 @@ sp-std = { workspace = true, default-features = false }
[dev-dependencies]
blake2b_simd = { workspace = true }
env_logger = { workspace = true }
multihash-codetable = { workspace = true, features = ["blake2b"] }
pallet-balances = { workspace = true, default-features = false }
sp-core = { workspace = true, default-features = false }
sp-io = { workspace = true }
Expand Down
199 changes: 196 additions & 3 deletions pallets/market/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,10 @@ pub mod pallet {
ExistenceRequirement::{AllowDeath, KeepAlive},
ReservableCurrency,
},
PalletId,
PalletError, PalletId,
};
use frame_system::{pallet_prelude::*, Config as SystemConfig};
use frame_system::{pallet_prelude::*, Config as SystemConfig, Pallet as System};
use multihash_codetable::{Code, MultihashDigest};
use scale_info::TypeInfo;
use sp_arithmetic::traits::BaseArithmetic;
use sp_std::vec::Vec;
Expand Down Expand Up @@ -126,7 +127,7 @@ pub mod pallet {
/// Reference: <https://github.com/filecoin-project/builtin-actors/blob/17ede2b256bc819dc309edf38e031e246a516486/actors/market/src/deal.rs#L138>
pub struct ActiveDealState<BlockNumber> {
/// Sector in which given piece has been included
sector_number: u128,
sector_number: SectorNumber,

/// At which block (time) the deal's sector has been activated.
sector_start_block: BlockNumber,
Expand Down Expand Up @@ -201,6 +202,9 @@ pub mod pallet {
}
}

type DealProposalOf<T> =
DealProposal<<T as frame_system::Config>::AccountId, BalanceOf<T>, BlockNumberFor<T>>;

/// After Storage Client has successfully negotiated with the Storage Provider, they prepare a DealProposal,
/// sign it with their signature and send to the Storage Provider.
/// Storage Provider only after successful file transfer and verification of the data, calls an extrinsic `market.publish_storage_deals`.
Expand All @@ -212,6 +216,47 @@ pub mod pallet {
pub client_signature: OffchainSignature,
}

// TODO(@th7nder,20/06/2024): this DOES NOT belong here. it should be somewhere else.
#[allow(non_camel_case_types)]
#[derive(Debug, Decode, Encode, TypeInfo, Eq, PartialEq, Clone)]
pub enum RegisteredSealProof {
StackedDRG2KiBV1P1,
}

impl RegisteredSealProof {
pub fn sector_size(&self) -> SectorSize {
SectorSize::_2KiB
}
}

/// SectorSize indicates one of a set of possible sizes in the network.
#[derive(Encode, Decode, TypeInfo, Clone, Debug, PartialEq, Eq, Copy)]
pub enum SectorSize {
_2KiB,
}

impl SectorSize {
/// <https://github.com/filecoin-project/ref-fvm/blob/5659196fa94accdf1e7f10e00586a8166c44a60d/shared/src/sector/mod.rs#L40>
pub fn bytes(&self) -> u64 {
match self {
SectorSize::_2KiB => 2 << 10,
}
}
}

// TODO(@th7nder,20/06/2024): this DOES not belong here. it should be somewhere else.
pub type SectorNumber = u64;

#[derive(Debug, Decode, Encode, TypeInfo, Eq, PartialEq, Clone)]
pub struct SectorDeal<BlockNumber> {
pub sector_number: SectorNumber,
pub sector_expiry: BlockNumber,
pub sector_type: RegisteredSealProof,
pub deal_ids: BoundedVec<DealId, ConstU32<128>>,
}
// verify_deals_for_activation is called by Storage Provider Pallllllet!
// it's not an extrinsic then?

#[pallet::pallet]
pub struct Pallet<T>(_);

Expand Down Expand Up @@ -266,6 +311,18 @@ pub mod pallet {
AllProposalsInvalid,
/// `publish_storage_deals`'s core logic was invoked with a broken invariant that should be called by `validate_deals`.
UnexpectedValidationError,
DuplicateDeal(DealId),
DealPreconditionFailed(DealId),
DealNotFound(DealId),
DealActivationError(DealId, DealActivationError),
DealsTooLargeToFitIntoSector(SectorNumber),
}

#[derive(RuntimeDebug, Encode, Decode, TypeInfo, PalletError)]
pub enum DealActivationError {
InvalidProvider,
StartBlockElapsed,
SectorExpiresBeforeDeal,
}

// NOTE(@th7nder,18/06/2024):
Expand Down Expand Up @@ -456,6 +513,142 @@ pub mod pallet {
Ok(())
}

/// Exposed to the Storage Provider Pallet
/// They call it when doing `precommit`.
/// Verify that a given set of storage deals is valid for a sector currently being PreCommitted
/// and return UnsealedCID for the set of deals.
// TODO(@th7nder,20/06/2024): what's a good number for BoundedVec `sector_deals`?
pub fn verify_deals_for_activation(
storage_provider: &T::AccountId,
sector_deals: BoundedVec<SectorDeal<BlockNumberFor<T>>, ConstU32<32>>,
) -> Result<BoundedVec<Option<Cid>, ConstU32<32>>, DispatchError> {
// TODO:
// - unit tests
// - trait in primitives
// - docs
let curr_block = System::<T>::block_number();
let mut unsealed_cids = BoundedVec::new();
for sector in sector_deals {
let proposals = Self::proposals_for_deals(sector.deal_ids)?;
let sector_size = sector.sector_type.sector_size();
Self::validate_deals_for_sector(
&proposals,
storage_provider,
sector.sector_number,
sector.sector_expiry,
curr_block,
sector_size,
)?;

// Sealing a Sector without Deals, Committed Capacity Only.
let commd = if proposals.is_empty() {
None
} else {
Some(Self::compute_commd(
proposals.iter().map(|(_, deal)| deal),
sector.sector_type,
)?)
};

// PRE-COND: can't fail, unsealed_cids<_, X> == BoundedVec<_ X> == sector_deals<_, X>
unsealed_cids
.try_push(commd)
.map_err(|_| "programmer error, there should be space for Cids")?;
}

Ok(unsealed_cids)
}

/// <https://github.com/filecoin-project/builtin-actors/blob/17ede2b256bc819dc309edf38e031e246a516486/actors/market/src/lib.rs#L1370>
fn compute_commd<'a>(
_proposals: impl IntoIterator<Item = &'a DealProposalOf<T>>,
_sector_type: RegisteredSealProof,
) -> Result<Cid, DispatchError> {
// TODO(@th7nder,#92,21/06/2024):
// https://github.com/filecoin-project/rust-fil-proofs/blob/daec42b64ae6bf9a537545d5f116d57b9a29cc11/filecoin-proofs/src/pieces.rs#L85
let cid = Cid::new_v1(
CID_CODEC,
Code::Blake2b256.digest(b"placeholder-to-be-done"),
);

Ok(cid)
}

/// <https://github.com/filecoin-project/builtin-actors/blob/17ede2b256bc819dc309edf38e031e246a516486/actors/market/src/lib.rs#L1388>
fn validate_deals_for_sector(
deals: &BoundedVec<(DealId, DealProposalOf<T>), ConstU32<32>>,
provider: &T::AccountId,
sector_number: SectorNumber,
sector_expiry: BlockNumberFor<T>,
sector_activation: BlockNumberFor<T>,
sector_size: SectorSize,
) -> DispatchResult {
let mut total_deal_space = 0;
for (deal_id, deal) in deals {
Self::validate_deal_can_activate(deal, provider, sector_expiry, sector_activation)
.map_err(|e| Error::<T>::DealActivationError(*deal_id, e))?;
total_deal_space += deal.piece_size;
}

ensure!(
total_deal_space <= sector_size.bytes(),
Error::<T>::DealsTooLargeToFitIntoSector(sector_number)
);

Ok(())
}

/// <https://github.com/filecoin-project/builtin-actors/blob/17ede2b256bc819dc309edf38e031e246a516486/actors/market/src/lib.rs#L1570>
fn validate_deal_can_activate(
deal: &DealProposalOf<T>,
provider: &T::AccountId,
sector_expiry: BlockNumberFor<T>,
sector_activation: BlockNumberFor<T>,
) -> Result<(), DealActivationError> {
ensure!(
*provider == deal.provider,
DealActivationError::InvalidProvider
);
ensure!(
sector_activation <= deal.start_block,
DealActivationError::StartBlockElapsed
);
ensure!(
sector_expiry >= deal.end_block,
DealActivationError::SectorExpiresBeforeDeal
);

Ok(())
}

fn proposals_for_deals(
deal_ids: BoundedVec<DealId, ConstU32<128>>,
) -> Result<BoundedVec<(DealId, DealProposalOf<T>), ConstU32<32>>, DispatchError> {
let mut unique_deals: BoundedBTreeSet<DealId, ConstU32<32>> = BoundedBTreeSet::new();
let mut proposals = BoundedVec::new();
for deal_id in deal_ids {
ensure!(
!unique_deals.contains(&deal_id),
Error::<T>::DuplicateDeal(deal_id)
);

// PRE-COND: always succeeds, unique_deals has the same boundary as sector.deal_ids[]
unique_deals
.try_insert(deal_id)
.map_err(|deal_id| Error::<T>::DealPreconditionFailed(deal_id))?;

let proposal: DealProposalOf<T> = Proposals::<T>::try_get(&deal_id)
.map_err(|_| Error::<T>::DealNotFound(deal_id))?;

// PRE-COND: always succeeds, unique_deals has the same boundary as sector.deal_ids[]
proposals
.try_push((deal_id, proposal))
.map_err(|_| Error::<T>::DealPreconditionFailed(deal_id))?;
}

Ok(proposals)
}

fn generate_deal_id() -> DealId {
let ret = NextDealId::<T>::get();
let next = ret
Expand Down

0 comments on commit 6eef4b0

Please sign in to comment.