-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
300 additions
and
132 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
use crate::OperatorId; | ||
use crate::Share; | ||
use derive_more::{Deref, From}; | ||
use types::{Address, Graffiti, PublicKey}; | ||
|
||
/// Unique identifier for a cluster | ||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, From, Deref)] | ||
pub struct ClusterId(pub u64); | ||
|
||
/// A Cluster is a group of Operators that are acting on behalf of a Validator | ||
#[derive(Debug, Clone)] | ||
pub struct Cluster { | ||
/// Unique identifier for a Cluster | ||
pub cluster_id: ClusterId, | ||
/// All of the members of this Cluster | ||
pub cluster_members: Vec<ClusterMember>, | ||
/// The number of faulty operator in the Cluster | ||
pub faulty: u64, | ||
/// If the Cluster is liquidated or active | ||
pub liquidated: bool, | ||
/// Metadata about the validator this committee represents | ||
pub validator_metadata: ValidatorMetadata, | ||
} | ||
|
||
/// A member of a Cluster. This is just an Operator that holds onto a share of the Validator key | ||
#[derive(Debug, Clone)] | ||
pub struct ClusterMember { | ||
/// Unique identifier for the Operator this member represents | ||
pub operator_id: OperatorId, | ||
/// Unique identifier for the Cluster this member is a part of | ||
pub cluster_id: ClusterId, | ||
/// The Share this member is responsible for | ||
pub share: Share, | ||
} | ||
|
||
/// Index of the validator in the validator registry. | ||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, From, Deref)] | ||
pub struct ValidatorIndex(pub usize); | ||
|
||
/// General Metadata about a Validator | ||
#[derive(Debug, Clone)] | ||
pub struct ValidatorMetadata { | ||
/// Index of the validator | ||
pub validator_index: ValidatorIndex, | ||
/// Public key of the validator | ||
pub validator_pubkey: PublicKey, | ||
/// Eth1 fee address | ||
pub fee_recipient: Address, | ||
/// Graffiti | ||
pub graffiti: Graffiti, | ||
/// The owner of the validator | ||
pub owner: Address, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
pub use cluster::{Cluster, ClusterId, ClusterMember, ValidatorIndex, ValidatorMetadata}; | ||
pub use operator::{Operator, OperatorId}; | ||
pub use share::Share; | ||
mod cluster; | ||
mod operator; | ||
mod share; | ||
mod util; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
use crate::util::parse_rsa; | ||
use derive_more::{Deref, From}; | ||
use openssl::pkey::Public; | ||
use openssl::rsa::Rsa; | ||
use std::cmp::Eq; | ||
use std::fmt::Debug; | ||
use std::hash::Hash; | ||
use types::Address; | ||
|
||
/// Unique identifier for an Operator. | ||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, From, Deref)] | ||
pub struct OperatorId(pub 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 rsa_pubkey: Rsa<Public>, | ||
/// Owner of the operator | ||
pub owner: Address, | ||
} | ||
|
||
impl Operator { | ||
/// Creates a new operator from its OperatorId and PEM-encoded public key string | ||
pub fn new(pem_data: &str, operator_id: OperatorId, owner: Address) -> Result<Self, String> { | ||
let rsa_pubkey = parse_rsa(pem_data)?; | ||
Ok(Self::new_with_pubkey(rsa_pubkey, operator_id, owner)) | ||
} | ||
|
||
// Creates a new operator from an existing RSA public key and OperatorId | ||
pub fn new_with_pubkey(rsa_pubkey: Rsa<Public>, id: OperatorId, owner: Address) -> Self { | ||
Self { | ||
id, | ||
rsa_pubkey, | ||
owner, | ||
} | ||
} | ||
} | ||
|
||
#[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 address = Address::random(); | ||
|
||
let operator = Operator::new(pem_data, operator_id.into(), address); | ||
assert!(operator.is_ok()); | ||
|
||
if let Ok(op) = operator { | ||
assert_eq!(op.id.0, operator_id); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
use types::PublicKey; | ||
|
||
/// One of N shares of a split validator key. | ||
#[derive(Debug, Clone)] | ||
pub struct Share { | ||
/// The public key of this Share | ||
pub share_pubkey: PublicKey, | ||
/// The encrypted private key of the share | ||
pub encrypted_private_key: [u8; 256], | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
use base64::prelude::*; | ||
use openssl::pkey::Public; | ||
use openssl::rsa::Rsa; | ||
|
||
// Parse from a RSA public key string into the associated RSA representation | ||
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 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 = Rsa::public_key_from_pem(pem_string.as_bytes()) | ||
.map_err(|e| format!("Failed to parse RSA public key: {}", e))?; | ||
|
||
Ok(rsa_pubkey) | ||
} |