Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Miden Faucet

The Miden Faucet is a token distribution service that provides developers and users with test tokens for the Miden blockchain without cost.

It implements proof-of-work challenges to ensure fair distribution of tokens, balancing accessibility for legitimate users while preventing malicious actors from draining the token supply.

It contains a library and a CLI binary.

Miden faucet library

The Miden faucet library is a Rust library that can be integrated into projects, allowing developers to run the core faucet functionality.

The library provides a Faucet struct that can be run in a thread and receive minting requests through a channel.

Miden faucet CLI

The Miden faucet also includes a command-line interface (CLI) that allows to run the faucet behind a REST API and serves a frontend to interact with it.

More information about the CLI can be found in the CLI reference.

More information about the REST API can be found in the API reference.

Installation

We provide Debian packages for official releases for the Faucet software. Alternatively, it also can be installed from source on most systems using the Rust package manager cargo.

Debian package

Official Debian packages are available under our releases page. Both amd64 and arm64 packages are available.

Note that the packages include a systemd service which is disabled by default.

To install, download the desired releases .deb package and checksum files. Install using

sudo dpkg -i $package_name.deb

You can (and should) verify the checksum prior to installation using a SHA256 utility. This differs from platform to platform, but on most linux distros:

sha256sum --check $checksum_file.deb.checksum

can be used so long as the checksum file and the package file are in the same folder.

Install using cargo

Install Rust version 1.89 or greater using the official Rust installation instructions.

Depending on the platform, you may need to install additional libraries. For example, on Ubuntu 22.04 the following command ensures that all required libraries are installed.

sudo apt install llvm clang bindgen pkg-config libssl-dev libsqlite3-dev

Install the latest faucet binary:

cargo install miden-faucet --locked

This will install the latest official version of the faucet. You can install a specific version x.y.z using

cargo install miden-faucet --locked --version x.y.z

You can also use cargo to compile the node from the source code if for some reason you need a specific git revision. Note that since these aren't official releases we cannot provide much support for any issues you run into, so consider this for advanced use only. The incantation is a little different as you'll be targeting our repo instead:

# Install from a specific branch
cargo install --locked --git https://github.com/0xMiden/miden-faucet miden-faucet --branch <branch>

# Install a specific tag
cargo install --locked --git https://github.com/0xMiden/miden-faucet miden-faucet --tag <tag>

# Install a specific git revision
cargo install --locked --git https://github.com/0xMiden/miden-faucet miden-faucet --rev <git-sha>

More information on the various cargo install options can be found here.

Updating

Updating the faucet to a new version is as simply as re-running the install process.

If the node version is updated, you may encounter an error like this:

Error: faucet failed

Caused by:
    0: transaction executor error
    1: failed to execute transaction kernel program:
         × advice provider error at clock cycle 152
         ╰─▶   × value for key 0x85dee386c7e023b13a5cf16def1c421c57f76c9135a665bdd5b547d6a54d1b15 not present in the advice map

This is a common error that occurs when the Miden network undergoes updates during its active development phase. The error happens because the local client store contains data that's incompatible with the updated node version.

To resolve this issue, delete the faucet_client_store.sqlite3 file and restart the faucet. This will force the client to re-sync with the updated network state.

Quick Start

Get the Miden Faucet running in minutes.

Prerequisites

  • Miden Faucet installed (see Installation)
  • Access to a Miden node (testnet, devnet, or local)

Step 1: Create a Faucet Account

First, we need to create a faucet account that will hold the tokens to be distributed. This command generates a new account with the specified token configuration and saves the account data to a local file (faucet.mac). The account is not yet deployed to the network - that will happen when the faucet is running and the first transaction is sent to the node.

miden-faucet create-faucet-account \
  --output-path ./faucet.mac \
  --token-symbol MIDEN \
  --decimals 6 \
  --max-supply 100000000000000000

Step 2: Start the Faucet Server

