Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create a dedicated ssv_types crate. #58

Closed
wants to merge 27 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 76 additions & 67 deletions Cargo.lock

Large diffs are not rendered by default.

23 changes: 14 additions & 9 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ members = [
"anchor/http_api",
"anchor/http_metrics",
"anchor/network",
"anchor/processor",
"anchor/qbft",
"anchor/common/version",
"anchor/common/ssv_types",
]
resolver = "2"

Expand All @@ -20,25 +20,29 @@ client = { path = "anchor/client" }
qbft = { path = "anchor/qbft" }
http_api = { path = "anchor/http_api" }
http_metrics = { path = "anchor/http_metrics" }
network = { path ="anchor/network"}
version = { path ="anchor/common/version"}
network = { path = "anchor/network" }
version = { path = "anchor/common/version" }
ssv_types = { path = "anchor/common/ssv_types" }
processor = { path = "anchor/processor" }
lighthouse_network = { git = "https://github.com/sigp/lighthouse", branch = "unstable"}
task_executor = { git = "https://github.com/sigp/lighthouse", branch = "unstable", default-features = false, features = [ "tracing", ] }
lighthouse_network = { git = "https://github.com/sigp/lighthouse", branch = "unstable" }
task_executor = { git = "https://github.com/sigp/lighthouse", branch = "unstable", default-features = false, features = [
"tracing",
] }
metrics = { git = "https://github.com/agemanning/lighthouse", branch = "modularize-vc" }
validator_metrics = { git = "https://github.com/agemanning/lighthouse", branch = "modularize-vc" }
sensitive_url = { git = "https://github.com/agemanning/lighthouse", branch = "modularize-vc" }
slot_clock = { git = "https://github.com/agemanning/lighthouse", branch = "modularize-vc" }
unused_port = { git = "https://github.com/sigp/lighthouse", branch = "unstable" }
types = { git = "https://github.com/sigp/lighthouse", branch = "unstable" }
derive_more = { version = "1.0.0", features = ["full"] }
async-channel = "1.9"
axum = "0.7.7"
clap = { version = "4.5.15", features = ["derive", "wrap_help"]}
clap = { version = "4.5.15", features = ["derive", "wrap_help"] }
discv5 = "0.8.0"
dirs = "5.0.1"
either = "1.13.0"
futures = "0.3.30"
tower-http = {version = "0.6", features = ["cors"] }
tower-http = { version = "0.6", features = ["cors"] }
hyper = "1.4"
num_cpus = "1"
parking_lot = "0.12"
Expand All @@ -53,10 +57,11 @@ tokio = { version = "1.39.2", features = [
] }
tracing = "0.1.40"
tracing-subscriber = { version = "0.3.18", features = ["fmt", "env-filter"] }
openssl = "0.10.68"
base64 = "0.22.1"

[profile.maxperf]
inherits = "release"
lto = "fat"
codegen-units = 1
incremental = false

11 changes: 11 additions & 0 deletions anchor/common/ssv_types/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "ssv_types"
version = "0.1.0"
edition = { workspace = true }
authors = ["Sigma Prime <[email protected]>"]

[dependencies]
types = { workspace = true }
openssl = { workspace = true }
derive_more = { workspace = true }
base64 = { workspace = true }
63 changes: 63 additions & 0 deletions anchor/common/ssv_types/src/committee.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
use crate::util::parse_rsa;
use crate::{Operator, OperatorId};
use derive_more::{Deref, From};
use openssl::pkey::Public;
use openssl::rsa::Rsa;
use types::Domain;

/// Unique identifier for a committee.
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, From, Deref)]
pub struct CommitteeId(pub u64);

