The leo devnode command runs a single-node local development server with instant block creation, ideal for rapid testing and iteration.
leo devnode <SUBCOMMAND> [OPTIONS]
Subcommands
Start the devnode server.
leo devnode start [OPTIONS]
advance
Advance the ledger by a specified number of blocks.
leo devnode advance [OPTIONS]
Start Options
Private key to use for the devnode. Defaults to test private key.Default test key: APrivateKey1zkp8CZNn3yeCseEtxuVPbDCwSyhGW6yZKUYKfgXmcpoGPWH is for development only.
Network type: mainnet, testnet, or canary.
Endpoint override (for configuration purposes).
Advance Options
Number of blocks to advance.
--endpoint
string
default:"http://localhost:3030"
Devnode endpoint URL.
Examples
Start Devnode
Output:
π Starting Devnode...
β
Devnode running at http://localhost:3030
Network: testnet
Address: aleo1...
Press Ctrl+C to stop.
The devnode:
- Listens on
http://localhost:3030
- Creates blocks instantly on transaction receipt
- Maintains in-memory ledger state
- Provides REST API compatible with snarkOS
Start with Custom Private Key
leo devnode start --private-key APrivateKey1zkp...
Advance Blocks
In a separate terminal:
leo devnode advance --blocks 10
Output:
β© Advancing 10 blocks...
β
Advanced to block 10
This creates 10 empty blocks, useful for:
- Testing time-dependent logic
- Advancing past block height thresholds
- Simulating network progression
Advance Custom Number of Blocks
leo devnode advance --blocks 100
Using the Devnode
Once running, interact with it like any Aleo node:
Deploy Programs
leo deploy \
--broadcast \
--devnet \
--endpoint http://localhost:3030 \
--private-key APrivateKey1zkp8CZNn3yeCseEtxuVPbDCwSyhGW6yZKUYKfgXmcpoGPWH
The devnode instantly creates a block with the deployment.
Execute Transactions
leo execute main 1u32 2u32 \
--broadcast \
--devnet \
--endpoint http://localhost:3030 \
--private-key APrivateKey1zkp8CZNn3yeCseEtxuVPbDCwSyhGW6yZKUYKfgXmcpoGPWH
The devnode instantly creates a block with the execution.
Query State
leo query block latest \
--endpoint http://localhost:3030 \
--network testnet
REST API
The devnode provides a REST API compatible with snarkOS:
Get Latest Block
curl http://localhost:3030/testnet/latest/block
Get Latest Height
curl http://localhost:3030/testnet/latest/height
Response:
Get Block by Height
curl http://localhost:3030/testnet/block/10
Get State Root
curl http://localhost:3030/testnet/latest/stateRoot
Get Program
curl http://localhost:3030/testnet/program/hello_world.aleo
Broadcast Transaction
curl -X POST http://localhost:3030/testnet/transaction/broadcast \
-H "Content-Type: application/json" \
-d @transaction.json
Devnode vs Devnet
| Feature | Devnode | Devnet |
|---|
| Nodes | Single node | Multiple validators + clients |
| Block Creation | Instant | Consensus (slower) |
| Consensus | None | Full BFT |
| Startup Time | Fast | Slower |
| Resource Usage | Low | Higher |
| Realism | Less realistic | More realistic |
| Use Case | Rapid iteration | Integration testing |
Use leo devnode for rapid development and iteration. Use leo devnet for more realistic testing.
Block Creation Behavior
The devnode creates blocks instantly when:
- A transaction is broadcast
leo devnode advance is called
Transaction β Devnode β Instant Block
No waiting for consensus or block time.
State Persistence
The devnode maintains state only in memory:
- State is lost when the devnode stops
- No persistence to disk
- Fast startup with clean state
Default Configuration
The devnode uses:
Endpoint: http://localhost:3030
Network: testnet
Private Key: APrivateKey1zkp8CZNn3yeCseEtxuVPbDCwSyhGW6yZKUYKfgXmcpoGPWH
Address: aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px
Never use the default devnode private key on mainnet or testnet.
Development Workflow
Typical workflow with devnode:
# Terminal 1: Start devnode
leo devnode start
# Terminal 2: Develop and test
cd my_project
leo build
leo deploy --broadcast --devnet --endpoint http://localhost:3030
leo execute main 1u32 2u32 --broadcast --devnet --endpoint http://localhost:3030
# Make changes, rebuild, redeploy
vim src/main.leo
leo build
leo deploy --broadcast --devnet --endpoint http://localhost:3030
# Advance time if needed
leo devnode advance --blocks 100
Environment Variables
Configure devnode via .env:
PRIVATE_KEY=APrivateKey1zkp...
NETWORK=testnet
ENDPOINT=http://localhost:3030
DEVNET=true
Then simply:
leo devnode start
leo deploy --broadcast
leo execute main 1u32 2u32 --broadcast
Logging
Devnode logs to stdout:
[2026-03-04 15:30:00] Starting Devnode on port 3030
[2026-03-04 15:30:00] Genesis block created
[2026-03-04 15:30:01] REST API listening on http://localhost:3030
[2026-03-04 15:30:05] Received transaction at1abc...
[2026-03-04 15:30:05] Created block 1
For verbose logging:
RUST_LOG=debug leo devnode start
Stopping the Devnode
Press Ctrl+C to stop:
^C
π Shutting down Devnode...
β
Devnode stopped.
Troubleshooting
Port Already in Use
Failed to bind to address: Address already in use
Solutions:
- Stop existing devnode
- Kill process using port 3030:
lsof -ti:3030 | xargs kill -9
Transaction Not Confirmed
If a transaction doesnβt confirm:
- Check devnode is running
- Verify endpoint is
http://localhost:3030
- Check devnode logs for errors
- Ensure transaction is valid
State Mismatch
If state is inconsistent:
- Restart devnode (clears state)
- Redeploy programs
- Re-execute transactions
Advanced Usage
Custom REST Endpoints
The devnode supports standard Aleo REST endpoints:
# Get account balance
curl http://localhost:3030/testnet/program/credits.aleo/mapping/account/aleo1.../balance
# Get transaction
curl http://localhost:3030/testnet/transaction/at1...
# Get program mappings
curl http://localhost:3030/testnet/program/my_program.aleo/mappings
Programmatic Interaction
Interact programmatically via HTTP:
const fetch = require('node-fetch');
// Get latest height
const height = await fetch('http://localhost:3030/testnet/latest/height')
.then(r => r.text());
console.log('Current height:', height);
// Advance blocks
await fetch('http://localhost:3030/testnet/devnode/advance', {
method: 'POST',
body: JSON.stringify({ blocks: 10 }),
headers: { 'Content-Type': 'application/json' }
});
Testing Strategies
Unit Testing
Use leo test for unit tests:
Integration Testing
Use devnode for integration tests:
# Start devnode
leo devnode start &
DEVNODE_PID=$!
# Run integration tests
./run-integration-tests.sh
# Stop devnode
kill $DEVNODE_PID
End-to-End Testing
Use devnet for E2E tests:
leo devnet --num-validators 4
# Run E2E tests
Next Steps