Skip to content

Commit 1da8b77

Browse files
committed
feat: add cluster lock v1.10.0 support
1 parent ba15e52 commit 1da8b77

9 files changed

Lines changed: 400 additions & 3 deletions

File tree

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ prost-build = "0.14"
4040
rand = {version = "0.8", features = ["std_rng"]}
4141
uuid = {version = "1.16", features = ["serde"] }
4242
serde_with = { version = "3", features = ["hex"] }
43+
charon-crypto = { path = "crates/charon-crypto" }
4344

4445
[workspace.lints.rust]
4546
missing_docs = "deny"

crates/charon-cluster/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ serde.workspace = true
1515
serde_json.workspace = true
1616
hex.workspace = true
1717
serde_with.workspace = true
18+
charon-crypto.workspace = true
19+
thiserror.workspace = true
1820

1921
[build-dependencies]
2022
prost-build.workspace = true

crates/charon-cluster/src/definition.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,10 @@ pub struct Definition {
3232
pub timestamp: DateTime<Utc>,
3333
/// NumValidators is the number of DVs to be created in the cluster lock
3434
/// file.
35-
pub num_validators: u32,
35+
pub num_validators: u64,
3636
/// Threshold required for signature reconstruction. Defaults to safe value
3737
/// for number of nodes/peers.
38-
pub threshold: u32,
38+
pub threshold: u64,
3939
/// DKGAlgorithm to use for key generation. Max 32 chars.
4040
pub dkg_algorithm: String,
4141
/// ForkVersion defines the cluster's 4 byte beacon chain fork version
@@ -59,7 +59,7 @@ pub struct Definition {
5959
/// cluster, e.g. "abft".
6060
pub consensus_protocol: String,
6161
/// TargetGasLimit is the target block gas limit for the cluster.
62-
pub target_gas_limit: u32,
62+
pub target_gas_limit: u64,
6363
/// Compounding flag enables compounding rewards for validators by using
6464
/// 0x02 withdrawal credentials.
6565
pub compounding: bool,
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,28 @@
1+
use crate::helpers::EthHex;
2+
use serde::{Deserialize, Serialize};
3+
use serde_with::{DisplayFromStr, serde_as};
14

5+
/// DepositData defines the deposit data to activate a validator.
6+
/// https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#depositdata
7+
#[serde_as]
8+
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
9+
pub struct DepositData {
10+
/// PubKey is the validator's public key.
11+
#[serde_as(as = "EthHex")]
12+
#[serde(rename = "pubkey")]
13+
pub pub_key: Vec<u8>,
14+
15+
/// WithdrawalCredentials included in the deposit.
16+
#[serde_as(as = "EthHex")]
17+
pub withdrawal_credentials: Vec<u8>,
18+
19+
/// Amount is the amount in Gwei to be deposited [1ETH..2048ETH].
20+
/// We use DisplayFromStr to allow for easy conversion from string to u64.
21+
#[serde_as(as = "DisplayFromStr")]
22+
pub amount: u64,
23+
24+
/// Signature is the BLS signature of the deposit message (above three
25+
/// fields).
26+
#[serde_as(as = "EthHex")]
27+
pub signature: Vec<u8>,
28+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,97 @@
1+
use charon_crypto::types::{PUBLIC_KEY_LENGTH, PublicKey};
2+
use serde::{Deserialize, Serialize};
13

4+
use crate::{deposit::DepositData, helpers::EthHex, registration::BuilderRegistration};
5+
use serde_with::serde_as;
6+
7+
/// DistValidator is a distributed validator managed by the cluster.
8+
#[serde_as]
9+
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
10+
pub struct DistValidator {
11+
/// PubKey is the distributed validator group public key.
12+
#[serde(rename = "distributed_public_key")]
13+
#[serde_as(as = "EthHex")]
14+
pub pub_key: Vec<u8>,
15+
16+
/// PubShares are the public keys corresponding to each node's secret key
17+
/// share. It can be used to verify a partial signature created by any
18+
/// node in the cluster.
19+
#[serde(rename = "public_shares")]
20+
#[serde_as(as = "Vec<EthHex>")]
21+
pub pub_shares: Vec<Vec<u8>>,
22+
23+
/// PartialDepositData is the list of partial deposit data.
24+
pub partial_deposit_data: Vec<DepositData>,
25+
26+
/// BuilderRegistration is the pre-generated signed validator builder
27+
/// registration.
28+
pub builder_registration: BuilderRegistration,
29+
}
30+
31+
/// DistValidatorError is an error type for DistValidator operations.
32+
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
33+
pub enum DistValidatorError {
34+
/// Invalid public key length.
35+
#[error("invalid public key length: got {0}, want {1}")]
36+
InvalidPublicKeyLength(usize, usize),
37+
/// Invalid public share index.
38+
#[error("invalid public share index: got {0}, want less than {1}")]
39+
InvalidPublicShareIndex(usize, usize),
40+
}
41+
42+
impl DistValidator {
43+
/// PublicKey returns the distributed validator group public key.
44+
pub fn public_key(&self) -> Result<PublicKey, DistValidatorError> {
45+
if self.pub_key.len() != PUBLIC_KEY_LENGTH {
46+
return Err(DistValidatorError::InvalidPublicKeyLength(
47+
self.pub_key.len(),
48+
PUBLIC_KEY_LENGTH,
49+
));
50+
}
51+
let mut pub_key = [0u8; PUBLIC_KEY_LENGTH];
52+
pub_key.copy_from_slice(&self.pub_key);
53+
Ok(pub_key)
54+
}
55+
56+
/// PublicKeyHex returns the validator hex group public key.
57+
pub fn public_key_hex(&self) -> Result<String, DistValidatorError> {
58+
let pub_key = self.public_key()?;
59+
Ok(format!("0x{}", hex::encode(pub_key)))
60+
}
61+
62+
/// PublicShare returns a peer's threshold BLS public share.
63+
pub fn public_share(&self, index: usize) -> Result<PublicKey, DistValidatorError> {
64+
if index >= self.pub_shares.len() {
65+
return Err(DistValidatorError::InvalidPublicShareIndex(
66+
index,
67+
self.pub_shares.len(),
68+
));
69+
}
70+
if self.pub_shares[index].len() != PUBLIC_KEY_LENGTH {
71+
return Err(DistValidatorError::InvalidPublicKeyLength(
72+
self.pub_shares[index].len(),
73+
PUBLIC_KEY_LENGTH,
74+
));
75+
}
76+
let mut pub_share = [0u8; PUBLIC_KEY_LENGTH];
77+
pub_share.copy_from_slice(&self.pub_shares[index]);
78+
Ok(pub_share)
79+
}
80+
81+
/// ZeroRegistration returns a true if the validator has zero valued
82+
/// registration.
83+
pub fn zero_registration(&self) -> bool {
84+
self.builder_registration.signature.is_empty()
85+
&& self.builder_registration.message.fee_recipient.is_empty()
86+
&& self.builder_registration.message.gas_limit == 0
87+
&& self.builder_registration.message.timestamp.timestamp() == 0
88+
&& self.builder_registration.message.pub_key.is_empty()
89+
}
90+
91+
/// Eth2Registration returns the validator's Eth2 registration.
92+
pub fn eth2_registration(&self) -> Result<(), DistValidatorError> {
93+
unimplemented!(
94+
"Eth2 registration requires to have ethereum types library which is not yet integrated in charon-cluster"
95+
)
96+
}
97+
}

crates/charon-cluster/src/helpers.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use chrono::{DateTime, Utc};
12
use serde::{Deserialize, Deserializer, Serialize, Serializer};
23
use serde_with::{DeserializeAs, SerializeAs};
34
use std::borrow::Cow;
@@ -90,6 +91,28 @@ impl TryFrom<&str> for EthHex {
9091
}
9192
}
9293

94+
/// TimestampSeconds represents a timestamp in seconds since the Unix epoch.
95+
pub struct TimestampSeconds;
96+
97+
impl SerializeAs<DateTime<Utc>> for TimestampSeconds {
98+
fn serialize_as<S>(value: &DateTime<Utc>, serializer: S) -> Result<S::Ok, S::Error>
99+
where
100+
S: Serializer,
101+
{
102+
serializer.serialize_i64(value.timestamp())
103+
}
104+
}
105+
106+
impl<'de> DeserializeAs<'de, DateTime<Utc>> for TimestampSeconds {
107+
fn deserialize_as<D>(deserializer: D) -> Result<DateTime<Utc>, D::Error>
108+
where
109+
D: Deserializer<'de>,
110+
{
111+
let timestamp = i64::deserialize(deserializer)?;
112+
Ok(DateTime::<Utc>::from_timestamp(timestamp, 0).unwrap())
113+
}
114+
}
115+
93116
#[cfg(test)]
94117
mod tests {
95118
use super::*;

0 commit comments

Comments
 (0)