Skip to content

Commit 5417968

Browse files
committed
docs: clean up bluffs, sync with code, and upgrade dependencies
- Corrected Readme.md, backend/doc.md, and backend/test.md to accurately reflect current codebase. - Fixed collector log (7s vs 1s interval). - Implemented missing 'mempool_health_statistics' in backend to fix frontend gap. - Upgraded backend and frontend dependencies to latest production-ready versions.
1 parent 0516d80 commit 5417968

File tree

7 files changed

+109
-114
lines changed

7 files changed

+109
-114
lines changed

Readme.md

Lines changed: 34 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,47 @@
1-
# Bitcoin Core Fee Tracker & Visualizer
1+
### Bitcoin Core Fee Rate Estimator
22

3-
A comprehensive full-stack application for monitoring and visualizing Bitcoin Core transaction fees, mempool dynamics, and block statistics. This tool provides real-time insights through a modern web interface powered by a robust backend that interfaces directly with a Bitcoin Core node.
3+
- A full-stack application for monitoring and validating Bitcoin Core transaction fee estimates against actual block data.
4+
- Built on top of Bitcoin Core PR #34075
45

5-
## Overview
6+
### Overview
67

7-
This project provides a centralized platform to track and analyze Bitcoin transaction fees. It bridges the gap between raw Bitcoin Core RPC data and human-readable visualizations.
8+
This project tracks `estimatesmartfee` from a Bitcoin Core node and compares those estimates with the feerate percentiles of subsequent blocks. It provides a visual interface to verify the accuracy of the node's fee predictions.
89

9-
### Key Features
10-
- **Real-time Fee Estimation**: Get feerate estimates based on current mempool percentiles and historical data.
11-
- **Interactive Charts**: Visualize fee history, block statistics, and mempool status using Recharts and D3.
12-
- **Mempool Diagram**: View the current state of the mempool in a graphical format.
13-
- **Unified API**: A clean REST API that handles multiple data sources and internal caching.
10+
#### Key Features
11+
- **Fee Estimate Tracking**: A background service polls Bitcoin Core every 7 seconds for smart fee estimates.
12+
- **Historical Accuracy**: Visualizes the accuracy of estimates (within range, overpaid, or underpaid) compared to real block data.
13+
- **Mempool Diagram**: Real-time visualization of the mempool fee/weight accumulation curve.
14+
- **Block Statistics**: Direct insights into feerate percentiles for recent blocks.
1415

15-
## Architecture & Integration
16+
#### Architecture
1617

17-
The project is divided into two main modules that are linked through a secure proxy layer:
18+
- **Backend (Python/Flask)**: Communicates with Bitcoin Core via RPC. Collects estimates into SQLite and serves data via a REST API.
19+
- **Frontend (Next.js/TypeScript)**: Modern UI using Recharts and D3. Communicates with the backend via a secure API proxy route.
1820

19-
- **Backend (Python/Flask)**: Communicates with your Bitcoin Core node via RPC. It handles data collection, persistence in SQLite, and serves as the source of truth for all analytics.
20-
- **Frontend (Next.js/TypeScript)**: Provides the user interface. It communicates with the backend via an internal API route (`frontend/src/app/api/[...path]/route.ts`) which proxies requests to the backend service. This architecture simplifies deployment and enhances security.
21-
22-
## Project Structure
21+
#### Project Structure
2322

2423
```text
2524
.
26-
├── backend/ # Flask API, data collector, and database services
27-
│ ├── src/ # Application logic (services, app.py)
25+
├── backend/ # Flask API, data collector, and SQLite database
26+
│ ├── src/ # Core logic and RPC services
2827
│ └── tests/ # Pytest suite for backend validation
2928
├── frontend/ # Next.js web application
30-
│ ├── src/app/ # App router, pages, and secure API proxy
31-
│ └── src/components/ # Reusable UI components and dynamic charts
32-
└── .github/workflows/ # Automated testing workflow (GitHub Actions)
29+
│ ├── src/app/ # App router and pages
30+
│ └── src/components/ # D3 and Recharts visualization components
31+
└── .github/workflows/ # Automated testing workflow
3332
```
3433

35-
## How to Use
34+
#### How to Use
3635

37-
### Prerequisites
38-
- **Bitcoin Core Node**: Access to a running Bitcoin Core node with RPC enabled.
39-
- **Python**: Version 3.12+
40-
- **Node.js**: Version 22+
36+
#### Prerequisites
37+
- **Bitcoin Core Node**: Access to a node with RPC enabled (`getblockstats` support required).
38+
- **Python**: 3.12+
39+
- **Node.js**: 22+
4140

