Repository navigation
Tell the operator whether their node exchanges transactions, not just blocks - #51
Merged
Merged
Conversation
… blocks A node can be synced, well peered, serving blocks and healthy by every measure this app already has, while never once receiving a transaction. Measured across two independent public BTX nodes on 2026-09-06: 21 of 22 peers had never sent one, the two nodes' mempools shared none of their 10 and 32 entries, and valid transactions paying twice the going rate had waited four days while blocks were mined empty. Nothing reported a problem because nothing was measuring it. summarize_tx_relay reads the per-peer message byte counters directly, the same evidence that identified the one working archive during the api.btxscan.io incident, and judge_tx_relay turns that into receiving, isolated or unknown. It rides the peer census the app already runs, so it costs no RPC, and it lands in the local service report as tx_relay (schema 3). Unknown is the default and a first-class answer. A node with under three peers, or whose oldest connection is under six hours, has observed nothing: BTX carries roughly ten transactions an hour network-wide, so a quiet half hour proves nothing, and a check that cannot evaluate must never read as a check that passed. PeerInfo gained conntime, relaytxes and minfeefilter; a peer object without conntime is unknown rather than connected since 1970, which is the difference between a fresh node and an accusation. Six tests, built on the live peer shapes from that measurement. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
# Conflicts: # apps/node/CHANGELOG.md
# Conflicts: # apps/node/CHANGELOG.md # crates/btx-core/src/node_api.rs
The tx-relay verdict now reaches the screen. "Your node's role" ends with a Transactions line (btx_core::node_api::tx_relay_line), from the refresher's per-tick peer census: Exchanging transactions, Not receiving any with the likely reason and what to do, or Not known yet and why. get_node_status also returns the verdict itself as tx_relay, with its TypeScript type. Re-measured 2026-10-29 on the two v0.34.15 nodes behind btxscan.io (read- only getpeerinfo/getnetworkinfo/getmempoolinfo). Relay works today, but 17 open peers under six hours old on one node had delivered no transaction, so six hours would call a healthy node isolated; the window is a day. The 0.09170997 BTX/kvB peers are nodes in initial block download, not a fee policy, so the wording says so, the judge counts peers open to transactions, and a node in initial download is not judged at all. The measurements sit beside TX_WALL_BTX_PER_KVB and TX_RELAY_WINDOW_SECS. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…g entries Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The gap. Every health signal easyNode has is about blocks: synced, peers, bytes served, archive census, stall verdict. A node can pass all of them and never receive a transaction, and nobody would know.
What the operator sees now. "Your node's role" ends with a Transactions line, same shape as the lines above it (mark, value, one plain note), sentence rendered in Rust:
unknownreads grey (helps = null), never green.How it works.
summarize_tx_relayreads the per-peer message byte counters (bytesrecv_per_msg.tx) from thegetpeerinfothe refresher already runs each tick, so it costs no extra RPC.judge_tx_relay(summary, initial_block_download)turns it intoreceiving,isolatedorunknown; the refresher caches it (AppState::tx_relay_cache, cleared on stop/start like the archive census),get_node_statusreturns it astx_relay(TS typeTxRelayHealthin main.ts) and appendstx_relay_linetorole_lines. The local service report carries it astx_relay, schema 3 (main was still at 2, so 3 was free).PeerInfogainedconntime,relaytxesandminfeefilter. A peer object withoutconntimeis unknown rather than connected since 1970.Re-measured today, and two things changed from September. 2026-10-29 10:46 and 10:58 UTC, the two btxd v0.34.15 nodes behind btxscan.io (btx-esplora-1,
/data/btxand/data/btx2), read-onlygetpeerinfo,getnetworkinfo,getmempoolinfoonly:relaytxes)minfeefiltervalues seenmempoolminfee0.00001Open peers (want transactions at the relay floor) by how long connected, open / delivered:
open_to_txcount (wants transactions and not walled) picks the reason sentence. For the same reason the judge now answersunknownwhile this node itself is in initial block download, since it asks its peers for nothing then.Measurements and dates are in comments at
TX_WALL_BTX_PER_KVBandTX_RELAY_WINDOW_SECSincrates/btx-core/src/node_api.rs.EasyBTX site: nothing reads
service-report.jsonthere (searchedEasyBTX/site); the report is a local file andcheckin.rshas its own schema. No site change needed.Tests. 12 tx-relay tests in node_api.rs (the September peer shapes, plus: six quiet hours are not isolation, a node in initial download is not judged, the four line wordings, the wall threshold against the values peers send). Ran locally what CI runs, on this branch merged with main: web
tsc --noEmit,npm test(280 passed),vite build; btx-corecargo check --locked --all-targets,cargo test --locked(1228 passed, 2 ignored, before the last main merge; after it,node::tests::an_adopted_node_is_found_by_name_and_lifted_before_its_stophung past 60 s on this Mac, the known local-node case, andplatform::unix::tests::process_cpu_time_reads_a_live_processfailed once under load and passes alone; neither is touched here, CI is the judge); tauri shellcargo check --locked --all-targetsandcargo test --locked(297 passed, 1 ignored).Not in this change: no attempt to fix relay. This makes the failure visible, which is the precondition for that.
🤖 Generated with Claude Code