Skip to content

Commit feacf24

Browse files
emlautarom1varex83
andauthored
feat(dkg): implement dkg/disk and dkg/share (#285)
* Initial `dkg/disk` module - Add `load_definition` * Add `write_to_keymanager` - Requires `shares` * Add `write_keys_to_disk` - Adjust some pending TODOs. * Add `write_lock` * Add `check_clear_data_dir` * Add `check_writes` * Test `clear_data_dir_does_not_exist` * Test `clear_data_dir_is_file` * Test `clear_data_dir_contains_validator_keys_file` * Test `clear_data_dir_contains_validator_keys_dir` * Test `clear_data_dir_contains_validator_keys_dir` * Test `clear_data_dir_contains_cluster_lock` * Test `clear_data_dir_contains_deposit_data` * Test `clear_data_dir_missing_private_key` * Test `clear_data_dir_contains_private_key` - Fix required file name * Formatting * Fix clippy lints * Test `load_definition_file_does_not_exist` * Test `load_definition_invalid_file` - Fix `load_definition_file_does_not_exist` error * Add `Generalized Parameter Types` rule * Replace `todo!` with actual calls * Make `test_cluster` non test-only - Workaround to be used by dkg * Update module docs * Test `load_definition_valid` * Use defaults when key is missing * Test `load_definition_invalid_definition_no_verify` * Test `load_definition_invalid_definition_verify` * Add `ShareMsg` - Impl `From<&Share> for ShareMsg` * Update `Cargo.lock` * Allow unwrap in test code * Adjust visibility and docs - Make everything public - Add docs to all exposed symbols * Fix clippy lints * Use `File::create` instead of `File::open` - We need to create the file first. * Persist readonly permissions * Resolve TODOs in module docs * Prefer `AsRef<Path>` * Fix typo * Reduce visibility of utility * Use `PathBuf` for the data_dir * Simplify error handling * Add `Debug` and `Clone` to `Share` and `ShareMsg` * Fix typo & formatting * feat: make test-cluster be feature based * feat: update dkg Cargo.toml to split testing and release versions of cluster * Remove inlined definitions * Use default argument * Include payload in error messages * Inline function * Use `Zeroizing` * Add error unused payload lint --------- Co-authored-by: Bohdan Ohorodnii <35969035+varex83@users.noreply.github.com>
1 parent a2989a9 commit feacf24

File tree

12 files changed

+750
-19
lines changed

12 files changed

+750
-19
lines changed

AGENTS.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ pub type Result<T> = std::result::Result<T, ModuleError>;
132132
Rules:
133133

134134
- `errors.New("msg")` → enum variant with `#[error("msg")]` (match strings exactly).
135+
- Every field/payload in an error variant **must** appear in its `#[error("...")]` format string. If a field is not surfaced in the error message, either include it or remove it from the variant. Dead payload (captured but never displayed) is not allowed.
135136
- `errors.Wrap(err, "...")``#[from]` / `#[source]` where appropriate.
136137
- Always propagate with `?`; avoid silent `filter_map(|x| x.ok())` patterns in production code.
137138

@@ -145,6 +146,43 @@ Rules:
145146
- Prefer copying doc comments from Go and adapting to Rust conventions (avoid “Type is a …”).
146147
- Avoid leaving TODOs in merged code. If a short-lived internal note is necessary, use `// TODO:` and remove before PR merge.
147148

149+
## Generalized Parameter Types
150+
151+
Prefer generic parameters over concrete types when a function only needs the behavior of a trait. This mirrors the standard library's own conventions and makes functions callable with a wider range of inputs without extra allocations.
152+
153+
| Instead of | Prefer | Accepts |
154+
| --- | --- | --- |
155+
| `&str` | `impl AsRef<str>` | `&str`, `String`, `&String`, … |
156+
| `&Path` | `impl AsRef<Path>` | `&str`, `String`, `PathBuf`, `&Path`, … |
157+
| `&[u8]` | `impl AsRef<[u8]>` | `&[u8]`, `Vec<u8>`, arrays, … |
158+
| `&Vec<T>` | `impl AsRef<[T]>` | `Vec<T>`, slices, arrays, … |
159+
| `String` (owned, read-only) | `impl Into<String>` | `&str`, `String`, … |
160+
161+
Examples:
162+
163+
```rust
164+
// accepts &str, String, PathBuf, &Path, …
165+
fn read_file(path: impl AsRef<std::path::Path>) -> std::io::Result<String> {
166+
std::fs::read_to_string(path.as_ref())
167+
}
168+
169+
// accepts &str, String, &String, …
170+
fn print_message(msg: impl AsRef<str>) {
171+
println!(“{}”, msg.as_ref());
172+
}
173+
174+
// accepts &[u8], Vec<u8>, arrays, …
175+
fn hash_bytes(data: impl AsRef<[u8]>) -> [u8; 32] {
176+
sha256(data.as_ref())
177+
}
178+
```
179+
180+
Rules:
181+
182+
- Call `.as_ref()` once at the top of the function and bind it to a local variable when the value is used in multiple places.
183+
- Do not use `impl AsRef<T>` if the function immediately converts to an owned type anyway — use `impl Into<T>` (or just accept the owned type) in that case.
184+
- Applies to public and private functions alike; the gain is ergonomics, not just API surface.
185+
148186
## Testing
149187

150188
- Translate Go tests to Rust where applicable; keep similar test names for cross-reference.

Cargo.lock

Lines changed: 14 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/cluster/Cargo.toml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ pluto-core.workspace = true
1919
pluto-crypto.workspace = true
2020
pluto-eth2api.workspace = true
2121
thiserror.workspace = true
22-
rand_core.workspace = true
22+
rand.workspace = true
2323
libp2p.workspace = true
2424
pluto-p2p.workspace = true
2525
pluto-eth2util.workspace = true
@@ -29,16 +29,21 @@ pluto-ssz.workspace = true
2929
k256.workspace = true
3030
tokio.workspace = true
3131
reqwest = { workspace = true, features = ["json"] }
32+
# Workaround to use test code from different crate.
33+
# See: https://github.com/NethermindEth/pluto/pull/285
34+
pluto-testutil = { workspace = true, optional = true }
3235

3336
[build-dependencies]
3437
prost-build.workspace = true
3538

3639
[dev-dependencies]
3740
test-case.workspace = true
3841
pluto-testutil.workspace = true
39-
rand.workspace = true
4042
tempfile.workspace = true
4143
wiremock.workspace = true
4244

4345
[lints]
4446
workspace = true
47+
48+
[features]
49+
test-cluster = ["dep:pluto-testutil"]

crates/cluster/src/definition.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -934,10 +934,12 @@ pub struct DefinitionV1x2or3 {
934934
/// Config hash uniquely identifies a cluster definition excluding operator
935935
/// ENRs and signatures.
936936
#[serde_as(as = "Hex0x")]
937+
#[serde(default)]
937938
pub config_hash: Vec<u8>,
938939
/// Definition hash uniquely identifies a cluster definition including
939940
/// operator ENRs and signatures.
940941
#[serde_as(as = "Hex0x")]
942+
#[serde(default)]
941943
pub definition_hash: Vec<u8>,
942944
}
943945

@@ -1048,10 +1050,12 @@ pub struct DefinitionV1x4 {
10481050
/// Config hash uniquely identifies a cluster definition excluding operator
10491051
/// ENRs and signatures.
10501052
#[serde_as(as = "Hex0x")]
1053+
#[serde(default)]
10511054
pub config_hash: Vec<u8>,
10521055
/// Definition hash uniquely identifies a cluster definition including
10531056
/// operator ENRs and signatures.
10541057
#[serde_as(as = "Hex0x")]
1058+
#[serde(default)]
10551059
pub definition_hash: Vec<u8>,
10561060
}
10571061

@@ -1160,10 +1164,12 @@ pub struct DefinitionV1x5to7 {
11601164
/// Config hash uniquely identifies a cluster definition excluding operator
11611165
/// ENRs and signatures.
11621166
#[serde_as(as = "Hex0x")]
1167+
#[serde(default)]
11631168
pub config_hash: Vec<u8>,
11641169
/// Definition hash uniquely identifies a cluster definition including
11651170
/// operator ENRs and signatures.
11661171
#[serde_as(as = "Hex0x")]
1172+
#[serde(default)]
11671173
pub definition_hash: Vec<u8>,
11681174
}
11691175

@@ -1261,10 +1267,12 @@ pub struct DefinitionV1x8 {
12611267
/// ConfigHash uniquely identifies a cluster definition excluding operator
12621268
/// ENRs and signatures.
12631269
#[serde_as(as = "Hex0x")]
1270+
#[serde(default)]
12641271
pub config_hash: Vec<u8>,
12651272
/// DefinitionHash uniquely identifies a cluster definition including
12661273
/// operator ENRs and signatures.
12671274
#[serde_as(as = "Hex0x")]
1275+
#[serde(default)]
12681276
pub definition_hash: Vec<u8>,
12691277
}
12701278

@@ -1366,10 +1374,12 @@ pub struct DefinitionV1x9 {
13661374
/// ConfigHash uniquely identifies a cluster definition excluding operator
13671375
/// ENRs and signatures.
13681376
#[serde_as(as = "Hex0x")]
1377+
#[serde(default)]
13691378
pub config_hash: Vec<u8>,
13701379
/// DefinitionHash uniquely identifies a cluster definition including
13711380
/// operator ENRs and signatures.
13721381
#[serde_as(as = "Hex0x")]
1382+
#[serde(default)]
13731383
pub definition_hash: Vec<u8>,
13741384
}
13751385

@@ -1478,10 +1488,12 @@ pub struct DefinitionV1x10 {
14781488
/// Config hash uniquely identifies a cluster definition excluding operator
14791489
/// ENRs and signatures.
14801490
#[serde_as(as = "Hex0x")]
1491+
#[serde(default)]
14811492
pub config_hash: Vec<u8>,
14821493
/// Definition hash uniquely identifies a cluster definition including
14831494
/// operator ENRs and signatures.
14841495
#[serde_as(as = "Hex0x")]
1496+
#[serde(default)]
14851497
pub definition_hash: Vec<u8>,
14861498
}
14871499

crates/cluster/src/helpers.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,10 @@ pub async fn fetch_definition(
6565
/// Creates a new directory for validator keys.
6666
/// If the directory "validator_keys" exists, it checks if the directory is
6767
/// empty.
68-
pub async fn create_validator_keys_dir(parent_dir: &std::path::Path) -> std::io::Result<PathBuf> {
69-
let vk_dir = parent_dir.join("validator_keys");
68+
pub async fn create_validator_keys_dir(
69+
parent_dir: impl AsRef<std::path::Path>,
70+
) -> std::io::Result<PathBuf> {
71+
let vk_dir = parent_dir.as_ref().join("validator_keys");
7072

7173
if let Err(e) = tokio::fs::create_dir(&vk_dir).await {
7274
if e.kind() != std::io::ErrorKind::AlreadyExists {

crates/cluster/src/lib.rs

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,30 +4,37 @@
44
//! This crate handles the formation, management, and coordination of validator
55
//! clusters in the Charon network.
66
7-
/// Cluster definition management and coordination.
7+
/// `Definition` type representing the intended cluster configuration
8+
/// (operators, validators, fork version) with EIP-712 hashing and verification.
89
pub mod definition;
9-
/// Cluster deposit management and coordination.
10+
/// `DepositData` type for activating validators.
1011
pub mod deposit;
11-
/// Cluster distributed validator management and coordination.
12+
/// `DistValidator` type representing a distributed validator with its group
13+
/// public key, per-node public shares, and deposit data.
1214
pub mod distvalidator;
13-
/// Cluster EIP-712 signatures management and coordination.
15+
/// EIP-712 typed data construction and signing for cluster definition config
16+
/// hashes and operator ENR signatures.
1417
pub mod eip712sigs;
15-
/// Cluster helpers management and coordination.
18+
/// General helper utilities.
1619
pub mod helpers;
17-
/// Cluster lock management and coordination.
20+
/// `Lock` type representing the finalized cluster configuration, including
21+
/// distributed validators and node signatures.
1822
pub mod lock;
19-
/// Manifest
23+
/// Cluster manifest types, loading, mutation, and materialization.
2024
pub mod manifest;
21-
/// Manifest protocol buffers.
25+
/// Generated protobuf types for the cluster manifest (v1).
2226
pub mod manifestpb;
23-
/// Cluster operator management and coordination.
27+
/// `Operator` type representing a charon node operator with Ethereum address,
28+
/// ENR, and config/ENR signatures.
2429
pub mod operator;
25-
/// Cluster registration management and coordination.
30+
/// `BuilderRegistration` and `Registration` types for pre-generated signed
31+
/// validator registrations sent to the builder network.
2632
pub mod registration;
27-
/// Cluster SSZ management and coordination.
33+
/// SSZ serialization for various cluster types.
2834
pub mod ssz;
29-
/// Cluster test cluster management and coordination.
30-
#[cfg(test)]
35+
/// Factory for constructing deterministic or random cluster locks for use in
36+
/// tests.
37+
#[cfg(any(test, feature = "test-cluster"))]
3138
pub mod test_cluster;
32-
/// Cluster version management and coordination.
39+
/// Supported cluster definition version constants and feature-flag helpers.
3340
pub mod version;

crates/cluster/src/test_cluster.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#![allow(clippy::unwrap_used, reason = "test code")]
2+
13
use crate::{definition, distvalidator, helpers, lock, operator, registration, version};
24
use chrono::{TimeZone, Utc};
35
use pluto_crypto::tbls::Tbls;

crates/dkg/Cargo.toml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,26 @@ publish.workspace = true
99
[dependencies]
1010
prost.workspace = true
1111
prost-types.workspace = true
12+
pluto-cluster.workspace = true
13+
pluto-crypto.workspace = true
14+
pluto-eth1wrap.workspace = true
15+
pluto-eth2util.workspace = true
16+
hex.workspace = true
17+
rand.workspace = true
18+
serde.workspace = true
19+
serde_json.workspace = true
20+
thiserror.workspace = true
21+
tokio.workspace = true
22+
tracing.workspace = true
23+
url.workspace = true
24+
zeroize.workspace = true
1225

1326
[build-dependencies]
1427
pluto-build-proto.workspace = true
1528

29+
[dev-dependencies]
30+
pluto-cluster = { workspace = true, features = ["test-cluster"] }
31+
tempfile.workspace = true
32+
1633
[lints]
1734
workspace = true

0 commit comments

Comments
 (0)