From a0b8b75552465763fe32ed349e2fac250daffc92 Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Mon, 2 Dec 2024 17:17:11 +0100 Subject: [PATCH 1/4] feat(cli): get validator status --- bolt-cli/src/cli.rs | 16 ++++++++++- bolt-cli/src/commands/validators.rs | 42 ++++++++++++++++++++++++++++- bolt-cli/src/contracts/bolt.rs | 12 +++++++++ 3 files changed, 68 insertions(+), 2 deletions(-) diff --git a/bolt-cli/src/cli.rs b/bolt-cli/src/cli.rs index 1778704c..84974cba 100644 --- a/bolt-cli/src/cli.rs +++ b/bolt-cli/src/cli.rs @@ -1,6 +1,6 @@ use std::path::PathBuf; -use alloy::primitives::{Address, B256, U256}; +use alloy::primitives::{Address, FixedBytes, B256, U256}; use clap::{ builder::styling::{AnsiColor, Color, Style}, Parser, Subcommand, ValueEnum, @@ -168,6 +168,20 @@ pub enum ValidatorsSubcommand { #[clap(long, env = "ADMIN_PRIVATE_KEY")] admin_private_key: B256, }, + /// Check the status of a validator (batch). + Status { + /// The URL of the RPC to broadcast the transaction. + #[clap(long, env = "RPC_URL")] + rpc_url: Url, + + /// The path to the JSON pubkeys file, containing an array of BLS public keys. + #[clap(long, env = "PUBKEYS_PATH", conflicts_with = "PUBKEYS")] + pubkeys_path: Option, + + /// The validator public key to check the status of. + #[clap(long, env = "PUBKEYS", conflicts_with = "PUBKEYS_PATH")] + pubkeys: Vec>, + }, } #[derive(Debug, Clone, Parser)] diff --git a/bolt-cli/src/commands/validators.rs b/bolt-cli/src/commands/validators.rs index 43954ef2..888f7254 100644 --- a/bolt-cli/src/commands/validators.rs +++ b/bolt-cli/src/commands/validators.rs @@ -5,7 +5,7 @@ use alloy::{ }; use ethereum_consensus::crypto::PublicKey as BlsPublicKey; use eyre::Context; -use tracing::info; +use tracing::{info, warn}; use crate::{ cli::{Chain, ValidatorsCommand, ValidatorsSubcommand}, @@ -74,6 +74,46 @@ impl ValidatorsCommand { info!("Successfully registered validators into bolt"); + Ok(()) + } + ValidatorsSubcommand::Status { rpc_url, pubkeys_path, pubkeys } => { + let provider = ProviderBuilder::new().on_http(rpc_url); + let chain_id = provider.get_chain_id().await?; + let chain = Chain::from_id(chain_id) + .unwrap_or_else(|| panic!("chain id {} not supported", chain_id)); + let registry = deployments_for_chain(chain).bolt.validators; + + let mut bls_pubkeys = Vec::new(); + + if let Some(pubkeys_path) = pubkeys_path { + let pubkeys_file = std::fs::File::open(&pubkeys_path)?; + let keys: Vec = serde_json::from_reader(pubkeys_file)?; + bls_pubkeys.extend(keys); + } + + for bytes in pubkeys { + let key = BlsPublicKey::try_from(bytes.as_ref())?; + bls_pubkeys.push(key); + } + + info!(pubkeys = bls_pubkeys.len(), %registry, ?chain, "Checking status of validators"); + + let pubkey_hashes: Vec<_> = bls_pubkeys.iter().map(compress_bls_pubkey).collect(); + + let bolt_validators = BoltValidators::new(registry, provider); + + for (hash, pubkey) in pubkey_hashes.iter().zip(bls_pubkeys.iter()) { + match bolt_validators.getValidatorByPubkeyHash(*hash).call().await.map(|v| v._0) + { + Ok(info) => { + info!(%pubkey, operator = %info.authorizedOperator, controller = %info.controller, gas_limit = info.maxCommittedGasLimit, "Validator registered"); + } + Err(_e) => { + warn!(%pubkey, "Validator not registered"); + } + } + } + Ok(()) } } diff --git a/bolt-cli/src/contracts/bolt.rs b/bolt-cli/src/contracts/bolt.rs index 97d1988d..46ff0a08 100644 --- a/bolt-cli/src/contracts/bolt.rs +++ b/bolt-cli/src/contracts/bolt.rs @@ -4,6 +4,13 @@ sol! { #[allow(missing_docs)] #[sol(rpc)] interface BoltValidators { + struct ValidatorInfo { + bytes20 pubkeyHash; + uint32 maxCommittedGasLimit; + address authorizedOperator; + address controller; + } + /// @notice Register a batch of Validators and authorize a Collateral Provider and Operator for them /// @dev This function allows anyone to register a list of Validators. /// @param pubkeyHashes List of BLS public key hashes for the Validators to be registered @@ -11,6 +18,11 @@ sol! { /// @param authorizedOperator The address of the authorized operator function batchRegisterValidatorsUnsafe(bytes20[] calldata pubkeyHashes, uint32 maxCommittedGasLimit, address authorizedOperator); + /// @notice Get a validator by its BLS public key hash + /// @param pubkeyHash BLS public key hash of the validator + /// @return ValidatorInfo struct + function getValidatorByPubkeyHash(bytes20 pubkeyHash) public view returns (ValidatorInfo memory); + error KeyNotFound(); error InvalidQuery(); #[derive(Debug)] From 8efa439e76c3a75a75175703661bc9c7b80423ff Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Mon, 2 Dec 2024 17:22:06 +0100 Subject: [PATCH 2/4] feat(cli): get validator status fixes --- bolt-cli/src/cli.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bolt-cli/src/cli.rs b/bolt-cli/src/cli.rs index 84974cba..fbfbb3cc 100644 --- a/bolt-cli/src/cli.rs +++ b/bolt-cli/src/cli.rs @@ -175,11 +175,11 @@ pub enum ValidatorsSubcommand { rpc_url: Url, /// The path to the JSON pubkeys file, containing an array of BLS public keys. - #[clap(long, env = "PUBKEYS_PATH", conflicts_with = "PUBKEYS")] + #[clap(long, env = "PUBKEYS_PATH", conflicts_with = "pubkeys")] pubkeys_path: Option, /// The validator public key to check the status of. - #[clap(long, env = "PUBKEYS", conflicts_with = "PUBKEYS_PATH")] + #[clap(long, env = "PUBKEYS", conflicts_with = "pubkeys_path")] pubkeys: Vec>, }, } From a2e5acab461e82222ad4dc4c2c83d775f3f4d80e Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Mon, 2 Dec 2024 18:23:23 +0100 Subject: [PATCH 3/4] fix(cli): derives --- bolt-cli/src/contracts/bolt.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bolt-cli/src/contracts/bolt.rs b/bolt-cli/src/contracts/bolt.rs index 46ff0a08..b74f8f7a 100644 --- a/bolt-cli/src/contracts/bolt.rs +++ b/bolt-cli/src/contracts/bolt.rs @@ -1,9 +1,11 @@ use alloy::sol; +use serde::Serialize; sol! { #[allow(missing_docs)] #[sol(rpc)] interface BoltValidators { + #[derive(Debug, Serialize)] struct ValidatorInfo { bytes20 pubkeyHash; uint32 maxCommittedGasLimit; From 0b79bdc817193603a9d85766bccdec2b23f53913 Mon Sep 17 00:00:00 2001 From: Jonas Bostoen Date: Mon, 2 Dec 2024 18:26:58 +0100 Subject: [PATCH 4/4] test(cli): validators unit test --- bolt-cli/src/commands/validators.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/bolt-cli/src/commands/validators.rs b/bolt-cli/src/commands/validators.rs index 888f7254..7f786cfa 100644 --- a/bolt-cli/src/commands/validators.rs +++ b/bolt-cli/src/commands/validators.rs @@ -134,6 +134,8 @@ mod tests { #[tokio::test] async fn test_register_validators() { + let _ = tracing_subscriber::fmt::try_init(); + let rpc_url = "https://holesky.drpc.org"; let anvil = Anvil::default().fork(rpc_url).spawn(); let anvil_url = Url::parse(&anvil.endpoint()).expect("valid URL"); @@ -151,7 +153,17 @@ mod tests { admin_private_key: B256::try_from(secret_key.to_bytes().as_slice()).unwrap(), authorized_operator: account, pubkeys_path: "./test_data/pubkeys.json".parse().unwrap(), + rpc_url: anvil_url.clone(), + }, + }; + + command.run().await.expect("run command"); + + let command = ValidatorsCommand { + subcommand: ValidatorsSubcommand::Status { rpc_url: anvil_url, + pubkeys_path: Some("./test_data/pubkeys.json".parse().unwrap()), + pubkeys: Vec::new(), }, };