@optimystic/db-p2p 0.24.1 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/{readme.md → README.md} +688 -668
- package/dist/src/cluster/block-transfer-service.d.ts +133 -10
- package/dist/src/cluster/block-transfer-service.d.ts.map +1 -1
- package/dist/src/cluster/block-transfer-service.js +155 -9
- package/dist/src/cluster/block-transfer-service.js.map +1 -1
- package/dist/src/cluster/block-transfer.d.ts +67 -7
- package/dist/src/cluster/block-transfer.d.ts.map +1 -1
- package/dist/src/cluster/block-transfer.js +127 -24
- package/dist/src/cluster/block-transfer.js.map +1 -1
- package/dist/src/cluster/certified-claims.d.ts +170 -0
- package/dist/src/cluster/certified-claims.d.ts.map +1 -0
- package/dist/src/cluster/certified-claims.js +200 -0
- package/dist/src/cluster/certified-claims.js.map +1 -0
- package/dist/src/cluster/client-signature-verifier.d.ts +23 -0
- package/dist/src/cluster/client-signature-verifier.d.ts.map +1 -0
- package/dist/src/cluster/client-signature-verifier.js +33 -0
- package/dist/src/cluster/client-signature-verifier.js.map +1 -0
- package/dist/src/cluster/cluster-policy.d.ts +9 -3
- package/dist/src/cluster/cluster-policy.d.ts.map +1 -1
- package/dist/src/cluster/cluster-policy.js +111 -27
- package/dist/src/cluster/cluster-policy.js.map +1 -1
- package/dist/src/cluster/cluster-repo.d.ts +140 -11
- package/dist/src/cluster/cluster-repo.d.ts.map +1 -1
- package/dist/src/cluster/cluster-repo.js +485 -29
- package/dist/src/cluster/cluster-repo.js.map +1 -1
- package/dist/src/cluster/commit-proof.d.ts +134 -0
- package/dist/src/cluster/commit-proof.d.ts.map +1 -0
- package/dist/src/cluster/commit-proof.js +263 -0
- package/dist/src/cluster/commit-proof.js.map +1 -0
- package/dist/src/cluster/quorum-restore.d.ts +95 -8
- package/dist/src/cluster/quorum-restore.d.ts.map +1 -1
- package/dist/src/cluster/quorum-restore.js +113 -14
- package/dist/src/cluster/quorum-restore.js.map +1 -1
- package/dist/src/cluster/rebalance-monitor.d.ts +84 -0
- package/dist/src/cluster/rebalance-monitor.d.ts.map +1 -1
- package/dist/src/cluster/rebalance-monitor.js +207 -5
- package/dist/src/cluster/rebalance-monitor.js.map +1 -1
- package/dist/src/cluster/reconcile-block.d.ts +27 -7
- package/dist/src/cluster/reconcile-block.d.ts.map +1 -1
- package/dist/src/cluster/reconcile-block.js +179 -38
- package/dist/src/cluster/reconcile-block.js.map +1 -1
- package/dist/src/cluster/service.d.ts +21 -4
- package/dist/src/cluster/service.d.ts.map +1 -1
- package/dist/src/cluster/service.js +28 -17
- package/dist/src/cluster/service.js.map +1 -1
- package/dist/src/cluster/spread-on-churn.d.ts +8 -2
- package/dist/src/cluster/spread-on-churn.d.ts.map +1 -1
- package/dist/src/cluster/spread-on-churn.js +7 -7
- package/dist/src/cluster/spread-on-churn.js.map +1 -1
- package/dist/src/cohort-topic/host.d.ts.map +1 -1
- package/dist/src/cohort-topic/host.js +8 -7
- package/dist/src/cohort-topic/host.js.map +1 -1
- package/dist/src/cohort-topic/stream-util.d.ts +7 -6
- package/dist/src/cohort-topic/stream-util.d.ts.map +1 -1
- package/dist/src/cohort-topic/stream-util.js +14 -41
- package/dist/src/cohort-topic/stream-util.js.map +1 -1
- package/dist/src/dispute/cascade.d.ts +7 -8
- package/dist/src/dispute/cascade.d.ts.map +1 -1
- package/dist/src/dispute/cascade.js +24 -7
- package/dist/src/dispute/cascade.js.map +1 -1
- package/dist/src/dispute/dispute-service.d.ts +5 -0
- package/dist/src/dispute/dispute-service.d.ts.map +1 -1
- package/dist/src/dispute/dispute-service.js +13 -3
- package/dist/src/dispute/dispute-service.js.map +1 -1
- package/dist/src/dispute/invalidation.d.ts +33 -15
- package/dist/src/dispute/invalidation.d.ts.map +1 -1
- package/dist/src/dispute/invalidation.js +127 -34
- package/dist/src/dispute/invalidation.js.map +1 -1
- package/dist/src/dispute/service.d.ts.map +1 -1
- package/dist/src/dispute/service.js +2 -1
- package/dist/src/dispute/service.js.map +1 -1
- package/dist/src/index.d.ts +7 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +7 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/libp2p-key-network.d.ts +78 -6
- package/dist/src/libp2p-key-network.d.ts.map +1 -1
- package/dist/src/libp2p-key-network.js +136 -57
- package/dist/src/libp2p-key-network.js.map +1 -1
- package/dist/src/libp2p-node-base.d.ts +38 -7
- package/dist/src/libp2p-node-base.d.ts.map +1 -1
- package/dist/src/libp2p-node-base.js +150 -62
- package/dist/src/libp2p-node-base.js.map +1 -1
- package/dist/src/logger.d.ts +2 -2
- package/dist/src/logger.js +2 -2
- package/dist/src/network/open-protocol-stream.d.ts +56 -0
- package/dist/src/network/open-protocol-stream.d.ts.map +1 -0
- package/dist/src/network/open-protocol-stream.js +60 -0
- package/dist/src/network/open-protocol-stream.js.map +1 -0
- package/dist/src/network/register-protocol-handler.d.ts +66 -0
- package/dist/src/network/register-protocol-handler.d.ts.map +1 -0
- package/dist/src/network/register-protocol-handler.js +51 -0
- package/dist/src/network/register-protocol-handler.js.map +1 -0
- package/dist/src/peer-address-book.d.ts +128 -1
- package/dist/src/peer-address-book.d.ts.map +1 -1
- package/dist/src/peer-address-book.js +198 -15
- package/dist/src/peer-address-book.js.map +1 -1
- package/dist/src/pend-validation.d.ts +65 -0
- package/dist/src/pend-validation.d.ts.map +1 -0
- package/dist/src/pend-validation.js +69 -0
- package/dist/src/pend-validation.js.map +1 -0
- package/dist/src/reactivity/notify-transport.d.ts.map +1 -1
- package/dist/src/reactivity/notify-transport.js +2 -1
- package/dist/src/reactivity/notify-transport.js.map +1 -1
- package/dist/src/reactivity/push-state-gossip.d.ts.map +1 -1
- package/dist/src/reactivity/push-state-gossip.js +2 -1
- package/dist/src/reactivity/push-state-gossip.js.map +1 -1
- package/dist/src/repo/cluster-coordinator.d.ts +32 -6
- package/dist/src/repo/cluster-coordinator.d.ts.map +1 -1
- package/dist/src/repo/cluster-coordinator.js +44 -10
- package/dist/src/repo/cluster-coordinator.js.map +1 -1
- package/dist/src/repo/coordinator-repo.d.ts +181 -25
- package/dist/src/repo/coordinator-repo.d.ts.map +1 -1
- package/dist/src/repo/coordinator-repo.js +650 -62
- package/dist/src/repo/coordinator-repo.js.map +1 -1
- package/dist/src/repo/served-repo-proxy.d.ts +30 -0
- package/dist/src/repo/served-repo-proxy.d.ts.map +1 -0
- package/dist/src/repo/served-repo-proxy.js +50 -0
- package/dist/src/repo/served-repo-proxy.js.map +1 -0
- package/dist/src/repo/service.d.ts +22 -1
- package/dist/src/repo/service.d.ts.map +1 -1
- package/dist/src/repo/service.js +25 -13
- package/dist/src/repo/service.js.map +1 -1
- package/dist/src/reputation/types.d.ts +3 -1
- package/dist/src/reputation/types.d.ts.map +1 -1
- package/dist/src/reputation/types.js +3 -1
- package/dist/src/reputation/types.js.map +1 -1
- package/dist/src/rn.d.ts +24 -2
- package/dist/src/rn.d.ts.map +1 -1
- package/dist/src/rn.js +33 -4
- package/dist/src/rn.js.map +1 -1
- package/dist/src/routing/libp2p-known-peers.d.ts.map +1 -1
- package/dist/src/routing/libp2p-known-peers.js +5 -0
- package/dist/src/routing/libp2p-known-peers.js.map +1 -1
- package/dist/src/storage/block-archive.d.ts +153 -0
- package/dist/src/storage/block-archive.d.ts.map +1 -0
- package/dist/src/storage/block-archive.js +194 -0
- package/dist/src/storage/block-archive.js.map +1 -0
- package/dist/src/storage/block-latch.d.ts +92 -0
- package/dist/src/storage/block-latch.d.ts.map +1 -0
- package/dist/src/storage/block-latch.js +132 -0
- package/dist/src/storage/block-latch.js.map +1 -0
- package/dist/src/storage/block-storage.d.ts +169 -12
- package/dist/src/storage/block-storage.d.ts.map +1 -1
- package/dist/src/storage/block-storage.js +415 -152
- package/dist/src/storage/block-storage.js.map +1 -1
- package/dist/src/storage/cached-raw-storage.d.ts +5 -1
- package/dist/src/storage/cached-raw-storage.d.ts.map +1 -1
- package/dist/src/storage/cached-raw-storage.js +16 -1
- package/dist/src/storage/cached-raw-storage.js.map +1 -1
- package/dist/src/storage/cached-store-driver.d.ts +14 -4
- package/dist/src/storage/cached-store-driver.d.ts.map +1 -1
- package/dist/src/storage/cached-store-driver.js +39 -5
- package/dist/src/storage/cached-store-driver.js.map +1 -1
- package/dist/src/storage/i-block-storage.d.ts +70 -19
- package/dist/src/storage/i-block-storage.d.ts.map +1 -1
- package/dist/src/storage/i-block-storage.js +21 -1
- package/dist/src/storage/i-block-storage.js.map +1 -1
- package/dist/src/storage/i-raw-storage.d.ts +38 -0
- package/dist/src/storage/i-raw-storage.d.ts.map +1 -1
- package/dist/src/storage/kv-raw-storage.d.ts +14 -8
- package/dist/src/storage/kv-raw-storage.d.ts.map +1 -1
- package/dist/src/storage/kv-raw-storage.js +29 -9
- package/dist/src/storage/kv-raw-storage.js.map +1 -1
- package/dist/src/storage/memory-store-driver.d.ts +4 -1
- package/dist/src/storage/memory-store-driver.d.ts.map +1 -1
- package/dist/src/storage/memory-store-driver.js +22 -2
- package/dist/src/storage/memory-store-driver.js.map +1 -1
- package/dist/src/storage/raw-store-codec.js +2 -2
- package/dist/src/storage/raw-store-driver.d.ts +44 -3
- package/dist/src/storage/raw-store-driver.d.ts.map +1 -1
- package/dist/src/storage/shared-cache-pool.d.ts +60 -3
- package/dist/src/storage/shared-cache-pool.d.ts.map +1 -1
- package/dist/src/storage/shared-cache-pool.js +68 -4
- package/dist/src/storage/shared-cache-pool.js.map +1 -1
- package/dist/src/storage/storage-repo.d.ts +204 -26
- package/dist/src/storage/storage-repo.d.ts.map +1 -1
- package/dist/src/storage/storage-repo.js +422 -119
- package/dist/src/storage/storage-repo.js.map +1 -1
- package/dist/src/storage/store-identity.d.ts +21 -0
- package/dist/src/storage/store-identity.d.ts.map +1 -0
- package/dist/src/storage/store-identity.js +33 -0
- package/dist/src/storage/store-identity.js.map +1 -0
- package/dist/src/storage/struct.d.ts +15 -0
- package/dist/src/storage/struct.d.ts.map +1 -1
- package/dist/src/storage/with-read-cache.d.ts +92 -0
- package/dist/src/storage/with-read-cache.d.ts.map +1 -0
- package/dist/src/storage/with-read-cache.js +132 -0
- package/dist/src/storage/with-read-cache.js.map +1 -0
- package/dist/src/sync/service.d.ts +13 -3
- package/dist/src/sync/service.d.ts.map +1 -1
- package/dist/src/sync/service.js +7 -30
- package/dist/src/sync/service.js.map +1 -1
- package/dist/src/testing/mesh-harness.d.ts +75 -2
- package/dist/src/testing/mesh-harness.d.ts.map +1 -1
- package/dist/src/testing/mesh-harness.js +193 -81
- package/dist/src/testing/mesh-harness.js.map +1 -1
- package/dist/src/testing/raw-storage-conformance.d.ts +8 -0
- package/dist/src/testing/raw-storage-conformance.d.ts.map +1 -1
- package/dist/src/testing/raw-storage-conformance.js +97 -2
- package/dist/src/testing/raw-storage-conformance.js.map +1 -1
- package/package.json +5 -6
- package/src/cluster/block-transfer-service.ts +528 -312
- package/src/cluster/block-transfer.ts +547 -424
- package/src/cluster/certified-claims.ts +319 -0
- package/src/cluster/client-signature-verifier.ts +34 -0
- package/src/cluster/cluster-policy.ts +294 -203
- package/src/cluster/cluster-repo.ts +2501 -2034
- package/src/cluster/commit-proof.ts +344 -0
- package/src/cluster/quorum-restore.ts +348 -223
- package/src/cluster/rebalance-monitor.ts +539 -254
- package/src/cluster/reconcile-block.ts +404 -203
- package/src/cluster/service.ts +308 -293
- package/src/cluster/spread-on-churn.ts +352 -346
- package/src/cohort-topic/host.ts +8 -7
- package/src/cohort-topic/stream-util.ts +14 -43
- package/src/dispute/cascade.ts +31 -15
- package/src/dispute/dispute-service.ts +706 -696
- package/src/dispute/invalidation.ts +162 -52
- package/src/dispute/service.ts +126 -125
- package/src/index.ts +7 -0
- package/src/libp2p-key-network.ts +142 -58
- package/src/libp2p-node-base.ts +1793 -1675
- package/src/logger.ts +27 -27
- package/src/network/open-protocol-stream.ts +87 -0
- package/src/network/register-protocol-handler.ts +85 -0
- package/src/peer-address-book.ts +360 -149
- package/src/pend-validation.ts +89 -0
- package/src/reactivity/notify-transport.ts +145 -144
- package/src/reactivity/push-state-gossip.ts +292 -291
- package/src/repo/cluster-coordinator.ts +65 -11
- package/src/repo/coordinator-repo.ts +729 -71
- package/src/repo/served-repo-proxy.ts +55 -0
- package/src/repo/service.ts +332 -313
- package/src/reputation/types.ts +3 -1
- package/src/rn.ts +34 -4
- package/src/routing/libp2p-known-peers.ts +31 -26
- package/src/storage/block-archive.ts +242 -0
- package/src/storage/block-latch.ts +144 -0
- package/src/storage/block-storage.ts +787 -498
- package/src/storage/cached-raw-storage.ts +21 -1
- package/src/storage/cached-store-driver.ts +898 -859
- package/src/storage/i-block-storage.ts +172 -116
- package/src/storage/i-raw-storage.ts +40 -0
- package/src/storage/kv-raw-storage.ts +34 -9
- package/src/storage/memory-store-driver.ts +26 -2
- package/src/storage/raw-store-codec.ts +42 -42
- package/src/storage/raw-store-driver.ts +45 -3
- package/src/storage/shared-cache-pool.ts +516 -452
- package/src/storage/storage-repo.ts +1421 -1014
- package/src/storage/store-identity.ts +41 -0
- package/src/storage/struct.ts +52 -34
- package/src/storage/with-read-cache.ts +182 -0
- package/src/sync/service.ts +178 -198
- package/src/testing/cohort-topic-mesh-harness.ts +673 -673
- package/src/testing/mesh-harness.ts +524 -344
- package/src/testing/raw-storage-conformance.ts +114 -2
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import type { AbortOptions,
|
|
1
|
+
import type { AbortOptions, Libp2p, PeerId, Stream } from "@libp2p/interface";
|
|
2
2
|
import { toString as u8ToString } from 'uint8arrays'
|
|
3
3
|
import type { ClusterPeers, CoordinatorIntent, FindCoordinatorOptions, IKeyNetwork, IPeerNetwork } from "@optimystic/db-core";
|
|
4
4
|
import { peerIdFromString } from '@libp2p/peer-id'
|
|
5
5
|
import type { FretService, SerializedTable } from 'p2p-fret'
|
|
6
6
|
import { hashKey } from 'p2p-fret'
|
|
7
7
|
import { createLogger, verbose } from './logger.js'
|
|
8
|
-
import { mergePeerAddresses,
|
|
8
|
+
import { classifySelfDialability, mergePeerAddresses, publishableConnectionAddr, unionPublishableAddrs, type AddressLog } from './peer-address-book.js'
|
|
9
9
|
import type { IPeerReputation } from './reputation/types.js'
|
|
10
|
+
import { openProtocolStream } from './network/open-protocol-stream.js'
|
|
10
11
|
|
|
11
12
|
interface WithFretService { services?: { fret?: FretService } }
|
|
12
13
|
|
|
@@ -70,6 +71,39 @@ export class FindCoordinatorError extends Error {
|
|
|
70
71
|
}
|
|
71
72
|
}
|
|
72
73
|
|
|
74
|
+
/**
|
|
75
|
+
* `.code` on {@link SelfRelayOnlyAddressesError}. A stable string so it survives the
|
|
76
|
+
* `ClusterErrorEnvelope` round trip (`toClusterErrorEnvelope` carries `name` and `code`) and shows
|
|
77
|
+
* up as `code=SELF_RELAY_ONLY_ADDRESSES` on `ProtocolClient`'s `dial:fail` line instead of `none`.
|
|
78
|
+
*/
|
|
79
|
+
export const SELF_RELAY_ONLY_ERROR_CODE = 'SELF_RELAY_ONLY_ADDRESSES';
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Thrown by {@link Libp2pKeyPeerNetwork.connect} when we hold addresses for a peer but EVERY one
|
|
83
|
+
* of them reaches it by relaying through this node.
|
|
84
|
+
*
|
|
85
|
+
* This is the steady state a relay reaches for its own reservation holders: the address such a
|
|
86
|
+
* client advertises is `/<our transport addr>/p2p/<our peer id>/p2p-circuit`, which is correct and
|
|
87
|
+
* useful to every node except us. Dialing it asks us to relay to the client through ourselves, so
|
|
88
|
+
* it can only fail — with an error text (`NoValidAddressesError`, or an `AggregateError` of
|
|
89
|
+
* `Can not dial self`, depending on whether the circuit transport is registered as a dialer here)
|
|
90
|
+
* that is indistinguishable from "nobody ever taught us an address". Retrying cannot help: once the
|
|
91
|
+
* client's connection drops, only the client can re-initiate. So we fail fast and distinctly,
|
|
92
|
+
* letting the caller's existing exclude-and-continue logic move to another cohort member instead of
|
|
93
|
+
* burning a dial timeout.
|
|
94
|
+
*/
|
|
95
|
+
export class SelfRelayOnlyAddressesError extends Error {
|
|
96
|
+
readonly code = SELF_RELAY_ONLY_ERROR_CODE;
|
|
97
|
+
constructor(peer: string, protocol: string, addrCount: number) {
|
|
98
|
+
super(
|
|
99
|
+
`Peer ${peer} is reachable only through a circuit on THIS node: all ${addrCount} address(es) ` +
|
|
100
|
+
`we hold route back through us, so no dial for ${protocol} can succeed. ` +
|
|
101
|
+
`Only that peer can re-establish the connection.`
|
|
102
|
+
);
|
|
103
|
+
this.name = 'SelfRelayOnlyAddressesError';
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
73
107
|
/** Snapshot format this build writes and is willing to read. Bump on any incompatible change. */
|
|
74
108
|
export const PERSISTED_STATE_VERSION = 2 as const;
|
|
75
109
|
|
|
@@ -248,6 +282,12 @@ export class Libp2pKeyPeerNetwork implements IKeyNetwork, IPeerNetwork {
|
|
|
248
282
|
private readonly coordinatorCache = new Map<string, { id: PeerId, expires: number }>()
|
|
249
283
|
private static readonly MAX_CACHE_ENTRIES = 1000
|
|
250
284
|
private readonly log: ReturnType<typeof createLogger>
|
|
285
|
+
/**
|
|
286
|
+
* This instance's logger, in the shape `peer-address-book.ts` accepts. Declared once so the
|
|
287
|
+
* five address predicates that take a sink all report under the same peer-id-suffixed
|
|
288
|
+
* namespace, rather than five separately-written adapters drifting apart.
|
|
289
|
+
*/
|
|
290
|
+
private readonly addressLog: AddressLog = (fmt, ...args) => this.log(fmt, ...args)
|
|
251
291
|
|
|
252
292
|
private toCacheKey(key: Uint8Array): string { return u8ToString(key, 'base64url') }
|
|
253
293
|
|
|
@@ -390,6 +430,15 @@ export class Libp2pKeyPeerNetwork implements IKeyNetwork, IPeerNetwork {
|
|
|
390
430
|
* nor banned by reputation. Shared by all three places `findCoordinator` narrows a candidate
|
|
391
431
|
* list — the FRET tier, the connected-peer fallback, and the retry-futility input — so the
|
|
392
432
|
* futility test can never disagree with the tiers about who is pickable.
|
|
433
|
+
*
|
|
434
|
+
* NOTE: eligibility here is deliberately blind to self-dialability, so on a relay
|
|
435
|
+
* `findCoordinator` can still pick one of our own reservation holders and only then fail fast
|
|
436
|
+
* with `SelfRelayOnlyAddressesError`. The caller's exclude-and-continue walk
|
|
437
|
+
* (`db-core/utility/batch-coordinator.ts`) absorbs it, and each such pick now costs an
|
|
438
|
+
* instant refusal rather than a burned dial timeout, so it is a selection round-trip, not a
|
|
439
|
+
* stall — and the verdict is a live peerStore read that a stale eligibility filter would have
|
|
440
|
+
* to guess at. If a relay serving many reservation holders is ever measured spending real time
|
|
441
|
+
* walking through them, feed the verdict into the tiers instead of discovering it at dial.
|
|
393
442
|
*/
|
|
394
443
|
private isSelectable(id: string, excluded: Set<string>): boolean {
|
|
395
444
|
return !excluded.has(id) && !(this.reputation?.isBanned(id));
|
|
@@ -532,7 +581,7 @@ export class Libp2pKeyPeerNetwork implements IKeyNetwork, IPeerNetwork {
|
|
|
532
581
|
* protocol client dials through.
|
|
533
582
|
*/
|
|
534
583
|
public recordPeerAddresses(peerId: PeerId, multiaddrs: string[]): void {
|
|
535
|
-
mergePeerAddresses(this.libp2p, peerId, multiaddrs,
|
|
584
|
+
mergePeerAddresses(this.libp2p, peerId, multiaddrs, this.addressLog)
|
|
536
585
|
}
|
|
537
586
|
|
|
538
587
|
private getCachedCoordinator(key: Uint8Array): PeerId | undefined {
|
|
@@ -544,49 +593,60 @@ export class Libp2pKeyPeerNetwork implements IKeyNetwork, IPeerNetwork {
|
|
|
544
593
|
}
|
|
545
594
|
|
|
546
595
|
/**
|
|
547
|
-
*
|
|
548
|
-
*
|
|
549
|
-
*
|
|
550
|
-
*
|
|
596
|
+
* Open a stream to `peerId` on `protocol` — reusing a live connection when we hold one, and
|
|
597
|
+
* otherwise dialing.
|
|
598
|
+
*
|
|
599
|
+
* Connection selection (prefer a direct connection over a resettable circuit-relay one, skip
|
|
600
|
+
* entries libp2p has not yet evicted, opt in to limited connections) lives in
|
|
601
|
+
* {@link openProtocolStream}, the single place in this package that opens a protocol stream.
|
|
602
|
+
*
|
|
603
|
+
* `negotiateFully: false` is safe here and saves a round trip: this is request/response and the
|
|
604
|
+
* caller always reads a reply, so an unsupported-protocol failure deferred to the first read is
|
|
605
|
+
* still observed. The caller's `AbortSignal` is forwarded so a per-peer dial deadline (enforced
|
|
606
|
+
* upstream by `ProtocolClient.processMessage`) can actually cancel a stuck dial — without it,
|
|
607
|
+
* libp2p falls back to its built-in connection-manager `dialTimeout` and the caller's tighter
|
|
608
|
+
* deadline is decorative.
|
|
609
|
+
*
|
|
610
|
+
* The cold path pays one `peerStore.get` before dialing, to separate two failures libp2p
|
|
611
|
+
* reports identically: "nobody ever taught us an address" and "every address we hold routes
|
|
612
|
+
* back through us" (see {@link SelfRelayOnlyAddressesError}). Only the second is diagnosed
|
|
613
|
+
* here; the first still dials, so an unknown peer produces libp2p's own `NoValidAddressesError`
|
|
614
|
+
* exactly as before. It rides `beforeDial`, which never runs on the reuse path — the warm path
|
|
615
|
+
* is deliberately kept clear of that read, since a live connection is the case this method
|
|
616
|
+
* exists to make cheap.
|
|
551
617
|
*/
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
618
|
+
async connect(peerId: PeerId, protocol: string, options?: AbortOptions): Promise<Stream> {
|
|
619
|
+
return await openProtocolStream(this.libp2p, peerId, protocol, {
|
|
620
|
+
signal: options?.signal,
|
|
621
|
+
negotiateFully: false,
|
|
622
|
+
beforeDial: () => this.assertNotSelfRelayOnly(peerId, protocol, options)
|
|
623
|
+
})
|
|
556
624
|
}
|
|
557
625
|
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
//
|
|
569
|
-
// the
|
|
570
|
-
//
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
})
|
|
583
|
-
}
|
|
584
|
-
// Forward the caller's AbortSignal so a per-peer dial deadline (enforced
|
|
585
|
-
// upstream by ProtocolClient.processMessage) can actually cancel a stuck
|
|
586
|
-
// dial — without this, libp2p falls back to its built-in dial timeout
|
|
587
|
-
// (default ~30s) and the caller's tighter deadline is decorative.
|
|
588
|
-
const dialOptions = { runOnLimitedConnection: true, negotiateFully: false, signal: options?.signal } as const
|
|
589
|
-
return this.libp2p.dialProtocol(peerId, [protocol], dialOptions)
|
|
626
|
+
/**
|
|
627
|
+
* Throw {@link SelfRelayOnlyAddressesError} when every address we hold for `peerId` routes
|
|
628
|
+
* through us, so the caller skips a dial that cannot succeed.
|
|
629
|
+
*
|
|
630
|
+
* Holding NOTHING is left alone on purpose — that dial still happens and still fails with
|
|
631
|
+
* libp2p's `NoValidAddressesError`, because "we were never told an address" is a genuinely
|
|
632
|
+
* different condition with a genuinely different remedy (someone teaches us one; see
|
|
633
|
+
* `recordPeerAddresses`).
|
|
634
|
+
*/
|
|
635
|
+
private async assertNotSelfRelayOnly(peerId: PeerId, protocol: string, options?: AbortOptions): Promise<void> {
|
|
636
|
+
// NOTE: this costs one `peerStore.get` on EVERY cold dial, not only on relays, and libp2p's
|
|
637
|
+
// own dial queue reads the same record moments later — so a cold dial pays the peerStore
|
|
638
|
+
// twice. Unmeasured, and negligible against a dial's own cost; if cold-dial latency or
|
|
639
|
+
// peerStore contention ever shows up in a profile, hoist the verdict into the dial path
|
|
640
|
+
// rather than reading ahead of it.
|
|
641
|
+
const idStr = peerId.toString()
|
|
642
|
+
const held = (await this.getPeerStoreAddrsByPeer([idStr]))[idStr] ?? []
|
|
643
|
+
// A caller that cancelled while we were reading the peerStore is owed ITS reason, not a
|
|
644
|
+
// verdict we computed from a snapshot it no longer cares about.
|
|
645
|
+
options?.signal?.throwIfAborted()
|
|
646
|
+
if (classifySelfDialability(held, this.libp2p.peerId.toString(), this.addressLog) !== 'self-relay-only') return
|
|
647
|
+
this.log('connect:self-relay-only peer=%s protocol=%s addrs=%d',
|
|
648
|
+
idStr.substring(0, 12), protocol, held.length)
|
|
649
|
+
throw new SelfRelayOnlyAddressesError(idStr, protocol, held.length)
|
|
590
650
|
}
|
|
591
651
|
|
|
592
652
|
private getFret(): FretService {
|
|
@@ -836,21 +896,27 @@ export class Libp2pKeyPeerNetwork implements IKeyNetwork, IPeerNetwork {
|
|
|
836
896
|
);
|
|
837
897
|
}
|
|
838
898
|
|
|
899
|
+
/**
|
|
900
|
+
* Connection-derived addresses for the cluster record, keyed by peer id.
|
|
901
|
+
*
|
|
902
|
+
* Only OUTBOUND connections contribute — see {@link publishableConnectionAddr} for why an
|
|
903
|
+
* inbound connection's `remoteAddr` is an ephemeral source socket that no third party can
|
|
904
|
+
* reach. This is the only place `findCluster` derives addresses from connections, so the
|
|
905
|
+
* rule has exactly one site.
|
|
906
|
+
*/
|
|
839
907
|
private getConnectedAddrsByPeer(): Record<string, string[]> {
|
|
840
908
|
const conns = this.libp2p.getConnections()
|
|
841
909
|
const byPeer: Record<string, string[]> = {}
|
|
842
910
|
for (const c of conns) {
|
|
911
|
+
const addr = publishableConnectionAddr(c, this.addressLog)
|
|
912
|
+
if (addr === undefined) continue
|
|
843
913
|
const id = c.remotePeer.toString()
|
|
844
|
-
const
|
|
845
|
-
|
|
914
|
+
const forPeer = byPeer[id] ??= []
|
|
915
|
+
forPeer.push(addr)
|
|
846
916
|
}
|
|
847
917
|
return byPeer
|
|
848
918
|
}
|
|
849
919
|
|
|
850
|
-
private parseMultiaddrs(addrs: string[]): string[] {
|
|
851
|
-
return validMultiaddrStrings(addrs, (fmt, ...args) => this.log(fmt, ...args))
|
|
852
|
-
}
|
|
853
|
-
|
|
854
920
|
async findCluster(key: Uint8Array): Promise<ClusterPeers> {
|
|
855
921
|
const t0 = Date.now();
|
|
856
922
|
const fret = this.getFret()
|
|
@@ -947,6 +1013,13 @@ export class Libp2pKeyPeerNetwork implements IKeyNetwork, IPeerNetwork {
|
|
|
947
1013
|
// reported symptom: clean membership logs on every node while every dial to the
|
|
948
1014
|
// addressless member died instantly and the write never completed.
|
|
949
1015
|
const addressless: string[] = []
|
|
1016
|
+
// The OTHER way a member can be undialable by us: we hold addresses, but every one of
|
|
1017
|
+
// them is a circuit through this node — the steady state for our own reservation holders.
|
|
1018
|
+
// Counted separately because the two have different remedies (be taught an address, vs.
|
|
1019
|
+
// wait for the client to re-dial us) and libp2p's dial error cannot tell them apart.
|
|
1020
|
+
// These addresses are still PUBLISHED: a cohort sibling reaching the member through our
|
|
1021
|
+
// relay is the working path, and dropping them would break it.
|
|
1022
|
+
const selfRelayOnly: string[] = []
|
|
950
1023
|
|
|
951
1024
|
for (const idStr of ids) {
|
|
952
1025
|
if (idStr === selfId) {
|
|
@@ -956,12 +1029,13 @@ export class Libp2pKeyPeerNetwork implements IKeyNetwork, IPeerNetwork {
|
|
|
956
1029
|
}
|
|
957
1030
|
const connectedStrings = connectedByPeer[idStr] ?? []
|
|
958
1031
|
const peerStoreStrings = peerStoreAddrs[idStr] ?? []
|
|
959
|
-
//
|
|
960
|
-
//
|
|
961
|
-
//
|
|
962
|
-
//
|
|
963
|
-
|
|
964
|
-
|
|
1032
|
+
// The union rule — connected-first, de-duplicated, validated — lives in
|
|
1033
|
+
// `unionPublishableAddrs` so a cluster record and a redirect payload cannot describe
|
|
1034
|
+
// the same peer differently. The sync form is used here (rather than
|
|
1035
|
+
// `publishableAddrsForPeer`) because both halves are already in hand: the peerStore
|
|
1036
|
+
// read above is batched across the whole cohort, and on the membership-scoped path it
|
|
1037
|
+
// is the same `store.get` that fetched protocols.
|
|
1038
|
+
const parsed = unionPublishableAddrs(connectedStrings, peerStoreStrings, this.addressLog)
|
|
965
1039
|
const remotePeerId = peerIdFromString(idStr)
|
|
966
1040
|
const raw = remotePeerId.publicKey?.raw ?? new Uint8Array()
|
|
967
1041
|
// Note: parsed may be empty for a cohort member we have neither a
|
|
@@ -970,7 +1044,9 @@ export class Libp2pKeyPeerNetwork implements IKeyNetwork, IPeerNetwork {
|
|
|
970
1044
|
// retry/exclude logic takes over — we intentionally do NOT drop
|
|
971
1045
|
// addressless members here, because shrinking the cohort below
|
|
972
1046
|
// `clusterSize` puts consensus supermajority out of reach.
|
|
973
|
-
|
|
1047
|
+
const dialability = classifySelfDialability(parsed, selfId, this.addressLog)
|
|
1048
|
+
if (dialability === 'none') addressless.push(idStr.substring(0, 12))
|
|
1049
|
+
else if (dialability === 'self-relay-only') selfRelayOnly.push(idStr.substring(0, 12))
|
|
974
1050
|
peers[idStr] = { multiaddrs: parsed, publicKey: u8ToString(raw, 'base64url') }
|
|
975
1051
|
}
|
|
976
1052
|
|
|
@@ -983,8 +1059,16 @@ export class Libp2pKeyPeerNetwork implements IKeyNetwork, IPeerNetwork {
|
|
|
983
1059
|
keyStr, addressless.length, Object.keys(peers).length, addressless)
|
|
984
1060
|
}
|
|
985
1061
|
|
|
986
|
-
|
|
987
|
-
|
|
1062
|
+
// Same reasoning, second condition: a non-zero count here means we DO hold addresses for
|
|
1063
|
+
// these members and still cannot dial them, because the only route we know runs through
|
|
1064
|
+
// our own relay. No amount of retrying changes that (see `SelfRelayOnlyAddressesError`).
|
|
1065
|
+
if (selfRelayOnly.length > 0) {
|
|
1066
|
+
this.log('findCluster:self-relay-only-members key=%s count=%d of=%d peers=%o',
|
|
1067
|
+
keyStr, selfRelayOnly.length, Object.keys(peers).length, selfRelayOnly)
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
this.log('findCluster:done key=%s ms=%d peers=%d addressless=%d selfRelayOnly=%d',
|
|
1071
|
+
keyStr, Date.now() - t0, Object.keys(peers).length, addressless.length, selfRelayOnly.length)
|
|
988
1072
|
return peers
|
|
989
1073
|
}
|
|
990
1074
|
|