Skip to content

Commit

Permalink
Built-in market API for deal proposal metadata (#818)
Browse files Browse the repository at this point in the history
  • Loading branch information
anorth authored and arajasek committed Nov 17, 2022
1 parent 5d1ea32 commit 6caea40
Show file tree
Hide file tree
Showing 4 changed files with 307 additions and 0 deletions.
141 changes: 141 additions & 0 deletions actors/market/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,15 @@ pub enum Method {
AddBalanceExported = frc42_dispatch::method_hash!("AddBalance"),
WithdrawBalanceExported = frc42_dispatch::method_hash!("WithdrawBalance"),
GetBalanceExported = frc42_dispatch::method_hash!("GetBalance"),
GetDealDataCommitmentExported = frc42_dispatch::method_hash!("GetDealDataCommitment"),
GetDealClientExported = frc42_dispatch::method_hash!("GetDealClient"),
GetDealProviderExported = frc42_dispatch::method_hash!("GetDealProvider"),
GetDealLabelExported = frc42_dispatch::method_hash!("GetDealLabel"),
GetDealTermExported = frc42_dispatch::method_hash!("GetDealTerm"),
GetDealEpochPriceExported = frc42_dispatch::method_hash!("GetDealEpochPrice"),
GetDealClientCollateralExported = frc42_dispatch::method_hash!("GetDealClientCollateral"),
GetDealProviderCollateralExported = frc42_dispatch::method_hash!("GetDealProviderCollateral"),
GetDealVerifiedExported = frc42_dispatch::method_hash!("GetDealVerified"),
}

/// Market Actor
Expand Down Expand Up @@ -1049,6 +1058,101 @@ impl Actor {
}
Ok(())
}

/// Returns the data commitment and size of a deal proposal.
/// This will be available after the deal is published (whether or not is is activated)
/// and up until some undefined period after it is terminated.
fn get_deal_data_commitment(
rt: &mut impl Runtime,
params: GetDealDataCommitmentParams,
) -> Result<GetDealDataCommitmentReturn, ActorError> {
rt.validate_immediate_caller_accept_any()?;
let found = rt.state::<State>()?.get_proposal(rt.store(), params.id)?;
Ok(GetDealDataCommitmentReturn { data: found.piece_cid, size: found.piece_size })
}

/// Returns the client of a deal proposal.
fn get_deal_client(
rt: &mut impl Runtime,
params: GetDealClientParams,
) -> Result<GetDealClientReturn, ActorError> {
rt.validate_immediate_caller_accept_any()?;
let found = rt.state::<State>()?.get_proposal(rt.store(), params.id)?;
Ok(GetDealClientReturn { client: found.client.id().unwrap() })
}

/// Returns the provider of a deal proposal.
fn get_deal_provider(
rt: &mut impl Runtime,
params: GetDealProviderParams,
) -> Result<GetDealProviderReturn, ActorError> {
rt.validate_immediate_caller_accept_any()?;
let found = rt.state::<State>()?.get_proposal(rt.store(), params.id)?;
Ok(GetDealProviderReturn { provider: found.provider.id().unwrap() })
}

/// Returns the label of a deal proposal.
fn get_deal_label(
rt: &mut impl Runtime,
params: GetDealLabelParams,
) -> Result<GetDealLabelReturn, ActorError> {
rt.validate_immediate_caller_accept_any()?;
let found = rt.state::<State>()?.get_proposal(rt.store(), params.id)?;
Ok(GetDealLabelReturn { label: found.label })
}

/// Returns the start and end epochs of a deal proposal.
/// The deal term is a half-open range, exclusive of the end epoch.
fn get_deal_term(
rt: &mut impl Runtime,
params: GetDealTermParams,
) -> Result<GetDealTermReturn, ActorError> {
rt.validate_immediate_caller_accept_any()?;
let found = rt.state::<State>()?.get_proposal(rt.store(), params.id)?;
Ok(GetDealTermReturn { start: found.start_epoch, end: found.end_epoch })
}

/// Returns the per-epoch price of a deal proposal.
fn get_deal_epoch_price(
rt: &mut impl Runtime,
params: GetDealEpochPriceParams,
) -> Result<GetDealEpochPriceReturn, ActorError> {
rt.validate_immediate_caller_accept_any()?;
let found = rt.state::<State>()?.get_proposal(rt.store(), params.id)?;
Ok(GetDealEpochPriceReturn { price_per_epoch: found.storage_price_per_epoch })
}

/// Returns the client collateral requirement for a deal proposal.
fn get_deal_client_collateral(
rt: &mut impl Runtime,
params: GetDealClientCollateralParams,
) -> Result<GetDealClientCollateralReturn, ActorError> {
rt.validate_immediate_caller_accept_any()?;
let found = rt.state::<State>()?.get_proposal(rt.store(), params.id)?;
Ok(GetDealClientCollateralReturn { collateral: found.client_collateral })
}

