From 2f3b79e3af2eeb2560ab3e184d181954b31ed032 Mon Sep 17 00:00:00 2001 From: Bohdan Ohorodnii <35969035+varex83@users.noreply.github.com> Date: Wed, 8 Oct 2025 15:45:14 +0800 Subject: [PATCH 01/13] feat: setup initial project structure --- Cargo.toml | 15 ++-- cargo-generate.toml | 14 ---- {examples => crates/charon-cli}/Cargo.toml | 9 +-- crates/charon-cli/src/lib.rs | 40 +++++++++++ crates/charon-cluster/Cargo.toml | 12 ++++ crates/charon-cluster/src/lib.rs | 39 +++++++++++ crates/charon-consensus/Cargo.toml | 12 ++++ crates/charon-consensus/src/lib.rs | 39 +++++++++++ crates/charon-core/Cargo.toml | 12 ++++ crates/charon-core/src/lib.rs | 39 +++++++++++ crates/charon-crypto/Cargo.toml | 12 ++++ crates/charon-crypto/src/lib.rs | 39 +++++++++++ crates/charon-db/Cargo.toml | 12 ++++ crates/charon-db/src/lib.rs | 39 +++++++++++ crates/charon-dkg/Cargo.toml | 12 ++++ crates/charon-dkg/src/lib.rs | 39 +++++++++++ crates/charon-errors/Cargo.toml | 12 ++++ crates/charon-errors/src/lib.rs | 39 +++++++++++ crates/charon-eth2/Cargo.toml | 12 ++++ crates/charon-eth2/src/lib.rs | 39 +++++++++++ crates/charon-observability/Cargo.toml | 12 ++++ crates/charon-observability/src/lib.rs | 39 +++++++++++ crates/charon-p2p/Cargo.toml | 12 ++++ crates/charon-p2p/src/lib.rs | 39 +++++++++++ crates/charon-serde/Cargo.toml | 12 ++++ crates/charon-serde/src/lib.rs | 39 +++++++++++ crates/charon-testutil/Cargo.toml | 12 ++++ crates/charon-testutil/src/lib.rs | 39 +++++++++++ crates/charon-types/Cargo.toml | 12 ++++ crates/charon-types/src/lib.rs | 39 +++++++++++ crates/charon/Cargo.toml | 12 ++++ crates/charon/src/lib.rs | 39 +++++++++++ examples/README.md | 12 ---- examples/src/bin/addition.rs | 6 -- katex-header.html | 15 ---- rust-toolchain.toml | 3 +- template_crate/Cargo.toml | 24 ------- template_crate/README.md | 3 - template_crate/doc/mainpage-doc.md | 1 - template_crate/src/lib.rs | 81 ---------------------- 40 files changed, 765 insertions(+), 172 deletions(-) delete mode 100644 cargo-generate.toml rename {examples => crates/charon-cli}/Cargo.toml (51%) create mode 100644 crates/charon-cli/src/lib.rs create mode 100644 crates/charon-cluster/Cargo.toml create mode 100644 crates/charon-cluster/src/lib.rs create mode 100644 crates/charon-consensus/Cargo.toml create mode 100644 crates/charon-consensus/src/lib.rs create mode 100644 crates/charon-core/Cargo.toml create mode 100644 crates/charon-core/src/lib.rs create mode 100644 crates/charon-crypto/Cargo.toml create mode 100644 crates/charon-crypto/src/lib.rs create mode 100644 crates/charon-db/Cargo.toml create mode 100644 crates/charon-db/src/lib.rs create mode 100644 crates/charon-dkg/Cargo.toml create mode 100644 crates/charon-dkg/src/lib.rs create mode 100644 crates/charon-errors/Cargo.toml create mode 100644 crates/charon-errors/src/lib.rs create mode 100644 crates/charon-eth2/Cargo.toml create mode 100644 crates/charon-eth2/src/lib.rs create mode 100644 crates/charon-observability/Cargo.toml create mode 100644 crates/charon-observability/src/lib.rs create mode 100644 crates/charon-p2p/Cargo.toml create mode 100644 crates/charon-p2p/src/lib.rs create mode 100644 crates/charon-serde/Cargo.toml create mode 100644 crates/charon-serde/src/lib.rs create mode 100644 crates/charon-testutil/Cargo.toml create mode 100644 crates/charon-testutil/src/lib.rs create mode 100644 crates/charon-types/Cargo.toml create mode 100644 crates/charon-types/src/lib.rs create mode 100644 crates/charon/Cargo.toml create mode 100644 crates/charon/src/lib.rs delete mode 100644 examples/README.md delete mode 100644 examples/src/bin/addition.rs delete mode 100644 katex-header.html delete mode 100644 template_crate/Cargo.toml delete mode 100644 template_crate/README.md delete mode 100644 template_crate/doc/mainpage-doc.md delete mode 100644 template_crate/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 45ca2194..2704e388 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,16 +1,13 @@ - -# TODO(template) update for the crate name [workspace] -members = ["examples", "template_crate"] +name = "charon-rs" +members = [ "crates/charon", "crates/charon-cli", "crates/charon-cluster", "crates/charon-consensus", "crates/charon-core", "crates/charon-crypto", "crates/charon-db", "crates/charon-dkg", "crates/charon-errors", "crates/charon-eth2", "crates/charon-observability", "crates/charon-p2p", "crates/charon-serde", "crates/charon-testutil", "crates/charon-types"] resolver = "3" -default-members = ["template_crate"] [workspace.package] version = "0.1.0" edition = "2024" -# TODO(template) update for your repository -repository = "https://github.com/NethermindEth/rust-template" -license = "Apache-2.0" # TODO(template) update license if needed +repository = "https://github.com/NethermindEth/charon-rs" +license = "Apache-2.0" # TODO(template) update license publish = false [workspace.dependencies] @@ -18,10 +15,6 @@ proptest = "1" [workspace.lints.rust] missing_docs = "deny" -# TODO(template) if you use any unsafe code, then running miri in CI is a must -# if for a crate in the workspace you need an unsafe code -# prefer to preserve forbid for the workspace -# and allowing only at a specific crate level unsafe_code = "forbid" [workspace.lints.clippy] diff --git a/cargo-generate.toml b/cargo-generate.toml deleted file mode 100644 index 12c6a7c4..00000000 --- a/cargo-generate.toml +++ /dev/null @@ -1,14 +0,0 @@ -[template] -name = "rust-template" -description = "A template for a new Rust project" -author = "Nethermind" -license = "Apache-2.0" -cargo_generate_version = ">=0.23.0" - -# Exclude files from the placeholder generated for the template -exclude = [ - ".github/workflows/dependency-audit.yml", - ".github/workflows/coverage-pr.yml", - ".github/workflows/semver-checks.yml", - "target", -] diff --git a/examples/Cargo.toml b/crates/charon-cli/Cargo.toml similarity index 51% rename from examples/Cargo.toml rename to crates/charon-cli/Cargo.toml index b8924ed3..37a9cea3 100644 --- a/examples/Cargo.toml +++ b/crates/charon-cli/Cargo.toml @@ -1,11 +1,12 @@ [package] -name = "examples" -publish = false +name = "charon-cli" version.workspace = true edition.workspace = true repository.workspace = true license.workspace = true +publish.workspace = true [dependencies] -# TODO(template) update the crate name -template_crate = { path = "../template_crate" } + +[lints] +workspace = true diff --git a/crates/charon-cli/src/lib.rs b/crates/charon-cli/src/lib.rs new file mode 100644 index 00000000..5cfbd16e --- /dev/null +++ b/crates/charon-cli/src/lib.rs @@ -0,0 +1,40 @@ + +//! # Charon CLI +//! +//! Command-line interface for the Charon distributed validator node. +//! This crate provides the CLI tools and commands for managing and operating +//! Charon validator nodes. + +/// Adds two numbers together. +/// +/// # Arguments +/// +/// * `left` - The first number to add +/// * `right` - The second number to add +/// +/// # Returns +/// +/// The sum of the two numbers +/// +/// # Examples +/// +/// ``` +/// use charon_cli::add; +/// +/// let result = add(2, 2); +/// assert_eq!(result, 4); +/// ``` +pub fn add(left: u64, right: u64) -> u64 { + left.wrapping_add(right) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/crates/charon-cluster/Cargo.toml b/crates/charon-cluster/Cargo.toml new file mode 100644 index 00000000..7cfc7292 --- /dev/null +++ b/crates/charon-cluster/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "charon-cluster" +version.workspace = true +edition.workspace = true +repository.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] + +[lints] +workspace = true diff --git a/crates/charon-cluster/src/lib.rs b/crates/charon-cluster/src/lib.rs new file mode 100644 index 00000000..7050d743 --- /dev/null +++ b/crates/charon-cluster/src/lib.rs @@ -0,0 +1,39 @@ +//! # Charon Cluster +//! +//! Cluster management and coordination for Charon distributed validator nodes. +//! This crate handles the formation, management, and coordination of validator +//! clusters in the Charon network. + +/// Adds two numbers together. +/// +/// # Arguments +/// +/// * `left` - The first number to add +/// * `right` - The second number to add +/// +/// # Returns +/// +/// The sum of the two numbers +/// +/// # Examples +/// +/// ``` +/// use charon_cluster::add; +/// +/// let result = add(2, 2); +/// assert_eq!(result, 4); +/// ``` +pub fn add(left: u64, right: u64) -> u64 { + left.wrapping_add(right) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/crates/charon-consensus/Cargo.toml b/crates/charon-consensus/Cargo.toml new file mode 100644 index 00000000..b95b8877 --- /dev/null +++ b/crates/charon-consensus/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "charon-consensus" +version.workspace = true +edition.workspace = true +repository.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] + +[lints] +workspace = true diff --git a/crates/charon-consensus/src/lib.rs b/crates/charon-consensus/src/lib.rs new file mode 100644 index 00000000..cd038604 --- /dev/null +++ b/crates/charon-consensus/src/lib.rs @@ -0,0 +1,39 @@ +//! # Charon Consensus +//! +//! Consensus mechanisms and protocols for Charon distributed validator nodes. +//! This crate implements the consensus algorithms and protocols required for +//! coordinating validator operations across the distributed network. + +/// Adds two numbers together. +/// +/// # Arguments +/// +/// * `left` - The first number to add +/// * `right` - The second number to add +/// +/// # Returns +/// +/// The sum of the two numbers +/// +/// # Examples +/// +/// ``` +/// use charon_consensus::add; +/// +/// let result = add(2, 2); +/// assert_eq!(result, 4); +/// ``` +pub fn add(left: u64, right: u64) -> u64 { + left.wrapping_add(right) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/crates/charon-core/Cargo.toml b/crates/charon-core/Cargo.toml new file mode 100644 index 00000000..3b92ed1a --- /dev/null +++ b/crates/charon-core/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "charon-core" +version.workspace = true +edition.workspace = true +repository.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] + +[lints] +workspace = true diff --git a/crates/charon-core/src/lib.rs b/crates/charon-core/src/lib.rs new file mode 100644 index 00000000..a3ed9a9d --- /dev/null +++ b/crates/charon-core/src/lib.rs @@ -0,0 +1,39 @@ +//! # Charon Core +//! +//! Core functionality and utilities for the Charon distributed validator node. +//! This crate provides the fundamental building blocks, data structures, and +//! core algorithms used throughout the Charon system. + +/// Adds two numbers together. +/// +/// # Arguments +/// +/// * `left` - The first number to add +/// * `right` - The second number to add +/// +/// # Returns +/// +/// The sum of the two numbers +/// +/// # Examples +/// +/// ``` +/// use charon_core::add; +/// +/// let result = add(2, 2); +/// assert_eq!(result, 4); +/// ``` +pub fn add(left: u64, right: u64) -> u64 { + left.wrapping_add(right) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/crates/charon-crypto/Cargo.toml b/crates/charon-crypto/Cargo.toml new file mode 100644 index 00000000..0787a13c --- /dev/null +++ b/crates/charon-crypto/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "charon-crypto" +version.workspace = true +edition.workspace = true +repository.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] + +[lints] +workspace = true diff --git a/crates/charon-crypto/src/lib.rs b/crates/charon-crypto/src/lib.rs new file mode 100644 index 00000000..4a219bc9 --- /dev/null +++ b/crates/charon-crypto/src/lib.rs @@ -0,0 +1,39 @@ +//! # Charon Crypto +//! +//! Cryptographic primitives and utilities for the Charon distributed validator node. +//! This crate provides cryptographic functions, key management, and security +//! operations required for distributed validator operations. + +/// Adds two numbers together. +/// +/// # Arguments +/// +/// * `left` - The first number to add +/// * `right` - The second number to add +/// +/// # Returns +/// +/// The sum of the two numbers +/// +/// # Examples +/// +/// ``` +/// use charon_crypto::add; +/// +/// let result = add(2, 2); +/// assert_eq!(result, 4); +/// ``` +pub fn add(left: u64, right: u64) -> u64 { + left.wrapping_add(right) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/crates/charon-db/Cargo.toml b/crates/charon-db/Cargo.toml new file mode 100644 index 00000000..e0d33eb6 --- /dev/null +++ b/crates/charon-db/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "charon-db" +version.workspace = true +edition.workspace = true +repository.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] + +[lints] +workspace = true diff --git a/crates/charon-db/src/lib.rs b/crates/charon-db/src/lib.rs new file mode 100644 index 00000000..b3317219 --- /dev/null +++ b/crates/charon-db/src/lib.rs @@ -0,0 +1,39 @@ +//! # Charon Database +//! +//! Database layer and persistence for the Charon distributed validator node. +//! This crate provides database abstractions, storage backends, and data +//! persistence mechanisms for validator state and operations. + +/// Adds two numbers together. +/// +/// # Arguments +/// +/// * `left` - The first number to add +/// * `right` - The second number to add +/// +/// # Returns +/// +/// The sum of the two numbers +/// +/// # Examples +/// +/// ``` +/// use charon_db::add; +/// +/// let result = add(2, 2); +/// assert_eq!(result, 4); +/// ``` +pub fn add(left: u64, right: u64) -> u64 { + left.wrapping_add(right) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/crates/charon-dkg/Cargo.toml b/crates/charon-dkg/Cargo.toml new file mode 100644 index 00000000..07652833 --- /dev/null +++ b/crates/charon-dkg/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "charon-dkg" +version.workspace = true +edition.workspace = true +repository.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] + +[lints] +workspace = true diff --git a/crates/charon-dkg/src/lib.rs b/crates/charon-dkg/src/lib.rs new file mode 100644 index 00000000..907ce5f9 --- /dev/null +++ b/crates/charon-dkg/src/lib.rs @@ -0,0 +1,39 @@ +//! # Charon DKG +//! +//! Distributed Key Generation (DKG) protocols for Charon distributed validator nodes. +//! This crate implements the cryptographic protocols required for generating, +//! distributing, and managing validator keys across the distributed network. + +/// Adds two numbers together. +/// +/// # Arguments +/// +/// * `left` - The first number to add +/// * `right` - The second number to add +/// +/// # Returns +/// +/// The sum of the two numbers +/// +/// # Examples +/// +/// ``` +/// use charon_dkg::add; +/// +/// let result = add(2, 2); +/// assert_eq!(result, 4); +/// ``` +pub fn add(left: u64, right: u64) -> u64 { + left.wrapping_add(right) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/crates/charon-errors/Cargo.toml b/crates/charon-errors/Cargo.toml new file mode 100644 index 00000000..322b8b77 --- /dev/null +++ b/crates/charon-errors/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "charon-errors" +version.workspace = true +edition.workspace = true +repository.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] + +[lints] +workspace = true diff --git a/crates/charon-errors/src/lib.rs b/crates/charon-errors/src/lib.rs new file mode 100644 index 00000000..a8fe4963 --- /dev/null +++ b/crates/charon-errors/src/lib.rs @@ -0,0 +1,39 @@ +//! # Charon Errors +//! +//! Error types and error handling utilities for the Charon distributed validator node. +//! This crate defines the error types, error codes, and error handling mechanisms +//! used throughout the Charon system. + +/// Adds two numbers together. +/// +/// # Arguments +/// +/// * `left` - The first number to add +/// * `right` - The second number to add +/// +/// # Returns +/// +/// The sum of the two numbers +/// +/// # Examples +/// +/// ``` +/// use charon_errors::add; +/// +/// let result = add(2, 2); +/// assert_eq!(result, 4); +/// ``` +pub fn add(left: u64, right: u64) -> u64 { + left.wrapping_add(right) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/crates/charon-eth2/Cargo.toml b/crates/charon-eth2/Cargo.toml new file mode 100644 index 00000000..0474ba6f --- /dev/null +++ b/crates/charon-eth2/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "charon-eth2" +version.workspace = true +edition.workspace = true +repository.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] + +[lints] +workspace = true diff --git a/crates/charon-eth2/src/lib.rs b/crates/charon-eth2/src/lib.rs new file mode 100644 index 00000000..eb124245 --- /dev/null +++ b/crates/charon-eth2/src/lib.rs @@ -0,0 +1,39 @@ +//! # Charon ETH2 +//! +//! Ethereum 2.0 integration and utilities for the Charon distributed validator node. +//! This crate provides interfaces, types, and utilities for interacting with +//! Ethereum 2.0 networks and validator operations. + +/// Adds two numbers together. +/// +/// # Arguments +/// +/// * `left` - The first number to add +/// * `right` - The second number to add +/// +/// # Returns +/// +/// The sum of the two numbers +/// +/// # Examples +/// +/// ``` +/// use charon_eth2::add; +/// +/// let result = add(2, 2); +/// assert_eq!(result, 4); +/// ``` +pub fn add(left: u64, right: u64) -> u64 { + left.wrapping_add(right) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/crates/charon-observability/Cargo.toml b/crates/charon-observability/Cargo.toml new file mode 100644 index 00000000..7e69b12f --- /dev/null +++ b/crates/charon-observability/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "charon-observability" +version.workspace = true +edition.workspace = true +repository.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] + +[lints] +workspace = true diff --git a/crates/charon-observability/src/lib.rs b/crates/charon-observability/src/lib.rs new file mode 100644 index 00000000..ae536441 --- /dev/null +++ b/crates/charon-observability/src/lib.rs @@ -0,0 +1,39 @@ +//! # Charon Observability +//! +//! Observability and monitoring utilities for the Charon distributed validator node. +//! This crate provides logging, metrics, tracing, and monitoring capabilities +//! for tracking and debugging validator operations. + +/// Adds two numbers together. +/// +/// # Arguments +/// +/// * `left` - The first number to add +/// * `right` - The second number to add +/// +/// # Returns +/// +/// The sum of the two numbers +/// +/// # Examples +/// +/// ``` +/// use charon_observability::add; +/// +/// let result = add(2, 2); +/// assert_eq!(result, 4); +/// ``` +pub fn add(left: u64, right: u64) -> u64 { + left.wrapping_add(right) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/crates/charon-p2p/Cargo.toml b/crates/charon-p2p/Cargo.toml new file mode 100644 index 00000000..0795dc28 --- /dev/null +++ b/crates/charon-p2p/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "charon-p2p" +version.workspace = true +edition.workspace = true +repository.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] + +[lints] +workspace = true diff --git a/crates/charon-p2p/src/lib.rs b/crates/charon-p2p/src/lib.rs new file mode 100644 index 00000000..083992d0 --- /dev/null +++ b/crates/charon-p2p/src/lib.rs @@ -0,0 +1,39 @@ +//! # Charon P2P +//! +//! Peer-to-peer networking and communication for the Charon distributed validator node. +//! This crate provides networking protocols, peer discovery, and communication +//! mechanisms for validator nodes to coordinate and exchange information. + +/// Adds two numbers together. +/// +/// # Arguments +/// +/// * `left` - The first number to add +/// * `right` - The second number to add +/// +/// # Returns +/// +/// The sum of the two numbers +/// +/// # Examples +/// +/// ``` +/// use charon_p2p::add; +/// +/// let result = add(2, 2); +/// assert_eq!(result, 4); +/// ``` +pub fn add(left: u64, right: u64) -> u64 { + left.wrapping_add(right) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/crates/charon-serde/Cargo.toml b/crates/charon-serde/Cargo.toml new file mode 100644 index 00000000..1d33e20f --- /dev/null +++ b/crates/charon-serde/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "charon-serde" +version.workspace = true +edition.workspace = true +repository.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] + +[lints] +workspace = true diff --git a/crates/charon-serde/src/lib.rs b/crates/charon-serde/src/lib.rs new file mode 100644 index 00000000..ca27ddb4 --- /dev/null +++ b/crates/charon-serde/src/lib.rs @@ -0,0 +1,39 @@ +//! # Charon Serde +//! +//! Serialization and deserialization utilities for the Charon distributed validator node. +//! This crate provides custom serialization logic, format support, and data +//! transformation utilities for validator operations and communication. + +/// Adds two numbers together. +/// +/// # Arguments +/// +/// * `left` - The first number to add +/// * `right` - The second number to add +/// +/// # Returns +/// +/// The sum of the two numbers +/// +/// # Examples +/// +/// ``` +/// use charon_serde::add; +/// +/// let result = add(2, 2); +/// assert_eq!(result, 4); +/// ``` +pub fn add(left: u64, right: u64) -> u64 { + left.wrapping_add(right) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/crates/charon-testutil/Cargo.toml b/crates/charon-testutil/Cargo.toml new file mode 100644 index 00000000..8959f5f0 --- /dev/null +++ b/crates/charon-testutil/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "charon-testutil" +version.workspace = true +edition.workspace = true +repository.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] + +[lints] +workspace = true diff --git a/crates/charon-testutil/src/lib.rs b/crates/charon-testutil/src/lib.rs new file mode 100644 index 00000000..3e8f9f9d --- /dev/null +++ b/crates/charon-testutil/src/lib.rs @@ -0,0 +1,39 @@ +//! # Charon Test Utilities +//! +//! Testing utilities and mock implementations for the Charon distributed validator node. +//! This crate provides test helpers, mock objects, and testing utilities +//! for unit tests, integration tests, and development. + +/// Adds two numbers together. +/// +/// # Arguments +/// +/// * `left` - The first number to add +/// * `right` - The second number to add +/// +/// # Returns +/// +/// The sum of the two numbers +/// +/// # Examples +/// +/// ``` +/// use charon_testutil::add; +/// +/// let result = add(2, 2); +/// assert_eq!(result, 4); +/// ``` +pub fn add(left: u64, right: u64) -> u64 { + left.wrapping_add(right) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/crates/charon-types/Cargo.toml b/crates/charon-types/Cargo.toml new file mode 100644 index 00000000..0851da72 --- /dev/null +++ b/crates/charon-types/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "charon-types" +version.workspace = true +edition.workspace = true +repository.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] + +[lints] +workspace = true diff --git a/crates/charon-types/src/lib.rs b/crates/charon-types/src/lib.rs new file mode 100644 index 00000000..e2e368ee --- /dev/null +++ b/crates/charon-types/src/lib.rs @@ -0,0 +1,39 @@ +//! # Charon Types +//! +//! Common types and data structures for the Charon distributed validator node. +//! This crate defines the core types, enums, and data structures used +//! throughout the Charon system for type safety and consistency. + +/// Adds two numbers together. +/// +/// # Arguments +/// +/// * `left` - The first number to add +/// * `right` - The second number to add +/// +/// # Returns +/// +/// The sum of the two numbers +/// +/// # Examples +/// +/// ``` +/// use charon_types::add; +/// +/// let result = add(2, 2); +/// assert_eq!(result, 4); +/// ``` +pub fn add(left: u64, right: u64) -> u64 { + left.wrapping_add(right) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/crates/charon/Cargo.toml b/crates/charon/Cargo.toml new file mode 100644 index 00000000..fc437a8a --- /dev/null +++ b/crates/charon/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "charon" +version.workspace = true +edition.workspace = true +repository.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] + +[lints] +workspace = true diff --git a/crates/charon/src/lib.rs b/crates/charon/src/lib.rs new file mode 100644 index 00000000..1359319b --- /dev/null +++ b/crates/charon/src/lib.rs @@ -0,0 +1,39 @@ +//! # Charon +//! +//! The main Charon library providing distributed validator key management and coordination +//! for Ethereum 2.0 validators. This crate serves as the primary entry point for the +//! Charon distributed validator node implementation. + +/// Adds two numbers together. +/// +/// # Arguments +/// +/// * `left` - The first number to add +/// * `right` - The second number to add +/// +/// # Returns +/// +/// The sum of the two numbers +/// +/// # Examples +/// +/// ``` +/// use charon::add; +/// +/// let result = add(2, 2); +/// assert_eq!(result, 4); +/// ``` +pub fn add(left: u64, right: u64) -> u64 { + left.wrapping_add(right) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/examples/README.md b/examples/README.md deleted file mode 100644 index d2886b2b..00000000 --- a/examples/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# Examples - -TODO(template) describe your examples here - -To run an example - -```sh -# TODO(template) update an example name -cargo run -p examples --bin addition -``` - -* `addition` - shows how to add two bounded integers with the [`template_crate`](../template_crate/) library. \ No newline at end of file diff --git a/examples/src/bin/addition.rs b/examples/src/bin/addition.rs deleted file mode 100644 index 5d90fb75..00000000 --- a/examples/src/bin/addition.rs +++ /dev/null @@ -1,6 +0,0 @@ -// TODO(template) - remove/change the code below and rename the example -use template_crate::add_small_integers; - -fn main() { - println!("{:?}", add_small_integers(6, 8)); -} diff --git a/katex-header.html b/katex-header.html deleted file mode 100644 index 32ac35a4..00000000 --- a/katex-header.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 73cb934d..2936be92 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,4 @@ [toolchain] channel = "stable" -components = ["rustfmt", "clippy"] +version = "1.89.0" +components = ["rustfmt", "clippy"] \ No newline at end of file diff --git a/template_crate/Cargo.toml b/template_crate/Cargo.toml deleted file mode 100644 index e0b327d2..00000000 --- a/template_crate/Cargo.toml +++ /dev/null @@ -1,24 +0,0 @@ -[package] -# TODO(template) rename -name = "template_crate" -version.workspace = true -edition.workspace = true -repository.workspace = true -license.workspace = true - -[dependencies] -# TODO(template) prefer workspace level dependencies -# to keep DRY if you have a binary crate -# Use `default-features = false` -# and explicitly list features to prevent code bloat and -# code break after upgrades -thiserror = { version = "1.0", default-features = false } - -[dev-dependencies] -proptest = { workspace = true } - -# TODO(template) -# don't forget to put this at every crate -# to inherit workspace's lints -[lints] -workspace = true diff --git a/template_crate/README.md b/template_crate/README.md deleted file mode 100644 index 80857559..00000000 --- a/template_crate/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# TODO(template) update crate name here - -TODO(template) A short description of this specific crate \ No newline at end of file diff --git a/template_crate/doc/mainpage-doc.md b/template_crate/doc/mainpage-doc.md deleted file mode 100644 index e86bdddc..00000000 --- a/template_crate/doc/mainpage-doc.md +++ /dev/null @@ -1 +0,0 @@ -# TODO(template) main doc section heading \ No newline at end of file diff --git a/template_crate/src/lib.rs b/template_crate/src/lib.rs deleted file mode 100644 index f3b62def..00000000 --- a/template_crate/src/lib.rs +++ /dev/null @@ -1,81 +0,0 @@ -// TODO(template) additional documentation files -#![doc = include_str!("../doc/mainpage-doc.md")] - -// TODO(template) - remove/change the code below - -use thiserror::Error; - -/// Parameter error -#[derive(Debug, Error, PartialEq)] -pub enum ParameterError { - /// One of the arguments is greater than the `UPPER_BOUND` - #[error("the integer {0} is too large")] - TooLarge(u8), -} - -/// For constants prefer to provide -/// a reference to a paper section -/// where the constant is defined -/// or justify the value logically -pub const UPPER_BOUND: u8 = 2u8.pow(7); - -/// Adds two small integers together. -pub fn add_small_integers(a: u8, b: u8) -> Result { - if a.max(b) >= UPPER_BOUND { - return Err(ParameterError::TooLarge(a.max(b))); - } - Ok(a.checked_add(b) - .expect("the upper bound ensures non-overflow")) -} - -/// Subtracts one small integer from another. -/// This function is intentionally not used in tests to reduce code coverage. -pub fn sub_small_integers(a: u8, b: u8) -> Result { - if a.max(b) >= UPPER_BOUND { - return Err(ParameterError::TooLarge(a.max(b))); - } - - if b > a { - return Err(ParameterError::TooLarge(b)); - } - - Ok(a.checked_sub(b) - .expect("the upper bound ensures non-overflow")) -} - -#[cfg(test)] -mod tests { - use super::*; - use proptest::proptest; - - #[test] - fn addition_of_bounded() { - assert_eq!(Ok(8), add_small_integers(3, 5)); - } - - #[test] - fn addition_bound_check() { - assert_eq!( - Err(ParameterError::TooLarge(200)), - add_small_integers(200, 5) - ); - } - - #[test] - fn addition_edge_case() { - assert_eq!( - Ok(254), - add_small_integers(UPPER_BOUND - 1, UPPER_BOUND - 1) - ); - } - - proptest! { - #[test] - fn addition_proptest(a in 0_u8..20, b in 0_u8..20) { - assert_eq!( - a.checked_add(b), - add_small_integers(a, b).ok() - ); - } - } -} From a05777180aa78bb547d11c13a56f350f3ae9cd74 Mon Sep 17 00:00:00 2001 From: Bohdan Ohorodnii <35969035+varex83@users.noreply.github.com> Date: Wed, 8 Oct 2025 15:47:39 +0800 Subject: [PATCH 02/13] feat: update CI to use nightly toolchain for rustfmt --- .github/workflows/linter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 2f437c36..4a0b061d 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -14,7 +14,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Run Fmt - run: cargo fmt --all -- --check + run: cargo +nightly fmt --all -- --check - name: Run Clippy run: cargo clippy --all-targets --all-features From 05287a52ede17f87528994a9b48782454c13ce8e Mon Sep 17 00:00:00 2001 From: Bohdan Ohorodnii <35969035+varex83@users.noreply.github.com> Date: Wed, 8 Oct 2025 15:49:21 +0800 Subject: [PATCH 03/13] feat: update CI to use nightly toolchain for rustfmt --- .github/workflows/linter.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 4a0b061d..b1ee0724 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -13,6 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - run: rustup +nightly component add rustfmt - name: Run Fmt run: cargo +nightly fmt --all -- --check From 30c33ef9c4b1edadedd60021852cc827a54e5380 Mon Sep 17 00:00:00 2001 From: Bohdan Ohorodnii <35969035+varex83@users.noreply.github.com> Date: Wed, 8 Oct 2025 15:51:30 +0800 Subject: [PATCH 04/13] fix: fmt --- crates/charon-cli/src/lib.rs | 1 - crates/charon-crypto/src/lib.rs | 6 +++--- crates/charon-dkg/src/lib.rs | 7 ++++--- crates/charon-errors/src/lib.rs | 6 +++--- crates/charon-eth2/src/lib.rs | 6 +++--- crates/charon-observability/src/lib.rs | 6 +++--- crates/charon-p2p/src/lib.rs | 7 ++++--- crates/charon-serde/src/lib.rs | 7 ++++--- crates/charon-testutil/src/lib.rs | 6 +++--- crates/charon/src/lib.rs | 6 +++--- 10 files changed, 30 insertions(+), 28 deletions(-) diff --git a/crates/charon-cli/src/lib.rs b/crates/charon-cli/src/lib.rs index 5cfbd16e..338a369a 100644 --- a/crates/charon-cli/src/lib.rs +++ b/crates/charon-cli/src/lib.rs @@ -1,4 +1,3 @@ - //! # Charon CLI //! //! Command-line interface for the Charon distributed validator node. diff --git a/crates/charon-crypto/src/lib.rs b/crates/charon-crypto/src/lib.rs index 4a219bc9..5eb6f3e5 100644 --- a/crates/charon-crypto/src/lib.rs +++ b/crates/charon-crypto/src/lib.rs @@ -1,8 +1,8 @@ //! # Charon Crypto //! -//! Cryptographic primitives and utilities for the Charon distributed validator node. -//! This crate provides cryptographic functions, key management, and security -//! operations required for distributed validator operations. +//! Cryptographic primitives and utilities for the Charon distributed validator +//! node. This crate provides cryptographic functions, key management, and +//! security operations required for distributed validator operations. /// Adds two numbers together. /// diff --git a/crates/charon-dkg/src/lib.rs b/crates/charon-dkg/src/lib.rs index 907ce5f9..e5ba56a5 100644 --- a/crates/charon-dkg/src/lib.rs +++ b/crates/charon-dkg/src/lib.rs @@ -1,8 +1,9 @@ //! # Charon DKG //! -//! Distributed Key Generation (DKG) protocols for Charon distributed validator nodes. -//! This crate implements the cryptographic protocols required for generating, -//! distributing, and managing validator keys across the distributed network. +//! Distributed Key Generation (DKG) protocols for Charon distributed validator +//! nodes. This crate implements the cryptographic protocols required for +//! generating, distributing, and managing validator keys across the distributed +//! network. /// Adds two numbers together. /// diff --git a/crates/charon-errors/src/lib.rs b/crates/charon-errors/src/lib.rs index a8fe4963..938ced88 100644 --- a/crates/charon-errors/src/lib.rs +++ b/crates/charon-errors/src/lib.rs @@ -1,8 +1,8 @@ //! # Charon Errors //! -//! Error types and error handling utilities for the Charon distributed validator node. -//! This crate defines the error types, error codes, and error handling mechanisms -//! used throughout the Charon system. +//! Error types and error handling utilities for the Charon distributed +//! validator node. This crate defines the error types, error codes, and error +//! handling mechanisms used throughout the Charon system. /// Adds two numbers together. /// diff --git a/crates/charon-eth2/src/lib.rs b/crates/charon-eth2/src/lib.rs index eb124245..da8e769e 100644 --- a/crates/charon-eth2/src/lib.rs +++ b/crates/charon-eth2/src/lib.rs @@ -1,8 +1,8 @@ //! # Charon ETH2 //! -//! Ethereum 2.0 integration and utilities for the Charon distributed validator node. -//! This crate provides interfaces, types, and utilities for interacting with -//! Ethereum 2.0 networks and validator operations. +//! Ethereum 2.0 integration and utilities for the Charon distributed validator +//! node. This crate provides interfaces, types, and utilities for interacting +//! with Ethereum 2.0 networks and validator operations. /// Adds two numbers together. /// diff --git a/crates/charon-observability/src/lib.rs b/crates/charon-observability/src/lib.rs index ae536441..6d097efd 100644 --- a/crates/charon-observability/src/lib.rs +++ b/crates/charon-observability/src/lib.rs @@ -1,8 +1,8 @@ //! # Charon Observability //! -//! Observability and monitoring utilities for the Charon distributed validator node. -//! This crate provides logging, metrics, tracing, and monitoring capabilities -//! for tracking and debugging validator operations. +//! Observability and monitoring utilities for the Charon distributed validator +//! node. This crate provides logging, metrics, tracing, and monitoring +//! capabilities for tracking and debugging validator operations. /// Adds two numbers together. /// diff --git a/crates/charon-p2p/src/lib.rs b/crates/charon-p2p/src/lib.rs index 083992d0..9144de5e 100644 --- a/crates/charon-p2p/src/lib.rs +++ b/crates/charon-p2p/src/lib.rs @@ -1,8 +1,9 @@ //! # Charon P2P //! -//! Peer-to-peer networking and communication for the Charon distributed validator node. -//! This crate provides networking protocols, peer discovery, and communication -//! mechanisms for validator nodes to coordinate and exchange information. +//! Peer-to-peer networking and communication for the Charon distributed +//! validator node. This crate provides networking protocols, peer discovery, +//! and communication mechanisms for validator nodes to coordinate and exchange +//! information. /// Adds two numbers together. /// diff --git a/crates/charon-serde/src/lib.rs b/crates/charon-serde/src/lib.rs index ca27ddb4..81789bf5 100644 --- a/crates/charon-serde/src/lib.rs +++ b/crates/charon-serde/src/lib.rs @@ -1,8 +1,9 @@ //! # Charon Serde //! -//! Serialization and deserialization utilities for the Charon distributed validator node. -//! This crate provides custom serialization logic, format support, and data -//! transformation utilities for validator operations and communication. +//! Serialization and deserialization utilities for the Charon distributed +//! validator node. This crate provides custom serialization logic, format +//! support, and data transformation utilities for validator operations and +//! communication. /// Adds two numbers together. /// diff --git a/crates/charon-testutil/src/lib.rs b/crates/charon-testutil/src/lib.rs index 3e8f9f9d..76bcca44 100644 --- a/crates/charon-testutil/src/lib.rs +++ b/crates/charon-testutil/src/lib.rs @@ -1,8 +1,8 @@ //! # Charon Test Utilities //! -//! Testing utilities and mock implementations for the Charon distributed validator node. -//! This crate provides test helpers, mock objects, and testing utilities -//! for unit tests, integration tests, and development. +//! Testing utilities and mock implementations for the Charon distributed +//! validator node. This crate provides test helpers, mock objects, and testing +//! utilities for unit tests, integration tests, and development. /// Adds two numbers together. /// diff --git a/crates/charon/src/lib.rs b/crates/charon/src/lib.rs index 1359319b..5ef0c7fe 100644 --- a/crates/charon/src/lib.rs +++ b/crates/charon/src/lib.rs @@ -1,8 +1,8 @@ //! # Charon //! -//! The main Charon library providing distributed validator key management and coordination -//! for Ethereum 2.0 validators. This crate serves as the primary entry point for the -//! Charon distributed validator node implementation. +//! The main Charon library providing distributed validator key management and +//! coordination for Ethereum 2.0 validators. This crate serves as the primary +//! entry point for the Charon distributed validator node implementation. /// Adds two numbers together. /// From d4147f3f0827458f064b4cc5d0a593855c7b23a6 Mon Sep 17 00:00:00 2001 From: Bohdan Ohorodnii <35969035+varex83@users.noreply.github.com> Date: Wed, 8 Oct 2025 16:55:41 +0800 Subject: [PATCH 05/13] refactor: reorganize workspace members and remove charon-types crate --- Cargo.toml | 20 ++++++++++++++--- crates/charon-types/Cargo.toml | 12 ----------- crates/charon-types/src/lib.rs | 39 ---------------------------------- 3 files changed, 17 insertions(+), 54 deletions(-) delete mode 100644 crates/charon-types/Cargo.toml delete mode 100644 crates/charon-types/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 2704e388..3be35481 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,13 +1,27 @@ [workspace] -name = "charon-rs" -members = [ "crates/charon", "crates/charon-cli", "crates/charon-cluster", "crates/charon-consensus", "crates/charon-core", "crates/charon-crypto", "crates/charon-db", "crates/charon-dkg", "crates/charon-errors", "crates/charon-eth2", "crates/charon-observability", "crates/charon-p2p", "crates/charon-serde", "crates/charon-testutil", "crates/charon-types"] +members = [ + "crates/charon", + "crates/charon-cli", + "crates/charon-cluster", + "crates/charon-consensus", + "crates/charon-core", + "crates/charon-crypto", + "crates/charon-db", + "crates/charon-dkg", + "crates/charon-errors", + "crates/charon-eth2", + "crates/charon-observability", + "crates/charon-p2p", + "crates/charon-serde", + "crates/charon-testutil", +] resolver = "3" [workspace.package] version = "0.1.0" edition = "2024" repository = "https://github.com/NethermindEth/charon-rs" -license = "Apache-2.0" # TODO(template) update license +license = "Apache-2.0" # TODO(template) update license publish = false [workspace.dependencies] diff --git a/crates/charon-types/Cargo.toml b/crates/charon-types/Cargo.toml deleted file mode 100644 index 0851da72..00000000 --- a/crates/charon-types/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "charon-types" -version.workspace = true -edition.workspace = true -repository.workspace = true -license.workspace = true -publish.workspace = true - -[dependencies] - -[lints] -workspace = true diff --git a/crates/charon-types/src/lib.rs b/crates/charon-types/src/lib.rs deleted file mode 100644 index e2e368ee..00000000 --- a/crates/charon-types/src/lib.rs +++ /dev/null @@ -1,39 +0,0 @@ -//! # Charon Types -//! -//! Common types and data structures for the Charon distributed validator node. -//! This crate defines the core types, enums, and data structures used -//! throughout the Charon system for type safety and consistency. - -/// Adds two numbers together. -/// -/// # Arguments -/// -/// * `left` - The first number to add -/// * `right` - The second number to add -/// -/// # Returns -/// -/// The sum of the two numbers -/// -/// # Examples -/// -/// ``` -/// use charon_types::add; -/// -/// let result = add(2, 2); -/// assert_eq!(result, 4); -/// ``` -pub fn add(left: u64, right: u64) -> u64 { - left.wrapping_add(right) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } -} From 27267735da8c0df59bc8e5eea61d6595ef25c808 Mon Sep 17 00:00:00 2001 From: Bohdan Ohorodnii <35969035+varex83@users.noreply.github.com> Date: Thu, 9 Oct 2025 15:30:07 +0800 Subject: [PATCH 06/13] feat: add core types and dependencies for Charon --- Cargo.toml | 9 +- crates/charon-core/Cargo.toml | 4 + crates/charon-core/src/lib.rs | 35 +-- crates/charon-core/src/types.rs | 539 ++++++++++++++++++++++++++++++++ 4 files changed, 551 insertions(+), 36 deletions(-) create mode 100644 crates/charon-core/src/types.rs diff --git a/Cargo.toml b/Cargo.toml index 3be35481..e7428797 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,13 +24,16 @@ repository = "https://github.com/NethermindEth/charon-rs" license = "Apache-2.0" # TODO(template) update license publish = false -[workspace.dependencies] -proptest = "1" - [workspace.lints.rust] missing_docs = "deny" unsafe_code = "forbid" +[workspace.dependencies] +serde = { version = "1.0", features = ["derive"] } +serde_json = { version = "^1.0" } +hex = { version = "^0.4.3" } +chrono = { version = "0.4", features = ["serde"] } + [workspace.lints.clippy] arithmetic_side_effects = "deny" cast_lossless = "deny" diff --git a/crates/charon-core/Cargo.toml b/crates/charon-core/Cargo.toml index 3b92ed1a..baba8427 100644 --- a/crates/charon-core/Cargo.toml +++ b/crates/charon-core/Cargo.toml @@ -7,6 +7,10 @@ license.workspace = true publish.workspace = true [dependencies] +serde.workspace = true +serde_json.workspace = true +hex.workspace = true +chrono.workspace = true [lints] workspace = true diff --git a/crates/charon-core/src/lib.rs b/crates/charon-core/src/lib.rs index a3ed9a9d..7a6c37d5 100644 --- a/crates/charon-core/src/lib.rs +++ b/crates/charon-core/src/lib.rs @@ -4,36 +4,5 @@ //! This crate provides the fundamental building blocks, data structures, and //! core algorithms used throughout the Charon system. -/// Adds two numbers together. -/// -/// # Arguments -/// -/// * `left` - The first number to add -/// * `right` - The second number to add -/// -/// # Returns -/// -/// The sum of the two numbers -/// -/// # Examples -/// -/// ``` -/// use charon_core::add; -/// -/// let result = add(2, 2); -/// assert_eq!(result, 4); -/// ``` -pub fn add(left: u64, right: u64) -> u64 { - left.wrapping_add(right) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } -} +/// Types for the Charon core. +pub mod types; diff --git a/crates/charon-core/src/types.rs b/crates/charon-core/src/types.rs new file mode 100644 index 00000000..088cf44a --- /dev/null +++ b/crates/charon-core/src/types.rs @@ -0,0 +1,539 @@ +//! Types for the Charon core. + +use std::{collections::HashMap, fmt::Display, iter, time::Duration}; + +use chrono::{DateTime, Utc}; +use serde::{Deserialize, Serialize}; +use std::fmt::Debug as StdDebug; + +/// The type of duty. +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum DutyType { + /// Unknown duty type. + Unknown, + /// Proposer duty type. + Proposer, + /// Attester duty type. + Attester, + /// Signature duty type. + Signature, + /// Exit duty type. + Exit, + /// Builder proposer duty type. + BuilderProposer, + /// Builder registration duty type. + BuilderRegistration, + /// Randao duty type. + Randao, + /// Prepare aggregator duty type. + PrepareAggregator, + /// Aggregator duty type. + Aggregator, + /// Sync message duty type. + SyncMessage, + /// Prepare sync contribution duty type. + PrepareSyncContribution, + /// Sync contribution duty type. + SyncContribution, + /// Info sync duty type. + InfoSync, + /// Duty sentinel duty type. Must always be last. + DutySentinel(Box), +} + +impl Display for DutyType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + // safe to unwrap because we know the duty type is valid + let v = serde_json::to_value(self).expect("failed to serialize duty type"); + if let Some(s) = v.as_str() { + write!(f, "{}", s) + } else { + // fallback for non-string variants (structs, numbers, etc.) + write!(f, "{}", v) + } + } +} + +/// SlotNumber struct +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct SlotNumber(u64); + +impl Display for SlotNumber { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +impl From for SlotNumber { + fn from(slot: u64) -> Self { + Self::new(slot) + } +} + +impl From for u64 { + fn from(slot: SlotNumber) -> Self { + slot.inner() + } +} + +impl SlotNumber { + /// Create a new slot number. + pub fn new(slot: u64) -> Self { + SlotNumber(slot) + } + + /// Inner slot number. + pub fn inner(&self) -> u64 { + self.0 + } + + /// Next slot number. + pub fn next(&self) -> Self { + Self::new(self.inner().saturating_add(1)) + } +} + +/// Duty struct +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct Duty { + /// Ethereum consensus layer slot. + pub slot: SlotNumber, + /// Duty type performed in the slot. + pub duty_type: DutyType, +} + +impl Display for Duty { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}/{}", self.slot, self.duty_type) + } +} + +impl Duty { + /// Create a new duty. + pub fn new(slot: SlotNumber, duty_type: DutyType) -> Self { + Self { slot, duty_type } + } + + /// Create a new attester duty. + pub fn new_attester_duty(slot: SlotNumber) -> Self { + Self::new(slot, DutyType::Attester) + } + + /// Create a new randao duty. + pub fn new_randao_duty(slot: SlotNumber) -> Self { + Self::new(slot, DutyType::Randao) + } + + /// Create a new proposer duty. + pub fn new_proposer_duty(slot: SlotNumber) -> Self { + Self::new(slot, DutyType::Proposer) + } + + /// Create a new builder proposer duty. + pub fn new_builder_proposer_duty(slot: SlotNumber) -> Self { + Self::new(slot, DutyType::BuilderProposer) + } + + /// Create a new builder registration duty. + pub fn new_builder_registration_duty(slot: SlotNumber) -> Self { + Self::new(slot, DutyType::BuilderRegistration) + } + + /// Create a new sync contribution duty. + pub fn new_sync_contribution_duty(slot: SlotNumber) -> Self { + Self::new(slot, DutyType::SyncContribution) + } + + /// Create a new signature duty. + pub fn new_signature_duty(slot: SlotNumber) -> Self { + Self::new(slot, DutyType::Signature) + } + + /// Create a new prepare aggregator duty. + pub fn new_prepare_aggregator_duty(slot: SlotNumber) -> Self { + Self::new(slot, DutyType::PrepareAggregator) + } + + /// Create a new aggregator duty. + pub fn new_aggregator_duty(slot: SlotNumber) -> Self { + Self::new(slot, DutyType::Aggregator) + } + + /// Create a new sync message duty. + pub fn new_sync_message_duty(slot: SlotNumber) -> Self { + Self::new(slot, DutyType::SyncMessage) + } + + /// Create a new prepare sync contribution duty. + pub fn new_prepare_sync_contribution_duty(slot: SlotNumber) -> Self { + Self::new(slot, DutyType::PrepareSyncContribution) + } + + /// Create a new info sync duty. + pub fn new_info_sync_duty(slot: SlotNumber) -> Self { + Self::new(slot, DutyType::InfoSync) + } +} + +/// The type of proposal. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum ProposalType { + /// Full proposal type. + Full, + /// Builder proposal type. + Builder, + /// Synthetic proposal type. + Synthetic, +} + +// In golang implementation they use pk_len = 98, which is 0x + [48 bytes] +// We use pk_len = 48, which is [48 bytes], the main difference is that we store +// the pub key as [u8; 48] instead of string. +const PK_LEN: usize = 48; +// const SIG_LEN: usize = 96; + +/// Public key struct +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct PubKey(pub(crate) [u8; PK_LEN]); + +impl Serialize for PubKey { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +impl<'de> Deserialize<'de> for PubKey { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let hex_str = String::deserialize(deserializer)?; + let hex_str = hex_str.strip_prefix("0x").unwrap_or(&hex_str); + + let bytes = hex::decode(hex_str).map_err(serde::de::Error::custom)?; + + if bytes.len() != PK_LEN { + return Err(serde::de::Error::custom(format!( + "invalid public key length: got {}, want {}", + bytes.len(), + PK_LEN + ))); + } + + let mut pk = [0u8; PK_LEN]; + pk.copy_from_slice(&bytes); + Ok(PubKey(pk)) + } +} + +impl From<[u8; PK_LEN]> for PubKey { + fn from(pk: [u8; PK_LEN]) -> Self { + PubKey(pk) + } +} + +impl PubKey { + /// Create a new public key. + pub fn new(pk: [u8; PK_LEN]) -> Self { + PubKey(pk) + } +} + +impl Display for PubKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "0x{}", hex::encode(self.0)) + } +} + +/// Implement AsRef<[u8]> for PubKey to allow for easy conversion to bytes. +impl AsRef<[u8]> for PubKey { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +// todo: add toEth2Format for the pub key +// https://github.com/ObolNetwork/charon/blob/b3008103c5429b031b63518195f4c49db4e9a68d/core/types.go#L311 + +/// Duty definition type +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct DutyDefinition(T); + +/// Duty definition set +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct DutyDefinitionSet(HashMap>) +where + T: Clone + Serialize + StdDebug; + +/// Unsigned data type +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct UnsignedData(T); + +/// Unsigned data set +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct UnsignedDataSet(HashMap>) +where + T: Clone + Serialize + StdDebug; + +// todo: add proper signature type +/// Signature type +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Signature(pub(crate) [u8; 1]); + +/// Signed data type +pub trait SignedData: Clone + Serialize + StdDebug { + /// The error type + type Error: std::error::Error; + + /// signature returns the signed duty data's signature. + fn signature(&self) -> Signature; + + /// set_signature returns a copy of signed duty data with the signature + /// replaced. + fn set_signature(&mut self, signature: Signature) -> Result<(), Self::Error>; + + /// message_root returns the message root for the unsigned data. + fn message_root(&self) -> [u8; 32]; +} + +// todo: add Eth2SignedData type +// https://github.com/ObolNetwork/charon/blob/b3008103c5429b031b63518195f4c49db4e9a68d/core/types.go#L396 + +/// ParSignedData is a partially signed duty data only signed by a single +/// threshold BLS share. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ParSignedData { + /// Partially signed duty data. + pub signed_data: T, + + /// Threshold BLS share index. + pub share_idx: u64, +} + +/// ParSignedDataSet is a set of partially signed duty data only signed by a +/// single threshold BLS share. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ParSignedDataSet(HashMap>); + +/// SignedDataSet is a set of signed duty data. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct SignedDataSet(HashMap); + +/// Slot struct +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Slot { + /// The slot number. + pub slot: SlotNumber, + + /// The time. + pub time: DateTime, + + /// The slot type. + pub slot_duration: Duration, + + /// Slots per epoch. + pub slots_per_epoch: u64, +} + +impl Slot { + /// Get the epoch of the slot + pub fn epoch(&self) -> u64 { + #[allow(clippy::arithmetic_side_effects)] + self.slot.inner().saturating_div(self.slots_per_epoch) + } + + /// Returns true if this is the last slot in the epoch. + #[allow(clippy::arithmetic_side_effects)] + pub fn last_in_epoch(&self) -> bool { + self.slot.inner().wrapping_rem(self.slots_per_epoch) + == self.slots_per_epoch.saturating_sub(1) + } + + /// Returns true if this is the first slot in the epoch. + #[allow(clippy::arithmetic_side_effects)] + pub fn first_in_epoch(&self) -> bool { + self.slot.inner().wrapping_rem(self.slots_per_epoch) == 0 + } + + /// Returns the next slot + #[allow(clippy::arithmetic_side_effects)] + pub fn next_slot(&self) -> Slot { + Slot { + slot: self.slot.next(), + time: self.time + self.slot_duration, + slot_duration: self.slot_duration, + slots_per_epoch: self.slots_per_epoch, + } + } + + /// Returns an iterator over slots starting from this one + pub fn iter(&self) -> impl Iterator { + iter::successors(Some(self.clone()), |slot| Some(slot.next_slot())) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_pub_key_to_string() { + const ORIGINAL_PK_LEN: usize = 98; + + let key = PubKey::new([0; PK_LEN]); + + // Check whether the string representation is the same as the go's public key + // length + assert_eq!(key.to_string().len(), ORIGINAL_PK_LEN); + assert_eq!( + key.to_string(), + "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + ); + } + + #[test] + fn test_new_builder_registration_duty() { + let duty = Duty::new_builder_registration_duty(SlotNumber(1)); + assert_eq!(duty.duty_type, DutyType::BuilderRegistration); + assert_eq!(duty.to_string(), "1/builder_registration"); + assert_eq!(u64::from(duty.slot), 1); + } + + #[test] + fn test_new_signature_duty() { + let duty = Duty::new_signature_duty(SlotNumber(1)); + assert_eq!(duty.duty_type, DutyType::Signature); + assert_eq!(duty.to_string(), "1/signature"); + assert_eq!(u64::from(duty.slot), 1); + } + + #[test] + fn test_new_prepare_aggregator_duty() { + let duty = Duty::new_prepare_aggregator_duty(SlotNumber(1)); + assert_eq!(duty.duty_type, DutyType::PrepareAggregator); + assert_eq!(duty.to_string(), "1/prepare_aggregator"); + assert_eq!(u64::from(duty.slot), 1); + } + + #[test] + fn test_new_aggregator_duty() { + let duty = Duty::new_aggregator_duty(SlotNumber(1)); + assert_eq!(duty.duty_type, DutyType::Aggregator); + assert_eq!(duty.to_string(), "1/aggregator"); + assert_eq!(u64::from(duty.slot), 1); + } + + #[test] + fn test_new_sync_contribution_duty() { + let duty = Duty::new_sync_contribution_duty(SlotNumber(1)); + assert_eq!(duty.duty_type, DutyType::SyncContribution); + assert_eq!(duty.to_string(), "1/sync_contribution"); + assert_eq!(u64::from(duty.slot), 1); + } + + #[test] + fn test_new_sync_message_duty() { + let duty = Duty::new_sync_message_duty(SlotNumber(1)); + assert_eq!(duty.duty_type, DutyType::SyncMessage); + assert_eq!(duty.to_string(), "1/sync_message"); + assert_eq!(u64::from(duty.slot), 1); + } + + #[test] + fn test_new_prepare_sync_contribution_duty() { + let duty = Duty::new_prepare_sync_contribution_duty(SlotNumber(1)); + assert_eq!(duty.duty_type, DutyType::PrepareSyncContribution); + assert_eq!(duty.to_string(), "1/prepare_sync_contribution"); + assert_eq!(u64::from(duty.slot), 1); + } + + #[test] + fn test_new_info_sync_duty() { + let duty = Duty::new_info_sync_duty(SlotNumber(1)); + assert_eq!(duty.duty_type, DutyType::InfoSync); + assert_eq!(duty.to_string(), "1/info_sync"); + assert_eq!(u64::from(duty.slot), 1); + } + + #[test] + fn test_slot() { + let slot = Slot { + slot: SlotNumber(123), + time: DateTime::from_timestamp(100, 100).unwrap(), + slot_duration: Duration::from_secs(4), + slots_per_epoch: 32, + }; + + assert_eq!(u64::from(slot.slot), 0x7b); + assert_eq!(slot.epoch(), 3); + assert!(!slot.last_in_epoch()); + assert!(!slot.first_in_epoch()); + + let next = slot.next_slot(); + assert_eq!(next.slot, SlotNumber(124)); + assert_eq!(next.time, DateTime::from_timestamp(104, 100).unwrap()); + assert_eq!(next.slot_duration, Duration::from_secs(4)); + assert_eq!(next.slots_per_epoch, 32); + } + + #[test] + fn test_serialize_pubkey() { + let pk = PubKey::new([42u8; PK_LEN]); + let serialized = serde_json::to_string(&pk).unwrap(); + assert_eq!(serialized, format!("\"0x{}\"", hex::encode([42u8; PK_LEN]))); + } + + #[test] + fn test_deserialize_pubkey() { + let serialized = format!("\"0x{}\"", hex::encode([42u8; PK_LEN])); + let deserialized: PubKey = serde_json::from_str(&serialized).unwrap(); + assert_eq!(deserialized, PubKey::new([42u8; PK_LEN])); + } + + #[test] + fn test_slot_iter() { + let slot = Slot { + slot: SlotNumber(123), + time: DateTime::from_timestamp(100, 100).unwrap(), + slot_duration: Duration::from_secs(4), + slots_per_epoch: 32, + }; + + assert_eq!(slot.iter().nth(10).unwrap().slot, SlotNumber(133)); + assert_eq!(slot.iter().nth(31).unwrap().slot, SlotNumber(154)); + assert_eq!(slot.iter().nth(32).unwrap().slot, SlotNumber(155)); + assert_eq!(slot.iter().nth(33).unwrap().slot, SlotNumber(156)); + } + + #[test] + fn test_display_duty_type() { + assert_eq!(DutyType::Unknown.to_string(), "unknown"); + assert_eq!(DutyType::Proposer.to_string(), "proposer"); + assert_eq!(DutyType::Attester.to_string(), "attester"); + assert_eq!(DutyType::Signature.to_string(), "signature"); + assert_eq!(DutyType::Exit.to_string(), "exit"); + assert_eq!(DutyType::BuilderProposer.to_string(), "builder_proposer"); + assert_eq!( + DutyType::BuilderRegistration.to_string(), + "builder_registration" + ); + assert_eq!(DutyType::Randao.to_string(), "randao"); + assert_eq!( + DutyType::PrepareAggregator.to_string(), + "prepare_aggregator" + ); + assert_eq!(DutyType::Aggregator.to_string(), "aggregator"); + assert_eq!(DutyType::SyncMessage.to_string(), "sync_message"); + assert_eq!( + DutyType::PrepareSyncContribution.to_string(), + "prepare_sync_contribution" + ); + assert_eq!(DutyType::SyncContribution.to_string(), "sync_contribution"); + assert_eq!(DutyType::InfoSync.to_string(), "info_sync"); + } +} From b44afb68df0f0722a00d867020153a0f5c73cb29 Mon Sep 17 00:00:00 2001 From: Bohdan Ohorodnii <35969035+varex83@users.noreply.github.com> Date: Thu, 9 Oct 2025 15:36:40 +0800 Subject: [PATCH 07/13] feat: add is_valid method and tests for DutyType --- crates/charon-core/src/types.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/crates/charon-core/src/types.rs b/crates/charon-core/src/types.rs index 088cf44a..c487f2e3 100644 --- a/crates/charon-core/src/types.rs +++ b/crates/charon-core/src/types.rs @@ -55,6 +55,13 @@ impl Display for DutyType { } } +impl DutyType { + /// Returns true if the duty type is valid. + pub fn is_valid(&self) -> bool { + !matches!(self, DutyType::Unknown | DutyType::DutySentinel(_)) + } +} + /// SlotNumber struct #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct SlotNumber(u64); @@ -536,4 +543,15 @@ mod tests { assert_eq!(DutyType::SyncContribution.to_string(), "sync_contribution"); assert_eq!(DutyType::InfoSync.to_string(), "info_sync"); } + + #[test] + fn test_duty_type_is_valid() { + assert!(!DutyType::Unknown.is_valid()); + assert!(DutyType::Proposer.is_valid()); + assert!(DutyType::Attester.is_valid()); + assert!(DutyType::Signature.is_valid()); + assert!(DutyType::Exit.is_valid()); + assert!(!DutyType::DutySentinel(Box::new(DutyType::Unknown)).is_valid()); + assert!(!DutyType::DutySentinel(Box::new(DutyType::Attester)).is_valid()); + } } From 20bd80947b1a2013038a758e25ce841cef2c3b22 Mon Sep 17 00:00:00 2001 From: Bohdan Ohorodnii <35969035+varex83@users.noreply.github.com> Date: Thu, 9 Oct 2025 18:50:08 +0800 Subject: [PATCH 08/13] feat: enhance Charon core types with new methods and error handling for PubKey --- crates/charon-core/src/types.rs | 330 +++++++++++++++++++++++++++++++- 1 file changed, 322 insertions(+), 8 deletions(-) diff --git a/crates/charon-core/src/types.rs b/crates/charon-core/src/types.rs index c487f2e3..9280c497 100644 --- a/crates/charon-core/src/types.rs +++ b/crates/charon-core/src/types.rs @@ -1,8 +1,8 @@ //! Types for the Charon core. -use std::{collections::HashMap, fmt::Display, iter, time::Duration}; +use std::{collections::HashMap, fmt::Display, iter}; -use chrono::{DateTime, Utc}; +use chrono::{DateTime, Duration, Utc}; use serde::{Deserialize, Serialize}; use std::fmt::Debug as StdDebug; @@ -132,6 +132,11 @@ impl Duty { Self::new(slot, DutyType::Randao) } + /// Create a new voluntary exit duty. + pub fn new_voluntary_exit_duty(slot: SlotNumber) -> Self { + Self::new(slot, DutyType::Exit) + } + /// Create a new proposer duty. pub fn new_proposer_duty(slot: SlotNumber) -> Self { Self::new(slot, DutyType::Proposer) @@ -199,7 +204,7 @@ pub enum ProposalType { // We use pk_len = 48, which is [48 bytes], the main difference is that we store // the pub key as [u8; 48] instead of string. const PK_LEN: usize = 48; -// const SIG_LEN: usize = 96; +const SIG_LEN: usize = 96; /// Public key struct #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -214,6 +219,15 @@ impl Serialize for PubKey { } } +impl TryFrom for PubKey { + type Error = PubKeyError; + + fn try_from(value: String) -> Result { + let value = value.strip_prefix("0x").unwrap_or(&value); + PubKey::from_bytes(&hex::decode(value).map_err(|_| PubKeyError::InvalidString)?) + } +} + impl<'de> Deserialize<'de> for PubKey { fn deserialize(deserializer: D) -> Result where @@ -244,11 +258,36 @@ impl From<[u8; PK_LEN]> for PubKey { } } +/// Public key error type +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum PubKeyError { + /// Invalid public key length. + InvalidLength, + /// Invalid public key string. + InvalidString, +} + impl PubKey { /// Create a new public key. pub fn new(pk: [u8; PK_LEN]) -> Self { PubKey(pk) } + + /// Create a new public key from bytes. + pub fn from_bytes(bytes: &[u8]) -> Result { + if bytes.len() != PK_LEN { + return Err(PubKeyError::InvalidLength); + } + let mut arr = [0u8; PK_LEN]; + arr.copy_from_slice(bytes); + Ok(PubKey(arr)) + } + + /// Returns logging-friendly abbreviated form: "b82_97f" + pub fn abbreviated(&self) -> String { + let hex = hex::encode(self.0); + format!("{}_{}", &hex[0..3], &hex[93..96]) + } } impl Display for PubKey { @@ -271,26 +310,122 @@ impl AsRef<[u8]> for PubKey { #[derive(Debug, Clone, PartialEq, Eq)] pub struct DutyDefinition(T); +impl DutyDefinition +where + T: Clone + Serialize + StdDebug, +{ + /// Create a new duty definition. + pub fn new(duty_definition: T) -> Self { + Self(duty_definition) + } +} + /// Duty definition set -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Default, Clone, PartialEq, Eq)] pub struct DutyDefinitionSet(HashMap>) where T: Clone + Serialize + StdDebug; +impl DutyDefinitionSet +where + T: Clone + Serialize + StdDebug, +{ + /// Create a new duty definition set. + pub fn new() -> Self { + Self(HashMap::default()) + } + + /// Get a duty definition by duty type. + pub fn get(&self, duty_type: &DutyType) -> Option<&DutyDefinition> { + self.0.get(duty_type) + } + + /// Insert a duty definition. + pub fn insert(&mut self, duty_type: DutyType, duty_definition: DutyDefinition) { + self.0.insert(duty_type, duty_definition); + } + + /// Remove a duty definition by duty type. + pub fn remove(&mut self, duty_type: &DutyType) -> Option> { + self.0.remove(duty_type) + } + + /// Inner duty definition set. + pub fn inner(&self) -> &HashMap> { + &self.0 + } + + /// Inner duty definition set. + pub fn inner_mut(&mut self) -> &mut HashMap> { + &mut self.0 + } +} + /// Unsigned data type #[derive(Debug, Clone, PartialEq, Eq)] pub struct UnsignedData(T); +impl UnsignedData +where + T: Clone + Serialize + StdDebug, +{ + /// Create a new unsigned data. + pub fn new(unsigned_data: T) -> Self { + Self(unsigned_data) + } +} /// Unsigned data set #[derive(Debug, Clone, PartialEq, Eq)] pub struct UnsignedDataSet(HashMap>) where T: Clone + Serialize + StdDebug; +impl UnsignedDataSet +where + T: Clone + Serialize + StdDebug, +{ + /// Create a new unsigned data set. + pub fn new() -> Self { + Self(HashMap::default()) + } + + /// Get an unsigned data by duty type. + pub fn get(&self, duty_type: &DutyType) -> Option<&UnsignedData> { + self.0.get(duty_type) + } + + /// Insert an unsigned data. + pub fn insert(&mut self, duty_type: DutyType, unsigned_data: UnsignedData) { + self.0.insert(duty_type, unsigned_data); + } + + /// Remove an unsigned data by duty type. + pub fn remove(&mut self, duty_type: &DutyType) -> Option> { + self.0.remove(duty_type) + } + + /// Inner unsigned data set. + pub fn inner(&self) -> &HashMap> { + &self.0 + } + + /// Inner unsigned data set. + pub fn inner_mut(&mut self) -> &mut HashMap> { + &mut self.0 + } +} + // todo: add proper signature type /// Signature type #[derive(Debug, Clone, PartialEq, Eq)] -pub struct Signature(pub(crate) [u8; 1]); +pub struct Signature(pub(crate) [u8; SIG_LEN]); + +impl Signature { + /// Create a new signature. + pub fn new(signature: [u8; SIG_LEN]) -> Self { + Signature(signature) + } +} /// Signed data type pub trait SignedData: Clone + Serialize + StdDebug { @@ -322,15 +457,98 @@ pub struct ParSignedData { pub share_idx: u64, } +impl ParSignedData +where + T: SignedData, +{ + /// Create a new partially signed data. + pub fn new(partially_signed_data: T, share_idx: u64) -> Self { + Self { + signed_data: partially_signed_data, + share_idx, + } + } +} + /// ParSignedDataSet is a set of partially signed duty data only signed by a /// single threshold BLS share. #[derive(Debug, Clone, PartialEq, Eq)] pub struct ParSignedDataSet(HashMap>); +impl ParSignedDataSet +where + T: SignedData, +{ + /// Create a new partially signed data set. + pub fn new() -> Self { + Self(HashMap::default()) + } + + /// Get a partially signed data by public key. + pub fn get(&self, pub_key: &PubKey) -> Option<&ParSignedData> { + self.inner().get(pub_key) + } + + /// Insert a partially signed data. + pub fn insert(&mut self, pub_key: PubKey, partially_signed_data: ParSignedData) { + self.inner_mut().insert(pub_key, partially_signed_data); + } + + /// Remove a partially signed data by public key. + pub fn remove(&mut self, pub_key: &PubKey) -> Option> { + self.inner_mut().remove(pub_key) + } + + /// Inner partially signed data set. + pub fn inner(&self) -> &HashMap> { + &self.0 + } + + /// Inner partially signed data set. + pub fn inner_mut(&mut self) -> &mut HashMap> { + &mut self.0 + } +} + /// SignedDataSet is a set of signed duty data. #[derive(Debug, Clone, PartialEq, Eq)] pub struct SignedDataSet(HashMap); +impl SignedDataSet +where + T: SignedData, +{ + /// Create a new signed data set. + pub fn new() -> Self { + Self(HashMap::default()) + } + + /// Get a signed data by public key. + pub fn get(&self, pub_key: &PubKey) -> Option<&T> { + self.0.get(pub_key) + } + + /// Insert a signed data. + pub fn insert(&mut self, pub_key: PubKey, signed_data: T) { + self.0.insert(pub_key, signed_data); + } + + /// Remove a signed data by public key. + pub fn remove(&mut self, pub_key: &PubKey) -> Option { + self.0.remove(pub_key) + } + + /// Inner signed data set. + pub fn inner(&self) -> &HashMap { + &self.0 + } + + /// Inner signed data set. + pub fn inner_mut(&mut self) -> &mut HashMap { + &mut self.0 + } +} + /// Slot struct #[derive(Debug, Clone, PartialEq, Eq)] pub struct Slot { @@ -472,7 +690,7 @@ mod tests { let slot = Slot { slot: SlotNumber(123), time: DateTime::from_timestamp(100, 100).unwrap(), - slot_duration: Duration::from_secs(4), + slot_duration: Duration::seconds(4), slots_per_epoch: 32, }; @@ -484,7 +702,7 @@ mod tests { let next = slot.next_slot(); assert_eq!(next.slot, SlotNumber(124)); assert_eq!(next.time, DateTime::from_timestamp(104, 100).unwrap()); - assert_eq!(next.slot_duration, Duration::from_secs(4)); + assert_eq!(next.slot_duration, Duration::seconds(4)); assert_eq!(next.slots_per_epoch, 32); } @@ -507,7 +725,7 @@ mod tests { let slot = Slot { slot: SlotNumber(123), time: DateTime::from_timestamp(100, 100).unwrap(), - slot_duration: Duration::from_secs(4), + slot_duration: Duration::seconds(4), slots_per_epoch: 32, }; @@ -554,4 +772,100 @@ mod tests { assert!(!DutyType::DutySentinel(Box::new(DutyType::Unknown)).is_valid()); assert!(!DutyType::DutySentinel(Box::new(DutyType::Attester)).is_valid()); } + + #[test] + fn test_pub_key_from_bytes() { + let bytes = [42u8; PK_LEN]; + let pk = PubKey::from_bytes(&bytes).unwrap(); + assert_eq!(pk, PubKey::new(bytes)); + } + + #[test] + fn test_pub_key_from_bytes_invalid_length() { + let bytes = [42u8; PK_LEN + 1]; + let result = PubKey::from_bytes(&bytes); + assert!(result.is_err()); + } + + #[test] + fn test_pub_key_abbreviated() { + let pk = PubKey::new([42u8; PK_LEN]); + assert_eq!(pk.abbreviated(), "2a2_a2a"); + } + + #[test] + fn test_duty_definition_set() { + let mut duty_definition_set = DutyDefinitionSet::new(); + duty_definition_set.insert(DutyType::Proposer, DutyDefinition::new(DutyType::Proposer)); + assert_eq!( + duty_definition_set.get(&DutyType::Proposer), + Some(&DutyDefinition::new(DutyType::Proposer)) + ); + } + + #[test] + fn test_unsigned_data_set() { + let mut unsigned_data_set = UnsignedDataSet::new(); + unsigned_data_set.insert(DutyType::Proposer, UnsignedData::new(DutyType::Proposer)); + assert_eq!( + unsigned_data_set.get(&DutyType::Proposer), + Some(&UnsignedData::new(DutyType::Proposer)) + ); + } + + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] + struct MockSignedData; + + impl SignedData for MockSignedData { + type Error = std::io::Error; + + fn signature(&self) -> Signature { + Signature::new([42u8; SIG_LEN]) + } + + fn set_signature(&mut self, _signature: Signature) -> Result<(), std::io::Error> { + Ok(()) + } + + fn message_root(&self) -> [u8; 32] { + [42u8; 32] + } + } + + #[test] + fn test_partially_signed_data_set() { + let mut partially_signed_data_set = ParSignedDataSet::new(); + partially_signed_data_set.insert( + PubKey::new([42u8; PK_LEN]), + ParSignedData::new(MockSignedData, 0), + ); + assert_eq!( + partially_signed_data_set.get(&PubKey::new([42u8; PK_LEN])), + Some(&ParSignedData::new(MockSignedData, 0)) + ); + } + + #[test] + fn test_signed_data_set() { + let mut signed_data_set = SignedDataSet::new(); + signed_data_set.insert(PubKey::new([42u8; PK_LEN]), MockSignedData); + assert_eq!( + signed_data_set.get(&PubKey::new([42u8; PK_LEN])), + Some(&MockSignedData) + ); + } + + #[test] + fn test_pub_key_from_string() { + let pk_str = "0x7f790ba343adf8891fac21a94b02d6ca93d0bc2199a5ec083ff6676e8c2f9f78b08bb122f1093675f9d24c8b5e7af241".to_string(); + let pk = PubKey::try_from(pk_str).unwrap(); + assert_eq!(pk, PubKey::new([127, 121, 11, 163, 67, 173, 248, 137, 31, 172, 33, 169, 75, 2, 214, 202, 147, 208, 188, 33, 153, 165, 236, 8, 63, 246, 103, 110, 140, 47, 159, 120, 176, 139, 177, 34, 241, 9, 54, 117, 249, 210, 76, 139, 94, 122, 242, 65])); + } + + #[test] + fn test_pub_key_from_string_invalid_length() { + let pk_str = "0x7f790ba343adf8891fac21a94b02d6ca93d0bc2199a5ec083ff6676e8c2f9f78b08bb121093675f9d24c8b5e7af241".to_string(); + let result = PubKey::try_from(pk_str); + assert!(result.is_err()); + } } From cb28a3a3c7d749112960b8cbde6d3cafb3418dec Mon Sep 17 00:00:00 2001 From: Bohdan Ohorodnii <35969035+varex83@users.noreply.github.com> Date: Thu, 9 Oct 2025 18:52:14 +0800 Subject: [PATCH 09/13] chore: remove unused proptest dependency from workspace --- Cargo.toml | 3 --- 1 file changed, 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3be35481..03f294c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,9 +24,6 @@ repository = "https://github.com/NethermindEth/charon-rs" license = "Apache-2.0" # TODO(template) update license publish = false -[workspace.dependencies] -proptest = "1" - [workspace.lints.rust] missing_docs = "deny" unsafe_code = "forbid" From df5d4aeefe83cf1a6f3d2895ff159e774a7c670e Mon Sep 17 00:00:00 2001 From: Bohdan Ohorodnii <35969035+varex83@users.noreply.github.com> Date: Thu, 9 Oct 2025 19:56:53 +0800 Subject: [PATCH 10/13] feat: implement Default trait for UnsignedDataSet, ParSignedDataSet, and SignedDataSet --- crates/charon-core/src/types.rs | 42 +++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/crates/charon-core/src/types.rs b/crates/charon-core/src/types.rs index 9280c497..389f071e 100644 --- a/crates/charon-core/src/types.rs +++ b/crates/charon-core/src/types.rs @@ -380,13 +380,22 @@ pub struct UnsignedDataSet(HashMap>) where T: Clone + Serialize + StdDebug; +impl Default for UnsignedDataSet +where + T: Clone + Serialize + StdDebug, +{ + fn default() -> Self { + Self(HashMap::default()) + } +} + impl UnsignedDataSet where T: Clone + Serialize + StdDebug, { /// Create a new unsigned data set. pub fn new() -> Self { - Self(HashMap::default()) + Self::default() } /// Get an unsigned data by duty type. @@ -475,13 +484,22 @@ where #[derive(Debug, Clone, PartialEq, Eq)] pub struct ParSignedDataSet(HashMap>); +impl Default for ParSignedDataSet +where + T: SignedData, +{ + fn default() -> Self { + Self(HashMap::default()) + } +} + impl ParSignedDataSet where T: SignedData, { /// Create a new partially signed data set. pub fn new() -> Self { - Self(HashMap::default()) + Self::default() } /// Get a partially signed data by public key. @@ -514,13 +532,22 @@ where #[derive(Debug, Clone, PartialEq, Eq)] pub struct SignedDataSet(HashMap); +impl Default for SignedDataSet +where + T: SignedData, +{ + fn default() -> Self { + Self(HashMap::default()) + } +} + impl SignedDataSet where T: SignedData, { /// Create a new signed data set. pub fn new() -> Self { - Self(HashMap::default()) + Self::default() } /// Get a signed data by public key. @@ -859,7 +886,14 @@ mod tests { fn test_pub_key_from_string() { let pk_str = "0x7f790ba343adf8891fac21a94b02d6ca93d0bc2199a5ec083ff6676e8c2f9f78b08bb122f1093675f9d24c8b5e7af241".to_string(); let pk = PubKey::try_from(pk_str).unwrap(); - assert_eq!(pk, PubKey::new([127, 121, 11, 163, 67, 173, 248, 137, 31, 172, 33, 169, 75, 2, 214, 202, 147, 208, 188, 33, 153, 165, 236, 8, 63, 246, 103, 110, 140, 47, 159, 120, 176, 139, 177, 34, 241, 9, 54, 117, 249, 210, 76, 139, 94, 122, 242, 65])); + assert_eq!( + pk, + PubKey::new([ + 127, 121, 11, 163, 67, 173, 248, 137, 31, 172, 33, 169, 75, 2, 214, 202, 147, 208, + 188, 33, 153, 165, 236, 8, 63, 246, 103, 110, 140, 47, 159, 120, 176, 139, 177, 34, + 241, 9, 54, 117, 249, 210, 76, 139, 94, 122, 242, 65 + ]) + ); } #[test] From 2a2fdf88c943d8a200abf2298dde1cd6c74338c2 Mon Sep 17 00:00:00 2001 From: Bohdan Ohorodnii <35969035+varex83@users.noreply.github.com> Date: Fri, 10 Oct 2025 18:00:31 +0800 Subject: [PATCH 11/13] refactor: update PubKey methods for improved error handling and add try_from implementation --- crates/charon-core/src/types.rs | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/crates/charon-core/src/types.rs b/crates/charon-core/src/types.rs index 389f071e..8c89b7a1 100644 --- a/crates/charon-core/src/types.rs +++ b/crates/charon-core/src/types.rs @@ -203,6 +203,7 @@ pub enum ProposalType { // In golang implementation they use pk_len = 98, which is 0x + [48 bytes] // We use pk_len = 48, which is [48 bytes], the main difference is that we store // the pub key as [u8; 48] instead of string. +// [original implementation](https://github.com/ObolNetwork/charon/blob/b3008103c5429b031b63518195f4c49db4e9a68d/core/types.go#L264) const PK_LEN: usize = 48; const SIG_LEN: usize = 96; @@ -224,7 +225,8 @@ impl TryFrom for PubKey { fn try_from(value: String) -> Result { let value = value.strip_prefix("0x").unwrap_or(&value); - PubKey::from_bytes(&hex::decode(value).map_err(|_| PubKeyError::InvalidString)?) + let hex_value = hex::decode(value).map_err(|_| PubKeyError::InvalidString)?; + PubKey::try_from(hex_value.as_slice()) } } @@ -273,8 +275,17 @@ impl PubKey { PubKey(pk) } - /// Create a new public key from bytes. - pub fn from_bytes(bytes: &[u8]) -> Result { + /// Returns logging-friendly abbreviated form: "b82_97f" + pub fn abbreviated(&self) -> String { + let hex = hex::encode(self.0); + format!("{}_{}", &hex[0..3], &hex[93..96]) + } +} + +impl TryFrom<&[u8]> for PubKey { + type Error = PubKeyError; + + fn try_from(bytes: &[u8]) -> Result { if bytes.len() != PK_LEN { return Err(PubKeyError::InvalidLength); } @@ -282,12 +293,6 @@ impl PubKey { arr.copy_from_slice(bytes); Ok(PubKey(arr)) } - - /// Returns logging-friendly abbreviated form: "b82_97f" - pub fn abbreviated(&self) -> String { - let hex = hex::encode(self.0); - format!("{}_{}", &hex[0..3], &hex[93..96]) - } } impl Display for PubKey { @@ -803,14 +808,14 @@ mod tests { #[test] fn test_pub_key_from_bytes() { let bytes = [42u8; PK_LEN]; - let pk = PubKey::from_bytes(&bytes).unwrap(); + let pk = PubKey::try_from(&bytes[..]).unwrap(); assert_eq!(pk, PubKey::new(bytes)); } #[test] fn test_pub_key_from_bytes_invalid_length() { let bytes = [42u8; PK_LEN + 1]; - let result = PubKey::from_bytes(&bytes); + let result = PubKey::try_from(&bytes[..]); assert!(result.is_err()); } From 319cfac5e8bbb68a9a9d97da25c11666b76c4f54 Mon Sep 17 00:00:00 2001 From: Bohdan Ohorodnii <35969035+varex83@users.noreply.github.com> Date: Fri, 10 Oct 2025 18:02:43 +0800 Subject: [PATCH 12/13] docs: clarify slot duration comment and add debug prints for PubKey serialization/deserialization tests --- crates/charon-core/src/types.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/charon-core/src/types.rs b/crates/charon-core/src/types.rs index 8c89b7a1..a5b8b5e1 100644 --- a/crates/charon-core/src/types.rs +++ b/crates/charon-core/src/types.rs @@ -590,7 +590,7 @@ pub struct Slot { /// The time. pub time: DateTime, - /// The slot type. + /// The slot duration. pub slot_duration: Duration, /// Slots per epoch. @@ -742,6 +742,7 @@ mod tests { fn test_serialize_pubkey() { let pk = PubKey::new([42u8; PK_LEN]); let serialized = serde_json::to_string(&pk).unwrap(); + println!("serialized: {}", serialized); assert_eq!(serialized, format!("\"0x{}\"", hex::encode([42u8; PK_LEN]))); } @@ -749,6 +750,7 @@ mod tests { fn test_deserialize_pubkey() { let serialized = format!("\"0x{}\"", hex::encode([42u8; PK_LEN])); let deserialized: PubKey = serde_json::from_str(&serialized).unwrap(); + println!("deserialized: {}", deserialized); assert_eq!(deserialized, PubKey::new([42u8; PK_LEN])); } From 5e7ff968dc91456ff01cbf2baaa8db0b761587a0 Mon Sep 17 00:00:00 2001 From: Bohdan Ohorodnii <35969035+varex83@users.noreply.github.com> Date: Sun, 19 Oct 2025 12:49:41 +0800 Subject: [PATCH 13/13] fix: remove unnecessary println --- crates/charon-core/src/types.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/charon-core/src/types.rs b/crates/charon-core/src/types.rs index a5b8b5e1..9328ded2 100644 --- a/crates/charon-core/src/types.rs +++ b/crates/charon-core/src/types.rs @@ -742,7 +742,6 @@ mod tests { fn test_serialize_pubkey() { let pk = PubKey::new([42u8; PK_LEN]); let serialized = serde_json::to_string(&pk).unwrap(); - println!("serialized: {}", serialized); assert_eq!(serialized, format!("\"0x{}\"", hex::encode([42u8; PK_LEN]))); } @@ -750,7 +749,6 @@ mod tests { fn test_deserialize_pubkey() { let serialized = format!("\"0x{}\"", hex::encode([42u8; PK_LEN])); let deserialized: PubKey = serde_json::from_str(&serialized).unwrap(); - println!("deserialized: {}", deserialized); assert_eq!(deserialized, PubKey::new([42u8; PK_LEN])); }