42-
### 1. Configuration
43-
- **Backend**: Navigate to `backend/`, copy `rpc_config.ini.example` to `rpc_config.ini`, and fill in your node's details.
44-
- **Frontend**: Ensure the `BACKEND_URL` environment variable is set (defaults to `http://127.0.0.1:5001`).
41+
#### 1. Configuration
42+
- **Backend**: Copy `backend/rpc_config.ini.example` to `backend/rpc_config.ini` and provide RPC credentials.
4543

46-
### 2. Manual Startup
44+
#### 2. Manual Startup
4745
**Backend:**
4846
```bash
4947
cd backend
@@ -60,20 +58,15 @@ npm install
6058
npm run dev
6159
```
6260

63-
### 3. Automated Startup (Production-like)
64-
A `restart.sh` script is provided in the root directory to stop any existing instances and start both services in the background using Gunicorn (backend) and Next.js (frontend):
61+
#### 3. Automated Startup
62+
Use the provided `restart.sh` script to launch both services in the background:
6563
```bash
6664
chmod +x restart.sh
6765
./restart.sh
6866
```
6967

70-
## Credits
71-
72-
This project is a collaborative effort between:
73-
- **Ismael Sadeeq**: Main contributor and maintainer.
74-
- **Gemini**: AI-assisted development, architectural design, and test automation.
75-
- **Claude**: AI-assisted development, code optimization, and documentation.
76-
- **b-l-u-e** ([winnie.gitau282@gmail.com](mailto:winnie.gitau282@gmail.com)): Contributions to core services, backend logic, and test suites.
77-
- **mercie-ux** ([mbaomercy0@gmail.com](mailto:mbaomercy0@gmail.com)): Contributions to user experience, frontend design, and visual components.
78-
79-
The codebase represents a merge of PR work from contributors and AI-generated improvements for a complete, robust experience.
68+
### Credits
69+
- **Abubakar Sadiq Ismail**: Bitcoin Core contributor and architecture.
70+
- **b-l-u-e**: Backend logic and service implementation.
71+
- **mercie-ux**: Frontend design and visual components.
72+
- **Gemini & Claude**: AI-assisted development and test automation.

backend/doc.md

Lines changed: 16 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Backend - Bitcoin Core Fees API
22

3-
This service provides a Flask-based REST API to interact with Bitcoin Core RPC and provide fee analytics.
3+
This Flask-based REST API interacts with Bitcoin Core RPC and a local SQLite database to provide fee analytics and block statistics.
44

55
## Running the Application
66

@@ -15,55 +15,36 @@ pip install -r requirements.txt
1515
Ensure `rpc_config.ini` is configured with your Bitcoin Core RPC credentials.
1616

1717
### 2. Start the App (Background)
18-
To start the application and keep it running after you disconnect from the terminal, use the following `nohup` command:
18+
To start the application in the background:
1919

2020
```bash
21-
nohup .venv/bin/python app.py > debug.log 2>&1 &
21+
nohup env PYTHONPATH=src .venv/bin/gunicorn --workers 4 --bind 127.0.0.1:5001 app:app > debug.log 2>&1 &
2222
```
2323

24-
**What this command does:**
25-
* `nohup`: Stands for "No Hang Up". It allows the command to continue running even after you logout or close the terminal.
26-
* `.venv/bin/python app.py`: Executes the Flask app using the Python interpreter inside your virtual environment.
27-
* `> debug.log`: Redirects standard output (logs) to a file named `debug.log`.
28-
* `2>&1`: Redirects standard error (errors) to the same location as standard output (`debug.log`).
29-
* `&`: Puts the command in the background, allowing you to continue using the terminal.
30-
3124
### 3. Monitoring Logs
3225
To see the logs in real-time:
3326
```bash
3427
tail -f debug.log
3528
```
3629

3730
### 4. Stopping the App
38-
To stop the background process, you can find the Process ID (PID) and kill it, or use `pkill`:
39-
40-
**Option A (Using pkill):**
41-
```bash
42-
pkill -f "python app.py"
43-
```
44-
45-
**Option B (By Port):**
31+
To stop the process:
4632
```bash
47-
kill $(lsof -t -i:5001)
33+
pkill -f "gunicorn"
4834
```
4935

50-
**Option C (Manual):**
51-
1. Find the PID: `ps aux | grep "python app.py"`
52-
2. Kill the process: `kill <PID>`
53-
5436
## API Endpoints
5537

