Skip to content

Commit

Permalink
newtypes & rsa support
Browse files Browse the repository at this point in the history
  • Loading branch information
Zacholme7 committed Dec 2, 2024
1 parent 80b71c1 commit 4a4dfb5
Show file tree
Hide file tree
Showing 8 changed files with 316 additions and 164 deletions.
305 changes: 169 additions & 136 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ tokio = { version = "1.39.2", features = [
] }
tracing = "0.1.40"
tracing-subscriber = { version = "0.3.18", features = ["fmt", "env-filter"] }
rsa = "0.9.7"
base64 = "0.22.1"

[profile.maxperf]
inherits = "release"
Expand Down
3 changes: 3 additions & 0 deletions anchor/common/ssv_types/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@ authors = ["Sigma Prime <contact@sigmaprime.io>"]

[dependencies]
types = { workspace = true}
rsa = { workspace = true }
derive_more = { workspace = true }
base64 = { workspace = true }
66 changes: 54 additions & 12 deletions anchor/common/ssv_types/src/committee.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,65 @@
use crate::{Operator, OperatorID, OperatorPublicKey};
use crate::util::parse_rsa;
use crate::{Operator, OperatorId};
use derive_more::{Deref, From};
use rsa::RsaPublicKey;
use std::cmp::Eq;
use std::fmt::Debug;
use std::hash::Hash;
use types::Domain;

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

/// Member of a SSV Committee. A `CommitteeMember` is just an operator that is part of the committee
/// 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 share_public_key: OperatorPublicKey,
// Number of nodes that are faulty/malicious in the committee
/// 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: RsaPublicKey,
/// Number of nodes that are faulty/malicious in the committee
pub faulty: u64,
// All of the operators that are a part of this committee
/// All of the operators that are a part of this committee
pub members: Vec<Operator>,
// Signature domain
/// 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: RsaPublicKey,
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,
}
}
}
5 changes: 3 additions & 2 deletions anchor/common/ssv_types/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
pub use committee::{CommitteeID, CommitteeMember};
pub use operator::{Operator, OperatorID, OperatorPublicKey};
pub use committee::{CommitteeId, CommitteeMember};
pub use operator::{Operator, OperatorId};
pub use share::SSVShare;
mod committee;
mod operator;
mod share;
mod util;
56 changes: 48 additions & 8 deletions anchor/common/ssv_types/src/operator.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,54 @@
/// Unique identifier for an Operator
pub type OperatorID = u64;
use crate::util::parse_rsa;
use derive_more::{Deref, From};
use rsa::RsaPublicKey;
use std::cmp::Eq;
use std::fmt::Debug;
use std::hash::Hash;

/// Operator RSA public key
pub type OperatorPublicKey = [u8; 459];
/// 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: OperatorPublicKey,
/// ID to uniquely identify this operator
pub id: OperatorId,
/// Base-64 encoded PEM RSA public key
pub public_key: RsaPublicKey,
}

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: RsaPublicKey, 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);
}
}
}
14 changes: 8 additions & 6 deletions anchor/common/ssv_types/src/share.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
use crate::CommitteeID;
use crate::CommitteeId;
use std::time::SystemTime;
use types::{Address, Domain, Graffiti, PublicKey};

type ValidatorIndex = usize;

/// Share of a key that a operator owns and accompanying metadata
/// 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,
}

/// One of N shares of a split validator key
/// One of N shares of a split validator key.
#[derive(Debug, Clone)]
pub struct Share {
/// Index of the validator
Expand All @@ -30,16 +32,16 @@ pub struct Share {
pub graffiti: Graffiti,
}

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

/// General metadata
/// General metadata.
#[derive(Debug, Clone)]
pub struct Metadata {
/// The owner of the validator
Expand Down
29 changes: 29 additions & 0 deletions anchor/common/ssv_types/src/util.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use base64::prelude::*;
use rsa::pkcs8::DecodePublicKey;
use rsa::RsaPublicKey;

// Parse from a RSA public key string into the associated RSA representation
pub fn parse_rsa(pem_data: &str) -> Result<RsaPublicKey, 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 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 using PKCS8 format
let rsa_pubkey = RsaPublicKey::from_public_key_pem(&pem_string)
.map_err(|e| format!("Failed to parse RSA public key: {}", e))?;

Ok(rsa_pubkey)
}

0 comments on commit 4a4dfb5

Please sign in to comment.