Skip to content
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

feat: Add init commands #392

Merged
merged 9 commits into from
Jun 28, 2024
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Changelog

* Add `init` command for node and faucet (#392).
* Added crate to distribute node RPC protobuf files (#391).
* Changed state sync endpoint to return a list of `TransactionSummary` objects instead of just transaction IDs (#386).
* Fixed faucet note script so that it uses the `aux` input (#387).
Expand Down
3 changes: 3 additions & 0 deletions Cargo.lock

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

2 changes: 2 additions & 0 deletions bin/faucet/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ actix-cors = "0.7.0"
actix-files = "0.6.5"
actix-web = "4"
async-mutex = "1.4.0"
clap = { version = "4.3", features = ["derive"] }
derive_more = "0.99.17"
figment = { version = "0.10", features = ["toml", "env"] }
miden-lib = { workspace = true, features = ["concurrent"] }
Expand All @@ -32,5 +33,6 @@ rand = { version = "0.8.5" }
rand_chacha = "0.3"
serde = { version = "1.0", features = ["derive"] }
thiserror = { workspace = true }
toml = { version = "0.8" }
tonic = { workspace = true }
tracing = { workspace = true }
18 changes: 14 additions & 4 deletions bin/faucet/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,23 @@ This crate contains a binary for running a Miden rollup faucet.
## Running the faucet
1. Run a local node, for example using the docker image. From the "miden-node" repo root run the following commands:
```bash
cargo make docker-build-node
cargo make docker-run-node
make docker-build-node
make docker-run-node
```

2. From the "miden-node" repo root run the faucet:
2. Install the faucet (with the "testing" feature):
```bash
cargo run --bin miden-faucet --features testing --release
make install-faucet-testing
```

3. Create the default faucet configuration file:
```bash
miden-faucet init
```

4. Start the faucet server:
```bash
miden-faucet start
```

After a few seconds you may go to `http://localhost:8080` and see the faucet UI.
Expand Down
17 changes: 16 additions & 1 deletion bin/faucet/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::{
path::PathBuf,
};

use miden_node_utils::config::Endpoint;
use miden_node_utils::config::{Endpoint, DEFAULT_FAUCET_SERVER_PORT, DEFAULT_NODE_RPC_PORT};
use serde::{Deserialize, Serialize};

// Faucet config
Expand Down Expand Up @@ -43,3 +43,18 @@ impl Display for FaucetConfig {
))
}
}

impl Default for FaucetConfig {
fn default() -> Self {
Self {
endpoint: Endpoint::localhost(DEFAULT_FAUCET_SERVER_PORT),
node_url: Endpoint::localhost(DEFAULT_NODE_RPC_PORT).to_string(),
timeout_ms: 10000,
database_filepath: PathBuf::from("store.sqlite3"),
asset_amount_options: vec![100, 500, 1000],
token_symbol: "POL".to_string(),
decimals: 8,
max_supply: 1000000,
}
}
}
123 changes: 90 additions & 33 deletions bin/faucet/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ mod errors;
mod handlers;
mod state;

use std::path::PathBuf;
use std::{fs::File, io::Write, path::PathBuf};

use actix_cors::Cors;
use actix_files::Files;
use actix_web::{
middleware::{DefaultHeaders, Logger},
web, App, HttpServer,
};
use clap::{Parser, Subcommand};
use errors::FaucetError;
use miden_node_utils::config::load_config;
use state::FaucetState;
Expand All @@ -26,8 +27,32 @@ use crate::{
// =================================================================================================

const COMPONENT: &str = "miden-faucet";
const FAUCET_CONFIG_FILE_PATH: &str = "miden-faucet.toml";

const FAUCET_CONFIG_FILE_PATH: &str = "config/miden-faucet.toml";
// COMMANDS
// ================================================================================================

#[derive(Parser)]
#[command(version, about, long_about = None)]
pub struct Cli {
#[command(subcommand)]
pub command: Command,
}

#[derive(Subcommand)]
pub enum Command {
/// Start the faucet server
Start {
#[arg(short, long, value_name = "FILE", default_value = FAUCET_CONFIG_FILE_PATH)]
config: PathBuf,
},

/// Generates default configuration file for the faucet
Init {
#[arg(short, long, default_value = FAUCET_CONFIG_FILE_PATH)]
config_path: String,
},
}

// MAIN
// =================================================================================================
Expand All @@ -37,37 +62,69 @@ async fn main() -> Result<(), FaucetError> {
miden_node_utils::logging::setup_logging()
.map_err(|err| FaucetError::StartError(err.to_string()))?;

let config: FaucetConfig = load_config(PathBuf::from(FAUCET_CONFIG_FILE_PATH).as_path())
.extract()
.map_err(|err| FaucetError::ConfigurationError(err.to_string()))?;

let faucet_state = FaucetState::new(config.clone()).await?;

info!(target: COMPONENT, %config, "Initializing server");

info!("Server is now running on: {}", config.endpoint_url());

HttpServer::new(move || {
let cors = Cors::default().allow_any_origin().allow_any_method();
App::new()
.app_data(web::Data::new(faucet_state.clone()))
.wrap(cors)
.wrap(Logger::default())
.wrap(DefaultHeaders::new().add(("Cache-Control", "no-cache")))
.service(get_metadata)
.service(get_tokens)
.service(
Files::new("/", "bin/faucet/src/static")
.use_etag(false)
.use_last_modified(false)
.index_file("index.html"),
)
})
.bind((config.endpoint.host, config.endpoint.port))
.map_err(|err| FaucetError::StartError(err.to_string()))?
.run()
.await
.map_err(|err| FaucetError::StartError(err.to_string()))?;
let cli = Cli::parse();

match &cli.command {
Command::Start { config } => {
let config: FaucetConfig = load_config(config.as_path())
.extract()
.map_err(|err| FaucetError::ConfigurationError(err.to_string()))?;

let faucet_state = FaucetState::new(config.clone()).await?;

info!(target: COMPONENT, %config, "Initializing server");

info!("Server is now running on: {}", config.endpoint_url());

HttpServer::new(move || {
let cors = Cors::default().allow_any_origin().allow_any_method();
App::new()
.app_data(web::Data::new(faucet_state.clone()))
.wrap(cors)
.wrap(Logger::default())
.wrap(DefaultHeaders::new().add(("Cache-Control", "no-cache")))
.service(get_metadata)
.service(get_tokens)
.service(
Files::new("/", "bin/faucet/src/static")
.use_etag(false)
.use_last_modified(false)
.index_file("index.html"),
)
})
.bind((config.endpoint.host, config.endpoint.port))
.map_err(|err| FaucetError::StartError(err.to_string()))?
.run()
.await
.map_err(|err| FaucetError::StartError(err.to_string()))?;
},
Command::Init { config_path } => {
let current_dir = std::env::current_dir().map_err(|err| {
FaucetError::ConfigurationError(format!("failed to open current directory: {err}"))
})?;

let mut config_file_path = current_dir.clone();
config_file_path.push(config_path);

let config = FaucetConfig::default();
let config_as_toml_string = toml::to_string(&config).map_err(|err| {
FaucetError::ConfigurationError(format!(
"Failed to serialize default config: {err}"
))
})?;

let mut file_handle =
File::options().write(true).create_new(true).open(&config_file_path).map_err(
|err| FaucetError::ConfigurationError(format!("Error opening the file: {err}")),
)?;

file_handle.write(config_as_toml_string.as_bytes()).map_err(|err| {
FaucetError::ConfigurationError(format!("Error writing to file: {err}"))
})?;

println!("Config file successfully created at: {:?}", config_file_path);
},
}

Ok(())
}
1 change: 1 addition & 0 deletions bin/node/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ miden-objects = { workspace = true }
rand_chacha = "0.3"
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1.29", features = ["rt-multi-thread", "net", "macros"] }
toml = { version = "0.8" }
tracing = { workspace = true }
tracing-subscriber = { workspace = true }

Expand Down
47 changes: 41 additions & 6 deletions bin/node/src/commands/genesis/inputs.rs
Original file line number Diff line number Diff line change
@@ -1,34 +1,36 @@
use serde::Deserialize;
use std::time::{SystemTime, UNIX_EPOCH};

use serde::{Deserialize, Serialize};

// INPUT HELPER STRUCTS
// ================================================================================================

/// Input types are helper structures designed for parsing and deserializing genesis input files.
/// They serve as intermediary representations, facilitating the conversion from
/// placeholder types (like `GenesisInput`) to internal types (like `GenesisState`).
#[derive(Debug, Clone, Deserialize)]
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct GenesisInput {
pub version: u32,
pub timestamp: u32,
pub accounts: Vec<AccountInput>,
}

#[derive(Debug, Clone, Deserialize)]
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(tag = "type")]
pub enum AccountInput {
BasicWallet(BasicWalletInputs),
BasicFungibleFaucet(BasicFungibleFaucetInputs),
}

#[derive(Debug, Clone, Deserialize)]
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct BasicWalletInputs {
pub init_seed: String,
pub auth_scheme: AuthSchemeInput,
pub auth_seed: String,
pub storage_mode: String,
}

#[derive(Debug, Clone, Deserialize)]
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct BasicFungibleFaucetInputs {
pub init_seed: String,
pub auth_scheme: AuthSchemeInput,
Expand All @@ -39,7 +41,40 @@ pub struct BasicFungibleFaucetInputs {
pub storage_mode: String,
}

#[derive(Debug, Clone, Copy, Deserialize)]
#[derive(Debug, Clone, Copy, Deserialize, Serialize)]
pub enum AuthSchemeInput {
RpoFalcon512,
}

impl Default for GenesisInput {
fn default() -> Self {
Self {
version: 1,
timestamp: SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("Current timestamp should be greater than unix epoch")
.as_secs() as u32,
accounts: vec![
AccountInput::BasicWallet(BasicWalletInputs {
init_seed: "0xa123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
.to_string(),
auth_scheme: AuthSchemeInput::RpoFalcon512,
auth_seed: "0xb123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
.to_string(),
storage_mode: "off-chain".to_string(),
}),
AccountInput::BasicFungibleFaucet(BasicFungibleFaucetInputs {
init_seed: "0xc123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
.to_string(),
auth_scheme: AuthSchemeInput::RpoFalcon512,
auth_seed: "0xd123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
.to_string(),
token_symbol: "POL".to_string(),
decimals: 12,
max_supply: 1000000,
storage_mode: "on-chain".to_string(),
}),
],
}
}
}
2 changes: 1 addition & 1 deletion bin/node/src/commands/genesis/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::{
};

use anyhow::{anyhow, Result};
use inputs::{AccountInput, AuthSchemeInput, GenesisInput};
pub use inputs::{AccountInput, AuthSchemeInput, GenesisInput};
use miden_lib::{
accounts::{faucets::create_basic_fungible_faucet, wallets::create_basic_wallet},
AuthScheme,
Expand Down
42 changes: 42 additions & 0 deletions bin/node/src/commands/init.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use std::{fs::File, io::Write, path::PathBuf};

use anyhow::{anyhow, Result};

use crate::{commands::genesis::GenesisInput, config::NodeConfig};

// INIT
// ===================================================================================================

pub fn init_config_files(config_file_path: PathBuf, genesis_file_path: PathBuf) -> Result<()> {
let config = NodeConfig::default();
let config_as_toml_string = toml::to_string(&config)
.map_err(|err| anyhow!("Failed to serialize default config: {}", err))?;

write_string_in_file(config_as_toml_string, &config_file_path)?;

println!("Config file successfully created at: {:?}", config_file_path);

let genesis = GenesisInput::default();
let genesis_as_toml_string = toml::to_string(&genesis)
.map_err(|err| anyhow!("Failed to serialize default config: {}", err))?;

write_string_in_file(genesis_as_toml_string, &genesis_file_path)?;

println!("Genesis config file successfully created at: {:?}", genesis_file_path);

Ok(())
}

fn write_string_in_file(content: String, path: &PathBuf) -> Result<()> {
let mut file_handle = File::options()
.write(true)
.create_new(true)
.open(path)
.map_err(|err| anyhow!("Error opening the file: {err}"))?;

file_handle
.write(content.as_bytes())
.map_err(|err| anyhow!("Error writing to file: {err}"))?;

Ok(())
}
Loading
Loading