Next, start the faucet server by specifying the endpoint it should listen on, the Miden node it will connect to, and the account file to use for distributing tokens. The server will handle incoming token requests and manage the minting process.

miden-faucet start \
  --endpoint http://localhost:8080 \
  --node-url https://rpc.testnet.miden.io \
  --account ./faucet.mac

Step 3: Access the Web Interface

The faucet provides a web interface for easy token requests. Open http://localhost:8080 in your browser to access it.

Step 4: Request Test Tokens

Once the faucet is running, you can request test tokens through either the web interface or the REST API.

Via Web Interface

  1. Enter your Miden account ID or account bech32 address.
  2. Select token amount
  3. Choose note type (private or public)
  4. Submit request

Via API

You can also programmatically interact with the REST API to mint tokens. Check out the complete working examples below. Make sure the faucet is running at http://localhost:8080 before using them.

Common Configurations

Localhost

If you have a Miden Node running locally, you can run the faucet against that node.

miden-faucet start \
  --endpoint http://localhost:8080 \
  --node-url http://localhost:57291 \
  --account ./faucet.mac \
  --network localhost

Development

Connect to the node deployed in Miden Devnet.

miden-faucet start \
  --endpoint http://localhost:8080 \
  --node-url https://rpc.devnet.miden.io \
  --account ./faucet.mac \
  --network devnet

Testnet

Connect to the node deployed in Miden Testnet.

miden-faucet start \
  --endpoint http://localhost:8080 \
  --node-url https://rpc.testnet.miden.io \
  --account ./faucet.mac \
  --explorer-url https://testnet.midenscan.com \
  --network testnet

CLI configuration and usage

This guide shows the available commands and their configuration options to run with the Miden Faucet CLI.

Available Commands

CommandDescription
startStart the faucet server
create-faucet-accountCreate a new faucet account
create-api-keysGenerate API keys for authentication
helpShow help information

Configuration Methods

The Miden Faucet can be configured using:

  1. Command-line arguments
  2. Environment variables

Command-Line Arguments

Basic Configuration

miden-faucet start \
  --endpoint <URL> \
  --node-url <URL> \
  --account <PATH> \
  --network <NETWORK>

All Available Options

OptionDescriptionDefaultRequired
--endpointFaucet endpoint-Yes
--node-urlMiden node RPC endpoint-Yes
--accountPath to faucet account file-Yes
--networkNetwork configurationlocalhostNo
--timeoutRPC request timeout5sNo
--max-claimable-amountMax claimable base units per request1000000000No
--storeSQLite store pathfaucet_client_store.sqlite3No
--explorer-urlMidenscan URL-No

Proof of Work Configuration

OptionDescriptionDefaultRequired
--pow-secretSecret to sign PoW challenges. This should NOT be shared""No
--pow-baselineBase PoW difficulty (0-32). It's the starting difficulty when no requests are pending12No
--pow-challenge-lifetimeChallenge validity duration, i.e. how long challenges remain valid. This affects the rate limiting, since it works by rejecting new submissions while the previous submitted challenge is still valid30sNo
--pow-cleanup-intervalCache cleanup interval, i.e. how often expired challenges are removed2sNo
--pow-growth-rateDifficulty growth rate, i.e. how quickly difficulty increases with load. When set to 1, the difficulty will roughly double when the number of requests doubles.1No

Advanced Configuration

OptionDescriptionDefaultRequired
--remote-tx-prover-urlRemote transaction prover-No
--api-keysComma-separated API keys-No
--enable-otelEnable OpenTelemetryfalseNo
--batch-sizeMaximum number of P2ID notes to create per transaction32No

Environment Variables

All configuration options can be set using environment variables:

# Basic configuration
export MIDEN_FAUCET_ENDPOINT=http://localhost:8080
export MIDEN_FAUCET_NODE_URL=https://rpc.testnet.miden.io
export MIDEN_FAUCET_ACCOUNT_PATH=./faucet.mac
export MIDEN_FAUCET_NETWORK=testnet
export MIDEN_FAUCET_EXPLORER_URL=https://testnet.midenscan.com

