@optimystic/db-p2p 0.21.0 → 0.24.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/dist/src/cluster/client.d.ts +10 -0
- package/dist/src/cluster/client.d.ts.map +1 -1
- package/dist/src/cluster/client.js +30 -1
- package/dist/src/cluster/client.js.map +1 -1
- package/dist/src/cluster/cluster-policy.d.ts +13 -2
- package/dist/src/cluster/cluster-policy.d.ts.map +1 -1
- package/dist/src/cluster/cluster-policy.js +51 -4
- package/dist/src/cluster/cluster-policy.js.map +1 -1
- package/dist/src/cluster/cluster-repo.d.ts +42 -17
- package/dist/src/cluster/cluster-repo.d.ts.map +1 -1
- package/dist/src/cluster/cluster-repo.js +229 -122
- package/dist/src/cluster/cluster-repo.js.map +1 -1
- package/dist/src/cluster/cluster-size-coupling.d.ts +28 -0
- package/dist/src/cluster/cluster-size-coupling.d.ts.map +1 -0
- package/dist/src/cluster/cluster-size-coupling.js +35 -0
- package/dist/src/cluster/cluster-size-coupling.js.map +1 -0
- package/dist/src/cluster/quorum-restore.d.ts +6 -0
- package/dist/src/cluster/quorum-restore.d.ts.map +1 -1
- package/dist/src/cluster/quorum-restore.js +1 -1
- package/dist/src/cluster/quorum-restore.js.map +1 -1
- package/dist/src/cluster/reconcile-block.d.ts.map +1 -1
- package/dist/src/cluster/reconcile-block.js +15 -3
- package/dist/src/cluster/reconcile-block.js.map +1 -1
- package/dist/src/cluster/service.d.ts +32 -1
- package/dist/src/cluster/service.d.ts.map +1 -1
- package/dist/src/cluster/service.js +43 -2
- package/dist/src/cluster/service.js.map +1 -1
- package/dist/src/cohort-topic/stream-util.d.ts +22 -6
- package/dist/src/cohort-topic/stream-util.d.ts.map +1 -1
- package/dist/src/cohort-topic/stream-util.js +56 -10
- package/dist/src/cohort-topic/stream-util.js.map +1 -1
- package/dist/src/dispute/dispute-service.d.ts.map +1 -1
- package/dist/src/dispute/dispute-service.js +9 -3
- package/dist/src/dispute/dispute-service.js.map +1 -1
- package/dist/src/index.d.ts +5 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +5 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/libp2p-key-network.d.ts +134 -7
- package/dist/src/libp2p-key-network.d.ts.map +1 -1
- package/dist/src/libp2p-key-network.js +174 -37
- package/dist/src/libp2p-key-network.js.map +1 -1
- package/dist/src/libp2p-node-base.d.ts +3 -2
- package/dist/src/libp2p-node-base.d.ts.map +1 -1
- package/dist/src/libp2p-node-base.js +859 -778
- package/dist/src/libp2p-node-base.js.map +1 -1
- package/dist/src/libp2p-node-rn.d.ts +2 -2
- package/dist/src/libp2p-node-rn.d.ts.map +1 -1
- package/dist/src/libp2p-node-rn.js.map +1 -1
- package/dist/src/libp2p-node.d.ts +2 -2
- package/dist/src/libp2p-node.d.ts.map +1 -1
- package/dist/src/libp2p-node.js.map +1 -1
- package/dist/src/logger.d.ts +17 -1
- package/dist/src/logger.d.ts.map +1 -1
- package/dist/src/logger.js +19 -2
- package/dist/src/logger.js.map +1 -1
- package/dist/src/network/network-manager-service.d.ts +2 -0
- package/dist/src/network/network-manager-service.d.ts.map +1 -1
- package/dist/src/network/network-manager-service.js +4 -0
- package/dist/src/network/network-manager-service.js.map +1 -1
- package/dist/src/optimystic-node.d.ts +35 -0
- package/dist/src/optimystic-node.d.ts.map +1 -0
- package/dist/src/optimystic-node.js +2 -0
- package/dist/src/optimystic-node.js.map +1 -0
- package/dist/src/owned-block-seed.d.ts +6 -3
- package/dist/src/owned-block-seed.d.ts.map +1 -1
- package/dist/src/owned-block-seed.js +16 -3
- package/dist/src/owned-block-seed.js.map +1 -1
- package/dist/src/peer-address-book.d.ts +72 -0
- package/dist/src/peer-address-book.d.ts.map +1 -0
- package/dist/src/peer-address-book.js +123 -0
- package/dist/src/peer-address-book.js.map +1 -0
- package/dist/src/repo/client.d.ts.map +1 -1
- package/dist/src/repo/client.js +11 -2
- package/dist/src/repo/client.js.map +1 -1
- package/dist/src/repo/cluster-coordinator.d.ts +30 -0
- package/dist/src/repo/cluster-coordinator.d.ts.map +1 -1
- package/dist/src/repo/cluster-coordinator.js +95 -3
- package/dist/src/repo/cluster-coordinator.js.map +1 -1
- package/dist/src/repo/coordinator-repo.d.ts +78 -14
- package/dist/src/repo/coordinator-repo.d.ts.map +1 -1
- package/dist/src/repo/coordinator-repo.js +266 -81
- package/dist/src/repo/coordinator-repo.js.map +1 -1
- package/dist/src/rn.d.ts +5 -0
- package/dist/src/rn.d.ts.map +1 -1
- package/dist/src/rn.js +5 -0
- package/dist/src/rn.js.map +1 -1
- package/dist/src/storage/block-storage.d.ts.map +1 -1
- package/dist/src/storage/block-storage.js +57 -5
- package/dist/src/storage/block-storage.js.map +1 -1
- package/dist/src/storage/cached-raw-storage.d.ts +83 -0
- package/dist/src/storage/cached-raw-storage.d.ts.map +1 -0
- package/dist/src/storage/cached-raw-storage.js +152 -0
- package/dist/src/storage/cached-raw-storage.js.map +1 -0
- package/dist/src/storage/cached-store-driver.d.ts +186 -0
- package/dist/src/storage/cached-store-driver.d.ts.map +1 -0
- package/dist/src/storage/cached-store-driver.js +775 -0
- package/dist/src/storage/cached-store-driver.js.map +1 -0
- package/dist/src/storage/i-block-storage.d.ts +20 -1
- package/dist/src/storage/i-block-storage.d.ts.map +1 -1
- package/dist/src/storage/i-raw-storage.d.ts +12 -5
- package/dist/src/storage/i-raw-storage.d.ts.map +1 -1
- package/dist/src/storage/shared-cache-pool.d.ts +234 -0
- package/dist/src/storage/shared-cache-pool.d.ts.map +1 -0
- package/dist/src/storage/shared-cache-pool.js +354 -0
- package/dist/src/storage/shared-cache-pool.js.map +1 -0
- package/dist/src/storage/storage-repo.d.ts +56 -3
- package/dist/src/storage/storage-repo.d.ts.map +1 -1
- package/dist/src/storage/storage-repo.js +124 -18
- package/dist/src/storage/storage-repo.js.map +1 -1
- package/dist/src/testing/raw-storage-conformance.d.ts +2 -1
- package/dist/src/testing/raw-storage-conformance.d.ts.map +1 -1
- package/dist/src/testing/raw-storage-conformance.js +52 -2
- package/dist/src/testing/raw-storage-conformance.js.map +1 -1
- package/package.json +3 -3
- package/readme.md +668 -653
- package/src/cluster/block-transfer.ts +424 -424
- package/src/cluster/client.ts +119 -88
- package/src/cluster/cluster-error.ts +64 -64
- package/src/cluster/cluster-policy.ts +203 -152
- package/src/cluster/cluster-repo.ts +245 -125
- package/src/cluster/cluster-size-coupling.ts +45 -0
- package/src/cluster/commit-cert.ts +139 -139
- package/src/cluster/i-transaction-state-store.ts +43 -43
- package/src/cluster/memory-transaction-state-store.ts +56 -56
- package/src/cluster/peer-key-binding.ts +37 -37
- package/src/cluster/persistent-transaction-state-store.ts +92 -92
- package/src/cluster/quorum-restore.ts +223 -223
- package/src/cluster/reconcile-block.ts +203 -191
- package/src/cluster/service.ts +293 -241
- package/src/cluster/supermajority-coupling.ts +37 -37
- package/src/cohort-topic/bootstrap-evidence-builder.ts +122 -122
- package/src/cohort-topic/bootstrap-evidence-verifiers.ts +132 -132
- package/src/cohort-topic/bootstrap-parent-reference.ts +159 -159
- package/src/cohort-topic/change-bridge.ts +109 -109
- package/src/cohort-topic/cohort-gossip-driver.ts +231 -231
- package/src/cohort-topic/cohort-gossip-transport.ts +84 -84
- package/src/cohort-topic/fret-trust-anchor.ts +153 -153
- package/src/cohort-topic/host.ts +2901 -2901
- package/src/cohort-topic/index.ts +13 -13
- package/src/cohort-topic/membership-publish-sink.ts +20 -20
- package/src/cohort-topic/membership-source.ts +68 -68
- package/src/cohort-topic/peer-codec.ts +31 -31
- package/src/cohort-topic/peer-sig.ts +86 -86
- package/src/cohort-topic/protocols.ts +71 -71
- package/src/cohort-topic/reactivity-membership-gate.ts +77 -77
- package/src/cohort-topic/size-estimator.ts +16 -16
- package/src/cohort-topic/stream-util.ts +135 -87
- package/src/cohort-topic/threshold-crypto.ts +239 -239
- package/src/cohort-topic/topic-router.ts +77 -77
- package/src/dispute/arbitrator-selection.ts +138 -138
- package/src/dispute/cascade.ts +524 -524
- package/src/dispute/dispute-service.ts +11 -5
- package/src/dispute/invalidation.ts +625 -625
- package/src/inbound-authorization.ts +190 -190
- package/src/index.ts +52 -47
- package/src/libp2p-key-network.ts +1120 -958
- package/src/libp2p-node-base.ts +1675 -1591
- package/src/libp2p-node-rn.ts +30 -30
- package/src/libp2p-node.ts +36 -36
- package/src/logger.ts +19 -2
- package/src/matchmaking/aggregate-counts.ts +104 -104
- package/src/matchmaking/index.ts +20 -20
- package/src/matchmaking/module.ts +363 -363
- package/src/matchmaking/protocols.ts +51 -51
- package/src/matchmaking/provider-manager.ts +95 -95
- package/src/matchmaking/query-handler.ts +88 -88
- package/src/matchmaking/query-transport.ts +492 -492
- package/src/matchmaking/seeker-manager.ts +64 -64
- package/src/matchmaking/seeker-walk-client.ts +293 -293
- package/src/matchmaking/traffic-validation.ts +195 -195
- package/src/network/network-manager-service.ts +5 -0
- package/src/optimystic-node.ts +36 -0
- package/src/owned-block-seed.ts +53 -40
- package/src/peer-address-book.ts +149 -0
- package/src/protocol-limits.ts +33 -33
- package/src/reactivity/forwarder-host.ts +438 -438
- package/src/reactivity/index.ts +19 -19
- package/src/reactivity/notify-transport.ts +144 -144
- package/src/reactivity/origination-manager.ts +192 -192
- package/src/reactivity/protocols.ts +61 -61
- package/src/reactivity/push-state-gossip.ts +291 -291
- package/src/reactivity/recover-transport.ts +408 -408
- package/src/reactivity/rotation-rereg-scheduler.ts +256 -256
- package/src/reactivity/subscriber-registry.ts +96 -96
- package/src/reactivity/subscription-manager.ts +450 -450
- package/src/reactivity/topic-bytes.ts +37 -37
- package/src/repo/client.ts +12 -2
- package/src/repo/cluster-coordinator.ts +99 -3
- package/src/repo/coordinator-repo.ts +305 -82
- package/src/repo/types.ts +7 -7
- package/src/rn.ts +39 -34
- package/src/rpc-deadline.ts +45 -45
- package/src/storage/arachnode-partition.ts +74 -74
- package/src/storage/block-storage.ts +59 -6
- package/src/storage/cached-raw-storage.ts +180 -0
- package/src/storage/cached-store-driver.ts +859 -0
- package/src/storage/i-block-storage.ts +20 -1
- package/src/storage/i-kv-store.ts +8 -8
- package/src/storage/i-raw-storage.ts +12 -5
- package/src/storage/kv-raw-storage.ts +135 -135
- package/src/storage/memory-kv-store.ts +28 -28
- package/src/storage/memory-storage.ts +25 -25
- package/src/storage/memory-store-driver.ts +157 -157
- package/src/storage/raw-store-codec.ts +42 -42
- package/src/storage/raw-store-driver.ts +80 -80
- package/src/storage/ring-selector.ts +317 -317
- package/src/storage/ring-shift-coordinator.ts +271 -271
- package/src/storage/shared-cache-pool.ts +452 -0
- package/src/storage/storage-repo.ts +1014 -903
- package/src/testing/cohort-topic-mesh-harness.ts +663 -663
- package/src/testing/index.ts +8 -8
- package/src/testing/matchmaking-mesh-harness.ts +475 -475
- package/src/testing/raw-storage-conformance.ts +453 -397
- package/src/testing/reactivity-mesh-harness.ts +922 -922
- package/dist/src/storage/restoration-coordinator-v2.d.ts +0 -67
- package/dist/src/storage/restoration-coordinator-v2.d.ts.map +0 -1
- package/dist/src/storage/restoration-coordinator-v2.js +0 -172
- package/dist/src/storage/restoration-coordinator-v2.js.map +0 -1
|
@@ -1,77 +1,77 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Reactivity origination membership gate (`docs/reactivity.md` §Anchor, `docs/internals.md`
|
|
3
|
-
* §Cohort-Topic Origination Bridge).
|
|
4
|
-
*
|
|
5
|
-
* Builds the `selfIsCohortMember(event)` predicate the change-notifier bridge gates each member commit
|
|
6
|
-
* on: this node is responsible for a collection's reactivity-topic fan-out iff it sits in the FRET
|
|
7
|
-
* cohort around `coord_0(H(currentTailId ‖ "reactivity"))`. The tail comes from `event.tailId` (the seam
|
|
8
|
-
* ticket put the committed `CommitRequest.tailId` on the change event), so the decision is synchronous —
|
|
9
|
-
* no async storage read, no per-collection registry. A tail-less event (a read-driven promotion) is
|
|
10
|
-
* never a member; those never originate and are cert-gated out downstream anyway.
|
|
11
|
-
*
|
|
12
|
-
* Extracted from the node assembly so the encoding + coord derivation are unit-testable in isolation and
|
|
13
|
-
* reusable by the future reactivity origination wiring.
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import {
|
|
17
|
-
createReactivityTopicAnchor,
|
|
18
|
-
createTierAddressing,
|
|
19
|
-
createRingHash,
|
|
20
|
-
type ReactivityTopicAnchor,
|
|
21
|
-
type TierAddressing,
|
|
22
|
-
type CollectionChangeEvent,
|
|
23
|
-
} from "@optimystic/db-core";
|
|
24
|
-
import type { FretService } from "p2p-fret";
|
|
25
|
-
|
|
26
|
-
// The pinned `BlockId` → raw tail-bytes encoding now lives in the reactivity surface so the origination
|
|
27
|
-
// gate (here) and the subscriber-side subscribe factory import the SAME function — see `reactivity/
|
|
28
|
-
// topic-bytes.ts` for the load-bearing rationale. Re-exported so this module's existing callers (tests,
|
|
29
|
-
// node wiring) keep importing `reactivityTailBytes` from the gate.
|
|
30
|
-
export { reactivityTailBytes } from "../reactivity/topic-bytes.js";
|
|
31
|
-
import { reactivityTailBytes } from "../reactivity/topic-bytes.js";
|
|
32
|
-
|
|
33
|
-
/** Construction inputs for {@link createReactivitySelfMembershipGate}. */
|
|
34
|
-
export interface ReactivitySelfMembershipGateDeps {
|
|
35
|
-
/** FRET membership read — the cohort assembler around a served coord (returns peer-id strings). */
|
|
36
|
-
readonly fret: Pick<FretService, "assembleCohort">;
|
|
37
|
-
/** This node's peer-id string, compared against the assembled cohort. */
|
|
38
|
-
readonly selfPeerId: string;
|
|
39
|
-
/**
|
|
40
|
-
* Requested cohort size `wantK`. MUST equal the cohort-topic host's `wantK`, so the gate checks the
|
|
41
|
-
* exact cohort the host serves; a mismatch silently breaks origination.
|
|
42
|
-
*/
|
|
43
|
-
readonly wantK: number;
|
|
44
|
-
/**
|
|
45
|
-
* Reactivity topic anchor. Default db-core's {@link createReactivityTopicAnchor} (256-bit SHA-256),
|
|
46
|
-
* byte-identical to the host's internal `new RingHash()` and the subscriber-side anchor.
|
|
47
|
-
*/
|
|
48
|
-
readonly anchor?: ReactivityTopicAnchor;
|
|
49
|
-
/**
|
|
50
|
-
* Tier addressing. Default `createTierAddressing(createRingHash())`. `coord_0` is fan-out independent
|
|
51
|
-
* (`H(0x00 ‖ topicId)`), so the default fan-out is irrelevant to the tier-0 reactivity coord.
|
|
52
|
-
*/
|
|
53
|
-
readonly addressing?: TierAddressing;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Build the synchronous `selfIsCohortMember(event)` gate for {@link attachCohortChangeBridge}.
|
|
58
|
-
*
|
|
59
|
-
* Returns `false` for a tail-less event; otherwise resolves `coord_0(H(event.tailId ‖ "reactivity"))`
|
|
60
|
-
* via the shared anchor + tier addressing and returns whether `fret.assembleCohort(coord, wantK)`
|
|
61
|
-
* includes this node's peer id. Pure aside from the live FRET read; membership churn between commits is
|
|
62
|
-
* fine because reactivity is hint-only.
|
|
63
|
-
*/
|
|
64
|
-
export function createReactivitySelfMembershipGate(
|
|
65
|
-
deps: ReactivitySelfMembershipGateDeps,
|
|
66
|
-
): (event: CollectionChangeEvent) => boolean {
|
|
67
|
-
const anchor = deps.anchor ?? createReactivityTopicAnchor();
|
|
68
|
-
const addressing = deps.addressing ?? createTierAddressing(createRingHash());
|
|
69
|
-
return (event: CollectionChangeEvent): boolean => {
|
|
70
|
-
if (event.tailId === undefined) {
|
|
71
|
-
return false; // promotion / tail-less event never originates
|
|
72
|
-
}
|
|
73
|
-
const topicId = anchor.topicId(reactivityTailBytes(event.tailId));
|
|
74
|
-
const coord = addressing.coord0(topicId);
|
|
75
|
-
return deps.fret.assembleCohort(coord, deps.wantK).includes(deps.selfPeerId);
|
|
76
|
-
};
|
|
77
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Reactivity origination membership gate (`docs/reactivity.md` §Anchor, `docs/internals.md`
|
|
3
|
+
* §Cohort-Topic Origination Bridge).
|
|
4
|
+
*
|
|
5
|
+
* Builds the `selfIsCohortMember(event)` predicate the change-notifier bridge gates each member commit
|
|
6
|
+
* on: this node is responsible for a collection's reactivity-topic fan-out iff it sits in the FRET
|
|
7
|
+
* cohort around `coord_0(H(currentTailId ‖ "reactivity"))`. The tail comes from `event.tailId` (the seam
|
|
8
|
+
* ticket put the committed `CommitRequest.tailId` on the change event), so the decision is synchronous —
|
|
9
|
+
* no async storage read, no per-collection registry. A tail-less event (a read-driven promotion) is
|
|
10
|
+
* never a member; those never originate and are cert-gated out downstream anyway.
|
|
11
|
+
*
|
|
12
|
+
* Extracted from the node assembly so the encoding + coord derivation are unit-testable in isolation and
|
|
13
|
+
* reusable by the future reactivity origination wiring.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import {
|
|
17
|
+
createReactivityTopicAnchor,
|
|
18
|
+
createTierAddressing,
|
|
19
|
+
createRingHash,
|
|
20
|
+
type ReactivityTopicAnchor,
|
|
21
|
+
type TierAddressing,
|
|
22
|
+
type CollectionChangeEvent,
|
|
23
|
+
} from "@optimystic/db-core";
|
|
24
|
+
import type { FretService } from "p2p-fret";
|
|
25
|
+
|
|
26
|
+
// The pinned `BlockId` → raw tail-bytes encoding now lives in the reactivity surface so the origination
|
|
27
|
+
// gate (here) and the subscriber-side subscribe factory import the SAME function — see `reactivity/
|
|
28
|
+
// topic-bytes.ts` for the load-bearing rationale. Re-exported so this module's existing callers (tests,
|
|
29
|
+
// node wiring) keep importing `reactivityTailBytes` from the gate.
|
|
30
|
+
export { reactivityTailBytes } from "../reactivity/topic-bytes.js";
|
|
31
|
+
import { reactivityTailBytes } from "../reactivity/topic-bytes.js";
|
|
32
|
+
|
|
33
|
+
/** Construction inputs for {@link createReactivitySelfMembershipGate}. */
|
|
34
|
+
export interface ReactivitySelfMembershipGateDeps {
|
|
35
|
+
/** FRET membership read — the cohort assembler around a served coord (returns peer-id strings). */
|
|
36
|
+
readonly fret: Pick<FretService, "assembleCohort">;
|
|
37
|
+
/** This node's peer-id string, compared against the assembled cohort. */
|
|
38
|
+
readonly selfPeerId: string;
|
|
39
|
+
/**
|
|
40
|
+
* Requested cohort size `wantK`. MUST equal the cohort-topic host's `wantK`, so the gate checks the
|
|
41
|
+
* exact cohort the host serves; a mismatch silently breaks origination.
|
|
42
|
+
*/
|
|
43
|
+
readonly wantK: number;
|
|
44
|
+
/**
|
|
45
|
+
* Reactivity topic anchor. Default db-core's {@link createReactivityTopicAnchor} (256-bit SHA-256),
|
|
46
|
+
* byte-identical to the host's internal `new RingHash()` and the subscriber-side anchor.
|
|
47
|
+
*/
|
|
48
|
+
readonly anchor?: ReactivityTopicAnchor;
|
|
49
|
+
/**
|
|
50
|
+
* Tier addressing. Default `createTierAddressing(createRingHash())`. `coord_0` is fan-out independent
|
|
51
|
+
* (`H(0x00 ‖ topicId)`), so the default fan-out is irrelevant to the tier-0 reactivity coord.
|
|
52
|
+
*/
|
|
53
|
+
readonly addressing?: TierAddressing;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Build the synchronous `selfIsCohortMember(event)` gate for {@link attachCohortChangeBridge}.
|
|
58
|
+
*
|
|
59
|
+
* Returns `false` for a tail-less event; otherwise resolves `coord_0(H(event.tailId ‖ "reactivity"))`
|
|
60
|
+
* via the shared anchor + tier addressing and returns whether `fret.assembleCohort(coord, wantK)`
|
|
61
|
+
* includes this node's peer id. Pure aside from the live FRET read; membership churn between commits is
|
|
62
|
+
* fine because reactivity is hint-only.
|
|
63
|
+
*/
|
|
64
|
+
export function createReactivitySelfMembershipGate(
|
|
65
|
+
deps: ReactivitySelfMembershipGateDeps,
|
|
66
|
+
): (event: CollectionChangeEvent) => boolean {
|
|
67
|
+
const anchor = deps.anchor ?? createReactivityTopicAnchor();
|
|
68
|
+
const addressing = deps.addressing ?? createTierAddressing(createRingHash());
|
|
69
|
+
return (event: CollectionChangeEvent): boolean => {
|
|
70
|
+
if (event.tailId === undefined) {
|
|
71
|
+
return false; // promotion / tail-less event never originates
|
|
72
|
+
}
|
|
73
|
+
const topicId = anchor.topicId(reactivityTailBytes(event.tailId));
|
|
74
|
+
const coord = addressing.coord0(topicId);
|
|
75
|
+
return deps.fret.assembleCohort(coord, deps.wantK).includes(deps.selfPeerId);
|
|
76
|
+
};
|
|
77
|
+
}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import type { ISizeEstimator } from "@optimystic/db-core";
|
|
2
|
-
import type { FretService } from "p2p-fret";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* FRET-backed {@link ISizeEstimator}: feeds `d_max` from FRET's network-size estimate
|
|
6
|
-
* (`FretService.getNetworkSizeEstimate`). A clean, total binding — db-core's `d_max` computer applies
|
|
7
|
-
* the low-confidence clamp itself, so this adapter only forwards FRET's `(size_estimate, confidence)`.
|
|
8
|
-
*/
|
|
9
|
-
export class FretSizeEstimator implements ISizeEstimator {
|
|
10
|
-
constructor(private readonly fret: FretService) {}
|
|
11
|
-
|
|
12
|
-
estimate(): { nEst: number; confidence: number } {
|
|
13
|
-
const { size_estimate, confidence } = this.fret.getNetworkSizeEstimate();
|
|
14
|
-
return { nEst: size_estimate, confidence };
|
|
15
|
-
}
|
|
16
|
-
}
|
|
1
|
+
import type { ISizeEstimator } from "@optimystic/db-core";
|
|
2
|
+
import type { FretService } from "p2p-fret";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* FRET-backed {@link ISizeEstimator}: feeds `d_max` from FRET's network-size estimate
|
|
6
|
+
* (`FretService.getNetworkSizeEstimate`). A clean, total binding — db-core's `d_max` computer applies
|
|
7
|
+
* the low-confidence clamp itself, so this adapter only forwards FRET's `(size_estimate, confidence)`.
|
|
8
|
+
*/
|
|
9
|
+
export class FretSizeEstimator implements ISizeEstimator {
|
|
10
|
+
constructor(private readonly fret: FretService) {}
|
|
11
|
+
|
|
12
|
+
estimate(): { nEst: number; confidence: number } {
|
|
13
|
+
const { size_estimate, confidence } = this.fret.getNetworkSizeEstimate();
|
|
14
|
+
return { nEst: size_estimate, confidence };
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -1,87 +1,135 @@
|
|
|
1
|
-
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* mirrors FRET's `rpc/maybe-act.ts`
|
|
7
|
-
*
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Single-frame libp2p stream helpers, shared by cohort-topic, matchmaking, and reactivity.
|
|
3
|
+
*
|
|
4
|
+
* These protocols exchange a single self-delimiting cohort frame each way (the db-core wire codec
|
|
5
|
+
* already length-prefixes the body), so request/response is one `send` + one bounded read. This
|
|
6
|
+
* mirrors FRET's `rpc/maybe-act.ts` and reuses FRET's exported `readAllBounded`, keeping the stream
|
|
7
|
+
* lifecycle (open → send → close-write → read → close) consistent across both protocol families.
|
|
8
|
+
*
|
|
9
|
+
* {@link openStream} deliberately duplicates FRET's `rpc/protocols.ts#openRpcStream` connection
|
|
10
|
+
* selection, because FRET does not export that helper (its package `exports` map exposes only the
|
|
11
|
+
* root entry, so there is no deep import either).
|
|
12
|
+
* NOTE: if FRET ever exports `openRpcStream`, delete {@link openStream} and call it instead — the
|
|
13
|
+
* divergence between the two copies is exactly what let the missing `runOnLimitedConnection` flag
|
|
14
|
+
* hide here. Same applies to `libp2p-key-network.ts#connect`, which is a third copy.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import type { Libp2p } from "libp2p";
|
|
18
|
+
import type { Connection, PeerId, Stream } from "@libp2p/interface";
|
|
19
|
+
import { readAllBounded } from "p2p-fret";
|
|
20
|
+
|
|
21
|
+
/** Default per-frame ceiling — matches FRET's 512 KiB maybe-act bound. */
|
|
22
|
+
export const DEFAULT_STREAM_MAX_BYTES = 512 * 1024;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* libp2p refuses to open a protocol stream over a *limited* (circuit-relay) connection unless the
|
|
26
|
+
* caller opts in, so every peer reachable only through a relay — the steady state for browsers and
|
|
27
|
+
* NATed peers — depends on this flag. It is a harmless no-op on a direct connection.
|
|
28
|
+
*
|
|
29
|
+
* NOTE: accepted tradeoff — FRET's `openRpcStream` and `libp2p-key-network.ts#connect` also set
|
|
30
|
+
* `negotiateFully: false`; deliberately not set here. It saves a round trip but defers an
|
|
31
|
+
* unsupported-protocol failure from stream-open to the first read, which would turn
|
|
32
|
+
* {@link sendOneWay} against a peer lacking the protocol into a silent no-op. Revisit if
|
|
33
|
+
* stream-open latency shows up in a profile.
|
|
34
|
+
*/
|
|
35
|
+
const STREAM_OPTIONS = { runOnLimitedConnection: true } as const;
|
|
36
|
+
|
|
37
|
+
/** True for a circuit-relay ("limited") connection: libp2p stamps one with per-circuit `limits`;
|
|
38
|
+
* sniffing `/p2p-circuit` in the multiaddr covers transports that leave `limits` unpopulated. */
|
|
39
|
+
function isLimitedConnection(c: Connection): boolean {
|
|
40
|
+
if ((c as { limits?: unknown }).limits != null) return true;
|
|
41
|
+
return c.remoteAddr?.toString?.().includes("/p2p-circuit") ?? false;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Open `protocol` to `peer`, reusing a healthy existing connection when there is one.
|
|
46
|
+
*
|
|
47
|
+
* Skips connections libp2p has not yet evicted from its index but that are no longer open, and
|
|
48
|
+
* prefers a direct connection over a relayed one — a relayed connection can be reset once the
|
|
49
|
+
* relay's per-circuit cap or reservation lapses, and after DCUtR upgrades a link to direct both
|
|
50
|
+
* briefly coexist. Falls back to the relayed connection when it is the only open path.
|
|
51
|
+
*/
|
|
52
|
+
async function openStream(node: Libp2p, peer: PeerId, protocol: string): Promise<Stream> {
|
|
53
|
+
const open = node.getConnections(peer).filter(c => c?.status === "open" && typeof c?.newStream === "function");
|
|
54
|
+
const chosen = open.find(c => !isLimitedConnection(c)) ?? open[0];
|
|
55
|
+
return chosen ? await chosen.newStream([protocol], STREAM_OPTIONS) : await node.dialProtocol(peer, [protocol], STREAM_OPTIONS);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Open `protocol` to `peer`, send `frame`, and read the bounded reply frame.
|
|
60
|
+
*
|
|
61
|
+
* NOTE: takes no `AbortSignal`, so a caller cannot set its own deadline. Bounded today anyway —
|
|
62
|
+
* `readAllBounded` self-times-out at 5s and `dialProtocol` falls back to libp2p's default dial
|
|
63
|
+
* timeout (~30s) — so an unresponsive peer is slow, not hung. If a caller ever needs a tighter
|
|
64
|
+
* deadline (`membership-source.fetch` walks candidate peers *sequentially*, so its worst case is
|
|
65
|
+
* peers × dial-timeout), thread a signal through {@link openStream} the way
|
|
66
|
+
* `libp2p-key-network.ts#connect` does.
|
|
67
|
+
*/
|
|
68
|
+
export async function requestResponse(
|
|
69
|
+
node: Libp2p,
|
|
70
|
+
peer: PeerId,
|
|
71
|
+
protocol: string,
|
|
72
|
+
frame: Uint8Array,
|
|
73
|
+
maxBytes = DEFAULT_STREAM_MAX_BYTES,
|
|
74
|
+
): Promise<Uint8Array> {
|
|
75
|
+
let stream: Stream | undefined;
|
|
76
|
+
try {
|
|
77
|
+
stream = await openStream(node, peer, protocol);
|
|
78
|
+
stream.send(frame);
|
|
79
|
+
await stream.close();
|
|
80
|
+
return await readAllBounded(stream, maxBytes);
|
|
81
|
+
} finally {
|
|
82
|
+
if (stream != null) {
|
|
83
|
+
try {
|
|
84
|
+
await stream.close();
|
|
85
|
+
} catch {
|
|
86
|
+
/* already closed */
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Open `protocol` to `peer` and send `frame` without awaiting a reply (fire-and-forget gossip). */
|
|
93
|
+
export async function sendOneWay(node: Libp2p, peer: PeerId, protocol: string, frame: Uint8Array): Promise<void> {
|
|
94
|
+
let stream: Stream | undefined;
|
|
95
|
+
try {
|
|
96
|
+
stream = await openStream(node, peer, protocol);
|
|
97
|
+
stream.send(frame);
|
|
98
|
+
await stream.close();
|
|
99
|
+
} finally {
|
|
100
|
+
if (stream != null) {
|
|
101
|
+
try {
|
|
102
|
+
await stream.close();
|
|
103
|
+
} catch {
|
|
104
|
+
/* already closed */
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** Register a request/response handler for `protocol`: read one bounded frame, reply with one frame. */
|
|
111
|
+
export function handleRequestResponse(
|
|
112
|
+
node: Libp2p,
|
|
113
|
+
protocol: string,
|
|
114
|
+
handle: (frame: Uint8Array, from: PeerId) => Promise<Uint8Array | undefined>,
|
|
115
|
+
maxBytes = DEFAULT_STREAM_MAX_BYTES,
|
|
116
|
+
): void {
|
|
117
|
+
void node.handle(protocol, (stream: Stream, connection: Connection) => {
|
|
118
|
+
void (async (): Promise<void> => {
|
|
119
|
+
try {
|
|
120
|
+
const frame = await readAllBounded(stream, maxBytes);
|
|
121
|
+
const reply = await handle(frame, connection.remotePeer);
|
|
122
|
+
if (reply !== undefined) {
|
|
123
|
+
stream.send(reply);
|
|
124
|
+
}
|
|
125
|
+
await stream.close();
|
|
126
|
+
} catch {
|
|
127
|
+
try {
|
|
128
|
+
stream.abort(new Error("cohort-topic stream handler error"));
|
|
129
|
+
} catch {
|
|
130
|
+
/* already aborted */
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
})();
|
|
134
|
+
});
|
|
135
|
+
}
|