Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(storage-provider-server): add deal validations & market account check #614

Merged
merged 3 commits into from
Nov 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
29 changes: 22 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,28 @@ 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
// Once subxt breaks our code with https://github.com/paritytech/subxt/pull/1850
// we'll be able to make all this uniform
.retrieve_balance(subxt::ext::sp_runtime::AccountId32::new(
storage_provider_account_id.0,
))
.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 greater than 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