/// Member of a SSV Committee. A CommitteeMember is just an operator that is part of the committee
/// a validator has chosen to distribute its keyshares to.
#[derive(Debug, Clone)]
pub struct CommitteeMember {
/// Unique identifier for the operator
pub operator_id: OperatorId,
/// Unique identifier for the committee this member is a part of
pub committee_id: CommitteeId,
/// Base-64 encoded PEM RSA public key of the operator
pub operator_public_key: Rsa<Public>,
/// Number of nodes that are faulty/malicious in the committee
pub faulty: u64,
/// All of the operators that are a part of this committee
pub members: Vec<Operator>,
/// Signature domain
pub domain: Domain,
}

impl CommitteeMember {
/// Creates a new committee member from a PEM-encoded public key string
pub fn new(
pem_data: &str,
operator_id: OperatorId,
committee_id: CommitteeId,
domain: Domain,
) -> Result<Self, String> {
let rsa_pubkey = parse_rsa(pem_data)?;
Ok(Self::new_with_pubkey(
rsa_pubkey,
operator_id,
committee_id,
domain,
))
}

/// Creates a new committee member from an existing RSA public key
pub fn new_with_pubkey(
rsa_pubkey: Rsa<Public>,
operator_id: OperatorId,
committee_id: CommitteeId,
domain: Domain,
) -> Self {
Self {
operator_id,
committee_id,
operator_public_key: rsa_pubkey,
faulty: 0,
members: Vec::new(),
domain,
}
}
}
7 changes: 7 additions & 0 deletions anchor/common/ssv_types/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
pub use committee::{CommitteeId, CommitteeMember};
pub use operator::{Operator, OperatorId};
pub use share::{SSVShare, Share, ShareMember, ValidatorIndex};
mod committee;
mod operator;
mod share;
mod util;
57 changes: 57 additions & 0 deletions anchor/common/ssv_types/src/operator.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use crate::util::parse_rsa;
use derive_more::{Deref, From};
use openssl::rsa::Rsa;
use std::cmp::Eq;
use std::fmt::Debug;
use std::hash::Hash;

/// Unique identifier for an Operator.
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, From, Deref)]
pub struct OperatorId(u64);

/// Client responsible for maintaining the overall health of the network.
#[derive(Debug, Clone)]
pub struct Operator {
/// ID to uniquely identify this operator
pub id: OperatorId,
/// Base-64 encoded PEM RSA public key
pub public_key: Rsa<openssl::pkey::Public>,
}

impl Operator {
/// Creates a new operator from its OperatorId and PEM-encoded public key string
pub fn new(pem_data: &str, operator_id: OperatorId) -> Result<Self, String> {
let rsa_pubkey = parse_rsa(pem_data)?;
Ok(Self::new_with_pubkey(rsa_pubkey, operator_id))
}

// Creates a new operator from an existing RSA public key and OperatorId
pub fn new_with_pubkey(
rsa_pubkey: Rsa<openssl::pkey::Public>,
operator_id: OperatorId,
) -> Self {
Self {
id: operator_id,
public_key: rsa_pubkey,
}
}
}

#[cfg(test)]
mod operator_tests {
use super::*;

#[test]
fn operator_from_pubkey_and_id() {
// Random valid operator public key and id: https://explorer.ssv.network/operators/1141
let pem_data = "LS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBbFFmQVIzMEd4bFpacEwrNDByU0IKTEpSYlkwY2laZDBVMXhtTlp1bFB0NzZKQXJ5d2lia0Y4SFlQV2xkM3dERVdWZXZjRzRGVVBSZ0hDM1MrTHNuMwpVVC9TS280eE9nNFlnZ0xqbVVXQysyU3ZGRFhXYVFvdFRXYW5UU0drSEllNGFnTVNEYlUzOWhSMWdOSTJhY2NNCkVCcjU2eXpWcFMvKytkSk5xU002S1FQM3RnTU5ia2IvbEtlY0piTXM0ZWNRMTNkWUQwY3dFNFQxcEdTYUdhcEkKbFNaZ2lYd0cwSGFNTm5GUkt0OFlkZjNHaTFMRlh3Zlo5NHZFRjJMLzg3RCtidjdkSFVpSGRjRnh0Vm0rVjVvawo3VFptcnpVdXB2NWhKZ3lDVE9zc0xHOW1QSGNORnhEVDJ4NUJKZ2FFOVpJYnMrWVZ5a1k3UTE4VEhRS2lWcDFaCmp3SURBUUFCCi0tLS0tRU5EIFJTQSBQVUJMSUMgS0VZLS0tLS0K";
let operator_id = 1141;

let operator = Operator::new(pem_data, operator_id.into());
assert!(operator.is_ok());

if let Ok(op) = operator {
assert_eq!(op.id.0, operator_id);
}
}
}
56 changes: 56 additions & 0 deletions anchor/common/ssv_types/src/share.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
use crate::OperatorId;
use derive_more::{Deref, From};
use std::time::SystemTime;
use types::{Address, Domain, Graffiti, PublicKey};