# Proof of Work
export MIDEN_FAUCET_POW_SECRET=your-secret-here
export MIDEN_FAUCET_POW_BASELINE=12
export MIDEN_FAUCET_POW_CHALLENGE_LIFETIME=30s
export MIDEN_FAUCET_POW_GROWTH_RATE=1

# Advanced
export MIDEN_FAUCET_MAX_CLAIMABLE_AMOUNT=1000
export MIDEN_FAUCET_TIMEOUT=10s
export MIDEN_FAUCET_ENABLE_OTEL=true
export MIDEN_FAUCET_API_KEYS=key1,key2,key3
export MIDEN_FAUCET_BATCH_SIZE=32

Network Configurations

Predefined Networks

Localhost

--network localhost
  • Explorer URL: Not available
  • Address Display: mlcl
  • Use Case: Local development

Devnet

--network devnet
  • Explorer URL: Not available
  • Address Display: mdev
  • Use Case: Development testing

Testnet

--network testnet
  • Explorer URL: https://testnet.midenscan.com/
  • Address Display: mtst
  • Use Case: Integration testing

Custom Network

--network custom
  • Explorer URL: Not available
  • Address Display: mcst
  • Use Case: Run your custom network

API Key Configuration

Generate API Keys

miden-faucet create-api-keys 5

This generates 5 API keys that can be used for authentication. They are printed to stdout.

API Key Benefits

  • Rate Limiting: Separate rate limits per API key
  • Access Control: Distribute keys to different users/teams

Store Configuration

SQLite Store

This is the store that is used by the Miden Client to store all the faucet account state. Default is SQLite:

--store ./faucet_client_store.sqlite3.sqlite3

Monitoring Configuration

OpenTelemetry

Enable OpenTelemetry for production monitoring:

--enable-otel

Configuration Example

miden-faucet start \
  --endpoint http://localhost:8080 \
  --node-url http://localhost:57291 \
  --account ./faucet.mac \
  --network localhost \
  --pow-baseline 8 \
  --pow-challenge-lifetime 1s

For detailed options, run miden-faucet [COMMAND] --help.

Usage

The faucet can be accessed by the HTTP API interactively through the frontend or programmatically by building the requests manually.

Programmatic API Usage

The faucet provides a REST API. The typical flow to request tokens involves:

  1. Requesting a Proof-of-Work challenge from /pow
const baseUrl = 'http://localhost:8080';
const accountId = 'mlcl1qq8mcy8pdvl0cgqfkjzf8efjjsnlzf7q';

const powUrl = new URL('/pow', baseUrl);
powUrl.searchParams.set('account_id', accountId);
const powResp = await fetch(powUrl);
if (!powResp.ok) throw new Error(`PoW error: ${powResp.status} ${await powResp.text()}`);
const powJson: any = await powResp.json();
const challenge: string = powJson.challenge;
const target: bigint = BigInt(powJson.target);
  1. Solving the computational challenge
// Dependencies: npm i @noble/hashes
import { sha_256 } from '@noble/hashes/sha2.js';
import { utf8ToBytes } from '@noble/hashes/utils.js';

let nonce = 0;
while (true) {
    nonce = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);

    try {
        // Compute hash using SHA-256 with the challenge and nonce
        let hash = sha_256.create();
        hash.update(utf8ToBytes(challenge)); // Use the hex-encoded challenge string directly

        // Convert nonce to 8-byte big-endian format to match backend
        const nonceBytes = new ArrayBuffer(8);
        const nonceView = new DataView(nonceBytes);
        nonceView.setBigUint64(0, BigInt(nonce), false); // false = big-endian
        const nonceByteArray = new Uint8Array(nonceBytes);
        hash.update(nonceByteArray);

        // Take the first 8 bytes of the hash and parse them as u64 in big-endian
        const hashBytes: Uint8Array = hash.digest().slice(0, 8);
        let digest = BigInt('0x' + Array.from(hashBytes).map(b => b.toString(16).padStart(2, '0')).join(''));

        // Check if the hash is less than the target
        if (digest < target) {
            console.log('Found nonce:', nonce);
            return nonce;
        }
    } catch (error: any) {
        throw new Error('Failed to compute hash: ' + error.message);
    }
}
  1. Requesting tokens along with your solved challenge to /get_tokens
