Skip to content

Commit

Permalink
Feature/guardian exits (#42)
Browse files Browse the repository at this point in the history
* add sign_voluntary_exit_message()

* add sign_exit() handler to guardian bin and client
  • Loading branch information
JasonVranek authored Oct 31, 2023
1 parent 62a391a commit d6253bf
Show file tree
Hide file tree
Showing 9 changed files with 199 additions and 28 deletions.
6 changes: 3 additions & 3 deletions conf/network_config.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"network_name": "goerli",
"network_name": "ephemery",
"deposit_cli_version": "2.3.0",
"fork_info": {
"fork": {
"previous_version": "0x00001020",
"current_version": "0x00001020",
"previous_version": "0x1000101a",
"current_version": "0x1000101b",
"epoch": "0"
},
"genesis_validators_root": "0x270d43e74ce340de4bca2b1936beca0f4f5408d9e78aec4850920baf659d5b69"
Expand Down
19 changes: 14 additions & 5 deletions src/bin/guardian.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,23 +29,32 @@ async fn main() {
"/health",
axum::routing::get(puffersecuresigner::enclave::shared::handlers::health::handler),
)
// Endpoint generated a new ETH key
.route(
"/eth/v1/keygen",
axum::routing::post(
puffersecuresigner::enclave::guardian::handlers::attest_fresh_eth_key_with_blockhash::handler,
),
)
// Endpoint to list the pks of all the generated ETH keys
.route(
"/eth/v1/validate-custody",
"/eth/v1/keygen",
axum::routing::get(
puffersecuresigner::enclave::shared::handlers::list_eth_keys::handler,
),
)
// Endpoint to validate and receive BLS keyshare custody
.route(
"/guardian/v1/validate-custody",
axum::routing::post(
puffersecuresigner::enclave::guardian::handlers::validate_custody::handler,
),
)
// Endpoint to list the pks of all the generated ETH keys
// Endpoint to sign a VoluntaryExitMessage
.route(
"/eth/v1/keygen/secp256k1",
axum::routing::get(
puffersecuresigner::enclave::shared::handlers::list_eth_keys::handler,
"/guardian/v1/sign-exit",
axum::routing::post(
puffersecuresigner::enclave::guardian::handlers::sign_exit::handler,
),
)
;
Expand Down
27 changes: 26 additions & 1 deletion src/client/guardian.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,38 @@ impl GuardianClient {
.await?)
}


pub async fn list_eth_keys(&self) -> anyhow::Result<crate::enclave::types::ListKeysResponse> {
Ok(self
.client
.get(format!("{}/eth/v1/keygen", self.url))
.send()
.await?
.json::<crate::enclave::types::ListKeysResponse>()
.await?)
}

pub async fn validate_custody(
&self,
request: crate::enclave::types::ValidateCustodyRequest,
) -> anyhow::Result<crate::enclave::types::ValidateCustodyResponse> {
Ok(dbg!(
self.client
.post(format!("{}/eth/v1/validate-custody", self.url))
.post(format!("{}/guardian/v1/validate-custody", self.url))
.json(&request)
.send()
.await?
)
.json()
.await?)
}

pub async fn sign_exit(&self,
request: crate::enclave::types::SignExitRequest
) -> anyhow::Result<crate::enclave::types::SignExitResponse> {
Ok(dbg!(
self.client
.post(format!("{}/guardian/v1/sign-exit", self.url))
.json(&request)
.send()
.await?
Expand Down
4 changes: 2 additions & 2 deletions src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,13 @@ impl ClientBuilder {
},
guardian: GuardianClient {
url: self
.secure_signer_url
.guardian_url
.unwrap_or(default_client_guardian_url()),
client: client.clone(),
},
secure_signer: SecureSignerClient {
url: self
.guardian_url
.secure_signer_url
.unwrap_or(default_client_secure_signer_url()),
client: client.clone(),
},
Expand Down
30 changes: 28 additions & 2 deletions src/client/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ mod validator;

fn build_client() -> super::Client {
let builder = super::ClientBuilder::new();
builder.build()
builder.validator_url("http://localhost:3031".to_string())
.guardian_url("http://localhost:3032".to_string())
.build()
}

#[tokio::test]
Expand All @@ -27,6 +29,15 @@ async fn registration_flow_succeeds() {

dbg!(&resp1);

// Guardian's keys increase
let r: crate::enclave::types::ListKeysResponse = client
.guardian
.list_eth_keys()
.await
.unwrap();
assert!(dbg!(r.data).len() > 0);


// Assume guardian called rotateGuardianKey()

// Assume fetched from on-chain by validator:
Expand All @@ -50,7 +61,7 @@ async fn registration_flow_succeeds() {

// Assume validator is enqueued on-chain
let req = crate::enclave::types::ValidateCustodyRequest {
keygen_payload: resp2,
keygen_payload: resp2.clone(),
guardian_enclave_public_key: guardian_pk,
mrenclave,
mrsigner,
Expand All @@ -63,5 +74,20 @@ async fn registration_flow_succeeds() {

dbg!(&resp3);

// Guardian signs VEMs
let req = crate::enclave::types::SignExitRequest {
bls_pub_key_set: resp2.bls_pub_key_set.clone(),
guardian_index: 0,
validator_index: 0,
fork_info: crate::eth2::eth_types::ForkInfo::default(),
};
let resp4: crate::enclave::types::SignExitResponse = client
.guardian
.sign_exit(req)
.await
.unwrap();

dbg!(resp4);

assert!(false);
}
1 change: 1 addition & 0 deletions src/enclave/guardian/handlers/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub mod attest_fresh_eth_key_with_blockhash;
pub mod validate_custody;
pub mod sign_exit;
23 changes: 23 additions & 0 deletions src/enclave/guardian/handlers/sign_exit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use axum::response::IntoResponse;
use axum::Json;
use log::{error, info};

pub async fn handler(
Json(request): Json<crate::enclave::types::SignExitRequest>,
) -> axum::response::Response {
info!("sign_exit()");
match crate::enclave::guardian::sign_voluntary_exit_message(request) {
Ok(resp) => {
(axum::http::status::StatusCode::OK, Json(resp)).into_response()
}

Err(e) => {
error!("{:?}", e);
(
axum::http::status::StatusCode::INTERNAL_SERVER_ERROR,
format!("{}", e),
)
.into_response()
}
}
}
92 changes: 84 additions & 8 deletions src/enclave/guardian/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ use ethers::signers::{LocalWallet, Signer};
use sha3::Digest;
pub mod handlers;
use anyhow::{anyhow, bail, Result};
use blsttc::PublicKeySet;
use blsttc::{PublicKeySet, SecretKeyShare};
use libsecp256k1::SecretKey as EthSecretKey;
use ssz::Encode;

use crate::eth2::eth_types::BLSSignature;

#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct KeygenWithBlockhashRequest {
Expand All @@ -16,6 +19,7 @@ pub fn attest_new_eth_key_with_blockhash(
crate::io::remote_attestation::AttestationEvidence,
ecies::PublicKey,
)> {
dbg!("attest_new_eth_key_with_blockhash()");
// Generate a fresh SECP256K1 ETH keypair (saving ETH private key)
let pk = crate::crypto::eth_keys::eth_key_gen()?;
let blockhash: String = crate::strip_0x_prefix!(blockhash);
Expand Down Expand Up @@ -236,11 +240,50 @@ async fn approve_custody(
Ok(sig_str)
}

pub fn sign_voluntary_exit_message(
req: crate::enclave::types::SignExitRequest,
) -> Result<crate::enclave::types::SignExitResponse> {

// Read the validator's secret key share
let pk_hex = hex::encode(req.public_key_set()?.public_key_share(req.guardian_index).to_bytes());
let sk = crate::crypto::bls_keys::fetch_bls_sk(&pk_hex)?.secret_key();

// Sign a VoluntaryExitMessage with Epoch 0
let (sig, _root) = sign_vem(sk, 0, req.validator_index, req.fork_info)?;

Ok(crate::enclave::types::SignExitResponse {
signature: hex::encode(sig.as_ssz_bytes())
})
}

fn sign_vem(
sk_share: blsttc::SecretKey,
epoch: crate::eth2::eth_types::Epoch,
validator_index: crate::eth2::eth_types::ValidatorIndex,
fork_info: crate::eth2::eth_types::ForkInfo,
) -> Result<(BLSSignature, crate::eth2::eth_types::Root)> {
let voluntary_exit = crate::eth2::eth_types::VoluntaryExit {
epoch,
validator_index,
};

let vem_req = crate::eth2::eth_types::VoluntaryExitRequest {
fork_info,
signingRoot: None,
voluntary_exit,
};

let root = crate::eth2::eth_signing::BLSSignMsg::VOLUNTARY_EXIT(vem_req).to_signing_root(None);

let sig: BLSSignature = BLSSignature::from(sk_share.sign(&root).to_bytes().to_vec());

Ok((sig, root))
}

#[cfg(test)]
mod tests {
use super::*;
use crate::enclave::types::BlsKeygenPayload;
use ecies::{PublicKey as EthPublicKey, SecretKey as EthSecretKey};
use tree_hash::TreeHash;

fn setup() -> (BlsKeygenPayload, Vec<EthSecretKey>, String, String) {
Expand Down Expand Up @@ -402,7 +445,7 @@ mod tests {

#[test]
fn test_verify_custody_with_success() {
let (resp, g_sks, mre, mrs) = setup();
let (resp, g_sks, _mre, _mrs) = setup();

for g_sk in g_sks {
assert!(verify_custody(&resp, &g_sk).is_ok());
Expand All @@ -411,31 +454,64 @@ mod tests {

#[test]
fn test_verify_custody_with_fail() {
let (resp, g_sks, mre, mrs) = setup();
let (resp, _g_sks, _mre, _mrs) = setup();
let g_sk = EthSecretKey::default();
assert!(verify_custody(&resp, &g_sk).is_err());
}

#[test]
fn test_verify_remote_attestation_evidence_with_success() {
let (resp, g_sks, mre, mrs) = setup();
let (resp, _g_sks, mre, mrs) = setup();

verify_remote_attestation_evidence(&resp, &mre, &mrs).unwrap();
}

#[test]
fn test_verify_deposit_message() {
let (resp, g_sks, mre, mrs) = setup();
let (resp, _g_sks, _mre, _mrs) = setup();
verify_deposit_message(&resp).unwrap();
}

#[tokio::test]
async fn test_approve_custody() {
let (resp, g_sks, mre, mrs) = setup();
let (resp, g_sks, _mre, _mrs) = setup();

for g_sk in g_sks {
assert!(approve_custody(&resp, &g_sk).await.is_ok());
}
assert!(false);
}

#[test]
fn test_sign_vem() {
let (resp, _g_sks, _mre, _mrs) = setup();

let mut sig_shares: Vec<blsttc::SignatureShare> = Vec::new();
let mut msg_root = [0_u8; 32];
for i in 0.._g_sks.len() {
let req = crate::enclave::types::SignExitRequest {
bls_pub_key_set: resp.bls_pub_key_set.clone(),
guardian_index: i as u64,
validator_index: 0,
fork_info: crate::eth2::eth_types::ForkInfo::default(),
};
let sk_share = verify_custody(&resp, &_g_sks[i]).unwrap();
let sk = blsttc::SecretKey::from_bytes(sk_share.to_bytes()).unwrap();
let (sig, root) = sign_vem(sk, 0, req.validator_index, req.fork_info).unwrap();
msg_root = root;

let mut sig_bytes: [u8; crate::constants::BLS_SIG_BYTES] =
[0; crate::constants::BLS_SIG_BYTES];
sig_bytes.copy_from_slice(
&sig[..]);
let sig = blsttc::SignatureShare::from_bytes(sig_bytes).unwrap();

sig_shares.push(sig);
}

// aggregate the partial sigs
let sig = crate::crypto::bls_keys::aggregate_signature_shares(&resp.public_key_set().unwrap(), &sig_shares).unwrap();

// verify the signature is valid
assert!(resp.public_key_set().unwrap().public_key().verify(&sig, msg_root));
}
}
25 changes: 18 additions & 7 deletions src/enclave/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,13 +276,24 @@ impl BlsKeygenPayload {

#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct EigenPodData {
pub puffer_pool_address: ethers::abi::Address,
pub eigen_pod_manager_address: ethers::abi::Address,
pub eigen_pod_beacon_address: ethers::abi::Address,
pub eigen_pod_proxy_init_code: String,
pub beacon_proxy_bytecode: String,
pub pod_account_owners: Vec<ethers::types::Address>,
pub struct SignExitRequest {
pub bls_pub_key_set: String,
pub guardian_index: u64,
pub validator_index: u64,
pub fork_info: crate::eth2::eth_types::ForkInfo,
}

impl SignExitRequest {
pub fn public_key_set(&self) -> Result<PublicKeySet> {
let sanitized: String = crate::strip_0x_prefix!(&self.bls_pub_key_set);
Ok(PublicKeySet::from_bytes(hex::decode(sanitized)?)?)
}
}

#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SignExitResponse {
pub signature: String,
}

#[derive(serde::Serialize, serde::Deserialize, Debug)]
Expand Down

0 comments on commit d6253bf

Please sign in to comment.