diff --git a/CHANGELOG.md b/CHANGELOG.md index d6676e7d675..293c929e118 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,8 @@ `Filecoin.StateGetClaim` RPC method. - [#4356](https://github.com/ChainSafe/forest/pull/4356) Add support for the `Filecoin.NetProtectAdd` RPC method. +- [#4382](https://github.com/ChainSafe/forest/pull/4382) Add support for the + `Filecoin.StateGetAllocation` RPC method. ### Changed diff --git a/src/lotus_json/allocation.rs b/src/lotus_json/allocation.rs new file mode 100644 index 00000000000..4351df883e7 --- /dev/null +++ b/src/lotus_json/allocation.rs @@ -0,0 +1,88 @@ +// Copyright 2019-2024 ChainSafe Systems +// SPDX-License-Identifier: Apache-2.0, MIT + +use super::*; +use ::cid::Cid; +use fil_actor_interface::verifreg::Allocation; +use fvm_shared4::clock::ChainEpoch; +use fvm_shared4::piece::PaddedPieceSize; +use fvm_shared4::ActorID; + +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "PascalCase")] +#[schemars(rename = "Allocation")] +pub struct AllocationLotusJson { + // The verified client which allocated the DataCap. + pub client: ActorID, + // The provider (miner actor) which may claim the allocation. + pub provider: ActorID, + // Identifier of the data to be committed. + #[schemars(with = "LotusJson")] + #[serde(with = "crate::lotus_json")] + pub data: Cid, + // The (padded) size of data. + #[schemars(with = "u64")] + pub size: PaddedPieceSize, + // The minimum duration which the provider must commit to storing the piece to avoid + // early-termination penalties (epochs). + pub term_min: ChainEpoch, + // The maximum period for which a provider can earn quality-adjusted power + // for the piece (epochs). + pub term_max: ChainEpoch, + // The latest epoch by which a provider must commit data before the allocation expires. + pub expiration: ChainEpoch, +} + +impl HasLotusJson for Allocation { + type LotusJson = AllocationLotusJson; + #[cfg(test)] + fn snapshots() -> Vec<(serde_json::Value, Self)> { + vec![( + json! {{ + "TermMin": 0, + "TermMax": 0, + "Provider": 0, + "Client": 0, + "Expiration": 0, + "Size": 0, + "Data": {"/":"baeaaaaa"}, + }}, + Allocation { + term_min: 0, + term_max: 0, + provider: 0, + client: 0, + expiration: 0, + size: PaddedPieceSize(0), + data: Cid::default(), + }, + )] + } + fn into_lotus_json(self) -> Self::LotusJson { + AllocationLotusJson { + client: self.client, + provider: self.provider, + data: self.data, + size: self.size, + term_min: self.term_min, + term_max: self.term_max, + expiration: self.expiration, + } + } + fn from_lotus_json(lotus_json: Self::LotusJson) -> Self { + Allocation { + client: lotus_json.client, + provider: lotus_json.provider, + data: lotus_json.data, + size: lotus_json.size, + term_min: lotus_json.term_min, + term_max: lotus_json.term_max, + expiration: lotus_json.expiration, + } + } +} + +#[test] +fn snapshots() { + assert_all_snapshots::(); +} diff --git a/src/lotus_json/mod.rs b/src/lotus_json/mod.rs index 4566227fe26..7dce1a880d5 100644 --- a/src/lotus_json/mod.rs +++ b/src/lotus_json/mod.rs @@ -123,7 +123,7 @@ use derive_more::From; use fil_actor_interface::miner::DeadlineInfo; -use fvm_shared2::piece::PaddedPieceSize; +use fvm_shared4::piece::PaddedPieceSize; use schemars::{gen::SchemaGenerator, schema::Schema, JsonSchema}; use serde::{de::DeserializeOwned, Deserialize, Deserializer, Serialize, Serializer}; #[cfg(test)] @@ -216,6 +216,7 @@ decl_and_test!( // If a module cannot be tested normally above, you MAY declare it separately here // but you MUST document any tech debt - the reason WHY it cannot be tested above. +mod allocation; mod beneficiary_term; // fil_actor_miner_state::v12::BeneficiaryTerm: !quickcheck::Arbitrary mod bit_field; // fil_actors_shared::fvm_ipld_bitfield::BitField: !quickcheck::Arbitrary mod cid; // can't make snapshots of generic type diff --git a/src/rpc/methods/state.rs b/src/rpc/methods/state.rs index 6fb650a85c6..f92eb17e197 100644 --- a/src/rpc/methods/state.rs +++ b/src/rpc/methods/state.rs @@ -38,7 +38,7 @@ use anyhow::Context as _; use anyhow::Result; use cid::Cid; use fil_actor_interface::market::DealState; -use fil_actor_interface::verifreg::Claim; +use fil_actor_interface::verifreg::{Allocation, Claim}; use fil_actor_interface::{ market, miner, miner::{MinerInfo, MinerPower}, @@ -1879,6 +1879,28 @@ impl RpcMethod<3> for StateGetClaim { } } +pub enum StateGetAllocation {} + +impl RpcMethod<3> for StateGetAllocation { + const NAME: &'static str = "Filecoin.StateGetAllocation"; + const PARAM_NAMES: [&'static str; 3] = ["address", "allocation_id", "tipset_key"]; + const API_VERSION: ApiVersion = ApiVersion::V1; + const PERMISSION: Permission = Permission::Read; + + type Params = (Address, ClaimID, ApiTipsetKey); + type Ok = Option; + + async fn handle( + ctx: Ctx, + (address, allocation_id, ApiTipsetKey(tsk)): Self::Params, + ) -> Result { + let ts = ctx.chain_store.load_required_tipset_or_heaviest(&tsk)?; + Ok(ctx + .state_manager + .get_allocation(&address, &ts, allocation_id)?) + } +} + pub enum StateGetNetworkParams {} impl RpcMethod<0> for StateGetNetworkParams { diff --git a/src/rpc/mod.rs b/src/rpc/mod.rs index c81f453cba4..b572382f9a9 100644 --- a/src/rpc/mod.rs +++ b/src/rpc/mod.rs @@ -157,6 +157,7 @@ macro_rules! for_each_method { $callback!(crate::rpc::state::StateMinerPreCommitDepositForPower); $callback!(crate::rpc::state::StateVerifierStatus); $callback!(crate::rpc::state::StateGetClaim); + $callback!(crate::rpc::state::StateGetAllocation); $callback!(crate::rpc::state::StateSectorExpiration); // sync vertical diff --git a/src/state_manager/mod.rs b/src/state_manager/mod.rs index 3b979ac172a..ed65d95bf29 100644 --- a/src/state_manager/mod.rs +++ b/src/state_manager/mod.rs @@ -48,10 +48,10 @@ use cid::Cid; pub use circulating_supply::GenesisInfo; use fil_actor_interface::init::{self, State}; use fil_actor_interface::miner::{MinerInfo, MinerPower, Partition}; -use fil_actor_interface::verifreg::Claim; +use fil_actor_interface::verifreg::{Allocation, Claim}; use fil_actor_interface::*; use fil_actor_verifreg_state::v12::DataCap; -use fil_actor_verifreg_state::v13::ClaimID; +use fil_actor_verifreg_state::v13::{AllocationID, ClaimID}; use fil_actors_shared::fvm_ipld_amt::Amtv0 as Amt; use fil_actors_shared::fvm_ipld_bitfield::BitField; use fil_actors_shared::v12::runtime::DomainSeparationTag; @@ -1332,6 +1332,17 @@ where state.get_claim(self.blockstore(), id_address.into(), claim_id) } + pub fn get_allocation( + self: &Arc, + addr: &Address, + ts: &Arc, + allocation_id: AllocationID, + ) -> anyhow::Result> { + let id_address = self.lookup_required_id(addr, ts)?; + let state = self.get_verified_registry_actor_state(ts)?; + state.get_allocation(self.blockstore(), id_address.id()?, allocation_id) + } + pub fn verified_client_status( self: &Arc, addr: &Address, diff --git a/src/tool/subcommands/api_cmd.rs b/src/tool/subcommands/api_cmd.rs index dd26f938fda..3c2053f51e2 100644 --- a/src/tool/subcommands/api_cmd.rs +++ b/src/tool/subcommands/api_cmd.rs @@ -846,6 +846,13 @@ fn state_tests_with_tipset( 0, tipset.key().into(), ))?), + // NOTE: Once StateGetAllocations is implemented we need to retrieve a valid + // allocation_id and use that for testing. + RpcTest::identity(StateGetAllocation::request(( + block.miner_address, + 0, + tipset.key().into(), + ))?), ]); for sector in StateSectorGetInfo::get_sectors(store, &block.miner_address, tipset)?