/// Returns the provider collateral requirement for a deal proposal.
fn get_deal_provider_collateral(
rt: &mut impl Runtime,
params: GetDealProviderCollateralParams,
) -> Result<GetDealProviderCollateralReturn, ActorError> {
rt.validate_immediate_caller_accept_any()?;
let found = rt.state::<State>()?.get_proposal(rt.store(), params.id)?;
Ok(GetDealProviderCollateralReturn { collateral: found.provider_collateral })
}

/// Returns the verified flag for a deal proposal.
/// Note that the source of truth for verified allocations and claims is
/// the verified registry actor.
fn get_deal_verified(
rt: &mut impl Runtime,
params: GetDealVerifiedParams,
) -> Result<GetDealVerifiedReturn, ActorError> {
rt.validate_immediate_caller_accept_any()?;
let found = rt.state::<State>()?.get_proposal(rt.store(), params.id)?;
Ok(GetDealVerifiedReturn { verified: found.verified_deal })
}
}

fn compute_data_commitment<BS: Blockstore>(
Expand Down Expand Up @@ -1464,6 +1568,43 @@ impl ActorCode for Actor {
let res = Self::get_balance(rt, cbor::deserialize_params(params)?)?;
Ok(RawBytes::serialize(res)?)
}
Some(Method::GetDealDataCommitmentExported) => {
let res = Self::get_deal_data_commitment(rt, cbor::deserialize_params(params)?)?;
Ok(RawBytes::serialize(res)?)
}
Some(Method::GetDealClientExported) => {
let res = Self::get_deal_client(rt, cbor::deserialize_params(params)?)?;
Ok(RawBytes::serialize(res)?)
}
Some(Method::GetDealProviderExported) => {
let res = Self::get_deal_provider(rt, cbor::deserialize_params(params)?)?;
Ok(RawBytes::serialize(res)?)
}
Some(Method::GetDealLabelExported) => {
let res = Self::get_deal_label(rt, cbor::deserialize_params(params)?)?;
Ok(RawBytes::serialize(res)?)
}
Some(Method::GetDealTermExported) => {
let res = Self::get_deal_term(rt, cbor::deserialize_params(params)?)?;
Ok(RawBytes::serialize(res)?)
}
Some(Method::GetDealEpochPriceExported) => {
let res = Self::get_deal_epoch_price(rt, cbor::deserialize_params(params)?)?;
Ok(RawBytes::serialize(res)?)
}
Some(Method::GetDealClientCollateralExported) => {
let res = Self::get_deal_client_collateral(rt, cbor::deserialize_params(params)?)?;
Ok(RawBytes::serialize(res)?)
}
Some(Method::GetDealProviderCollateralExported) => {
let res =
Self::get_deal_provider_collateral(rt, cbor::deserialize_params(params)?)?;
Ok(RawBytes::serialize(res)?)
}
Some(Method::GetDealVerifiedExported) => {
let res = Self::get_deal_verified(rt, cbor::deserialize_params(params)?)?;
Ok(RawBytes::serialize(res)?)
}
None => Err(actor_error!(unhandled_message, "Invalid method")),
}
}
Expand Down
16 changes: 16 additions & 0 deletions actors/market/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,22 @@ impl State {
+ &self.total_client_storage_fee
}

pub fn get_proposal<BS: Blockstore>(
&self,
store: &BS,
id: DealID,
) -> Result<DealProposal, ActorError> {
let proposals = DealArray::load(&self.proposals, store)
.context_code(ExitCode::USR_ILLEGAL_STATE, "failed to load deal proposals")?;
let found = proposals
.get(id)
.with_context_code(ExitCode::USR_ILLEGAL_STATE, || {
format!("failed to load deal proposal {}", id)
})?
.with_context_code(ExitCode::USR_NOT_FOUND, || format!("no such deal {}", id))?;
Ok(found.clone())
}

pub(super) fn mutator<'bs, BS: Blockstore>(
&mut self,
store: &'bs BS,
Expand Down
69 changes: 69 additions & 0 deletions actors/market/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use fvm_shared::econ::TokenAmount;
use fvm_shared::piece::PaddedPieceSize;
use fvm_shared::ActorID;

use crate::Label;
use fvm_shared::sector::RegisteredSealProof;

use super::deal::{ClientDealProposal, DealProposal, DealState};
Expand Down Expand Up @@ -136,3 +137,71 @@ pub struct SectorDataSpec {
pub deal_ids: Vec<DealID>,
pub sector_type: RegisteredSealProof,
}

#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)]
#[serde(transparent)]
pub struct DealQueryParams {
pub id: DealID,
}

pub type GetDealDataCommitmentParams = DealQueryParams;
#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)]
pub struct GetDealDataCommitmentReturn {
pub data: Cid,
pub size: PaddedPieceSize,
}

pub type GetDealClientParams = DealQueryParams;
#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)]
#[serde(transparent)]
pub struct GetDealClientReturn {
pub client: ActorID,
}

