Skip to content

Commit

Permalink
feat: initial delegation + e2e test
Browse files Browse the repository at this point in the history
  • Loading branch information
merklefruit committed Oct 23, 2024
1 parent fd1120e commit abeb889
Show file tree
Hide file tree
Showing 11 changed files with 211 additions and 20 deletions.
63 changes: 63 additions & 0 deletions bolt-cli/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions bolt-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ dotenvy = "0.15.7"
eyre = "0.6.12"
thiserror = "1.0"
hex = "0.4.3"
tracing = "0.1.40"
tracing-subscriber = "0.3.18"

[dev-dependencies]
tempfile = "3.13.0"
Expand Down
46 changes: 45 additions & 1 deletion bolt-cli/src/delegation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ use ethereum_consensus::crypto::{
use eyre::Result;
use lighthouse_eth2_keystore::Keystore;
use serde::Serialize;
use tracing::debug;

use crate::{
cli::{Action, Chain},
utils::{
dirk::Dirk,
keystore::{keystore_paths, KeystoreError, KeystoreSecret},
signing::compute_commit_boost_signing_root,
signing::{compute_commit_boost_signing_root, compute_domain_from_mask},
},
};

Expand Down Expand Up @@ -99,6 +101,48 @@ pub fn generate_from_keystore(
Ok(signed_messages)
}

/// Generate signed delegations/revocations using a remote Dirk signer
pub async fn generate_from_dirk(
dirk: &mut Dirk,
delegatee_pubkey: BlsPublicKey,
account_paths: Vec<String>,
chain: Chain,
action: Action,
) -> Result<Vec<SignedMessage>> {
let mut signed_messages = Vec::new();

// first read the accounts from the remote keystore
let accounts = dirk.list_accounts(account_paths).await?;
debug!("Found {} remote accounts", accounts.len());

// specify the signing domain (needs to be included in the signing request)
let domain = compute_domain_from_mask(chain.fork_version());

for account in accounts {
// for each available pubkey we control, sign a delegation message
let pubkey = BlsPublicKey::try_from(account.public_key.as_slice())?;

match action {
Action::Delegate => {
let message = DelegationMessage::new(pubkey.clone(), delegatee_pubkey.clone());
let signing_root = compute_commit_boost_signing_root(message.digest(), &chain)?;
let signature = dirk.request_signature(pubkey, signing_root, domain.into()).await?;
let signed = SignedDelegation { message, signature };
signed_messages.push(SignedMessage::Delegation(signed));
}
Action::Revoke => {
let message = RevocationMessage::new(pubkey.clone(), delegatee_pubkey.clone());
let signing_root = compute_commit_boost_signing_root(message.digest(), &chain)?;
let signature = dirk.request_signature(pubkey, signing_root, domain.into()).await?;
let signed = SignedRevocation { message, signature };
signed_messages.push(SignedMessage::Revocation(signed));
}
}
}

Ok(signed_messages)
}

/// Event types that can be emitted by the validator pubkey to
/// signal some action on the Bolt protocol.
#[derive(Debug, Clone, Copy)]
Expand Down
20 changes: 17 additions & 3 deletions bolt-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ mod pubkeys;

/// Utility functions and helpers for the CLI.
mod utils;
use tracing::debug;
use utils::{dirk::Dirk, keystore::KeystoreSecret, parse_bls_public_key, write_to_file};

/// Protocol Buffers definitions generated by `prost`.
Expand All @@ -21,6 +22,8 @@ mod pb;
#[tokio::main]
async fn main() -> Result<()> {
let _ = dotenvy::dotenv();
let _ = tracing_subscriber::fmt::try_init();

let cli = Opts::parse();

// Init the default rustls provider for Dirk
Expand Down Expand Up @@ -55,9 +58,20 @@ async fn main() -> Result<()> {
println!("Signed delegation messages generated and saved to {}", out);
}
KeySource::Dirk { opts } => {
let _dirk = Dirk::connect(opts.url, opts.tls_credentials).await?;
let mut dirk = Dirk::connect(opts.url, opts.tls_credentials).await?;
let delegatee_pubkey = parse_bls_public_key(&delegatee_pubkey)?;
let signed_messages = delegation::generate_from_dirk(
&mut dirk,
delegatee_pubkey,
opts.accounts,
chain,
action,
)
.await?;
debug!("Signed {} messages with Dirk", signed_messages.len());

todo!("generate delegations from dirk");
write_to_file(&out, &signed_messages)?;
println!("Signed delegation messages generated and saved to {}", out);
}
},

Expand All @@ -76,7 +90,7 @@ async fn main() -> Result<()> {
println!("Pubkeys generated and saved to {}", out);
}
KeySource::Dirk { opts } => {
let dirk = Dirk::connect(opts.url, opts.tls_credentials).await?;
let mut dirk = Dirk::connect(opts.url, opts.tls_credentials).await?;
let accounts = dirk.list_accounts(opts.accounts).await?;
let pubkeys = pubkeys::list_from_dirk_accounts(&accounts)?;

Expand Down
74 changes: 58 additions & 16 deletions bolt-cli/src/utils/dirk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ use crate::{
/// Reference: https://github.com/attestantio/dirk
#[derive(Clone)]
pub struct Dirk {
conn: Channel,
lister: ListerClient<Channel>,
signer: SignerClient<Channel>,
}

impl Dirk {
Expand All @@ -27,33 +28,33 @@ impl Dirk {
let tls_config = compose_credentials(credentials)?;
let conn = Channel::builder(addr).tls_config(tls_config)?.connect().await?;

Ok(Self { conn })
let lister = ListerClient::new(conn.clone());
let signer = SignerClient::new(conn.clone());

Ok(Self { lister, signer })
}

/// List all accounts in the keystore.
pub async fn list_accounts(&self, paths: Vec<String>) -> Result<Vec<Account>> {
let mut lister = ListerClient::new(self.conn.clone());
let accs = lister.list_accounts(ListAccountsRequest { paths }).await?;
pub async fn list_accounts(&mut self, paths: Vec<String>) -> Result<Vec<Account>> {
let accs = self.lister.list_accounts(ListAccountsRequest { paths }).await?;

Ok(accs.into_inner().accounts)
}

/// Request a signature from the remote signer.
pub async fn request_signature(
&self,
&mut self,
pubkey: BlsPublicKey,
hash: B256,
domain: B256,
) -> Result<BlsSignature> {
let mut signer = SignerClient::new(self.conn.clone());

let req = SignRequest {
data: hash.to_vec(),
domain: domain.to_vec(),
id: Some(SignRequestId::PublicKey(pubkey.to_vec())),
};

let res = signer.sign(req).await?;
let res = self.signer.sign(req).await?;
let sig = res.into_inner().signature;
let sig = BlsSignature::try_from(sig.as_slice()).wrap_err("Failed to parse signature")?;

Expand Down Expand Up @@ -83,33 +84,74 @@ fn compose_credentials(creds: TlsCredentials) -> Result<ClientTlsConfig> {

#[cfg(test)]
mod tests {
use std::{process::Command, time::Duration};

use super::*;

/// Test connecting to a DIRK server
/// Test connecting to a DIRK server and listing available accounts.
///
/// This test should be run manually against a running DIRK server.
/// Eventually this could become part of the entire test setup but for now it's ignored.
/// ```shell
/// cargo test --package bolt-cli --bin bolt-cli -- utils::dirk::tests::test_dirk_connection_e2e
/// --exact --show-output --ignored
/// ```
#[tokio::test]
#[ignore]
async fn test_connect_to_dirk() -> eyre::Result<()> {
async fn test_dirk_connection_e2e() -> eyre::Result<()> {
// Init the default rustls provider
let _ = rustls::crypto::ring::default_provider().install_default();

let url = "https://localhost:9091".to_string();

let test_data_dir = env!("CARGO_MANIFEST_DIR").to_string() + "/test_data/dirk";

// Init the DIRK config file
init_dirk_config(test_data_dir.clone())?;

// Check if dirk is installed (in $PATH)
if Command::new("dirk")
.arg("--base-dir")
.arg(&test_data_dir)
.arg("--help")
.status()
.is_err()
{
eprintln!("DIRK is not installed in $PATH");
return Ok(());
}

// Start the DIRK server in the background
let mut dirk_proc = Command::new("dirk").arg("--base-dir").arg(&test_data_dir).spawn()?;

// Wait for some time for the server to start up
tokio::time::sleep(Duration::from_secs(3)).await;

let url = "https://localhost:9091".to_string();

let cred = TlsCredentials {
client_cert_path: test_data_dir.clone() + "/client1.crt",
client_key_path: test_data_dir.clone() + "/client1.key",
ca_cert_path: Some(test_data_dir.clone() + "/security/ca.crt"),
};

let dirk = Dirk::connect(url, cred).await?;
let mut dirk = Dirk::connect(url, cred).await?;

let accounts = dirk.list_accounts(vec!["wallet1".to_string()]).await?;
println!("Dirk Accounts: {:?}", accounts);

// make sure to stop the dirk server
dirk_proc.kill()?;

Ok(())
}

fn init_dirk_config(test_data_dir: String) -> eyre::Result<()> {
// read the template json file from test_data
let template_path = test_data_dir.clone() + "/dirk.template.json";
let template = fs::read_to_string(template_path).wrap_err("Failed to read template")?;

// change the occurrence of $PWD to the current working directory in the template
let new_file = test_data_dir.clone() + "/dirk.json";
let new_content = template.replace("$PWD", &test_data_dir);
fs::write(new_file, new_content).wrap_err("Failed to write dirk config file")?;

Ok(())
}
}
1 change: 1 addition & 0 deletions bolt-cli/test_data/dirk/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
dirk.json
23 changes: 23 additions & 0 deletions bolt-cli/test_data/dirk/dirk.template.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"server": {
"id": 212483780,
"name": "localhost",
"listen-address": "localhost:9091"
},
"certificates": {
"ca-cert": "file://$PWD/security/ca.crt",
"server-cert": "file://$PWD/security/localhost.crt",
"server-key": "file://$PWD/security/localhost.key"
},
"peers": {
"212483780": "localhost:9091"
},
"permissions": {
"client1": {
"wallet1": "All"
},
"localhost": {
"wallet1": "All"
}
}
}
Binary file added bolt-cli/test_data/dirk/storage/000000.vlog
Binary file not shown.
1 change: 1 addition & 0 deletions bolt-cli/test_data/dirk/storage/KEYREGISTRY
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
�����A��c���W�Hello Badger
1 change: 1 addition & 0 deletions bolt-cli/test_data/dirk/storage/LOCK
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
33375
Binary file added bolt-cli/test_data/dirk/storage/MANIFEST
Binary file not shown.

0 comments on commit abeb889

Please sign in to comment.