const params = new URLSearchParams({
    account_id: accountId,
    is_private_note: 'true',
    asset_amount: '100',
    challenge: challenge,
    nonce: nonce.toString()
});

const response = await fetch(`${baseUrl}/get_tokens?${params}`);
if (!response.ok) throw new Error(`Get tokens error: ${response.status} ${await response.text()}`);

const text = await response.text();
const json = JSON.parse(text);
const noteId = json.note_id;
const txId = json.tx_id;
const explorerUrl = json.explorer_url;
  1. Requesting note to download generated notes

You must complete this step to retrieve private notes. For public notes, normal client sync is sufficient.

const response = await fetch(`${baseUrl}/get_note?note_id=${noteId}`);
if (!response.ok) throw new Error(`Get note error: ${response.status} ${await response.text()}`);

const text = await response.text();
const json = JSON.parse(text);

// Decode note with base64
const noteData = Buffer.from(json.data_base64, 'base64');

fs.writeFileSync('note.mno', noteData);

Examples

Check out the complete working examples below. Make sure the faucet API is running at http://localhost:8080 before using them.

REST API Reference

REST API for programmatic access to the faucet service.

The Miden Faucet API follows a two-step process to request tokens:

  1. Request a Challenge (GET /pow): Obtain a proof-of-work challenge that must be solved computationally
  2. Request tokens (GET /get_tokens): Submit the solved challenge along with your token request

For detailed information about the token request flow, see the Architecture section.

Responses

HTTP Status Codes

CodeDescription
200Success
400Bad Request
429Rate limited
500Internal Server Error

Endpoints

Proof of Work Challenge

Endpoint: GET /pow

  • Purpose: Request a proof-of-work challenge

  • Query Parameters:

    • account_id (string, required): The account ID requesting the challenge
    • api_key (string, optional): API key for authentication
  • Response: JSON object containing:

    • challenge (string): The encoded challenge string in hexadecimal format
    • target (number): The target value for the proof-of-work challenge. A solution is valid if the hash H(challenge, nonce) is less than this target. As the hashing function we use SHA-256
    • timestamp (number): The timestamp when the challenge was created (seconds since UNIX epoch)

Get Tokens

Endpoint: GET /get_tokens

  • Purpose: Request tokens

  • Query Parameters:

    • account_id (string, required): The account ID requesting tokens
    • is_private_note (boolean, required): Whether to create a private note
    • asset_amount (number, required): Requested asset amount (in base units)
    • challenge (string, required): The encoded challenge from the /pow endpoint
    • nonce (number, required): The nonce used to solve the challenge
    • api_key (string, optional): API key for authentication
  • Response: JSON object containing:

    • tx_id (string): ID of the created transaction
    • note_id (string): ID of the created note

Get Metadata

Endpoint: GET /metadata

  • Purpose: Request the faucet metadata to show on the frontend

  • Response: JSON object containing:

    • id (string): ID of the faucet account
    • issuance (number): amount of tokens issued by the faucet (in base units)
    • max_supply (number): maximum available supply of the faucet (in base units)
    • decimals (number): number of decimals of the token minted by the faucet. It is needed to convert base units into token amounts.
    • explorer_url (string): URL to view the transaction in the explorer. Only present if available for the current network.

Get Note

Endpoint: GET /get_note

  • Purpose: Request a specific note by its ID

  • Query Parameters:

    • note_id (string, required): The ID of the note to retrieve
  • Response: JSON object containing:

    • note_id (string): The ID of the requested note
    • data_base64 (string): The note data encoded in base64 format. This data should be decoded and saved as a file with .mno extension and application/octet-stream media type