/// Index of the validator in the validator registry.
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, From, Deref)]
pub struct ValidatorIndex(usize);

/// Share of a key that a operator owns and its accompanying metadata.
#[derive(Debug, Clone)]
pub struct SSVShare {
// A single share of a validator private key.
pub share: Share,
// Miscellaneous metadata relevant to the share
pub metadata: Metadata,
Zacholme7 marked this conversation as resolved.
Show resolved Hide resolved
}

/// One of N shares of a split validator key.
#[derive(Debug, Clone)]
pub struct Share {
/// Index of the validator
pub validator_index: ValidatorIndex,
/// Public key of the validator
pub validator_pubkey: PublicKey,
/// Public key for this portion of the share
pub share_public_key: PublicKey,
/// All committee members that contain a sibling share
pub committee: Vec<ShareMember>,
/// Identifies the context/purpose of signature
pub domain_type: Domain,
/// Eth1 fee address
pub fee_recipient: Address,
/// Graffiti
pub graffiti: Graffiti,
}

/// A operator who holds a portion of the share.
#[derive(Debug, Clone)]
pub struct ShareMember {
/// Unique identifier for the operator
pub operator: OperatorId,
/// The public key for this members share
pub share_public_key: PublicKey,
}

/// General metadata.
#[derive(Debug, Clone)]
pub struct Metadata {
/// The owner of the validator
pub owner: Address,
/// Is the committee this share is a part of currently liquidated
pub liquidated: bool,
/// Track the last time the metadata was updated.
pub last_updated: SystemTime,
}
31 changes: 31 additions & 0 deletions anchor/common/ssv_types/src/util.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
use base64::prelude::*;
use openssl::pkey::Public;
use openssl::rsa::Rsa;

// Parse from a RSA public key string into the associated RSA representation
// NOTE: This function handles a legacy system quirk where RSA keys are
// incorrectly formatted with PKCS8 headers but PKCS1 content.
pub fn parse_rsa(pem_data: &str) -> Result<Rsa<Public>, String> {
// First decode the base64 data
let pem_decoded = BASE64_STANDARD
.decode(pem_data)
.map_err(|e| format!("Unable to decode base64 pem data: {}", e))?;

// Convert the decoded data to a utf8 string
let mut pem_string = String::from_utf8(pem_decoded)
.map_err(|e| format!("Unable to convert decoded pem data into a string: {}", e))?;

// Fix the header - replace PKCS1 header with PKCS8 header
pem_string = pem_string
.replace(
"-----BEGIN RSA PUBLIC KEY-----",
"-----BEGIN PUBLIC KEY-----",
)
.replace("-----END RSA PUBLIC KEY-----", "-----END PUBLIC KEY-----");

// Parse the PEM string into an RSA public key
let rsa_pubkey = Rsa::public_key_from_pem(pem_string.as_bytes())
.map_err(|e| format!("Failed to parse RSA public key: {}", e))?;

Ok(rsa_pubkey)
}
2 changes: 1 addition & 1 deletion anchor/network/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ serde = { workspace = true }
tracing = { workspace = true }

[dev-dependencies]
async-channel = { workspace = true }
async-channel = { workspace = true }
Loading