Skip to content

Commit

Permalink
POST certifier service
Browse files Browse the repository at this point in the history
  • Loading branch information
poszu committed Nov 2, 2023
1 parent 3cf5917 commit e883bf0
Show file tree
Hide file tree
Showing 13 changed files with 794 additions and 40 deletions.
536 changes: 502 additions & 34 deletions Cargo.lock

Large diffs are not rendered by default.

12 changes: 10 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
[workspace]
members = [".", "ffi", "scrypt-ocl", "initializer", "profiler", "service"]
members = [
".",
"ffi",
"scrypt-ocl",
"initializer",
"profiler",
"service",
"certifier",
]

[package]
name = "post-rs"
Expand All @@ -20,7 +28,7 @@ itertools = "0.10.5"
serde = { version = "1.0.152", features = ["derive"] }
serde_json = "1.0.93"
bytemuck = "1.13.0"
serde_with = { version = "2.2.0", features = ["base64"] }
serde_with = { version = "2.2.0", features = ["base64", "hex"] }

scrypt-jane = { git = "https://github.com/spacemeshos/scrypt-jane-rs", branch = "main" }
blake3 = "1.3.3"
Expand Down
26 changes: 26 additions & 0 deletions certifier/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[package]
name = "certifier"
version = "0.1.0"
edition = "2021"

[dependencies]
axum = "0.6.20"
serde = { version = "1.0.190", features = ["derive"] }
tokio = { version = "1.0", features = [
"rt-multi-thread",
"macros",
"sync",
"time",
] }
post-rs = { path = "../" }
serde_with = { version = "3.4.0", features = ["base64", "hex"] }
ed25519-dalek = { version = "2.0.0", features = ["rand_core"] }
clap = { version = "4.4.7", features = ["derive", "env"] }
hex = "0.4.3"
config = "0.13.3"
secrecy = { version = "0.8.0", features = ["serde"] }
tracing = { version = "0.1.40", features = ["log"] }
tracing-log = "0.2.0"
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
rand = "0.8.5"
serde_json = "1.0.108"
28 changes: 28 additions & 0 deletions certifier/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
FROM rust:1 AS chef

