Skip to content

Add sign sub-command in aggregator genesis command #1082

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jul 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

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

38 changes: 25 additions & 13 deletions docs/root/manual/developer-docs/nodes/mithril-aggregator.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,8 @@ SUBCOMMANDS:
import Import payload signed with genesis secret key and create & import a genesis certificate
```

### bootstrap sub-command (test-only)

Run 'genesis bootstrap' command in release with default configuration, **only in test mode**.
This allows the Mithril Aggregator node to bootstrap a `Genesis Certificate`. After this operation, the Mithril Aggregator will be able to produce new snapshots and certificates.

Expand All @@ -203,27 +205,28 @@ Or with a specific `Genesis Secret Key`, **only in test mode**.
./mithril-aggregator genesis bootstrap --genesis-secret-key **YOUR_SECRET_KEY*
```

Run 'genesis export' command in release with default configuration.
This allows the Mithril Aggregator node to export the `Genesis Payload` that needs to be signed (and later reimported) of the `Genesis Certificate`. The signature of the `Genesis Payload` must be done manually with the owner of the `Genesis Secret Key`.

```bash
./mithril-aggregator genesis export
```
### export sub-command

Or with a custom export path (to override the default value `./mithril-genesis-payload.txt`)
Run 'genesis export' command in release.
This allows the Mithril Aggregator node to export the `Genesis Payload` that needs to be signed (and later reimported) of the `Genesis Certificate`. The signature of the `Genesis Payload` must be done manually with the owner of the `Genesis Secret Key`.

```bash
./mithril-aggregator genesis export --target-path **YOUR_TARGET_PATH**
```

Run 'genesis import' command in release with default configuration.
This allows the Mithril Aggregator node to import the signed payload of the `Genesis Certificate` and create it in the store. After this operation, the Mithril Aggregator will be able to produce new snapshots and certificates.
### sign sub-command

Run 'genesis sign' command in release.
This allows the Mithril Aggregator node to sign the `Genesis Payload` that needs to be reimported. The signature of the `Genesis Payload` must be done manually by the owner of the `Genesis Secret Key`.

```bash
./mithril-aggregator genesis import
./mithril-aggregator genesis sign --to-sign-payload-path **TO_SIGN_PAYLOAD_PATH** --target-signed-payload-path **TARGET_SIGNED_PAYLOAD_PATH** --genesis-secret-key-path **GENESIS_SECRET_KEY_PATH**
```

Or with a custom export path (to override the default value `./mithril-genesis-signed-payload.txt`)
### import sub-command

Run 'genesis import' command in release.
This allows the Mithril Aggregator node to import the signed payload of the `Genesis Certificate` and create it in the store. After this operation, the Mithril Aggregator will be able to produce new snapshots and certificates.

