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

fix: various faucet fixes for next #362

Merged
merged 13 commits into from
May 21, 2024
194 changes: 78 additions & 116 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Makefile.toml
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ workspace = false
script = '''
docker volume create miden-db

ABSOLUTE_PATH="$(pwd)/bin/node/miden-node.toml"
ABSOLUTE_PATH="$(pwd)/config/miden-node.toml"

docker run --name miden-node \
-p 57291:57291 \
Expand Down
8 changes: 4 additions & 4 deletions bin/faucet/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "miden-faucet"
version = "0.3.0"
version = "0.3.1"
description = "Miden node token faucet"
readme = "README.md"
keywords = ["miden", "node", "faucet"]
Expand All @@ -23,11 +23,11 @@ actix-web = "4"
async-mutex = "1.4.0"
derive_more = "0.99.17"
figment = { version = "0.10", features = ["toml", "env"] }
miden-client = { version = "0.2", features = ["concurrent"] }
miden-lib = { version = "0.2" }
miden-client = { version = "0.3", features = ["concurrent"] }
miden-lib = { version = "0.3" }
miden-node-proto = { workspace = true }
miden-node-utils = { workspace = true }
miden-objects = { version = "0.2" }
miden-objects = { version = "0.3" }
rand_chacha = "0.3"
serde = { version = "1.0", features = ["derive"] }
tracing = { workspace = true }
Expand Down
14 changes: 14 additions & 0 deletions bin/faucet/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,19 @@

This crate contains a binary for running a Miden rollup faucet.

## Running the faucet
mFragaBA marked this conversation as resolved.
Show resolved Hide resolved
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
```

2. From the "miden-node" repo root run the faucet:
```bash
cargo run --bin miden-faucet --features testing --release
```

After a few seconds you may go to `http://localhost:8080` and see the faucet UI.

## License
This project is [MIT licensed](../../LICENSE).
25 changes: 12 additions & 13 deletions bin/faucet/src/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,16 @@ use miden_objects::{
accounts::AccountId,
assets::FungibleAsset,
notes::{NoteId, NoteType},
transaction::OutputNote,
utils::serde::Serializable,
};
use serde::{Deserialize, Serialize};
use tracing::info;

use crate::{errors::FaucetError, utils::FaucetState};
use crate::{
errors::FaucetError,
utils::{build_client, FaucetState},
};

#[derive(Deserialize)]
struct FaucetRequest {
Expand All @@ -30,7 +34,7 @@ struct FaucetMetadataReponse {
pub async fn get_metadata(state: web::Data<FaucetState>) -> HttpResponse {
let response = FaucetMetadataReponse {
id: state.id.to_string(),
asset_amount_options: state.asset_amount_options.clone(),
asset_amount_options: state.faucet_config.asset_amount_options.clone(),
};

HttpResponse::Ok().json(response)
Expand All @@ -47,11 +51,12 @@ pub async fn get_tokens(
);

// Check that the amount is in the asset amount options
if !state.asset_amount_options.contains(&req.asset_amount) {
if !state.faucet_config.asset_amount_options.contains(&req.asset_amount) {
return Err(FaucetError::BadRequest("Invalid asset amount.".to_string()).into());
}

let client = state.client.clone();
let client_config = state.faucet_config.clone();
let mut client = build_client(client_config.database_filepath, &client_config.node_url)?;

// Receive and hex user account id
let target_account_id = AccountId::from_hex(req.account_id.as_str())
Expand All @@ -73,15 +78,11 @@ pub async fn get_tokens(

// Instantiate transaction request
let tx_request = client
.lock()
.await
.build_transaction_request(tx_template)
.map_err(|err| FaucetError::InternalServerError(err.to_string()))?;

// Run transaction executor & execute transaction
let tx_result = client
.lock()
.await
.new_transaction(tx_request)
.map_err(|err| FaucetError::InternalServerError(err.to_string()))?;

Expand All @@ -90,21 +91,19 @@ pub async fn get_tokens(

// Run transaction prover & send transaction to node
client
.lock()
.await
.submit_transaction(tx_result)
.await
.map_err(|err| FaucetError::InternalServerError(err.to_string()))?;

let note_id: NoteId;

// Serialize note into bytes
let bytes = match created_notes.first() {
Some(note) => {
let bytes = match created_notes.get_note(0) {
OutputNote::Full(note) => {
note_id = note.id();
InputNoteRecord::from(note.clone()).to_bytes()
},
None => {
OutputNote::Header(_) => {
return Err(
FaucetError::InternalServerError("Failed to generate note.".to_string()).into()
)
Expand Down
2 changes: 1 addition & 1 deletion bin/faucet/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ 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";

// MAIN
// =================================================================================================
Expand Down
26 changes: 23 additions & 3 deletions bin/faucet/src/static/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ body {
height: 100vh;
display: flex;
flex-direction: column;
padding-top: 100px;
padding-top: 350px;
justify-content: center;
align-items: center;
background-image: url(./background.png);
Expand Down Expand Up @@ -53,6 +53,8 @@ body {
}

#center-container {
position: absolute;
top: 125px;
background-color: rgb(17, 24, 39);
border-radius: 10px;
display: flex;
Expand Down Expand Up @@ -109,20 +111,38 @@ body {
margin-top: 10px;
}

#info-message {
#visibility-buttons button:disabled {
background-color: rgb(124, 58, 237, 0.5);
}

.info-message {
color: white;
font-weight: bold;
text-align: center;
}

#info-container {
.info-container {
display: none;
background-color: rgb(17, 24, 39);
border-radius: 10px;
padding: 20px;
margin-top: 25px;
}

.loader {
border: 8px solid #ccc;
border-top: 8px solid rgb(124, 58, 237);
border-radius: 50%;
width: 60px;
height: 60px;
animation: spin 2s linear infinite;
}

@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}

#client-commands {
background-color: black;
color: lightgray;
Expand Down
11 changes: 7 additions & 4 deletions bin/faucet/src/static/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,15 @@ <h2 id="subtitle">Request test tokens</h2>
<button id="button-public">Send Public Note</button>
</div>
</div>
<div id="info-container">
<p id="info-message">You can consume the note by running the following commands in your shell</p>
<div id="loading" class="info-container">
<div class="loader"></div>
</div>
<div id="info" class="info-container">
<p class="info-message">You can consume the note by running the following commands in your shell</p>
<p id="client-commands">
<span id="import-command" style="display: none;">miden input-notes import ./path/to/downloaded/note<br></span>
<span id="import-command" style="display: none;">miden import ./path/to/downloaded/note<br></span>
miden sync <br>
miden tx new consume-notes --account <span id="command-account-id"></span> <span id="note-id"></span>
miden consume-notes --account <span id="command-account-id"></span> <span id="note-id"></span>
</p>
</div>
<script src="./index.js"></script>
Expand Down
15 changes: 12 additions & 3 deletions bin/faucet/src/static/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ document.addEventListener('DOMContentLoaded', function () {
const publicButton = document.getElementById('button-public');
const accountIdInput = document.getElementById('account-id');
const errorMessage = document.getElementById('error-message');
const infoContainer = document.getElementById('info-container');
const info = document.getElementById('info');
const importCommand = document.getElementById('import-command');
const noteIdElem = document.getElementById('note-id');
const accountIdElem = document.getElementById('command-account-id');
const assetSelect = document.getElementById('asset-amount');
const loading = document.getElementById('loading');

fetchMetadata();

Expand Down Expand Up @@ -45,9 +46,13 @@ document.addEventListener('DOMContentLoaded', function () {
return;
}

infoContainer.style.display = 'none';
privateButton.disabled = true;
publicButton.disabled = true;

info.style.display = 'none';
importCommand.style.display = 'none';

loading.style.display = 'block';
try {
const response = await fetch('http://localhost:8080/get_tokens', {
method: 'POST',
Expand All @@ -68,12 +73,16 @@ document.addEventListener('DOMContentLoaded', function () {
const noteId = response.headers.get('Note-Id');
noteIdElem.textContent = noteId;
accountIdElem.textContent = accountId;
infoContainer.style.display = 'block';
info.style.display = 'block';
loading.style.display = 'none';
} catch (error) {
console.error('Error:', error);
errorMessage.textContent = 'Failed to receive tokens. Please try again.';
errorMessage.style.display = 'block';
}

privateButton.disabled = false;
publicButton.disabled = false;
}

function downloadBlob(blob, filename) {
Expand Down
48 changes: 26 additions & 22 deletions bin/faucet/src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
use std::{path::PathBuf, sync::Arc};
use std::{path::PathBuf, rc::Rc};

use async_mutex::Mutex;
use miden_client::{
client::{get_random_coin, rpc::TonicRpcClient, Client},
config::StoreConfig,
store::{sqlite_store::SqliteStore, AuthInfo},
client::{
get_random_coin, rpc::TonicRpcClient, store_authenticator::StoreAuthenticator, Client,
},
config::{Endpoint, RpcConfig, StoreConfig},
store::sqlite_store::SqliteStore,
};
use miden_lib::{accounts::faucets::create_basic_fungible_faucet, AuthScheme};
use miden_objects::{
accounts::{Account, AccountId, AccountStorageType},
accounts::{Account, AccountId, AccountStorageType, AuthSecretKey},
assets::TokenSymbol,
crypto::{dsa::rpo_falcon512::SecretKey, rand::RpoRandomCoin},
Felt,
Expand All @@ -18,18 +19,22 @@ use tracing::info;

use crate::{config::FaucetConfig, errors::FaucetError};

pub type FaucetClient = Client<TonicRpcClient, RpoRandomCoin, SqliteStore>;
pub type FaucetClient = Client<
TonicRpcClient,
RpoRandomCoin,
SqliteStore,
StoreAuthenticator<RpoRandomCoin, SqliteStore>,
>;

#[derive(Clone)]
pub struct FaucetState {
pub id: AccountId,
pub asset_amount_options: Vec<u64>,
pub client: Arc<Mutex<FaucetClient>>,
pub faucet_config: FaucetConfig,
}

/// Instatiantes the Miden faucet
pub async fn build_faucet_state(config: FaucetConfig) -> Result<FaucetState, FaucetError> {
let mut client = build_client(config.database_filepath.clone(), config.node_url.clone())?;
let mut client = build_client(config.database_filepath.clone(), &config.node_url)?;

let faucet_account = create_fungible_faucet(
&config.token_symbol,
Expand All @@ -45,15 +50,14 @@ pub async fn build_faucet_state(config: FaucetConfig) -> Result<FaucetState, Fau

Ok(FaucetState {
id: faucet_account.id(),
asset_amount_options: config.asset_amount_options,
client: Arc::new(Mutex::new(client)),
faucet_config: config,
})
}

/// Instantiates the Miden client
pub fn build_client(
database_filepath: PathBuf,
node_url: String,
node_url: &str,
) -> Result<FaucetClient, FaucetError> {
let database_filepath_os_string = database_filepath.into_os_string();
let database_filepath = match database_filepath_os_string.into_string() {
Expand All @@ -73,23 +77,23 @@ pub fn build_client(
let store = SqliteStore::new(store_config)
.map_err(|err| FaucetError::DatabaseError(err.to_string()))?;

// Setup the executor store
let executor_store_config = StoreConfig {
database_filepath: database_filepath.clone(),
};
let executor_store = SqliteStore::new(executor_store_config)
.map_err(|err| FaucetError::DatabaseError(err.to_string()))?;
let store = Rc::new(store);

// Setup the tonic rpc client
let api = TonicRpcClient::new(&node_url);
let endpoint = Endpoint::try_from(node_url).map_err(|err| {
FaucetError::ConfigurationError(format!("Error parsing RPC endpoint: {}", err))
})?;
let rpc_config = RpcConfig { endpoint, ..Default::default() };
let api = TonicRpcClient::new(&rpc_config);

// Setup the rng
let rng = get_random_coin();
let authenticator = StoreAuthenticator::new_with_rng(store.clone(), rng);

info!("Successfully built client");

// Setup the client
Ok(Client::new(api, rng, store, executor_store, false))
Ok(Client::new(api, rng, store, authenticator, false))
}

/// Creates a Miden fungible faucet from arguments
Expand Down Expand Up @@ -122,7 +126,7 @@ pub fn create_fungible_faucet(
.map_err(|err| FaucetError::AccountCreationError(err.to_string()))?;

client
.insert_account(&account, Some(account_seed), &AuthInfo::RpoFalcon512(secret))
.insert_account(&account, Some(account_seed), &AuthSecretKey::RpoFalcon512(secret))
.map_err(|err| FaucetError::DatabaseError(err.to_string()))?;

Ok(account)
Expand Down
4 changes: 2 additions & 2 deletions bin/node/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM rust:1.76-slim-bookworm AS builder
FROM rust:1.78-slim-bookworm AS builder

RUN apt-get update && \
apt-get -y upgrade && \
Expand All @@ -21,7 +21,7 @@ RUN apt-get update && \
sqlite3 \
&& rm -rf /var/lib/apt/lists/*

COPY --from=builder /app/genesis.dat genesis.dat
COPY --from=builder /app/genesis.dat /opt/miden/genesis.dat
COPY --from=builder /app/accounts accounts
COPY --from=builder /usr/local/cargo/bin/miden-node /usr/local/bin/miden-node

Expand Down
File renamed without changes.