use std::path::{Path, PathBuf};
use alloy_primitives::{Address, B256, Bytes};
use alloy_signer_local::PrivateKeySigner;
use blueprint_crypto::BytesEncoding;
use blueprint_crypto::k256::{K256Ecdsa, K256SigningKey};
use blueprint_keystore::backends::Backend;
use blueprint_keystore::{Keystore, KeystoreConfig};
use color_eyre::eyre::{Context, Result, eyre};
pub const KEYSTORE_PATH_ENV: &str = "BLUEPRINT_KEYSTORE_URI";
#[derive(Debug, Clone)]
pub struct EvmSigner {
pub operator_address: Address,
pub signer: PrivateKeySigner,
pub public_key: Bytes,
}
impl EvmSigner {
fn from_signing_key(key: &K256SigningKey) -> Result<Self> {
let secret = key.to_bytes();
let signer = PrivateKeySigner::from_bytes(&B256::from_slice(&secret))
.map_err(|e| eyre!("failed to create signer: {e}"))?;
let operator_address = signer.address();
let public_key = Bytes::copy_from_slice(&key.public().to_bytes());
Ok(Self {
operator_address,
signer,
public_key,
})
}
}
#[derive(Debug, Clone)]
pub struct RegistrationPayload {
pub signer: EvmSigner,
pub rpc_endpoint: String,
pub registration_inputs: Bytes,
}
pub fn keystore_path_from_env() -> Result<PathBuf> {
std::env::var(KEYSTORE_PATH_ENV)
.wrap_err_with(|| format!("{KEYSTORE_PATH_ENV} is not set – point it at your keystore"))
.map(PathBuf::from)
}
pub fn load_keystore(path: impl AsRef<Path>) -> Result<Keystore> {
let config = KeystoreConfig::new().fs_root(path);
Keystore::new(config).context("failed to open keystore")
}
pub fn load_ecdsa_signing_key(keystore: &Keystore) -> Result<K256SigningKey> {
let public = keystore
.first_local::<K256Ecdsa>()
.context("keystore does not contain an ECDSA key")?;
keystore
.get_secret::<K256Ecdsa>(&public)
.context("failed to load ECDSA secret")
}
pub fn load_evm_signer(keystore_path: impl AsRef<Path>) -> Result<EvmSigner> {
let keystore = load_keystore(keystore_path)?;
let signing_key = load_ecdsa_signing_key(&keystore)?;
EvmSigner::from_signing_key(&signing_key)
}
pub fn build_registration_payload(
keystore_path: impl AsRef<Path>,
rpc_endpoint: impl Into<String>,
registration_inputs: Option<Vec<u8>>,
) -> Result<RegistrationPayload> {
let keystore = load_keystore(keystore_path)?;
let signing_key = load_ecdsa_signing_key(&keystore)?;
let signer = EvmSigner::from_signing_key(&signing_key)?;
Ok(RegistrationPayload {
signer,
rpc_endpoint: rpc_endpoint.into(),
registration_inputs: Bytes::copy_from_slice(®istration_inputs.unwrap_or_default()),
})
}