RUN cargo install cargo-chef
RUN apt-get update && apt-get install -y\
llvm-dev \
libclang-dev \
clang \
cmake \
protobuf-compiler \
&& rm -rf /var/lib/apt/lists/*

WORKDIR /certifier

FROM chef AS planner
COPY . .
RUN cargo chef prepare --recipe-path recipe.json

FROM chef AS builder
COPY --from=planner /certifier/recipe.json recipe.json
RUN cargo chef cook --release -p certifier --recipe-path recipe.json

COPY . .
RUN cargo build --release -p certifier --bin certifier

FROM debian:bookworm-slim AS runtime
WORKDIR /certifier
COPY --from=builder /certifier/target/release/certifier /usr/local/bin
ENTRYPOINT ["/usr/local/bin/certifier"]
11 changes: 11 additions & 0 deletions certifier/mainnet.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Mainnet configuration
listen: "0.0.0.0:80"
post_cfg:
k1: 26
k2: 37
k3: 37
pow_difficulty: "000dfb23b0979b4b000000000000000000000000000000000000000000000000"
scrypt:
n: 8192
r: 1
p: 1
14 changes: 14 additions & 0 deletions certifier/proof.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"proof": {
"nonce": 77,
"pow": 1234,
"indices": "hBGTHs44tav7YR87sRVafuzZwObCZnK1Z/exYpxwqSQ="
},
"metadata": {
"node_id": "hBGTHs44tav7YR87sRVafuzZwObCZnK1Z/exYpxwqSQ=",
"challenge": "hBGTHs44tav7YR87sRVafuzZwObCZnK1Z/exYpxwqSQ=",
"commitment_atx_id": "ZuxocVjIYWfv7A/K1Lmm8+mNsHzAZaWVpbl5+KINx+I=",
"num_units": 1,
"labels_per_unit": 65536
}
}
76 changes: 76 additions & 0 deletions certifier/src/certifier.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
use std::sync::Arc;

use axum::{extract::State, Json};
use axum::{routing::post, Router};
use ed25519_dalek::{Signer, SigningKey};
use post::pow::randomx::{PoW, RandomXFlag};
use post::verification::{Verifier, VerifyingParams};
use serde::{Deserialize, Serialize};
use serde_with::{base64::Base64, serde_as};
use tracing::instrument;

#[derive(Debug, Deserialize)]

Check warning on line 12 in certifier/src/certifier.rs

View check run for this annotation

Codecov / codecov/patch

certifier/src/certifier.rs#L12

Added line #L12 was not covered by tests
struct CertifyRequest {
proof: post::prove::Proof<'static>,
metadata: post::metadata::ProofMetadata,
}

#[serde_as]
#[derive(Debug, Serialize)]

Check warning on line 19 in certifier/src/certifier.rs

View check run for this annotation

Codecov / codecov/patch

certifier/src/certifier.rs#L19

Added line #L19 was not covered by tests
struct CertifyResponse {
#[serde_as(as = "Base64")]
signature: Vec<u8>,
#[serde_as(as = "Base64")]
pub_key: Vec<u8>,
}

#[instrument(skip(state))]

Check warning on line 27 in certifier/src/certifier.rs

View check run for this annotation

Codecov / codecov/patch

certifier/src/certifier.rs#L27

Added line #L27 was not covered by tests
async fn certify(
State(state): State<Arc<AppState>>,
Json(request): Json<CertifyRequest>,
) -> Result<Json<CertifyResponse>, String> {
tracing::debug!("certifying");

Check warning on line 32 in certifier/src/certifier.rs

View check run for this annotation

Codecov / codecov/patch

certifier/src/certifier.rs#L32

Added line #L32 was not covered by tests

let pub_key = request.metadata.node_id;
let s = state.clone();
let result = tokio::task::spawn_blocking(move || {
s.verifier.verify(
&request.proof,
&request.metadata,
VerifyingParams::new(&request.metadata, &s.cfg).unwrap(),
)
})

Check warning on line 42 in certifier/src/certifier.rs

View check run for this annotation

Codecov / codecov/patch

certifier/src/certifier.rs#L36-L42

Added lines #L36 - L42 were not covered by tests
.await;
match result {
Err(e) => return Err(format!("internal error verifying proof: {e:?}")),
Ok(Err(e)) => return Err(format!("invalid proof: {e:?}")),
_ => {}
}

// Sign the nodeID
let response = CertifyResponse {
signature: state.signer.sign(&pub_key).to_vec(),
pub_key: state.signer.verifying_key().to_bytes().to_vec(),
};
Ok(Json(response))
}

struct AppState {
verifier: Verifier,
cfg: post::config::Config,
signer: SigningKey,
}

pub fn new(cfg: post::config::Config, signer: SigningKey) -> Router {
let state = AppState {
verifier: Verifier::new(Box::new(
PoW::new(RandomXFlag::get_recommended_flags()).expect("creating RandomX PoW verifier"),
)),
cfg,
signer,
};

Router::new()
.route("/certify", post(certify))
.with_state(Arc::new(state))
}

Check warning on line 76 in certifier/src/certifier.rs

View check run for this annotation

Codecov / codecov/patch

certifier/src/certifier.rs#L64-L76

Added lines #L64 - L76 were not covered by tests
25 changes: 25 additions & 0 deletions certifier/src/configuration.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use std::path::Path;

use ed25519_dalek::SecretKey;
use serde_with::{base64::Base64, serde_as};
use tracing::info;

#[serde_as]
#[derive(serde::Deserialize, Clone)]

Check warning on line 8 in certifier/src/configuration.rs

View check run for this annotation

Codecov / codecov/patch

certifier/src/configuration.rs#L8

Added line #L8 was not covered by tests
pub struct Config {
pub listen: std::net::SocketAddr,
#[serde_as(as = "Base64")]
pub signing_key: SecretKey,
pub post_cfg: post::config::Config,
}

pub fn get_configuration(config_path: &Path) -> Result<Config, config::ConfigError> {
info!("loading configuration from {config_path:?}");

Check warning on line 17 in certifier/src/configuration.rs

View check run for this annotation

Codecov / codecov/patch

certifier/src/configuration.rs#L16-L17

Added lines #L16 - L17 were not covered by tests

let config = config::Config::builder()
.add_source(config::File::from(config_path).required(true))
.add_source(config::Environment::with_prefix("CERTIFIER").try_parsing(true))
.build()?;

Check warning on line 22 in certifier/src/configuration.rs

View check run for this annotation

Codecov / codecov/patch

certifier/src/configuration.rs#L19-L22

Added lines #L19 - L22 were not covered by tests

config.try_deserialize()
}

Check warning on line 25 in certifier/src/configuration.rs

View check run for this annotation

Codecov / codecov/patch

certifier/src/configuration.rs#L24-L25

Added lines #L24 - L25 were not covered by tests
2 changes: 2 additions & 0 deletions certifier/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod certifier;
pub mod configuration;
83 changes: 83 additions & 0 deletions certifier/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
use std::path::PathBuf;

use clap::{arg, Parser, Subcommand};
use ed25519_dalek::SigningKey;
use tracing::info;
use tracing_log::LogTracer;
use tracing_subscriber::{EnvFilter, FmtSubscriber};

#[derive(Parser, Debug)]
#[command(version)]
struct Cli {
#[arg(
short,
long,
default_value = "config.yml",
env("CERTIFIER_CONFIG_PATH")
)]
config_path: PathBuf,

#[command(subcommand)]
cmd: Option<Commands>,
}

#[derive(Subcommand, Debug)]
enum Commands {
/// generate keypair and write it to standard out.
/// the keypair is encoded as json
GenerateKeys,
}

fn generate_keys() -> Result<(), Box<dyn std::error::Error>> {
let signing_key: SigningKey = SigningKey::generate(&mut rand::rngs::OsRng);

#[serde_with::serde_as]
#[derive(serde::Serialize)]
struct KeyPair {
#[serde_as(as = "serde_with::base64::Base64")]
public_key: [u8; ed25519_dalek::PUBLIC_KEY_LENGTH],
#[serde_as(as = "serde_with::base64::Base64")]
secret_key: [u8; ed25519_dalek::SECRET_KEY_LENGTH],
}

let keypair = KeyPair {
public_key: signing_key.verifying_key().to_bytes(),
secret_key: signing_key.to_bytes(),
};

serde_json::to_writer_pretty(std::io::stdout(), &keypair)?;
Ok(())
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let args = Cli::parse();

if let Some(Commands::GenerateKeys) = args.cmd {
return generate_keys();
}

LogTracer::init()?;
let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("INFO"));
let subscriber = FmtSubscriber::builder()
.with_env_filter(env_filter)
.finish();
tracing::subscriber::set_global_default(subscriber)?;

let config = certifier::configuration::get_configuration(&args.config_path)?;
let signer = SigningKey::from_bytes(&config.signing_key);

info!(
"listening on: {:?}, pubkey: {:?}",
config.listen,
signer.verifying_key()
);
info!("using POST configuration: {:?}", config.post_cfg);

let app: axum::Router = certifier::certifier::new(config.post_cfg, signer);

axum::Server::bind(&config.listen)
.serve(app.into_make_service())
.await?;
Ok(())
}
9 changes: 7 additions & 2 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
use serde::Deserialize;

/// POST configuration (network parameter)
#[repr(C)]
#[derive(Debug, Clone, Copy)]
#[serde_with::serde_as]
#[derive(Debug, Clone, Copy, Deserialize)]

Check warning on line 6 in src/config.rs

View check run for this annotation

Codecov / codecov/patch

src/config.rs#L6

Added line #L6 was not covered by tests
pub struct Config {
/// K1 specifies the difficulty for a label to be a candidate for a proof.
pub k1: u32,
Expand All @@ -9,13 +13,14 @@ pub struct Config {
pub k3: u32,
/// Difficulty for the nonce proof of work. Lower values increase difficulty of finding
/// `pow` for [Proof][crate::prove::Proof].
#[serde_as(as = "serde_with::hex::Hex")]
pub pow_difficulty: [u8; 32],
/// Scrypt paramters for initilizing labels
pub scrypt: ScryptParams,
}

#[repr(C)]
#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy, Deserialize)]

Check warning on line 23 in src/config.rs

View check run for this annotation

Codecov / codecov/patch

src/config.rs#L23

Added line #L23 was not covered by tests
pub struct ScryptParams {
pub n: usize,
pub r: usize,
Expand Down
6 changes: 5 additions & 1 deletion src/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,14 @@ pub fn load(datadir: &Path) -> eyre::Result<PostMetadata> {
}

#[repr(C)]
#[derive(Debug, Clone)]
#[serde_as]
#[derive(Debug, Clone, Deserialize, Serialize)]

Check warning on line 65 in src/metadata.rs

View check run for this annotation

Codecov / codecov/patch

src/metadata.rs#L65

Added line #L65 was not covered by tests
pub struct ProofMetadata {
#[serde_as(as = "Base64")]
pub node_id: [u8; 32],
#[serde_as(as = "Base64")]
pub commitment_atx_id: [u8; 32],
#[serde_as(as = "Base64")]
pub challenge: [u8; 32],
pub num_units: u32,
pub labels_per_unit: u64,
Expand Down
6 changes: 5 additions & 1 deletion src/prove.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ use eyre::Context;
use primitive_types::U256;
use randomx_rs::RandomXFlag;
use rayon::prelude::{ParallelBridge, ParallelIterator};
use serde::{Deserialize, Serialize};
use serde_with::{base64::Base64, serde_as};

use crate::{
cipher::AesCipher,
Expand All @@ -37,9 +39,11 @@ const BLOCK_SIZE: usize = 16; // size of the aes block
const AES_BATCH: usize = 8; // will use encrypt8 asm method
const CHUNK_SIZE: usize = BLOCK_SIZE * AES_BATCH;

#[derive(Debug, Clone, PartialEq, Eq)]
#[serde_as]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]

Check warning on line 43 in src/prove.rs

View check run for this annotation

Codecov / codecov/patch

src/prove.rs#L43

Added line #L43 was not covered by tests
pub struct Proof<'a> {
pub nonce: u32,
#[serde_as(as = "Base64")]
pub indices: Cow<'a, [u8]>,
pub pow: u64,
}
Expand Down

0 comments on commit e883bf0

Please sign in to comment.