pub type GetDealProviderParams = DealQueryParams;
#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)]
#[serde(transparent)]
pub struct GetDealProviderReturn {
pub provider: ActorID,
}

pub type GetDealLabelParams = DealQueryParams;
#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)]
#[serde(transparent)]
pub struct GetDealLabelReturn {
pub label: Label,
}

pub type GetDealTermParams = DealQueryParams;
#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)]
pub struct GetDealTermReturn {
pub start: ChainEpoch, // First epoch for the deal (inclusive)
pub end: ChainEpoch, // Epoch at which the deal expires (i.e. exclusive).
}

pub type GetDealEpochPriceParams = DealQueryParams;
#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)]
pub struct GetDealEpochPriceReturn {
pub price_per_epoch: TokenAmount,
}

pub type GetDealClientCollateralParams = DealQueryParams;
#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)]
#[serde(transparent)]
pub struct GetDealClientCollateralReturn {
pub collateral: TokenAmount,
}

pub type GetDealProviderCollateralParams = DealQueryParams;
#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)]
#[serde(transparent)]
pub struct GetDealProviderCollateralReturn {
pub collateral: TokenAmount,
}

pub type GetDealVerifiedParams = DealQueryParams;
#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)]
#[serde(transparent)]
pub struct GetDealVerifiedReturn {
pub verified: bool,
}
81 changes: 81 additions & 0 deletions actors/market/tests/deal_api_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
use fvm_ipld_encoding::RawBytes;
use fvm_shared::clock::ChainEpoch;
use serde::de::DeserializeOwned;

use fil_actor_market::{
Actor as MarketActor, DealQueryParams, GetDealClientCollateralReturn, GetDealClientReturn,
GetDealDataCommitmentReturn, GetDealEpochPriceReturn, GetDealLabelReturn,
GetDealProviderCollateralReturn, GetDealProviderReturn, GetDealTermReturn,
GetDealVerifiedReturn, Method,
};
use fil_actors_runtime::network::EPOCHS_IN_DAY;
use fil_actors_runtime::test_utils::{MockRuntime, ACCOUNT_ACTOR_CODE_ID};
use harness::*;

mod harness;

#[test]
fn proposal_data() {
let start_epoch = 1000;
let end_epoch = start_epoch + 200 * EPOCHS_IN_DAY;
let publish_epoch = ChainEpoch::from(1);

let mut rt = setup();
rt.set_epoch(publish_epoch);
let next_allocation_id = 1;

let proposal = generate_deal_and_add_funds(
&mut rt,
CLIENT_ADDR,
&MinerAddresses::default(),
start_epoch,
end_epoch,
);
rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, WORKER_ADDR);
let id =
publish_deals(&mut rt, &MinerAddresses::default(), &[proposal.clone()], next_allocation_id)
[0];

let data: GetDealDataCommitmentReturn =
query_deal(&mut rt, Method::GetDealDataCommitmentExported, id);
assert_eq!(proposal.piece_cid, data.data);
assert_eq!(proposal.piece_size, data.size);

let client: GetDealClientReturn = query_deal(&mut rt, Method::GetDealClientExported, id);
assert_eq!(proposal.client.id().unwrap(), client.client);

let provider: GetDealProviderReturn = query_deal(&mut rt, Method::GetDealProviderExported, id);
assert_eq!(proposal.provider.id().unwrap(), provider.provider);

let label: GetDealLabelReturn = query_deal(&mut rt, Method::GetDealLabelExported, id);
assert_eq!(proposal.label, label.label);

let term: GetDealTermReturn = query_deal(&mut rt, Method::GetDealTermExported, id);
assert_eq!(proposal.start_epoch, term.start);
assert_eq!(proposal.end_epoch, term.end);

let price: GetDealEpochPriceReturn = query_deal(&mut rt, Method::GetDealEpochPriceExported, id);
assert_eq!(proposal.storage_price_per_epoch, price.price_per_epoch);

let client_collateral: GetDealClientCollateralReturn =
query_deal(&mut rt, Method::GetDealClientCollateralExported, id);
assert_eq!(proposal.client_collateral, client_collateral.collateral);

let provider_collateral: GetDealProviderCollateralReturn =
query_deal(&mut rt, Method::GetDealProviderCollateralExported, id);
assert_eq!(proposal.provider_collateral, provider_collateral.collateral);

let verified: GetDealVerifiedReturn = query_deal(&mut rt, Method::GetDealVerifiedExported, id);
assert_eq!(proposal.verified_deal, verified.verified);

check_state(&rt);
}

fn query_deal<T: DeserializeOwned>(rt: &mut MockRuntime, method: Method, id: u64) -> T {
let params = DealQueryParams { id };
rt.expect_validate_caller_any();
rt.call::<MarketActor>(method as u64, &RawBytes::serialize(params).unwrap())
.unwrap()
.deserialize()
.unwrap()
}

0 comments on commit 6caea40

Please sign in to comment.