```bash
./mithril-aggregator genesis import --signed-payload-path **YOUR_SIGNED_PAYLOAD_PATH**
Expand Down Expand Up @@ -397,6 +400,7 @@ Here are the subcommands available:
| **serve** | Aggregator runs its HTTP server in nominal mode and orchestrates multi signatures production |
| **help** | Print this message or the help of the given subcommand(s) |
| **genesis export** | Export genesis payload to sign with genesis secret key |
| **genesis sign** | Sign genesis payload with genesis secret key |
| **genesis import** | Import genesis signature (payload signed with genesis secret key) and create & import a genesis certificate in the store |
| **genesis bootstrap** | Bootstrap a genesis certificate (test only usage) |
| **era list** | List the supported eras |
Expand Down Expand Up @@ -450,17 +454,25 @@ General parameters:
|-----------|---------------------|:---------------------:|----------------------|-------------|---------------|---------|:---------:|
| `genesis_secret_key` | - | - | `GENESIS_SECRET_KEY` | Genesis secret key, :warning: for test only | - | - | - |

`genesis export` command:

| Parameter | Command Line (long) | Command Line (short) | Environment Variable | Description | Default Value | Example | Mandatory |
|-----------|---------------------|:---------------------:|----------------------|-------------|---------------|---------|:---------:|
| `target_path` | `--target-path` | - | - | Path of the file to export the payload to. | - | - | - | - |

`genesis import` command:

| Parameter | Command Line (long) | Command Line (short) | Environment Variable | Description | Default Value | Example | Mandatory |
|-----------|---------------------|:---------------------:|----------------------|-------------|---------------|---------|:---------:|
| `signed_payload_path` | `--signed-payload-path` | - | - | Path of the payload to import. | - | - | - | - |

`genesis export` command:
`genesis sign` command:

| Parameter | Command Line (long) | Command Line (short) | Environment Variable | Description | Default Value | Example | Mandatory |
|-----------|---------------------|:---------------------:|----------------------|-------------|---------------|---------|:---------:|
| `target_path` | `--target-path` | - | - | Path of the file to export the payload to. | - | - | - | - |
| `to_sign_payload_path` | `--to-sign-payload-path` | - | - | Path of the payload to sign. | - | - | - | - |
| `target_signed_payload_path` | `--target-signed-payload-path` | - | - | Path of the signed payload to export. | - | - | - | - |
| `genesis_secret_key_path` | `--genesis-secret-key-path` | - | - | Path of the Genesis secret key. | - | - | - |

`era list` command:

Expand Down
2 changes: 1 addition & 1 deletion mithril-aggregator/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "mithril-aggregator"
version = "0.3.57"
version = "0.3.58"
description = "A Mithril Aggregator server"
authors = { workspace = true }
edition = { workspace = true }
Expand Down
42 changes: 42 additions & 0 deletions mithril-aggregator/src/commands/genesis_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ pub enum GenesisSubCommand {
/// Genesis certificate import command.
Import(ImportGenesisSubCommand),

/// Genesis certificate sign command.
Sign(SignGenesisSubCommand),

/// Genesis certificate bootstrap command.
Bootstrap(BootstrapGenesisSubCommand),
}
Expand All @@ -48,6 +51,7 @@ impl GenesisSubCommand {
Self::Bootstrap(cmd) => cmd.execute(config_builder).await,
Self::Export(cmd) => cmd.execute(config_builder).await,
Self::Import(cmd) => cmd.execute(config_builder).await,
Self::Sign(cmd) => cmd.execute(config_builder).await,
}
}
}
Expand Down Expand Up @@ -124,6 +128,44 @@ impl ImportGenesisSubCommand {
}
}

#[derive(Parser, Debug, Clone)]
pub struct SignGenesisSubCommand {
/// To Sign Payload Path
#[clap(long)]
to_sign_payload_path: PathBuf,

/// Target Signed Payload Path
#[clap(long)]
target_signed_payload_path: PathBuf,

/// Genesis Secret Key Path
#[clap(long)]
genesis_secret_key_path: PathBuf,
}

impl SignGenesisSubCommand {
pub async fn execute(
&self,
_config_builder: ConfigBuilder<DefaultState>,
) -> Result<(), Box<dyn Error>> {
debug!("SIGN GENESIS command");
println!(
"Genesis sign payload from {} to {}",
self.to_sign_payload_path.to_string_lossy(),
self.target_signed_payload_path.to_string_lossy()
);

GenesisTools::sign_genesis_certificate(
&self.to_sign_payload_path,
&self.target_signed_payload_path,
&self.genesis_secret_key_path,
)
.await
.map_err(|err| format!("genesis-tools: sign error: {err}"))?;

Ok(())
}
}
#[derive(Parser, Debug, Clone)]
pub struct BootstrapGenesisSubCommand {
/// Genesis Secret Key (test only)
Expand Down
62 changes: 43 additions & 19 deletions mithril-aggregator/src/tools/genesis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,32 @@ impl GenesisTools {
.await
}

/// Sign the genesis certificate
pub async fn sign_genesis_certificate(
to_sign_payload_path: &Path,
target_signed_payload_path: &Path,
genesis_secret_key_path: &Path,
) -> StdResult<()> {
let mut genesis_secret_key_file = File::open(genesis_secret_key_path).unwrap();
let mut genesis_secret_key_serialized = String::new();
genesis_secret_key_file.read_to_string(&mut genesis_secret_key_serialized)?;

let genesis_secret_key = key_decode_hex(&genesis_secret_key_serialized.trim().to_string())?;
let genesis_signer = ProtocolGenesisSigner::from_secret_key(genesis_secret_key);

let mut to_sign_payload_file = File::open(to_sign_payload_path).unwrap();
let mut to_sign_payload_buffer = Vec::new();
to_sign_payload_file.read_to_end(&mut to_sign_payload_buffer)?;

let genesis_signature = genesis_signer.sign(&to_sign_payload_buffer);
let signed_payload = genesis_signature.to_bytes();

let mut target_signed_payload_file = File::create(target_signed_payload_path)?;
target_signed_payload_file.write_all(&signed_payload)?;

Ok(())
}

async fn create_and_save_genesis_certificate(
&self,
genesis_signature: ProtocolGenesisSignature,
Expand Down Expand Up @@ -162,7 +188,10 @@ mod tests {
use super::*;

fn get_temp_dir(dir_name: &str) -> PathBuf {
let dir = std::env::temp_dir().join("mithril_test").join(dir_name);
let dir = std::env::temp_dir()
.join("mithril_test")
.join("genesis")
.join(dir_name);

if dir.exists() {
let _ = fs::remove_dir_all(&dir);
Expand All @@ -180,20 +209,6 @@ mod tests {
clerk.compute_avk()
}

fn sign_avk_payload(
payload_path: &Path,
target_path: &Path,
genesis_signer: &ProtocolGenesisSigner,
) -> StdResult<()> {
let mut payload_file = File::open(payload_path).unwrap();
let mut payload_buffer = Vec::new();
payload_file.read_to_end(&mut payload_buffer)?;
let payload_signed = genesis_signer.sign(&payload_buffer).to_bytes();
let mut target_file = File::create(target_path)?;
target_file.write_all(&payload_signed)?;
Ok(())
}

fn build_tools(
genesis_signer: &ProtocolGenesisSigner,
) -> (
Expand Down Expand Up @@ -229,17 +244,26 @@ mod tests {
#[tokio::test]
async fn export_sign_then_import_genesis_payload() {
let test_dir = get_temp_dir("export_payload_to_sign");
let path = test_dir.join("payload.txt");
let payload_path = test_dir.join("payload.txt");
let signed_payload_path = test_dir.join("payload-signed.txt");
let genesis_secret_key_path = test_dir.join("genesis.sk");
let genesis_signer = ProtocolGenesisSigner::create_deterministic_genesis_signer();
let (genesis_tools, certificate_store, genesis_verifier, certificate_verifier) =
build_tools(&genesis_signer);

genesis_signer
.export_to_file(&genesis_secret_key_path)
.expect("exporting the secret key should not fail");
genesis_tools
.export_payload_to_sign(&path)
.export_payload_to_sign(&payload_path)
.expect("export_payload_to_sign should not fail");
sign_avk_payload(&path, &signed_payload_path, &genesis_signer)
.expect("sign avk payload should not fail");
GenesisTools::sign_genesis_certificate(
&payload_path,
&signed_payload_path,
&genesis_secret_key_path,
)
.await
.expect("sign_genesis_certificate should not fail");
genesis_tools
.import_payload_signature(&signed_payload_path)
.await
Expand Down
21 changes: 20 additions & 1 deletion mithril-common/src/crypto_helper/genesis.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
use crate::StdResult;
use ed25519_dalek::{ExpandedSecretKey, SignatureError};
use rand_chacha_dalek_compat::rand_core::{self, CryptoRng, RngCore, SeedableRng};
use rand_chacha_dalek_compat::ChaCha20Rng;
use serde::{Deserialize, Serialize};
use std::{fs::File, io::Write, path::Path};
use thiserror::Error;

use super::{ProtocolGenesisSecretKey, ProtocolGenesisSignature, ProtocolGenesisVerificationKey};
use super::{
key_encode_hex, ProtocolGenesisSecretKey, ProtocolGenesisSignature,
ProtocolGenesisVerificationKey,
};

#[derive(Error, Debug)]
/// [ProtocolGenesisSigner] and [ProtocolGenesisVerifier] related errors.
Expand All @@ -18,6 +23,7 @@ pub enum ProtocolGenesisError {
/// [Genesis Certificate](https://mithril.network/doc/mithril/mithril-protocol/certificates#the-certificate-chain-design)
#[derive(Debug, Serialize, Deserialize)]
pub struct ProtocolGenesisSigner {
/// Protocol Genesis secret key
pub(crate) secret_key: ProtocolGenesisSecretKey,
}

Expand Down Expand Up @@ -75,6 +81,19 @@ impl ProtocolGenesisSigner {
let verification_key = self.create_verification_key(&expanded_secret_key);
expanded_secret_key.sign(message, &verification_key)
}

/// Export the secret key from the genesis verifier to a file. TEST ONLY
#[doc(hidden)]
pub fn export_to_file(&self, secret_key_path: &Path) -> StdResult<()> {
let mut genesis_secret_key_file = File::create(secret_key_path)?;
genesis_secret_key_file.write_all(
key_encode_hex(self.secret_key.as_bytes())
.unwrap()
.as_bytes(),
)?;

Ok(())
}
}

/// A protocol Genesis Verifier that is responsible for verifying the
Expand Down