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 the option to mint public notes with the faucet #339

Merged
merged 15 commits into from
May 3, 2024
Merged
Show file tree
Hide file tree
Changes from 13 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
Expand Up @@ -2,6 +2,7 @@

## 0.2.1 (2024-04-27)

* Added option to mint pulic notes in the faucet (#339).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should go into v0.3.0 section (you may need to rebase from the latest next).

* Combined node components into a single binary (#323).

## 0.2.0 (2024-04-11)
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ authors = ["Miden contributors"]
homepage = "https://polygon.technology/polygon-miden"
repository = "https://github.com/0xPolygonMiden/miden-node"
exclude = [".github/"]
readme = "README.md"

[workspace.dependencies]
miden-air = { version = "0.9", default-features = false }
Expand Down
2 changes: 1 addition & 1 deletion bin/faucet/miden-faucet.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ database_filepath = "store.sqlite3"
node_url = "http://localhost:57291"

# Data used to construct the faucet account of the faucet
asset_amount = 333
asset_amount_options = [100, 500, 1000]
token_symbol = "POL"
decimals = 8
max_supply = 1000000
8 changes: 4 additions & 4 deletions bin/faucet/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ pub struct FaucetConfig {
pub node_url: String,
/// Location to store database files
pub database_filepath: PathBuf,
/// Amount of asset that should be dispered on each faucet request
pub asset_amount: u64,
/// Possible options on the amount of asset that should be dispered on each faucet request
pub asset_amount_options: Vec<u64>,
/// Token symbol of the generated fungible asset
pub token_symbol: String,
/// Number of decimals of the generated fungible asset
Expand All @@ -36,8 +36,8 @@ impl FaucetConfig {
impl Display for FaucetConfig {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!(
"{{ endpoint: \"{}\", database_filepath: {:?}, asset_amount: {}, token_symbol: {}, decimals: {}, max_supply: {} }}",
self.endpoint, self.database_filepath, self.asset_amount, self.token_symbol, self.decimals, self.max_supply
"{{ endpoint: \"{}\", database_filepath: {:?}, asset_amount_options: {:?}, token_symbol: {}, decimals: {}, max_supply: {} }}",
self.endpoint, self.database_filepath, self.asset_amount_options, self.token_symbol, self.decimals, self.max_supply
))
}
}
25 changes: 20 additions & 5 deletions bin/faucet/src/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,21 @@ use crate::{errors::FaucetError, utils::FaucetState};
#[derive(Deserialize)]
struct FaucetRequest {
account_id: String,
is_private_note: bool,
asset_amount: u64,
}

#[derive(Serialize)]
struct FaucetMetadataReponse {
id: String,
asset_amount: u64,
asset_amount_options: Vec<u64>,
}

#[get("/get_metadata")]
pub async fn get_metadata(state: web::Data<FaucetState>) -> HttpResponse {
let response = FaucetMetadataReponse {
id: state.id.to_string(),
asset_amount: state.asset_amount,
asset_amount_options: state.asset_amount_options.clone(),
};

HttpResponse::Ok().json(response)
Expand All @@ -39,7 +41,15 @@ pub async fn get_tokens(
req: web::Json<FaucetRequest>,
state: web::Data<FaucetState>,
) -> Result<HttpResponse> {
info!("Received a request with account_id: {}", req.account_id);
info!(
"Received a request with account_id: {}, is_private_note: {}, asset_amount: {}",
req.account_id, req.is_private_note, req.asset_amount
);

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

let client = state.client.clone();

Expand All @@ -48,11 +58,15 @@ pub async fn get_tokens(
.map_err(|err| FaucetError::BadRequest(err.to_string()))?;

// Instantiate asset
let asset = FungibleAsset::new(state.id, state.asset_amount)
let asset = FungibleAsset::new(state.id, req.asset_amount)
.map_err(|err| FaucetError::InternalServerError(err.to_string()))?;

// Instantiate note type
let note_type = NoteType::OffChain;
let note_type = if req.is_private_note {
NoteType::OffChain
} else {
NoteType::Public
};

// Instantiate transaction template
let tx_template = TransactionTemplate::MintFungibleAsset(asset, target_account_id, note_type);
Expand Down Expand Up @@ -108,5 +122,6 @@ pub async fn get_tokens(
"note.mno".to_string(),
)],
})
.append_header(("Note-Id", note_id.to_string()))
.body(bytes))
}
50 changes: 46 additions & 4 deletions bin/faucet/src/static/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ body {
-webkit-font-smoothing: antialiased;
height: 100vh;
display: flex;
flex-direction: column;
padding-top: 100px;
justify-content: center;
align-items: center;
background-image: url(./background.png);
Expand Down Expand Up @@ -67,24 +69,64 @@ body {
margin-bottom: 20px;
}

#form-container {
display: flex;
flex-direction: row;
align-items: center;
}

#account-id {
padding: 10px;
border-radius: 10px;
border: 1px solid #ccc;
margin-bottom: 30px;
width: 300px;
margin-bottom: 10px;
width: 200px;
}