Rate Limiting

When a challenge is submitted for an account, that same account cannot submit a new challenge while the previous one is still valid. This effectively rate limits accounts minting requests. Though, the same account can use API keys to submit multiple challenges simultaneously:

  • Without API Key: Global rate limiting based on account ID
  • With API Key: Separate rate limits per API key

API Keys

Some endpoints support API key authentication for enhanced PoW.

The PoW difficulty of the faucet scales with the number of active requests. When using an API key, the difficulty will only depend on the number of active requests under that API key.

To request a challenge using an API key:

GET /pow?account_id=0xca8203e8e58cf72049b061afca78ce&api_key=miden_faucet_wONsvRXLZ9FgQG+nlkaq9f2X53cLswe4HSzEIUjFIkQ=

Architecture Overview

This document provides a high-level overview of the Miden faucet architecture.

System Architecture

The high-level structure of the project looks like follows:

Core Components

1. Web Frontend

  • Technology: Vanilla JavaScript, HTML5, CSS3
  • Purpose: User interface for token requests
  • Features:
    • Token amount selection
    • Address input
    • Public/private note selection
    • PoW challenge solving
    • Request status display

2. REST API Server

  • Technology: Axum HTTP framework
  • Purpose: HTTP API endpoints for programmatic access
  • Features:
    • Input parsing and validation
    • Handle account metadata

3. Faucet lib

  • Technology: Rust library (crates/faucet)
  • Purpose: Faucet core logic
  • Features:
    • Account handling
    • Request batching
    • Notes creation
    • Token issuance tracking

4. PoW Rate Limiter

  • Technology: Rust library (crates/pow)
  • Purpose: Proof of Work rate limiter
  • Features:
    • PoW challenges issuing, tracking and validation
    • Rate limiting

5. Miden Client

  • Purpose: Connection with the Miden Node
  • Features:
    • Transaction creation, execution, and submission

Token request Flow

The basic HTTP requests for minting tokens involves /pow and /get_tokens. These are the entry points for the minting process. This is how the whole flow looks like:

Detailed Flow

  • Request Initiation

    • User submits token request (web or API)
    • System generates proof-of-work challenge
    • Challenge stored in cache with expiration
  • Challenge Resolution

    • User solves computational challenge
    • Solution validated against challenge
    • Rate limiting enforced
  • Token Distribution

    • Validated request processed
    • Token transaction created
    • Note generated (public or private)
    • Transaction is created, executed, and stored in the local database
    • Transaction is proven and submitted to Miden Node
  • Response

    • Transaction ID and Note ID returned

Why do we need a backend?

Could we not simply use the Miden Web SDK without a backend? The reason is security: if we were to use the Web SDK without a backend, the account's private key would need to be shared with the frontend. This would expose the private key to anyone, allowing them to potentially mint unlimited tokens and bypass all the rate limiting and throttling mechanisms.

The solution is to have a single backend running that receives and validates the minting requests and stores the account's private key needed to mint the tokens.

Security Features

The faucet implements several security measures to prevent abuse:

  • Proof of Work requests:

    • Users must complete a computational challenge before their request is processed.
    • The challenge difficulty increases with the load. The load is measured by the amount of challenges that were submitted but still haven't expired.
    • Each challenge is signed with a secret only known by the server. It should NOT be shared.
    • Rate limiting: if an account submitted a challenge, it can't submit another one until the previous one is expired. The challenge lifetime duration is fixed and set when running the faucet.
    • API Keys: the faucet is initialized with a set of API Keys that can be distributed to developers. The difficulty of the challenges requested using the API Key will increase only with the load of that key, it won't be influenced by the overall load of the faucet.
  • Requests batching:

    • Maximum batch size: 100 requests
    • Requests are processed in batches to optimize performance
    • Failed requests within a batch are handled individually