Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions crates/app/src/eth2wrap/valcache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ use pluto_core::types::PubKey;
use pluto_eth2api::{
EthBeaconNodeApiClient, EthBeaconNodeApiClientError, GetStateValidatorsResponseResponse,
GetStateValidatorsResponseResponseDatum, PostStateValidatorsRequest,
PostStateValidatorsRequestPath, PostStateValidatorsResponse, ValidatorIndex,
ValidatorRequestBody,
PostStateValidatorsRequestPath, PostStateValidatorsResponse, ValidatorRequestBody,
spec::phase0::ValidatorIndex,
};
use std::{collections::HashMap, sync::Arc};
use tokio::sync::RwLock;
Expand Down
1 change: 1 addition & 0 deletions crates/cluster/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ serde_json.workspace = true
hex.workspace = true
serde_with.workspace = true
pluto-crypto.workspace = true
pluto-eth2api.workspace = true
thiserror.workspace = true
rand_core.workspace = true
libp2p.workspace = true
Expand Down
38 changes: 27 additions & 11 deletions crates/cluster/src/deposit.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,44 @@
use crate::helpers::EthHex;
use pluto_eth2api::spec::phase0;
use serde::{Deserialize, Serialize};
use serde_with::{DisplayFromStr, serde_as};

/// DepositData defines the deposit data to activate a validator.
/// https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#depositdata
///
/// This is a cluster-specific wrapper around the canonical
/// `phase0::DepositData` that uses EthHex serialization (with 0x prefix) to
/// maintain lock file JSON compatibility.
///
/// Specification: <https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#depositdata>
#[serde_as]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct DepositData {
/// Validator's public key.
/// Validator's public key (48 bytes).
#[serde_as(as = "EthHex")]
#[serde(rename = "pubkey")]
pub pub_key: Vec<u8>,
pub pub_key: phase0::BLSPubKey,

/// Withdrawal credentials included in the deposit.
/// Withdrawal credentials included in the deposit (32 bytes).
#[serde_as(as = "EthHex")]
pub withdrawal_credentials: Vec<u8>,
pub withdrawal_credentials: phase0::WithdrawalCredentials,

/// Amount in Gwei to be deposited [1ETH..2048ETH].
/// We use DisplayFromStr to allow for easy conversion from string to u64.
/// We use DisplayFromStr to allow for easy conversion from string to u64
#[serde_as(as = "DisplayFromStr")]
pub amount: u64,
pub amount: phase0::Gwei,

/// Signature is the BLS signature of the deposit message (above three
/// fields).
/// Signature is the BLS signature of the deposit message (96 bytes).
#[serde_as(as = "EthHex")]
pub signature: Vec<u8>,
pub signature: phase0::BLSSignature,
}