5638
| Endpoint | Method | Description |
5739
|----------|--------|-------------|
58-
| `/health` | GET | Check service and RPC connection status. |
59-
| `/blockchain/info` | GET | Get general info about the Bitcoin blockchain. |
60-
| `/blockcount` | GET | Get the current block height. |
61-
| `/mempool/info` | GET | Get current mempool state (size, bytes, etc.). |
62-
| `/fees/<target>/<mode>/<level>` | GET | Get `estimatesmartfee` from Bitcoin Core. |
63-
| `/fees/mempool` | GET | Get fee estimates based on current mempool percentiles. |
64-
| `/api/v1/fees/estimate` | GET | Unified endpoint for mempool, historical, or hybrid estimates. |
65-
| `/analytics/summary` | GET | Get summarized fee and block analytics (internal or external fallback). |
66-
| `/blockstats/<height>` | GET | Get detailed stats for a specific block height. |
67-
| `/external/block-stats/<count>` | GET | Proxy to external API for block statistics. |
68-
| `/external/fees-stats/<count>` | GET | Proxy to external API for fee statistics. |
69-
| `/external/fees-sum/<count>` | GET | Proxy to external API for fee summation analytics. |
40+
| `/blockcount` | GET | Current block height from node. |
41+
| `/fees/<target>/<mode>/<level>` | GET | `estimatesmartfee` results converted to sat/vB. |
42+
| `/mempool-diagram` | GET | Analyzed feerate diagram for mempool accumulation. |
43+
| `/performance-data/<start_block>/` | GET | Block feerate percentiles vs. recorded estimates. |
44+
| `/fees-sum/<start_block>/` | GET | Aggregated accuracy metrics (within, over, under). |
45+
46+
### Parameters:
47+
- `target`: Confirmation target (e.g., 2, 7, 144).
48+
- `mode`: Fee estimation mode (`economical`, `conservative`, `unset`).
49+
- `level`: Verbosity level for fee estimation.
50+
- `start_block`: Block height to start range analysis from.

backend/requirements.txt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
Flask==3.1.3
2-
Flask-CORS==4.0.2
2+
Flask-CORS==6.0.2
33
Flask-Limiter==4.1.1
44
requests==2.32.5
55
configparser==6.0.0
6-
gunicorn
6+
gunicorn==25.1.0
7+
pytest==9.0.2
8+
pytest-cov==7.0.0

backend/src/services/collector_service.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
_collector_started = False
99

1010
def run_collector():
11-
logger.info("Starting high-resolution fee estimate collector (1s interval)...")
11+
logger.info("Starting high-resolution fee estimate collector (7s interval)...")
1212
# 1 and 2 are the same, so we only poll 2
1313
targets = [2, 7, 144]
1414

