-
Notifications
You must be signed in to change notification settings - Fork 39
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
519 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
[workspace] | ||
members = [ | ||
"block-producer", | ||
"faucet", | ||
"node", | ||
"proto", | ||
"rpc", | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
[package] | ||
name = "miden-node-faucet" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
|
||
[dependencies] | ||
actix-web = "4" | ||
actix-files = "0.6.5" | ||
actix-cors = "0.7.0" | ||
async-mutex = "1.4.0" | ||
clap = { version = "4.5.1", features = ["derive"] } | ||
derive_more = "0.99.17" | ||
miden-client = { git = "https://github.com/0xPolygonMiden/miden-client", branch = "phklive-fix-faucet", features = [ "testing", "concurrent", ] } | ||
miden-lib = { workspace = true } | ||
miden-node-proto = { path = "../proto" } | ||
miden-objects = { workspace = true } | ||
serde = { version = "1.0", features = ["derive"] } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
use std::path::PathBuf; | ||
|
||
use clap::{Parser, Subcommand}; | ||
|
||
#[derive(Parser)] | ||
#[clap(name = "Miden Faucet")] | ||
#[clap(about = "A command line tool for Miden Faucet", long_about = None)] | ||
pub struct Cli { | ||
#[clap(subcommand)] | ||
pub command: Command, | ||
} | ||
|
||
#[derive(Subcommand)] | ||
pub enum Command { | ||
/// Initialise a new Miden faucet from arguments | ||
Init { | ||
#[clap(short, long, required = true)] | ||
token_symbol: String, | ||
|
||
#[clap(short, long, required = true)] | ||
decimals: u8, | ||
|
||
#[clap(short, long, required = true)] | ||
max_supply: u64, | ||
|
||
#[clap(short, long)] | ||
asset_amount: u64, | ||
}, | ||
|
||
/// Imports an existing Miden faucet from specified file | ||
Import { | ||
#[clap(short, long, required = true)] | ||
faucet_path: PathBuf, | ||
|
||
#[clap(short, long)] | ||
asset_amount: u64, | ||
}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
use actix_web::{ | ||
error, | ||
http::{header::ContentType, StatusCode}, | ||
HttpResponse, | ||
}; | ||
use derive_more::Display; | ||
|
||
#[derive(Debug, Display)] | ||
pub enum FaucetError { | ||
BadRequest(String), | ||
InternalServerError(String), | ||
} | ||
|
||
impl error::ResponseError for FaucetError { | ||
fn error_response(&self) -> HttpResponse<actix_web::body::BoxBody> { | ||
let message = match self { | ||
FaucetError::BadRequest(msg) => msg, | ||
FaucetError::InternalServerError(msg) => msg, | ||
}; | ||
|
||
HttpResponse::build(self.status_code()) | ||
.insert_header(ContentType::html()) | ||
.body(message.to_owned()) | ||
} | ||
|
||
fn status_code(&self) -> actix_web::http::StatusCode { | ||
match *self { | ||
FaucetError::InternalServerError(_) => StatusCode::INTERNAL_SERVER_ERROR, | ||
FaucetError::BadRequest(_) => StatusCode::BAD_REQUEST, | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
use crate::{errors::FaucetError, FaucetState}; | ||
use actix_web::{get, http::header, web, HttpResponse, Result}; | ||
use miden_client::client::transactions::TransactionTemplate; | ||
use miden_objects::{ | ||
accounts::AccountId, assets::FungibleAsset, notes::NoteId, utils::serde::Serializable, | ||
}; | ||
use serde::Deserialize; | ||
|
||
#[derive(Deserialize)] | ||
struct User { | ||
account_id: String, | ||
} | ||
|
||
#[get("/get_tokens")] | ||
pub async fn get_tokens( | ||
req: web::Query<User>, | ||
state: web::Data<FaucetState>, | ||
) -> Result<HttpResponse> { | ||
println!("Received a request with account_id: {}", req.account_id); | ||
|
||
let client = state.client.clone(); | ||
|
||
// Receive and hex user account id | ||
let target_account_id = AccountId::from_hex(req.account_id.as_str()) | ||
.map_err(|err| FaucetError::BadRequest(err.to_string()))?; | ||
|
||
// Instantiate asset | ||
let asset = | ||
FungibleAsset::new(state.id, state.asset_amount).expect("Failed to instantiate asset."); | ||
|
||
// Instantiate transaction template | ||
let tx_template = TransactionTemplate::MintFungibleAsset { | ||
asset, | ||
target_account_id, | ||
}; | ||
|
||
// Run transaction executor & execute transaction | ||
let tx_result = client | ||
.lock() | ||
.await | ||
.new_transaction(tx_template) | ||
.map_err(|err| FaucetError::InternalServerError(err.to_string()))?; | ||
|
||
// Get note id | ||
let note_id: NoteId = tx_result | ||
.created_notes() | ||
.first() | ||
.ok_or_else(|| { | ||
FaucetError::InternalServerError("Failed to access generated note.".to_string()) | ||
})? | ||
.id(); | ||
|
||
// Run transaction prover & send transaction to node | ||
{ | ||
let mut client_guard = client.lock().await; | ||
client_guard | ||
.send_transaction(tx_result) | ||
.await | ||
.map_err(|err| FaucetError::InternalServerError(err.to_string()))?; | ||
} | ||
|
||
// Get note from client store | ||
let input_note = state | ||
.client | ||
.clone() | ||
.lock() | ||
.await | ||
.get_input_note(note_id) | ||
.map_err(|err| FaucetError::InternalServerError(err.to_string()))?; | ||
|
||
// Serialize note for transport | ||
let bytes = input_note.to_bytes(); | ||
|
||
// Send generated note to user | ||
Ok(HttpResponse::Ok() | ||
.content_type("application/octet-stream") | ||
.append_header(header::ContentDisposition { | ||
disposition: actix_web::http::header::DispositionType::Attachment, | ||
parameters: vec![actix_web::http::header::DispositionParam::Filename( | ||
"note.mno".to_string(), | ||
)], | ||
}) | ||
.body(bytes)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
use actix_cors::Cors; | ||
use actix_files::Files; | ||
use actix_web::{web, App, HttpServer}; | ||
use async_mutex::Mutex; | ||
use clap::Parser; | ||
use cli::Cli; | ||
use handlers::get_tokens; | ||
use miden_client::client::rpc::TonicRpcClient; | ||
use miden_client::client::Client; | ||
use miden_client::config::{ClientConfig, RpcConfig, StoreConfig}; | ||
use miden_client::store::data_store::SqliteDataStore; | ||
use miden_client::store::Store; | ||
use miden_objects::accounts::AccountId; | ||
use std::io; | ||
use std::sync::Arc; | ||
|
||
mod cli; | ||
mod errors; | ||
mod handlers; | ||
mod utils; | ||
|
||
#[derive(Clone)] | ||
pub struct FaucetState { | ||
id: AccountId, | ||
asset_amount: u64, | ||
client: Arc<Mutex<Client<TonicRpcClient, SqliteDataStore>>>, | ||
} | ||
|
||
#[actix_web::main] | ||
async fn main() -> std::io::Result<()> { | ||
let cli = Cli::parse(); | ||
|
||
// Setup the data_store | ||
let store_config = StoreConfig::default(); | ||
let store = Store::new(store_config).expect("Failed to instantiate store."); | ||
let data_store = SqliteDataStore::new(store); | ||
|
||
// Setup the tonic rpc client | ||
let rpc_config = RpcConfig::default(); | ||
let api = TonicRpcClient::new(&rpc_config.endpoint.to_string()); | ||
|
||
// Setup the client | ||
let client_config = ClientConfig::default(); | ||
let mut client = | ||
Client::new(client_config, api, data_store).expect("Failed to instantiate client."); | ||
|
||
let amount: u64; | ||
|
||
// Create the faucet account | ||
let faucet_account = match &cli.command { | ||
cli::Command::Init { | ||
token_symbol, | ||
decimals, | ||
max_supply, | ||
asset_amount, | ||
} => { | ||
amount = *asset_amount; | ||
utils::create_fungible_faucet(token_symbol, decimals, max_supply, &mut client) | ||
} | ||
cli::Command::Import { | ||
faucet_path, | ||
asset_amount, | ||
} => { | ||
amount = *asset_amount; | ||
utils::import_fungible_faucet(faucet_path, &mut client) | ||
} | ||
} | ||
.map_err(|_| { | ||
io::Error::new( | ||
io::ErrorKind::InvalidData, | ||
"Failed to create faucet account.", | ||
) | ||
})?; | ||
|
||
// Sync client | ||
client | ||
.sync_state() | ||
.await | ||
.map_err(|_| io::Error::new(io::ErrorKind::ConnectionRefused, "Failed to sync state."))?; | ||
|
||
println!( | ||
"✅ Faucet setup successful, account id: {}", | ||
faucet_account.id() | ||
); | ||
|
||
println!("🚀 Starting server on: http://127.0.0.1:8080"); | ||
|
||
// Instantiate faucet state | ||
let faucet_state = FaucetState { | ||
id: faucet_account.id(), | ||
asset_amount: amount, | ||
client: Arc::new(Mutex::new(client)), | ||
}; | ||
|
||
HttpServer::new(move || { | ||
let cors = Cors::default() | ||
.allow_any_origin() | ||
.allowed_methods(vec!["GET"]); | ||
App::new() | ||
.app_data(web::Data::new(faucet_state.clone())) | ||
.wrap(cors) | ||
.service(get_tokens) | ||
.service(Files::new("/", "src/static").index_file("index.html")) | ||
}) | ||
.bind(("127.0.0.1", 8080))? | ||
.run() | ||
.await?; | ||
|
||
Ok(()) | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
input:focus { | ||
outline: none; | ||
} | ||
|
||
*, | ||
*::before, | ||
*::after { | ||
box-sizing: border-box; | ||
} | ||
|
||
|
||
body { | ||
margin: 0; | ||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', | ||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', | ||
sans-serif; | ||
-webkit-font-smoothing: antialiased; | ||
height: 100vh; | ||
display: flex; | ||
justify-content: center; | ||
align-items: center; | ||
background-image: url(./background.png); | ||
background-repeat: repeat; | ||
} | ||
|
||
#error-message { | ||
display: none; | ||
color: red; | ||
text-align: center; | ||
margin-bottom: 5px; | ||
} | ||
|
||
#navbar { | ||
position: fixed; | ||
top: 0; | ||
left: 0; | ||
background-color: rgb(17, 24, 39); | ||
width: 100%; | ||
padding: 20px; | ||
} | ||
|
||
#title { | ||
font-size: x-large; | ||
text-align: center; | ||
font-weight: bold; | ||
color: white; | ||
margin: 0; | ||
} | ||
|
||
#center-container { | ||
background-color: rgb(17, 24, 39); | ||
border-radius: 10px; | ||
display: flex; | ||
flex-direction: column; | ||
padding: 30px; | ||
} | ||
|
||
#subtitle { | ||
font-size: large; | ||
text-align: center; | ||
font-weight: bold; | ||
color: white; | ||
margin: 0; | ||
margin-bottom: 20px; | ||
} | ||
|
||
#account-id { | ||
padding: 10px; | ||
border-radius: 10px; | ||
border: 1px solid #ccc; | ||
margin-bottom: 30px; | ||
width: 300px; | ||
} | ||
|
||
#button { | ||
color: white; | ||
border-radius: 10px; | ||
font-weight: bold; | ||
padding: 10px 20px; | ||
background-color: rgb(124, 58, 237); | ||
width: 300px; | ||
} |
Oops, something went wrong.