Blockchain applications / D60
View on GitHubMonero Blockchain
Overview
This example deploys a Monero blockchain across an emulated Internet using real Monero client software. Users can configure different types of nodes, including seed nodes, full nodes, pruned nodes, and light wallets, as well as customize mining behavior and peer connections. The example supports both simplified deployment and detailed configuration, allowing users to construct Monero networks for different experimental needs. By inspecting peer connections, block production, and wallet transactions, users can explore how Monero nodes communicate and maintain the blockchain. You can further extend the scenario by changing mining difficulty, adjusting node connectivity, or deploying modified Monero clients to study blockchain performance, peer-to-peer networking, and security vulnerabilities.
D60_monero: Monero Blockchain Service Examples
This folder provides three Seed-Emulator examples for the Monero blockchain service:
- ver1 (detailed configuration): Shows the full API surface with fine-grained options.
- ver2 (default configuration): Minimal configuration to spin up a ready-to-use blockchain.
- ver3 (custom binaries): Demonstrates how to run with your own Monero executables.
ver1: Detailed configuration (monero_ver1_detailed_config.py)
- Create the blockchain with
MoneroService.createBlockchain("base-monero", net_type=...) - Configure blockchain-level options:
setSeedConnectionMode(): seed connectivity policy (e.g., EXCLUSIVE)setFixedDifficulty(): fixed mining difficulty (reduce for demos/teaching)setDefaultWalletTemplate(): default wallet template
- Define nodes and roles:
createNode()withsetSeedRole()/setClientRole()- Control mining, threads, and triggers (e.g., AFTER_SEED_REACHABLE)
- Enable Wallet RPC for light/full nodes as needed
Topology layout:
- AS150-154: two hosts per AS;
host_0is seed,host_1is client (only AS150 client mines) - AS160-161: one client per AS
- AS162-163: one light wallet per AS
- AS164: one pruned node
Run:
python3 monero_ver1_detailed_config.py [amd|arm]
cd output && docker compose build && docker compose up
ver2: Default configuration (monero_ver2_default_config.py)
- Create a blockchain with minimal configuration:
MoneroService.createBlockchain("base-monero") - Use convenience node APIs:
createSeedNode()/createClientNode()/createLightWallet()/createPrunedNode()
- Demonstrate a mining trigger that starts only after seeds are reachable
Run:
python3 monero_ver2_default_config.py [amd|arm]
cd output && docker compose build && docker compose up
ver3: Custom binaries (monero_ver3_custom_binary.py)
When you modify Monero (research/experiments), place your binaries under custom/:
- Required files:
custom/monerodcustom/monero-wallet-clicustom/monero-wallet-rpc
- The script validates these files before proceeding
- Configure each node to use custom binaries:
server.options.binary_source = MoneroBinarySource.CUSTOM server.options.custom_binary_imports = { "/usr/local/bin/monerod": "<path>/custom/monerod", "/usr/local/bin/monero-wallet-cli": "<path>/custom/monero-wallet-cli", "/usr/local/bin/monero-wallet-rpc": "<path>/custom/monero-wallet-rpc", }
Run:
python3 monero_ver3_custom_binary.py [amd|arm]
cd output && docker compose build && docker compose up
Interacting and verifying
- Seed and full nodes expose P2P/RPC/ZMQ ports (determined by blockchain defaults and auto assignment)
- Light wallet nodes enable Wallet RPC by default; check
enableWalletRpc()in scripts for exposure settings
Enter a container:
docker ps | grep monero
docker exec -it <container_id_or_name> bash
FAQ
- Port conflicts: Ports are auto-assigned across nodes; if you override ports manually, ensure no conflicts (P2P/RPC/ZMQ/Wallet-RPC).
- Slow mining: Reduce difficulty for demos (e.g.,
setFixedDifficulty(2000)). - Missing custom binaries: Ensure all three files under
custom/exist and are executable.
Testing
The following commands help you validate the deployment. By default:
- Wallet RPC (digest auth) listens on 18088 inside containers (username/password are typically
seed:seedin examples). - Daemon RPC (no auth) listens on 28081 inside containers.
Replace <container> with an actual container name (e.g., as150h-Monero-Client-150-10.150.0.72).
Wallet RPC (port 18088, digest auth)
# Get wallet height
docker exec <container> curl -s --digest -u <user>:<pass> \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":"height","method":"get_height"}' \
http://127.0.0.1:18088/json_rpc
# Get wallet balance for account 0
docker exec <container> curl -s --digest -u <user>:<pass> \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":"balance","method":"get_balance","params":{"account_index":0}}' \
http://127.0.0.1:18088/json_rpc
# Get primary address (account 0)
docker exec <container> curl -s --digest -u <user>:<pass> \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":"addr","method":"get_address","params":{"account_index":0}}' \
http://127.0.0.1:18088/json_rpc
# Refresh wallet (rescan)
docker exec <container> curl -s --digest -u <user>:<pass> \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":"refresh","method":"refresh"}' \
http://127.0.0.1:18088/json_rpc
# List transfers (in/out/pending/failed)
docker exec <container> curl -s --digest -u <user>:<pass> \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":"txs","method":"get_transfers","params":{"in":true,"out":true,"pending":true,"failed":true}}' \
http://127.0.0.1:18088/json_rpc
Example transfer request (may return “not enough unlocked money” until coinbase rewards unlock; retry after sufficient blocks):
docker exec <container> curl -s --digest -u <user>:<pass> \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":"tx","method":"transfer","params":{"destinations":[{"address":"<recipient-address>","amount":1000000000000}],"account_index":0,"ring_size":12,"priority":0,"get_tx_key":true,"do_not_relay":false}}' \
http://127.0.0.1:18088/json_rpc
Daemon RPC (port 28081, no auth)
# Get block count
docker exec <container> curl -s \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":"count","method":"get_block_count"}' \
http://127.0.0.1:28081/json_rpc
# Get node info
docker exec <container> curl -s \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":"info","method":"get_info"}' \
http://127.0.0.1:28081/json_rpc
# Get peer connections
docker exec <container> curl -s \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":"conn","method":"get_connections"}' \
http://127.0.0.1:28081/json_rpc
# Get block hash at height 123
docker exec <container> curl -s \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":"hash","method":"get_block_hash","params":[123]}' \
http://127.0.0.1:28081/json_rpc
Logs and troubleshooting
# Recent logs of a container
docker logs <container_name> | tail -n 120
# Check daemon logs for ERROR/WARNING
docker exec <container_name> grep -i "error" /var/log/monero/<log_dir>/monerod.log | tail
docker exec <container_name> grep -i "warning" /var/log/monero/<log_dir>/monerod.log | tail
# Inspect wallet CLI / RPC logs
docker exec <container_name> tail -n 40 /var/log/monero/<log_dir>/wallet-cli.log
docker exec <container_name> tail -n 40 /var/log/monero/<log_dir>/wallet-rpc.log
# Ensure wallet-rpc process exists (light/RPC nodes)
docker exec <container_name> ps aux | grep monero-wallet-rpc
Notes on ports
- Full/pruned nodes expose P2P/RPC/ZMQ on incrementing testnet-style ports (P2P ≈ 28080+N, RPC ≈ 28081+N, ZMQ ≈ 28082+N).
- Wallet RPC defaults to
0.0.0.0:18088inside containers with digest auth (examples useseed:seed). - Light nodes run only
monero-wallet-rpc(port 18088); they do not startmonerod.
Source: SEED Emulator · D60 README