From 21b7b1d8950b4943b44e4e484642a1cf53b8d1dc Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Tue, 22 Oct 2024 13:28:54 +0200 Subject: [PATCH] feat: refactor delegation cli for modularity --- bolt-cli/Cargo.lock | 2 +- bolt-cli/src/config.rs | 4 +- bolt-cli/src/delegation.rs | 164 +++++++++++++++++ bolt-cli/src/main.rs | 174 ++----------------- bolt-cli/src/types.rs | 12 -- bolt-cli/src/{utils.rs => utils/keystore.rs} | 90 ++-------- bolt-cli/src/utils/mod.rs | 15 ++ bolt-cli/src/utils/signing.rs | 65 +++++++ bolt-cli/test_data/README.md | 2 +- 9 files changed, 276 insertions(+), 252 deletions(-) create mode 100644 bolt-cli/src/delegation.rs rename bolt-cli/src/{utils.rs => utils/keystore.rs} (55%) create mode 100644 bolt-cli/src/utils/mod.rs create mode 100644 bolt-cli/src/utils/signing.rs diff --git a/bolt-cli/Cargo.lock b/bolt-cli/Cargo.lock index 31990b74e..502dd91db 100644 --- a/bolt-cli/Cargo.lock +++ b/bolt-cli/Cargo.lock @@ -1177,7 +1177,7 @@ dependencies = [ ] [[package]] -name = "bolt-delegations-cli" +name = "bolt-cli" version = "0.1.0" dependencies = [ "account_utils", diff --git a/bolt-cli/src/config.rs b/bolt-cli/src/config.rs index 5b2294fa1..7609d51d5 100644 --- a/bolt-cli/src/config.rs +++ b/bolt-cli/src/config.rs @@ -1,7 +1,7 @@ use clap::{Parser, Subcommand, ValueEnum}; use serde::Deserialize; -use crate::utils::KEYSTORE_PASSWORD; +use crate::utils::keystore::DEFAULT_KEYSTORE_PASSWORD; /// A CLI tool to generate signed delegation messages for BLS keys. #[derive(Parser, Debug, Clone, Deserialize)] @@ -70,7 +70,7 @@ pub enum KeySource { long, env = "KEYSTORE_PASSWORD", hide_env_values = true, - default_value = KEYSTORE_PASSWORD, + default_value = DEFAULT_KEYSTORE_PASSWORD, conflicts_with = "password_path" )] password: Option, diff --git a/bolt-cli/src/delegation.rs b/bolt-cli/src/delegation.rs new file mode 100644 index 000000000..a3e31dc1d --- /dev/null +++ b/bolt-cli/src/delegation.rs @@ -0,0 +1,164 @@ +use ethereum_consensus::crypto::{ + PublicKey as BlsPublicKey, SecretKey as BlsSecretKey, Signature as BlsSignature, +}; +use eyre::Result; +use lighthouse_eth2_keystore::Keystore; + +use crate::{ + config::{Action, Chain}, + types::{ + DelegationMessage, RevocationMessage, SignedDelegation, SignedMessage, SignedRevocation, + }, + utils::{ + keystore::{keystore_paths, KeystoreError, KeystoreSecret}, + signing::compute_commit_boost_signing_root, + }, +}; + +/// Generate signed delegations/revocations using local BLS private keys +/// +/// - Use the provided private keys from either CLI or env variable +/// - Create message +/// - Compute the signing roots and sign the messages +/// - Return the signed messages +pub fn generate_from_local_keys( + secret_keys: &Vec, + delegatee_pubkey: BlsPublicKey, + chain: &Chain, + action: Action, +) -> Result> { + let mut signed_messages = Vec::with_capacity(secret_keys.len()); + + for sk in secret_keys { + let sk = BlsSecretKey::try_from(sk.trim().to_string())?; + + match action { + Action::Delegate => { + let message = DelegationMessage::new(sk.public_key(), delegatee_pubkey.clone()); + let signing_root = compute_commit_boost_signing_root(message.digest(), chain)?; + let signature = sk.sign(signing_root.0.as_ref()); + let signed = SignedDelegation { message, signature }; + signed_messages.push(SignedMessage::Delegation(signed)) + } + Action::Revoke => { + let message = RevocationMessage::new(sk.public_key(), delegatee_pubkey.clone()); + let signing_root = compute_commit_boost_signing_root(message.digest(), chain)?; + let signature = sk.sign(signing_root.0.as_ref()); + let signed = SignedRevocation { message, signature }; + signed_messages.push(SignedMessage::Revocation(signed)); + } + } + } + + Ok(signed_messages) +} + +/// Generate signed delegations/revocations using a keystore file +/// +/// - Read the keystore file +/// - Decrypt the keypairs using the password +/// - Create messages +/// - Compute the signing roots and sign the message +/// - Return the signed message +pub fn generate_from_keystore( + keys_path: &str, + keystore_secret: KeystoreSecret, + delegatee_pubkey: BlsPublicKey, + chain: Chain, + action: Action, +) -> Result> { + let keystores_paths = keystore_paths(keys_path)?; + let mut signed_messages = Vec::with_capacity(keystores_paths.len()); + + for path in keystores_paths { + let ks = Keystore::from_json_file(path).map_err(KeystoreError::Eth2Keystore)?; + let password = keystore_secret.get(ks.pubkey()).ok_or(KeystoreError::MissingPassword)?; + let kp = ks.decrypt_keypair(password.as_bytes()).map_err(KeystoreError::Eth2Keystore)?; + let validator_pubkey = BlsPublicKey::try_from(kp.pk.serialize().to_vec().as_ref())?; + let validator_private_key = kp.sk; + + match action { + Action::Delegate => { + let message = DelegationMessage::new(validator_pubkey, delegatee_pubkey.clone()); + let signing_root = compute_commit_boost_signing_root(message.digest(), &chain)?; + let signature = validator_private_key.sign(signing_root.0.into()); + let signature = BlsSignature::try_from(signature.serialize().as_ref())?; + let signed = SignedDelegation { message, signature }; + signed_messages.push(SignedMessage::Delegation(signed)); + } + Action::Revoke => { + let message = RevocationMessage::new(validator_pubkey, delegatee_pubkey.clone()); + let signing_root = compute_commit_boost_signing_root(message.digest(), &chain)?; + let signature = validator_private_key.sign(signing_root.0.into()); + let signature = BlsSignature::try_from(signature.serialize().as_ref())?; + let signed = SignedRevocation { message, signature }; + signed_messages.push(SignedMessage::Revocation(signed)); + } + } + } + + Ok(signed_messages) +} + +#[cfg(test)] +mod tests { + use ethereum_consensus::crypto::PublicKey as BlsPublicKey; + + use crate::{ + config::{Action, Chain}, + types::SignedMessage, + utils::{keystore::KeystoreSecret, parse_public_key, signing::verify_commit_boost_root}, + }; + + use super::generate_from_keystore; + + #[test] + fn test_delegation_keystore_signer_lighthouse() -> eyre::Result<()> { + // Read the keystore from test_data + let keys_path = env!("CARGO_MANIFEST_DIR").to_string() + "/test_data/lighthouse/validators"; + let secrets_path = env!("CARGO_MANIFEST_DIR").to_string() + "/test_data/lighthouse/secrets"; + + let keystore_secret = KeystoreSecret::from_directory(secrets_path)?; + + let delegatee_pubkey = "0x83eeddfac5e60f8fe607ee8713efb8877c295ad9f8ca075f4d8f6f2ae241a30dd57f78f6f3863a9fe0d5b5db9d550b93"; + let delegatee_pubkey = parse_public_key(delegatee_pubkey)?; + let chain = Chain::Mainnet; + + let signed_delegations = generate_from_keystore( + &keys_path, + keystore_secret, + delegatee_pubkey.clone(), + chain, + Action::Delegate, + )?; + + let signed_message = signed_delegations.first().expect("to get signed delegation"); + + verify_delegation_signature(signed_message, delegatee_pubkey, chain); + + Ok(()) + } + + fn verify_delegation_signature( + message: &SignedMessage, + delegatee_pubkey: BlsPublicKey, + chain: Chain, + ) { + match message { + SignedMessage::Delegation(signed_delegation) => { + let output_delegatee_pubkey = signed_delegation.message.delegatee_pubkey.clone(); + let signer_pubkey = signed_delegation.message.validator_pubkey.clone(); + let digest = signed_delegation.message.digest(); + assert_eq!(output_delegatee_pubkey, delegatee_pubkey); + + let blst_sig = + blst::min_pk::Signature::from_bytes(signed_delegation.signature.as_ref()) + .expect("Failed to convert delegation signature"); + + // Verify the signature + assert!(verify_commit_boost_root(signer_pubkey, digest, &blst_sig, &chain).is_ok()); + } + _ => panic!("Expected a delegation message"), + } + } +} diff --git a/bolt-cli/src/main.rs b/bolt-cli/src/main.rs index 572462201..49eff94b4 100644 --- a/bolt-cli/src/main.rs +++ b/bolt-cli/src/main.rs @@ -1,24 +1,19 @@ use std::{fs, path::PathBuf}; use clap::Parser; -use ethereum_consensus::crypto::{ - PublicKey as BlsPublicKey, SecretKey as BlsSecretKey, Signature as BlsSignature, -}; use eyre::{bail, Result}; -use lighthouse_eth2_keystore::Keystore; use serde::Serialize; -pub mod config; -use config::{Action, Chain, Commands, KeySource, Opts}; +mod config; +use config::{Commands, KeySource, Opts}; -pub mod types; -use types::{ - DelegationMessage, KeystoreError, RevocationMessage, SignedDelegation, SignedMessage, - SignedRevocation, -}; +mod delegation; +use delegation::{generate_from_keystore, generate_from_local_keys}; +use utils::{keystore::KeystoreSecret, parse_public_key}; -pub mod utils; -use utils::{compute_commit_boost_signing_root, keystore_paths, parse_public_key, KeystoreSecret}; +mod types; + +mod utils; fn main() -> Result<()> { let _ = dotenvy::dotenv(); @@ -54,157 +49,10 @@ fn main() -> Result<()> { Ok(()) } -/// Generate signed delegations/revocations using local BLS private keys -/// -/// - Use the provided private keys from either CLI or env variable -/// - Create message -/// - Compute the signing roots and sign the messages -/// - Return the signed messages -fn generate_from_local_keys( - secret_keys: &Vec, - delegatee_pubkey: BlsPublicKey, - chain: &Chain, - action: Action, -) -> Result> { - let mut signed_messages = Vec::with_capacity(secret_keys.len()); - - for sk in secret_keys { - let sk = BlsSecretKey::try_from(sk.trim().to_string())?; - - match action { - Action::Delegate => { - let message = DelegationMessage::new(sk.public_key(), delegatee_pubkey.clone()); - let signing_root = compute_commit_boost_signing_root(message.digest(), chain)?; - let signature = sk.sign(signing_root.0.as_ref()); - let signed = SignedDelegation { message, signature }; - signed_messages.push(SignedMessage::Delegation(signed)) - } - Action::Revoke => { - let message = RevocationMessage::new(sk.public_key(), delegatee_pubkey.clone()); - let signing_root = compute_commit_boost_signing_root(message.digest(), chain)?; - let signature = sk.sign(signing_root.0.as_ref()); - let signed = SignedRevocation { message, signature }; - signed_messages.push(SignedMessage::Revocation(signed)); - } - } - } - - Ok(signed_messages) -} - -/// Generate signed delegations/revocations using a keystore file -/// -/// - Read the keystore file -/// - Decrypt the keypairs using the password -/// - Create messages -/// - Compute the signing roots and sign the message -/// - Return the signed message -fn generate_from_keystore( - keys_path: &str, - keystore_secret: KeystoreSecret, - delegatee_pubkey: BlsPublicKey, - chain: Chain, - action: Action, -) -> Result> { - let keystores_paths = keystore_paths(keys_path)?; - let mut signed_messages = Vec::with_capacity(keystores_paths.len()); - - for path in keystores_paths { - let ks = Keystore::from_json_file(path).map_err(KeystoreError::Eth2Keystore)?; - let password = keystore_secret.get(ks.pubkey()).ok_or(KeystoreError::MissingPassword)?; - let kp = ks.decrypt_keypair(password.as_bytes()).map_err(KeystoreError::Eth2Keystore)?; - let validator_pubkey = BlsPublicKey::try_from(kp.pk.serialize().to_vec().as_ref())?; - let validator_private_key = kp.sk; - - match action { - Action::Delegate => { - let message = DelegationMessage::new(validator_pubkey, delegatee_pubkey.clone()); - let signing_root = compute_commit_boost_signing_root(message.digest(), &chain)?; - let signature = validator_private_key.sign(signing_root.0.into()); - let signature = BlsSignature::try_from(signature.serialize().as_ref())?; - let signed = SignedDelegation { message, signature }; - signed_messages.push(SignedMessage::Delegation(signed)); - } - Action::Revoke => { - let message = RevocationMessage::new(validator_pubkey, delegatee_pubkey.clone()); - let signing_root = compute_commit_boost_signing_root(message.digest(), &chain)?; - let signature = validator_private_key.sign(signing_root.0.into()); - let signature = BlsSignature::try_from(signature.serialize().as_ref())?; - let signed = SignedRevocation { message, signature }; - signed_messages.push(SignedMessage::Revocation(signed)); - } - } - } - - Ok(signed_messages) -} - -/// Write the signed delegation to an output json file -fn write_to_file(out: &str, messages: &Vec) -> Result<()> { +/// Write some serializable data to an output json file +fn write_to_file(out: &str, data: &T) -> Result<()> { let out_path = PathBuf::from(out); let out_file = fs::File::create(out_path)?; - serde_json::to_writer_pretty(out_file, &messages)?; + serde_json::to_writer_pretty(out_file, data)?; Ok(()) } - -#[cfg(test)] -mod tests { - use ethereum_consensus::crypto::PublicKey as BlsPublicKey; - - use crate::{ - config::{Action, Chain}, - generate_from_keystore, - types::SignedMessage, - utils::{parse_public_key, verify_commit_boost_root, KeystoreSecret}, - }; - - #[test] - fn test_delegation_keystore_signer_lighthouse() -> eyre::Result<()> { - // Read the keystore from test_data - let keys_path = env!("CARGO_MANIFEST_DIR").to_string() + "/test_data/lighthouse/validators"; - let secrets_path = env!("CARGO_MANIFEST_DIR").to_string() + "/test_data/lighthouse/secrets"; - - let keystore_secret = KeystoreSecret::from_directory(secrets_path)?; - - let delegatee_pubkey = "0x83eeddfac5e60f8fe607ee8713efb8877c295ad9f8ca075f4d8f6f2ae241a30dd57f78f6f3863a9fe0d5b5db9d550b93"; - let delegatee_pubkey = parse_public_key(delegatee_pubkey)?; - let chain = Chain::Mainnet; - - let signed_delegations = generate_from_keystore( - &keys_path, - keystore_secret, - delegatee_pubkey.clone(), - chain, - Action::Delegate, - )?; - - let signed_message = signed_delegations.first().expect("to get signed delegation"); - - verify_delegation_signature(signed_message, delegatee_pubkey, chain); - - Ok(()) - } - - fn verify_delegation_signature( - message: &SignedMessage, - delegatee_pubkey: BlsPublicKey, - chain: Chain, - ) { - match message { - SignedMessage::Delegation(signed_delegation) => { - let output_delegatee_pubkey = signed_delegation.message.delegatee_pubkey.clone(); - let signer_pubkey = signed_delegation.message.validator_pubkey.clone(); - let digest = signed_delegation.message.digest(); - assert_eq!(output_delegatee_pubkey, delegatee_pubkey); - - let blst_sig = - blst::min_pk::Signature::from_bytes(signed_delegation.signature.as_ref()) - .expect("Failed to convert delegation signature"); - - // Verify the signature - assert!(verify_commit_boost_root(signer_pubkey, digest, &blst_sig, &chain).is_ok()); - } - _ => panic!("Expected a delegation message"), - } - } -} diff --git a/bolt-cli/src/types.rs b/bolt-cli/src/types.rs index 9fa5b917e..3e47fa978 100644 --- a/bolt-cli/src/types.rs +++ b/bolt-cli/src/types.rs @@ -2,18 +2,6 @@ use alloy::signers::k256::sha2::{Digest, Sha256}; use ethereum_consensus::crypto::{PublicKey as BlsPublicKey, Signature as BlsSignature}; use serde::Serialize; -#[derive(Debug, thiserror::Error)] -pub enum KeystoreError { - #[error("failed to read keystore directory: {0}")] - ReadFromDirectory(#[from] std::io::Error), - #[error("Failed to read or decrypt keystore: {0:?}")] - Eth2Keystore(lighthouse_eth2_keystore::Error), - #[error("Failed to get public key from keypair: {0}")] - UnknownPublicKey(String), - #[error("Missing password for keypair")] - MissingPassword, -} - /// Event types that can be emitted by the validator pubkey to /// signal some action on the Bolt protocol. #[derive(Debug, Clone, Copy)] diff --git a/bolt-cli/src/utils.rs b/bolt-cli/src/utils/keystore.rs similarity index 55% rename from bolt-cli/src/utils.rs rename to bolt-cli/src/utils/keystore.rs index d38c18bf9..02f35cc96 100644 --- a/bolt-cli/src/utils.rs +++ b/bolt-cli/src/utils/keystore.rs @@ -1,28 +1,27 @@ use std::{ collections::HashMap, ffi::OsString, - fs::{self, read_dir, DirEntry}, + fs::{self, DirEntry}, io, path::{Path, PathBuf}, }; -use alloy::primitives::FixedBytes; -use blst::{min_pk::Signature, BLST_ERROR}; -use ethereum_consensus::{ - crypto::PublicKey as BlsPublicKey, - deneb::{compute_fork_data_root, compute_signing_root, Root}, -}; use eyre::{Context, Result}; -use crate::{config::Chain, types::KeystoreError}; - -// Reference: https://eips.ethereum.org/EIPS/eip-2335#test-cases -pub const KEYSTORE_PASSWORD: &str = r#"𝔱𝔢𝔰𝔱𝔭𝔞𝔰𝔰𝔴𝔬𝔯𝔡🔑"#; - -pub const COMMIT_BOOST_DOMAIN_MASK: [u8; 4] = [109, 109, 111, 67]; - -/// The BLS Domain Separator used in Ethereum 2.0. -pub const BLS_DST_PREFIX: &[u8] = b"BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_"; +/// Default password used for keystores in the test vectors. +/// +/// Reference: https://eips.ethereum.org/EIPS/eip-2335#test-cases +pub const DEFAULT_KEYSTORE_PASSWORD: &str = r#"𝔱𝔢𝔰𝔱𝔭𝔞𝔰𝔰𝔴𝔬𝔯𝔡🔑"#; + +#[derive(Debug, thiserror::Error)] +pub enum KeystoreError { + #[error("failed to read keystore directory: {0}")] + ReadFromDirectory(#[from] std::io::Error), + #[error("Failed to read or decrypt keystore: {0:?}")] + Eth2Keystore(lighthouse_eth2_keystore::Error), + #[error("Missing password for keypair")] + MissingPassword, +} pub enum KeystoreSecret { /// When using a unique password for all validators in the keystore @@ -85,13 +84,6 @@ impl Drop for KeystoreSecret { } } -/// Parse the delegated public key from a string -pub fn parse_public_key(delegatee_pubkey: &str) -> Result { - let hex_pk = delegatee_pubkey.strip_prefix("0x").unwrap_or(delegatee_pubkey); - BlsPublicKey::try_from(hex::decode(hex_pk).expect("Failed to decode pubkey").as_slice()) - .map_err(|e| eyre::eyre!("Failed to parse public key '{}': {}", hex_pk, e)) -} - /// Returns the paths of all the keystore files provided in `keys_path`. /// /// We're expecting a directory structure like: @@ -125,54 +117,6 @@ fn read_path(entry: io::Result) -> Result { Ok(entry.map_err(KeystoreError::ReadFromDirectory)?.path()) } -/// Helper function to compute the signing root for a message -pub fn compute_commit_boost_signing_root( - message: [u8; 32], - chain: &Chain, -) -> Result> { - compute_signing_root(&message, compute_domain_from_mask(chain.fork_version())) - .map_err(|e| eyre::eyre!("Failed to compute signing root: {}", e)) -} - -/// Compute the commit boost domain from the fork version -pub fn compute_domain_from_mask(fork_version: [u8; 4]) -> [u8; 32] { - let mut domain = [0; 32]; - - // Note: the application builder domain specs require the genesis_validators_root - // to be 0x00 for any out-of-protocol message. The commit-boost domain follows the - // same rule. - let root = Root::default(); - let fork_data_root = compute_fork_data_root(fork_version, root).expect("valid fork data"); - - domain[..4].copy_from_slice(&COMMIT_BOOST_DOMAIN_MASK); - domain[4..].copy_from_slice(&fork_data_root[..28]); - domain -} - -/// Verify the signature with the public key of the signer using the Commit Boost domain. -pub fn verify_commit_boost_root( - pubkey: BlsPublicKey, - root: [u8; 32], - signature: &Signature, - chain: &Chain, -) -> Result<()> { - verify_root(pubkey, root, signature, compute_domain_from_mask(chain.fork_version())) -} - -/// Verify the signature of the object with the given public key. -pub fn verify_root( - pubkey: BlsPublicKey, - root: [u8; 32], - signature: &Signature, - domain: [u8; 32], -) -> Result<()> { - let signing_root = compute_signing_root(&root, domain)?; - let pk = blst::min_pk::PublicKey::from_bytes(pubkey.as_ref()).unwrap(); - - let res = signature.verify(true, signing_root.as_ref(), BLS_DST_PREFIX, &[], &pk, true); - if res == BLST_ERROR::BLST_SUCCESS { - Ok(()) - } else { - Err(eyre::eyre!("bls verification failed")) - } +fn read_dir(path: PathBuf) -> Result { + fs::read_dir(path).wrap_err("Failed to read directory") } diff --git a/bolt-cli/src/utils/mod.rs b/bolt-cli/src/utils/mod.rs new file mode 100644 index 000000000..6fa96f1d2 --- /dev/null +++ b/bolt-cli/src/utils/mod.rs @@ -0,0 +1,15 @@ +use ethereum_consensus::crypto::PublicKey as BlsPublicKey; +use eyre::{Context, Result}; + +/// Utilities and types for EIP-2335 keystore files. +pub mod keystore; + +/// Utilities for signing and verifying messages. +pub mod signing; + +/// Parse a BLS public key from a string +pub fn parse_public_key(delegatee_pubkey: &str) -> Result { + let hex_pk = delegatee_pubkey.strip_prefix("0x").unwrap_or(delegatee_pubkey); + BlsPublicKey::try_from(hex::decode(hex_pk).wrap_err("Failed to hex-decode pubkey")?.as_slice()) + .map_err(|e| eyre::eyre!("Failed to parse public key '{}': {}", hex_pk, e)) +} diff --git a/bolt-cli/src/utils/signing.rs b/bolt-cli/src/utils/signing.rs new file mode 100644 index 000000000..80e898c37 --- /dev/null +++ b/bolt-cli/src/utils/signing.rs @@ -0,0 +1,65 @@ +use alloy::primitives::B256; +use blst::{min_pk::Signature, BLST_ERROR}; +use ethereum_consensus::{ + crypto::PublicKey as BlsPublicKey, + deneb::{compute_fork_data_root, compute_signing_root, Root}, +}; +use eyre::Result; + +use crate::config::Chain; + +/// The domain mask for the Commit Boost domain. +pub const COMMIT_BOOST_DOMAIN_MASK: [u8; 4] = [109, 109, 111, 67]; + +/// The BLS Domain Separator used in Ethereum 2.0. +pub const BLS_DST_PREFIX: &[u8] = b"BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_"; + +/// Helper function to compute the signing root for a message +pub fn compute_commit_boost_signing_root(message: [u8; 32], chain: &Chain) -> Result { + compute_signing_root(&message, compute_domain_from_mask(chain.fork_version())) + .map_err(|e| eyre::eyre!("Failed to compute signing root: {}", e)) +} + +/// Compute the commit boost domain from the fork version +pub fn compute_domain_from_mask(fork_version: [u8; 4]) -> [u8; 32] { + let mut domain = [0; 32]; + + // Note: the application builder domain specs require the genesis_validators_root + // to be 0x00 for any out-of-protocol message. The commit-boost domain follows the + // same rule. + let root = Root::default(); + let fork_data_root = compute_fork_data_root(fork_version, root).expect("valid fork data"); + + domain[..4].copy_from_slice(&COMMIT_BOOST_DOMAIN_MASK); + domain[4..].copy_from_slice(&fork_data_root[..28]); + domain +} + +/// Verify the signature with the public key of the signer using the Commit Boost domain. +#[allow(dead_code)] +pub fn verify_commit_boost_root( + pubkey: BlsPublicKey, + root: [u8; 32], + signature: &Signature, + chain: &Chain, +) -> Result<()> { + verify_root(pubkey, root, signature, compute_domain_from_mask(chain.fork_version())) +} + +/// Verify the signature of the object with the given public key. +pub fn verify_root( + pubkey: BlsPublicKey, + root: [u8; 32], + signature: &Signature, + domain: [u8; 32], +) -> Result<()> { + let signing_root = compute_signing_root(&root, domain)?; + let pk = blst::min_pk::PublicKey::from_bytes(pubkey.as_ref()).unwrap(); + + let res = signature.verify(true, signing_root.as_ref(), BLS_DST_PREFIX, &[], &pk, true); + if res == BLST_ERROR::BLST_SUCCESS { + Ok(()) + } else { + Err(eyre::eyre!("bls verification failed")) + } +} diff --git a/bolt-cli/test_data/README.md b/bolt-cli/test_data/README.md index 7aa1be7fe..fc94ba753 100644 --- a/bolt-cli/test_data/README.md +++ b/bolt-cli/test_data/README.md @@ -1,4 +1,4 @@ -# test data for the delegation cli tool +# test data for the bolt cli tool - `lighthouse`: A lighthouse-format keystore according to the [specs][lh-specs]. It contains two directories: `validators` for the voting-keystores, and `secrets` for the passwords