#asset-amount {
padding: 10px;
border-radius: 10px;
border: 1px solid #ccc;
margin-bottom: 10px;
width: 95px;
background-color: white;
margin-left: 5px;
text-align: center;
}

#faucetId {
color: white;
margin-top: 4px;
}

#button {
#visibility-buttons button{
color: white;
border-radius: 10px;
font-weight: bold;
padding: 10px 20px;
padding: 10px;
background-color: rgb(124, 58, 237);
width: 300px;
margin-top: 10px;
}

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

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

#client-commands {
background-color: black;
color: lightgray;
font-family: Consolas,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New, monospace;
padding: 10px;
border: 1px solid #ccc;
}
19 changes: 17 additions & 2 deletions bin/faucet/src/static/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,23 @@ <h1 id="title">Miden Faucet</h1>
<div id="center-container">
<h2 id="subtitle">Request test tokens</h2>
<span id="error-message"></span>
<input type="text" id="account-id" placeholder="Hex encoded Account id" required>
<button id="button"></button>
<div id="form-container">
<input type="text" id="account-id" placeholder="Hex encoded Account id" required>
<select id="asset-amount"></select>
</div>
<div id="visibility-buttons">
<button id="button-private">Send Private Note</button>
<br>
<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>
<p id="client-commands">
<span id="import-command" style="display: none;">miden-client input-notes import ./path/to/downloaded/note<br></span>
miden-client sync <br>
miden-client tx new consume-notes --account <span id="command-account-id"></span> <span id="note-id"></span>
</p>
</div>
<script src="./index.js"></script>
</body>
Expand Down
42 changes: 31 additions & 11 deletions bin/faucet/src/static/index.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,41 @@
document.addEventListener('DOMContentLoaded', function () {
const faucetIdElem = document.getElementById('faucetId');
const button = document.getElementById('button');
const privateButton = document.getElementById('button-private');
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 importCommand = document.getElementById('import-command');
const noteIdElem = document.getElementById('note-id');
const accountIdElem = document.getElementById('command-account-id');
const assetSelect = document.getElementById('asset-amount');

fetchMetadata();

button.addEventListener('click', handleButtonClick);
privateButton.addEventListener('click', () => {handleButtonClick(true)});
publicButton.addEventListener('click', () => {handleButtonClick(false)});

function fetchMetadata() {
fetch('http://localhost:8080/get_metadata')
.then(response => response.json())
.then(data => {
faucetIdElem.textContent = data.id;
button.textContent = `Send me ${data.asset_amount} tokens!`;
button.dataset.originalText = button.textContent;
for (const amount of data.asset_amount_options){
const option = document.createElement('option');
option.value = amount;
option.textContent = amount;
assetSelect.appendChild(option);
}
})
.catch(error => {
console.error('Error fetching metadata:', error);
faucetIdElem.textContent = 'Error loading Faucet ID.';
button.textContent = 'Error retrieving Faucet asset amount.';
errorMessage.textContent = 'Failed to load metadata. Please try again.';
errorMessage.style.display = 'block';
});
}

async function handleButtonClick() {
async function handleButtonClick(isPrivateNote) {
let accountId = accountIdInput.value.trim();
errorMessage.style.display = 'none';

Expand All @@ -33,26 +45,34 @@ document.addEventListener('DOMContentLoaded', function () {
return;
}

button.textContent = 'Loading...';
infoContainer.style.display = 'none';
importCommand.style.display = 'none';

try {
const response = await fetch('http://localhost:8080/get_tokens', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ account_id: accountId })
body: JSON.stringify({ account_id: accountId, is_private_note: isPrivateNote, asset_amount: parseInt(assetSelect.value)})
});

if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}

const blob = await response.blob();
downloadBlob(blob, 'note.mno');
if(isPrivateNote) {
importCommand.style.display = 'block';
downloadBlob(blob, 'note.mno');
}

const noteId = response.headers.get('Note-Id');
noteIdElem.textContent = noteId;
accountIdElem.textContent = accountId;
infoContainer.style.display = 'block';
} catch (error) {
console.error('Error:', error);
errorMessage.textContent = 'Failed to receive tokens. Please try again.';
errorMessage.style.display = 'block';
} finally {
button.textContent = button.dataset.originalText;
}
}

Expand Down
4 changes: 2 additions & 2 deletions bin/faucet/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pub type FaucetClient = Client<TonicRpcClient, RpoRandomCoin, SqliteStore>;
#[derive(Clone)]
pub struct FaucetState {
pub id: AccountId,
pub asset_amount: u64,
pub asset_amount_options: Vec<u64>,
pub client: Arc<Mutex<FaucetClient>>,
}

Expand All @@ -45,7 +45,7 @@ pub async fn build_faucet_state(config: FaucetConfig) -> Result<FaucetState, Fau

Ok(FaucetState {
id: faucet_account.id(),
asset_amount: config.asset_amount,
asset_amount_options: config.asset_amount_options,
client: Arc::new(Mutex::new(client)),
})
}
Expand Down
Loading