Skip to content

Commit

Permalink
feat(pallet-market): sketch on_sectors_terminate
Browse files Browse the repository at this point in the history
  • Loading branch information
jmg-duarte committed Jun 28, 2024
1 parent a268f11 commit 3708065
Showing 1 changed file with 119 additions and 2 deletions.
121 changes: 119 additions & 2 deletions pallets/market/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ mod test;
// TODO(@th7nder,#77,14/06/2024): take the pallet out of dev mode
#[frame_support::pallet(dev_mode)]
pub mod pallet {
pub const CID_CODEC: u64 = 0x55;
pub const LOG_TARGET: &'static str = "runtime::market";

use cid::Cid;
use codec::{Decode, Encode};
Expand Down Expand Up @@ -48,6 +46,9 @@ pub mod pallet {
use sp_arithmetic::traits::BaseArithmetic;
use sp_std::vec::Vec;

pub const CID_CODEC: u64 = 0x55;
pub const LOG_TARGET: &'static str = "runtime::market";

/// Allows to extract Balance of an account via the Config::Currency associated type.
/// BalanceOf is a sophisticated way of getting an u128.
pub type BalanceOf<T> =
Expand Down Expand Up @@ -296,6 +297,37 @@ pub mod pallet {
pub type PendingProposals<T: Config> =
StorageValue<_, BoundedBTreeSet<T::Hash, T::MaxDeals>, ValueQuery>;

/// Holds a mapping from [`SectorId`] to [`SectorState`] and all the sector deals.
#[pallet::storage]
pub type SectorsState<T: Config> =
StorageMap<_, _, SectorId, (SectorState, BoundedVec<DealId, ConstU32<128>>)>; // TODO: const 128 and get a proper value for it

/// Size of a CID with a 512-bit multihash — i.e. the default CID size.
const CID_SIZE_IN_BYTES: u32 = 64;

/// The CID (in bytes) of a given sector.
pub type SectorId = BoundedVec<u8, ConstU32<CID_SIZE_IN_BYTES>>;

/// The possible states a sector may be in.
///
/// Source: <https://spec.filecoin.io/#section-systems.filecoin_mining.sector.lifecycle>
#[derive(RuntimeDebug, Encode, Decode, TypeInfo, MaxEncodedLen, Eq, PartialEq)]
pub enum SectorState {
/// Storage provider sealed the sector.
Precommited,
/// Storage provider generated a seal proof.
Commited,
/// Storage provider generates valid PoSt proofs and timely submits them.
Active,
/// Storage provider fails to generate a proof.
Faulty,
/// Storage provider declared a faulty sector.
Recovering,
/// Sector has expired, storage provider terminated the sector early or
/// failed to provide proofs for 42 consecutive rounds.
Terminated,
}

#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
Expand Down Expand Up @@ -330,6 +362,12 @@ pub mod pallet {
},
/// Deal was slashed.
DealSlashed(DealId),
// Deal has been terminated.
DealTerminated {
deal_id: DealId,
client: T::AccountId,
provider: T::AccountId,
},
}

/// Utility type to ensure that the bound for deal settlement is in sync.
Expand Down Expand Up @@ -678,6 +716,85 @@ pub mod pallet {

Ok(())
}

// NOTE(@jmg-duarte,26/06/2024): this should probably be an hook

/// Source: <https://github.com/filecoin-project/builtin-actors/blob/54236ae89880bf4aa89b0dba6d9060c3fd2aacee/actors/market/src/lib.rs#L786-L876>
pub fn on_sectors_terminate(
origin: OriginFor<T>,
sector_ids: BoundedVec<SectorId, ConstU32<128>>, // TODO: const the 128 AND figure out a better value
) -> DispatchResult {
let storage_provider = ensure_signed(origin)?;

let mut slash_amount = 0;
let mut terminated_deals: BoundedVec<DealId, ConstU32<128>> = bounded_vec![];
for sector_id in sector_ids {
let Some((sector_state, deal_ids)) = SectorsState::<T>::delete(sector_id) else {
continue;
};

for deal_id in deal_ids {
let SectorState::Terminated = sector_state else {
todo!("error: sector is not terminated");
};

let deal_proposal =
Proposals::<T>::get(deal_id).ok_or(|| todo!("error: deal not found"))?;
if storage_provider != deal_proposal.provider {
todo!("error: caller is not the deal provider");
}

let current_block = <frame_system::Pallet<T>>::block_number();
if deal_proposal.end_block <= current_block {
// not slashing finished deals
continue;
}

let DealState::Active(mut active_deal_state) = deal_proposal.state else {
todo!(
"error: if the deal is not active, the sector shouldn't be terminated"
);
};

// https://github.com/filecoin-project/builtin-actors/blob/54236ae89880bf4aa89b0dba6d9060c3fd2aacee/actors/market/src/lib.rs#L840-L844
if let Some(slash_block) = active_deal_state.slash_block {
log::warn!("deal {} was already slashed, terminating anyway", deal_id);
}

// https://github.com/filecoin-project/builtin-actors/blob/54236ae89880bf4aa89b0dba6d9060c3fd2aacee/actors/market/src/lib.rs#L846-L850
if let None = active_deal_state.last_updated_block {
let _ = PendingProposals::<T>::delete(deal_id);
}

// Handle payments
// https://github.com/filecoin-project/builtin-actors/blob/54236ae89880bf4aa89b0dba6d9060c3fd2aacee/actors/market/src/state.rs#L922-L962

// active_deal_state.slash_block = Some(current_block);
let payment_start_block =
if let Some(last_updated_block) = active_deal_state.last_updated_block {
core::cmp::max(deal_proposal.start_block, last_updated_block)
} else {
deal_proposal.start_block
};
// The only reason we can do this is because of the line
// https://github.com/filecoin-project/builtin-actors/blob/54236ae89880bf4aa89b0dba6d9060c3fd2aacee/actors/market/src/lib.rs#L852
let payment_end_block = core::cmp::min(deal_proposal.end_block, current_block);
let n_blocks_elapsed = max(0, payment_end_block - payment_start_block);
let total_payment = deal_proposal.storage_price_per_block * n_blocks_elapsed;

// TODO: pay the provider (function is in another PR)

let _ = Proposals::<T>::remove(deal_id);

Self::deposit_event(Event::<T>::DealTerminated {
deal_id,
client: deal.client.clone(),
provider: deal.provider.clone(),
});
}
}
Ok(())
}
}

/// Functions exposed by the pallet
Expand Down

0 comments on commit 3708065

Please sign in to comment.