Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions contracts/contracts/lib/LibStaking.sol
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,18 @@ library LibValidatorSet {
return addresses;
}

function listWaitingValidators(ValidatorSet storage validators) internal view returns (address[] memory addresses) {
uint16 size = validators.waitingValidators.getSize();
addresses = new address[](size);
for (uint16 i = 1; i <= size; ) {
addresses[i - 1] = validators.waitingValidators.getAddress(i);
unchecked {
++i;
}
}
return addresses;
}

/// @notice Get the total collateral of *active* validators.
function getTotalActivePower(ValidatorSet storage validators) internal view returns (uint256 collateral) {
uint16 size = validators.activeValidators.getSize();
Expand Down Expand Up @@ -431,6 +443,18 @@ library LibStaking {
return s.validatorSet.waitingValidators.getSize() + s.validatorSet.activeValidators.getSize();
}

/// @notice Returns all active validators.
function listActiveValidators() internal view returns (address[] memory addresses) {
SubnetActorStorage storage s = LibSubnetActorStorage.appStorage();
return s.validatorSet.listActiveValidators();
}

/// @notice Returns all waiting validators.
function listWaitingValidators() internal view returns (address[] memory addresses) {
SubnetActorStorage storage s = LibSubnetActorStorage.appStorage();
return s.validatorSet.listWaitingValidators();
}

function getTotalConfirmedCollateral() internal view returns (uint256) {
SubnetActorStorage storage s = LibSubnetActorStorage.appStorage();
return s.validatorSet.getTotalConfirmedCollateral();
Expand Down
10 changes: 10 additions & 0 deletions contracts/contracts/subnet/SubnetActorGetterFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,16 @@ contract SubnetActorGetterFacet {
validator = s.validatorSet.validators[validatorAddress];
}

/// @notice Returns detailed information about all active validators.
function getActiveValidators() external view returns (address[] memory) {
return LibStaking.listActiveValidators();
}

/// @notice Returns detailed information about all waiting validators.
function getWaitingValidators() external view returns (address[] memory) {
return LibStaking.listWaitingValidators();
}

/// @notice Returns the total number of validators (active and waiting).
function getTotalValidatorsNumber() external view returns (uint16) {
return LibStaking.totalValidators();
Expand Down
2 changes: 1 addition & 1 deletion contracts/test/helpers/SelectorLibrary.sol
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ library SelectorLibrary {
if (keccak256(abi.encodePacked(facetName)) == keccak256(abi.encodePacked("SubnetActorGetterFacet"))) {
return
abi.decode(
hex"000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000203354c3e10000000000000000000000000000000000000000000000000000000035142c8c0000000000000000000000000000000000000000000000000000000006c46853000000000000000000000000000000000000000000000000000000004b27aa72000000000000000000000000000000000000000000000000000000004b0694e200000000000000000000000000000000000000000000000000000000b6797d3c000000000000000000000000000000000000000000000000000000008ef3f76100000000000000000000000000000000000000000000000000000000e02d971b00000000000000000000000000000000000000000000000000000000903e693000000000000000000000000000000000000000000000000000000000948628a900000000000000000000000000000000000000000000000000000000d92e8f1200000000000000000000000000000000000000000000000000000000c7cda762000000000000000000000000000000000000000000000000000000009754b29e0000000000000000000000000000000000000000000000000000000038a210b30000000000000000000000000000000000000000000000000000000080f76021000000000000000000000000000000000000000000000000000000005dd9147c00000000000000000000000000000000000000000000000000000000d6eb591000000000000000000000000000000000000000000000000000000000332a5ac9000000000000000000000000000000000000000000000000000000001597bf7e0000000000000000000000000000000000000000000000000000000052d182d1000000000000000000000000000000000000000000000000000000001904bb2e00000000000000000000000000000000000000000000000000000000cfca28240000000000000000000000000000000000000000000000000000000040550a1c00000000000000000000000000000000000000000000000000000000d081be03000000000000000000000000000000000000000000000000000000001f3a0e410000000000000000000000000000000000000000000000000000000072d0a0e000000000000000000000000000000000000000000000000000000000599c7bd1000000000000000000000000000000000000000000000000000000009e33bd0200000000000000000000000000000000000000000000000000000000c5ab224100000000000000000000000000000000000000000000000000000000f0cf6c9600000000000000000000000000000000000000000000000000000000ad81e4d60000000000000000000000000000000000000000000000000000000080875df700000000000000000000000000000000000000000000000000000000",
hex"000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000223354c3e10000000000000000000000000000000000000000000000000000000035142c8c0000000000000000000000000000000000000000000000000000000006c46853000000000000000000000000000000000000000000000000000000004b27aa72000000000000000000000000000000000000000000000000000000004b0694e200000000000000000000000000000000000000000000000000000000b6797d3c000000000000000000000000000000000000000000000000000000008ef3f76100000000000000000000000000000000000000000000000000000000e02d971b00000000000000000000000000000000000000000000000000000000903e693000000000000000000000000000000000000000000000000000000000948628a900000000000000000000000000000000000000000000000000000000d92e8f12000000000000000000000000000000000000000000000000000000009de7025800000000000000000000000000000000000000000000000000000000c7cda762000000000000000000000000000000000000000000000000000000009754b29e0000000000000000000000000000000000000000000000000000000038a210b30000000000000000000000000000000000000000000000000000000080f76021000000000000000000000000000000000000000000000000000000005dd9147c00000000000000000000000000000000000000000000000000000000d6eb591000000000000000000000000000000000000000000000000000000000332a5ac9000000000000000000000000000000000000000000000000000000001597bf7e0000000000000000000000000000000000000000000000000000000052d182d1000000000000000000000000000000000000000000000000000000001904bb2e000000000000000000000000000000000000000000000000000000006ad04c7900000000000000000000000000000000000000000000000000000000cfca28240000000000000000000000000000000000000000000000000000000040550a1c00000000000000000000000000000000000000000000000000000000d081be03000000000000000000000000000000000000000000000000000000001f3a0e410000000000000000000000000000000000000000000000000000000072d0a0e000000000000000000000000000000000000000000000000000000000599c7bd1000000000000000000000000000000000000000000000000000000009e33bd0200000000000000000000000000000000000000000000000000000000c5ab224100000000000000000000000000000000000000000000000000000000f0cf6c9600000000000000000000000000000000000000000000000000000000ad81e4d60000000000000000000000000000000000000000000000000000000080875df700000000000000000000000000000000000000000000000000000000",
(bytes4[])
);
}
Expand Down
9 changes: 8 additions & 1 deletion contracts/test/integration/SubnetActorDiamond.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase {
require(saDiamond.getter().bottomUpCheckPeriod() == params.bottomUpCheckPeriod, "unexpected bottom-up period");
require(saDiamond.getter().majorityPercentage() == params.majorityPercentage, "unexpected majority percentage");
require(saDiamond.getter().getParent().toHash() == _parentId.toHash(), "unexpected parent subnetID hash");
require(saDiamond.getter().genesisValidators().length == 0, "unexpected genesis validators");
require(saDiamond.getter().getActiveValidators().length == 0, "unexpected active validators");
require(saDiamond.getter().getWaitingValidators().length == 0, "unexpected waiting validators");
Comment on lines +78 to +80
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good work strengthening the assertions here with the new accessors!

}

function testSubnetActorDiamondReal_LoupeFunction() public view {
Expand Down Expand Up @@ -131,7 +134,9 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase {
TestUtils.ensureBytesEqual(v.metadata, publicKey1);
require(saDiamond.getter().bootstrapped(), "subnet not bootstrapped");
require(!saDiamond.getter().killed(), "subnet killed");
require(saDiamond.getter().genesisValidators().length == 1, "not one validator in genesis");
require(saDiamond.getter().genesisValidators().length == 1, "not 1 genesis validator");
require(saDiamond.getter().getActiveValidators().length == 1, "not 1 active validator");
require(saDiamond.getter().getWaitingValidators().length == 0, "not 0 waiting validator");

(uint64 nextConfigNum, uint64 startConfigNum) = saDiamond.getter().getConfigurationNumbers();
require(nextConfigNum == LibStaking.INITIAL_CONFIGURATION_NUMBER, "next config num not 1");
Expand Down Expand Up @@ -177,6 +182,8 @@ contract SubnetActorDiamondTest is Test, IntegrationTestBase {
require(!saDiamond.getter().isWaitingValidator(validator1), "waiting validator1");
require(saDiamond.getter().isActiveValidator(validator2), "not active validator2");
require(!saDiamond.getter().isWaitingValidator(validator2), "waiting validator2");
require(saDiamond.getter().getActiveValidators().length == 2, "not 2 active validators");
require(saDiamond.getter().getWaitingValidators().length == 0, "not 0 waiting validators");

(nextConfigNum, startConfigNum) = saDiamond.getter().getConfigurationNumbers();
require(
Expand Down
42 changes: 42 additions & 0 deletions ipc/cli/src/commands/subnet/list_validators.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright 2022-2024 Protocol Labs
// SPDX-License-Identifier: MIT
//! List subnets cli command

use crate::{get_ipc_provider, CommandLineHandler, GlobalArguments};
use async_trait::async_trait;
use clap::Args;
use ipc_api::subnet_id::SubnetID;
use std::fmt::Debug;
use std::str::FromStr;

/// The command to create a new subnet actor.
pub(crate) struct ListValidators;

#[async_trait]
impl CommandLineHandler for ListValidators {
type Arguments = ListValidatorsArgs;

async fn handle(global: &GlobalArguments, arguments: &Self::Arguments) -> anyhow::Result<()> {
log::debug!("list validators with args: {:?}", arguments);

let provider = get_ipc_provider(global)?;
let subnet = SubnetID::from_str(&arguments.subnet)?;

let validators = provider.list_validators(&subnet).await?;

for (addr, info) in validators {
println!("{}: {}", addr, info);
}
Ok(())
}
}

#[derive(Debug, Args)]
#[command(
name = "list validators",
about = "List the info of all the validators in the subnet"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
about = "List the info of all the validators in the subnet"
about = "List the info of all the validators in the subnet, as viewed by the parent"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important to note this, since one would default to thinking that we're querying the subnet itself.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pushed in 8f12868.

)]
pub(crate) struct ListValidatorsArgs {
#[arg(long, help = "The target subnet to perform query")]
pub subnet: String,
}
13 changes: 8 additions & 5 deletions ipc/cli/src/commands/subnet/mod.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
// Copyright 2022-2024 Protocol Labs
// SPDX-License-Identifier: MIT

use self::bootstrap::{AddBootstrap, AddBootstrapArgs, ListBootstraps, ListBootstrapsArgs};
use self::join::{StakeSubnet, StakeSubnetArgs, UnstakeSubnet, UnstakeSubnetArgs};
use self::leave::{Claim, ClaimArgs};
use self::rpc::{ChainIdSubnet, ChainIdSubnetArgs};
pub use crate::commands::subnet::create::{CreateSubnet, CreateSubnetArgs};
use crate::commands::subnet::genesis_epoch::{GenesisEpoch, GenesisEpochArgs};
pub use crate::commands::subnet::join::{JoinSubnet, JoinSubnetArgs};
pub use crate::commands::subnet::kill::{KillSubnet, KillSubnetArgs};
pub use crate::commands::subnet::leave::{LeaveSubnet, LeaveSubnetArgs};
use crate::commands::subnet::list_subnets::{ListSubnets, ListSubnetsArgs};
use crate::commands::subnet::list_validators::{ListValidators, ListValidatorsArgs};
use crate::commands::subnet::rpc::{RPCSubnet, RPCSubnetArgs};
use crate::commands::subnet::send_value::{SendValue, SendValueArgs};
use crate::commands::subnet::set_federated_power::{SetFederatedPower, SetFederatedPowerArgs};
Expand All @@ -17,18 +22,14 @@ use crate::commands::subnet::validator::{ValidatorInfo, ValidatorInfoArgs};
use crate::{CommandLineHandler, GlobalArguments};
use clap::{Args, Subcommand};

use self::bootstrap::{AddBootstrap, AddBootstrapArgs, ListBootstraps, ListBootstrapsArgs};
use self::join::{StakeSubnet, StakeSubnetArgs, UnstakeSubnet, UnstakeSubnetArgs};
use self::leave::{Claim, ClaimArgs};
use self::rpc::{ChainIdSubnet, ChainIdSubnetArgs};

pub mod bootstrap;
pub mod create;
mod genesis_epoch;
pub mod join;
pub mod kill;
pub mod leave;
pub mod list_subnets;
pub mod list_validators;
pub mod rpc;
pub mod send_value;
mod set_federated_power;
Expand All @@ -53,6 +54,7 @@ impl SubnetCommandsArgs {
match &self.command {
Commands::Create(args) => CreateSubnet::handle(global, args).await,
Commands::List(args) => ListSubnets::handle(global, args).await,
Commands::ListValidators(args) => ListValidators::handle(global, args).await,
Commands::Join(args) => JoinSubnet::handle(global, args).await,
Commands::Rpc(args) => RPCSubnet::handle(global, args).await,
Commands::ChainId(args) => ChainIdSubnet::handle(global, args).await,
Expand All @@ -78,6 +80,7 @@ impl SubnetCommandsArgs {
pub(crate) enum Commands {
Create(CreateSubnetArgs),
List(ListSubnetsArgs),
ListValidators(ListValidatorsArgs),
Join(JoinSubnetArgs),
Rpc(RPCSubnetArgs),
ChainId(ChainIdSubnetArgs),
Expand Down
10 changes: 10 additions & 0 deletions ipc/provider/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,16 @@ impl IpcProvider {
conn.manager().get_validator_info(subnet, validator).await
}

pub async fn list_validators(
&self,
subnet: &SubnetID,
) -> anyhow::Result<Vec<(Address, ValidatorInfo)>> {
let parent = subnet.parent().ok_or_else(|| anyhow!("no parent found"))?;
let conn = self.get_connection(&parent)?;

conn.manager().list_validators(subnet).await
}

/// Get the changes in subnet validators. This is fetched from parent.
pub async fn get_validator_changeset(
&self,
Expand Down
44 changes: 44 additions & 0 deletions ipc/provider/src/manager/evm/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -877,6 +877,50 @@ impl SubnetManager for EthSubnetManager {
})
}

async fn list_validators(&self, subnet: &SubnetID) -> Result<Vec<(Address, ValidatorInfo)>> {
let address = contract_address_from_subnet(subnet)?;
let contract = subnet_actor_getter_facet::SubnetActorGetterFacet::new(
address,
Arc::new(self.ipc_contract_info.provider.clone()),
);

let mut addresses: Vec<Address> = vec![];
let mut validators: Vec<ValidatorInfo> = vec![];

let active = contract.get_active_validators().call().await?;
addresses.extend(
active
.iter()
.map(ethers_address_to_fil_address)
.collect::<Result<Vec<_>, _>>()?,
);
for addr in active {
let info = contract.get_validator(addr).call().await?;
validators.push(ValidatorInfo {
staking: ValidatorStakingInfo::try_from(info)?,
is_active: true,
is_waiting: false,
});
}
let waiting = contract.get_waiting_validators().call().await?;
addresses.extend(
waiting
.iter()
.map(ethers_address_to_fil_address)
.collect::<Result<Vec<_>, _>>()?,
);
for addr in waiting {
let info = contract.get_validator(addr).call().await?;
validators.push(ValidatorInfo {
staking: ValidatorStakingInfo::try_from(info)?,
is_active: false,
is_waiting: true,
});
}

Ok(addresses.into_iter().zip(validators).collect())
}

async fn set_federated_power(
&self,
from: &Address,
Expand Down
6 changes: 4 additions & 2 deletions ipc/provider/src/manager/subnet.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
// Copyright 2022-2024 Protocol Labs
// SPDX-License-Identifier: MIT

use std::collections::{BTreeMap, HashMap};

use anyhow::Result;
use async_trait::async_trait;
use fvm_shared::clock::ChainEpoch;
Expand All @@ -15,6 +13,7 @@ use ipc_api::staking::{StakingChangeRequest, ValidatorInfo};
use ipc_api::subnet::{Asset, ConstructParams, PermissionMode};
use ipc_api::subnet_id::SubnetID;
use ipc_api::validator::Validator;
use std::collections::{BTreeMap, HashMap};

use crate::lotus::message::ipc::SubnetInfo;

Expand Down Expand Up @@ -189,6 +188,9 @@ pub trait SubnetManager: Send + Sync + TopDownFinalityQuery + BottomUpCheckpoint
validator: &Address,
) -> Result<ValidatorInfo>;

/// Lists all the validators
async fn list_validators(&self, subnet: &SubnetID) -> Result<Vec<(Address, ValidatorInfo)>>;

async fn set_federated_power(
&self,
from: &Address,
Expand Down