From a0b3b33c47d68e32ce828ea7ddc977b0d99ba836 Mon Sep 17 00:00:00 2001 From: hanabi1224 Date: Tue, 19 Mar 2024 14:58:03 +0800 Subject: [PATCH] feat(rpc): implement Filecoin.StateMarketStorageDeal --- src/rpc/mod.rs | 1 + src/rpc/state_api.rs | 52 +++++++++++++--- src/rpc_api/data_types.rs | 105 ++++++++++++++++++++++++++++++++ src/rpc_api/mod.rs | 2 + src/rpc_client/state_ops.rs | 9 ++- src/tool/subcommands/api_cmd.rs | 31 +++++++++- 6 files changed, 189 insertions(+), 11 deletions(-) diff --git a/src/rpc/mod.rs b/src/rpc/mod.rs index f17eb10230dd..bd6faa809fa8 100644 --- a/src/rpc/mod.rs +++ b/src/rpc/mod.rs @@ -289,6 +289,7 @@ where STATE_VM_CIRCULATING_SUPPLY_INTERNAL, state_vm_circulating_supply_internal::, )?; + module.register_async_method(STATE_MARKET_STORAGE_DEAL, state_market_storage_deal::)?; module.register_async_method(MSIG_GET_AVAILABLE_BALANCE, msig_get_available_balance::)?; module.register_async_method(MSIG_GET_PENDING, msig_get_pending::)?; // Gas API diff --git a/src/rpc/state_api.rs b/src/rpc/state_api.rs index 9fc4f049aaec..267290da9091 100644 --- a/src/rpc/state_api.rs +++ b/src/rpc/state_api.rs @@ -7,13 +7,9 @@ use crate::cid_collections::CidHashSet; use crate::libp2p::NetworkMessage; use crate::lotus_json::LotusJson; use crate::rpc::error::JsonRpcError; -use crate::rpc_api::data_types::{ - ApiActorState, ApiDeadline, ApiInvocResult, ApiTipsetKey, CirculatingSupply, Data, MarketDeal, - MessageFilter, MessageLookup, MinerSectors, MiningBaseInfo, RPCState, SectorOnChainInfo, - Transaction, -}; +use crate::rpc_api::data_types::*; use crate::shim::{ - address::Address, clock::ChainEpoch, econ::TokenAmount, executor::Receipt, + address::Address, clock::ChainEpoch, deal::DealID, econ::TokenAmount, executor::Receipt, state_tree::ActorState, version::NetworkVersion, }; use crate::state_manager::chain_rand::ChainRand; @@ -24,6 +20,7 @@ use ahash::{HashMap, HashMapExt}; use anyhow::Context as _; use anyhow::Result; use cid::Cid; +use fil_actor_interface::market::{DealProposal, DealProposals, DealState}; use fil_actor_interface::miner::DeadlineInfo; use fil_actor_interface::{ market, miner, @@ -1009,7 +1006,7 @@ pub async fn state_list_miners( let actor = data .state_manager .get_actor(&Address::POWER_ACTOR, *ts.parent_state())? - .context("Power actor not found".to_string())?; + .context("Power actor not found")?; let state = power::State::load(store, actor.code, actor.state)?; let miners = state @@ -1020,3 +1017,44 @@ pub async fn state_list_miners( Ok(LotusJson(miners)) } + +pub async fn state_market_storage_deal( + params: Params<'_>, + data: Data>, +) -> Result { + let LotusJson((deal_id, ApiTipsetKey(tsk))): LotusJson<(DealID, ApiTipsetKey)> = + params.parse()?; + + let ts = data + .state_manager + .chain_store() + .load_required_tipset_or_heaviest(&tsk)?; + let store = data.state_manager.blockstore(); + let actor = data + .state_manager + .get_actor(&Address::MARKET_ACTOR, *ts.parent_state())? + .context("Market actor not found")?; + let market_state = market::State::load(store, actor.code, actor.state)?; + let proposals = market_state.proposals(store)?; + let proposal: DealProposal = match proposals { + DealProposals::V9(deal_array) => deal_array.get(deal_id)?.map(TryFrom::try_from), + DealProposals::V10(deal_array) => deal_array.get(deal_id)?.map(TryFrom::try_from), + DealProposals::V11(deal_array) => deal_array.get(deal_id)?.map(TryFrom::try_from), + DealProposals::V12(deal_array) => deal_array.get(deal_id)?.map(TryFrom::try_from), + DealProposals::V13(deal_array) => deal_array.get(deal_id)?.map(TryFrom::try_from), + } + .ok_or_else(|| anyhow::anyhow!("deal {deal_id} not found - deal may not have completed sealing before deal proposal start epoch, or deal may have been slashed"))??; + + let states = market_state.states(store)?; + + const EMPTY_DEAL_STATE: DealState = DealState { + sector_start_epoch: -1, + last_updated_epoch: -1, + slash_epoch: -1, + verified_claim: 0, + }; + + let state = states.get(deal_id)?.unwrap_or(EMPTY_DEAL_STATE); + + Ok(MarketDeal { proposal, state }.into()) +} diff --git a/src/rpc_api/data_types.rs b/src/rpc_api/data_types.rs index 9b6d9d03f995..4075bdc0a701 100644 --- a/src/rpc_api/data_types.rs +++ b/src/rpc_api/data_types.rs @@ -31,6 +31,7 @@ use crate::state_manager::StateManager; use ahash::HashSet; use chrono::Utc; use cid::Cid; +use fil_actor_interface::market::AllocationID; use fil_actor_interface::miner::MinerInfo; use fil_actor_interface::{ market::{DealProposal, DealState}, @@ -103,6 +104,110 @@ pub struct MessageSendSpec { lotus_json_with_self!(MessageSendSpec); +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "PascalCase")] +pub struct ApiDealState { + pub sector_start_epoch: ChainEpoch, + pub last_updated_epoch: ChainEpoch, + pub slash_epoch: ChainEpoch, + #[serde(skip)] + pub verified_claim: AllocationID, +} + +lotus_json_with_self!(ApiDealState); + +impl From for ApiDealState { + fn from(s: DealState) -> Self { + let DealState { + sector_start_epoch, + last_updated_epoch, + slash_epoch, + verified_claim, + } = s; + Self { + sector_start_epoch, + last_updated_epoch, + slash_epoch, + verified_claim, + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "PascalCase")] +pub struct ApiDealProposal { + #[serde(rename = "PieceCID", with = "crate::lotus_json")] + pub piece_cid: Cid, + pub piece_size: u64, + pub verified_deal: bool, + #[serde(with = "crate::lotus_json")] + pub client: Address, + #[serde(with = "crate::lotus_json")] + pub provider: Address, + pub label: String, + pub start_epoch: ChainEpoch, + pub end_epoch: ChainEpoch, + #[serde(with = "crate::lotus_json")] + pub storage_price_per_epoch: TokenAmount, + #[serde(with = "crate::lotus_json")] + pub provider_collateral: TokenAmount, + #[serde(with = "crate::lotus_json")] + pub client_collateral: TokenAmount, +} + +lotus_json_with_self!(ApiDealProposal); + +impl From for ApiDealProposal { + fn from(p: DealProposal) -> Self { + let DealProposal { + piece_cid, + piece_size, + verified_deal, + client, + provider, + label, + start_epoch, + end_epoch, + storage_price_per_epoch, + provider_collateral, + client_collateral, + } = p; + Self { + piece_cid, + piece_size: piece_size.0, + verified_deal, + client: client.into(), + provider: provider.into(), + label, + start_epoch, + end_epoch, + storage_price_per_epoch: storage_price_per_epoch.into(), + provider_collateral: provider_collateral.into(), + client_collateral: client_collateral.into(), + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "PascalCase")] +pub struct ApiMarketDeal { + #[serde(with = "crate::lotus_json")] + pub proposal: ApiDealProposal, + #[serde(with = "crate::lotus_json")] + pub state: ApiDealState, +} + +lotus_json_with_self!(ApiMarketDeal); + +impl From for ApiMarketDeal { + fn from(d: MarketDeal) -> Self { + Self { + proposal: d.proposal.into(), + state: d.state.into(), + } + } +} + #[derive(Serialize)] #[serde(rename_all = "PascalCase")] pub struct MarketDeal { diff --git a/src/rpc_api/mod.rs b/src/rpc_api/mod.rs index b8111843646d..ba7de7a8f7c9 100644 --- a/src/rpc_api/mod.rs +++ b/src/rpc_api/mod.rs @@ -115,6 +115,7 @@ pub static ACCESS_MAP: Lazy> = Lazy::new(|| { access.insert(state_api::STATE_LIST_MINERS, Access::Read); access.insert(state_api::STATE_MINER_SECTOR_COUNT, Access::Read); access.insert(state_api::STATE_VERIFIED_CLIENT_STATUS, Access::Read); + access.insert(state_api::STATE_MARKET_STORAGE_DEAL, Access::Read); access.insert( state_api::STATE_VM_CIRCULATING_SUPPLY_INTERNAL, Access::Read, @@ -400,6 +401,7 @@ pub mod state_api { pub const STATE_VERIFIED_CLIENT_STATUS: &str = "Filecoin.StateVerifiedClientStatus"; pub const STATE_VM_CIRCULATING_SUPPLY_INTERNAL: &str = "Filecoin.StateVMCirculatingSupplyInternal"; + pub const STATE_MARKET_STORAGE_DEAL: &str = "Filecoin.StateMarketStorageDeal"; pub const MSIG_GET_AVAILABLE_BALANCE: &str = "Filecoin.MsigGetAvailableBalance"; pub const MSIG_GET_PENDING: &str = "Filecoin.MsigGetPending"; } diff --git a/src/rpc_client/state_ops.rs b/src/rpc_client/state_ops.rs index 663c670ab8dd..2d304afca6e7 100644 --- a/src/rpc_client/state_ops.rs +++ b/src/rpc_client/state_ops.rs @@ -8,7 +8,7 @@ use crate::{ blocks::TipsetKey, rpc_api::{data_types::*, state_api::*}, shim::{ - address::Address, clock::ChainEpoch, econ::TokenAmount, message::Message, + address::Address, clock::ChainEpoch, deal::DealID, econ::TokenAmount, message::Message, message::MethodNum, state_tree::ActorState, version::NetworkVersion, }, }; @@ -222,6 +222,13 @@ impl ApiInfo { RpcRequest::new(STATE_LIST_MESSAGES, (from_to, tsk, max_height)) } + pub fn state_market_storage_deal_req( + deal_id: DealID, + tsk: ApiTipsetKey, + ) -> RpcRequest { + RpcRequest::new(STATE_MARKET_STORAGE_DEAL, (deal_id, tsk)) + } + pub fn msig_get_available_balance_req( addr: Address, tsk: ApiTipsetKey, diff --git a/src/tool/subcommands/api_cmd.rs b/src/tool/subcommands/api_cmd.rs index 4574f9f9fbb6..8382c8971f27 100644 --- a/src/tool/subcommands/api_cmd.rs +++ b/src/tool/subcommands/api_cmd.rs @@ -25,12 +25,14 @@ use crate::rpc_api::{data_types::RPCState, eth_api::*}; use crate::rpc_client::{ApiInfo, JsonRpcError, RpcRequest, DEFAULT_PORT}; use crate::shim::address::{Address, Protocol}; use crate::shim::crypto::Signature; +use crate::shim::state_tree::StateTree; use crate::state_manager::StateManager; use crate::utils::version::FOREST_VERSION_STRING; use crate::Client; use ahash::HashMap; use anyhow::Context as _; use clap::{Subcommand, ValueEnum}; +use fil_actor_interface::market; use fil_actors_shared::v10::runtime::DomainSeparationTag; use futures::stream::FuturesUnordered; use futures::StreamExt; @@ -551,7 +553,7 @@ fn eth_tests_with_tipset(shared_tipset: &Tipset) -> Vec { // Extract tests that use chain-specific data such as block CIDs or message // CIDs. Right now, only the last `n_tipsets` tipsets are used. -fn snapshot_tests(store: &ManyCar, n_tipsets: usize) -> anyhow::Result> { +fn snapshot_tests(store: Arc, n_tipsets: usize) -> anyhow::Result> { let mut tests = vec![]; let shared_tipset = store.heaviest_tipset()?; let root_tsk = shared_tipset.key(); @@ -764,6 +766,29 @@ fn snapshot_tests(store: &ManyCar, n_tipsets: usize) -> anyhow::Result