Skip to content

Commit

Permalink
fix(storage-provider-server): add deal validations
Browse files Browse the repository at this point in the history
  • Loading branch information
jmg-duarte committed Nov 26, 2024
1 parent 76f79ce commit feab741
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 16 deletions.
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,13 @@ target/

# code coverage
coverage/

# PoRep Params
*.porep.params
*.porep.vk
*.porep.vk.scale

# PoSt Params
*.post.params
*.post.vk
*.post.vk.scale
25 changes: 18 additions & 7 deletions cli/polka-storage-provider/server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use primitives_proofs::{RegisteredPoStProof, RegisteredSealProof};
use rand::Rng;
use storagext::{
multipair::{DebugPair, MultiPairSigner},
StorageProviderClientExt,
MarketClientExt, StorageProviderClientExt,
};
use subxt::{
ext::sp_core::{
Expand Down Expand Up @@ -113,6 +113,9 @@ pub enum ServerError {
#[error("storage provider is not registered")]
UnregisteredStorageProvider,

#[error("storage provider does not have a market account")]
NoMarketAccountStorageProvider,

#[error("registered proof does not match the configuration")]
ProofMismatch,

Expand Down Expand Up @@ -437,16 +440,24 @@ impl ServerConfiguration {
) -> Result<storagext::Client, ServerError> {
let xt_client = storagext::Client::new(rpc_address, RETRY_NUMBER, RETRY_INTERVAL).await?;

let storage_provider_account_id = subxt::utils::AccountId32(
// account_id() -> sp_core::crypto::AccountId
// as_ref() -> &[u8]
// * -> [u8]
*xt_keypair.account_id().as_ref(),
);

// Check if the storage provider has been registered to the chain
let storage_provider_info = xt_client
.retrieve_storage_provider(&subxt::utils::AccountId32(
// account_id() -> sp_core::crypto::AccountId
// as_ref() -> &[u8]
// * -> [u8]
*xt_keypair.account_id().as_ref(),
))
.retrieve_storage_provider(&storage_provider_account_id)
.await?;

// Check if the account exists on the market
xt_client
.retrieve_balance(storage_provider_account_id)
.await?
.ok_or(ServerError::NoMarketAccountStorageProvider)?;

match storage_provider_info {
Some(storage_provider_info) => {
if &storage_provider_info.info.window_post_proof_type != post_proof {
Expand Down
73 changes: 64 additions & 9 deletions cli/polka-storage-provider/server/src/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ use jsonrpsee::server::Server;
use polka_storage_provider_common::rpc::{
CidString, RpcError, ServerInfo, StorageProviderRpcServer,
};
use primitives_commitment::Commitment;
use primitives_commitment::{CommP, Commitment, CommitmentKind};
use storagext::{
types::market::{ClientDealProposal as SxtClientDealProposal, DealProposal as SxtDealProposal},
MarketClientExt,
};
use subxt::tx::Signer;
use tokio::sync::mpsc::UnboundedSender;
use tokio_util::sync::CancellationToken;
use tower_http::cors::{Any, CorsLayer};
Expand Down Expand Up @@ -42,21 +43,75 @@ impl StorageProviderRpcServer for RpcServerState {
}

async fn propose_deal(&self, deal: SxtDealProposal) -> Result<CidString, RpcError> {
// TODO(@jmg-duarte,26/11/2024): proper unit or e2e testing of these validations

if deal.piece_size > self.server_info.post_proof.sector_size().bytes() {
// once again, the rpc error is wrong, we'll need to fix that
return Err(RpcError::invalid_params(
"Piece size cannot be larger than the registered sector size",
None,
));
}

// TODO(@jmg-duarte,25/11/2024): Add sanity validations
// end_block > start_block
// available balance (sp & client)
// the provider matches us
// storage price per block > 0
// piece size <= 2048 && power of two
// check the piece_cid code
if deal.start_block > deal.end_block {
return Err(RpcError::invalid_params(
"start_block cannot be after end_block",
None,
));
}

if deal.provider != self.xt_keypair.account_id() {
return Err(RpcError::invalid_params(
"deal's provider ID does not match the current provider ID",
None,
));
}

if deal.piece_cid.codec() != CommP::multicodec() {
return Err(RpcError::invalid_params(
"piece_cid is not a piece commitment",
None,
));
}

if !deal.piece_size.is_power_of_two() {
return Err(RpcError::invalid_params(
"invalid piece_size, must be a power of two",
None,
));
}

if deal.storage_price_per_block <= 0 {
return Err(RpcError::invalid_params(
"storage_price_per_block must be superior to 0",
None,
));
}

let storage_provider_balance = self
.xt_client
.retrieve_balance(self.xt_keypair.account_id())
.await?
.ok_or_else(|| RpcError::internal_error("Storage Provider not found", None))?;

if storage_provider_balance.free < deal.provider_collateral {
return Err(RpcError::invalid_params(
"storage provider balance is lower than the deal's collateral",
None,
));
}

let client_balance = self
.xt_client
.retrieve_balance(deal.client.clone())
.await?
.ok_or_else(|| RpcError::internal_error("Client not found", None))?;

if client_balance.free < deal.cost() {
return Err(RpcError::invalid_params(
"client's balance is lower than the deal's cost",
None,
));
}

let cid = self
.deal_db
Expand Down
10 changes: 10 additions & 0 deletions storagext/lib/src/types/market.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,16 @@ pub struct DealProposal {
pub state: RuntimeDealState<BlockNumber>,
}

impl DealProposal {
/// Calculates the deal cost for the client —
/// `(end_block - start_block) * storage_price_per_block`.
pub fn cost(&self) -> Currency {
let deal_duration = self.end_block - self.start_block;
// Cast is save because it's to a bigger int size
self.storage_price_per_block * (deal_duration as u128)
}
}

impl From<DealProposal>
for RuntimeDealProposal<subxt::ext::subxt_core::utils::AccountId32, Currency, BlockNumber>
{
Expand Down

0 comments on commit feab741

Please sign in to comment.