Skip to content

fix(node): reject sendTx when node has no peers to propagate the tx - #25182

Closed
spalladino wants to merge 2 commits into
spl/status-p2p-healthfrom
spl/sendtx-reject-no-peers
Closed

fix(node): reject sendTx when node has no peers to propagate the tx#25182
spalladino wants to merge 2 commits into
spl/status-p2p-healthfrom
spl/sendtx-reject-no-peers

Conversation

@spalladino

@spalladino spalladino commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

A node whose p2p stack is up but has zero connected peers used to accept txs
over RPC and "gossip" them to nobody: the tx sat in the local pool forever
while the caller believed it had been submitted. The failure was silent — no
error, no receipt, nothing actionable.

#sendTx now checks p2p connectivity before doing any work and rejects with a
clear error when p2p is enabled and no peers are connected, so callers fail
immediately and can retry against a healthy node. The check runs ahead of
isValidTx so a peerless node does not pay for full tx validation before
rejecting, and the rejection is counted in the received-tx metric like the
other failure paths.

Nodes running without a p2p stack (sandbox, single-node setups) report
enabled: false and are unaffected — they keep accepting txs, since their
local sequencer mines straight from the local pool.

Also includes a test-infra commit: registerPhantomGossipPeer in the p2p test helpers, so the one single-node e2e test that runs on the in-memory mock gossip bus (missed_l1_slot) presents one connected peer to the connectivity gates instead of looking peerless.

Part of A-1701.


Part of a stacked-PR chain (bottom → top) hardening the node against running with a dead p2p stack; each PR targets the branch below it and the bottom targets merge-train/spartan-v5:

  1. fix(p2p): fail node startup when p2p service fails to start #25177 — fail node startup when the p2p service fails to start
  2. feat(p2p): expose p2p connectivity (enabled + connected peer count) #25178 — expose p2p connectivity (enabled + connected peer count)
  3. fix(slasher): do not file data-withholding offenses while peerless #25179 — do not file data-withholding offenses while peerless
  4. fix(sequencer): skip proposing when node has no connected peers #25180 — skip proposing when the node has no connected peers
  5. feat(node): report per-component health with p2p peer count on GET /status #25181 — report per-component health with p2p peer count on GET /status
  6. fix(node): reject sendTx when node has no peers to propagate the tx #25182 — reject sendTx when the node has no peers to propagate the tx
  7. feat(p2p): warn periodically while node has zero connected peers #25183 — warn periodically while the node has zero connected peers

A node whose p2p stack is up but has zero connected peers used to accept txs
over RPC and "gossip" them to nobody: the tx sat in the local pool forever
while the caller believed it had been submitted. The failure was silent — no
error, no receipt, nothing actionable.

`#sendTx` now checks p2p connectivity before doing any work and rejects with a
clear error when p2p is enabled and no peers are connected, so callers fail
immediately and can retry against a healthy node. The check runs ahead of
`isValidTx` so a peerless node does not pay for full tx validation before
rejecting, and the rejection is counted in the received-tx metric like the
other failure paths.

Nodes running without a p2p stack (sandbox, single-node setups) report
`enabled: false` and are unaffected — they keep accepting txs, since their
local sequencer mines straight from the local pool.

Part of A-1701.
`single-node/misc/missed_l1_slot` is the only test that runs a single node on the
in-memory mock gossip bus (it needs the bus so the proposer's own broadcasts route
through the local proposal handler). With one member on the bus, `DummyPeerManager`
filters the node itself out and reports zero peers, so the node ends up looking
peerless to every connectivity gate: `sendTx` now rejects because there is nobody
to propagate to, and a proposer under `minPeersToPropose: 1` skips its slot.

Adds `registerPhantomGossipPeer` to the p2p test helpers: it registers a bare
`MockGossipSubService` on the network and returns its peer id. The phantom peer
subscribes to no topics and drops anything delivered to it, so it changes nothing
about message flow — it only makes the node count one connected peer. The test
registers one right after setup.

Test-infra only; the production gates are unchanged.

Part of A-1701.
@spalladino

Copy link
Copy Markdown
Contributor Author

Superseded by #25185, which groups this stack's dependent layers (connectivity signal + slasher/proposer/health/sendTx gates) into one PR with a commit per concern, targeting merge-train/spartan-v5 directly. The independent fixes remain as #25177 and #25183.

@spalladino spalladino closed this Aug 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant