monero-balance/balance.py
Kumi 87705d5f51
feat: Implement Monero wallet Prometheus exporter
Introduced a Prometheus exporter for Monero wallets, capable of reporting wallet balance metrics. The implementation includes:

- A Python script `balance.py` for fetching and exposing the wallet balance both as a Prometheus metric and a JSON endpoint.
- Dependencies required for running the exporter specified in `requirements.txt`.
- A basic `.gitignore` to prevent committing unnecessary files.
- A `README.md` file detailing usage and setup instructions.

This exporter simplifies monitoring Monero wallet balances by integrating with Prometheus, providing users with real-time insights into their wallet's status. It automates the process of starting a `monero-wallet-rpc` process if needed, making it more convenient for users to set up in various environments. By running this in a Prometheus monitored environment, users can track wallet balances over time, set up alerts, and integrate with Grafana for visualization purposes.

This feature enhances the operability and observability of Monero wallets within a Prometheus ecosystem, catering to the needs of Monero wallet owners who require continuous monitoring and reporting of their crypto assets.
2024-05-25 22:21:05 +02:00

94 lines
2.7 KiB
Python

import subprocess
import time
import os
from flask import Flask, jsonify, Response
from prometheus_client import Gauge, generate_latest
from monero.wallet import Wallet
from monero.backends.jsonrpc import JSONRPCWallet
app = Flask(__name__)
wallet_path = os.environ.get("MONERO_WALLET_PATH", "wallet/readonly")
rpc_host = os.environ.get("MONERO_RPC_HOST", "localhost")
rpc_port = os.environ.get("MONERO_RPC_PORT", 18083)
daemon_address = os.environ.get(
"MONERO_DAEMON_ADDRESS", "xmr-node.cakewallet.com:18081"
)
wallet_password = os.environ.get("MONERO_WALLET_PASSWORD", "")
listen_port = os.environ.get("PORT", 5000)
listen_host = os.environ.get("HOST", "localhost")
skip_wallet_rpc = os.environ.get("MONERO_SKIP_RPC", False)
def log(msg):
print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] {msg}")
# Function to start the Monero wallet RPC server
def start_rpc_server():
rpc_command = [
"monero-wallet-rpc",
"--wallet-file",
wallet_path,
"--rpc-bind-port",
str(rpc_port),
"--daemon-address",
daemon_address,
"--password",
wallet_password,
"--disable-rpc-login",
]
return subprocess.Popen(rpc_command)
# Function to check if the RPC server is up
def is_rpc_server_up(host, port):
try:
wallet = Wallet(JSONRPCWallet(host=host, port=port))
wallet.height() # Try to get the current blockchain height, just to check if the RPC server is up
return True
except ConnectionError:
return False
except Exception as e:
print(f"Unexpected error: {e}")
return False
if not skip_wallet_rpc:
# Start the wallet RPC server using the public node
wallet_rpc_process = start_rpc_server()
# Wait for the wallet RPC server to be up
while not is_rpc_server_up("localhost", rpc_port):
log("Waiting for the wallet RPC server to start...")
time.sleep(2)
log("Wallet RPC server is up!")
# Connect to the Monero wallet RPC server
wallet = Wallet(JSONRPCWallet(host=rpc_host, port=rpc_port))
# Prometheus gauge for the balance
balance_gauge = Gauge("monero_wallet_balance", "Current balance of the Monero wallet")
@app.route("/balance")
def get_balance():
balance = wallet.balance()
balance_gauge.set(balance)
return jsonify({"balance": balance})
@app.route("/metrics")
def metrics():
balance = wallet.balance()
balance_gauge.set(balance)
return Response(generate_latest(), mimetype="text/plain")
if __name__ == "__main__":
try:
app.run(host=listen_host, port=listen_port)
finally:
# Ensure the RPC server is terminated when the application exits
wallet_rpc_process.terminate()