diff --git a/Cargo.lock b/Cargo.lock index b7b5175..7172f0e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -469,6 +469,7 @@ dependencies = [ "async-trait", "clap", "delegate", + "derive_builder", "jf-primitives", "lazy_static", "local-ip-address", @@ -612,6 +613,7 @@ name = "client" version = "0.1.0" dependencies = [ "clap", + "derive_builder", "jf-primitives", "proto", "rand", @@ -776,6 +778,41 @@ dependencies = [ "syn 2.0.48", ] +[[package]] +name = "darling" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 1.0.109", +] + +[[package]] +name = "darling_macro" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +dependencies = [ + "darling_core", + "quote", + "syn 1.0.109", +] + [[package]] name = "data-encoding" version = "2.5.0" @@ -824,6 +861,37 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "derive_builder" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f59169f400d8087f238c5c0c7db6a28af18681717f3b623227d92f397e938c7" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4ec317cc3e7ef0928b0ca6e4a634a4d6c001672ae210438cf114a83e56b018d" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_builder_macro" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "870368c3fb35b8031abb378861d4460f573b92238ec2152c927a21f77e3e0127" +dependencies = [ + "derive_builder_core", + "syn 1.0.109", +] + [[package]] name = "digest" version = "0.10.7" @@ -1300,6 +1368,12 @@ dependencies = [ "want", ] +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "idna" version = "0.5.0" @@ -1529,6 +1603,7 @@ name = "marshal" version = "0.1.0" dependencies = [ "clap", + "derive_builder", "jf-primitives", "proto", "tokio", diff --git a/Cargo.toml b/Cargo.toml index 37aecd0..26f0406 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,4 +13,5 @@ tracing-subscriber = "0.3.18" clap = { version = "4.4.18", features = ["derive"] } async-trait = "0.1.77" prometheus = { version = "0.13.3" } -lazy_static = "1.4.0" \ No newline at end of file +lazy_static = "1.4.0" +derive_builder = "0.13.1" \ No newline at end of file diff --git a/broker/Cargo.toml b/broker/Cargo.toml index 4965e8f..13c6d98 100644 --- a/broker/Cargo.toml +++ b/broker/Cargo.toml @@ -24,3 +24,4 @@ async-trait.workspace = true paste = "1.0.14" prometheus = { workspace = true } lazy_static = { workspace = true } +derive_builder.workspace = true \ No newline at end of file diff --git a/broker/src/lib.rs b/broker/src/lib.rs index 0d1eb2b..06db27b 100644 --- a/broker/src/lib.rs +++ b/broker/src/lib.rs @@ -18,6 +18,7 @@ use std::{ }; mod metrics; +use derive_builder::Builder; use proto::{ crypto::signature::{KeyPair, SignatureScheme}, metrics as proto_metrics, @@ -37,7 +38,7 @@ use tokio::{select, spawn, sync::RwLock}; use crate::metrics::RUNNING_SINCE; /// The broker's configuration. We need this when we create a new one. -/// TODO: clean up these generics. could be a generic type that implements both +#[derive(Builder)] pub struct Config { /// The user (public) advertise address: what the marshals send to users upon authentication. /// Users connect to us with this address. @@ -46,12 +47,15 @@ pub struct Config { pub public_bind_address: String, /// Whether or not we want to serve metrics + #[builder(default = "true")] pub metrics_enabled: bool, /// The port we want to serve metrics on + #[builder(default = "9090")] pub metrics_port: u16, /// The IP/interface we want to serve the metrics on + #[builder(default = "String::from(\"127.0.0.1\")")] pub metrics_ip: String, /// The broker (private) advertise address: what other brokers use to connect to us. @@ -65,9 +69,12 @@ pub struct Config { pub keypair: KeyPair, /// An optional TLS cert path - pub maybe_tls_cert_path: Option, + #[builder(default)] + pub tls_cert_path: Option, + /// An optional TLS key path - pub maybe_tls_key_path: Option, + #[builder(default)] + pub tls_key_path: Option, } /// The broker `Inner` that we use to share common data between broker tasks. @@ -138,8 +145,8 @@ impl Broker Broker::bind( public_bind_address, - maybe_tls_cert_path.clone(), - maybe_tls_key_path.clone(), + tls_cert_path.clone(), + tls_key_path.clone(), ) .await, Connection, @@ -174,12 +181,8 @@ impl Broker::bind( - private_bind_address, - maybe_tls_cert_path, - maybe_tls_key_path, - ) - .await, + ::bind(private_bind_address, tls_cert_path, tls_key_path,) + .await, Connection, format!( "failed to bind to public (user) bind address {}", diff --git a/broker/src/main.rs b/broker/src/main.rs index b67ebc3..61f6021 100644 --- a/broker/src/main.rs +++ b/broker/src/main.rs @@ -1,7 +1,7 @@ //! The following is the main `Broker` binary, which just instantiates and runs //! a `Broker` object. -use broker::{Broker, Config}; +use broker::{Broker, Config, ConfigBuilder}; use clap::Parser; use jf_primitives::signatures::{ bls_over_bn254::BLSOverBN254CurveSignatureScheme as BLS, SignatureScheme, @@ -56,35 +56,26 @@ async fn main() -> Result<()> { // Get our local IP address let private_ip_address = bail!(local_ip(), Connection, "failed to get local IP address"); + let private_address = format!("{}:{}", private_ip_address, args.private_bind_port); // Create deterministic keys for brokers (for now, obviously) let (private_key, public_key) = BLS::key_gen(&(), &mut DeterministicRng(0)).unwrap(); - let broker_config = Config { - // Public addresses: explicitly defined advertise address, bind address is on every interface - // but with the specified port. - public_advertise_address: args.public_advertise_address, - public_bind_address: format!("0.0.0.0:{}", args.public_bind_port), - - metrics_enabled: args.metrics_enabled, - metrics_port: args.metrics_port, - metrics_ip: args.metrics_ip, - - // Private addresses: bind to the local interface with the specified port - private_advertise_address: format!("{}:{}", private_ip_address, args.private_bind_port), - private_bind_address: format!("{}:{}", private_ip_address, args.private_bind_port), - - discovery_endpoint: args.discovery_endpoint, - - keypair: KeyPair { - public_key, - private_key, - }, - - // TODO: clap this - maybe_tls_cert_path: None, - maybe_tls_key_path: None, - }; + let broker_config: Config = bail!( + ConfigBuilder::default() + .public_advertise_address(args.public_advertise_address) + .public_bind_address(format!("0.0.0.0:{}", args.public_bind_port)) + .private_advertise_address(private_address.clone()) + .private_bind_address(private_address) + .discovery_endpoint(args.discovery_endpoint) + .keypair(KeyPair { + public_key, + private_key + }) + .build(), + Parse, + "failed to build broker configuration" + ); // Create new `Broker` // Uses TCP from broker connections and Quic for user connections. diff --git a/client/Cargo.toml b/client/Cargo.toml index e22a937..2021a84 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -17,4 +17,5 @@ tokio.workspace = true tracing-subscriber.workspace = true rand.workspace = true tracing.workspace = true -clap.workspace = true \ No newline at end of file +clap.workspace = true +derive_builder.workspace = true \ No newline at end of file diff --git a/client/src/lib.rs b/client/src/lib.rs index dfb6e18..64e24f6 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -25,6 +25,7 @@ use retry::Retry; pub struct Client(Retry); pub type Config = retry::Config; +pub type ConfigBuilder = retry::ConfigBuilder; impl Client { /// Creates a new `Retry` from a configuration. diff --git a/client/src/main.rs b/client/src/main.rs index 440d68b..207d6d3 100644 --- a/client/src/main.rs +++ b/client/src/main.rs @@ -2,11 +2,16 @@ //! We spawn two clients. In a single-broker run, this lets them connect //! cross-broker. -use std::{marker::PhantomData, time::Duration}; +use std::time::Duration; use clap::Parser; -use client::{Client, Config, KeyPair}; -use proto::{connection::protocols::quic::Quic, crypto::rng::DeterministicRng, error::Result}; +use client::{Client, ConfigBuilder, KeyPair}; +use proto::{ + bail, + connection::protocols::quic::Quic, + crypto::rng::DeterministicRng, + error::{Error, Result}, +}; use jf_primitives::signatures::{ bls_over_bn254::BLSOverBN254CurveSignatureScheme as BLS, SignatureScheme, @@ -31,19 +36,22 @@ async fn main() -> Result<()> { tracing_subscriber::fmt::init(); // Generate two random keypairs, one for each client - let (private_key, public_key) = BLS::key_gen(&(), &mut DeterministicRng(args.id)).unwrap(); - let client = Client::::new(Config { - endpoint: "127.0.0.1:8082".to_string(), - keypair: KeyPair { - public_key, - private_key, - }, - subscribed_topics: vec![], - pd: PhantomData, - }) - .await?; + // Build the config, the endpoint being where we expect the marshal to be + let config = bail!( + ConfigBuilder::default() + .endpoint("127.0.0.1:8082".to_string()) + .keypair(KeyPair { + public_key, + private_key, + }) + .build(), + Parse, + "failed to build client config" + ); + + let client = Client::::new(config).await?; // We want the first node to send to the second if args.id != 0 { diff --git a/client/src/retry.rs b/client/src/retry.rs index 8032ff9..9c6b26f 100644 --- a/client/src/retry.rs +++ b/client/src/retry.rs @@ -7,6 +7,7 @@ use std::{collections::HashSet, marker::PhantomData, sync::Arc, time::Duration}; +use derive_builder::Builder; use proto::{ connection::{ auth::user::UserAuth, @@ -65,6 +66,7 @@ pub struct Inner { } /// The configuration needed to construct a `Retry` connection. +#[derive(Builder)] pub struct Config { /// This is the remote address that we authenticate to. It can either be a broker /// or a marshal. @@ -76,9 +78,11 @@ pub struct Config { /// The topics we're currently subscribed to. We need this here so we can send our subscriptions /// when we connect to a new server. + #[builder(default = "Vec::new()")] pub subscribed_topics: Vec, /// The phantom data we need to be able to make use of these types + #[builder(default)] pub pd: PhantomData, } diff --git a/marshal/Cargo.toml b/marshal/Cargo.toml index 5822dcc..2ae4a20 100644 --- a/marshal/Cargo.toml +++ b/marshal/Cargo.toml @@ -17,3 +17,4 @@ tokio.workspace = true tracing.workspace = true tracing-subscriber.workspace = true clap.workspace = true +derive_builder.workspace = true \ No newline at end of file diff --git a/marshal/src/lib.rs b/marshal/src/lib.rs index 5e5ac3d..e5dcdd5 100644 --- a/marshal/src/lib.rs +++ b/marshal/src/lib.rs @@ -7,12 +7,37 @@ use std::{marker::PhantomData, sync::Arc}; mod handlers; +use derive_builder::Builder; use proto::{ - bail, connection::protocols::{Listener, Protocol}, crypto::signature::SignatureScheme, discovery::DiscoveryClient, error::{Error, Result}, DiscoveryClientType, UserProtocol + bail, + connection::protocols::{Listener, Protocol}, + crypto::signature::SignatureScheme, + discovery::DiscoveryClient, + error::{Error, Result}, + DiscoveryClientType, UserProtocol, }; use tokio::spawn; use tracing::warn; +/// The `Marshal's` configuration (with a Builder), to help with usability. +/// We need this to construct a `Marshal` +#[derive(Builder)] +pub struct Config { + /// The bind address that users will reach. Example: `0.0.0.0:1738` + bind_address: String, + + /// The discovery client endpoint (either Redis or local depending on feature) + discovery_endpoint: String, + + /// The optional TLS cert path. If one is not specified, it will be self-signed + #[builder(default)] + tls_cert_path: Option, + + /// The optional TLS key path. If one is not specified, it will be self-signed + #[builder(default)] + tls_key_path: Option, +} + /// A connection `Marshal`. The user authenticates with it, receiving a permit /// to connect to an actual broker. Think of it like a load balancer for /// the brokers. @@ -28,26 +53,27 @@ pub struct Marshal { pd: PhantomData, } -impl Marshal -{ +impl Marshal { /// Create and return a new marshal from a bind address, and an optional /// TLS cert and key path. /// /// # Errors /// - If we fail to bind to the local address - pub async fn new( - bind_address: String, - discovery_endpoint: String, - maybe_tls_cert_path: Option, - maybe_tls_key_path: Option, - ) -> Result { + pub async fn new(config: Config) -> Result { + // Extrapolate values from the underlying marshal configuration + let Config { + bind_address, + discovery_endpoint, + tls_cert_path, + tls_key_path, + } = config; + // Parse bind address let bind_address = bail!(bind_address.parse(), Parse, "failed to parse bind address"); // Create the `Listener` from the bind address let listener = bail!( - ::bind(bind_address, maybe_tls_cert_path, maybe_tls_key_path) - .await, + ::bind(bind_address, tls_cert_path, tls_key_path).await, Connection, format!("failed to listen to address {}", bind_address) ); diff --git a/marshal/src/main.rs b/marshal/src/main.rs index a994e70..c238c5e 100644 --- a/marshal/src/main.rs +++ b/marshal/src/main.rs @@ -3,8 +3,11 @@ use clap::Parser; use jf_primitives::signatures::bls_over_bn254::BLSOverBN254CurveSignatureScheme as BLS; -use marshal::Marshal; -use proto::error::Result; +use marshal::{ConfigBuilder, Marshal}; +use proto::{ + bail, + error::{Error, Result}, +}; //TODO: for both client and marshal, clean up and comment `main.rs` // TODO: forall, add logging where we need it @@ -30,14 +33,18 @@ async fn main() -> Result<()> { // Initialize tracing tracing_subscriber::fmt::init(); - // Create new `Marshal` - let marshal = Marshal::::new( - format!("0.0.0.0:{}", args.bind_port), - args.discovery_endpoint, - None, - None, - ) - .await?; + // Create a new `Config` + let config = bail!( + ConfigBuilder::default() + .bind_address(format!("0.0.0.0:{}", args.bind_port)) + .discovery_endpoint(args.discovery_endpoint) + .build(), + Parse, + "failed to build Marshal config" + ); + + // Create new `Marshal` from the config + let marshal = Marshal::::new(config).await?; // Start the main loop, consuming it marshal.start().await?; diff --git a/proto/src/crypto/signature.rs b/proto/src/crypto/signature.rs index b990f4b..dcda13b 100644 --- a/proto/src/crypto/signature.rs +++ b/proto/src/crypto/signature.rs @@ -13,9 +13,9 @@ use super::rng::DeterministicRng; /// with the associated public and private keys. pub trait SignatureScheme: Send + Sync + Clone + 'static { /// The signing key type - type PrivateKey: Send + Sync; + type PrivateKey: Clone + Send + Sync; /// The verification key type - type PublicKey: Serializable + Eq + Send + Sync; + type PublicKey: Serializable + Eq + Clone + Send + Sync; /// Sign a message using a private key /// @@ -47,6 +47,7 @@ pub trait Serializable: Sized { } /// We encapsulate keys here to help readability. +#[derive(Clone)] pub struct KeyPair { /// The underlying (public) verification key, used to authenticate with the server. pub public_key: Scheme::PublicKey,