auto broadcast pending txns, show net info

This commit is contained in:
Dobromir Popov
2025-09-30 01:33:23 +03:00
parent b96778196c
commit 499ad9690e
2 changed files with 147 additions and 42 deletions

View File

@@ -140,13 +140,26 @@ def get_network_info():
mempool = rpc_call("getrawmempool")
blockchain_info = rpc_call("getblockchaininfo")
# Format peer information
peers = []
for peer in peer_info:
peers.append({
"addr": peer.get("addr", ""),
"services": peer.get("servicesnames", []),
"relaytxes": peer.get("relaytxes", False),
"synced_blocks": peer.get("synced_blocks", 0),
"last_transaction": peer.get("last_transaction", 0),
"version": peer.get("subver", ""),
"pingtime": round(peer.get("pingtime", 0) * 1000, 1),
})
return jsonify({
"network": {
"connections": network_info.get("connections", 0),
"networkactive": network_info.get("networkactive", False),
"relayfee": network_info.get("relayfee", 0),
},
"peers": len(peer_info),
"peers": peers,
"mempool_size": len(mempool),
"blockchain": {
"blocks": blockchain_info.get("blocks", 0),
@@ -159,6 +172,34 @@ def get_network_info():
return jsonify({"error": str(exc)}), 500
@app.route("/api/rebroadcast", methods=["POST"])
@require_token
def rebroadcast_pending():
try:
# Get all pending transactions
txs = rpc_call("listtransactions", "*", 100, 0, True)
pending = [tx for tx in txs if tx.get("confirmations", 0) == 0]
rebroadcasted = []
for tx in pending:
txid = tx.get("txid")
if txid:
try:
# Get raw transaction
tx_data = rpc_call("gettransaction", txid, True)
raw_hex = tx_data.get("hex")
if raw_hex:
# Rebroadcast
rpc_call("sendrawtransaction", raw_hex)
rebroadcasted.append(txid)
except Exception:
pass # Already in mempool or other error
return jsonify({"rebroadcasted": rebroadcasted, "count": len(rebroadcasted)})
except RuntimeError as exc:
return jsonify({"error": str(exc)}), 500
@app.route("/")
def root():
return send_from_directory(app.static_folder, "index.html")

View File

@@ -280,15 +280,39 @@
function renderTx(tx) {
const li = document.createElement('li');
const amount = Number(tx.amount).toFixed(8);
const amount = Math.abs(Number(tx.amount)).toFixed(8);
const type = tx.category === 'send' ? 'Sent' : 'Received';
const confirmations = tx.confirmations || 0;
let status, statusClass, tooltip = '';
let ageInfo = '';
if (confirmations === 0) {
status = 'Pending';
statusClass = 'status-pending';
// Calculate transaction age
const txTime = tx.time || tx.timereceived;
if (txTime) {
const now = Math.floor(Date.now() / 1000);
const ageSeconds = now - txTime;
const ageMinutes = Math.floor(ageSeconds / 60);
const ageHours = Math.floor(ageMinutes / 60);
let ageText = '';
if (ageHours > 0) {
ageText = `${ageHours}h ${ageMinutes % 60}m ago`;
} else if (ageMinutes > 0) {
ageText = `${ageMinutes}m ago`;
} else {
ageText = `${ageSeconds}s ago`;
}
ageInfo = `<div class="muted" style="font-size: 11px;">Age: ${ageText}</div>`;
tooltip = `title="0 confirmations - waiting for network confirmation (${ageText})"`;
} else {
tooltip = 'title="0 confirmations - waiting for network confirmation"';
}
} else if (confirmations < 20) {
status = `Immature (${confirmations}/20)`;
statusClass = 'status-immature';
@@ -302,6 +326,7 @@
li.innerHTML = `
<div><strong>${type}</strong> ${amount} RIN <span class="status-badge ${statusClass}" ${tooltip}>${status}</span></div>
<div class="muted">${tx.time ? new Date(tx.time * 1000).toLocaleString() : ''}</div>
${ageInfo}
<div class="muted"><a href="https://explorer.rin.so/tx/${tx.txid}" target="_blank" class="tx-link">${tx.txid}</a></div>
`;
return li;
@@ -403,6 +428,7 @@
}
async function fetchNetworkInfo() {
try {
let res = await fetch('/api/network', { headers: authHeaders() });
let data = await res.json();
@@ -421,6 +447,23 @@
const network = data.network;
const blockchain = data.blockchain;
// Build peers list
let peersHtml = '';
if (data.peers && data.peers.length > 0) {
peersHtml = '<div style="margin-top: 12px;"><div class="muted" style="font-size: 12px; margin-bottom: 8px;">Connected Peers</div><div style="max-height: 200px; overflow-y: auto;">';
data.peers.forEach(peer => {
const relayStatus = peer.relaytxes ? '✓ Relay' : '✗ No Relay';
const syncStatus = peer.synced_blocks === blockchain.blocks ? '✓ Synced' : `${peer.synced_blocks}`;
peersHtml += `
<div style="padding: 6px; margin-bottom: 4px; background: rgba(9, 19, 30, 0.6); border-radius: 4px; font-size: 11px;">
<div style="font-weight: 600;">${peer.addr}</div>
<div class="muted">${relayStatus} | ${peer.version} | Ping: ${peer.pingtime}ms | Blocks: ${syncStatus}</div>
</div>
`;
});
peersHtml += '</div></div>';
}
networkInfo.innerHTML = `
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-bottom: 16px;">
<div>
@@ -440,18 +483,39 @@
<div style="font-size: 18px; font-weight: 600;">${blockchain.chain}</div>
</div>
</div>
<div class="muted" style="font-size: 12px;">
${peersHtml}
<div class="muted" style="font-size: 12px; margin-top: 12px;">
Network Active: ${network.networkactive ? 'Yes' : 'No'} |
Relay Fee: ${network.relayfee} RIN |
Difficulty: ${blockchain.difficulty.toFixed(2)}
Difficulty: ${Number(blockchain.difficulty).toFixed(2)}
</div>
`;
} catch (err) {
console.error('Network info fetch failed:', err);
networkInfo.innerHTML = `<div class="muted">Error loading network info: ${err.message}</div>`;
}
}
async function rebroadcastPending() {
try {
const res = await fetch('/api/rebroadcast', {
method: 'POST',
headers: authHeaders(),
});
const data = await res.json();
console.log(`Rebroadcasted ${data.count} pending transactions`);
} catch (err) {
console.error('Rebroadcast failed:', err);
}
}
async function refresh() {
await Promise.all([fetchBalances(), fetchTransactions(), fetchNetworkInfo()]);
}
// Auto-rebroadcast pending transactions every minute
setInterval(rebroadcastPending, 60000);
document.getElementById('refreshButton').addEventListener('click', refresh);
document.getElementById('generateButton').addEventListener('click', generateAddress);
document.getElementById('sendButton').addEventListener('click', sendCoins);