impl Default for DepositData {
fn default() -> Self {
Self {
pub_key: [0u8; 48],
withdrawal_credentials: [0u8; 32],
amount: 0,
signature: [0u8; 96],
}
}
}
56 changes: 37 additions & 19 deletions crates/cluster/src/lock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -590,8 +590,11 @@ mod tests {
lock.distributed_validators[0]
.builder_registration
.message
.fee_recipient,
hex::decode("89b79bf504cfb57c7601232d589baccea9d6e263").unwrap()
.fee_recipient
.as_ref(),
hex::decode("89b79bf504cfb57c7601232d589baccea9d6e263")
.unwrap()
.as_slice()
);
assert_eq!(
lock.distributed_validators[0]
Expand All @@ -608,32 +611,38 @@ mod tests {
.timestamp(),
1655733600
);
assert_eq!(lock.distributed_validators[0].builder_registration.message.pub_key, hex::decode("1814be823350eab13935f31d84484517e924aef78ae151c00755925836b7075885650c30ec29a3703934bf50a28da102").unwrap());
assert_eq!(lock.distributed_validators[0].builder_registration.signature, hex::decode("d313c8a3b4c1c0e05447f4ba370eb36dbcfdec90b302dcdc3b9ef522e2a6f1ed0afec1f8e20faabedf6b162e717d3a748a58677a0c56348f8921a266b11d0f334c62fe52ba53af19779cb2948b6570ffa0b773963c130ad797ddeafe4e3ad29b").unwrap());
assert_eq!(lock.distributed_validators[0].builder_registration.message.pub_key.as_ref(), hex::decode("1814be823350eab13935f31d84484517e924aef78ae151c00755925836b7075885650c30ec29a3703934bf50a28da102").unwrap().as_slice());
assert_eq!(lock.distributed_validators[0].builder_registration.signature.as_ref(), hex::decode("d313c8a3b4c1c0e05447f4ba370eb36dbcfdec90b302dcdc3b9ef522e2a6f1ed0afec1f8e20faabedf6b162e717d3a748a58677a0c56348f8921a266b11d0f334c62fe52ba53af19779cb2948b6570ffa0b773963c130ad797ddeafe4e3ad29b").unwrap().as_slice());

// Test first validator partial_deposit_data
assert_eq!(lock.distributed_validators[0].partial_deposit_data.len(), 2);
assert_eq!(lock.distributed_validators[0].partial_deposit_data[0].pub_key, hex::decode("1814be823350eab13935f31d84484517e924aef78ae151c00755925836b7075885650c30ec29a3703934bf50a28da102").unwrap());
assert_eq!(lock.distributed_validators[0].partial_deposit_data[0].pub_key.as_ref(), hex::decode("1814be823350eab13935f31d84484517e924aef78ae151c00755925836b7075885650c30ec29a3703934bf50a28da102").unwrap().as_slice());
assert_eq!(
lock.distributed_validators[0].partial_deposit_data[0].withdrawal_credentials,
lock.distributed_validators[0].partial_deposit_data[0]
.withdrawal_credentials
.as_ref(),
hex::decode("76b0620556304a3e3eae14c28d0cea39d2901a52720da85ca1e4b38eaf3f44c6")
.unwrap()
.as_slice()
);
assert_eq!(
lock.distributed_validators[0].partial_deposit_data[0].amount,
5919415281453547599
);
assert_eq!(lock.distributed_validators[0].partial_deposit_data[1].pub_key, hex::decode("1814be823350eab13935f31d84484517e924aef78ae151c00755925836b7075885650c30ec29a3703934bf50a28da102").unwrap());
assert_eq!(lock.distributed_validators[0].partial_deposit_data[1].pub_key.as_ref(), hex::decode("1814be823350eab13935f31d84484517e924aef78ae151c00755925836b7075885650c30ec29a3703934bf50a28da102").unwrap().as_slice());
assert_eq!(
lock.distributed_validators[0].partial_deposit_data[1].withdrawal_credentials,
lock.distributed_validators[0].partial_deposit_data[1]
.withdrawal_credentials
.as_ref(),
hex::decode("c7ae77ba1d259b188a4b21c86fbc23d728b45347eada650af24c56d0800a8691")
.unwrap()
.as_slice()
);
assert_eq!(
lock.distributed_validators[0].partial_deposit_data[1].amount,
8817733914007551237
);
assert_eq!(lock.distributed_validators[0].partial_deposit_data[1].signature, hex::decode("332088a8b07590bafcccbec6177536401d9a2b7f512b54bfc9d00532adf5aaa7c3a96bc59b489f77d9042c5bce26b163defde5ee6a0fbb3e9346cef81f0ae9515ef30fa47a364e75aea9e111d596e685a591121966e031650d510354aa845580").unwrap());
assert_eq!(lock.distributed_validators[0].partial_deposit_data[1].signature.as_ref(), hex::decode("332088a8b07590bafcccbec6177536401d9a2b7f512b54bfc9d00532adf5aaa7c3a96bc59b489f77d9042c5bce26b163defde5ee6a0fbb3e9346cef81f0ae9515ef30fa47a364e75aea9e111d596e685a591121966e031650d510354aa845580").unwrap().as_slice());

// Test second validator (index 1)
assert_eq!(lock.distributed_validators[1].pub_key, hex::decode("5125210f0ef1c314090f07c79a6f571c246f3e9ac0b7413ef110bd58b00ce73bff706f7ff4b6f44090a32711f3208e4e").unwrap());
Expand All @@ -646,8 +655,11 @@ mod tests {
lock.distributed_validators[1]
.builder_registration
.message
.fee_recipient,
hex::decode("72e6415a761f03abaa40abc9448fddeb2191d945").unwrap()
.fee_recipient
.as_ref(),
hex::decode("72e6415a761f03abaa40abc9448fddeb2191d945")
.unwrap()
.as_slice()
);
assert_eq!(
lock.distributed_validators[1]
Expand All @@ -664,34 +676,40 @@ mod tests {
.timestamp(),
1655733600
);
assert_eq!(lock.distributed_validators[1].builder_registration.message.pub_key, hex::decode("5125210f0ef1c314090f07c79a6f571c246f3e9ac0b7413ef110bd58b00ce73bff706f7ff4b6f44090a32711f3208e4e").unwrap());
assert_eq!(lock.distributed_validators[1].builder_registration.signature, hex::decode("e65a31bd5d41e2d2ce9c2b17892f0fea1931a290220777a93143dfdcbfa68406e877073ff08834e197a4034aa48afa3f85b8a62708caebbac880b5b89b93da53810164402104e648b6226a1b78021851f5d9ac0f313a89ddfc454c5f8f72ac89").unwrap());
assert_eq!(lock.distributed_validators[1].builder_registration.message.pub_key.as_ref(), hex::decode("5125210f0ef1c314090f07c79a6f571c246f3e9ac0b7413ef110bd58b00ce73bff706f7ff4b6f44090a32711f3208e4e").unwrap().as_slice());
assert_eq!(lock.distributed_validators[1].builder_registration.signature.as_ref(), hex::decode("e65a31bd5d41e2d2ce9c2b17892f0fea1931a290220777a93143dfdcbfa68406e877073ff08834e197a4034aa48afa3f85b8a62708caebbac880b5b89b93da53810164402104e648b6226a1b78021851f5d9ac0f313a89ddfc454c5f8f72ac89").unwrap().as_slice());

// Test second validator partial_deposit_data
assert_eq!(lock.distributed_validators[1].partial_deposit_data.len(), 2);
assert_eq!(lock.distributed_validators[1].partial_deposit_data[0].pub_key, hex::decode("5125210f0ef1c314090f07c79a6f571c246f3e9ac0b7413ef110bd58b00ce73bff706f7ff4b6f44090a32711f3208e4e").unwrap());
assert_eq!(lock.distributed_validators[1].partial_deposit_data[0].pub_key.as_ref(), hex::decode("5125210f0ef1c314090f07c79a6f571c246f3e9ac0b7413ef110bd58b00ce73bff706f7ff4b6f44090a32711f3208e4e").unwrap().as_slice());
assert_eq!(
lock.distributed_validators[1].partial_deposit_data[0].withdrawal_credentials,
lock.distributed_validators[1].partial_deposit_data[0]
.withdrawal_credentials
.as_ref(),
hex::decode("0152e5d49435807f9d4b97be6fb77970466a5626fe33408cf9e88e2c797408a3")
.unwrap()
.as_slice()
);
assert_eq!(
lock.distributed_validators[1].partial_deposit_data[0].amount,
534275443587623213
);
assert_eq!(lock.distributed_validators[1].partial_deposit_data[0].signature, hex::decode("329cfffd4a75e498320982c85aad70384859c05a4b13a1d5b2f5bfef5a6ed92da482caa9568e5b6fe9d8a9ddd9eb09277b92cef9046efa18500944cbe800a0b1527ea64729a861d2f6497a3235c37f4192779ec1d96b3b1c5424fce0b727b030").unwrap());
assert_eq!(lock.distributed_validators[1].partial_deposit_data[0].signature.as_ref(), hex::decode("329cfffd4a75e498320982c85aad70384859c05a4b13a1d5b2f5bfef5a6ed92da482caa9568e5b6fe9d8a9ddd9eb09277b92cef9046efa18500944cbe800a0b1527ea64729a861d2f6497a3235c37f4192779ec1d96b3b1c5424fce0b727b030").unwrap().as_slice());

assert_eq!(lock.distributed_validators[1].partial_deposit_data[1].pub_key, hex::decode("5125210f0ef1c314090f07c79a6f571c246f3e9ac0b7413ef110bd58b00ce73bff706f7ff4b6f44090a32711f3208e4e").unwrap());
assert_eq!(lock.distributed_validators[1].partial_deposit_data[1].pub_key.as_ref(), hex::decode("5125210f0ef1c314090f07c79a6f571c246f3e9ac0b7413ef110bd58b00ce73bff706f7ff4b6f44090a32711f3208e4e").unwrap().as_slice());
assert_eq!(
lock.distributed_validators[1].partial_deposit_data[1].withdrawal_credentials,
lock.distributed_validators[1].partial_deposit_data[1]
.withdrawal_credentials
.as_ref(),
hex::decode("078143ee26a586ad23139d5041723470bf24a865837c9123461c41f5ff99aa99")
.unwrap()
.as_slice()
);
assert_eq!(
lock.distributed_validators[1].partial_deposit_data[1].amount,
2408919902728845389
);
assert_eq!(lock.distributed_validators[1].partial_deposit_data[1].signature, hex::decode("ce24eb65491622558fdf297b9fa007864bafd7cd4ca1b2fb5766ab431a032b72b9a7e937ed648d0801f29055d3090d2463718254f9442483c7b98b938045da519843854b0ed3f7ba951a493f321f0966603022c1dfc579b99ed9d20d573ad531").unwrap());
assert_eq!(lock.distributed_validators[1].partial_deposit_data[1].signature.as_ref(), hex::decode("ce24eb65491622558fdf297b9fa007864bafd7cd4ca1b2fb5766ab431a032b72b9a7e937ed648d0801f29055d3090d2463718254f9442483c7b98b938045da519843854b0ed3f7ba951a493f321f0966603022c1dfc579b99ed9d20d573ad531").unwrap().as_slice());

// Test signature_aggregate
assert_eq!(
Expand Down
35 changes: 28 additions & 7 deletions crates/cluster/src/registration.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use chrono::{DateTime, Utc};
use pluto_eth2api::spec::phase0;
use serde::{Deserialize, Serialize};
use serde_with::serde_as;

Expand All @@ -7,23 +8,23 @@ use crate::helpers::{EthHex, TimestampSeconds};
/// BuilderRegistration defines pre-generated signed validator builder
/// registration to be sent to builder network.
#[serde_as]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct BuilderRegistration {
/// Registration message.
pub message: Registration,

/// BLS signature of the registration message.
/// BLS signature of the registration message (96 bytes).
#[serde_as(as = "EthHex")]
pub signature: Vec<u8>,
pub signature: phase0::BLSSignature,
}

/// Registration defines unsigned validator registration message.
#[serde_as]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Registration {
/// Fee recipient address for the registration.
#[serde_as(as = "EthHex")]
pub fee_recipient: Vec<u8>,
pub fee_recipient: [u8; 20],

/// Gas limit for the registration.
pub gas_limit: u64,
Expand All @@ -32,8 +33,28 @@ pub struct Registration {
#[serde_as(as = "TimestampSeconds")]
pub timestamp: DateTime<Utc>,

/// Validator's public key.
/// Validator's public key (48 bytes).
#[serde(rename = "pubkey")]
#[serde_as(as = "EthHex")]
pub pub_key: Vec<u8>,
pub pub_key: phase0::BLSPubKey,
}

impl Default for BuilderRegistration {
fn default() -> Self {
Self {
message: Registration::default(),
signature: [0u8; 96],
}
}
}

impl Default for Registration {
fn default() -> Self {
Self {
fee_recipient: [0u8; 20],
gas_limit: 0,
timestamp: DateTime::<Utc>::default(),
pub_key: [0u8; 48],
}
}
}
2 changes: 2 additions & 0 deletions crates/eth2api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ thiserror.workspace = true
chrono.workspace = true
hex.workspace = true
validator.workspace = true
tree_hash.workspace = true
tree_hash_derive.workspace = true

[dev-dependencies]
testcontainers.workspace = true
Expand Down
9 changes: 3 additions & 6 deletions crates/eth2api/src/extensions.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
ConsensusVersion, EthBeaconNodeApiClient, GetGenesisRequest, GetGenesisResponse,
GetSpecRequest, GetSpecResponse, ValidatorStatus,
GetSpecRequest, GetSpecResponse, ValidatorStatus, spec::phase0,
};
use chrono::{DateTime, Utc};
use std::{collections::HashMap, time};
Expand All @@ -27,9 +27,6 @@ pub enum EthBeaconNodeApiClientError {
ZeroSlotDurationOrSlotsPerEpoch,
}

/// Type alias for validator index.
pub type ValidatorIndex = u64;

const FORKS: [ConsensusVersion; 6] = [
ConsensusVersion::Altair,
ConsensusVersion::Bellatrix,
Expand All @@ -44,9 +41,9 @@ const FORKS: [ConsensusVersion; 6] = [
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ForkSchedule {
/// The fork version, as a 4-byte array.
pub version: [u8; 4],
pub version: phase0::Version,
/// The epoch at which the fork activates.
pub epoch: u64,
pub epoch: phase0::Epoch,
}

impl ValidatorStatus {
Expand Down
3 changes: 3 additions & 0 deletions crates/eth2api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ pub mod extensions;

pub use extensions::*;

/// Ethereum 2.0 consensus layer specification types.
pub mod spec;

#[cfg(test)]
#[cfg(feature = "integration")]
mod integration;
4 changes: 4 additions & 0 deletions crates/eth2api/src/spec/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
//! Ethereum 2.0 consensus layer specification types.

/// Phase 0 consensus types from the Ethereum beacon chain specification.
pub mod phase0;
Loading