From bacd583a4c2058fa1af7d6a1f5c15b08cc94c0ca Mon Sep 17 00:00:00 2001 From: bomanaps Date: Tue, 2 Dec 2025 09:19:08 -0300 Subject: [PATCH 1/6] Replace JSON key export with SSZ-encoded binary output --- README.md | 12 +++++----- src/main.rs | 64 +++++++++++++++++++++++------------------------------ 2 files changed, 35 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index 3a4c1d7..5f588fb 100644 --- a/README.md +++ b/README.md @@ -35,17 +35,19 @@ cargo run --release --bin hashsig -- generate \ - `--create-manifest`: Create a manifest file (optional, defaults to `true`) **Output:** -The tool creates a directory with key pairs in JSON format: +The tool creates a directory with key pairs exported as **SSZ-encoded binary files**: ``` generated_keys/ ├── validator-keys-manifest.yaml # Manifest file (if --create-manifest is true) -├── validator_0_pk.json # Public key for validator 0 -├── validator_0_sk.json # Secret key for validator 0 -├── validator_1_pk.json # Public key for validator 1 -├── validator_1_sk.json # Secret key for validator 1 +├── validator_0_pk.ssz # Public key for validator 0 (SSZ bytes) +├── validator_0_sk.ssz # Secret key for validator 0 (SSZ bytes) +├── validator_1_pk.ssz # Public key for validator 1 (SSZ bytes) +├── validator_1_sk.ssz # Secret key for validator 1 (SSZ bytes) └── ... ``` +The `.ssz` files contain the **canonical SSZ serialization** (`to_bytes()`) of the underlying key types from `leanSig`, written directly as raw bytes (not JSON or hex). + ## Current Implementation Currently uses the `SIGTopLevelTargetSumLifetime32Dim64Base8` scheme: diff --git a/src/main.rs b/src/main.rs index 6599932..147dd01 100644 --- a/src/main.rs +++ b/src/main.rs @@ -92,20 +92,24 @@ fn generate_keys( println!("Generating {}...", key_prefix); // Generate the key pair - let (pk, sk) = SIGTopLevelTargetSumLifetime32Dim64Base8::key_gen(&mut rng, 0, activation_duration); - - // Serialize the public key - let pk_json = serde_json::to_string_pretty(&pk).expect("Failed to serialize public key"); - let mut pk_file = File::create(output_dir.join(format!("{}_pk.json", key_prefix)))?; - pk_file.write_all(pk_json.as_bytes())?; - - // Serialize the secret key - let sk_json = serde_json::to_string_pretty(&sk).expect("Failed to serialize secret key"); - let mut sk_file = File::create(output_dir.join(format!("{}_sk.json", key_prefix)))?; - sk_file.write_all(sk_json.as_bytes())?; - - println!(" ✅ {}_pk.json", key_prefix); - println!(" ✅ {}_sk.json", key_prefix); + let (pk, sk) = SIGTopLevelTargetSumLifetime32Dim64Base8::key_gen( + &mut rng, + 0, + activation_duration, + ); + + // Serialize the public key to SSZ bytes and write to a binary .ssz file + let pk_bytes = pk.to_bytes(); + let mut pk_file = File::create(output_dir.join(format!("{}_pk.ssz", key_prefix)))?; + pk_file.write_all(&pk_bytes)?; + + // Serialize the secret key to SSZ bytes and write to a binary .ssz file + let sk_bytes = sk.to_bytes(); + let mut sk_file = File::create(output_dir.join(format!("{}_sk.ssz", key_prefix)))?; + sk_file.write_all(&sk_bytes)?; + + println!(" ✅ {}_pk.ssz", key_prefix); + println!(" ✅ {}_sk.ssz", key_prefix); } println!("\n✅ Successfully generated and saved {} validator key pairs.", num_validators); @@ -113,24 +117,12 @@ fn generate_keys( Ok(()) } -/// Convert pubkey JSON file to hex string -/// Reads the JSON file, deserializes into PublicKey type using serde, -/// then uses SSZ serialization (to_bytes) to get canonical form bytes. -/// Returns hex string with "0x" prefix. -/// -/// Note: The JSON file contains field elements in Montgomery form (internal representation). -/// We must use to_bytes() which performs SSZ serialization to get the canonical form -/// that is expected by from_bytes() during signature verification. -fn pubkey_json_to_hex(pk_file_path: &PathBuf) -> Result> { - // Read JSON file - let json_content = fs::read_to_string(pk_file_path)?; - - // Deserialize into PublicKeyType using serde (this handles Montgomery form) - let public_key: PublicKeyType = serde_json::from_str(&json_content)?; - - // Use to_bytes() which uses SSZ serialization (canonical form) - // This is the correct format expected by from_bytes() during verification - let pubkey_bytes = public_key.to_bytes(); +/// Convert pubkey SSZ file to hex string +/// Reads the `.ssz` file as raw bytes (already in SSZ/canonical form) +/// and returns a hex string with "0x" prefix. +fn pubkey_ssz_to_hex(pk_file_path: &PathBuf) -> Result> { + // Read SSZ bytes from file + let pubkey_bytes = fs::read(pk_file_path)?; // Convert bytes to hex string with "0x" prefix let hex_string = format!("0x{}", hex::encode(&pubkey_bytes)); @@ -161,9 +153,9 @@ fn create_validator_manifest( writeln!(manifest_file, "validators:")?; for i in 0..num_validators { - // Read the pubkey JSON file and convert to hex - let pk_file_path = output_dir.join(format!("validator_{}_pk.json", i)); - let pubkey_hex = pubkey_json_to_hex(&pk_file_path) + // Read the pubkey SSZ file and convert to hex + let pk_file_path = output_dir.join(format!("validator_{}_pk.ssz", i)); + let pubkey_hex = pubkey_ssz_to_hex(&pk_file_path) .map_err(|e| std::io::Error::new( std::io::ErrorKind::Other, format!("Failed to convert pubkey to hex for validator {}: {}", i, e) @@ -171,7 +163,7 @@ fn create_validator_manifest( writeln!(manifest_file, " - index: {}", i)?; writeln!(manifest_file, " pubkey_hex: {}", pubkey_hex)?; - writeln!(manifest_file, " privkey_file: validator_{}_sk.json", i)?; + writeln!(manifest_file, " privkey_file: validator_{}_sk.ssz", i)?; if i < num_validators - 1 { writeln!(manifest_file)?; } From 1eee317b2abd443106698f464519eec57204e4a4 Mon Sep 17 00:00:00 2001 From: bomanaps Date: Tue, 2 Dec 2025 11:51:11 -0300 Subject: [PATCH 2/6] enable export format --- README.md | 16 +++++++++++++--- src/main.rs | 43 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 54 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 5f588fb..b5c235c 100644 --- a/README.md +++ b/README.md @@ -25,29 +25,39 @@ Generate validator key pairs for hash-based signatures: cargo run --release --bin hashsig -- generate \ --num-validators 5 \ --log-num-active-epochs 18 \ - --output-dir ./generated_keys + --output-dir ./generated_keys \ + --export-format both ``` **Parameters:** - `--num-validators`: Number of validator key pairs to generate - `--log-num-active-epochs`: Log2 of the number of active epochs (e.g., 18 for 2^18 = 262,144 active epochs) - `--output-dir`: Directory where keys will be saved +- `--export-format`: Key export format, one of: + - `both` (default): export **SSZ binaries** (`.ssz`) and **legacy JSON** (`.json`) + - `ssz`: export **only** SSZ binaries (`.ssz`) - `--create-manifest`: Create a manifest file (optional, defaults to `true`) -**Output:** -The tool creates a directory with key pairs exported as **SSZ-encoded binary files**: +**Output (default `--export-format both`):** +The tool creates a directory with key pairs exported as **SSZ-encoded binary files** plus **legacy JSON**: ``` generated_keys/ ├── validator-keys-manifest.yaml # Manifest file (if --create-manifest is true) ├── validator_0_pk.ssz # Public key for validator 0 (SSZ bytes) ├── validator_0_sk.ssz # Secret key for validator 0 (SSZ bytes) +├── validator_0_pk.json # Public key for validator 0 (legacy JSON) +├── validator_0_sk.json # Secret key for validator 0 (legacy JSON) ├── validator_1_pk.ssz # Public key for validator 1 (SSZ bytes) ├── validator_1_sk.ssz # Secret key for validator 1 (SSZ bytes) +├── validator_1_pk.json # Public key for validator 1 (legacy JSON) +├── validator_1_sk.json # Secret key for validator 1 (legacy JSON) └── ... ``` The `.ssz` files contain the **canonical SSZ serialization** (`to_bytes()`) of the underlying key types from `leanSig`, written directly as raw bytes (not JSON or hex). +The `.json` files are provided **only for backwards compatibility** and may be removed in a future version once all clients consume SSZ. + ## Current Implementation Currently uses the `SIGTopLevelTargetSumLifetime32Dim64Base8` scheme: diff --git a/src/main.rs b/src/main.rs index 147dd01..a11aaf6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,7 @@ use std::fs::{self, File}; use std::io::Write; use std::path::PathBuf; -use clap::{Parser, Subcommand}; +use clap::{Parser, Subcommand, ValueEnum}; use leansig::serialization::Serializable; use leansig::signature::{ generalized_xmss::instantiations_poseidon_top_level::lifetime_2_to_the_32::hashing_optimized::SIGTopLevelTargetSumLifetime32Dim64Base8, @@ -12,6 +12,14 @@ use leansig::signature::{ // Type alias for the public key type type PublicKeyType = ::PublicKey; +#[derive(Copy, Clone, Debug, Eq, PartialEq, ValueEnum)] +enum ExportFormat { + /// Export only SSZ-encoded binary files (`.ssz`) + Ssz, + /// Export both SSZ-encoded binaries (`.ssz`) and legacy JSON files + Both, +} + /// A CLI tool to generate cryptographic keys for hash-based signatures. #[derive(Parser, Debug)] #[command(author, version, about, long_about = None)] @@ -36,6 +44,10 @@ enum Commands { #[arg(long)] output_dir: PathBuf, + /// Export format for keys: `ssz` (binary only) or `both` (SSZ + JSON, legacy) + #[arg(long, value_enum, default_value_t = ExportFormat::Both)] + export_format: ExportFormat, + /// Create a manifest file for validator keys #[arg(long, default_value = "true")] create_manifest: bool, @@ -50,9 +62,15 @@ fn main() -> std::io::Result<()> { num_validators, log_num_active_epochs, output_dir, + export_format, create_manifest, } => { - generate_keys(num_validators, log_num_active_epochs, output_dir.clone())?; + generate_keys( + num_validators, + log_num_active_epochs, + export_format, + output_dir.clone(), + )?; if create_manifest { create_validator_manifest(&output_dir, num_validators, log_num_active_epochs)?; @@ -66,6 +84,7 @@ fn main() -> std::io::Result<()> { fn generate_keys( num_validators: usize, log_num_active_epochs: usize, + export_format: ExportFormat, output_dir: PathBuf, ) -> std::io::Result<()> { // Create the output directory if it doesn't exist @@ -86,6 +105,8 @@ fn generate_keys( let mut rng = rand::rng(); + let write_json = matches!(export_format, ExportFormat::Both); + for i in 0..num_validators { let key_prefix = format!("validator_{}", i); @@ -110,6 +131,24 @@ fn generate_keys( println!(" ✅ {}_pk.ssz", key_prefix); println!(" ✅ {}_sk.ssz", key_prefix); + + if write_json { + // Also export legacy JSON representations for backwards compatibility + let pk_json = + serde_json::to_string_pretty(&pk).expect("Failed to serialize public key to JSON"); + let mut pk_json_file = + File::create(output_dir.join(format!("{}_pk.json", key_prefix)))?; + pk_json_file.write_all(pk_json.as_bytes())?; + + let sk_json = + serde_json::to_string_pretty(&sk).expect("Failed to serialize secret key to JSON"); + let mut sk_json_file = + File::create(output_dir.join(format!("{}_sk.json", key_prefix)))?; + sk_json_file.write_all(sk_json.as_bytes())?; + + println!(" ⚠️ (legacy) {}_pk.json", key_prefix); + println!(" ⚠️ (legacy) {}_sk.json", key_prefix); + } } println!("\n✅ Successfully generated and saved {} validator key pairs.", num_validators); From 8b32221cb4f555e84d2f1029ed008e43d2237632 Mon Sep 17 00:00:00 2001 From: bomanaps Date: Tue, 2 Dec 2025 20:15:17 +0100 Subject: [PATCH 3/6] Removed the unused PublicKeyType alias --- src/main.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index a11aaf6..edecbbc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,9 +9,6 @@ use leansig::signature::{ SignatureScheme, }; -// Type alias for the public key type -type PublicKeyType = ::PublicKey; - #[derive(Copy, Clone, Debug, Eq, PartialEq, ValueEnum)] enum ExportFormat { /// Export only SSZ-encoded binary files (`.ssz`) From d4e18237ef6fbbae2a9041cf94431392a1bdb0dd Mon Sep 17 00:00:00 2001 From: Chetany Bhardwaj Date: Thu, 4 Dec 2025 20:48:09 +0530 Subject: [PATCH 4/6] fix: image build --- Dockerfile | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/Dockerfile b/Dockerfile index 064f996..bcd90f0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,20 +2,15 @@ FROM rust:1.87 AS builder WORKDIR /usr/src/hash-sig-cli +# Copy manifest and pre-fetch dependencies (cached if unchanged) COPY Cargo.toml ./ - -# Create a new empty shell project to cache dependencies RUN mkdir src && echo "fn main() {}" > src/main.rs +RUN cargo fetch +RUN rm -rf src -# Build the dependencies -RUN cargo build --release -RUN rm -f target/release/deps/hash_sig_cli* - -# Copy the source code -COPY . . - -# Build the actual application -RUN cargo build --release +# Copy actual sources and build the binary +COPY src ./src +RUN cargo build --release --bin hashsig # Use a smaller base image for the final image FROM debian:bookworm-slim @@ -24,4 +19,4 @@ FROM debian:bookworm-slim COPY --from=builder /usr/src/hash-sig-cli/target/release/hashsig /usr/local/bin/hashsig # Set the entry point for the container -ENTRYPOINT ["hashsig"] \ No newline at end of file +ENTRYPOINT ["hashsig"] From 949cadd47a94ff1745d0f80041466d0ac267c1bd Mon Sep 17 00:00:00 2001 From: Mercy Boma Naps Nkari <96525594+bomanaps@users.noreply.github.com> Date: Tue, 9 Dec 2025 15:45:50 +0100 Subject: [PATCH 5/6] add validator byte-based naming format (#32) * add validator byte-based naming format * change flag to distributed --- README.md | 80 ++++++++++++++++++++++++++++++++++++++++ src/main.rs | 103 ++++++++++++++++++++++++++++++++++------------------ 2 files changed, 148 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index b5c235c..94387df 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ cargo build --release Generate validator key pairs for hash-based signatures: +**Indexed format (default, index-based naming):** ```bash cargo run --release --bin hashsig -- generate \ --num-validators 5 \ @@ -29,6 +30,16 @@ cargo run --release --bin hashsig -- generate \ --export-format both ``` +**Distributed format (first-3 last-3 bytes naming):** +```bash +cargo run --release --bin hashsig -- generate \ + --num-validators 5 \ + --log-num-active-epochs 18 \ + --output-dir ./generated_keys \ + --export-format both \ + --distributed +``` + **Parameters:** - `--num-validators`: Number of validator key pairs to generate - `--log-num-active-epochs`: Log2 of the number of active epochs (e.g., 18 for 2^18 = 262,144 active epochs) @@ -37,8 +48,11 @@ cargo run --release --bin hashsig -- generate \ - `both` (default): export **SSZ binaries** (`.ssz`) and **legacy JSON** (`.json`) - `ssz`: export **only** SSZ binaries (`.ssz`) - `--create-manifest`: Create a manifest file (optional, defaults to `true`) +- `--distributed`: Use distributed naming format based on first-3 and last-3 bytes of public key (e.g., `validator-987678-de4578-pk.ssz`). When enabled, the manifest will not include the `index` field. **Output (default `--export-format both`):** + +**Indexed format (default, without `--distributed`):** The tool creates a directory with key pairs exported as **SSZ-encoded binary files** plus **legacy JSON**: ``` generated_keys/ @@ -54,6 +68,72 @@ generated_keys/ └── ... ``` +**Distributed format (with `--distributed`):** +When using `--distributed`, validators are named using the first-3 and last-3 bytes of the public key (hex-encoded): +``` +generated_keys/ +├── validator-keys-manifest.yaml # Manifest file (if --create-manifest is true) +├── validator-987678-de4578-pk.ssz # Public key (SSZ bytes) +├── validator-987678-de4578-sk.ssz # Secret key (SSZ bytes) +├── validator-987678-de4578-pk.json # Public key (legacy JSON) +├── validator-987678-de4578-sk.json # Secret key (legacy JSON) +├── validator-52d9eb-dd0a4f-pk.ssz # Public key (SSZ bytes) +├── validator-52d9eb-dd0a4f-sk.ssz # Secret key (SSZ bytes) +├── validator-52d9eb-dd0a4f-pk.json # Public key (legacy JSON) +├── validator-52d9eb-dd0a4f-sk.json # Secret key (legacy JSON) +└── ... +``` + +**Manifest Format:** + +**Indexed format manifest** (default, without `--distributed`): +```yaml +# Hash-Signature Validator Keys Manifest +# Generated by hash-sig-cli + +key_scheme: SIGTopLevelTargetSumLifetime32Dim64Base8 +hash_function: Poseidon2 +encoding: TargetSum +lifetime: 4294967296 +log_num_active_epochs: 18 +num_active_epochs: 262144 +num_validators: 2 + +validators: + - index: 0 + pubkey_hex: 0x... + privkey_file: validator_0_sk.ssz + + - index: 1 + pubkey_hex: 0x... + privkey_file: validator_1_sk.ssz +``` + +**Distributed format manifest** (with `--distributed`): +```yaml +# Hash-Signature Validator Keys Manifest +# Generated by hash-sig-cli + +key_scheme: SIGTopLevelTargetSumLifetime32Dim64Base8 +hash_function: Poseidon2 +encoding: TargetSum +lifetime: 4294967296 +log_num_active_epochs: 18 +num_active_epochs: 262144 +num_validators: 2 + +validators: + - pubkey_hex: 0x... + privkey_file: validator-987678-de4578-sk.ssz + + - pubkey_hex: 0x... + privkey_file: validator-52d9eb-dd0a4f-sk.ssz +``` + +**Key differences:** +- **Indexed format**: Manifest includes an `index` field for each validator +- **Distributed format**: Manifest does **not** include the `index` field (only `pubkey_hex` and `privkey_file`) + The `.ssz` files contain the **canonical SSZ serialization** (`to_bytes()`) of the underlying key types from `leanSig`, written directly as raw bytes (not JSON or hex). The `.json` files are provided **only for backwards compatibility** and may be removed in a future version once all clients consume SSZ. diff --git a/src/main.rs b/src/main.rs index edecbbc..673f3ad 100644 --- a/src/main.rs +++ b/src/main.rs @@ -48,6 +48,10 @@ enum Commands { /// Create a manifest file for validator keys #[arg(long, default_value = "true")] create_manifest: bool, + + /// Use distributed format: name validators with first-3 last-3 bytes of public key + #[arg(long)] + distributed: bool, }, } @@ -61,16 +65,24 @@ fn main() -> std::io::Result<()> { output_dir, export_format, create_manifest, + distributed, } => { - generate_keys( + let validator_info = generate_keys( num_validators, log_num_active_epochs, export_format, output_dir.clone(), + distributed, )?; if create_manifest { - create_validator_manifest(&output_dir, num_validators, log_num_active_epochs)?; + create_validator_manifest( + &output_dir, + num_validators, + log_num_active_epochs, + distributed, + &validator_info, + )?; } } } @@ -78,12 +90,18 @@ fn main() -> std::io::Result<()> { Ok(()) } +struct ValidatorInfo { + pubkey_hex: String, + privkey_file: String, +} + fn generate_keys( num_validators: usize, log_num_active_epochs: usize, export_format: ExportFormat, output_dir: PathBuf, -) -> std::io::Result<()> { + distributed: bool, +) -> std::io::Result> { // Create the output directory if it doesn't exist fs::create_dir_all(&output_dir)?; @@ -103,12 +121,9 @@ fn generate_keys( let mut rng = rand::rng(); let write_json = matches!(export_format, ExportFormat::Both); + let mut validator_info_list = Vec::new(); for i in 0..num_validators { - let key_prefix = format!("validator_{}", i); - - println!("Generating {}...", key_prefix); - // Generate the key pair let (pk, sk) = SIGTopLevelTargetSumLifetime32Dim64Base8::key_gen( &mut rng, @@ -116,8 +131,30 @@ fn generate_keys( activation_duration, ); - // Serialize the public key to SSZ bytes and write to a binary .ssz file + // Serialize the public key to SSZ bytes let pk_bytes = pk.to_bytes(); + + // Determine key prefix based on format + let key_prefix = if distributed { + // Extract first 3 and last 3 bytes from pk_bytes + if pk_bytes.len() < 3 { + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Public key bytes too short to extract first-3 last-3 bytes" + )); + } + let first_3 = &pk_bytes[0..3]; + let last_3 = &pk_bytes[pk_bytes.len() - 3..]; + let first_3_hex = hex::encode(first_3); + let last_3_hex = hex::encode(last_3); + format!("validator-{}-{}", first_3_hex, last_3_hex) + } else { + format!("validator_{}", i) + }; + + println!("Generating {}...", key_prefix); + + // Write public key to SSZ file let mut pk_file = File::create(output_dir.join(format!("{}_pk.ssz", key_prefix)))?; pk_file.write_all(&pk_bytes)?; @@ -146,30 +183,27 @@ fn generate_keys( println!(" ⚠️ (legacy) {}_pk.json", key_prefix); println!(" ⚠️ (legacy) {}_sk.json", key_prefix); } + + // Store validator info for manifest + let pubkey_hex = format!("0x{}", hex::encode(&pk_bytes)); + let privkey_file = format!("{}_sk.ssz", key_prefix); + validator_info_list.push(ValidatorInfo { + pubkey_hex, + privkey_file, + }); } println!("\n✅ Successfully generated and saved {} validator key pairs.", num_validators); - Ok(()) -} - -/// Convert pubkey SSZ file to hex string -/// Reads the `.ssz` file as raw bytes (already in SSZ/canonical form) -/// and returns a hex string with "0x" prefix. -fn pubkey_ssz_to_hex(pk_file_path: &PathBuf) -> Result> { - // Read SSZ bytes from file - let pubkey_bytes = fs::read(pk_file_path)?; - - // Convert bytes to hex string with "0x" prefix - let hex_string = format!("0x{}", hex::encode(&pubkey_bytes)); - - Ok(hex_string) + Ok(validator_info_list) } fn create_validator_manifest( output_dir: &PathBuf, num_validators: usize, log_num_active_epochs: usize, + distributed: bool, + validator_info: &[ValidatorInfo], ) -> std::io::Result<()> { println!("\n📄 Creating validator manifest..."); @@ -188,19 +222,18 @@ fn create_validator_manifest( writeln!(manifest_file, "num_validators: {}\n", num_validators)?; writeln!(manifest_file, "validators:")?; - for i in 0..num_validators { - // Read the pubkey SSZ file and convert to hex - let pk_file_path = output_dir.join(format!("validator_{}_pk.ssz", i)); - let pubkey_hex = pubkey_ssz_to_hex(&pk_file_path) - .map_err(|e| std::io::Error::new( - std::io::ErrorKind::Other, - format!("Failed to convert pubkey to hex for validator {}: {}", i, e) - ))?; - - writeln!(manifest_file, " - index: {}", i)?; - writeln!(manifest_file, " pubkey_hex: {}", pubkey_hex)?; - writeln!(manifest_file, " privkey_file: validator_{}_sk.ssz", i)?; - if i < num_validators - 1 { + for (i, info) in validator_info.iter().enumerate() { + if distributed { + // Distributed format: no index field + writeln!(manifest_file, " - pubkey_hex: {}", info.pubkey_hex)?; + writeln!(manifest_file, " privkey_file: {}", info.privkey_file)?; + } else { + // Indexed format: include index field + writeln!(manifest_file, " - index: {}", i)?; + writeln!(manifest_file, " pubkey_hex: {}", info.pubkey_hex)?; + writeln!(manifest_file, " privkey_file: {}", info.privkey_file)?; + } + if i < validator_info.len() - 1 { writeln!(manifest_file)?; } } From e0139c62ab0f236de4eb81804ed121cda1fe726e Mon Sep 17 00:00:00 2001 From: Mercy Boma Naps Nkari <96525594+bomanaps@users.noreply.github.com> Date: Fri, 23 Jan 2026 06:15:15 +0100 Subject: [PATCH 6/6] update hash commit (#33) --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index be6363e..affb116 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ path = "src/main.rs" [dependencies] # LeanSig library (from GitHub repository, pinned to commit) -leansig = { git = "https://github.com/leanEthereum/leanSig.git", rev = "f10dcbefac2502d356d93f686e8b4ecd8dc8840a" } +leansig = { git = "https://github.com/leanEthereum/leanSig.git", rev = "73bedc26ed961b110df7ac2e234dc11361a4bf25" } # CLI framework clap = { version = "4.5", features = ["derive"] }