diff --git a/actors/power/src/lib.rs b/actors/power/src/lib.rs index 9d5552094e..5eeac63a0e 100644 --- a/actors/power/src/lib.rs +++ b/actors/power/src/lib.rs @@ -62,8 +62,11 @@ pub enum Method { SubmitPoRepForBulkVerify = 8, CurrentTotalPower = 9, // Method numbers derived from FRC-0042 standards + CreateMinerExported = frc42_dispatch::method_hash!("CreateMiner"), NetworkRawPowerExported = frc42_dispatch::method_hash!("NetworkRawPower"), MinerRawPowerExported = frc42_dispatch::method_hash!("MinerRawPower"), + MinerCountExported = frc42_dispatch::method_hash!("MinerCount"), + MinerConsensusCountExported = frc42_dispatch::method_hash!("MinerConsensusCount"), } pub const ERR_TOO_MANY_PROVE_COMMITS: ExitCode = ExitCode::new(32); @@ -396,6 +399,26 @@ impl Actor { Ok(MinerRawPowerReturn { raw_byte_power, meets_consensus_minimum }) } + /// Returns the total number of miners created, regardless of whether or not + /// they have any pledged storage. + fn miner_count(rt: &mut impl Runtime) -> Result { + rt.validate_immediate_caller_accept_any()?; + let st: State = rt.state()?; + + Ok(MinerCountReturn { miner_count: st.miner_count }) + } + + /// Returns the total number of miners that have more than the consensus minimum amount of storage active. + /// Active means that the storage must not be faulty. + fn miner_consensus_count( + rt: &mut impl Runtime, + ) -> Result { + rt.validate_immediate_caller_accept_any()?; + let st: State = rt.state()?; + + Ok(MinerConsensusCountReturn { miner_consensus_count: st.miner_above_min_power_count }) + } + fn process_batch_proof_verifies( rt: &mut impl Runtime, rewret: &ThisEpochRewardReturn, @@ -663,7 +686,7 @@ impl ActorCode for Actor { Self::constructor(rt)?; Ok(RawBytes::default()) } - Some(Method::CreateMiner) => { + Some(Method::CreateMiner) | Some(Method::CreateMinerExported) => { let res = Self::create_miner(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(res)?) } @@ -700,6 +723,14 @@ impl ActorCode for Actor { let res = Self::miner_raw_power(rt, cbor::deserialize_params(params)?)?; Ok(RawBytes::serialize(res)?) } + Some(Method::MinerCountExported) => { + let res = Self::miner_count(rt)?; + Ok(RawBytes::serialize(res)?) + } + Some(Method::MinerConsensusCountExported) => { + let res = Self::miner_consensus_count(rt)?; + Ok(RawBytes::serialize(res)?) + } None => Err(actor_error!(unhandled_message; "Invalid method")), } } diff --git a/actors/power/src/types.rs b/actors/power/src/types.rs index 3df018c187..7afe6f1d37 100644 --- a/actors/power/src/types.rs +++ b/actors/power/src/types.rs @@ -93,3 +93,15 @@ pub struct MinerRawPowerReturn { } impl Cbor for MinerRawPowerReturn {} + +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] +#[serde(transparent)] +pub struct MinerCountReturn { + pub miner_count: i64, +} + +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)] +#[serde(transparent)] +pub struct MinerConsensusCountReturn { + pub miner_consensus_count: i64, +} diff --git a/actors/power/tests/harness/mod.rs b/actors/power/tests/harness/mod.rs index b236610984..cad99e160a 100644 --- a/actors/power/tests/harness/mod.rs +++ b/actors/power/tests/harness/mod.rs @@ -1,15 +1,15 @@ use cid::Cid; use fil_actor_power::detail::GAS_ON_SUBMIT_VERIFY_SEAL; -use fil_actor_power::epoch_key; use fil_actor_power::ext::miner::ConfirmSectorProofsParams; use fil_actor_power::ext::miner::CONFIRM_SECTOR_PROOFS_VALID_METHOD; use fil_actor_power::ext::reward::Method::ThisEpochReward; use fil_actor_power::ext::reward::UPDATE_NETWORK_KPI; use fil_actor_power::testing::check_state_invariants; -use fil_actor_power::CronEvent; use fil_actor_power::EnrollCronEventParams; use fil_actor_power::CRON_QUEUE_AMT_BITWIDTH; use fil_actor_power::CRON_QUEUE_HAMT_BITWIDTH; +use fil_actor_power::{epoch_key, MinerCountReturn}; +use fil_actor_power::{CronEvent, MinerConsensusCountReturn}; use fil_actors_runtime::runtime::RuntimePolicy; use fil_actors_runtime::test_utils::CRON_ACTOR_CODE_ID; use fil_actors_runtime::Multimap; @@ -210,9 +210,15 @@ impl Harness { keys.iter().map(|k| Address::from_bytes(k).unwrap()).collect::>() } - pub fn miner_count(&self, rt: &MockRuntime) -> i64 { - let st: State = rt.get_state(); - st.miner_count + pub fn miner_count(&self, rt: &mut MockRuntime) -> i64 { + rt.expect_validate_caller_any(); + let ret: MinerCountReturn = rt + .call::(Method::MinerCountExported as MethodNum, &RawBytes::default()) + .unwrap() + .deserialize() + .unwrap(); + + ret.miner_count } pub fn this_epoch_baseline_power(&self) -> &StoragePower { @@ -366,8 +372,17 @@ impl Harness { } pub fn expect_miners_above_min_power(&self, rt: &mut MockRuntime, count: i64) { - let st: State = rt.get_state(); - assert_eq!(count, st.miner_above_min_power_count); + rt.expect_validate_caller_any(); + let ret: MinerConsensusCountReturn = rt + .call::( + Method::MinerConsensusCountExported as MethodNum, + &RawBytes::default(), + ) + .unwrap() + .deserialize() + .unwrap(); + + assert_eq!(count, ret.miner_consensus_count); } pub fn expect_query_network_info(&self, rt: &mut MockRuntime) { diff --git a/actors/power/tests/power_actor_tests.rs b/actors/power/tests/power_actor_tests.rs index fba2a73596..addff16244 100644 --- a/actors/power/tests/power_actor_tests.rs +++ b/actors/power/tests/power_actor_tests.rs @@ -13,11 +13,12 @@ use fvm_shared::clock::ChainEpoch; use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; use fvm_shared::sector::{RegisteredPoStProof, StoragePower}; +use fvm_shared::MethodNum; use num_traits::Zero; use std::ops::Neg; use fil_actor_power::{ - consensus_miner_min_power, Actor as PowerActor, Actor, CreateMinerParams, + consensus_miner_min_power, Actor as PowerActor, Actor, CreateMinerParams, CreateMinerReturn, EnrollCronEventParams, Method, MinerRawPowerParams, MinerRawPowerReturn, NetworkRawPowerReturn, State, UpdateClaimedPowerParams, CONSENSUS_MINER_MIN_MINERS, }; @@ -326,8 +327,7 @@ fn new_miner_updates_miner_above_min_power_count() { h.window_post_proof = test.proof; h.create_miner_basic(&mut rt, *OWNER, *OWNER, MINER1).unwrap(); - let st: State = rt.get_state(); - assert_eq!(test.expected_miners, st.miner_above_min_power_count); + h.expect_miners_above_min_power(&mut rt, test.expected_miners); h.check_state(&rt); } } @@ -372,8 +372,7 @@ fn power_accounting_crossing_threshold() { let expected_total_above = &(power_unit * 4); h.expect_total_power_eager(&mut rt, expected_total_above, &(expected_total_above * 10)); - let st: State = rt.get_state(); - assert_eq!(4, st.miner_above_min_power_count); + h.expect_miners_above_min_power(&mut rt, 4); // Less than 4 miners above threshold again small miner power is counted again h.update_claimed_power(&mut rt, MINER4, &delta.neg(), &(delta.neg() * 10)); @@ -1011,7 +1010,7 @@ mod cron_tests { assert!(h.get_claim(&rt, &miner1).is_none()); // miner count has been reduced to 1 - assert_eq!(h.miner_count(&rt), 1); + assert_eq!(h.miner_count(&mut rt), 1); // next epoch, only the reward actor is invoked rt.set_epoch(3); @@ -1440,3 +1439,72 @@ mod submit_porep_for_bulk_verify_tests { h.check_state(&rt); } } + +#[test] +fn create_miner_restricted_correctly() { + let (h, mut rt) = setup(); + + let peer = "miner".as_bytes().to_vec(); + let multiaddrs = vec![BytesDe("multiaddr".as_bytes().to_vec())]; + + let params = serialize( + &CreateMinerParams { + owner: *OWNER, + worker: *OWNER, + window_post_proof_type: RegisteredPoStProof::StackedDRGWinning2KiBV1, + peer: peer.clone(), + multiaddrs: multiaddrs.clone(), + }, + "create miner params", + ) + .unwrap(); + + rt.set_caller(make_identity_cid(b"1234"), *OWNER); + + // cannot call the unexported method + expect_abort_contains_message( + ExitCode::USR_FORBIDDEN, + "must be built-in", + rt.call::(Method::CreateMiner as MethodNum, ¶ms), + ); + + // can call the exported method + + rt.expect_validate_caller_any(); + let expected_init_params = ExecParams { + code_cid: *MINER_ACTOR_CODE_ID, + constructor_params: serialize( + &MinerConstructorParams { + owner: *OWNER, + worker: *OWNER, + control_addresses: vec![], + window_post_proof_type: RegisteredPoStProof::StackedDRGWinning2KiBV1, + peer_id: peer, + multi_addresses: multiaddrs, + }, + "minerctor params", + ) + .unwrap(), + }; + let create_miner_ret = CreateMinerReturn { id_address: *MINER, robust_address: *ACTOR }; + rt.expect_send( + INIT_ACTOR_ADDR, + EXEC_METHOD, + RawBytes::serialize(expected_init_params).unwrap(), + TokenAmount::zero(), + RawBytes::serialize(create_miner_ret).unwrap(), + ExitCode::OK, + ); + + let ret: CreateMinerReturn = rt + .call::(Method::CreateMinerExported as MethodNum, ¶ms) + .unwrap() + .deserialize() + .unwrap(); + rt.verify(); + + assert_eq!(ret.id_address, *MINER); + assert_eq!(ret.robust_address, *ACTOR); + + h.check_state(&rt); +}