chore: remove configuration files and enhance blockchain explorer with advanced search, analytics, and export features
- Delete .aitbc.yaml.example CLI configuration template - Delete .lycheeignore link checker exclusion rules - Delete .nvmrc Node.js version specification - Add advanced search panel with filters for address, amount range, transaction type, time range, and validator - Add analytics dashboard with transaction volume, active addresses, and block time metrics - Add Chart.js integration
This commit is contained in:
@@ -38,7 +38,7 @@ class InMemoryMempool:
|
||||
self._max_size = max_size
|
||||
self._min_fee = min_fee
|
||||
|
||||
def add(self, tx: Dict[str, Any]) -> str:
|
||||
def add(self, tx: Dict[str, Any], chain_id: str = "ait-devnet") -> str:
|
||||
fee = tx.get("fee", 0)
|
||||
if fee < self._min_fee:
|
||||
raise ValueError(f"Fee {fee} below minimum {self._min_fee}")
|
||||
@@ -56,14 +56,14 @@ class InMemoryMempool:
|
||||
self._evict_lowest_fee()
|
||||
self._transactions[tx_hash] = entry
|
||||
metrics_registry.set_gauge("mempool_size", float(len(self._transactions)))
|
||||
metrics_registry.increment("mempool_tx_added_total")
|
||||
metrics_registry.increment(f"mempool_tx_added_total_{chain_id}")
|
||||
return tx_hash
|
||||
|
||||
def list_transactions(self) -> List[PendingTransaction]:
|
||||
def list_transactions(self, chain_id: str = "ait-devnet") -> List[PendingTransaction]:
|
||||
with self._lock:
|
||||
return list(self._transactions.values())
|
||||
|
||||
def drain(self, max_count: int, max_bytes: int) -> List[PendingTransaction]:
|
||||
def drain(self, max_count: int, max_bytes: int, chain_id: str = "ait-devnet") -> List[PendingTransaction]:
|
||||
"""Drain transactions for block inclusion, prioritized by fee (highest first)."""
|
||||
with self._lock:
|
||||
sorted_txs = sorted(
|
||||
@@ -84,17 +84,17 @@ class InMemoryMempool:
|
||||
del self._transactions[tx.tx_hash]
|
||||
|
||||
metrics_registry.set_gauge("mempool_size", float(len(self._transactions)))
|
||||
metrics_registry.increment("mempool_tx_drained_total", float(len(result)))
|
||||
metrics_registry.increment(f"mempool_tx_drained_total_{chain_id}", float(len(result)))
|
||||
return result
|
||||
|
||||
def remove(self, tx_hash: str) -> bool:
|
||||
def remove(self, tx_hash: str, chain_id: str = "ait-devnet") -> bool:
|
||||
with self._lock:
|
||||
removed = self._transactions.pop(tx_hash, None) is not None
|
||||
if removed:
|
||||
metrics_registry.set_gauge("mempool_size", float(len(self._transactions)))
|
||||
return removed
|
||||
|
||||
def size(self) -> int:
|
||||
def size(self, chain_id: str = "ait-devnet") -> int:
|
||||
with self._lock:
|
||||
return len(self._transactions)
|
||||
|
||||
@@ -104,7 +104,7 @@ class InMemoryMempool:
|
||||
return
|
||||
lowest = min(self._transactions.values(), key=lambda t: (t.fee, -t.received_at))
|
||||
del self._transactions[lowest.tx_hash]
|
||||
metrics_registry.increment("mempool_evictions_total")
|
||||
metrics_registry.increment(f"mempool_evictions_total_{chain_id}")
|
||||
|
||||
|
||||
class DatabaseMempool:
|
||||
@@ -123,17 +123,19 @@ class DatabaseMempool:
|
||||
with self._lock:
|
||||
self._conn.execute("""
|
||||
CREATE TABLE IF NOT EXISTS mempool (
|
||||
tx_hash TEXT PRIMARY KEY,
|
||||
chain_id TEXT NOT NULL,
|
||||
tx_hash TEXT NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
fee INTEGER DEFAULT 0,
|
||||
size_bytes INTEGER DEFAULT 0,
|
||||
received_at REAL NOT NULL
|
||||
received_at REAL NOT NULL,
|
||||
PRIMARY KEY (chain_id, tx_hash)
|
||||
)
|
||||
""")
|
||||
self._conn.execute("CREATE INDEX IF NOT EXISTS idx_mempool_fee ON mempool(fee DESC)")
|
||||
self._conn.commit()
|
||||
|
||||
def add(self, tx: Dict[str, Any]) -> str:
|
||||
def add(self, tx: Dict[str, Any], chain_id: str = "ait-devnet") -> str:
|
||||
fee = tx.get("fee", 0)
|
||||
if fee < self._min_fee:
|
||||
raise ValueError(f"Fee {fee} below minimum {self._min_fee}")
|
||||
@@ -144,33 +146,34 @@ class DatabaseMempool:
|
||||
|
||||
with self._lock:
|
||||
# Check duplicate
|
||||
row = self._conn.execute("SELECT 1 FROM mempool WHERE tx_hash = ?", (tx_hash,)).fetchone()
|
||||
row = self._conn.execute("SELECT 1 FROM mempool WHERE chain_id = ? AND tx_hash = ?", (chain_id, tx_hash)).fetchone()
|
||||
if row:
|
||||
return tx_hash
|
||||
|
||||
# Evict if full
|
||||
count = self._conn.execute("SELECT COUNT(*) FROM mempool").fetchone()[0]
|
||||
count = self._conn.execute("SELECT COUNT(*) FROM mempool WHERE chain_id = ?", (chain_id,)).fetchone()[0]
|
||||
if count >= self._max_size:
|
||||
self._conn.execute("""
|
||||
DELETE FROM mempool WHERE tx_hash = (
|
||||
SELECT tx_hash FROM mempool ORDER BY fee ASC, received_at DESC LIMIT 1
|
||||
DELETE FROM mempool WHERE chain_id = ? AND tx_hash = (
|
||||
SELECT tx_hash FROM mempool WHERE chain_id = ? ORDER BY fee ASC, received_at DESC LIMIT 1
|
||||
)
|
||||
""")
|
||||
metrics_registry.increment("mempool_evictions_total")
|
||||
""", (chain_id, chain_id))
|
||||
metrics_registry.increment(f"mempool_evictions_total_{chain_id}")
|
||||
|
||||
self._conn.execute(
|
||||
"INSERT INTO mempool (tx_hash, content, fee, size_bytes, received_at) VALUES (?, ?, ?, ?, ?)",
|
||||
(tx_hash, content, fee, size_bytes, time.time())
|
||||
"INSERT INTO mempool (chain_id, tx_hash, content, fee, size_bytes, received_at) VALUES (?, ?, ?, ?, ?, ?)",
|
||||
(chain_id, tx_hash, content, fee, size_bytes, time.time())
|
||||
)
|
||||
self._conn.commit()
|
||||
metrics_registry.increment("mempool_tx_added_total")
|
||||
self._update_gauge()
|
||||
metrics_registry.increment(f"mempool_tx_added_total_{chain_id}")
|
||||
self._update_gauge(chain_id)
|
||||
return tx_hash
|
||||
|
||||
def list_transactions(self) -> List[PendingTransaction]:
|
||||
def list_transactions(self, chain_id: str = "ait-devnet") -> List[PendingTransaction]:
|
||||
with self._lock:
|
||||
rows = self._conn.execute(
|
||||
"SELECT tx_hash, content, fee, size_bytes, received_at FROM mempool ORDER BY fee DESC, received_at ASC"
|
||||
"SELECT tx_hash, content, fee, size_bytes, received_at FROM mempool WHERE chain_id = ? ORDER BY fee DESC, received_at ASC",
|
||||
(chain_id,)
|
||||
).fetchall()
|
||||
return [
|
||||
PendingTransaction(
|
||||
@@ -179,10 +182,11 @@ class DatabaseMempool:
|
||||
) for r in rows
|
||||
]
|
||||
|
||||
def drain(self, max_count: int, max_bytes: int) -> List[PendingTransaction]:
|
||||
def drain(self, max_count: int, max_bytes: int, chain_id: str = "ait-devnet") -> List[PendingTransaction]:
|
||||
with self._lock:
|
||||
rows = self._conn.execute(
|
||||
"SELECT tx_hash, content, fee, size_bytes, received_at FROM mempool ORDER BY fee DESC, received_at ASC"
|
||||
"SELECT tx_hash, content, fee, size_bytes, received_at FROM mempool WHERE chain_id = ? ORDER BY fee DESC, received_at ASC",
|
||||
(chain_id,)
|
||||
).fetchall()
|
||||
|
||||
result: List[PendingTransaction] = []
|
||||
@@ -203,29 +207,29 @@ class DatabaseMempool:
|
||||
|
||||
if hashes_to_remove:
|
||||
placeholders = ",".join("?" * len(hashes_to_remove))
|
||||
self._conn.execute(f"DELETE FROM mempool WHERE tx_hash IN ({placeholders})", hashes_to_remove)
|
||||
self._conn.execute(f"DELETE FROM mempool WHERE chain_id = ? AND tx_hash IN ({placeholders})", [chain_id] + hashes_to_remove)
|
||||
self._conn.commit()
|
||||
|
||||
metrics_registry.increment("mempool_tx_drained_total", float(len(result)))
|
||||
self._update_gauge()
|
||||
metrics_registry.increment(f"mempool_tx_drained_total_{chain_id}", float(len(result)))
|
||||
self._update_gauge(chain_id)
|
||||
return result
|
||||
|
||||
def remove(self, tx_hash: str) -> bool:
|
||||
def remove(self, tx_hash: str, chain_id: str = "ait-devnet") -> bool:
|
||||
with self._lock:
|
||||
cursor = self._conn.execute("DELETE FROM mempool WHERE tx_hash = ?", (tx_hash,))
|
||||
cursor = self._conn.execute("DELETE FROM mempool WHERE chain_id = ? AND tx_hash = ?", (chain_id, tx_hash))
|
||||
self._conn.commit()
|
||||
removed = cursor.rowcount > 0
|
||||
if removed:
|
||||
self._update_gauge()
|
||||
self._update_gauge(chain_id)
|
||||
return removed
|
||||
|
||||
def size(self) -> int:
|
||||
def size(self, chain_id: str = "ait-devnet") -> int:
|
||||
with self._lock:
|
||||
return self._conn.execute("SELECT COUNT(*) FROM mempool").fetchone()[0]
|
||||
return self._conn.execute("SELECT COUNT(*) FROM mempool WHERE chain_id = ?", (chain_id,)).fetchone()[0]
|
||||
|
||||
def _update_gauge(self) -> None:
|
||||
count = self._conn.execute("SELECT COUNT(*) FROM mempool").fetchone()[0]
|
||||
metrics_registry.set_gauge("mempool_size", float(count))
|
||||
def _update_gauge(self, chain_id: str = "ait-devnet") -> None:
|
||||
count = self._conn.execute("SELECT COUNT(*) FROM mempool WHERE chain_id = ?", (chain_id,)).fetchone()[0]
|
||||
metrics_registry.set_gauge(f"mempool_size_{chain_id}", float(count))
|
||||
|
||||
|
||||
# Singleton
|
||||
|
||||
Reference in New Issue
Block a user