Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 30 additions & 5 deletions src/main/p2p/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ export const INV_TYPE_NAMES: Record<number, string> = {
export const FORWARDED_EVENTS: Array<keyof PoolServiceEventMap> = [
'peerconnect', 'peerready', 'peerdisconnect', 'peerversion',
'peerheaders', 'peerinv', 'peerblock', 'peeraddr', 'peertx',
// Not for subscribers — nothing reads them — but because a pool with few
// peers on a quiet chain hears nothing else for minutes at a time.
'peerping', 'peerpong',
'peercfcheckpt', 'peercfheaders', 'peercfilter',
'peerislock', 'peerisdlock', 'peerclsig',
'seederror',
Expand Down Expand Up @@ -77,7 +80,7 @@ export const POOL_CONNECT_HEADROOM = 8

// Matches dash-core-p2p's internal default; higher makes initial sync slow to
// find +CF peers.
export const POOL_REFILL_INTERVAL_MS = 5_000
export const POOL_REFILL_INTERVAL_MS = 2_500

// Refill ticks between reports while a pool is under its minimum. Past
// POOL_FILL_STALL_LIMIT the refill branch goes quiet, so without this an empty
Expand All @@ -102,11 +105,26 @@ export const POOL_FALLBACK_TICKS = 2
// holds its slot until the process restarts. This hands the OS the job.
export const PEER_KEEPALIVE_DELAY_MS = 60_000

// Total silence across every peer in a pool, after which they are assumed dead
// with the link rather than merely quiet. Peers gossip inv continuously, so this
// only trips when the path to all of them broke at once.
// Delay from a completed handshake to a peer's first ping. Long enough to be
// clear of the burst of version/verack/addr traffic that arrives with it —
// measuring inside that burst times our own reader — and short enough that a
// peer has a latency about as soon as it has a user agent.
export const POOL_PING_FIRST_MS = 1_000

// How often every ready peer is pinged. Under the silence timeout by design: a
// quiet peer answering these never reaches it.
export const POOL_PING_INTERVAL_MS = 30_000

// Total silence across every peer in a pool, after which the pool stops assuming
// they are merely quiet and asks. A pool of one or two peers on a quiet chain
// reaches this routinely, so it opens a probe rather than a redial.
export const POOL_SILENCE_TIMEOUT_MS = 90_000

// Grace for the pong that probe asks for. dash-core-p2p answers a ping but never
// sends one, so this is the only round trip that can tell a dead link from a
// quiet one; a peer that lets it lapse is dropped.
export const POOL_SILENCE_PROBE_MS = 10_000

// Dialled when a pool has produced no live peer at all: a resolver that cannot
// answer the single mainnet DNS seed — or answers it with rewritten records —
// and the bulk pool, which runs no DNS of its own and lives off the lock pool's
Expand Down Expand Up @@ -148,6 +166,13 @@ export const FALLBACK_PEERS: Record<Network, string[]> = {
],
}

export const DEFAULT_PEER_PORT: Record<Network, number> = {mainnet: 9999, testnet: 19999}

// How long a probe dial waits for the version handshake. Node retries SYN for
// minutes before reporting a connect failure, so nothing else bounds a dial to
// a host that is simply not there.
export const PEER_PROBE_TIMEOUT_MS = 8_000

// ── Header sync ─────────────────────────────────────────────────────────────

// A latency hedge, not a throughput knob: the response that counts is the first
Expand Down Expand Up @@ -190,7 +215,7 @@ export const MAX_INFLIGHT_BATCHES = 10
// cfheaders chunks requested at once. The walk is a round trip per 1000 blocks
// and nothing else — neither CPU nor bandwidth is near its limit while one is
// outstanding — so this is what decides how long the phase takes.
export const MAX_INFLIGHT_CFHEADERS = 10
export const MAX_INFLIGHT_CFHEADERS = 20

// Peers asked for a given cfilter batch. Unlike the cf* races below, one request
// draws CFILTER_BATCH separate cfilter messages back per peer, so every peer past
Expand Down
13 changes: 13 additions & 0 deletions src/main/p2p/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import {probePeer} from './net/peerProbe'
import {SyncService} from './sync/SyncService'
import {P2PCommand, P2PEvent} from './types/messages'
import {MB} from './constants'
Expand Down Expand Up @@ -86,6 +87,18 @@ process.parentPort.on('message', ({data}) => {
case 'reseedUtxos':
sync.reseedUtxos(data)
return
case 'banPeers':
sync.setBannedPeers(data.banned)
return
case 'getConnectedPeers':
process.parentPort.postMessage({type: 'peers', requestId: data.requestId, peers: sync.getConnectedPeers()})
return
// Straight to the dialler: a probe touches no pool and no chain state, so
// routing it through SyncService would only rename the call.
case 'probePeer':
probePeer(data.peer, data.network).then(result =>
process.parentPort.postMessage({type: 'peerProbe', requestId: data.requestId, result}))
return
}
})

Expand Down
6 changes: 5 additions & 1 deletion src/main/p2p/net/BroadcastService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,11 @@ export class BroadcastService {
succeed()
return
}
if (spread()) succeed()
// A pinned peer set is normally too small to hold a witness back, and
// Core never relays a tx toward the peer that announced it — so nothing
// short of a lock can ever prove propagation here. Delivery is then the
// only positive evidence there is.
if (spread() || (this.peerPool.pinnedOnly && witnesses.size === 0 && delivered().size > 0)) succeed()
}

const armUnsolicited = (peer: Peer): void => {
Expand Down
Loading
Loading