diff --git a/Cargo.lock b/Cargo.lock
index ba6be93c1..81d7c1c4f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2308,7 +2308,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "337f65eb93d9996551b9442423480eca4532586b337484446eb5138d0cd8fcf0"
dependencies = [
"heck 0.5.0",
- "indexmap 2.13.0",
+ "indexmap 1.9.3",
"itertools 0.14.0",
"proc-macro-crate",
"proc-macro2",
@@ -6945,12 +6945,11 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
[[package]]
name = "tapes"
version = "0.1.0"
-source = "git+https://github.com/Cuprate/Tapes#a30a5261564b12b21b126599149a88d5a0c5087c"
+source = "git+https://github.com/Cuprate/Tapes#5bb3fc964341f0d608c13275e3496d43d4b28ab1"
dependencies = [
"blake2",
"borsh",
"bytemuck",
- "libc",
"parking_lot 0.12.5",
"slab",
]
diff --git a/bin/explorer/python/explorer.py b/bin/explorer/python/explorer.py
index 52923cc20..58a07616d 100755
--- a/bin/explorer/python/explorer.py
+++ b/bin/explorer/python/explorer.py
@@ -15,6 +15,7 @@
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
+import asyncio
from datetime import datetime, timezone
from quart import Quart, render_template, abort, request, redirect, url_for, Response
@@ -150,6 +151,70 @@ async def index():
)
+@app.route("/blocks")
+async def list_blocks():
+ """List all blocks with pagination"""
+ BLOCKS_PER_PAGE = 100
+
+ # Get page number from query param (1-indexed for users)
+ page = request.args.get("page", 1, type=int)
+ if page < 1:
+ page = 1
+
+ current_difficulty = await rpc.call("current_difficulty", params=[])
+ current_height = await rpc.call("current_height", params=[])
+ hashrate = await rpc.call("get_hashrate", params=[])
+
+ total = current_height + 1 # blocks 0 to current_height
+
+ # Calculate range: newest first
+ offset = (page - 1) * BLOCKS_PER_PAGE
+ start_height = current_height - offset
+ end_height = max(0, start_height - BLOCKS_PER_PAGE + 1)
+
+ # Build list of heights to fetch
+ heights = [h for h in range(start_height, end_height - 1, -1) if h >= 0]
+
+ async def fetch_block(height):
+ try:
+ block = await rpc.call("get_block", params=[height])
+ dt = datetime.fromtimestamp(block["timestamp"], tz=timezone.utc)
+ return {
+ "height": int(block["height"]),
+ "size": int(block["size"]),
+ "n_txs": len(block["txs"]),
+ "timestamp": dt.strftime("%H:%M UTC %d %b %Y"),
+ "powtype": block["powtype"],
+ "hash": block["hash"],
+ }
+ except JsonRpcError:
+ return None
+
+ # Fetch all blocks in parallel
+ results = await asyncio.gather(*[fetch_block(h) for h in heights])
+ blocks = [b for b in results if b is not None]
+
+ # Calculate pagination info
+ total_pages = (total + BLOCKS_PER_PAGE - 1) // BLOCKS_PER_PAGE
+ has_prev = page > 1
+ has_next = page < total_pages
+
+ return await render_template(
+ "blocks.html",
+ network=app.config["NETWORK"],
+ current_difficulty=current_difficulty[0],
+ current_height=current_height,
+ hashrate=format_hashrate(hashrate),
+ blocks=blocks,
+ total=total,
+ page=page,
+ total_pages=total_pages,
+ has_prev=has_prev,
+ has_next=has_next,
+ blocks_per_page=BLOCKS_PER_PAGE,
+ )
+
+
@app.route("/block/")
async def get_block_by_height(block_height: int):
if block_height < 0:
diff --git a/bin/explorer/python/static/layout.css b/bin/explorer/python/static/layout.css
index d44421893..94d029f00 100644
--- a/bin/explorer/python/static/layout.css
+++ b/bin/explorer/python/static/layout.css
@@ -335,6 +335,10 @@ code {
overflow: hidden;
}
+.table-section.full-width {
+ width: 100%;
+}
+
.table-header {
display: flex;
justify-content: space-between;
@@ -562,6 +566,58 @@ tbody tr:hover td { background: var(--color-hover); }
white-space: pre-wrap;
}
+.pagination {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ gap: 1rem;
+ padding: 1.5rem;
+ border-top: 1px solid var(--table-border-color);
+}
+
+.pagination .btn-sm {
+ padding: 0.5rem 1rem;
+ font-size: 1.3rem;
+}
+
+.pagination .btn-sm.disabled {
+ opacity: 0.4;
+ cursor: not-allowed;
+ pointer-events: none;
+}
+
+.pagination-info {
+ font-size: 1.4rem;
+ color: var(--color-muted);
+ padding: 0 1rem;
+}
+
+.pagination {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ gap: 1rem;
+ padding: 1.5rem;
+ border-top: 1px solid var(--table-border-color);
+}
+
+.pagination .btn-sm {
+ padding: 0.5rem 1rem;
+ font-size: 1.3rem;
+}
+
+.pagination .btn-sm.disabled {
+ opacity: 0.4;
+ cursor: not-allowed;
+ pointer-events: none;
+}
+
+.pagination-info {
+ font-size: 1.4rem;
+ color: var(--color-muted);
+ padding: 0 1rem;
+}
+
.footer {
border-top: 1px solid var(--table-border-color);
padding: 3rem 0;
diff --git a/bin/explorer/python/templates/blocks.html b/bin/explorer/python/templates/blocks.html
new file mode 100644
index 000000000..37927be53
--- /dev/null
+++ b/bin/explorer/python/templates/blocks.html
@@ -0,0 +1,88 @@
+
+
+
+
+
+ All Blocks - DarkFi Block Explorer
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ | Height |
+ Size |
+ Txs |
+ Timestamp |
+ PoW |
+ Block Hash |
+
+
+
+ {% for block in blocks %}
+
+ | {{ block.height|int }} |
+ {{ block.size|int }} |
+ {{ block.n_txs|int }} |
+ {{ block.timestamp }} |
+ {{ block.powtype }} |
+ {{ block.hash }} |
+
+ {% endfor %}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bin/explorer/python/templates/index.html b/bin/explorer/python/templates/index.html
index 59a9cd6ee..90d4ce77c 100644
--- a/bin/explorer/python/templates/index.html
+++ b/bin/explorer/python/templates/index.html
@@ -79,7 +79,7 @@