From 33fa947fcc328abc54c8e399bc7fb1be381655b3 Mon Sep 17 00:00:00 2001 From: Aayush Date: Mon, 7 Nov 2022 14:42:27 -0500 Subject: [PATCH] Miner actor: Add exported getters for info and monies --- actors/miner/src/lib.rs | 104 ++++++++++++ actors/miner/src/types.rs | 58 ++++++- actors/miner/tests/exported_getters.rs | 222 +++++++++++++++++++++++++ actors/miner/tests/util.rs | 1 + 4 files changed, 384 insertions(+), 1 deletion(-) create mode 100644 actors/miner/tests/exported_getters.rs diff --git a/actors/miner/src/lib.rs b/actors/miner/src/lib.rs index 9b88ed5824..4a595147cf 100644 --- a/actors/miner/src/lib.rs +++ b/actors/miner/src/lib.rs @@ -126,6 +126,14 @@ pub enum Method { // Method numbers derived from FRC-XXXX standards ChangeBenificiaryExported = frc42_dispatch::method_hash!("ChangeBeneficiary"), GetBeneficiaryExported = frc42_dispatch::method_hash!("GetBeneficiary"), + GetOwnerExported = frc42_dispatch::method_hash!("GetOwner"), + GetWorkerExported = frc42_dispatch::method_hash!("GetWorker"), + GetControlsExported = frc42_dispatch::method_hash!("GetControls"), + GetSectorSizeExported = frc42_dispatch::method_hash!("GetSectorSize"), + GetPreCommitDepositExported = frc42_dispatch::method_hash!("GetPreCommitDeposit"), + GetInitialPledgeExported = frc42_dispatch::method_hash!("GetInitialPledge"), + GetFeeDebtExported = frc42_dispatch::method_hash!("GetFeeDebt"), + GetLockedFundsExported = frc42_dispatch::method_hash!("GetLockedFunds"), } pub const ERR_BALANCE_INVARIANTS_BROKEN: ExitCode = ExitCode::new(1000); @@ -205,6 +213,7 @@ impl Actor { Ok(()) } + /// Returns the "controlling" addresses: the owner, the worker, and all control addresses fn control_addresses(rt: &mut impl Runtime) -> Result { rt.validate_immediate_caller_accept_any()?; let state: State = rt.state()?; @@ -216,6 +225,69 @@ impl Actor { }) } + /// Returns the owner address + fn get_owner(rt: &mut impl Runtime) -> Result { + rt.validate_immediate_caller_accept_any()?; + let state: State = rt.state()?; + let owner = get_miner_info(rt.store(), &state)?.owner; + Ok(GetOwnerReturn { owner }) + } + + /// Returns the worker address + fn get_worker(rt: &mut impl Runtime) -> Result { + rt.validate_immediate_caller_accept_any()?; + let state: State = rt.state()?; + let worker = get_miner_info(rt.store(), &state)?.worker; + Ok(GetWorkerReturn { worker }) + } + + /// Returns the control addresses + fn get_controls(rt: &mut impl Runtime) -> Result { + rt.validate_immediate_caller_accept_any()?; + let state: State = rt.state()?; + let controls = get_miner_info(rt.store(), &state)?.control_addresses; + Ok(GetControlsReturn { controls }) + } + + /// Returns the miner's sector size + fn get_sector_size(rt: &mut impl Runtime) -> Result { + rt.validate_immediate_caller_accept_any()?; + let state: State = rt.state()?; + let sector_size = get_miner_info(rt.store(), &state)?.sector_size; + Ok(GetSectorSizeReturn { sector_size }) + } + + /// Returns the miner's total funds locked as pre_commit_deposit + fn get_pre_commit_deposit( + rt: &mut impl Runtime, + ) -> Result { + rt.validate_immediate_caller_accept_any()?; + let state: State = rt.state()?; + Ok(GetPreCommitDepositReturn { pre_commit_deposit: state.pre_commit_deposits }) + } + + /// Returns the sum of the initial pledge requirements of all active sectors of this miner. + fn get_initial_pledge(rt: &mut impl Runtime) -> Result { + rt.validate_immediate_caller_accept_any()?; + let state: State = rt.state()?; + Ok(GetInitialPledgeReturn { initial_pledge: state.initial_pledge }) + } + + /// Returns the absolute value of debt this miner owes from unpaid fees. + fn get_fee_debt(rt: &mut impl Runtime) -> Result { + rt.validate_immediate_caller_accept_any()?; + let state: State = rt.state()?; + Ok(GetFeeDebtReturn { fee_debt: state.fee_debt }) + } + + /// Returns the absolute value of the locked funds in this miner's vesting table. + /// Note that this does NOT include other types of locked funds like precommit deposits. + fn get_locked_funds(rt: &mut impl Runtime) -> Result { + rt.validate_immediate_caller_accept_any()?; + let state: State = rt.state()?; + Ok(GetLockedFundsReturn { locked_funds: state.locked_funds }) + } + /// Will ALWAYS overwrite the existing control addresses with the control addresses passed in the params. /// If an empty addresses vector is passed, the control addresses will be cleared. /// A worker change will be scheduled if the worker passed in the params is different from the existing worker. @@ -5001,6 +5073,38 @@ impl ActorCode for Actor { Ok(RawBytes::default()) } None => Err(actor_error!(unhandled_message, "Invalid method")), + Some(Method::GetOwnerExported) => { + let res = Self::get_owner(rt)?; + Ok(RawBytes::serialize(res)?) + } + Some(Method::GetWorkerExported) => { + let res = Self::get_worker(rt)?; + Ok(RawBytes::serialize(res)?) + } + Some(Method::GetControlsExported) => { + let res = Self::get_controls(rt)?; + Ok(RawBytes::serialize(res)?) + } + Some(Method::GetSectorSizeExported) => { + let res = Self::get_sector_size(rt)?; + Ok(RawBytes::serialize(res)?) + } + Some(Method::GetPreCommitDepositExported) => { + let res = Self::get_pre_commit_deposit(rt)?; + Ok(RawBytes::serialize(res)?) + } + Some(Method::GetInitialPledgeExported) => { + let res = Self::get_initial_pledge(rt)?; + Ok(RawBytes::serialize(res)?) + } + Some(Method::GetFeeDebtExported) => { + let res = Self::get_fee_debt(rt)?; + Ok(RawBytes::serialize(res)?) + } + Some(Method::GetLockedFundsExported) => { + let res = Self::get_locked_funds(rt)?; + Ok(RawBytes::serialize(res)?) + } } } } diff --git a/actors/miner/src/types.rs b/actors/miner/src/types.rs index 0d8dd31f18..b2024f74fb 100644 --- a/actors/miner/src/types.rs +++ b/actors/miner/src/types.rs @@ -13,7 +13,7 @@ use fvm_shared::econ::TokenAmount; use fvm_shared::randomness::Randomness; use fvm_shared::sector::{ PoStProof, RegisteredPoStProof, RegisteredSealProof, RegisteredUpdateProof, SectorNumber, - StoragePower, + SectorSize, StoragePower, }; use fvm_shared::smooth::FilterEstimate; @@ -482,3 +482,59 @@ pub struct GetBeneficiaryReturn { } impl Cbor for GetBeneficiaryReturn {} + +#[derive(Serialize_tuple, Deserialize_tuple)] +pub struct GetOwnerReturn { + pub owner: Address, +} + +impl Cbor for GetOwnerReturn {} + +#[derive(Serialize_tuple, Deserialize_tuple)] +pub struct GetWorkerReturn { + pub worker: Address, +} + +impl Cbor for GetWorkerReturn {} + +#[derive(Serialize_tuple, Deserialize_tuple)] +pub struct GetControlsReturn { + pub controls: Vec
, +} + +impl Cbor for GetControlsReturn {} + +#[derive(Serialize_tuple, Deserialize_tuple)] +pub struct GetSectorSizeReturn { + pub sector_size: SectorSize, +} + +impl Cbor for GetSectorSizeReturn {} + +#[derive(Serialize_tuple, Deserialize_tuple)] +pub struct GetPreCommitDepositReturn { + pub pre_commit_deposit: TokenAmount, +} + +impl Cbor for GetPreCommitDepositReturn {} + +#[derive(Serialize_tuple, Deserialize_tuple)] +pub struct GetInitialPledgeReturn { + pub initial_pledge: TokenAmount, +} + +impl Cbor for GetInitialPledgeReturn {} + +#[derive(Serialize_tuple, Deserialize_tuple)] +pub struct GetFeeDebtReturn { + pub fee_debt: TokenAmount, +} + +impl Cbor for GetFeeDebtReturn {} + +#[derive(Serialize_tuple, Deserialize_tuple)] +pub struct GetLockedFundsReturn { + pub locked_funds: TokenAmount, +} + +impl Cbor for GetLockedFundsReturn {} diff --git a/actors/miner/tests/exported_getters.rs b/actors/miner/tests/exported_getters.rs new file mode 100644 index 0000000000..3613c31dbc --- /dev/null +++ b/actors/miner/tests/exported_getters.rs @@ -0,0 +1,222 @@ +use fil_actor_miner::{ + locked_reward_from_reward, Actor, GetControlsReturn, GetFeeDebtReturn, GetInitialPledgeReturn, + GetLockedFundsReturn, GetOwnerReturn, GetPreCommitDepositReturn, GetSectorSizeReturn, + GetWorkerReturn, Method, +}; +use fil_actors_runtime::test_utils::make_identity_cid; +use fvm_ipld_encoding::RawBytes; +use fvm_shared::address::Address; +use fvm_shared::{clock::ChainEpoch, econ::TokenAmount, sector::MAX_SECTOR_NUMBER}; +use num_traits::Zero; + +mod util; + +use util::*; + +const PERIOD_OFFSET: ChainEpoch = 100; + +// an expiration ~10 days greater than effective min expiration taking into account 30 days max +// between pre and prove commit +const DEFAULT_SECTOR_EXPIRATION: ChainEpoch = 220; + +#[test] +fn info_getters() { + let h = ActorHarness::new(PERIOD_OFFSET); + let mut rt = h.new_runtime(); + rt.set_balance(BIG_BALANCE.clone()); + h.construct_and_verify(&mut rt); + + // set caller to not-builtin + rt.set_caller(make_identity_cid(b"1234"), Address::new_id(1234)); + + // owner is good + rt.expect_validate_caller_any(); + let owner_ret: GetOwnerReturn = rt + .call::(Method::GetOwnerExported as u64, &RawBytes::default()) + .unwrap() + .deserialize() + .unwrap(); + + rt.verify(); + + assert_eq!(h.owner, owner_ret.owner); + + // worker is good + rt.expect_validate_caller_any(); + let worker_ret: GetWorkerReturn = rt + .call::(Method::GetWorkerExported as u64, &RawBytes::default()) + .unwrap() + .deserialize() + .unwrap(); + + rt.verify(); + + assert_eq!(h.worker, worker_ret.worker); + + // controls are good + rt.expect_validate_caller_any(); + let control_ret: GetControlsReturn = rt + .call::(Method::GetControlsExported as u64, &RawBytes::default()) + .unwrap() + .deserialize() + .unwrap(); + + rt.verify(); + + // let's be sure we're not vacuously testing this method + assert_ne!(0, h.control_addrs.len()); + assert_eq!(h.control_addrs, control_ret.controls); + + // sector size is good + rt.expect_validate_caller_any(); + let sector_size_ret: GetSectorSizeReturn = rt + .call::(Method::GetSectorSizeExported as u64, &RawBytes::default()) + .unwrap() + .deserialize() + .unwrap(); + + rt.verify(); + + assert_eq!(h.sector_size, sector_size_ret.sector_size); + + h.check_state(&rt); +} + +#[test] +fn collateral_getters() { + let h = ActorHarness::new(PERIOD_OFFSET); + let mut rt = h.new_runtime(); + rt.balance.replace(BIG_BALANCE.clone()); + + let precommit_epoch = PERIOD_OFFSET + 1; + rt.set_epoch(precommit_epoch); + + h.construct_and_verify(&mut rt); + let dl_info = h.deadline(&rt); + + // Precommit a sector + // Use the max sector number to make sure everything works. + let sector_no = MAX_SECTOR_NUMBER; + let prove_commit_epoch = precommit_epoch + rt.policy.pre_commit_challenge_delay + 1; + let expiration = + dl_info.period_end() + DEFAULT_SECTOR_EXPIRATION * rt.policy.wpost_proving_period; // something on deadline boundary but > 180 days + + let precommit_params = + h.make_pre_commit_params(sector_no, precommit_epoch - 1, expiration, vec![]); + let precommit = + h.pre_commit_sector_and_get(&mut rt, precommit_params, PreCommitConfig::empty(), true); + + // Query the total precommit deposit from a non-builtin actor + + // set caller to not-builtin + rt.set_caller(make_identity_cid(b"1234"), Address::new_id(1234)); + + rt.expect_validate_caller_any(); + let pre_commit_deposit_ret: GetPreCommitDepositReturn = rt + .call::(Method::GetPreCommitDepositExported as u64, &RawBytes::default()) + .unwrap() + .deserialize() + .unwrap(); + + rt.verify(); + + // let's be sure we're not vacuously testing this method + assert!(!precommit.pre_commit_deposit.is_zero()); + assert_eq!(precommit.pre_commit_deposit, pre_commit_deposit_ret.pre_commit_deposit); + + // run prove commit logic + rt.set_epoch(prove_commit_epoch); + rt.balance.replace(TokenAmount::from_whole(1000)); + let pcc = ProveCommitConfig::empty(); + + let sector = h + .prove_commit_sector_and_confirm( + &mut rt, + &precommit, + h.make_prove_commit_params(sector_no), + pcc, + ) + .unwrap(); + + // Query the total precommit deposits and initial pledge from a non-builtin actor + + // set caller to not-builtin + rt.set_caller(make_identity_cid(b"1234"), Address::new_id(1234)); + + // query PCD + rt.expect_validate_caller_any(); + let pre_commit_deposit_ret: GetPreCommitDepositReturn = rt + .call::(Method::GetPreCommitDepositExported as u64, &RawBytes::default()) + .unwrap() + .deserialize() + .unwrap(); + + rt.verify(); + + assert!(pre_commit_deposit_ret.pre_commit_deposit.is_zero()); + + // query IP + + rt.expect_validate_caller_any(); + let initial_pledge_ret: GetInitialPledgeReturn = rt + .call::(Method::GetInitialPledgeExported as u64, &RawBytes::default()) + .unwrap() + .deserialize() + .unwrap(); + + rt.verify(); + + // let's be sure we're not vacuously testing this method + assert!(!sector.initial_pledge.is_zero()); + assert_eq!(sector.initial_pledge, initial_pledge_ret.initial_pledge); + + h.check_state(&rt); +} + +#[test] +fn debt_and_vesting_getters() { + let h = ActorHarness::new(PERIOD_OFFSET); + let mut rt = h.new_runtime(); + h.construct_and_verify(&mut rt); + + let reward_amount: TokenAmount = 4 * &*BIG_BALANCE; + let (amount_locked, _) = locked_reward_from_reward(reward_amount.clone()); + rt.set_balance(amount_locked.clone()); + h.apply_rewards(&mut rt, reward_amount, TokenAmount::zero()); + + // introduce fee debt + let mut st = h.get_state(&rt); + st.fee_debt = 4 * &*BIG_BALANCE; + rt.replace_state(&st); + + // Query the total locked funds & debt from a non-builtin actor + + // set caller to not-builtin + rt.set_caller(make_identity_cid(b"1234"), Address::new_id(1234)); + + // locked funds + rt.expect_validate_caller_any(); + let locked_funds_ret: GetLockedFundsReturn = rt + .call::(Method::GetLockedFundsExported as u64, &RawBytes::default()) + .unwrap() + .deserialize() + .unwrap(); + + rt.verify(); + + // let's be sure we're not vacuously testing this method + assert!(!amount_locked.is_zero()); + assert_eq!(amount_locked, locked_funds_ret.locked_funds); + + // debt + rt.expect_validate_caller_any(); + let debt_ret: GetFeeDebtReturn = rt + .call::(Method::GetFeeDebtExported as u64, &RawBytes::default()) + .unwrap() + .deserialize() + .unwrap(); + + rt.verify(); + + assert_eq!(st.fee_debt, debt_ret.fee_debt); +} diff --git a/actors/miner/tests/util.rs b/actors/miner/tests/util.rs index e5929b9584..e4a1932ab3 100644 --- a/actors/miner/tests/util.rs +++ b/actors/miner/tests/util.rs @@ -860,6 +860,7 @@ impl ActorHarness { pc: &SectorPreCommitOnChainInfo, params: ProveCommitSectorParams, ) -> Result<(), ActorError> { + rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, self.worker); let seal_rand = TEST_RANDOMNESS_ARRAY_FROM_ONE; let seal_int_rand = TEST_RANDOMNESS_ARRAY_FROM_TWO; let interactive_epoch = pc.pre_commit_epoch + rt.policy.pre_commit_challenge_delay;