backend/src/services/rpc_service.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ def _get_single_block_stats_cached(height: int) -> tuple:
104104
Returns a frozen (JSON-serialised) snapshot so the lru_cache holds
105105
immutable data. Use get_single_block_stats() for normal access.
106106
"""
107-
result = _rpc_call("getblockstats", [height, ["height", "feerate_percentiles", "minfeerate", "maxfeerate"]])
107+
result = _rpc_call("getblockstats", [height, ["height", "feerate_percentiles", "minfeerate", "maxfeerate", "total_weight"]])
108108
return json.dumps(result) # freeze as string
109109

110110

@@ -121,12 +121,47 @@ def get_block_count() -> int:
121121
return _rpc_call("getblockcount", [])
122122

123123

124+
def get_mempool_health_statistics() -> List[Dict[str, Any]]:
125+
"""
126+
Fetches stats for the last 5 blocks to compare their weights with
127+
the current mempool's readiness.
128+
"""
129+
current_height = get_block_count()
130+
stats = []
131+
132+
# Using getmempoolfeeratediagram for accurate total weight
133+
mempool_diagram = _rpc_call("getmempoolfeeratediagram", [])
134+
total_mempool_weight = mempool_diagram[-1]["weight"] if mempool_diagram else 0
135+
136+
for h in range(current_height - 4, current_height + 1):
137+
try:
138+
b = get_single_block_stats(h)
139+
weight = b.get("total_weight", 0)
140+
141+
stats.append({
142+
"block_height": h,
143+
"block_weight": weight,
144+
"mempool_txs_weight": total_mempool_weight,
145+
"ratio": min(1.0, total_mempool_weight / 4_000_000)
146+
})
147+
except Exception:
148+
continue
149+
return stats
150+
151+
124152
def estimate_smart_fee(conf_target: int, mode: str = "unset", verbosity_level: int = 2) -> Dict[str, Any]:
125153
effective_target = _clamp_target(conf_target)
126154
result = _rpc_call("estimatesmartfee", [effective_target, mode, verbosity_level])
127155
if result and "feerate" in result:
128156
# feerate is BTC/kVB → sat/vB: × 1e8 (BTC→sat) ÷ 1e3 (kVB→vB) = × 1e5
129157
result["feerate_sat_per_vb"] = result["feerate"] * 100_000
158+
159+
# Include health stats for the frontend
160+
try:
161+
result["mempool_health_statistics"] = get_mempool_health_statistics()
162+
except Exception as e:
163+
logger.error(f"Failed to include health stats: {e}")
164+
130165
return result
131166

132167

backend/test.md

Lines changed: 13 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,23 @@
22

33
## Prerequisites
44

5-
`requirements.txt` should include:
5+
Ensure all dependencies including test tools are installed:
66

7-
```
8-
Flask==3.1.3
9-
Flask-CORS==4.0.2
10-
Flask-Limiter==4.1.1
11-
requests==2.32.3
12-
configparser==6.0.0
13-
pytest
14-
pytest-cov
7+
```bash
8+
pip install pytest pytest-cov
159
```
1610

1711
---
1812

1913
## Test Structure
2014

21-
```
15+
```text
2216
tests/
23-
├── conftest.py # pytest path setup
24-
├── helpers.py # shared app factory
25-
├── test_app.py # HTTP layer — routes, error handlers, mode validation
26-
├── test_rpc_service.py # RPC logic, fee math, caching, mempool diagram
27-
├── test_database_service.py # SQLite writes, queries, indexes, edge cases
28-
└── test_collector_service.py # Collector lifecycle, duplicate guard, error resilience
17+
├── conftest.py # Pytest path and app setup
18+
├── helpers.py # Shared app factory for tests
19+
├── test_app.py # HTTP layer (routes, validation)
20+
├── test_rpc_service.py # RPC conversion and calculation logic
21+
└── test_database_service.py # SQLite writes and query filtering
2922
```
3023

3124
---
@@ -34,11 +27,6 @@ tests/
3427

3528
All commands should be run from the `backend/` directory.
3629

37-
**Install all required packages**
38-
```
39-
pip install -r requirements.txt
40-
```
41-
4230
**Run the full suite:**
4331
```bash
4432
python -m pytest tests/ -v
@@ -49,12 +37,11 @@ python -m pytest tests/ -v
4937
python -m pytest tests/test_app.py -v
5038
python -m pytest tests/test_rpc_service.py -v
5139
python -m pytest tests/test_database_service.py -v
52-
python -m pytest tests/test_collector_service.py -v
5340
```
5441

5542
**Run a single test by name:**
5643
```bash
57-
python -m pytest tests/test_rpc_service.py::TestRpcService::test_feerate_conversion_is_correct -v
44+
python -m pytest tests/test_rpc_service.py::test_feerate_conversion_is_correct -v
5845
```
5946

6047
**Stop on first failure:**
@@ -68,13 +55,10 @@ python -m pytest tests/ -v -x
6855

6956
**Print coverage summary in terminal:**
7057
```bash
71-
python -m pytest tests/ -v --cov=src/services --cov=src/app --cov-report=term-missing
58+
python -m pytest tests/ -v --cov=src --cov-report=term-missing
7259
```
7360

74-
**Generate an HTML report (opens in browser):**
61+
**Generate an HTML report:**
7562
```bash
76-
python -m pytest tests/ --cov=src/services --cov=src/app --cov-report=html
77-
open htmlcov/index.html
63+
python -m pytest tests/ --cov=src --cov-report=html
7864
```
79-
80-

frontend/package.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@
1111
},
1212
"dependencies": {
1313
"d3": "^7.9.0",
14-
"lucide-react": "^0.544.0",
15-
"next": "15.5.10",
16-
"react": "19.1.5",
17-
"react-dom": "19.1.5",
18-
"recharts": "^3.2.1"
14+
"lucide-react": "^0.575.0",
15+
"next": "16.1.6",
16+
"react": "19.2.4",
17+
"react-dom": "19.2.4",
18+
"recharts": "^3.7.0"
1919
},
2020
"devDependencies": {
2121
"@eslint/eslintrc": "^3",

0 commit comments

Comments
 (0)